Skocz do zawartości

Pomocna odpowiedź

Napisano

Cześć,

Buduje proste urządzenie którego zadaniem jest sterowanie przekaźnikami i odczytywanie stanów przycisków. Dodatkową funkcją jest możliwość sterowanie przez WebServer WiFi. ESP32 jest serwerem i działa w trybie mieszanym (acces point + klient po połączeniu z moim ruterem). Napisany poniżej kod działa i jest oparty o asynchroniczny webserwer oraz wątki. Jest z nim jednak pewien problem. Serwer gubi około 30-40% żądań. Gdy wysyłam żądanie z przeglądarki na adres 192.168.1.1 to czasami dostaje odpowiedź natychmiast, czasami po 1-3 sekundach, a czasami od razu dostaje odpowiedz, że serwer nie odpowiada. Taki stan rzeczy występuje bez względu na tryb działania, czy to AP czy podpięcie do innego wifi. Nie mam pojęcia co może być nie tak. Czemu odpowiedzi są tak niestabilne? Ktoś miał podobny problem?  

#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <Preferences.h>
#include <nvs_flash.h>

SET_LOOP_TASK_STACK_SIZE(64 * 1024);

#if !defined(ARDUINO_ESP32C3_DEV)  // This is due to a bug in RISC-V compiler, which requires unused function sections :-(.
#define DISABLE_CODE_FOR_RECEIVER  // Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required.
#endif

const char JSON_PAGE[] PROGMEM = R"=====(
{"door":<DOOR>,"button-blue":"<BUTTON_BLUE>","button-stop":"<BUTTON_STOP>","rssi":<RSSI>,"deviceName":"<DEVICE_NAME>","ip":"<IP>","mac":"<MAC_NUMBER>"}
  )=====";

const char PAGE_HEAD[] PROGMEM = R"=====(

<!DOCTYPE html><html lang="pl-PL">
<head><meta name="viewport" content="width=device-width, initial-scale=1" http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="icon" href="data:,">
<style>
  html { 
    font-family: Helvetica;
  display: inline-block; 
  margin: 0px auto; 
  text-align: center;
  }
  .button { 
    background-color: #4CAF50; 
    border: none; 
    border-radius: 30px;
    font-weight:bolder; 
    color: white; 
    padding: 16px 10px;
  text-decoration: none; 
  font-size: 20px; 
  margin: 2px; cursor: pointer;}
  .button2 {
    background-color: #555555;
    }
</style>
</head>
  <body>
  <style>
  input { width:80%; max-width:400px;font-size:1.5em;margin-bottom:10px;} 
  select { width:80%; max-width:400px;font-size:1.5em;margin-bottom:10px;}
  </style> 
  <h1 style="background-color: #eb34de;padding:10px;color:#ffffff;">ESP32 MODUŁ BEZPIECZEŃSTWA MASTER
  <br><DEVICE_NAME></h1><h3>IP: 
  <IP>
  RSSI: 
  <RSSI>
  dbm</h3>
  <p>Natężenie:<b>
  <NATEZENIE>
  A</b></p>
 

)=====";


const char PAGE_BOTTOM[] PROGMEM = R"=====(
  <footer>Połączono z Wifi:<br><b>
  <WIFI_NAME>
  </b><br>
  MAC:<br><b>
  <MAC_NUMBER>
  </b></footer>
  </body>
</html>
)=====";

const char MAIN_PAGE[] PROGMEM = R"=====(
   <p><a href="/setNetwork"><button>Ustawienia sieci</button></a></p>
  <p><a href="/setSettings"><button>Ustawienia urządzenia</button></a></p>
  <p><a href="/reset"><button>Reset urządzenia</button></a></p>
  <p>DRZWI: <DOOR></p>
  <p>PRZYCISK: <BUTTON_BLUE></p>
  <p>GUZIK BEZPIECZENSTWA: <BUTTON_STOP></p>
  <p>
  <a href="/on?id=1"><button class="button">P1 ON</button></a>
  <a href="/off?id=1"><button class="button" style="background-color:red;">P1 OFF</button></a>
  </p>
  <p>
  <a href="/on?id=2"><button class="button">P1 ON</button></a>
  <a href="/off?id=2"><button class="button" style="background-color:red;">P1 OFF</button></a>
  </p>
  <p>
  <a href="/on?id=3"><button class="button">P1 ON</button></a>
  <a href="/off?id=3"><button class="button" style="background-color:red;">P1 OFF</button></a>
  </p>
)=====";

