This post is quite a deal more technical than my usual fare. I’m doing this as a write-up of some of the learnings, in part to share with participants at the OzBerryPi IoT meetup that occurred last night.
I’ve recently been investing some time into a project using Arduino as a wifi-enabled energy monitor.
I had an hypothesis that open-source hardware such as the Arduino and related chips like the ESP8266 wifi chip were approaching the point where someone like myself, with a reasonable degree of experience in web development, might be able to build a low-cost embedded electronics project (and potentially, down the track, product).
- Existing libraries are firmware dependent. Few work with the current version of the ESP8266 firmware. Be sure that you can flash your chips with the target/tested/stable firmware.
- The current version of the ESP8266 firmware seems to have some bugs/quirks (returning mangled responses, or cruft at the beginning of responses, at times). I wouldn’t consider it “reliable”. YMMV on older (or future) firmware versions.
- There’s tonnes of information snippets out there, but there’s few “definitive” guides to get up and running. There’s a lot of information that is either stale or inaccurate (now) based on current firmware/library versions etc.
- Arduino quirks and low memory conditions make it challenging to work with strings without understanding C/C++ character arrays and related methods/utilities. The String class is problematic (buggy, poor memory management) and should be avoided if possible.
- Design to use very short/small strings to be passed between client and server. Don’t try to implement full HTTP requests/response construction/parsing.
The basic gist of the project is to have a power-point level energy monitor, similar to the Belkin Wemo but at a reduced build cost, so I could run 6–12 units in a variety of locations/contexts (i.e. potentially 20–30 units).
The parameters of my “experiment”/project:
- Operate at a power point-level (i.e. for individual power points in a house), not aggregate (i.e. whole of house)
- Target a USD$10–15 (or less) component cost per unit
- Built upon common open-source hardware and readily available sensors etc.
- A hardware architecture/spec that could be scaled up later (i.e. not something as a “one off” hobby approach)
- Support 5v analog sensors
- Be wifi-based (not bluetooth)
- Be self-contained (i.e. not require separate power supplies etc.)
- Get access to the raw backend data
- While longer term the vision/idea was to have individual points send messages to a central unit on the same network, with that central unit communicating with an online service, for my first prototype I wanted the device to post directly to the internet (so I didn’t have to write server code just to get a basic prototype working).
- Ideally, post to an existing online “Internet of Things” server, such as Thingspeak, Phant.io/data.sparkfun.com etc.
- Had a strong development community/base (incl. documentation)
- Didn’t require coding/linking to an online service to get basics working (like posting code to the device)
I note the parameters above as there are a number of existing projects (such as the excellent OpenEnergyMonitor that meet some, but not all of the requirements. There are a number of systems/architectures USD$25+, such as NodeMCU-style development boards that have wifi built in. The Particle range, the soon to be released Tessel2, the Arduino Yun, etc. But these wouldn’t meet the price point, nor the longer-term “architecture” goals.
Doing some preliminary research it seemed that this should be possible using Arduino with the Espressif ESP8266 wifi chip. I also worked out the similarly low-cost Allegro Microsystems ACS712 chip could be used for sensing energy usage. A quick inventory suggested that these two components combined with an Arduino (for example a non-official Nano, that are available on Ebay and Aliexpress at very low cost) would be feasible.
In fact, the single most expensive component, it appeared, would be the switching power supply.
Doing a bit more research, I found were lots of “getting started” tutorials that suggested this would be doable. Most get to the point of sending basic “GET” commands for the ESP8266, and for reading DC current using the ACS712 sensor.
So I got started…
I got the ACS712 working quite quickly with a DC current and basic sketch. Working with AC current was a bit more challenging, but I was able to find sufficient documentation online to take samples over a period to calculate RMS voltage and derive current from that. A lot of the projects assume a inductive sensor (rather than one that could connect directly to the mains).
While I found it a little frustrating piecing together bits of information from all over the web, overall it was a relatively painless experience (about 1–1.5 days effort I would estimate). With the assistance of a friend who maintains and repairs power supplies for a living, I built up a basic test rig containing the ACS712 sensor, that could plug into the Arduino prototype rig I was developing (see picture above).
On the wifi front, I thought I’d make things easier for myself by using a pre-designed shield that used the ESP8266 chipset as a means of getting up and running, knowing with a view to moving towards connecting directly to an ESP8266 chip (and reducing cost) using the same code/library/architecture.
I picked up the Sparkfun ESP8266 shield, and used the library that was provided by Sparkfun. The board would power up (sometimes) and the blue status light with flicker then disappear. Other times it would remain on all the time. Other times it would be in between. The library consistently returned “Could not communicate with the device errors”. I tried running it using breadboard jumpers, rather than using the header pins as a shield configuration. While this was more stable, it was still very inconsistent. This seems to be a power-related issue. I’ve since been using an independent power supply for other ESP8266 units, but this didn’t fix the issue for the shield (i.e. when sending VCC directly to the shield).
This was the beginning of a very time consuming and frustrating experience with trying to get the Arduino/ESP8266 architecture, using AT commands, working.
After a lot of head scratching, and starting to reverse engineer the SparkFun library to see if I could work out what was going wrong, I switched tact.
I bought an ESP8266-01. I didn’t have a lot of experience with serial comms and using FTDI interfaces etc. it was a bit of a slog to work out how to get it communicating with the Arduino. I also had to buy a logic level converter and an FTDI interface. With a big assist from some folks at the OzBerryPi IoT meetup a few weeks back, I got comms (simple AT commands) working first using CoolTerm (a serial terminal), then the Arduino, via the logic level converter. I also added a more stable power supply, so as not to rely on the Arduino 5v/3v3 VCC outs (see pic below for the updated development rig).
Once these comms had been established, I searched for libraries that used AT commands to communicate with the ESP8266 chipset. I found a couple of promising ones (for example, WeeESP8266 and ESP8266_Simple), but these didn’t work consistently. Sometimes this seemed to be a firmware issue (expecting certain responses that had changed). Sometimes this related to memory issues (with Serial.println() statements getting corrupted or not working). Other times it would be a non-responsive chip (not responding to the most basic AT command).
Again, I tried to reverse engineer/trouble-shoot with the existing libraries, but found them a little light on documenting what was going on/expected as a response, and as such it was difficult to decipher and work out what was expected to be received vs. what was received etc.
From what I’d read, I realised that most code examples were very sensitive to the firmware version and AT command set version supported by the device. So I worked out how to update the firmware, but was unable to find the specific versions that the libraries needed that I could install via Mac. (There are a lot of references to 0.9.2.4 as being a “stable” version.)
Eventually, as a “last resort”, I opted to implement a “first principles” approach using the latest firmware (reporting the version number as “0020000903”) from Espressif, which I was able to flash to the device successfully using esptool. This was after losing a few hours working out how to reset the baud rate of the ESP (which was now too high for the Arduino). (“AT+IPR=9600” did the trick—thanks Andrew Rapp.)
I got a basic (albeit what I would consider brittle) version working—despite inconsistent range and formats for AT command responses and weird cruft either at the start of the AT response, or mangling the body of the responses. Then I tried to refactor into my only library for cleanliness. Which of course borked everything (again, seemingly memory-related issues).
I presented my experience to the OzBerryPi IoT Meetup group last night. Details of my experience/experiment with this approach are available at my Bitbucket repo of the resultant code.
Days have passed. Still no stable code.
Strings and low memory
Admittedly, not all of this is the ESP’s fault. While there are definitely some quirks and issues with the AT firmware, a lot of it is to do with the Arduino’s constraints and quirks. Two in particular being low memory, and limited string functionality (which are related).
Arduino code is a weird in between of C and C++. It doesn’t implement the std::string library, nor common string parsing and manipulation tools like regex, apparently due to the memory and storage limitations of the device. The fact that I’m new to C/C++ didn’t help either.
The Arduino environment does provide a String class (not to be confused with std::string) but is very limited, and by all reports is both buggy and causes memory issues. This means that you’re often having to work with raw character arrays to do string manipulation. There are a bunch of functions to support this (like strstr, memcpy, strcat, strcpy etc.), but it’s a long ways away from “easy” and straightforward.
Lastly, there seem to be very limited tools for debugging and monitoring memory etc. on the Arduino. This makes it difficult to know where the problem lay, when things just randomly fail and there’s no clear way to determine if it’s a memory issue. (I’m aware there are some rudimentary memory monitoring tools/libraries around—but when Serial.println is failing, there’s not much you can do to troubleshoot.)
Why spend so much time?
An obvious question arises: wouldn’t I have been better off spending the $25 for the more expensive, but more stable (one would hope, anyway), architectures to get the data and get started on the bigger picture goal I have in mind? A false economy, perhaps…
I’ve asked myself this numerous times along the journey, and decided to continue persevering because of some “bigger picture” motives, and other considerations, that suggested investing further time in trying to get this to work. For example:
- There was no guarantee these more expensive units would be more stable in practice/reality
- Often these units required entirely different code bases (e.g. NodeMCU uses Lua, Tessel2 uses io.js etc.) and often these are not as well documented, as the technology and communities around then are still nascent
- Some development boards didn’t have the ADC (analog to digital) conversion capacity—e.g. the ESP8266 has only one ADC, and it requires a logic level conversion down to 1.8v (from 3v or 5v source, depending on your sensor)
- I wanted to see if I could get this low-cost architecture working with a view to potentially developing a product to sell in the future, so was trying to stick with components that would have a chance of meeting a target price point
- I wanted to see if I could get a library working with the current firmware version, to contribute back to community (given there were limited options available)
- I was using this entire exercise as a learning exercise in developing for devices and hardware, and I was learning a lot of the nuances of C++ (and the Arduino environment, which is not true C/C++) which just takes time (so I’m thinking of this as professional development/training, to some degree)
I think, however, it’s time to let go of my original ideas/approach, and start looking at alternatives.
Possible next steps
I have a couple of ideas about how to get a stable implementation using this kind of architecture:
- Use a different platform (as noted above) and wear the additional expense/unit to get the basics working, and revisit down the track (or wait for the cost of the other platforms to come down, as has happened recently with the RaspberryPi Zero)
- Continue to experiment with different libraries (ESP8266wifi looks promising, seemingly developed with low memory situations in mind, but tested on 0.9.2.4 firmware.)
- Ditch the use of HTTP/POST method and go for a “lighter-weight” protocol. Either to a cloud-based server I control/code for, or on a central device on the local network that then posts to the cloud.
- Use Arduino on the ESP8266 chip and work out how to deal with sensor input given the limited ADC capabilities. My understanding is some ESP variants have more memory than the Arduino boards, which may make life easier too.
- Run Arduino code on the chip and use the built-in libraries, implementing a simple, text/serial based interface that is then run on the chip. (Or you could use NodeMCU in a similar capacity.) This would be terribly difficult to debug, and far from desirable.
- Look into embedded programming practices (e.g. coding for the Atmel out of the Arduino environment, or coding for the ESP8266 using the SDK directly)
I’m very tempted to take option 1, unless someone cracks the nut with a stable firmware/library combination.
I’ve had to invest a lot more time than I intended/wanted to get to this point. So I thought it would be helpful to share some more top-level thoughts based on that experience. I’m hoping this will be useful to someone considering this sort of architecture for their own project. This is what I’d tell my prior self, if I knew what I know now…
Don’t rely on the AT firmware I would seek alternatives to AT command firmware to get a reliable system up and running. The latest version seems to be buggy and, while better documented than previously, the current docs still leave a bit to be desired, with inconsistent response formats and some missing responses for certain states (for example, sometimes the device will return “no ip” as the message, but this is not in the documentation, sometimes it will return a “busy…” message, but I’ve not seen this in the docs either) etc. See Possible next steps above for my thoughts on how you could get around this. YMMV with future (or older) versions of the firmware.
Develop to a specific firmware version If you do find a specific firmware version and you develop code for it, stick with it. Flash all your devices so you’re working with a consistent/known base that you can be confident with. As noted earlier, 0.9.2.X seems to be a popular choice/period, but I couldn’t work out how to flash the chip on a Mac to this version.
Be a hard-ass on the KISS principle Given the constraints of the Arduino environment, it’s difficult to build libraries that handle generic use cases (see next point). I’ve found that refactoring into libraries that are more “generic” in what they try to do is where a lot of issues are introduced, due to bloat, additional memory requirements, passing objects/data around in memory etc. I would recommend a laser-like focus on what you need it to do and keep it as simple as absolutely possible to achieve that objective. i.e. Build only what you need, resist temptation to “library-fi”.
Only use simple text strings for comms Given all the limitations noted above, trying to do anything beyond very basic string manipulation is not advised. For example, the Thingspeak API returns a 700 character plus response to a POST message. This is a big string to handle on an Arduino. Creating a basic POST request is about 256 chars.
The lack of std::string functionality and other string manipulation tools also makes it quite cumbersome. I now understand why a lot of folks throw out the HTTP verbs other than GET! I’m aware there’s some libraries around (like Arduino JSON or the MQTT libraries like pubsubclient or Adafruit’s implementation etc. would probably help with some of this… but given the previous point re: KISS + my experience working with the AT libraries for the ESP8266, you’ll have to forgive me for being nervous about relying on libraries too much.)
So, if you’re communicating with a server you have control over, and you can build very basic API input/output protocol, this would probably be the best way to go. The idea I mentioned earlier of having a central unit receiving the smaller units’ data using a simple text protocol to a server running on a beefier device, that is then relayed up to the cloud using the standard HTTP stack. For example, run a Raspberry Pi or BeagleBone as a central point, running a node.js or python-based server. Keep the individual points doing the absolute bare minimum (getting a basic text string to the local server) and do the “heavy lifting” on a device with more headroom.