//(c) Wojciech Mamak
// bezprzewodowa stacja monitorowania pyłu zawieszonego PM10, PM2.5
// ESP8266 + Plantower 1003 + DHT22
// wersja testowa, rzeczywiście działająca może się różnić parametrami
// automatyczna aktualizacja oprogramowania (OTA)
// Smog Pszów
#include <Arduino.h>
#include "DHT.h"
#include <ESP8266WiFi.h>
#include "ESP8266HTTPUpdate.h"

#define WERSJA "016" //wersja programu
#define LEN 31  //długość wiadomości dla PMS1003 32 bajty
//#define SETPIN 3
#define DHTPIN 4 // pin DHT22
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

const unsigned long czasuspienia = 300; //czas pomiedzy pomiarami w sekundach
const unsigned long CZAS_START = 15000; // czas od wlaczenia do startu pomiaru [ms]
const unsigned long CZAS_PAUZA = 3000 ; // pauza pomiedzy powtorzeniami pomiaru [ms]

#define adresSerwera  "nazwaserwera.pl"
#define portSerwera        80
#define AP_SSID "nazwa_sieci"
#define AP_PASSWORD "haslo_sieci"

//dla ułatwienia obliczyłem komendy dla PMS1003 / PMS3003
const byte wakeup[7] = {0x42, 0x4d, 0xe4, 0x00, 0x01, 0x01, 0x74};
const byte sleep[7] =  {0x42, 0x4d, 0xe4, 0x00, 0x00, 0x01, 0x73};
const byte active[7] = {0x42, 0x4d, 0xe1, 0x00, 0x01, 0x01, 0x71};

unsigned char buf[LEN];

float t=0;
float h=0;


