Основные операции с Wi-Fi-модулем ESP8266 от Arduino
Cuando Эспрессиф выпустила первые модули на рынок Wi-Fi с интегрированным ESP8266 y el прошивки с помощью которого можно было бы обрабатывать его с помощью AT-команд, нас, пользователей, интересовала интеграция его в сборки с микроконтроллеры и проблемы сводились к знанию (ранее) темного Таблица AT-команд ESP8266потребности в кормлении или Обновление прошивки ESP8266.
Затем быстро появились альтернативы для программирования ESP8266 и реализации модулей Wi-Fi очень разных форматов, что вызвало другие опасения: какой Wi-Fi модуль ESP8266 выбрать в зависимости от радиуса действия различных антенн (в том числе внешних) или физической интеграции этих новых модулей в наши сборки.
Конечно, из-за всех этих изменений акцент, возможно, не был сделан на самых основных аспектах, самом базовом управлении Модуль Wi-Fi ESP8266. Хотя полярность.es Вы можете найти информацию об использовании ESP8266 и есть некоторые приложения, предназначенные для общего объяснения работы Модуль Wi-Fi ESP8266 с помощью AT-команд, особенно в статье о библиотека для выполнения HTTP-запросов от Arduino с помощью Wi-Fi-модуля ESP8266., впечатления читателей подсказывают, что было бы полезно добавить еще немного базовой информации, чтобы помочь пользователям ESP8266 осуществлять свои собственные реализации.
Обсудим основные операции по работе с ESP8266 и предложение общих решений является целью нескольких совершенно разных частей; Чтобы лучше понять содержание статьи, в качестве руководства может служить следующий указатель:
- Управляйте модулем Wi-Fi ESP8266 с компьютера через последовательный порт.
- Обновить прошивку с помощью esptool
- Отправка заказов в модуль
- Получить данные от ESP8266
- Проанализируйте ответ, выполнив поиск по тексту в контенте.
- Ограничьте время ожидания получения ответа
- Выполнить сложную операцию, определенную несколькими AT-командами.
Управляйте модулем Wi-Fi ESP8266 с компьютера через последовательный порт.
Из тарелки Arduino и используя свой IDE есть возможность контролировать работу Модуль Wi-Fi ESP8266, Отправить ESP8266 AT-команды и посмотреть ответ но гораздо удобнее это сделать с компьютера с приложением терминального типа.
В зависимости от того, какая доска Arduino При использовании может быть доступен только один аппаратный последовательный порт, что добавляет немного неудобств при отправке и получении. Изменение скорости связи гораздо удобнее в приложении последовательной связи с компьютера и некоторых материнских плат. Arduino (и в некоторых случаях) плохо поддерживают самые высокие скорости последовательной связи, особенно 115200 бод, что является скоростью по умолчанию в последних версиях прошивки.
О нас Какую программу использовать для мониторинга ESP8266 используя последовательный порт, есть из чего выбирать в зависимости от потребностей и предпочтений; в последнее время я больше использую классику МилыйКом (тот, что на скриншоте выше), потому что мне очень удобно повторять определенные Модуль Wi-Fi ESP8266 при заказе в тестировании проекта.
Здесь уже были даны некоторые рекомендации по программам, работающим как последовательная консоль; Например, когда речь идет о PuTTY для управления последовательными устройствами UART с компьютера. PuTTYЭто не только отличное приложение, но и доступное для большинства настольных операционных систем. Кроме того, как PuTTY может использоваться в качестве консоли как с последовательным портом, так и с Семейство интернет-протоколов (TCP/IP), в том числе те, которые работают на TLS, становится распространенным инструментом, который с лихвой окупает (небольшое) время, потраченное на его настройку и привыкание к его использованию.
В дополнение к программному обеспечению последовательной связи, подключить Модуль Wi-Fi ESP8266 к порту USB Для компьютера также требуется конвертер USB серия TTL. Как и в случае с ПО, существует несколько версий, из которых используется только конвертация порта USB на последовательном порту TTL (которые можно получить за один евро) до тех, которые могут эмулировать различные протоколы (например, SPI o I2C).
Подобно программе, которая функционирует как последовательная консоль, аппаратное обеспечение для связи с компьютером через USB с логической схемой (не только ESP8266) будет обычным инструментом в работе разработчика микроуправляемых приложений, стоит как можно скорее иметь его в наборе инструментов и работать с ним Модуль Wi-Fi ESP8266 Это отличная возможность получить его.
Конвертер USB a UART TTL Его также можно использовать для мониторинга поведения схемы, использующей ESP8266, для этого выходы, которые вы хотите контролировать, последовательно подключаются ко входу данных (RX) преобразователя с быстродействующим диодом ( 1N4148, например) и резистор (например, 2К2) параллельно друг другу. Такая установка работает как аппаратный анализатор последовательного порта.
Хотя сниффер на изображении выше, безусловно, рудиментарен (помимо прочего, он не имеет буфер) достаточно для контроля работы сборки с Arduino y el ESP8266.
Убрав сниффер из предыдущей схемы, схема, показывающая, как подключить Модуль Wi-Fi ESP8266 к тарелке Arduino. Помимо подачи на 3V3, вывод сброса и вывод активации встроенного должны быть подключены к высокому уровню (разрешение). Разумеется, вывод RX одного должен подключаться к выводу TX другого.
Для упрощения предыдущей схемы была изображена пластина Arduino питание от напряжения 3 В3, для которого напряжение на последовательном порту также предполагается равным 3 В3. Если вы используете микроконтроллер с другим уровнем сигнала на последовательном порту (обычно 5 В), чтобы не повредить ESP8266, использовать преобразователь уровней как на схемах ниже. Эта схема часто встречается во многих готовых коммерческих реализациях модулей.
Обновите прошивку ESP8266.
Лас- ESP8266 AT-команды, его завершение, скорость модуля по умолчанию... зависят от версии Прошивка ESP8266. Лучше всего убедиться, что у вас установлена одна и та же версия во всех модулях и, если возможно, что это самая последняя версия.
К сожалению, большая часть Модели модулей Wi-Fi ESP8266 У них всего 4 Мбит, поэтому самую последнюю версию на них установить нельзя. Последняя (официальная) версия прошивки, которую можно установить на Модули Wi-Fi ESP8266 с 4 Мбит (большая часть) — это 0.9.4, включая версию 0.2 ESP8266 AT-команды.
Вкратце, для обновления прошивки необходимо:
-
Загрузите соответствующую версию прошивки. La последняя (официальная) версия модуля с 4 Мбит памяти находится в папке Espressif на github.. В Сайт Эспрессиф Вы можете скачать самую свежую версию прошивки, но очень важно убедиться, что у модуля, на котором она установлена, достаточно памяти.
-
Загрузите последнюю версию инструмента установки прошивки.. Мой любимый инструмент что написано в Питон, поэтому он работает на любой платформе. Помимо загрузки, его также можно установить с помощью
pip install esptool
(opip2
opython -m pip
…). Конечно, Эспрессиф Он также предлагает собственный инструмент, но в настоящее время доступен только для Windows. -
Подготовьте загруженные файлы; разархивируйте их в доступную папку и при необходимости сделайте инструмент исполняемым инструментв моем случае, поскольку GNU / Linux, con
chmod +x esptool
-
Подключите модуль к компьютеру с помощью конвертера USB UART TTL это работает на 3V3 или используйте преобразователь уровней, если он работает от 5 В. Помимо питания вам придется подключить TX к RX преобразователя USB UART TTL, RX к TX, GPIO0 на низком уровне (GND) и, возможно, GPIO2 на высоком уровне (в моих тестах он работал как при подключении на низком уровне, так и при отключении). Если у модуля свободное соединение GPIO15 (как это происходит в ESP-12), его необходимо подключить к низкому уровню. RESET, который во время работы обычно находится на высоком уровне, можно оставить неподключенным или подключить к высокому уровню с помощью резистора (например, 10К), поскольку перед началом записи может потребоваться перезагрузить устройство, подключив его. до низкого уровня.
После включения модуля его можно будет обновить, но Если отображается ошибка подключения, необходимо будет ее сбросить. подключив RESET на низком уровне на мгновение и затем оставив его в эфире (без подключения) для процесса обновления.
Модуль имеет пики потребления на полампера (до 600 мА, по мнению некоторых пользователей), поэтому важно использовать блок питания, способный поддерживать такое потребление, особенно для обновления прошивки. -
Запустите инструмент для обновления прошивки. В моем случае я сохранил документы инструмента и прошивки на шаге 3 в одной папке, поэтому запускаю из консоли:
cd ~/Datos/firmwareESP8266
(перейдите в папку, содержащую инструмент и прошивку)./esptool.py --baud 115200 --port /dev/ttyUSB0 write_flash \
0x00000 ./boot_v1.1.bin \
0x01000 ./user1.bin \
0x7C000 ./esp_init_data_default.bin \
0x7E000 ./blank.bin
--baud
устанавливает скорость ESP8266 (в моем случае 115200 бод) и--port
последовательный порт, к которому он подключается (в моем случае эмулируемый, первый USB). Различные документы, составляющие прошивку, остаются позади.write_flash
которому предшествует адрес с документом user1.bin, содержащим полезные данные обновления.
Отправка команд на модуль Wi-Fi ESP8266.
Чтобы контролировать ESP8266 с компьютера нам придется начать с настроить приложение для чего будет достаточно ① выбрать порт, к которому подключен преобразователь USB UART TTL, Что-то типа /dev/USB0
в GNU/Linux и подобных или что-то в этом роде COM6
в Windows: ② выберите скорость, с которой ESP8266, вероятно, 115200 бод, ③ установить 8 бит данных плюс один стоповый бит, без контроля четности или установления связи, и ④ установить конец строки, в зависимости от прошивки, почти всегда CR+LF.
После того как приложение настроено (или, где это возможно, сохранено и выбрано), оно открыть соединение («открыть устройство» и «открыть» соответственно на скриншотах примеров выше с МилыйКом y PuTTY) и вы можете начать отправлять заказы на ESP8266.
Как можно видеть в Таблица AT-команд ESP8266, формат активации, деактивации, установки значения и обращения к нему вполне предсказуем, но в целом запомнить их все непросто и для обращения к нему, вероятно, потребуется иметь его под рукой.
Форма Enviar АТ заказы al Модуль Wi-Fi ESP8266 от Arduino Это очень просто: ① настроить связь с Serial.begin(115200);
(или Serial1, Serial2… на платах с несколькими аппаратными последовательными портами) и ② отправьте команды в формате Serial.print(orden+"\r\n");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#define PUERTO_SERIE Serial // Objeto serie que corresponde a puerto serie hardware al que está conectado el módulo wifi ESP8266
#define VELOCIDAD_ESP8266 115200 // Velocidad, en baudios, a la que está configurado el ESP8266
#define IDENTIFICADOR_WIFI “polaridad.es” // SSID (Service Set Identifier)
#define CLAVE_WIFI “54lLij1RiTn3MEd3v41C” // Clave del punto de acceso wifi al que se conecta el ESP8266
void setup()
{
PUERTO_SERIE.begin(VELOCIDAD_ESP8266);
PUERTO_SERIE.print
(
“AT+CWJAP=\””+
String(IDENTIFICADOR_WIFI)+
“\”,\””+
String(CLAVE_WIFI)+
“\”\r\n”
);
}
void loop()
{
}
|
В приведенном выше примере показано, как отправить Модуль Wi-Fi ESP8266 при заказе от Arduino. В данном случае это иллюстрируется AT+CWJAP
, который используется для подключения к точке доступа. Эта команда использует в качестве аргументов идентификатор точки доступа (SSID) и ключ, оба в кавычках, поэтому они становятся объектом Srtring
и заключен в кавычки с помощью escape-кода (\"
). Для оформления заказа используйте \r\n
что соответствует CR
y LF
.
Помните, что последовательный порт не всегда идентифицируется с Serial
(на некоторых пластинах это может быть Serial1
, Serial2
…) используемый объект порта был определен путем присвоения его макросу PUERTO_SERIE
. Определение типа используемой платы может добавить немного интеллекта при выборе последовательного порта; Позже мы рассмотрим, как можно узнать тип Arduino. Остальные определения — обычные, которые позволяют «называть» константные значения, чтобы избежать их повторения (и ошибок) и облегчить их изменение.
Приведенный выше пример должен соединить Модуль Wi-Fi ESP8266 к указанной точке доступа, но была ли она уже подключена раньше? Связь сработала? Чтобы узнать это, нам нужно «прислушаться» к тому, что ESP8266
Получение данных от Wi-Fi-модуля ESP8266.
Подключив анализатор данных, описанный выше, к компьютеру, вы сможете увидеть, что Arduino послал ESP8266 и его ответ. Чтобы прочитать из Arduino и обрабатывать информацию в нем необходимо будет обнаружить с помощью Serial.available()
поступили ли какие-либо данные, и если да, загрузите их с помощью Serial.read()
. В следующем примере показано, как прочитать ответ от AT+CWJAP?
, который сообщит, есть ли соединение с какой-либо точкой доступа.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#define PUERTO_ESP8266 Serial // Objeto serie que corresponde a puerto serie hardware al que está conectado el módulo wifi ESP8266
#define VELOCIDAD_ESP8266 115200 // Velocidad, en baudios, a la que está configurado el ESP8266
char letra_recibida;
void setup()
{
PUERTO_ESP8266.begin(VELOCIDAD_ESP8266);
PUERTO_ESP8266.print(“AT+CWJAP?\r\n”);
}
void loop()
{
while(PUERTO_ESP8266.available())
{
letra_recibida=PUERTO_ESP8266.read();
}
}
|
Как на тарелке Arduino Uno (и в других случаях) открытие последовательного монитора сбрасывает программу, ее можно использовать для просмотра в последовательной консоли. Arduino информация, которую вы отправляете ESP8266 как показано на скриншоте ниже.
Проанализируйте ответ, отправленный модулем Wi-Fi ESP8266.
Мы уже видели, как читать информацию, которая достигает Arduino от ESP8266. Проблема, с которой вам приходится иметь дело, заключается в том, что вы не знаете, когда он начнет прибывать, сколько времени потребуется, чтобы прибыть, какой длины он будет... и не очень эффективно ждать ответа от ESP8266 принимается, не позволяя микроконтроллер параллельно выполнять другие задачи.
Простой способ справиться с этой ситуацией — перебирать полученные данные в поисках конкретных ответов с помощью которых, например, активировать индикаторы (флаги или логические переменные), которые будут определять, стоит ли продолжать поиск в полученном тексте и какие действия следует выполнить на основании информации, поступающей от ESP8266. Пока приходит ответ микроконтроллер можно посвятить другим задачам, например, получение данных от датчиков и их обработка.
Поиск текста в информации, полученной от ESP8266
Для поиска текста, полученного из ESP8266 может быть сравните каждое полученное письмо с тем, которое соответствует сообщению, которое вы ищете. Необходимо будет использовать счетчик (или указатель), указывающий на сравниваемую букву; Если персонаж, пришедший из ESP8266 такой же, как тот, который проверяется в сообщении, счетчик увеличивается, если он отличается, он инициализируется.
Чтобы узнать, что конец достигнут, проверяется следующий символ искомого сообщения, который будет нулевым (\0
) или сохраняется длина сообщения, чтобы путем сравнения ее со счетчиком узнать, завершилось ли сравнение и, следовательно, Модуль Wi-Fi ESP8266 отправил желаемое сообщение.
В следующем примере используется команда AT+CWLAP
который вернет список точек доступа, и в них выполняется поиск точки под названием «wifi Polaridad.es». Хотя мы решили проверить, что последний символ равен нулю, поскольку буфер В нем хранится только искомый текст и известна его длина, также можно было проверить, было ли получено такое количество правильных букв. С LED подключенный к контакту 2 сообщается, что ожидаемый текст найден.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define ORDEN “AT+CWLAP\r\n” // Buscar los puntos de acceso wifi disponibles. (Dependiendo de la versión del firmware) Las órdenes terminan en CR+LF
#define MENSAJE_BUSCADO “wifi polaridad.es” // Ver si está disponible el punto de acceso llamado “wifi polaridad.es”
#define LONGITUD_MENSAJE 18 // Se necesita guardar, al menos, 17 letras y el terminador \0
#define PIN_LED_ENCONTRADO 2 // Pin al que se conecta el LED que informa de que se ha encontrado el texto (el punto de acceso buscado está disponible)
#include <string.h> // strncpy
boolean esperando=true;
char buffer_mensaje;
char mensaje[LONGITUD_MENSAJE];
byte posicion_mensaje=0;
void setup()
{
pinMode(PIN_LED_ENCONTRADO,OUTPUT);
digitalWrite(PIN_LED_ENCONTRADO,LOW); // Apagar el LED (por ahora no se ha encontrado el mensaje)
strncpy(mensaje,MENSAJE_BUSCADO,LONGITUD_MENSAJE); // sizeof(mensaje)
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
SERIE.print(ORDEN); // Enviar la orden (consultar los puntos de acceso disponibles) al módulo wifi ESP8266
}
void loop()
{
if(esperando) // Buscará infinitamente hasta que llegue el texto esperado (y si no existe el punto de acceso nunca llegará ¡Es un ejemplo!)
{
while(SERIE.available()) // Si ha llegado algún dato por el puerto serie…
{
buffer_mensaje=SERIE.read(); // …almacenarlo en el buffer
if(buffer_mensaje==mensaje[posicion_mensaje]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
posicion_mensaje++; // Pasar a la siguiente letra del mensaje
if(mensaje[posicion_mensaje]==0) // ¿Ha terminado de analizarse todo el mensaje? (la última letra de la cadena de texto es \0)
{
esperando=false; // Si se ha terminado de analizar con éxito todo el mensaje ya no se está esperando
digitalWrite(PIN_LED_ENCONTRADO,HIGH); // Encender el LED para indicar que se ha encontrado el texto buscado
}
}
else // Si la letra que ha llegado por el puerto serie no corresponde con la buscada del mensaje…
{
posicion_mensaje=0; // …empezar desde la primera letra del texto buscado
}
}
}
}
|
В коде предыдущего примера вы также можете увидеть способ выберите последовательный порт в зависимости от типа платы Arduino использовал. В этом примере предполагается, что у вас есть три типа досок для проекта: одна Arduino Uno, una
Arduino Mega 2560 y una
Arduino Leonardo. Если вы работаете с Arduino Uno оно будет использовано Serial
и в противном случае Serial1
.
Если вы работаете с тарелкой Arduino Leonardo Вы можете использовать тот же метод, чтобы остановить программу и дождаться консоли (последовательный порт, связанный с Serial
) доступен.
1
2
3
4
5
6
7
8
9
10
11
|
#ifdef ARDUINO_AVR_LEONARDO
#define SERIE Serial1
#define ESPERA_CONSOLA while(!Serial){} /* Esperar a la consola */
#else
#define ESPERA_CONSOLA /* Si no es un Arduino Leonardo no hace falta esperar a la consola */
#ifdef ARDUINO_AVR_MEGA2560
#define SERIE Serial1
#else // En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí
#define SERIE Serial
#endif
#endif
|
Поиск различных текстов в ответе ESP8266
Код в предыдущем примере используется для поиска текста в информации, отправленной ESP8266 но ответ может включать различную информацию в зависимости от операции. Предположим, для начала с простого случая в следующем примере, что текст, отправленный MCU ESP8266 es OK
когда операция выполнена правильно и ERROR
В противном случае, как и в случае с приказом AT+CWJAP?
, который служит для проверки того, Модуль Wi-Fi ESP8266 уже подключен к точке доступа.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define ORDEN “AT+CWJAP?\r\n” // Verificar que está conectado a un punto de acceso
#define CANTIDAD_MENSAJES 2 // Se distingue entre dos mensajes: acierto y error que se identificarán con true y false
#define MENSAJE_ACIERTO “OK”
#define MENSAJE_ERROR “ERROR”
#define LONGITUD_MENSAJE 6 // Se necesita guardar, al menos, 5 letras y el terminador \0 (Un pequeño desperdicio al hacer una matriz en la que todos los elementos ocupan como el mayor. Es admisible porque son muy pocos elementos y así no se complica este ejemplo inicial)
#define PIN_LED_ACIERTO 2 // Pin al que se conecta el LED que informa del acierto
#define PIN_LED_ERROR 3 // Pin al que se conecta el LED que informa del error
#include <string.h> // strncpy
boolean esperando=true; // Todavía no se ha encontrado el final del mensaje
char buffer_mensaje; // Para almacenar la última letra cargada desde el ESP8266
byte led_estado[CANTIDAD_MENSAJES]; // Un LED (pin) para cada estado
char mensaje[CANTIDAD_MENSAJES][LONGITUD_MENSAJE]; // Mensajes de acierto y error
byte posicion_mensaje[CANTIDAD_MENSAJES]; // Un contador de posición para cada mensaje
void setup()
{
led_estado[true]=PIN_LED_ACIERTO;
led_estado[false]=PIN_LED_ERROR;
for(byte numero_mensaje=0;numero_mensaje<CANTIDAD_MENSAJES;numero_mensaje++)
{
pinMode(led_estado[numero_mensaje],OUTPUT); // Establecer el pin del LED
digitalWrite(led_estado[numero_mensaje],LOW); // Apagar el LED
posicion_mensaje[numero_mensaje]=0; // Inicializar a cero el número de letra a analizar de cada mensaje
}
strncpy(mensaje[true],MENSAJE_ACIERTO,LONGITUD_MENSAJE); // Preparar el mensaje de acierto
strncpy(mensaje[false],MENSAJE_ERROR,LONGITUD_MENSAJE); // Preparar el mensaje de error
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
SERIE.print(ORDEN); // Enviar la orden (verificar si existe conexión a un punto de acceso) al módulo wifi ESP8266
}
void loop()
{
if(esperando) // Buscará infinitamente hasta que llegue el texto esperado (y si no existe el punto de acceso nunca llegará ¡Es un ejemplo!)
{
while(SERIE.available()) // Si ha llegado algún dato por el puerto serie…
{
buffer_mensaje=SERIE.read(); // …almacenarlo en el buffer
for(byte numero_mensaje=0;numero_mensaje<CANTIDAD_MENSAJES;numero_mensaje++)
{
if(buffer_mensaje==mensaje[numero_mensaje][posicion_mensaje[numero_mensaje]]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
posicion_mensaje[numero_mensaje]++; // Pasar a la siguiente letra del mensaje
if(mensaje[numero_mensaje][posicion_mensaje[numero_mensaje]]==0) // ¿Ha terminado de analizarse todo el mensaje actual? (la última letra de la cadena de texto es \0)
{
esperando=false; // Si se ha encontrado algún mensaje y ya no se está esperando
digitalWrite(led_estado[numero_mensaje],HIGH); // Encender el LED correspondiente al mensaje encontrado
}
}
else // Si la letra que ha llegado por el puerto serie no corresponde con la buscada del mensaje…
{
posicion_mensaje[numero_mensaje]=0; // …empezar desde la primera letra del texto buscado
}
}
}
}
}
|
Эта новая реализация того же метода, который ищет совпадение с несколькими возможными сообщениями, позволяет вам выбирать между различными действиями в зависимости от ответа, полученного от ESP8266, просто включите LED соответствующий.
Ограничьте время, необходимое для получения ответа
До сих пор не было сделано никаких упоминаний о соответствующем вопросе: максимальное время ожидания (таймаут), прежде чем считать операцию неудачной. Если по какой-либо причине соединение с Модуль Wi-Fi ESP8266, модуль с точкой доступа, точка доступа с Интернетом или, например, гипотетический сервер недоступен, программа может быть заблокирована в один момент в ожидании на неопределенный срок, поэтому придется артикулировать реакцию на такие обстоятельства. Максимальное время ожидания можно настроить для всего приложения, обычно в этом случае оно будет более «щедрым», или для каждой операции можно запрограммировать индивидуальное время ожидания.
Чтобы проверить, что (хотя бы) прошел определенный интервал времени «Время» момента запуска учетной записи обычно вычитается из текущего «времени» и проверяется, что разница превышает желаемый предел.. Это «время» не обязательно должно быть реальным, обычно оно соответствует интервалу, прошедшему с момента MCU начать отсчитывать время; Это не влияет на программу, поскольку интересно затраченное время, а не абсолютное время.
Обычно для проверки того, прошел ли определенный интервал, используется выражение типа:
1
|
(unsigned long)(millis()–milisegundos_al_empezar)>intervalo_de_tiempo
|
переменная milisegundos_al_empezar
содержит значение millis()
определенного момента исполнения, от которого он отсчитывается, поэтому нет ничего необычного в том, что его название отсылает к слову «хронометр». Переменная intervalo_de_tiempo
содержит максимальное количество миллисекунд, которое делает предыдущее выражение истинным, то есть представляет тайм-аут; Обычно это константа (или макрос) и, как и в предыдущем случае, в ее названии часто встречается слово «TIMEOUT». Если вы работаете с очень короткими интервалами, вы можете использовать micros()
вместо millis()
(микросекунды вместо миллисекунд), хотя это гораздо менее распространено и гораздо менее точно.
1
|
(unsigned long)(millis()–cronometro)>TIMEOUT
|
Длинное целое число в Arduino (unsigned long
) занимает 4 байта (32 бита), поэтому наибольшее значение, которое оно может представлять, — 4294967295 (2 в 32-й степени минус один, поскольку оно начинается с нуля). на тарелке Arduino При непрерывной работе счетчик миллисекунд будет сбрасываться (возвращаться к нулю) примерно каждые 50 дней. При вычитании беззнаковых типов данных воспроизводится то же самое поведение (переворот счетчика), поэтому можно бесконечно контролировать тайм-аут.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define ORDEN “AT+CWJAP?\r\n” // Verificar que está conectado a un punto de acceso
#define CANTIDAD_MENSAJES 2 // Se distingue entre dos mensajes: acierto y error que se identificarán con true y false
#define MENSAJE_ACIERTO “OK”
#define MENSAJE_ERROR “ERROR”
#define LONGITUD_MENSAJE 6 // Se necesita guardar, al menos, 5 letras y el terminador \0 (Un pequeño desperdicio al hacer una matriz en la que todos los elementos ocupan como el mayor. Es admisible porque son muy pocos elementos y así no se complica este ejemplo inicial)
#define PIN_LED_ACIERTO 2 // Pin al que se conecta el LED que informa del acierto
#define PIN_LED_ERROR 3 // Pin al que se conecta el LED que informa del error
#define TIMEOUT 5000 // Espera 5 segundos la respuesta del ESP8266 (y su análisis) antes de desistir
#include <string.h> // strncpy
boolean esperando=true; // Todavía no se ha encontrado el final del mensaje
boolean encontrado=false; // Salvo que se encuentre el punto de acceso se considera que la operación ha fracasado
char buffer_mensaje; // Para almacenar la última letra cargada desde el ESP8266
byte led_estado[CANTIDAD_MENSAJES]; // Un LED (pin) para cada estado
char mensaje[CANTIDAD_MENSAJES][LONGITUD_MENSAJE]; // Mensajes de acierto y error
byte posicion_mensaje[CANTIDAD_MENSAJES]; // Un contador de posición para cada mensaje
unsigned long cronometro;
void setup()
{
led_estado[true]=PIN_LED_ACIERTO;
led_estado[false]=PIN_LED_ERROR;
for(byte numero_mensaje=0;numero_mensaje<CANTIDAD_MENSAJES;numero_mensaje++)
{
pinMode(led_estado[numero_mensaje],OUTPUT); // Establecer el pin del LED
digitalWrite(led_estado[numero_mensaje],LOW); // Apagar el LED
posicion_mensaje[numero_mensaje]=0; // Inicializar a cero el número de letra a analizar de cada mensaje
}
strncpy(mensaje[true],MENSAJE_ACIERTO,LONGITUD_MENSAJE); // Preparar el mensaje de acierto
strncpy(mensaje[false],MENSAJE_ERROR,LONGITUD_MENSAJE); // Preparar el mensaje de error
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
SERIE.print(ORDEN); // Enviar la orden (verificar si existe conexión a un punto de acceso) al módulo wifi ESP8266
cronometro=millis();
}
void loop()
{
if(esperando) // Buscará infinitamente hasta que llegue el texto esperado (y si no existe el punto de acceso nunca llegará ¡Es un ejemplo!)
{
while(SERIE.available()) // Si ha llegado algún dato por el puerto serie…
{
buffer_mensaje=SERIE.read(); // …almacenarlo en el buffer
for(byte numero_mensaje=0;numero_mensaje<CANTIDAD_MENSAJES;numero_mensaje++)
{
if(buffer_mensaje==mensaje[numero_mensaje][posicion_mensaje[numero_mensaje]]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
posicion_mensaje[numero_mensaje]++; // Pasar a la siguiente letra del mensaje
if(mensaje[numero_mensaje][posicion_mensaje[numero_mensaje]]==0) // ¿Ha terminado de analizarse todo el mensaje actual? (la última letra de la cadena de texto es \0)
{
encontrado=numero_mensaje; // (numero_mensaje!=0) Hay conexión con el punto de acceso
esperando=false; // Si se ha encontrado algún mensaje y ya no se está esperando
digitalWrite(led_estado[numero_mensaje],HIGH); // Encender el LED correspondiente al mensaje encontrado
}
}
else // Si la letra que ha llegado por el puerto serie no corresponde con la buscada del mensaje…
{
posicion_mensaje[numero_mensaje]=0; // …empezar desde la primera letra del texto buscado
}
}
}
if((unsigned long)(millis()–cronometro)>TIMEOUT&&!encontrado) // Se ha superado el tiempo de espera y no hay conexión (se ha verificado que no hay o no ha llegado respuesta)
{
digitalWrite(PIN_LED_ERROR,HIGH); // Si ha superado el tiempo de espera encender el LED de error
esperando=false;
}
}
}
|
Приведенный выше код показывает очень простая реализация ограничения таймаута включая строки, отмеченные в предыдущем примере. Поскольку проверка таймаута производится после обработки данных, поступающих от Модуль Wi-Fi ESP8266, операцию можно считать успешной, даже если прием занимает больше установленного времени ожидания.
Выполнить сложную операцию, определенную несколькими AT-командами.
Чтобы иметь примерную ссылку на цель приложения, использующего Модуль Wi-Fi ESP8266, предположим, что это хранить информацию в базе данных, доступ к которой осуществляется через веб-сервис чтобы следить за температурой. Следующий код считывает датчик, подключенный к аналоговому входу, каждый определенный интервал времени, вычисляет среднее значение и после более длительного интервала времени отправляет его на веб-сервер (стиль IoT) через Запрос HTTP (ОТПРАВИТЬ, ПОЛУЧИТЬ…).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#define PIN_TEMPERATURA A0 // Pin analógico al que se conecta la salida del sensor de temperatura LM35
#define INTERVALO_LECTURA_TEMPERATURA 30000 // Leer la temperatura cada 30 segundos (30*1000)
#define INTERVALO_GRABACION_TEMPERATURA 300000 // Grabar la media de la temperatura cada 5 minutos (5*60*1000)
unsigned long muestras=0; // Número de veces que se ha medido la temperatura (para calcular la media)
float temperatura; // de -55.0 °C a +150 °C | de -550 mV a +1500 mV | de 0 V a 2.050 V | analogRead*5.0/1023.0*100-55.0 -> analogRead/2.46-55.0
float media_temperaturas=0.0;
unsigned long cronometro_lectura_temperatura;
unsigned long cronometro_grabacion_temperatura;
void setup()
{
Serial.begin(9600);
cronometro_lectura_temperatura=millis();
cronometro_grabacion_temperatura=millis();
}
void loop()
{
if((unsigned long)(millis()–cronometro_lectura_temperatura)>INTERVALO_LECTURA_TEMPERATURA)
{
cronometro_lectura_temperatura=millis();
temperatura=analogRead(PIN_TEMPERATURA)/2.46–55.0;
muestras++;
media_temperaturas=(float)temperatura/(float)muestras+media_temperaturas*(float)(muestras–1)/(float)(muestras);
}
if((unsigned long)(millis()–cronometro_grabacion_temperatura)>INTERVALO_GRABACION_TEMPERATURA)
{
cronometro_grabacion_temperatura=millis();
// Aquí iría la parte del código que graba la temperatura. Para verificar el funcionamiento, en este ejemplo simplemente se muestra en la consola
Serial.println(“\nTemperatura media “+String(temperatura,DEC)+” °C (“+String((float)millis()/1000.0,DEC)+” s)\n”);
}
}
|
В этом примере записи температуры доступ к веб-серверу осуществляется каждые пять минут. Хотя доступность не особенно высока, следует ожидать, что предложение сработает, но если бы была необходима более высокая частота записи, пришлось бы реализовать другие ресурсы, например, буфер данных жду отправки, чтобы отправить несколько, когда сервер может присутствовать, и сохранить их, когда он недоступен. Если бы частота, с которой необходимо записывать данные, была бы еще выше, в качестве альтернативы протоколам пришлось бы предложить другие типы протоколов. HTTP или даже заменить TCP по UDP иметь возможность отправлять большую часть данных с необходимой скоростью даже ценой потери некоторых.
Операции, составляющие задачу, которую необходимо выполнить для отправки температуры, будут следующими:
- Сбросьте модуль Wi-Fi
- Отключиться от текущей точки доступа (если существует соединение по умолчанию)
- Установите настройки. Для примера предполагается, что необходимо настроить режим подключения (простой) и роль в связи Wi-Fi (станция).
- Подключиться к точке доступа
- Проверьте правильность подключения (собственно, это точка входа) Если соединения нет, начните процесс с начала
- Подключиться к серверу
- Отправить запрос HTTP с данными, которые будут храниться
Порядок операций не обязательно должен быть именно таким (хотя операция именно такая), и каждый шаг может потребовать несколько ESP8266 AT-командыНапример, для конфигурации, указанной выше, потребуется два: AT+CIPMUX=0
y AT+CWMODE=1
.
Структура данных для представления операций на ESP8266.
В предыдущих примерах, хотя и в самом простом виде, уже предложено общее решение проблемы: использовать структуру данных, в которой хранятся возможные ответы и действия, которые необходимо предпринять в каждом случае; отправьте действие, дождитесь ответа и действуйте в соответствии с тем, что означает ответ. Поскольку каждая сложная операция потребует нескольких ESP8266 AT-команды, структура данных должна связывать одну операцию с другими, последующими или предыдущими, что должно выполняться в каждом конкретном случае в зависимости от реакции ESP8266.
В предыдущих примерах поиск сообщения осуществлялся в ответе ESP8266 и это интерпретировалось как успех или ошибка. Помимо приема (и анализа) всего полученного текста, Чтобы иметь общий минимум, желательно также уделить внимание завершению сообщения. или, другими словами, к наличию Модуль Wi-Fi ESP8266 получать новые заказы. Таким образом, переход в состояние, которое мы могли бы назвать, например, «доступен Wi-Fi», может заключаться в получении имени точки доступа и получении текста. ERROR
или текст OK
будет означать, что ESP8266 вы закончили ответ и теперь можете отправить следующий AT-команда для ESP8266.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
// inicializar_operaciones.h
// 0 Reiniciar el módulo wifi ESP8266
operacion[REINICIAR_ESP8266]=“AT+RST”;
mensaje[REINICIAR_ESP8266][FALLO]=mensaje_fallo;
mensaje[REINICIAR_ESP8266][ACIERTO]=“ready\r\n”;
mensaje[REINICIAR_ESP8266][LITERAL]=mensaje_vacio;
siguiente_operacion[REINICIAR_ESP8266][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[REINICIAR_ESP8266][ACIERTO]=DESCONECTAR_WIFI;
siguiente_operacion[REINICIAR_ESP8266][LITERAL]=DESCONECTAR_WIFI;
configuracion[REINICIAR_ESP8266]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[REINICIAR_ESP8266]=10000;
// 1 Desconectar del punto de acceso por defecto (si fuera el caso)
operacion[DESCONECTAR_WIFI]=“AT+CWQAP”;
mensaje[DESCONECTAR_WIFI][FALLO]=mensaje_fallo;
mensaje[DESCONECTAR_WIFI][ACIERTO]=mensaje_acierto;
mensaje[DESCONECTAR_WIFI][LITERAL]=mensaje_vacio;
siguiente_operacion[DESCONECTAR_WIFI][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[DESCONECTAR_WIFI][ACIERTO]=MODO_ESTACION;
siguiente_operacion[DESCONECTAR_WIFI][LITERAL]=MODO_ESTACION;
configuracion[DESCONECTAR_WIFI]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[DESCONECTAR_WIFI]=2500;
// 2 Establecer el modo de estación (no punto de acceso)
operacion[MODO_ESTACION]=“AT+CWMODE=1”;
mensaje[MODO_ESTACION][FALLO]=mensaje_fallo;
mensaje[MODO_ESTACION][ACIERTO]=mensaje_acierto;
mensaje[MODO_ESTACION][LITERAL]=mensaje_vacio;
siguiente_operacion[MODO_ESTACION][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[MODO_ESTACION][ACIERTO]=MODO_SIMPLE;
siguiente_operacion[MODO_ESTACION][LITERAL]=MODO_SIMPLE;
configuracion[MODO_ESTACION]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[MODO_ESTACION]=2500;
// 3 Establecer el modo de conexión simple
operacion[MODO_SIMPLE]=“AT+CIPMUX=0”;
mensaje[MODO_SIMPLE][FALLO]=mensaje_fallo;
mensaje[MODO_SIMPLE][ACIERTO]=mensaje_acierto;
mensaje[MODO_SIMPLE][LITERAL]=mensaje_vacio;
siguiente_operacion[MODO_SIMPLE][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[MODO_SIMPLE][ACIERTO]=CONECTAR_WIFI;
siguiente_operacion[MODO_SIMPLE][LITERAL]=CONECTAR_WIFI;
configuracion[MODO_SIMPLE]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[MODO_SIMPLE]=2500;
// 4 Conectar al punto de acceso
operacion[CONECTAR_WIFI]=“AT+CWJAP=\”polaridad.es\”,\”54lLij1RiTn3MEd3v41C\””;
mensaje[CONECTAR_WIFI][FALLO]=mensaje_fallo;
mensaje[CONECTAR_WIFI][ACIERTO]=mensaje_acierto;
mensaje[CONECTAR_WIFI][LITERAL]=mensaje_vacio;
siguiente_operacion[CONECTAR_WIFI][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[CONECTAR_WIFI][ACIERTO]=VERIFICAR_CONEXION;
siguiente_operacion[CONECTAR_WIFI][LITERAL]=VERIFICAR_CONEXION;
configuracion[CONECTAR_WIFI]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[CONECTAR_WIFI]=20000;
// 5 Verificar si hay conexión
operacion[VERIFICAR_CONEXION]=“AT+CIPSTATUS”;
mensaje[VERIFICAR_CONEXION][FALLO]=mensaje_fallo;
mensaje[VERIFICAR_CONEXION][ACIERTO]=mensaje_acierto;
mensaje[VERIFICAR_CONEXION][LITERAL]=“STATUS:5”;
siguiente_operacion[VERIFICAR_CONEXION][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[VERIFICAR_CONEXION][ACIERTO]=REINICIAR_ESP8266;
siguiente_operacion[VERIFICAR_CONEXION][LITERAL]=CONECTAR_SERVIDOR;
configuracion[VERIFICAR_CONEXION]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[VERIFICAR_CONEXION]=5000;
// 6 Conectar al servidor
operacion[CONECTAR_SERVIDOR]=“AT+CIPSTART=\”TCP\”,\”servidoriot.com\”,80″;
mensaje[CONECTAR_SERVIDOR][FALLO]=mensaje_fallo;
mensaje[CONECTAR_SERVIDOR][ACIERTO]=mensaje_acierto;
mensaje[CONECTAR_SERVIDOR][LITERAL]=“CONNECT”;
siguiente_operacion[CONECTAR_SERVIDOR][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[CONECTAR_SERVIDOR][ACIERTO]=INFORMAR_CANTIDAD;
siguiente_operacion[CONECTAR_SERVIDOR][LITERAL]=INFORMAR_CANTIDAD;
configuracion[CONECTAR_SERVIDOR]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[CONECTAR_SERVIDOR]=15000;
// 7 Avisar de la cantidad de datos que se envían
operacion[INFORMAR_CANTIDAD]=“AT+CIPSEND=”;
mensaje[INFORMAR_CANTIDAD][FALLO]=mensaje_vacio;
mensaje[INFORMAR_CANTIDAD][ACIERTO]=mensaje_acierto;
mensaje[INFORMAR_CANTIDAD][LITERAL]=mensaje_vacio;
siguiente_operacion[INFORMAR_CANTIDAD][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[INFORMAR_CANTIDAD][ACIERTO]=ENVIAR_CANTIDAD;
siguiente_operacion[INFORMAR_CANTIDAD][LITERAL]=ENVIAR_CANTIDAD;
configuracion[INFORMAR_CANTIDAD]=NO_ESPERAR_RESPUESTA;
timeout[INFORMAR_CANTIDAD]=1000;
// 8 Enviar cantidad
//operacion[ENVIAR_CANTIDAD]=”123″; // Definido para cada envío
mensaje[ENVIAR_CANTIDAD][FALLO]=sin_enlace;
mensaje[ENVIAR_CANTIDAD][ACIERTO]=“>”;
mensaje[ENVIAR_CANTIDAD][LITERAL]=mensaje_vacio;
siguiente_operacion[ENVIAR_CANTIDAD][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[ENVIAR_CANTIDAD][ACIERTO]=ENVIAR_PREFIJO_PETICION;
siguiente_operacion[ENVIAR_CANTIDAD][LITERAL]=ENVIAR_PREFIJO_PETICION;
configuracion[ENVIAR_CANTIDAD]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[ENVIAR_CANTIDAD]=5000;
// 9 Enviar el prefijo de la petición
operacion[ENVIAR_PREFIJO_PETICION]=“GET /frigo03/almacenar_temperatura.php?temperatura=”;
mensaje[ENVIAR_PREFIJO_PETICION][FALLO]=mensaje_vacio;
mensaje[ENVIAR_PREFIJO_PETICION][ACIERTO]=mensaje_vacio;
mensaje[ENVIAR_PREFIJO_PETICION][LITERAL]=mensaje_vacio;
siguiente_operacion[ENVIAR_PREFIJO_PETICION][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[ENVIAR_PREFIJO_PETICION][ACIERTO]=ENVIAR_DATOS;
siguiente_operacion[ENVIAR_PREFIJO_PETICION][LITERAL]=ENVIAR_DATOS;
configuracion[ENVIAR_PREFIJO_PETICION]=NO_ESPERAR_RESPUESTA;
timeout[ENVIAR_PREFIJO_PETICION]=10000;
// 10 Enviar la temperatura
//operacion[ENVIAR_DATOS]=”+000.00″; // Definido para cada envío
mensaje[ENVIAR_DATOS][FALLO]=mensaje_vacio;
mensaje[ENVIAR_DATOS][ACIERTO]=mensaje_vacio;
mensaje[ENVIAR_DATOS][LITERAL]=mensaje_vacio;
siguiente_operacion[ENVIAR_DATOS][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[ENVIAR_DATOS][ACIERTO]=ENVIAR_SUFIJO_PETICION;
siguiente_operacion[ENVIAR_DATOS][LITERAL]=ENVIAR_SUFIJO_PETICION;
configuracion[ENVIAR_DATOS]=NO_ESPERAR_RESPUESTA;
timeout[ENVIAR_DATOS]=5000;
// 11 Enviar el sufijo de la petición
operacion[ENVIAR_SUFIJO_PETICION]=” HTTP/1.1\r\nHost: www.servidoriot.com\r\nUser-Agent: ESP8266\r\nConnection: close\r\n\r\n”;
mensaje[ENVIAR_SUFIJO_PETICION][FALLO]=sin_enlace;
mensaje[ENVIAR_SUFIJO_PETICION][ACIERTO]=“CLOSED\r\n\r\nOK\r\n”;
mensaje[ENVIAR_SUFIJO_PETICION][LITERAL]=mensaje_vacio; // “SEND OK”
siguiente_operacion[ENVIAR_SUFIJO_PETICION][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[ENVIAR_SUFIJO_PETICION][ACIERTO]=VERIFICAR_CONEXION;
siguiente_operacion[ENVIAR_SUFIJO_PETICION][LITERAL]=VERIFICAR_CONEXION;
configuracion[ENVIAR_SUFIJO_PETICION]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[ENVIAR_SUFIJO_PETICION]=20000;
|
В приведенном выше коде используется вектор (operacion
) для хранения текста последовательных операций, образующих полную задачу. Используется двумерный массив (mensaje
) с тремя анализируемыми ответами. Как объяснялось выше, необходимо искать сообщения, обозначающие конец ответа, в дополнение к сообщению, обозначающему правильный или неправильный ответ. Не все операции будут иметь одинаковое количество возможных ответов; Когда ответов меньше, можно использовать пустое сообщение, занимающее на анализ минимально возможное количество циклов (даже в этом случае это не самый оптимальный способ). По логике вещей, необходимо, чтобы минимальное количество искомых ответов (три в примере) включало все рабочие возможности, даже если они не все возможны.
Говоря о возможных ответах, уже видно, что этот пример не очень полезен для получения данных произвольного формата из Модуль Wi-Fi ESP8266, но дело в том, что в контексте использования с микроконтроллеры это необычно; Чаще всего это отправка данных, собранных подключенными к ним датчиками, и/или получение информации о том, что делать с управляемыми им исполнительными механизмами. Очень ценная информация, которую можно очень хорошо предсказать.
В предыдущей структуре данных, так же, как это делается для выражения возможных анализируемых ответов, также используется двумерная матрица для определения операции, которую необходимо выполнить в каждом случае (siguiente_operacion
). В частности, мы решили отвечать на три типа сообщений: ① произвольный текст (LITERAL
) для проверки наличия соединения с точкой доступа Wi-Fi и сервером, ② текст для обнаружения ошибок в процессе (FALLO
) и ③ текст, указывающий на успешное завершение операции (ACIERTO
).
Наконец, есть еще два вектора для установки максимального времени ожидания перед тем, как сдаться (timeout
) и укажите (configuracion
), если операция завершается, не дождавшись ответа (ESPERAR_RESPUESTA
) и сообщения, указывающие на окончание связи. Этот последний вектор, иллюстрирующий пример экономии памяти, работает с битами конфигурационного байта, указывая различные состояния.
Первый ESP8266 AT-команды структуры данных всегда ожидают ответа, который может быть сообщением об успехе или ошибке. При возникновении ошибки модуль перезапускается и запускается заново, и если сообщение указывает на правильность операции, он переходит к следующему.
При подключении к серверу картина меняется. В этом случае необходимо ① отправить длину передаваемого пакета данных и ② составить запрос. HTTP с фиксированным текстом плюс значение (температуры), которое отправляется для хранения на сервере. Подготовка этих данных осуществляется в каждой отправке и ее необходимо разделить на две (сообщить длину) или три (отправить запрос HTTP) в ESP8266 при заказе. Ответа будет ждать только последняя из частей, на которые разбита операция.
В этом случае он будет работать без проблем (возможно, предупреждая, что модуль занят), но когда длина данных будет больше, необходимо будет делить блоки данных на более мелкие части и может даже потребоваться реализовать ожидание, т.к. выполняется вместе с показаниями температуры, чтобы дать модулю время отправить данные без заполнения буфер.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define REINICIAR_ESP8266 0 // Índice del vector de operaciones que representa la orden de reinicio del módulo wifi ESP8266
#define DESCONECTAR_WIFI 1
#define MODO_ESTACION 2
#define MODO_SIMPLE 3
#define CONECTAR_WIFI 4
#define VERIFICAR_CONEXION 5
#define CONECTAR_SERVIDOR 6
#define INFORMAR_CANTIDAD 7
#define ENVIAR_CANTIDAD 8
#define ENVIAR_PREFIJO_PETICION 9
#define ENVIAR_DATOS 10
#define ENVIAR_SUFIJO_PETICION 11
#define CANTIDAD_OPERACIONES 12 // Cantidad de operaciones que forma el cuerpo de la aplicación
#define FALLO 0 // Índice del vector de respuestas que representa el mensaje de error
#define FALLO_TERMINA 0B00000001 // 1<<FALLO
#define ACIERTO 1
#define ACIERTO_TERMINA 0B00000010 // 1<<ACIERTO
#define LITERAL 2
#define LITERAL_TERMINA 0B00000100 // 1<<LITERAL
#define CANTIDAD_RESPUESTAS 3 // Cantidad de posibles respuestas que se buscan en el texto recibido desde el ESP8266
#define ESPERAR_RESPUESTA 0B00001000 // 1<<CANTIDAD_RESPUESTAS
#define NO_ESPERAR_RESPUESTA 0B00000000
#define ENVIAR_OPERACION esperando_respuesta=true;SERIE.print(operacion[operacion_actual]);if(configuracion[operacion_actual]&ESPERAR_RESPUESTA){SERIE.print(“\r\n”);}for(unsigned char numero_respuesta=0;numero_respuesta<CANTIDAD_RESPUESTAS;numero_respuesta++){numero_caracter[numero_respuesta]=0;}cronometro_esp8266=millis();
|
Вместе с другими макросами, которые уже были объяснены ранее, приведенный выше пример кода показывает, как определяются различные состояния, с помощью которых можно указать, следует ли ждать ответа и, если применимо, какое сообщение указывает на его завершение.
Так как в разных точках кода будет отправлена операция (когда пришло время отправить среднюю температуру, если время ожидания операции превышено, когда текущая операция успешно завершена...), но как это сделать - установлен во всем мире, он был определен как макрос ENVIAR_OPERACION
который группирует этапы доставки.
1
2
3
4
5
6
7
8
9
10
11
12
|
// La macro ENVIAR_OPERACION corresponde con las operaciones:
esperando_respuesta=true;
SERIE.print(operacion[operacion_actual]);
if(configuracion[operacion_actual]&ESPERAR_RESPUESTA)
{
SERIE.print(“\r\n”);
}
for(unsigned char numero_respuesta=0;numero_respuesta<CANTIDAD_RESPUESTAS;numero_respuesta++)
{
numero_caracter[numero_respuesta]=0;
}
cronometro_esp8266=millis();
|
Ниже приведен код основной программы примера. Самая внешняя задача — это задача, отвечающая за выборку температуры для расчета среднего значения, и каждый определенный период времени она отправляется на сервер с помощью Модуль Wi-Fi ESP8266. После отправки каждой операции ответ анализируется, чтобы определить, какая операция следующая и завершена ли задача отправки информации.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
#include “ESP8266_operacion_compleja_varias_ordenes_AT.h”
#define PIN_TEMPERATURA A0 // Pin analógico al que se conecta la salida del sensor de temperatura LM35
#define INTERVALO_LECTURA_TEMPERATURA 30000 // Leer la temperatura cada 30 segundos (30*1000)
#define INTERVALO_GRABACION_TEMPERATURA 300000 // Grabar la media de la temperatura cada 5 minutos (5*60*1000)
unsigned long muestras=0; // Número de veces que se ha medido la temperatura (para calcular la media)
float temperatura; // de -55.0 °C a +150 °C | de 0 V a 2.050 V | de -550 mV a +1500 mV | analogRead*5.0/1023.0*100-55.0 analogRead/2.46-55.0
float media_temperaturas=0.0;
unsigned long cronometro_lectura_temperatura;
unsigned long cronometro_grabacion_temperatura;
char *mensaje_fallo=“ERROR\r\n”;
char *mensaje_acierto=“OK\r\n”;
char *sin_enlace=“link is not\r\n”;
char *mensaje_vacio=“\f”;
char *operacion[CANTIDAD_OPERACIONES]; // Matriz de punteros a constantes de caracteres con las operaciones que se envían al ESP8266 (no solo órdenes AT, aunque seguro que algunas son órdenes AT)
char *mensaje[CANTIDAD_OPERACIONES][CANTIDAD_RESPUESTAS]; // Mensajes de respuesta
unsigned char siguiente_operacion[CANTIDAD_OPERACIONES][CANTIDAD_RESPUESTAS];
unsigned char configuracion[CANTIDAD_OPERACIONES];
unsigned int timeout[CANTIDAD_OPERACIONES];
unsigned char numero_caracter[CANTIDAD_RESPUESTAS];
unsigned int longitud_peticion;
char texto_longitud_peticion[4]; // 3 caracteres para almacenar la longitud de la petición en formato texto
char valor_enviado[9]; // signo + 4 enteros + punto + 2 decimales + \0 = 9
unsigned long cronometro_esp8266;
unsigned char operacion_actual; // Número de operación que se está procesando
unsigned char proxima_operacion; // Siguiente operación que se procesará cuando termine la actual
char lectura_serie;
boolean grabando_datos=false;
boolean esperando_respuesta;
void setup()
{
#include “inicializar_operaciones.h”
longitud_peticion=strlen(operacion[ENVIAR_PREFIJO_PETICION])+strlen(operacion[ENVIAR_SUFIJO_PETICION]);
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
//delay(8000); // En fase de pruebas se puede introducir un tiempo de espera para conectar una consola/sniffer
cronometro_lectura_temperatura=millis();
cronometro_grabacion_temperatura=millis();
}
void loop()
{
if((unsigned long)(millis()–cronometro_lectura_temperatura)>INTERVALO_LECTURA_TEMPERATURA)
{
cronometro_lectura_temperatura=millis();
temperatura=analogRead(PIN_TEMPERATURA)/2.46–55.0;
muestras++;
media_temperaturas=(float)temperatura/(float)muestras+media_temperaturas*(float)(muestras–1)/(float)(muestras);
}
if(grabando_datos)
{
if((unsigned long)(millis()–cronometro_esp8266)>timeout[operacion_actual]) // Si se ha superado el tiempo de espera máximo
{
operacion_actual=siguiente_operacion[operacion_actual][FALLO]; // Pasar a la operación correspondiente al error
ENVIAR_OPERACION
}
else // Si no se ha superado el tiempo de espera máximo
{
if(configuracion[operacion_actual]&ESPERAR_RESPUESTA) // Si la siguiente operación depende de la respuesta a la actual desde el ESP8266 hay que leer la información que llegue desde el puerto serie
{
while(SERIE.available())
{
lectura_serie=SERIE.read();
for(unsigned char numero_respuesta=0;numero_respuesta<CANTIDAD_RESPUESTAS;numero_respuesta++) // Comparar la letra cargada desde el puerto serie con la correspondiente de los mensajes disponibles
{
if(lectura_serie==mensaje[operacion_actual][numero_respuesta][numero_caracter[numero_respuesta]]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
numero_caracter[numero_respuesta]++; // Como el carácter coincide, se puede comparar con el siguiente lo próximo que llegue por el puerto serie
if(mensaje[operacion_actual][numero_respuesta][numero_caracter[numero_respuesta]]==0) // Si el carácter que toca es \0 es que se ha terminado de analizar el mensaje
{
if(esperando_respuesta) // Todavía no se ha encontrado un mensaje que determine la siguiente operación
{
proxima_operacion=siguiente_operacion[operacion_actual][numero_respuesta]; // La próxima operación que habrá que procesar será la que indique el mensaje encontrado para la operación actual
esperando_respuesta=false; // Ya se ha encontrado un mensaje que determina la siguiente operación
}
if(configuracion[operacion_actual]&(1<<numero_respuesta)) // Si el mensaje encontrado es uno de los que terminan la operación…
{
if(operacion_actual+1==CANTIDAD_OPERACIONES) // Se ha completado la última operación de la tarea compleja
{
grabando_datos=false; // Se ha terminado la tarea compleja (grabar datos en el servidor)
}
else // No es la última operación de la tarea compleja, hay que seguir realizando otras operaciones
{
operacion_actual=proxima_operacion; // Ejecutar la siguiente operación
ENVIAR_OPERACION
}
}
}
}
else // Si la letra recibida no es igual que la correspondiente del mensaje
{
numero_caracter[numero_respuesta]=0; // Empezar a comparar desde la primera letra del mensaje
}
}
}
}
else // Si no hay que esperar datos desde el puerto serie
{
operacion_actual=siguiente_operacion[operacion_actual][ACIERTO];
ENVIAR_OPERACION
}
}
}
else
{
if((unsigned long)(millis()–cronometro_grabacion_temperatura)>INTERVALO_GRABACION_TEMPERATURA)
{
cronometro_grabacion_temperatura=millis();
dtostrf(media_temperaturas,4,2,valor_enviado); // snprintf(valor_enviado,9,”%+3.2f”,media_temperaturas); // printf y derivadas no funcionan en AVR
snprintf(texto_longitud_peticion,4,“%d”,longitud_peticion+strlen(valor_enviado));
grabando_datos=true;
operacion_actual=VERIFICAR_CONEXION;
operacion[ENVIAR_DATOS]=valor_enviado;
operacion[ENVIAR_CANTIDAD]=texto_longitud_peticion;
ENVIAR_OPERACION
}
}
}
|
Логично, что с предыдущим кодом можно выполнить несколько действий по оптимизации, но, поскольку это пример, позволяющий понять, как ESP8266 В общем, стоит сосредоточиться только на некоторых аспектах, первым из которых является структура данных. Кажется, что логично использовать структуру данных языка программирования (struct
) для представления обрабатываемой информации: ESP8266 AT-команды и сообщения, которые анализируются.
Используйте структуру (struct
) хранить данные вместо массивов примеров (на их основе) тривиально и, хотя и может привести к более элегантному коду, не предполагает какого-либо улучшения результата. Истинная альтернатива, возникающая при использовании struct
заключается в реализации, как поясняется ниже, переменная длина в структурах, содержащих «внутренние» данные на которые они ссылаются. Таким образом, например, операции не потребуется иметь фиксированное количество ответов для анализа.
Этот подход предполагает, что это лучший способ реализовать решение, но недостатком является то, что это будет необходимо. использовать динамическое распределение памяти — рискованная практика при работе с микроконтроллер что требует тщательного измерения того, сколько памяти будет использоваться во время выполнения, так как компилятор вряд ли сможет нас об этом предупредить и существует определенная вероятность исчерпания памяти (или стека) с фатальными последствиями для выполнения программы.
В плане оптимизации кода интересно помнить, что в программе такого типа, использующей большое количество текста, может сэкономить место в памяти SRAM сохранение текстовых строк в памяти программы (вспышка) с макросом F()
. На следующих скриншотах вы можете увидеть различное распределение программ и динамической памяти при обычном использовании текста и использовании макроса. F()
.
В отношении действий, выполняемых по информации, поступающей от Модуль Wi-Fi ESP8266, в качестве альтернативы проверке сообщения по коду и выполнению того или другого в зависимости от полученного, может храниться в этой структуре данных указатели на функции, выполняющие каждую задачу, вместо индикаторов состояния (флаги), которые предупреждают об определенном состоянии, за управление которым отвечает приложение, например, в основном цикле.
Ниже приведен пример структур для хранения данных запросов к ESP8266 (тип данных operacion_esp8266
) и их ответы (тип данных respuesta_esp8266
).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
typedef struct estructura_operacion_esp8266 operacion_esp8266; // Se define el tipo de datos operacion_esp8266 que corresponde con la estructura (struct) llamada estructura_operacion_esp8266 que se define más adelante
typedef struct estructura_respuesta_esp8266 respuesta_esp8266; // Se define el tipo de datos respuesta_esp8266 que corresponde con la estructura (struct) llamada struct estructura_respuesta_esp8266 que se define más adelante
struct estructura_operacion_esp8266
{
char *peticion; // Datos que se envían al ESP8266 para iniciar una operación (como una orden AT, pero también algo como la petición a un servidor…)
unsigned char cantidad_respuestas; // Número de posibles respuestas del ESP8266 que se van a analizar, que puede ser variable en cada petición (no solo OK y ERROR)
unsigned char timeout; // Tiempo (segundos) que se espera la respuesta del ESP8266 antes de desistir
respuesta_esp8266 *respuesta; // Respuestas (estructura) que se esperan de esta operación
};
struct estructura_respuesta_esp8266
{
char *mensaje; // Mensaje que se espera recibir desde el ESP8266
unsigned char posicion_mensaje; // Posición (letra) que se está comparando con la recibida desde el ESP8266
//boolean estado; // El estado se representa por un valor booleano (por ejemplo ¿se ha encontrado ya esta respuesta? para no seguir buscándola)
//unsigned char *estado; // El estado se representa con un texto (de longitud variable)
unsigned char estado; // El estado se establece con 8 banderas, una por bit
operacion_esp8266 *operacion; // Puntero a operación que se ejecutará si se encuentra esta respuesta en el mensaje devuelto por el ESP8266
};
operacion_esp8266 comprobar_conexion;
respuesta_esp8266 respuesta_OK;
respuesta_esp8266 respuesta_ERROR;
|
Поскольку структура, представляющая операцию (данные, отправляемые в Модуль Wi-Fi ESP8266) относится к структуре, с помощью которой определяются ответы, а структура ответов — к структуре операций, необходимо сначала объявить оба, определив новый тип данных, а затем определив его содержимое.
В предыдущем примере предполагается, что программа, включающая его, решила использовать индикатор состояния, что должно соответствовать переменной, доступной из кода, отвечающей за выполнение той или иной операции, на которую указывает указанное значение. Если в ответ ESP8266 При анализе определенного текста состояние принимает значение, указывающее на структуру соответствующего ответа.
Как говорилось ранее, другой альтернативой замены или дополнения индикатора статуса может быть сохранить функцию в ссылочной структуре (указатель), который будет вызываться при обнаружении определенного текста в ответе от Модуль Wi-Fi ESP8266.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
typedef struct estructura_operacion_esp8266 operacion_esp8266; // Se define el tipo de datos operacion_esp8266 que corresponde con la estructura (struct) llamada estructura_operacion_esp8266 que se define más adelante
typedef struct estructura_respuesta_esp8266 respuesta_esp8266; // Se define el tipo de datos respuesta_esp8266 que corresponde con la estructura (struct) llamada struct estructura_respuesta_esp8266 que se define más adelante
struct estructura_operacion_esp8266
{
char *peticion; // Datos que se envían al ESP8266 para iniciar una operación (como una orden AT, pero también algo como la petición a un servidor…)
unsigned char cantidad_respuestas; // Número de posibles respuestas del ESP8266 que se van a analizar, que puede ser variable en cada petición (no solo OK y ERROR)
unsigned char timeout; // Tiempo (segundos) que se espera la respuesta del ESP8266 antes de desistir
respuesta_esp8266 *respuesta; // Respuestas (estructura) que se esperan de esta operación
};
struct estructura_respuesta_esp8266
{
char *mensaje; // Mensaje que se espera recibir desde el ESP8266
unsigned char posicion_mensaje; // Posición (letra) que se está comparando con la recibida desde el ESP8266
//boolean estado; // El estado se representa por un valor booleano (por ejemplo ¿se ha encontrado ya esta respuesta? para no seguir buscándola)
//unsigned char *estado; // El estado se representa con un texto (de longitud variable)
unsigned char estado; // El estado se establece con 8 banderas, una por bit
float (*accion)(unsigned char,unsigned char); // Puntero a la función que se llama si se encuentra la respuesta
operacion_esp8266 *operacion; // Puntero a operación que se ejecutará si se encuentra esta respuesta en el mensaje devuelto por el ESP8266
};
operacion_esp8266 comprobar_conexion;
respuesta_esp8266 respuesta_OK;
respuesta_esp8266 respuesta_ERROR;
|
В предыдущем примере он был добавлен в структуру данных, которая используется для обработки ответа от Модуль Wi-Fi ESP8266 указатель на (предполагаемую) функцию, которая возвращает данные типа float
(может быть взвешенное значение аналогового чтения) и которому в качестве аргументов предоставляются два байта (два unsigned char
это может быть контакт, с которого считывается аналоговый вход, и тот, который активирует РАЗРЕШЕНИЕ гипотетического интегрированного устройства).
В разработке для MCU, в отличие от того, что происходит в стиле разработки для более крупных систем, не так уж и редко используются глобальные переменные при определении (глобального) поведения приложения, управляющего сборкой, поэтому не будет особой редкостью встретить такого типа определения как функции без параметров и не возвращающие значения, что-то вроде void (*accion)();
Если вы работаете с таким способом представления данных, используя struct
данных переменной длины, необходимо будет динамически выделять память с помощью malloc()
(o new()
, если используются объекты), который будет использовать объем выделенной памяти в качестве параметра и возвращать указатель на начало зарезервированной области памяти. С sizeof()
От типа, который хранится, умножив на количество используемых элементов, можно получить тот объем памяти, который необходим. Пример с его использованием и без можно увидеть на скриншотах ниже. malloc()
; Будьте осторожны с памятью, используемой программой: в первом случае вам необходимо загрузить библиотеку, содержащую эту функцию.
Если операции над Модуль Wi-Fi ESP8266 будет меняться на протяжении всего выполнения программы, необходимо будет освободить память, которая не используется при free()
(o delete()
, в случае, если они являются объектами). Хотя разумно ожидать, что компилятор (НКУ) оптимизирует программу, чтобы избежать разделения памяти, производительность наверняка не будет такой оптимальной, как при работе со статически выделенной памятью.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
error_operacion.mensaje=“ERROR\r\n”;
error_operacion.termina_operacion=true;
error_operacion.operacion=&reiniciar_esp8266;
error_enlace.mensaje=“link is not\r\n”;
error_enlace.termina_operacion=true;
error_enlace.operacion=&reiniciar_esp8266;
reiniciar_esp8266_correcto.mensaje=“ready\r\n”;
reiniciar_esp8266_correcto.termina_operacion=true;
reiniciar_esp8266_correcto.operacion=&desconectar_wifi;
desconectar_wifi_correcto.mensaje=mensaje_acierto;
desconectar_wifi_correcto.termina_operacion=true;
desconectar_wifi_correcto.operacion=&establecer_modo_estacion;
establecer_modo_estacion_correcto.mensaje=mensaje_acierto;
establecer_modo_estacion_correcto.termina_operacion=true;
establecer_modo_estacion_correcto.operacion=&establecer_modo_simple;
establecer_modo_simple_correcto.mensaje=mensaje_acierto;
establecer_modo_simple_correcto.termina_operacion=true;
establecer_modo_simple_correcto.operacion=&conectar_wifi;
conectar_wifi_correcto.mensaje=mensaje_acierto;
conectar_wifi_correcto.termina_operacion=true;
conectar_wifi_correcto.operacion=&verificar_conexion;
verificar_conexion_correcto.mensaje=“STATUS:5”;
verificar_conexion_correcto.termina_operacion=false;
verificar_conexion_correcto.operacion=&conectar_servidor;
verificar_conexion_terminado.mensaje=mensaje_acierto;
verificar_conexion_terminado.termina_operacion=true;
verificar_conexion_terminado.operacion=&reiniciar_esp8266;
conectar_servidor_correcto.mensaje=“CONNECT”;
conectar_servidor_correcto.termina_operacion=false;
conectar_servidor_correcto.operacion=&informar_cantidad;
conectar_servidor_terminado.mensaje=mensaje_acierto;
conectar_servidor_terminado.termina_operacion=true;
conectar_servidor_terminado.operacion=&reiniciar_esp8266;
informar_cantidad_correcto.mensaje=mensaje_acierto;
informar_cantidad_correcto.termina_operacion=true;
informar_cantidad_correcto.operacion=&enviar_cantidad;
enviar_cantidad_correcto.mensaje=“>”;
enviar_cantidad_correcto.termina_operacion=true;
enviar_cantidad_correcto.operacion=&enviar_prefijo;
enviar_prefijo_correcto.operacion=&enviar_datos;
enviar_datos_correcto.operacion=&enviar_sufijo;
enviar_sufijo_correcto.mensaje=“CLOSED\r\n\r\nOK\r\n”;
enviar_sufijo_correcto.termina_operacion=true;
enviar_sufijo_correcto.operacion=&verificar_conexion;
reiniciar_esp8266.peticion=“AT+RST”;
reiniciar_esp8266.timeout=15000;
reiniciar_esp8266.cantidad_respuestas=2;
reiniciar_esp8266.respuesta=malloc(sizeof(respuesta_esp8266*)*reiniciar_esp8266.cantidad_respuestas);
reiniciar_esp8266.respuesta[FALLO]=&error_operacion;
reiniciar_esp8266.respuesta[ACIERTO]=&reiniciar_esp8266_correcto;
desconectar_wifi.peticion=“AT+CWQAP”;
desconectar_wifi.timeout=2500;
desconectar_wifi.cantidad_respuestas=2;
desconectar_wifi.respuesta=malloc(sizeof(respuesta_esp8266*)*desconectar_wifi.cantidad_respuestas);
desconectar_wifi.respuesta[FALLO]=&error_operacion;
desconectar_wifi.respuesta[ACIERTO]=&desconectar_wifi_correcto;
establecer_modo_estacion.peticion=“AT+CWMODE=1”;
establecer_modo_estacion.timeout=2500;
establecer_modo_estacion.cantidad_respuestas=2;
establecer_modo_estacion.respuesta=malloc(sizeof(respuesta_esp8266*)*establecer_modo_estacion.cantidad_respuestas);
establecer_modo_estacion.respuesta[FALLO]=&error_operacion;
establecer_modo_estacion.respuesta[ACIERTO]=&establecer_modo_estacion_correcto;
establecer_modo_simple.peticion=“AT+CIPMUX=0”;
establecer_modo_simple.timeout=2500;
establecer_modo_simple.cantidad_respuestas=2;
establecer_modo_simple.respuesta=malloc(sizeof(respuesta_esp8266*)*establecer_modo_simple.cantidad_respuestas);
establecer_modo_simple.respuesta[FALLO]=&error_operacion;
establecer_modo_simple.respuesta[ACIERTO]=&establecer_modo_simple_correcto;
conectar_wifi.peticion=“AT+CWJAP=\”polaridad.es\”,\”54lLij1RiTn3MEd3v41C\””;;
conectar_wifi.timeout=20000;
conectar_wifi.cantidad_respuestas=2;
conectar_wifi.respuesta=malloc(sizeof(respuesta_esp8266*)*conectar_wifi.cantidad_respuestas);
conectar_wifi.respuesta[FALLO]=&error_operacion;
conectar_wifi.respuesta[ACIERTO]=&conectar_wifi_correcto;
verificar_conexion.peticion=“AT+CIPSTATUS”;
verificar_conexion.timeout=5000;
verificar_conexion.cantidad_respuestas=3;
verificar_conexion.respuesta=malloc(sizeof(respuesta_esp8266*)*verificar_conexion.cantidad_respuestas);
verificar_conexion.respuesta[FALLO]=&error_operacion;
verificar_conexion.respuesta[ACIERTO]=&verificar_conexion_correcto;
verificar_conexion.respuesta[OTRO_MENSAJE]=&verificar_conexion_terminado;
conectar_servidor.peticion=“AT+CIPSTART=\”TCP\”,\”servidoriot.com\”,80″;
conectar_servidor.timeout=15000;
conectar_servidor.cantidad_respuestas=3;
conectar_servidor.respuesta=malloc(sizeof(respuesta_esp8266*)*conectar_servidor.cantidad_respuestas);
conectar_servidor.respuesta[FALLO]=&error_operacion;
conectar_servidor.respuesta[ACIERTO]=&conectar_servidor_correcto;
conectar_servidor.respuesta[OTRO_MENSAJE]=&conectar_servidor_terminado; // OK, no significa que haya conexión pero sí termina la operación
informar_cantidad.peticion=“AT+CIPSEND=”;
informar_cantidad.timeout=1000;
informar_cantidad.cantidad_respuestas=1;
informar_cantidad.respuesta=malloc(sizeof(respuesta_esp8266*)*informar_cantidad.cantidad_respuestas);
informar_cantidad.respuesta[0]=&informar_cantidad_correcto;
//enviar_cantidad.peticion=””; // Se asigna cuando se conoce el valor que se va a enviar y se puede calcular la longitud que ocupa (número de caracteres)
enviar_cantidad.timeout=5000;
enviar_cantidad.cantidad_respuestas=2;
enviar_cantidad.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_cantidad.cantidad_respuestas);
enviar_cantidad.respuesta[FALLO]=&error_enlace;
enviar_cantidad.respuesta[ACIERTO]=&enviar_cantidad_correcto;
enviar_prefijo.peticion=“GET /frigo03/almacenar_temperatura.php?temperatura=”;
enviar_prefijo.timeout=10000;
enviar_prefijo.cantidad_respuestas=1;
enviar_prefijo.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_prefijo.cantidad_respuestas);
enviar_prefijo.respuesta[0]=&enviar_prefijo_correcto;
//enviar_datos.peticion=””; // Se asigna en cuando se conoce el valor que se va a enviar
enviar_datos.timeout=5000;
enviar_datos.cantidad_respuestas=1;
enviar_datos.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_datos.cantidad_respuestas);
enviar_datos.respuesta[0]=&enviar_datos_correcto;
enviar_sufijo.peticion=” HTTP/1.1\r\nHost: www.servidoriot.com\r\nUser-Agent: ESP8266\r\nConnection: close\r\n\r\n”;
enviar_sufijo.timeout=20000;
enviar_sufijo.cantidad_respuestas=2;
enviar_sufijo.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_sufijo.cantidad_respuestas);
enviar_sufijo.respuesta[FALLO]=&error_enlace;
enviar_sufijo.respuesta[ACIERTO]=&enviar_sufijo_correcto;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define FALLO 0 // Índice del vector de respuestas que representa el mensaje de error
#define FALLO_TERMINA 0B00000001 // 1<<FALLO
#define ACIERTO 1
#define ACIERTO_TERMINA 0B00000010 // 1<<ACIERTO
#define OTRO_MENSAJE 2
#define OTRO_MENSAJE_TERMINA 0B00000100 // 1<<OTRO_MENSAJE
#define CANTIDAD_RESPUESTAS 3 // Cantidad de posibles respuestas que se buscan en el texto recibido desde el ESP8266
#define ESPERAR_RESPUESTA 0B00001000 // 1<<CANTIDAD_RESPUESTAS
#define NO_ESPERAR_RESPUESTA 0B00000000
#define ENVIAR_OPERACION esperando_respuesta=true;SERIE.print((*operacion_actual).peticion);if((*operacion_actual).cantidad_respuestas>1){SERIE.print(“\r\n”);for(unsigned char numero_respuesta=0;numero_respuesta<(*operacion_actual).cantidad_respuestas;numero_respuesta++){numero_caracter[numero_respuesta]=0;}}cronometro_esp8266=millis();
#define PIN_TEMPERATURA A0 // Pin analógico al que se conecta la salida del sensor de temperatura LM35
#define INTERVALO_LECTURA_TEMPERATURA 30000 // Leer la temperatura cada 30 segundos (30*1000)
#define INTERVALO_GRABACION_TEMPERATURA 300000 // Grabar la media de la temperatura cada 5 minutos (5*60*1000)
unsigned long muestras=0; // Número de veces que se ha medido la temperatura (para calcular la media)
float temperatura; // de -55.0 °C a +150 °C | de 0 V a 2.050 V | de -550 mV a +1500 mV | analogRead*5.0/1023.0*100-55.0 analogRead/2.46-55.0
float media_temperaturas=0.0;
unsigned long cronometro_lectura_temperatura;
unsigned long cronometro_grabacion_temperatura;
char *mensaje_acierto=“OK\r\n”;
typedef struct estructura_operacion_esp8266 operacion_esp8266; // Se define el tipo de datos operacion_esp8266 que corresponde con la estructura (struct) llamada estructura_operacion_esp8266 que se define más adelante
typedef struct estructura_respuesta_esp8266 respuesta_esp8266; // Se define el tipo de datos respuesta_esp8266 que corresponde con la estructura (struct) llamada struct estructura_respuesta_esp8266 que se define más adelante
struct estructura_operacion_esp8266
{
char *peticion; // Datos que se envían al ESP8266 para iniciar una operación (como una orden AT, pero también algo como la petición a un servidor…)
unsigned int timeout; // Tiempo (segundos) que se espera la respuesta del ESP8266 antes de desistir
bool espera_respuesta; // Si no espera respuesta en cuanto se termine de enviar la orden se puede pasar a la siguiente
unsigned char cantidad_respuestas; // Número de posibles respuestas del ESP8266 que se van a analizar, que puede ser variable en cada petición (no solo OK y ERROR)
respuesta_esp8266 **respuesta; // Respuestas (estructura) que se esperan de esta operación
};
struct estructura_respuesta_esp8266
{
char *mensaje; // Mensaje que se espera recibir desde el ESP8266
bool termina_operacion; // Cuando se termina de leer el mensaje ha terminado la operación
operacion_esp8266 *operacion; // Puntero a operación que se ejecutará si se encuentra esta respuesta en el mensaje devuelto por el ESP8266
};
operacion_esp8266 *operacion_actual; // Operación sobre el ESP8266 que se está ejecutando actualmente
operacion_esp8266 *proxima_operacion; // Siguiente operación que se procesará cuando termine la actual
unsigned int longitud_peticion; // Número de caracteres que ocupa la petición HTTP
char texto_longitud_peticion[4]; // 3 caracteres para almacenar la longitud de la petición en formato texto
char valor_enviado[9]; // signo + 4 enteros + punto + 2 decimales + \0 = 9
unsigned long cronometro_esp8266; // Cronómetro para controlar el tiempo máximo de respuesta del ESP8266 antes de desistir
char lectura_serie; // buffer con el carácter leído desde el ESP8266
boolean grabando_datos=false; // Verdadero cuando han terminado todas las operaciones necesarias para grabar los datos
boolean esperando_respuesta; // Verdadero si aún no se ha encontrado una de los mensajes que indica que ha terminado la respuesta
unsigned char numero_caracter[CANTIDAD_RESPUESTAS]; // Número de orden de la letra del mensaje-respuesta que se está almacenando (una matriz de, como máximo, el mayor número de respuestas posible)
operacion_esp8266 reiniciar_esp8266; // Reiniciar el módulo wifi ESP8266
operacion_esp8266 desconectar_wifi; // Desconectar del punto de acceso por defecto (si fuera el caso)
operacion_esp8266 establecer_modo_estacion; // Establecer el modo de estación (no punto de acceso)
operacion_esp8266 establecer_modo_simple; // Establecer el modo de conexión simple
operacion_esp8266 conectar_wifi; // Conectar al punto de acceso
operacion_esp8266 verificar_conexion; // Verificar si hay conexión
operacion_esp8266 conectar_servidor; // Conectar al servidor
operacion_esp8266 informar_cantidad; // Avisar de la cantidad de datos que se envían
operacion_esp8266 enviar_cantidad; // Enviar cantidad
operacion_esp8266 enviar_prefijo; // Enviar el prefijo de la petición
operacion_esp8266 enviar_datos; // Enviar la temperatura
operacion_esp8266 enviar_sufijo; // Enviar el sufijo de la petición
respuesta_esp8266 error_operacion; // Todas las respuestas “ERROR” reinician el módulo wifi ESP8266
respuesta_esp8266 error_enlace; // Las respuestas “link is not” también reinician el módulo wifi ESP8266
respuesta_esp8266 reiniciar_esp8266_correcto;
respuesta_esp8266 desconectar_wifi_correcto;
respuesta_esp8266 establecer_modo_estacion_correcto;
respuesta_esp8266 establecer_modo_simple_correcto;
respuesta_esp8266 conectar_wifi_correcto;
respuesta_esp8266 verificar_conexion_correcto;
respuesta_esp8266 verificar_conexion_terminado;
respuesta_esp8266 conectar_servidor_correcto;
respuesta_esp8266 conectar_servidor_terminado;
respuesta_esp8266 informar_cantidad_correcto;
respuesta_esp8266 enviar_cantidad_correcto;
respuesta_esp8266 enviar_prefijo_correcto;
respuesta_esp8266 enviar_datos_correcto;
respuesta_esp8266 enviar_sufijo_correcto;
void setup()
{
#include “inicializar_operaciones.h”
longitud_peticion=strlen(enviar_prefijo.peticion)+strlen(enviar_sufijo.peticion);
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
//delay(8000); // En fase de pruebas se puede introducir un tiempo de espera para conectar una consola/sniffer
cronometro_lectura_temperatura=millis();
cronometro_grabacion_temperatura=millis();
}
void loop()
{
if((unsigned long)(millis()–cronometro_lectura_temperatura)>INTERVALO_LECTURA_TEMPERATURA)
{
cronometro_lectura_temperatura=millis();
temperatura=analogRead(PIN_TEMPERATURA)/2.46–55.0;
muestras++;
media_temperaturas=(float)temperatura/(float)muestras+media_temperaturas*(float)(muestras–1)/(float)(muestras);
}
if(grabando_datos)
{
if((unsigned long)(millis()–cronometro_esp8266)>(unsigned long)(*operacion_actual).timeout) // Si se ha superado el tiempo de espera máximo
{
operacion_actual=(*(*operacion_actual).respuesta[FALLO]).operacion; // Pasar a la operación correspondiente al error
ENVIAR_OPERACION
}
else // Si no se ha superado el tiempo de espera máximo
{
if((*operacion_actual).cantidad_respuestas>1) // Si la siguiente operación depende de la respuesta a la actual desde el ESP8266 hay que leer la información que llegue desde el puerto serie
{
while(SERIE.available())
{
lectura_serie=SERIE.read();
for(unsigned char numero_respuesta=0;numero_respuesta<(*operacion_actual).cantidad_respuestas;numero_respuesta++) // Comparar la letra cargada desde el puerto serie con la correspondiente de los mensajes disponibles
{
if(lectura_serie==(*(*operacion_actual).respuesta[numero_respuesta]).mensaje[numero_caracter[numero_respuesta]]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
numero_caracter[numero_respuesta]++; // Como el carácter coincide, se puede comparar con el siguiente lo próximo que llegue por el puerto serie
if((*(*operacion_actual).respuesta[numero_respuesta]).mensaje[numero_caracter[numero_respuesta]]==0) // Si el carácter que toca es \0 es que se ha terminado de analizar el mensaje
{
if(esperando_respuesta) // Todavía no se ha encontrado un mensaje que determine la siguiente operación
{
proxima_operacion=(*(*operacion_actual).respuesta[numero_respuesta]).operacion; // La próxima operación que habrá que procesar será la que indique el mensaje encontrado para la operación actual
esperando_respuesta=false; // Ya se ha encontrado un mensaje que determina la siguiente operación
}
if((*(*operacion_actual).respuesta[numero_respuesta]).termina_operacion) // Si el mensaje encontrado es uno de los que terminan la operación…
{
if(operacion_actual==&enviar_sufijo) // Se ha completado la última operación de la tarea compleja
{
grabando_datos=false; // Se ha terminado la tarea compleja (grabar datos en el servidor)
}
else // No es la última operación de la tarea compleja, hay que seguir realizando otras operaciones
{
operacion_actual=proxima_operacion; // Ejecutar la siguiente operación
ENVIAR_OPERACION
}
}
}
}
else // Si la letra recibida no es igual que la correspondiente del mensaje
{
numero_caracter[numero_respuesta]=0; // Empezar a comparar desde la primera letra del mensaje
}
}
}
}
else // Si no hay que esperar datos desde el puerto serie
{
operacion_actual=(*(*operacion_actual).respuesta[0]).operacion;
ENVIAR_OPERACION
}
}
}
else
{
if((unsigned long)(millis()–cronometro_grabacion_temperatura)>INTERVALO_GRABACION_TEMPERATURA)
{
cronometro_grabacion_temperatura=millis();
dtostrf(media_temperaturas,4,2,valor_enviado); // snprintf(valor_enviado,9,”%+3.2f”,media_temperaturas); // printf y derivadas no funcionan en AVR
snprintf(texto_longitud_peticion,4,“%d”,longitud_peticion+strlen(valor_enviado));
grabando_datos=true;
operacion_actual=&verificar_conexion;
enviar_datos.peticion=valor_enviado;
enviar_cantidad.peticion=texto_longitud_peticion;
ENVIAR_OPERACION
}
}
}
|
Хотя в данном примере (в обеих реализациях) это не имеет особого смысла, чтобы обобщить операцию и иметь возможность применить ее к другим случаям, следует отметить, что отправка данных всегда повторяет один и тот же протокол: сообщите количество байтов, которые будут отправлены, дождитесь индикатора (>) и отправьте данные.
Поскольку в данном примере он используется только один раз (весь запрос выполняется в одном пакете), он не кажется очень полезным, но в целом может потребоваться выполнить несколько отправок за одну операцию, включая случаи, когда они должны передаваться значительные объемы данных, которые необходимо фрагментировать во избежание переполнения памяти устройства. ESP8266.
Для реализации такого поведения можно использовать два последних элемента соединения, чтобы при каждой отправке данных данные заполнялись соответствующими значениями: в первом случае — количество отправленных байтов, а во втором — ( часть) запроса для передачи.
Повторить назначение и отправку различных элементов, которые необходимо передать, можно сохранить в векторе. Этот новый вектор будет тем, который определяет окончание сложной операции, а не последней операцией, как до сих пор.
1 комментарий