Периодически поврежденные данные возвращаются из ESP8266.

У меня есть Arduino Uno, подключенный к ESp8266 через адаптер ESP-01. Адаптер питается от контактов Arduino 5 В и Gnd, а Tx/Rx к адаптеру использует контакты Arduino 2/3. У меня есть базовый код, который устанавливает ESP8266 в качестве точки доступа, и я могу подключиться к точке доступа со своего телефона, отправить сообщение и получить ответ от ESP8266. Это работает нормально в 75% случаев. в других случаях сообщение, полученное ESP8266 (и распечатанное на последовательном мониторе для отладки), усекается и/или повреждается. Я пробовал другой ESp8266 и другой адаптер; Я не получаю ошибок, связанных с повреждением, при использовании других программ, запущенных на Uno; Я снизил скорость связи между Uno и ESp8266 до 9600 бод. В чем может быть причина искажения данных, которое я вижу? Спасибо

Я обновил свое приложение, и данные больше не повреждены, но в возвращенном потоке данных отсутствуют символы.

Вот мой скетч:

#include <SoftwareSerial.h>

//
// Интерфейс ESP8266
/////////////////////

SoftwareSerial wifiSerial(2, 3);            // RX, TX для ESP8266

#define TICK  100

bool DEBUG = true;
int responseTime = 1000;

#define ESP8266CMD_SET_UART_9600            "AT+UART_CUR=9600,8,1,0,3"
#define ESP8266CMD_SET_UART_38400           "AT+UART_CUR=38400,8,1,0,0"

#define ESP8266CMD_ECHO_OFF                 "ATE0"
#define ESP8266CMD_ECHO_ON                  "ATE1"
#define ESP8266CMD_SET_WIFI_STATION_MODE    "AT+CWMODE=1"
#define ESP8266CMD_SET_WIFI_SOFTAP_MODE     "AT+CWMODE=2"
#define ESP8266CMD_GET_IP_ADDRESS           "AT+CIFSR"
#define ESP8266CMD_CONFIG_AP_LIST_ALL       "AT+CWLAPOPT=1,2047" // Сортировка и отображение всех сетей
#define ESP8266CMD_CONFIG_AP_LIST           "AT+CWLAPOPT=1,3"    // Сортировка и отображение только ECN/ESSID
#define ESP8266CMD_LIST_AVAILABLE_APS       "AT+CWLAP"

#define ESP8266CMD_SEND_DATA                "AT+CIPSEND=0,"     // требуемая длина добавляемых данных
#define ESP8266CMD_CLOSE_ALL_CONNECTIONS    "AT+CIPCLOSE=5"

//
// ЖК-интерфейс
////////////////

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR                            0x27
#define BACKLIGHT_PIN                       3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

LiquidCrystal_I2C  lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

//
// НАСТРАИВАТЬ
////////////

void setup()
{
  Serial.begin(115200);
  Serial.println("Initialising...");

  lcd.begin( 16, 2 );                                 // ЖК-дисплей 16x2

  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);        // Включаем подсветку
  lcd.setBacklight(HIGH);

  lcd.home();
  lcd.print("Initialising...");

  wifiSerial.begin(9600);

  sendToWifi( ESP8266CMD_ECHO_OFF, responseTime, DEBUG );
  sendToWifi( ESP8266CMD_SET_WIFI_STATION_MODE, responseTime, DEBUG );

  sendToWifi( ESP8266CMD_CONFIG_AP_LIST, responseTime, DEBUG );

  Serial.println("Wifi connection is running!");

  lcd.clear();
  lcd.print("Ready");

  pinMode(13,OUTPUT);  //устанавливаем встроенный светодиод в качестве вывода
}

//
// ПЕТЛЯ
//////////

void loop()
{
String APList = sendToWifi(ESP8266CMD_LIST_AVAILABLE_APS, responseTime, DEBUG);

  Serial.println(APList);

  delay(100 * TICK);
}



/*
* Name:         sendToWifi
* Description:  Send data to ESP8266.
* Params:       command - the data/command to send 
*               timeout - the time to wait for a response
*               debug - print to Serial window?(true = yes, false = no)
* Returns:      Response from the esp8266 (if there is a reponse)
*/
String sendToWifi(String command, const int timeout, boolean debug){
 String response = "";

 if(debug)
    Serial.println( "Command[" + command + "]" );

 wifiSerial.println(command);       // отправляем строку esp8266

 long int time = millis();
 while( (time+timeout) > millis())
 {
   while(wifiSerial.available())
   {
   char c = wifiSerial.read();      // читаем ответ

    response += c;
    time = millis();                // данные получены - сброс таймаута
   }  
 }
 if(debug)
    Serial.println( "Response[" + response + "]" );

 return response;
}

