Software apps and online services
Ever wanted to make a motion-activated alert system that doesn't depend on WiFi or Bluetooth? Maybe you've set up a hunting trap and want to be notified when something's in the vicinity, or you're a beekeeper and want to know if there's something or someone prowling around your livelihood with nefarious intentions. Perhaps you have a really cool tree house that everyone envies and you want to be alerted when an uninvited guest makes his way up the rope, or maybe you're just clinically anti-social and want to be alerted whenever possible acquaintances present themselves. And then there's the just the average Joe craving for some adventure with electronics and IoT. In any case, we got you covered!
First of all, a quick shout out to Elegoo for sending me their Elegoo Mega 2560 starter kit. They have other kit versions available (like the Uno starter kit) and I will honestly say that they're great for beginners as these kits include just about everything you'll ever need to get started with most projects, and all in a nice organizer box!
In this tutorial we'll be using an Elegoo Uno, PIR motion sensor, and a Botletics SIM7000 cellular shield to send out text alerts to your phone when the motion sensor picks up motion. Now instead of constantly monitoring the motion sensor to see if there's any motion, the cool thing about it is that we can put everything to sleep when nothing's happening and use the output pin of the motion detector to wake up our device only when there's movement! This saves a great deal of energy and makes it possible to run the project on a battery. That being said, this particular project is to give you the fundamentals for how to approach this sort of project and isn't perfectly suited for ultra low-power applications due to the nature of the parts being used. However, feel free to improve on this project and have fun with it!
- Uno R3 board (or equivalent)
- PIR motion sensor (any should work, I used mine from Elegoo kit)
- 3.7V LiPo battery (> 1000mAH capacity)
- Some male-to-female Dupont jumper wires
The first thing is to set up the Botletics SIM7000 shield. If you haven't already, you might want to check out this getting started tutorial for the Botletics SIM7000 shield but if you're lazy just follow along anyway and you might be perfectly OK :D
The first step for the SIM7000 shield is to solder on the headers. This is explained in the shield's wiki tutorial which also contains a lot of other info you might need. Next, plug the shield directly into the Uno:
Now insert the SIM card (note orientation) so that it's all the way in (none of the SIM card should stick out of the holder):
You'll need to make sure you go online and register your SIM card so that you can start using it. You'll also need to attach the flexible antenna, making sure you have the right wires on to the right connectors (they will be criss-crossed):
Note that in this tutorial we won't be using GPS, but that's something you could program quite easily as it's already built into the shield! This could perhaps be useful if you're a forgetful person and left this device in the woods.
In order to adequately power the shield you'll need to connect a 3.7V LiPo battery to the JST connector on the shield. You can easily pick one of these batteries up from SparkFun or Adafruit and I suggest a battery size of 1200mAH or more, depending on how long you want this project to last.
PIR Motion Sensor
Now let's talk about the motion detector. This cool little sensor came with the Elegoo kit I received but you can get them just about anywhere and they pretty much all look the same and have the same pin layouts, whether they're not labeled or not. As you have probably noticed already, they can be called "PIR motion sensors", "motion detectors", etc. but the main way you can tell is that eyeball-looking plastic piece that covers the actual sensor. This gives the sensor a wider angle to detect motion.
The nice part is that there are only a total of 3 wires we have to connect! The picture below shows the PIR sensor pinout:
Make the following connections:
- Motion sensor VCC to the 5V pin on the Uno
- Motion sensor GND to GND pin on the Uno
- Motion sensor output pin to pin 2 on the Uno
So how this works is you power the sensor with VCC and GND pins (obvious to some), and the sensor's output pin is normally LOW (0V) when there's no motion, and it pulses HIGH (3.3V) then back LOW when it detects motion. (The duration of the pulse and the triggering behavior of this pin is explained in more detail right below). This pulse wakes up the microcontroller via an interrupt set to a rising edge (LOW to HIGH) trigger and our device proceeds to connect to the cell network and send you a text!
The other thing you'll notice are two potentiometers on the board and you can use a screwdriver to spin these around to change things. The first pot changes the sensitivity (range) of the sensor on one pot, and the second one sets the ON time from a minimum of 5s to a max of 200s but that really depends on what brand of sensor you have (for example, the ones Adafruit sells goes from ~2.5s to ~250s).
The main point here is that because the microcontroller wakes up when the sensor's output goes from LOW to HIGH (and not HIGH to LOW), you can somewhat set a timeout period between text messages by changing the ON time of the sensor. For example, if you set it to 100s, once motion is detected the microcontroller wakes up, sends you a text, and goes to sleep, but it won't wake up until *at least* 100s after the initial movement. This could be useful, for example, if you have set up a hunting trap and you know your catch will be stuck there for quite some time and don't want to receive 100 text message by the time you get there. However, to some extent this timeout can also be done via software.
One last thing I'll mention is the little jumper on the PIR sensor board. The layout might be slightly different depending on exactly which sensor board you're using, but they should all have 2 jumper positions labeled "L" and "H". By default mine was on the "L" position but for this project it actually won't matter at all what position it's in.
What this jumper does it set the "re-triggering" habits of the sensor. "L" makes it so that the output of the sensor goes HIGH and goes back LOW when triggered, whereas "H" makes it so that the output stays HIGH for the entire duration of the detected motion if (re-triggering).
Here's how your hardware setup should look like when everything's connected:
The only thing I haven't mentioned yet is powering the Arduino itself, which you can do by connecting it to your computer via USB. Again, for actual low-power implementations you wouldn't do this, but that isn't the focus of this tutorial.
Enough of that hardware business; let's actually move on to the code and start testing our contraption! The first thing you'll have to do is download and open the code in the attachments section of this tutorial.
Honestly I think I've laid out the code to where pretty much anyone can understand the gist of it, but I'll highlight some key features just in case. If you're really not interested in how it works or are already well-versed in these concepts, go ahead and upload the code and start testing, but WAIT! Remember to change the phone number on line 76 right before the setup() function to your actual phone number or you might be disappointed! And please don't change it to 911, please.
So for those who stuck around, let's talk a little about interrupts. You can find a ton of literature on this topic online so I'll spare you the boredom, but essentially the microcontroller normally runs the stuff in setup() once, then runs the stuff in loop() forever and ever. Interrupts allow the microcontroller to interrupt the process at pretty much any point in time and run an Interrupt Service Routine (ISR) in which you can do stuff like set logic flags (like "interrupted = true";) and maybe have it do some special thing when it returns to the loop() function. (After running the ISR the program goes back to loop()). This can be handy, say, if you set a flag like "blinkLED = true" in the ISR and when you go back into loop() you check for something like "if (blinkLED) <BLINK CODE HERE>".
Now in our case we'll be using external interrupts (we pulse a pin to wake up the microcontroller) and therefore we have to first set the interrupt pin. On the ATmega328P (the Uno's microcontroller), the hardware interrupt pin is pin D2 (labeled "INT0" on the datasheet). We could theoretically use other pins BUT then we'd have to use the pinChangeInterrupt library. So for now we'll keep it simple and just use pin D2. This means we can use the "attachInterrupt()" function in the Arduino IDE.
So here's the overall process:
- Run stuff in setup() once
- Run stuff in loop() once, because at the end of loop() the "sleepDevice()" function makes the microcontroller go to sleep
- In the sleepDevice() function right before actually going to sleep we attach the interrupt so that pulsing the pin will make it wake up from sleep. If we don't attach the interrupt nothing will happen when the sensor detects motion! Note that "digitalPinToInterrupt(wakeUpPin)" is the same as just using "0" because we are using INT0. However, if you use the pinChangeInterrupt library you will have to use digitalPinToInterrupt(pinNum).
- Right when the motion sensor detects motion it pulses our interrupt pin from LOW to HIGH (hence RISING edge trigger on the attachInterrupt() call), and this wakes up the microcontroller
- Right after waking up the microcontroller runs the ISR which in this case is called "wakeUp()". Notice how this function is empty because we don't really have anything to do here.
- Right after running wakeUp() it returns to loop()
- At the beginning of loop() we detach the interrupt so that if the sensor pulses the pin again, we won't have to start over! Remember, an interrupt does exactly that: it interrupts the program at any point in time, so if the interrupt is still active while we're trying to send a text message and the sensor pulses the interrupt pin again, we'd have to start over!
- After connecting to the cell network, sending a text, etc. the microcontroller turns off the SIM7000 modem and then sleeps itself, and the process repeats.
For ease of use we'll be using the Rocketscream LowPower library which basically provides convenient functions for us to sleep the microcontroller and sets all the appropriate register bits behind the scenes to reduce power consumption. The library also supports a variety of Atmel microcontrollers which is really convenient, especially if you swap out boards.
Now I have included commented out sections in the code that you can play with to do HTTP GET or POST requests instead of sending a text (or both if you want!). In order to use this, delete the comment markers (/* and */) and you will also need to uncomment the body character array on line 72 if you use the block of code for the POST request. For this tutorial we'll keep it simple and just send a text, but just know this option is available to you in case you want to link your project with a dashboard, IFTTT, or other bells and whistles!
Now is time to upload the code to your board and start testing! Again, please make sure you change the dummy phone number to your actual phone number. Just hit the upload button in the Arduino IDE and open the serial monitor. You should initially see the green "PWR" LED of the SIM7000 shield turn on as well as the blue network status LED, then the MCU will try to establish communication with the shield. Once it communicates you should see it try to connect to the cell network and after it does it will proceed to send you a text message and go to sleep! Now wave your hand in front of the motion detector and you should see it wake back up and send you another text. How much fun is that!!!
The following are the main culprits when it comes to power consumption:
- The microcontroller board
- The SIM7000 cellular modem
- The PIR motion sensor
The largest power consumption occurs when the cellular module is sending stuff (texts, HTTP requests, etc). However, once it turns off the SIM7000 shield only draws less than 8uA of current from the LiPo battery as detailed in this page.
The next biggest culprit is the microcontroller board. Most of the common development boards like the Uno/Mega boards out there aren't optimized for ultra low-power applications and their regulators have rather large quiescent (standby) currents when compared to others optimized for low power. Moreover, most of them have usb-to-serial converter chips on them to make programming much easier, but after you deploy the project they're just a pure waste of power.
The last thing is the PIR motion sensor, which draws around 70uA or so, depending on your board and depending on who measures it. I didn't bother measuring it myself since this project already isn't optimized and just the fact that I'm using an Uno board makes it meaningless to measure the PIR sensor consumption. Welcome to engineering: do as little as possible to get the job done :)
How to Improve Battery Life
This isn't really the focus of this tutorial but I just want to give you some pro tips on how to minimize power consumption for battery-powered applications:
- Use a lower-voltage microcontroller, like 3.3V/8MHz boards
- Choose a board without a USB-to-serial converter and use your own FTDI or CP2102/2104 board to program
- Take away any unnecessary power LED's that are always on
- Power the entire thing from a single battery instead of using 2 batteries. Since the SIM7000 shield runs on a 3.7V LiPo battery you can feed the battery voltage (VBAT pin on the shield) as the power supply to your 3.3V microcontroller board. However, please note that the PIR sensor requires a 4.5-20V input so you might also have to hack the sensor board's regulator or use a small boost regulator.
- If you're really picky, use your own linear voltage regulator with low quiescent current and de-solder the one already on the board.
For some quick info you can learn more about how to minimize the power consumption of your project in this SparkFun article.
Welcome to IoT: where small meets long battery life!
The PIR motion sensor is a go-to sensor for detecting motion and it's output conveniently allows us to wake up a microcontroller via an interrupt to save power. The SIM7000 shield gives us LTE CAT-M/NB-IoT cellular connectivity to send text messages, get GPS coordinates, and utilize cell data to send HTTP requests if needed and is a great low-power board for IoT applications in which WiFi or Bluetooth aren't available.
Feel free to improve this project and make it super low-power, build an enclosure for it, throw in some crazy stuff like sensors, cameras, etc. and have fun with it! Some have asked me about just using a pushbutton instead of a PIR sensor to initiate the text message, so I've attached code for that as well.
Best of luck with your project!