SerialTransfer Arduino Library

repository·master·Indexed 19 days ago

https://github.com/powerbroker2/serialtransfer

An Arduino library for reliable, non-blocking, packetized data transfer over Serial, I2C, and SPI. It ensures data integrity using Consistent Overhead Byte Stuffing (COBS) and CRC-8 (Polynomial 0x9B), supporting payloads from 1 to 254 bytes. The library allows for the transfer of various data types including bytes, ints, floats, structs, and large files. A Python mirror library, pySerialTransfer, is available for cross-platform communication.

Tokens
5K
Snippets
16
Records
23
Agent score
67%

What's inside SerialTransfer

  1. Overview of SerialTransfer

    master

    SerialTransfer is an Arduino library designed for fast and reliable packetized data transfer over Serial, I2C, and SPI interfaces. It is non-blocking and uses consistent overhead byte stuffing (COBS) and CRC-8 (Polynomial 0x9B) to ensure data integrity.

    Key Features:

    • Supports Serial, I2C, and SPI (including software-based versions).
    • Uses packet delimiters and COBS for robust framing.
    • Supports dynamically sized packets (payloads from 1 to 254 bytes).
    • Supports user-specified callback functions.
    • Capable of transferring various data types: bytes, ints, floats, structs, and large files (e.g., JPEGs, CSVs).
    • A Python mirror library (pySerialTransfer) is available for cross-platform communication.
  2. Understand the SerialTransfer packet anatomy

    master

    Every packet sent via SerialTransfer follows a specific structure to ensure reliable framing and error detection. The packet consists of a constant Start byte, a Packet ID, a COBS overhead byte, the payload (variable length), an 8-bit CRC, and a Stop byte.

    Packet Structure:

    1. Start byte: A constant byte marking the beginning.
    2. Packet ID: Identifies the packet (defaults to 0).
    3. COBS Overhead byte: Used for consistent overhead byte stuffing.
    4. Payload: The actual data being transferred (1 to 254 bytes).
    5. 8-bit CRC: Error checking using Polynomial 0x9B with a lookup table.
    6. Stop byte: A constant byte marking the end.
    01111110 00000000 11111111 00000000 00000000 00000000 ... 00000000 10000001
    |      | |      | |      | |      | |      | |      | | | |      | |______|__Stop byte
    |      | |      | |      | |      | |      | |      | | | |______|___________8-bit CRC
    |      | |      | |      | |      | |      | |      | |_|____________________Rest of payload
    |      | |      | |      | |      | |      | |______|________________________2nd payload byte
    |      | |      | |      | |      | |______|_________________________________1st payload byte
    |      | |      | |      | |______|__________________________________________# of payload bytes
    |      | |      | |______|___________________________________________________COBS Overhead byte
    |      | |______|____________________________________________________________Packet ID (0 by default)
    |______|_____________________________________________________________________Start byte (constant)
  3. Initialize I2CTransfer

    master

    To use I2CTransfer, you must first initialize it with an Arduino TwoWire port (e.g., Wire or Wire1). You can provide a configuration object or enable debug mode by passing a Stream object (like Serial).

    I2CTransfer transfer;
    // Initialize with default debug settings (debug=true, port=Serial)
    transfer.begin(Wire);
  4. Configure the Packet class

    master

    The Packet class can be initialized using the begin() method. You can either pass a configST struct for detailed configuration or use the simplified overload for quick setup.

    Configuration Options (configST)

    • debugPort: A pointer to a Stream object (e.g., &Serial) used for debugging. Defaults to &Serial.
    • debug: A boolean to enable or disable debugging. Defaults to true.
    • callbacks: A pointer to an array of functionPtr (void functions) to be executed on specific events.
    • callbacksLen: The number of callbacks in the array.
    • timeout: A uint32_t value representing the timeout in milliseconds. Defaults to __UINT32_MAX__.

    Initialization Methods

    • begin(const configST& configs): Uses the provided configuration struct.
    • begin(const bool& _debug = true, Stream& _debugPort = Serial, const uint32_t& _timeout = DEFAULT_TIMEOUT): A simplified version for setting debug mode, the debug port, and the timeout.
    // Using the config struct
    configST myConfig;
    myConfig.debug = false;
    myConfig.timeout = 100;
    packet.begin(myConfig);
    
    // Using the simplified overload
    packet.begin(true, Serial, 100);
  5. Troubleshoot SPI support on specific Arduino boards

    master

    If you are using an Arduino Nano 33 BLE, Arduino DUE, or other specific boards, SPITransfer.h and its associated features are not supported and are disabled by default.

    To enable SPI support on these boards (if applicable/supported), you must manually edit the library source:

    1. Locate SerialTransfer.h in your library folder.
    2. Find the line #define DISABLE_SPI_SERIALTRANSFER 1.
    3. Comment out that line to enable the feature.
  6. Check SPITransfer status and availability

    master

    Use the following methods to monitor the state of the SPI transfer:

    • uint8_t available(): Returns the number of bytes available to be read.
    • uint8_t currentPacketID(): Returns the ID of the current packet being processed.
    • uint8_t bytesRead: A public member tracking the number of bytes read.
    • int8_t status: A public member tracking the current status.
  7. Populate transmit buffer with txObj()

    master

    The txObj<T>() method copies an arbitrary object into the internal transmit buffer (txBuff) at a specific index. This allows you to build complex packets by concatenating multiple objects before sending them.

    Parameters:

    • val: The object to be copied to the transmit buffer.
    • index: The starting index within the transmit buffer.
    • len: The number of bytes of the object to transmit. Defaults to sizeof(T).

    Returns:

    • uint16_t: The index in the transmit buffer immediately following the bytes just processed.
    // Building a multi-part packet
    st.txObj(header, 0, sizeof(header));
    st.txObj(payload, 0, sizeof(payload)); // Note: index 0 here is relative to the packet logic or handled by the internal packet object
  8. Pack and unpack arbitrary objects with txObj and rxObj

    master

    The Packet class provides template methods to easily move arbitrary data types (integers, floats, structs, etc.) into and out of the packet buffers.

    txObj<T>(const T& val, const uint16_t& index = 0, const uint16_t& len = sizeof(T))

    Stuffs len bytes of an object into the transmit buffer (txBuff) starting at the specified index.

    • Returns: The maxIndex (the index in txBuff immediately following the bytes just processed).

    rxObj<T>(const T& val, const uint16_t& index = 0, const uint16_t& len = sizeof(T))

    Reads len bytes from the receive buffer (rxBuff) starting at the specified index into the provided object val.

    • Returns: The maxIndex (the index in rxBuff immediately following the bytes just processed).

    Note: Both methods ensure they do not exceed MAX_PACKET_SIZE (0xFE).

    // Example: Packing a struct into the transmit buffer
    struct MyData {
        int16_t sensorValue;
        float temperature;
    };
    
    MyData data = {1024, 25.5f};
    uint16_t nextIdx = packet.txObj(data, 0);
    
    // Example: Unpacking a float from the receive buffer
    float receivedTemp;
    packet.rxObj(receivedTemp, 0, sizeof(float));
  9. Pack objects into the transmit buffer

    master

    To send multiple pieces of data in a single packet, use txObj to stuff bytes into the transmit buffer (txBuff) at specific indices before calling sendData.

    // Packing a float and an int into one packet
    float temperature = 25.5f;
    uint16_t sensorID = 1;
    
    spiTransfer.txObj(temperature, 0);
    spiTransfer.txObj(sensorID, sizeof(float));
    
    // Send the total length of the packed data
    spiTransfer.sendData(sizeof(float) + sizeof(uint16_t));
  10. Read data using rxObj

    master

    After receiving data via I2C, use rxObj<T>(val, index, len) to extract bytes from the internal receive buffer into your local variables (e.g., int, float, or struct).

    // Example: Extracting a float from the receive buffer at index 0
    float receivedValue;
    transfer.rxObj<float>(receivedValue, 0, sizeof(float));