const char RESET_PAGE[] PROGMEM = R"=====(
 <!DOCTYPE html><html lang="pl-PL">
    <head><meta name="viewport" content="width=device-width, initial-scale=1" http-equiv="Content-Type" content="text/html; charset=UTF-8"></head>
    <body style="text-align:center;font-size:1.2em;font-weight:bold;">
  
    <p>Następuje restart sterownika...:)</p>
  
    <p>Przekierowanie za 5 sekund</p>
    <script>
      window.setTimeout(function(){
        window.location.href = "/";
      }, 5000);</script>
    </body></html>
)=====";


String deviceName = "SERWEREK";

// Replace with your network credentials
String ssidAP = ""; 
const char* passwordAP = "12345678";

const char* http_username = "admin";
const char* http_password = "admin";


String ssid = "ESP32";
String password = "12345678";

String myIp = "-.-.-.-";


#define PIN_LIGHT_RED 25
#define PIN_LIGHT_GREEN 26
#define PIN_BUZZER 27

#define PIN_BUTTON_BLUE 33
#define PIN_BUTTON_STOP 35
#define PIN_DOOR_OPEN 34


// Set web server port number to 80
char XML[4048];

WebServer server(80);

TaskHandle_t mainTask;
TaskHandle_t serverTask;
TaskHandle_t resetTask;
TaskHandle_t checkWiFiTask;
TaskHandle_t checkButtons;

volatile uint8_t ApWifiMode = false;

volatile int rstMinuteInterval = 60;
volatile int tickMinuteInterval = 10;

volatile int rssi = 0;

String mac = "";

volatile uint8_t measurement_count = 0;
volatile bool lastLedState = true;

volatile uint8_t door = 0;
volatile uint8_t button = 0;
volatile uint8_t stop = 0;


bool connectWiFi() {
  uint8_t licznik = 0;

  if (WiFi.status() != WL_CONNECTED /*|| WiFi.status() != WL_IDLE_STATUS*/) {

    WiFi.begin(ssid, password);
    licznik = 0;
    while (WiFi.status() != WL_CONNECTED) {
      vTaskDelay(pdMS_TO_TICKS(500));
      licznik++;
      if (licznik > 20) {  // jak sie nie uda połlączyc przez 20 sekund to zmień  na acces pint
        //Serial.println("Nie udało się połączyć do wifi...");
        myIp = "-:-:-:-";
        rstMinuteInterval = 5;
        ApWifiMode = true;
        break;
      }
    }
  }
  if (WiFi.status() == WL_CONNECTED) {

    myIp = WiFi.localIP().toString();
    ApWifiMode = false;
    rssi = WiFi.RSSI();
    Serial.println("Pobieram rssi");
    return true;
  }

  return false;
}


void mainPageAction() {

  strcpy(XML, PAGE_HEAD);
  strcat(XML, MAIN_PAGE);
  strcat(XML, PAGE_BOTTOM);

  String toSend = String(XML);

  toSend.replace("<RSSI>", String(rssi));
  toSend.replace("<DEVICE_NAME>", (String)deviceName);
  toSend.replace("<IP>", String(myIp));
  toSend.replace("<NATEZENIE>", String("0"));

  if (ApWifiMode == false) {
    toSend.replace("<WIFI_NAME>", String(ssid));
  } else {
    toSend.replace("<WIFI_NAME>", "BRAK POŁACZENIA Z WIFI");
  }
  toSend.replace("<MAC_NUMBER>", String(mac));

  if (door == 0) {
    toSend.replace("<DOOR>", String("ZAMKNIETE"));
  } else {
    toSend.replace("<DOOR>", String("OTWARTE"));
  }

  if (button == 0) {
    toSend.replace("<BUTTON_BLUE>", String("WCISNIETY"));
  } else {
    toSend.replace("<BUTTON_BLUE>", String(" - "));
  }

  if (stop == 0) {
    toSend.replace("<BUTTON_STOP>", String("!!! ALARM !!!"));
  } else {
    toSend.replace("<BUTTON_STOP>", String("GIT"));
  }


  server.send(200, "text/html", toSend);
}

