Welcome to the Bird-IoT Platform! This project is designed to run on ESP32-based devices with a highly modular approach. It provides a system of plugins (each derived from a common interface) and a task system that allows easy scheduling and concurrency.
Note: This project is not seeing any regular updates. The intent of this repo is to share the concepts and code within.
Note: Be sure to configure your authorization headers. Look for TODO_ in the codebase
Get the Docker Image
docker pull ajcasagrande/bird-iot:esp-idf-v4.0.2Build a binary for the ttgo-display project
./docker-make.sh env=ttgo-displayThe Bird-IoT Platform is built around a central IotApp singleton that:
- Manages device profiles (GPIO pin assignments, hardware specifics).
- Initializes core services (NVS, Wi-Fi, MQTT, safe mode, etc.).
- Registers plugins (each plugin is a self-contained module providing some feature or driver).
- Coordinates tasks (each plugin can schedule its own asynchronous task, e.g. for polling hardware or performing repeated actions).
Key Points:
- Safe Mode: The platform supports a “safe mode” that can limit or bypass certain features if too many failed boots occur.
- Profiles: A JSON or struct-based system describes board-specific pins (e.g., SDA, SCL, servo pins, etc.).
- Startup Flow:
- System initializes
- NVS is set up
- Boot counters are incremented
- Wi-Fi and MQTT connect
- Plugins are setup (configure) and then start (run)
- Tasks begin periodic or event-driven execution
IotPlugin is the base class. Each plugin:
- Has a name (e.g.
"Console Plugin"). - Implements any of these optional lifecycle hooks:
bool setup()— configure, allocate resources, and do any required initialization.bool start()— start actual execution (e.g. begin tasks, open sockets, etc.).void onWifiConnected()/onWifiDisconnected()void onMqttConnected()/onMqttDisconnected()bool handleMqttMessage(const char *topic, int id, const char *cmd, const JsonDocument& json)bool handleCommand(const std::string &cmd)— for console or remote command handling.void addFramesAndOverlays()— for UI-based plugins that draw on a display (OLED, TFT, etc.).
Plugin Registration:
- Plugins are registered in
IotAppviaApp.registerPlugin(...). - Once registered, they will be automatically
setup()andstart()in the platform’s initialization sequence. - A plugin can also register or spawn its own tasks for scheduling repeated or event-driven actions.
Most plugins rely on TaskClass (or Task<T> templated class) to schedule:
- Periodic tasks with intervals (e.g. 1 second).
- Single-shot tasks that run once after an initial delay.
- Paused/resume functionality.
- Pinning to a specific ESP32 core (
coreId). - Exact timing vs. yield-based timing.
Key APIs in TaskClass:
configure(interval, stackSize, priority, coreId, useExactTiming, allowDuringOTA, initialDelay)start() / pause() / resume() / remove()_executeIfAllowed()— an internal guard to skip runs if paused or in an OTA update.
Any plugin can:
- Subclass
TaskClass(or useTask<PluginType>) to create a periodic job. - Start the task inside
setup()orstart(). - Implement the
execute()routine that is called each time the task fires.
Below is a summary of all major plugins/features discovered in this project. Many are conditioned by #if CONFIG_ENABLE_... macros in sdkconfig or Kconfig defaults.
Note: Not all plugins must be enabled at once. Each plugin can be individually toggled via build config to keep firmware lean.
- Macro:
CONFIG_ENABLE_CONSOLE_PLUGIN - Description: Provides a serial console interface (via UART). It integrates with linenoise for command history, completion, etc.
- Features:
- Supports live command input on serial
- Basic console commands (
help,meminfo, etc.) - Ties into the
CommandPluginfor additional commands
- Macro:
CONFIG_ENABLE_COMMANDS_PLUGIN - Description: Allows execution of text-based commands from multiple sources (serial console, websockets, MQTT messages, etc.).
- Features:
- Provides a unified
handleCommand(const std::string&). - Built-in commands:
help,tasks,heap,ip,vcc,reset,version, and more. - Simplifies creating new commands in a centralized place.
- Provides a unified
- Macro:
CONFIG_ENABLE_BUTTON_PLUGIN - Description: Manages button inputs (GPIO pins). Each button is an instance of
IotButton. - Features:
- Schedules a background task to handle debouncing or event polling
- Allows hooking custom callbacks on button press
- Macro:
CONFIG_ENABLE_SECURE_OTA_PLUGIN - Description: Handles firmware over-the-air updates, with optional:
- HTTPS downloads
- MD5 or SHA256 checks
- Rollback support and verification
- Workflow:
- On receiving an MQTT message with OTA details, it suspends non-critical tasks, downloads new firmware, and if successful, reboots into the new image.
- Macro:
CONFIG_ENABLE_CAMERA_SERVER_PLUGIN - Description: Sets up a minimal HTTP server that can serve camera frames or respond to a basic route (
/).
- Macro:
CONFIG_ENABLE_HTTPS_SERVER_PLUGIN - Description: Embedded HTTPS server built on httpsserver library.
- Features:
- Creates a self-signed certificate
- Serves static or dynamic content over TLS
- Example handler for root
/endpoint
- Macro:
CONFIG_ENABLE_BLE_BEACON - Description: Configures an iBeacon on the ESP32 that broadcasts a custom UUID, major/minor, etc.
- Features:
- Use
btStart()internally - Advertise indefinite iBeacon data
- Optionally releases BT Classic memory
- Use
- Macro:
CONFIG_ENABLE_BLE_KEYBOARD_PLUGIN - Description: Emulates a BLE HID keyboard that can send key presses (including media keys).
- Features:
- Uses NimBLE or Bluedroid under the hood
sendKey(),sendMediaKey()for controlling remote devices from MQTT commands- Pairing/bonding optional
- Macro:
CONFIG_ENABLE_NIMBLE_PLUGIN - Description: Enables the NimBLE stack for BLE operations (lightweight alternative to Classic).
- Features:
- Creates a basic iBeacon advertisement
- Hooks into the standard NimBLE host tasks
- Macro:
CONFIG_ENABLE_MDNS_PLUGIN - Description: Registers an mDNS service so the device can be discovered locally via
deviceid.local. - Features:
- Optionally includes additional mDNS service TXT records (version, environment, etc.)
- Macro:
CONFIG_ENABLE_I2C_OLED_DISPLAY_PLUGIN - Description: Manages a SSD1306/SH1106 or similar small OLED display over I2C.
- Features:
- Uses
OLEDDisplayUifor frames and overlays - Automatic progress bar for e.g. OTA
addFramesAndOverlays()allows each plugin to draw a portion of the UI
- Uses
- Macro:
CONFIG_ENABLE_SPI_DISPLAY_PLUGIN - Description: Uses spilcd driver for ST7789 or similar SPI-based LCD.
- Features:
- Screen saver example
- Optional backbuffer
- Rotation settings
- Macro:
CONFIG_ENABLE_TFT_DISPLAY - Description: Manages a TFT_eSPI-driven display (e.g. ILI9341, ST7735, ST7789).
- Features:
- Basic UI overlays, progress bars, screen saver
- Adjust backlight brightness
- Rotation toggles
- Macro:
CONFIG_ENABLE_GARAGE_PLUGIN - Description: Simple garage door opener plugin using a relay or GPIO toggle.
- Features:
- MQTT command to
toggleordouble_toggle - Delays to simulate relay press
- MQTT command to
- Macro:
CONFIG_ENABLE_GPS_PLUGIN - Description: Reads data from an attached GPS (e.g. UART NMEA).
- Features:
- Uses
TinyGPS++ - Maintains average speed, max speed, satellite info
- Can draw overlays on TFT display
- Uses
- Macro:
CONFIG_ENABLE_HOBBYWING_TASK_PLUGIN - Description: Interacts with a Hobbywing ESC or car ESC over a UART or TTL signal.
- Features:
- Captures raw data frames from ESC
- Example logging of data (RPM, voltage, etc.)
- Macro:
CONFIG_ENABLE_IR_PLUGIN - Description: Transmits 38kHz IR signals for devices (e.g. TVs or AC).
- Features:
send_nec()andsend_lg_ac()examples- Mark/space approach at 38kHz using raw GPIO toggling
- Macro:
CONFIG_ENABLE_RF_RAW_PLUGIN - Description: Captures raw edges from an RF receiver pin, logs timing for further decode.
- Features:
- Attaches interrupt
- Collects microsecond-level timing for analysis
- Macro:
CONFIG_ENABLE_RF_SNIFFER_PLUGIN - Description: Uses a 433/315MHz receiving library to decode pulses into recognizable codes (like “RC Switch” style codes).
- Features:
- Publishes discovered codes via MQTT
- Optional raw capturing
- Macro:
CONFIG_ENABLE_RF_TRANSMIT_PLUGIN - Description: Transmits 433/315MHz codes (like RC Switch).
- Features:
- MQTT command to send a code with a given bit length, pulse length, protocol, sync factors
- Macro:
CONFIG_ENABLE_HUNTER_FAN_PLUGIN - Description: Sends Hunter Fan-style remote codes via a certain timing pattern on a GPIO.
- Features:
- Supports a specific pulse logic for older Hunter fans
- Macro:
CONFIG_ENABLE_ACURITE_SENSOR_PLUGIN - Description: Work in progress to decode Acurite temperature/humidity sensor signals in the 433MHz band.
- Macro:
CONFIG_ENABLE_SERVO_INPUT_PLUGIN - Description: Uses RMT to read servo pulses from an RC receiver.
- Features:
- Multiple channels
- Each channel has a
duty()or pulse width in microseconds - Notifies changes via a background task
- Macro:
CONFIG_ENABLE_SNTP_PLUGIN - Description: Synchronizes system time via NTP servers.
- Features:
- Automatic updates after Wi-Fi is connected
- Timezone support, time sync callback
- Can show a time overlay on TFT or OLED
- Macro:
CONFIG_ENABLE_SPIFFS_PLUGIN - Description: Initializes SPIFFS filesystem.
- Features:
- Formats on failure (if needed)
- Logs total/used space
- Macro:
CONFIG_ENABLE_SECURE_WEBSOCKET_PLUGIN - Description: Creates an HTTPS server with websocket capability on port 443, using a certificate/key stored in the firmware.
- Features:
- 2-way TLS
- Real-time data over wss
- Macro:
CONFIG_ENABLE_WEBSOCKET_COMMAND_PLUGIN - Description: Allows receiving console-like commands over a websocket (usually on some port).
- Features:
- Forwards lines to the
CommandPlugin::handleCommand()
- Forwards lines to the
- Macro:
CONFIG_ENABLE_WEBSOCKET_SERVER_PLUGIN - Description: Similar to above, but might run on a simpler or different port (e.g.
81or8081). - Features:
- Echo or dispatch commands
- Maintains a map of connected clients
- Macro:
CONFIG_ENABLE_WEBSOCKET_CLIENT_PLUGIN - Description: Connects to a remote wss server from the device as a client.
- Features:
- Periodic keepalive
- Data exchange in real-time
- Macro:
CONFIG_ENABLE_CONNECTION_STATS_PLUGIN - Description: Tracks Wi-Fi and MQTT connect/disconnect events:
- Number of drops
- Time of last connect
- Optionally logs or publishes stats
- Macro:
CONFIG_ENABLE_RSSI_STATS_PLUGIN - Description: Periodically polls Wi-Fi RSSI (signal strength).
- Features:
- Publishes min / max / average RSSI after a configurable interval
- Optional logs
- Macro:
CONFIG_ENABLE_WIFI_SCAN_PLUGIN - Description: Scans for nearby APs either on each channel or a single channel, then publishes results over MQTT.
- Features:
- Passive scanning
- Displays SSID, RSSI, channel, auth mode
- Macro:
CONFIG_ENABLE_EXAMPLE_BASIC_PLUGIN - Description: A minimal plugin example with basic
setup()/start()stubs.
- Macro:
CONFIG_ENABLE_EXAMPLE_TASK_PLUGIN - Description: Demonstrates scheduling an internal
TaskClassto run periodically.
- Macro:
CONFIG_ENABLE_EXAMPLE_SENSOR_PLUGIN - Description: Illustrates a sensor plugin pattern with periodic polling and publishing (two tasks):
pollTaskpublishTask
This platform is highly modular. Each plugin can be toggled on/off via build flags (CONFIG_ENABLE_*).
Most plugins use their own TaskClass for concurrency.
To add a new feature:
- Subclass
IotPlugin. - Implement
setup(),start(), and any relevant hooks. - Optionally create a
Task<YourPlugin>for periodic or event-based logic. - Register the plugin with
App.registerPlugin(new YourPlugin());.
Complete IoT platform for the ESP32 using ESP-IDF and FreeRTOS.
- IR
- RF 433mhz / 315mhz
- Hunter Fan Remote
- Temp / Humidity
- LG Portable AC
- Garage Opener
- Door Sensor (Reed Switch) x2
mkdir -p ~/esp
cd ~/esp
git clone --recurse-submodules --branch v4.0.2 [email protected]:espressif/esp-idf.git
cd esp-idf
./install.shD0appears to control an on board RED led on the NodeMCU boardsD4controls the very bright blue light on the ESP-12E module
Plug one wire into GND and the other wire into a GPIO pin. D0 and D4 are some known working GPIO pins, however not all GPIO pins will work. Typically the reed switches I have are normally open switches, but this can be confirmed by running the software and checking.
- Not all GPIO pins are good for i2c. Some known good ones are
D1,D2,D3,D4,D5,D6. - Auto detection is supported, just setup the
SDA_PINandSCL_PIN
Wire into the Normally Open Pins which is the middle pin and the pin not connected by a white line. First and second pins if looking from the front.
- Auto detect using i2c
- Connect GND, VCC to 5v (Vin), and the Digital Output pin to
D2on the nodemcu.- From left to right it is:
GND, D2, __, Vin, ................. __, __, __, (optional antenna)
- Create a new basic plugin
make create-plugin name=<...> folder=<...> - Create a new Task plugin
make create-task-plugin name=<...> folder=<...>
Note: name should include the suffix of Plugin or whatever.
Note: folder is the sub-folder within plugins folder the plugin is to be put.
./docker-make.shmake udev-rules