19 Mar 2020 - tsp
Last update 20 Mar 2020
7 mins
This is a really really short tutorial on how to use the NodeMCU Amica and an cheap PIR sensor board to build an WiFi capable motion sensor that publishes motion status via MQTT. This tutorial will also use the Arduino framework for the firmware because of it’s really low entry barrier. This is really an entry level project.
Basically all you need is (note: all links are Amazon affilate links, this pages author profits from qualified purchases):
The software also uses the Adafruit MQTT library that can easily be installed via the Arduino framework.
The assembly is pretty simple. Connect the Vcc and GND pins of the PIR module (on the linked HC-SR501 these are the outermost pins) to the 3V3 and GND pins on your NodeMCU. Then select any GPIO pin like the D7 (used in this sample) and connect the output pin of your module (middle pin). That’s all that’s required for your hardware setup.
This is basically a simplified version of the firmware I’m previously described for the Sonoff T4EU1C
Basically the structure of this code is pretty similar:
As usual for small Arduino programs I’ve used a bunch of definitions that can be automatically filled in by your deployment system (like Jenkins, etc.) that can also automate your OTA updates - or manually in case of a small amount of nodes:
{FILLIN_WIFI_SSID}
is the SSID of the network this node should connect
to{FILLIN_WIFI_PSK}
contains the preshared key. This sample does not
implement EAP-TLS, but that’s entirely possible using the ESP8266 SDKs
routines for enterprise WPA. In fact I’m using EAP-TLS for my personal
nodes (the artifacts included for WPA enterprise are actualy in there
to remove configuration for WPA enterprise to be able to test the code).{FILLIN_MQTT_HOST}
Hostname or IP adress of the MQTT broker. I’m using
RabbitMQ with the MQTT adapter enabled.{FILLIN_MQTT_PORT}
MQTT port that should be used. Depends on the usage
of SSL on the MQTT broker. Note that this sample does not use SSL but usage
of SSL is highly encounraged especially when using WiFi for your nodes.{FILLIN_MQTT_USER}
contains the username used during authentication on
the MQTT broker{FILLIN_MQTT_PASS}
is the password for the given MQTT user{FILLIN_MQTT_TOPIC}
contains the topic under which the JSON message will
be published.{FILLIN_OTA_HOSTNAME}
contains the hostname that should be announced
using mdns by this node.{FILLIN_OTA_SECRET}
is the shared secret that’s used by the OTA software
during upload. It should be different for each node and only be known to
the deployment system and the MCU.Note that all secrets can be extracted from the firmware when someone gets access to the device. This includes access to WiFi PSK, MQTT users and password and OTA secrets. Because of this one should:
The code also supports the following configuration options:
OTA_ENABLE
can be defined to allow over the air updating of the firmware.MQTT_RECONNECT_INTERVAL
is an reconnection intervall for the MQTT client
if it looses connection to the MQTT broker. It’s given in seconds and defaults
to 5
MQTT_REPORT_INTERVAL
configures at which rate the sensor should transmit
an status report even if the state has not changed. This can be used like
an heartbeat to detect failure of sensors (might be especially interesting
when used with battery powered devices). It defaults to 15
and is given
in seconds.MQTT_PROCESS_PACKETS_TIMEOUT
specifies after which timeout the process
packets callback should be called. Note that this parameter normally should
not be changed and defaults to 150
microsecondsPORT_MOTIONSENSOR
selects the port at which the motion sensor is
attached. Defaults to D7
PORT_LED
selects the port at which an optional status LED is
attached. Since the NodeMCU contains a builtin LED at digital pin 0 it
defaults to D0
. This might be interesting to verify operational
status of a sensor in the field.Basically the code just initializes the I/O ports, in debug mode also the serial port and dumps some information. Then it configures WiFi and tries to establish a connection (which will be reconnected automatically again after network loss). Storage of WiFi configuration in flash has been disabled since the credentials are already contained in the firmware (one might extend that in own versions - it’s necessary when using EAP to be capable of exchanging certificates on expiry). In case OTA hasn’t been disabled OTA will be initialized. It’s a really good idea to support OTA as long as the keys as unique and secure.
In the main loop the application tries to reconnect to MQTT after an WiFi
connection has been established whenever MQTT is not active. Then it handles
transfer of MQTT packets periodically and verifies pin changes on the
GPIO pin. In case of a pin change or a timeout for periodic reporting
the mqttSendStatus
function is invoked which assembles the JSON
message and passes it to the MQTT client for transmission.
The JSON message contains two fields:
status
indicates if the motion sensor has been triggered (true
)
or not (false
).changed
indicates if it’s a periodic report (false
) or if there
has been an event (motion sensor triggered or stopped triggering - true
).The code is available as a GitHub GIST
CHANGE
interrupt using attachInterrupt
with a static ICACHE_RAM_ATTR void (void)
function. This function will be called whenever the pin changes from low to high
or high to low. One can then either set a flag or perform some operations directly.
This might be interesting for faster sensors that might be missed with the polling
loop.Adafruit_MQTT_Subscribe
object
that’s bound to the mqtt
object. In the setup
function one then
binds an callback (by using setCallback
function on the subscribe object)
that sets the desired action. This might be an interesting feature to identify
devices when triggering the identification step from an app or webpage.This article is tagged: Electronics, DIY, Home automation, ESP8266, Programming
Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)
This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/