void jsonDataAction() {
  strcpy(XML, JSON_PAGE);

  String toSend = String(XML);

  toSend.replace("<RSSI>", String(rssi));
  toSend.replace("<DEVICE_NAME>", String(deviceName));
  toSend.replace("<IP>", myIp);
  toSend.replace("<DOOR>", String(door));
  toSend.replace("<BUTTON_BLUE>", String(button));
  toSend.replace("<BUTTON_STOP>", String(stop));

  toSend.replace("<MAC_NUMBER>", mac);

  server.send(200, "application/json", toSend);
}

void setup() {

  pinMode(PIN_LIGHT_RED, OUTPUT);
  pinMode(PIN_LIGHT_GREEN, OUTPUT);
  pinMode(PIN_BUZZER, OUTPUT);

  digitalWrite(PIN_LIGHT_RED, LOW);
  digitalWrite(PIN_LIGHT_GREEN, LOW);
  digitalWrite(PIN_BUZZER, LOW);

  pinMode(PIN_BUTTON_BLUE, INPUT);
  pinMode(PIN_BUTTON_STOP, INPUT);
  pinMode(PIN_DOOR_OPEN, INPUT);

  ssidAP = (String) "ESP32-" + (String)deviceName;

  WiFi.setSleep(WIFI_PS_NONE);
  WiFi.mode(WIFI_AP_STA);
  WiFi.softAP(ssidAP, passwordAP);
  vTaskDelay(pdMS_TO_TICKS(2000));

  IPAddress Ip(192, 168, 1, 1);
  IPAddress NMask(255, 255, 255, 0);
  WiFi.softAPConfig(Ip, Ip, NMask);
  vTaskDelay(pdMS_TO_TICKS(2000));
  mac = WiFi.macAddress();

  //WiFi.begin(ssid, password);
  Serial.println("Połaczony z AP");

  Serial.begin(115200);
  Serial.println(ssidAP);

  //M_deleteAll();

  disableCore0WDT();

  server.on("/", mainPageAction);
  server.on("/reset", ResetEspAction);
  server.on("/ajax/data", jsonDataAction);

  server.begin();

  //Serial.println("Podpinam taski....");
  xTaskCreatePinnedToCore(ResetTask, "resetTask", 2048, NULL, 1, &resetTask, 0);
  xTaskCreatePinnedToCore(CheckWiFiTask, "checkWiFiTask", 2048, NULL, 2, &checkWiFiTask, 0);
  xTaskCreatePinnedToCore(CheckButtons, "checkButtons", 4048, NULL, 2, &checkButtons, 0);
  xTaskCreatePinnedToCore(ServerTask, "serverTask", 8192, NULL, 1, &serverTask, 1);
}

void ServerTask(void* pvParameters) {
  //taskCount++;
  while (true) {
    // Update the server task core indicators
    server.handleClient();
    vTaskDelay(pdMS_TO_TICKS(10));
  }
}

void ResetEspAction() {
  server.send(200, "text/html", RESET_PAGE);
  ESP.restart();
}

void ResetTask(void* pvParameters) {
  //taskCount++;
  while (true) {
    if (millis() > (rstMinuteInterval * 60000)) {  //to takie zabezpieczenie wrazie wypadku
      //Serial.println("RESETUJE ESP32!!!");
      WiFi.disconnect(true);
      WiFi.mode(WIFI_OFF);
      vTaskDelay(pdMS_TO_TICKS(2000));
      Serial.println("RESETUJE BO JUZ CZAS");
      ESP.restart();
    }
    vTaskDelay(pdMS_TO_TICKS(5000));
  }
}

