BLYNK blokuje program przy braku połączenia – co robić? cz. II

By: | Post date: Wrzesień 29, 2017

Rozwiązanie I – Łatanie dziur

Rozważmy najtrudniejszy przypadek – połączenie z serwerem BLYNK siecią WiFi. Złożoność problemu polega na tym, że biblioteka BLYNK oprócz połączenia z serwerem musi wcześniej zestawić połączenie WiFi. Ten problem nie występuje przy dostępie do sieci via Ethernet – tam połączenie dokonuje się sprzętowo bez udziału bibliotek BLYNKa.

Rozwiązanie jest jedno – należy w jakikolwiek sposób „ominąć” powyższe procedury jeśli nie dochodzi do połączenia w określonym czasie ale jednocześnie umożliwić połączenie gdy ustąpią przyczyny jego przerwania.Nie jest to proste zadanie.

Wiemy że program może zawiesić się na dwu procedurach BLYNK

  • Blynk.begin(………)
  • Blynk.run()

Blynk.begin(………)

zawiera w sobie kilka ważnych funkcji

  • Ustawienie podstawowych parametrów dostępu do serwera BLYNK – AUTCH i adres serwera (publiczny – domyślny lub lokalny)
  • Nawiązanie połączenia WiFi (ssid i pass) lub powtarzanie prób nawiązania połączenia WiFi – do skutku
  • Nawiązanie połączenia z serwerem BLYNK lub powtarzanie prób nawiązania połączenia z serwerem – do skutku

Są więc dwa przypadki mogące zawiesić program na procedurze Blynk.begin – niemożność nawiązania połączenia WiFi i brak połączenia z serwerem. Oba te zdarzenia trzeba rozpatrywać osobno.

Po pierwsze musimy zamienić procedurę  Blynk.begin kilkoma podprocedurami w sumie realizującymi całość funkcji. Dokumentacja BLYNKa zaleca następujący ciąg procedur:

Blynk.config(auth, server, port);

ustalającą parametry dostępu do serwera BLYNK

Blynk.connectWiFi(ssid, pass);

nawiązującą połączenie WiFi

Blynk.connect();

nawiązującą połączenie z serwerem BLYNK.

Po rozdzieleniu funkcji  i sprawdzeniu w działaniu wiemy że:

  • Pierwsza procedura nie wstrzymuje nigdy przebiegu programu
  • Druga staje murem aż do nawiązania połączenia WiFi (niestety)
  • Trzecia zatrzymuje  program na około 5 sek  przy braku połączenia z serwerem (choć powinna na 30 sek)

Z nawiązaniem połączenia WiFi jest więc problem – ale został on już szczęśliwie rozwiązany na forum BLYNKa. Należy użyć innej procedury, która jest „przelotowa” a dodatkowo sama odtwarza połączenie w tle działania programu głównego.

WiFi.begin(ssid, pass);

Pierwsza część problemu za nami.

Blynk.run()

To podstawowa procedura odpowiedzialna za wszystkie zdarzenia pomiędzy modułem, serwerem i aplikacją mobilną w czasie normalnej pracy systemu. Dodatkowo zawiera funkcję odtwarzania połączenia z serwerem w przypadku jego zaniku. Umieszczona w pętli głównej wywoływana jest setki a może tysiące razy w ciągu sekundy. Przy braku łączności praktycznie blokuje  dostęp innych części programu do procesora. Rozwiązaniem jest sprawdzanie stanu łącza. Przy prawidłowym połączeniu Blynk.run() wywoływana jest z maksymalną częstotliwością. W razie awarii sieci lub serwera należy ją (lub Blynk.connect) uruchamiać w dużo dłuższych odstępach (sekundowych, minutowych). Dobór czasu zależy już od konkretnej aplikacji. Rozsądnym przedziałem jest 0,5 – 10 minut.

