GET-запрос Arduino ESP8266 Wifi Shield с LiteESP8266Client останавливается после нескольких HTTP-запросов

Ниже HTTP-запрос Get работает, но после нескольких запросов GET (5-6) код останавливается, и как только я нажимаю кнопку сброса Arduino, только он начинает работать. Вероятно, похоже, что что-то вызывает OOM:

// Это демонстрационный пример использования общедоступного http-сервера для тестирования запросов GET и POST.

#include <Arduino.h>
#include <LiteESP8266Client.h>

#define PACKET_MTU 1500    // Стандартный MTU сети составляет 1500 байт.

LiteESP8266 radio;

const char ssid[] PROGMEM = "abcd";    // измените его на SSID вашего Wi-Fi
const char password[] PROGMEM = "abcd";    // измените его на ваш пароль от Wi-Fi
const char host[] PROGMEM = "192.168.0.156";
const int port = 8080;

const char http_get_request[] PROGMEM = "GET /getLedStatus HTTP/1.1\r\n";
const char http_useragent[] PROGMEM = "User-Agent: Arduino-stm32/0.1\r\n";
const char http_content_type_json[] PROGMEM = "Content-Type: application/json\r\n";
const char http_host[] PROGMEM = "Host: 192.168.0.156\r\n";
const char http_close_connection[] PROGMEM = "Connection: close\r\n\r\n";
const char http_content_length_header[] PROGMEM = "Content-Length: ";
const char success[] PROGMEM = "success";
const char failed[] PROGMEM = "failed";
const char CRLF[] PROGMEM = "\r\n";
const char error_data_null[] PROGMEM = "Error: data came back null.";
const int ledPin=11;
void setupStationMode() {
  Serial.print("Setup station mode... ");
  if (radio.set_station_mode()) {
    Serial.println("success");
  } else {
    Serial.println("failed");
  }
}

void joinAP() {
  Serial.print("Join AP ");
  Serial.print("... ");
  if (radio.connect_to_ap(ssid, password)) {
    Serial.println("Success");
  } else {
    Serial.println("Failed");
  }
}

void establishTcpConnect() {
  Serial.print("Establish TCP Connection... ");
  if (radio.connect_progmem(host, port)) {
    Serial.println( "Success");
  } else {
    Serial.println( "Failed");
  }
}

void getHttpPacket() {
  char *data;
  while ((data = radio.get_response_packet(PACKET_MTU, 5000))) {
    if (data) {
      Serial.println("Response Received...");
      Serial.println(data);
      if(strstr(data, "ON") != NULL) {
        digitalWrite(ledPin, HIGH);
      }else{
        digitalWrite(ledPin, LOW);
      }
    } else {
      Serial.println(error_data_null);
    }
  }
  free(data);
}

void httpGet() {
  Serial.println("Sending GET request... ");
  radio.send_progmem(http_get_request);
  radio.send_progmem(http_useragent);
  radio.send_progmem(http_host);
  radio.send_progmem(http_close_connection);
}
void setup() {
  delay(2000);
  pinMode(ledPin, OUTPUT);
  radio.begin(9600,2,3);
  Serial.begin(9600);
  while (!Serial) {};

  setupStationMode();
  joinAP();
}

void loop() {
  establishTcpConnect();

  httpGet();
  getHttpPacket();
  }

Завершить ВЫВОД (см. Последний вызов TCP, когда он был остановлен):

Setup station mode... success
Join AP ... Success
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 3
Date: Fri, 07 Aug 2020 13:47:47 GMT
Connection: close

OFF
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 3
Date: Fri, 07 Aug 2020 13:47:53 GMT
Connection: close

OFF
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 3
Date: Fri, 07 Aug 2020 13:47:58 GMT
Connection: close

OFF
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 3
Date: Fri, 07 Aug 2020 13:48:04 GMT
Connection: close

OFF
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 3
Date: Fri, 07 Aug 2020 13:48:10 GMT
Connection: close

OFF
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 2
Date: Fri, 07 Aug 2020 13:48:16 GMT
Connection: close

ON
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 2
Date: Fri, 07 Aug 2020 13:48:21 GMT
Connection: close

ON
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 2
Date: Fri, 07 Aug 2020 13:48:27 GMT
Connection: close

ON
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 2
Date: Fri, 07 Aug 2020 13:48:33 GMT
Connection: close

ON
Establish TCP Connection... Success
Sending GET request... 
Establish TCP Connection...

