diff --git a/succbone/succd/go.mod b/succbone/succd/go.mod index 574985d..d35e107 100644 --- a/succbone/succd/go.mod +++ b/succbone/succd/go.mod @@ -4,5 +4,8 @@ go 1.22.3 require ( github.com/coder/websocket v1.8.12 + github.com/simonvetter/modbus v1.6.3 k8s.io/klog v1.0.0 ) + +require github.com/goburrow/serial v0.1.0 // indirect diff --git a/succbone/succd/go.sum b/succbone/succd/go.sum index c8b5195..ab9ca54 100644 --- a/succbone/succd/go.sum +++ b/succbone/succd/go.sum @@ -1,5 +1,9 @@ github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/goburrow/serial v0.1.0 h1:v2T1SQa/dlUqQiYIT8+Cu7YolfqAi3K96UmhwYyuSrA= +github.com/goburrow/serial v0.1.0/go.mod h1:sAiqG0nRVswsm1C97xsttiYCzSLBmUZ/VSlVLZJ8haA= +github.com/simonvetter/modbus v1.6.3 h1:kDzwVfIPczsM4Iz09il/Dij/bqlT4XiJVa0GYaOVA9w= +github.com/simonvetter/modbus v1.6.3/go.mod h1:hh90ZaTaPLcK2REj6/fpTbiV0J6S7GWmd8q+GVRObPw= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= diff --git a/succbone/succd/main.go b/succbone/succd/main.go index a56f2cc..cfd93a9 100644 --- a/succbone/succd/main.go +++ b/succbone/succd/main.go @@ -61,6 +61,11 @@ func main() { } d.adcPirani = adc + err = d.modbusConnect() + if err != nil { + klog.Exitf("Failed to connect to modbus %v", err) + } + for _, c := range []struct { out *gpio num int @@ -92,5 +97,9 @@ func main() { }() go d.process(ctx) + if !flagFake { + go d.modbusProcess(ctx) + } + <-ctx.Done() } diff --git a/succbone/succd/modbus.go b/succbone/succd/modbus.go new file mode 100644 index 0000000..15c3697 --- /dev/null +++ b/succbone/succd/modbus.go @@ -0,0 +1,101 @@ +package main + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/simonvetter/modbus" +) + +func modbusValuesToFloat(v uint16) float32 { + return float32(v) / 10.0 +} + +func (d *daemon) modbusConnect() error { + var err error + + d.mu.Lock() + defer d.mu.Unlock() + + // Setup modbus client + d.modbusClient, err = modbus.NewClient(&modbus.ClientConfiguration{ + URL: "tcp://10.250.241.20:8887", + Timeout: 5 * time.Second, + }) + if err != nil { + return err + } + // Connect to modbus client + err = d.modbusClient.Open() + if err != nil { + return err + } + + return nil +} + +// There are currently two devices connected to the modbus. +// The first one (slave 1) is a temperature/humidity sensor. +// The second one (slave 2) is a PTA8D08 transmitter +func (d *daemon) modbusUpdate() error { + d.mu.Lock() + defer d.mu.Unlock() + + var err error + var registers []uint16 // temperature, humidity + + // Switch to slave 1 + d.modbusClient.SetUnitId(1) + + // Read temperature and humidity + registers, err = d.modbusClient.ReadRegisters(1, 2, modbus.INPUT_REGISTER) + if err != nil { + return err + } + + if len(registers) != 2 { + msg := fmt.Sprintf("Expected two registers from modbus slave 1, but got %d", len(registers)) + return errors.New(msg) + } + + d.daemonState.tempSEM = modbusValuesToFloat(registers[0]) + d.daemonState.humiditySEM = modbusValuesToFloat(registers[1]) + + // Switch to slave 2 + d.modbusClient.SetUnitId(2) + + // PT100 mapping + // Channel 0: Cable -WGA6, Sensor "dp bottom" + // Channel 1: Cable -WGA8, Sensor "dp inlet" + // Channel 2: Cable WGA7, Sensor "dp top" + registers, err = d.modbusClient.ReadRegisters(0, 3, modbus.HOLDING_REGISTER) + if err != nil { + return err + } + + if len(registers) != 3 { + msg := fmt.Sprintf("Expected three registers from modbus slave 2, but got %d", len(registers)) + return errors.New(msg) + } + + d.daemonState.tempDPBottom = modbusValuesToFloat(registers[0]) + d.daemonState.tempDPInlet = modbusValuesToFloat(registers[1]) + d.daemonState.tempDPTop = modbusValuesToFloat(registers[2]) + + return nil +} + +// Call modbusUpdate every second +func (d *daemon) modbusProcess(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + default: + d.modbusUpdate() + time.Sleep(time.Second * 1) + } + } +} diff --git a/succbone/succd/process.go b/succbone/succd/process.go index c886260..7ade055 100644 --- a/succbone/succd/process.go +++ b/succbone/succd/process.go @@ -8,11 +8,13 @@ import ( "sync/atomic" "time" + "github.com/simonvetter/modbus" "k8s.io/klog" ) // daemon is the main service of the succdaemon. type daemon struct { + modbusClient *modbus.ModbusClient // adcPirani is the adc implementation returning the voltage of the Pfeiffer // Pirani gauge. adcPirani adc