Podstawowe operacje na module WiFi ESP8266 firmy Arduino
Kiedy ekspresyjny wprowadziła na rynek pierwsze moduły wifi z integrą ESP8266 i firmware z którymi można sobie z tym poradzić za pomocą poleceń AT, a tym, co nas interesowało, była integracja z złożeniami mikrokontrolery a problemy sprowadzały się do poznania (wcześniej) ciemności Tabela poleceń ESP8266 AT, potrzeby żywieniowe lub Aktualizacja oprogramowania układowego ESP8266.
Potem szybko pojawiły się alternatywy, aby zaprogramować ESP8266 i wdrożenia modułów wifi bardzo różnych formatów, co wzbudziło inne obawy: jaki moduł Wi-Fi ESP8266 wybrać w zależności od zasięgu różnych anten (w tym zewnętrznych) lub fizycznej integracji tych nowych modułów w naszych zespołach.
Z pewnością ze względu na te wszystkie zmiany nie położono nacisku na najbardziej podstawowe aspekty, najbardziej podstawowe zarządzanie Moduł Wi-Fi ESP8266. Chociaż polaryzacja.es Można znaleźć informacje na temat korzystania z ESP8266 istnieją także aplikacje, które mają na celu ogólne wyjaśnienie działania programu Moduł Wi-Fi ESP8266 za pomocą poleceń AT, szczególnie w artykule nt biblioteka do wykonywania zapytań HTTP z Arduino za pomocą modułu Wi-Fi ESP8266, z opinii czytelników wynika, że przydatne byłoby dodanie bardziej podstawowych informacji, aby pomóc użytkownikom ESP8266 do realizacji własnych wdrożeń.
Omów podstawowe operacje potrzebne do pracy z plikiem ESP8266 a proponowanie rozwiązań ogólnych jest celem składającym się z kilku bardzo różnych części; Poniższy indeks może pomóc w śledzeniu treści artykułu:
- Steruj modułem Wi-Fi ESP8266 z komputera poprzez port szeregowy
- Zaktualizuj oprogramowanie sprzętowe za pomocą esptoola
- Wysyłaj zamówienia do modułu
- Odbierz dane z ESP8266
- Przeanalizuj odpowiedź, wyszukując teksty w treści
- Ogranicz czas oczekiwania na otrzymanie odpowiedzi
- Wykonaj złożoną operację zdefiniowaną przez wiele poleceń AT
Steruj modułem Wi-Fi ESP8266 z komputera poprzez port szeregowy
Z talerza Arduino i używanie twojego IDE możliwe jest monitorowanie działania m.in Moduł Wi-Fi ESP8266, wysłać Polecenia ESP8266 AT i zobacz odpowiedź, ale o wiele wygodniej jest to zrobić z komputera z aplikacją typu terminal.
Zależnie od tego, która tablica Arduino używany, dostępny może być tylko jeden sprzętowy port szeregowy, co powoduje niewielką niedogodność w wysyłaniu i odbieraniu. Zmiana szybkości komunikacji jest znacznie wygodniejsza w przypadku aplikacji komunikacji szeregowej z komputera i niektórych płyt głównych. Arduino (i w niektórych okolicznościach) nie obsługują dobrze wyższych prędkości komunikacji szeregowej, zwłaszcza 115200 bodów, co jest domyślną szybkością najnowszych wersji firmware.
O Jakiego programu użyć do monitorowania ESP8266 za pomocą portu szeregowegojest ich wiele do wyboru, w zależności od potrzeb i preferencji; ostatnio coraz częściej sięgam po klasykę Śliczne Com (ten na zrzucie ekranu powyżej), ponieważ jest mi bardzo wygodnie powtarzać pewne Moduł Wi-Fi ESP8266 przy zamówieniach w testowaniu projektu.
Podano już tutaj pewne zalecenia dotyczące programów działających jako konsola szeregowa; Na przykład, gdy mówimy o PuTTY do sterowania urządzeniami szeregowymi UART z komputera. PuTTYOprócz tego, że jest doskonałą aplikacją, jest dostępna dla większości systemów operacyjnych dla komputerów stacjonarnych. Ponadto, jako PuTTY może służyć jako konsola zarówno z portem szeregowym, jak i Rodzina protokołów internetowych (TCP/IP), w tym te, na których działają TLS, staje się powszechnym narzędziem, które z nawiązką rekompensuje (niewielki) czas spędzony na jego konfigurowaniu i przyzwyczajaniu się do jego używania.
Oprócz oprogramowania do komunikacji szeregowej, połączyć Moduł Wi-Fi ESP8266 do portu USB Komputer wymaga również konwertera USB do serii TTL. Podobnie jak w przypadku oprogramowania, istnieje kilka wersji, z których służą one jedynie do konwersji portu USB na porcie szeregowym TTL (które można uzyskać za jedno euro) do tych, które mogą emulować różne protokoły (takie jak SPI o I2C).
Podobnie jak program, który działa jako konsola szeregowa, sprzęt do komunikacji z komputerem USB z obwodem logicznym (nie tylko ESP8266) będzie powszechnym narzędziem w pracy programisty aplikacji mikrosterowanych, warto mieć go jak najszybciej w przyborniku i pracować z nim Moduł Wi-Fi ESP8266 To doskonała okazja, żeby taki zdobyć.
Konwerter USB a UART TTL Można go również wykorzystać do monitorowania zachowania obwodu korzystającego z ESP8266w tym celu wyjścia, które chcesz monitorować, łączy się szeregowo z wejściem danych (RX) konwertera za pomocą szybkiej diody (tzw. 1N4148na przykład) i rezystor (na przykład 2K2) równolegle do siebie. Taka konfiguracja działa jak sprzętowy sniffer szeregowy.
Chociaż sniffer na powyższym obrazku jest z pewnością prymitywny (między innymi go nie ma bufor) wystarczy do monitorowania działania zespołu za pomocą Arduino i ESP8266.
Usunięcie sniffera z poprzedniego schematu, schemat pokazujący sposób podłączenia a Moduł Wi-Fi ESP8266 na talerz Arduino. Oprócz zasilania go napięciem 3V3, pin resetowania i pin aktywacji integry muszą być podłączone do wysokiego poziomu (włączone). Oczywiście pin RX jednego musi być połączony z pinem TX drugiego.
Aby uprościć poprzedni diagram, przedstawiono płytę Arduino zasilany napięciem 3V3 i dla którego zakłada się, że napięcie na porcie szeregowym również wynosi 3V3. Jeśli używasz A mikrokontroler z innym poziomem sygnału na porcie szeregowym (zwykle 5 V), aby nie uszkodzić ESP8266, użyć konwerter poziomów jak na poniższych schematach. Obwód ten często można znaleźć w wielu komercyjnych, gotowych implementacjach modułów.
Zaktualizuj oprogramowanie układowe ESP8266
Te Polecenia ESP8266 AT, jego zakończenie, domyślna prędkość modułu... zależą od wersji Oprogramowanie układowe ESP8266. Najlepiej zadbać o to, aby we wszystkich modułach mieć tę samą wersję i jeśli to możliwe, aby była to wersja najnowsza.
Niestety większość Modele modułów Wi-Fi ESP8266 Mają tylko 4Mbit, więc nie da się na nich zainstalować najnowszej wersji. Najnowsza (oficjalna) wersja oprogramowania sprzętowego, na którym można zainstalować Moduły Wi-Fi ESP8266 z 4 Mbit (większość) to 0.9.4, który obejmuje wersję 0.2 Polecenia ESP8266 AT.
Podsumowując, aby zaktualizować oprogramowanie, potrzebujesz:
-
Pobierz odpowiednią wersję oprogramowania sprzętowego. najnowsza (oficjalna) wersja modułu z 4Mbit pamięci, znaleziona w folderze Espressif na githubie. W stronie internetowej Espressif Można pobrać najnowszą wersję oprogramowania, jednak bardzo ważne jest sprawdzenie, czy moduł, na którym jest zainstalowany, ma wystarczającą ilość pamięci.
-
Pobierz najnowszą wersję narzędzia do instalacji oprogramowania sprzętowego. Moim ulubionym jest język w którym jest napisane Python, więc działa na każdej platformie. Oprócz tego, że można go pobrać, można go także zainstalować
pip install esptool
(opip2
opython -m pip
…). Oczywiście, ekspresyjny Oferuje również własne narzędzie, ale obecnie jest dostępne tylko dla systemu Windows. -
Przygotuj pobrane pliki; rozpakuj je w dostępnym folderze i, jeśli to konieczne, uczyń narzędzie wykonywalnym językw moim przypadku od GNU / Linux, Z
chmod +x esptool
-
Podłącz moduł do komputera za pomocą konwertera USB UART TTL który działa przy 3V3 lub użyj konwertera poziomów jeśli działa na 5 V. Oprócz zasilania będziesz musiał podłączyć TX do RX konwertera USB UART TTL, RX do TX, GPIO0 na niskim poziomie (GND) i być może GPIO2 na wysokim poziomie (w moich testach zadziałało zarówno podłączenie go na niskim poziomie, jak i rozłączenie). Jeżeli moduł ma wolne złącze GPIO15 (jak to ma miejsce w ESP-12) należy je podłączyć na poziomie niskim. RESET, który w trakcie pracy normalnie byłby na wysokim poziomie, można pozostawić niepodłączony lub podłączyć na wysoki poziom za pomocą rezystora (np. 10K), ponieważ przed rozpoczęciem nagrywania może zaistnieć konieczność zresetowania urządzenia poprzez jego podłączenie do niskiego poziomu.
Po włączeniu modułu będzie można go zaktualizować, ale Jeśli wyświetli się błąd połączenia, konieczne będzie jego zresetowanie podłączając na chwilę RESET na niskim poziomie, a następnie pozostawiając go na antenie (bez łączenia) w celu przeprowadzenia procesu aktualizacji.
Moduł posiada szczytowe zużycie prądu o wartości pół ampera (według niektórych użytkowników do 600 mA), dlatego ważne jest, aby używać zasilacza zdolnego obsłużyć takie zużycie, szczególnie w przypadku aktualizacji oprogramowania sprzętowego. -
Uruchom narzędzie, aby zaktualizować oprogramowanie sprzętowe. W moim przypadku zapisałem dokumenty narzędzia i oprogramowania sprzętowego z kroku 3 w tym samym folderze, więc uruchamiam z konsoli:
cd ~/Datos/firmwareESP8266
(przejdź do folderu zawierającego narzędzie i oprogramowanie sprzętowe)./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
ustawia prędkość ESP8266 (w moim przypadku 115200 bodów) i--port
port szeregowy, z którym się łączy (w moim przypadku emulowany, pierwszy USB). Różne dokumenty tworzące oprogramowanie sprzętowe pozostają w tylewrite_flash
poprzedzony adresem, z dokumentem user1.bin zawierającym ładunek aktualizacji.
Wysyłaj polecenia do modułu Wi-Fi ESP8266
Aby kontrolować ESP8266 z komputera, od którego będziemy musieli zacząć skonfigurować aplikację dla którego wystarczy ① wybrać port, do którego podłączony jest konwerter USB UART TTL, Coś jak /dev/USB0
w GNU/Linux i podobnym lub podobnym COM6
w systemie Windows, ② wybierz prędkość, z jaką ESP8266, prawdopodobnie 115200 bodów, ③ ustaw 8 bitów danych plus jeden bit stopu, bez parzystości i uzgadniania, oraz ④ ustaw koniec linii, w zależności od firmware, prawie zawsze CR+LF.
Po skonfigurowaniu aplikacji (lub, w stosownych przypadkach, zapisaniu i wybraniu) aplikacja jest gotowa otwórz połączenie (odpowiednio „otwarte urządzenie” i „otwarte” na zrzutach ekranu powyższych przykładów za pomocą Śliczne Com y PuTTY) i możesz zacząć wysyłać zamówienia do ESP8266.
Jak widać w Tabela poleceń ESP8266 AT, format aktywacji, dezaktywacji, ustawienia wartości i odwoływania się do niej jest dość przewidywalny, ale ogólnie nie jest łatwo zapamiętać je wszystkie i prawdopodobnie będziesz musiał mieć go pod ręką, aby się do niego odwołać.
Sposób wysłać W rozkazach al Moduł Wi-Fi ESP8266 z Arduino jest bardzo proste: ① skonfiguruj komunikację z Serial.begin(115200);
(lub Serial1, Serial2… na płytach z kilkoma sprzętowymi portami szeregowymi) i ② wyślij polecenia w formacie 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()
{
}
|
Powyższy przykład pokazuje, jak wysłać plik Moduł Wi-Fi ESP8266 przy zamówieniach z Arduino. W tym przypadku jest to zilustrowane AT+CWJAP
, który służy do łączenia się z punktem dostępu. To polecenie używa jako argumentów identyfikatora punktu dostępu (SSID) i klucz, oba w cudzysłowie, dzięki czemu stają się obiektem Srtring
i ujmij je w cudzysłów, używając kodu ucieczki (\"
). Aby sfinalizować zamówienie, użyj \r\n
co odpowiada CR
y LF
.
Należy pamiętać, że port szeregowy nie zawsze jest identyfikowany z Serial
(na niektórych talerzach może tak być Serial1
, Serial2
…) zdefiniowany został używany obiekt portu poprzez przypisanie go do makra PUERTO_SERIE
. Wykrycie typu użytej płytki mogłoby dodać trochę inteligencji do wyboru portu szeregowego; Później omówimy, jak sprawdzić typ Arduino. Reszta definicji to zwykłe definicje, które pozwalają „nazwać” wartości stałe, aby uniknąć ich powtarzania (i popełniania błędów) oraz ułatwić ich zmianę.
Powyższy przykład ma na celu połączenie Moduł Wi-Fi ESP8266 do wskazanego punktu dostępowego, ale czy był on już wcześniej podłączony? Czy połączenie zadziałało? Aby to wiedzieć, musimy „posłuchać” tego, co ESP8266
Odbierz dane z modułu Wi-Fi ESP8266
Podłączając opisany powyżej sniffer danych do komputera, możesz zobaczyć, co Arduino wysłał do ESP8266 i jego odpowiedź. Do przeczytania Arduino i przetwarzać zawarte w nim informacje, za pomocą których będą konieczne do wykrycia Serial.available()
czy dotarły jakieś dane i jeśli tak, załaduj je Serial.read()
. Poniższy przykład pokazuje, jak odczytać odpowiedź z AT+CWJAP?
, który będzie raportował, czy istnieje połączenie z dowolnym punktem dostępu.
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();
}
}
|
Jak na talerzu Arduino Uno (i w innych) otwarcie monitora szeregowego resetuje program, można go użyć do przeglądania konsoli szeregowej Arduino informacje, które wysyłasz ESP8266 jak pokazuje zrzut ekranu poniższego obrazu.
Przeanalizuj odpowiedź wysłaną przez moduł Wi-Fi ESP8266
Widzieliśmy już, jak czytać docierające informacje Arduino z ESP8266. Problem, z którym musisz się uporać, polega na tym, że nie wiesz, kiedy przesyłka zacznie docierać, ile czasu zajmie jej dotarcie, jaka będzie długość... Poza tym czekanie na odpowiedź ze strony klienta nie jest zbyt efektywne. ESP8266 jest odbierany bez pozwolenia mikrokontroler w międzyczasie wykonywać inne zadania.
Prostym sposobem poradzenia sobie z tą sytuacją jest iteruj na podstawie otrzymanych danych, szukając konkretnych odpowiedzi za pomocą którego można np. aktywować wskaźniki (flagi lub zmienne logiczne), które określą, czy kontynuować wyszukiwanie w otrzymanym tekście i jakie działania należy wykonać na podstawie informacji, które napływają z ESP8266. Gdy nadejdzie odpowiedź mikrokontroler może poświęcić się innym zadaniomna przykład odbieranie danych z czujników i ich przetwarzanie.
Wyszukaj tekst w informacji otrzymanej z ESP8266
Aby przeszukać tekst pochodzący z ESP8266 możesz porównaj każdy otrzymany list z tym, który odpowiada wiadomości, której szukasz. Konieczne będzie użycie licznika (lub wskaźnika) wskazującego literę do porównania; Jeśli znak pochodzący z ESP8266 jest taki sam jak ten sprawdzany w wiadomości, licznik przesuwa się, jeśli jest inny, zostaje zainicjowany.
Aby wiedzieć, że osiągnięto koniec, sprawdzany jest następny znak szukanej wiadomości, który będzie wynosił zero (\0
) lub zapamiętywana jest długość wiadomości, aby porównując ją z licznikiem dowiedzieć się, czy porównanie zostało zakończone i dlatego Moduł Wi-Fi ESP8266 wysłał żądaną wiadomość.
W poniższym przykładzie zastosowano polecenie AT+CWLAP
co zwróci listę punktów dostępowych, a w ich obrębie przeszukiwany będzie jeden o nazwie „wifi polaridad.es”. Chociaż zdecydowaliśmy się sprawdzić, czy ostatni znak ma wartość zero, ponieważ bufor Zapamiętuje jedynie szukany tekst i znana jest jego długość, można też sprawdzić, czy otrzymano taką liczbę poprawnych liter. Z DOPROWADZIŁO podłączony do pinu 2, pojawia się raport, że odnaleziono oczekiwany tekst.
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
}
}
}
}
|
W kodzie z poprzedniego przykładu widać także sposób wybierz port szeregowy w zależności od typu płytki Arduino używany. W tym przykładzie założono, że w projekcie masz trzy typy desek: jedną Arduino Uno, Arduino Mega2560 i a Arduino Leonardo. Jeśli pracujesz z Arduino Uno będzie używany Serial
i inaczej Serial1
.
Jeśli pracujesz z talerzem Arduino Leonardo Możesz użyć tej samej metody, aby zatrzymać program i poczekać, aż konsola (port szeregowy powiązany z Serial
) jest dostępny.
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
|
Wyszukaj różne teksty w odpowiedzi ESP8266
Kod z poprzedniego przykładu służy do wyszukiwania tekstu w informacjach przesyłanych przez ESP8266 ale odpowiedź może zawierać różne informacje w zależności od operacji. Załóżmy, zaczynając od prostego przypadku w następnym przykładzie, że tekst wysłany przez MCU ESP8266 es OK
kiedy operacja zostanie wykonana prawidłowo i ERROR
W przeciwnym razie zgodnie z zamówieniem AT+CWJAP?
, który służy do sprawdzenia, czy Moduł Wi-Fi ESP8266 jest już podłączony do punktu dostępowego.
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
}
}
}
}
}
|
Ta nowa implementacja tej samej metody, która wyszukuje dopasowania z kilkoma możliwymi wiadomościami, pozwala wybierać pomiędzy różnymi działaniami w zależności od odpowiedzi otrzymanej od ESP8266, po prostu włącz DOPROWADZIŁO odpowiedni.
Ogranicz czas potrzebny na otrzymanie odpowiedzi
Jak dotąd nie wspomniano o istotnej kwestii: maksymalny czas oczekiwania (limit czasu) przed uznaniem operacji za nieudaną. Jeśli z jakiegoś powodu połączenie z Moduł Wi-Fi ESP8266, moduł z punktem dostępowym, punkt dostępowy z Internetem lub np. hipotetyczny serwer nie jest dostępny, program może zostać w pewnym momencie zablokowany i czekać w nieskończoność, więc trzeba będzie odpowiednio zareagować na takie okoliczności. Maksymalny czas oczekiwania można skonfigurować dla całej aplikacji, zazwyczaj będzie on wtedy bardziej „hojny” lub można zaprogramować indywidualne czasy oczekiwania dla każdej operacji.
Aby sprawdzić, czy (przynajmniej) upłynął określony przedział czasu „Czas” momentu uruchomienia konta jest zwykle odejmowany od aktualnego „czasu” i sprawdza się, czy różnica jest większa niż pożądany limit. Ten „czas” nie musi być czasem rzeczywistym, zwykle odpowiada odstępowi, jaki upłynął od momentu MCU zacznij odliczać czas; Nie ma to wpływu na program, ponieważ interesujący jest czas, który upłynął, a nie czas bezwzględny.
Zwykle, aby sprawdzić, czy upłynął określony przedział czasu, stosuje się wyrażenie typu:
1
|
(unsigned long)(millis()–milisegundos_al_empezar)>intervalo_de_tiempo
|
Niech zmienna milisegundos_al_empezar
zawiera wartość millis()
od określonego momentu wykonania, od którego jest liczony, dlatego nie jest niczym niezwykłym, że jego nazwa nawiązuje do słowa „chronometr”. Zmienna intervalo_de_tiempo
zawiera maksymalną liczbę milisekund, przez którą poprzednie wyrażenie jest prawdziwe, czyli reprezentuje limit czasu; Zwykle jest to stała (lub makro) i tak jak w poprzednim przypadku często w jej nazwie pojawia się słowo „TIMEOUT”. Jeśli pracujesz z bardzo krótkimi przerwami, możesz użyć micros()
zamiast millis()
(mikrosekundy zamiast milisekund), chociaż jest to znacznie mniej powszechne i znacznie mniej precyzyjne.
1
|
(unsigned long)(millis()–cronometro)>TIMEOUT
|
Długa liczba całkowita Arduino (unsigned long
) zajmuje 4 bajty (32 bity), więc największa wartość, jaką może reprezentować, to 4294967295 (2 do potęgi 32 minus jeden, ponieważ zaczyna się od zera). na talerzu Arduino Podczas ciągłej pracy licznik milisekund będzie resetowany (powraca do zera) mniej więcej co 50 dni. Podczas odejmowania typów danych bez znaku odtwarzane jest to samo zachowanie (odwracanie licznika), dzięki czemu możliwe jest kontrolowanie limitu czasu w nieskończoność.
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;
}
}
}
|
Powyższy kod pokazuje a bardzo podstawowa implementacja ograniczenia limitu czasu włączając linie zaznaczone w odniesieniu do poprzedzającego przykładu. Ponieważ weryfikacja limitu czasu odbywa się po przetworzeniu danych przychodzących z Moduł Wi-Fi ESP8266, operację można uznać za udaną nawet jeśli odbiór trwa dłużej niż narzucony czas oczekiwania.
Wykonaj złożoną operację zdefiniowaną przez wiele poleceń AT
Aby mieć przykładowe odniesienie do celu aplikacji wykorzystującej Moduł Wi-Fi ESP8266, załóżmy, że tak przechowuje informacje w bazie danych dostępnej za pośrednictwem usługi internetowej aby śledzić temperaturę. Poniższy kod co określony czas odczytuje czujnik podłączony do wejścia analogowego, oblicza średnią wartość i po dłuższym czasie wysyła ją na serwer WWW (w stylu Internet przedmiotów) przez A prośba HTTP (POST, POBIERZ…).
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”);
}
}
|
W tym przykładzie rejestracji temperatury dostęp do serwera internetowego jest uzyskiwany co pięć minut. Chociaż dostępność nie jest szczególnie wysoka, można oczekiwać, że propozycja zadziała, jednak gdyby konieczna była większa częstotliwość rejestracji, konieczne byłoby wdrożenie innych zasobów, na przykład bufor danych czeka na wysłanie, aby wysłać kilka, gdy serwer będzie mógł obsłużyć, i przechowywać je na wypadek, gdy nie będzie dostępny. Jeżeli częstotliwość, z jaką dane muszą być rejestrowane, byłaby jeszcze większa, należałoby zaproponować inne typy protokołów jako alternatywę dla HTTP lub nawet wymienić TCP przez UDP aby móc wysłać większość danych z wymaganą szybkością, nawet za cenę ich utraty.
Operacje składające się na zadanie, które należy wykonać w celu przesłania temperatury, to:
- Zresetuj moduł Wi-Fi
- Rozłącz się z bieżącym punktem dostępu (jeśli istnieje połączenie domyślne)
- Skonfiguruj ustawienia. Dla przykładu zakłada się, że należy skonfigurować tryb połączenia (prosty) i rolę w komunikacji Wi-Fi (stacja).
- Połącz się z punktem dostępowym
- Sprawdź, czy połączenie jest prawidłowe (właściwie jest to punkt wejścia) Jeśli nie ma połączenia, rozpocznij proces od początku
- Połączyć się z serwerem
- Wyślij prośbę HTTP z danymi, które mają być przechowywane
Kolejność operacji nie musi być dokładnie taka (chociaż operacja jest), a każdy krok może wymagać kilku Polecenia ESP8266 ATNa przykład konfiguracja wymieniona powyżej wymagałaby dwóch: AT+CIPMUX=0
y AT+CWMODE=1
.
Struktura danych reprezentująca operacje na ESP8266
W poprzednich przykładach, choć w bardzo prosty sposób, zasugerowano już ogólne rozwiązanie problemu: użyj struktury danych, która przechowuje możliwe odpowiedzi i działania, które należy podjąć w każdym przypadku; wyślij akcję, poczekaj na odpowiedź i postępuj zgodnie z jej znaczeniem. Ponieważ każda złożona operacja będzie wymagała kilku Polecenia ESP8266 AT, struktura danych musi łączyć operację z innymi, późniejszymi lub wcześniejszymi, które należy wykonać w każdym przypadku w zależności od odpowiedzi ESP8266.
W poprzednich przykładach wyszukiwana była wiadomość w odpowiedzi typu ESP8266 i interpretowano to jako sukces lub błąd. Oprócz odbioru (i analizy) całego otrzymanego tekstu, Aby uzyskać ogólne minimum, zaleca się również zadbanie o uzupełnienie wiadomości lub innymi słowy, od dostępności Moduł Wi-Fi ESP8266 otrzymać nowe zamówienia. W ten sposób zmiana stanu, który moglibyśmy nazwać np. „wifi dostępne”, mogłaby polegać na otrzymaniu nazwy punktu dostępowego i otrzymaniu SMS-a ERROR
lub tekst OK
oznaczałoby, że ESP8266 skończyłeś odpowiadać i możesz teraz wysłać następną Polecenie AT do 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;
|
Powyższy kod wykorzystuje wektor (operacion
) do przechowywania tekstu kolejnych operacji tworzących kompletne zadanie. Używana jest tablica dwuwymiarowa (mensaje
) z trzema analizowanymi odpowiedziami. Jak wyjaśniono powyżej, oprócz komunikatu reprezentującego poprawną lub niepoprawną odpowiedź należy szukać komunikatów reprezentujących koniec odpowiedzi. Nie wszystkie operacje będą miały tę samą liczbę możliwych odpowiedzi; W przypadku mniejszej liczby odpowiedzi można zastosować pusty komunikat, który zajmie w swojej analizie możliwie najmniejszą liczbę cykli (mimo to nie jest to najbardziej optymalny sposób). Logicznie rzecz biorąc, konieczne będzie, aby minimalna liczba poszukiwanych odpowiedzi (w przykładzie trzy) obejmowała wszystkie możliwości operacyjne, nawet jeśli nie wszystkie są możliwe.
Mówiąc o możliwych odpowiedziach, można już zauważyć, że ten przykład nie jest zbyt przydatny do otrzymywania danych o dowolnym formacie z Moduł Wi-Fi ESP8266, ale chodzi o to, że w kontekście użycia with mikrokontrolery to nie jest normalne; Najczęściej jest to przesyłanie danych zebranych przez podłączone do nich czujniki i/lub otrzymywanie informacji o tym, co zrobić ze sterowanymi przez nie elementami wykonawczymi. Bardzo cenne informacje, które można bardzo dobrze przewidzieć.
W poprzedniej strukturze danych, tak jak robi się to w celu wyrażenia możliwych odpowiedzi, które są analizowane, tak i dwuwymiarowa macierz służy do określenia operacji, którą należy wykonać w każdym przypadku (siguiente_operacion
). W szczególności zdecydowaliśmy się odpowiedzieć na trzy typy wiadomości: ① dowolny tekst (LITERAL
) w celu sprawdzenia, czy istnieje połączenie z punktem dostępowym Wi-Fi i serwerem, ② tekst wykrywający błędy w procesie (FALLO
) oraz ③ tekst wskazujący, że operacja została pomyślnie zakończona (ACIERTO
).
Na koniec istnieją jeszcze dwa wektory określające maksymalny czas oczekiwania przed poddaniem się (timeout
) i określ (configuracion
), jeśli operacja zakończy się bez oczekiwania na odpowiedź (ESPERAR_RESPUESTA
) oraz komunikaty informujące o zakończeniu komunikacji. Ten ostatni wektor, aby zilustrować przykład oszczędzania pamięci, współpracuje z bitami bajtu konfiguracyjnego, aby wskazać różne stany.
Pierwszy Polecenia ESP8266 AT struktury danych zawsze oczekuj odpowiedzi, która może być komunikatem o powodzeniu lub błędzie. W przypadku wystąpienia błędu moduł zostaje zrestartowany i uruchamia się od nowa i jeżeli pojawi się komunikat wskazujący, że operacja została wykonana poprawnie, przechodzi do kolejnej.
Po połączeniu się z serwerem wzór się zmienia. W takim przypadku konieczne jest ① przesłanie długości pakietu danych do przesłania i ② zredagowanie żądania HTTP ze stałym tekstem plus wartość (temperatury), która jest wysyłana do przechowywania na serwerze. Przygotowanie tych danych odbywa się w każdej przesyłce i konieczne jest podzielenie ich na dwie (podaj długość) lub trzy (wyślij zapytanie HTTP) Aby ESP8266 na zamówienie. Na odpowiedź będzie czekać tylko ostatnia z części, na które podzielona jest operacja.
W tym przypadku zadziała to bez problemów (być może ostrzeże, że moduł jest zajęty), jednak przy większej długości danych konieczne będzie podzielenie bloków danych na mniejsze części, a nawet może zaistnieć konieczność zastosowania oczekiwania, gdyż odbywa się na podstawie odczytu temperatury, aby dać modułowi czas na przesłanie danych bez ich uzupełniania bufor.
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();
|
Wraz z innymi makrami, które zostały już wcześniej wyjaśnione, powyższy przykładowy kod pokazuje, w jaki sposób zdefiniowane są różne stany, za pomocą których można określić, czy czekać na odpowiedź i, jeśli ma to zastosowanie, jaki komunikat wskazuje, że została ona zakończona.
Podobnie jak w różnych punktach kodu, operacja zostanie wysłana (kiedy nadejdzie czas wysłania średniej temperatury, jeśli przekroczony zostanie czas oczekiwania operacji, kiedy bieżąca operacja zostanie pomyślnie zakończona...), ale jak to zrobić ustalony globalnie, został zdefiniowany jako makro ENVIAR_OPERACION
który grupuje etapy związane z wysyłką.
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();
|
Poniżej znajduje się kod głównego programu przykładu. Najbardziej zewnętrznym zadaniem jest pobieranie próbek temperatury w celu obliczenia średniej i co pewien okres czasu jest ona wysyłana do serwera za pomocą metody Moduł Wi-Fi ESP8266. Po wysłaniu każdej operacji odpowiedź jest analizowana w celu ustalenia, która jest następna lub czy zadanie wysłania informacji zostało zakończone.
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
}
}
}
|
Logicznie rzecz biorąc, na poprzednim kodzie można przeprowadzić kilka działań optymalizacyjnych, ale ponieważ jest to przykład pozwalający zrozumieć, w jaki sposób ESP8266 Ogólnie rzecz biorąc, warto skupić się tylko na niektórych aspektach, z których pierwszym jest struktura danych. Wydaje się, że jest to logiczne użyj struktury danych języka programowania (struct
) do reprezentowania przetwarzanych informacji: the Polecenia ESP8266 AT i wiadomości, które są analizowane.
Użyj struktury (struct
) przechowywanie danych zamiast przykładowych tablic (w oparciu o nie) jest banalne i choć może skutkować bardziej eleganckim kodem, nie oznacza to żadnej poprawy wyniku. Prawdziwa alternatywa wynikająca z użycia struct
ma na celu wdrożenie, jak wyjaśniono poniżej, zmienne długości w strukturach zawierających dane „wewnętrzne”. o których mowa. W ten sposób na przykład nie byłoby konieczne, aby operacja miała stałą liczbę analizowanych odpowiedzi.
Podejście to sugeruje, że jest to najlepszy sposób wdrożenia rozwiązania, ale wadą jest to, że byłoby konieczne użyj dynamicznej alokacji pamięci, co jest ryzykowną praktyką w pracy z plikami a mikrokontroler co wymaga dokładnego pomiaru ilości pamięci używanej w czasie wykonywania, ponieważ kompilator prawie nie będzie w stanie nas o tym ostrzec i istnieje pewna możliwość wyczerpania pamięci (lub stosu) z fatalnymi konsekwencjami dla wykonania programu.
W kwestii optymalizacji kodu warto pamiętać, że w programie tego typu, który wykorzystuje dużą ilość tekstu, może zaoszczędzić miejsce w pamięci SRAM przechowywanie ciągów tekstowych w pamięci programu (lampa błyskowa) z makro F()
. Na poniższych zrzutach ekranu możesz zobaczyć inny program i dynamiczną dystrybucję pamięci przy normalnym użyciu tekstu i użyciu makra F()
.
W odniesieniu do działań, które są wykonywane zgodnie z informacjami otrzymanymi z Moduł Wi-Fi ESP8266, jako alternatywę do sprawdzania komunikatu z kodu i wykonywania jednego lub drugiego w zależności od tego, co zostało odebrane, może być przechowywane w tej strukturze danych wskaźniki do funkcji wykonujących każde zadanie zamiast wskaźników stanu (flagi) ostrzegające o pewnym stanie, za zarządzanie którym odpowiada aplikacja, np. w obrębie pętli głównej.
Poniżej znajduje się przykład struktur do przechowywania danych żądań do ESP8266 (typ danych operacion_esp8266
) i ich odpowiedzi (typ danych 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;
|
Ponieważ struktura reprezentująca operację (dane wysyłane do Moduł Wi-Fi ESP8266) odnosi się do struktury, za pomocą której definiowane są odpowiedzi, a struktura odpowiedzi do struktury operacji, należy najpierw zadeklarować oba, definiując nowy typ danych, a następnie definiując jego zawartość.
W poprzednim przykładzie uwzględniono, że program, który go zawiera, zdecydował się użyć a wskaźnik stanu, który musi odpowiadać zmiennej dostępnej z kodu odpowiedzialnej za wykonanie jednej lub innych operacji wskazanych przez wspomnianą wartość. Jeżeli w odpowiedzi ESP8266 Kiedy analizowany jest określony tekst, stan przyjmuje wartość wskazującą strukturę odpowiedniej odpowiedzi.
Jak powiedziano wcześniej, inną alternatywą byłoby zastąpienie lub uzupełnienie wskaźnika stanu przechowuj funkcję w strukturze referencyjnej (wskaźnik), który zostanie wywołany po napotkaniu określonego tekstu w odpowiedzi z Moduł 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;
|
W poprzednim przykładzie został on dodany do struktury danych, która służy do przetwarzania odpowiedzi z pliku Moduł Wi-Fi ESP8266 wskaźnik do (rzekomej) funkcji zwracającej dane typu float
(może to być wartość ważona odczytu analogowego) i do którego dostarczane są dwa bajty jako argumenty (dwa unsigned char
który mógłby być pinem, z którego odczytywane jest wejście analogowe i tym, który aktywuje ENABLE hipotetycznej integry).
W opracowaniu dla MCU, w przeciwieństwie do tego, co ma miejsce w stylu programowania dla większych systemów, nierzadko używa się zmiennych globalnych przy definiowaniu (globalnego) zachowania aplikacji sterującej zespołem, więc nie będzie szczególnie rzadko spotykane definicje tego typu jako funkcje bez parametrów i które nie zwracają wartości, coś w tym stylu void (*accion)();
Jeśli pracujesz z tym sposobem reprezentowania danych, użyj struct
danych o zmiennej długości, konieczne będzie dynamiczne przydzielanie pamięci malloc()
(o new()
, jeśli używane są obiekty), który użyje ilości przydzielonej pamięci jako parametru i zwróci wskaźnik do początku zarezerwowanego obszaru pamięci. Z sizeof()
Na podstawie rodzaju przechowywanych danych, pomnożonego przez liczbę użytych elementów, można uzyskać ilość potrzebnej pamięci. Przykład z jego użyciem i bez niego można zobaczyć na zrzutach ekranu poniżej. malloc()
; Uważaj na pamięć używaną przez program, w pierwszym przypadku musisz załadować bibliotekę zawierającą tę funkcję.
Jeśli operacje na Moduł Wi-Fi ESP8266 będzie się zmieniać w trakcie wykonywania programu, konieczne będzie zwolnienie nieużywanej pamięci free()
(o delete()
w przypadku obiektów). Chociaż rozsądne jest oczekiwanie, że kompilator (GCC) zoptymalizuje program, aby uniknąć partycjonowania pamięci, z pewnością wydajność nie będzie tak optymalna, jak w przypadku pracy z pamięcią alokowaną statycznie.
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
}
}
}
|
Choć w tym przykładzie (w obu realizacjach) nie ma to większego sensu, to aby uogólnić operację, aby móc zastosować ją w innych przypadkach, należy zauważyć, że wysyłanie danych zawsze powtarza ten sam protokół: powiadom liczbę bajtów, które zostaną wysłane, poczekaj na wskaźnik (>) i wyślij dane.
Ponieważ w tym przykładzie jest on używany tylko jednorazowo (całe żądanie jest składane w jednym pakiecie), nie wydaje się to zbyt przydatne, ale ogólnie może być konieczne wykonanie kilku wysyłek w tej samej operacji, także w przypadkach, w których muszą być przesyłane znaczne ilości danych, które muszą zostać pofragmentowane, aby uniknąć przepełnienia pamięci ESP8266.
Aby zaimplementować to zachowanie, można wykorzystać dwa ostatnie elementy połączenia, dzięki czemu za każdym razem, gdy dane zostaną wysłane, dane zostaną wypełnione odpowiednimi wartościami: w pierwszym przypadku liczbą wysłanych bajtów, a w drugim ( część) żądania.
Aby powtórzyć przypisanie i wysłanie różnych elementów, które muszą zostać przesłane, można je zapisać w wektorze. Ten nowy wektor będzie tym, który określi koniec złożonej operacji, a nie ostatnią operacją, jak dotychczas.
1 komentarz