Skip to content

EmbeddedComm – A lightweight, half-duplex, memory-based master–slave communication protocol for microcontrollers. Supports one master and multiple slaves over hardware interfaces (e.g., I²C). Designed for cross-platform communication (Raspberry Pi Pico, Arduino, ESP, etc.), with built-in error detection via status and checksum bytes.

License

Notifications You must be signed in to change notification settings

mboguslawski/EmbeddedComm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

84 Commits
 
 
 
 
 
 
 
 

Repository files navigation

EmbeddedComm

  • Half-Duplex
  • Memory-Based Master-Slave Communication Protocol for Microcontrollers

Table of contents

  1. GenericMaster API
  2. GenericSlave API
  3. Protocol Specification
  4. i2c implementation
  5. usb implementation

GenericMaster API

The GenericMaster class provides a hardware-agnostic implementation of the master-side logic for the EmbeddedComm protocol. It handles packet construction, checksum calculation, and protocol flow control, while leaving the actual byte transmission to derived classes.

template <typename slaveInfo>
class GenericMaster

Template Parameters

  • slaveInfo: A user-defined type containing information required to identify and connect to a specific slave (e.g., I2C address, CS pin number, SPI handle). This type is passed by reference to all methods.

Public Methods

GenericMaster()

Constructs a new GenericMaster object.


write()

Writes bytes to the slave's memory starting at a specific address.

StatusValue write(
    slaveInfo &sinfo, 
    uint32_t memoryAddress, 
    uint8_t *data, 
    uint32_t writeSize
);

Parameters:

  • sinfo: Reference to the slave configuration object.
  • memoryAddress: The 32-bit start address in the slave's memory map to write to.
  • data: Pointer to the buffer containing the data to send.
  • writeSize: Number of bytes to write.

Returns:

  • StatusValue: The status byte returned by the slave (e.g., Ok, ErrDataCorrupted, ErrMemoryOutOfRange). Returns 0 if the low-level transport write/read failed.

Constructs a protocol packet containing the data length, target address, payload, and checksum. It transmits this packet using writeBytes() and immediately reads back the status byte from the slave to confirm success.


read()

Reads bytes from the slave's memory starting at a specific address.

StatusValue read(
    slaveInfo &sinfo, 
    uint32_t memoryAddress, 
    uint8_t *buffer, 
    uint32_t readSize
);

Parameters:

  • sinfo: Reference to the slave configuration object.
  • memoryAddress: The 32-bit start address in the slave's memory to read from.
  • buffer: Pointer to the destination buffer where received data will be stored. Must be at least readSize bytes large.
  • readSize: Number of bytes to read.

Returns:

  • StatusValue: status received from slave if the read was successful and checksums matched. Returns ErrDataCorrupted if the checksum validation failed on the master side. Returns 0 if low-level transport failed.

Description: Sends a read request header (length + address) to the slave. It then reads the requested data bytes, followed by a checksum byte and a status byte. The master validates the integrity of the received data by recalculating the checksum.


readStatus()

Reads the current status of the slave without performing a significant data transfer.

inline StatusValue readStatus(slaveInfo &sinfo);

Parameters:

  • sinfo: Reference to the slave configuration object.

Returns:

  • StatusValue: The current status code of the slave.

Protected Virtual Methods (To Be Implemented)

These pure virtual methods must be implemented by any child class to define the specific hardware transport layer (e.g., I2C, SPI, UART).

writeBytes()

Transmits raw bytes to the physical medium.

virtual int writeBytes(
    slaveInfo &sinfo, 
    uint8_t *bytes, 
    uint32_t numberOfBytes
) = 0;

Returns:

  • Should return 0 (or positive) on success, and a negative value on failure.

readBytes()

Receives raw bytes from the physical medium.

virtual int readBytes(
    slaveInfo &sinfo, 
    uint8_t *bytes, 
    uint32_t numberOfBytes
) = 0;

Returns:

  • Should return 0 (or positive) on success, and a negative value on failure.

GenericSlave API

The GenericSlave class implements the slave-side logic for the EmbeddedComm protocol. It is hardware-agnostic and designed to operate within interrupt service routines (ISRs) for byte-by-byte processing, while offloading heavier tasks (like callbacks and memory restoration) to the main loop.

Class Definition

class GenericSlave

Public Methods

GenericSlave()

Constructs a new GenericSlave object.


initialize()

Assigns the memory buffer that acts as the slave's register map or storage.

void initialize(
    uint8_t *memory, 
    uint32_t memorySize
);

Parameters:

  • memory: Pointer to the byte array that will serve as the device's accessible memory.
  • memorySize: The size of the memory buffer in bytes.

Description: Configures the main storage area. The master will read from and write directly to this buffer based on the protocol commands.


enableMemBackups()

Enables the transactional backup feature to prevent data corruption during failed writes.

void enableMemBackups(
    uint8_t *backupBuffer, 
    uint32_t backupBufferSize
);

Parameters:

  • backupBuffer: Pointer to a separate buffer used to temporarily store data before a write is finalized.
  • backupBufferSize: The size of the backup buffer in bytes.

Description: When enabled, the slave saves the current state of memory to backupBuffer before applying new writes from the master. If the transaction fails (checksum mismatch), the original data is automatically restored during the process() call. This limits the maximum writable data length per transaction to backupBufferSize.