void CheckButtons(void* pvParameters) {
  //taskCount++;
  while (true) {
    door = digitalRead(PIN_DOOR_OPEN);
    button = digitalRead(PIN_BUTTON_BLUE);
    stop = digitalRead(PIN_BUTTON_STOP);

    vTaskDelay(pdMS_TO_TICKS(200));
  }
}

void CheckWiFiTask(void* pvParameters) {
  //taskCount++;
  while (true) {
    if (ApWifiMode == false) {
      //Serial.println("SPRAWDZAM POLACZENIE Z WIFI");
      connectWiFi();
    }
    vTaskDelay(pdMS_TO_TICKS(123000));
  }
}

void loop() {
}

 

Jeżeli nie wina kanałów i kodu, to problem może być z płytką, zasilaniem. Trafiłem np. na 4 esp32-wroom-32d, w których wifi jest niestabilne w działaniu, kiedy zasilam w inny sposób niż przez usb. Najłatwiej przetestować ten sam kod na innej płytce.

(edytowany)

Początkowo też myślałem że to kwestia stabilności zasilania, ale testowałem ten kod w kilku różnych wytrawionych płytkach, gdzie zasilanie jest stabilne, poza tym samo ESP32 nie zawiesza się, program działa normalnie, tylko dziwi mnie, że nie każdy request dochodzi. 

EDIT: 

W sumie to wszystkie moduły zasilam przez pin VIN ustabilizowanym zasilaniem 5V, i może zainstalowany zasilacz na płytce jest za mało wydajny? Czy coś da jeżeli dolutuje kondensator pomiędzy pinami GND i 3V3? 

Edytowano przez wojtek6000
(edytowany)

Przy zestawieniu WIFI_AP_STA ESP32 faktycznie pracuje jako dwa interfejsy, ale fizycznie ma tylko jedno radio.
Zakładam, że gdy tylko ramka musi przelecieć po interfejsie STA (np. DHCP, NTP, cokolwiek z LAN-u), układ przełącza radio na chwilę w tryb klienta, a interfejs AP „zamiera”. Pakiet wysłany przez przeglądarkę w tym ułamku sekundy nie dolatuje i TCP zrywa połączenie – i stąd część żądań trafia natychmiast, część dostaje odpowiedź po 1-3 s (TCP retransmisja), a część kończy się błędem „serwer nie odpowiada”.

Efekt jest tym gorszy, im więcej ruchu ma interfejs STA lub im więcej urządzeń wisi na AP.

Dodatkowo wydaje mi się, że w Twoim kodzie występują blokujące wywołania vTaskDelay() w taskach, które powinny być "non-blocking", np. w ServerTask. Generalnie lepiej pisać taki kod w sposób który nie blokuje niepotrzebnie kontekstu zadań (taskYIELD()), lub robi to zbyt długo. Jeszcze w tym kontekście - sprawdź priorytety zadań. Task serwera ma niższy priorytet (1) niż task WiFi (2). Gdy task WiFi jest aktywny, może "zagłodzić" task serwera.

I na koniec - wiem że String wygodnie się używa, ale ta klasa ma tendencje do fragmentacji pamięci, co też może się z czasem odbić na stabilności.

To tak na szybko. 😉

Edytowano przez jaszczurtd
  • Lubię! 1

vTaskDelay akurat jest wskazany (równie dobrze można użyć arduinowego delay, vide źródła). Nic nie blokuje.

Zastanawiam się po co kombinować zamiast użyć asynchronicznych bibliotek (AsyncWebServer na przykład), ale pewnie autor ma w tym jakiś ukryty cel...

W takich sytuacjach o wiele lepszym wyborem jest taskYELD()

"vTaskDelay() should not be used where a non-blocking API is required. Use taskYIELD() for cooperative yielding."

Co do AsyncWebServer się zgadzam.

37 minut temu, jaszczurtd napisał:

W takich sytuacjach o wiele lepszym wyborem jest taskYELD()