Szkielet instrukcji umożliwiający w miarę płynne działanie programu podstawowego przy problemach z łącznością wygląda mniej więcej tak.

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <Timers.h>

#define treconnect 30 //czasy wywoływania Blynk.run przy braku łączności w sek
int liczreconnect = 10;

void setup()
{
akcja.attach(0, 1000, testconnect); // timer 1 sek
  .......
  WiFi.begin(ssid, pass);
  Blynk.config(auth, IPAddress(192, 168, 2, 19));
  Blynk.connect();
  .............
}
  void loop()
  {
    akcja.process(); // timer Timers.h
    if (Blynk.connected()) {
      Blynk.run();
    }
  }

  void testconnect() //procedura wywoływana co 1 sek
{
  if (Blynk.connected()) {
    liczreconnect = treconnect;
  } else {
    liczreconnect--;
    if (liczreconnect==0) {
      Blynk.connect();
      liczreconnect = treconnect;
    }
  }
}

 

Podsumowanie

Programowe ominięcie problemu zawieszania programu przez bibliotekę BLYNKa wymaga nieco zachodu i pewnej znajomości specyfiki systemu. Powyższy przykład jest maksymalnie złożony – w modułach z kablowym łączem do sieci problemy związane z  WiFi nie występują. Nie jest to niestety uniwersalna recepta dla wszystkich obsługiwnych przez BLYNKa modułów i platform. To raczej wskazówka jak rozwiązać problem w swoim projekcie gdy program główny jest priorytetowy w stosunku do aplikacji BLYNK.

W kolejnym odcinku o optymalnym moim zdaniem rozwiązaniu tego problemu opartym na strukturze dwuprocesorowej.

 12

