IoT Temperature Monitoring System

Powered by QBFT Blockchain Technology

System Overview

This project implements a decentralized IoT temperature monitoring system using QBFT (Quorum Byzantine Fault Tolerant) blockchain technology. Two ESP32 microcontrollers communicate through a blockchain network to ensure immutable, tamper-proof data storage.

Decentralized Storage

QBFT consensus ensures data integrity across 5 validator nodes

Immutable Records

Temperature data stored permanently on blockchain

Real-time Communication

Instant data transmission between IoT devices

Alert System

Automatic notifications for temperature thresholds

Key Components

  • ESP32 Device 1: Simulates and sends temperature data
  • ESP32 Device 2: Receives and displays temperature data
  • Blockchain Network: 5-node QBFT network for consensus
  • Backend Server: Node.js API bridge between devices and blockchain

System Requirements

Verified Environment: This system has been tested on Ubuntu 22.04.3 LTS with WSL2

Minimum Hardware Requirements

Component Requirement
CPU 2 cores or more
RAM 4GB minimum, 8GB recommended
Storage 10GB free space
ESP32 Boards 2x ESP32 Development Boards

Software Prerequisites

1. Node.js & npm

Bash
# Install Node.js v22.x
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs

# Verify installation
node --version  # Should output: v22.21.0 or later
npm --version   # Should output: 10.9.4 or later

2. GoQuorum

Bash
# Download GoQuorum v24.4.1
cd ~
# File given in github

# Extract
tar -xvf geth_v24.4.1_linux_amd64.tar.gz

# Install globally
sudo mv geth /usr/local/bin/
sudo mv bootnode /usr/local/bin/

# Verify installation
geth version

3. Arduino IDE & ESP32 Support

Bash
# Install Arduino IDE
sudo snap install arduino
ESP32 Board Package: Add this URL to Arduino IDE Preferences → Additional Boards Manager URLs:
https://dl.espressif.com/dl/package_esp32_index.json

Architecture

System Data Flow

ESP32-1

Temperature Sender

Backend API

Node.js Server

QBFT Network

5 Validator Nodes

ESP32-2

Temperature Receiver

Data Flow Process

  1. Temperature Generation: ESP32-1 simulates temperature (20-30°C)
  2. HTTP POST: Device sends JSON payload to backend API
  3. Blockchain Transaction: Backend creates signed transaction
  4. QBFT Consensus: 5 validator nodes reach consensus
  5. Block Creation: Transaction included in new block (5s intervals)
  6. Data Retrieval: ESP32-2 queries backend via HTTP GET
  7. Display: Temperature displayed with alerts

Installation Guide

Phase 1: Project Files Setup

Bash
git clone https://github.com/Arasulingam/QBFT-Quorum.git
cd QBFT-Network

# Verify directory structure
ls -la QBFT-Network/

Phase 2: Backend Dependencies

Bash
# Navigate to backend directory
cd ~/QBFT-Network/server/iot-blockchain-backend

# Install Node.js dependencies
npm install

# Verify installation
npm list --depth=0
Expected Packages:
body-parser@1.20.2, cors@2.8.5, dotenv@16.0.3, express@4.18.2, web3@1.10.0

Network Configuration

Parameter Value
Chain ID 1337
Consensus QBFT (Byzantine Fault Tolerant)
Block Period 5 seconds
Validators 5 nodes
Gas Price 0 (free transactions)

Running the System

Important: You need 5 separate terminal windows to run all blockchain nodes. Consider using tmux or screen for easier management.

Step 1: Start QBFT Blockchain Network

Terminal 1 - Node 0 (RPC: 22000, P2P: 30300)

Bash - Node 0
cd /home/arasu/QBFT-Network/QBFT-Network/Node-0

export ADDRESS=$(grep -o '"address": *"[^"]*"' ./data/keystore/accountKeystore | grep -o '"[^"]*"$' | sed 's/"//g')
export PRIVATE_CONFIG=ignore

