[TOC]
An ESP8266 based, WS2812 night light with wifi based (web) configuration and MQTT control.
Like PixelBall ( Gitlab / Github ) on steroids. Whilst PixelBall is simple and pre-programmed, NightyNight allows for initial setup to be done via a web interface, accessible by connecting to it, whilst it runs as a WiFi access point. Once configured, it connects to an MQTT server and accepts commands.
Designed as a Night Light for my Son.
Initially, NightyNight (v1) was designed around an ESP8266 running nothing but the raw code itself. I liked it because I could write it in C and just have it run on the hardware directly.
Unfortunately the development model for this method is slower - somewhat
similar to AVR - where you write some code, flash to test, debug etc. I was
also originally working on ESP-01
's and therefore didn't have much choice in
what the stack was either way due to the constraints it posed.
I eventually found the WEMOS Mini-D1
boards ( and
their clones
) and thought they were too good to miss. Whilst costing an extra 50%, it's
MORE than worth it considering what you get, not forgetting too that the 50%
more is a whole $1.50 (AU).
With the new board specs I could now look at heavier base. Additionally, all the extra pins would allow for greater functionality. I don't recall how now, but I stumbled on MicroPython and thought it would be a good tool for the job, thus NightyPyte began it's life.
In version 1, the ESP-01 expects 3.3v, so a power circuit needed to be used to drop the 5v USB input power down to acceptable limits. With the Mini-D1, this is done for us.
In version 1, the interface I was designing was based around using a single button for input, and only the coloured LED as output. With the extra pins and an OLED screen I had laying around, I will now have an actual interface on a screen, driven by 1, 2 or even 3 buttons.
- Download the latest ESP8266 build of MicroPython image; eg. For v1.18, released 2022-01-17:
wget https://micropython.org/resources/firmware/esp8266-20220117-v1.18.bin
- Erase the flash:
esptool.py --port /dev/ttyUSB0 erase_flash
esptool.py v1.2
Connecting...
Running Cesanta flasher stub...
Erasing flash (this may take a while)...
Erase took 10.2 seconds
- Flash the image:
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash -fm dio --flash_size=detect 0 esp8266-20220117-v1.18.bin
esptool.py v1.2
Connecting...
Auto-detected Flash size: 32m
Running Cesanta flasher stub...
Flash params set to 0x0240
Writing 618496 @ 0x0... 618496 (100 %)
Wrote 618496 bytes at 0x0 in 13.9 seconds (354.8 kbit/s)...
Leaving...
- At this point you should be able to connect to the device via picocom(8) or screen(1) etc:
baud=115200
stty \
ispeed ${baud} \
ospeed ${baud} \
min 1 \
time 10 \
-icrnl -onlcr \
-opost -isig \
-icanon -iexten \
-echo -echoe \
-echok -echoctl \
-echoke \
</dev/ttyUSB0 && picocom -i --baud ${baud} --imap lfcrlf /dev/ttyUSB0
- Once connected, pressing ENTER should show the Python interactive prompt:
>>>
-
Disconnect.
-
If you don't have your own config yet, create one:
cp -ai config.json{.TEMPLATE,}
- Run the
minify.bash
script to shrink the HTML/CSS files, eg.
./minify.bash
Minifying files...
web_nn.SOURCE.css (3944) --> web_nn.MINIFIED.css (3280): 17% reduced
- Use something like pyboard.py or uPyLoader or ampy to transfer the NightyNight python files directly to the device. eg.
pyboard.py --device /dev/ttyUSB0 -f cp \
config.json app.py globs.py ui.py utils.py \
debug.py config.py main.py boot.py \
web_server.py web_pyhtml.py web_nn.css \
index.py index.pyhtml \
web_reboot.py web_reboot.pyhtml \
web_config_wifi.py web_config_wifi.pyhtml \
favicon.ico favicon.16x16.png favicon.32x32.png \
:
- don't forget the
:
at the end there - it's the destination (the device)
On boot of the ESP8266, Boot (boot.py
) script will run.
boot.py
does the following:
- Turns off wifi;
- Turns off blue status LED (connected to Pin 2 (D4);
- Check if the Button (connected to Pin 12 (D6)) is being held down;
- If button is held:
- Start flashing at increasing speed;
- If still held after 8 "rounds":
- Set
globs.mode
toMODE_DEBUG
- GOTO Debug Mode;
- Set
- If NOT still held after 8 "rounds":
- Set
globs.mode
toMODE_CONFIG
- Set
- Load configuration (from
config.json
); - If config contains valid wifi settings:
- Turn on wifi client and try to connect;
- If config does NOT contain valid wifi settings:
- Set
globs.mode
toMODE_CONFIG
- Set
- If we did not connect successfully:
- Configure own Access Point (AP);
- Set
globs.mode
toMODE_CONFIG
- Run Main (
main.py
)
main.py
does the following:
- Checks the
globs.mode
variable and routes to either:MODE_CONFIG
: Config Mode (config.py:config.launch_app()
)MODE_APP
: App Mode (app.py
)
app.py
does the following:
- Turns the blue status LED on when the button is pressed;
- Turns the blue status LED off when the button is not pressed;
In certain cases, Config Mode (config.launch_app()
) can be activated.
Config Mode can be entered when either connected to an existing network, or when running it's own Access Point (AP). The AP will run when either:
- The connection failed in some way (timed out, password wrong, AP not found etc); or
- Or, the user selected Config Mode (by holding the button down on boot for less than 8 seconds) and then selected own AP (by pressing the button during the connection (increased pulsing LED) phase).
When running its own AP, the device sets its SSID to
"NightyNight-<hexid>
-<ip_address>
", where:
<hexid>
- The 8 hexidecimal unique identifier for the device; and<ip_address>
- The IP address that the device will be listening on, once you connect to it.
The password for this AP will be "configure<hexid>
", where:
<hexid>
- The 8 hexidecimal unique identifier for the device.
Once in Config Mode, you can connect to the device's AP (if applicable)
then direct your browser to http://<ip_address>
to configure the device.
Configurable options are:
- Network details
- SSID
- Password
- MQTT details
- MQTT Server
- MQTT Port
- MQTT Topic
Once these are saved, a reboot will occur.
In certain cases, Debug Mode (debug_mode()
) can be activated.
Debug Mode does the following:
- Turns on blue status LED
- Launches the REPL interface (on UART 0)
The reason I seemingly overengineered a html templatey thing is mainly due to the ESP8266's (well, D1 Mini really) memory limitations. With very limited memory, even a basic page couldn't easily be stored as a string in a module and served. Once I knew I had to stream them off "disk", I figured I'd add a way to easily insert variables within the content. Good idea? Dunno yet but it works :)