{"id":47838815,"url":"https://github.com/cloasdata/mbparser","last_synced_at":"2026-04-03T20:35:40.518Z","repository":{"id":158375373,"uuid":"600698207","full_name":"cloasdata/mbparser","owner":"cloasdata","description":"mbparser is a simple but modern C++ library to parse from a modbus RTU master or slave. The Modbus Protocol is handled via finite state machine pattern. This makes it very easy to debug or validate the communication partner. A minimal modbus slave can be implemented in less than 5 lines.","archived":false,"fork":false,"pushed_at":"2025-03-15T18:03:17.000Z","size":68,"stargazers_count":7,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-15T19:19:49.738Z","etag":null,"topics":["arduino","cpp11","esp32","esp8266","modbus"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloasdata.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-12T10:03:15.000Z","updated_at":"2025-03-15T18:03:21.000Z","dependencies_parsed_at":null,"dependency_job_id":"9b9ef02a-b410-48e3-864f-abed1bb260f0","html_url":"https://github.com/cloasdata/mbparser","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cloasdata/mbparser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloasdata%2Fmbparser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloasdata%2Fmbparser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloasdata%2Fmbparser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloasdata%2Fmbparser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloasdata","download_url":"https://codeload.github.com/cloasdata/mbparser/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloasdata%2Fmbparser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31375770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T17:53:18.093Z","status":"ssl_error","status_checked_at":"2026-04-03T17:53:17.617Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","cpp11","esp32","esp8266","modbus"],"created_at":"2026-04-03T20:35:40.425Z","updated_at":"2026-04-03T20:35:40.504Z","avatar_url":"https://github.com/cloasdata.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mbparser\n**mbparser** is a simple but modern C++ library to parse from a modbus RTU master or slave.\nThe Modbus Protocol is handled via finite state machine pattern. This makes it very easy to debug or validate the communication partner. \nA minimal modbus slave can be implemented in less than 5 lines. \n\nmbParser classes are typically wrapped into a modbus client or server (master or slave) implementation, which handles all the hardware and ambient stuff. \n\n## Features\n* Simple and expressive API.\n* Memory Footprint:\n  * Response 164 bytes on stack + payload size on heap.\n  * Request 152 bytes on stack.\n* Supports functions codes: 01, 02, 03, 04, 05, 06, 15, 16\n* Maps modbus responses and requests to C++ interfaces\n* State machine can be polled or\n* Callbacks can be set for on complete and on error events.\n* Can change on fly endianness.\n* Has less than 1.000 loc.\n* Partly test driven development.\n* ModbusParser Base Class can be extended for particular user solutions. For exampling including payload handling on byte level within the state machine\n* Uses old style C++ memory allocation via new to handle non deterministic payload of response frame\n* Header File only library. As most of the code is implemented in a template class, the child classes are also defined in the header. \n\n## Flags\nTo run the package with std functional library i.e. lambdas set compiler flag -D STD_FUNCTIONAL\nThis flag shall not be set when AVR or other non std conforming compilers are used.\n\n## Performance\nProfiling on a ESP8266 with 60 MHz gives a parser throughput of 0.5 - 0.6 megabyte per second. That should be far more than typical a modbus network can achieve through RTU (RS485) or even on TCP/IP.\nProfiling can be found in test section of the source code. \n\n## Disclaimer\n* C++11 \n* Developed on ESP8266 little Endian machine. \n* Uses machine depend unions for byte conversion. \n* May not run with other arduino devices (not tested) or on other machines (not tested).\n* Was original developed to read from a Eastron SDM72D-M Smartmeter. But can be used for any other device to parse its response.\n\n## Installation\nAt the moment the library is distrubted via git or platform.io only.\nTo install it simply add ```seimen/mbparser@0.1.1``` to you platformio.ini or use platformio gui. \n\n## Usage / Example\nBelows example parses a response from a modbus slave on slave id 1.\nUser code should typical transfer (copy) the payload to the desired format/type.\nImportant to know is that parser.payload() is only valid during when parse is complete until user frees \nor new payload is allocated. \n```C++\n    #include \u003cArduino.h\u003e\n    #include \u003cmbparser.h\u003e\n    \n    ResponseParser responseParser{};\n\n    void doRequest(){\n        uint8_t request[8] = {0x01, 0x04, 0x00, 0x00, 0x00, 0x06, 0x70, 0x08};\n        for (int i =0; i \u003c8; i++) Serial.write(request[i]);\n        Serial.flush();\n        delay(100); // until response\n    }\n\n    void setup(){\n        Serial.begin(9600); // slave\n        Serial1.begin(9600); // debug interface\n        doRequest();\n    }\n\n    void loop(){\n\n        ParserState status;\n        // read one token\n        if(Serial.available()){\n            status = responseParser.parse(Serial.read());\n        }\n        if (status == ParserState::complete){\n            uint8_t *payload = responseParser.payload();\n            Serial1.print(\"Payload: \");\n            for(int i=0; i\u003cresponseParser.byteCount(); i++) Serial1.print(payload[i], HEX);\n            Serial1.print(\"\\n\");\n            doRequest();\n        } else if (status == ParserState::error){\n            Serial1.print(\"ERROR: \");\n            Serial1.print(static_cast\u003cint\u003e(responseParser.errorCode()));\n            Serial1.print(\"\\n\");\n            doRequest();\n        }\n\n    }\n\n```\n\nInstead of polling parsers state one could use callbacks to handle the response/request.\nNext example demonstrates a simple modbus slave on id 1. On request complete the slave will send a 174 byte long response to the master. \n\n```C++\n    #include \u003cArduino.h\u003e\n    #include \u003cmbparser.h\u003e\n    \n    RequestParser responseParser{};\n    const uint16_t lenResponse = 174;\n    const uint8_t LongResponse[lenResponse] {\n        0x01, 0x04, 0x50, 0x40, 0x6A, 0x9F, 0xBE, 0x40, 0xF5, 0x4F, 0xDF, 0x41, 0x3A, 0xA7, 0xF0, 0x41, 0x7A, 0xA7, 0xF0, 0x41, 0x9D, 0x53, 0xF8, 0x41, 0xBD, 0x53, 0xF8, 0x41, 0xDD, 0x53, 0xF8, 0x41, 0xFD, 0x53, 0xF8, 0x42, 0x0E, 0xA9, 0xFC, 0x42, 0x1E, 0xA9, 0xFC, 0x42, 0x2E, 0xA9, 0xFC, 0x42, 0x3E, 0xA9, 0xFC, 0x42, 0x4E, 0xA9, 0xFC, 0x42, 0x5E, 0xA9, 0xFC, 0x42, 0x6E, 0xA9, 0xFC, 0x42, 0x7E, 0xA9, 0xFC, 0x42, 0x87, 0x54, 0xFE, 0x42, 0x8F, 0x54, 0xFE, 0x42, 0x97, 0x54, 0xFE, 0x42, 0x9F, 0x54, 0xFE, 0x11, 0x94, 0x01, 0x04, 0x54, 0x40, 0x6A, 0x9F, 0xBE, 0x40, 0xF5, 0x4F, 0xDF, 0x41, 0x3A, 0xA7, 0xF0, 0x41, 0x7A, 0xA7, 0xF0, 0x41, 0x9D, 0x53, 0xF8, 0x41, 0xBD, 0x53, 0xF8, 0x41, 0xDD, 0x53, 0xF8, 0x41, 0xFD, 0x53, 0xF8, 0x42, 0x0E, 0xA9, 0xFC, 0x42, 0x1E, 0xA9, 0xFC, 0x42, 0x2E, 0xA9, 0xFC, 0x42, 0x3E, 0xA9, 0xFC, 0x42, 0x4E, 0xA9, 0xFC, 0x42, 0x5E, 0xA9, 0xFC, 0x42, 0x6E, 0xA9, 0xFC, 0x42, 0x7E, 0xA9, 0xFC, 0x42, 0x87, 0x54, 0xFE, 0x42, 0x8F, 0x54, 0xFE, 0x42, 0x97, 0x54, 0xFE, 0x42, 0x9F, 0x54, 0xFE, 0x42, 0xA7, 0x54, 0xFE, 0x0A, 0xE9\n    };\n\n    void send_response(){\n        for (uint8_t b : LongResponse){\n            Serial.write(b);\n        }\n    }\n\n    void handleRequest(RequestParser *request){\n        if (request-\u003efunctionCode() == 0x04 \u0026\u0026 request-\u003equantity()==40){\n            send_response();\n        } /* else ignore this frame*/\n    }\n\n    void setup(){\n        Serial.begin(9600); // slave\n        Serial1.begin(9600); // debug interface\n        responseParser.setSlaveAddress(1);\n        responseParser.setOnCompleteCB(handleRequest);\n    }\n\n    void loop(){\n        while (Serial.available()){\n            // parse as many as possible\n            responseParser.parse(Serial.read());\n        }\n    }\n```\n\n## Todo\n* Test on Big Endian Machine.\n* Add more bad responses/requests to tests. ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloasdata%2Fmbparser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloasdata%2Fmbparser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloasdata%2Fmbparser/lists"}