geth --datadir data \
    --networkid 1337 --nodiscover --verbosity 5 \
    --syncmode full \
    --istanbul.blockperiod 5 --mine --miner.threads 1 --miner.gasprice 0 --emitcheckpoints \
    --http --http.addr 127.0.0.1 --http.port 22000 --http.corsdomain "*" --http.vhosts "*" \
    --ws --ws.addr 127.0.0.1 --ws.port 32000 --ws.origins "*" \
    --http.api admin,eth,debug,miner,net,txpool,personal,web3,istanbul \
    --ws.api admin,eth,debug,miner,net,txpool,personal,web3,istanbul \
    --unlock ${ADDRESS} --allow-insecure-unlock --password ./data/keystore/accountPassword \
    --port 30300

Terminal 2 - Node 1 (RPC: 22001, P2P: 30301)

Bash - Node 1
cd /home/arasu/QBFT-Network/QBFT-Network/Node-1

export ADDRESS=$(grep -o '"address": *"[^"]*"' ./data/keystore/accountKeystore | grep -o '"[^"]*"$' | sed 's/"//g')
export PRIVATE_CONFIG=ignore

geth --datadir data \
    --networkid 1337 --nodiscover --verbosity 5 \
    --syncmode full \
    --istanbul.blockperiod 5 --mine --miner.threads 1 --miner.gasprice 0 --emitcheckpoints \
    --http --http.addr 127.0.0.1 --http.port 22001 --http.corsdomain "*" --http.vhosts "*" \
    --ws --ws.addr 127.0.0.1 --ws.port 32001 --ws.origins "*" \
    --http.api admin,eth,debug,miner,net,txpool,personal,web3,istanbul \
    --ws.api admin,eth,debug,miner,net,txpool,personal,web3,istanbul \
    --unlock ${ADDRESS} --allow-insecure-unlock --password ./data/keystore/accountPassword \
    --port 30301

Repeat similar commands for Nodes 2, 3, and 4 with respective port numbers...

Network Status Indicators:
  • INFO Started P2P networking - Node started successfully
  • INFO Looking for peers peercount=4 - Connected to other nodes
  • INFO IPC endpoint opened - RPC interface ready

Step 2: Start Backend Server

Bash - Terminal 6
cd /home/arasu/QBFT-Network/server/iot-blockchain-backend

node server.js
Expected Output:
🚀 Server running on port 3000
📡 Connected to blockchain: http://127.0.0.1:22000
📝 Contract address: 0xb5963eaf88065e44cd8f94ebd71b8bd7099c3ec3

Step 3: Verify System Status

Bash - Test Commands
# Health check
curl http://localhost:3000/health

# Check registered devices
curl http://localhost:3000/api/devices

ESP32 Configuration

Find Your Server IP Address

Bash
hostname -I
# Example output: 192.168.1.150

ESP32 Device 1 (Sender) - Key Configuration

C++ (Arduino)

#include 
#include 
#include 

// WiFi credentials
const char* ssid = "";
const char* password = "";

// Backend server configuration
const char* serverUrl = "http://192.168.1.9:3000/api/temperature"; //YOUR IP

// Device configuration
const char* deviceId = "ESP32-TEMP-001";
const char* devicePrivateKey = "0x87123236c30996f3116601ed979e7b4f526e0f7c738e6c8a1ef843602059e05d"; // Get from blockchain account

// Temperature simulation settings
float baseTemperature = 25.0;      // Base temperature in Celsius
float currentTemperature = 25.0;
bool simulateAlert = false;        // Set true to test alerts
int readingCounter = 0;

// Timing configuration
unsigned long lastSendTime = 0;
const unsigned long sendInterval = 15000; // Send every 15 seconds (faster for demo)

