Limit sample-to-sample change.
clamp_filter_t filter;
clamp_filter_init(&filter, 0, 10);
int32_t value = clamp_filter_step(&filter, adc_read());
Full contract and API →
Packages/Examples
Integration examples
Each example is the integration block from the package README. Board wiring stays in the project adapter; the catalog unit stays portable.
Limit sample-to-sample change.
clamp_filter_t filter;
clamp_filter_init(&filter, 0, 10);
int32_t value = clamp_filter_step(&filter, adc_read());
Full contract and API →
Remove impulses with an N-sample median.
uint16_t window[5];
median_filter_t filter;
median_filter_init(&filter, window, 5);
uint16_t clean = median_filter_step(&filter, adc_read());
Full contract and API →
Smooth with an N-sample mean.
uint16_t window[8];
moving_average_t filter;
moving_average_init(&filter, window, 8);
uint16_t smooth = moving_average_step(&filter, adc_read());
Full contract and API →
First-order lag with a power-of-two shift.
ema_filter_t filter;
ema_filter_init(&filter, 0, 3); // factor 8
int32_t smooth = ema_filter_step(&filter, adc_read());
Full contract and API →
Average a batch after dropping the minimum and maximum.
uint16_t batch[6];
/* fill batch from the sensor, then: */
uint16_t value = trimmed_mean(batch, 6);
Full contract and API →
Delay a binary change until the input is stable.
debounce_t key;
debounce_init(&key, 20);
/* every 1 ms: */
uint8_t pressed = debounce_step(&key, raw_pin(), 1);
Full contract and API →
Keep a binary state across a threshold band.
hysteresis_t battery;
hysteresis_init(&battery, 3200, 3400, 0);
uint8_t low = hysteresis_step(&battery, millivolts());
Full contract and API →
Turn a key level into short and long events.
key_fsm_t key;
key_fsm_init(&key, 20, 1000);
/* every 1 ms: */
switch (key_fsm_step(&key, raw_pin(), 1)) {
case KEY_EVENT_SHORT: toggle_output(); break;
case KEY_EVENT_LONG: enter_setup(); break;
default: break;
}
Full contract and API →
CRC-16/CCITT-FALSE for frames and images.
uint16_t crc = crc16_ccitt(payload, length);
if (crc != expected) { reject(); }
Full contract and API →
Fixed-size byte ring buffer.
uint8_t storage[64];
ring_buffer_t rx;
ring_buffer_init(&rx, storage, sizeof(storage));
/* ISR: */ ring_buffer_push(&rx, byte);
/* main: */ while (ring_buffer_pop(&rx, &byte)) { handle(byte); }
Full contract and API →
Arithmetic mean of a batch.
uint16_t samples[8];
/* fill samples, then: */
uint16_t value = mean_u16(samples, 8);
Full contract and API →
Limit each step, then smooth with a moving average.
uint16_t window[4];
clamp_average_t filter;
clamp_average_init(&filter, 0, 20, window, 4);
uint16_t value = clamp_average_step(&filter, adc_read());
Full contract and API →
Weighted mean of a batch.
const uint16_t samples[3] = {10, 20, 30};
const uint8_t weights[3] = {1, 2, 3};
uint16_t value = weighted_mean_u16(samples, weights, 3);
Full contract and API →
Limit each step, then accept after N stable samples.
clamp_debounce_t filter;
clamp_debounce_init(&filter, 1000, 10, 3);
uint16_t value = clamp_debounce_step(&filter, adc_read());
Full contract and API →
Decode NEC falling-edge intervals.
ir_nec_frame_t frame;
if (ir_nec_decode(intervals, 33, &frame) == IR_NEC_FRAME) {
handle_key(frame.address, frame.command);
}
Full contract and API →
Integer PID with integral clamping and output limits.
pid_t pid;
pid_init(&pid, 80, 4, 2, 8, -2000, 2000, 0, 1000);
int32_t duty = pid_step(&pid, target_rpm, measured_rpm);
Full contract and API →
Map an input through a lookup table with interpolation.
static const uint16_t gamma[5] = {0, 20, 120, 600, 4095};
uint16_t duty = gamma_lut_apply(gamma, 5, level, 255);
Full contract and API →
CRC-32/ISO-HDLC for images and frames.
uint32_t crc = crc32(payload, length);
if (crc != expected) { reject(); }
Full contract and API →
Q16.16 integer square root.
int32_t rms = fixed_sqrt_q16((a * a + b * b) >> 16);
Full contract and API →
Q15 direct-form I biquad section.
biquad_t lowpass;
biquad_init(&lowpass, 10923, 21845, 10923, -10923, 0, 15);
int32_t filtered = biquad_step(&lowpass, sample);
Full contract and API →
Q15 finite-impulse-response filter.
static const int16_t taps[3] = {10923, 10923, 10923};
static int16_t history[3];
fir_t average;
fir_init(&average, taps, history, 3, 15);
int32_t filtered = fir_step(&average, sample);
Full contract and API →
Modbus RTU frame builder and parser.
uint8_t frame[8];
uint16_t registers[4];
uint8_t count;
modbus_build_read(1, 0x03, 0x0000, 4, frame);
/* send frame, receive response into frame */
if (modbus_parse_read(frame, response_length, 1, 0x03, registers, 4, &count)) {
use(registers, count);
}
Full contract and API →
PIR motion detector signal chain for sampled pyroelectric data.
pir_t pir;
pir_init(&pir, 245);
pir_set_warm(&pir, 1);
for (;;) {
uint16_t sample = read_pir_sample();
if (pir_step(&pir, sample, millis())) {
publish_motion();
}
}
Full contract and API →
Blocking I2C master with a port and fake bus.
typedef struct { /* SCL/SDA or hardware handle */ } board_i2c_t;
static void start(void *ctx) { board_i2c_start((board_i2c_t *)ctx); }
static void stop(void *ctx) { board_i2c_stop((board_i2c_t *)ctx); }
static uint8_t write(void *ctx, uint8_t byte) { return board_i2c_write((board_i2c_t *)ctx, byte); }
static uint8_t read(void *ctx, uint8_t ack) { return board_i2c_read((board_i2c_t *)ctx, ack); }
static uint32_t now(void *ctx) { return board_now_us((board_i2c_t *)ctx); }
i2c_master_t bus;
i2c_master_init(&bus, (i2c_port_t){ctx, start, stop, write, read, now}, 20000);
uint8_t reg = 0x10;
uint8_t value[2];
i2c_master_write_read(&bus, 0x50, ®, 1, value, 2);
Full contract and API →
Blocking UART core with a port and timeout.
uart_t link;
uart_init(&link, board_uart_port(), 100000);
uart_write(&link, (const uint8_t *)"AT\r\n", 4);
uint8_t reply[2];
if (uart_read(&link, reply, 2) == UART_OK) { handle(reply); }
Full contract and API →
Encode RGB pixels into WS2812 GRB bytes.
const uint8_t red[3] = {255, 0, 0};
ws2812_t strip;
ws2812_init(&strip, board_ws2812_port());
ws2812_send_rgb(&strip, red, 1);
Full contract and API →
Collect falling-edge intervals for an IR receiver.
ir_capture_t capture;
ir_capture_init(&capture);
/* EXTI ISR: */ ir_capture_edge(&capture, board_now_us());
uint16_t intervals[33];
if (ir_capture_take(&capture, intervals, 33) == 33) {
ir_nec_frame_t frame;
if (ir_nec_decode(intervals, 33, &frame) == IR_NEC_FRAME) {
handle_key(frame.address, frame.command);
}
}
Full contract and API →
Average N ADC reads on a channel.
adc_sampler_t battery;
adc_sampler_init(&battery, board_adc_port(), 8);
uint16_t raw = adc_sampler_read(&battery, 0);
uint32_t mv = adc_sampler_mv(&battery, raw, 12, 3300);
Full contract and API →
Software PWM on a single pin.
soft_pwm_t led;
soft_pwm_init(&led, board_led_port(), 100);
soft_pwm_set_duty(&led, 25);
/* every 100 us: */ soft_pwm_tick(&led);
Full contract and API →
READ DS18B20 temperature over 1-Wire.
onewire_port_t bus = board_onewire_port();
int16_t raw;
if (ds18b20_read(bus, &raw)) {
int32_t milli_c = (int32_t)raw * 1000 / 16;
}
Full contract and API →
Minimal SSD1306 128x64 I2C driver.
ssd1306_port_t port = board_ssd1306_port();
ssd1306_init(port);
ssd1306_clear(port);
uint8_t row[128] = {0};
row[0] = 0x3C;
ssd1306_write_page(port, 2, 0, row, 128);
Full contract and API →
DHT11 and DHT22 frame decoders.
uint16_t highs[40];
dht11_reading_t reading;
if (dht11_decode(highs, 40, &reading)) {
show(reading.temperature, reading.humidity);
}
Full contract and API →
Quadrature decoder for a mechanical rotary encoder.
encoder_t knob;
encoder_init(&knob);
int8_t step = encoder_update(&knob, read_a(), read_b());
if (step != 0) {
menu_move(step);
}
Full contract and API →
AT24Cxx I2C EEPROM driver.
at24cxx_t eeprom;
at24cxx_init(&eeprom, board_i2c_port(), 0x50, AT24C02);
uint8_t serial[8];
at24cxx_read(&eeprom, 0x00, serial, sizeof(serial));
Full contract and API →
DS3231 I2C real-time clock.
ds3231_time_t now;
if (ds3231_read_time(rtc_port(), &now)) {
log_time(now.year, now.month, now.date, now.hour, now.minute, now.second);
}
Full contract and API →
MAX7219/MAX7221 SPI LED driver.
max7219_t display;
max7219_init(&display, board_max7219_port(), 2);
max7219_clear(&display, 0);
max7219_shutdown(&display, 0, 0);
max7219_set_digit(&display, 0, 0, 0x0F);
Full contract and API →
PCA9685 16-channel I2C PWM driver.
pca9685_t pwm;
pca9685_init(&pwm, board_i2c_port(), 0x40, 50);
pca9685_set_pwm(&pwm, 0, 0, 300); /* 1.5 ms pulse */
Full contract and API →
MPU-6050 six-axis IMU driver.
mpu6050_t imu;
mpu6050_raw_t raw;
mpu6050_init(&imu, board_i2c_port(), 0x68);
mpu6050_read_raw(&imu, &raw);
Full contract and API →
SHT30/SHT31/SHT35 temperature and humidity sensor.
sht3x_t sensor;
sht3x_init(&sensor, board_i2c_port(), 0x44);
int32_t temperature, humidity;
if (sht3x_read(&sensor, SHT3X_HIGH, &temperature, &humidity)) {
show(temperature / 1000, humidity / 1000);
}
Full contract and API →
AHT20 temperature and humidity sensor.
aht20_t sensor;
aht20_init(&sensor, board_i2c_port(), 0x38);
int32_t temperature, humidity;
if (aht20_read(&sensor, &temperature, &humidity)) {
show(temperature / 1000, humidity / 1000);
}
Full contract and API →
MCP2515 CAN controller over SPI.
mcp2515_t can;
can_frame_t frame = {0x123, 0, 2, {0x11, 0x22}};
mcp2515_init(&can, board_mcp2515_port(), 16, 500);
mcp2515_send(&can, &frame);
if (mcp2515_receive(&can, &frame)) {
handle(frame.id, frame.data, frame.dlc);
}
Full contract and API →
Step/direction stepper driver for A4988 and DRV8825.
stepper_t motor;
stepper_init(&motor, board_stepper_port(), 200, 16);
stepper_set_speed(&motor, 60);
stepper_enable(&motor, 1);
stepper_move(&motor, 3200);
Full contract and API →
HX711 24-bit load-cell amplifier.
hx711_t scale;
hx711_init(&scale, board_hx711_port());
scale.gain = 128;
int32_t raw;
if (hx711_read(&scale, &raw)) {
weight = (raw - offset) / scale_factor;
}
Full contract and API →
MAX31855 thermocouple amplifier.
max31855_reading_t temperature;
if (max31855_read(board_max31855_port(), &temperature)) {
if (temperature.fault) {
report_fault(temperature.open_circuit, temperature.short_to_gnd);
} else {
show(temperature.thermocouple_milli_c / 1000);
}
}
Full contract and API →
BME280 temperature, pressure, and humidity sensor.
bme280_t weather;
bme280_reading_t sample;
bme280_init(&weather, board_i2c_port(), 0x76);
bme280_read(&weather, &sample);
show(sample.temperature_milli_c / 1000, sample.pressure_pa, sample.humidity_milli_percent);
Full contract and API →
ADXL345 three-axis accelerometer.
adxl345_t accel;
adxl345_reading_t sample;
adxl345_init(&accel, board_i2c_port(), 0x53);
adxl345_read(&accel, &sample);
Full contract and API →
MCP23017 16-bit I2C GPIO expander.
mcp23017_t expander;
mcp23017_init(&expander, board_i2c_port(), 0x20);
mcp23017_pin_mode(&expander, 0, 1);
mcp23017_write_pin(&expander, 0, 1);
Full contract and API →
ADS1115 16-bit I2C analog-to-digital converter.
ads1115_t adc;
int16_t raw;
int32_t microvolts;
ads1115_init(&adc, board_i2c_port(), 0x48);
ads1115_read_single(&adc, 0, ADS1115_RANGE_4P096V, &raw, µvolts);
Full contract and API →
Minimal SH1106 128x64 I2C OLED driver.
sh1106_port_t port = board_sh1106_port();
sh1106_init(port);
sh1106_clear(port);
uint8_t row[128] = {0};
row[0] = 0x3C;
sh1106_write_page(port, 2, 0, row, 128);
Full contract and API →
MCP4725 12-bit I2C digital-to-analog converter.
mcp4725_t dac;
mcp4725_init(&dac, board_i2c_port(), 0x60);
mcp4725_write_value(&dac, 2048); /* about half scale */
Full contract and API →
PCF8574 8-bit I2C GPIO expander.
pcf8574_t expander;
pcf8574_init(&expander, board_i2c_port(), 0x20);
pcf8574_write_pin(&expander, 0, 0); /* drive low */
pcf8574_write_pin(&expander, 0, 1); /* release */
Full contract and API →
HC-SR04 ultrasonic distance sensor.
uint32_t millimeters;
if (hc_sr04_read(board_hc_sr04_port(), &millimeters)) {
show(millimeters);
}
Full contract and API →
INA219 high-side current and power monitor.
ina219_t monitor;
ina219_init(&monitor, board_i2c_port(), 0x40, 100, 100);
uint32_t millivolts;
ina219_read_bus_voltage_mv(&monitor, &millivolts);
Full contract and API →
MCP9808 high-accuracy I2C temperature sensor.
mcp9808_t sensor;
mcp9808_init(&sensor, board_i2c_port(), 0x18);
int32_t temperature;
mcp9808_read(&sensor, &temperature);
Full contract and API →
nRF24L01+ 2.4 GHz SPI transceiver core.
static const uint8_t address[5] = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7};
nrf24l01_t radio;
nrf24l01_init(&radio, board_nrf24l01_port(), 76, address, 4);
uint8_t payload[4] = {1, 2, 3, 4};
nrf24l01_send(&radio, payload, sizeof(payload));
Full contract and API →
HD44780-compatible character LCD, 4-bit parallel interface.
hd44780_t lcd;
hd44780_init(&lcd, board_hd44780_port(), 16, 2);
hd44780_set_cursor(&lcd, 0, 0);
hd44780_write_string(&lcd, "loom");
Full contract and API →
MFRC522 13.56 MHz RFID reader over SPI.
mfrc522_t reader;
uint8_t uid[4];
mfrc522_init(&reader, board_mfrc522_port());
if (mfrc522_read_uid(&reader, uid)) {
show_uid(uid);
}
Full contract and API →
Sensirion SCD40/SCD41 CO2, temperature, and humidity sensor.
scd4x_t sensor;
uint16_t co2;
int32_t temperature;
uint32_t humidity;
scd4x_init(&sensor, board_i2c_port(), 0x62);
if (scd4x_read(&sensor, &co2, &temperature, &humidity)) {
show(co2, temperature / 1000, humidity / 1000);
}
Full contract and API →
SD card block device over SPI.
sdcard_t card;
uint8_t block[512];
sdcard_init(&card, board_sdcard_port());
if (sdcard_read_block(&card, 0, block)) {
parse_boot_sector(block);
}
Full contract and API →