{"id":15416982,"url":"https://github.com/gabryelreyes/serialmuxprot","last_synced_at":"2026-01-03T13:34:51.879Z","repository":{"id":183279475,"uuid":"655854771","full_name":"gabryelreyes/SerialMuxProt","owner":"gabryelreyes","description":"Serial Multiplexer Protocol for lightweight communication based on Arduino Streams.","archived":false,"fork":false,"pushed_at":"2024-07-16T09:21:19.000Z","size":107,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-01T23:32:03.070Z","etag":null,"topics":["channel","multiplexer","protocol","serial-communication","stream"],"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/gabryelreyes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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-06-19T18:41:27.000Z","updated_at":"2024-07-16T09:20:57.000Z","dependencies_parsed_at":"2024-07-16T10:04:54.683Z","dependency_job_id":"65196dbe-f92b-40e9-9e22-0a829599b8ae","html_url":"https://github.com/gabryelreyes/SerialMuxProt","commit_stats":{"total_commits":49,"total_committers":3,"mean_commits":"16.333333333333332","dds":0.5102040816326531,"last_synced_commit":"10270033b0eb5e19cae39c35978745a778eb6acc"},"previous_names":["gabryelreyes/serialmuxprot"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabryelreyes%2FSerialMuxProt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabryelreyes%2FSerialMuxProt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabryelreyes%2FSerialMuxProt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabryelreyes%2FSerialMuxProt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gabryelreyes","download_url":"https://codeload.github.com/gabryelreyes/SerialMuxProt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243926666,"owners_count":20370020,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["channel","multiplexer","protocol","serial-communication","stream"],"created_at":"2024-10-01T17:14:20.508Z","updated_at":"2026-01-03T13:34:51.853Z","avatar_url":"https://github.com/gabryelreyes.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Serial Multiplexer Protocol (SerialMuxProt)\r\n\r\nCommunication Protocol based on Streams. Uses Multiplexing to differentiate data channels.\r\nIt is originally being developed for the communication between the [RadonUlzer](https://github.com/BlueAndi/RadonUlzer) and the [DroidControlShip](https://github.com/BlueAndi/DroidControlShip) projects.\r\n\r\n## Table of Contents\r\n\r\n- [Installation](#installation)\r\n- [Network Architecture](#network-architecture)\r\n- [Frame](#frame)\r\n- [Control Channel](#control-channel-channel-0)\r\n  - [SYNC](#sync)\r\n  - [SYNC_RSP](#sync)\r\n  - [SCRB](#scrb)\r\n  - [SCRB_RSP](#scrb)\r\n- [Internal Architecture](#internal-architecture)\r\n- [SerialMuxChannels](#serialmuxchannels)\r\n\r\n---\r\n\r\n## Installation\r\n\r\n- Using PlatformIO CLI:\r\n\r\n```bash\r\npio pkg install --library \"gabryelreyes/SerialMuxProt@^2.0.0\"\r\n```\r\n\r\n- Adding library to `lib_deps` manually:\r\n\r\n```ini\r\nlib_deps =\r\n    gabryelreyes/SerialMuxProt@^2.0.0\r\n```\r\n\r\n### Python Installation\r\n\r\n- Navigate to the root of the Python library:\r\n\r\n```bash\r\ncd python/SerialMuxProt\r\n```\r\n- Install the package using `pip`:\r\n\r\n```bash\r\npip install .\r\n```\r\n\r\n## Network Architecture\r\n\r\n- Server-Client Architecture\r\n- One-to-one. One Server to one client.\r\n\r\n---\r\n\r\n## Frame\r\n\r\nThe Protocol sends and received Frames of the following form:\r\n\r\n```cpp\r\n/** Data container of the Frame Fields */\r\ntypedef union _Frame\r\n{\r\n    /** Frame Fields */\r\n    struct _Fields\r\n    {\r\n        /** Header */\r\n        union _Header\r\n        {\r\n            /** Header Fields Struct */\r\n            struct _HeaderFields\r\n            {\r\n                /** Channel ID */\r\n                uint8_t m_channel;\r\n\r\n                /** Channel DLC */\r\n                uint8_t m_dlc;\r\n\r\n                /** Frame Checksum */\r\n                uint8_t m_checksum;\r\n\r\n            } __attribute__((packed)) headerFields; /**\u003c Header Fields */\r\n\r\n            /** Raw Header Data*/\r\n            uint8_t rawHeader[HEADER_LEN];\r\n\r\n        } __attribute__((packed)) header; /**\u003c Header */\r\n\r\n        /** Payload */\r\n        struct _Payload\r\n        {\r\n            /** Data of the Frame */\r\n            uint8_t m_data[MAX_DATA_LEN];\r\n\r\n        } __attribute__((packed)) payload; /**\u003c Payload */\r\n\r\n    } __attribute__((packed)) fields; /**\u003c Frame Fields */\r\n\r\n    /** Raw Frame Data */\r\n    uint8_t raw[MAX_FRAME_LEN] = {0U};\r\n\r\n} __attribute__((packed)) Frame; /**\u003c Frame */\r\n```\r\n\r\n### Header\r\n\r\n#### Channel Field\r\n\r\n- Length: 1 Byte.\r\n- Channel on which the data is being sent.\r\n- [Channel 0](#control-channel-channel-0) is reserved for the server.\r\n- Channels 1 to 255 are \"Data Channels\".\r\n- The Application can publish or subscribe to any of these channels using the channel's name.\r\n- Client suscribes to a channel using [Channel 0](#control-channel-channel-0).\r\n\r\n#### Data Length Code (DLC) Field\r\n\r\n- Contains the size of the payload contained by the frame.\r\n\r\n#### Checksum Field\r\n\r\n- checksum = sum(Channel + DLC + Data Bytes) % UINT8_MAX\r\n\r\n### Payload Field\r\n\r\n- Data Length: Set by the DLC Field.\r\n- Contains Application Data Bytes.\r\n\r\n---\r\n\r\n## Control Channel (Channel 0)\r\n\r\n- Control Channel: Exchange of Commands. Can not be used to send data.\r\n- Channel 0 has no **external** Callback. The state of the server can be polled by the application through getter functions ( or similar depending on the implementation).\r\n- D0 (Data Byte 0) is used as a Command Byte. Defines the command that is being sent.\r\n- Even-number *Commands* in D0 are used as Commands, while uneven-number *Commands* are used as the response to the immediately previous (n-1) command.\r\n\r\n### SYNC\r\n\r\n- D0 = 0x00\r\n- Server sends SYNC Command with current timestamp.\r\n- Client responds with [SYNC_RSP](#sync).\r\n- Server can calculate Round-Trip-Time.\r\n- SYNC Package must be sent periodically depending on current [State](#state-machine). The period is also used as a timeout for the previous SYNC.\r\n- Used as a \"Heartbeat\" or \"keep-alive\" by the client.\r\n\r\n### SYNC_RSP\r\n\r\n- D0 = 0x01\r\n- Client Response to [SYNC](#sync).\r\n- Data Payload is the same timestamp as in SYNC Command.\r\n\r\n### SCRB\r\n\r\n- D0 = 0x02\r\n- Client sends the name of the channel it wants to suscribe to.\r\n- Server responds with the number and the name of the requested channel, if it is found and valid. If the channel is not found, the response has channel number = 0.\r\n\r\n### SCRB_RSP\r\n\r\n- D0 = 0x03\r\n- Server Response to [SCRB](#scrb).\r\n- Channel Number on Data Byte 1 (D1).\r\n- Channel Name on the following bytes\r\n\r\n---\r\n\r\n## Internal Architecture\r\n\r\n### Data\r\n\r\n- Information is sent directly from application to the Serial Driver. No queueing or buffering.\r\n- The Protocol can send a maximum of 255 Bytes.\r\n\r\n### Channels\r\n\r\n```cpp\r\n/**\r\n * Channel Definition.\r\n */\r\nstruct Channel\r\n{\r\n    char            m_name[CHANNEL_NAME_MAX_LEN]; /**\u003c Name of the channel. */\r\n    uint8_t         m_dlc;                        /**\u003c Payload length of channel */\r\n    ChannelCallback m_callback;                   /**\u003c Callback to provide received data to the application. */\r\n\r\n    /**\r\n     * Channel Constructor.\r\n     */\r\n    Channel() : m_name{0U}, m_dlc(0U), m_callback(nullptr)\r\n    {\r\n    }\r\n};\r\n```\r\n\r\n- Channel has 3 members: Name, DLC, and callback function.\r\n\r\n### Channel Creation and Subscription\r\n\r\n![CreateSubscribeSequence](http://www.plantuml.com/plantuml/proxy?cache=no\u0026src=https://raw.githubusercontent.com/gabryelreyes/SerialMuxProt/main/doc/SubscribeSequence.puml)\r\n\r\n#### Channel Creation\r\n\r\n- Application initializes a channel with a name and a DLC, protocol looks for a free channel number and returns its channel number to the application.\r\n- If no channel is free, it returns 0 as it is an invalid Data Channel.\r\n\r\n#### Channel Subscription\r\n\r\n- Application can subscribe to a remote data channel by its name and a callback to the function that must be called when data is received in said channel.\r\n- Function has no return value, as the response from the server is asynchron.\r\n\r\n### Callback\r\n\r\n```cpp\r\n/**\r\n * Channel Notification Prototype Callback.\r\n * Provides the received data in the respective channel to the application.\r\n *\r\n * @param[in] payload       Received data.\r\n * @param[in] payloadSize   Size of the received data.\r\n * @param[in] userData      User data provided by the application.\r\n */\r\ntypedef void (*ChannelCallback)(const uint8_t* payload, const uint8_t payloadSize, void* userData);\r\n```\r\n\r\n- Callback passes only a pointer to the received Buffer. Data must be copied by application.\r\n- Memory is freed by the protocol after the callback is done.\r\n- DLC is passed as payloadSize to the application.\r\n- The `userData` pointer specified in the constructor is passed to the application.\r\n\r\n### State Machine\r\n\r\n#### Out-of-Sync\r\n\r\n- Client is disconnected/does not respond to SYNC Command.\r\n- No external data is sent in this state.\r\n- SYNC Period set to 1 second.\r\n\r\n#### Synced\r\n\r\n- Client is connected and responds to SYNC Commands.\r\n- SYNC Period set to 5 seconds.\r\n\r\n### Event Callbacks\r\n\r\nIt is possible to register `EventCallback` callbacks for the Synced and DeSynced events. These will be called once an event is triggered to notify the user.\r\n\r\n```cpp\r\n/**\r\n * Event Notification Prototype Callback.\r\n * Provides a notification to the application on the event it is registered to.\r\n *\r\n * @param[in] userData  User data provided by the application.\r\n */\r\ntypedef void (*EventCallback)(void* userData);\r\n\r\n/* Called on Sync. */\r\nbool registerOnSyncedCallback(EventCallback callback);\r\n\r\n/* Called on DeSync. */\r\nbool registerOnDeSyncedCallback(EventCallback callback);\r\n```\r\n\r\n---\r\n\r\n## SerialMuxChannels\r\n\r\nThe `SerialMuxChannels.h` file should be used to define the structures and channel information to be shared between two instances of the SerialMuxServer.\r\nThis file defines the Channel Names, DLCs, and the data structures of the payloads.\r\nIt is important to note that the structs must include the `packed` attribute in order to ensure the access to the data correctly.\r\nA sample file can be found in [here](examples/SerialMuxChannels.h).\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgabryelreyes%2Fserialmuxprot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgabryelreyes%2Fserialmuxprot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgabryelreyes%2Fserialmuxprot/lists"}