Airly pod nową bibliotekę SuplaDevice 😉

User avatar
veeroos
Posts: 539
Joined: Sun Mar 20, 2022 9:30 am
Location: Głogów

Post

Zainspirowany kolegi @Duch__'a postanowiłem troszkę przerobić jego program, tak aby chodziło to na nowej bibliotece SuplaDevice. Siedziałem, myślałem, pytałem, no i w końcu wyszło, choć czuje niedosyt, ale może któryś z kolegów pomoże, no więc tak to wygląda na chwilę obecną:
Screenshot_2024-06-25-09-38-53-443_org.supla.android.jpg
Czego mi tutaj brakuje, hmmm jak dodaję webserwer to urządzenie nie łączy się z serwerem Airly. Jakby komuś się udało to by było super, bo wtedy bym dodał okna do wprowadzania API KEY i współrzędnych naszej lokalizacji, na razie trzeba te dane wpisać ręcznie do kodu.
Kod wygląda tak:

Code: Select all

/*
Copyright (C) AC SOFTWARE SP. Z O.O.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#define SUPLA_COMM_DEBUG  //Zakomentuj tą linię by wyłączyć DEBUGOWANIE
#include <SPI.h>
#include <SuplaDevice.h>   //https://github.com/SUPLA/arduino/tree/develop
#include <ArduinoJson.h>  //Benonit Blanchot 6.14.0
#include <EEPROM.h>
#include <supla/control/relay.h>
#include "supla/sensor/therm_hygro_press_meter.h"
#include <supla/sensor/general_purpose_measurement.h>
#include <supla/network/html_element.h>
#include <supla/network/web_sender.h>
#include <supla/storage/config.h>
#include <supla/storage/storage.h>
#include <supla/storage/littlefs_config.h>
#include <string.h>
#include <supla/network/html/protocol_parameters.h>
#include <supla/device/supla_ca_cert.h>


#include <supla/network/esp_wifi.h>

Supla::LittleFsConfig configSupla;
//******************************************************************WIFI SETUP************************************************************************************************
Supla::ESPWifi wifi;


//*****************************************************************SUPLA SETUP************************************************************************************************


//*****************************************************************AIRLY SETUP************************************************************************************************
String lat = "51.00000";                             //Swoją lokalizację można pobrać z https://mapy.google.pl klikajac na swoj dom. Na dole ekranu pojawią się współrzędne
String lon = "16.00000";
String distance = "5.0";                              //Określamy promień od naszej lokalizacji, gdzie AIRLY będzie szukać najbliższego nam czujnika
String apiKey = "jfhjkfhjgdfjvhfhcvhgf";   //api key dostajemy po zalozeniu konta na https://developer.airly.eu/api
		 

//Dla PM1 nie liczy się stężenia w %, każda wartość jest groźna!
#define norma_WHO_PM25 25 //Norma PM2.5 wg. WHO
#define norma_WHO_PM10 50 //Norma PM10  wg. WHO
Supla::Sensor::GeneralPurposeMeasurement *gpm1 = nullptr;
Supla::Sensor::GeneralPurposeMeasurement *gpm2 = nullptr;
Supla::Sensor::GeneralPurposeMeasurement *gpm3 = nullptr;
Supla::Sensor::GeneralPurposeMeasurement *gpm4 = nullptr;


int timer = 0;
float current_PM1_value = 0;
float current_PM25_value = 0;
float current_PM10_value = 0;
float procent_PM25_value = 0;
float procent_PM10_value = 0;
float current_PRESSURE_value = 0;
float current_HUMIDITY_value = 0;
float current_TEMPERATURE_value = 0;
float current_CAQI_value = 0;
float current_WIND_value = 0;
float CAQI_old = 0;
String city_ = "";
String street_ = "";
String number_ = "";
String Device_name = "";
String Device_name_old = "";
String zmienna = "Airly";

byte r_new = 0;
byte g_new = 0;
byte b_new = 0;

byte r = 0;
byte g = 0;
byte b = 0;
byte Led_max = 0;
byte Led_max_old = 0;

byte licz_sprawdzenie_adresu_stacji = 0;


class odczyt_temp_wilg : public Supla::Sensor::ThermHygroPressMeter {
  public : 
  double getTemp() {
    return temperatura;
  }

  double getHumi() {
    return wilgotnosc;
  }

  double getPressure() {
    return cisnienie;
  }
  void setTemp(double val) {
    temperatura = val;
  }

  void setHumi(double val) {
    wilgotnosc = val;
  }
  void setPressure(double val){
    cisnienie = val;
  }

  void onInit() {
    pressureChannel.setNewValue(getPressure());
    channel.setNewValue(getTemp(), getHumi());
  }
   void iterateAlways() override {
    if (millis() - lastReadTime > 5000) {
      lastReadTime = millis();
      pressureChannel.setNewValue(getPressure());
      channel.setNewValue(getTemp(), getHumi());
    }
  }
 protected:
  double temperatura;
  double wilgotnosc;
  double cisnienie;
  double value;
  uint64_t lastReadTime = 0;
};
odczyt_temp_wilg *czujniczek = nullptr;

//****************************************************************************************************************************************************************************
void setup() {
  // Replace the falowing GUID with value that you can retrieve from https://www.supla.org/arduino/get-guid
  char GUID[SUPLA_GUID_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

  // Replace the following AUTHKEY with value that you can retrieve from: https://www.supla.org/arduino/get-authkey
  char AUTHKEY[SUPLA_AUTHKEY_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};


  //auto r1 = new Supla::Control::Relay(12);
  czujniczek = new odczyt_temp_wilg();
  Serial.begin(115200);
  Serial.println(" ");
  //Odczytaj_zakres_eeprom(100, 200);
  Device_name = zmienna.c_str();
  Serial.print("Set device name to: ");
  Serial.println(Device_name);
  SuplaDevice.setName(Device_name.c_str());
  Device_name_old = Device_name;  
 // cfg->setDeviceConfigChangeFlag();
 auto cfg = Supla::Storage::ConfigInstance();

  if (cfg)
  {
    char buf[100];
    if (!cfg->getGUID(buf))
    {
      Serial.println("[Main] setting config.");
      
      // PODMIEN SOBIE NA RZECZYWISTE WARTOSCI
      const char serverVariable[] = "twoj.server.srv";
      const char emailVariable[] = "twój mail";
      const char wifiSSID[] = "Twój SSID";
      const char wifiPass[] = "Hasło do Twojego wifi";
      
      cfg->setGUID(GUID);
      cfg->setAuthKey(AUTHKEY);
      cfg->setWiFiSSID(wifiSSID);
      cfg->setWiFiPassword(wifiPass);
      cfg->setSuplaServer(serverVariable);
      cfg->setEmail(emailVariable);
    }
    else
    {
      Serial.println("[Main] Can load guid.");
    }
  }
  else
  {
    Serial.println("[Main] Config not found.");
  }

  gpm1 = new Supla::Sensor::GeneralPurposeMeasurement();
  gpm2 = new Supla::Sensor::GeneralPurposeMeasurement();
  gpm3 = new Supla::Sensor::GeneralPurposeMeasurement();
  gpm4 = new Supla::Sensor::GeneralPurposeMeasurement();

  // Default channel config values are initialized only once. They can be
  // modified later, but they won't change corresponding used configurable
  // parameters.
  // Below lines are optional, just remove them if you don't want to set
  // them.
  gpm1->setDefaultUnitBeforeValue("PM1");
  gpm1->setDefaultUnitAfterValue("μg/m³");
  gpm1->setDefaultValuePrecision(2);  // 0..4 - number of decimal places
  gpm1->getChannel()->setDefaultIcon(16);
  gpm1->setValue(0);

  gpm2->setDefaultUnitBeforeValue("PM2,5");
  gpm2->setDefaultUnitAfterValue("μg/m³");
  gpm2->setDefaultValuePrecision(2);  // 0..4 - number of decimal places
  gpm2->getChannel()->setDefaultIcon(17);
  gpm2->setValue(0);

  gpm3->setDefaultUnitBeforeValue("PM10");
  gpm3->setDefaultUnitAfterValue("μg/m³");
  gpm3->setDefaultValuePrecision(2);  // 0..4 - number of decimal places
  gpm3->getChannel()->setDefaultIcon(18);
  gpm3->setValue(0);

  gpm4->setDefaultUnitBeforeValue("CAQI");
  gpm4->setDefaultValuePrecision(2);  // 0..4 - number of decimal places
  gpm4->getChannel()->setDefaultIcon(8);
  gpm4->setValue(0);

  //gpm1->saveConfig();
  SuplaDevice.setSuplaCACert(suplaCACert);
  SuplaDevice.setSupla3rdPartyCACert(supla3rdCACert);

  SuplaDevice.begin();
  /*(GUID,              // Global Unique Identifier 
                    SERVER_SUPLA,  // SUPLA server address
                    Email_adres,   // Email address used to login to Supla Cloud
                    AUTHKEY);          // Authorization key
*/
}

