This documents how I made a device that can measure how much snow has accumulated on the ground. It is able to run on small batteries for years, survive sub-zero (Celcius) temperatures and uses LoRaWAN to transmit measurements to the internet for further processing.
WhyThis project started out of necessity - I wanted my wake-up alarm to go off an hour earlier if there was a need for clearing out snow from our rather long driveway. In my windy town I will need several sensors deployed to account for build-up.
This could have many practical applications, including
- Road snow clearing
- Property management, avoiding roop collapse
- Parking lots
- Electrical grid management
- Airfield maintenance
I am a software developer in the field of e-health who has tinkered with electronics and 3D-printers on my spare time for the last years. I love to share my journey at my local makerspace and have written a few guides, mainly on hackster and element14.
HowThis guide will cover interesting steps in creating a low-power sensor device, going in-depth on certain points of interest. The principles can be useful for other types of applications, substituting for instance the sensor. My coding style and approach reveals my background in software. Always keen to learn.
Throughout this guide I will provide links to datasheets. Datasheets may not sound exciting but are key when trying to push the limits of hardware.
Sensor principleI did a lot of searching for what principles others have used for measuring snow build-up. These are some of my findings:
- Melting of snowfall
- Visual monitoring of a height scale
- Radar
None of these would agree with a low-cost device that could last for years on batteries.
I ended up using an ultrasonic sensor. It can't actually measure depth of snow, but by subtracting a reading from the known lenght to ground, it can be deduced.
The sensor I have used, HRXL-MaxSonar MB7374, is specifically designed for taking readings against the characteristics different types of snow surfaces present. It is also waterproof, rated IP67.
MicrocontrollerI have wanted to try a new SoC, Seeed Studio Lora-E5, with it's small formfactor, LoRaWAN-radio and low energy requirements. Seeed Studio graciously offered a sample and a Lora-E5-mini development board. This project is otherwise not sponsored in any way. If the proof-of-concept proves viable, I plan to apply for the PCBA-offering from Seeed Studio to have a custom PCB designed around the Lora-E5. I have found a very nice project that would be my inspiration, with a few modifications. The author Charles Hallard helped me out while trying to figure out some issues with power consumption.
Power sourceThe sensor and the devboard would set premises with regards to total size of the device. I am aiming for a coin cell powered device, but quickly concluded this would not be feasible for the dev board. I chose an AA-sized battery, the SAFT LS14500. This battery can safely provide a 50mA continous current, up to 250 mA pulse current, without damaging it due to voltage drop. It can provide a few years operation, more than enough for a PoC.
Unboxing the Lora-E5-miniThe development board feels solid and sports an USB-C connector. Beware, this connector is only for powering the device and for serial communication. You can not program the device this way, as you might be used to from Arduino-compatible boards. However, I was able to have it configured for my The Things Stack application using the factory provided AT firmware in a matter of minutes. The full procedure is documented in the wiki.
I will assume prior knowledge to TTN (I use The Things Stack Community Edition), if not you are in luck, as there are many great guides out there.
Programming the microcontrollerProgrammerAs mentioned the development board can't have new firmware programmed through USB. I ordered a ST Link V3 (STLINK-V3SET) at about 35$ that allow my computer to both program and debug firmware to the microcontroller. You can't use a USB-to-FTDI adapter, Atmel-ICE or similar, something that I spent a while finding definitive answers to.
First order of business was to update the ST Link over USB, requiring creation of an ST account. Really?!
I then proceeded building the included MB1440B board so I could actually access the pins needed to interface with the MCU.
Further I spent some time figuring out what pins to use, both on the programmer and on the development board. I hope the Seeed wiki will make this clearer, as I had to rely on zooming in on pictures of the connections and then studying the MB1440B datasheet to guess the corresponding pins.
On the MB1440B you will find sideways extruding rows of pins. On set of these are called CN6, though it's hard to read and find. Check the pictures. The following table lists the needed connectors to program the MCU via SWD interface (reset is optional):
Connection table ST Link v3 <--> LoRa-E5-mini
Color | ST Link (CN6) | LoRa-E5-mini
Yellow | NRST | RST
Green | DIO | DIO
Purple | GND | GND
Grey | CLK | CLK
In this setup USB-C still needs to be connected to provide power and optional serial connection.
Writing device applicationThe Lora-E5 contains a microcontroller from ST, the STM32WLE5JC. I have only briefly developed using STM32 (or any of the STM lines for that matter) in the past, so I had to decide on a toolchain and IDE for programming it. I have wanted to test out the Mbed-os framework and IDE and gave it a go. Mbed provides an abstraction over different types of hardware, hides away libraries and is supposed to let you concentrate on writing application code. Sounds great! While I was able to have it compile and run on the device, I did not take a liking to Mbed. There are many reasons, but mostly the IDE felt too basic, lacking refactoring support, debugging e.g. Charles Hallard has a nice walkthrough of how to set it up if you want to give it a go.
The next sensible alternative would be the STM32 Cube IDE with STM32Cube MCU Package for STM32WL and optionally STM32 Cube Programmer. I followed the Lora-E5 wiki, starting by erasing factory AT firmware, but could not compile the sample project. It turned out the sample project was out of date and also based on a template where user code is heavily intertwined. I received confirmation this will be updated in the near future, and that the repo has been moved from the location when I tested.
After a lot of experimentation and searching I found a nice repo containing a code base for the LoRa-E5 that was up to date and demonstrated sending sensor data on The Things Network (TTN). I made a fork and spent many evenings studying and debugging the code. STM32 Cube IDE is pretty overwhelming at first glance. I have used similar IDE's in the past which helped me establish a rough overview. What I found most confusing, and to some degree still does, is the mix of generated code and user code. Eventhough I make git commits regularly I spent quite a lot of time being frustrated over my changes being overwritten. I learned the hard way to try to use the configuration wizards over making direct code changes in certain areas. I am still not comfortable with many aspects of the configuration tools, but I'm not as surprised over the consequences now.
/* USER CODE BEGIN */
/* USER CODE END */
There are too many key aspects of the code to cover in detail, I will expand on a few. I hope you are able to compile and run the project, if so, use the debugger to your advantage to understand the flow of code.
Debugging setupFirstly, make sure you get in a habit of switching between debugging and low-power mode. This is dictated by the following defines in sys_conf.h:
VERBOSE_LEVEL
APP_LOG_ENABLED
DEBUGGER_ENABLED
LOW_POWER_DISABLE
STATUS_LED_ENABLE
I used the last one to toggle LED-indicators, but in an effort to track down all possible use of LEDs I left the actual toggling commented out. As a note you can not control whether LEDs are used when communicating serially over USB, as you can see on the board schematic, the CP2102N USB bridge. This caused me a bit of headache untill I discovered this was not the case when running disconnected.
If you set for instance DEBUGGER_ENABLED = 0 and LOW_POWER_DISABLE = 0 any attempt of debugging will soon timeout.
I did not find a way to have serial output displayed inside STM Cube, so I used Termite RS232 terminal with the correct baud rate to capture logging over USB. The MCU has several USARTs, USART1 is used for logging and can be read over USB or through LoRa-E5-mini pin TX(PB6 on the SoC).
Moving on, you will need to set your own LoRaWAN settings from a new device registration. Use the configuration tool by opening the.ioc-file in STM32CubeIDE, selecting Middleware-> LoRaWAN -> LoRaWAN comissioning. Do not write directly to the se-identity.h-file, as it will be overwritten.
- "App/Join EUI" is mapped to LORAWAN_JOIN_EUI
- "Application Key" is mapped to LORAWAN_APP_KEY in se-identity.h
- "Network Key" is mapped to LORAWAN_NWK_KEY The "Static Device Address" should not be used anyhow for security reasons and thus network and application session keys will not be used.
- TTN "AppEUI" corresponds to LORAWAN_JOIN_EUI in
se-identity.h
- TTN "DevEUI" corresponds to LORAWAN_DEVICE_EUI in
se-identity.h
- TTN "AppKEY" corresponds to LORAWAN_APP_KEY in
se-identity.h
A frequent problem arises: how can I track my project on a public code repository without disclosing private keys? An option is to commit a version of the configuration with fake values, stop tracking the file in git, and change to real values. This poses some problems: how can I continue developing the project on other computers, or even be sure to keep the correct values when I pick up the project in the future? Also, what if the configuration files contain other settings I actually want to change and share? I am all open for suggestions.
Edit: Thanks to my colleague Erling for suggesting GitHub Encrypted secrets.
I decided to commit my keys during development and then regenerate them before publishing.
Interfacing with the sensorI studied the MB7374 datasheet to understand it's many aspects and options. Reading measurements through pin 5 is recommended for accuracy as it provides TTL output. After booting and printing product information it will output each measurement in continous mode as a string of ASCII characters starting with "R" followed by the distance to surface as millimeters using 4 digits, e.g. "R0928", corresponding to 92.8 cm. I used USART2 on the STM32 with a 9600 baud rate.
6HRXL-MaxSonar-WRST7
MB7374-1XX
Copr. 2011-2019
MaxBotix Inc.
RoHS 23b 119 0819
TempI
R0937
R0937
R0936
R0936
R0936
R0936
TempI denotes the internal thermometer is used.
Note: For snow applications a 5v power supply is recommended for best operation of the MB7374. The regulator on the LoRa-E5-mini, AP2112 only provides 3.3v when battery operated (the 5v pin is only engaged when USB-C is connected). This proof-of-concept will therefore hopefully provide answers whether the next version needs to accommodate 5v for the sensor.
MB7374 Pin 5 (Serial output) was wired to LoRa-E5-mini pin TX2 (PA2 of STM32WLE5JC). Keep in mind that the serial is output only, no need for input. MB7374 Pin 7 (GND) was wired to GND. I wired MB7374 Pin 6 (V+) to dev board pin D0 (PA0). The current draw of the sensor in standby is too high, I decided to use a GPIO to engage it before each transmission. The pin has a user label US_ENABLE (UltraSonic enable). I made sure the pin could provide enough current, reading both datasheets, also by measuring. A more robust solution would require toggling the sensor using a transistor. Check out my soil moisture guide for an example.
The main device logic is found in the file lora_app.c. SendTxData simply enables the sensor, gets a reading and prepares a buffer with the actual bytes to transmit, transmits, then enters sleep (stop2 mode), repeat.
The project repo contains a Javascript uplink decode script for The Things Stack.
function decodeUplink(input) {
var bytes = input.bytes
return {
data: {
distance: ((bytes[0] & 0x80 ? 0xFFFF<<16 : 0) | bytes[0]<<8 | bytes[1]),
temperaturec: ((bytes[2] & 0x80 ? 0xFFFF<<16 : 0) | bytes[2]<<8 | bytes[3]),
voltage : (bytes[4]<<8 | bytes[5])
},
warnings: [],
errors: []
};
}
distance: Distance to surface in mm. The more snow, the lower number.
temperaturec: This is the MCU temperature. This should not be a reliable measure of the environment surrounding the device, as it is meant to provide calibration reference for certain MCU features. The device is watertight, further removing sensitivity to the environment. As the MCU does very little work I still wanted to see whether it could provide useful data. I have a reliable temperature sensor installed close to the device and so far they report pretty similar temperatures. Due to a bug in my code the temperature value is reported as whole numbers.
voltage: This is supposed to be the battery voltage, an indicator of a dying battery. This is retrieved using the generated function SYS_GetBatteryLevel. I think this function is flaved, as it seems to report MCU AREF. I would appreciate any suggestions on how this should be implemented.
OnRxData is triggered if a transmission is followed by the TTN-application returning a queued downlink message. I have implemented one downlink message: configuration of transmission interval. This enables me to change the number of minutes between measurements, from 0 to 65535. This allows for a maximum sleep time of about 45 days. Note that if you want to change the interval the request will be queued until the next scheduled device transmission, so be careful. In The Things Stack console you can queue a downlink by navigating to the device, going to the tab Messaging, and then Downlink. FPort is 1, Payload type Bytes. You need to format the number of minutes as two pairs of hexadecimal digits, most significant byte first. E.g. "01FF" will correspond to 511 minutes. Due to lazyness the device application expects the two pairs, so if you want to set the interval to 1 minute you need to send "0001".
There are thousands of options for what to do with the data once it is routed to The Things Network. I have covered a few in my previous guides. For this guide I want to focus on a handy way to investigate a few days worth of measurements using The Things Network Command Line Interface (CLI).
Enable the free short term storage integration and create an API key.
Follow the installation instructions to install ttn-lw-cli and create a configuration,
ttn-lw-cli use <eu1/au1/nam1>.cloud.thethings.network
finally logging in using OAuth2.0.
ttn-lw-cli login
Now you can make queries to retrieve messages. As it default uses gRPC protobuf, except for cold starts, the transfers are lightning fast.
ttn-lw-cli applications storage get "snowmonitor" --limit 10 --order "-received_at" --up.uplink-message.decoded-payload
Dumping the results to a.json file you can use your favorite graphing tool to make nice visualisations locally.
Power profilingThroughout the development I have actively used my trusted Qoitech Otii Arc to measure and compare impacts to current consumption when changing hardware and code. This enables me to be systematic when experimenting with different supply voltage levels, antenna designs, code execution order and so forth. A few interesting observations:
- Leaving the ST Link connected to the dev board, but disconnected from the computer resulted in a higher current consumption. This prompted me to be consistent in taking measurements, disconnecting the ST Link each time.
- Adaptive Data Rate (ADR) takes a while to optimise. In my setup I had to wait about 4-5 transmission to be sure it had settled and I was comparing apples to apples. In my setup this was DR5 (SF7BW125).
- A simple 8.6 cm wire antenna performed really inferior to the supplied whip antenna.
- A possible hardware design limiting really low power consumption when transmitting at HF (868MHz).
- Regular logging (VLEVEL_OFF = non-verbose) having unsignificant negative effect on consumption.
- Transmitting UNCONFIRMED_MSG seeming to consume more current - worth investigating the implementation.
- LoRa-E5-mini browning out at the minimum 2.5v regulator input voltage. For use with the LS14500 battery this is fine, as it has a really flat discharge curve.
The power profile is included in the repo for your viewing pleasure.
In conclusion I have achieved about 73uA current consumption during sleep. While far from the SoC advertised 2uA rate, this isn't bad for a PoC. The development board regulator probably accounts for most of the excess (datasheet: 55uA typically), floating pins and such for the rest.
Transmission peaks at about 80mA, ref. the HF-issue. About 7uWh is consumed pr. transmission. Using a simple calculator tells us the difference in transmitting every 15 minutes compared to every 60 minutes is rather insignificant in this context. The former gives a very rough estimated battery life of over 3.5 years, much more than needed for testing before designing a custom circuit.
This proof-of-concept obviously needed to be tested outdoors, in pretty harsh conditions. I spent half of the project time designing a watertight enclosure and 3D-printing it using a resin SLA-printer. Accommodating both the antenna and the sensor was quite a challenge, I ended up designing an enclosure that allowed the electronics to rotate while screwing the sensor in. Rubber washers provided seals.
I started by modelling all significant components, this saves a lot of iterations.
The sensor comes threaded using a weird US standard, NPT American Taper Pipe Thread (ANSI B 1.20.1) at 3/4"-14. What?! I spent a lot of time trying to make a matching hole in Fusion 360, finally finding a plugin that would help. You need to install this plugin each time Fusion 360 is updated, pretty lame.
The end result looks like a lightsaber had a child with a pipe bomb, but it seems to serve the purpose.
3D-printingI believe this could be printed using a good consistent FDM-printer, but I think you would need to seal it using a resin coating. The upshot of using a good quality SLA-printer, such as Formlabs Form 3 is that the printing process was pretty uneventful. I needed to do a few iterations, as I hadn't figured out the threads and I wanted to add some text in case it got lost. Just as an extra measure I added some blue thread locker around the opening for the antenna.
The device has been live for 14 days, transmitting 1903 measurements. I am pleasantly surprised the sensor isn't as reliant on a straight angle to the surface as I had feared. Surface roughness seems to reflect enough sound waves. It has stayed dry, my other great concern. I plan to let the sensor measure for a little longer before returning an average to increase accuracy. I also have to make a larger cap to keep snow out, as observed during a particularly bad storm.
I had hoped to experiment powering the bare LoRa-E5 chip using a coin cell battery. Discovering the high spikes in current consumption during high frequency transmission urged me to postpone experimentation using capacitors to this version of the PoC was completed. A custom coin cell powered PCB will open great opportunities in reducing size and complexity in the overall design.
Comments