I’ve been experimenting with MicroPython 1.9.3 on some Wemos D1 Mini boards I have. I suspect they’re clones, but seem to work fine. My project is to port a MQTT data logging temperature and light sensor that I created in Arduino a while back. I’ve encountered a few details that might be helpful to others.
First, in order to flash MicroPython on these devices, you must add the correct flash mode (-fm) parameter to your esptool.py commands.
esptool.py --port /dev/cu.yourserialport --baud 460800 write_flash -fm dio --flash_size=detect 0 esp8266-X.X.X.bin
RAM is really short with MicroPython on these devices. The datasheet for the ESP8266EX the says it has 80KB of data RAM. Other documentation says that you can expect 50KB after WiFi is connected. And running MicroPython, you end up with about 30KB. This is quickly eaten up if you’re not careful with data types and how you manipulate things like strings.
When serving pages over HTTP, care needs to be taken when assembling the response, which may easily be larger than available data memory. I first encountered this problem with a WiFi manager module I found on github. Sure enough, in the issue queue I saw the same problem. A quick review of the code revealed that it was assembling the list of SSIDs in a very memory-hungry fashion.
- An empty array was initialized.
- The header was built from a string embedded in the code using str.format(), then pushed onto the array.
- Each SSID was enumerated, building a string of markup for each one using str.format(), then pushing each onto the array.
- The footer was built like the header, using str.format(), then pushed onto the array.
- The array was then joined using join(), and sent as a single response.
Each of the above steps copies a string from program space into data space. Joining the strings creates another large string in data space which is mostly a copy of the program strings.
To solve this, I sent the HTTP response in chunks. First, this meant I had to remove the Content-Length header that was being sent, because we don’t know the final string length. Then I sent a chunk for the header, still using str.format() to replace tokens. I sent one chunk each for the SSID markup, again using str.format() to insert the SSID names. I sent the footer also using str.format(). And finally, I called socket.close() to signal the end of the response. This meant that the assembled strings could be garbage collected right after use, and no complete copy was necessary.