//****************************************************************************************************************************************************************************
void loop() {
  SuplaDevice.iterate();
 
  ++timer;
  if(timer == 10000){
      ++licz_sprawdzenie_adresu_stacji;
      if(licz_sprawdzenie_adresu_stacji==1){
        Odczytaj_ulice_z_Airly();
        Device_name = "";
        Device_name = "Airly: " + city_ + " " + street_ + " " + number_;
     
        
      }
      if(licz_sprawdzenie_adresu_stacji==100){
        licz_sprawdzenie_adresu_stacji==0;    
      }
      Odczytaj_dane_z_Airly();  
  }
  if(timer >= 180000){        //Dane z serwera pobierane są co około 180 sekund. Limit dzienny zapytań dla serwera AIRLY wynosi 1000 zapytań. 
                              //AIRLY sugeruje odpytywać serwer nieczęśniej niż co 90 sekund
    timer = 0;
  }
  delay(1);
}
//****************************************************************************************************************************************************************************
double get_pressure(int channelNumber, double last_val) {
    double pressure= 0;
    pressure = current_PRESSURE_value;
    return pressure;
}

void Odczytaj_dane_z_Airly(){
  ESP.wdtFeed();
  const char* host= "airapi.airly.eu"; 
  const int httpPort = 443; 
  String url = "/v2/measurements/nearest?lat=" + lat + "&lng=" + lon + "&maxDistanceKM=" + distance;
  char json[1024] = {};
  bool begin = false;
  begin = false; WiFiClientSecure client; client.setInsecure();
  if (!client.connect(host, httpPort)) {
      Serial.println("Blad polaczenia z ");Serial.print(host);return;
  }else{
    Serial.println("Polaczono z airly");
    client.print("GET " + url + " HTTP/1.1\r\n" + 
                 "Host: " + host + "\r\n" +
                 "User-Agent: ESP8266\r\n" +
                 "Accept: application/json\r\n" + 
                 "apikey: " + apiKey + "\r\n" +
                 "Connection: close\r\n\r\n");
      
    byte licz_znak = 0; int licz_znaki = 0; char c = 0; int index = 0;  memset(&json[0], 0, sizeof(json));delay(500);
    for (licz_znaki = 0; licz_znaki < 30000; ++licz_znaki) {
      ESP.wdtFeed();
      delay(1);
      c = client.read(); 
      if (c == '{') {
        begin = true;
      } 
      if(begin) {
        json[index] = c;index++;
      }
      if (c == ']') {
        ++licz_znak;
        if(licz_znak == 2){  //Szukamy drugiego znaku ] który pojawi się w transmisji i ją przerywamy, potem są już tylko dane historyczne wysylane przez serwer        
          json[index] = 125;index++; //Dodaje znak }
          json[index] = 125;index++; //Dodaje znak } i kończe odbieranie reszty danych. Udaje w ten sposób że transmisja była znacznie krótsza niż w rzeczywistości
          goto Koniec;
        } 
      }     
    }
    Koniec:  
    client.stop(); 
    Serial.println("Koniec polaczenia");
    Serial.println(json);
    DynamicJsonDocument doc(1024); DeserializationError error = deserializeJson(doc, json); if (error) return;
    JsonObject current = doc["current"];
    JsonArray current_values = current["values"];
    Serial.println(" ");

    current_PM1_value = current_values[0]["value"];
    Serial.print("PM1: ");
    Serial.print(current_PM1_value);
    Serial.println("ug/m3");

    current_PM25_value = current_values[1]["value"];
    procent_PM25_value = current_PM25_value * 100 / norma_WHO_PM25;
    Serial.print("PM25: ");
    Serial.print(current_PM25_value);
    Serial.print("ug/m3     ");
    Serial.print(procent_PM25_value);
    Serial.println("%");

    current_PM10_value = current_values[2]["value"];
    procent_PM10_value = current_PM10_value * 100 / norma_WHO_PM10; 
    Serial.print("PM10: ");
    Serial.print(current_PM10_value);
    Serial.print("ug/m3     ");
    Serial.print(procent_PM10_value);
    Serial.println("%");

    current_PRESSURE_value = current_values[3]["value"];
    Serial.print("PRESSURE: ");
    Serial.print(current_PRESSURE_value);
    Serial.println("hPa");

    current_HUMIDITY_value = current_values[4]["value"];
    Serial.print("HUMIDITY: ");
    Serial.print(current_HUMIDITY_value);
    Serial.println("%");

    current_TEMPERATURE_value = current_values[5]["value"];
    Serial.print("TEMPERATURE: ");
    Serial.print(current_TEMPERATURE_value);
    Serial.println("*C");

    current_values = current["indexes"];
    current_CAQI_value = current_values[0]["value"];
    Serial.print("CAQI: ");
    Serial.print(current_CAQI_value);
    Serial.println(" ");
    czujniczek->setTemp(current_TEMPERATURE_value);
    czujniczek->setHumi(current_HUMIDITY_value);
    czujniczek->setPressure(current_PRESSURE_value);
    gpm1->setValue(current_PM1_value);
    gpm2->setValue(current_PM25_value);
    gpm3->setValue(current_PM10_value);
    gpm4->setValue(current_CAQI_value);
  }
  return;
}

