Harun YenisanMehmet Emin Uğur
Published © MIT

Automated Window/Ventilation Control for Enhanced Indoor Air

Automated Window/Ventilation Control for Enhanced Indoor Air Quality

IntermediateWork in progress24 hours87
Automated Window/Ventilation Control for Enhanced Indoor Air

Things used in this project

Hardware components

RAKwireless RAK4631
×1
RAKwireless RAK19003
×1
RAKwireless RAK1906
×1
RAKwireless RAK7268V2
×1
RAKwireless RAK13009
×1

Software apps and online services

Arduino IDE
Arduino IDE
The Things Industries TheThingsNetwork
Node-RED
Node-RED

Hand tools and fabrication machines

Multitool, Screwdriver
Multitool, Screwdriver

Story

Read more

Code

Automated Window/Ventilation Control for Enhanced Indoor Air Quality

Arduino
Automated Window/Ventilation Control for Enhanced Indoor Air Quality
#include <LoRaWan-RAK4631.h> // LoRaWAN library for RAK4631
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME680.h> // For RAK1906 (uses BME680 sensor)

// LoRaWAN Join Settings
// You must obtain these values from your TheThingsNetwork (TTN) console.
String node_device_eui = "AC1F09FFFE0xxxx"; // <<< CHANGE THIS
String node_app_eui = "70B3D57ED000xxxx";   // <<< CHANGE THIS
String node_app_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // <<< CHANGE THIS

// LoRaWAN Application Port
#define LORAWAN_APP_PORT 1

// Data Transmission Interval (in seconds)
#define SEND_INTERVAL_SEC 60 // Interval for sending sensor data (1 minute)

// Relay Pin Definition (for Window Opener or Fan)
#define RELAY_VENTILATION_PIN WB_IO3 // Example GPIO pin for relay (e.g., WisBlock IO3)

// Sensor Object
Adafruit_BME680 bme; // RAK1906 (BME680) environmental sensor object

// Timer Variable
time_t lastSendTime = 0; // Last sensor data transmission time (millis())

void setup() {
  // Set internal LED as OUTPUT and turn it off initially
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW); 
  
  // Start serial communication (for debug messages)
  Serial.begin(115200);
  while (!Serial); // Wait for serial port to be ready
  Serial.println("Automated Window/Ventilation Control Starting...");

  // Set relay pin as OUTPUT and keep it off initially
  pinMode(RELAY_VENTILATION_PIN, OUTPUT);
  digitalWrite(RELAY_VENTILATION_PIN, LOW); 

  // Start I2C communication (for BME680)
  Wire.begin();

  // Initialize RAK1906 (BME680) sensor
  if (!bme.begin(0x76)) { // I2C address can be 0x76 or 0x77
    Serial.println("RAK1906 BME680 not found, check connection!");
    while (1); 
  }
  // Sampling and gas heater settings for BME680 (for air quality detection)
  bme.setTemperatureOversampling(BME680_OS_8X);
  bme.setHumidityOversampling(BME680_OS_2X);
  bme.setGasHeater(320, 150); // Heater settings for gas sensor (VOC) (320°C, 150ms)
  Serial.println("RAK1906 BME680 successfully initialized.");

  // Initialize and configure LoRaWAN module
  api.lorawan.setMcuStatus(true);         // Set MCU status
  api.lorawan.setDeviceClass(CLASS_A);    // Set device class
  api.lorawan.setRegion(EU868);           // Set LoRaWAN region
  api.lorawan.setDevEui(node_device_eui); // Set Device EUI
  api.lorawan.setAppEui(node_app_eui);   // Set Application EUI
  api.lorawan.setAppKey(node_app_key);   // Set Application Key
  api.lorawan.setConfirm(true);          // Use confirmed messages?
  api.lorawan.setAdaptiveDr(true);       // Use Adaptive Data Rate (ADR)?
  
  Serial.println("Attempting to join LoRaWAN network...");
  api.lorawan.join(); // Start LoRaWAN network join process

  // Initialize timer
  lastSendTime = millis();
}

