Hardware abstraction for MicroPython that makes embedded development as simple as working with modern web APIs.
MicroPython is excellent, but complex hardware interactions often require deep knowledge of bus protocols and register configurations. BitBound abstracts hardware components (sensors, motors, displays) with a modern, declarative APIβsimilar to how web frameworks abstract HTTP.
from bitbound import Hardware
# Create hardware manager
hardware = Hardware()
# Attach devices with simple, declarative syntax
sensor = hardware.attach("I2C", type="BME280")
fan = hardware.attach("GPIO", type="Relay", pin=5)
# Use threshold-based events instead of polling loops
sensor.on_threshold("temperature > 25Β°C", lambda e: fan.on())
sensor.on_threshold("temperature < 23Β°C", lambda e: fan.off())
# Read values naturally
print(f"Temperature: {sensor.temperature}Β°C")
print(f"Humidity: {sensor.humidity}%")
print(f"Pressure: {sensor.pressure} hPa")
# Run the event loop
hardware.run()- Declarative Hardware Attachment: No more manual register configuration
- Natural Unit Expressions:
"temperature > 25Β°C","pressure < 1000hPa" - Event-Driven Programming: Threshold callbacks, change detection, interval events
- Simulation Mode: Develop and test without physical hardware
- Wide Device Support: Sensors, actuators, displays
- Cross-Platform: Works on MicroPython, CircuitPython, and standard Python
- Networking: WiFi, MQTT, HTTP Client/Server, WebSocket
- Data Logging: CSV/JSON/JSONL logging, ring buffers, persistent storage
- Configuration Management: Board profiles, dot-notation config, project templates
- Async Support: Async device readings, event loops, gather_readings
- Power Management: Deep/light sleep, watchdog, battery monitoring
- OTA Updates: Version checking, firmware updates, rollback support
- CLI Tool:
bitboundcommand for scanning, init, config, monitor, deploy
pip install bitboundgit clone https://github.com/bitbound/bitbound.git
cd bitbound
pip install -e .Copy the bitbound folder to your device's filesystem:
# Using mpremote
mpremote cp -r bitbound :
# Or using ampy
ampy -p /dev/ttyUSB0 put bitbound
# Or using Thonny IDE - just copy the bitbound folder to your devicefrom bitbound import Hardware
hw = Hardware()
# BME280 temperature/humidity/pressure sensor
sensor = hw.attach("I2C", type="BME280")
# Read all values
data = sensor.read_all()
print(f"Temperature: {data['temperature']}Β°C")
print(f"Humidity: {data['humidity']}%")
print(f"Pressure: {data['pressure']} hPa")
print(f"Altitude: {data['altitude']} m")from bitbound import Hardware
hw = Hardware()
# Simple LED
led = hw.attach("GPIO", type="LED", pin=2)
led.on()
led.off()
led.blink(times=5)
# RGB LED
rgb = hw.attach("GPIO", type="RGB", pins={"r": 12, "g": 13, "b": 14})
rgb.color = (255, 0, 0) # Red
rgb.color = "#00FF00" # Green (hex)
rgb.color = "blue" # Named colorfrom bitbound import Hardware
hw = Hardware()
# DC Motor with H-Bridge
motor = hw.attach("GPIO", type="DCMotor",
enable_pin=5, in1_pin=6, in2_pin=7)
motor.forward(speed=75) # 75% speed
motor.backward(speed=50)
motor.stop()
# Servo Motor
servo = hw.attach("GPIO", type="Servo", pin=15)
servo.angle = 90 # Move to 90 degrees
servo.sweep(0, 180) # Sweep from 0 to 180from bitbound import Hardware
hw = Hardware()
# Character LCD
lcd = hw.attach("I2C", type="LCD1602")
lcd.write("Hello World!")
lcd.write("Line 2", y=1)
# OLED Display
oled = hw.attach("I2C", type="SSD1306")
oled.text("BitBound", 0, 0)
oled.line(0, 10, 127, 10)
oled.show()from bitbound import Hardware
hw = Hardware()
sensor = hw.attach("I2C", type="BME280")
fan = hw.attach("GPIO", type="Relay", pin=5)
led = hw.attach("GPIO", type="LED", pin=2)
buzzer = hw.attach("GPIO", type="Buzzer", pin=15)
# Temperature thresholds
sensor.on_threshold("temperature > 30Β°C", lambda e: (
fan.on(),
buzzer.beep()
))
sensor.on_threshold("temperature < 25Β°C", lambda e: fan.off())
# Humidity alert
sensor.on_threshold("humidity > 80%", lambda e: led.blink(3))
# Value change detection
sensor.on_change("temperature", lambda e:
print(f"Temp changed: {e.old_value} -> {e.new_value}")
)
# Run event loop
hw.run()# Compound conditions
sensor.on_threshold(
"temperature > 25Β°C AND humidity < 40%",
lambda e: humidifier.on()
)
# Range check
sensor.on_threshold(
"temperature BETWEEN 20Β°C AND 25Β°C",
lambda e: print("Optimal temperature!")
)
# Pressure monitoring
sensor.on_threshold(
"pressure < 1000hPa OR pressure > 1030hPa",
lambda e: alert()
)| Device | Bus | Properties |
|---|---|---|
| BME280/BMP280 | I2C | temperature, humidity, pressure, altitude |
| DHT11/DHT22 | GPIO | temperature, humidity |
| DS18B20 | OneWire | temperature |
| BH1750 | I2C | lux |
| MPU6050 | I2C | acceleration, gyroscope, temperature |
| PIR | GPIO | motion |
| Analog | ADC | value, voltage, percent |
| Device | Bus | Methods |
|---|---|---|
| Relay | GPIO | on(), off(), toggle() |
| LED | GPIO | on(), off(), blink() |
| RGB LED | GPIO | color property |
| NeoPixel | GPIO | fill(), pixel[], rainbow() |
| DC Motor | GPIO | forward(), backward(), stop() |
| Servo | GPIO | angle property, sweep() |
| Stepper | GPIO | step(), rotate() |
| Buzzer | GPIO | beep(), tone(), melody() |
| Device | Bus | Methods |
|---|---|---|
| LCD1602/LCD2004 | I2C | write(), clear(), backlight |
| SSD1306 OLED | I2C | text(), line(), rect(), pixel() |
| 7-Segment | GPIO | digit(), number() |
hw = Hardware()
# I2C with custom pins
sensor = hw.attach("I2C", type="BME280", scl=22, sda=21, freq=400000)
# SPI with all pins specified
device = hw.attach("SPI", type="...", sck=18, mosi=23, miso=19, cs=5)
# UART with custom settings
gps = hw.attach("UART", type="...", tx=17, rx=16, baudrate=9600)hw = Hardware()
# Scan I2C bus
addresses = hw.scan("I2C")
print(f"Found devices at: {[hex(a) for a in addresses]}")
# Auto-discover devices
discovered = hw.discover()
for category, devices in discovered.items():
print(f"{category}: {devices}")BitBound automatically runs in simulation mode when no hardware is detected. This allows development and testing on any computer:
from bitbound import Hardware
# Simulation mode is automatic on desktop
hw = Hardware()
# All devices work in simulation
sensor = hw.attach("I2C", type="BME280")
print(sensor.temperature) # Returns simulated value: 23.5
led = hw.attach("GPIO", type="LED", pin=2)
led.on() # Works without hardwareBitBound understands physical units:
from bitbound.units import parse_value, convert
# Parse values with units
value, unit = parse_value("25Β°C")
print(unit.to_si()) # 298.15 (Kelvin)
# Convert between units
celsius = convert(77, "Β°F", "Β°C") # 25.0Supported units:
- Temperature: Β°C, Β°F, K
- Pressure: Pa, hPa, kPa, bar, mbar, psi, atm
- Humidity: %, RH, %RH
- Length: mm, cm, m, km, in, ft
- Time: ms, s, min, h
- Electrical: V, mV, A, mA, Β΅A, W, Ξ©, kΞ©, MΞ©
- Light: lux, lx
- Frequency: Hz, kHz, MHz
from bitbound.network import WiFiManager
wifi = WiFiManager()
wifi.connect("MyNetwork", "password123")
# Scan for networks
networks = wifi.scan()
for net in networks:
print(f"{net['ssid']} ({net['rssi']} dBm)")
# AP mode
wifi.start_ap("BitBound-AP", "ap-password")from bitbound.network import MQTTClient
mqtt = MQTTClient("my-device", broker="mqtt.example.com")
mqtt.connect()
# Publish sensor data
mqtt.publish("sensors/temp", '{"value": 23.5}')
# Subscribe to commands
mqtt.subscribe("commands/#", callback=lambda topic, msg: print(f"{topic}: {msg}"))from bitbound.network import HTTPClient, HTTPServer
# Client
http = HTTPClient()
response = http.get("http://api.example.com/data")
print(response.json())
http.post("http://api.example.com/data", json_data={"temp": 23.5})
# Server
server = HTTPServer(port=80)
@server.route("/api/temperature")
def get_temp(request):
return {"temperature": 23.5}
server.start()from bitbound.network import WebSocketClient
ws = WebSocketClient("ws://server.local/ws")
ws.on_message(lambda msg: print(f"Received: {msg}"))
ws.connect()
ws.send({"temperature": 23.5})from bitbound.logging import DataLogger, RingBuffer, Storage
# CSV/JSON data logging with rotation
logger = DataLogger("sensor_data", format="csv", max_entries=1000)
logger.log({"temperature": 23.5, "humidity": 65.0})
# Ring buffer for memory-constrained devices
buffer = RingBuffer(100)
buffer.append(23.5)
print(f"Average: {buffer.average()}, Min: {buffer.min()}, Max: {buffer.max()}")
# Persistent key-value storage
storage = Storage()
storage.set("wifi_ssid", "MyNetwork")
print(storage.get("wifi_ssid"))from bitbound import Config
# Load config with board profiles
config = Config(board="esp32")
# Dot-notation access
config.set("mqtt.broker", "mqtt.example.com")
config.set("mqtt.port", 1883)
print(config.get("mqtt.broker"))
# Board-specific pin mappings included
print(config.get("pins.i2c_sda")) # Board-specific defaultAvailable board profiles: esp32, esp32s3, esp32c3, esp8266, rpi_pico, rpi_pico_w
from bitbound.async_support import AsyncDevice, gather_readings
# Async device readings
async_sensor = AsyncDevice(sensor)
temp = await async_sensor.read("temperature")
# Gather multiple readings concurrently
results = await gather_readings([sensor1, sensor2, sensor3], "temperature")from bitbound import PowerManager
pm = PowerManager()
# Deep sleep with timed wake
pm.deep_sleep(duration_ms=60000)
# Light sleep
pm.light_sleep(duration_ms=5000)
# Battery monitoring
level = pm.battery_level()
print(f"Battery: {level}%")
# Watchdog timer
pm.watchdog_enable(timeout_ms=8000)
pm.watchdog_feed()from bitbound.ota import OTAManager
ota = OTAManager(
update_url="https://api.example.com/firmware",
current_version="1.0.0"
)
# Check for updates
if ota.check_update():
print(f"New version: {ota.available_version}")
ota.update()
# Rollback support
ota.rollback()# Install with CLI support
pip install bitbound
# Initialize a new project
bitbound init my-project --board esp32
# List supported boards
bitbound boards
# Scan for connected devices
bitbound scan
# Monitor device readings
bitbound monitor --port COM3
# Deploy to device
bitbound deploy --port COM3bitbound/
βββ __init__.py # Main exports
βββ hardware.py # Hardware manager
βββ device.py # Device base classes
βββ event.py # Event system
βββ expression.py # Expression parser
βββ units.py # Unit handling
βββ config.py # Configuration management
βββ power.py # Power management
βββ ota.py # OTA update manager
βββ async_support.py # Async device support
βββ cli.py # CLI tool
βββ buses/
β βββ base.py # Bus abstraction
β βββ i2c.py # I2C implementation
β βββ spi.py # SPI implementation
β βββ gpio.py # GPIO implementation
β βββ uart.py # UART implementation
β βββ onewire.py # OneWire implementation
βββ devices/
β βββ sensors/ # Sensor implementations
β βββ actuators/ # Actuator implementations
β βββ displays/ # Display implementations
βββ network/
β βββ wifi.py # WiFi management
β βββ mqtt.py # MQTT client
β βββ http.py # HTTP client/server
β βββ websocket.py # WebSocket client
βββ logging/
βββ datalogger.py # Data logging (CSV/JSON/JSONL)
βββ storage.py # Persistent storage
βββ ringbuffer.py # Memory-efficient ring buffer
- ESP32 / ESP32-S2 / ESP32-S3 / ESP32-C3
- Raspberry Pi Pico (RP2040)
- ESP8266 (limited memory)
- STM32 with MicroPython
- Any board running MicroPython or CircuitPython
- Windows, macOS, Linux with Python 3.7+
- Full simulation mode for development without hardware
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
git clone https://github.com/bitbound/bitbound.git
cd bitbound
pip install -e ".[dev]"
pytest tests/MIT License - see LICENSE for details.
- PyPI: https://pypi.org/project/bitbound/
- Documentation: https://bitbound.readthedocs.io/
- GitHub: https://github.com/bitbound/bitbound
- Issues: https://github.com/bitbound/bitbound/issues
- Inspired by modern web frameworks and their declarative approaches
- Built on top of the excellent MicroPython/CircuitPython ecosystems
- Thanks to all the open-source hardware driver contributors
Made with β€οΈ for the Maker community