// LED indicator
#define LED_BUILTIN 2

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  
  // Seed random number generator
  randomSeed(analogRead(0));
  
  Serial.println("\n\n=================================");
  Serial.println("ESP32 Temperature Sender (SIMULATED)");
  Serial.println("=================================\n");
  
  Serial.println("⚠️  SIMULATION MODE - No sensor required");
  Serial.println("Generating realistic temperature data...\n");
  
  // Connect to WiFi
  connectToWiFi();
  
  Serial.println("\nDevice Configuration:");
  Serial.print("Device ID: ");
  Serial.println(deviceId);
  Serial.print("Server URL: ");
  Serial.println(serverUrl);
  Serial.print("Base Temperature: ");
  Serial.print(baseTemperature);
  Serial.println(" °C");
  Serial.println("\nStarting temperature monitoring...\n");
  
  // Initial blink
  for(int i = 0; i < 3; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(200);
    digitalWrite(LED_BUILTIN, LOW);
    delay(200);
  }
}

void loop() {
  // Check WiFi connection
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi disconnected! Reconnecting...");
    connectToWiFi();
  }
  
  // Check for serial commands
  checkSerialCommands();
  
  // Send temperature at regular intervals
  if (millis() - lastSendTime >= sendInterval) {
    readAndSendTemperature();
    lastSendTime = millis();
  }
  
  // Heartbeat blink (fast pulse every 2 seconds)
  static unsigned long lastBlink = 0;
  if (millis() - lastBlink > 2000) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(50);
    digitalWrite(LED_BUILTIN, LOW);
    lastBlink = millis();
  }
  
  delay(100);
}

void connectToWiFi() {
  Serial.print("Connecting to WiFi: ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 20) {
    delay(500);
    Serial.print(".");
    attempts++;
  }
  
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\n✓ WiFi Connected!");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
    Serial.print("Signal Strength: ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
  } else {
    Serial.println("\n✗ WiFi Connection Failed!");
  }
}

void readAndSendTemperature() {
  Serial.println("\n--- Simulating Temperature Reading ---");
  
  // Simulate realistic temperature with variations
  currentTemperature = simulateTemperature();
  
  Serial.print("📊 Reading #");
  Serial.println(++readingCounter);
  Serial.print("🌡️  Temperature: ");
  Serial.print(currentTemperature, 2);
  Serial.println(" °C");
  
  // Show status
  if (currentTemperature > 35.0) {
    Serial.println("⚠️  HIGH TEMPERATURE ALERT!");
  } else if (currentTemperature < 10.0) {
    Serial.println("⚠️  LOW TEMPERATURE ALERT!");
  } else {
    Serial.println("✓ Temperature Normal");
  }
  
  // Send to blockchain via backend
  sendToBlockchain(currentTemperature);
}

void sendToBlockchain(float temperature) {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    
    Serial.println("\n→ Sending to blockchain...");
    
    // Create JSON payload
    StaticJsonDocument<256> doc;
    doc["deviceId"] = deviceId;
    doc["temperature"] = temperature;
    doc["privateKey"] = devicePrivateKey;
    
    String jsonPayload;
    serializeJson(doc, jsonPayload);
    
    // Send HTTP POST request
    http.begin(serverUrl);
    http.addHeader("Content-Type", "application/json");
    
    int httpResponseCode = http.POST(jsonPayload);
    
    if (httpResponseCode > 0) {
      String response = http.getString();
      Serial.print("✓ HTTP Response code: ");
      Serial.println(httpResponseCode);
      Serial.println("Response:");
      Serial.println(response);
      
      // Parse response
      StaticJsonDocument<512> responseDoc;
      DeserializationError error = deserializeJson(responseDoc, response);
      
      if (!error) {
        if (responseDoc["success"] == true) {
          Serial.println("✓ Temperature recorded on blockchain!");
          Serial.print("Transaction Hash: ");
          Serial.println(responseDoc["transactionHash"].as());
          Serial.print("Block Number: ");
          Serial.println(responseDoc["blockNumber"].as());
          
          // Blink LED rapidly to indicate success
          for (int i = 0; i < 5; i++) {
            digitalWrite(LED_BUILTIN, HIGH);
            delay(100);
            digitalWrite(LED_BUILTIN, LOW);
            delay(100);
          }
        } else {
          Serial.println("✗ Blockchain transaction failed!");
        }
      }
    } else {
      Serial.print("✗ HTTP Error code: ");
      Serial.println(httpResponseCode);
      Serial.println(http.errorToString(httpResponseCode).c_str());
    }
    
    http.end();
  } else {
    Serial.println("✗ WiFi not connected!");
  }
}

