succd: Migrate to KEC1 MODBUS relay board
This commit is contained in:
		
							parent
							
								
									0e45650972
								
							
						
					
					
						commit
						606d470577
					
				
					 3 changed files with 61 additions and 63 deletions
				
			
		|  | @ -48,12 +48,6 @@ func main() { | ||||||
| 	if flagFake { | 	if flagFake { | ||||||
| 		klog.Infof("Starting with fake peripherals") | 		klog.Infof("Starting with fake peripherals") | ||||||
| 		d.adcPirani = &fakeADC{} | 		d.adcPirani = &fakeADC{} | ||||||
| 		d.gpioRoughingPump = &fakeGPIO{desc: "rp"} |  | ||||||
| 		d.gpioDiffusionPump = &fakeGPIO{desc: "~dp"} |  | ||||||
| 		d.gpioBtnPumpDown = &fakeGPIO{desc: "~pd"} |  | ||||||
| 		d.gpioBtnVent = &fakeGPIO{desc: "~vent"} |  | ||||||
| 		d.gpioBelowRough = &fakeGPIO{desc: "~rough"} |  | ||||||
| 		d.gpioBelowHigh = &fakeGPIO{desc: "~high"} |  | ||||||
| 	} else { | 	} else { | ||||||
| 		adc, err := newBBADC(0) | 		adc, err := newBBADC(0) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | @ -65,23 +59,6 @@ func main() { | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			klog.Exitf("Failed to connect to modbus %v", err) | 			klog.Exitf("Failed to connect to modbus %v", err) | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 		for _, c := range []struct { |  | ||||||
| 			out *gpio |  | ||||||
| 			num int |  | ||||||
| 		}{ |  | ||||||
| 			{&d.gpioRoughingPump, 115}, |  | ||||||
| 			{&d.gpioDiffusionPump, 49}, |  | ||||||
| 			{&d.gpioBtnPumpDown, 48}, |  | ||||||
| 			{&d.gpioBtnVent, 60}, |  | ||||||
| 			{&d.gpioBelowRough, 30}, |  | ||||||
| 			{&d.gpioBelowHigh, 7}, |  | ||||||
| 		} { |  | ||||||
| 			*c.out, err = newBBGPIO(c.num, true) |  | ||||||
| 			if err != nil { |  | ||||||
| 				klog.Exitf("Failed to setup GPIO: %v", err) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	web := webServer{ | 	web := webServer{ | ||||||
|  |  | ||||||
|  | @ -63,7 +63,7 @@ func (d *daemon) modbusUpdate() error { | ||||||
| 	d.daemonState.tempSEM = modbusValuesToFloat(registers[0]) | 	d.daemonState.tempSEM = modbusValuesToFloat(registers[0]) | ||||||
| 	d.daemonState.humiditySEM = modbusValuesToFloat(registers[1]) | 	d.daemonState.humiditySEM = modbusValuesToFloat(registers[1]) | ||||||
| 
 | 
 | ||||||
| 	// Switch to slave 2 | 	// Switch to slave 2 (KEC2) | ||||||
| 	d.modbusClient.SetUnitId(2) | 	d.modbusClient.SetUnitId(2) | ||||||
| 
 | 
 | ||||||
| 	// PT100 mapping | 	// PT100 mapping | ||||||
|  | @ -76,18 +76,74 @@ func (d *daemon) modbusUpdate() error { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if len(registers) != 3 { | 	if len(registers) != 3 { | ||||||
| 		msg := fmt.Sprintf("Expected three registers from modbus slave 2, but got %d", len(registers)) | 		return fmt.Errorf("expected three registers from modbus slave 2, but got %d", len(registers)) | ||||||
| 		return errors.New(msg) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	d.daemonState.tempDPBottom = modbusValuesToFloat(registers[0]) | 	d.daemonState.tempDPBottom = modbusValuesToFloat(registers[0]) | ||||||
| 	d.daemonState.tempDPInlet = modbusValuesToFloat(registers[1]) | 	d.daemonState.tempDPInlet = modbusValuesToFloat(registers[1]) | ||||||
| 	d.daemonState.tempDPTop = modbusValuesToFloat(registers[2]) | 	d.daemonState.tempDPTop = modbusValuesToFloat(registers[2]) | ||||||
| 
 | 
 | ||||||
|  | 	// Switch to slave 3 (KEC1) | ||||||
|  | 	d.modbusClient.SetUnitId(3) | ||||||
|  | 
 | ||||||
|  | 	// Do a read first to avoid side-effects from the subsequent write to the relay states | ||||||
|  | 	var digitalInputs [8]bool | ||||||
|  | 	var digitalInputRegisters []uint16 | ||||||
|  | 
 | ||||||
|  | 	digitalInputRegisters, err = d.modbusClient.ReadRegisters(0x81, 8, modbus.HOLDING_REGISTER) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	// Convert MODBUS words into bools | ||||||
|  | 	for idx, value := range digitalInputRegisters { | ||||||
|  | 		if value != 0 { | ||||||
|  | 			digitalInputs[idx] = true | ||||||
|  | 		} else { | ||||||
|  | 			digitalInputs[idx] = false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// TODO: Input mapping goes here | ||||||
|  | 
 | ||||||
|  | 	// KFA1-KFA8 | ||||||
|  | 	var relayState [8]bool | ||||||
|  | 
 | ||||||
|  | 	// -KFA1 Roughing Pump | ||||||
|  | 	relayState[0] = d.daemonState.rpOn | ||||||
|  | 	// -KFA2 Diffusion Pump | ||||||
|  | 	relayState[1] = d.daemonState.dpOn | ||||||
|  | 	// -KFA4 Button Vent | ||||||
|  | 	relayState[3] = d.daemonState.vent.output | ||||||
|  | 	// -KFA5 Button Pump-Down | ||||||
|  | 	relayState[4] = d.daemonState.pumpdown.output | ||||||
|  | 	// -KFA6 Fake-Pirani Rough | ||||||
|  | 	relayState[5] = d.aboveRough.output | ||||||
|  | 	// -KFA7 Fake-Pirani High | ||||||
|  | 	relayState[6] = d.aboveHigh.output | ||||||
|  | 
 | ||||||
|  | 	// The KEC1 module uses a non-standard MODBUS interface | ||||||
|  | 	// instead of coils | ||||||
|  | 	// 0x0100 is the open command | ||||||
|  | 	// 0x0200 is the close command | ||||||
|  | 	// We write 8 words (16-bit) to address 0x01 to update the relays | ||||||
|  | 	var registerValues [8]uint16 | ||||||
|  | 	// Convert the boolean values to the commands | ||||||
|  | 	for idx, state := range relayState { | ||||||
|  | 		if state { | ||||||
|  | 			registerValues[idx] = 0x0100 | ||||||
|  | 		} else { | ||||||
|  | 			registerValues[idx] = 0x0200 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = d.modbusClient.WriteRegisters(0x01, registerValues[:]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Call modbusUpdate every second | // Call modbusUpdate every 100 milliseconds | ||||||
| func (d *daemon) modbusProcess(ctx context.Context) { | func (d *daemon) modbusProcess(ctx context.Context) { | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		select { | ||||||
|  | @ -95,7 +151,7 @@ func (d *daemon) modbusProcess(ctx context.Context) { | ||||||
| 			return | 			return | ||||||
| 		default: | 		default: | ||||||
| 			d.modbusUpdate() | 			d.modbusUpdate() | ||||||
| 			time.Sleep(time.Second * 1) | 			time.Sleep(time.Millisecond * 100) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -19,13 +19,6 @@ type daemon struct { | ||||||
| 	// Pirani gauge. | 	// Pirani gauge. | ||||||
| 	adcPirani adc | 	adcPirani adc | ||||||
| 
 | 
 | ||||||
| 	gpioDiffusionPump gpio |  | ||||||
| 	gpioRoughingPump  gpio |  | ||||||
| 	gpioBtnPumpDown   gpio |  | ||||||
| 	gpioBtnVent       gpio |  | ||||||
| 	gpioBelowRough    gpio |  | ||||||
| 	gpioBelowHigh     gpio |  | ||||||
| 
 |  | ||||||
| 	load atomic.Int64 | 	load atomic.Int64 | ||||||
| 
 | 
 | ||||||
| 	// mu guards the state below. | 	// mu guards the state below. | ||||||
|  | @ -171,33 +164,5 @@ func (d *daemon) processOnce(_ context.Context) error { | ||||||
| 		d.dpOn = false | 		d.dpOn = false | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Update relay outputs. |  | ||||||
| 	for _, rel := range []struct { |  | ||||||
| 		name string |  | ||||||
| 		gpio gpio |  | ||||||
| 		// activeHigh means the relay is active high, ie. a true source will |  | ||||||
| 		// mean that NO/COM get connected, and a false source means that NC/COM |  | ||||||
| 		// get connected. |  | ||||||
| 		activeHigh bool |  | ||||||
| 		source     bool |  | ||||||
| 	}{ |  | ||||||
| 		{"rp", d.gpioRoughingPump, false, d.rpOn}, |  | ||||||
| 		{"dp", d.gpioDiffusionPump, true, d.dpOn}, |  | ||||||
| 		{"pumpdown", d.gpioBtnPumpDown, true, d.pumpdown.output}, |  | ||||||
| 		{"vent", d.gpioBtnVent, true, d.vent.output}, |  | ||||||
| 		{"rough", d.gpioBelowRough, false, d.aboveRough.output}, |  | ||||||
| 		{"high", d.gpioBelowHigh, false, d.aboveHigh.output}, |  | ||||||
| 	} { |  | ||||||
| 		val := rel.source |  | ||||||
| 		if rel.activeHigh { |  | ||||||
| 			// Invert because the relays go through logical inversion (ie. a |  | ||||||
| 			// GPIO false is a relay trigger). |  | ||||||
| 			val = !val |  | ||||||
| 		} |  | ||||||
| 		if err := rel.gpio.set(val); err != nil { |  | ||||||
| 			return fmt.Errorf("when outputting %s: %w", rel.name, err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 hmelder
						hmelder