Temperature Measurement & MODBUS Integration #17
|
@ -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…
Reference in a new issue