// Simulate realistic temperature with variations
float simulateTemperature() {
  static float temp = baseTemperature;
  static unsigned long lastChangeTime = 0;
  static int alertCycle = 0;
  
  unsigned long currentTime = millis();
  
  // Every 10 readings, trigger an alert for testing
  if (readingCounter > 0 && readingCounter % 10 == 0 && simulateAlert) {
    alertCycle++;
    if (alertCycle % 2 == 0) {
      // High temperature alert
      temp = 36.0 + random(0, 30) / 10.0; // 36.0 to 39.0°C
      Serial.println("🔥 Simulating HIGH temperature spike!");
    } else {
      // Low temperature alert
      temp = 8.0 + random(0, 20) / 10.0; // 8.0 to 10.0°C
      Serial.println("❄️  Simulating LOW temperature drop!");
    }
    lastChangeTime = currentTime;
    return temp;
  }
  
  // Normal operation: gradual temperature changes
  if (currentTime - lastChangeTime > 5000) { // Change every 5 seconds
    // Random walk: small changes
    float change = (random(-50, 51)) / 100.0; // -0.5 to +0.5°C
    temp += change;
    
    // Keep within realistic bounds (15-32°C for normal operation)
    if (temp < 18.0) temp = 18.0 + random(0, 20) / 10.0;
    if (temp > 30.0) temp = 30.0 - random(0, 20) / 10.0;
    
    lastChangeTime = currentTime;
  }
  
  // Add small random noise
  float noise = (random(-10, 11)) / 100.0; // ±0.1°C
  return temp + noise;
}

// Simulate different temperature patterns
void setSimulationMode(int mode) {
  switch(mode) {
    case 1: // Normal room temperature
      baseTemperature = 22.0;
      simulateAlert = false;
      Serial.println("Mode: Normal Room (22°C)");
      break;
    
    case 2: // Server room
      baseTemperature = 25.0;
      simulateAlert = false;
      Serial.println("Mode: Server Room (25°C)");
      break;
    
    case 3: // Hot environment
      baseTemperature = 32.0;
      simulateAlert = true;
      Serial.println("Mode: Hot Environment (32°C + alerts)");
      break;
    
    case 4: // Cold environment
      baseTemperature = 15.0;
      simulateAlert = true;
      Serial.println("Mode: Cold Environment (15°C + alerts)");
      break;
    
    default:
      baseTemperature = 25.0;
      simulateAlert = false;
  }
  currentTemperature = baseTemperature;
}

// Check for serial commands to change simulation
void checkSerialCommands() {
  if (Serial.available() > 0) {
    char cmd = Serial.read();
    
    switch(cmd) {
      case '1':
        setSimulationMode(1);
        break;
      case '2':
        setSimulationMode(2);
        break;
      case '3':
        setSimulationMode(3);
        break;
      case '4':
        setSimulationMode(4);
        break;
      case 'h':
      case 'H':
        // Force high temperature
        currentTemperature = 38.0;
        Serial.println("🔥 FORCED HIGH TEMP: 38°C");
        break;
      case 'l':
      case 'L':
        // Force low temperature
        currentTemperature = 5.0;
        Serial.println("❄️  FORCED LOW TEMP: 5°C");
        break;
      case 'n':
      case 'N':
        // Reset to normal
        currentTemperature = baseTemperature;
        Serial.println("✓ Reset to normal");
        break;
      case '?':
        printHelp();
        break;
    }
  }
}

