WifiClient и aREST на esp32 – цикл до получения новых инструкций?

esp32 wifi led-strip cpp

Я создал простой скетч для esp32, на котором размещен REST API, доступ к которому можно получить через HTTP. На основании инструкций, полученных от клиента, esp32 управляет некоторыми неопикселями с помощью различных написанных мной анимаций.

Все работало так, как ожидалось, пока я не добрался до анимации, которая выполняется в цикле while. Цикл разрывается при обнаружении нового клиента, чтобы светильник мог переключать функции. Однако, когда обнаруживается новый клиент, клиент, кажется, отбрасывается или потребляется, и я просто возвращаюсь к функции цикла(), пока не сделаю второй HTTP-запрос, а затем отобразятся изменения.

Я уверен, это что-то очевидное, но что я сделал не так?

Код:

#include "WiFi.h"
#include "FastLED.h"
#include "aREST.h"
#include "ArduinoJson.h"

#define NUM_LEDS 64
#define DATA_PIN 18

CRGB leds[NUM_LEDS];
int LED_COUNT = 64;
int MAX_BRIGHTNESS = 200;

const char* ssid = "wifi";
const char* password = "password";
aREST rest = aREST();
WiFiServer server(80);
WiFiClient client;

void setup() {
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, LED_COUNT);
  flashAll(CRGB::Black);
  // Привязываем URL-адреса к функциям обработки
  rest.function("random-strobe", randomStrobe);
  rest.function("solid-color", solidColor);
  rest.function("reset", reset);

  // Подключаемся к Wi-Fi и ждем подключения, чтобы продолжить
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    flashAll(CRGB::Cyan);
    delay(500);
    flashAll(CRGB::Black);
    delay(500);
  }
  server.begin();
  flashAll(CRGB::Green);
  delay(2000);
  flashAll(CRGB::Black);
}

void loop() {
  if (checkForTask()) {
    rest.handle(client);
  }
}

boolean checkForTask() {
  // Я попробовал сделать это переназначение условным
  // когда глобальный клиент имеет значение false, но это не помогло
  //if (!client) { client = server.available(); }
  client = server.available(); 
  if (client) {
    return true;
  }
  return false;
}

int randomStrobe(String params){
  client.println("HTTP/1.1 200 OK");
  client.println("Content-type:application/json");
  client.println();
  client.stop();
  StaticJsonDocument<200> jsonDoc;
  Serial.println(params);
  Serial.println("params recieved. parsing...");
  auto error = deserializeJson(jsonDoc, params);
  if (!error) {
    // некоторые настройки
    while(!checkForTask()){
      // делаем что-то здесь
    }
    return 0;
  }
}

, 👍0

Обсуждение

«Я пытался сделать это переназначение условным», как?, @Jaromanda X

@JaromandaX Что-то вроде: if (client) {...} else { client = server.available(); if (client) {...} - я не смог найти хорошей документации по проверке клиента без этой части переназначения, но я уверен, что есть лучший способ.., @shadowkrazee

@Юрай, правда, я не понимаю, чем это отличается от того, что есть у меня. не могли бы вы уточнить?, @shadowkrazee


2 ответа


Лучший ответ:

1

Хорошо, мне удалось решить проблему благодаря общему указанию @juraj.

Теперь у меня есть ДВА экземпляра WifiClient:

WiFiClient client;
WiFiClient newClient;

Это позволяет мне использовать экземпляр newClient для проверки наличия нового клиента:

boolean checkForTask() {
  newClient = server.available();
  if (newClient) {
    Serial.println("new client detected.");
    return true;
  }
  return false;
}

и переназначать экземпляр основного клиента только тогда, когда действительно обнаружен новый клиент:

void loop() {
  if (!newClient.connected()) {
    checkForTask();     
  } else {
    client = newClient;
    runTask();  
  }
}

Похоже, что это работает без каких-либо заметных побочных эффектов или дополнительного использования ресурсов, но если есть более эффективный способ добиться этого, поделитесь. Спасибо!

,

В своем ответе я показываю более простое решение, @Juraj


1

server.available() не возвращает уже «заявленное» соединение снова. Он возвращает новое соединение или пустой объект WiFiClient.

Ядро WiFi-библиотеки Arduino esp8266 и esp32 закрывает базовое соединение, если освобождается последний ссылающийся экземпляр WiFiClient. Присвоение пустого объекта WiFiClient освобождает предыдущую ссылку на базовый сокет, и если это была последняя ссылка, библиотека закрывает сокет.

Вы получаете клиент с server.available() в checkForTask(), а затем снова вызываете server.available(). Он возвращает пустой объект WiFiClient, и вы назначаете его своему объекту WiFiClient. Библиотека закрывает указанное соединение.

Решение состоит в том, чтобы не проверять server.available до тех пор, пока WiFiClient не станет действительным.

if (!client) {
  client = server.available();
}
,

Спасибо за помощь. Как я могу проверить наличие нового клиента для выхода из цикла, а затем выполнить инструкции клиента? Как должен выглядеть метод checkForTask()? (Я подозреваю, что условие «если» просто нужно пересмотреть, хотя вы должны отметить, что клиент объявлен глобально и должен быть «уничтожен» только путем переназначения, если я правильно понимаю.) У меня больше опыта работы с другими языки, но мой C++ ограничен Arduino. извините, если я упускаю что-то очевидное., @shadowkrazee

@shadowkrazee, я отредактировал ответ. Я не понимаю, почему я не увидел, что «клиент» является глобальным. Я решил все подобные ловушки с сетевыми библиотеками в своем проекте с сервером Telnet и веб-сервером в библиотеках Wi-Fi и Ethernet https://github.com/jandrassy/Regulator/blob/8262f28b7452df0b75aa6c5a7bc676e14cd0cd26/Regulator/Telnet.ino#L23, @Juraj