Основні операції з Wi-Fi модулем ESP8266 від Arduino
Коли Еспресиф випустив на ринок перші модулі Wi-Fi з інтегрованим ESP8266 у-ель- Firmware за допомогою якого можна обробляти його за допомогою AT-команд, ми, користувачі, цікавилися інтеграцією його в збірки мікроконтролери і проблеми зводилися до пізнання (колишньої) темряви Таблиця команд AT ESP8266, потреби в годівлі або Оновлення мікропрограми ESP8266.
Потім швидко з’явилися альтернативи для програмування ESP8266 і реалізації модулів Wi-Fi дуже різних форматів, що викликало інші проблеми: який Wi-Fi модуль ESP8266 вибрати залежно від радіусу дії різних антен (включаючи зовнішні) або фізичної інтеграції цих нових модулів у наші вузли.
Звичайно, через усі ці зміни наголос, можливо, не було зроблено на найосновніших аспектах, найпростішому управлінні Wi-Fi модуль ESP8266. Хоча полярність.es Ви можете знайти інформацію про використання ESP8266 і є деякі програми, призначені для загального пояснення роботи Wi-Fi модуль ESP8266 за допомогою AT-команд, особливо в статті про бібліотека для виконання HTTP-запитів від Arduino за допомогою модуля wifi ESP8266, враження читачів свідчать про те, що було б корисно додати ще деяку базову інформацію, щоб допомогти користувачам ESP8266 здійснювати власні реалізації.
Обговоріть основні операції для роботи з ESP8266 і пропонування загальних рішень є ціллю кількох дуже різних частин; Щоб допомогти слідкувати за змістом статті, наведений нижче покажчик може служити орієнтиром:
- Керуйте Wi-Fi модулем ESP8266 з комп'ютера через послідовний порт
- Оновіть прошивку за допомогою esptool
- Надсилайте замовлення на модуль
- Отримувати дані від ESP8266
- Проаналізуйте відповідь, шукаючи тексти у змісті
- Обмежте час очікування отримання відповіді
- Виконайте складну операцію, визначену кількома AT-командами
Керуйте Wi-Fi модулем ESP8266 з комп'ютера через послідовний порт
З тарілки Arduino і використовуючи ваш IDE можна контролювати роботу a Wi-Fi модуль ESP8266, надішліть Команди ESP8266 AT і подивіться відповідь, але це набагато зручніше робити з комп’ютера з програмою термінального типу.
Залежно яка дошка Arduino використовується лише один апаратний послідовний порт може бути доступним, що додає невеликі незручності для надсилання та отримання. Змінювати швидкість зв’язку набагато зручніше в програмі послідовного зв’язку з комп’ютера та деяких материнських плат. Arduino (і за деяких обставин) погано підтримують вищі швидкості послідовного зв’язку, особливо 115200 бод, що є швидкістю за замовчуванням для останніх версій Firmware.
Про нас Яку програму використовувати для моніторингу ESP8266 за допомогою послідовного порту, є багато на вибір відповідно до потреб і вподобань; Останнім часом я більше використовую класику CuteCom (той, що на скріншоті вище), тому що мені дуже зручно повторювати певне Замовлення модуля wifi ESP8266 AT у тестуванні проекту.
Деякі рекомендації вже були надані тут щодо програм, які функціонують як послідовна консоль; Наприклад, коли говорять про 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, наприклад) і резистор (2K2, наприклад) паралельно один одному. Така установка працює як апаратний серійний сніффер.
Хоча сніффер на зображенні вище, безумовно, рудиментарний (між іншим, він не має буфера) достатньо для моніторингу роботи збірки Arduino у-ель- ESP8266.
Видалення сніфера з попередньої схеми, схематично показує, як підключити a Wi-Fi модуль ESP8266 до тарілки Arduino. На додаток до живлення на 3V3, контакт скидання та контакт активації інтегрованого повинні бути підключені до високого рівня (увімкнути). Звичайно, контакт RX одного повинен підключатися до TX іншого.
Для спрощення попередньої діаграми була зображена пластина Arduino з живленням 3V3 і для якого напруга на послідовному порту також передбачається рівною 3V3. Якщо ви використовуєте a мікроконтролер з іншим рівнем сигналу на послідовному порту (зазвичай 5 В), щоб не пошкодити ESP8266, використовуйте a перетворювач рівня як на діаграмах нижче. Ця схема часто зустрічається в багатьох комерційних готових модулях.
Оновіть мікропрограму ESP8266
The Команди ESP8266 AT, його завершення, швидкість модуля за замовчуванням... залежать від версії Прошивка ESP8266. Найкраще переконатися, що у вас однакова версія в усіх модулях і, якщо можливо, що це остання версія.
На жаль, більшість Моделі модулів Wi-Fi ESP8266 Вони мають лише 4 Мбіт, тому на них не можна встановити останню версію. Остання (офіційна) версія мікропрограми, яку можна встановити на Модулі Wi-Fi ESP8266 з 4 Мбіт (більшість) — це 0.9.4, який включає версію 0.2 Команди ESP8266 AT.
Таким чином, щоб оновити мікропрограму, вам потрібно:
-
Завантажте відповідну версію прошивки. Остання (офіційна) версія для модуля з 4 Мбіт пам'яті, знайдена в папці Espressif на github. У Веб-сайт Espressif Ви можете завантажити останню версію мікропрограми, але дуже важливо переконатися, що модуль, на якому вона встановлена, має достатньо пам’яті.
-
Завантажте останню версію засобу встановлення мікропрограми. Моїм улюбленим є esptool який написано в Python, тому він працює на будь-якій платформі. Окрім завантаження, його також можна встановити за допомогою
pip install esptool
(opip2
opython -m pip
…). Звичайно, Еспресиф Він також пропонує власний інструмент, але наразі доступний лише для Windows. -
Підготуйте завантажені файли; розпакуйте їх у доступну папку та, якщо необхідно, зробіть інструмент виконуваним esptool, у моєму випадку, оскільки GNU / Linux, С
chmod +x esptool
-
Підключіть модуль до комп'ютера за допомогою конвертера USB UART TTL який працює на 3V3 або використовуйте перетворювач рівнів, якщо він працює при напрузі 5 В. Окрім живлення, вам доведеться підключити TX до RX перетворювача USB UART TTL, RX до TX, GPIO0 на низькому рівні (GND) і, можливо, GPIO2 на високому рівні (у моїх тестах це спрацювало як підключення на низькому рівні, так і відключення). Якщо модуль має вільне підключення GPIO15 (як це відбувається в ESP-12), він повинен бути підключений до низького рівня. RESET, який зазвичай має високий рівень під час роботи, можна залишити не підключеним або підключити до високого рівня за допомогою резистора (10K, наприклад), оскільки перед початком запису може знадобитися скинути пристрій, підключивши його до низького рівня.
Увімкнувши модуль, він стане доступним для оновлення, але Якщо відображається помилка з'єднання, її необхідно скинути підключивши 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 біт даних плюс один стоп-біт, без паритету чи квитування, і ④ встановити кінець рядка, залежно від Firmware, майже завжди CR+LF.
Після того, як програму налаштовано (або, якщо необхідно, збережено та вибрано), вона є відкрити з'єднання ("відкрити пристрій" і "відкрити", відповідно, на скріншотах прикладів вище з CuteCom y PuTTY) і ви можете почати надсилати замовлення на адресу ESP8266.
Як видно в Таблиця команд AT ESP8266, формат для активації, дезактивації, встановлення значення та посилання на нього цілком передбачуваний, але загалом запам’ятати їх усіх непросто, і вам, ймовірно, знадобиться мати його під рукою, щоб звернутися до нього.
Шлях відправити АТ замовлення 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() { } |
Наведений вище приклад показує, як надіслати Замовлення модуля wifi ESP8266 AT від 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, One Arduino Mega 2560 а Ардуїно Леонардо. Якщо ви працюєте з a Arduino Uno це буде використано Serial
і інакше Serial1
.
Якщо працювати з плитою Ардуїно Леонардо Ви можете використати той самий метод, щоб зупинити програму та дочекатися консолі (послідовний порт, пов’язаний з 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; } } } |
Наведений вище код показує a дуже проста реалізація обмеження тайм-ауту включаючи рядки, позначені відносно прикладу, який йому передує. Оскільки перевірка тайм-ауту виконується після обробки даних, що надходять від Wi-Fi модуль ESP8266, операцію можна вважати успішною, навіть якщо прийом займає більше часу, ніж встановлений час очікування.
Виконайте складну операцію, визначену кількома AT-командами
Щоб мати приклад посилання на призначення програми, яка використовує Wi-Fi модуль ESP8266, припустимо, що так зберігати інформацію в базі даних, доступ до якої здійснюється через веб-службу стежити за температурою. Наступний код зчитує датчик, підключений до аналогового входу, кожен певний проміжок часу, обчислює середнє значення та, після більшого проміжку часу, надсилає його на веб-сервер (стиль IoT) через a клопотання 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 AT замовлення. Лише остання частина, на яку розділено операцію, чекатиме відповіді.
У цьому випадку він працюватиме без проблем (можливо, попередження про те, що модуль зайнятий), але коли довжина даних більша, необхідно буде розділити блоки даних на менші частини, і навіть може знадобитися реалізувати очікування, оскільки виконується зі зчитуванням температури, щоб дати модулю час для надсилання даних без заповнення буфера.
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
) для представлення інформації, що обробляється: the Команди ESP8266 AT і повідомлення, які аналізуються.
Використовуйте структуру (struct
) зберігати дані замість прикладів масивів (на їх основі) є тривіальним і, хоча це може призвести до більш елегантного коду, це не означає жодного покращення результату. Справжньою альтернативою є використання struct
полягає в реалізації, як пояснюється нижче, змінної довжини в структурах, які містять «внутрішні» дані на які вони посилаються. Таким чином, наприклад, для операції не потрібно буде мати фіксовану кількість відповідей для аналізу.
Цей підхід припускає, що це найкращий спосіб реалізації рішення, але недоліком є те, що це буде необхідно використання динамічного розподілу пам’яті, ризикована практика роботи з a мікроконтролер що вимагає ретельного вимірювання того, скільки пам'яті буде використано під час виконання, оскільки компілятор навряд чи зможе попередити нас про це і існує певна ймовірність виснаження пам'яті (або стека) з фатальними наслідками для виконання програми.
Що стосується оптимізації коду, цікаво пам’ятати, що в програмі такого типу, яка використовує велику кількість тексту, може заощадити місце в пам'яті 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) відноситься до структури, за допомогою якої визначені відповіді, а структура відповідей до структури операцій, спочатку необхідно задекларувати обидва, визначивши новий тип даних, а потім визначивши його вміст.
У попередньому прикладі враховується, що програма, яка його включає, вибрала використання a індикатор стану, яка має відповідати змінній, доступній із коду, який відповідає за виконання тих чи інших операцій, як зазначено у зазначеному значенні. Якщо у відповіді о 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
який може бути контактом, з якого зчитується аналоговий вхід, і тим, що активує ENABLE гіпотетичного інтегрованого).
У розробці для MCU, на відміну від того, що відбувається в стилі розробки для великих систем, не так вже й рідко використовувати глобальні змінні під час визначення (глобальної) поведінки програми, яка контролює збірку, тому не буде особливою рідкістю знайти цей тип визначень як функції без параметрів, які не повертають значення, щось на зразок void (*accion)();
Якщо ви працюєте з таким способом представлення даних, використовуючи struct
даних змінної довжини, потрібно буде динамічно виділяти пам’ять malloc()
(o new()
, якщо використовуються об’єкти), який використовуватиме обсяг виділеної пам’яті як параметр і повертатиме покажчик на початок зарезервованої області пам’яті. с sizeof()
На основі типу, який зберігається, помноженого на кількість використаних елементів, ви можете отримати необхідний обсяг пам’яті. Приклад із його використанням і без нього можна побачити на скріншотах нижче. malloc()
; Будьте обережні з пам'яттю, яку використовує програма, у першому випадку потрібно завантажити бібліотеку, яка містить цю функцію.
Якщо операції на Wi-Fi модуль ESP8266 змінюватиметься під час виконання програми, необхідно буде звільнити пам'ять, яка не використовується free()
(o delete()
, у випадку об’єктів). Хоча розумно очікувати, що компілятор (GCC) оптимізує програму, щоб уникнути розділення пам’яті, звичайно, продуктивність не буде такою оптимальною, як робота зі статично виділеною пам’яттю.
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 коментар