void printHelp() {
  Serial.println("\n=== SIMULATION COMMANDS ===");
  Serial.println("1 - Normal Room (22°C)");
  Serial.println("2 - Server Room (25°C)");
  Serial.println("3 - Hot Environment (32°C)");
  Serial.println("4 - Cold Environment (15°C)");
  Serial.println("H - Force HIGH temp alert (38°C)");
  Serial.println("L - Force LOW temp alert (5°C)");
  Serial.println("N - Reset to Normal");
  Serial.println("? - Show this help");
  Serial.println("===========================\n");
}

ESP32 Device 2 (Receiver) - Key Configuration

C++ (Arduino)
/*
 * ESP32 Temperature Receiver (Device 2 - Receiver/Display) - SIMULATION MODE
 * Retrieves temperature data from blockchain via backend API
 * Displays on Serial Monitor (no OLED needed!)
 * 
 * Hardware Required:
 * - ESP32 Development Board only (no display needed!)
 * 
 * Features:
 * - Fetches data from blockchain
 * - Displays on Serial Monitor
 * - Built-in LED for status/alerts
 * - Buzzer optional (can be disabled)
 * 
 * Libraries Required:
 * - WiFi (built-in)
 * - HTTPClient (built-in)
 * - ArduinoJson (install via Library Manager)
 */

#include 
#include 
#include 

// WiFi credentials
const char* ssid = "";
const char* password = "";

// Backend server configuration
const char* serverUrl = "http://192.168.1.9:3000/api/temperature/"; //YOUR_IP
const char* senderDeviceAddress = "0x5c303cef53e62c9ea820bacfc67066362c1daed7"; // Address of Device 1

// Device configuration
const char* deviceId = "ESP32-TEMP-002";
const char* devicePrivateKey = "0x1ada126eca1b7650a33834e7da8135e27df076b726c1339bef5a6743655683be";

// Pin definitions
#define LED_BUILTIN 2
#define BUZZER_PIN 25  // Optional: comment out if no buzzer
#define USE_BUZZER false  // Set to true if you have a buzzer connected

// Temperature thresholds
const float HIGH_TEMP_THRESHOLD = 35.0; // °C
const float LOW_TEMP_THRESHOLD = 10.0;  // °C

// Timing configuration
unsigned long lastFetchTime = 0;
const unsigned long fetchInterval = 10000; // Fetch every 10 seconds

// Temperature data
float currentTemperature = 0.0;
String lastUpdate = "";
String deviceIdReceived = "";
String location = "";
bool dataAvailable = false;
int fetchCounter = 0;

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  
  if (USE_BUZZER) {
    pinMode(BUZZER_PIN, OUTPUT);
    digitalWrite(BUZZER_PIN, LOW);
  }
  
  Serial.println("\n\n====================================");
  Serial.println("ESP32 Temperature Receiver (Display)");
  Serial.println("====================================\n");
  
  Serial.println("⚠️  SERIAL MONITOR MODE - No display hardware required");
  Serial.println("Temperature data will be shown here\n");
  
  // Connect to WiFi
  connectToWiFi();
  
  Serial.println("\n✓ Initialization complete!");
  Serial.println("\nDevice Configuration:");
  Serial.print("Device ID: ");
  Serial.println(deviceId);
  Serial.print("Monitoring Device: ");
  Serial.println(senderDeviceAddress);
  Serial.println("\nStarting data retrieval...");
  Serial.println("=====================================\n");
  
  // Initial LED pattern
  for(int i = 0; i < 3; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(200);
    digitalWrite(LED_BUILTIN, LOW);
    delay(200);
  }
  
  delay(2000);
}

void loop() {
  // Check WiFi connection
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi disconnected! Reconnecting...");
    connectToWiFi();
  }
  
  // Fetch temperature at regular intervals
  if (millis() - lastFetchTime >= fetchInterval) {
    fetchTemperatureFromBlockchain();
    lastFetchTime = millis();
  }
  
  // Heartbeat blink
  static unsigned long lastBlink = 0;
  if (millis() - lastBlink > 2000) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(50);
    digitalWrite(LED_BUILTIN, LOW);
    lastBlink = millis();
  }
  
  delay(100);
}

