ArduboyI2C Library
Loading...
Searching...
No Matches
ArduboyI2C Library

Release License Arduino Library PlatformIO Registry Docs

ArduboyI2C Library

The ArduboyI2C library provides I2C support for Arduboy multiplayer games. It includes standard I2C functionality, support for multi-controller (master) setups, and helpers for multiplayer handshakes while keeping PROGMEM and RAM usage low.

Documentation

See the online documentation for an API reference. See An I2C Primer for a more in-depth explanation of writing I2C games.

Features

  • Controller (master) and target (slave) I2C support.
  • Built-in handshake function for multiplayer games.
  • Built-in cable flip detection for FX-C.
  • Bufferless reading.
  • Minimal memory overhead.
  • Customizable configuration.

Why ArduboyI2C over Wire?

  • Highly optimized (saves ~1KiB of PROGMEM and ~200 bytes of RAM).
  • Adds easy handshake functionality.
  • Adds cable flip detection for FX-C.
  • More customizable (see Configuration).

Installation

Arduino IDE

Install via the Library Manager:

  1. Open the Arduino IDE.
  2. Go to Sketch > Include Library > Manage Libraries...
  3. Search for "ArduboyI2C" and install the library by sub1inear.

PlatformIO

Add to your platformio.ini:

lib_deps =
sub1inear/ArduboyI2C

Quick Start

1. Include the library

#define I2C_IMPLEMENTATION
#include <ArduboyI2C.h>
An I2C library for Arduboy multiplayer games.

2. Initialize

void setup() {
arduboy.begin();
...
}
static void begin()
Initializes I2C hardware.

3. Handshake

static I2C::Role handshake(void(*startFunction)()=nullptr, void(*loopFunction)()=nullptr)
Waits for another device and returns whether this device is the controller (master) or target (slave)...
Role
Definition ArduboyI2C.h:205

4. Read/Write

@ Sync
Synchronous write. The function will block until the write is complete.
Definition ArduboyI2C.h:191
static void read(uint8_t address, void *buffer, uint8_t size)
Attempts to become the bus controller (master) and reads data over I2C from the specified address.
static void write(uint8_t address, const void *buffer, uint8_t size, I2C::Mode mode)
Attempts to become the bus controller (master) and sends data over I2C to the specified address.
static constexpr uint8_t targetAddress
The 7-bit address of the target (slave) device, set by I2C::handshake().
Definition ArduboyI2C.h:213

5. Callbacks

I2C::onReceive(onReceive);
I2C::onRequest(onRequest);
static void onRequest(void(*function)())
Sets up/disables the callback to be called when data is requested from the device's address (a read).
static void onReceive(void(*function)())
Sets up/disables the callback to be called when data is sent to the device's address (a write).
void onReceive() {
const uint8_t *data = I2C::getBuffer();
...
}
void onRequest() {
I2C::reply(data);
}
static void reply(const void *buffer, uint8_t size)
Replies back to the controller (master).
static uint8_t * getBuffer()
Gets a pointer to the internal buffer used for I2C communication.

6. Follow these Rules:

  1. One controller (master) and one or more targets (slaves) at all times.
  2. Define I2C_IMPLEMENTATION in one source file before including ArduboyI2C.h.
  3. In callbacks, use I2C::reply() and I2C::getBuffer(). Do not use delay(), Serial, millis(), etc.

Configuration

Define before #include <ArduboyI2C.h>.

Macro Default Options Description
I2C_IMPLEMENTATION - - Must be defined in one source file to include the implementation.
I2C_FREQUENCY 100000 1-200000 Bus frequency (Hz)
I2C_BUFFER_CAPACITY 32 1-255 Transaction buffer size
I2C_HANDSHAKE_BUSY_CHECKS 128 1-65535 Number of times to check for a busy bus during a handshake.
I2C_CHECK_CABLE_FLIPPED_CHECKS 128 1-65535 Number of checks to perform during cable flip detection.
I2C_CHECK_CABLE_FLIPPED_DEBOUNCE_CHECKS 128 1-65535 Number of passing flip checks before confirming cable is flipped back.

Migrating from Wire Library

Wire API ArduboyI2C
Wire.begin() I2C::begin()
Wire.begin(addr) I2C::setAddress(addr)
Wire.onReceive() I2C::onReceive()
Wire.onRequest() I2C::onRequest()
Wire.beginTransmission(addr) -> Wire.write(...) -> Wire.endTransmission() I2C::write(address, data, mode)
Wire.requestFrom(address, quantity) -> Wire.read() I2C::read(address, data, wait)
Wire.setClock() I2C_FREQUENCY

FAQ

Why am I getting linker errors about ArduboyI2C functions?

Make sure you define I2C_IMPLEMENTATION in one source file before including ArduboyI2C.h.

Why can't I use delay(), Serial, millis(), etc. inside onReceive/onRequest callbacks?

Callbacks are executed inside the I2C interrupt handler, where interrupts are disabled. Functions that rely on interrupts (delay, millis, Serial, etc.) won't work. Keep callbacks short and only use I2C::reply() and I2C::getBuffer(). If you need to do extended processing or rely on interrupts, set a volatile bool flag, copy the data out of the buffer, and then poll the flag in your main loop.

Can I use ArduboyI2C in Ardens?

Ardens will soon support I2C multiplayer; stay tuned.

When should I use I2C::write() vs I2C::reply()?

Use I2C::write() as the controller (master) to send data. Use I2C::reply() inside an onRequest callback to send data back when another device reads from you. Calling write() from a callback won't work correctly.

How do I send/receive more than 32 bytes at once?

Writes and reply() are limited by I2C_BUFFER_CAPACITY (default: 32). Define it before including the header (e.g., #define I2C_BUFFER_CAPACITY 64). Reads have no such buffer limit.

Why must I2C::begin() be called after arduboy.begin()?

arduboy.boot() (called inside arduboy.begin()) disables the TWI (I2C) hardware as a side effect. Calling I2C::begin() afterwards re-initializes it.

Why can't I use certain I2C addresses for my own address?

Addresses 0–7 and 120–127 are reserved by the I2C spec (general call, etc.).

I've passed my own loop function to I2C::handshake() or I2C::checkCableFlipped(), and it breaks handshaking/cable flip detection. Why?

The loop function disrupts the careful timing of the handshake/cable flip detection. You must experimentally redefine the I2C_*_CHECKS macros; see Network of the Damned for an example of how to do this. Most likely, you want to increase I2C_HANDSHAKE_BUSY_CHECKS and I2C_CHECK_CABLE_FLIPPED_CHECKS while decreasing I2C_CHECK_CABLE_FLIPPED_DEBOUNCE_CHECKS.