writeHandler()

Processes a single byte received from the master.

void writeHandler(uint8_t receivedByte);

Parameters:

  • receivedByte: The byte received from the hardware interface (e.g., I2C RX register).

Description: This function drives the internal state machine. It handles the protocol phases: receiving data length, memory address, payload, and checksum verification. It is typically called inside a hardware Receive Interrupt.


readHandler()

Retrieves the next byte to send to the master.

uint8_t readHandler();

Returns:

  • uint8_t: The byte to be transmitted to the hardware interface (e.g., I2C TX register).

Description: Calculates the response byte based on the current protocol state. This may be requested data from memory, the calculated checksum, or the status byte. It is typically called inside a hardware Transmit Request Interrupt.


addMemoryChangeCallback()

Registers a callback function to be executed when a specific memory address is modified by the master.

bool addMemoryChangeCallback(
    uint32_t memoryAddress, 
    CallbackFunction callback
);

Parameters:

  • memoryAddress: The index in the memory buffer to monitor.
  • callback: Function pointer (void(*)()) to execute when the value at memoryAddress changes.

Returns:

  • true if the callback was successfully registered.
  • false if the maximum number of callbacks (defined by MAX_MEMORY_CHANGE_CALLBACKS, default 10) has been reached.

process()

Performs non-time-critical maintenance tasks.

void process();

Description: This method must be called frequently from the main application loop. It handles tasks that are too slow for an interrupt context, such as:

  1. Restoring memory from the backup buffer if a transaction was corrupted.
  2. Executing registered callbacks if memory values were changed by the master.
  3. Clearing the Busy status flag once these tasks are complete.

EmbeddedComm Protocol Specification

The EmbeddedComm protocol is a binary, master-slave communication standard designed for reliable memory access over byte-oriented streams (I2C, SPI, UART). It supports data integrity checks via checksums and transactional atomic operations using status flags.

Byte Order: Little Endian (LSB first)

Data Types

Type Size Description
Address 4 Bytes 32-bit Memory Address.
Length 4 Bytes 32-bit Data Length. Bit 31 (MSB): Read Flag (1 = Read, 0 = Write).
Checksum 1 Byte 8-bit Checksum (Algorithm defined by implementation).
Status 1 Byte 8-bit Status Register (Bitmap).

Length field visualization:

31                                                            0
+---+---------------------------------------------------------+
| R |                      Data Length                        |
+---+---------------------------------------------------------+
  ^                             ^
  |                             |
  +-- Read Flag                 +-- Actual Length
      1: Master Read
      0: Master Write

1. Write Transaction

Used to write $N$ bytes of data to the slave.

Sequence:

  1. Master sends Header:
    • Data Length. Bit 31 is 0 (ReadFlag) (4 Bytes)
    • Memory Address (4 Bytes)
  2. Master sends Payload:
    • Data ($N$ Bytes)
  3. Master sends Checksum:
    • Checksum (1 Byte) - Calculated over [Length + Address + Data].
  4. Slave responds:
    • Status (1 Byte) - Returns Ok (0x80) or Error Flags.
Master >>> [Length. Bit 31 is 0. (4B)] [Address (4B)] [Data (N Bytes)] [Checksum (1B)] >>> Slave
Master <<< [Status (1B)] <<< Slave

2. Read Transaction

Used to read $N$ bytes of data from the slave.

Sequence:

  1. Master sends Header:
    • Read Size. Bit 31 is 1 (ReadFlag) (4 Bytes)
    • Memory Address (4 Bytes)
  2. Slave sends Payload:
    • Data ($N$ Bytes)
  3. Slave sends Checksum:
    • Checksum (1 Byte) - Calculated over [Header + Data].
  4. Slave sends Status:
    • Status (1 Byte) - Returns Ok (0x80) or Error Flags.
Master >>> [Length. Bit 31 is 1. (4B)] [Address (4B)] >>> Slave
Master <<< [Data (N Bytes)] [Checksum (1B)] [Status (1B)] <<< Slave

3. Status Register

The Status Byte indicates the result of the last operation. It is a bitmask where 0x80 represents Success, and lower bits represent specific errors.

Status Code Definitions:

Name Value (Hex) Value (Dec) Description
NotUsed 0x00 0 Default value, indicates initialization or failed low-level read.
ErrMemoryOutOfRange 0x01 1 Address falls outside valid memory range.
ErrBackupBufferOverflow 0x02 2 Write size exceeds the enabled backup buffer capacity.
ErrInvalidRead 0x04 4 Protocol violation: Read requested without valid header.
ErrInvalidWrite 0x08 8 Protocol violation: Write attempted during read phase.
ErrDataCorrupted 0x10 16 Checksum mismatch.
Busy 0x20 32 Slave is processing previous request or callback.
Ok 0x80 128 Success. Operation completed without errors.

About

EmbeddedComm – A lightweight, half-duplex, memory-based master–slave communication protocol for microcontrollers. Supports one master and multiple slaves over hardware interfaces (e.g., I²C). Designed for cross-platform communication (Raspberry Pi Pico, Arduino, ESP, etc.), with built-in error detection via status and checksum bytes.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published