Tylko że robi coś innego niż vTaskDelay i do czego innego służy, a przede wszystkim nie w takich sytuacjach. Używam delay z powodzeniem i nie narzekam. Zresztą jest jeszcze parę funkcji które nie blokują innych tasków...

Dziękuję za odpowiedzi, poprawiłem część z priorytetami wątków:

 xTaskCreatePinnedToCore(TickTask, "tickTask", 8192, NULL, 0, &tickTask, 1);
  xTaskCreatePinnedToCore(ResetTask, "resetTask", 2048, NULL, 1, &resetTask, 1);
  xTaskCreatePinnedToCore(CheckButtons, "checkButtons", 4048, NULL, 2, &checkButtons, 1);
  xTaskCreatePinnedToCore(LcdTask, "lcdTask", 4048, NULL, 3, &lcdTask, 1);

  xTaskCreatePinnedToCore(CheckWiFiTask, "checkWiFiTask", 2048, NULL, 1, &checkWiFiTask, 0);
  xTaskCreatePinnedToCore(ServerTask, "serverTask", 8192, NULL, 0, &serverTask, 0);

Dodatkowo poprawiłem wyłączyłem tryb AP i używam tylko STA. Jest nieznaczna różnica, teraz czasy odpowiedzi kształtują się w przedziale 20-200ms ale wciąż dość często pojawiają się wartości 3-5s. 

Pytanie mam jeszcze o funkcje taskYIELD(). W przeciwieństwie do vTaskDelay() nie przyjmuje żadnych parametrów, a np. w wątkach które sprawdzają status wciśnięcia przycisku potrzebuje czekać 0,2 sekundy. Jak w takim przypadku mam zrealizować takie oczekiwanie?

Co do przesiadki na AsyncWebSerwer spróbuje to przetestować jak nic innego nie zadziała.

38 minut temu, wojtek6000 napisał:

Pytanie mam jeszcze o funkcje taskYIELD(). W przeciwieństwie do vTaskDelay() nie przyjmuje żadnych parametrów, a np. w wątkach które sprawdzają status wciśnięcia przycisku potrzebuje czekać 0,2 sekundy. Jak w takim przypadku mam zrealizować takie oczekiwanie?

Użyć normalnego delay() czy vTaskDelay(), to w sumie jedna chwała, a w międzyczasie przeczyta sobie w dokumentacji co tak naprawdę robi yield i kiedy się go stosuje.

Fragment z esp32-hal-misc.c:

void delay(uint32_t ms) {
  vTaskDelay(ms / portTICK_PERIOD_MS);
}

przy okazji przypominam, że portTICK_PERIOD_MS w Arduino dla ESP32 to 1 a nie 10.

(edytowany)
49 minut temu, wojtek6000 napisał:

Pytanie mam jeszcze o funkcje taskYIELD(). W przeciwieństwie do vTaskDelay() nie przyjmuje żadnych parametrów, a np. w wątkach które sprawdzają status wciśnięcia przycisku potrzebuje czekać 0,2 sekundy. Jak w takim przypadku mam zrealizować takie oczekiwanie?

Używaj millis() i konstrukcji warunkowych/licznikowych które polegają na sprawdzaniu czy upłynął już założony czas. Dla przykładu, kod CheckButtons() który realizuje tą filozofię:

void CheckButtons() {
  static uint32_t lastCheckTime = 0;
  uint32_t now = millis();

  if (now - lastCheckTime >= 200) {
    lastCheckTime = now;

    door = digitalRead(PIN_DOOR_OPEN);
    button = digitalRead(PIN_BUTTON_BLUE);
    stop = digitalRead(PIN_BUTTON_STOP);
  }
}

I teraz wyobraźmy sobie że funkcja CheckButtons() ma być rozszerzona o jakąś dodatkową funkcjonalność - vTaskDelay() będzie w tym przypadku jedynie niepotrzebnie zatrzymywać kontekst, i marnować czas procesora, który można by wykorzystać na coś innego.