static int PM01Val[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static int PM25Val[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static int PM10Val[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static int PMa01Val[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static int PMa25Val[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static int PMa10Val[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

static int tPM01Val = 0;
static int tPM25Val = 0;
static int tPM10Val = 0;
static int tPMa01Val = 0;
static int tPMa25Val = 0;
static int tPMa10Val = 0;


static unsigned long licznikczasu;
static int licznik;

char sprawdzSume(unsigned char *buf, char leng) //sprawdzanie sumy kontrolnej z PMS1003
{
  char receiveflag = 0;
  int receiveSum = 0;
  for (int i = 0; i < (leng - 2); i++) {
    receiveSum = receiveSum + buf[i];
  }
  receiveSum = receiveSum + 0x42;
  if (receiveSum == ((buf[leng - 2] << 8) + buf[leng - 1])) 
  {
    receiveSum = 0;
    receiveflag = 1;
  }
  return receiveflag;
}

void setup() {

  Serial.begin(9600);   
  Serial.setTimeout(1500);   
  Serial.println(WERSJA);

  pinMode ( DHTPIN, INPUT_PULLUP );
  dht.begin();

  delay(1000);
  Serial.write(wakeup, sizeof(wakeup));
  delay(200);
  Serial.write(active, sizeof(active));
  licznikczasu = millis();
  licznik = 0;
}


void dodajpomiar(int i)
{
  PM01Val[i] = tPM01Val;
  PM25Val[i] = tPM25Val;
  PM10Val[i] = tPM10Val;
  PMa01Val[i] = tPMa01Val;
  PMa25Val[i] = tPMa25Val;
  PMa10Val[i] = tPMa10Val;

}



void wifiConnect()  //laczenie z sieci wifi
{
  WiFi.mode(WIFI_STA);
  WiFi.begin ( AP_SSID, AP_PASSWORD );
  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 1500 );
     if(millis()>120000) ESP.restart();
  }
}





//wysyłanie wyników przez HTTP/POST
void wyslijPomiar(int d1, int d2, int d3, int d4, int d5, int d6, float d7, float d8)
{
  WiFiClient client;
  while (!client.connect(adresSerwera, portSerwera)) {
    wifiConnect();
  if(millis()>120000) ESP.restart();
  }
  bool aktualizuj=false;
  String url = "";
  url += "/plik.php?d1=" + String(d1);
  url += "&d2=" + String(d2);
  url += "&d3=" + String(d3);
  url += "&d4=" + String(d4);
  url += "&d5=" + String(d5);
  url += "&d6=" + String(d6);
  url += "&d7=" + String(d7);
  url += "&d8=" + String(d8);
  url += "&d9=" + String(WERSJA);
  client.print(String("POST ") + url + " HTTP/1.1\r\n" +
               "Host: " + String(adresSerwera) + "\r\n" +
               "Connection: close\r\n" +
               "Content-Length: 0\r\n" +
               "\r\n");
  delay(100);
  while (client.available()) {
   String line = client.readStringUntil('\r');
  
  if(line.indexOf("Wersja") >=0) //sprawdzanie wersji
    { 
      String wersja=line.substring(8,11);
      if (wersja!=WERSJA)
      {
        aktualizuj=true;
                } ;  
  }//sprawdzanie wersji

  if(aktualizuj) // aktualizacja
  {
    Serial.println("Aktualizacja..."); 
        ESPhttpUpdate.update(adresSerwera, portSerwera, "/firmware.bin");
        delay(500);
        client.stop();
        ESP.restart();
  }
      
    }
    client.stop();
      Serial.println("Dane wyslane");
}


void transmisja(unsigned char *thebuf)  //odczyt aktualnych wartosci z PMS1003
{
  tPM01Val = ((thebuf[3] << 8) + thebuf[4]);
  tPM25Val = ((thebuf[5] << 8) + thebuf[6]);
  tPM10Val = ((thebuf[7] << 8) + thebuf[8]);
  tPMa01Val = ((thebuf[9] << 8) + thebuf[10]);
  tPMa25Val = ((thebuf[11] << 8) + thebuf[12]);
  tPMa10Val = ((thebuf[13] << 8) + thebuf[14]);
}


void sortujint(int tablica[], int rozmiar) {
  for (int i = 0; i < (rozmiar - 1); i++) {
    for (int j = 0; j < (rozmiar - (i + 1)); j++) {
      if (tablica[j] > tablica[j + 1]) {
        int t = tablica[j];
        tablica[j] = tablica[j + 1];
        tablica[j + 1] = t;
      }
    }
  }
}


void oblicz()
{
  sortujint(PM01Val, 10);
  sortujint(PM25Val, 10);
  sortujint(PM10Val, 10);
  sortujint(PMa01Val, 10);
  sortujint(PMa25Val, 10);
  sortujint(PMa10Val, 10);


  // obliczanie sredniej z 6 pomiarów
  int d1 = (PM01Val[2] + PM01Val[3] + PM01Val[4] + PM01Val[5] + PM01Val[6] + PM01Val[7]) / 6;
  int d2 = (PM25Val[2] + PM25Val[3] + PM25Val[4] + PM25Val[5] + PM25Val[6] + PM25Val[7]) / 6;
  int d3 = (PM10Val[2] + PM10Val[3] + PM10Val[4] + PM10Val[5] + PM10Val[6] + PM10Val[7]) / 6;
  int d4 = (PMa01Val[2] + PMa01Val[3] + PMa01Val[4] + PMa01Val[5] + PMa01Val[6] + PMa01Val[7]) / 6;
  int d5 = (PMa25Val[2] + PMa25Val[3] + PMa25Val[4] + PMa25Val[5] + PMa25Val[6] + PMa25Val[7]) / 6;
  int d6 = (PMa10Val[2] + PMa10Val[3] + PMa10Val[4] + PMa10Val[5] + PMa10Val[6] + PMa10Val[7]) / 6;

  float  d7 = h;
  float  d8 = t;


  wyslijPomiar(d1, d2, d3, d4, d5, d6, d7, d8);

}

void loop() {
  tPM01Val = 0; //tymczasowe dane dla pojedynczego odczytu
  tPM25Val = 0;
  tPM10Val = 0;
  tPMa01Val = 0;
  tPMa25Val = 0;
  tPMa10Val = 0;


  if (Serial.find(0x42)) {  //wykrycie transmisji z PMS1003
    Serial.readBytes(buf, LEN);

    if (buf[0] == 0x4d) {

      if (sprawdzSume(buf, LEN)) { //spr. czy suma kontrolna sie zgadza
        transmisja(buf); // odczyt danych do tymczasowych zmiennych
      }
    }

  } //koniec odczytu 0x42


  if (millis() > 120000) // jesli nie ma reakcji przez 2 minuty - restart
  {
    ESP.deepSleep(czasuspienia * 1000000);
  }


  //zapis pojedynczego pomiaru
  if (millis() > CZAS_START && millis() - licznikczasu >= CZAS_PAUZA && tPM01Val > 0)
  {
     float  wilg = dht.readHumidity();
     float  temp = dht.readTemperature();
     //czasem jest problem z odczytem z DHT22 (NaN) wiec przy okazji 10 prob
     if (!isnan(temp))
        t=temp;
     if (!isnan(wilg))
        h=wilg;

    dodajpomiar(licznik); //zapis pojedynczego pomiaru  do tablicy
    licznikczasu = millis();
    licznik++;
  }


  if (licznik >= 10) // po 10 odczytach wyslij dane i zasnij
  {
    oblicz();  //oblicza srednie i wysyla dane
    delay(250);
    //uspienie PMS1003
    Serial.write(sleep, sizeof(sleep)); 
      delay(100);

    //uspienie esp8266
    long czasusp = (czasuspienia * 1000) - millis();
    if (czasusp < 10000) czasusp = 10000;
    // zakładam czas uśpienia w mikrosekundach minus czas jaki zajęło wykonanie pomiaru
    Serial.println("Odczyt zakonczony, uspienie esp8266");
    ESP.deepSleep(czasusp * 1000);

  }



}