..а вот выходные данные отладки из примера.

Initialising...
Command[ATE0]
Response[
OK
]
Command[AT+CWMODE=1]
Response[
OK
]
Command[AT+CWLAPOPT=1,3]
Response[
OK
]
Wifi connection is running!
Command[AT+CWLAP]
Response[]

Command[AT+CWLAP]
Response[+CWLAP:(4,"VM2293456_EXT")
+CWLAP:(3,"NVR9ca3a92708f2")
+CWLA]
+CWLAP:(4,"VM2293456_EXT")
+CWLAP:(3,"NVR9ca3a92708f2")
+CWLA
Command[AT+CWLAP]
Response[+CWLAP:(4,"VM2293456_EXT")
+CWLAP:(4,"VM2293456")
+CWLAP:(5,"]
+CWLAP:(4,"VM2293456_EXT")
+CWLAP:(4,"VM2293456")
+CWLAP:(5,"

Помимо двух сетей VM2293456 и VM2993456_EXT существует ряд других доступных сетей, и при некоторых запусках я вижу именно эти сети. В любом случае в тексте ответа пропущены символы.

, 👍0

Обсуждение

у тебя АТ прошивка в esp-01? Какова длина сообщения и как его прочитать? покажи свой эскиз., @Juraj

Спасибо, что вернулись ко мне. У меня есть прошивка в esp, и я рад поделиться своим эскизом, но, как новичок, я не вижу, как поделиться кодом., @MPH

Я добавил к своему вопросу эскиз и образец выходных данных. Спасибо, @MPH

ESP-01 — это устройство с напряжением 3,3 В., @JRobert

Спасибо, Роберт. Я полагаю, что адаптер, который я использую, понижает напряжение с 5 В до 3,3 В., @MPH


2 ответа


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

0

Прошивка AT записывает данные в последовательный порт без какого-либо контроля. Если данные не считываются быстро, последовательный буфер переполняется. Если вы подсчитаете байты, прочитанные вашим скетчем (включая 2 невидимых символа конца строки в каждой строке), вы посчитаете 64 байта. Это размер буфера RX SoftwareSerial. SoftwareSerial может сообщить вам, если буфер переполнен. Вызовите wifiSerial.overflow().

CWLAP требуется время для выполнения на стороне esp8266, а затем прошивка AT передает полный список на последовательный порт. Чтение его побайтно и сохранение millis() между ними будет слишком медленным. Вам нужно прочитать его быстрее с буфером.

Я использую readBytesUntil для чтения строки в буфер. Эта функция ожидает следующего байта, если между байтами есть пробел, и прекращает чтение, если получен терминатор или истекло время ожидания. Тайм-аут по умолчанию составляет 1000 миллис. Его можно установить с помощью setTimeout(t).

В вашем скетче вы используете String. Не рекомендую для проекта, но для теста можно использовать:

result += wifiSerial.readStringUntil('\n');

чтобы прочитать и добавить целую строку.

,

Спасибо, это имеет большой смысл — я изменю свой код и попробую еще раз. Ваше здоровье, @MPH

Итак, теперь я изменил свой фонд получения следующим образом: `void readFromWiFi() { int l, j = 0; делать { l = wifiSerial.readBytesUntil('\0', &Buffer[j], BUFFER_LEN); j += л; Буфер[j] = '\0'; } Пока (л > 0); } ` .. но я все еще получаю только 64 символа данных. Обратите внимание: я использую «SoftwareSerial», и в этом случае я не понимаю, почему мне доступна функция readBytesUntil(). Где я ошибаюсь?, @MPH

AT-прошивка никогда не отправляет \0. строка заканчивается на \r\n. и читать в буфер readBytesUntil('\n', &Buffer, BUFFER_LEN) https://arduinoprosto.ru/q/63106/how-to-compare-a-string/63116#63116, @Juraj

@Juraj: вам нужно будет добавить '\n' вручную - result += wifiSerial.readStringUntil('\n') + "\n";, @SBF

@SBF, нам нужны только данные, @Juraj


1

Используя информацию, любезно предоставленную мне людьми, я переписал свой код для использования библиотеки AltSoftSerial, и теперь мое небольшое приложение работает нормально. Спасибо. AltSoftSerial использует контакты 8/9 в режиме прерываний, оставляя «стандартные» контакты 1/2 последовательной связи для канала отладки. У меня он работает на скорости 19200 бод. Я удалил переменную String из кода, и моим последним улучшением будет использование malloc() для выделения буфера данных вместо текущего статически объявленного буфера. Цель моего тестового приложения — подключиться к моей домашней сети Wi-Fi и распечатать выделенный IP-адрес. Весь вывод поступает на ЖК-дисплей, но вместо него можно использовать последовательный монитор.

Вот мой код:

#include <AltSoftSerial.h>
#include "ESP8266_cmd.h"

//
// Подключаемся к домашней сети Wi-Fi с помощью AltSoftSerial через контакты 8/9 к ESP8266.
/////////////////////////////////////////////////// /////////////////////////


//
// Интерфейс ESP8266
/////////////////////

AltSoftSerial wifiSerial;                   // RX, TX для ESP8266

#define TICK  100

#define DEBUG                               false
#define ResponseTime                        1000

#define NW_CREDENTIALS                      "\"VM2293456\",\"----------\""

//
// ЖК-дисплей определяет
//////////////

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR          0x27
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

int n = 1;

LiquidCrystal_I2C  lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);


//
// Буфер для данных ESP8266
//////////////////////////

#define BUFFER_LEN    512
char Buffer[BUFFER_LEN];

//
// Состояние////////////////

enum {
 STATE_INIT_WIFI = 0,
 STATE_WIFI_CONNECT,
 STATE_WAIT_FOR_CONNECTED,
 STATE_GET_IP_ADDRESS,
 STATE_WAIT,

 STATE_ERROR,
} State;

int MachineState = STATE_ERROR;
int ErrorCode = 0;


void setup()
{
String response;

  Serial.begin(115200);
  Serial.println("Initialising...");

  lcd.begin( 16, 2 );                                 // ЖК-дисплей имеет размер 16x2. Cmd — столбец, строка

  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);        // Включаем подсветку
  lcd.setBacklight(HIGH);

  lcd.clear();
  lcd.home();
  lcd.print( "Network Connect " );

  wifiSerial.begin(19200);

  MachineState = STATE_INIT_WIFI;
}


void loop()
{
String response;
char cmd[64];

  switch( MachineState )
  {
  case STATE_INIT_WIFI:
    lcd.setCursor( 0, 1);
    lcd.print("Initialising    ");

    sendToWifi( ESP8266CMD_ECHO_OFF, ResponseTime, DEBUG );
    if( strcmp( Buffer, "OK" ) != 0 )
    {
      ErrorCode = 1;
      MachineState = STATE_ERROR;
      break;
    }

    sendToWifi( ESP8266CMD_SET_WIFI_STATION_MODE, ResponseTime, DEBUG );
    if( strcmp( Buffer, "OK" ) != 0 )
    {
      ErrorCode = 2;
      MachineState = STATE_ERROR;
      break;
    }
    MachineState = STATE_WIFI_CONNECT;
    break;  

  case STATE_WIFI_CONNECT:
    lcd.setCursor( 0, 1);
    lcd.print("Connecting           ");

    strcpy( cmd, ESP8266CMD_CONNECT_TO_AP );
    strcat( cmd, NW_CREDENTIALS );
    sendToWifi( cmd, ResponseTime, DEBUG );
    MachineState = STATE_WAIT_FOR_CONNECTED;
    break;

  case STATE_WAIT_FOR_CONNECTED:
    lcd.setCursor( 0, 1);
    lcd.print("Wait for connect");
    readFromWiFi( ResponseTime );
    if( strstr(Buffer, "WIFI GOT IP") != NULL )
       MachineState = STATE_GET_IP_ADDRESS;
    else
       delay( 1000 );
    break;

  case STATE_GET_IP_ADDRESS:
    lcd.setCursor( 0, 1);
    lcd.print("Getting IP Addr ");

    sendToWifi(ESP8266CMD_GET_IP_ADDRESS,ResponseTime,DEBUG);

    MachineState = STATE_ERROR;   // премьер для случая ошибки
    ErrorCode = 3;

    if( strstr( Buffer, "OK" ) != NULL )
    {
      if( strncmp( Buffer, "+CIFSR:STAIP", 12 ) == 0 )
      {
        lcd.setCursor( 0, 1 );
        lcd.print( "                " );
        lcd.setCursor( 0, 1 );
        for( int i = 15; i < strlen(Buffer ); i++ )
        {
          if( Buffer[i] == '"' )
            Buffer[i] = '\0';
        }
        lcd.print( &Buffer[14] );   // Отображение извлеченного IP-адреса
        MachineState = STATE_WAIT;
      }
    }
    break;

  case STATE_WAIT:
    delay(10000);
    MachineState = STATE_GET_IP_ADDRESS;
    break;

  case STATE_ERROR:
  default:
    lcd.setCursor( 0, 1 );
    lcd.print( "                " );
    lcd.setCursor( 0, 1 );
    strcpy( cmd, "Err " );
    itoa( ErrorCode, &cmd[strlen(cmd)], 10 );
    lcd.print( cmd );
    delay(10000);
    break;
  } 
}


/*
* Name:         readFromWiFi
* Description:  Read data from ESP8266.
* Params:       timeout - the time to wait for a response
*               debug - print to Serial window?(true = yes, false = no)
* Returns:      Response from the esp8266 (if there is a reponse) in Global Buffer
*/
void readFromWiFi(int timeout)
{
int l = 0;
long int t = millis();

    while( ( t + timeout ) > millis() ){
      while( wifiSerial.available() > 0 ) {
        Buffer[l++] = wifiSerial.read();
        t = millis();
       }
    }

    Buffer[l] = '\0';

    trimRight( Buffer );
    trimLeft( Buffer );
}



/*
* Name: sendToWifi
* Description: Function used to send data to ESP8266.
* Params: command - the data/command to send; timeout - the time to wait for a response; debug - print to Serial window?(true = yes, false = no)
* Returns: The response from the esp8266 (if there is a reponse)
*/
void sendToWifi( const char *command, const int timeout, boolean debug){

 if(debug)
 {
    Serial.print( "Command[" );
    Serial.print( command );
    Serial.println( "]" );
 }

 wifiSerial.println( command );       // отправляем строку esp8266

 readFromWiFi( timeout);

 if(debug)
  {
    Serial.print( "Response[" );
    Serial.print( Buffer );
    Serial.println( "]" );
  }
}

void trimRight( char *src )
{
  int i = strlen(src)-1;

  for( ; i >= 0; i-- )
  {
    if( (src[i] != ' ' ) && ( src[i] != '\t' ) && ( src[i] != '\n' ) )
      break;
  }  
  src[i] = '\0';
}

void trimLeft( char *src )
{
  int i, l = strlen(src);

  for( i = 0; i < l; i++ )
  {
    if( (src[i] != ' ' ) && ( src[i] != '\t' ) && ( src[i] != '\n' ) && ( src[i] != '\r' ) )
      break;
  }  
  memmove( src, &src[i], l); 
}

а вот заголовочный файл ESP822_cmd.h, который я создал:

// ESP82266_cmd.h
//
// #defines для интерфейса ESP8266
//


#define ESP8266CMD_SET_UART_9600            "AT+UART_DEF=9600,8,1,0,0"
#define ESP8266CMD_SET_UART_19200           "AT+UART_DEF=19200,8,1,0,0"
#define ESP8266CMD_SET_UART_38400           "AT+UART_CUR=38400,8,1,0,0"

#define ESP8266CMD_ECHO_OFF                 "ATE0"
#define ESP8266CMD_ECHO_ON                  "ATE1"
#define ESP8266CMD_SET_WIFI_STATION_MODE    "AT+CWMODE=1"
#define ESP8266CMD_SET_WIFI_SOFTAP_MODE     "AT+CWMODE=2"
#define ESP8266CMD_GET_IP_ADDRESS           "AT+CIFSR"
#define ESP8266CMD_CONFIG_AP_LIST_ALL       "AT+CWLAPOPT=1,2047" // Сортировка и отображение всех сетей
#define ESP8266CMD_CONFIG_AP_LIST           "AT+CWLAPOPT=1,3"    // Сортировка и отображение только ECN/ESSID
#define ESP8266CMD_LIST_AVAILABLE_APS       "AT+CWLAP"

#define ESP8266CMD_SEND_DATA                "AT+CIPSEND=0,"      // требуемая длина добавляемых данных
#define ESP8266CMD_CLOSE_ALL_CONNECTIONS    "AT+CIPCLOSE=5"

#define ESP8266CMD_CONNECT_TO_AP            "AT+CWJAP_CUR="      // требуется добавить essid и пароль
```

,