{"id":20366491,"url":"https://github.com/fortyseveneffects/serde","last_synced_at":"2025-04-12T05:11:07.364Z","repository":{"id":146212792,"uuid":"194557078","full_name":"FortySevenEffects/serde","owner":"FortySevenEffects","description":"Exchange structured data between Arduino boards","archived":false,"fork":false,"pushed_at":"2019-07-05T06:20:55.000Z","size":54,"stargazers_count":10,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-26T00:41:23.513Z","etag":null,"topics":["arduino-library","deserializer","serializer"],"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/FortySevenEffects.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":"2019-06-30T20:14:24.000Z","updated_at":"2025-03-03T20:33:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"03891a06-0ccd-482d-9ed6-79ab531186a1","html_url":"https://github.com/FortySevenEffects/serde","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FortySevenEffects%2Fserde","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FortySevenEffects%2Fserde/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FortySevenEffects%2Fserde/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FortySevenEffects%2Fserde/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FortySevenEffects","download_url":"https://codeload.github.com/FortySevenEffects/serde/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248519545,"owners_count":21117761,"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":["arduino-library","deserializer","serializer"],"created_at":"2024-11-15T00:25:05.190Z","updated_at":"2025-04-12T05:11:07.330Z","avatar_url":"https://github.com/FortySevenEffects.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 📡 Serde\n\n[![MIT License](https://img.shields.io/github/license/FortySevenEffects/serde.svg?color=blue)](https://github.com/FortySevenEffects/serde/blob/master/LICENSE)\n![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/FortySevenEffects/serde.svg?color=blue\u0026label=release)\n[![Travis CI Build](https://img.shields.io/travis/com/FortySevenEffects/serde.svg)](https://travis-ci.com/FortySevenEffects/serde)\n[![Coverage Status](https://coveralls.io/repos/github/FortySevenEffects/serde/badge.svg?branch=master)](https://coveralls.io/github/FortySevenEffects/serde?branch=master)\n\nExchange structured data between Arduino boards.\n\n## Features\n\n- 💪 Cross-device strong data typing\n- 📦 Send / receive either structs or scalar types\n- 🔌 Use any Stream-based interface (HardwareSerial, SoftwareSerial, I2C, SPI...)\n- 🔒 Internal checksum for data integrity verification\n- ↔️ Receive and send different types on the same stream\n  ([example](./examples/DifferentTypesForTXandRX/DifferentTypesForTXandRX.ino))\n- ⚡ Advanced command processing with [Commander](#commander)\n\n## Install\n\nUse the Arduino Library Manager to install the library.\n\n## Usage\n\n1. Define [shared data](#sharing-data) (using a weather station as an example):\n\n```cpp\n// libraries/weather-shared/src/sensor-data.h\n#pragma once\n\nstruct SensorData\n{\n    float temperature;\n    float humidity;\n    float latitude;\n    float longitude;\n    unsigned long time;\n};\n```\n\n2. Send it from one device:\n\n```cpp\n#include \u003cserde.h\u003e\n#include \u003csensor-data.h\u003e\n\nusing SerdeTX = Serde\u003cSensorData\u003e;\n\nvoid setup()\n{\n    Serial1.begin(115200);\n}\n\nvoid loop()\n{\n    SensorData data = getSensorData();\n    data.time = millis();\n    SerdeTX::send(data, Serial1);\n}\n```\n\n3. Receive it on another device:\n\n```cpp\n#include \u003cserde.h\u003e\n#include \u003csensor-data.h\u003e\n\nusing SerdeRX = Serde\u003cSensorData\u003e;\n\n// This is called when new data is available\nvoid recordSensorData(const SensorData\u0026 data)\n{\n    recordWeather(data.temperature, data.humidity);\n    recordPosition(data.latitude, data.longitude);\n    logTime(data.time);\n}\n\nvoid setup()\n{\n    Serial1.begin(115200);\n}\n\nvoid loop()\n{\n    // Pass it the serial port to read from and a callback:\n    SerdeRX::read(Serial1, recordSensorData);\n\n    // Or you can also check manually for incoming data:\n    // SensorData data;\n    // if (SerdeRX::receive(Serial1, data))\n    // {\n    //     recordSensorData(data);\n    // }\n}\n```\n\n## Sharing data\n\nThis library requires both emitter and receiver to use the same data types.\n\nCopying the definitions back and forth is cumbersome and will lead to errors,\nso sharing the type definitions between sketches is essential.\n\nThe best way to share files between sketches is to create a library.\n\n1. Go to the Arduino libraries directory:\n\n- `~/Documents/Arduino/libraries` on macOS and Linux\n- `~\\Documents\\Arduino\\libraries` on Windows\n\n2. Create a new directory, name it as you wish, for example: `weather-shared`\n3. Create a subdirectory `src` in `weather-shared`\n4. Place your definitions in a header file in `src`, like `weather-shared/src/sensor-data.h`\n5. Create a `library.properties` file in `weather-shared`:\n\n```ini\nname=WeatherShared      # This name is only used for published libraries\nincludes=sensor-data.h  # The name of the file where you placed your definitions\n\n# The rest is required by Arduino:\nversion=0.0.1\nauthor=\nmaintainer=\nsentence=\nparagraph=\ncategory=Uncategorized\narchitectures=*\n```\n\nYou can now include your definitions as such:\n\n```cpp\n#include \u003cserde.h\u003e\n#include \u003csensor-data.h\u003e\n\nusing SerdeTX = Serde\u003cSensorData\u003e;\n```\n\n## Caveats\n\n- Both sending and receiving devices must have the same\n  [endianness](https://en.wikipedia.org/wiki/Endianness).\n- You can only exchange [Plain Old Data (POD)](https://stackoverflow.com/questions/146452/what-are-pod-types-in-c)\n  objects, nothing allocated or which size is unknown at compile time.\n\n## Sending / Receiving strings\n\nBecause strings are of an arbitrary, runtime-defined length, you will have\nto send a `char` buffer of a fixed length, capable of containing the\nlargest string you need (size it appropriately, as all messages will be\nthis big), plus one byte for\n[null-termination](https://en.wikipedia.org/wiki/Null-terminated_string).\n\nTo send a string:\n\n```cpp\nstruct Message\n{\n    // Can hold at maximum 31 characters + 1 null terminator\n    char text[32] = { 0 };\n};\n\nMessage message;\nmemset(message.text, 0, sizeof(Message::text));  // clear\nmemcpy(message.text, \"Hello, World !\", 14);      // copy\n\nSerde\u003cMessage\u003e::send(message, Serial);\n```\n\nThere is nothing particular to do after reception, just use it:\n\n```cpp\nMessage message;\nif (Serde\u003cMessage\u003e::receive(Serial1, message))\n{\n    Serial.println(message.text);\n}\n```\n\n## Threading considerations\n\n- When calling `receive`, you can be sure that the object passed will\n  never have been changed if `receive` returns false.\n- Since Arduino is single-threaded, there should be no race conditions\n  when `receive` returns true, however interrupts can be seen as threads,\n  so if using the received value in an interrupt handler, be sure to know\n  that it could be in the middle of an update.\n\n## Commander\n\nCommander is a [Remote Procedure Call](https://en.wikipedia.org/wiki/Remote_procedure_call)\nlayer built on top of Serde to handle multiple types of messages\n(commands) being sent and received, with different attributes:\n\n```cpp\n#include \u003cserde-commander.h\u003e\n\n// 1. Define a structure for each of\n// the commands you want to receive:\nstruct SayHello\n{\n    char name[32];\n};\n\nstruct SetPinState\n{\n    byte pinNumber;\n    bool state;\n};\n\n// 2. Create a CommanderRX interface and\n// list the commands it will handle:\nSERDE_COMMANDER_CREATE_RX(CommanderRX,\n    SayHello,\n    SetPinState\n);\n\n// 3. Create a function for each command\n// with the following signature (required):\n// void on{CommandName}Received(const CommandName\u0026)\n// It will be called automatically when the\n// corresponding command is received.\nvoid onSayHelloReceived(const SayHello\u0026 data)\n{\n    Serial.print(\"Hello, \");\n    Serial.println(data.name);\n}\n\nvoid onSetPinStateReceived(const SetPinState\u0026 data)\n{\n    digitalWrite(data.pinNumber, data.state ? HIGH : LOW);\n}\n\n// --\n\nvoid setup()\n{\n    Serial.begin(115200);\n    Serial1.begin(115200);\n}\n\nvoid loop()\n{\n    // Just pass it where to read from:\n    CommanderRX::read(Serial1);\n}\n```\n\nTo send commands:\n\n```cpp\n#include \u003cserde-commander.h\u003e\n\n// Those structs would be defined in a shared file\nstruct SayHello\n{\n    char name[32];\n};\n\nstruct SetPinState\n{\n    byte pinNumber;\n    bool state;\n};\n\n// 2. Create a CommanderTX interface and\n// list the commands it may send:\nSERDE_COMMANDER_CREATE_TX(CommanderTX,\n    SayHello,\n    SetPinState\n);\n\n// No need to define callbacks for Commander TX\n// They are only required if you want to receive\n// commands (RX-only or bidirectional).\n\n// --\n\nvoid setup()\n{\n    Serial1.begin(115200);\n}\n\nvoid loop()\n{\n    // Create commands and send them:\n    SayHello hello;\n    memset(hello.name, 0, sizeof(SayHello::name));\n    memcpy(hello.name, \"Commander\", 9);\n    CommanderTX::send(hello, Serial1);\n\n    SetPinState pinState;\n    pinState.pinNumber = 13;\n    pinState.state = true;\n    CommanderTX::send(pinState, Serial1);\n    delay(100);\n    pinState.state = false;\n    CommanderTX::send(pinState, Serial1);\n    delay(100);\n}\n```\n\nThere are 3 Commander creation macros:\n\n- `SERDE_COMMANDER_CREATE_TX`: Send-only (no need to implement the callbacks\n  for this one).\n- `SERDE_COMMANDER_CREATE_RX`: Receive-only (callbacks required)\n- `SERDE_COMMANDER_CREATE`: Both send and receive the same commands\n  (callbacks required)\n\nCheckout the Commander examples for more details:\n\n- [Robot](./examples/CommanderRobot/CommanderRobot.ino)\n- [Calculator](./examples/CommanderCalculator/CommanderCalculator.ino)\n- [Remote IO](./examples/CommanderRemoteIO/CommanderRemoteIO.ino)\n\n### Commander caveats and limitations\n\nCommander currently supports up to 8 commands. If you need more, please\nopen a PR on\n[`src/serde-macros.h`](./src/serde-macros.h).\n\nAlso please note that a Commander message will not be compatible with a\nplain Serde message (and vice-versa).\n\nYou can have one side of the communication handled by Commander, and the\n\"reply\" handled by Serde (as done in the\n[CommanderCalculator](./examples/CommanderCalculator/CommanderCalculator.ino)\nexample), as long as both devices use the right types:\n\n```\n   Device A      Device B\n-----------      -----------\nCommanderTX  -\u003e  CommanderRX\n    SerdeRX  \u003c-  SerdeTX\n```\n\nBut these connections won't work:\n\n```\nCommanderTX  -\u003e  SerdeRX\n             or\n    SerdeTX  -\u003e  CommanderRX\n```\n\n## License \u0026 Aknowledgements\n\nInspired from Rust's awesome crate\n[serde](https://crates.io/crates/serde). 🦀\n\n[MIT](https://github.com/47ng/typescript-library-starter/blob/master/LICENSE) - Made with ❤️ by [François Best](https://francoisbest.com).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffortyseveneffects%2Fserde","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffortyseveneffects%2Fserde","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffortyseveneffects%2Fserde/lists"}