commit f07b88e6dbb7a7ee81895d773e7cbac9ebafb651
parent b6e16b841aaaa7088d089e78f73d74e4d7c9c20f
Author: Milan Pässler <me@pbb.lc>
Date: Wed, 11 Sep 2019 22:00:36 +0200
parent b6e16b841aaaa7088d089e78f73d74e4d7c9c20f
Author: Milan Pässler <me@pbb.lc>
Date: Wed, 11 Sep 2019 22:00:36 +0200
replace libmodbus
6 files changed, 143 insertions(+), 109 deletions(-)
diff --git a/config.json b/config.json @@ -2,7 +2,7 @@ "devices": { "modbus-10": { "type": "RelayBoard", - "firstRegister": 101, + "firstRegister": 0, "count": 8, "address": 10 }, @@ -16,15 +16,20 @@ "model": "SDM120", "address": 60 }, - "lacrosse-21": { + "lacrosse-raum": { "type": "LacrosseTempSensor", - "id": "21", - "address": 21 + "id": "5", + "address": 5 }, - "lacrosse-31": { + "lacrosse-draussen": { "type": "LacrosseTempSensor", "id": "31", "address": 31 + }, + "lacrosse-bad": { + "type": "LacrosseTempSensor", + "id": "3f", + "address": 42 } }, "clientConfigs": { @@ -43,7 +48,6 @@ ] }, "smarthome-pwa": { - "source": "https://git.pbb.lc/petabyteboy/smarthome-pwa", "views": [ { "url": "lights", @@ -54,16 +58,18 @@ { "name": "Decke", "device": "modbus-10", "relay": 0 }, { "name": "Bett", "device": "modbus-10", "relay": 1 }, { "name": "Küche", "device": "modbus-10", "relay": 2 }, + { "name": "Regal", "device": "modbus-10", "relay": 7 }, { "name": "Bad", "device": "modbus-10", "relay": 3 } ] }, { "url": "switches", "name": "Switches", - "icon": "switch", + "icon": "power_settings_new", "type": "switches", "switches": [ - { "name": "Verstärker", "device": "modbus-10", "relay": 7 } + { "name": "Bett-Monitor", "device": "modbus-10", "relay": 5 }, + { "name": "Verstärker", "device": "modbus-10", "relay": 6 } ] }, { @@ -77,6 +83,17 @@ ] }, { + "url": "temperature", + "name": "Temperature", + "icon": "brightness_7", + "type": "temperature", + "sensors": [ + { "name": "Raum", "device": "lacrosse-raum" }, + { "name": "Bad", "device": "lacrosse-bad" }, + { "name": "Draussen", "device": "lacrosse-draussen" } + ] + }, + { "url": "departures", "name": "Departures", "icon": "departure_board", @@ -88,7 +105,21 @@ "name": "netdata", "icon": "show_chart", "type": "redirect", - "destination": "/netdata/" + "destination": "http://192.168.1.1/netdata/" + }, + { + "url": "fritzbox", + "name": "Fritz!Box", + "icon": "router", + "type": "redirect", + "destination": "http://10.0.0.1/" + }, + { + "url": "settings", + "name": "Settings", + "icon": "settings", + "type": "settings", + "sourceLink": "https://git.pbb.lc/petabyteboy/smarthome-pwa" } ] } @@ -101,5 +132,5 @@ "lacrosseAddr": "192.168.1.1", "lacrossePort": 2342, "powermeterUpdateIntervalSec": 20, - "accessToken": "passwort" + "accessToken": "penis123" }
diff --git a/src/backend_powermeter.nim b/src/backend_powermeter.nim @@ -8,11 +8,11 @@ import vars import times proc updatePowermeter(key: string, device: DeviceConfig) {.async.} = - let voltage = await mb.asyncReadFloat(device.address, 0) - let frequency = await mb.asyncReadFloat(device.address, 70) - let `import` = await mb.asyncReadFloat(device.address, 72) - let cosphi = await mb.asyncReadFloat(device.address, 30) - let power = await mb.asyncReadFloat(device.address, 12) + let voltage = mbFloatDCBA(await readInputRegisters(device.address, 0, 2)) + let frequency = mbFloatDCBA(await readInputRegisters(device.address, 70, 2)) + let `import` = mbFloatDCBA(await readInputRegisters(device.address, 72, 2)) + let cosphi = mbFloatDCBA(await readInputRegisters(device.address, 30, 2)) + let power = mbFloatDCBA(await readInputRegisters(device.address, 12, 2)) server.state[key].voltage = voltage server.state[key].frequency = frequency
diff --git a/src/backend_relayboard.nim b/src/backend_relayboard.nim @@ -3,6 +3,7 @@ import types import modbus import tables import vars +import sequtils proc updateRelayboards() {.async.} = echo "updating relayboards" @@ -11,7 +12,7 @@ proc updateRelayboards() {.async.} = continue try: - let data = await mb.asyncReadBits(device.address, device.firstRegister, device.count) + let data = map(await readRegisters(device.address, device.firstRegister, device.count), proc (i: uint16): bool = i != 0) server.state[key] = DeviceState(type: RelayBoard, relays: data) except: let e = getCurrentException()
diff --git a/src/modbus.nim b/src/modbus.nim @@ -1,84 +1,85 @@ -import types -import tables -import sequtils +import asyncnet import asyncdispatch -import posix +import strutils import vars -{.passL: "-lmodbus".} -proc modbus_new_tcp*(ad: cstring, port: cint): modbus {.importc, dynlib: "libmodbus.so.5"} -proc modbus_connect*(mb: modbus): cint {.importc.} -proc modbus_close*(mb: modbus): void {.importc.} -proc modbus_free*(mb: modbus): void {.importc.} -proc modbus_set_slave*(mb: modbus, ad: cint): void {.importc.} -proc modbus_read_registers*(mb: modbus, ad: cint, nb: cint, dest: pointer): cint {.importc.} -proc modbus_write_register*(mb: modbus, ad: cint, val: cint): cint {.importc.} -proc modbus_read_input_registers*(mb: modbus, ad: cint, nb: cint, dest: pointer): cint {.importc.} -proc modbus_get_float_dcba*(src: pointer): cfloat {.importc.} -proc modbus_strerror*(src: cint): cstring {.importc.} - -proc retry*[T](mb: modbus, fun: proc(): (cint, T) {.gcsafe.}): Future[T] = - var fut = newFuture[T]() - var retries = 5 - - proc timerFunc(fd: AsyncFD): bool {.gcsafe.} = - let (status, res) = fun() - if status != -1: - fut.complete(res) - else: - if errno == EBADF or errno == ECONNRESET or errno == EPIPE or errno == 112345691: - mb.modbus_close() - discard mb.modbus_connect() - echo("modbus request try ", 6 - retries, " failed: ", modbus_strerror(errno)) - retries = retries - 1 - if retries == 0: - echo "failing" - let errmsg = $(modbus_strerror(errno)) - fut.fail(newException(OsError, "modbus request failed: " & errmsg)) - else: - addTimer(100, true, timerFunc) - - return true - - addTimer(1, true, timerFunc) - return fut - -proc asyncReadFloat*(mb: modbus, ad: uint8, reg: uint8): Future[float32] {.async.} = - return await mb.retry(proc (): (cint, float32) = - var data = [0u32] - mb.modbus_set_slave(cint(ad)) - let status = mb.modbus_read_input_registers(cint(reg), 2, data.addr) - let res = modbus_get_float_dcba(data.addr) - return (status, float32(res)) - ) - -proc asyncWriteBits*(mb: modbus, ad: uint8, reg: uint8, val: seq[bool]): Future[void] {.async.} = - var data = 0u16 - for i, v in val.pairs: - data = data + uint8(v) shl i - echo val - echo data - discard await mb.retry(proc (): (cint, bool) = - mb.modbus_set_slave(cint(ad)) - let status = mb.modbus_write_register(cint(reg), cint(data)) - return (status, false) - ) - -proc asyncReadBits*(mb: modbus, ad: uint8, reg: uint8, nb: uint8): Future[seq[bool]] {.async.} = - var data: array[256, bool] - let num_registers = uint8((nb - 1) * 8) + 1 - discard await mb.retry(proc (): (cint, bool) = - mb.modbus_set_slave(cint(ad)) - let status = mb.modbus_read_registers(cint(reg), cint(num_registers), data.addr) - return (status, false) - ) - return @data[0..nb-1] - -proc initModbus*() = - let port: cint = int32(server.config.modbusPort) - mb = modbus_new_tcp(server.config.modbusAddr, port) - discard mb.modbus_connect() - -proc deinitModbus*() = - mb.modbus_close() - mb.modbus_free() +### modbus general ### + +var sock {.threadvar.}: AsyncSocket +var transaction_id {.threadvar.}: uint16 + +proc mkPacket_mbTcp(mb_packet: string): string = + inc(transaction_id) + return parseHexStr(toHex(transaction_id) & toHex(0u16) & toHex(uint16(len(mb_packet)))) & mb_packet + +proc readPacket_mbTcp(): Future[string] {.async.} = + var res = "" + + res = await sock.recv(6) + if res == "": + raise newException(OsError, "verbindung putt") + let length = fromHex[uint16](toHex(res[4..5])) + + res = await sock.recv(int(length)) + if res == "": + raise newException(OsError, "verbindung putt") + return res + +proc doRequest[T](req: string, parse_proc: proc(foo: string): T): Future[T] {.async.} = + let tcp_packet = mkPacket_mbTcp(req) + await sock.send(tcp_packet) + let answer = await readPacket_mbTcp() + return parse_proc(answer) + +### readInputRegisters ### + +proc mkPacket_readInputRegisters(unit_id: uint8, address: uint16, count: uint16): string = + return parseHexStr(toHex(unit_id) & toHex(4u8) & toHex(address) & toHex(count)) + +proc parsePacket_readInputRegisters(packet: string): seq[uint16] = + var res: seq[uint16] = @[] + let count = fromHex[int](toHex(packet[2..2])) / 2 + for i in int(1)..int(count): + res.add(fromHex[uint16](toHex(packet[2*i+1..2*i+2]))) + return res + +proc readInputRegisters*(unit_id: uint8, address: uint16, count: uint16): Future[seq[uint16]] {.async.} = + return await doRequest(mkPacket_readInputRegisters(unit_id, address, count), parsePacket_readInputRegisters) + +### readRegisters ### + +proc mkPacket_readRegisters(unit_id: uint8, address: uint16, count: uint16): string = + return parseHexStr(toHex(unit_id) & toHex(3u8) & toHex(address) & toHex(count)) + +proc parsePacket_readRegisters(packet: string): seq[uint16] = + var res: seq[uint16] = @[] + let count = fromHex[int](toHex(packet[2..2])) / 2 + for i in int(1)..int(count): + res.add(fromHex[uint16](toHex(packet[2*i+1..2*i+2]))) + return res + +proc readRegisters*(unit_id: uint8, address: uint16, count: uint16): Future[seq[uint16]] {.async.} = + return await doRequest(mkPacket_readRegisters(unit_id, address, count), parsePacket_readRegisters) + +### writeRegister ### + +proc mkPacket_writeRegister(unit_id: uint8, address: uint16, value: uint16): string = + return parseHexStr(toHex(unit_id) & toHex(6u8) & toHex(address) & toHex(value)) + +proc parsePacket_writeRegister(packet: string): bool = + return true + +proc writeRegister*(unit_id: uint8, address: uint16, value: uint16): Future[bool] {.async.} = + return await doRequest(mkPacket_writeRegister(unit_id, address, value), parsePacket_writeRegister) + +### conversion ### + +proc mbFloatDCBA*(input: seq[uint16]): float32 = + let i: uint32 = uint32(input[0]) * 65536u32 + uint32(input[1]) + return cast[float32](i) + +### main ### + +proc initModbus*() {.async.} = + sock = await asyncnet.dial(server.config.modbusAddr, Port(server.config.modbusPort)) + transaction_id = 0u16
diff --git a/src/smartied.nim b/src/smartied.nim @@ -14,16 +14,17 @@ import tables server = Server(config: parseJson(readFile("./config.json")).to(Config)) -initUtil() -initModbus() -initBackendPowermeter() -initBackendRelayboard() -initBackendLacrosse() +proc main() {.async.} = + initUtil() + await initModbus() + initBackendPowermeter() + initBackendRelayboard() + initBackendLacrosse() -asyncCheck serveTcp() -asyncCheck serveWs() -asyncCheck serveHttp() + asyncCheck serveTcp() + asyncCheck serveWs() + asyncCheck serveHttp() -runForever() + runForever() -deinitModbus() +waitFor main()
diff --git a/src/util.nim b/src/util.nim @@ -41,7 +41,7 @@ proc handleRequest*(client: Client, req: string): Future[JsonNode] {.async.} = let config = server.config.devices[action.setRelayBoard] server.state[action.setRelayBoard].relays[action.setRelay] = action.setValue - await mb.asyncWriteBits(config.address, config.firstRegister + action.setRelay, server.state[action.setRelayBoard].relays) + discard await writeRegister(config.address, config.firstRegister + action.setRelay, uint16(server.state[action.setRelayBoard].relays[action.setRelay])) broadcast($(%*server.state)) return JsonNode() @@ -49,7 +49,7 @@ proc handleRequest*(client: Client, req: string): Future[JsonNode] {.async.} = let config = server.config.devices[action.toggleRelayBoard] server.state[action.toggleRelayBoard].relays[action.toggleRelay] = not server.state[action.toggleRelayBoard].relays[action.toggleRelay] - await mb.asyncWriteBits(config.address, config.firstRegister + action.toggleRelay, server.state[action.toggleRelayBoard].relays) + discard await writeRegister(config.address, config.firstRegister + action.toggleRelay, uint16(server.state[action.toggleRelayBoard].relays[action.toggleRelay])) broadcast($(%*server.state)) return JsonNode()