// Sietse Jonker & Dano Bosch
// 13/02/2024

#include <Wire.h>
#include <Adafruit_SH110X.h>
#include <Adafruit_SGP30.h>
#include <DHT.h>
#include <WiFiMulti.h>
#include <WiFi.h>
#include <WebSocketsClient.h>


#define MICPIN 6
#define DHTPIN 7
#define SCL 9
#define SDA 8
#define DHTTYPE DHT11
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define i2c_adress 0x3c

Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
DHT dht(DHTPIN, DHTTYPE);
WiFiMulti WiFiMulti;
Adafruit_SGP30 sgp;
WebSocketsClient webSocket;

uint16_t TVOC_base, eCO2_base;
uint16_t counter = 0;
float temperature = 0;
float humidity = 0;
uint16_t eCO2 = 0;
uint16_t TVOC = 0;
bool noise = false;
#define USE_SERIAL Serial

void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
	const uint8_t* src = (const uint8_t*) mem;
	USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
	for(uint32_t i = 0; i < len; i++) {
		if(i % cols == 0) {
			USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
		}
		USE_SERIAL.printf("%02X ", *src);
		src++;
	}
	USE_SERIAL.printf("\n");
}

void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {

	switch(type) {
		case WStype_DISCONNECTED:
			USE_SERIAL.printf("[WSc] Disconnected!\n");
			break;
		case WStype_CONNECTED:
			USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);

			// send message to server when Connected
			webSocket.sendTXT("Connected");
			break;
		case WStype_TEXT:
			USE_SERIAL.printf("[WSc] get text: %s\n", payload);

			// send message to server
			// webSocket.sendTXT("message here");
			break;
		case WStype_BIN:
			USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
			hexdump(payload, length);

			// send data to server
			// webSocket.sendBIN(payload, length);
			break;
		case WStype_ERROR:			
		case WStype_FRAGMENT_TEXT_START:
		case WStype_FRAGMENT_BIN_START:
		case WStype_FRAGMENT:
		case WStype_FRAGMENT_FIN:
			break;
	}
}

void websocketSetup(){
	for(uint8_t t = 4; t > 0; t--) {
		USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
		USE_SERIAL.flush();
		delay(1000);
	}

	WiFiMulti.addAP("iotroam", "vbK9gbDBIB");

	//WiFi.disconnect();
	while(WiFiMulti.run() != WL_CONNECTED) {
		delay(100);
	}

	// server address, port and URL
	webSocket.begin("145.92.8.114", 80, "/ws");

	// event handler
	webSocket.onEvent(webSocketEvent);

	// use HTTP Basic Authorization this is optional remove if not needed


	// try ever 5000 again if connection has failed
	webSocket.setReconnectInterval(5000);
}

void resetValues() {
  TVOC_base;
  eCO2_base;
  counter = 0;
  temperature = 0;
  humidity = 0;
  eCO2 = 0;
  TVOC = 0;
  noise = false;
}

void displayData() {
  // clear display for new info
  display.clearDisplay();

  // set custom text properties
  display.setTextSize(2);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(0, 0);

  // display info on oled screen
  display.println((String) "Temp: " + int(temperature) + "C" + '\n'
    + "Humi: " + int(humidity) + "%" + '\n'
    + "eCO2: " + int(sgp.eCO2) + '\n'
    + "TVOC: " + int(sgp.TVOC));
                  
  display.display();
}

// bool wifiConnect(){
//   while (WiFi.status() != WL_CONNECTED)
//   {
//     delay(250);

//     wifiMulti.addAP("LansanKPN-boven", "19sander71vlieland14");
//     wifiMulti.addAP("iotroam", "vbK9gbDBIB");
//     Serial.println("Connectie maken...");

//     wifiMulti.run();
//   }
//   Serial.println("Wifi Connected...");

//   return true;
// }

void setup() {
  Serial.begin(115200);

  // tell display what settings to use
  display.begin(i2c_adress, true);
  display.clearDisplay();

  // tell sensors to start reading
  dht.begin();
  sgp.begin();

  pinMode(MICPIN, INPUT);
  pinMode(DHTPIN, INPUT);
  
  websocketSetup();
  resetValues();
}

void loop() {
  webSocket.loop();

  webSocket.sendTXT("Temp: " + String(temperature));
  webSocket.sendTXT("Humi: " + String(humidity));
  webSocket.sendTXT("eCO2: " + String(sgp.eCO2));
  webSocket.sendTXT("TVOC: " + String(sgp.TVOC));


  // if sensor doesnt work print in serialmonitor
  if (!sgp.IAQmeasure()) {
    Serial.println("SGP30: BAD");
    return;
  } else {
    Serial.println("SGP30: OK");
  }

  // read dht11 sensor
  temperature = float(dht.readTemperature());
  humidity = float(dht.readHumidity());

  // display sensordata on oled screen
  displayData();

  // get new baseline every 30 seconds
  if (counter == 30) {
    sgp.getIAQBaseline(&eCO2_base, &TVOC_base);
  }
  counter++;
}