---
# System prepended metadata

title: Bridging DHT11 Sensor and LoPy4

---

## Bridging DHT11 and LoPy4 for Climate Insights on MagicMirror.

> Mustafa Aldaher (ma227hy)
> Estimated time: 3 hours

![](https://hackmd.io/_uploads/ry4TudlTh.jpg)


Imagine starting your day with a mirror that not only provides insights into your immediate environment but also organizes your day and keeps you ahead of time. The Smart Mirror does precisely that!

In this tutorial, we'll guide us through setting up an indoor climate monitoring system. By leveraging the capabilities of the DHT11 sensor and the LoPy4, we can fetch and display periodic updates about the indoor climate directly on our MagicMirror, powered by a Raspberry Pi. With projects like the MagicMirror, there's always room for creativity and improvement. Technically, we can add and integrate whatever we desire, making it a versatile platform to experiment and expand upon.


![](https://hackmd.io/_uploads/r1tC_f-pn.png)


## Indoor Climate Monitoring
- **```DHT11 Sensor:```** Measures temperature and humidity.
- **```LoPy4:```** Gathers data from the DHT11 and sends it to InfluxDB.
- **```InfluxDB:```** A time-series database where the climate data is stored.
- **```Raspberry Pi:```** Powers the MagicMirror and fetches data from InfluxDB at regular intervals.


### Material


| Item                        | Website                                                                                                 | Price   |
|:--------------------------- |:------------------------------------------------------------------------------------------------------- |:------- |
| Raspberry Pi                | [Electrokit](https://www.electrokit.com/produkt/raspberry-pi-4-model-b-1gb/)                            | 499 SEK |
| Lopy4                       | Not available                                                                                           | 34.95€  |
| Expansion Board             | Not available                                                                                           | 16.00€  |
| Antenna                     | [Electrokit](https://www.electrokit.com/produkt/raspberry-pi-compute-antenna-kit/)                      | 99 SEK  |
| Usb cable                   | [Electrokit](https://www.electrokit.com/produkt/usb-kabel-a-hane-micro-b-5p-hane-1-8m/)                 | 39 SEK  |
| DHT11                       | [Electrokit](https://www.electrokit.com/produkt/digital-temperatur-och-fuktsensor-dht11/)               | 49 SEK  |
| TV/Monitor                  | Any                                                                                                     | 500 SEK |
| One-way mirror (50 x 70 cm) | [Haglofsglas](https://haglofsglasshop.se/inredningsglas/speglar/spegel/spionspegel-6-mm-forhorsspegel/) | 910 SEK |
| Frame (50 x 70 cm)          | [IKEA](https://www.ikea.com/se/sv/p/ribba-ram-svart-50268874/)                                          | 229 SEK |


### Computer setup
Before diving into the project, let's begin by setting up our development environment with an IDE. In this tutorial, I'll be using VS Code, [download and install Visual Studio Code](https://code.visualstudio.com). 
However, feel free to choose and work with an editor that suits your preference.

Additionally, to facilitate the uploading and execution of code on the LoPy4 device, we'll be installing a VSCode extension: [Pymakr VSCode Extension](https://marketplace.visualstudio.com/items?itemName=pycom.Pymakr). This extension streamlines the process for LoPy4, ensuring a smoother development experience.

We'll also need NodeJS installed on our device. Please download the latest LTS version available from the [NodeJS website](https://nodejs.org/en).

We'll also be setting up the MagicMirror on our Raspberry Pi. To do so, please follow these [installation instructions](https://docs.magicmirror.builders/getting-started/installation.html).

### Putting everything together
Alright, let's piece everything together. As illustrated below, the DHT11 sensor takes measurements and feeds this data to the LoPy4 then processes this data and sends it to InfluxDB, ensuring that it's stored securely.

<img src="https://hackmd.io/_uploads/rkuuwH-Tn.png" width="400"/>


### Platform
In this project, the heart of data storage and management lies in the platform we've chosen: InfluxDB Cloud.

Our project primarily deals with time-stamped data from sensors, which are time-series in nature. InfluxDB Cloud is specifically designed to handle time series data, ensuring optimal performance and efficient querying.

The cloud infrastructure of InfluxDB ensures that our data is always available and safeguarded against potential failures.

### Get started with InfluxDB Cloud

1. Log In to InfluxDB Cloud: Navigate to [InfluxDB Cloud](https://cloud2.influxdata.com/) and sign in using your credentials.
2. Access the Data Management Interface.
3. Create a New Bucket.
5. Navigate to the Tokens Section.
6. Generate a New Token.
7. Specify Token Details:
    - *Description:* Give your token a name or description.
    - *Read/Write Permissions:* Specify the permissions for this token.
8. Save the Token.

## The micropython code
> LoPy4-based Climate Data Transmission to InfluxDB.

```python
import pycom
import network
import time
import requests
from machine import Pin
from dth import DTH
from config import TOKEN, URL, PIN, RED, GREEN, BLUE, SSID, PWD

# Set the heartbeat LED to False
pycom.heartbeat(False)

# Set the LED color to blue
pycom.rgbled(BLUE)

# Create a DTH object
dht_sensor = DTH(Pin(PIN, mode=Pin.OPEN_DRAIN), 0)

# Allow sensor to settle
time.sleep(2)

# Connect to WiFi
wlan = network.WLAN(mode=network.WLAN.STA)
wlan.connect(SSID, auth=(network.WLAN.WPA2, PWD))

# Wait for the module to connect to the WiFi
while not wlan.isconnected():
    time.sleep(1)

def send_to_influxdb(temperature, humidity):
    headers = {'Authorization': 'Token ' + TOKEN, 'Content-Type': 'text/plain'}
    data = 'temperature value={}\nhumidity value={}'.format(temperature, humidity)
    response = requests.post(URL, headers=headers, data=data)
    if response.status_code != 204:
        pycom.rgbled(RED)
        print('Failed to send data to InfluxDB, response code:', response.status_code)
    response.close()

while True:
    result = dht_sensor.read()
    while not result.is_valid():
        print('Error reading from sensor')    
        pycom.rgbled(RED)
        time.sleep(1)
        result = dht_sensor.read()

    pycom.rgbled(GREEN)
    print("Temperature: %d C" % result.temperature)
    print("Humidity: %d %%" % result.humidity)
    send_to_influxdb(result.temperature, result.humidity)
    time.sleep(60)
```

### Transmitting the data
Utilizing WiFi as the chosen wireless protocol, our device sends climate data to InfluxDB via HTTP POST requests every one minutes. This approach works well indoors, but WiFi uses more power than other options like LoRa.

For security, the system employs WPA2 authentication for WiFi access and uses authorization tokens for data transmission, and the use of HTTPS ensures that the data is encrypted and secure during transmission.


---



## The code of our MagicMirror module

### MMM-DHT11.js
```js
Module.register("MMM-DHT11", {
  defaults: {
    updateInterval: 60000,  // 1 minute
  },

  start: function () {
    this.temperature = null;
    this.humidity = null;
    this.update();
    setInterval(() => {
      this.update();
    }, this.config.updateInterval);
  },

  update: function () {
    this.sendSocketNotification('GET_DATA', {});
  },

  getDom: function () {
    var wrapper = document.createElement("div");
    wrapper.classList.add("dht11Container");

    if (this.temperature !== null && this.humidity !== null) {
      var strokeHumiValue = (this.humidity * Math.PI * 16) / 100;
      var strokeTempValue = (this.temperature * Math.PI * 16) / 50;

      wrapper.innerHTML = `
        <svg viewbox="0 0 36 19">
          <path class="dht11Circle_bg" d="M2,18 a16,16 0 0,1 32,0" />
          <path class="dht11CircleHumi" d="M2,18 a16,16 0 0,1 32,0" style="stroke-dasharray: ${strokeHumiValue} ${Math.PI * 16}" />
          <text class="dht11Humidity" x="18" y="11" dominant-baseline="middle" text-anchor="middle">${this.humidity}%</text>
          <text class="dht11Title" x="18" y="17.5">Humidity</text>
        </svg>
        <svg viewbox="0 0 36 19">
          <path class="dht11Circle_bg" d="M2,18 a16,16 0 0,1 32,0" />
          <path class="dht11CircleTemp" d="M2,18 a16,16 0 0,1 32,0" style="stroke-dasharray: ${strokeTempValue} ${Math.PI * 16}" />
          <text class="dht11Temperature" x="18" y="11" dominant-baseline="middle" text-anchor="middle">${this.temperature}°C</text>
          <text class="dht11Title" x="18" y="17.5">Temperature</text>
        </svg>`;
    }

    return wrapper;
  },

  socketNotificationReceived: function (notification, payload) {
    if (notification === "TEMPERATURE_DATA") {
      this.temperature = payload.value;
      this.updateDom();
    }
    if (notification === "HUMIDITY_DATA") {
      this.humidity = payload.value;
      this.updateDom();
    }
  },

  getStyles: function () {
    return ["MMM-DHT11.css"];
  },
});
```

### node_helper.js
```js
const NodeHelper = require('node_helper');
const { InfluxDB } = require('@influxdata/influxdb-client');
const { APIBase, Organization, Bucket, Token } = require('./config');

module.exports = NodeHelper.create({
  start: function() {
    console.log("Starting node_helper for module [" + this.name + "]");
  },

  getData: function() {
    const queryApi = new InfluxDB({ url: APIBase, token: Token }).getQueryApi(Organization);
    const fluxQueryTemp = `from(bucket: "${Bucket}") |> range(start: -7d) |> filter(fn: (r) => r._measurement == "temperature") |> last()`;
    const fluxQueryHumidity = `from(bucket: "${Bucket}") |> range(start: -7d) |> filter(fn: (r) => r._measurement == "humidity") |> last()`;

    // Query Temperature
    queryApi.queryRows(fluxQueryTemp, {
      next: (row, tableMeta) => {
        const o = tableMeta.toObject(row);
        this.sendSocketNotification('TEMPERATURE_DATA', { measurement: o._measurement, value: o._value });
      },
      error: (error) => {
        console.error(error);
        console.log('\nTemperature Query Finished with ERROR');
      },
      complete: () => {
        console.log('\nTemperature Query Finished SUCCESS');
      },
    });

    // Query Humidity
    queryApi.queryRows(fluxQueryHumidity, {
      next: (row, tableMeta) => {
        const o = tableMeta.toObject(row);
        this.sendSocketNotification('HUMIDITY_DATA', { measurement: o._measurement, value: o._value });
      },
      error: (error) => {
        console.error(error);
        console.log('\nHumidity Query Finished with ERROR');
      },
      complete: () => {
        console.log('\nHumidity Query Finished SUCCESS');
      },
    });
  },

  socketNotificationReceived: function(notification, payload) {
    if (notification === 'GET_DATA') {
      this.getData();
    }
  },
});
```

### MMM-DHT11.css
```css
.dht11Container {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  align-items: center;
  margin-top: 60px;
  margin-bottom: 0px;
  margin-left: auto;
  margin-right: auto;
}

.dht11Circle_bg {
  fill: none;
  stroke: #242424;
  stroke-width: 2;
}

.dht11CircleHumi,
.dht11CircleTemp {
  fill: none;
  stroke: #fff;
  stroke-width: 2;
}

.dht11Humidity,
.dht11Temperature {
  font-size: 0.4em;
  text-anchor: middle;
  fill: #fff;
}

.dht11Title {
  font-size: 0.17em;
  text-anchor: middle;
  fill: #fff;
  position: relative;
  display: inline-block;
  margin: 0 auto;
}
```



---


# Final results

![](https://hackmd.io/_uploads/r1XGoPZpn.jpg)
![](https://hackmd.io/_uploads/rkWSqPW62.jpg)
![](https://hackmd.io/_uploads/BkjScw-an.jpg)




