Skip to content

Serial IO

Ashish Jaiswal edited this page Jul 21, 2026 · 2 revisions

Serial-IO.h

Serial communication interfaces for UART, I2C, and SPI. Initialize a bus once in plutoInit(), then read and write on it during plutoLoop().

Enumerations

UART_Baud_Rate_e

typedef enum {
    BAUD_RATE_4800, BAUD_RATE_9600, BAUD_RATE_14400,
    BAUD_RATE_19200, BAUD_RATE_38400, BAUD_RATE_57600,
    BAUD_RATE_115200, BAUD_RATE_128000, BAUD_RATE_256000,
    BAUD_RATE_420000   // CRSF / ELRS
} UART_Baud_Rate_e;

UART_Port_e

typedef enum {
    UART2    // UART2 port
} UART_Port_e;

SPImode_t

typedef enum SPImode_s {
    MODE0 = 0,    // CPOL=0, CPHA=0
    MODE1,        // CPOL=0, CPHA=1
    MODE2,        // CPOL=1, CPHA=0
    MODE3         // CPOL=1, CPHA=1
} SPImode_t;

SPIfirst_bit_t

typedef enum SPIfirst_bit_s {
    LSBFIRST = 0,    // LSB first
    MSBFIRST         // MSB first
} SPIfirst_bit_t;

UART Functions

void Uart_Init(UART_Port_e PORT, UART_Baud_Rate_e BAUD)

Initializes a UART port at the given baud rate. Do it once at startup.

void plutoInit ( void ) {
    Uart_Init ( UART2, BAUD_RATE_115200 );
}

Reading: Uart_Read8 / Uart_Read16 / Uart_Read32

Read 8, 16, or 32 bits from the port. Check for data first with Uart_rxBytesWaiting().

// uint8_t  Uart_Read8  ( UART_Port_e PORT );
// uint16_t Uart_Read16 ( UART_Port_e PORT );
// uint32_t Uart_Read32 ( UART_Port_e PORT );

void plutoLoop ( void ) {
    if ( Uart_rxBytesWaiting ( UART2 ) ) {
        uint8_t byte = Uart_Read8 ( UART2 );
        Monitor_Print ( "RX: ", byte );
    }
}

Writing: Uart_Write (byte / string / buffer)

Three overloads write a single byte, a null-terminated string, or a byte array.

// void Uart_Write ( UART_Port_e PORT, uint8_t data );
// void Uart_Write ( UART_Port_e PORT, const char *str );
// void Uart_Write ( UART_Port_e PORT, uint8_t *data, uint16_t length );

void plutoLoop ( void ) {
    Uart_Write ( UART2, "Hello\n" );          // string
    Uart_Write ( UART2, ( uint8_t ) 0x41 );   // single byte

    uint8_t packet[3] = { 0x01, 0x02, 0x03 };
    Uart_Write ( UART2, packet, 3 );          // buffer
}

bool Uart_rxBytesWaiting(UART_Port_e PORT)

Returns true if received bytes are waiting to be read.

void plutoLoop ( void ) {
    if ( Uart_rxBytesWaiting ( UART2 ) ) {
        uint8_t b = Uart_Read8 ( UART2 );
        Monitor_Print ( "Got: ", b );
    }
}

bool Uart_txBytesFree(UART_Port_e PORT)

Returns true if the transmit buffer has space.

void plutoLoop ( void ) {
    if ( Uart_txBytesFree ( UART2 ) ) {
        Uart_Write ( UART2, "data\n" );
    }
}

I2C Functions

bool I2C_Read(uint8_t device_add, uint8_t reg, uint8_t &value)

Reads one byte from reg into value. Returns true on success.

void plutoLoop ( void ) {
    uint8_t whoAmI;
    if ( I2C_Read ( 0x68, 0x75, whoAmI ) ) {   // device 0x68, reg 0x75
        Monitor_Print ( "WHO_AM_I: ", whoAmI );
    }
}