void loop() {
  // Check LoRaWAN network connection status
  if (api.lorawan.queryNetworkStatus() == LORAWAN_JOINED) {
    // Check if it's time to send data
    if (millis() - lastSendTime > (SEND_INTERVAL_SEC * 1000)) {
      sendSensorData();       // Send sensor data
      lastSendTime = millis(); // Reset timer
    }
  } else {
    // If not connected to LoRaWAN network, try to join again...
    Serial.println("Not connected to LoRaWAN network, trying to join again...");
    api.lorawan.join(); 
    delay(10000); // Wait 10 seconds before retrying
  }
}

// Function to read sensor data and send it via LoRaWAN
void sendSensorData() {
  Serial.println("Reading sensor data...");
  
  // Start BME680 (RAK1906) readings
  bme.performReading(); 
  float temperature = bme.temperature; // Temperature
  float humidity = bme.humidity;       // Humidity
  float gas_resistance = bme.gas_resistance / 1000.0; // Gas Resistance (Ohms to KOhms)

  // Print read data to Serial Monitor
  Serial.printf("Temperature: %.2f C, Humidity: %.2f %%RH, Gas Resistance: %.2f KOhms\n", temperature, humidity, gas_resistance);

  // Package data into LoRaWAN payload
  // Payload structure: Temperature (float), Humidity (float), Gas (float)
  uint8_t payload[12]; // 3 floats * 4 bytes = 12 bytes
  memcpy(&payload[0], &temperature, sizeof(float)); // Temperature
  memcpy(&payload[4], &humidity, sizeof(float));    // Humidity
  memcpy(&payload[8], &gas_resistance, sizeof(float)); // Gas Resistance (Air Quality)

  Serial.println("Sending LoRaWAN data...");
  // Send payload to LoRaWAN network. Use LORAWAN_APP_PORT and send as confirmed message (true).
  api.lorawan.send(sizeof(payload), payload, LORAWAN_APP_PORT, true); 
}

// Callback function to be called when a LoRaWAN downlink message is received
// Processes commands from Node-RED or similar automation systems.
void OnRxData(lorawan_AppData_t* appData) {
  Serial.printf("LoRaWAN Downlink message received, Port: %d, Length: %d\n", appData->Port, appData->BuffSize);
  
  // Print received payload to serial monitor
  Serial.print("Payload (hex): ");
  for (int i = 0; i < appData->BuffSize; i++) {
    Serial.printf("%02X ", appData->Buffer[i]);
  }
  Serial.println();

  // Convert payload to String and interpret as command
  String command = "";
  for (int i = 0; i < appData->BuffSize; i++) {
    command += (char)appData->Buffer[i];
  }
  Serial.print("Received Command: ");
  Serial.println(command);

  // Control relay based on incoming commands (ventilation fan/window opener)
  if (command == "VENT_ON") {
    digitalWrite(RELAY_VENTILATION_PIN, HIGH); // Turn on relay
    Serial.println("VENTILATION TURNED ON!");
  } else if (command == "VENT_OFF") {
    digitalWrite(RELAY_VENTILATION_PIN, LOW);  // Turn off relay
    Serial.println("VENTILATION TURNED OFF!");
  } else {
    Serial.println("Unknown command.");
  }
}

// Mandatory callback function definitions for LoRaWAN library
void lorawan_rx_handler(lorawan_AppData_t* appData) {
  OnRxData(appData); // Call OnRxData function when data is received
}

void lorawan_has_joined_handler(void) {
  Serial.println("Successfully joined LoRaWAN network!");
  digitalWrite(LED_BUILTIN, HIGH); // Turn on internal LED after joining network
}

void lorawan_join_failed_handler(void) {
  Serial.println("LoRaWAN network join failed. Retrying...");
  digitalWrite(LED_BUILTIN, LOW); // Turn off internal LED if join fails
}

void lorawan_tx_handler(void) {
  Serial.println("LoRaWAN Tx Done!"); // Inform when data transmission is complete
}

Credits

Harun Yenisan
8 projects • 8 followers
Electronic engineer, working as an IT teacher at the Ministry of National Education.
Mehmet Emin Uğur
9 projects • 10 followers
Computer engineer, working as an IT teacher at the Ministry of National Education.

Comments