We’re coding in C++, specifically Arduino code. Download the Arduino IDE and use this guide to add your XIAO RP2040 to it, if you’re using that.
Find sample code in the datasheet
Go back to your best friend (the datasheet) and try to find example code or pseudocode tucked near the back. Even if it’s written for a different microcontroller, it’s still very helpful.
Find a code library (if one exists)
Search the Arduino Library Manager and/or the internet for your exact part number. If someone’s already written a library for it, use it. If you’re having trouble getting it to work, then keep reading to write register-level code.
If nothing exists, you’re writing register-level code yourself, which is very doable. Keep reading!
Reading input, getting output to work
Get your input printing real values over Serial first (Serial.println() it and watch the Serial Monitor) before you touch your output logic at all.
SPI and I2C
Every I2C device has an address, check the datasheet (CTRL+F “address”) to find it. It’ll usually show up as a 7-bit binary number, which you convert to hex for your code.
// pull this from your part’s datasheet #include <Wire.h> #define SENSOR_ADDR 0x68
Wire.h is Arduino’s I2C library, it’s how you read and write data to your parts. Before you can talk to anything, initialize it with Wire.begin():
void setup() { Serial.begin(9600); Wire.begin(); }
To write to a device, wrap your message between beginTransmission() and endTransmission():
Wire.beginTransmission(SENSOR_ADDR); Wire.write(0x6B); // register address Wire.write(0x00); // value to write Wire.endTransmission();
Before they’ll send you real data, CTRL+F “sleep mode” or “power management” in the datasheet to find the right register and value to write.
Getting data back works a little differently, you point to a register, then request bytes from it:
Wire.beginTransmission(SENSOR_ADDR); Wire.write(0x43); // starting register for output data Wire.endTransmission(false); // false = keep the bus open, don’t hang up yet Wire.requestFrom(SENSOR_ADDR, 6);
endTransmission(true), the default, means “I’m done talking.” endTransmission(false) means “I’ve got more to say,” which is what you want right before requestFrom() so you can read from the register you just pointed at.
Sensors send data in bytes (8 bits), but most real readings are 16-bit numbers, so you get a high byte and a low byte and have to smush them back together:
int16_t x = (Wire.read() << 8) | Wire.read();
Shift the high byte left by 8, OR it with the low byte, and you’ve got your real 16-bit value.
Finish a working version before the PCB arrives
You won’t be able to fully test this until your PCB physically shows up, but you need something working to submit. Breadboard or jumper-wire test your input and output against your microcontroller ahead of time if you can, get the logic right now, and save fine-tuning (thresholds, calibration, exact timing) for when the real board’s in your hands.
Firmware checklist
- Include Wire.h (or SPI.h) and declare your part’s address
- In setup(), begin Wire/SPI and Serial
- Wake up your part if its datasheet requires it
- Optional: calibrate
- Write a read function using write() + requestFrom() (I2C) or the SPI equivalent
- Bit-shift multi-byte reads into the right format
- Call your read function in loop()
- Drive your output based on that data
Helpful links
- Your part’s official Arduino library repo, if one exists, check its GitHub examples folder first, it’ll save you hours
- Arduino Reference
- Searching “[your microcontroller] + [protocol] example” (like “XIAO RP2040 I2C example”) almost always turns up a working starting point