{"id":13812494,"url":"https://github.com/MightyPork/TinyFrame","last_synced_at":"2025-05-14T22:30:42.645Z","repository":{"id":19941275,"uuid":"85873467","full_name":"MightyPork/TinyFrame","owner":"MightyPork","description":"A simple library for building and parsing data frames for serial interfaces (like UART / RS232)","archived":false,"fork":false,"pushed_at":"2022-08-26T20:45:54.000Z","size":148,"stargazers_count":345,"open_issues_count":9,"forks_count":111,"subscribers_count":21,"default_branch":"master","last_synced_at":"2024-11-19T07:38:50.448Z","etag":null,"topics":["arduino","embedded","esp8266","protocol","stm32","uart"],"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/MightyPork.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}},"created_at":"2017-03-22T20:34:05.000Z","updated_at":"2024-11-18T04:53:18.000Z","dependencies_parsed_at":"2022-09-04T22:01:37.452Z","dependency_job_id":null,"html_url":"https://github.com/MightyPork/TinyFrame","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MightyPork%2FTinyFrame","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MightyPork%2FTinyFrame/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MightyPork%2FTinyFrame/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MightyPork%2FTinyFrame/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MightyPork","download_url":"https://codeload.github.com/MightyPork/TinyFrame/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254239447,"owners_count":22037713,"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","embedded","esp8266","protocol","stm32","uart"],"created_at":"2024-08-04T04:00:52.579Z","updated_at":"2025-05-14T22:30:42.342Z","avatar_url":"https://github.com/MightyPork.png","language":"C","readme":"# TinyFrame\n\nTinyFrame is a simple library for building and parsing data frames to be sent \nover a serial interface (e.g. UART, telnet, socket). The code is written to build with \n`--std=gnu99` and mostly compatible with `--std=gnu89`.\n\nThe library provides a high level interface for passing messages between the two peers.\nMulti-message sessions, response listeners, checksums, timeouts are all handled by the library.\n\nTinyFrame is suitable for a wide range of applications, including inter-microcontroller \ncommunication, as a protocol for FTDI-based PC applications or for messaging through\nUDP packets.\n\nThe library lets you register listeners (callback functions) to wait for (1) any frame, (2)\na particular frame Type, or (3) a specific message ID. This high-level API is general \nenough to implement most communication patterns.\n\nTinyFrame is re-entrant and supports creating multiple instances with the limitation\nthat their structure (field sizes and checksum type) is the same. There is a support\nfor adding multi-threaded access to a shared instance using a mutex.\n\nTinyFrame also comes with (optional) helper functions for building and parsing message\npayloads, those are provided in the `utils/` folder.\n\n## Ports\n\nTinyFrame has been ported to mutiple languages:\n\n- The reference C implementation is in this repo\n- Python port - [MightyPork/PonyFrame](https://github.com/MightyPork/PonyFrame)\n- Rust port - [cpsdqs/tinyframe-rs](https://github.com/cpsdqs/tinyframe-rs)\n- JavaScript port - [cpsdqs/tinyframe-js](https://github.com/cpsdqs/tinyframe-js)\n\nPlease note most of the ports are experimental and may exhibit various bugs or missing \nfeatures. Testers are welcome :)\n\n## Functional overview\n\nThe basic functionality of TinyFrame is explained here. For particlars, such as the\nAPI functions, it's recommended to read the doc comments in the header file.\n\n### Structure of a frame\n\nEach frame consists of a header and a payload. Both parts can be protected by a checksum, \nensuring a frame with a malformed header (e.g. with a corrupted length field) or a corrupted\npayload is rejected.\n\nThe frame header contains a frame ID and a message type. Frame ID is incremented with each\nnew message. The highest bit of the ID field is fixed to 1 and 0 for the two peers, \navoiding a conflict.\n\nFrame ID can be re-used in a response to tie the two messages together. Values of the\ntype field are user defined.\n\nAll fields in the frame have a configurable size. By changing a field in the config \nfile, such as `TF_LEN_BYTES` (1, 2 or 4), the library seamlessly switches between `uint8_t`,\n`uint16_t` and `uint32_t` for all functions working with the field. \n\n```\n,-----+-----+-----+------+------------+- - - -+-------------,\n| SOF | ID  | LEN | TYPE | HEAD_CKSUM | DATA  | DATA_CKSUM  |\n| 0-1 | 1-4 | 1-4 | 1-4  | 0-4        | ...   | 0-4         | \u003c- size (bytes)\n'-----+-----+-----+------+------------+- - - -+-------------'\n\nSOF ......... start of frame, usually 0x01 (optional, configurable)\nID  ......... the frame ID (MSb is the peer bit)\nLEN ......... number of data bytes in the frame\nTYPE ........ message type (used to run Type Listeners, pick any values you like)\nHEAD_CKSUM .. header checksum\n\nDATA ........ LEN bytes of data\nDATA_CKSUM .. data checksum (left out if LEN is 0)\n```\n\n### Message listeners\n\nTinyFrame is based on the concept of message listeners. A listener is a callback function \nwaiting for a particular message Type or ID to be received.\n\nThere are 3 listener types, in the order of precedence:\n \n- **ID listeners** - waiting for a response\n- **Type listeners** - waiting for a message of the given Type field\n- **Generic listeners** - fallback\n\nID listeners can be registered automatically when sending a message. All listeners can \nalso be registered and removed manually. \n\nID listeners are used to receive the response to a request. When registerign an ID \nlistener, it's possible to attach custom user data to it that will be made available to \nthe listener callback. This data (`void *`) can be any kind of application context \nvariable.\n\nID listeners can be assigned a timeout. When a listener expires, before it's removed,\nthe callback is fired with NULL payload data in order to let the user `free()` any\nattached userdata. This happens only if the userdata is not NULL.\n\nListener callbacks return values of the `TF_Result` enum:\n\n- `TF_CLOSE` - message accepted, remove the listener\n- `TF_STAY` - message accepted, stay registered\n- `TF_RENEW` - sameas `TF_STAY`, but the ID listener's timeout is renewed\n- `TF_NEXT` - message NOT accepted, keep the listener and pass the message to the next \n              listener capable of handling it.\n\n### Data buffers, multi-part frames\n\nTinyFrame uses two data buffers: a small transmit buffer and a larger receive buffer.\nThe transmit buffer is used to prepare bytes to send, either all at once, or in a \ncircular fashion if the buffer is not large enough. The buffer must only contain the entire \nframe header, so e.g. 32 bytes should be sufficient for short messages.\n\nUsing the `*_Multipart()` sending functions, it's further possible to split the frame \nheader and payload to multiple function calls, allowing the applciation to e.g. generate\nthe payload on-the-fly.\n\nIn contrast to the transmit buffer, the receive buffer must be large enough to contain \nan entire frame. This is because the final checksum must be verified before the frame \nis handled.\n \nIf frames larger than the possible receive buffer size are required (e.g. in embedded \nsystems with small RAM), it's recommended to implement a multi-message transport mechanism\nat a higher level and send the data in chunks.\n\n## Usage Hints\n\n- All TinyFrame functions, typedefs and macros start with the `TF_` prefix.\n- Both peers must include the library with the same config parameters\n- See `TF_Integration.example.c` and `TF_Config.example.c` for reference how to configure and integrate the library.\n- DO NOT modify the library files, if possible. This makes it easy to upgrade.\n- Start by calling `TF_Init()` with `TF_MASTER` or `TF_SLAVE` as the argument. This creates a handle.\n  Use `TF_InitStatic()` to avoid the use of malloc(). \n- If multiple instances are used, you can tag them using the `tf.userdata` / `tf.usertag` field.\n- Implement `TF_WriteImpl()` - declared at the bottom of the header file as `extern`.\n  This function is used by `TF_Send()` and others to write bytes to your UART (or other physical layer).\n  A frame can be sent in it's entirety, or in multiple parts, depending on its size.\n- Use TF_AcceptChar(tf, byte) to give read data to TF. TF_Accept(tf, bytes, count) will accept mulitple bytes.  \n- If you wish to use timeouts, periodically call `TF_Tick()`. The calling period determines \n  the length of 1 tick. This is used to time-out the parser in case it gets stuck \n  in a bad state (such as receiving a partial frame) and can also time-out ID listeners.\n- Bind Type or Generic listeners using `TF_AddTypeListener()` or `TF_AddGenericListener()`.\n- Send a message using `TF_Send()`, `TF_Query()`, `TF_SendSimple()`, `TF_QuerySimple()`.\n  Query functions take a listener callback (function pointer) that will be added as \n  an ID listener and wait for a response.\n- Use the `*_Multipart()` variant of the above sending functions for payloads generated in\n  multiple function calls. The payload is sent afterwards by calling `TF_Multipart_Payload()`\n  and the frame is closed by `TF_Multipart_Close()`.\n- If custom checksum implementation is needed, select `TF_CKSUM_CUSTOM8`, 16 or 32 and \n  implement the three checksum functions.\n- To reply to a message (when your listener gets called), use `TF_Respond()`\n  with the msg object you received, replacing the `data` pointer (and `len`) with a response.\n- At any time you can manually reset the message parser using `TF_ResetParser()`. It can also \n  be reset automatically after a timeout configured in the config file.\n\n### Gotchas to look out for\n\n- If any userdata is attached to an ID listener with a timeout, when the listener times out,\n  it will be called with NULL `msg-\u003edata` to let the user free the userdata. Therefore \n  it's needed to check `msg-\u003edata` before proceeding to handle the message.\n- If a multi-part frame is being sent, the Tx part of the library is locked to prevent \n  concurrent access. The frame must be fully sent and closed before attempting to send\n  anything else. \n- If multiple threads are used, don't forget to implement the mutex callbacks to avoid \n  concurrent access to the Tx functions. The default implementation is not entirely thread\n  safe, as it can't rely on platform-specific resources like mutexes or atomic access. \n  Set `TF_USE_MUTEX` to `1` in the config file.\n\n### Examples\n\nYou'll find various examples in the `demo/` folder. Each example has it's own Makefile,\nread it to see what options are available.\n\nThe demos are written for Linux, some using sockets and `clone()` for background processing.\nThey try to simulate real TinyFrame behavior in an embedded system with asynchronous \nRx and Tx. If you can't run the demos, the source files are still good as examples.\n","funding_links":[],"categories":["Protocols"],"sub_categories":["Flash Memory"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMightyPork%2FTinyFrame","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMightyPork%2FTinyFrame","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMightyPork%2FTinyFrame/lists"}