void Odczytaj_ulice_z_Airly(){
  ESP.wdtFeed();
  const char* host= "airapi.airly.eu"; 
  const int httpPort = 443; 
  String url = "/v2/installations/nearest?lat=" + lat + "&lng=" + lon + "&maxDistanceKM=" + distance; + "&maxResult=1";
  char json[1024] = {};
  bool begin = false;
  begin = false; WiFiClientSecure client; client.setInsecure();
  if (!client.connect(host, httpPort)) {
      Serial.println("Blad polaczenia z ");Serial.print(host);return;
  }else{
    Serial.println("Polaczono z airly");
    client.print("GET " + url + " HTTP/1.1\r\n" + 
                "Host: " + host + "\r\n" +
                "User-Agent: ESP8266\r\n" +
                "Accept: application/json\r\n" + 
                "apikey: " + apiKey + "\r\n" +
                "Connection: close\r\n\r\n");
      
    byte licz_znak = 0; int licz_znaki = 0; char c = 0; int index = 0;  memset(&json[0], 0, sizeof(json));delay(500);
      //while (client.available()) {
    for (licz_znaki = 0; licz_znaki < 30000; ++licz_znaki) {
      ESP.wdtFeed();
      delay(1);
      c = client.read(); 
      if (c == '{') {
        begin = true;
      } 
      if(begin){
        json[index] = c;index++;
      }
      if (c == '}') {
        ++licz_znak;
        if(licz_znak == 4){  //Szukamy czwartego znaku ] który pojawi się w transmisji i ją przerywamy
          goto Koniec;
        } 
      }
          
    }
    Koniec:  
    client.stop();
    Serial.println("Koniec polaczenia");
    Serial.println(json);
    DynamicJsonDocument doc(1024); DeserializationError error = deserializeJson(doc, json); if (error) return;
    Serial.println(" ");
    String city = doc["address"]["city"];
    String street = doc["address"]["street"];
    String number = doc["address"]["number"];
    city_ = city;
    street_ = street;
    number_ = number;
    Serial.print(city_);Serial.print(" ");Serial.print(street_);Serial.print(" ");Serial.println(number_);
    Serial.println(" "); 
  }
}
You do not have the required permissions to view the files attached to this post.
Zamel Mew-01, Wemos D1 mini Pro + Ikea vindriktning + BME280, 3x - SonOff mini, 3x - SonOff Basic, 3xGosund SP111, SonOff S55, 2x GOSUND WB4

