## Building a cat detection system using HFS-DC06 microwave sensor with data logging and notifications Project by: Mohammad Qasem (mq222bp) This project aims to create a baseline or a little more than a POC (Proof Of Concept) for how to create a wireless movement detection unit that can detect the change of speed of any electrically conductive object including animals and humans, as well as a home unit consisting of a microcontroller with an that with the help of wireless module can communicate with the microcontroller inside the detection unit. In it's current state this project is not optimized for power saving but it can easily be modified to accomplish that (recommended). Time approximation: 3 hours ### Objective As an outside-cat 😸 owner I always had the problem of having to check the balcony to see if my cat is back home or not. I manly have to do that because my cat never tries to grab attention when he is back but rather decides to just sleep on the chair outside drawing the sympathy of everyone passing by. I therefor came up with the idea of a waterproof enclosure with a sensor and a microcontroller inside that would send a signal to another microcontroller placed inside the house that would notify the family whenever the cat is back. This project should be able to give you a bit of idea on how to read data from sensors as well as how to communicate wirelessly between 2 microcontrollers. ### Material #### Detection Unit: | 📃Name | 💲Price | 🔗Link | 🔨Usage | 💬Comments | | :-------------------------------------------------: | :----: | :----------------------------------------------------------: | ------------------------------------------------------------ | :----------------------------------------------------------- | | 1x HFS-DC06 | ~7.22$ | [Link](https://www.banggood.com/HFS-DC06-5_8GHz-Microwave-Radar-Sensor-Module-DC-5V-ISM-Waveband-Sensing-12M-p-1250518.html) | Used to detect the cat when it walks into the balcony. | There are cheaper sensors out there but what makes this one stand out is the easily adjustable sensitivity and delay as well as the 180 degree detection area contrary to the 360° on most other sensor. | | 1x DHT22 | ~4.29$ | [Link](https://www.banggood.com/AM2302-DHT22-Temperature-And-Humidity-Sensor-Module-p-937403.html) | Used to measure temperature and humidity inside the enclosure. | Not really necessary. | | 1x DC-DC Boost Converter (Step Up) | ~2.82$ | [Link](https://www.banggood.com/DC-DC-Boost-Buck-Adjustable-Step-Up-Step-Down-Automatic-Converter-XL6009-Module-p-1087346.html) | Used to smooth the voltage going to the HFS-DC06 sensor, Can be replaced with any smoothing circuit. | It is not used to boost the 5v input at all. 5v in ➡ 5v out. | | 1x Arduino Pro mini 3.3v | ~7.83$ | [Link](https://www.banggood.com/3Pcs-3_3V-8MHz-ATmega328P-AU-Pro-Mini-Microcontroller-With-Pins-Development-Board-p-980290.html) | Reads data from DHT22 and HFS and decides when the data should be sent to the home unit. | The brain of the detection unit. Can be replaced with any similar board or any other board if you account for the differences. | | 1x FT232RL FTDI USB To TTL Serial Converter Adapter | ~3$ | [Link](https://www.banggood.com/FT232RL-FTDI-USB-To-TTL-Serial-Converter-Adapter-Module-p-917226.html) | Used to program the Arduino pro mini | **IMPORTANT** Make sure to move the jumper to the 3.3v position to match your Arduino board voltage otherwise you will fry your Arduino board. | | 1x USB breakout board | <1$ | [Link](https://www.banggood.com/10pcs-Micro-USB-To-Dip-Female-Socket-B-Type-Microphone-5P-Patch-To-Dip-With-Soldering-Adapter-Board-p-1165563.html) | Used as a main input port to power the detection unit . | Optional, but can make life easier. | | 1x NRF24l01+ or non-plus | ~2$ | [Link](https://www.banggood.com/NRF24L01-SI24R1-2_4G-Wireless-Power-Enhanced-Communication-Receiver-Module-p-1056647.html) | Used to communicate with the home unit. | You might also need a 10uF capacitor or even better ➡ [Link](https://www.banggood.com/Socket-Adapter-Module-Board-For-8-Pin-NRF24L01-Wireless-Transceiver-p-935508.html), Also you can ditch that entirely and use a ESP board but you will have to optimize the power usage. | | 1x Waterproof ABS Plastic Electronic Box | ~9$ | [Link](https://www.banggood.com/Waterproof-ABS-Plastic-Electronic-Box-White-Case-6-Size-p-948279.html) | Used to house the electronics and protect them from humidity, water, rain and snow. | I got the 200x120x75mm one but it's dependent on your components, especially the battery. | | 1x Omni Plus Metal Powerbank 20.000 mAh | ~75$ | [Link](https://www.trust.com/en/product/22790-omni-plus-metal-powerbank-20-000-mah-usb-c-qc3-0) | Used as the main power source for the detection unit | Not recommended. I bought along time ago when there was a great discount on it. You can get power banks with much bigger capacity for a lower price or you can use a lipo/lion battery with a charge and protection circuit if you want to cut down on space. | | 1x Strong Velcro tape | ~10$ | [Link](https://www.clasohlson.com/se/Kardborreband-Gripotec/p/34-4682) | Used to stick the power bank to the lid of the enclosure. | I really recommend this one since it has super strong Velcro with good adhesive side. | #### Home Unit: | 📃Name | 💲Price | 🔗Link | 🔨 Usage | 💬 Comments | | :--------------------------: | :-----: | :----------------------------------------------------------: | ------------------------------------------------------------ | ------------------------------------------------------------ | | 1x LILYGO TTGO LORA32 868Mhz | ~17.13$ | [Link](https://www.banggood.com/LILYGO-TTGO-LORA32-868Mhz-SX1276-ESP32-Oled-Display-bluetooth-WIFI-Lora-Development-Module-Board-p-1248652.html?rmmds=myorder&cur_warehouse=UK) | Used as the brain of the main unit. Has 2 buttons to control the unit and send commands. | Again any other similar microcontroller works, you can also use an external SPI OLED Screen | | 2x 6mm push button | <1$ | [Link](https://www.banggood.com/100pcs-Mini-Micro-Momentary-Tactile-Tact-Switch-Push-Button-DIP-P4-Normally-Open-p-917570.html?rmmds=search&cur_warehouse=CN) | Used as the direct input to the TTGO board | Didn't buy it since I already had it | | 1x Passive Buzzer | <1$ | [Link](https://www.banggood.com/30pcs-3V-12V-Buzzer-16R-Resistance-AC-Passive-Buzzer-12085-p-1479120.html?rmmds=search) | Used as an acoustic indicator of either cat or high temperature | Didn't buy it since I already had it | | 1x NRF24l01+ or non-plus | ~2$ | [Link](https://www.banggood.com/NRF24L01-SI24R1-2_4G-Wireless-Power-Enhanced-Communication-Receiver-Module-p-1056647.html) | Used to communicate with the detection unit. | You might also need a 10uF capacitor or even better ➡ [Link](https://www.banggood.com/Socket-Adapter-Module-Board-For-8-Pin-NRF24L01-Wireless-Transceiver-p-935508.html). Also you might want to use the amplified version but make sure you got a good power source. | #### Other: | 📃Name | 💲Price | 🔗Link | 💬 Comments | | :----------------------------------------: | :----: | :----------------------------------------------------------: | ------------------------------------------------------------ | | Raspberry pi 4 | ~46$ | [Link](https://www.electrokit.com/produkt/raspberry-pi-4-model-b-1gb/) | Used as a backend server and MQTT broker (optional if you want to ditch datalogging and telegram notifications) | | Dupont header kit | ~4$ | [Link](https://www.banggood.com/Geekcreit-310pcs-2_54mm-Male-Female-Dupont-Wire-Jumper-With-Header-Connector-Housing-Kit-p-1063303.html?rmmds=myorder&cur_warehouse=CN) | Recommended. Can be very useful and it makes for a solid enough connection without any permanent bonds | | Jumper Cable DuPont Wire Rainbow Flat Wire | ~9$ | [Link](https://www.banggood.com/Geekcreit-5M-1_27mm-20P-Jumper-Cable-DuPont-Wire-Rainbow-Flat-Wire-Support-Wire-Soldered-p-959792.html?rmmds=myorder&cur_warehouse=UK) | Not Recommended. Or at least not from this seller. The one I got had a very strong paint smell. The wires are aluminum with copper colored coating so solder **NEVER** sticks to them. A better alternative would be ➡ [Link](https://www.amazon.de/gp/product/B01BI1G88C/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1) | #### Notes and recommendations: The choice of the microwave sensor is specific to my use case. I wanted a sensor that: 1. Could sense my cat without annoying it, so no ultra sound (has been tested and it really disturbs **MY** cat, your cat might not react in the same way) 2. Can be placed outside without being effected by the sun (PIR Out) 3. Don't want to drill a hole in the enclosure and have to re-seal it with all that in mind the doppler effect sensors were the way to go. The reason I settled on the more expensive HFS-DC06 is because it's easily adjustable as not to be so sensitive and detect the neighbors going into the building or walking past the balcony, and also for having a 180 degrees filed of sense instead of the more common 360 degrees found on other sensors. If you decide to follow this tutorial and use the **NRF24l01** then I really recommend using the base module and applying a stable 5v instead of applying direct 3.3v from the microcontroller like I did since it will lead to more stability. Also I would recommend ditching the **TTGO LORA32** board for a different board with **ESP8266** and running **circuitpython** instead of **micropython** since it has a much better [library](https://circuitpython-nrf24l01.readthedocs.io/en/latest/) for the **NRF24l01** module. The Project consists of 3 Parts or main units: 1. The detection unit. 2. The home unit. 3. The Raspberry pi backend unit. Every one of those 3 stages has some kind of improvement or adaptation that can be done. I can never say for sure which modification is best since it is very case specific but one thing I can say for sure is that you will need to do some kind of modification either in the software or hardware to get the best out of this project. ***You don't need a home unit and a phone notification is enough?*** Replace the Arduino pro mini with an ESP board with deep sleep and you're good to go. ***You don't care for datalogging and just need a simple acoustic indication?*** ditch the raspberry pi. *** You want a simple, cheap, easy, and power efficient setup?** Train you cat to ring a bell and voila!! One thing that I am considering is to ditch the **NRF24l01** and the **Arduino pro mini** in favor of a cheap **ESP** module and using **MQTT** to communicate with the **Raspberry PI** backend and the home unit. Although that would probably work nicely I still prefer to have a setup that would work even without the raspberry PI broker. ### Computer setup #### The detection Unit: For the detection unit I used [Arduino IDE](https://www.arduino.cc/en/main/software) and connected the Arduino board to the FTDI module which I connected to my computer through USB. To upload the code to the Arduino board all I had to do was: 1. Choose the board type: "Arduino Pro or Pro mini". 2. Choose the processor type: "Atmega328p (3.3v 8Mhz)" 3. Choose the correct serial port (for example COM4). 4. Click on the upload button #### The home unit: I used **VSCode** since I am fairly familiar with it and already am using it for a multitude of other things. The only thing needed to proceed was to install [Pymakr VSCode Extension](https://marketplace.visualstudio.com/items?itemName=pycom.Pymakr) following [this](https://docs.pycom.io/pymakr/installation/vscode/) tutorial. I also installed [micropy-cli](https://github.com/BradenM/micropy-cli) in order to get Intellisense support for micropython libraries by following [this](https://lemariva.com/blog/2019/08/micropython-vsc-ide-intellisense) tutorial. Uploading code to the device was very easy: 1. Make sure all the files that I need are in the **src** folder and all the libraries that I want to upload are in the **lib** folder. 2. Make sure that I am connected to the device and that the device is plugged in. 3. Click the upload button. The devices then resets on it's own but if that's not the case you can manually reset the device using the **RST** button. #### The Raspberry Pi MQTT broker and backend: Now all that was left to do is to install [Mosquitto](https://mosquitto.org/) MQTT broker by following [this](https://randomnerdtutorials.com/how-to-install-mosquitto-broker-on-raspberry-pi/) tutorial. Then I installed [NodeRed](https://nodered.org/) by following [this](https://randomnerdtutorials.com/getting-started-with-node-red-on-raspberry-pi/) tutorial, and to be able to store data and visualize it I set up [Grafana](https://grafana.com/) along side [InfluxDB](https://www.influxdata.com/get-influxdb/) by following [this](https://simonhearne.com/2020/pi-influx-grafana/) tutorial. Before proceeding we are also going to add a few pallets to NodeRed: | 📃 Name | 🔨 Usage | 🔗 Link | | :--------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | | node-red-contrib-influxdb 0.4.1 | Used to store data to the local influxDB | [Link](https://flows.nodered.org/node/node-red-contrib-influxdb) | | node-red-contrib-binary 0.1.3 | Used to parse the binary struct that will be sent from the detection unit through the main unit | [Link](https://flows.nodered.org/node/node-red-contrib-binary) | | node-red-contrib-telegrambot 8.1.0 | To be able to send and receive messages and commands from a telegram bot | [Link](https://flows.nodered.org/node/node-red-contrib-telegrambot) | ### Putting everything together ### Home #### The detection Unit: ![](https://i.imgur.com/lryD53b.png) In the schematics above you won't need **R1** resistor if you use the **DHT22** breakout board listed in the material list above since it already comes installed. However if you use the sensor on it's own without the breakout board then you will need to place a **1k Ω** resistor between **VCC** and **DATA**. The **"PowerBank Button +"** is connected to the high lead of the on button of my power bank. This isn't necessarily the same for you and probably is not the same. More on that later. The boost converter might be needed since some reported having an issue with just supplying **5V** to the sensor and needed to boost it to **6V** or more. However I solved my issues by using a boost converter without actually boosting the voltage. The reason boost converters solve the problem is probably because it soothes it out not because it is boosting it and therefore you can just use a regular **5V** power source and just use a circuit to smooth it out. The **1k Ω** and **2k Ω** between **GND** and **OUT** of the **HFS-DC06** sensor are a simple voltage divider. Since the **OUT** pin of the sensor is **5V** **HIGH** when it detects movement we need to reduce the voltage to **3.3V** so we don't fry the Arduino. This also means that you won't need to do that if you have a **5V** board or if you power the board with **3.3V** which I don't recommend. I also didn't check the voltage level when the sensor is supplied with a voltage higher than **5V** since it can actually withstand higher voltages than **5V**. The resistors values were calculated using the formula: $$ V_{out} = \frac{V_{s} \cdot R_{2}}{R_{1} + R_{2}} $$ Where **V<sub>out</sub>** is the voltage we want at the end of the division which in our case is **3.3V** And **V<sub>s</sub>** is the source voltage we are getting from the **OUT** pin on the sensor which in our case is **5V** I choose *R<sub>1</sub>** to be **1k Ω** and if we substitute all the values we get: ​ $$ 3.3 = \frac{5 \cdot R_{2}}{1000 + R_{2}} $$ If we solve for **R<sub>2</sub>** we get **1.941** and the closest resistor value we have to that is **2K Ω**. As you might have noticed we can use other resistor values if we use the same ratio between **R1** and **R2** but that's only in theory. In practice we have to account for current and power that will be running through the resistor so it doesn't overheat and burn. To wire everything up I used multi-core copper wires and crimped DuPont terminals to the end so that I can easily connect them to the Arduino board and the sensors without worrying about having a permanent connection. The HFS-DC06 sensor however was soldered to the boost convert out. I also created a 3D printed holder and a little bit of electrical tape to keep everything in place. After everything was wired up it looked something like this: ![](https://i.imgur.com/LFGBWCb.jpg) In the picture you can also see that I have added a **10uF** between **VCC** and **GND** of the **NRF24l01** module. This has been confirmed by many to lead to better performance and more stability. I still however recommend using a base module and supplying **5V** to the base module from a good power source aka not the Arduino :P . You might have noticed that the power bank is missing and that there is no more space for it. The power bank I got is quit big and heavy and would not fit into the enclosure if placed on it's edge and it will take more than 75% of the space if placed facing down. My solution was to use the empty space above the components by sticking some Velcro to the enclosure lid and to one side of the power bank as such: ![](https://i.imgur.com/ZjGneUQ.png) ![](https://i.imgur.com/Nv9DvYY.png) #### Power bank auto shut-off: Most power banks if not all have a feature that prevents battery drain by shutting down that battery after a specific amount of time if not enough current is being drawn. The current draw of the current setup is around **50mA**. The power bank seems to require more than **100mA**. I also measured the time between the absence of current and the power shut-off and found that it was exactly **30 seconds** . Some power banks do have a so called **"always on mode"** which can be accessed in different ways such as holding a button for a set amount of time. I tried multiple different ways but with no success and never got an answer from the manufacturer regarding the feature. After looking on the internet I found that some people add a dummy load which increases current rate or create a circuit that draws high current for a short amount of time every now and then to reset the battery timer. My solution was based on the fact that the power bank button resets the timer if clicked during the **30 seconds** delay (holding the button has no effect). So knowing that I opened the power bank and checked the button with my multimeter do discover that it was normally closed with **3.1V**. That meant I could just pull that signal to ground by connecting it to a GPIO pin set as **OUTPUT** **LOW** on my Arduino. So I soldered a wire to the buttons + and 3D printed a new button for the power bank with a hole in the middle for the wire to pass through. I then crimped it and added a female DuPont connector to the end and now it was ready to be connected to my Arduino. I tested it with a minimal testing sketch and it was working perfectly! ##### ⚠️Disclaimer⚠️ I do **NOT** recommend that you follow the previously mentioned steps if you decide to do that procced at your own risk. Remember that battery cells are sensitive to high heat and might explode if poked 🔥. Also it's very possible that you short ⚡ something which also could cause an explosion. Power banks come with short circuit protection but that is for what comes after the USB port so you can't rely on it since you might short something that is connected behind the short circuit protection. You also need to understand that this is very specific to this power bank and this revision. Even if you have the same exact power bank it's not guaranteed that you will have the exact same PCB inside so do your own research and proceed at your own risk. #### The home unit: ![](https://i.imgur.com/ciNhw3F.png) Do note that the board in the picture is the **heltec lora 32 v2** while the one used in this project is **LILYGO TTGO LORA32 868Mhz v1**. However the only fritzing board design I found for the **TTGO V1** used in this project had wrong pin order where **GPIO PIN 5** and **GPIO PIN 15** were swapped. The GPIO pins of both boards is exactly the same so you can follow the wiring as shown above when it comes to the GPIO numbering without a problem. What you need to be aware of however is the fact that the Heltec board has **GND-3v3-3v3** on one side and **GND-5V** on the other. Whereas the **TTGO v1** board has **GND-5v-3v3** on both sides. #### ### Platform #### The Detection unit: For the detection unit I went with **Arduino** since I am using an **Arduino pro mini**. This unit communicates with the home unit through the NRF24 module. It pings the home unit with a 6 bytes payload that describes the temperature reading from the **DHT22** sensor, the current state of the detection, and if any movement has been detected (Alarm). The state of the detection can be one of 3 values. It's either **Enabled**, **Disabled** or **Waiting**. Waiting means that the microcontroller has been instructed to enable detection after a predetermined delay. This is the default behavior for the enable button on the home unit. When I enable the detection I don't want it to detect my cat going out but rather wait for a while so that my cat is out of the balcony before the detection unit starts to detect movement. If the detection unit is in the **Disabled** or **Waiting** state the signals from the **HFS-DC06** are ignored. However if the sensor is in the **Enabled** state then it will try to immediately send a packet with alarm set to true. If it fails then it will keep the value set to true and that value will be sent along with every ping until the ping is received by the home unit. This ensures that even if the packet with the alarm indicator was lost then the alarm will indicated until it's received. #### The Home unit: For the Home unit I had the option of using either **Arduino** or **Micropython**. Unfortunately due to the board being based on the **ESP32** chip I didn't have the option of installing **CircuitPython** -which is a fork of **Micropython** by **Adafruit**- since it has better libraries for interacting with the NRF24l01. I settled on **Micropython** since it would allow me to take benefit of the advanced features that the **Python** language has which will come in handy when it comes to stuff like connecting to WIFI, exception handling and MQTT. Also it is much easier to debug code running **Micropython** due to the way **Micropython** works. The Home unit tries to connect to WIFI on boot. When the main program is run it will try to connect to the MQTT broker and if that succeeded it will indicate that by showing a WIFI icon on the OLED display. If the connection was not successful then it won't try to connect again and will only show a different icon indicating that failure. The user then can press and hold the red button to retry. The home unit checks for packages from the detection unit and if so it will check the temperature and the alarm indicator. High temperature and alarm cause an alarm state which will invert the color of the screen, show a message and start the buzzer. If the temperature is high and movement has been detected then the high temperature message is prioritized. If the temperature is too low or if the no packets has been received in while a warning sign ⚠️ will be shown to alert the user of the problem, however there will be no sound warning as not to annoy the user every time the connection is lost or the temperature is too low. The home unit also passes the data from the detection unit as is to the MQTT broker if it's connected to it. This allows the home unit to function with the detection unit without the need of a MQTT broker but also it can work with the MQTT broker without a problem if it's up and running. To interact with the home unit 2 colored buttons are installed. Both support single clicks, double clicks and hold. The double clicks are disable since there are not needed currently and are not as easy to accomplish as single clicks and holding. If the green button is clicked then it will send a command to put the detection unit in **Waiting** state. If the the button is held down it will send a command to put the detection unit in the **Enabled** state. If the command was successfully sent the screen will blink. Unfortunately due to how the NRF24l01 driver works it's possible that the command will be received by the NRF module but not read by the microcontroller. It seems to happen mostly when a command is sent after a package is received but that wasn't consistent and more research is needed. The red button is used to dismiss the alarm if it has been triggered by single clicking the button. If the alarm has not been triggered then single clicking the button will send a command to the detection unit to put it in the **Disabled** state. If the button is held down it will try to connect to the MQTT broker if the connection is not already in place. #### The Raspberry Pi Backend: The original plan was to use my own **Nodejs** or **ASP Core** implementation to manage and visualize data as well as an **SQLite** database to store the data. I already had a working version but after seeing how well **NodeRed**, **InfluxDB** and **Grafana** work together, how easy it is to integrate them, as well as how feature rich they are I decided to ditch my own website implementation. The biggest advantage of this approach is how flexible it is, I can very easily change how I want to handle the data, how I want to store it and finally how I want to represent it in a few minutes. Since I am using my raspberry pi and running it on my local network I didn't have to worry much about the rate of sending or how often I was sending the data which is the biggest reason for the unnecessarily huge data rate of 6 bytes every 5 seconds. The data sent to the raspberry pi is mostly discarded unless it indicates high temp, low temp, or indicates an alarm. In that case it will be stored to the influx database which uses a retention policy of 4 weeks, and also a message will be sent from my Bot on telegram to my telegram account. #### The Telegram bot: In order to receive notifications on my phone I decided to create a telegram bot that will be used by NodeRed to send me messages every time my cat is detected in the balcony. There are many options to get notifications ranging from custom apps that can be programmed by placing blocks to already made solutions just for that purpose. They also range from totally free to expensive. I already use **IFTTT** webhooks to get a notification every time my printer finishes a print but I can't say that I am satisfied with how long it takes before I get a message. I also remember that the procedure to setup the webhooks was a bit tedious to the point I didn't want to touch them ever again. Can't remember what exactly made the experience the bad for me but one thing is for sure...Telegram bot solves my problem. It's so easy to create a bot. The only hard thing is to find a name not already taken, but other than that it's pretty straight forward. You send commands as messages but you already have a button to display all commands. You can with a few messages add new commands, change the picture, nickname and much more. I also get a notification as soon as a movement is detect since I set it up to use the polling mode in **NodeRed**. ### The code Since the code is a bit long to be included in this tutorial I decided to upload it to [my Github](https://github.com/mhmd98/Cat-Detector-POC). Feel free to submit pull request as well as forking the project. I will go a little bit through some part of the code that might need to be explained other than you should be able to understand the code since it's not that complicated. I used [Adafruits SSD1306](https://github.com/adafruit/micropython-adafruit-ssd1306) library for micropython but I had to modify it a bit by defining the function **fill_rect()** that already exists in the underlying [framebuf](https://docs.micropython.org/en/latest/library/framebuf.html) class but is not exposed by the library as well as overload the **pixel()** function to also expose the full potential that is provided by the [framebuf](https://docs.micropython.org/en/latest/library/framebuf.html) class. ```python def fill_rect(self,x,y,w,h,c): self.framebuf.fill_rect(x,y,w,h,c) def pixel(self, x, y, col = None): if(col == None): return self.framebuf.pixel(x, y) else: self.framebuf.pixel(x, y, col) ``` #### The detection unit: The detection unit has a main loop which checks if the variable **commandToExcute** has a value different than **NOCOMMAND** which is equal to **0**. If that's the case then it will begin executing the code that matches the command value. the **HFSTrigger()** function is an interrupt callback function that is called whenever the signal from the **HFS-DC06** sensor goes from **LOW** to **HIGH**. The function checks if the state is set to enabled and if it's it will change the variable **isHfsTriggered** to **true** which in return will cause the main loop to execute the appropriate code. The **NRFTrigger()** function is also an interrupt function that is called whenever the **NRF24l01** module receives a packet. The function [maskIRQ()](https://tmrh20.github.io/RF24/classRF24.html#abf68b9b0c9cd17179e9e144c3e7f9c45) function is used to decide the behavior of the interrupt pin so it is possible to mask or ignore the interrupts that happen when sending or on failure. When a new packet is received the microcontroller will read the data from the chip equal to the size of the predetermined payload size. Then it will empty the buffer after reading since that is commented out for some reason in the **NRF24l01** library used. The main loop also constantly checks if the buffer is full and empties it, which might cause packet loss but it could otherwise lead to the microcontroller crashing. I still didn't figure out what exactly is causing the problem but will continue to research the behavior if I feel it is happening too often. ```python= void HFSTrigger() { if (currentHFSState == SensorEnabled && !isHfsTriggered) { isHfsTriggered = true; } } void NRFTrigger() { commandType commandReceived = NOCOMMAND; // set the command to be no command if (radio.available()) // check if there is a packet { byte payload[payloadSize]; radio.read(&payload, sizeof(payloadSize)); // read the command and store it in the variable created radio.flush_rx(); commandReceived = (commandType)payload[0]; } commandToExcute = commandReceived; // set the global command variable to the command received } ``` #### The home unit: The home unit runs micropython which isn't really my specialty and I was learning as I go so there is a big chance that some parts of the code don't follow the best practices of the python language. Also due to the lack of time on my part I had to take a few shortcuts here and there in exchange of code reusability. This is the main loop of the home unit: ```python while True: # check how the red button is pressed red_b = checkButton(red_button) if red_b == 1: red_click_event() if red_b == 2: red_double_click_event() if red_b == 3: red_hold_event() # check how the green button is pressed green_b = checkButton(green_button) if green_b == 1: green_click_event() if green_b == 2: green_double_click_event() if green_b == 3: green_hold_event() if not alarm_state: if(beeper.duty() != 0): beeper.duty(0) # turn it off then draw_status_screen(oled) oled.show() else: # alarm is on if(time.ticks_diff(time.ticks_ms(), alarm_sound_time) > 1000): # has it been a second? beeper.duty(512) # turn it on if(time.ticks_diff(time.ticks_ms(), alarm_sound_time) > 2000): beeper.duty(0) # turn it on alarm_sound_time = time.ticks_ms() # check for nrf24 packages if radio.any(): # check if package is recieved radio_timeout_time = time.ticks_ms() radio_timedout = False # bool and char in arduino are represented as unsighned char with python type int for some reason, hence the usage of the two b's in the format payload = radio.recv() data = struct.unpack(struct_format, payload) info = list(data) # but the data into a list print(info) # convert the numeric value of the state to the char value char = chr(info[2]) info[1] = float("{:.2f}".format(info[1])) # info[2] = float("{:.2f}".format(info[2])) if(client is not None): try: client.publish(topic_pub, payload) except OSError as e: print("failed to send MQTT message") # convert the value from a char to a string representing the state if char == 'D': info[2] = "Disabled" elif char == 'E': info[2] = "Enabled" elif char == 'W': info[2] = "Waiting" else: info[2] = "UNKNOWN" latest_values = info[1:] # get the values that need presentation if info[1] > 35 or info[0] == 1: # temp too high or alarm alarm_state = True if info[1] > 35: # temp too high text = "!!TOO HOT:{}C!!".format(info[1]) draw_alarm_screen(oled, text, warning) else: # alarm text = "!!CAT DETECTED!!" draw_alarm_screen(oled, text, cat) oled.show() # show the buffer on the oled screen # check for mqtt messages if(client is not None): try: client.check_msg() except Exception: client = None # check for timeout if time.ticks_diff(time.ticks_ms(), radio_timeout_time) > (radio_timeout_delay * 1000): radio_timedout = True latest_values = [None, None] else: radio_timedout = False ``` The loop first runs the function **checkButton()** which is taken and translated from an Arduino sketch **(4-Way Button By Jeff Saltzman)**. I converted the sketch to python and also removed the long hold function as well as converted the button logic to work with a button class called **AdvancedButton** so that I can assign use it with multiple buttons more easily. The button then checks if the alarm has been triggered which is indicated by the **alarm_state** variable and if true will draw the appropriate text and graphics on the OLED screen as well as sound the buzzer. After that it will be time to check if any new packets are in the **NRF24l01** buffer and if so read them and send them as is to the **MQTT broker** if there is an active connection to it. The data is then checked and handled accordingly. The last 2 steps in the loop are to check if any message has come from the **MQTT broker** and to check if the time since the last packet from the detection unit has exceeded the timeout value and if so change the **radio_timedout** value to **true** which will be handled in the next loop cycle. ### Transmitting the data / connectivity Since I am running everything locally and I am mainly testing stuff I had the opportunity to test sending data at quite high rate of 6 bytes every 5 seconds. The data is send from the detection unit to the home unit over 2.4GHz band through the **NRF24l01** module which uses the **"Enhanced ShockBurst Protocol"**. The data rate used for the module is 250KB per second. Do note that the data rate, channel and CRC has to be the same on both modules in order for them to communicate with each others. From the home unit the data is display on the OLED screen but also passed as is to the MQTT broker **Mosquitto** that is running on my raspberry pi if the home unit is connected to it. The data is then received by NodeRed that is running on the same raspberry pi and is checked in order to determine if it should be discarded or not. If not it will be stored and sent to me on telegram through my telegram bot. ##### Transmission protocol: ```mermaid graph TD A[Detection unit] -->|2.4GHz| B[Home unit] B --> |WIFI| C[Raspberry pi] C -->|Local| D[NodeRed] D --> |Local| E[influxDB] D --> |Internet| F[telegram] ``` ##### Transport protocol: ```mermaid graph TD A[Detection unit] -->|Enhanced ShockBurst Protocol| B[Home unit] B --> |MQTT| C[Raspberry pi] C -->|MQTT| D[NodeRed] D --> |InfluxQL| E[influxDB] D --> |Polling| F[telegram] ``` Due to the usage of the normal NRF24l01module and not the amplified version, sending data that often doesn't seem to cause big battery drain. However it is very unnecessary to send data that often and instead it's a better approach for the final product to just put the Arduino board to sleep and wake it up when it has to send a packet or when the HFS sensor or the NRF24l01 send an interrupt signal. It can also be extended further and stop sending packets all together and instead only respond to pings coming from the home unit and therefor is in sleep until it either senses movement or responds to a packet. That also makes it much easier to re-configure how often the data is send just by changing how often the home unit pings the detection unit. One reason to have a short ping time is to immediately detect temperatures which is the reason I configured it as such in the first place so that I could test and see if the temperature would be too high to have a **20000mAh** power bank in a shut enclosure in outside weather. Also low temperatures can severely affect the performance of batteries leading to much shorter battery time. The data is sent as a **6 bytes** buffer consisting of **2 bytes** and a **float**. One of the **2 bytes** is for the current state and the other is to indicate if the alarm is triggered. The remaining **4 bytes** store the temperature reading from the sensor. The payload can be further reduced in size to **5 bytes** by representing the alarm and the state value in the same byte. Currently the state byte stores the numeric **ASCII** value of the first letter the represent the state so: **E,D, and W**. This means that I still have place for an extra number at the hundreds place so if I send **68** that means the state is **Disabled** and the alarm **is not** triggered but if I receive **168** that means the alarm **is** triggered and the state is **Disabled** ### Presenting the data Currently the data is stored in an **InfluxDB** database which seems like a very obvious choice when it comes to IOT application. Features like retention policy and how easy it is to configure makes it a really great choice. More importantly being a time series database means that I don't have to worry about time when storing data. The data that is stored currently is the temperature when the alarm was triggered or when it went over or under a set limit. I could reduce the retention police from **4 weeks** to **1 week** or less and have it register all the temperature values but temperature doesn't fluctuate that much especially in closed enclosure, which also is another reason do discard the data since it doesn't serve any other purpose since it does not accurately represent the actual temperature of the outside. One more thing I like about **InfluxDB** is how easy it integrates with **NodeRed** and **Grafana**. Speaking of **Grafana**. I created a new test dashboard that shows every time the alarm has been triggered and the temperature at that time and used points to represent the data. This way I can monitor when my cat arrives home and also can look to see if there are any patterns for when he comes back. The dashboard look rather empty currently but it gave me the Idea of moving the temperature senor when I am done testing from the detection unit to the home unit. This way I can have a dashboard for the Cat alarm and another for the temperature in the living room in one system. ![dashboard](https://i.imgur.com/Oys4Bg7.png) ### Finalizing the design To summarize the project was fun and quite straight forward. Having the right tools for the job made it a lot easier and also having a 3D printer allowed me to test some ideas that I probably would have totally ignored (such as the power bank reset button). The home unit still needs a case which I will be working on soon enough and I will also be modifying the code a bit to change it from one used to test the idea to one that is actually usable and deployable. I encourage you to try it out on your own and add your own touch, modify it and make it better. Good luck! :happy: ![final setup](https://i.imgur.com/cKWAnwu.png)