本文章介紹如何使用 nanoMODBUS 函式庫在 Raspberry Pi Pico W上實作 MODBUS TCP/RTU Gateway。使用 MODBUS TCP Master透過 Node-RED UI 控制兩個 Modbus RTU Slaves。
MODBUS TCP/RTU Gateway收到modbus tcp master(client) request 根據UintID轉送到相對應的MODUBS RTU Slave(msater),Response依相反順序進行。
#include "stdio.h"
#include "pico/stdlib.h"
#include "modbus_tcp_client.h"
#include "lwip/pbuf.h"
#include "lwip/tcp.h"
#include "picow_tcp_server/picow_tcp_server.h"
#include "modbus_rtu_master/modbus_rtu_master.h"
//#define _DEBUG
#ifdef _DEBUG
#define DEBUG_PRINT(...) printf(__VA_ARGS__)
#else
#define DEBUG_PRINT(...) (void) (0)
#endif
nmbs_t nmbs_gateway_tcp;
// A single nmbs_bitfield variable can keep 2000 coils
bool terminate = false;
nmbs_bitfield server_coils[MAX_CLIENT_DEVICES] = {0};
uint16_t server_registers[MAX_CLIENT_DEVICES][REGS_ADDR_MAX] = {0};
uint16_t server_file[MAX_CLIENT_DEVICES][FILE_SIZE_MAX];
#define UNUSED_PARAM(x) ((x) = (x))
void sighandler(int s) {
UNUSED_PARAM(s);
terminate = true;
}
nmbs_error handle_tcp_read_coils(uint16_t address, uint16_t quantity, nmbs_bitfield coils_out, uint8_t unit_id, void* arg) {
UNUSED_PARAM(arg);
UNUSED_PARAM(unit_id);
MB_SERVER_T * mb = (MB_SERVER_T*) arg;
if (address + quantity > COILS_ADDR_MAX + 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
nmbs_set_destination_rtu_address(&nmbs_rtu_master, unit_id);
nmbs_error err = nmbs_read_coils(&nmbs_rtu_master, address, quantity, coils_out);
if (err != NMBS_ERROR_NONE) {
DEBUG_printf("Error info(read coils): unit_id:%d, %s\n", unit_id,
nmbs_strerror(err));
return err;
}
for (int i = 0; i < quantity; i++) {
nmbs_bitfield_write(server_coils[unit_id], address + i, nmbs_bitfield_read(coils_out, i));
}
return NMBS_ERROR_NONE;
}
nmbs_error handle_tcp_read_discrete_inputs(uint16_t address, uint16_t quantity, nmbs_bitfield coils_out, uint8_t unit_id, void* arg) {
UNUSED_PARAM(arg);
UNUSED_PARAM(unit_id);
if (address + quantity > COILS_ADDR_MAX + 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
nmbs_set_destination_rtu_address(&nmbs_rtu_master, unit_id);
nmbs_error err = nmbs_read_discrete_inputs(&nmbs_rtu_master, address, quantity, coils_out);
if (err != NMBS_ERROR_NONE) {
DEBUG_printf("Error info(read discrete coils): unit_id:%d, %s\n", unit_id,
nmbs_strerror(err));
return err;
}
for (int i = 0; i < quantity; i++) {
nmbs_bitfield_write(server_coils[unit_id], address + i, nmbs_bitfield_read(coils_out, i));
}
return NMBS_ERROR_NONE;
}
nmbs_error handle_tcp_write_single_coil(uint16_t address, bool value, uint8_t unit_id, void* arg) {
UNUSED_PARAM(arg);
UNUSED_PARAM(unit_id);
if (address > COILS_ADDR_MAX )
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
nmbs_set_destination_rtu_address(&nmbs_rtu_master, unit_id);
nmbs_error err = nmbs_write_single_coil(&nmbs_rtu_master, address, value);
if (err != NMBS_ERROR_NONE) {
DEBUG_printf("Error info(write single coil): unit_id:%d, %s\n", unit_id,
nmbs_strerror(err));
return err;
}
// Write coils values to our server_coils
nmbs_bitfield_write(server_coils[unit_id], address, value);
return NMBS_ERROR_NONE;
}
nmbs_error handle_tcp_write_multiple_coils(uint16_t address, uint16_t quantity, const nmbs_bitfield coils, uint8_t unit_id,
void* arg) {
UNUSED_PARAM(arg);
UNUSED_PARAM(unit_id);
if (address + quantity > COILS_ADDR_MAX + 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
nmbs_set_destination_rtu_address(&nmbs_rtu_master, unit_id);
nmbs_error err = nmbs_write_multiple_coils(&nmbs_rtu_master, address, quantity, coils);
if (err != NMBS_ERROR_NONE) {
DEBUG_printf("Error info(write mulitple coils): unit_id:%d, %s\n", unit_id,
nmbs_strerror(err));
uart_reset(UART_ID);
return err;
}
// Write coils values to our server_coils
for (int i = 0; i < quantity; i++) {
nmbs_bitfield_write(server_coils[unit_id], address + i, nmbs_bitfield_read(coils, i));
}
return NMBS_ERROR_NONE;
}
nmbs_error handler_tcp_read_holding_registers(uint16_t address, uint16_t quantity, uint16_t* registers_out, uint8_t unit_id,
void* arg) {
UNUSED_PARAM(arg);
UNUSED_PARAM(unit_id);
if (address + quantity > REGS_ADDR_MAX + 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
nmbs_set_destination_rtu_address(&nmbs_rtu_master, unit_id);
nmbs_error err = nmbs_read_holding_registers(&nmbs_rtu_master, address, quantity, registers_out);
// Read our registers values into registers_out
if (err != NMBS_ERROR_NONE) {
DEBUG_printf("Error info(read holding regiseters): unit_id:%d, %s\n", unit_id,
nmbs_strerror(err));
return err;
}
for (int i = 0; i < quantity; i++)
server_registers[unit_id][address + i] = registers_out[i];
return NMBS_ERROR_NONE;
}
nmbs_error handle_tcp_write_single_register(uint16_t address, uint16_t value, uint8_t unit_id, void* arg) {
UNUSED_PARAM(arg);
UNUSED_PARAM(unit_id);
if (address > REGS_ADDR_MAX)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
nmbs_set_destination_rtu_address(&nmbs_rtu_master, unit_id);
nmbs_error err = nmbs_write_single_register(&nmbs_rtu_master, address, value);
if (err != NMBS_ERROR_NONE) {
DEBUG_printf("Error info(write single register): unit_id:%d, %s\n", unit_id,
nmbs_strerror(err));
return err;
}
// Write registers values to our server_registers
server_registers[unit_id][address] = value;
return NMBS_ERROR_NONE;
}
nmbs_error handle_tcp_write_multiple_registers(uint16_t address, uint16_t quantity, const uint16_t* registers,
uint8_t unit_id, void* arg) {
UNUSED_PARAM(arg);
UNUSED_PARAM(unit_id);
if (address + quantity > REGS_ADDR_MAX + 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
nmbs_set_destination_rtu_address(&nmbs_rtu_master, unit_id);
nmbs_error err = nmbs_write_multiple_registers(&nmbs_rtu_master, address, quantity, registers);
if (err != NMBS_ERROR_NONE) {
DEBUG_printf("Error info(write multiple registers): unit_id:%d, %s\n", unit_id,
nmbs_strerror(err));
return err;
}
// Write registers values to our server_registers
for (int i = 0; i < quantity; i++)
server_registers[unit_id][address + i] = registers[i];
return NMBS_ERROR_NONE;
}
nmbs_error handle_tcp_read_file_record(uint16_t file_number, uint16_t record_number, uint16_t* registers, uint16_t count,
uint8_t unit_id, void* arg) {
UNUSED_PARAM(arg);
UNUSED_PARAM(unit_id);
if (file_number != 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
if ((record_number + count) > FILE_SIZE_MAX)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
nmbs_set_destination_rtu_address(&nmbs_rtu_master, unit_id);
nmbs_error err = nmbs_read_file_record(&nmbs_rtu_master, file_number, record_number, registers, count);
if (err != NMBS_ERROR_NONE) {
DEBUG_printf("Error info: unit_id:%d, %s\n", unit_id,
nmbs_strerror(err));
return err;
}
memcpy(registers, server_file[unit_id] + record_number, count * sizeof(uint16_t));
return NMBS_ERROR_NONE;
}
nmbs_error handle_tcp_write_file_record(uint16_t file_number, uint16_t record_number, const uint16_t* registers,
uint16_t count, uint8_t unit_id, void* arg) {
UNUSED_PARAM(arg);
UNUSED_PARAM(unit_id);
if (file_number != 1)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
if ((record_number + count) > FILE_SIZE_MAX)
return NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS;
nmbs_set_destination_rtu_address(&nmbs_rtu_master, unit_id);
nmbs_error err = nmbs_write_file_record(&nmbs_rtu_master, file_number, record_number, registers, count);
if (err != NMBS_ERROR_NONE) {
DEBUG_printf("Error info: unit_id:%d, %s\n", unit_id,
nmbs_strerror(err));
return err;
}
memcpy(server_file[unit_id] + record_number, registers, count * sizeof(uint16_t));
return NMBS_ERROR_NONE;
}
int32_t nmbs_transport_read(uint8_t* buf, uint16_t count, int32_t timeout_ms, void* arg) {
MB_SERVER_T *state=(MB_SERVER_T*) arg;
uint16_t loop_count = 0;
if (state->queue_left == 0) {
while (queue_is_empty(&state->recv_queue) && loop_count < 20) {
loop_count++;
busy_wait_ms(50);
}
if (!queue_try_remove(&(state->recv_queue), &state->temp_recv)) {
return 0;
} else {
DEBUG_PRINT("\t\t\t\t---loop count:%d\n", loop_count);
}
state->queue_left = state->temp_recv.buffer_len;
}
state->recv_count = state->queue_left <= count ? state->queue_left : count;
state->start_index = state->temp_recv.buffer_len-state->queue_left;
for (int i=0; i < state->recv_count; i++) buf[i] = state->temp_recv.buffer[state->start_index+i];
state->queue_left = state->queue_left-state->recv_count;
//Debug
#ifdef _DEBUG
DEBUG_PRINT("\n=====transport read:%d:%d\n", count, state->temp_recv.buffer_len);
for (int i=0; i < state->recv_count; i++) {
DEBUG_PRINT("%02x ", buf[i]);
}
DEBUG_PRINT("\n==\n");
#endif
return state->recv_count;
}
int32_t nmbs_transport_write(const uint8_t* buf, uint16_t count, int32_t timeout_ms, void* arg) {
MB_SERVER_T *state = (MB_SERVER_T*)arg;
// Debug
#ifdef _DEBUG
DEBUG_PRINT("====\nnmbs_transport_write count:%d\n", count);
for (int i=0; i < count;i++) {
DEBUG_PRINT("%02x ", buf[i]);
}
DEBUG_PRINT("\n====\n");
#endif
err_t err = tcp_write(state->client_pcb, buf, count, 0 /*TCP_WRITE_FLAG_COPY*/);
if (err != ERR_OK)
{
//tcp_abort(state->tcp_pcb);
DEBUG_PRINT("tcp_write error:%d:%d\n", err, tcp_sndbuf(state->client_pcb));
return 0;
}
err = tcp_output(state->client_pcb);
if (err != ERR_OK)
{
DEBUG_PRINT("%s:tcp_output:%d\n", state->server_host_name, err);
return 0;
}
return count;
}
[
{
"id": "48fad0c0f57cea35",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "67287decfe7536cf",
"type": "ui_colour_picker",
"z": "48fad0c0f57cea35",
"name": "",
"label": "Color of LED",
"group": "fc04be05304743a6",
"format": "rgb",
"outformat": "object",
"showSwatch": true,
"showPicker": false,
"showValue": false,
"showHue": true,
"showAlpha": false,
"showLightness": false,
"square": "true",
"dynOutput": "false",
"order": 2,
"width": 0,
"height": 0,
"passthru": false,
"topic": "color",
"topicType": "str",
"className": "",
"x": 510,
"y": 40,
"wires": [
[
"e50739a98c836bc2"
]
]
},
{
"id": "730e5141782e4353",
"type": "ui_slider",
"z": "48fad0c0f57cea35",
"name": "",
"label": "num 0f Leds",
"tooltip": "",
"group": "fc04be05304743a6",
"order": 3,
"width": 0,
"height": 0,
"passthru": false,
"outs": "all",
"topic": "leds",
"topicType": "str",
"min": 0,
"max": 10,
"step": 1,
"className": "",
"x": 530,
"y": 140,
"wires": [
[
"945a43328cae473b"
]
]
},
{
"id": "850f63e35b7c0dd6",
"type": "function",
"z": "48fad0c0f57cea35",
"name": "Modbus Uint 2",
"func": "var regs = [4];\nregs[0] = flow.get('color_red');\nregs[1] = flow.get('color_green');\nregs[2] = flow.get('color_blue');\nregs[3] = flow.get('num_leds');\nmsg.payload = {value: regs, 'fc': 16, 'unitid': 2, 'address': 0, 'quantity': 4 };\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 900,
"y": 80,
"wires": [
[
"0b897cabd78f207b"
]
]
},
{
"id": "e50739a98c836bc2",
"type": "change",
"z": "48fad0c0f57cea35",
"name": "set flow color",
"rules": [
{
"t": "set",
"p": "color_red",
"pt": "flow",
"to": "payload.r",
"tot": "msg"
},
{
"t": "set",
"p": "color_green",
"pt": "flow",
"to": "payload.g",
"tot": "msg"
},
{
"t": "set",
"p": "color_blue",
"pt": "flow",
"to": "payload.b",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 710,
"y": 40,
"wires": [
[
"850f63e35b7c0dd6"
]
]
},
{
"id": "945a43328cae473b",
"type": "change",
"z": "48fad0c0f57cea35",
"name": "",
"rules": [
{
"t": "set",
"p": "num_leds",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 710,
"y": 140,
"wires": [
[
"850f63e35b7c0dd6"
]
]
},
{
"id": "0b897cabd78f207b",
"type": "modbus-flex-write",
"z": "48fad0c0f57cea35",
"name": "",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"server": "54922a4357ba259d",
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 1090,
"y": 260,
"wires": [
[
"24a8a58f01fcf5cc",
"00a3c1cedc235bc8"
],
[]
]
},
{
"id": "24a8a58f01fcf5cc",
"type": "modbus-response",
"z": "48fad0c0f57cea35",
"name": "",
"registerShowMax": 20,
"x": 1130,
"y": 160,
"wires": []
},
{
"id": "40e24a450a247c58",
"type": "ui_button",
"z": "48fad0c0f57cea35",
"name": "Gate",
"group": "fc04be05304743a6",
"order": 8,
"width": 2,
"height": 1,
"passthru": false,
"label": "{{lbl}}",
"tooltip": "",
"color": "",
"bgcolor": "{{background}}",
"className": "",
"icon": "{{myicon}}",
"payload": "u1_coil0",
"payloadType": "flow",
"topic": "addr0",
"topicType": "str",
"x": 570,
"y": 240,
"wires": [
[
"c24f886f7213b046"
]
]
},
{
"id": "5654c6c9dbd8c887",
"type": "function",
"z": "48fad0c0f57cea35",
"name": "Modbus Uint 1",
"func": "if (msg.address == 0)\n msg.payload = {value: msg.payload, 'fc': 15, 'unitid': 1, 'address': msg.address, 'quantity': msg.quantity };\nif (msg.address == 1)\n msg.payload = {value: msg.payload, 'fc': 5, 'unitid': 1, 'address': msg.address, 'quantity': msg.quantity };\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 900,
"y": 320,
"wires": [
[
"0b897cabd78f207b"
]
]
},
{
"id": "c24f886f7213b046",
"type": "function",
"z": "48fad0c0f57cea35",
"name": "Uint 1",
"func": "var newmsg={};\n\nif (msg.payload == true) {\n if (msg.topic == 'addr0') {\n newmsg.lbl = 'CLOSE';\n newmsg.background=\"magenta\";\n }\n else {\n newmsg.lbl = \"OFF\";\n newmsg.background=\"red\";\n }\n newmsg.myicon = 'fa-toggle-off';\n\n \n} else { \n if (msg.topic == 'addr0') {\n newmsg.lbl='OPEN';\n newmsg.background='lightblue';\n }\n else {\n newmsg.lbl = 'ON';\n newmsg.background='blue';\n }\n newmsg.myicon = 'fa-toggle-on';\n \n}\nnewmsg.topic = msg.topic;\nlet address =2;\nif (newmsg.topic == 'addr0') {\n address =0;\n //newmsg.payload={};\n flow.set('u1_coil0', !msg.payload);\n flow.set('u1_coil1', !msg.payload);\n newmsg.payload = [msg.payload, msg.payload];\n newmsg.quantity = 2;\n}\nif (newmsg.topic == 'addr1') {\n address =1;\n flow.set('u1_coil1', !msg.payload);\n newmsg.payload=msg.payload;\n newmsg.quantity = 1;\n}\nnewmsg.address=address;\n\nreturn newmsg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 710,
"y": 320,
"wires": [
[
"5654c6c9dbd8c887",
"b090ab4f179755b5"
]
]
},
{
"id": "3009c8f5180b8b48",
"type": "ui_button",
"z": "48fad0c0f57cea35",
"name": "Light",
"group": "fc04be05304743a6",
"order": 12,
"width": 2,
"height": 1,
"passthru": false,
"label": "{{lbl}}",
"tooltip": "",
"color": "",
"bgcolor": "{{background}}",
"className": "",
"icon": "{{myicon}}",
"payload": "u1_coil1",
"payloadType": "flow",
"topic": "addr1",
"topicType": "str",
"x": 570,
"y": 420,
"wires": [
[
"c24f886f7213b046"
]
]
},
{
"id": "b090ab4f179755b5",
"type": "switch",
"z": "48fad0c0f57cea35",
"name": "toggle",
"property": "topic",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "addr0",
"vt": "str"
},
{
"t": "eq",
"v": "addr1",
"vt": "str"
}
],
"checkall": "false",
"repair": false,
"outputs": 2,
"x": 430,
"y": 360,
"wires": [
[
"deb6e9852981cbe7"
],
[
"ea4d168a958d4731"
]
]
},
{
"id": "7fcff809d9501142",
"type": "modbus-getter",
"z": "48fad0c0f57cea35",
"name": "Uint1 FC:1",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"logIOActivities": false,
"unitid": "1",
"dataType": "Coil",
"adr": "0",
"quantity": "2",
"server": "54922a4357ba259d",
"useIOFile": false,
"ioFile": "",
"useIOForPayload": false,
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 170,
"y": 340,
"wires": [
[
"78865a9a77ef7a00",
"cf34a46dd051bf77"
],
[]
]
},
{
"id": "78865a9a77ef7a00",
"type": "function",
"z": "48fad0c0f57cea35",
"name": "get coil 0 init value",
"func": "var newmsg={};\nflow.set('u1_coil0',!msg.payload[0]);\n\nnewmsg.payload=msg.payload[0];\n\nif (msg.payload[0] == false) {\n newmsg.lbl = \"OPEN\";\n newmsg.myicon='fa-toggle-on';\n newmsg.background='lightblue';\n} else {\n newmsg.lbl = 'CLOSE';\n newmsg.myicon='fa-toggle-off';\n newmsg.background='magenta';\n}\nreturn newmsg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 290,
"y": 240,
"wires": [
[
"deb6e9852981cbe7"
]
]
},
{
"id": "cf34a46dd051bf77",
"type": "function",
"z": "48fad0c0f57cea35",
"name": "get coil 1 init value",
"func": "var newmsg={};\nflow.set('u1_coil1',!msg.payload[1]);\n\nnewmsg.payload=msg.payload[1];\n\nif (msg.payload[1] == false) {\n newmsg.lbl = \"ON\";\n newmsg.background='blue';\n newmsg.myicon='fa-toggle-on';\n} else {\n newmsg.lbl = 'OFF';\n newmsg.myicon='fa-toggle-off';\n newmsg.background='red';\n}\n//newmsg.label = newmsg.label_v;\nreturn newmsg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 290,
"y": 460,
"wires": [
[
"ea4d168a958d4731"
]
]
},
{
"id": "714e1f0e2afa0885",
"type": "function",
"z": "48fad0c0f57cea35",
"name": "color",
"func": "var newmsg={};\nnewmsg.payload = {r:msg.payload[0],g:msg.payload[1],b:msg.payload[2], a:1};\nreturn newmsg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 330,
"y": 40,
"wires": [
[
"67287decfe7536cf"
]
]
},
{
"id": "c1fc304462222840",
"type": "function",
"z": "48fad0c0f57cea35",
"name": "num_of_leds",
"func": "var newmsg = {};\nnewmsg.payload = msg.payload[3];\nreturn newmsg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 350,
"y": 140,
"wires": [
[
"730e5141782e4353"
]
]
},
{
"id": "22c0fbed37498038",
"type": "ui_text",
"z": "48fad0c0f57cea35",
"group": "fc04be05304743a6",
"order": 1,
"width": 0,
"height": 0,
"name": "",
"label": "Unit 1",
"format": "{{msg.payload}}",
"layout": "row-left",
"className": "",
"style": true,
"font": "Times New Roman,Times,serif",
"fontSize": "22",
"color": "#0000ff",
"x": 890,
"y": 280,
"wires": []
},
{
"id": "b3dc9565e22052fe",
"type": "ui_text",
"z": "48fad0c0f57cea35",
"group": "fc04be05304743a6",
"order": 5,
"width": 0,
"height": 0,
"name": "",
"label": "Unit 2",
"format": "{{msg.payload}}",
"layout": "row-left",
"className": "",
"style": true,
"font": "Times New Roman,Times,serif",
"fontSize": "22",
"color": "#0000ff",
"x": 890,
"y": 40,
"wires": []
},
{
"id": "40896a542df60d8a",
"type": "ui_text",
"z": "48fad0c0f57cea35",
"group": "fc04be05304743a6",
"order": 6,
"width": 1,
"height": 2,
"name": "",
"label": "Gate",
"format": "{{msg.payload}}",
"layout": "col-center",
"className": "",
"style": true,
"font": "",
"fontSize": "15",
"color": "#8f0a0a",
"x": 710,
"y": 240,
"wires": []
},
{
"id": "4828163558cac23f",
"type": "ui_text",
"z": "48fad0c0f57cea35",
"group": "fc04be05304743a6",
"order": 10,
"width": 1,
"height": 2,
"name": "",
"label": "Light:",
"format": "{{msg.payload}}",
"layout": "col-center",
"className": "",
"style": true,
"font": "Verdana,Verdana,Geneva,sans-serif",
"fontSize": "15",
"color": "#133305",
"x": 710,
"y": 420,
"wires": []
},
{
"id": "7710bdc96bee5a64",
"type": "link in",
"z": "48fad0c0f57cea35",
"name": "Unit 1:Read Coil 1",
"links": [
"336bf1d73e3c16f0"
],
"x": 35,
"y": 340,
"wires": [
[
"7fcff809d9501142"
]
]
},
{
"id": "336bf1d73e3c16f0",
"type": "link out",
"z": "48fad0c0f57cea35",
"name": "Unit 1: Read Coil 1",
"mode": "link",
"links": [
"7710bdc96bee5a64"
],
"x": 1235,
"y": 300,
"wires": []
},
{
"id": "fe478f2f1bf74258",
"type": "ui_ui_control",
"z": "48fad0c0f57cea35",
"name": "",
"events": "all",
"x": 80,
"y": 200,
"wires": [
[
"4855c1d9cb3c5de3",
"7fcff809d9501142"
]
]
},
{
"id": "4855c1d9cb3c5de3",
"type": "modbus-getter",
"z": "48fad0c0f57cea35",
"name": "Unit2 FC:3",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"logIOActivities": false,
"unitid": "2",
"dataType": "HoldingRegister",
"adr": "0",
"quantity": "4",
"server": "54922a4357ba259d",
"useIOFile": false,
"ioFile": "",
"useIOForPayload": false,
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 170,
"y": 100,
"wires": [
[
"714e1f0e2afa0885",
"c1fc304462222840"
],
[]
]
},
{
"id": "ea4d168a958d4731",
"type": "ui_template",
"z": "48fad0c0f57cea35",
"group": "fc04be05304743a6",
"name": "Light Status",
"order": 11,
"width": 2,
"height": 2,
"format": "<script>\n(function(scope) {\n scope.$watch('msg', function(msg) {\n if (msg) {\n if (msg.payload) {\n scope.light_icon=\"light_on.png\";\n } else {\n scope.light_icon=\"light_off.png\";\n }\n }\n\n\n });\n\n})(scope);\n</script>\n\n\n<div>\n <img src = {{light_icon}} width=60 height=60>\n \n</div>\n",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 510,
"y": 480,
"wires": [
[
"3009c8f5180b8b48"
]
]
},
{
"id": "deb6e9852981cbe7",
"type": "ui_template",
"z": "48fad0c0f57cea35",
"group": "fc04be05304743a6",
"name": "Gate Status",
"order": 7,
"width": 2,
"height": 2,
"format": "<script>\n(function(scope) {\n scope.$watch('msg', function(msg) {\n if (msg) {\n if (msg.payload) {\n scope.light_icon=\"gate_open.png\";\n } else {\n scope.light_icon=\"gate_close.png\";\n }\n }\n\n\n });\n\n})(scope);\n</script>\n\n\n<div>\n <img src = {{light_icon}} width=60 height=60>\n \n</div>\n",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 490,
"y": 300,
"wires": [
[
"40e24a450a247c58"
]
]
},
{
"id": "1f2e434da8912dc8",
"type": "ui_template",
"z": "48fad0c0f57cea35",
"group": "fc04be05304743a6",
"name": "seprate line",
"order": 4,
"width": 0,
"height": 0,
"format": "<div>\n <img src='line.png' width=250>\n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 630,
"y": 180,
"wires": [
[]
]
},
{
"id": "d11918fa96d8a971",
"type": "link in",
"z": "48fad0c0f57cea35",
"name": "Unit 2: read registers",
"links": [
"41175f479b349001"
],
"x": 45,
"y": 100,
"wires": [
[
"4855c1d9cb3c5de3"
]
]
},
{
"id": "41175f479b349001",
"type": "link out",
"z": "48fad0c0f57cea35",
"name": "Unit 2: read registers",
"mode": "link",
"links": [
"d11918fa96d8a971"
],
"x": 1235,
"y": 340,
"wires": []
},
{
"id": "00a3c1cedc235bc8",
"type": "switch",
"z": "48fad0c0f57cea35",
"name": "link out",
"property": "payload.unitid",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "1",
"vt": "num"
},
{
"t": "eq",
"v": "2",
"vt": "num"
}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 1120,
"y": 320,
"wires": [
[
"336bf1d73e3c16f0"
],
[
"41175f479b349001"
]
]
},
{
"id": "60796979c4948a75",
"type": "ui_spacer",
"z": "48fad0c0f57cea35",
"name": "spacer",
"group": "fc04be05304743a6",
"order": 9,
"width": 2,
"height": 1
},
{
"id": "136b2d0e7ac84aae",
"type": "ui_spacer",
"z": "48fad0c0f57cea35",
"name": "spacer",
"group": "fc04be05304743a6",
"order": 13,
"width": 2,
"height": 1
},
{
"id": "fc04be05304743a6",
"type": "ui_group",
"name": "Gateway",
"tab": "cb1cfb3cbe2a4244",
"order": 1,
"disp": true,
"width": 5,
"collapse": false,
"className": ""
},
{
"id": "54922a4357ba259d",
"type": "modbus-client",
"name": "picow_mb_gateway",
"clienttype": "tcp",
"bufferCommands": true,
"stateLogEnabled": false,
"queueLogEnabled": false,
"failureLogEnabled": true,
"tcpHost": "picow_mb_gateway.local",
"tcpPort": 502,
"tcpType": "DEFAULT",
"serialPort": "/dev/ttyUSB",
"serialType": "RTU-BUFFERD",
"serialBaudrate": 9600,
"serialDatabits": 8,
"serialStopbits": 1,
"serialParity": "none",
"serialConnectionDelay": 100,
"serialAsciiResponseStartDelimiter": "0x3A",
"unit_id": 1,
"commandDelay": 1,
"clientTimeout": 2000,
"reconnectOnTimeout": false,
"reconnectTimeout": 2000,
"parallelUnitIdsAllowed": true,
"showErrors": false,
"showWarnings": true,
"showLogs": true
},
{
"id": "cb1cfb3cbe2a4244",
"type": "ui_tab",
"name": "MODBUS",
"icon": "dashboard",
"disabled": false,
"hidden": false
}
]