4 komentarze

  • Krzycho napisał(a):

    Blynk nie będzie działał bo kierujesz połączenie na serwer lokalny
    Blynk.config(auth, IPAddress(192, 168, 2, 19));
    ma być tak jak w działającym ci programie z Blynk.begin(auth);
    czyli Blynk.config(auth);
    tu domyślnie kierujesz BLYNKa na serwer w chmurze

    to nauczka dla mnie by pisać jaśniej w komentarzach

    biblioteka timers.h MUSI działać 🙂

  • Tomek napisał(a):

    Cześć, niestety u mnie nie działa, mam podpięte po kablu(ethernet), mógłbyś napisać czy wtedy kod jest taki sam?
    Dodatkowo u mnie nie działa biblioteka Timers.h(nie wiem czemu), więc zrobiłem to „ręcznie” na millisach.
    Czy w linijce: Blynk.config(auth, IPAddress(192, 168, 2, 19)); mam podać adres IP który był przypisany mojemu arduino(mój 192.168.254.105)?
    Mój kod już znasz dostawiłem tylko twoje linijki:
    #include // standardowa biblioteka Arduino
    #include // dolaczenie pobranej biblioteki I2C dla LCD
    #include „RTClib.h”
    #include „Adafruit_HTU21DF.h”
    #include „i2c.h”
    #include „i2c_BMP280.h”
    #include
    #include
    #include „Timers.h”
    LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Ustawienie adresu ukladu na 0x27
    RTC_DS1307 czas;
    Adafruit_HTU21DF htu = Adafruit_HTU21DF();
    BMP280 bmp280;
    #define led_blue 50
    #define treconnect 30 //czasy wywoływania Blynk.run przy braku łączności w sek
    int liczreconnect = 10;
    enum{STAN1=1, STAN2};
    int stan = STAN1;
    int wilg;
    int temp;
    unsigned long cMillis1;
    unsigned long pMillis1= 0;
    unsigned long cMillis;
    unsigned long pMillis = 0;
    const long interval = 6000;
    char auth[] = „4fc87431c0ec4c1e90778d8c5765b1c8”;

    void setup() {
    Serial.begin(9600);
    // akcja.attach(0, 1000, testconnect); // timer 1 sek
    // Blynk.begin(auth);
    Blynk.config(auth, IPAddress(192, 168, 2, 19));
    Blynk.connect();
    pinMode(led_blue, OUTPUT);
    htu.begin();
    Wire.begin();
    czas.begin();
    lcd.begin(20,4); // Inicjalizacja LCD 2×16
    lcd.backlight(); // zalaczenie podwietlenia
    wilg = htu.readHumidity();
    temp = htu.readTemperature();
    }

    void loop() {
    cMillis1= millis();
    if(cMillis1 – pMillis1>1000){
    test_connect();
    pMillis1 = cMillis1;
    }
    if (Blynk.connected()) {
    Blynk.run();
    }

    cMillis = millis();
    switch(stan){
    case STAN1:
    ZEGAR_HTU();
    if(cMillis – pMillis > interval){
    pMillis = cMillis;
    lcd.clear();
    stan=STAN2;
    }
    break;
    case STAN2:
    BMP_280();
    if(cMillis – pMillis > interval){
    pMillis = cMillis;
    lcd.clear();
    stan=STAN1;
    }
    break;
    }

    }
    void ZEGAR_HTU(){
    DateTime now = czas.now();
    lcd.setCursor(5,0); // Ustawienie kursora w pozycji 0,0 (pierwszy wiersz, pierwsza kolumna)
    lcd.print(„Temp: „);
    lcd.print(temp);
    lcd.print(„*C”);
    lcd.setCursor(1,1); //Ustawienie kursora w pozycji 0,0 (drugi wiersz, pierwsza kolumna)
    lcd.print(„Wilgotnosc: „);
    lcd.print(wilg);
    lcd.print(„%RH”);
    lcd.setCursor(5,2);
    lcd.print(now.day(), DEC);
    lcd.print(‚.’);
    lcd.print(now.month(), DEC);
    lcd.print(‚.’);
    lcd.print(now.year(), DEC);
    lcd.setCursor(6,3);
    lcd.print(now.hour(), DEC);
    if(now.minute()<10){
    lcd.print(':0');
    }
    else{
    lcd.print(":");
    }
    lcd.print(now.minute(), DEC);
    lcd.print(':');
    lcd.print(now.second(), DEC);
    }

    void BMP_280(){
    bmp280.initialize();
    bmp280.setEnabled(0);
    bmp280.triggerMeasurement();
    bmp280.awaitMeasurement();
    float temperature;
    bmp280.getTemperature(temperature);
    float pascal;
    int hPascal = pascal/100;
    bmp280.getPressure(pascal);
    static float meters, metersold;
    int metry = meters;
    bmp280.getAltitude(meters);
    metersold = (metersold * 10 + meters)/11;
    bmp280.triggerMeasurement();
    lcd.setCursor(2,0);
    lcd.print(" Wys npm.: ");
    lcd.print(metry);
    lcd.print(" m");
    lcd.setCursor(2,1);
    lcd.print(" Temp: ");
    lcd.print(temperature);
    lcd.print(" *C");
    lcd.setCursor(1,2);
    lcd.print("Cisnienie atmosf.:");
    lcd.setCursor(6,3);
    lcd.print(hPascal);
    lcd.print(" hPa");
    }
    void testconnect()
    {
    if (Blynk.connected()) {
    digitalWrite(led_blue, HIGH); //wskaźnik łączności z serwerem stan wysoki- wyłączenie LEDa
    } else {
    digitalWrite(led_blue, LOW);
    }
    }
    void test_connect(){ //procedura wywoływana co 1 sek
    if (Blynk.connected()) {
    liczreconnect = treconnect;
    } else {
    liczreconnect–;
    if (liczreconnect==0) {
    Blynk.connect();
    liczreconnect = treconnect;
    }
    }
    }

  • Krzycho napisał(a):

    akcja.process() to cykliczne wywołanie timera odliczającego czasy w programie

  • lukacjusz napisał(a):

    a co to jest ‚akcja’ ??????

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Translate »