123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445 |
- /*
- nanoMODBUS - A compact MODBUS RTU/TCP C library for microcontrollers
- MIT License
- Copyright (c) 2024 Valerio De Benedetto (@debevv)
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- */
- #include "modbus.h"
- #include <stdbool.h>
- #include <stdint.h>
- #include <string.h>
- #define NMBS_UNUSED_PARAM(x) ((x) = (x))
- #ifdef NMBS_DEBUG
- #include <stdio.h>
- #define NMBS_DEBUG_PRINT(...) printf(__VA_ARGS__)
- #else
- #define NMBS_DEBUG_PRINT(...) (void) (0)
- #endif
- static uint8_t get_1(nmbs_t* nmbs) {
- uint8_t result = nmbs->msg.buf[nmbs->msg.buf_idx];
- nmbs->msg.buf_idx++;
- return result;
- }
- static void put_1(nmbs_t* nmbs, uint8_t data) {
- nmbs->msg.buf[nmbs->msg.buf_idx] = data;
- nmbs->msg.buf_idx++;
- }
- static void discard_1(nmbs_t* nmbs) {
- nmbs->msg.buf_idx++;
- }
- #ifndef NMBS_SERVER_DISABLED
- #if !defined(NMBS_SERVER_READ_FILE_RECORD_DISABLED) || !defined(NMBS_SERVER_WRITE_FILE_RECORD_DISABLED)
- static void discard_n(nmbs_t* nmbs, uint16_t n) {
- nmbs->msg.buf_idx += n;
- }
- #endif
- #endif
- static uint16_t get_2(nmbs_t* nmbs) {
- uint16_t result =
- ((uint16_t) nmbs->msg.buf[nmbs->msg.buf_idx]) << 8 | (uint16_t) nmbs->msg.buf[nmbs->msg.buf_idx + 1];
- nmbs->msg.buf_idx += 2;
- return result;
- }
- static void put_2(nmbs_t* nmbs, uint16_t data) {
- nmbs->msg.buf[nmbs->msg.buf_idx] = (uint8_t) ((data >> 8) & 0xFFU);
- nmbs->msg.buf[nmbs->msg.buf_idx + 1] = (uint8_t) data;
- nmbs->msg.buf_idx += 2;
- }
- #define NMBS_SERVER_DISABLED
- #ifndef NMBS_SERVER_DISABLED
- #if !defined(NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED)
- static void set_1(nmbs_t* nmbs, uint8_t data, uint8_t index) {
- nmbs->msg.buf[index] = data;
- }
- static void set_2(nmbs_t* nmbs, uint16_t data, uint8_t index) {
- nmbs->msg.buf[index] = (uint8_t) ((data >> 8) & 0xFFU);
- nmbs->msg.buf[index + 1] = (uint8_t) data;
- }
- #endif
- #endif
- static uint8_t* get_n(nmbs_t* nmbs, uint16_t n) {
- uint8_t* msg_buf_ptr = nmbs->msg.buf + nmbs->msg.buf_idx;
- nmbs->msg.buf_idx += n;
- return msg_buf_ptr;
- }
- #ifndef NMBS_SERVER_DISABLED
- #if !defined(NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED)
- static void put_n(nmbs_t* nmbs, const uint8_t* data, uint8_t size) {
- memcpy(&nmbs->msg.buf[nmbs->msg.buf_idx], data, size);
- nmbs->msg.buf_idx += size;
- }
- #endif
- #if !defined(NMBS_SERVER_WRITE_FILE_RECORD_DISABLED)
- static uint16_t* get_regs(nmbs_t* nmbs, uint16_t n) {
- uint16_t* msg_buf_ptr = (uint16_t*) (nmbs->msg.buf + nmbs->msg.buf_idx);
- nmbs->msg.buf_idx += n * 2;
- while (n--) {
- msg_buf_ptr[n] = (msg_buf_ptr[n] << 8) | ((msg_buf_ptr[n] >> 8) & 0xFF);
- }
- return msg_buf_ptr;
- }
- #endif
- #endif
- #ifndef NMBS_CLIENT_DISABLED
- static void put_regs(nmbs_t* nmbs, const uint16_t* data, uint16_t n) {
- uint16_t* msg_buf_ptr = (uint16_t*) (nmbs->msg.buf + nmbs->msg.buf_idx);
- nmbs->msg.buf_idx += n * 2;
- while (n--) {
- msg_buf_ptr[n] = (data[n] << 8) | ((data[n] >> 8) & 0xFF);
- }
- }
- #endif
- static void swap_regs(uint16_t* data, uint16_t n) {
- while (n--) {
- data[n] = (data[n] << 8) | ((data[n] >> 8) & 0xFF);
- }
- }
- static void msg_buf_reset(nmbs_t* nmbs) {
- nmbs->msg.buf_idx = 0;
- }
- static void msg_state_reset(nmbs_t* nmbs) {
- msg_buf_reset(nmbs);
- nmbs->msg.unit_id = 0;
- nmbs->msg.fc = 0;
- nmbs->msg.transaction_id = 0;
- nmbs->msg.broadcast = false;
- nmbs->msg.ignored = false;
- }
- #ifndef NMBS_CLIENT_DISABLED
- static void msg_state_req(nmbs_t* nmbs, uint8_t fc) {
- if (nmbs->current_tid == UINT16_MAX)
- nmbs->current_tid = 1;
- else
- nmbs->current_tid++;
- // Flush the remaining data on the line before sending the request
- nmbs->platform.read(nmbs->msg.buf, sizeof(nmbs->msg.buf), 0, nmbs->platform.arg);
- msg_state_reset(nmbs);
- nmbs->msg.unit_id = nmbs->dest_address_rtu;
- nmbs->msg.fc = fc;
- nmbs->msg.transaction_id = nmbs->current_tid;
- if (nmbs->msg.unit_id == 0 && nmbs->platform.transport == NMBS_TRANSPORT_RTU)
- nmbs->msg.broadcast = true;
- }
- #endif
- nmbs_error nmbs_create(nmbs_t* nmbs, const nmbs_platform_conf* platform_conf) {
- if (!nmbs)
- return NMBS_ERROR_INVALID_ARGUMENT;
- memset(nmbs, 0, sizeof(nmbs_t));
- nmbs->byte_timeout_ms = -1;
- nmbs->read_timeout_ms = -1;
- if (!platform_conf || platform_conf->initialized != 0xFFFFDEBE)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if (platform_conf->transport != NMBS_TRANSPORT_RTU && platform_conf->transport != NMBS_TRANSPORT_TCP)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if (!platform_conf->read || !platform_conf->write)
- return NMBS_ERROR_INVALID_ARGUMENT;
- nmbs->platform = *platform_conf;
- return NMBS_ERROR_NONE;
- }
- void nmbs_set_read_timeout(nmbs_t* nmbs, int32_t timeout_ms) {
- nmbs->read_timeout_ms = timeout_ms;
- }
- void nmbs_set_byte_timeout(nmbs_t* nmbs, int32_t timeout_ms) {
- nmbs->byte_timeout_ms = timeout_ms;
- }
- void nmbs_platform_conf_create(nmbs_platform_conf* platform_conf) {
- memset(platform_conf, 0, sizeof(nmbs_platform_conf));
- platform_conf->crc_calc = nmbs_crc_calc;
- // Workaround for older user code not calling nmbs_platform_conf_create()
- platform_conf->initialized = 0xFFFFDEBE;
- }
- void nmbs_set_destination_rtu_address(nmbs_t* nmbs, uint8_t address) {
- nmbs->dest_address_rtu = address;
- }
- void nmbs_set_platform_arg(nmbs_t* nmbs, void* arg) {
- nmbs->platform.arg = arg;
- }
- uint16_t nmbs_crc_calc(const uint8_t* data, uint32_t length, void* arg) {
- NMBS_UNUSED_PARAM(arg);
- uint16_t crc = 0xFFFF;
- for (uint32_t i = 0; i < length; i++) {
- crc ^= (uint16_t) data[i];
- for (int j = 8; j != 0; j--) {
- if ((crc & 0x0001) != 0) {
- crc >>= 1;
- crc ^= 0xA001;
- }
- else
- crc >>= 1;
- }
- }
- return (uint16_t) (crc << 8) | (uint16_t) (crc >> 8);
- }
- static nmbs_error recv(nmbs_t* nmbs, uint16_t count) {
- int32_t ret =
- nmbs->platform.read(nmbs->msg.buf + nmbs->msg.buf_idx, count, nmbs->byte_timeout_ms, nmbs->platform.arg);
- if (ret == count)
- return NMBS_ERROR_NONE;
- if (ret < count) {
- if (ret < 0)
- return NMBS_ERROR_TRANSPORT;
- return NMBS_ERROR_TIMEOUT;
- }
- return NMBS_ERROR_TRANSPORT;
- }
- static nmbs_error send(nmbs_t* nmbs, uint16_t count) {
- int32_t ret = nmbs->platform.write(nmbs->msg.buf, count, nmbs->byte_timeout_ms, nmbs->platform.arg);
- if (ret == count)
- return NMBS_ERROR_NONE;
- if (ret < count) {
- if (ret < 0)
- return NMBS_ERROR_TRANSPORT;
- return NMBS_ERROR_TIMEOUT;
- }
- return NMBS_ERROR_TRANSPORT;
- }
- static nmbs_error recv_msg_footer(nmbs_t* nmbs) {
- NMBS_DEBUG_PRINT("\n");
- if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
- uint16_t crc = nmbs->platform.crc_calc(nmbs->msg.buf, nmbs->msg.buf_idx, nmbs->platform.arg);
- nmbs_error err = recv(nmbs, 2);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t recv_crc = get_2(nmbs);
- if (recv_crc != crc)
- return NMBS_ERROR_CRC;
- }
- return NMBS_ERROR_NONE;
- }
- static nmbs_error recv_msg_header(nmbs_t* nmbs, bool* first_byte_received) {
- // We wait for the read timeout here, just for the first message byte
- int32_t old_byte_timeout = nmbs->byte_timeout_ms;
- nmbs->byte_timeout_ms = nmbs->read_timeout_ms;
- msg_state_reset(nmbs);
- *first_byte_received = false;
- if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
- nmbs_error err = recv(nmbs, 1);
- nmbs->byte_timeout_ms = old_byte_timeout;
- if (err != NMBS_ERROR_NONE)
- return err;
- *first_byte_received = true;
- nmbs->msg.unit_id = get_1(nmbs);
- err = recv(nmbs, 1);
- if (err != NMBS_ERROR_NONE)
- return err;
- nmbs->msg.fc = get_1(nmbs);
- }
- else if (nmbs->platform.transport == NMBS_TRANSPORT_TCP) {
- nmbs_error err = recv(nmbs, 1);
- nmbs->byte_timeout_ms = old_byte_timeout;
- if (err != NMBS_ERROR_NONE)
- return err;
- *first_byte_received = true;
- // Advance buf_idx
- discard_1(nmbs);
- err = recv(nmbs, 7);
- if (err != NMBS_ERROR_NONE)
- return err;
- // Starting over
- msg_buf_reset(nmbs);
- nmbs->msg.transaction_id = get_2(nmbs);
- uint16_t protocol_id = get_2(nmbs);
- uint16_t length = get_2(nmbs); // We should actually check the length of the request against this value
- nmbs->msg.unit_id = get_1(nmbs);
- nmbs->msg.fc = get_1(nmbs);
- if (protocol_id != 0)
- return NMBS_ERROR_INVALID_TCP_MBAP;
- if (length > 255)
- return NMBS_ERROR_INVALID_TCP_MBAP;
- }
- return NMBS_ERROR_NONE;
- }
- static void put_msg_header(nmbs_t* nmbs, uint16_t data_length) {
- msg_buf_reset(nmbs);
- if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
- put_1(nmbs, nmbs->msg.unit_id);
- }
- else if (nmbs->platform.transport == NMBS_TRANSPORT_TCP) {
- put_2(nmbs, nmbs->msg.transaction_id);
- put_2(nmbs, 0);
- put_2(nmbs, (uint16_t) (1 + 1 + data_length));
- put_1(nmbs, nmbs->msg.unit_id);
- }
- put_1(nmbs, nmbs->msg.fc);
- }
- #ifndef NMBS_SERVER_DISABLED
- #if !defined(NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED)
- static void set_msg_header_size(nmbs_t* nmbs, uint16_t data_length) {
- if (nmbs->platform.transport == NMBS_TRANSPORT_TCP) {
- data_length += 2;
- set_2(nmbs, data_length, 4);
- }
- }
- #endif
- #endif
- static nmbs_error send_msg(nmbs_t* nmbs) {
- NMBS_DEBUG_PRINT("\n");
- if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
- uint16_t crc = nmbs->platform.crc_calc(nmbs->msg.buf, nmbs->msg.buf_idx, nmbs->platform.arg);
- put_2(nmbs, crc);
- }
- nmbs_error err = send(nmbs, nmbs->msg.buf_idx);
- return err;
- }
- #ifndef NMBS_SERVER_DISABLED
- static nmbs_error recv_req_header(nmbs_t* nmbs, bool* first_byte_received) {
- nmbs_error err = recv_msg_header(nmbs, first_byte_received);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
- // Check if request is for us
- if (nmbs->msg.unit_id == NMBS_BROADCAST_ADDRESS)
- nmbs->msg.broadcast = true;
- else if (nmbs->msg.unit_id != nmbs->address_rtu)
- nmbs->msg.ignored = true;
- else
- nmbs->msg.ignored = false;
- }
- return NMBS_ERROR_NONE;
- }
- static void put_res_header(nmbs_t* nmbs, uint16_t data_length) {
- put_msg_header(nmbs, data_length);
- NMBS_DEBUG_PRINT("%d NMBS res -> address_rtu %d\tfc %d\t", nmbs->address_rtu, nmbs->address_rtu, nmbs->msg.fc);
- }
- static nmbs_error send_exception_msg(nmbs_t* nmbs, uint8_t exception) {
- nmbs->msg.fc += 0x80;
- put_msg_header(nmbs, 1);
- put_1(nmbs, exception);
- NMBS_DEBUG_PRINT("%d NMBS res -> address_rtu %d\texception %d", nmbs->address_rtu, nmbs->address_rtu, exception);
- return send_msg(nmbs);
- }
- #endif
- static nmbs_error recv_res_header(nmbs_t* nmbs) {
- uint16_t req_transaction_id = nmbs->msg.transaction_id;
- uint8_t req_unit_id = nmbs->msg.unit_id;
- uint8_t req_fc = nmbs->msg.fc;
- bool first_byte_received = false;
- nmbs_error err = recv_msg_header(nmbs, &first_byte_received);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (nmbs->platform.transport == NMBS_TRANSPORT_TCP) {
- if (nmbs->msg.transaction_id != req_transaction_id)
- return NMBS_ERROR_INVALID_TCP_MBAP;
- }
- if (nmbs->platform.transport == NMBS_TRANSPORT_RTU && nmbs->msg.unit_id != req_unit_id)
- return NMBS_ERROR_INVALID_UNIT_ID;
- if (nmbs->msg.fc != req_fc) {
- if (nmbs->msg.fc - 0x80 == req_fc) {
- err = recv(nmbs, 1);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t exception = get_1(nmbs);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (exception < 1 || exception > 4)
- return NMBS_ERROR_INVALID_RESPONSE;
- NMBS_DEBUG_PRINT("%d NMBS res <- address_rtu %d\texception %d\n", nmbs->address_rtu, nmbs->msg.unit_id,
- exception);
- return (nmbs_error) exception;
- }
- return NMBS_ERROR_INVALID_RESPONSE;
- }
- NMBS_DEBUG_PRINT("%d NMBS res <- address_rtu %d\tfc %d\t", nmbs->address_rtu, nmbs->msg.unit_id, nmbs->msg.fc);
- return NMBS_ERROR_NONE;
- }
- #ifndef NMBS_CLIENT_DISABLED
- static void put_req_header(nmbs_t* nmbs, uint16_t data_length) {
- put_msg_header(nmbs, data_length);
- #ifdef NMBS_DEBUG
- printf("%d ", nmbs->address_rtu);
- printf("NMBS req -> ");
- if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
- if (nmbs->msg.broadcast)
- printf("broadcast\t");
- else
- printf("address_rtu %d\t", nmbs->dest_address_rtu);
- }
- printf("fc %d\t", nmbs->msg.fc);
- #endif
- }
- #endif
- #if !defined(NMBS_CLIENT_DISABLED) || \
- (!defined(NMBS_SERVER_DISABLED) && \
- (!defined(NMBS_SERVER_READ_COILS_DISABLED) || !defined(NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED)))
- static nmbs_error recv_read_discrete_res(nmbs_t* nmbs, nmbs_bitfield values) {
- nmbs_error err = recv_res_header(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- err = recv(nmbs, 1);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t coils_bytes = get_1(nmbs);
- NMBS_DEBUG_PRINT("b %d\t", coils_bytes);
- if (coils_bytes > 250) {
- return NMBS_ERROR_INVALID_RESPONSE;
- }
- err = recv(nmbs, coils_bytes);
- if (err != NMBS_ERROR_NONE)
- return err;
- NMBS_DEBUG_PRINT("coils ");
- for (int i = 0; i < coils_bytes; i++) {
- uint8_t coil = get_1(nmbs);
- if (values)
- values[i] = coil;
- NMBS_DEBUG_PRINT("%d ", coil);
- }
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- return NMBS_ERROR_NONE;
- }
- #endif
- #if !defined(NMBS_CLIENT_DISABLED) || \
- (!defined(NMBS_SERVER_DISABLED) && (!defined(NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED) || \
- !defined(NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED)))
- static nmbs_error recv_read_registers_res(nmbs_t* nmbs, uint16_t quantity, uint16_t* registers) {
- nmbs_error err = recv_res_header(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- err = recv(nmbs, 1);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t registers_bytes = get_1(nmbs);
- NMBS_DEBUG_PRINT("b %d\t", registers_bytes);
- if (registers_bytes > 250)
- return NMBS_ERROR_INVALID_RESPONSE;
- err = recv(nmbs, registers_bytes);
- if (err != NMBS_ERROR_NONE)
- return err;
- NMBS_DEBUG_PRINT("regs ");
- for (int i = 0; i < registers_bytes / 2; i++) {
- uint16_t reg = get_2(nmbs);
- if (registers)
- registers[i] = reg;
- NMBS_DEBUG_PRINT("%d ", reg);
- }
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (registers_bytes != quantity * 2)
- return NMBS_ERROR_INVALID_RESPONSE;
- return NMBS_ERROR_NONE;
- }
- #endif
- nmbs_error recv_write_single_coil_res(nmbs_t* nmbs, uint16_t address, uint16_t value_req) {
- nmbs_error err = recv_res_header(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- err = recv(nmbs, 4);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t address_res = get_2(nmbs);
- uint16_t value_res = get_2(nmbs);
- NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value_res);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (address_res != address)
- return NMBS_ERROR_INVALID_RESPONSE;
- if (value_res != value_req)
- return NMBS_ERROR_INVALID_RESPONSE;
- return NMBS_ERROR_NONE;
- }
- nmbs_error recv_write_single_register_res(nmbs_t* nmbs, uint16_t address, uint16_t value_req) {
- nmbs_error err = recv_res_header(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- err = recv(nmbs, 4);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t address_res = get_2(nmbs);
- uint16_t value_res = get_2(nmbs);
- NMBS_DEBUG_PRINT("a %d\tvalue %d ", address, value_res);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (address_res != address)
- return NMBS_ERROR_INVALID_RESPONSE;
- if (value_res != value_req)
- return NMBS_ERROR_INVALID_RESPONSE;
- return NMBS_ERROR_NONE;
- }
- nmbs_error recv_write_multiple_coils_res(nmbs_t* nmbs, uint16_t address, uint16_t quantity) {
- nmbs_error err = recv_res_header(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- err = recv(nmbs, 4);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t address_res = get_2(nmbs);
- uint16_t quantity_res = get_2(nmbs);
- NMBS_DEBUG_PRINT("a %d\tq %d", address_res, quantity_res);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (address_res != address)
- return NMBS_ERROR_INVALID_RESPONSE;
- if (quantity_res != quantity)
- return NMBS_ERROR_INVALID_RESPONSE;
- return NMBS_ERROR_NONE;
- }
- nmbs_error recv_write_multiple_registers_res(nmbs_t* nmbs, uint16_t address, uint16_t quantity) {
- nmbs_error err = recv_res_header(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- err = recv(nmbs, 4);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t address_res = get_2(nmbs);
- uint16_t quantity_res = get_2(nmbs);
- NMBS_DEBUG_PRINT("a %d\tq %d", address_res, quantity_res);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (address_res != address)
- return NMBS_ERROR_INVALID_RESPONSE;
- if (quantity_res != quantity)
- return NMBS_ERROR_INVALID_RESPONSE;
- return NMBS_ERROR_NONE;
- }
- nmbs_error recv_read_file_record_res(nmbs_t* nmbs, uint16_t* registers, uint16_t count) {
- nmbs_error err = recv_res_header(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- err = recv(nmbs, 1);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t response_size = get_1(nmbs);
- if (response_size > 250) {
- return NMBS_ERROR_INVALID_RESPONSE;
- }
- err = recv(nmbs, response_size);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t subreq_data_size = get_1(nmbs) - 1;
- uint8_t subreq_reference_type = get_1(nmbs);
- uint16_t* subreq_record_data = (uint16_t*) get_n(nmbs, subreq_data_size);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (registers) {
- if (subreq_reference_type != 6)
- return NMBS_ERROR_INVALID_RESPONSE;
- if (count != (subreq_data_size / 2))
- return NMBS_ERROR_INVALID_RESPONSE;
- swap_regs(subreq_record_data, subreq_data_size / 2);
- memcpy(registers, subreq_record_data, subreq_data_size);
- }
- return NMBS_ERROR_NONE;
- }
- nmbs_error recv_write_file_record_res(nmbs_t* nmbs, uint16_t file_number, uint16_t record_number,
- const uint16_t* registers, uint16_t count) {
- nmbs_error err = recv_res_header(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- err = recv(nmbs, 1);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t response_size = get_1(nmbs);
- if (response_size > 251)
- return NMBS_ERROR_INVALID_RESPONSE;
- err = recv(nmbs, response_size);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t subreq_reference_type = get_1(nmbs);
- uint16_t subreq_file_number = get_2(nmbs);
- uint16_t subreq_record_number = get_2(nmbs);
- uint16_t subreq_record_length = get_2(nmbs);
- NMBS_DEBUG_PRINT("a %d\tr %d\tl %d\t fwrite ", subreq_file_number, subreq_record_number, subreq_record_length);
- uint16_t subreq_data_size = subreq_record_length * 2;
- uint16_t* subreq_record_data = (uint16_t*) get_n(nmbs, subreq_data_size);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (registers) {
- if (subreq_reference_type != 6)
- return NMBS_ERROR_INVALID_RESPONSE;
- if (subreq_file_number != file_number)
- return NMBS_ERROR_INVALID_RESPONSE;
- if (subreq_record_number != record_number)
- return NMBS_ERROR_INVALID_RESPONSE;
- if (subreq_record_length != count)
- return NMBS_ERROR_INVALID_RESPONSE;
- swap_regs(subreq_record_data, subreq_record_length);
- if (memcmp(registers, subreq_record_data, subreq_data_size) != 0)
- return NMBS_ERROR_INVALID_RESPONSE;
- }
- return NMBS_ERROR_NONE;
- }
- nmbs_error recv_read_device_identification_res(nmbs_t* nmbs, uint8_t buffers_count, char** buffers_out,
- uint8_t buffers_length, const uint8_t* order, uint8_t* ids_out,
- uint8_t* next_object_id_out, uint8_t* objects_count_out) {
- nmbs_error err = recv_res_header(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- err = recv(nmbs, 6);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t mei_type = get_1(nmbs);
- if (mei_type != 0x0E)
- return NMBS_ERROR_INVALID_RESPONSE;
- uint8_t read_device_id_code = get_1(nmbs);
- if (read_device_id_code < 1 || read_device_id_code > 4)
- return NMBS_ERROR_INVALID_RESPONSE;
- uint8_t conformity_level = get_1(nmbs);
- if (conformity_level < 1 || (conformity_level > 3 && conformity_level < 0x81) || conformity_level > 0x83)
- return NMBS_ERROR_INVALID_RESPONSE;
- uint8_t more_follows = get_1(nmbs);
- if (more_follows != 0 && more_follows != 0xFF)
- return NMBS_ERROR_INVALID_RESPONSE;
- uint8_t next_object_id = get_1(nmbs);
- uint8_t objects_count = get_1(nmbs);
- if (objects_count_out)
- *objects_count_out = objects_count;
- if (buffers_count == 0) {
- buffers_out = NULL;
- }
- else if (objects_count > buffers_count)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if (more_follows == 0)
- next_object_id = 0x7F; // This value is reserved in the spec, we use it to signal stream is finished
- if (next_object_id_out)
- *next_object_id_out = next_object_id;
- uint8_t res_size_left = 253 - 7;
- for (int i = 0; i < objects_count; i++) {
- err = recv(nmbs, 2);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t object_id = get_1(nmbs);
- uint8_t object_length = get_1(nmbs);
- res_size_left -= 2;
- if (object_length > res_size_left)
- return NMBS_ERROR_INVALID_RESPONSE;
- err = recv(nmbs, object_length);
- if (err != NMBS_ERROR_NONE)
- return err;
- const char* str = (const char*) get_n(nmbs, object_length);
- if (ids_out)
- ids_out[i] = object_id;
- uint8_t buf_index = i;
- if (order)
- buf_index = order[object_id];
- if (buffers_out) {
- strncpy(buffers_out[buf_index], str, buffers_length);
- buffers_out[buf_index][object_length] = 0;
- }
- }
- return recv_msg_footer(nmbs);
- }
- #ifndef NMBS_SERVER_DISABLED
- #if !defined(NMBS_SERVER_READ_COILS_DISABLED) || !defined(NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED)
- static nmbs_error handle_read_discrete(nmbs_t* nmbs,
- nmbs_error (*callback)(uint16_t, uint16_t, nmbs_bitfield, uint8_t, void*)) {
- nmbs_error err = recv(nmbs, 4);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t address = get_2(nmbs);
- uint16_t quantity = get_2(nmbs);
- NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.ignored) {
- if (quantity < 1 || quantity > 2000)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (callback) {
- nmbs_bitfield bitfield = {0};
- err = callback(address, quantity, bitfield, nmbs->msg.unit_id, nmbs->callbacks.arg);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- if (!nmbs->msg.broadcast) {
- uint8_t discrete_bytes = (quantity + 7) / 8;
- put_res_header(nmbs, 1 + discrete_bytes);
- put_1(nmbs, discrete_bytes);
- NMBS_DEBUG_PRINT("b %d\t", discrete_bytes);
- NMBS_DEBUG_PRINT("coils ");
- for (int i = 0; i < discrete_bytes; i++) {
- put_1(nmbs, bitfield[i]);
- NMBS_DEBUG_PRINT("%d ", bitfield[i]);
- }
- err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- }
- }
- else {
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- }
- }
- else {
- return recv_read_discrete_res(nmbs, NULL);
- }
- return NMBS_ERROR_NONE;
- }
- #endif
- #if !defined(NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED) || !defined(NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED)
- static nmbs_error handle_read_registers(nmbs_t* nmbs,
- nmbs_error (*callback)(uint16_t, uint16_t, uint16_t*, uint8_t, void*)) {
- nmbs_error err = recv(nmbs, 4);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t address = get_2(nmbs);
- uint16_t quantity = get_2(nmbs);
- NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.ignored) {
- if (quantity < 1 || quantity > 125)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (callback) {
- uint16_t regs[125] = {0};
- err = callback(address, quantity, regs, nmbs->msg.unit_id, nmbs->callbacks.arg);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- // TODO check all these read request broadcast use cases
- if (!nmbs->msg.broadcast) {
- uint8_t regs_bytes = quantity * 2;
- put_res_header(nmbs, 1 + regs_bytes);
- put_1(nmbs, regs_bytes);
- NMBS_DEBUG_PRINT("b %d\t", regs_bytes);
- NMBS_DEBUG_PRINT("regs ");
- for (int i = 0; i < quantity; i++) {
- put_2(nmbs, regs[i]);
- NMBS_DEBUG_PRINT("%d ", regs[i]);
- }
- err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- }
- }
- else {
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- }
- }
- else {
- return recv_read_registers_res(nmbs, quantity, NULL);
- }
- return NMBS_ERROR_NONE;
- }
- #endif
- #ifndef NMBS_SERVER_READ_COILS_DISABLED
- static nmbs_error handle_read_coils(nmbs_t* nmbs) {
- return handle_read_discrete(nmbs, nmbs->callbacks.read_coils);
- }
- #endif
- #ifndef NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED
- static nmbs_error handle_read_discrete_inputs(nmbs_t* nmbs) {
- return handle_read_discrete(nmbs, nmbs->callbacks.read_discrete_inputs);
- }
- #endif
- #ifndef NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED
- static nmbs_error handle_read_holding_registers(nmbs_t* nmbs) {
- return handle_read_registers(nmbs, nmbs->callbacks.read_holding_registers);
- }
- #endif
- #ifndef NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED
- static nmbs_error handle_read_input_registers(nmbs_t* nmbs) {
- return handle_read_registers(nmbs, nmbs->callbacks.read_input_registers);
- }
- #endif
- #ifndef NMBS_SERVER_WRITE_SINGLE_COIL_DISABLED
- static nmbs_error handle_write_single_coil(nmbs_t* nmbs) {
- nmbs_error err = recv(nmbs, 4);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t address = get_2(nmbs);
- uint16_t value = get_2(nmbs);
- NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.ignored) {
- if (nmbs->callbacks.write_single_coil) {
- if (value != 0 && value != 0xFF00)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- err = nmbs->callbacks.write_single_coil(address, value == 0 ? false : true, nmbs->msg.unit_id,
- nmbs->callbacks.arg);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- if (!nmbs->msg.broadcast) {
- put_res_header(nmbs, 4);
- put_2(nmbs, address);
- put_2(nmbs, value);
- NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value);
- err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- }
- }
- else {
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- }
- }
- else {
- return recv_write_single_coil_res(nmbs, address, value);
- }
- return NMBS_ERROR_NONE;
- }
- #endif
- #ifndef NMBS_SERVER_WRITE_SINGLE_REGISTER_DISABLED
- static nmbs_error handle_write_single_register(nmbs_t* nmbs) {
- nmbs_error err = recv(nmbs, 4);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t address = get_2(nmbs);
- uint16_t value = get_2(nmbs);
- NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.ignored) {
- if (nmbs->callbacks.write_single_register) {
- err = nmbs->callbacks.write_single_register(address, value, nmbs->msg.unit_id, nmbs->callbacks.arg);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- if (!nmbs->msg.broadcast) {
- put_res_header(nmbs, 4);
- put_2(nmbs, address);
- put_2(nmbs, value);
- NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value);
- err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- }
- }
- else {
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- }
- }
- else {
- return recv_write_single_register_res(nmbs, address, value);
- }
- return NMBS_ERROR_NONE;
- }
- #endif
- #ifndef NMBS_SERVER_WRITE_MULTIPLE_COILS_DISABLED
- static nmbs_error handle_write_multiple_coils(nmbs_t* nmbs) {
- nmbs_error err = recv(nmbs, 5);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t address = get_2(nmbs);
- uint16_t quantity = get_2(nmbs);
- uint8_t coils_bytes = get_1(nmbs);
- NMBS_DEBUG_PRINT("a %d\tq %d\tb %d\tcoils ", address, quantity, coils_bytes);
- if (coils_bytes > 246)
- return NMBS_ERROR_INVALID_REQUEST;
- err = recv(nmbs, coils_bytes);
- if (err != NMBS_ERROR_NONE)
- return err;
- nmbs_bitfield coils = {0};
- for (int i = 0; i < coils_bytes; i++) {
- coils[i] = get_1(nmbs);
- NMBS_DEBUG_PRINT("%d ", coils[i]);
- }
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.ignored) {
- if (quantity < 1 || quantity > 0x07B0)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (coils_bytes == 0)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if ((quantity + 7) / 8 != coils_bytes)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if (nmbs->callbacks.write_multiple_coils) {
- err = nmbs->callbacks.write_multiple_coils(address, quantity, coils, nmbs->msg.unit_id,
- nmbs->callbacks.arg);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- if (!nmbs->msg.broadcast) {
- put_res_header(nmbs, 4);
- put_2(nmbs, address);
- put_2(nmbs, quantity);
- NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
- err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- }
- }
- else {
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- }
- }
- else {
- return recv_write_multiple_coils_res(nmbs, address, quantity);
- }
- return NMBS_ERROR_NONE;
- }
- #endif
- #ifndef NMBS_SERVER_WRITE_MULTIPLE_REGISTERS_DISABLED
- static nmbs_error handle_write_multiple_registers(nmbs_t* nmbs) {
- nmbs_error err = recv(nmbs, 5);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t address = get_2(nmbs);
- uint16_t quantity = get_2(nmbs);
- uint8_t registers_bytes = get_1(nmbs);
- NMBS_DEBUG_PRINT("a %d\tq %d\tb %d\tregs ", address, quantity, registers_bytes);
- err = recv(nmbs, registers_bytes);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (registers_bytes > 246)
- return NMBS_ERROR_INVALID_REQUEST;
- uint16_t registers[0x007B];
- for (int i = 0; i < registers_bytes / 2; i++) {
- registers[i] = get_2(nmbs);
- NMBS_DEBUG_PRINT("%d ", registers[i]);
- }
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.ignored) {
- if (quantity < 1 || quantity > 0x007B)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (registers_bytes == 0)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if (registers_bytes != quantity * 2)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if (nmbs->callbacks.write_multiple_registers) {
- err = nmbs->callbacks.write_multiple_registers(address, quantity, registers, nmbs->msg.unit_id,
- nmbs->callbacks.arg);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- if (!nmbs->msg.broadcast) {
- put_res_header(nmbs, 4);
- put_2(nmbs, address);
- put_2(nmbs, quantity);
- NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
- err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- }
- }
- else {
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- }
- }
- else {
- return recv_write_multiple_registers_res(nmbs, address, quantity);
- }
- return NMBS_ERROR_NONE;
- }
- #endif
- #ifndef NMBS_SERVER_READ_FILE_RECORD_DISABLED
- static nmbs_error handle_read_file_record(nmbs_t* nmbs) {
- nmbs_error err = recv(nmbs, 1);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t request_size = get_1(nmbs);
- if (request_size > 245)
- return NMBS_ERROR_INVALID_REQUEST;
- err = recv(nmbs, request_size);
- if (err != NMBS_ERROR_NONE)
- return err;
- const uint8_t subreq_header_size = 7;
- const uint8_t subreq_count = request_size / subreq_header_size;
- struct {
- uint8_t reference_type;
- uint16_t file_number;
- uint16_t record_number;
- uint16_t record_length;
- }
- #ifdef __STDC_NO_VLA__
- subreq[35]; // 245 / subreq_header_size
- #else
- subreq[subreq_count];
- #endif
- uint8_t response_data_size = 0;
- for (uint8_t i = 0; i < subreq_count; i++) {
- subreq[i].reference_type = get_1(nmbs);
- subreq[i].file_number = get_2(nmbs);
- subreq[i].record_number = get_2(nmbs);
- subreq[i].record_length = get_2(nmbs);
- response_data_size += 2 + subreq[i].record_length * 2;
- }
- discard_n(nmbs, request_size % subreq_header_size);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.ignored) {
- if (request_size % subreq_header_size)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if (request_size < 0x07 || request_size > 0xF5)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- for (uint8_t i = 0; i < subreq_count; i++) {
- if (subreq[i].reference_type != 0x06)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (subreq[i].file_number == 0x0000)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (subreq[i].record_number > 0x270F)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (subreq[i].record_length > 124)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- NMBS_DEBUG_PRINT("a %d\tr %d\tl %d\t fread ", subreq[i].file_number, subreq[i].record_number,
- subreq[i].record_length);
- }
- put_res_header(nmbs, response_data_size);
- put_1(nmbs, response_data_size);
- if (nmbs->callbacks.read_file_record) {
- for (uint8_t i = 0; i < subreq_count; i++) {
- uint16_t subreq_data_size = subreq[i].record_length * 2;
- put_1(nmbs, subreq_data_size + 1);
- put_1(nmbs, 0x06); // add Reference Type const
- uint16_t* subreq_data = (uint16_t*) get_n(nmbs, subreq_data_size);
- err = nmbs->callbacks.read_file_record(subreq[i].file_number, subreq[i].record_number, subreq_data,
- subreq[i].record_length, nmbs->msg.unit_id, nmbs->callbacks.arg);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- swap_regs(subreq_data, subreq[i].record_length);
- }
- }
- else {
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- }
- if (!nmbs->msg.broadcast) {
- err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- }
- }
- else {
- return recv_read_file_record_res(nmbs, NULL, 0);
- }
- return NMBS_ERROR_NONE;
- }
- #endif
- #ifndef NMBS_SERVER_WRITE_FILE_RECORD_DISABLED
- static nmbs_error handle_write_file_record(nmbs_t* nmbs) {
- nmbs_error err = recv(nmbs, 1);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t request_size = get_1(nmbs);
- if (request_size > 251) {
- return NMBS_ERROR_INVALID_REQUEST;
- }
- err = recv(nmbs, request_size);
- if (err != NMBS_ERROR_NONE)
- return err;
- // We can save msg.buf index and use it later for context recovery.
- uint16_t msg_buf_idx = nmbs->msg.buf_idx;
- discard_n(nmbs, request_size);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.ignored) {
- const uint8_t subreq_header_size = 7;
- uint16_t size = request_size;
- nmbs->msg.buf_idx = msg_buf_idx; // restore context
- if (request_size < 0x07 || request_size > 0xFB)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- do {
- uint8_t subreq_reference_type = get_1(nmbs);
- uint16_t subreq_file_number_c = get_2(nmbs);
- uint16_t subreq_record_number_c = get_2(nmbs);
- uint16_t subreq_record_length_c = get_2(nmbs);
- discard_n(nmbs, subreq_record_length_c * 2);
- if (subreq_reference_type != 0x06)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (subreq_file_number_c == 0x0000)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (subreq_record_number_c > 0x270F)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (subreq_record_length_c > 122)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- NMBS_DEBUG_PRINT("a %d\tr %d\tl %d\t fwrite ", subreq_file_number_c, subreq_record_number_c,
- subreq_record_length_c);
- size -= (subreq_header_size + subreq_record_length_c * 2);
- } while (size >= subreq_header_size);
- if (size)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- // checks completed
- size = request_size;
- nmbs->msg.buf_idx = msg_buf_idx; // restore context
- do {
- discard_1(nmbs);
- uint16_t subreq_file_number = get_2(nmbs);
- uint16_t subreq_record_number = get_2(nmbs);
- uint16_t subreq_record_length = get_2(nmbs);
- uint16_t* subreq_data = get_regs(nmbs, subreq_record_length);
- if (nmbs->callbacks.write_file_record) {
- err = nmbs->callbacks.write_file_record(subreq_file_number, subreq_record_number, subreq_data,
- subreq_record_length, nmbs->msg.unit_id, nmbs->callbacks.arg);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- swap_regs(subreq_data, subreq_record_length); // restore swapping
- }
- else {
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- }
- size -= (subreq_header_size + subreq_record_length * 2);
- } while (size >= subreq_header_size);
- if (!nmbs->msg.broadcast) {
- // The normal response to 'Write File' is an echo of the request.
- // We can restore buffer index and response msg.
- nmbs->msg.buf_idx = msg_buf_idx;
- discard_n(nmbs, request_size);
- err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- }
- }
- else {
- return recv_write_file_record_res(nmbs, 0, 0, NULL, 0);
- }
- return NMBS_ERROR_NONE;
- }
- #endif
- #ifndef NMBS_SERVER_READ_WRITE_REGISTERS_DISABLED
- static nmbs_error handle_read_write_registers(nmbs_t* nmbs) {
- nmbs_error err = recv(nmbs, 9);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint16_t read_address = get_2(nmbs);
- uint16_t read_quantity = get_2(nmbs);
- uint16_t write_address = get_2(nmbs);
- uint16_t write_quantity = get_2(nmbs);
- uint8_t byte_count_write = get_1(nmbs);
- NMBS_DEBUG_PRINT("ra %d\trq %d\t wa %d\t wq %d\t b %d\tregs ", read_address, read_quantity, write_address,
- write_quantity, byte_count_write);
- if (byte_count_write > 242)
- return NMBS_ERROR_INVALID_REQUEST;
- err = recv(nmbs, byte_count_write);
- if (err != NMBS_ERROR_NONE)
- return err;
- #ifdef __STDC_NO_VLA__
- uint16_t registers[0x007B];
- #else
- uint16_t registers[byte_count_write / 2];
- #endif
- for (int i = 0; i < byte_count_write / 2; i++) {
- registers[i] = get_2(nmbs);
- NMBS_DEBUG_PRINT("%d ", registers[i]);
- }
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.ignored) {
- if (read_quantity < 1 || read_quantity > 0x007D)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if (write_quantity < 1 || write_quantity > 0x007B)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if (byte_count_write != write_quantity * 2)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if ((uint32_t) read_address + (uint32_t) read_quantity > ((uint32_t) 0xFFFF) + 1)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if ((uint32_t) write_address + (uint32_t) write_quantity > ((uint32_t) 0xFFFF) + 1)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (!nmbs->callbacks.write_multiple_registers || !nmbs->callbacks.read_holding_registers)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- err = nmbs->callbacks.write_multiple_registers(write_address, write_quantity, registers, nmbs->msg.unit_id,
- nmbs->callbacks.arg);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- if (!nmbs->msg.broadcast) {
- #ifdef __STDC_NO_VLA__
- uint16_t regs[125];
- #else
- uint16_t regs[read_quantity];
- #endif
- err = nmbs->callbacks.read_holding_registers(read_address, read_quantity, regs, nmbs->msg.unit_id,
- nmbs->callbacks.arg);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- uint8_t regs_bytes = read_quantity * 2;
- put_res_header(nmbs, 1 + regs_bytes);
- put_1(nmbs, regs_bytes);
- NMBS_DEBUG_PRINT("b %d\t", regs_bytes);
- NMBS_DEBUG_PRINT("regs ");
- for (int i = 0; i < read_quantity; i++) {
- put_2(nmbs, regs[i]);
- NMBS_DEBUG_PRINT("%d ", regs[i]);
- }
- err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- }
- }
- else {
- return recv_write_multiple_registers_res(nmbs, write_address, write_quantity);
- }
- return NMBS_ERROR_NONE;
- }
- #endif
- #ifndef NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED
- static nmbs_error handle_read_device_identification(nmbs_t* nmbs) {
- nmbs_error err = recv(nmbs, 3);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t mei_type = get_1(nmbs);
- uint8_t read_device_id_code = get_1(nmbs);
- uint8_t object_id = get_1(nmbs);
- NMBS_DEBUG_PRINT("c %d\to %d", read_device_id_code, object_id);
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.ignored) {
- if (!nmbs->callbacks.read_device_identification_map || !nmbs->callbacks.read_device_identification)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- if (mei_type != 0x0E)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- if (read_device_id_code < 1 || read_device_id_code > 4)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);
- if (object_id > 6 && object_id < 0x80)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- if (!nmbs->msg.broadcast) {
- char str[NMBS_DEVICE_IDENTIFICATION_STRING_LENGTH];
- nmbs_bitfield_256 map;
- nmbs_bitfield_reset(map);
- err = nmbs->callbacks.read_device_identification_map(map);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- put_res_header(nmbs, 0); // Length will be set later
- put_1(nmbs, 0x0E);
- put_1(nmbs, read_device_id_code);
- put_1(nmbs, 0x83);
- if (read_device_id_code == 4) {
- if (!nmbs_bitfield_read(map, object_id))
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- put_1(nmbs, 0); // More follows
- put_1(nmbs, 0); // Next Object Id
- put_1(nmbs, 1); // Number of objects
- str[0] = 0;
- err = nmbs->callbacks.read_device_identification(object_id, str);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- size_t str_len = strlen(str);
- put_1(nmbs, object_id); // Object id
- put_1(nmbs, str_len); // Object length
- put_n(nmbs, (uint8_t*) str, str_len);
- set_msg_header_size(nmbs, 6 + 2 + str_len);
- return send_msg(nmbs);
- }
- uint8_t more_follows_idx = nmbs->msg.buf_idx;
- put_1(nmbs, 0);
- uint8_t next_object_id_idx = nmbs->msg.buf_idx;
- put_1(nmbs, 0);
- uint8_t number_of_objects_idx = nmbs->msg.buf_idx;
- put_1(nmbs, 0);
- int16_t res_size_left = 253 - 7;
- uint8_t last_id = 0;
- uint8_t msg_size = 6;
- uint8_t res_more_follows = 0;
- uint8_t res_next_object_id = 0;
- uint8_t res_number_of_objects = 0;
- switch (read_device_id_code) {
- case 1:
- if (object_id > 0x02)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- last_id = 0x02;
- break;
- case 2:
- if (object_id < 0x03 || object_id > 0x07)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- last_id = 0x07;
- break;
- case 3:
- if (object_id < 0x80)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
- last_id = 0xFF;
- break;
- default:
- // Unreachable
- break;
- }
- for (uint16_t id = object_id; id <= last_id; id++) {
- if (!nmbs_bitfield_read(map, id)) {
- if (id < 0x03)
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- continue;
- }
- str[0] = 0;
- err = nmbs->callbacks.read_device_identification((uint8_t) id, str);
- if (err != NMBS_ERROR_NONE) {
- if (nmbs_error_is_exception(err))
- return send_exception_msg(nmbs, err);
- return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
- }
- int16_t str_len = (int16_t) strlen(str);
- res_size_left = (int16_t) (res_size_left - 2 - str_len);
- if (res_size_left < 0) {
- res_more_follows = 0xFF;
- res_next_object_id = id;
- break;
- }
- put_1(nmbs, (uint8_t) id); // Object id
- put_1(nmbs, str_len); // Object length
- put_n(nmbs, (uint8_t*) str, str_len);
- msg_size += (2 + str_len);
- res_number_of_objects++;
- }
- set_1(nmbs, res_more_follows, more_follows_idx);
- set_1(nmbs, res_next_object_id, next_object_id_idx);
- set_1(nmbs, res_number_of_objects, number_of_objects_idx);
- set_msg_header_size(nmbs, msg_size);
- return send_msg(nmbs);
- }
- }
- else {
- return recv_read_device_identification_res(nmbs, 0, NULL, 0, NULL, NULL, NULL, NULL);
- }
- return NMBS_ERROR_NONE;
- }
- #endif
- static nmbs_error handle_req_fc(nmbs_t* nmbs) {
- NMBS_DEBUG_PRINT("fc %d\t", nmbs->msg.fc);
- nmbs_error err = NMBS_ERROR_NONE;
- switch (nmbs->msg.fc) {
- #ifndef NMBS_SERVER_READ_COILS_DISABLED
- case 1:
- err = handle_read_coils(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED
- case 2:
- err = handle_read_discrete_inputs(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED
- case 3:
- err = handle_read_holding_registers(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED
- case 4:
- err = handle_read_input_registers(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_WRITE_SINGLE_COIL_DISABLED
- case 5:
- err = handle_write_single_coil(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_WRITE_SINGLE_REGISTER_DISABLED
- case 6:
- err = handle_write_single_register(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_WRITE_MULTIPLE_COILS_DISABLED
- case 15:
- err = handle_write_multiple_coils(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_WRITE_MULTIPLE_REGISTERS_DISABLED
- case 16:
- err = handle_write_multiple_registers(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_READ_FILE_RECORD_DISABLED
- case 20:
- err = handle_read_file_record(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_WRITE_FILE_RECORD_DISABLED
- case 21:
- err = handle_write_file_record(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_READ_WRITE_REGISTERS_DISABLED
- case 23:
- err = handle_read_write_registers(nmbs);
- break;
- #endif
- #ifndef NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED
- case 43:
- err = handle_read_device_identification(nmbs);
- break;
- #endif
- default:
- err = send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
- }
- return err;
- }
- void nmbs_callbacks_create(nmbs_callbacks* callbacks) {
- memset(callbacks, 0, sizeof(nmbs_callbacks));
- callbacks->initialized = 0xFFFFDEBE;
- }
- nmbs_error nmbs_server_create(nmbs_t* nmbs, uint8_t address_rtu, const nmbs_platform_conf* platform_conf,
- const nmbs_callbacks* callbacks) {
- if (platform_conf->transport == NMBS_TRANSPORT_RTU && address_rtu == 0)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if (!callbacks || callbacks->initialized != 0xFFFFDEBE)
- return NMBS_ERROR_INVALID_ARGUMENT;
- nmbs_error ret = nmbs_create(nmbs, platform_conf);
- if (ret != NMBS_ERROR_NONE)
- return ret;
- nmbs->address_rtu = address_rtu;
- nmbs->callbacks = *callbacks;
- return NMBS_ERROR_NONE;
- }
- nmbs_error nmbs_server_poll(nmbs_t* nmbs) {
- msg_state_reset(nmbs);
- bool first_byte_received = false;
- nmbs_error err = recv_req_header(nmbs, &first_byte_received);
- if (err != NMBS_ERROR_NONE) {
- if (!first_byte_received && err == NMBS_ERROR_TIMEOUT)
- return NMBS_ERROR_NONE;
- return err;
- }
- #ifdef NMBS_DEBUG
- printf("%d ", nmbs->address_rtu);
- printf("NMBS req <- ");
- if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
- if (nmbs->msg.broadcast)
- printf("broadcast\t");
- else
- printf("address_rtu %d\t", nmbs->msg.unit_id);
- }
- #endif
- err = handle_req_fc(nmbs);
- if (err != NMBS_ERROR_NONE && !nmbs_error_is_exception(err)) {
- if (nmbs->platform.transport == NMBS_TRANSPORT_RTU && err != NMBS_ERROR_TIMEOUT && nmbs->msg.ignored) {
- // Flush the remaining data on the line
- nmbs->platform.read(nmbs->msg.buf, sizeof(nmbs->msg.buf), 0, nmbs->platform.arg);
- }
- return err;
- }
- return NMBS_ERROR_NONE;
- }
- void nmbs_set_callbacks_arg(nmbs_t* nmbs, void* arg) {
- nmbs->callbacks.arg = arg;
- }
- #endif
- #ifndef NMBS_CLIENT_DISABLED
- nmbs_error nmbs_client_create(nmbs_t* nmbs, const nmbs_platform_conf* platform_conf) {
- return nmbs_create(nmbs, platform_conf);
- }
- static nmbs_error read_discrete(nmbs_t* nmbs, uint8_t fc, uint16_t address, uint16_t quantity, nmbs_bitfield values) {
- if (quantity < 1 || quantity > 2000)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
- return NMBS_ERROR_INVALID_ARGUMENT;
- msg_state_req(nmbs, fc);
- put_req_header(nmbs, 4);
- put_2(nmbs, address);
- put_2(nmbs, quantity);
- NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- return recv_read_discrete_res(nmbs, values);
- }
- nmbs_error nmbs_read_coils(nmbs_t* nmbs, uint16_t address, uint16_t quantity, nmbs_bitfield coils_out) {
- return read_discrete(nmbs, 1, address, quantity, coils_out);
- }
- nmbs_error nmbs_read_discrete_inputs(nmbs_t* nmbs, uint16_t address, uint16_t quantity, nmbs_bitfield inputs_out) {
- return read_discrete(nmbs, 2, address, quantity, inputs_out);
- }
- static nmbs_error read_registers(nmbs_t* nmbs, uint8_t fc, uint16_t address, uint16_t quantity, uint16_t* registers) {
- if (quantity < 1 || quantity > 125)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
- return NMBS_ERROR_INVALID_ARGUMENT;
- msg_state_req(nmbs, fc);
- put_req_header(nmbs, 4);
- put_2(nmbs, address);
- put_2(nmbs, quantity);
- NMBS_DEBUG_PRINT("a %d\tq %d ", address, quantity);
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- return recv_read_registers_res(nmbs, quantity, registers);
- }
- nmbs_error nmbs_read_holding_registers(nmbs_t* nmbs, uint16_t address, uint16_t quantity, uint16_t* registers_out) {
- return read_registers(nmbs, 3, address, quantity, registers_out);
- }
- nmbs_error nmbs_read_input_registers(nmbs_t* nmbs, uint16_t address, uint16_t quantity, uint16_t* registers_out) {
- return read_registers(nmbs, 4, address, quantity, registers_out);
- }
- nmbs_error nmbs_write_single_coil(nmbs_t* nmbs, uint16_t address, bool value) {
- msg_state_req(nmbs, 5);
- put_req_header(nmbs, 4);
- uint16_t value_req = value ? 0xFF00 : 0;
- put_2(nmbs, address);
- put_2(nmbs, value_req);
- NMBS_DEBUG_PRINT("a %d\tvalue %d ", address, value_req);
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.broadcast)
- return recv_write_single_coil_res(nmbs, address, value_req);
- return NMBS_ERROR_NONE;
- }
- nmbs_error nmbs_write_single_register(nmbs_t* nmbs, uint16_t address, uint16_t value) {
- msg_state_req(nmbs, 6);
- put_req_header(nmbs, 4);
- put_2(nmbs, address);
- put_2(nmbs, value);
- NMBS_DEBUG_PRINT("a %d\tvalue %d", address, value);
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.broadcast)
- return recv_write_single_register_res(nmbs, address, value);
- return NMBS_ERROR_NONE;
- }
- nmbs_error nmbs_write_multiple_coils(nmbs_t* nmbs, uint16_t address, uint16_t quantity, const nmbs_bitfield coils) {
- if (quantity < 1 || quantity > 0x07B0)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
- return NMBS_ERROR_INVALID_ARGUMENT;
- uint8_t coils_bytes = (quantity + 7) / 8;
- msg_state_req(nmbs, 15);
- put_req_header(nmbs, 5 + coils_bytes);
- put_2(nmbs, address);
- put_2(nmbs, quantity);
- put_1(nmbs, coils_bytes);
- NMBS_DEBUG_PRINT("a %d\tq %d\tb %d\t", address, quantity, coils_bytes);
- NMBS_DEBUG_PRINT("coils ");
- for (int i = 0; i < coils_bytes; i++) {
- put_1(nmbs, coils[i]);
- NMBS_DEBUG_PRINT("%d ", coils[i]);
- }
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.broadcast)
- return recv_write_multiple_coils_res(nmbs, address, quantity);
- return NMBS_ERROR_NONE;
- }
- nmbs_error nmbs_write_multiple_registers(nmbs_t* nmbs, uint16_t address, uint16_t quantity, const uint16_t* registers) {
- if (quantity < 1 || quantity > 0x007B)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
- return NMBS_ERROR_INVALID_ARGUMENT;
- uint8_t registers_bytes = quantity * 2;
- msg_state_req(nmbs, 16);
- put_req_header(nmbs, 5 + registers_bytes);
- put_2(nmbs, address);
- put_2(nmbs, quantity);
- put_1(nmbs, registers_bytes);
- NMBS_DEBUG_PRINT("a %d\tq %d\tb %d\t", address, quantity, registers_bytes);
- NMBS_DEBUG_PRINT("regs ");
- for (int i = 0; i < quantity; i++) {
- put_2(nmbs, registers[i]);
- NMBS_DEBUG_PRINT("%d ", registers[i]);
- }
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.broadcast)
- return recv_write_single_register_res(nmbs, address, quantity);
- return NMBS_ERROR_NONE;
- }
- nmbs_error nmbs_read_file_record(nmbs_t* nmbs, uint16_t file_number, uint16_t record_number, uint16_t* registers,
- uint16_t count) {
- if (file_number == 0x0000)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if (record_number > 0x270F)
- return NMBS_ERROR_INVALID_ARGUMENT;
- // In expected response: max PDU length = 253, assuming a single file request, (253 - 1 - 1 - 1 - 1) / 2 = 124
- if (count > 124)
- return NMBS_ERROR_INVALID_ARGUMENT;
- msg_state_req(nmbs, 20);
- put_req_header(nmbs, 8);
- put_1(nmbs, 7); // add Byte Count
- put_1(nmbs, 6); // add Reference Type const
- put_2(nmbs, file_number);
- put_2(nmbs, record_number);
- put_2(nmbs, count);
- NMBS_DEBUG_PRINT("a %d\tr %d\tl %d\t fread ", file_number, record_number, count);
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- return recv_read_file_record_res(nmbs, registers, count);
- }
- nmbs_error nmbs_write_file_record(nmbs_t* nmbs, uint16_t file_number, uint16_t record_number, const uint16_t* registers,
- uint16_t count) {
- if (file_number == 0x0000)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if (record_number > 0x270F)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if (count > 122)
- return NMBS_ERROR_INVALID_ARGUMENT;
- uint16_t data_size = count * 2;
- msg_state_req(nmbs, 21);
- put_req_header(nmbs, 8 + data_size);
- put_1(nmbs, 7 + data_size); // add Byte Count
- put_1(nmbs, 6); // add Reference Type const
- put_2(nmbs, file_number);
- put_2(nmbs, record_number);
- put_2(nmbs, count);
- put_regs(nmbs, registers, count);
- NMBS_DEBUG_PRINT("a %d\tr %d\tl %d\t fwrite ", file_number, record_number, count);
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.broadcast)
- return recv_write_file_record_res(nmbs, file_number, record_number, registers, count);
- return NMBS_ERROR_NONE;
- }
- nmbs_error nmbs_read_write_registers(nmbs_t* nmbs, uint16_t read_address, uint16_t read_quantity,
- uint16_t* registers_out, uint16_t write_address, uint16_t write_quantity,
- const uint16_t* registers) {
- if (read_quantity < 1 || read_quantity > 0x007D)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if ((uint32_t) read_address + (uint32_t) read_quantity > ((uint32_t) 0xFFFF) + 1)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if (write_quantity < 1 || write_quantity > 0x0079)
- return NMBS_ERROR_INVALID_ARGUMENT;
- if ((uint32_t) write_address + (uint32_t) write_quantity > ((uint32_t) 0xFFFF) + 1)
- return NMBS_ERROR_INVALID_ARGUMENT;
- uint8_t registers_bytes = write_quantity * 2;
- msg_state_req(nmbs, 23);
- put_req_header(nmbs, 9 + registers_bytes);
- put_2(nmbs, read_address);
- put_2(nmbs, read_quantity);
- put_2(nmbs, write_address);
- put_2(nmbs, write_quantity);
- put_1(nmbs, registers_bytes);
- NMBS_DEBUG_PRINT("read a %d\tq %d ", read_address, read_quantity);
- NMBS_DEBUG_PRINT("write a %d\tq %d\tb %d\t", write_address, write_quantity, registers_bytes);
- NMBS_DEBUG_PRINT("regs ");
- for (int i = 0; i < write_quantity; i++) {
- put_2(nmbs, registers[i]);
- NMBS_DEBUG_PRINT("%d ", registers[i]);
- }
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (!nmbs->msg.broadcast) {
- return recv_read_registers_res(nmbs, read_quantity, registers_out);
- }
- return NMBS_ERROR_NONE;
- }
- nmbs_error nmbs_read_device_identification_basic(nmbs_t* nmbs, char* vendor_name, char* product_code,
- char* major_minor_revision, uint8_t buffers_length) {
- const uint8_t order[3] = {0, 1, 2};
- char* buffers[3] = {vendor_name, product_code, major_minor_revision};
- uint8_t total_received = 0;
- uint8_t next_object_id = 0x00;
- while (next_object_id != 0x7F) {
- msg_state_req(nmbs, 43);
- put_msg_header(nmbs, 3);
- put_1(nmbs, 0x0E);
- put_1(nmbs, 1);
- put_1(nmbs, next_object_id);
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t objects_received = 0;
- err = recv_read_device_identification_res(nmbs, 3, buffers, buffers_length, order, NULL, &next_object_id,
- &objects_received);
- if (err != NMBS_ERROR_NONE)
- return err;
- total_received += objects_received;
- if (total_received > 3)
- return NMBS_ERROR_INVALID_RESPONSE;
- if (objects_received == 0)
- return NMBS_ERROR_INVALID_RESPONSE;
- }
- return NMBS_ERROR_NONE;
- }
- nmbs_error nmbs_read_device_identification_regular(nmbs_t* nmbs, char* vendor_url, char* product_name, char* model_name,
- char* user_application_name, uint8_t buffers_length) {
- const uint8_t order[7] = {0, 0, 0, 0, 1, 2, 3};
- char* buffers[4] = {vendor_url, product_name, model_name, user_application_name};
- uint8_t total_received = 0;
- uint8_t next_object_id = 0x03;
- while (next_object_id != 0x7F) {
- msg_state_req(nmbs, 43);
- put_req_header(nmbs, 3);
- put_1(nmbs, 0x0E);
- put_1(nmbs, 2);
- put_1(nmbs, next_object_id);
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t objects_received = 0;
- err = recv_read_device_identification_res(nmbs, 4, buffers, buffers_length, order, NULL, &next_object_id,
- &objects_received);
- if (err != NMBS_ERROR_NONE)
- return err;
- total_received += objects_received;
- if (total_received > 4)
- return NMBS_ERROR_INVALID_RESPONSE;
- if (objects_received == 0)
- return NMBS_ERROR_INVALID_RESPONSE;
- }
- return NMBS_ERROR_NONE;
- }
- nmbs_error nmbs_read_device_identification_extended(nmbs_t* nmbs, uint8_t object_id_start, uint8_t* ids, char** buffers,
- uint8_t ids_length, uint8_t buffer_length,
- uint8_t* objects_count_out) {
- if (object_id_start < 0x80)
- return NMBS_ERROR_INVALID_ARGUMENT;
- uint8_t total_received = 0;
- uint8_t next_object_id = object_id_start;
- while (next_object_id != 0x7F) {
- msg_state_req(nmbs, 43);
- put_req_header(nmbs, 3);
- put_1(nmbs, 0x0E);
- put_1(nmbs, 3);
- put_1(nmbs, next_object_id);
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- uint8_t objects_received = 0;
- err = recv_read_device_identification_res(nmbs, ids_length - total_received, &buffers[total_received],
- buffer_length, NULL, &ids[total_received], &next_object_id,
- &objects_received);
- if (err != NMBS_ERROR_NONE)
- return err;
- total_received += objects_received;
- }
- *objects_count_out = total_received;
- return NMBS_ERROR_NONE;
- }
- nmbs_error nmbs_read_device_identification(nmbs_t* nmbs, uint8_t object_id, char* buffer, uint8_t buffer_length) {
- if (object_id > 0x06 && object_id < 0x80)
- return NMBS_ERROR_INVALID_ARGUMENT;
- msg_state_req(nmbs, 43);
- put_req_header(nmbs, 3);
- put_1(nmbs, 0x0E);
- put_1(nmbs, 4);
- put_1(nmbs, object_id);
- nmbs_error err = send_msg(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- char* buf[1] = {buffer};
- return recv_read_device_identification_res(nmbs, 1, buf, buffer_length, NULL, NULL, NULL, NULL);
- }
- nmbs_error nmbs_send_raw_pdu(nmbs_t* nmbs, uint8_t fc, const uint8_t* data, uint16_t data_len) {
- msg_state_req(nmbs, fc);
- put_msg_header(nmbs, data_len);
- NMBS_DEBUG_PRINT("raw ");
- for (uint16_t i = 0; i < data_len; i++) {
- put_1(nmbs, data[i]);
- NMBS_DEBUG_PRINT("%d ", data[i]);
- }
- return send_msg(nmbs);
- }
- nmbs_error nmbs_receive_raw_pdu_response(nmbs_t* nmbs, uint8_t* data_out, uint8_t data_out_len) {
- nmbs_error err = recv_res_header(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- err = recv(nmbs, data_out_len);
- if (err != NMBS_ERROR_NONE)
- return err;
- if (data_out) {
- for (uint16_t i = 0; i < data_out_len; i++)
- data_out[i] = get_1(nmbs);
- }
- else {
- for (uint16_t i = 0; i < data_out_len; i++)
- get_1(nmbs);
- }
- err = recv_msg_footer(nmbs);
- if (err != NMBS_ERROR_NONE)
- return err;
- return NMBS_ERROR_NONE;
- }
- #endif
- #ifndef NMBS_STRERROR_DISABLED
- const char* nmbs_strerror(nmbs_error error) {
- switch (error) {
- case NMBS_ERROR_INVALID_REQUEST:
- return "invalid request received";
- case NMBS_ERROR_INVALID_UNIT_ID:
- return "invalid unit ID received";
- case NMBS_ERROR_INVALID_TCP_MBAP:
- return "invalid TCP MBAP received";
- case NMBS_ERROR_CRC:
- return "invalid CRC received";
- case NMBS_ERROR_TRANSPORT:
- return "transport error";
- case NMBS_ERROR_TIMEOUT:
- return "timeout";
- case NMBS_ERROR_INVALID_RESPONSE:
- return "invalid response received";
- case NMBS_ERROR_INVALID_ARGUMENT:
- return "invalid argument provided";
- case NMBS_ERROR_NONE:
- return "no error";
- case NMBS_EXCEPTION_ILLEGAL_FUNCTION:
- return "modbus exception 1: illegal function";
- case NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS:
- return "modbus exception 2: illegal data address";
- case NMBS_EXCEPTION_ILLEGAL_DATA_VALUE:
- return "modbus exception 3: data value";
- case NMBS_EXCEPTION_SERVER_DEVICE_FAILURE:
- return "modbus exception 4: server device failure";
- default:
- return "unknown error";
- }
- }
- #endif
|