{"id":21274164,"url":"https://github.com/endurodave/simplesocketprotocol","last_synced_at":"2025-07-15T16:09:45.609Z","repository":{"id":113549701,"uuid":"451167785","full_name":"endurodave/SimpleSocketProtocol","owner":"endurodave","description":"Simple Socket Protocol for Embedded Systems","archived":false,"fork":false,"pushed_at":"2025-07-02T14:16:24.000Z","size":274,"stargazers_count":8,"open_issues_count":1,"forks_count":6,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-11T08:48:43.689Z","etag":null,"topics":["c-language","communication-protocol","cross-platform","embedded-systems","linux","serial-communication","sockets","transport-protocol","windows"],"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/endurodave.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,"zenodo":null}},"created_at":"2022-01-23T16:47:29.000Z","updated_at":"2025-07-02T14:16:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"5ba0cf9a-2ac7-4daa-b3bf-bf620dd7ba7b","html_url":"https://github.com/endurodave/SimpleSocketProtocol","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/endurodave/SimpleSocketProtocol","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FSimpleSocketProtocol","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FSimpleSocketProtocol/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FSimpleSocketProtocol/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FSimpleSocketProtocol/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/endurodave","download_url":"https://codeload.github.com/endurodave/SimpleSocketProtocol/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2FSimpleSocketProtocol/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265444798,"owners_count":23766436,"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":["c-language","communication-protocol","cross-platform","embedded-systems","linux","serial-communication","sockets","transport-protocol","windows"],"created_at":"2024-11-21T09:19:12.030Z","updated_at":"2025-07-15T16:09:45.583Z","avatar_url":"https://github.com/endurodave.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"![License MIT](https://img.shields.io/github/license/BehaviorTree/BehaviorTree.CPP?color=blue)\n[![conan Ubuntu](https://github.com/endurodave/SimpleSocketProtocol/actions/workflows/cmake_ubuntu.yml/badge.svg)](https://github.com/endurodave/SimpleSocketProtocol/actions/workflows/cmake_ubuntu.yml)\n[![conan Ubuntu](https://github.com/endurodave/SimpleSocketProtocol/actions/workflows/cmake_clang.yml/badge.svg)](https://github.com/endurodave/SimpleSocketProtocol/actions/workflows/cmake_clang.yml)\n[![conan Windows](https://github.com/endurodave/SimpleSocketProtocol/actions/workflows/cmake_windows.yml/badge.svg)](https://github.com/endurodave/SimpleSocketProtocol/actions/workflows/cmake_windows.yml)\n\n# Simple Socket Protocol for Embedded Systems\n\nA simple C language socket-based communication transport protocol ported to Windows, Linux, Arduino and embedded systems. Any hardware transport is supported such as serial UART, SPI, CAN bus, etc... \n\nOriginally published on CodeProject at: \u003ca href=\"https://www.codeproject.com/Articles/5321271/Simple-Socket-Protocol-for-Embedded-Systems\"\u003e\u003cstrong\u003eSimple Socket Protocol for Embedded Systems\u003c/strong\u003e\u003c/a\u003e\n\nDavid Lafreniere, Jan 2022.\n\n# Table of Contents\n\n- [Simple Socket Protocol for Embedded Systems](#simple-socket-protocol-for-embedded-systems)\n- [Table of Contents](#table-of-contents)\n- [Introduction](#introduction)\n  - [What is SSP?](#what-is-ssp)\n- [Project Build](#project-build)\n- [Overview](#overview)\n- [Using the Code](#using-the-code)\n  - [Usage Notes](#usage-notes)\n    - [Socket Types](#socket-types)\n    - [Streaming](#streaming)\n    - [Message Throttling](#message-throttling)\n    - [Communication Down](#communication-down)\n  - [Configuration](#configuration)\n  - [Porting](#porting)\n- [Layers](#layers)\n  - [SSP Layers](#ssp-layers)\n    - [SSPCOM](#sspcom)\n    - [SSP](#ssp)\n- [Simple Socket Protocol (SSP)](#simple-socket-protocol-ssp)\n  - [Sockets](#sockets)\n  - [Packets](#packets)\n  - [Packet Types](#packet-types)\n    - [Data Packet](#data-packet)\n    - [ACK Packet](#ack-packet)\n    - [NAK Packet](#nak-packet)\n  - [Parsing](#parsing)\n  - [Queuing](#queuing)\n  - [Sequence Control](#sequence-control)\n  - [Duplicate Packets](#duplicate-packets)\n  - [Timeouts and Retries](#timeouts-and-retries)\n  - [Flow Control](#flow-control)\n  - [Sending Packets](#sending-packets)\n  - [Receiving Packets](#receiving-packets)\n  - [Error Handling](#error-handling)\n- [Porting](#porting-1)\n  - [Arduino](#arduino)\n- [Serialize](#serialize)\n- [Resources](#resources)\n- [Referenced Articles](#referenced-articles)\n- [Conclusion](#conclusion)\n\n\n# Introduction\n\n\u003cp\u003eSoftware transport communication protocols are difficult to implement. Embedded systems use a variety of hardware communication interfaces: UART, CAN bus, SPI, Bluetooth LE, etc\u0026hellip; In my experience, a custom-designed software transport protocol is usually implemented between the hardware driver and the application code. These one-off protocols are sometimes fragile, difficult to use, with minimal features. Maybe error checking is implemented, or retries, or acknowledgements, or thread-safety; or not.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eCreating a portable library capable of supporting the smallest embedded systems, with or without an operating system, is challenging. The Simple Socket Protocol (SSP) implementation presented here is a platform agnostic transport protocol supporting a socket-like interface. The goal is a reusable library for embedded systems to communicate over disparate (non-Ethernet) hardware interfaces while abstracting the operating system and hardware platform specifics.\u003c/p\u003e\n\n\u003cp\u003eSSP supports the following features:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003eImplemented in C\u003c/li\u003e\n\t\u003cli\u003eSupports\u0026nbsp;C or C++ application code\u003c/li\u003e\n\t\u003cli\u003eArduino, Eclipse and Visual Studio project included\u003c/li\u003e\n\t\u003cli\u003eSocket-based communication\u003c/li\u003e\n\t\u003cli\u003eCustomizable operating system and hardware abstraction layer interfaces\u003c/li\u003e\n\t\u003cli\u003eThread-safe library\u003c/li\u003e\n\t\u003cli\u003eSupports any physical hardware transport (UART, SPI, CAN, BLE, ...)\u003c/li\u003e\n\t\u003cli\u003eFixed block allocator or global heap for dynamic storage\u003c/li\u003e\n\t\u003cli\u003eDuplicate message prevention\u003c/li\u003e\n\t\u003cli\u003eGuaranteed send message order preserved\u003c/li\u003e\n\t\u003cli\u003eAutomatic retries\u003c/li\u003e\n\t\u003cli\u003eMessage timeouts\u003c/li\u003e\n\t\u003cli\u003eMessage acknowledgements\u003c/li\u003e\n\t\u003cli\u003eMessage corruption detection\u003c/li\u003e\n\t\u003cli\u003eAsynchronous send and receive\u003c/li\u003e\n\t\u003cli\u003eSender callback on send success or failure\u003c/li\u003e\n\t\u003cli\u003eReceiver callback on data received\u003c/li\u003e\n\t\u003cli\u003eConnectionless data send (i.e. not connection-oriented)\u003c/li\u003e\n\t\u003cli\u003ePower savings hooks\u003c/li\u003e\n\t\u003cli\u003eError callbacks and get last error\u003c/li\u003e\n\t\u003cli\u003eAutomatic endianness handling (protocol, not application payload)\u003c/li\u003e\n\t\u003cli\u003eCompact code and memory footprint\u003c/li\u003e\n\t\u003cli\u003eEasy compile-time configuration using a single header file\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eSSP does not support the following:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003eMessage fragmentation\u003c/li\u003e\n\t\u003cli\u003eBlocking (synchronous) send or receive\u003c/li\u003e\n\t\u003cli\u003eDynamic negotiation of communication operational parameters\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eThe source code should build and execute on any C or C++ system. SSP is implemented in C to offer support for most systems. To make evaluation easier, there is a memory buffer build option that allows testing the library without communication hardware.\u003c/p\u003e\n\n\u003cp\u003eI\u0026rsquo;ve used variations of this code on many different projects over 20-years or so, mainly small embedded devices communicating with one another over UART/CAN/SPI, or to a host device over BLE/Serial. If your embedded device is equipped with UART, or other non-Ethernet communication interface, and a simple software transport protocol is required then read on!\u003c/p\u003e\n\n## What is SSP?\n\n\u003cp\u003eLet me further clarify what SSP is and is not.\u003c/p\u003e\n\n\u003cp\u003eA transport layer protocol moves data between two endpoints. TCP/IP is a transport layer protocol, for instance. An application layer protocol makes use of the transport to send application specific data. HTTP is an application layer protocol.\u003c/p\u003e\n\n\u003cp\u003eSSP is a transport layer protocol that moves data between two devices over any hardware communication interface. Two embedded CPU\u0026rsquo;s, an embedded CPU and a PC, or whatever.\u003c/p\u003e\n\n\u003cp\u003eSSP is a point-to-point communication protocol; not a one-to-many publish/subscribe protocol.\u003c/p\u003e\n\n\u003cp\u003eSSP is a peer-to-peer protocol; not a master-slave.\u003c/p\u003e\n\n\u003cp\u003eSSP is used to send binary data, XML, JSON, or whatever the application requires.\u003c/p\u003e\n\n\u003cp\u003eSSP is not intended to implement the Berkeley sockets API. It is not a TCP/IP stack.\u003c/p\u003e\n\n\u003cp\u003eSSP is lightweight at about 2.5k of code ROM and a few hundred bytes of RAM at minimum. The RAM varies depending on the message size and maximum buffers configured.\u003c/p\u003e\n\n\u003cp\u003eSSP was designed to work over UART, CAN, BLE, or any other hardware communication interface. If a CPU supports Ethernet, then of course use a TCP/IP stack on that interface.\u003c/p\u003e\n\n\u003cp\u003eSSP supports a maximum 256-byte packet size. 10-byte header, 244-byte maximum payload, and 2-byte CRC.\u003c/p\u003e\n\n\u003cp\u003eSSP was not designed by committee and does not conform to any standard.\u003c/p\u003e\n\n# Project Build\n\n[CMake](https://cmake.org/) is used to create the project build files on any Windows or Linux machine. \n\n1. Clone the repository.\n2. From the repository root, run the following CMake command:   \n   `cmake -B Build .`\n3. Build and run the project within the `Build` directory. \n\nSee `ssp_opt.h` to configure the sample application build options for each target platform.\n\n# Overview\n\n\u003cp\u003eI\u0026rsquo;ll first present the API, simple SSP usage examples, and then dive into the technical details.\u003c/p\u003e\n\n\u003cp\u003eThe SSP API within \u003cstrong\u003essp.h\u003c/strong\u003e is shown below:\u003c/p\u003e\n\n```cpp\n// Called once per port to initialize\nSspErr SSP_Init(SspPortId portId);\n\n// Called once when application terminates\nvoid SSP_Term(void);\n\n// Open a socket on a specified port\nSspErr SSP_OpenSocket(SspPortId port, UINT8 socketId);\n\n// Close a socket\nSspErr SSP_CloseSocket(UINT8 socketId);\n\n// Send data over a socket\nSspErr SSP_Send(UINT8 srcSocketId, UINT8 destSocketId, const void* data, UINT16 dataSize);\n\n// Send multiple data arrays over a socket\nSspErr SSP_SendMultiple(UINT8 srcSocketId, UINT8 destSocketId, INT16 numData,\n    void const** dataArray, UINT16* dataSizeArray);\n\n// Listen for incoming data on a socket using a callback function\nSspErr SSP_Listen(UINT8 socketId, SspDataCallback callback, void* userData);\n\n// Get number of pending messages in outgoing queue\nUINT16 SSP_GetSendQueueSize(SspPortId portId);\n\n// Determine if the incoming queue has data or not\nBOOL SSP_IsRecvQueueEmpty(SspPortId portId);\n\n// Process outgoing and incoming socket messages\nvoid SSP_Process(void);\n\n// Register for callbacks when SSP error occurs\nvoid SSP_SetErrorHandler(ErrorHandler handler);\n\n// Get the last SSP error\nSspErr SSP_GetLastErr(void);\n```\n\n\u003cp\u003eSend status and receive data is notified by registering a callback function conforming to the \u003ccode\u003eSspDataCallback \u003c/code\u003efunction signature.\u003c/p\u003e\n\n```cpp\n/// SSP callback function signature for receiving socket data asynchronously.\n/// Called upon transmission success or failure. \n/// @param[in] socketId The socket identifier.\n/// @param[in] data The incoming data if type is SSP_RECEIVE. The outgoing data\n///     if type is SSP_SEND.\n/// @param[in] dataSize The number of bytes pointed to by data.\n/// @param[in] type The type of callback: SSP_RECEIVE or SSP_SEND.\n/// @param[in] status The status of the send/receive.\n/// @param[in] userData Optional user data pointer that was provided in \n///     SSP_Listen() callback registration. \ntypedef void(*SspDataCallback)(UINT8 socketId, const void* data, UINT16 dataSize,\n    SspDataType type, SspErr status, void* userData);\n```\n\n# Using the Code\n\n\u003cp\u003eInitialize one or more communication ports.\u003c/p\u003e\n\n```cpp\n// Initialize the ports\nSSP_Init(SSP_PORT1);\n```\n\n\u003cp\u003eOpen a socket on a specified port.\u003c/p\u003e\n\n```cpp\n// Open socket 0\nSspErr err = SSP_OpenSocket(SSP_PORT1, 0);\n```\n\n\u003cp\u003eRegister for callbacks on function \u003ccode\u003eSspCallbackSocket0()\u003c/code\u003e. The third argument is optional user data will be passed back during the callback, or \u003ccode\u003eNULL\u003c/code\u003e.\u003c/p\u003e\n\n```cpp\n// Listen on socket 0\nerr = SSP_Listen(0, \u0026SspCallbackSocket0, NULL);\n```\n\n\u003cp\u003eSend data over a socket. In this example, the source socket is 0 and destination socket is 1.\u003c/p\u003e\n\n```cpp\nchar sendData[32];\nsnprintf(sendData, 32, \"Hello World!\");\n\n// Send data\nerr = SSP_Send(0, 1, sendData, UINT16(strlen(sendData))+1);\n```\n\n\u003cp\u003eHandle callbacks from the SSP library. Use the callback argument to determine if the callback is a send or receive notification, and the error status.\u003c/p\u003e\n\n```cpp\nstatic void SspCallbackSocket0(UINT8 socketId, const void* data, UINT16 dataSize,\n    SspDataType type, SspErr status, void* userData)\n{\n    // Received data callback?\n    if (type == SSP_RECEIVE)\n    {\n        // Received successfully?\n        if (status == SSP_SUCCESS)\n        {\n            // Do something with incoming data\n            SSP_TRACE_FORMAT(\"SSP_RECEIVE PORT1: %s\", (char*)data);\n        }\n    }\n    // Send data callback?\n    else if (type == SSP_SEND)\n    {\n        if (status == SSP_SUCCESS)\n            SSP_TRACE(\"SSP_SEND PORT1 SUCCESS\");\n        else\n            SSP_TRACE(\"SSP_SEND PORT1 FAIL\");\n    }\n}\n```\n\n\u003cp\u003e\u003ccode\u003eSSP_Process()\u003c/code\u003e must be called periodically to send/receive messages.\u003c/p\u003e\n\n```cpp\ndo\n{\n    // Call while there is send or receive data to process\n    SSP_Process();\n} while (!SSP_IsRecvQueueEmpty(SSP_PORT1) ||\n         SSP_GetSendQueueSize(SSP_PORT1) != 0);\n```\n\n\u003cp\u003eWhen to call \u003ccode\u003eSSP_Process()\u003c/code\u003e is application defined. In this example, \u003ccode\u003eSSP_Process()\u003c/code\u003e is called repeatedly until all send or receive data is processed. \u003ccode\u003eSSP_Process()\u003c/code\u003e handles all sending, receiving and timeouts. If multithreaded, call from a single thread of control. \u003ccode\u003eSSP_Listen()\u003c/code\u003e registered callbacks are invoked during \u003ccode\u003eSSP_Process()\u003c/code\u003e. Ideas of when to call \u003ccode\u003eSSP_Process()\u003c/code\u003e.\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003ePeriodically call from the main loop.\u003c/li\u003e\n\t\u003cli\u003ePeriodically call from a task loop.\u003c/li\u003e\n\t\u003cli\u003ePeriodically call using a timer.\u003c/li\u003e\n\t\u003cli\u003eInitiate calling when the hardware driver receives data.\u003c/li\u003e\n\t\u003cli\u003eInitiate calling when the application sends a message.\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eAt application exit call the terminate function and close sockets.\u003c/p\u003e\n\n```cpp\nerr = SSP_CloseSocket(0);\nSSP_Term(); \n```\n\n## Usage Notes\n\n\u003cp\u003eSSP socket communication over an embedded hardware communication port\u0026nbsp;offers convenient design paradigms. Ideas on SSP usage follows.\u003c/p\u003e\n\n### Socket Types\n\n\u003cp\u003eSockets allow categorizing communications between two devices. Here are some ideas of different socket types.\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003eSensor socket \u0026ndash; send periodic sensor data\u003c/li\u003e\n\t\u003cli\u003eAlarm socket \u0026ndash; send alarm notifications\u003c/li\u003e\n\t\u003cli\u003eLog socket \u0026ndash; send log data\u003c/li\u003e\n\t\u003cli\u003eCommand socket \u0026ndash; send device commands\u003c/li\u003e\n\t\u003cli\u003eConfiguration socket \u0026ndash; send configuration data\u003c/li\u003e\n\u003c/ul\u003e\n\n### Streaming\n\n\u003cp\u003eMaybe an embedded device locally accumulates log data and the logs need to be transferred to another device for permanent storage or post-processing. Streaming the data over a log socket is easy. To start the transfer, send the first log message using \u003ccode\u003eSSP_Send()\u003c/code\u003e. During the sender socket callback notification, if \u003ccode\u003eSSP_SEND \u003c/code\u003eis \u003ccode\u003eSSP_SUCCESS\u003c/code\u003e then send the next log message. Keep sending a new log messages on each \u003ccode\u003eSSP_SEND\u003c/code\u003e/\u003ccode\u003eSSP_SUCCESS\u003c/code\u003e\u0026nbsp;callback until all logs are transferred. SSP allows calling \u003ccode\u003eSSP_Send()\u003c/code\u003e during a notification callback. This is an easy way to stream data without overwhelming the available buffers since only one send queue entry is used at a time.\u003c/p\u003e\n\n### Message Throttling\n\n\u003cp\u003eA low priority socket can suspend/slow data transfer if SSP is busy by using \u003ccode\u003eSSP_GetSendQueueSize()\u003c/code\u003e. Let\u0026rsquo;s say we are sending log data using the streaming method above and 10 total send queue buffers exist. Before sending a log message the queue size is checked. If 5 or more pending queue messages, the log data is not sent thus preserving the send queue for more critical messages. A timer periodically checks if the queue usage drops below 5 and calls \u003ccode\u003eSSP_Send()\u003c/code\u003e to continue log streaming. Using the available send queue entries the application best decides how to prioritize message sending.\u003c/p\u003e\n\n### Communication Down\n\n\u003cp\u003eSSP is connectionless, meaning SSP does not negotiate a connection between two devices. The sender opens a port and sends data. If a listener does not respond, a \u003ccode\u003eSSP_SEND_RETRIES_FAILED\u003c/code\u003e error occurs.\u003c/p\u003e\n\n\u003cp\u003eLoss of communication is detected in the \u003ccode\u003eSSP_Listen()\u003c/code\u003e callback if \u003ccode\u003eSSP_SEND \u003c/code\u003eand not \u003ccode\u003eSSP_SUCCESS \u003c/code\u003eis detected. Maybe an existing socket is used for communication loss detected. Or perhaps a heartbeat socket is dedicated to periodically ping the remote to detected communication loss. These details are left to the application.\u003c/p\u003e\n\n## Configuration\n\n\u003cp\u003eAll SSP options are defined within \u003cstrong\u003essp_opt.h\u003c/strong\u003e. Some options are shown below:\u003c/p\u003e\n\n```cpp\n// How long to wait for remote CPU to provide and ACK or NAK\n#define SSP_ACK_TIMEOUT     300    // in mS\n\n// How many times to retry a failed message\n#define SSP_MAX_RETRIES     4\n\n// How long to wait for a incoming character polling com port\n#define SSP_RECV_TIMEOUT    10  // in mS\n\n// Maximum number of outgoing messages per port that can be queued\n#define SSP_MAX_MESSAGES    5\n\n// Maximum packet size including header, body and CRC\n#define SSP_MAX_PACKET_SIZE 64\n\n// Define to output log messages\n#define USE_SSP_TRACE\n\n// Define uses fixed block allocator. Undefine uses malloc/free. \n#define USE_FB_ALLOCATOR\n```\n\n\u003cp\u003eSearch \u003ccode\u003eTODO \u003c/code\u003ewithin the source code to find other application specific code locations.\u003c/p\u003e\n\n\u003cp\u003eThe CRC table or loop based implementation is defined within \u003cstrong\u003essp_crc.c\u003c/strong\u003e. The table-based version is faster, but the loop version consumes less storage.\u003c/p\u003e\n\n## Porting\n\n\u003cp\u003eThe OS abstraction interface (OSAL) is in \u003cstrong\u003essp_osal.h\u003c/strong\u003e. The OSAL provides a critical section, software locks and ticks for timing. For systems without an operating system, lock-related functions below will do nothing.\u003c/p\u003e\n\n```cpp\nvoid SSPOSAL_Init(void);\nvoid SSPOSAL_Term(void);\n\nvoid SSPOSAL_EnterCritical(void);\nvoid SSPOSAL_ExitCritical(void);\n\nSSP_OSAL_HANDLE SSPOSAL_LockCreate(void);\nvoid SSPOSAL_LockDestroy(SSP_OSAL_HANDLE handle);\nBOOL SSPOSAL_LockGet(SSP_OSAL_HANDLE handle, UINT32 timeout);\nBOOL SSPOSAL_LockPut(SSP_OSAL_HANDLE handle);\n\nUINT32 SSPOSAL_GetTickCount(void);\n```\n\n\u003cp\u003eThe hardware abstraction interface (HAL) is in \u003cstrong\u003essp_hal.h\u003c/strong\u003e. HAL is the physical layer. The HAL isolates the SSP library from the details for sending/receiving data over a hardware interface. The HAL may support multiple interface types. For instance, port 1 is a UART and port 2 is SPI.\u003c/p\u003e\n\n```cpp\nvoid SSPHAL_Init(SspPortId portId);\nvoid SSPHAL_Term(void);\n\nBOOL SSPHAL_PortOpen(SspPortId portId);\nvoid SSPHAL_PortClose(SspPortId portId);\nBOOL SSPHAL_PortIsOpen(SspPortId portId);\n\nBOOL SSPHAL_PortSend(SspPortId portId, const char* buf, UINT16 bytesToSend);\nBOOL SSPHAL_PortRecv(SspPortId portId, char* buf, UINT16* bytesRead, UINT16 maxLen, UINT16 timeout);\n\nvoid SSPHAL_PortFlush(SspPortId portId);\nBOOL SSPHAL_IsRecvQueueEmpty(SspPortId portId);\n\nvoid SSPHAL_PowerSave(BOOL enable);\nBOOL SSPHAL_IsPowerSave(void);\n```\n\n\u003cp\u003eEach abstraction interface must be implemented for a specific target. Example implementations for Windows, Linux, Arduino, and C++ standard library are located within the \u003cstrong\u003eport \u003c/strong\u003edirectory.\u003c/p\u003e\n\n\u003cp\u003eThe HAL interfaces to the communication driver. \u003ccode\u003eSSPHAL_PortSend()\u003c/code\u003e sends data and \u003ccode\u003eSSPHAL_PortRecv()\u003c/code\u003e reads data. Typically the driver uses internal send/receive buffers to facilitate data communication. The driver details are application specific. Maybe the driver is interrupt driven sending/receiving one or more bytes per interrupt. Or perhaps DMA transfer is utilized. Regardless, the HAL abstracts those details from the SSP library.\u003c/p\u003e\n\n# Layers\n\n\u003cp\u003eThe layer diagram below shows the major components.\u003c/p\u003e\n\n\u003cp\u003e\u003cimg src=\"SSP_Diagram2.jpg\" style=\"width: 250px; height: 306px\" /\u003e\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003eApplication \u0026ndash; the application code.\u003c/li\u003e\n\t\u003cli\u003eSSL \u0026ndash; the Simple Socket Protocol library.\u003c/li\u003e\n\t\u003cli\u003eOSAL \u0026ndash; the operating system abstraction layer.\u003c/li\u003e\n\t\u003cli\u003eHAL \u0026ndash; the hardware abstraction layer.\u003c/li\u003e\n\t\u003cli\u003eOperating System \u0026ndash; the operating system, if any.\u003c/li\u003e\n\t\u003cli\u003eCommunication Driver \u0026ndash; the software driver for the hardware interface.\u003c/li\u003e\n\u003c/ul\u003e\n\n## SSP Layers\n\n\u003cp\u003eSSP consists of two layers: SSPCOM (\u003cstrong\u003essp_com.c\u003c/strong\u003e) and SSP (\u003cstrong\u003essp.c\u003c/strong\u003e).\u003c/p\u003e\n\n### SSPCOM\n\n\u003cp\u003eThe SSPCOM module sends/receives a single SSP packet. SSPCOM responsibilities include:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003eSend and receive SSP packets\u003c/li\u003e\n\t\u003cli\u003eAssemble SSP packets\u003c/li\u003e\n\t\u003cli\u003eParse incoming data\u003c/li\u003e\n\t\u003cli\u003eError detection\u003c/li\u003e\n\t\u003cli\u003eOpen and close sockets\u003c/li\u003e\n\t\u003cli\u003eSocket to port mapping\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eAn SSP packet is comprised of a packet header, client data, and footer. The header specifies source/destination sockets, message type, transaction ID and the body length among other things. The client data is the application defined payload sent to the remote CPU. The packet footer is a 16-bit CRC used for error detection.\u003c/p\u003e\n\n### SSP\n\n\u003cp\u003eSSP is the client application interface. SSP utilizes SSPCOM services and provides a protocol layer that guarantees packet delivery through message acknowledgement, message retries and timeouts. SSP responsibilities include:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003eSend and receive client data\u003c/li\u003e\n\t\u003cli\u003eSend queue\u003c/li\u003e\n\t\u003cli\u003eTimeouts\u003c/li\u003e\n\t\u003cli\u003eRetries\u003c/li\u003e\n\t\u003cli\u003eThread-safety\u003c/li\u003e\n\t\u003cli\u003eClient callback notification\u003c/li\u003e\n\t\u003cli\u003eError callback notification\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eSSP clients send data to/from a remote system using a socket ID. SSP automatically retries failed transmissions and notifies if the packet ultimately cannot be delivered.\u003c/p\u003e\n\n# Simple Socket Protocol (SSP)\n\n\u003cp\u003eSimple Socket Protocol (SSP) is a software-based, binary transport protocol transmitted over any hardware communication interface. SSP manages multiple hardware ports, sockets, packets, error detection, timeouts and retries.\u003c/p\u003e\n\n\u003cp\u003eSSP provides a common API for software communications. The SSP library is implemented in C. The library is thread-safe.\u003c/p\u003e\n\n## Sockets\n\n\u003cp\u003eAll SSP packets are sent and received through sockets. A SSP socket is an endpoint of a bidirectional inter-process communication over a communication interface. A socket paradigm multiplexes a single hardware interface to facilitate private communication between different subsystems endpoints residing on separate processors. A socket provides the appearance of multiple independent communication channels.\u003c/p\u003e\n\n\u003cp\u003eThe SSP library simultaneously supports multiple hardware communications ports. On a single CPU, sharing sockets across ports is not supported. A socket ID must be unique across all ports, meaning socket ID 1 on two ports is not allowed.\u003c/p\u003e\n\n## Packets\n\n\u003cp\u003eMessages are sent using packets. A packet contains a 10-byte header, the packet body with a variable length, and 2-byte footer.\u003c/p\u003e\n\n\u003cp\u003eThe SSP packet is structured as shown below:\u003c/p\u003e\n\n\u003cp\u003e\u003cimg src=\"SSP_Packet2.jpg\" style=\"width: 400px; height: 359px\" /\u003e\u003c/p\u003e\n\n\u003cp\u003eUse \u003ccode\u003eSSP_Send()\u003c/code\u003e or \u003ccode\u003eSSP_SendMultiple()\u003c/code\u003e to send data. All sending is asynchronous. Data is queued to be sent during \u003ccode\u003eSSP_Process()\u003c/code\u003e. A transmission failure is reported asynchronously to the client on the \u003ccode\u003eSSP_Listen()\u003c/code\u003e registered callback function.\u003c/p\u003e\n\n## Packet Types\n\n\u003cp\u003eSSP provides one 8-bit location for packet type.\u003c/p\u003e\n\n\u003col\u003e\n\t\u003cli\u003eData Packet\u003c/li\u003e\n\t\u003cli\u003eACK Packet\u003c/li\u003e\n\t\u003cli\u003eNAK Packet\u003c/li\u003e\n\u003c/ol\u003e\n\n### Data Packet\n\n\u003cp\u003eData packets transport data to a destination socket. The client data portion contains any data whatsoever; there is no structure to the data other than what the two communication parities agree upon. The sender\u0026rsquo;s data packet is routed to the destination socket. The receiver processes the incoming data on the \u003cstrong\u003eSSP_Listen()\u003c/strong\u003e registered callback.\u003c/p\u003e\n\n### ACK Packet\n\n\u003cp\u003eAn ACK (acknowledge) packet is sent in response to a data packet.\u0026nbsp;ACK indicates successful reception of the data packet. The SSP layer does not route ACK packets to the client.\u003c/p\u003e\n\n\u003cp\u003eThe ACK packet acknowledges a single data packet. The data packet to be acknowledged is identified using the transaction ID. Once the receiver SSP acknowledges the data packet, the sender SSP removes the outgoing message from the send queue.\u003c/p\u003e\n\n### NAK Packet\n\n\u003cp\u003eA NAK (negative-acknowledge) packet is sent in response to a data packet. NAK indicates unsuccessful reception of the data packet. NAK responses will trigger a retry of the message indicated by the transaction ID. After a certain number of retries, the failure will be returned to the caller.\u003c/p\u003e\n\n\u003cp\u003eA NAK packet is sent upon reception of a message if one of the following is true:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003eThe header checksum is valid but the packet footer CRC is not valid. This means the header is intact enough to NAK, but the client data and/or footer is corrupted or not fully received within \u003ccode\u003eSSP_RECV_TIMEOUT\u003c/code\u003e.\u003c/li\u003e\n\t\u003cli\u003eA listener callback is not registered on the destination socket. This means the message was received correctly, but the application isn\u0026rsquo;t listening to the socket.\u003c/li\u003e\n\u003c/ul\u003e\n\n## Parsing\n\n\u003cp\u003eEach packet contains two synchronization bytes: 0xBE and 0xEF. The parser uses these bytes to determine when the packet header starts. The header has an 8-bit checksum used by the parser to determine if the remaining packet should be parsed or not. The client data size is used to parse the packet data and footer. The 16-bit CRC packet footer allows error checking the entire packet before forwarding to the registered client.\u003c/p\u003e\n\n## Queuing\n\n\u003cp\u003eSSP stores each data packet in a queue for asynchronous transmission. The data packet is removed from the send queue if the receiver ACK\u0026rsquo;s the packet or all timeout retries have been exhausted.\u003c/p\u003e\n\n\u003cp\u003eSSP uses either a fixed block allocator or the global heap to manage buffers. Define \u003ccode\u003eUSE_FB_ALLOCATOR \u003c/code\u003ein \u003cstrong\u003essp_opt.h\u003c/strong\u003e to enable the fixed block allocator.\u003c/p\u003e\n\n\u003cp\u003eThe fixed block allocator is documented in the article:\u003c/p\u003e\n\n\u003cp\u003e\u003ca href=\"https://github.com/endurodave/C_Allocator\"\u003eA C-language Fixed Block Memory Allocator\u003c/a\u003e\u003c/p\u003e\n\n## Sequence Control\n\n\u003cp\u003eThe transaction ID is the message number used to identify packets. This value is incremented by 1 on each new data packet sent and wraps to 0 when 255 is reached. The recipient sends the received data packet transaction ID within the ACK or NAK packet.\u003c/p\u003e\n\n## Duplicate Packets\n\n\u003cp\u003eThe SSP layer protects against clients receiving duplicate data packets. The SSP layer keeps track of the last transaction ID. If another packet with the same transaction ID arrives, the message is considered a duplicate not forwarded to the registered client.\u003c/p\u003e\n\n## Timeouts and Retries\n\n\u003cp\u003eThe SSP layer handles timeout conditions. Every sent message must receive an ACK. If after a short duration an ACK is not received, the SSP layer retries sending. After a predetermined number of unsuccessful attempts, the sending client is notified of the communication timeout failure.\u003c/p\u003e\n\n## Flow Control\n\n\u003cp\u003eSSP does not use software-based flow control. Optionally the application-specific HAL (\u003cstrong\u003essp_hal.h\u003c/strong\u003e) implementation may implement hardware or software based flow control at the driver level as deemed necessary.\u003c/p\u003e\n\n## Sending Packets\n\n\u003cp\u003eEach call to \u003ccode\u003eSSP_Send()\u003c/code\u003e or \u003ccode\u003eSSP_SendMultiple()\u003c/code\u003e adds an outgoing message to a send queue if the return value is \u003ccode\u003eSSP_SUCCESS\u003c/code\u003e. Otherwise, the message was not queued for transmission.\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003eSSP_Send()\u003c/code\u003e API looks normal; source and destintation sockets, data, and data size.\u003c/p\u003e\n\n```cpp\nSspErr SSP_Send(UINT8 srcSocketId, UINT8 destSocketId, const void* data, UINT16 dataSize);\n```\n\n\u003cp\u003e\u003ccode\u003eSSP_SendMultiple()\u003c/code\u003e, on the other hand, looks strange.\u0026nbsp;\u003c/p\u003e\n\n```cpp\nSspErr SSP_SendMultiple(UINT8 srcSocketId, UINT8 destSocketId, INT16 numData,\n    void const** dataArray, UINT16* dataSizeArray);\n```\n\n\u003cp\u003eThe API above is used to send disparate data without pre-copying the data into an intermediary buffer. In other words, sometimes the data you want to send is not conveniently packed into a single buffer. This API allows sending discrete pieces of information while letting the SSP do the packing.\u003c/p\u003e\n\n```cpp\nUINT16 sendArrSize[2];\nconst void* sendArr[2]; \n     \nsendArr[0] = \"Hello \";\nsendArrSize[0] = (UINT16)strlen((char*)sendArr[0]);\nsendArr[1] = \"World\\0\";\nsendArrSize[1] = (UINT16)strlen((char*)sendArr[1]) + 1;\n\nerr = SSP_SendMultiple(1, 0, 2, sendArr, sendArrSize);\n```\n\n\u003cp\u003eThe send queue is serviced during \u003ccode\u003eSSP_Process()\u003c/code\u003e. One message at a time is sent in the order that it was received.\u003c/p\u003e\n\n\u003cp\u003eSSP sends the next message in queue when the previous message is ACK\u0026#39;ed or a timeout error occurs.\u0026nbsp;\u003c/p\u003e\n\n## Receiving Packets\n\n\u003cp\u003eClients register with SSP to receive asynchronous callbacks using the \u003ccode\u003eSSP_Listen()\u003c/code\u003e API. The API accepts a callback function pointer and a socket ID. When a packet successfully arrives on the specified socket, the client callback function is called.\u003c/p\u003e\n\n\u003cp\u003eA single receive buffer exists separate from the sending buffers. Once the client notification callback occurs, the receive buffer is free to be used\u0026nbsp;for the next incoming packet. Therefore, if a listener callback needs to retain the incoming data it must be copied\u0026nbsp;to another application defined location.\u003c/p\u003e\n\n\u003cp\u003eThe client callbacks occur on the context that calls \u003ccode\u003eSSP_Process()\u003c/code\u003e. During the callback, the client should do something quick and not block. For instance, post a message to another thread to be handled asynchronously.\u003c/p\u003e\n\n## Error Handling\n\n\u003cp\u003eErrors are reported to the application in a few different ways:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003eReturn value \u0026ndash; check each API call return error code for success or failure\u003c/li\u003e\n\t\u003cli\u003eListen callback \u0026ndash; check for send and receive error codes on the \u003ccode\u003eSSP_Listen() \u003c/code\u003eregistered callback\u003c/li\u003e\n\t\u003cli\u003eError callback \u0026ndash; register for error notification using \u003ccode\u003eSSP_SetErrorHandler()\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003eGet last error \u0026ndash; poll for errors using \u003ccode\u003eSSP_GetLastError()\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eSSP provides numerous error codes to assist with problem diagnosis.\u003c/p\u003e\n\n```cpp\ntypedef enum\n{\n    SSP_SUCCESS,\n    SSP_BAD_SIGNATURE,\n    SSP_PARTIAL_PACKET,\n    SSP_PARTIAL_PACKET_HEADER_VALID,\n    SSP_PORT_OPEN_FAILED,\n    SSP_SOCKET_NOT_OPEN,\n    SSP_PORT_NOT_OPEN,\n    SSP_BAD_SOCKET_ID,\n    SSP_SOCKET_ALREADY_OPEN,\n    SSP_PACKET_TOO_LARGE,\n    SSP_DATA_SIZE_TOO_LARGE,\n    SSP_PARSE_ERROR,\n    SSP_CORRUPTED_PACKET,\n    SSP_BAD_HEADER_CHECKSUM,\n    SSP_SEND_RETRIES_FAILED,\n    SSP_QUEUE_FULL,\n    SSP_OUT_OF_MEMORY,\n    SSP_BAD_ARGUMENT,\n    SSP_SEND_FAILURE,\n    SSP_NOT_INITIALIZED,\n    SSP_DUPLICATE_LISTENER,\n    SSP_SOFTWARE_FAULT\n} SspErr;\n```\n\n# Porting\n\n\u003cp\u003eImplement each function within \u003cstrong\u003essp_hal.h\u003c/strong\u003e, \u003cstrong\u003essp_osal.h\u003c/strong\u003e and \u003cstrong\u003essp_fault.h\u003c/strong\u003e based on your application. A few implementations are included.\u003c/p\u003e\n\n\u003cp\u003eHAL implementations:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003cstrong\u003essp_hal_arduino.cpp\u003c/strong\u003e \u0026ndash; implements serial communication on an Arduino.\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003essp_hal_windows.c\u003c/strong\u003e \u0026ndash; implements serial communication on Windows.\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003essp_hal_localhost.c\u003c/strong\u003e \u0026ndash; implements localhost communication on Linux.\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003essp_hal_mem_buf.c\u003c/strong\u003e \u0026ndash; implements communication via memory buffers. Allows SSP testing on a PC without actual communication hardware.\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eOSAL implementations:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003cstrong\u003essp_osal_no_os.c\u003c/strong\u003e \u0026ndash; implements the OS abstraction when not using an OS.\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003essp_osal_std.cpp\u003c/strong\u003e \u0026ndash; implements the OS abstraction using the C++ standard library.\u003c/li\u003e\n\t\u003cli\u003e\u003cstrong\u003essp_osal_windows.c\u003c/strong\u003e \u0026ndash; implements the OS abstraction for Windows.\u003c/li\u003e\n    \u003cli\u003e\u003cstrong\u003essp_osal_unix.c\u003c/strong\u003e \u0026ndash; implements the OS abstraction for Linux.\u003c/li\u003e\n\u003c/ul\u003e\n\n## Arduino\n\n\u003cp\u003eThe \u003cstrong\u003earduino\u003c/strong\u003e directory contains the \u003cstrong\u003earduino.ino\u003c/strong\u003e sketch file. This file contains the \u0026ldquo;main\u0026rdquo; application source code.\u003c/p\u003e\n\n\u003cp\u003eThe Arduino IDE requires the source code in a common directory. The \u003cstrong\u003ecopysrc.bat\u003c/strong\u003e copies the source files to a common directory for easy build and testing.\u003c/p\u003e\n\n# Serialize\n\nThe \u003cb\u003eexample_serialize.cpp\u003c/b\u003e shows how to use a simple C++ binary message serializer to encode C++ objects for transport over SSP (or any other protocol).\n\n# Resources\n\n\u003cp\u003eThe SSP library is compact. Arduino Sketch reports 9k\u0026nbsp;program storage space for the entire application (about 3% of program space on my ATmega 2560). Of that, SSP consumes about 2.5k.\u003c/p\u003e\n\n\u003cp\u003eThe memory map is shown below. Any \u0026ldquo;ssp\u0026rdquo; named data is required by the SSP library. Adjust the \u003cstrong\u003eSSP_MAX_PACKET_SIZE \u003c/strong\u003eand \u003cstrong\u003eSSP_MAX_MESSAGES \u003c/strong\u003ebuild options to increase/decrease memory utilization.\u003c/p\u003e\n\n\u003cp\u003e\u003cimg src=\"SSP_Map.jpg\" style=\"width: 350px; height: 404px\" /\u003e\u003c/p\u003e\n\n# Referenced Articles\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ca href=https://github.com/endurodave/C_Allocator\"\u003eA Fixed Block Allocator in C\u003c/a\u003e\u0026nbsp;- by David Lafreniere\u003c/li\u003e\n    \t\u003cli\u003e\u003ca href=\"https://github.com/endurodave/MessageSerialize\"\u003eA Binary Message Serializer in C++\u003c/a\u003e\u0026nbsp;- by David Lafreniere\u003c/li\u003e\n\u003c/ul\u003e\n\n# Conclusion\n\n\u003cp\u003eCreating a software protocol from scratch is time consuming and difficult. SSP is a portable library for socket-like communications on non-Ethernet based hardware communication interfaces for use on embedded or PC-based systems.\u003c/p\u003e\n\n\u003cp\u003eUsing a sockets paradigm on embedded communication interfaces eases messaging between different systems and increased reliability. SSP is portable and supports any hardware interface thus standardizing the application interface.\u003c/p\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fsimplesocketprotocol","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fendurodave%2Fsimplesocketprotocol","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fsimplesocketprotocol/lists"}