int16_t I2C_Read(uint8_t device_add, uint8_t reg, uint32_t length, uint8_t *buffer)

Reads length bytes starting at reg into buffer. Returns the byte count read, or a negative error code.

void plutoLoop ( void ) {
    uint8_t buf[6];
    I2C_Read ( 0x68, 0x3B, 6, buf );   // read 6 bytes (e.g. accel X/Y/Z)
}

bool I2C_Write(uint8_t device_add, uint8_t reg, uint8_t data)

Writes one byte to reg. Returns true on success. Device configuration is usually one-time, so do it in onLoopStart().

void onLoopStart ( void ) {
    I2C_Write ( 0x68, 0x6B, 0x00 );   // wake device (clear sleep bit)
}

bool I2C_Write(uint8_t device_add, uint8_t reg, uint32_t length, uint8_t *data)

Writes length bytes starting at reg. Returns true on success.

void onLoopStart ( void ) {
    uint8_t cfg[2] = { 0x01, 0x02 };
    I2C_Write ( 0x68, 0x1B, 2, cfg );
}

SPI Functions

SPI runs on the SPI2 peripheral.

void SPI_Init() / void SPI_Init(SPImode_t mode, uint16_t speed, SPIfirst_bit_t bit)

Initializes SPI2 with default settings, or with a custom mode, speed (Hz), and bit order. Do it once at startup.

void plutoInit ( void ) {
    SPI_Init ( MODE0, 1000000, MSBFIRST );   // or SPI_Init() for defaults
}

void SPI_Enable(void) / void SPI_Disable(void)

Enable or disable the SPI interface.

void onLoopStart ( void ) {
    SPI_Enable();
}

void onLoopFinish ( void ) {
    SPI_Disable();
}

uint8_t SPI_Read(uint8_t register_address)

Reads a single byte from a register.

void plutoLoop ( void ) {
    uint8_t id = SPI_Read ( 0x0F );   // e.g. WHO_AM_I
    Monitor_Print ( "SPI ID: ", id );
}

void SPI_Read(uint8_t register_address, int16_t length, uint8_t *buffer)

Reads length bytes into buffer (the register's MSB is cleared internally).

void plutoLoop ( void ) {
    uint8_t buf[6];
    SPI_Read ( 0x28, 6, buf );
}

void SPI_Write(uint8_t register_address, uint8_t data)

Writes a single byte to a register (the write bit is set internally).

void onLoopStart ( void ) {
    SPI_Write ( 0x20, 0x8F );   // configure a control register once
}

Usage Examples

UART

#include "PlutoPilot.h"

void plutoInit ( void ) {
    Uart_Init ( UART2, BAUD_RATE_115200 );
}

void plutoLoop ( void ) {
    Uart_Write ( UART2, "Hello\n" );

    if ( Uart_rxBytesWaiting ( UART2 ) ) {
        uint8_t data = Uart_Read8 ( UART2 );
        Monitor_Print ( "RX: ", data );
    }
}

I2C

#include "PlutoPilot.h"

const uint8_t SENSOR = 0x68;

// One-time device configuration.
void onLoopStart ( void ) {
    I2C_Write ( SENSOR, 0x6B, 0x00 );   // wake the device
}

// Poll the device.
void plutoLoop ( void ) {
    uint8_t id;
    if ( I2C_Read ( SENSOR, 0x75, id ) ) {
        Monitor_Print ( "Sensor ID: ", id );
    }
}

SPI

#include "PlutoPilot.h"

void plutoInit ( void ) {
    SPI_Init ( MODE0, 1000000, MSBFIRST );
}

void onLoopStart ( void ) {
    SPI_Enable();
    SPI_Write ( 0x20, 0x8F );          // configure once
}

void plutoLoop ( void ) {
    uint8_t id = SPI_Read ( 0x0F );
    Monitor_Print ( "Device ID: ", id );
}

void onLoopFinish ( void ) {
    SPI_Disable();
}

Clone this wiki locally