hpr4311 :: LoRaWAN and the Things Stack
Lee sets up some temperature and humidity sensors
Hosted by Lee on Monday, 2025-02-10 is flagged as Clean and is released under a CC-BY-SA license.
iot, lorawan.
1.
Listen in ogg,
spx,
or mp3 format. Play now:
Duration: 00:10:09
general.
I have set up some LoRaWAN temperature and humidity sensors, and am using the Things Stack to collect the data.
This gets processed via a web-hook and rendered as a graph.
The LoRaWAN Alliance - https://lora-alliance.org
Mastering LoRaWAN - https://www.amazon.com/Mastering-LoRaWAN-Comprehensive-Communication-Connectivity-ebook/dp/B0CTRH6MV6
The Things Industries - https://thethingsindustries.com
server.py
import json import sqlite3 import logging from http.server import BaseHTTPRequestHandler, HTTPServer rooms = { 'eui-24e12*********07': 'living-room', 'eui-24e12*********54': 'hall', 'eui-24e12*********42': 'downstairs-office', 'eui-24e12*********35': 'kitchen', 'eui-24e12*********29': 'conservatory', 'eui-24e12*********87': 'landing', 'eui-24e12*********45': 'main-bedroom', 'eui-24e12*********89': 'upstairs-office', 'eui-24e12*********38': 'spare-bedroom', 'eui-24e12*********37': 'playroom' }; # Configure logging logging.basicConfig(filename="server_log.txt", level=logging.INFO, format="%(asctime)s - %(message)s") # Define the web server handler class MyServerHandler(BaseHTTPRequestHandler): # Handle POST requests def do_POST(self): length = int(self.headers.get('Content-Length')) data = self.rfile.read(length).decode('utf-8') try: # Validate and parse JSON data json_data = json.loads(data) logging.info(f"Received valid JSON data: {json_data}") # Write the data to database id = json_data["end_device_ids"]["device_id"] room = rooms.get(id) readat = json_data["uplink_message"]["rx_metadata"][0]["time"] temp = json_data["uplink_message"]["decoded_payload"]["temperature"] hum = json_data["uplink_message"]["decoded_payload"]["humidity"] conn = sqlite3.connect('data.db') sql = """CREATE TABLE IF NOT EXISTS data ( id INTEGER PRIMARY KEY AUTOINCREMENT, room TEXT, readat DATETIME, temp DECIMAL(4,1), hum DECIMAL(4,1) );""" conn.execute(sql) sql = "INSERT INTO data (room, readat, temp, hum) VALUES (?, ?, ?, ?)" conn.execute(sql, (room, readat, temp, hum)) conn.commit() conn.close() self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(bytes("Data received and logged!", "utf-8")) except json.JSONDecodeError: logging.error("Invalid JSON data received.") self.send_response(400) # Bad Request self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(bytes("Invalid JSON format.", "utf-8")) except PermissionError: logging.error("File write permission denied.") self.send_response(500) # Internal Server Error self.send_header("Content-type", "text/html") self.end_headers() self.wfile.write(bytes("Server error: Unable to write data to file.", "utf-8")) # Start the server server_address = ('0.0.0.0', 12345) # Customize host and port if needed httpd = HTTPServer(server_address, MyServerHandler) print("Server started on http://localhost:12345") httpd.serve_forever()
process.php
<!DOCTYPE html> <html> <head> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> </head> <body> <canvas id="temp" style="height:50vh"></canvas> <canvas id="hum" style="height:50vh"></canvas> <script> <?php $colors = [ '#FF9999', // Light red '#FFCC99', // Light orange '#FFFF99', // Light yellow '#CCFF99', // Light lime green '#99FF99', // Light green '#99FFCC', // Light teal '#99FFFF', // Light sky blue '#99CCFF', // Light blue '#9999FF', // Light violet '#CC99FF', // Light lavender '#FF99FF', // Light pink '#FFCCFF', // Light rose '#FFD5D5', // Light salmon '#FFDDAA', // Light peach '#FFE0E0', // Light beige '#FFF0F0' // Light ivory ]; $results = [ 'living-room' => [ 'temperature' => [], 'humidity' => [] ], 'hall' => [ 'temperature' => [], 'humidity' => [] ], 'downstairs-office' => [ 'temperature' => [], 'humidity' => [] ], 'kitchen' => [ 'temperature' => [], 'humidity' => [] ], 'conservatory' => [ 'temperature' => [], 'humidity' => [] ], 'landing' => [ 'temperature' => [], 'humidity' => [] ], 'main-bedroom' => [ 'temperature' => [], 'humidity' => [] ], 'upstairs-office' => [ 'temperature' => [], 'humidity' => [] ], 'spare-bedroom' => [ 'temperature' => [], 'humidity' => [] ], 'playroom' => [ 'temperature' => [], 'humidity' => [] ] ]; $labels = []; $db = new SQLite3('data.db'); $sql = "SELECT room, readat, temp, hum FROM data"; $stmt = $db->prepare($sql); $result = $stmt->execute(); while ($row = $result->fetchArray(SQLITE3_ASSOC)) { $where = $row['room']; $tz = 'Europe/London'; $dt = new DateTime($row['readat']); $dt->setTimezone(new DateTimeZone($tz)); $when = $dt->format('d/m/Y, H:i'); $u = intdiv(date_format(date_create($row['readat']), "U"), 600); $temp = number_format($row['temp'], 1); $hum = number_format($row['hum'], 1); $labels[$u] = "\"$when\""; $results[$where]['temperature'][] = $temp; $results[$where]['humidity'][] = $hum; } $stmt->close(); $db->close(); $c = 0; foreach ($results as $key => $room) { $col = $colors[$c]; $temp_datasets[] = "{ label: \"$key °C\", data: [ ".implode(",", $room['temperature'])." ], borderColor: \"$col\" }"; $hum_datasets[] = "{ label: \"$key %\", data: [ ".implode(",", $room['humidity'])." ], borderColor: \"$col\" }"; $c++; } ?> const data1 = { datasets: [ <?php echo implode(",",$temp_datasets); ?> ], labels: [<?php echo implode(",", $labels); ?>] }; const ctx1 = document.getElementById("temp").getContext("2d"); const options1 = { type: "line", data: data1, options: { elements: { point:{ radius: 0 } } } }; const chart1 = new Chart(ctx1, options1); const data2 = { datasets: [ <?php echo implode(",",$hum_datasets); ?> ], labels: [<?php echo implode(",", $labels); ?>] }; const ctx2 = document.getElementById("hum").getContext("2d"); const options2 = { type: "line", data: data2, options: { elements: { point:{ radius: 0 } } } }; const chart2 = new Chart(ctx2, options2); </script> </body> </html>
Temperature Chart
Humidity Chart