void connectToWiFi() {
  Serial.print("Connecting to WiFi: ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 20) {
    delay(500);
    Serial.print(".");
    attempts++;
  }
  
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\n✓ WiFi Connected!");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
    Serial.print("Signal Strength: ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
  } else {
    Serial.println("\n✗ WiFi Connection Failed!");
  }
}

void fetchTemperatureFromBlockchain() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    
    Serial.println("\n╔══════════════════════════════════════╗");
    Serial.print("║ Fetch #");
    Serial.print(++fetchCounter);
    Serial.println(" - Querying Blockchain");
    Serial.println("╚══════════════════════════════════════╝");
    
    // Build URL
    String url = String(serverUrl) + String(senderDeviceAddress);
    
    http.begin(url);
    http.addHeader("Content-Type", "application/json");
    
    int httpResponseCode = http.GET();
    
    if (httpResponseCode > 0) {
      String response = http.getString();
      
      // Parse JSON response
      StaticJsonDocument<512> doc;
      DeserializationError error = deserializeJson(doc, response);
      
      if (!error) {
        if (doc["success"] == true) {
          currentTemperature = doc["data"]["temperature"];
          lastUpdate = doc["data"]["timestamp"].as();
          deviceIdReceived = doc["data"]["deviceId"].as();
          location = doc["data"]["location"].as();
          
          dataAvailable = true;
          
          // Display in a nice format
          displayTemperatureData();
          
          // Check for alerts
          checkTemperatureAlerts(currentTemperature);
          
        } else {
          Serial.println("✗ No data available");
          dataAvailable = false;
        }
      } else {
        Serial.print("✗ JSON parsing failed: ");
        Serial.println(error.c_str());
      }
    } else {
      Serial.print("✗ HTTP Error code: ");
      Serial.println(httpResponseCode);
      dataAvailable = false;
    }
    
    http.end();
  } else {
    Serial.println("✗ WiFi not connected!");
  }
}

