Temperature Measurement & MODBUS Integration #17
|
@ -4,5 +4,8 @@ go 1.22.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/coder/websocket v1.8.12
|
github.com/coder/websocket v1.8.12
|
||||||
|
github.com/simonvetter/modbus v1.6.3
|
||||||
k8s.io/klog v1.0.0
|
k8s.io/klog v1.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require github.com/goburrow/serial v0.1.0 // indirect
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
||||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
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/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 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||||
|
|
|
@ -61,6 +61,11 @@ func main() {
|
||||||
}
|
}
|
||||||
d.adcPirani = adc
|
d.adcPirani = adc
|
||||||
|
|
||||||
|
err = d.modbusConnect()
|
||||||
|
if err != nil {
|
||||||
|
klog.Exitf("Failed to connect to modbus %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
for _, c := range []struct {
|
for _, c := range []struct {
|
||||||
out *gpio
|
out *gpio
|
||||||
num int
|
num int
|
||||||
|
@ -92,5 +97,9 @@ func main() {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go d.process(ctx)
|
go d.process(ctx)
|
||||||
|
if !flagFake {
|
||||||
|
go d.modbusProcess(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
}
|
}
|
||||||
|
|
101
succbone/succd/modbus.go
Normal file
101
succbone/succd/modbus.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,11 +8,13 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/simonvetter/modbus"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// daemon is the main service of the succdaemon.
|
// daemon is the main service of the succdaemon.
|
||||||
type daemon struct {
|
type daemon struct {
|
||||||
|
modbusClient *modbus.ModbusClient
|
||||||
// adcPirani is the adc implementation returning the voltage of the Pfeiffer
|
// adcPirani is the adc implementation returning the voltage of the Pfeiffer
|
||||||
// Pirani gauge.
|
// Pirani gauge.
|
||||||
adcPirani adc
|
adcPirani adc
|
||||||
|
|
Loading…
Reference in a new issue