@@ -3970,6 +3970,240 @@ void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) {
39703970}
39713971#endif
39723972
3973+ #ifdef MG_ENABLE_LINES
3974+ #line 1 "src/modbus.c"
3975+ #endif
3976+
3977+
3978+
3979+
3980+
3981+ #define MG_MODBUS_MIN_PAYLOAD_LEN 12
3982+ #define MG_MODBUS_HEADER_LEN 7
3983+ #define MG_MODBUS_MAX_DATA_SIZE 250
3984+
3985+ static void mg_modbus_send_mbap(struct mg_connection *c, uint16_t txid,
3986+ uint16_t pdu_len, uint8_t unit_id) {
3987+ uint8_t hdr[7];
3988+ memset(hdr, 0, sizeof(hdr));
3989+ MG_STORE_BE16(&hdr[0], txid & 0xFFFFU);
3990+ MG_STORE_BE16(&hdr[4], (pdu_len + 1) & 0xFFFFU);
3991+ hdr[6] = unit_id;
3992+ mg_send(c, hdr, sizeof(hdr));
3993+ }
3994+
3995+ static void mg_modbus_send_response(struct mg_connection *c,
3996+ struct mg_modbus_req *req,
3997+ uint16_t txid, uint8_t unit_id) {
3998+ uint8_t buf[5];
3999+ uint16_t reg;
4000+ uint16_t i, nbytes;
4001+
4002+ memset(buf, 0, sizeof(buf));
4003+ if (req == NULL) return;
4004+ if (req->error != MG_MODBUS_ERR_NONE) {
4005+ mg_modbus_send_mbap(c, txid, 2, unit_id);
4006+ buf[0] = (uint8_t) (req->func | 0x80);
4007+ buf[1] = req->error;
4008+ mg_send(c, buf, 2);
4009+ return;
4010+ }
4011+ switch (req->func) {
4012+ case MG_MODBUS_FUNC_READ_COILS:
4013+ case MG_MODBUS_FUNC_READ_DISCRETE_INPUTS:
4014+ if (req->u.bits == NULL) return;
4015+ nbytes = (uint16_t) ((req->len + 7) / 8);
4016+ mg_modbus_send_mbap(c, txid, (uint16_t) (nbytes + 2), unit_id);
4017+ buf[0] = req->func;
4018+ buf[1] = (uint8_t) nbytes;
4019+ mg_send(c, buf, 2);
4020+ memset(buf, 0, sizeof(buf));
4021+ for (i = 0; i < req->len; i++) {
4022+ if (req->u.bits[i]) buf[i / 8] |= (uint8_t) (1 << (i % 8));
4023+ if ((i % 8) == 7 || i + 1 == req->len) {
4024+ uint8_t out = buf[i / 8];
4025+ mg_send(c, &out, 1);
4026+ }
4027+ }
4028+ break;
4029+ case MG_MODBUS_FUNC_READ_HOLDING_REGISTERS:
4030+ case MG_MODBUS_FUNC_READ_INPUT_REGISTERS:
4031+ if (req->u.regs == NULL) return;
4032+ nbytes = (uint16_t) (req->len * 2);
4033+ mg_modbus_send_mbap(c, txid, (uint16_t) (nbytes + 2), unit_id);
4034+ buf[0] = req->func;
4035+ buf[1] = (uint8_t) nbytes;
4036+ mg_send(c, buf, 2);
4037+ for (i = 0; i < req->len; i++) {
4038+ MG_STORE_BE16(®, req->u.regs[i] & 0xFFFFU);
4039+ mg_send(c, ®, 2);
4040+ }
4041+ break;
4042+ case MG_MODBUS_FUNC_WRITE_SINGLE_COIL:
4043+ if (req->len != 1 || req->u.bits == NULL) return;
4044+ mg_modbus_send_mbap(c, txid, 5, unit_id);
4045+ buf[0] = req->func;
4046+ MG_STORE_BE16(&buf[1], req->addr & 0xFFFFu);
4047+ MG_STORE_BE16(&buf[3], req->u.bits[0] ? 0xFF00U : 0x0000U);
4048+ mg_send(c, buf, 5);
4049+ break;
4050+ case MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER:
4051+ if (req->len != 1 || req->u.regs == NULL) return;
4052+ mg_modbus_send_mbap(c, txid, 5, unit_id);
4053+ buf[0] = req->func;
4054+ MG_STORE_BE16(&buf[1], req->addr & 0xFFFFU);
4055+ MG_STORE_BE16(&buf[3], req->u.regs[0] & 0xFFFFU);
4056+ mg_send(c, buf, 5);
4057+ break;
4058+ case MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS:
4059+ case MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS:
4060+ mg_modbus_send_mbap(c, txid, 5, unit_id);
4061+ buf[0] = req->func;
4062+ MG_STORE_BE16(&buf[1], req->addr & 0xFFFFU);
4063+ MG_STORE_BE16(&buf[3], req->len & 0xFFFFU);
4064+ mg_send(c, buf, 5);
4065+ break;
4066+ default:
4067+ break;
4068+ }
4069+ }
4070+
4071+ static void handle_pdu(struct mg_connection *c, uint8_t *buf, size_t len) {
4072+ uint16_t max_len, val, txid, addr, quantity;
4073+ uint8_t func, unit_id, byte_count, packed;
4074+ int i;
4075+ size_t pdu_len = len - MG_MODBUS_HEADER_LEN, len_check;
4076+ struct mg_modbus_req mr;
4077+
4078+ if (len < MG_MODBUS_MIN_PAYLOAD_LEN) return;
4079+ memset(&mr, 0, sizeof(mr));
4080+ txid = MG_LOAD_BE16(&buf[0]);
4081+ unit_id = buf[6];
4082+ func = buf[7];
4083+ addr = MG_LOAD_BE16(&buf[8]);
4084+ mr.func = func;
4085+ mr.addr = addr;
4086+ mr.error = MG_MODBUS_ERR_NONE;
4087+ mr.len = 0;
4088+ mr.u.bits = NULL;
4089+ switch (func) {
4090+ case MG_MODBUS_FUNC_READ_COILS:
4091+ case MG_MODBUS_FUNC_READ_DISCRETE_INPUTS:
4092+ if (pdu_len != 5) goto modbus_illegal_value;
4093+ quantity = MG_LOAD_BE16(&buf[10]);
4094+ max_len = 0x07D0; // 2000 bits
4095+ if (quantity == 0 || quantity > max_len) goto modbus_illegal_value;
4096+ mr.len = quantity;
4097+ mr.u.bits = (bool *) mg_calloc((size_t) mr.len, sizeof(bool));
4098+ if (mr.u.bits == NULL) goto modbus_oom;
4099+ break;
4100+ case MG_MODBUS_FUNC_READ_HOLDING_REGISTERS:
4101+ case MG_MODBUS_FUNC_READ_INPUT_REGISTERS:
4102+ if (pdu_len != 5) goto modbus_illegal_value;
4103+ quantity = MG_LOAD_BE16(&buf[10]);
4104+ max_len = 0x007D; // 125 registers
4105+ if (quantity == 0 || quantity > max_len) goto modbus_illegal_value;
4106+ mr.len = quantity;
4107+ mr.u.regs = (uint16_t *) mg_calloc((size_t) mr.len, sizeof(uint16_t));
4108+ if (mr.u.regs == NULL) goto modbus_oom;
4109+ break;
4110+ case MG_MODBUS_FUNC_WRITE_SINGLE_COIL:
4111+ if (pdu_len != 5) goto modbus_illegal_value;
4112+ mr.len = 1;
4113+ mr.u.bits = (bool *) mg_calloc(1, sizeof(bool));
4114+ if (mr.u.bits == NULL) goto modbus_oom;
4115+ val = MG_LOAD_BE16(&buf[10]);
4116+ if (val != 0x0000 && val != 0xFF00) goto modbus_illegal_value;
4117+ mr.u.bits[0] = (val == 0xFF00);
4118+ break;
4119+ case MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER:
4120+ if (pdu_len != 5) goto modbus_illegal_value;
4121+ mr.len = 1;
4122+ mr.u.regs = (uint16_t *) mg_calloc(1, sizeof(uint16_t));
4123+ if (mr.u.regs == NULL) goto modbus_oom;
4124+ mr.u.regs[0] = MG_LOAD_BE16(&buf[10]);
4125+ break;
4126+ case MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS: {
4127+ quantity = MG_LOAD_BE16(&buf[10]);
4128+ if (quantity == 0 || quantity > 0x07B0) goto modbus_illegal_value;
4129+ byte_count = buf[12];
4130+ if (byte_count != (uint8_t) ((quantity + 7) / 8)) goto modbus_illegal_value;
4131+ len_check = (size_t) (6 + byte_count);
4132+ if (len_check != pdu_len) goto modbus_illegal_value;
4133+ mr.len = quantity;
4134+ mr.u.bits = (bool *) mg_calloc((size_t) mr.len, sizeof(bool));
4135+ if (mr.u.bits == NULL) goto modbus_oom;
4136+ for (i = 0; i < mr.len; i++) {
4137+ packed = buf[13 + (i / 8)];
4138+ mr.u.bits[i] = ((packed >> (i % 8)) & 1) != 0;
4139+ }
4140+ break;
4141+ }
4142+ case MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS: {
4143+ quantity = MG_LOAD_BE16(&buf[10]);
4144+ if (quantity == 0 || quantity > 0x007B) goto modbus_illegal_value;
4145+ byte_count = buf[12];
4146+ if (byte_count != (uint8_t) (quantity * 2)) goto modbus_illegal_value;
4147+ len_check = (size_t) (6 + quantity * 2);
4148+ if (len_check != pdu_len) goto modbus_illegal_value;
4149+ mr.len = quantity;
4150+ mr.u.regs = (uint16_t *) mg_calloc((size_t) mr.len, sizeof(uint16_t));
4151+ if (mr.u.regs == NULL) goto modbus_oom;
4152+ for (i = 0; i < mr.len; i++) {
4153+ mr.u.regs[i] = MG_LOAD_BE16(&buf[13 + i * 2]);
4154+ }
4155+ break;
4156+ }
4157+ default:
4158+ MG_ERROR(("Unsupported modbus function"));
4159+ mr.error = MG_MODBUS_ERR_ILLEGAL_FUNCTION;
4160+ mg_modbus_send_response(c, &mr, txid, unit_id);
4161+ return;
4162+ }
4163+ mg_call(c, MG_EV_MODBUS_REQ, &mr);
4164+ mg_modbus_send_response(c, &mr, txid, unit_id);
4165+ if (mr.u.bits != NULL) mg_free(mr.u.bits);
4166+ return;
4167+
4168+ modbus_illegal_value:
4169+ MG_ERROR(("Invalid data"));
4170+ mr.error = MG_MODBUS_ERR_ILLEGAL_VALUE;
4171+ goto modbus_exit_err;
4172+ modbus_oom:
4173+ MG_ERROR(("OOM"));
4174+ mr.error = MG_MODBUS_ERR_DEVICE_FAILURE;
4175+
4176+ modbus_exit_err:
4177+ mg_modbus_send_response(c, &mr, txid, unit_id);
4178+ if (mr.u.bits != NULL) mg_free(mr.u.bits);
4179+ }
4180+
4181+ static void modbus_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
4182+ if (ev == MG_EV_READ) {
4183+ while (c->recv.len >= MG_MODBUS_MIN_PAYLOAD_LEN) {
4184+ uint16_t len = MG_LOAD_BE16(&c->recv.buf[4]); // PDU length
4185+ uint16_t proto_id = MG_LOAD_BE16(&c->recv.buf[2]);
4186+ if (len < 6 || proto_id != 0) {
4187+ mg_error(c, "invalid pdu");
4188+ break;
4189+ } else if (c->recv.len < len + 6U) {
4190+ break; // Partial frame, buffer more
4191+ } else {
4192+ handle_pdu(c, c->recv.buf, len + 6); // Parse PDU and call user
4193+ mg_iobuf_del(&c->recv, 0, len + 6); // Delete received PDU
4194+ }
4195+ }
4196+ }
4197+ (void) ev_data;
4198+ }
4199+
4200+ struct mg_connection *mg_modbus_listen(struct mg_mgr *mgr, const char *url,
4201+ mg_event_handler_t fn, void *fn_data) {
4202+ struct mg_connection *c = mg_listen(mgr, url, fn, fn_data);
4203+ if (c != NULL) c->pfn = modbus_ev_handler;
4204+ return c;
4205+ }
4206+
39734207#ifdef MG_ENABLE_LINES
39744208#line 1 "src/mqtt.c"
39754209#endif
0 commit comments