https://github.com/v33r005
User avatar
klew
Posts: 9712
Joined: Thu Jun 27, 2019 12:16 pm
Location: Wrocław

Post

veeroos wrote: Tue Jun 25, 2024 8:00 am Czego mi tutaj brakuje, hmmm jak dodaję webserwer to urządzenie nie łączy się z serwerem Airly. Jakby komuś się udało to by było super, bo wtedy bym dodał okna do wprowadzania API KEY i współrzędnych naszej lokalizacji, na razie trzeba te dane wpisać ręcznie do kodu.
Na jakim ESP to robisz? 8266 czy 32?
Airly wymaga szyfrowanego połączenia, co zużywa dużo pamięci RAM. Webserwer też zjada RAM. Więc prawdopodobnie po prostu brakuje RAM-u.
Kiedy będzie Supla Offline Party / SOP#2025 ?
User avatar
veeroos
Posts: 539
Joined: Sun Mar 20, 2022 9:30 am
Location: Głogów

Post

Na esp8266, możliwe że jest tak jak piszesz. Na ESP32 musiałbym spróbować to zrobić 🤔.
Zamel Mew-01, Wemos D1 mini Pro + Ikea vindriktning + BME280, 3x - SonOff mini, 3x - SonOff Basic, 3xGosund SP111, SonOff S55, 2x GOSUND WB4

