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. .. image:: images/MAS_513.png Library ---------- #. beegee-tokyo/DHT sensor library for ESPx@^1.19 #. adafruit/Adafruit CCS811 Library@^1.1.1 #. dfrobot/DFRobot_PH@^1.0.0 #. adafruit/Adafruit SH110X@^2.1.10 Main ---------- Main startup .. code-block:: c++ 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 .. code-block:: c++ 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 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 .. code-block:: c++ #include #include #include #include #include #include "Adafruit_CCS811.h" #include "DFRobot_PH.h" #include //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 .. code-block:: c++ 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 .. code-block:: c++ std::pair 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 .. code-block:: c++ 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 .. code-block:: c++ 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 .. code-block:: c++ 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 .. code-block:: c++ #ifndef SENSOR_H #define SENSOR_H #include #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 soil_sens(); sDHT hum_temp_sens(); sQ quality_sens(); float PH_sens(); }; #endif // SENSOR_H Oled ---------- Include and defines .. code-block:: c++ #include #include #include #include #include #include #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 .. code-block:: c++ void oled::oled_init () { display.begin(0, true); display.display(); display.clearDisplay(); } Wifi startup display .. code-block:: c++ 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 .. code-block:: c++ 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 .. code-block:: c++ #include #include #include 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 .. code-block:: c++ #include #include #include #include #include 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 .. code-block:: c++ 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 .. code-block:: c++ 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 .. code-block:: c++ #include class wifi { public: void wifi_init(); void wifi_transfer(const sensor_data& data); }; Server ---------- .. code-block:: python 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()