-
Notifications
You must be signed in to change notification settings - Fork 14
Firmware
- Summary
- Building
- Flashing the firmware
- Summary of program execution
- Define values
- RTC memory
- EEPROM
- Admin mode
- WIFI connectivity
- GET request
- Error reporting
- Serial debugging
- Battery saving measures
- Why things are the way they are
The Wall-Ink firmware was written for the ESP8266 using the Arduino stack. It downloads an image, displays it to a screen, and sleeps for a while.
The firmware was built using Arduino IDE 1.8.7. It used ESP8266 board version 2.4.2. It used commit af110914ef6618a974a062a860185aeeb0dc9e67 of the library found here: https://github.com/caedm/GxEPD. It used the Adafruit GFX library version 1.2.3. Later versions of these libraries may work, but these are included for reference in case they don't.
First, you need to install the Arduino IDE. To do this, download the IDE from Arduino's website: https://www.arduino.cc/en/Main/Software
TIP: For Linux users, do not install the IDE with your package manager; you will almost certainly end up with an older version!
Download the GxEPD library from https://github.com/caedm/GxEPD as a .zip file
Open the Arduino IDE
Go to Sketch -> Include Library -> Add .ZIP Library
and add the GxEPD library.
Go to Sketch -> Include Library -> Manage Libraries
and add the Adafruit GFX Library
Go to File -> Preferences
and paste http://arduino.esp8266.com/stable/package_esp8266com_index.json
into the Additional Board Manager URLs
field and hit 'okay'
Go to Tools -> Board: -> Boards Manager
and search for ESP8266; install version 2.4.2
Go to Tools -> Board:
and select Generic ESP8266 Module
Go to File -> Open
and select the stream_screen.ino
sketch in door-display/Arduino/stream_screen
Go to Sketch
and select Verify/Compile
You have now built the firmware!
To flash the firmware, follow the following steps:
- Open the Arduino IDE
- Connect your usb-serial adapter to your computer and to the Wall-Ink display
- Go to
Tools -> Port
and select the correct port - Use the switch on the PCB to put the device in flash mode (if using a dev board, you will need to hold the flash button)
- While the device is still in flash mode, hit the reset button
- Click the
Upload
button in the top-left corner of the screen, which looks like an arrow
When the ESP8266 comes out of deep sleep, the following sequence of events happens:
The ESP8266 boots. This takes about 0.3 seconds.
The ESP8266 checks to see if it's in admin mode. If it is, it starts broadcasting as a wifi access point.
If the ESP8266 is using information stored from a previous connection, connecting to WiFi takes the ESP8266 1-5 seconds. Most of this time is spent waiting for a DHCP lease. Otherwise, it takes about an extra 10 seconds for the ESP8266 to scan the different access points to determine which has the best connection.
If the file is a dynamically generated file, this takes 1-3 seconds. This is because the server needs to go through the process of contacting the relevant databases and generating it; more information on that process can be found at the [https://github.com/caedm/wall-ink-server Wall-Ink-Server github repo]. If the file is a static file, it usually takes 0.1-0.5 seconds.
The beginning of the image file contains certain useful data: the current time, the next time the ESP8266 should update, and a hash of the image. The ESP8266 first checks the image hash against the last one it stored. It then checks the current time against the time it expects it to be, and adjusts its sleep time accordingly. It then stores these values. If the image hashes matched, it immediately goes to sleep. Otherwise, it downloads the rest of the file. This step takes <0.1 seconds.
The ESP8266 initializes the display. It then downloads the rest of the file in chunks, and decompresses each chunk into an image buffer before moving on to the next one. This process usually takes about .4 seconds. Note that it only draws the black pixels; this is because the display buffer is pre-initialized to white. This optimization saves about .6 seconds.
After the entire image has been downloaded, the EPS8266 hashes it. It also hashes the image key. It then hashes the two hashes together and compares the result against the hash that came with the image. If they don't match, it is assumed that the image was served by an impostor server and the device sleeps. This step takes <0.1 seconds.
The ESP8266 updates the screen. On a 7" Waveshare display this takes 5 seconds. The time for the 4" Waveshare display is unknown.
The version of the firmware that gets reported to the server
Possible values are integers from 0-4. Describes the screen layout.
Whether admin mode is enabled, 0 or 1.
The longest the ESP8266 will try to deep sleep for at a time, in seconds. It might not be safe to increase this.
The shortest the ESP8266 will try to deep sleep for at a time, in seconds.
The number of seconds the ESP8266 waits after the first crash before trying again. This number actually gets multiplied by 4, so a value of 15 really means 60.
The number of seconds the ESP8266 adds to its sleep time by default to adjust for time drift in the RTC. This doesn't matter too much, because the ESP8266 will adapt this value as it runs. Note that this time drift compensation won't work well for sleep times of more than a few hours.
This is the password you'll need if you want to connect to the device when it is in admin mode
This is the image key your device will start with. It can be changed later in admin mode. Note that if you change it here, you'll also want to change it in compessImage.cpp on the server.
The RTC memory is memory that persists even when the ESP8266 is in deep sleep mode. There is only a half-kilobyte of RTC memory, but it is still very useful. This is because unlike flash memory, RTC memory can withstand a virtually unlimited number of reads and writes. We store the following variables in RTC memory:
This is a hash which is used to verify that the RTC memory hasn't been corrupted during a deep-sleep cycle, or if the ESP8266 is booting for the first time.
This is the current time, in Unix time.
This is the next time the ESP8266 plans on waking, in Unix time
This is the estimated amount of time the ESP8266 thinks has elapsed since it started sleeping
This is the number of seconds added to the sleep time to account for time drift in the RTC.
This is the number of seconds the ESP8266 will sleep for after a crash. It is multplied by 4 before each sleep cycle, but will never be allowed to exceed 1 day.
This is the wifi channel the ESP8266 last connected to.
This is the MAC address of the last wireless access point the ESP8266 connected to.
The RTC memory can only store memory in 4-byte chunks, so a 1-byte padding variable was added. Just to be clear, nothing is stored here and this variable is not used. Don't remove it though, or the firmware will break.
This is the SSID of the last wireless access point the ESP8266 connected to.
This is the password of the last wireless access point the ESP8266 connected to.
This is a hash of the last image to have been downloaded and displayed to the screen. This is referred to as "chunk 4" in the wink file header.
This is the last unreported error to have occurred. It will upload this next time it contacts the server, and then reset the value to 0.
The EEPROM is persists even when power is disconnected or the device is flashed, but has a limited number of reads & writes. This makes it optimal for saving things such as settings. Options set while in admin mode get stored here.
A boolean which stores information about whether the 2nd wifi profile is active. This currently does nothing.
Contains the location of the image file being downloaded. Parameters get stuck on the end of this URL in the setURL function.
Contains the first wifi profile's ssid
Contains the second wifi profile's ssid
Contains the first wifi profile's password
Contains the second wifi profile's password
Contains the key used to validate images received from the server
A boolean which stores whether debug mode is active
A sha1 hash of the rest of the data stored in EEPROM, used to detect memory corruption
When GPIO4 is pulled low (when the switch on the PCB is set to Admin), the ESP8266 will boot into admin mode. This means that it will set itself up as a wifi access point with an ssid of BYU DD
followed by its MAC address. It will then start a webserver on 192.168.4.1
Visitors to the web page hosted there will be able to change the settings stored in the EEPROM.
For more information, see the Admin-mode wiki page.
At the top of the sketch, you may define up to 2 default SSIDs and passwords (these can be changed in admin mode. The Wall-Ink device will, upon booting for the first time, scan for nearby wifi hotspots. It will connect to whichever has the better signal, and will continue trying to connect to the same access point after each reboot.
After the Wall-Ink display connects to wifi, it makes an http GET request that looks something like this: yourbaseurlhere?mac_address=ECFABC0DBAED&firmware=2.07c&error=0&voltage=3.05
When there is an error, it is saved to RTC memory and reported next time the device checks in. The error codes are then logged to www/log.txt
The key is at https://github.com/caedm/wall-ink/blob/master/stream_screen/error_codes.txt
When the device is in admin mode, there is an option to enable debug mode. When this option is enabled, you can use a USB to Serial adapter to connect to the device at 115200 baud to get some debug information from it.
Several Battery saving measures have been taken. Here are a few:
When the ESP8266 connects to WiFi, information about that connection is saved in RTC memory. This makes the next connection significantly faster. We would be able to save even more time by saving the IP address, thus skipping DHCP. Unfortunately there doesn't seem to be an easy way to get the DHCP lease time, making this dangerous. It is thus not implemented, but if you can find a way to do that it would speed up the process of connecting by 3-5 seconds, making an enormous difference in the battery life of these devices.
If the ESP8266 downloads an image with a hash which matches the hash stored in rtcMemory.imageHash, it will immediate go to sleep instead of downloading the remainder of the file and flashing the screen
It is about 1-1.5 seconds faster to serve static images to the ESP8266 than it is to dynamically create the image when the ESP8266 demands it. There is currently support for static images to be manually added on the server, but if there were a way to generate the images just in advance of the ESP8266's checkins that would be beneficial.
When the display is initialized, the display buffer is initialized to be white. There is therefore no reason to write the white pixels to that buffer. This optimization saved a little more than 0.6 seconds on the 7" screen.
Using WiFiMulti takes way longer than simply using WiFi.begin. Can't remember how big the difference is, but I think it's more than 1 second.
For some stupid reason, the delay()
and yield()
commands crash the ESP8266 unless they are called directly from within loop()
or setup()
The ESP8266 will crash even if these commands are called from other functions which are called from within loop()
or setup()
If they are not called, the watchdog timer reboots the ESP8266. This has encouraged me to write code in a worse way. Sorry about that.
The ESP8266 just doesn't have enough RAM for that.
Why is the ESP8266 set to sometimes ignore its saved WiFi information, and to instead do a full AP scan?
Without this, the ESP8266 would be stuck forever to whichever AP it first attaches to, unless that AP disappears entirely. This way the device can be moved down the hall a little ways and the ESP8266 will, within a few dozen power cycles, pick the new closest AP.
It saves the connection info to flash for every reboot. This kills the flash.