Idąc dalej, zastanów się czy do tego projektu faktycznie potrzebujesz FreeRTOS, i czy nie lepiej by było zrealizować logikę np. za pomocą maszyny stanów współpracującej z logiką która nie będzie blokować kontekstu. FreeRTOS to takie trochę leniwe pójście na skróty w przypadku takich prostych programów, i taką "wielowątkowość" jakiej tu potrzebujesz można spokojnie uzyskać bez niego.

Pamiętaj też o tym, że jak stosujesz filozofię wielowątkowości, która korzysta z jednego hardware, to może pojawić się problem związany z kolizją wątków/wywłaszczaniem zadań, co może być szczególnie trudne do opanowania w przypadku korzystania z bibliotek które nie są "multithread safe", a większość bibliotek Arduino przecież nie jest multithread safe. Można się bawić w sekcje krytyczne czy inne mutexy, ale o ile swój kod w ten sposób zabezpieczysz, tak nad tym co się będzie działo wewnątrz bibliotek Arduino w kontekście tasków FreeRTOS nie masz już zbytniej kontroli.

W kontekście maszyny stanów mam na myśli np. coś takiego:

enum AppState { 
  STATE_READ_GPIO, 
  STATE_UPDATE_SERVER, 
  STATE_CHECK_WIFI 
};

void loop() {
  static AppState state = STATE_READ_GPIO;
  static uint32_t lastWifiCheck = 0;
  
  switch(state) {
    case STATE_READ_GPIO:
      door = digitalRead(PIN_DOOR_OPEN);
      button = digitalRead(PIN_BUTTON_BLUE);
      stop = digitalRead(PIN_BUTTON_STOP);
      state = STATE_UPDATE_SERVER;
      break;
      
    case STATE_UPDATE_SERVER:
      server.handleClient(); // Non-blocking!
      if(millis() - lastWifiCheck > 30000) state = STATE_CHECK_WIFI;
      break;
      
    case STATE_CHECK_WIFI:
      if(!connectWiFi()) { /* handle error */ }
      lastWifiCheck = millis();
      state = STATE_READ_GPIO;
      break;
  }
}

To jest oczywiście tylko przykład który ma pokazać pewną filozofię.

Ale jeśli już chcesz pozostać w świecie FreeRTOS, to nie rozbijaj obsługi bibliotek takich jak Wifi na wiele wątków, tylko obsługuj funkcjonalność per wątek, np:

// Tylko 2 taski zamiast 4!
xTaskCreatePinnedToCore(
  SafetyTask,    // Odczyt GPIO + logika bezpieczeństwa
  "Safety", 
  4096, 
  NULL, 
  3,  // WYSOKI priorytet
  NULL, 
  0
);

xTaskCreatePinnedToCore(
  NetworkTask,   // WiFi + HTTP
  "Network", 
  8192, 
  NULL, 
  2,  // ŚREDNI priorytet
  NULL, 
  1
);

W ten sposób unikniesz sytuacji że wątki obsługujące wifi będą się wzajemnie wywłaszczać/kolidować ze sobą.

51 minut temu, ethanak napisał:

Tylko że robi coś innego niż vTaskDelay i do czego innego służy, a przede wszystkim nie w takich sytuacjach.

Oczywiście że w takich sytuacjach - pętla obsługująca serverTask() aż się prosi o taskYELD(). I żeby nie było - nie twierdzę że vTaskDelay to absolutne zło, bo są projekty w których delaye są prostym, w pełni uzasadnionym i wystarczającym podejściem. Ale warto rozważyć alternatywy, które spowodują że kod staje się po prostu sprawniejszy. Po co siedzieć w pętli serwera i blokować czas CPU, który mógłby zostać wykorzystany na obsługę przychodzącego żądania?

void ServerTask(void* pvParameters) {
  while (true) {
    server.handleClient();
    taskYIELD(); // "Hej schedulerze, może ktoś inny chce procesor?"
  }
}

 

Edytowano przez jaszczurtd
  • Nie zgadzam się! 1
6 minut temu, jaszczurtd napisał:

