In recent years, IoT-based weather stations have gained popularity due to their ability to provide real-time weather data. With advancements in IoT technologies, building a weather station using off-the-shelf components has become easier. In this article, we’ll build an IoT-based weather station using the following components:
- BeetleESP32-C3: A low-cost, low-power system-on-chip microcontroller with integrated Wi-Fi and Bluetooth. It has a powerful CPU, plenty of RAM, and built-in peripherals such as sensors and GPIO pins, making it an excellent choice for building IoT devices.
- UNIHIKRE: a single-board computer (SBC) that brings a brand new experience to developers and makers also it contains onboard sensors and a TFT Touch display, which is more suitable for an SBC-based project.
- LARK Weather Station: is a comprehensive solution for precise atmospheric data collection. This compact and portable weather station simplifies the process of measurement and recording key weather elements such as wind speed, wind direction, temperature, humidity, and air pressure.
- Node-RED: An open-source low-programming platform for project prototyping and quick programming. We can quickly implement and test many event-driven IoT project prototypes using Node-RED.
Before we begin, gather the following components:
- Beetle ESP32-C3 Development Board
- Lark Weather Station Kit
- UNIHIKER
- Gravity Connector Cable
- Connect the Beetle ESP32-C3 with the LARK kit via the Gravity connector cable.
- Then connect the development board to your computer using a micro-USB cable.
- Open the Arduino IDE and select the appropriate board and port from the Tools menu.
- Next, we need to install the LARK libraries to communicate with the Lark via Beetle ESP32-C3.
Lark Weather Station Public library
- After the library installation, you can see the demo sketch in the examples folder.
- Let's upload the GET DATA sketch to the Beetle ESP32 C3 and verify the serial terminal response.
NOTE: Change the Mode switch from 1 to 0 in the sketch to enable the I2C communication
/*!
* @file getData.ino
* @brief This is a routine to get skylark data
* ---------------------------------------------------------------------------------------------------------------
* board | MCU | Leonardo/Mega2560/M0 | UNO | ESP8266 | ESP32 | microbit |
* VCC | 3.3V/5V | VCC | VCC | VCC | VCC | X |
* GND | GND | GND | GND | GND | GND | X |
* RX | TX | Serial1 TX1 | 5 | 5/D6 | D2 | X |
* TX | RX | Serial1 RX1 | 4 | 4/D7 | D3 | X |
* ---------------------------------------------------------------------------------------------------------------
*
* @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
* @license The MIT License (MIT)
* @author [TangJie](jie.tang@dfrobot.com)
* @version V1.0.0
* @date 2023-06-8
* @url https://github.com/DFRobot/DFRobot_LarkWeatherStation
*/
#include "DFRobot_LarkWeatherStation.h"
#if defined(ARDUINO_AVR_UNO) || defined(ESP8266)
#include <SoftwareSerial.h>
#endif
#define DEVICE_ADDR 0x42
#define MODESWITCH /*UART:*/ 1 /*I2C: 0*/
#if MODESWITCH
#if defined(ARDUINO_AVR_UNO) || defined(ESP8266)
SoftwareSerial mySerial(/*rx =*/4, /*tx =*/5);
DFRobot_LarkWeatherStation_UART atm(&mySerial);
#else
DFRobot_LarkWeatherStation_UART atm(&Serial1);
#endif
#else
DFRobot_LarkWeatherStation_I2C atm(DEVICE_ADDR, &Wire);
#endif
void setup(void) {
#if MODESWITCH
//Init MCU communication serial port
#if defined(ARDUINO_AVR_UNO) || defined(ESP8266)
mySerial.begin(115200);
#elif defined(ESP32)
Serial1.begin(115200, SERIAL_8N1, /*rx =*/D3, /*tx =*/D2);
#else
Serial1.begin(115200);
#endif
#endif
Serial.begin(115200);
while (atm.begin() != 0) {
Serial.println("init error");
delay(1000);
}
Serial.println("init success");
atm.setTime(2023, 1, 11, 23, 59, 0);
}
void loop(void) {
Serial.println("----------------------------");
Serial.print(atm.getValue("Temp"));
Serial.println(atm.getUnit("Temp"));
Serial.print(atm.getValue("Humi"));
Serial.println(atm.getUnit("Humi"));
Serial.print(atm.getValue("Speed"));
Serial.println(atm.getUnit("Speed"));
Serial.println(atm.getValue("Dir"));
Serial.print(atm.getValue("Altitude"));
Serial.println(atm.getUnit("Altitude"));
Serial.print(atm.getValue("Pressure"));
Serial.println(atm.getUnit("Pressure"));
Serial.println("----------------------------");
Serial.println(atm.getInformation(true));
delay(100);
}
Here is the serial terminal response:
2️⃣Node-Red Installation on UNIHIKER:First, connect the UNIHIKER to your PC and just wait for 5 sec to boot up. Once you see the UNIHIKER logo, we are good to go.
Next, open Mind+ and connect to UNIHIKER via SSH. Then install the Node-Red using this command.
bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
Then press enter. First, it will ask for the user input. Just press Y.
Again, it will ask for the user's input by pressing Y.
Next, it will ask for your input to install the Raspberry Pi nodes, but we don't need them. So, just give No, That's all.
It will start installing all the necessary components. Just wait to finish the installation. Once all is done, it will show the IP address of the node red and log.
That's all now we have the Node-Red in the UNIHIKER.
Type the following command to start the Node-Red.
node-red-start
Now you can see the IP, from which we can access the Node-Red. Here is mine.
Then open that in the browser.
Just insert the Inject and payload nodes and try to deploy that.
Let's see the debug logs.
So, now our Node-Red server is working properly. Let's deploy our weather monitoring system.
3️⃣Lark Weather Data to ESP Web Server:The next step is to transmit our Lark weather data into our Node-Red for that we are going to utilize the ESP Web Server on the Beetle ESP32 C3.
Here is the demo sketch that can read the data from the Lark Weather station and it will publish those data into a local web page.
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include "DFRobot_LarkWeatherStation.h"
#define DEVICE_ADDR 0x42
DFRobot_LarkWeatherStation_I2C atm(DEVICE_ADDR, &Wire);
String temp;
String hum;
String mois;
String HeatIn;
// Your WiFi credentials.
const char* ssid = "ELDRADO";
const char* password = "amazon123";
WebServer server(80);
void handleRoot() {
server.send(200, "text/plain", "hello from esp32!");
}
void handleNotFound() {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void setup() {
// Debug console
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
while (atm.begin() != 0) {
Serial.println("init error");
delay(1000);
}
server.on("/", handleRoot); // http://localIPAddress/
server.on("/dht-temp", []() // http://localIPAddress/dht-temp
{
int t = 30;
temp = t;
server.send(200, "text/plain", atm.getValue("Temp"));
});
server.on("/dht-hum", []() // http://localIPAddress/dht-hum
{
int h = 10;
hum = h;
server.send(200, "text/plain", atm.getValue("Humi"));
});
server.on("/mois", []() // http://localIPAddress/mois
{
int m = 20;
mois = m;
server.send(200, "text/plain", atm.getValue("Pressure"));
});
server.on("/alti", []() // http://localIPAddress/alti
{
int t = 30;
temp = t;
server.send(200, "text/plain", atm.getValue("Altitude"));
});
server.on("/speed", []() // http://localIPAddress/speed
{
int h = 10;
hum = h;
server.send(200, "text/plain", atm.getValue("Speed"));
});
server.on("/wdir", []() // http://localIPAddress/wdir
{
String m = "NW";
server.send(200, "text/plain", atm.getValue("Dir"));
});
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
Serial.println(atm.getInformation(true));
}
void loop() {
server.handleClient();
}
For example, the wind speed readings will be posted on the /speed sub-page of the ESP web server.
Here is the Arduino serial terminal response.
Now our Beetle can read all our weather station data and it will put those readings into a separate web server.
The next step is to read those web server data and our UNIHIKER node-red server will help us to collect and visualize the data.
Just copy and paste this below JSON file into Node-Red.
[
{
"id": "dabf1b80e9dadf0a",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": ""
},
{
"id": "37f0268a7d5c7791",
"type": "http request",
"z": "dabf1b80e9dadf0a",
"name": "",
"method": "GET",
"ret": "txt",
"paytoqs": "ignore",
"url": "192.168.1.10/dht-temp",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 630,
"y": 480,
"wires": [
[
"cbfb8f662a9b51dc"
]
]
},
{
"id": "6553d1b42bca55f5",
"type": "inject",
"z": "dabf1b80e9dadf0a",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "3",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 390,
"y": 640,
"wires": [
[
"37f0268a7d5c7791",
"aec221dd8867c106",
"c8270e8c859590fb",
"f40230821399b5cf",
"a85b2cb6359cbc5b",
"045faf22fd809f8e"
]
]
},
{
"id": "aec221dd8867c106",
"type": "http request",
"z": "dabf1b80e9dadf0a",
"name": "",
"method": "GET",
"ret": "txt",
"paytoqs": "ignore",
"url": "192.168.1.10/dht-hum",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 630,
"y": 540,
"wires": [
[
"2fb2b291e8a0c9f0"
]
]
},
{
"id": "cbfb8f662a9b51dc",
"type": "ui_gauge",
"z": "dabf1b80e9dadf0a",
"name": "",
"group": "a7841362.ae40c",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Temperature Data",
"label": "deg C",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"x": 820,
"y": 480,
"wires": []
},
{
"id": "2fb2b291e8a0c9f0",
"type": "ui_gauge",
"z": "dabf1b80e9dadf0a",
"name": "",
"group": "a7841362.ae40c",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Humidity Data",
"label": "%",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"x": 820,
"y": 540,
"wires": []
},
{
"id": "c8270e8c859590fb",
"type": "http request",
"z": "dabf1b80e9dadf0a",
"name": "",
"method": "GET",
"ret": "txt",
"paytoqs": "ignore",
"url": "192.168.1.10/mois",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 630,
"y": 600,
"wires": [
[
"3021cd10fabe9250"
]
]
},
{
"id": "3021cd10fabe9250",
"type": "ui_chart",
"z": "dabf1b80e9dadf0a",
"name": "",
"group": "4a3bb07831450645",
"order": 2,
"width": 0,
"height": 0,
"label": "Pressure",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": true,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#33e133",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"x": 800,
"y": 600,
"wires": [
[]
]
},
{
"id": "f40230821399b5cf",
"type": "http request",
"z": "dabf1b80e9dadf0a",
"name": "",
"method": "GET",
"ret": "txt",
"paytoqs": "ignore",
"url": "192.168.1.10/alti",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 630,
"y": 660,
"wires": [
[
"8a72b69696d46440"
]
]
},
{
"id": "8a72b69696d46440",
"type": "ui_chart",
"z": "dabf1b80e9dadf0a",
"name": "",
"group": "4a3bb07831450645",
"order": 2,
"width": 0,
"height": 0,
"label": "Altitude",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": true,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#33e133",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"x": 820,
"y": 660,
"wires": [
[]
]
},
{
"id": "a85b2cb6359cbc5b",
"type": "http request",
"z": "dabf1b80e9dadf0a",
"name": "",
"method": "GET",
"ret": "txt",
"paytoqs": "ignore",
"url": "192.168.1.10/speed",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 630,
"y": 720,
"wires": [
[
"308584a7ddd6e61f"
]
]
},
{
"id": "308584a7ddd6e61f",
"type": "ui_gauge",
"z": "dabf1b80e9dadf0a",
"name": "",
"group": "a19ee1de98fca42c",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Wind Speed Data",
"label": "",
"format": "{{value}}",
"min": 0,
"max": "1034",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"x": 830,
"y": 720,
"wires": []
},
{
"id": "045faf22fd809f8e",
"type": "http request",
"z": "dabf1b80e9dadf0a",
"name": "",
"method": "GET",
"ret": "txt",
"paytoqs": "ignore",
"url": "192.168.1.10/wdir",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 630,
"y": 780,
"wires": [
[
"6b3f1449f4aba817"
]
]
},
{
"id": "6b3f1449f4aba817",
"type": "ui_text",
"z": "dabf1b80e9dadf0a",
"group": "a19ee1de98fca42c",
"order": 4,
"width": "6",
"height": "4",
"name": "",
"label": "Wind Direction",
"format": "{{msg.payload}}",
"layout": "row-center",
"x": 820,
"y": 780,
"wires": []
},
{
"id": "a7841362.ae40c",
"type": "ui_group",
"name": "Environmental Condition",
"tab": "2ff36ff5.bdc628",
"order": 1,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "4a3bb07831450645",
"type": "ui_group",
"name": "Pressure & Altitude",
"tab": "2ff36ff5.bdc628",
"order": 2,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "a19ee1de98fca42c",
"type": "ui_group",
"name": "Wind Peoperties",
"tab": "2ff36ff5.bdc628",
"order": 3,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "2ff36ff5.bdc628",
"type": "ui_tab",
"name": "UNIHIKER Node Lab",
"icon": "dashboard",
"disabled": false,
"hidden": false
}
]
Then open the HTTP request pallet and change the IP to your Beetle ESP32-C3's IP.
Finally, deploy the flow and navigate to the UI page of your Node-Red.
By combining the power of ESP32 and Node-RED, you can create your own weather station that provides real-time data. Feel free to customize the project further by adding more sensors or integrating additional features! 🌦️🌡️💧
Comments
Please log in or sign up to comment.