void displayTemperatureData() {
  Serial.println("\n┌─────────────────────────────────────┐");
  Serial.println("│     TEMPERATURE DATA RECEIVED       │");
  Serial.println("├─────────────────────────────────────┤");
  
  Serial.print("│ Device ID  : ");
  Serial.println(deviceIdReceived);
  
  Serial.print("│ Location   : ");
  Serial.println(location);
  
  Serial.print("│ Temperature: ");
  Serial.print(currentTemperature, 2);
  Serial.println(" °C");
  
  // Visual temperature bar
  Serial.print("│ ");
  int bars = map((int)(currentTemperature * 10), 0, 500, 0, 30);
  for (int i = 0; i < bars && i < 30; i++) {
    Serial.print("█");
  }
  Serial.println();
  
  // Status indicator
  Serial.print("│ Status     : ");
  if (currentTemperature > HIGH_TEMP_THRESHOLD) {
    Serial.println("🔥 HIGH TEMP ALERT!");
  } else if (currentTemperature < LOW_TEMP_THRESHOLD) {
    Serial.println("❄️  LOW TEMP ALERT!");
  } else {
    Serial.println("✓ Normal");
  }
  
  Serial.print("│ Updated    : ");
  if (lastUpdate.length() > 11) {
    Serial.println(lastUpdate.substring(11, 19));
  } else {
    Serial.println(lastUpdate);
  }
  
  Serial.print("│ WiFi Signal: ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  
  Serial.println("└─────────────────────────────────────┘\n");
}

void checkTemperatureAlerts(float temperature) {
  if (temperature > HIGH_TEMP_THRESHOLD) {
    Serial.println("⚠️  HIGH TEMPERATURE ALERT!");
    triggerAlert("HIGH", 3);
  } else if (temperature < LOW_TEMP_THRESHOLD) {
    Serial.println("⚠️  LOW TEMPERATURE ALERT!");
    triggerAlert("LOW", 2);
  }
}

void triggerAlert(String alertType, int beeps) {
  // Beep buzzer
  for (int i = 0; i < beeps; i++) {
    digitalWrite(BUZZER_PIN, HIGH);
    delay(200);
    digitalWrite(BUZZER_PIN, LOW);
    delay(200);
  }
  
  // Flash LED
  for (int i = 0; i < 10; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
  }
}


// Display historical data (if fetching history)
void displayHistory() {
  // Fetch last 5 readings
  String historyUrl = "http://192.168.1.9:3000/api/readings/" + 
                      String(senderDeviceAddress) + "?limit=5"; //YOUR_IP
  
  HTTPClient http;
  http.begin(historyUrl);
  
  int httpResponseCode = http.GET();
  
  if (httpResponseCode > 0) {
    String response = http.getString();
    
    DynamicJsonDocument doc(2048);
    DeserializationError error = deserializeJson(doc, response);
    
    if (!error && doc["success"] == true) {
      JsonArray data = doc["data"];
      
      Serial.println("\n=== Temperature History ===");
      for (JsonObject reading : data) {
        Serial.print("Time: ");
        Serial.print(reading["timestamp"].as());
        Serial.print(" | Temp: ");
        Serial.print(reading["temperature"].as());
        Serial.println(" °C");
      }
    }
  }
  
  http.end();
}

Upload to ESP32

  1. Connect ESP32 via USB
  2. Open Arduino IDE
  3. Select: Tools → Board → ESP32 Dev Module
  4. Select: Tools → Port → (Your ESP32 COM port)
  5. Click: Upload (→ button)
  6. Open: Serial Monitor (Ctrl+Shift+M)
  7. Set baud rate to: 115200
Expected ESP32-1 Output:
ESP32 Temperature Sender (SIMULATED)
✓ WiFi Connected!
IP Address: 192.168.1.105
📊 Reading #1
🌡️  Temperature: 25.23 °C
✓ Temperature recorded on blockchain!

API Reference

Base URL: http://localhost:3000

1. Health Check

HTTP Request
GET /health

Response:

JSON
{
  "status": "OK",
  "message": "IoT Blockchain Server is running"
}

2. Register Device

HTTP Request
POST /api/register-device
Content-Type: application/json

{
  "deviceAddress": "0x5c303cef53e62c9ea820bacfc67066362c1daed7",
  "deviceId": "ESP32-TEMP-001",
  "location": "Room-101"
}

Response:

JSON
{
  "success": true,
  "transactionHash": "0x4260c8065c765a623c06c5015f13e5fc85ed36b22a6f4d78d1a04bb92f703c5e",
  "deviceAddress": "0x5c303cef53e62c9ea820bacfc67066362c1daed7",
  "deviceId": "ESP32-TEMP-001",
  "location": "Room-101"
}

3. Record Temperature

HTTP Request
POST /api/temperature
Content-Type: application/json

{
  "deviceId": "ESP32-TEMP-001",
  "temperature": 25.5,
  "privateKey": "0x87123236c30996f3116601ed979e7b4f526e0f7c738e6c8a1ef843602059e05d"
}

Response:

JSON
{
  "success": true,
  "transactionHash": "0xc366ab4a3e85302c8ac709e2b37d2837b9b36bf013315965981ea03735924d76",
  "blockNumber": 531,
  "deviceId": "ESP32-TEMP-001",
  "temperature": 25.5,
  "timestamp": "2025-10-24T12:00:16.033Z"
}

4. Get Latest Reading

HTTP Request
GET /api/temperature/:deviceAddress

# Example
curl http://localhost:3000/api/temperature/0x5c303cef53e62c9ea820bacfc67066362c1daed7

Response:

JSON
{
  "success": true,
  "data": {
    "timestamp": "2025-10-24T12:00:16.000Z",
    "temperature": 25.5,
    "deviceId": "ESP32-TEMP-001",
    "location": "Room-101"
  }
}

5. Get Device Readings History

HTTP Request
GET /api/readings/:deviceAddress?limit=10

6. Get All Devices

HTTP Request
GET /api/devices

Temperature Thresholds

Alert Type Threshold Description
HIGH Alert 🔥 > 35.0°C Triggers high temperature warning
NORMAL ✓ 10.0°C - 35.0°C Temperature within safe range
LOW Alert ❄️ < 10.0°C Triggers low temperature warning

Port Configuration

Node RPC (HTTP) WebSocket P2P (DevP2P)
Node-0 22000 32000 30300
Node-1 22001 32001 30301
Node-2 22002 32002 30302
Node-3 22003 32003 30303
Node-4 22004 32004 30304
Backend Server: Port 3000 (HTTP)
Smart Contract: 0xb5963eaf88065e44cd8f94ebd71b8bd7099c3ec3

Troubleshooting

Issue 1: Port Already in Use

Symptom:
Fatal: Error starting protocol stack: listen tcp :30300: bind: address already in use

Solution:

Bash
# Find and kill existing geth processes
ps aux | grep geth
pkill geth

# Or kill specific port
lsof -ti:30300 | xargs kill -9

# Wait 5 seconds and restart node

Issue 2: Permission Denied

Symptom:
Fatal: Failed to create the protocol stack: open .../data/geth/LOCK: permission denied

Solution:

Bash
# Fix ownership of all directories
sudo chown -R $USER:$USER ~/QBFT-Network/

Issue 3: Nodes Not Connecting

Symptom:
Looking for peers peercount=0
Dial error connection refused

Solution:

Bash
# Ensure all 5 nodes are running
ps aux | grep geth | wc -l  # Should return 5

# Check static-nodes.json has correct IPs
cat ~/QBFT-Network/QBFT-Network/Node-0/data/static-nodes.json

# Verify ports are not blocked
netstat -tuln | grep -E "30300|30301|30302|30303|30304"

Issue 4: Backend Cannot Connect to Blockchain

Symptom:
Error: connection not open

Solution:

Bash
# Verify Node-0 is running and RPC is active
curl http://localhost:22000

# Check .env configuration
cat ~/QBFT-Network/server/iot-blockchain-backend/.env | grep BLOCKCHAIN_RPC

# Restart backend server
cd ~/QBFT-Network/server/iot-blockchain-backend
node server.js

Issue 5: ESP32 WiFi Connection Failed

Symptom:
✗ WiFi Connection Failed!

Solution:

  1. Verify WiFi credentials are correct
  2. Ensure using 2.4GHz network (ESP32 doesn't support 5GHz)
  3. Check WiFi signal strength
  4. Move ESP32 closer to router
  5. Verify SSID has no special characters

Issue 6: ESP32 HTTP Request Failed

Symptom:
✗ HTTP Error code: -1

Solution:

Bash
# Verify server IP address is correct
# Ping server from another device
ping 192.168.1.150

# Check firewall isn't blocking port 3000
sudo ufw status
sudo ufw allow 3000/tcp

# Ensure backend server is running
curl http://localhost:3000/health

Issue 7: Transaction Reverted

Symptom:
{"error":"Transaction has been reverted by the EVM"}

Solution:

Bash
# Check device is registered
curl http://localhost:3000/api/devices

# Verify contract address in .env
cat ~/QBFT-Network/server/iot-blockchain-backend/.env | grep CONTRACT_ADDRESS

Performance Metrics

Metric Value Notes
Block Time 5 seconds Configurable in genesis
Transaction Finality Immediate QBFT provides instant finality
Data Send Interval 15 seconds ESP32-1 sends temperature
Data Fetch Interval 10 seconds ESP32-2 retrieves data
Network Latency < 100ms Local network
Consensus Threshold 3 of 5 nodes Byzantine fault tolerance
Maximum Downtime 2 nodes Network continues with 3+ nodes
Security Note:
This configuration is for development and testing. For production deployment, implement proper key management, HTTPS, authentication, and firewall rules.

IoT Temperature Monitoring System with QBFT Blockchain
Built with ❤️ using ESP32, Node.js, and GoQuorum

GitHub Repository