Używaj millis() i konstrukcji warunkowych/licznikowych które polegają na sprawdzaniu czy upłynął już założony czas.

Nie skomentuję.

A różne guziki i inne buttony najlepiej zrobić na przerwaniach,

44 minuty temu, ethanak napisał:

Nie skomentuję.

Jak tam sobie uważasz. 

45 minut temu, ethanak napisał:

A różne guziki i inne buttony najlepiej zrobić na przerwaniach

W swoich programach możesz oczywiście robić co tylko uważasz za słuszne. 🙂 Możesz tam nawet jeszcze wstawić delay(), bo tak będzie przecież prościej 😄

  • Nie zgadzam się! 1

@jaszczurtd proponuję jednak, abyś swoje dobre rady zachował dla siebie. Na razie wprowadzasz kolegę w błąd.

A Twoja uwaga że mogę sobie stosować delay świadczy tylko o tym, że nie masz na ten temat zielonego pojęcia.

(edytowany)

ethanak, Takie górnolotne stwierdzenia jak to powyżej nie podnosi siły Twoich argumentów. Nie wystarczy powiedzieć że ktoś kogoś wprowadza w błąd, warto by było to jeszcze jakoś sensownie uzasadnić. Dodatkowo jak daje się zauważyć, nie zjarzyłeś oczywistej ironii z mojej strony (w kontekście używania delay w przerwaniach - to było podobnie trafne i dobre rozwiązanie jak Twoje ironiczne stwierdzenie o przerwaniach obsługujących przyciski). Ironia w dyskusji może być zabawna, ale tylko wtedy, gdy nie prowadzi do nieporozumień.

Przeanalizujmy jeszcze raz sytuację: mamy tutaj super prosty projekt, wykorzystujący FreeRTOS, ale z typowymi problemami wynikającymi z błędnych założeń początkowych, i niezrozumienia tego jak działa system wielozadaniowy czasu rzeczywistego, i po co się go stosuje. Przykład: connectWiFi() działa synchronicznie i blokuje task przez kilka sekund, a dane takie jak mac czy rssi są współdzielone między taskami bez synchronizacji – to klasyczny przykład braku zrozumienia, kiedy FreeRTOS daje korzyści, a kiedy jego użycie może stwarzać problemy. Nie wystarczy użyć volatile żeby wszystko działało poprawnie.

Ja ze swojej strony proponuję prostsze i czyste rozwiązanie, wykorzystujące nieblokujące funkcje (dzięki używaniu millis()), oraz prostą maszynę stanów do zarządzania logiką. W ten sposób od razu eliminuje się potencjalne problemy z wywłaszczaniem tasków kontrolujących wifi, co moim skromnym zdaniem jest tutaj jednym ze źródeł problemów zgłaszanych przez autora wątku. Jeśli uważasz że FreeRTOS w tym przypadku to ogólnie dobre podejście - gratulacje, ale wydaje mi się że nic w tym wątku bardziej nie wprowadzi w błąd autora niż takie właśnie podejście.

Kiedy millis() i sprawdzanie czasu który upłynął to zły pomysł, a FreeRTOS będzie lepszy?

Np. gdy masz więcej niż 3-4 niezależne „zadania”. Gdy potrzebujesz priorytetów zadań. Albo precyzyjnego timingu lub czasów reakcji. Gdy musisz synchronizować dane między "wątkami" (czyli funkcjami logicznie współbieżnymi). Który z tych powodów tutaj następuje? Trzeba promować efektywne podejście, zamiast strzelać z armat do muchy.

Poza tym, mamy tutaj jeszcze sporo innych problemów; pierwszy z brzegu: "bufor XML" ma rozmiar 4048 bajtów, ale nie ma sprawdzenia, czy strcat() nie wyjdzie poza zakres. Z jednej strony użycie strcat(), a chwilę potem String.replace(). To może lepiej to przepisać w całości na  strncat() / snprintf() albo używać konsekwentnie: String result = s1 + " " + s2; ?

 

 

Edytowano przez jaszczurtd
  • Lubię! 1

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...