Возможно, память не освобождается. Я использую плату Arduino UNO + ESP8266

Обновление 2 Пробовал с другим сервером REST, и наблюдается та же проблема:

// Это демонстрационный пример использования общедоступного http-сервера для тестирования запросов GET и POST.

#include <Arduino.h>
#include <LiteESP8266Client.h>

#define PACKET_MTU 1500    // Стандартный MTU сети составляет 1500 байт.

LiteESP8266 radio;

const char ssid[] PROGMEM = "abc";    // измените его на SSID вашего Wi-Fi
const char password[] PROGMEM = "abc";    // измените его на ваш пароль от Wi-Fi
const char host[] PROGMEM = "httpbin.org";
const int port = 80;

const char http_get_request[] PROGMEM = "GET /get HTTP/1.1\r\n";
const char http_useragent[] PROGMEM = "User-Agent: Arduino-stm32/0.1\r\n";
const char http_content_type_json[] PROGMEM = "Content-Type: application/json\r\n";
const char http_host[] PROGMEM = "Host: httpbin.org\r\n";
const char http_close_connection[] PROGMEM = "Connection: close\r\n\r\n";
const char http_content_length_header[] PROGMEM = "Content-Length: ";
const char success[] PROGMEM = "success";
const char failed[] PROGMEM = "failed";
const char CRLF[] PROGMEM = "\r\n";
const char error_data_null[] PROGMEM = "Error: data came back null.";
const int ledPin=11;
void setupStationMode() {
  Serial.print("Setup station mode... ");
  if (radio.set_station_mode()) {
    Serial.println("success");
  } else {
    Serial.println("failed");
  }
}

void joinAP() {
  Serial.print("Join AP ");
  Serial.print("... ");
  if (radio.connect_to_ap(ssid, password)) {
    Serial.println("Success");
  } else {
    Serial.println("Failed");
  }
}

void establishTcpConnect() {
  Serial.print("Establish TCP Connection... ");
  if (radio.connect_progmem(host, port)) {
    Serial.println( "Success");
  } else {
    Serial.println( "Failed");
  }
}

void getHttpPacket() {
  char *data;
  while ((data = radio.get_response_packet(PACKET_MTU, 5000))) {
    if (data) {
      Serial.println("Response Received...");
      Serial.println(data);
      if(strstr(data, "ON") != NULL) {
        digitalWrite(ledPin, HIGH);
      }else{
        digitalWrite(ledPin, LOW);
      }
    } else {
      Serial.println(error_data_null);
    }
  }
  free(data);
}

void httpGet() {
  Serial.println("Sending GET request... ");
  radio.send_progmem(http_get_request);
  radio.send_progmem(http_useragent);
  radio.send_progmem(http_host);
  radio.send_progmem(http_close_connection);
}
void setup() {
  delay(2000);
  pinMode(ledPin, OUTPUT);
  radio.begin(9600,2,3);
  Serial.begin(9600);
  while (!Serial) {};

  setupStationMode();
  joinAP();
}

void loop() {
  establishTcpConnect();

  httpGet();
  getHttpPacket();
  }

Вывод:

Setup station mode... success
Join AP ... Success
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 OK
Date: Fri, 07 Aug 2020 13:57:45 GMT
Content-Type: application/json
Content-Length: 238
Connection: close
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true


