In this tutorial we built a smart home center with the Raspberry Pi. We can receive MQTT messages, save them in a database and then display them in a Grafana dashboard. In this tutorial we want to dedicate ourselves to collecting the data.
An ESP32 (NodeMCU) is used as the brain of the weather station. It's cheap, readily available, and well documented. In order to accelerate the development process even further, we use MicroPython as the programming language. MicroPython is based on Python 3.5 and already offers some libraries that make our lives easier. Even pip works on an ESP32! So, let's go!
In preparation, MicroPython should already be installed on the ESP32. I have how that works here shown.
I start with the connection to the Rasperry Pi. In this way, I can see my measurement results later and check now whether my WiFi connection will even reach the later place of use.
We write our MicroPython script in the main.py file, which we then copy to the ESP32. This is then carried out automatically after the start.
We start by turning on the wifi and connecting to the network:
import network import time wlan = network.WLAN (network.STA_IF) wlan.active (True) wlan.connect ("[SSID]", "[Password]") # wait until wlan is connected while not wlan.isconnected () : time.sleep (1) print (wlan.ifconfig ())
We then copy this file to the ESP32 (I have explained exactly how this works in this tutorial). The output should look something like this:
MPY: soft reboot ('192.168.178.59', '255.255.255.0', '192.168.178.1', '192.168.178.1') MicroPython v1.13 on 2020-09-02; ESP32 module with ESP32 Type "help ()" for more information. >>>
The ESP32 (name: "espressif") should now also be visible in our router. It continues with the MQTT connection. First install the micropython-umqtt.simple and micropython-umqtt.robust libraries. To do this, we type the following into the REPL:
import upip upip.install ('micropython-umqtt.simple') upip.install ('micropython-umqtt.robust') from umqtt.robust import MQTTClient
If no error messages appear here, the libraries are installed and the MQTTClient can be imported. The library is then permanently available. With this we can extend the main.py:
import machine from umqtt.robust import MQTTClient import ubinascii client = MQTTClient (ubinascii.hexlify (machine.unique_id ()), "[MQTT-Broker-IP]", "[MQTT-Port]")
Now we are connected to the MQTT broker and can send data in an endless loop. To simply test that, we send the WiFi signal strength (RSSI, Received Signal Strength Indicator).
import ujson while True: data = {"rssi": wlan.status ("rssi")} client.publish ("iot / werkstatt / wetter", ujson.dumps (data))
Now just add the MQTT topic in Telegraf (telegraf.conf):
[[inputs.mqtt_consumer]] topics = ["test / #", "iot /"]
And restart the Docker container:
pi @ raspberrypi: ~ / docker $ sudo docker-compose down pi @ raspberrypi: ~ / docker $ sudo docker-compose up -d
We may now have to restart the ESP32 again because Mosquitto was briefly gone after restarting the Docker container. Now we can create a dashboard in Grafana and display the WiFi signal strength as the first signal.
Here again the main.py, a little rearranged:
import network import time import machine from umqtt.robust import MQTTClient import ubinascii import ujson wlan = network.WLAN (network.STA_IF) wlan.active (True) wlan.connect ("[SSID]", "[Password]") while not wlan.isconnected (): time.sleep (1) print (wlan.ifconfig ()) client = MQTTClient (ubinascii.hexlify (machine.unique_id ()), "[MQTT-Broker-IP]", "[MQTT-Port ] ") client.connect () while True: data = {" rssi ": wlan.status (" rssi ")} client.publish (" iot / werkstatt / wetter ", ujson.dumps (data))
Okay, now it's time to solder. I used the following sensors:
Some of it is probably more gimmicky than really scientific work, but you have to get into the subject somehow. If you are interested in the topic, you can delve deeper. I'll make some improvements over time, but let's just start and get more complex.
So what do we need:
So, these considerations must now be implemented in a circuit. I use mine for this SolidCircuit-HV3. If you want to implement this project too, I would be very grateful if you could buy one of these boards as well. Unfortunately, they are not cheap, but they do a lot of work for you. The scope of delivery includes a waterproof housing, a power supply unit (you only need to connect 230V), some JST-XH plugs so that the cables to the sensors do not have to be soldered on, and 2 relays. We don't need the relays in this project, but if you later want to switch a lamp near the weather station via smartphone, you don't need an additional device. So, think about it, you would be very supportive of me. But enough advertising now. Here we go!
Here is the circuit diagram and a few photos of the board. I hope it helps you to recreate it.
When everything is soldered together, the programming comes. As I said, everything is simplified here, but I point out the pitfalls. In further tutorials I will gradually explain how I solved these problems. But first the simple variant. Before we go into the endless loop, we have to initialize the sensors. There are libraries for the I2C sensors that make it easier for us to query. I have this for the BME280 and this for the BH1750 used.
The analog values for the solar cell, wind direction sensor and microphone are read in directly via the ADC converter of the ESP32.
We set interrupts to count the pulses. These are executed as soon as the state of a pin changes. Within these interrupts we simply increase the corresponding counters.
import bme import BH1750 i2c = machine.I2C (0, scl = machine.Pin (22), sda = machine.Pin (21)) bme = bme280.BME280 (i2c = i2c) bh = BH1750.BH1750 (i2c) adc_sol = machine.ADC (machine.Pin (34)) adc_vol = machine.ADC (machine.Pin (32)) adc_wind = machine.ADC (machine.Pin (35)) wind_speed = 0 rain = 0 def callback (pin): global wind_speed global rain if str (pin) == "Pin (18)": wind_speed + = 1 elif str (pin) == "Pin (19)": rain + = 1 pin_wind_speed = machine.Pin (18, machine.Pin .IN) pin_wind_speed.irq (trigger = machine.Pin.IRQ_RISING, handler = callback) pin_rain = machine.Pin (19, machine.Pin.IN) pin_rain.irq (trigger = machine.Pin.IRQ_RISING, handler = callback)
We also need a translation function for the wind direction:
def get_wind_dir (adc): wind_dir_dict = {1030: 0, 360: 22.5, 602: 45, 120: 67.5, 225: 90, 185: 112.5, 1749: 135, 1520: 157.5, 3083: 180, 2730: 202.5, 3585 : 225, 3270: 247.5, 3980: 270, 2310: 292.5, 2433: 315, 849: 337.5,} for key in wind_dir_dict: if key-20> adc> key + 20: return wind_dir_dict [key]
Ok, the sensors are initialized, the reading continues. I would skip the microphone at this point. I'll deal with that separately in another tutorial.
We can then read out the sensors in the endless loop:
while True: bme_data = bme.read_compensated_data () v_sol = adc_sol.read () / 4096.0 * 3.6 # voltage of the solar cell in V i_sol = v_sol / 22.0 # current through the resistor p_sol = v_sol * i_sol # power of the solar cell temperature ": bme_data [0] / 100," humidity ": bme_data [2] / 1000," pressure ": bme_data [1] / 256," luminance ": bh.luminance (BH1750.BH1750.ONCE_HIRES_1)," wind_dir " : get_wind_dir (adc_wind.read ()), "wind_dir_raw": adc_wind.read (), "wind_speed": wind_speed, "rain": rain, "solar": p_sol, "solar_raw": adc_sol.read (), "rssi ": wlan.status (" rssi ")} client.publish (" iot / werkstatt / wetter ", ujson.dumps (data))
The weather station should now run. As already mentioned, some problems, especially the reed contacts bouncing, still exist. I describe in another tutorial how to deal with it.
Eydam prototyping
Saccasner Strasse 19
03096 Schmogrow-Fehrow
Germany