mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-14 10:20:01 +00:00
Add modbus-tcp support
This commit is contained in:
1 parent
1bb85799ce
commit
d67b30aba3
7 files changed
+885
-4
No files matched your search
+233
@@ -3970,6 +3970,239 @@ void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/modbus.c"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define MG_MODBUS_MIN_PAYLOAD_LEN 12
|
||||
#define MG_MODBUS_HEADER_LEN 7
|
||||
|
||||
static void mg_modbus_send_mbap(struct mg_connection *c, uint16_t txid,
|
||||
uint16_t pdu_len, uint8_t unit_id) {
|
||||
uint8_t hdr[7];
|
||||
memset(hdr, 0, sizeof(hdr));
|
||||
MG_STORE_BE16(&hdr[0], txid & 0xFFFFU);
|
||||
MG_STORE_BE16(&hdr[4], (pdu_len + 1) & 0xFFFFU);
|
||||
hdr[6] = unit_id;
|
||||
mg_send(c, hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
static void mg_modbus_send_response(struct mg_connection *c,
|
||||
struct mg_modbus_req *req,
|
||||
uint16_t txid, uint8_t unit_id) {
|
||||
uint8_t buf[5];
|
||||
uint8_t out;
|
||||
uint16_t reg;
|
||||
uint16_t i, nbytes;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (req == NULL) return;
|
||||
if (req->error != MG_MODBUS_ERR_NONE) {
|
||||
mg_modbus_send_mbap(c, txid, 2, unit_id);
|
||||
buf[0] = (uint8_t) (req->func | 0x80);
|
||||
buf[1] = req->error;
|
||||
mg_send(c, buf, 2);
|
||||
return;
|
||||
}
|
||||
switch (req->func) {
|
||||
case MG_MODBUS_FUNC_READ_COILS:
|
||||
case MG_MODBUS_FUNC_READ_DISCRETE_INPUTS:
|
||||
if (req->u.bits == NULL) return;
|
||||
nbytes = (uint16_t) ((req->len + 7) / 8);
|
||||
mg_modbus_send_mbap(c, txid, (uint16_t) (nbytes + 2), unit_id);
|
||||
buf[0] = req->func;
|
||||
buf[1] = (uint8_t) nbytes;
|
||||
mg_send(c, buf, 2);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
for (i = 0; i < req->len; i++) {
|
||||
if (i % 8 == 0) out = 0;
|
||||
if (req->u.bits[i]) out |= (uint8_t) (1 << (i % 8));
|
||||
if ((i % 8) == 7 || i + 1 == req->len) {
|
||||
mg_send(c, &out, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MG_MODBUS_FUNC_READ_HOLDING_REGISTERS:
|
||||
case MG_MODBUS_FUNC_READ_INPUT_REGISTERS:
|
||||
if (req->u.regs == NULL) return;
|
||||
nbytes = (uint16_t) (req->len * 2);
|
||||
mg_modbus_send_mbap(c, txid, (uint16_t) (nbytes + 2), unit_id);
|
||||
buf[0] = req->func;
|
||||
buf[1] = (uint8_t) nbytes;
|
||||
mg_send(c, buf, 2);
|
||||
for (i = 0; i < req->len; i++) {
|
||||
MG_STORE_BE16(®, req->u.regs[i] & 0xFFFFU);
|
||||
mg_send(c, ®, 2);
|
||||
}
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_SINGLE_COIL:
|
||||
if (req->len != 1 || req->u.bits == NULL) return;
|
||||
mg_modbus_send_mbap(c, txid, 5, unit_id);
|
||||
buf[0] = req->func;
|
||||
MG_STORE_BE16(&buf[1], req->addr & 0xFFFFu);
|
||||
MG_STORE_BE16(&buf[3], req->u.bits[0] ? 0xFF00U : 0x0000U);
|
||||
mg_send(c, buf, 5);
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER:
|
||||
if (req->len != 1 || req->u.regs == NULL) return;
|
||||
mg_modbus_send_mbap(c, txid, 5, unit_id);
|
||||
buf[0] = req->func;
|
||||
MG_STORE_BE16(&buf[1], req->addr & 0xFFFFU);
|
||||
MG_STORE_BE16(&buf[3], req->u.regs[0] & 0xFFFFU);
|
||||
mg_send(c, buf, 5);
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS:
|
||||
case MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS:
|
||||
mg_modbus_send_mbap(c, txid, 5, unit_id);
|
||||
buf[0] = req->func;
|
||||
MG_STORE_BE16(&buf[1], req->addr & 0xFFFFU);
|
||||
MG_STORE_BE16(&buf[3], req->len & 0xFFFFU);
|
||||
mg_send(c, buf, 5);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pdu(struct mg_connection *c, uint8_t *buf, size_t len) {
|
||||
uint16_t max_len, val, txid, addr, quantity;
|
||||
uint8_t func, unit_id, byte_count, packed;
|
||||
int i;
|
||||
size_t pdu_len = len - MG_MODBUS_HEADER_LEN, len_check;
|
||||
struct mg_modbus_req mr;
|
||||
|
||||
if (len < MG_MODBUS_MIN_PAYLOAD_LEN) return;
|
||||
memset(&mr, 0, sizeof(mr));
|
||||
txid = MG_LOAD_BE16(&buf[0]);
|
||||
unit_id = buf[6];
|
||||
func = buf[7];
|
||||
addr = MG_LOAD_BE16(&buf[8]);
|
||||
mr.func = func;
|
||||
mr.addr = addr;
|
||||
mr.error = MG_MODBUS_ERR_NONE;
|
||||
mr.len = 0;
|
||||
mr.u.bits = NULL;
|
||||
switch (func) {
|
||||
case MG_MODBUS_FUNC_READ_COILS:
|
||||
case MG_MODBUS_FUNC_READ_DISCRETE_INPUTS:
|
||||
if (pdu_len != 5) goto modbus_illegal_value;
|
||||
quantity = MG_LOAD_BE16(&buf[10]);
|
||||
max_len = 0x07D0; // 2000 bits
|
||||
if (quantity == 0 || quantity > max_len) goto modbus_illegal_value;
|
||||
mr.len = quantity;
|
||||
mr.u.bits = (bool *) mg_calloc((size_t) mr.len, sizeof(bool));
|
||||
if (mr.u.bits == NULL) goto modbus_oom;
|
||||
break;
|
||||
case MG_MODBUS_FUNC_READ_HOLDING_REGISTERS:
|
||||
case MG_MODBUS_FUNC_READ_INPUT_REGISTERS:
|
||||
if (pdu_len != 5) goto modbus_illegal_value;
|
||||
quantity = MG_LOAD_BE16(&buf[10]);
|
||||
max_len = 0x007D; // 125 registers
|
||||
if (quantity == 0 || quantity > max_len) goto modbus_illegal_value;
|
||||
mr.len = quantity;
|
||||
mr.u.regs = (uint16_t *) mg_calloc((size_t) mr.len, sizeof(uint16_t));
|
||||
if (mr.u.regs == NULL) goto modbus_oom;
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_SINGLE_COIL:
|
||||
if (pdu_len != 5) goto modbus_illegal_value;
|
||||
mr.len = 1;
|
||||
mr.u.bits = (bool *) mg_calloc(1, sizeof(bool));
|
||||
if (mr.u.bits == NULL) goto modbus_oom;
|
||||
val = MG_LOAD_BE16(&buf[10]);
|
||||
if (val != 0x0000 && val != 0xFF00) goto modbus_illegal_value;
|
||||
mr.u.bits[0] = (val == 0xFF00);
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER:
|
||||
if (pdu_len != 5) goto modbus_illegal_value;
|
||||
mr.len = 1;
|
||||
mr.u.regs = (uint16_t *) mg_calloc(1, sizeof(uint16_t));
|
||||
if (mr.u.regs == NULL) goto modbus_oom;
|
||||
mr.u.regs[0] = MG_LOAD_BE16(&buf[10]);
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS: {
|
||||
quantity = MG_LOAD_BE16(&buf[10]);
|
||||
if (quantity == 0 || quantity > 0x07B0) goto modbus_illegal_value;
|
||||
if (pdu_len < 6) goto modbus_illegal_value;
|
||||
byte_count = buf[12];
|
||||
if (byte_count != (uint8_t) ((quantity + 7) / 8)) goto modbus_illegal_value;
|
||||
len_check = (size_t) (6 + byte_count);
|
||||
if (len_check != pdu_len) goto modbus_illegal_value;
|
||||
mr.len = quantity;
|
||||
mr.u.bits = (bool *) mg_calloc((size_t) mr.len, sizeof(bool));
|
||||
if (mr.u.bits == NULL) goto modbus_oom;
|
||||
for (i = 0; i < mr.len; i++) {
|
||||
packed = buf[13 + (i / 8)];
|
||||
mr.u.bits[i] = ((packed >> (i % 8)) & 1) != 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS: {
|
||||
quantity = MG_LOAD_BE16(&buf[10]);
|
||||
if (quantity == 0 || quantity > 0x007B) goto modbus_illegal_value;
|
||||
if (pdu_len < 6) goto modbus_illegal_value;
|
||||
byte_count = buf[12];
|
||||
if (byte_count != (uint8_t) (quantity * 2)) goto modbus_illegal_value;
|
||||
len_check = (size_t) (6 + quantity * 2);
|
||||
if (len_check != pdu_len) goto modbus_illegal_value;
|
||||
mr.len = quantity;
|
||||
mr.u.regs = (uint16_t *) mg_calloc((size_t) mr.len, sizeof(uint16_t));
|
||||
if (mr.u.regs == NULL) goto modbus_oom;
|
||||
for (i = 0; i < mr.len; i++) {
|
||||
mr.u.regs[i] = MG_LOAD_BE16(&buf[13 + i * 2]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MG_ERROR(("Unsupported modbus function"));
|
||||
mr.error = MG_MODBUS_ERR_ILLEGAL_FUNCTION;
|
||||
mg_modbus_send_response(c, &mr, txid, unit_id);
|
||||
return;
|
||||
}
|
||||
mg_call(c, MG_EV_MODBUS_REQ, &mr);
|
||||
goto modbus_exit;
|
||||
|
||||
modbus_illegal_value:
|
||||
MG_ERROR(("Invalid data"));
|
||||
mr.error = MG_MODBUS_ERR_ILLEGAL_VALUE;
|
||||
goto modbus_exit;
|
||||
modbus_oom:
|
||||
MG_ERROR(("OOM"));
|
||||
mr.error = MG_MODBUS_ERR_DEVICE_FAILURE;
|
||||
modbus_exit:
|
||||
mg_modbus_send_response(c, &mr, txid, unit_id);
|
||||
if (mr.u.bits != NULL) mg_free(mr.u.bits);
|
||||
}
|
||||
|
||||
static void modbus_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_READ) {
|
||||
while (c->recv.len >= MG_MODBUS_MIN_PAYLOAD_LEN) {
|
||||
uint16_t len = MG_LOAD_BE16(&c->recv.buf[4]); // PDU length
|
||||
uint16_t proto_id = MG_LOAD_BE16(&c->recv.buf[2]);
|
||||
if (len < 6 || proto_id != 0) {
|
||||
mg_error(c, "invalid pdu");
|
||||
break;
|
||||
} else if (c->recv.len < len + 6U) {
|
||||
break; // Partial frame, buffer more
|
||||
} else {
|
||||
handle_pdu(c, c->recv.buf, len + 6); // Parse PDU and call user
|
||||
mg_iobuf_del(&c->recv, 0, len + 6); // Delete received PDU
|
||||
}
|
||||
}
|
||||
}
|
||||
(void) ev_data;
|
||||
}
|
||||
|
||||
struct mg_connection *mg_modbus_listen(struct mg_mgr *mgr, const char *url,
|
||||
mg_event_handler_t fn, void *fn_data) {
|
||||
struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
|
||||
if (c != NULL) c->pfn = modbus_ev_handler;
|
||||
return c;
|
||||
}
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/mqtt.c"
|
||||
#endif
|
||||
|
||||
+35
@@ -1660,6 +1660,7 @@ enum {
|
||||
MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data
|
||||
MG_EV_MDNS_REQ, // mDNS request struct mg_mdns_req *
|
||||
MG_EV_MDNS_RESP, // mDNS response struct mg_mdns_resp *
|
||||
MG_EV_MODBUS_REQ, // ModBus request struct mg_modbus_cmd *
|
||||
MG_EV_USER // Starting ID for user events
|
||||
};
|
||||
|
||||
@@ -3024,6 +3025,40 @@ bool mg_mdns_query(struct mg_connection *, const char *, unsigned int);
|
||||
|
||||
|
||||
|
||||
// Functions
|
||||
#define MG_MODBUS_FUNC_READ_COILS 1
|
||||
#define MG_MODBUS_FUNC_READ_DISCRETE_INPUTS 2
|
||||
#define MG_MODBUS_FUNC_READ_HOLDING_REGISTERS 3
|
||||
#define MG_MODBUS_FUNC_READ_INPUT_REGISTERS 4
|
||||
#define MG_MODBUS_FUNC_WRITE_SINGLE_COIL 5
|
||||
#define MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER 6
|
||||
#define MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS 15
|
||||
#define MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS 16
|
||||
|
||||
// Error codes
|
||||
#define MG_MODBUS_ERR_NONE 0
|
||||
#define MG_MODBUS_ERR_ILLEGAL_FUNCTION 1
|
||||
#define MG_MODBUS_ERR_ILLEGAL_ADDRESS 2
|
||||
#define MG_MODBUS_ERR_ILLEGAL_VALUE 3
|
||||
#define MG_MODBUS_ERR_DEVICE_FAILURE 4
|
||||
|
||||
struct mg_modbus_req {
|
||||
uint8_t func; // Function code, one of MG_MODBUS_FUNC_*
|
||||
uint8_t error; // Error code user handler can set
|
||||
uint16_t addr; // Address
|
||||
union {
|
||||
bool *bits;
|
||||
uint16_t *regs;
|
||||
} u;
|
||||
uint16_t len; // Number of registers or bits
|
||||
};
|
||||
|
||||
struct mg_connection *mg_modbus_listen(struct mg_mgr *mgr, const char *url,
|
||||
mg_event_handler_t fn, void *fn_data);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef MG_JSON_MAX_DEPTH
|
||||
#define MG_JSON_MAX_DEPTH 30
|
||||
|
||||
@@ -29,5 +29,6 @@ enum {
|
||||
MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data
|
||||
MG_EV_MDNS_REQ, // mDNS request struct mg_mdns_req *
|
||||
MG_EV_MDNS_RESP, // mDNS response struct mg_mdns_resp *
|
||||
MG_EV_MODBUS_REQ, // ModBus request struct mg_modbus_cmd *
|
||||
MG_EV_USER // Starting ID for user events
|
||||
};
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
#include "modbus.h"
|
||||
#include "event.h"
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
|
||||
#define MG_MODBUS_MIN_PAYLOAD_LEN 12
|
||||
#define MG_MODBUS_HEADER_LEN 7
|
||||
|
||||
static void mg_modbus_send_mbap(struct mg_connection *c, uint16_t txid,
|
||||
uint16_t pdu_len, uint8_t unit_id) {
|
||||
uint8_t hdr[7];
|
||||
memset(hdr, 0, sizeof(hdr));
|
||||
MG_STORE_BE16(&hdr[0], txid & 0xFFFFU);
|
||||
MG_STORE_BE16(&hdr[4], (pdu_len + 1) & 0xFFFFU);
|
||||
hdr[6] = unit_id;
|
||||
mg_send(c, hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
static void mg_modbus_send_response(struct mg_connection *c,
|
||||
struct mg_modbus_req *req,
|
||||
uint16_t txid, uint8_t unit_id) {
|
||||
uint8_t buf[5];
|
||||
uint8_t out;
|
||||
uint16_t reg;
|
||||
uint16_t i, nbytes;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (req == NULL) return;
|
||||
if (req->error != MG_MODBUS_ERR_NONE) {
|
||||
mg_modbus_send_mbap(c, txid, 2, unit_id);
|
||||
buf[0] = (uint8_t) (req->func | 0x80);
|
||||
buf[1] = req->error;
|
||||
mg_send(c, buf, 2);
|
||||
return;
|
||||
}
|
||||
switch (req->func) {
|
||||
case MG_MODBUS_FUNC_READ_COILS:
|
||||
case MG_MODBUS_FUNC_READ_DISCRETE_INPUTS:
|
||||
if (req->u.bits == NULL) return;
|
||||
nbytes = (uint16_t) ((req->len + 7) / 8);
|
||||
mg_modbus_send_mbap(c, txid, (uint16_t) (nbytes + 2), unit_id);
|
||||
buf[0] = req->func;
|
||||
buf[1] = (uint8_t) nbytes;
|
||||
mg_send(c, buf, 2);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
for (i = 0; i < req->len; i++) {
|
||||
if (i % 8 == 0) out = 0;
|
||||
if (req->u.bits[i]) out |= (uint8_t) (1 << (i % 8));
|
||||
if ((i % 8) == 7 || i + 1 == req->len) {
|
||||
mg_send(c, &out, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MG_MODBUS_FUNC_READ_HOLDING_REGISTERS:
|
||||
case MG_MODBUS_FUNC_READ_INPUT_REGISTERS:
|
||||
if (req->u.regs == NULL) return;
|
||||
nbytes = (uint16_t) (req->len * 2);
|
||||
mg_modbus_send_mbap(c, txid, (uint16_t) (nbytes + 2), unit_id);
|
||||
buf[0] = req->func;
|
||||
buf[1] = (uint8_t) nbytes;
|
||||
mg_send(c, buf, 2);
|
||||
for (i = 0; i < req->len; i++) {
|
||||
MG_STORE_BE16(®, req->u.regs[i] & 0xFFFFU);
|
||||
mg_send(c, ®, 2);
|
||||
}
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_SINGLE_COIL:
|
||||
if (req->len != 1 || req->u.bits == NULL) return;
|
||||
mg_modbus_send_mbap(c, txid, 5, unit_id);
|
||||
buf[0] = req->func;
|
||||
MG_STORE_BE16(&buf[1], req->addr & 0xFFFFu);
|
||||
MG_STORE_BE16(&buf[3], req->u.bits[0] ? 0xFF00U : 0x0000U);
|
||||
mg_send(c, buf, 5);
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER:
|
||||
if (req->len != 1 || req->u.regs == NULL) return;
|
||||
mg_modbus_send_mbap(c, txid, 5, unit_id);
|
||||
buf[0] = req->func;
|
||||
MG_STORE_BE16(&buf[1], req->addr & 0xFFFFU);
|
||||
MG_STORE_BE16(&buf[3], req->u.regs[0] & 0xFFFFU);
|
||||
mg_send(c, buf, 5);
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS:
|
||||
case MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS:
|
||||
mg_modbus_send_mbap(c, txid, 5, unit_id);
|
||||
buf[0] = req->func;
|
||||
MG_STORE_BE16(&buf[1], req->addr & 0xFFFFU);
|
||||
MG_STORE_BE16(&buf[3], req->len & 0xFFFFU);
|
||||
mg_send(c, buf, 5);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pdu(struct mg_connection *c, uint8_t *buf, size_t len) {
|
||||
uint16_t max_len, val, txid, addr, quantity;
|
||||
uint8_t func, unit_id, byte_count, packed;
|
||||
int i;
|
||||
size_t pdu_len = len - MG_MODBUS_HEADER_LEN, len_check;
|
||||
struct mg_modbus_req mr;
|
||||
|
||||
if (len < MG_MODBUS_MIN_PAYLOAD_LEN) return;
|
||||
memset(&mr, 0, sizeof(mr));
|
||||
txid = MG_LOAD_BE16(&buf[0]);
|
||||
unit_id = buf[6];
|
||||
func = buf[7];
|
||||
addr = MG_LOAD_BE16(&buf[8]);
|
||||
mr.func = func;
|
||||
mr.addr = addr;
|
||||
mr.error = MG_MODBUS_ERR_NONE;
|
||||
mr.len = 0;
|
||||
mr.u.bits = NULL;
|
||||
switch (func) {
|
||||
case MG_MODBUS_FUNC_READ_COILS:
|
||||
case MG_MODBUS_FUNC_READ_DISCRETE_INPUTS:
|
||||
if (pdu_len != 5) goto modbus_illegal_value;
|
||||
quantity = MG_LOAD_BE16(&buf[10]);
|
||||
max_len = 0x07D0; // 2000 bits
|
||||
if (quantity == 0 || quantity > max_len) goto modbus_illegal_value;
|
||||
mr.len = quantity;
|
||||
mr.u.bits = (bool *) mg_calloc((size_t) mr.len, sizeof(bool));
|
||||
if (mr.u.bits == NULL) goto modbus_oom;
|
||||
break;
|
||||
case MG_MODBUS_FUNC_READ_HOLDING_REGISTERS:
|
||||
case MG_MODBUS_FUNC_READ_INPUT_REGISTERS:
|
||||
if (pdu_len != 5) goto modbus_illegal_value;
|
||||
quantity = MG_LOAD_BE16(&buf[10]);
|
||||
max_len = 0x007D; // 125 registers
|
||||
if (quantity == 0 || quantity > max_len) goto modbus_illegal_value;
|
||||
mr.len = quantity;
|
||||
mr.u.regs = (uint16_t *) mg_calloc((size_t) mr.len, sizeof(uint16_t));
|
||||
if (mr.u.regs == NULL) goto modbus_oom;
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_SINGLE_COIL:
|
||||
if (pdu_len != 5) goto modbus_illegal_value;
|
||||
mr.len = 1;
|
||||
mr.u.bits = (bool *) mg_calloc(1, sizeof(bool));
|
||||
if (mr.u.bits == NULL) goto modbus_oom;
|
||||
val = MG_LOAD_BE16(&buf[10]);
|
||||
if (val != 0x0000 && val != 0xFF00) goto modbus_illegal_value;
|
||||
mr.u.bits[0] = (val == 0xFF00);
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER:
|
||||
if (pdu_len != 5) goto modbus_illegal_value;
|
||||
mr.len = 1;
|
||||
mr.u.regs = (uint16_t *) mg_calloc(1, sizeof(uint16_t));
|
||||
if (mr.u.regs == NULL) goto modbus_oom;
|
||||
mr.u.regs[0] = MG_LOAD_BE16(&buf[10]);
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS: {
|
||||
quantity = MG_LOAD_BE16(&buf[10]);
|
||||
if (quantity == 0 || quantity > 0x07B0) goto modbus_illegal_value;
|
||||
if (pdu_len < 6) goto modbus_illegal_value;
|
||||
byte_count = buf[12];
|
||||
if (byte_count != (uint8_t) ((quantity + 7) / 8)) goto modbus_illegal_value;
|
||||
len_check = (size_t) (6 + byte_count);
|
||||
if (len_check != pdu_len) goto modbus_illegal_value;
|
||||
mr.len = quantity;
|
||||
mr.u.bits = (bool *) mg_calloc((size_t) mr.len, sizeof(bool));
|
||||
if (mr.u.bits == NULL) goto modbus_oom;
|
||||
for (i = 0; i < mr.len; i++) {
|
||||
packed = buf[13 + (i / 8)];
|
||||
mr.u.bits[i] = ((packed >> (i % 8)) & 1) != 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS: {
|
||||
quantity = MG_LOAD_BE16(&buf[10]);
|
||||
if (quantity == 0 || quantity > 0x007B) goto modbus_illegal_value;
|
||||
if (pdu_len < 6) goto modbus_illegal_value;
|
||||
byte_count = buf[12];
|
||||
if (byte_count != (uint8_t) (quantity * 2)) goto modbus_illegal_value;
|
||||
len_check = (size_t) (6 + quantity * 2);
|
||||
if (len_check != pdu_len) goto modbus_illegal_value;
|
||||
mr.len = quantity;
|
||||
mr.u.regs = (uint16_t *) mg_calloc((size_t) mr.len, sizeof(uint16_t));
|
||||
if (mr.u.regs == NULL) goto modbus_oom;
|
||||
for (i = 0; i < mr.len; i++) {
|
||||
mr.u.regs[i] = MG_LOAD_BE16(&buf[13 + i * 2]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MG_ERROR(("Unsupported modbus function"));
|
||||
mr.error = MG_MODBUS_ERR_ILLEGAL_FUNCTION;
|
||||
mg_modbus_send_response(c, &mr, txid, unit_id);
|
||||
return;
|
||||
}
|
||||
mg_call(c, MG_EV_MODBUS_REQ, &mr);
|
||||
goto modbus_exit;
|
||||
|
||||
modbus_illegal_value:
|
||||
MG_ERROR(("Invalid data"));
|
||||
mr.error = MG_MODBUS_ERR_ILLEGAL_VALUE;
|
||||
goto modbus_exit;
|
||||
modbus_oom:
|
||||
MG_ERROR(("OOM"));
|
||||
mr.error = MG_MODBUS_ERR_DEVICE_FAILURE;
|
||||
modbus_exit:
|
||||
mg_modbus_send_response(c, &mr, txid, unit_id);
|
||||
if (mr.u.bits != NULL) mg_free(mr.u.bits);
|
||||
}
|
||||
|
||||
static void modbus_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_READ) {
|
||||
while (c->recv.len >= MG_MODBUS_MIN_PAYLOAD_LEN) {
|
||||
uint16_t len = MG_LOAD_BE16(&c->recv.buf[4]); // PDU length
|
||||
uint16_t proto_id = MG_LOAD_BE16(&c->recv.buf[2]);
|
||||
if (len < 6 || proto_id != 0) {
|
||||
mg_error(c, "invalid pdu");
|
||||
break;
|
||||
} else if (c->recv.len < len + 6U) {
|
||||
break; // Partial frame, buffer more
|
||||
} else {
|
||||
handle_pdu(c, c->recv.buf, len + 6); // Parse PDU and call user
|
||||
mg_iobuf_del(&c->recv, 0, len + 6); // Delete received PDU
|
||||
}
|
||||
}
|
||||
}
|
||||
(void) ev_data;
|
||||
}
|
||||
|
||||
struct mg_connection *mg_modbus_listen(struct mg_mgr *mgr, const char *url,
|
||||
mg_event_handler_t fn, void *fn_data) {
|
||||
struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
|
||||
if (c != NULL) c->pfn = modbus_ev_handler;
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "net.h"
|
||||
|
||||
// Functions
|
||||
#define MG_MODBUS_FUNC_READ_COILS 1
|
||||
#define MG_MODBUS_FUNC_READ_DISCRETE_INPUTS 2
|
||||
#define MG_MODBUS_FUNC_READ_HOLDING_REGISTERS 3
|
||||
#define MG_MODBUS_FUNC_READ_INPUT_REGISTERS 4
|
||||
#define MG_MODBUS_FUNC_WRITE_SINGLE_COIL 5
|
||||
#define MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER 6
|
||||
#define MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS 15
|
||||
#define MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS 16
|
||||
|
||||
// Error codes
|
||||
#define MG_MODBUS_ERR_NONE 0
|
||||
#define MG_MODBUS_ERR_ILLEGAL_FUNCTION 1
|
||||
#define MG_MODBUS_ERR_ILLEGAL_ADDRESS 2
|
||||
#define MG_MODBUS_ERR_ILLEGAL_VALUE 3
|
||||
#define MG_MODBUS_ERR_DEVICE_FAILURE 4
|
||||
|
||||
struct mg_modbus_req {
|
||||
uint8_t func; // Function code, one of MG_MODBUS_FUNC_*
|
||||
uint8_t error; // Error code user handler can set
|
||||
uint16_t addr; // Address
|
||||
union {
|
||||
bool *bits;
|
||||
uint16_t *regs;
|
||||
} u;
|
||||
uint16_t len; // Number of registers or bits
|
||||
};
|
||||
|
||||
struct mg_connection *mg_modbus_listen(struct mg_mgr *mgr, const char *url,
|
||||
mg_event_handler_t fn, void *fn_data);
|
||||
+1
-2
@@ -251,7 +251,7 @@ mongoose.c: Makefile $(wildcard ../src/*.c) $(wildcard ../src/drivers/*.c)
|
||||
cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
|
||||
|
||||
mongoose.h: $(HDRS) Makefile
|
||||
cd .. && (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/profile.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_x25519.h src/tls_aes128.h src/tls_uecc.h src/tls_chacha20.h src/tls_rsa.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/ota.h src/flash.h src/wifi.h src/l2.h src/net_builtin.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
|
||||
cd .. && (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/profile.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_x25519.h src/tls_aes128.h src/tls_uecc.h src/tls_chacha20.h src/tls_rsa.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/modbus.h src/json.h src/rpc.h src/ota.h src/flash.h src/wifi.h src/l2.h src/net_builtin.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
|
||||
|
||||
|
||||
clean: clean_tutorials clean_tutorials_embedded
|
||||
@@ -285,4 +285,3 @@ tutorials_embedded:
|
||||
@for X in $(TUTORIALS_EMBEDDED); do test -f $$X/Makefile || continue; $(MAKE) -C $$X build || exit 1; done
|
||||
clean_tutorials_embedded:
|
||||
for X in $(TUTORIALS_EMBEDDED); do test -f $$X/Makefile || continue; $(MAKE) -C $$X clean || exit 1; done
|
||||
|
||||
+352
-2
@@ -768,7 +768,8 @@ static void test_mqtt_ver(uint8_t mqtt_version) {
|
||||
memset(mbuf + 1, 0, sizeof(mbuf) - 1);
|
||||
test_data.flags = 0;
|
||||
|
||||
opts.props = 0; opts.num_props = 0;
|
||||
opts.props = 0;
|
||||
opts.num_props = 0;
|
||||
mg_mqtt_unsub(c, &opts);
|
||||
for (i = 0; i < 500 && test_data.flags == 0; i++) mg_mgr_poll(&mgr, 10);
|
||||
ASSERT(test_data.flags == flags_unsubscribed);
|
||||
@@ -1466,7 +1467,8 @@ static void test_tls(void) {
|
||||
"%s",
|
||||
bd.len, bd.buf) == 200);
|
||||
ASSERT(cmpbody(buf, bd.buf) == 0);
|
||||
#if MG_TLS == MG_TLS_BUILTIN && defined(__linux__) && MG_ENABLE_CHACHA20 // skip for non-CHACHA tests
|
||||
#if MG_TLS == MG_TLS_BUILTIN && defined(__linux__) && \
|
||||
MG_ENABLE_CHACHA20 // skip for non-CHACHA tests
|
||||
// fire patched server, test multiple TLS records per TCP segment handling
|
||||
// skip other TLS stacks to avoid "bad client hello", we are 1.3 only
|
||||
if (access("tls_multirec/server", X_OK) == 0) {
|
||||
@@ -4041,11 +4043,359 @@ static void test_crypto(void) {
|
||||
#define DASHBOARD(x) \
|
||||
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true");
|
||||
|
||||
static uint8_t coils_database[25] = {
|
||||
0x4D, 0x9C, 0xAA, 0x55, 0xCC,
|
||||
0xA3, 0x66, 0xAF, 0x60, 0xBC,
|
||||
0xCC, 0x6C, 0x53, 0xFF, 0x00,
|
||||
0x55, 0x3C, 0x0F, 0xF0, 0x8F,
|
||||
0x54, 0x99, 0xF8, 0x0D, 0x2A,
|
||||
};
|
||||
|
||||
static bool modbus_read_coil(uint16_t i) {
|
||||
uint16_t start_byte = i / 8;
|
||||
int bit_pos = i % 8;
|
||||
uint8_t data = ((coils_database[start_byte] >> bit_pos) & 0x01);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void modbus_srv(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_modbus_req *mr = (struct mg_modbus_req *) ev_data;
|
||||
uint16_t i, r;
|
||||
size_t tc = *(size_t *) c->fn_data + 1;
|
||||
static bool coil_1[] = {1};
|
||||
static bool coil_7[] = {1, 0, 1, 0, 1, 0, 1};
|
||||
static uint16_t reg_1[] = {0x0001};
|
||||
static uint16_t reg_7[] = {0x0001, 0x0002, 0x0003, 0x0004,
|
||||
0x0005, 0x0006, 0x0007};
|
||||
|
||||
if (ev == MG_EV_READ) {
|
||||
*(size_t *) c->fn_data += 1;
|
||||
} else if (ev == MG_EV_MODBUS_REQ) {
|
||||
if (mr->addr == 0xFFFF) {
|
||||
mr->error = MG_MODBUS_ERR_ILLEGAL_ADDRESS;
|
||||
return;
|
||||
}
|
||||
switch (mr->func) {
|
||||
case MG_MODBUS_FUNC_READ_COILS:
|
||||
case MG_MODBUS_FUNC_READ_DISCRETE_INPUTS:
|
||||
if (tc != 0x1D) {
|
||||
for (i = 0; i < mr->len; i++) {
|
||||
mr->u.bits[i] = (mr->len == 1 ? coil_1[i] : coil_7[i]);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < mr->len; i++) {
|
||||
mr->u.bits[i] = modbus_read_coil(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MG_MODBUS_FUNC_READ_HOLDING_REGISTERS:
|
||||
case MG_MODBUS_FUNC_READ_INPUT_REGISTERS:
|
||||
if (tc == 0x1c) {
|
||||
mr->error = MG_MODBUS_ERR_DEVICE_FAILURE;
|
||||
break;
|
||||
}
|
||||
if (mr->len == 1) {
|
||||
mr->u.regs[0] = reg_1[0];
|
||||
} else if (mr->len == 7) {
|
||||
for (i = 0; i < 7; i++) mr->u.regs[i] = reg_7[i];
|
||||
} else {
|
||||
mr->error = MG_MODBUS_ERR_ILLEGAL_ADDRESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case MG_MODBUS_FUNC_WRITE_SINGLE_COIL:
|
||||
switch (tc) {
|
||||
case 0x05:
|
||||
ASSERT(mr->len == 1);
|
||||
ASSERT(mr->addr == 0x14);
|
||||
ASSERT(mr->u.bits != NULL);
|
||||
ASSERT(mr->u.bits[0] == true);
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER:
|
||||
switch (tc) {
|
||||
case 0x06:
|
||||
ASSERT(mr->len == 1);
|
||||
ASSERT(mr->addr == 0x15);
|
||||
ASSERT(mr->u.regs != NULL);
|
||||
ASSERT(mr->u.regs[0] == 1);
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS:
|
||||
switch (tc) {
|
||||
case 0x14:
|
||||
ASSERT(mr->addr == 0x23);
|
||||
ASSERT(mr->len == 1);
|
||||
ASSERT(mr->u.bits != NULL);
|
||||
ASSERT(mr->u.bits[0] == true);
|
||||
break;
|
||||
case 0x15:
|
||||
ASSERT(mr->addr == 0x24);
|
||||
ASSERT(mr->len == 7);
|
||||
ASSERT(mr->u.bits != NULL);
|
||||
for (i = 0; i < 7; i++) {
|
||||
ASSERT(mr->u.bits[i] == coil_7[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS:
|
||||
switch (tc) {
|
||||
case 0x17:
|
||||
r = 0;
|
||||
ASSERT(mr->addr == 0x26);
|
||||
ASSERT(mr->len == 1);
|
||||
ASSERT(mr->u.regs != NULL);
|
||||
r = mr->u.regs[0];
|
||||
ASSERT(r == 1);
|
||||
break;
|
||||
case 0x18:
|
||||
ASSERT(mr->addr == 0x27);
|
||||
ASSERT(mr->len == 7);
|
||||
ASSERT(mr->u.regs != NULL);
|
||||
for (i = 0; i < 7; i++) {
|
||||
r = mr->u.regs[i];
|
||||
ASSERT(r == reg_7[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mr->error = MG_MODBUS_ERR_ILLEGAL_FUNCTION;
|
||||
break;
|
||||
}
|
||||
}
|
||||
(void) c, (void) ev_data;
|
||||
}
|
||||
|
||||
static void test_modbus(void) {
|
||||
struct modbus_test {
|
||||
const char *name;
|
||||
uint8_t req[256];
|
||||
size_t req_len;
|
||||
uint8_t resp[256];
|
||||
size_t resp_len;
|
||||
} tests[] = {
|
||||
{"read coils - 1 @ 0x0010",
|
||||
{0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x10, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01},
|
||||
10},
|
||||
{"read discrete inputs - 1 @ 0x0011",
|
||||
{0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02, 0x00, 0x11, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x01, 0x01},
|
||||
10},
|
||||
{"read holding registers - 1 @ 0x0012",
|
||||
{0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x12, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00, 0x01},
|
||||
11},
|
||||
{"read input registers - 1 @ 0x0013",
|
||||
{0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x01, 0x04, 0x00, 0x13, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x01, 0x04, 0x02, 0x00, 0x01},
|
||||
11},
|
||||
{"write single coil @ 0x0014 = 1",
|
||||
{0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x01, 0x05, 0x00, 0x14, 0xFF, 0x00},
|
||||
12,
|
||||
{0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x01, 0x05, 0x00, 0x14, 0xFF, 0x00},
|
||||
12},
|
||||
{"write single register @ 0x0015 = 1",
|
||||
{0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x01, 0x06, 0x00, 0x15, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x01, 0x06, 0x00, 0x15, 0x00, 0x01},
|
||||
12},
|
||||
|
||||
/* --- Multi-value reads: 0, 1, 7 --- */
|
||||
|
||||
{"read coils - 0 @ 0x0016",
|
||||
{0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x16, 0x00, 0x00},
|
||||
12,
|
||||
{0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x01, 0x81, 0x03},
|
||||
9},
|
||||
{"read coils - 1 @ 0x0017",
|
||||
{0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x17, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01},
|
||||
10},
|
||||
{"read coils - 7 @ 0x0018",
|
||||
{0x00, 0x09, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x18, 0x00, 0x07},
|
||||
12,
|
||||
{0x00, 0x09, 0x00, 0x00, 0x00, 0x04, 0x01, 0x01, 0x01, 0x55},
|
||||
10},
|
||||
|
||||
{"read discrete inputs - 0 @ 0x0019",
|
||||
{0x00, 0x0A, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02, 0x00, 0x19, 0x00, 0x00},
|
||||
12,
|
||||
{0x00, 0x0A, 0x00, 0x00, 0x00, 0x03, 0x01, 0x82, 0x03},
|
||||
9},
|
||||
{"read discrete inputs - 1 @ 0x001A",
|
||||
{0x00, 0x0B, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02, 0x00, 0x1A, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x0B, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x01, 0x01},
|
||||
10},
|
||||
{"read discrete inputs - 7 @ 0x001B",
|
||||
{0x00, 0x0C, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02, 0x00, 0x1B, 0x00, 0x07},
|
||||
12,
|
||||
{0x00, 0x0C, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x01, 0x55},
|
||||
10},
|
||||
|
||||
{"read holding registers - 0 @ 0x001C",
|
||||
{0x00, 0x0D, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x1C, 0x00, 0x00},
|
||||
12,
|
||||
{0x00, 0x0D, 0x00, 0x00, 0x00, 0x03, 0x01, 0x83, 0x03},
|
||||
9},
|
||||
{"read holding registers - 1 @ 0x001D",
|
||||
{0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x1D, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x0E, 0x00, 0x00, 0x00, 0x05, 0x01, 0x03, 0x02, 0x00, 0x01},
|
||||
11},
|
||||
{"read holding registers - 7 @ 0x001E",
|
||||
{0x00, 0x0F, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x1E, 0x00, 0x07},
|
||||
12,
|
||||
{0x00, 0x0F, 0x00, 0x00, 0x00, 0x11, 0x01, 0x03, 0x0E, 0x00, 0x01, 0x00,
|
||||
0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07},
|
||||
23},
|
||||
|
||||
{"read input registers - 0 @ 0x001F",
|
||||
{0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x01, 0x04, 0x00, 0x1F, 0x00, 0x00},
|
||||
12,
|
||||
{0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x01, 0x84, 0x03},
|
||||
9},
|
||||
{"read input registers - 1 @ 0x0020",
|
||||
{0x00, 0x11, 0x00, 0x00, 0x00, 0x06, 0x01, 0x04, 0x00, 0x20, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x11, 0x00, 0x00, 0x00, 0x05, 0x01, 0x04, 0x02, 0x00, 0x01},
|
||||
11},
|
||||
{"read input registers - 7 @ 0x0021",
|
||||
{0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0x01, 0x04, 0x00, 0x21, 0x00, 0x07},
|
||||
12,
|
||||
{0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x01, 0x04, 0x0E, 0x00, 0x01, 0x00,
|
||||
0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07},
|
||||
23},
|
||||
{"write multiple coils - 0 @ 0x0022",
|
||||
{0x00, 0x13, 0x00, 0x00, 0x00, 0x07, 0x01, 0x0F, 0x00, 0x22, 0x00, 0x00,
|
||||
0x00},
|
||||
13,
|
||||
{0x00, 0x13, 0x00, 0x00, 0x00, 0x03, 0x01, 0x8F, 0x03},
|
||||
9},
|
||||
{"write multiple coils - 1 @ 0x0023",
|
||||
{0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x01, 0x0F, 0x00, 0x23, 0x00, 0x01,
|
||||
0x01, 0x01},
|
||||
14,
|
||||
{0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x01, 0x0F, 0x00, 0x23, 0x00, 0x01},
|
||||
12},
|
||||
{"write multiple coils - 7 @ 0x0024",
|
||||
{0x00, 0x15, 0x00, 0x00, 0x00, 0x08, 0x01, 0x0F, 0x00, 0x24, 0x00, 0x07,
|
||||
0x01, 0x55},
|
||||
14,
|
||||
{0x00, 0x15, 0x00, 0x00, 0x00, 0x06, 0x01, 0x0F, 0x00, 0x24, 0x00, 0x07},
|
||||
12},
|
||||
|
||||
{"write multiple registers - 0 @ 0x0025",
|
||||
{0x00, 0x16, 0x00, 0x00, 0x00, 0x07, 0x01, 0x10, 0x00, 0x25, 0x00, 0x00,
|
||||
0x00},
|
||||
13,
|
||||
{0x00, 0x16, 0x00, 0x00, 0x00, 0x03, 0x01, 0x90, 0x03},
|
||||
9},
|
||||
{"write multiple registers - 1 @ 0x0026",
|
||||
{0x00, 0x17, 0x00, 0x00, 0x00, 0x09, 0x01, 0x10, 0x00, 0x26, 0x00, 0x01,
|
||||
0x02, 0x00, 0x01},
|
||||
15,
|
||||
{0x00, 0x17, 0x00, 0x00, 0x00, 0x06, 0x01, 0x10, 0x00, 0x26, 0x00, 0x01},
|
||||
12},
|
||||
{"write multiple registers - 7 @ 0x0027",
|
||||
{0x00, 0x18, 0x00, 0x00, 0x00, 0x15, 0x01, 0x10, 0x00,
|
||||
0x27, 0x00, 0x07, 0x0E, 0x00, 0x01, 0x00, 0x02, 0x00,
|
||||
0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07},
|
||||
27,
|
||||
{0x00, 0x18, 0x00, 0x00, 0x00, 0x06, 0x01, 0x10, 0x00, 0x27, 0x00, 0x07},
|
||||
12},
|
||||
|
||||
/* --- Standard Modbus exception cases --- */
|
||||
|
||||
{"illegal function",
|
||||
{0x00, 0x19, 0x00, 0x00, 0x00, 0x06, 0x01, 0x11, 0x00, 0x28, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x19, 0x00, 0x00, 0x00, 0x03, 0x01, 0x91, 0x01},
|
||||
9},
|
||||
{"illegal data address",
|
||||
{0x00, 0x1A, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0xFF, 0xFF, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x1A, 0x00, 0x00, 0x00, 0x03, 0x01, 0x83, 0x02},
|
||||
9},
|
||||
{"illegal data value - bad coil value",
|
||||
{0x00, 0x1B, 0x00, 0x00, 0x00, 0x06, 0x01, 0x05, 0x00, 0x29, 0x12, 0x34},
|
||||
12,
|
||||
{0x00, 0x1B, 0x00, 0x00, 0x00, 0x03, 0x01, 0x85, 0x03},
|
||||
9},
|
||||
{"server device failure",
|
||||
{0x00, 0x1C, 0x00, 0x00, 0x00, 0x06, 0x01, 0x04, 0x00, 0x2A, 0x00, 0x01},
|
||||
12,
|
||||
{0x00, 0x1C, 0x00, 0x00, 0x00, 0x03, 0x01, 0x84, 0x04},
|
||||
9},
|
||||
|
||||
/* --- Stress Test ---*/
|
||||
{"read coils - 200 @ 0x0000",
|
||||
{0x00, 0x1D, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0xC8},
|
||||
12,
|
||||
{0x00, 0x1D, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x01, 0x19,
|
||||
0x4D, 0x9C, 0xAA, 0x55, 0xCC,
|
||||
0xA3, 0x66, 0xAF, 0x60, 0xBC,
|
||||
0xCC, 0x6C, 0x53, 0xFF, 0x00,
|
||||
0x55, 0x3C, 0x0F, 0xF0, 0x8F,
|
||||
0x54, 0x99, 0xF8, 0x0D, 0x2A},
|
||||
34},
|
||||
};
|
||||
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *c;
|
||||
const char *url = "tcp://localhost:32625";
|
||||
size_t i, j, total = 0, num_tests = sizeof(tests) / sizeof(tests[0]);
|
||||
|
||||
mg_mgr_init(&mgr);
|
||||
mg_modbus_listen(&mgr, url, modbus_srv, &total);
|
||||
c = mg_connect(&mgr, url, NULL, NULL);
|
||||
|
||||
for (i = 0; i < num_tests; i++) {
|
||||
MG_INFO(("TEST %s", tests[i].name));
|
||||
mg_send(c, tests[i].req, tests[i].req_len);
|
||||
for (j = 0; j < 20; j++) mg_mgr_poll(&mgr, 1);
|
||||
mg_hexdump(tests[i].req, tests[i].req_len);
|
||||
mg_hexdump(tests[i].resp, tests[i].resp_len);
|
||||
mg_hexdump(c->recv.buf, c->recv.len);
|
||||
ASSERT(c->recv.len == tests[i].resp_len);
|
||||
ASSERT(memcmp(c->recv.buf, tests[i].resp, c->recv.len) == 0);
|
||||
c->recv.len = 0;
|
||||
}
|
||||
ASSERT(total == num_tests);
|
||||
mg_mgr_free(&mgr);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const char *debug_level = getenv("V");
|
||||
if (debug_level == NULL) debug_level = "3";
|
||||
mg_log_set(atoi(debug_level));
|
||||
|
||||
s_error = false;
|
||||
test_modbus();
|
||||
DASHBOARD("modbus");
|
||||
|
||||
s_error = false;
|
||||
test_crypto();
|
||||
DASHBOARD("crypto");
|
||||
|
||||
Reference in new issue
Block a user