Response Received...
{
  "args": {}, 
  "headers": {
    "Host": "httpbin"origin": "106.51.29.214", 
  "url": "http://httpbin.org/get"
}
CLOSED
⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮
Establish TCP Connection... Success
Sending GET request... 
Response Received...
HTTP/1.1 200 OK
Date: Fri, 07 Aug 2020 13:57:57 GMT
Content-Type: application/json
Content-Length: 238
Connection: close
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true


Response Received...
{
  "args": {}, 
  "headers": {
    "Host": "httpbin"origin": "106.51.29.214", 
  "url": "http://httpbin.org/get"
}
CLOSED
⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮
Establish TCP Connection... Success
Sending GET request... 
Establish TCP Connection...

Код на стороне сервера

package com.test.iot;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    private static final Logger logger = LoggerFactory.getLogger(HelloController.class);
    private String ledStatus="OFF";
    @RequestMapping("/test")
    public String index() {
        logger.info("invoked");
        return "Greetings from Spring Boot!";
    }
    @RequestMapping("/getLedStatus")
    public String getLedStatus() {
        logger.info("ledStatus");
        return ledStatus;
    }
    @RequestMapping("/toggleLedStatus")
    public String toggleLedStatus(){
        if(ledStatus.equals("ON")){
            ledStatus="OFF";
        } else {
            ledStatus="ON";
        }
        return String.format("New LED Status: %s", ledStatus);
    }
}

, 👍1

Обсуждение

Какой код вашего сервера? Причина может быть на стороне сервера, а не на стороне клиента. Также вы запускаете клиент, используя общедоступный тестовый сервер, такой как httpbin.org, как в [примере](https://github.com/e-tinkers/LiteESP8266Client/blob/master/examples/webclient/webclient.ino ), чтобы узнать, столкнулись ли вы с той же проблемой?, @hcheung

Да, такая же проблема наблюдается в примере. Ответ прикреплен здесь, @Rajesh

Код на стороне сервера прилагается, @Rajesh

Если вы столкнулись с одной и той же проблемой на обоих серверах, значит что-то не так с клиентом, я завтра посмотрю, так как мне нужно перепрошить свой esp-01, чтобы сделать тест., @hcheung

Не могли бы вы добавить задержку в цикле, чтобы увидеть, есть ли разница?, @hcheung

Сделаю это для проверки и дам вам знать, но я сомневаюсь, что это поможет. Пожалуйста, прошейте ESP01 и помогите с исправлением, если это необходимо. Спасибо, @Rajesh

забавная библиотека. он использует malloc с размером ответа. он быстро фрагментирует кучу на 5 циклов. Кстати: вы позволяете ему выделять 5000 байт на MCU с 2 КБ ОЗУ, @Juraj

Можно ли как-то исправить, например, читать построчно и отбрасывать ненужные вещи ?? Любой фрагмент кода будет полезен для меня. Спасибо, @Rajesh

Я могу только порекомендовать мне библиотеку WiFiEspAT. но вам придется прошить более новую прошивку AT для esp8266 https://github.com/jandrassy/WiFiEspAT/blob/master/README.md, @Juraj

@Juraj, 5000 — это время ожидания в мс, а не выделение памяти., @hcheung

@hcheung есть идеи, можно ли это исправить в ближайшее время? Юрай, чем этот новый подход отличается от старого? Есть ли смысл прошивать новую прошивку?, @Rajesh

Я просто хочу проанализировать ответ, размер моего ответа также будет очень маленьким, например, 1-2 слова. Я довольно давно менял подходы, и чтобы заставить его работать до тех пор, пока этот шаг не был сложным, мне пришлось настроить скорость передачи данных, чтобы заставить его работать. Если вы можете предоставить дополнительную информацию о новой прошивке и о том, чем она отличается от предыдущей, это будет полезно., @Rajesh


1 ответ


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

2

В примере библиотеки webclient.ino в getHttpPacket() и getHttpResponse() обнаружена ошибка, из-за которой память не освобождалась правильно, что приводило к утечке памяти. Я обновил код в репозитории github.

В частности, для вашего getHttpPacket(), вот исправленный код:

void getHttpPacket() {
  char *data;
  while ((data = radio.get_response_packet(PACKET_MTU, 5000))) {
    if (data) {
      Serial.println(F("Response Received..."));
      Serial.println(data);
      if(strstr(data, "ON") != NULL) {
        digitalWrite(ledPin, HIGH);
      }else{
        digitalWrite(ledPin, LOW);
      }
      free(data);  // освобождаем память в каждом пакете, т.к. у Arduino недостаточно памяти
    } else {
      Serial.println(error_data_null);
    }
  }
  // бесплатно (данные);
}

Если у вас есть дальнейшие вопросы относительно библиотеки, вы можете поднять "проблему" на гитхабе, а я посмотрю.

,

Удивительно, я возьму последний код с github, @Rajesh

Кстати, поскольку я в основном использую stm32 для своих работ, я могу позволить себе выделить память, равную одному пакетному MTU (т.е. 1500 байт), если вы используете Arduino, это оставит очень мало памяти для вашего скетча, хотя LiteESP8266Client Библиотека предназначена для использования очень небольшого количества SRAM, если вы обнаружите, что вам нужно больше памяти, вы можете переопределить PACKET_MTU на меньшее значение. Также лучше обернуть строковый литерал макросом F(), чтобы сохранить его во флэш-памяти., @hcheung

Большое спасибо @hcheung. Работает как шарм .. пока .. коснитесь дерева, @Rajesh