https://github.com/v33r005
Bernix
Posts: 38
Joined: Tue Jan 03, 2023 10:29 am

Post

Koledzy
mam taki błąd przy próbie kompilacji. Help

Arduino:1.8.19 (Windows 10), Płytka:"Generic ESP8266 Module, 80 MHz, Flash, Disabled, All SSL ciphers (most compatible), ck, 26 MHz, 40MHz, DOUT (compatible), 512K (no SPIFFS), 2, nonos-sdk 2.2.1 (legacy), v2 Lower Memory, Disabled, None, Only Sketch, 115200"
C:\Users\Bernix\Documents\Arduino\libraries\SuplaDevice\src\supla\storage\littlefs_config.cpp:26:22: fatal error: LittleFS.h: No such file or directory
#include <LittleFS.h>
^
compilation terminated.
exit status 1
Błąd kompilacji dla płytki Generic ESP8266 Module.
rafalekkalwak@wp.pl
Posts: 666
Joined: Mon Feb 06, 2023 8:56 am

Post

Bernix wrote: Wed Jul 10, 2024 7:23 am Koledzy
mam taki błąd przy próbie kompilacji. Help

Arduino:1.8.19 (Windows 10), Płytka:"Generic ESP8266 Module, 80 MHz, Flash, Disabled, All SSL ciphers (most compatible), ck, 26 MHz, 40MHz, DOUT (compatible), 512K (no SPIFFS), 2, nonos-sdk 2.2.1 (legacy), v2 Lower Memory, Disabled, None, Only Sketch, 115200"
C:\Users\Bernix\Documents\Arduino\libraries\SuplaDevice\src\supla\storage\littlefs_config.cpp:26:22: fatal error: LittleFS.h: No such file or directory
#include <LittleFS.h>
^
compilation terminated.
exit status 1
Błąd kompilacji dla płytki Generic ESP8266 Module.
Masz napisane, czego brakuje ;)

Zainstaluj taką bibliotekę.
Bernix
Posts: 38
Joined: Tue Jan 03, 2023 10:29 am

Post

W Arduino nie znalazłem takowej LITTLEFS. Można ją pobrać z zewnątrz?
rafalekkalwak@wp.pl
Posts: 666
Joined: Mon Feb 06, 2023 8:56 am

Post

Bernix wrote: Wed Jul 10, 2024 8:03 am W Arduino nie znalazłem takowej LITTLEFS. Można ją pobrać z zewnątrz?
A jak znajdę....

Z tym, że tu jest ESP32
https://www.arduino.cc/reference/en/lib ... efs_esp32/
User avatar
klew
Posts: 9712
Joined: Thu Jun 27, 2019 12:16 pm
Location: Wrocław

Post

Ta biblioteka jest częścią boardow. Prawdopodobnie masz starszą wersję. Zaktualizuj. Jakieś dwa lata temu zmieniło się źródło tych bibliotek, także to też może wymagać aktualizacji
Kiedy będzie Supla Offline Party / SOP#2025 ?
Bernix
Posts: 38
Joined: Tue Jan 03, 2023 10:29 am

Post

Więc działam dalej. :D dziękuje

Aktualizacja:

Klew miał rację, aktualizacja płytek przyniosła oczekiwany skutek. Wszystko działa :D
Bernix
Posts: 38
Joined: Tue Jan 03, 2023 10:29 am

Post

Nie wiem czy dobrze myślę. Veeroos rzuć okiem na 'if(timer >= 180000){ //Dane z serwera pobierane są co około 180 sekund. Limit dzienny zapytań dla serwera AIRLY wynosi 1000 zapytań.
//AIRLY sugeruje odpytywać serwer nieczęśniej niż co 90 sekund".
Z tego co wyczytałem na stronie AIRLY limit połączeń to 100, a z twojego kodu połączenie jest co 3 minuty - 100 połączeń wypsztyka się przez 5 godzin.

Tak jakoś mi wyszło bo moduł przestał czytać dane z serwera. Po zmianie z 180000 na 900000 w następnej dobie dane spłynęły do aplikacji.

Return to “Projekty użytkowników”