Background
Concept
Billy is a program (an Arduino sketch) written for ESP32 and ESP8266 microcontrollers (systems-on-chip, SoCs). With this software an MCU can control a load using a digital (ON/OFF) and a pseudo-analog (PWM) output and receive commands over a UART and wireless networks.
In the following the name “Billy” may refer both to the software and a device that runs it (an MCU, a module, a development board, etc.).
Project pages
News and milestones
- 14.01.2024 - the sketch is successfully run on an ESP32-C6 module using 3.0.0-alpha3 version of the ESP32 Arduino core by Espressif Systems.
- 25.06.2024 - v.1.3 released, with numerous major changes made:
- PWM output support added, and thus the project name was changed from “Billy the Relay” to just “Billy”.
- The code was refactored significantly. Among other things, command handler functions are now declared and defined in separate files, not in the .ino file.
- Local TCP server will now restart automatically in case of a lost and subsequently restored connection to an access point.
- Introduced a data format check for the config update commands.
- 28.06.2024 - v.1.3.1 released. Added a PWM output control panel to the web interface.
- 29.07.2024 - v.1.3.2 released. Added a new command that outputs the information on the firmware version and the assigned pins.
TODO list
- Add a Bluetooth Low Energy (BLE) support.
- Add a support for switching between station (STA) and access point (AP) Wi-Fi modes, and afterwards make this switching possible in runtime.
Manual
Load control
Billy uses 2 GPIO pins to control a load - one for a digital output and another one for a PWM output.
A typical load for Billy would be:
- For a digital output - an electromechanical relay controlled via an octocoupler or a transistor driver.
- For a PWM output - an electric motor controlled via a MOSFET driver.
- For both output types - an LED (usually used for testing purposes).
Indicator LED
Billy uses a single additional GPIO pin for a digital control over an indicator LED. The LED indicates a Wi-Fi connection status: a low-frequency blinking for an ongoing connection attempt, a high-frequency blinking for a successful connection attempt.
Combined outputs
A combination of digital and PWM outputs in one pin is to be avoided, since it will most probably render digital output inoperable.
A combination of digital outputs for a load control and an indicator LED control is safe, but will effectively turn that same LED into the load to be controlled.
Disabling an output
Specifying in config_general.h
a respective #define
directive value as 0 disables an output.
Communications
Billy can receive commands:
- Over UART through a cable connection.
- Over Wi-Fi as a local TCP server, e.g. sent by a classic Unix utility
netcat
(nc
) or by an Anroid app like Serial Wi-Fi terminal. - Over Wi-Fi as a local HTTP server, e.g. from a web browser (sketch provides a simplistic web interface) or a different app capable of sending HTTP requests.
- Over Bluetooth Classic as a slave device1, e.g. from a desktop Bluetooth terminal or an Android app like Serial Bluetooth Controller.
- Over Wi-Fi as a TCP client (inter alia via Internet). To do so Billy sends requests to a custom-programmed TCP server and receives commands as a response (this implementation is described in detail below).
Billy works within a Wi-Fi network in a station (STA) mode. Billy uses an Internet access provided by an access point.
IoT: Billy as a TCP client and control over Internet
A remote TCP server (an IoT server) to which Billy sends requests must be able to:
- Send messages (strings) with valid commands in a response to Billy’s requests (e.g. string
"AT+SERVETOPIC"
). - Update a message prepared to be sent to Billy according to remote commands sent by other devices.
Say, an IoT server receives “turn load ON” command from your web browser and prepares to send AT+LOADDIGITAL=ON
string in a response to Billy’s next request. Billy receives the string and puts it into a buffer to check for valid commands, as it would do with a message received over any other communication channel.
Here you can find an example code for a Linux server that works in a described fashion written in C language. It’s been written with an interaction with Billy and similar devices in mind.
Storing configs
Billy’s operations rely heavily on a flash memory storage built into the MCU or into the integrated module. When you specify configs (an SSID, a password, a port number, etc.), they become saved in the storage and thus you don’t need to assign them again in case of a device reboot.
A current load state (a current digital output level or a PWM duty cycle) isn’t considered to be a config though, and thus it’s not stored in the flash storage. Any load will be turned off in case of a device reboot.
Complete command list
Refer to config_сmd.h
to see all available commands.
Quickstart
Code configuration and upload, minimal runtime configuration and initial testing are made as follows:
- In the file
config_general.h
:
- Specify a digital output pin using directive
#define DIGITAL_OUTPUT_PIN
. - Specify a PWM output pin using directive
#define PWM_OUTPUT_PIN
. - Specify an indicator LED control pin using directive
#define WIFI_INDICATOR_LED_PIN
. - If your digitally controlled load is turned on by a low logical level on a respective pin, uncomment the directive
#define INVERTED_DIGITAL_OUTPUT
. - Comment out the directive
#define BT_CLASSIC_PROVIDED
if your ESP32 device doesn’t support Bluetooth Classic or you just don’t want to use this technology. Ignore for ESP8266.
- Make sure your Arduino IDE (or Arduino SDK for a third-party IDE) has an appropriate core for ESP32 or ESP8266 by Espressif Systems.
- Compile the sketch and upload it to your device.2
- Turn on your device and connect to it by a cable (via USB-UART adapter or, if supported by your device, via UART over native USB).
- Open a serial terminal and set an appropriate baud rate (115200 by default).
- Send command
AT+LOCALSSID=<value>
to specify your Wi-Fi network access point SSID. - Send command
AT+LOCALPSWD=<value>
to specify your Wi-Fi network access point password. - Send command
AT+LOCALPORT=<value>
to specify a port number to be used by Billy as a local TCP server. - Reset Billy or send
AT+ALLCONNRST
command. - Make sure your device has established a connection to your Wi-Fi network.
- Send commands
AT+LOADDIGITAL=ON
,AT+LOADDIGITAL=OFF
,AT+LOADDIGITAL=TOGGLE
andAT+LOADPWM=<value from 0 to 255>
over a UART and via a Wi-Fi serial terminal, try controlling your load in different ways. - Start any web browser and insert Billy’s local IP. Try controlling a load using the web interface.
Test Bluetooth Classic functionality (if supported) as follows:
- Send command
AT+BTCLASSICDEVNAME=<value>
to specify Billy’s name as a slave device. - Send command
AT+BTCLASSIC=ON
to turn Bluetooth Classic functionality on. - Try using aforementioned load control commands.
Test IoT functionality as follows:
- Send command
AT+IOTIP=<value>
to assign an IP address of a remote server that will send commands to Billy. - Send command
AT+IOTPORT=<value>
to assign a port number of a remote server opened for requests from Billy. - Send command
AT+IOTREQMSG=<value>
to assign a request message that suits the remote server’s settings. - Send command
AT+IOT=ON
to turn IoT functionality on. - Make sure Billy receives commands from the remote server.
General notes on code
Sketch layout
Breaking a sketch into a basic .ino
file and local modules (pairs of .h
and .cpp
files) may not be a very popular approach for writing an Arduino sketch (multiple .ino
files concatenated by the IDE or packing functions into libraries seem to be more widespread), but it has an advantage of a more transparent code structure.
Global variables
Introduction of additional global variables is generally avoided since the flash storage serves as a “common space” for various sketch functions. Exceptions are made for certain flags and class instances (e.g. WiFiServer
, BluetoothSerial
, etc).
Local modules and wrapper functions
Method calls are mostly packed into wrapper functions declared and defined in the local modules. It’s handy since method calls are usually accompanied with related lines of code.
Local modules mostly do not refer to each other. Instead, their wrapper functions are called in the code listed in Billy.ino
and cmd.cpp
files. This allows for an easier sketch customization.
Expected questions
Why not MQTT? It’s so well-suited for IoT!
Because of an idea of more flexibility and not sticking to a particular OSI layer 7 protocol.
Why “Billy”?
A reference to an eponymous programmer parrot, a protagonist of meme videos by Mr. P Solver.
What about security?
Within your Wi-Fi network your best protection is your access point password. You can even run additional Wi-Fi network on the same router if you want to separate Billy and other IoT devices from your regular consumer electronics.
As for Bluetooth (and lack of PIN code for BT access), you can change a command prefix syntax in config_cmd.h
file and make commands look like ATMYPSWD+LOADDIGITAL=TOGGLE
, effectively introducing a password specific to your device.
Описание
Arduino sketch for ESP32 and ESP8266 microcontrollers (systems-on-chip, SoCs). Controls load using digital and PWM output. Receives commands and communicates over UART, Wi-Fi and Bluetooth.