Code
C++ is used for coding the microcontroller and python is used for server. Main setup initialize led display first then wifi so that led display may show connection status of wifi then lastly initializes all sensors. Then it gose to a loop witch starts sensor reading and send them to server and led display.

Library
beegee-tokyo/DHT sensor library for ESPx@^1.19
adafruit/Adafruit CCS811 Library@^1.1.1
adafruit/Adafruit SH110X@^2.1.10
Main
Main startup
void setup()
{
//Set data rate
Serial.begin(9600);
wifi net;
sensor sens;
oled led;
//initialize led display
led.oled_init();
//initialize wifi
net.wifi_init();
//initialize sensors
sens.sensor_init();
}
Main loop
void loop()
{
wifi net;
oled led;
sensor sens;
sensor_data data;
//Read data from DHT11 sensor
sDHT hum_temp = sens.hum_temp_sens();
//read data from Air quality 3 click
sQ quality = sens.quality_sens();
//read data from Soil sensor 1 and 2
std::pair<int, int> soil_sens = sens.soil_sens();
//Store sensor data in a struct
data.soil1 = soil_sens.first;
data.soil2 = soil_sens.second;
data.hum = hum_temp.Hum;
data.temp = hum_temp.Temp;
data.quality_temp = quality.Temp;
data.quality_eCO2 = quality.eCO2;
data.quality_TVOC = quality.TVOC;
//read data from pH senor
data.ph = sens.PH_sens();
//Print sensor data to led display
led.oled_Display(data);
//send data to server
net.wifi_transfer(data);
//Sampling delay
delay(300000);
}
Sensor
Include and defines
#include <sensor.h>
#include <Arduino.h>
#include <DHTesp.h>
#include <SPI.h>
#include <Wire.h>
#include "Adafruit_CCS811.h"
#include "DFRobot_PH.h"
#include <EEPROM.h>
//define pinns
//Soil sensor
#define pin_soil1_power 15
#define pin_soil2_power 3
#define pin_analog PIN_A0
//DHT11
#define pin_hum_temp_input 2
//SEN0161-V2
#define pin_PH_power 0
#define pin_analog PIN_A0 //pH meter Analog output to Arduino Analog Input 0
Adafruit_CCS811 ccs;
DHTesp dht;
DFRobot_PH ph;
Sensor initialization
void sensor::sensor_init()
{
//soil sensors
pinMode(pin_soil1_power,OUTPUT);
digitalWrite(pin_soil1_power, LOW);
pinMode(pin_soil2_power,OUTPUT);
digitalWrite(pin_soil2_power, LOW);
//DHT11
dht.setup(pin_hum_temp_input, DHTesp::DHT11);
//Air quality 3 click
ccs.begin();
//SEN0161-V2
pinMode(pin_PH_power,OUTPUT);
digitalWrite(pin_PH_power, LOW);
ph.begin();
}
Soil sensor
std::pair<int, int> sensor::soil_sens() //Function with 2 variables
{
digitalWrite(pin_soil1_power, HIGH); // Turn the sensor 1 ON
delay(10); // Allow power to settle
int val1 = analogRead(pin_analog); // Read the analog value form sensor
digitalWrite(pin_soil1_power, LOW); // Turn the sensor 1 OFF
digitalWrite(pin_soil2_power, HIGH); // Turn the sensor 2 ON
delay(10); // Allow power to settle
int val2 = analogRead(pin_analog); // Read the analog value form sensor
digitalWrite(pin_soil2_power, LOW); // Turn the sensor 2 OFF
return std::make_pair(val1, val2); // Return analog moisture value
}
DHT11
sDHT sensor::hum_temp_sens()
{
sDHT hum_temp;
hum_temp.Hum = dht.getHumidity(); // Read humidity from sensor
hum_temp.Temp = dht.getTemperature(); // Read temperature from sensor
return hum_temp; // Return Both
}
Air quality 3 click
sQ sensor::quality_sens()
{
sQ quality;
quality.Temp = ccs.calculateTemperature(); // Calculate temperature from sensor
ccs.readData(); // Read sensor data
quality.TVOC = ccs.getTVOC(); // Store TVOC data
quality.eCO2 = ccs.geteCO2(); // Store CO2 data
return quality; // Return data
}
SEN0161-V2
float sensor::PH_sens()
{
digitalWrite(pin_PH_power, HIGH); // Turn the sensor ON
delay(1000);
static unsigned long timepoint = millis();
sensor sens;
sQ temp = sens.quality_sens();
float voltage;
float temperature;
float phValue;
if(millis()-timepoint>1000U)
{ //time interval: 1s
timepoint = millis();
temperature = temp.Temp; // read your temperature sensor to execute temperature compensation
voltage = analogRead(pin_analog)/1024.0*3300; // read the voltage
//float phValue = ph.readPH(voltage,temperature); // convert voltage to pH with temperature compensation
float acidVoltage = 2032.44; //buffer solution 4.0 at 25C
float neutralVoltage = 1500.0; //buffer solution 7.0 at 25C
float slope = (7.0-4.0)/((neutralVoltage-1500.0)/3.0 - (acidVoltage-1500.0)/3.0); // two point: (_neutralVoltage,7.0),(_acidVoltage,4.0)
float intercept = 7.0 - slope*(neutralVoltage-1500.0)/3.0;
phValue = slope*(voltage-1500.0)/3.0+intercept; //y = k*x + b
}
ph.calibration(voltage,temperature); // calibration process by Serail CMD
digitalWrite(pin_PH_power, LOW); // Turn the sensor OFF
return phValue;
}
Header
#ifndef SENSOR_H
#define SENSOR_H
#include <DHTesp.h>
#include "Adafruit_CCS811.h"
//Global struct
struct sDHT
{
float Temp;
float Hum;
};
struct sQ
{
float Temp;
float TVOC;
float eCO2;
};
struct sensor_data
{
int soil1;
int soil2;
float hum;
float temp;
float quality_temp;
float quality_TVOC;
float quality_eCO2;
float ph;
};
class sensor
{
public:
void sensor_init();
std::pair<int, int> soil_sens();
sDHT hum_temp_sens();
sQ quality_sens();
float PH_sens();
};
#endif // SENSOR_H
Oled
Include and defines
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <oled.h>
#include <Arduino.h>
#define SCREEN_WIDTH 128 //Screen resolution
#define SCREEN_HEIGHT 64 //Screen resolution
#define mosi_pin 13 //I2C MOSI
#define sclk_pin 14 //I2C SCLK
#define dc_pin 16 //data command
#define res_pin 12 //reset
#define cs_pin 1 //chip select
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT,mosi_pin, sclk_pin, dc_pin, res_pin, cs_pin);
Led display initialization
Wifi startup display
void oled::oled_wifi_start(int count)
{
//print "." each secound while waiting for wifi conection as a loading screen
display.setCursor(count*5, 0);
display.setTextSize(0.5);
display.setTextColor(SH110X_WHITE);
display.println(F("."));
display.display();
}
void oled::oled_wifi_connect(const IPAddress& ip)
{
//Print microcontrollers ip address
display.clearDisplay();
display.setTextSize(0.5);
display.setTextColor(SH110X_WHITE);
display.setCursor(0, 0);
display.println(F("Connected to WiFi"));
display.println(F("IP Address: "));
display.println(ip);
display.display();
}
Sensor display
void oled::oled_Display(const sensor_data& data)
{
//Print sensor data to display
//First column
display.clearDisplay();
display.setTextSize(0.3);
display.setTextColor(SH110X_WHITE);
display.setCursor(0, 0);
display.println(F("S_1: "));
display.setCursor(30, 0);
display.println(data.soil1);
display.setCursor(0, 18);
display.println(F("T_i: "));
display.setCursor(30, 18);
display.println(data.temp);
display.setCursor(0, 36);
display.println(F("Hum: "));
display.setCursor(30, 36);
display.println(data.hum);
display.setCursor(0, 54);
display.println(F("eCO2: "));
display.setCursor(30, 54);
display.println(data.quality_eCO2);
//Secound column
display.setCursor(64, 0);
display.println(F("S_2: "));
display.setCursor(94, 0);
display.println(data.soil2);
display.setCursor(64, 18);
display.println(F("T_o: "));
display.setCursor(94, 18);
display.println(data.quality_temp);
display.setCursor(64, 36);
display.println(F("TVOC: "));
display.setCursor(94, 36);
display.println(data.quality_TVOC);
display.setCursor(64, 54);
display.println(F("PH: "));
display.setCursor(94, 54);
display.println(data.ph);
display.display();
}
Sensor display
#include <sensor.h>
#include <string>
#include <IPAddress.h>
class oled
{
public:
void oled_init();
void oled_Display(const sensor_data& data);
void oled_wifi_start(int count);
void oled_wifi_connect(const IPAddress& ip);
};
Wifi
Include and defines
#include <wifi.h>
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <oled.h>
const char* ssid = "GL-MT300N-V2-fe9"; //wifi name
const char* password = "goodlife"; //wifi password
const char* host = "http://192.168.8.130:8080/endpoint"; //server ip and port
oled led;
Wifi initialization
void wifi::wifi_init()
{
WiFi.begin(ssid,password); //connect to wifi
int count = 0;
while (WiFi.status() != WL_CONNECTED) //wait for connection
{
delay(1000);
led.oled_wifi_start(count); //display loading screen
count ++;
}
IPAddress localIP = WiFi.localIP(); //read microcontrollers ip adress
led.oled_wifi_connect(localIP); //display microcontrollers new ip
}
Wifi connection
void wifi::wifi_transfer(const sensor_data& data)
{
if (WiFi.status() == WL_CONNECTED)
{
WiFiClient client;
HTTPClient http;
// Set the target server address
http.begin(client, host);
// Set the content type header for JSON data
http.addHeader("Content-Type", "application/json");
// Your JSON data to send
String jsonData = "{\"Soil1\": "+String(data.soil1)+", \"Soil2\": "+String(data.soil2)+", \"Hum\": "+String(data.hum)+", \"Temp inn\": "+String(data.temp)+", \"Temp out\": "+String(data.quality_temp)+", \"eCO2\": "+String(data.quality_eCO2)+", \"TVOC\": "+String(data.quality_TVOC)+", \"PH\": "+String(data.ph)+"}";
// Send a POST request with the JSON data
int httpResponseCode = http.POST(jsonData);
// Check the response code
if (httpResponseCode > 0)
{
String response = http.getString();
Serial.println("HTTP Response: " + response);
}
else
{
Serial.println("HTTP Error: " + String(httpResponseCode));
}
http.end();
}
}
Header
#include <sensor.h>
class wifi
{
public:
void wifi_init();
void wifi_transfer(const sensor_data& data);
};
Server
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import csv
import os
from datetime import datetime
class RequestHandler(BaseHTTPRequestHandler):
def do_POST(self):
#get sensor data
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
json_data = json.loads(post_data.decode('utf-8'))
# get current time
current_time = str(datetime.now())
#print data recived
print("Received JSON data:")
print(json_data)
#add time stamp to sensor data
json_data['time'] = current_time
self.save_to_csv(json_data)
self.send_response(200)
self.end_headers()
self.wfile.write(b"Data received successfully!")
def save_to_csv(self, data):
# choose path to save CSV
csv_file_path = 'C:/python_venvs/MAS512_2023_AUTUMN/Assignment_1/server/data.csv'
# Check if the CSV file already exists; if not, create it and write the header
if not os.path.isfile(csv_file_path):
with open(csv_file_path, 'w', newline='') as csv_file:
csv_writer = csv.DictWriter(csv_file, fieldnames=data.keys())
csv_writer.writeheader()
# Append the received data to the CSV file
with open(csv_file_path, 'a', newline='') as csv_file:
csv_writer = csv.DictWriter(csv_file, fieldnames=data.keys())
csv_writer.writerow(data)
if __name__ == '__main__':
server_address = ('192.168.8.236', 8080) # server ip and port
httpd = HTTPServer(server_address, RequestHandler)
print('Server running on port 8080...')
httpd.serve_forever()