I wanted to design a low power device that could send open/closed status of my garage door. Initially just so I can check that I didn't forget to close the garage, but later this data could also be used for an alarm feature.
Since the garage is multiple floors below my apartment, Bluetooth was not an option. Also, I wanted to avoid handling the receiver side myself, but rather use an existing network which can support as many devices as I want in the future. This is where the Helium Network comes in. Based on LoRa protocol, it provides long range communication, with very low power, for extremely low cost (100k packets for 1 USD at time of writing). In my area there is a good amount of Helium hotspots installed, so I only needed to design the device and connect to the network.
Here is a picture of my "GarageNotifier" (GN in further text) PCB, with some critical components marked:
To detect open/closed state, I used a magnet on the garage door (moves with the door), while the GN PCB is in a fixed position cca. 5mm from the door. The magnet is read by magnetoresistive sensor U3 (Honeywell SM351LT). The sensor outputs state 0 when the magnet is close (door closed), and state 1 when magnet is far away (door open).
I used a relatively small magnet (less than 10 mm diameter) and it works great, but you should test this in your specific application. You can see what GN is doing by looking at the provided status LED, as described below.
GN status LED:
There is a dual-color LED on bottom side of GN PCB that shows current status. It always starts blinking when there is a change on the magnetoresistive sensor. Colors are as follows:
- Green blinking: door is closed
- Red blinking: door is open
In any state, the LED keeps on blinking until the LoRa message is sent.
Video of GarageNotifier in action:
HardwareI designed the PCB needed for this project in KiCad 6.0, and produced a couple of prototypes through JLCPCB. I also used their assembly service to solder most SMD components on TOP side of PCB, to save some of my time and to test how well they will manufacture this (results were very good, no issues). I also used a VNA to tune the LoRa antenna to the specific Hammond RL6105BK housing that I used (see schematic for info).
On my github you will find all outputs needed to produce the board (gerbers, BOM with JLCPCB part numbers, pick&place). Feel free to modify the sources as you want and make it better in your own project.
Some credits before going further: The design is based on Adafruit's nRF52 Feather board. They took care of nRF52 support in Arduino and provided a UART bootloader to make development simple. I kept the same MCU pins for UART, so my design is completely compatible with Adafruit's bootloader and I only needed to write the Arduino application. I recommend to check out Adafruit's documentation to get a better understanding of nRF52 with Arduino.
Here are some more details regarding hardware:
- You will need NRF52-DK or some other board with J-Link (I got the NRF52-DK from Mouser) to flash Adafruit's bootloader the first time (only done once, see Bootloader chapter for details). After the bootloader is flashed, you can use a simple USB-UART converter for all future updates of Arduino application and even updates to the bootloader.
- I used the Raytac MDBT42Q module (nRF52832) instead of going for the bare nRF52832 IC. This makes the design and soldering much simpler. Additionally, if wanting to commercialize the design in the future, certification is much easier since the module already has the needed radio certificates.
- To allow changing the battery in the future, I used a battery holder together with a Panasonic CR123A battery. These 3 V lithium batteries give enough current for this project, and are easily available in my area. Additionally, there is a soldering footprint for 18505-size lithium battery, that battery squeezes right into the Hammond housing that I used.
- For LoRa, I designed with the standard RFM95W module. In the end the original HopeRF module that I put in the schematic wasn't available, so I soldered the Seed Studio version. Not sure of the differences between manufacturers, but they seem identical, I didn't notice any issues.
- I used a magnetoresistive sensor to detect the presence of magnet on the door (U3, Honeywell SM351LT). The sensor outputs state 0 when the magnet is close (door closed), and state 1 when magnet is far away (door open). There is also a footprint for a regular reed switch, but note that I have it wired inverted compared to U3, so if you want to use it then you should either change the Arduino sketch, or redesign schematic, to correctly identify open/closed state.
- There is an accelerometer on board (LIS3DH) but I didn't use it yet. The idea was to skip the magnet altogether and have GN directly on garage door so it moves when opened. However, the magnet approach has worked reliably for almost a year, so I didn't have the need to change it. Therefore, you don't need to solder the accelerometer if you don't have some other purpose for it.
- I used a 868 MHz antenna since I am in the EU. There is also a 915 MHz version available, so the design could be adapted to US requirements.
- Power consumption: when I tested the first prototype, I measured the standby current to be around 10 uA using EEVBlog's uCurrent. As that was more than ok for me, I didn't try to push it any further.
Regarding KiCad project source, you need to do the following to get the design files and dependencies:
- Download PCB project files from my github: https://github.com/zbockaj/GarageNotifier-PCB
- Download my library: https://github.com/zbockaj/ZB_KiCad_Lib
- Download digikey KiCad library: https://github.com/Digi-Key/digikey-kicad-library
For digikey library just follow their guide to install it (although I prefer to have the library installed in Global Libraries instead of Project Specific like in that guide).
To install my library after downloading, open up KiCad and go to Preferences -> Configure Paths. Add a new variable by clicking the "+" symbol, name it "KICAD_ZB_LIB_DIR" and point it to the ZB_KiCad_Lib folder. After that you can add my symbol and footprint libraries the same way as for the digikey library, by going to Preferences -> Manage Symbol Libraries, and Preferences -> Manage Footprint Libraries.
Note: to make the project fully portable and independent it would probably be better if I had installed the libraries as Project Specific so everything would be included in my project folder. However, this would mean that for every new project the libraries would need to be added/duplicated, so it is not as practical from the designer point of view as having Global libraries installed in a single location (at least that was my impression from my brief time with KiCad).
Helium ConsoleBefore you are able to register any Helium device on the network, you need to visit Helium Console web page and register. You get 10000 credits free for data transfer, which equals 10000 messages for GarageNotifier. Note that there are also other platform providers for Helium, but I didn't really search for anything else as the Console worked fine.
Go to Devices and add a new device, the console will generate Dev EUI, App EUI and App Key parameters which need to be copied to the Arduino sketch (see Arduino IDE chapter). Later, when you build and flash the Arduino sketch to the device, you should get a similar device overview as shown here:
There is also an Event Log if you scroll down in the Device page, showing details of every message that was sent by GN:
To get more details, check out the Console quickstart guide: https://docs.helium.com/use-the-network/console/quickstart/
Additionally, for a better understanding of what is going on, check out this guide for a similar device (but don't install anything relating to Arduino, I will cover that later in Arduino IDE chapter): https://docs.helium.com/use-the-network/devices/development/adafruit/adafruit-feather-m0-rfm95/
To get GN data in something usable for me, I tested out multiple integration options available on Helium Console. You can use Adafruit IO as that works quite seamlessly. Also, I am using the connection to my MQTT broker running on RPi, this also works fine and I didn't notice any significant issues. I won't go into details here, you can check out the guides for Helium Console on how to connect your data to Adafruit IO, MQTT, or some other platform.
BootloaderTo allow easy integration with Arduino IDE, you first need to flash a UART bootloader to GN by using a J-Link programmer. The simplest way to get a J-Link programmer is to buy a NRF52 development board like the NRF52-DK, but potentially there are some Aliexpress clones that you might use at your own risk.
The need for a J-link programmer is a downside of the nRF52 + Arduino approach, especially compared to something like ESP32 where no additional programmer is needed. However, in my opinion the nRF52 is a much better platform for low power devices, and later the whole Arduino implementation could be skipped for something like a Zephyr-based system. In that case you would use just the J-Link anyway (plus BLE bootloader potentially).
I used Adafruit's bootloader for nRF52832 Feather without modification, I just renamed the files to be in line with GN hardware variant defined later in Arduino IDE. You can find the bootloader in my lib_hack.zip, in the bootloader folder.
- To flash the bootloader (only done once), you need to connect the J-Link programmer to SWD header J2 on GN and flash using nRF Connect, or directly in Arduino via Tools -> Programmer -> J-Link. For more info about the bootloader check out Adafruit's guide for nRF52 Feather bootloader.
- After you have the bootloader flashed, you don't need the J-Link SWD connection anymore. Just connect with a USB-UART converter to J3 header and use Arduino's standard Upload feature.
Before getting started, you need to install the following through Arduino IDE, Tools -> Manage Libraries:
- MCCI LoRaWAN LMIC library version 4.1.1
- CayenneLPP version 1.3.0
You also need to install Adafruit's BSP for nRF52 (version 1.3.0) through Arduino's Boards Manager, follow the first chapter in this guide: https://learn.adafruit.com/bluefruit-nrf52-feather-learning-guide/arduino-bsp-setup
Note that I used Arduino IDE version 1.8.19, I don't know of potential issues with other versions.
After all is installed, you need to configure the radio parameters of LMIC library, on my system this config file can be found here:
C:\Users\username\Documents\Arduino\libraries\MCCI_LoRaWAN_LMIC_library\project_config
If it's not there, you might also want to check out C:\Users\username\AppData\Local\Arduino15\...
The lmic_project_config.h file looks like this in my case:
// project-specific definitions
#define CFG_eu868 1
//#define CFG_us915 1
//#define CFG_au915 1
//#define CFG_as923 1
// #define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP /* for as923-JP; also define CFG_as923 */
//#define CFG_kr920 1
//#define CFG_in866 1
#define CFG_sx1276_radio 1
//#define LMIC_USE_INTERRUPTS
//#define LMIC_DEBUG_LEVEL 1
If you want to use the device outside EU, then the proper define needs to be uncommented. Also you will need to use another antenna, since mine is for 868 MHz.
Now we get into the hacky part, shut down the Arduino IDE before proceeding...
Adafruit's BSP for nRF52 will expose multiple boards in Arduino IDE (Tools -> Board -> Adafruit nRF52 Boards), where "Adafruit Feather nRF52832" is closest to my GN hardware. However, I couldn't use Adafruit's board definition directly since SPI and I2C pins are different on GN. So I had to define the GN hardware somehow. There are multiple workarounds possible, choose only one according to your skill level and needs:
1. Just edit Adafruit's variant.h file for Feather nRF52832, with proper GN pin definitions, and use "Adafruit Feather nRF52832" as the board in Arduino IDE. The downside of this approach is that you cannot use Adafruit's Feather board anymore, but this wasn't an issue for me since I didn't have that board anyway. So I used this approach for first testing, it works fine and I can recommend it for anybody starting out (just backup the original variant.h so you can revert back if needed). You can find my variant.h file (and with it proper definitions of GN pins) in the attached lib_hack.zip.
On my system, Adafruit's variant file was located here: C:\Users\username\Documents\ArduinoData\packages\adafruit\hardware\nrf52\1.3.0\variants\feather_nrf52832
2. Create a new board INSIDE Adafruit's BSP. This way the original Feather definition is kept and you end up with an additional "gn_nrf52832" board in Tools->Board in the IDE. To do this, definitions of Feather board inside "bootloader" and "variants" folders from Adafruit (...\adafruit\hardware\nrf52\1.3.0) need to be copied and renamed for GN. Also, boards.txt file needs to be updated with the new GN board. All of this is prepared in my lib_hack.zip, so you can just extract and copy it to Adafruit's folder (my folder structure should match Adafruit's).
Note that my files are only valid for version 1.3.0 of Adafruit's BSP. If Adafruit changes something in a future update, then the process of copy/pasting and adding proper GN definitions needs to be done from the start. Regardless of the downside, for me this was easy to do and fully acceptable for this DIY project.
3. Create own hardware definition inside Arduino's default sketchbook location (from Arduino IDE, go to File -> Preferences and check out sketchbook location). This would be the best way, but I had many issues with it. For one, it is not just a matter of creating the variant.h and .cpp, but also the platform.txt file is needed (and some modification so it doesn't search for tools inside the sketchbook instead of adafruit's files). Then you also need to copy the bootloader files into the sketchbook, and in the end the tools for flashing bootloader from Arduino IDE (nrfjprog or DFU) are still missing. To me it wasn't valuable to try and fix all the dependencies, so I didn't implement this approach. Maybe somebody has better luck in the future.
After adding the GN variant definition with any of the above approaches, you can start the Arduino IDE and open/copy my sketch (download it from attachments). Then select the GN board through Tools -> Board.
Note: my sketch is based on already existing examples from MCCI LoRaWAN LMIC library, with some tweaks that I noticed are needed to get it to run properly in this system (nRF52, Helium network in EU).
The only remaining thing to do before compiling the sketch is to add some device-specific parameters. The parameters are called APPEUI, DEVEUI, APPKEY:
static const u1_t PROGMEM APPEUI[8]={FILL_ME_IN};
static const u1_t PROGMEM DEVEUI[8]={FILL_ME_IN};
static const u1_t PROGMEM APPKEY[16] = {FILL_ME_IN};
These parameters are generated by Helium Console after creating a new device, they just need to be copied into the sketch (replace "FILL_ME_IN" fields). For details see the Helium Console chapter, here is just a screenshot of how it looks in the Console:
Note: you must create you own device on Helium Console and use its parameters, don't just copy the ones from example screenshot, it won't work! (but you can just fill in zeroes or something to see if the sketch compiles).
After filling in the missing parameters, the sketch should compile without any issues.
Some additional notes on the sketch:
- LORA_MSG_REPEAT is set to 1, to increase the probability of a message going through (my garage is under multiple floors of concrete). If you don't have such a bad scenario, you can set repeat to 0, so only one message is sent on status change.
- SF9 is used (more power consumption), again to increase probability of the message going through. You can easily set this to SF7 and see if it works for you (SF7 was perfect when I tested just inside apartment instead of garage).
Flashing the application:
Use a USB-UART converter to connect to J3 header on GN, select the proper COM port and use Arduino's standard Upload feature. After the upload finishes successfully, you should see GN's on-board LED blink as it tries to register on the network. This first connection can take a bit longer, leave it alone until the LED stops blinking (even a couple of minutes is ok). On Helium Console, in Devices page, you should see activity for your device (it also shows these first registrations and any retries if you have bad signal).
When the first message is sent (and network registration done), the LED stops blinking and you can use your magnet to test if open/closed state is registered and sent normally. LED will blink red for open state, and green for closed state, and will keep blinking until Helium/LoRa message is sent.
Note: if you generate many test messages, the LMIC library will do some throttling to stay within fair-use policy (since LoRa works in the unlicensed ISM sprectrum). So if the LED is blinking for what seems too long in normal operation (sending status, not registering for first time), throttling is the reason. This is especially the case with SF9 (like in my video) and is less visible with SF7.
ConclusionI currently have three GarageNotifier PCB's running for almost a year, without any issues on the device side (no stuck devices or any intervention needed from my side). Therefore I am extremely happy with the project and I'm sharing all the source files so maybe it is useful to somebody else.
Regardless of Helium ecosystem, the hardware should support any other LoRa networks and can therefore be used for other applications also. If somebody finds some good use for this design, let me know in the comments.
Comments