Osnovne operacije na wifi modulu ESP8266 iz Arduina
Pri Espressif lansirali prve module na trg Wifi z integriranim ESP8266 in firmware s katerim bi ga upravljali z uporabo ukazov AT, kar nas je zanimalo uporabnike, je bila integracija v sklope s mikrokrmilniki in težave so se zmanjšale na poznavanje (nekdanje) teme Tabela ukazov ESP8266 AT, potrebe po hranjenju oz Posodobitev vdelane programske opreme ESP8266.
Nato so hitro prispele alternative za programiranje ESP8266 in izvedbe modulov Wifi zelo različnih formatov, ki so sprožili druge pomisleke: kateri wifi modul ESP8266 izbrati odvisno od obsega različnih anten (vključno z zunanjimi) ali fizične integracije teh novih modulov v naše sklope.
Zagotovo zaradi vseh teh sprememb morda ni bil poudarek na najosnovnejših vidikih, najosnovnejšem upravljanju ESP8266 wifi modul. Čeprav polarnost.es Informacije o uporabi lahko najdete ESP8266 in obstaja nekaj aplikacij, ki so namenjene splošni razlagi delovanja ESP8266 wifi modul z uporabo ukazov AT, zlasti v članku o knjižnico za izdelavo poizvedb HTTP iz Arduina z wifi modulom ESP8266, vtisi bralcev kažejo, da bi bilo koristno dodati še nekaj osnovnih informacij za pomoč uporabnikom ESP8266 izvajati lastne izvedbe.
Pogovorite se o osnovnih operacijah za delo z ESP8266 in predlaganje splošnih rešitev je cilj več zelo različnih delov; Za lažje spremljanje vsebine članka vam lahko kot vodilo služi naslednje kazalo:
- Upravljajte wifi modul ESP8266 iz računalnika prek serijskih vrat
- Posodobite vdelano programsko opremo z esptool
- Pošljite naročila v modul
- Prejemanje podatkov iz ESP8266
- Analizirajte odgovor z iskanjem besedil v vsebini
- Omejite čakalni čas za prejem odgovora
- Izvedite zapleteno operacijo, ki jo definira več ukazov AT
Upravljajte wifi modul ESP8266 iz računalnika prek serijskih vrat
Iz krožnika Arduino in z uporabo vašega IDE je mogoče spremljati delovanje a ESP8266 wifi modul, pošljite Ukazi ESP8266 AT in si oglejte odgovor, vendar je veliko bolj priročno to narediti iz računalnika s terminalsko aplikacijo.
Odvisno na kateri plošči Arduino uporabljena, so lahko na voljo samo ena serijska vrata strojne opreme, kar dodaja malo neprijetnosti pri pošiljanju in prejemanju. Spreminjanje komunikacijske hitrosti je veliko bolj udobno v serijski komunikacijski aplikaciji iz računalnika in nekaterih matičnih plošč. Arduino (in v nekaterih okoliščinah) ne podpirajo dobro najvišjih hitrosti serijskih komunikacij, zlasti 115200 baud, kar je privzeta hitrost najnovejših različic firmware.
O Kateri program uporabiti za spremljanje ESP8266 uporabo serijskih vrat, lahko izbirate med številnimi glede na potrebe in želje; zadnje čase bolj uporabljam klasiko CuteCom (tisti na zgornjem posnetku zaslona), ker mi je zelo udobno ponavljati določene ESP8266 wifi modul AT naročila pri testiranju projekta.
Tukaj je bilo že podanih nekaj priporočil o programih, ki delujejo kot serijska konzola; Na primer, ko govorimo o PuTTY za krmiljenje serijskih naprav UART iz računalnika. PuTTYPoleg tega, da je odlična aplikacija, je na voljo za večino namiznih operacijskih sistemov. Poleg tega, kot PuTTY se lahko uporablja kot konzola tako s serijskimi vrati kot z Družina internetnih protokolov (TCP/IP), vključno s tistimi, ki delujejo na TLS, postane običajno orodje, ki več kot poplača (malo) časa, porabljenega za konfiguracijo in navajanje na njegovo uporabo.
Poleg programske opreme za serijske komunikacije, za povezavo ESP8266 wifi modul do pristanišča USB Računalnik zahteva tudi pretvornik USB do serije TTL. Tako kot v primeru programske opreme obstaja več različic, iz katerih se uporabljajo samo za pretvorbo vrat USB na serijskih vratih TTL (ki jih je mogoče dobiti od enega evra) do tistih, ki lahko posnemajo različne protokole (kot npr SPI o I2C).
Tako kot program, ki deluje kot serijska konzola, strojna oprema za komunikacijo z računalnikom USB z logičnim vezjem (ne samo z ESP8266) bo običajno orodje pri delu razvijalca mikrokrmiljenih aplikacij, zato ga je vredno imeti čim prej v orodju in delati z njim ESP8266 wifi modul To je odlična priložnost, da ga dobite.
Pretvornik USB a UART TTL Uporablja se lahko tudi za spremljanje obnašanja vezja, ki uporablja ESP8266Če želite to narediti, so izhodi, ki jih želite spremljati, zaporedno povezani s podatkovnim vhodom (RX) pretvornika s hitro diodo ( 1N4148, na primer) in upor (2K2, na primer) vzporedno drug z drugim. Takšna nastavitev deluje kot serijsko vohanje strojne opreme.
Čeprav je sniffer na zgornji sliki zagotovo rudimentaren (med drugim nima varovalni) zadostuje za spremljanje delovanja sklopa z Arduino in ESP8266.
Odstranitev snifferja iz prejšnje sheme, the shema, ki prikazuje, kako povezati a ESP8266 wifi modul na krožnik Arduino. Poleg napajanja pri 3V3 morata biti zatič za ponastavitev in zatič za aktiviranje integriranega priključena na visoko raven (omogoči). Seveda se mora RX pin enega povezati s TX drugega.
Za poenostavitev prejšnjega diagrama je bila predstavljena plošča Arduino napaja pri 3V3 in za katerega se predvideva, da je tudi napetost na serijskih vratih 3V3. Če uporabljate a mikrokrmilnik z drugačnim nivojem signala na serijskih vratih (običajno 5 V), da ne poškodujete ESP8266, uporabi nivojski pretvornik kot tiste v spodnjih diagramih. To vezje pogosto najdemo v mnogih komercialnih standardnih izvedbah modulov.
Posodobite vdelano programsko opremo ESP8266
The Ukazi ESP8266 AT, njegova prekinitev, privzeta hitrost modula ... je odvisno od različice Vdelana programska oprema ESP8266. Najbolje je zagotoviti, da imate v vseh modulih enako različico in, če je mogoče, najnovejšo različico.
Na žalost večina Modeli modulov wifi ESP8266 Imajo samo 4 Mbit, zato najnovejše različice ni mogoče namestiti nanje. Najnovejša (uradna) različica vdelane programske opreme, ki jo je mogoče namestiti ESP8266 wifi moduli s 4 Mbit (večina) je 0.9.4, ki vključuje različico 0.2 Ukazi ESP8266 AT.
Če povzamemo, za posodobitev vdelane programske opreme potrebujete:
-
Prenesite ustrezno različico vdelane programske opreme. zadnja (uradna) različica za modul, ki ima 4 Mbit pomnilnika, se nahaja v mapi Espressif na githubu. V Spletna stran Espressif Lahko prenesete najnovejšo različico vdelane programske opreme, vendar je zelo pomembno, da preverite, ali ima modul, na katerem je nameščena, dovolj pomnilnika.
-
Prenesite najnovejšo različico orodja za namestitev vdelane programske opreme. Moja najljubša je esptool ki je zapisan v Python, zato deluje na kateri koli platformi. Poleg tega, da ga lahko prenesete, ga lahko tudi namestite z
pip install esptool
(opip2
opython -m pip
…). Seveda, Espressif Ponuja tudi lastno orodje, vendar je trenutno na voljo samo za Windows. -
Pripravite prenesene datoteke; jih razpakirajte v dostopno mapo in po potrebi naredite orodje izvršljivo esptool, v mojem primeru od GNU / Linux, Z
chmod +x esptool
-
Povežite modul z računalnikom s pomočjo pretvornika USB UART TTL ki deluje pri 3V3 ali uporabite nivojski pretvornik, če deluje pri 5 V. Poleg napajanja boste morali priključiti TX na RX pretvornika USB UART TTL, RX v TX, GPIO0 na nizki ravni (GND) in morda GPIO2 na visoki ravni (v mojih preizkusih je delovalo tako pri povezovanju na nizki ravni kot prekinitvi povezave). Če ima modul prost priključek GPIO15 (kot se zgodi v ESP-12), mora biti priključen na nizko raven. RESET, ki bi bil običajno med delovanjem na visokem nivoju, lahko pustite nepovezan ali ga povežete na visok nivo s pomočjo upora (na primer 10K), saj bo morda treba pred začetkom snemanja ponastaviti napravo tako, da jo priključite na nizko raven.
Z vklopom modula bo na voljo za posodobitev, vendar Če se prikaže napaka povezave, jo bo treba ponastaviti povezavo RESET na nizki ravni za trenutek in nato pustite v zraku (brez povezave) za postopek posodobitve.
Modul ima pol ampera konice porabe (do 600 mA, po mnenju nekaterih uporabnikov), zato je pomembno, da uporabite napajalnik, ki lahko podpira to porabo, zlasti za posodabljanje vdelane programske opreme. -
Zaženite orodje za posodobitev vdelane programske opreme. V mojem primeru sem orodje in dokumente vdelane programske opreme v 3. koraku shranil v isto mapo, zato zaženem s konzole:
cd ~/Datos/firmwareESP8266
(sprememba v mapo z orodjem in vdelano programsko opremo)./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
nastavi hitrost ESP8266 (115200 baud v mojem primeru) in--port
serijska vrata, na katera se poveže (v mojem primeru emulirano, prvi USB). Zadaj so različni dokumenti, ki sestavljajo vdelano programsko opremowrite_flash
pred katerim je naslov, z dokumentom user1.bin, ki vsebuje vsebino posodobitve.
Pošlji ukaze modulu wifi ESP8266
Za nadzor nad ESP8266 iz računalnika, s katerim bomo morali začeti konfigurirajte aplikacijo za kar bo dovolj, da ① izberete vrata, na katera je pretvornik priključen USB UART TTL, nekaj kot /dev/USB0
v GNU/Linuxu in podobno ali kaj podobnega COM6
v sistemu Windows ② izberite hitrost, s katero se ESP8266, verjetno 115200 baudov, ③ nastavite 8 podatkovnih bitov in en stop bit, brez paritete ali rokovanja, in ④ nastavite konec vrstice, odvisno od firmware, skoraj vedno CR+LF.
Ko je aplikacija konfigurirana (ali, kjer je primerno, shranjena in izbrana), je odprite povezavo (»odprta naprava« oziroma »odprto« na posnetkih zaslona zgornjih primerov z CuteCom y PuTTY) in že lahko začnete pošiljati naročila na ESP8266.
Kot je razvidno iz Tabela ukazov ESP8266 AT, je oblika za aktiviranje, deaktiviranje, nastavitev vrednosti in sklicevanje nanjo precej predvidljiva, vendar si jih na splošno ni lahko zapomniti vseh in verjetno jo boste morali imeti pri roki, da se boste nanjo sklicevali.
Način pošlji AT naročila al ESP8266 wifi modul iz Arduino Je zelo preprosto: ① konfigurirajte komunikacije z Serial.begin(115200);
(ali Serial1, Serial2… na ploščah z več serijskimi vrati strojne opreme) in ② pošljite ukaze v formatu 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() { } |
Zgornji primer prikazuje, kako poslati ESP8266 wifi modul AT naročila iz Arduino. V tem primeru je prikazano AT+CWJAP
, ki se uporablja za povezavo z dostopno točko. Ta ukaz kot argument uporablja identifikator dostopne točke (SSID) in ključ, oba v narekovajih, tako da postaneta predmet Srtring
in jih zapiči v narekovaje z ubežno kodo (\"
). Za dokončanje naročila uporabite \r\n
ki ustreza CR
y LF
.
Ne pozabite, da se serijska vrata ne identificirajo vedno z Serial
(na določenih tablicah lahko Serial1
, Serial2
…) uporabljeni objekt vrat je bil definiran z dodelitvijo makru PUERTO_SERIE
. Zaznavanje vrste uporabljene plošče bi lahko dodalo malo inteligence k izbiri serijskih vrat; Kasneje bomo preučili, kako lahko ugotovite vrsto Arduino. Preostale definicije so običajne, ki vam omogočajo "poimenovanje" konstantnih vrednosti, da se izognete njihovemu ponavljanju (in delanju napak) in olajšate njihovo spreminjanje.
Zgornji primer naj bi povezoval ESP8266 wifi modul na navedeno dostopno točko, vendar je bila že prej povezana? Je povezava delovala? Da bi to vedeli, moramo »poslušati«, kaj ESP8266
Prejemanje podatkov iz wifi modula ESP8266
Če na računalnik povežete vohljalnik podatkov, ki je opisan zgoraj, lahko vidite, kaj Arduino je poslal na ESP8266 in njegov odziv. Za branje iz Arduino in obdelati informacije v njem, s katerimi bo potrebno zaznati Serial.available()
če so prispeli kakršni koli podatki in jih naložite Serial.read()
. Naslednji primer prikazuje, kako prebrati odgovor iz AT+CWJAP?
, ki bo poročal, če obstaja povezava s katero koli dostopno točko.
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(); } } |
Kot na krožniku Arduino Uno (in v drugih) odpiranje serijskega monitorja ponastavi program, lahko ga uporabite za ogled v serijski konzoli Arduino informacije, ki jih pošljete ESP8266 kot prikazuje posnetek zaslona spodnje slike.
Analizirajte odgovor, ki ga je poslal wifi modul ESP8266
Videli smo že, kako brati informacije, ki dosežejo Arduino iz ESP8266. Težava, s katero se morate soočiti, je, da ne veste, kdaj bo začelo prihajati, koliko časa bo trajalo, kako dolgo bo ... in ni zelo učinkovito čakati na odgovor od ESP8266 je prejet, ne da bi pustil mikrokrmilnik medtem opravljajo druge naloge.
Preprost način za obvladovanje te okoliščine je ponavljajo prejete podatke in iščejo konkretne odgovore s katerimi na primer aktivirate indikatorje (zastavice ali logične spremenljivke), ki bodo določali, ali nadaljevati iskanje v prejetem besedilu in katera dejanja je treba izvesti glede na informacije, ki prihajajo iz ESP8266. Medtem ko pride odgovor mikrokrmilnik se lahko posveti drugim nalogam, na primer prejemanje podatkov iz senzorjev in njihovo obdelavo.
Poiščite besedilo v informacijah, prejetih od ESP8266
Za iskanje besedila, ki prihaja iz ESP8266 lahko primerjajte vsako prejeto pismo s tistim, ki ustreza sporočilu, ki ga iščete. Uporabiti bo treba števec (ali kazalec), ki kaže na črko, ki jo je treba primerjati; Če lik, ki prihaja iz ESP8266 je enak tistemu, ki se pregleduje v sporočilu, števec napreduje, če je drugačen, se inicializira.
Če želite vedeti, da je bil dosežen konec, se pregleda naslednji znak iskanega sporočila, ki bo nič (\0
) ali pa se dolžina sporočila shrani, da se s primerjavo s števcem ugotovi, ali je primerjava končana in s tem ESP8266 wifi modul je poslal želeno sporočilo.
Naslednji primer uporablja ukaz AT+CWLAP
ki bo vrnil seznam dostopnih točk in znotraj njih se išče ena z imenom "wifi polaridad.es". Čeprav smo se odločili preveriti, ali je zadnji znak nič, kot je varovalni Shranjuje samo iskano besedilo in je znana njegova dolžina, lahko se tudi preveri, ali je prejeto toliko pravilnih črk. Z LED priključen na pin 2 je sporočeno, da je bilo najdeno pričakovano besedilo.
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 } } } } |
V kodi prejšnjega primera lahko vidite tudi način za izberite serijska vrata glede na vrsto plošče Arduino rabljeno. Ta primer predvideva, da imate za projekt tri vrste plošč: eno Arduino Uno, En Arduino Mega 2560 in arduino leonardo. Če delate z a Arduino Uno bo uporabljeno Serial
in drugače Serial1
.
Če delate s ploščo arduino leonardo Z isto metodo lahko zaustavite program in počakate na konzolo (zaporedna vrata, povezana z Serial
) Na voljo.
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 |
Preiščite različna besedila v odgovoru ESP8266
Koda v prejšnjem primeru se uporablja za iskanje besedila v informacijah, ki jih pošlje ESP8266 vendar lahko odgovor vključuje različne informacije, odvisno od operacije. Recimo, da začnemo s preprostim primerom v naslednjem primeru, da je besedilo, ki ga je poslal MCU ESP8266 es OK
ko je operacija izvedena pravilno in ERROR
Sicer pa tako kot pri naročilu AT+CWJAP?
, ki služi za preverjanje, ali je ESP8266 wifi modul je že povezan z dostopno točko.
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 nova izvedba iste metode, ki išče ujemanje z več možnimi sporočili, vam omogoča izbiro med različnimi dejanji glede na odgovor, prejet od ESP8266, preprosto vklopite LED ustrezna.
Omejite čas, potreben za prejem odgovora
Do zdaj ni bilo nobenega sklicevanja na pomembno vprašanje: najdaljši čas čakanja (časovna omejitev), preden se obravnava operacija kot neuspešna. Če je iz kakršnega koli razloga povezava z ESP8266 wifi modul, modul z dostopno točko, dostopna točka z internetom ali na primer hipotetični strežnik ni na voljo, je lahko program na eni točki blokiran in čaka v nedogled, zato bo treba na takšne okoliščine artikulirati odziv. Maksimalno čakalno dobo lahko konfigurirate za celotno aplikacijo, običajno bo v tem primeru bolj "radodarno" ali pa programirate posamezne čakalne dobe za vsako operacijo.
Za preverjanje, ali je minil (vsaj) določen časovni interval "Čas" trenutka, ko je račun odprt, se običajno odšteje od trenutnega "časa" in preveri, ali je razlika večja od želene meje. Ni nujno, da je ta "čas" realni čas, običajno ustreza intervalu, ki je pretekel od MCU začnite šteti čas; To ne vpliva na program, saj je zanimiv pretečeni čas in ne absolutni čas.
Običajno se za preverjanje, ali je določen interval potekel, uporablja izraz tipa:
1 | (unsigned long)(millis()–milisegundos_al_empezar)>intervalo_de_tiempo |
Spremenljivka milisegundos_al_empezar
vsebuje vrednost millis()
določenega trenutka v izvedbi, od katere se meri, zato ni nenavadno, da se njegovo ime nanaša na besedo "kronometer". Spremenljivka intervalo_de_tiempo
vsebuje največje število milisekund, zaradi katerih je prejšnji izraz resničen, kar pomeni, da predstavlja časovno omejitev; Običajno je konstanta (ali makro) in kot v prejšnjem primeru se v njenem imenu pogosto pojavi beseda "TIMEOUT". Če delate v zelo kratkih intervalih, lahko uporabite micros()
namesto millis()
(mikrosekunde namesto milisekund), čeprav je veliko manj pogost in veliko manj natančen.
1 | (unsigned long)(millis()–cronometro)>TIMEOUT |
Dolgo celo število v Arduino (unsigned long
) zaseda 4 bajte (32 bitov), tako da je največja vrednost, ki jo lahko predstavlja, 4294967295 (2 na potenco 32 minus ena, ker se začne pri nič). na krožniku Arduino Med neprekinjenim delovanjem se bo števec milisekund ponastavil (vrnil na nič) približno vsakih 50 dni. Pri odštevanju z nepodpisanimi tipi podatkov se ponovi enako vedenje (obračanje števca), tako da je možno nadzorovati časovno omejitev za nedoločen čas.
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; } } } |
Zgornja koda prikazuje a zelo osnovna izvedba omejitve časovne omejitve ki vključuje črte, označene glede na primer pred njim. Ker se preverjanje časovne omejitve izvede po obdelavi podatkov, ki prispejo iz ESP8266 wifi modul, lahko operacijo štejemo za uspešno tudi, če sprejem traja dlje od predpisane čakalne dobe.
Izvedite zapleteno operacijo, ki jo definira več ukazov AT
Imeti primer reference namena aplikacije, ki izkorišča ESP8266 wifi modul, recimo da je shranjevanje informacij v bazo podatkov, do katere dostopate prek spletne storitve da spremljate temperaturo. Naslednja koda prebere senzor, povezan z analognim vhodom, vsak določen časovni interval, izračuna povprečno vrednost in jo po daljšem časovnem intervalu pošlje spletnemu strežniku (slog Internet stvari) skozi a peticijo HTTP (OBJAVI, PRIDOBI…).
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”); } } |
V tem primeru beleženja temperature se spletni strežnik dostopa vsakih pet minut. Čeprav razpoložljivost ni posebej visoka, je pričakovati, da bo predlog deloval, če pa bi bila potrebna večja frekvenca snemanja, bi bilo treba uporabiti druge vire, npr. medpomnilnik podatkov čaka na pošiljanje, da pošlje več, ko se strežnik lahko udeleži, in jih shrani za čas, ko ni na voljo. Če bi bila pogostost, s katero je treba zapisovati podatke, še večja, bi bilo treba kot alternativo predlagati druge vrste protokolov. HTTP ali celo zamenjati TCP jo UDP da bi lahko poslali večino podatkov z zahtevano hitrostjo, tudi za ceno izgube nekaterih.
Operacije, ki sestavljajo nalogo, ki jo je treba izvesti za pošiljanje temperature, bi bile:
- Ponastavite wifi modul
- Prekini povezavo s trenutno dostopno točko (če obstaja privzeta povezava)
- Nastavite nastavitve. Za primer se predpostavlja, da je treba konfigurirati način povezave (enostavno) in vlogo v komunikaciji Wi-Fi (postaja).
- Povežite se z dostopno točko
- Preverite, ali je povezava pravilna (pravzaprav je to vstopna točka) Če povezave ni, začnite postopek od začetka
- Povežite se s strežnikom
- Pošljite zahtevo HTTP s podatki, ki jih je treba shraniti
Ni nujno, da je vrstni red operacij točno tak (čeprav je operacija) in vsak korak lahko zahteva več Ukazi ESP8266 ATNa primer, zgoraj navedena konfiguracija bi potrebovala dva: AT+CIPMUX=0
y AT+CWMODE=1
.
Podatkovna struktura za predstavitev operacij na ESP8266
V prejšnjih primerih je, čeprav na zelo osnovni način, že predlagana splošna rešitev problema: uporabite podatkovno strukturo, ki shranjuje možne odzive in dejanja, ki jih je treba izvesti v vsakem primeru; pošljite dejanje, počakajte na odgovor in nadaljujte glede na to, kaj odgovor pomeni. Ker bo vsaka kompleksna operacija zahtevala več Ukazi ESP8266 AT, mora podatkovna struktura povezati operacijo z drugimi, naslednjimi ali prejšnjimi, ki jih je treba izvesti v vsakem primeru glede na odziv ESP8266.
V prejšnjih primerih je bilo sporočilo preiskano v odgovoru ESP8266 in je bilo razloženo kot uspeh ali napaka. Poleg sprejema (in analize) vsega prejetega besedila, Če želite imeti splošni minimum, je priporočljivo, da se posvetite tudi izpolnjevanju sporočila ali, z drugimi besedami, na razpoložljivost ESP8266 wifi modul za prejemanje novih naročil. Na ta način bi lahko sprememba v stanje, ki bi ga lahko imenovali, na primer »wifi dostopen«, prejemala ime dostopne točke in prejemala besedilo ERROR
ali besedilo OK
bi pomenilo, da ESP8266 končali ste z odgovorom in zdaj lahko pošljete naslednjega Ukaz AT za 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; |
Zgornja koda uporablja vektor (operacion
) za shranjevanje besedila zaporednih operacij, ki tvorijo celotno nalogo. Uporabljen je dvodimenzionalni niz (mensaje
) s tremi analiziranimi odgovori. Kot je razloženo zgoraj, je treba poleg sporočila, ki predstavlja pravilen ali napačen odgovor, iskati sporočila, ki predstavljajo konec odgovora. Vse operacije ne bodo imele enakega števila možnih odgovorov; Kadar je odzivov manj, se lahko uporabi prazno sporočilo, ki pri analizi porabi najmanjše možno število ciklov (čeprav tako ni najbolj optimalno). Logično bo nujno, da minimalno število iskanih odgovorov (v primeru trije) vključuje vse možnosti delovanja, tudi če niso vse možne.
Ko govorimo o možnih odgovorih, je že razvidno, da ta primer ni zelo uporaben za prejemanje podatkov v poljubni obliki iz ESP8266 wifi modul, a stvar je v tem, da v kontekstu uporabe z mikrokrmilniki ni običajno; Najpogostejša stvar je pošiljanje podatkov, zbranih s senzorji, ki so jih povezali, in/ali prejemanje informacij o tem, kaj storiti z aktuatorji, ki jih nadzoruje. Zelo dragocena informacija, ki jo je mogoče zelo dobro predvideti.
V prejšnji podatkovni strukturi, tako kot je storjeno za izražanje možnih odzivov, ki se analizirajo, se dvodimenzionalna matrika uporablja tudi za določitev operacije, ki jo je treba izvesti v vsakem primeru (siguiente_operacion
). Natančneje, izbrali smo odgovor na tri vrste sporočil: ① poljubno besedilo (LITERAL
), da preverite, ali obstaja povezava z dostopno točko Wi-Fi in strežnikom, ② besedilo za odkrivanje napak v postopku (FALLO
) in ③ besedilo, ki označuje, da je bila operacija uspešno zaključena (ACIERTO
).
Končno sta na voljo še dva vektorja za nastavitev najdaljšega čakalnega časa, preden obupate (timeout
) in navedite (configuracion
), če se operacija konča brez čakanja na odgovor (ESPERAR_RESPUESTA
) in sporočila, ki označujejo konec komunikacije. Ta zadnji vektor, ki ponazarja primer, kako bi lahko shranili pomnilnik, deluje z biti konfiguracijskega bajta, da prikaže različna stanja.
Prvi Ukazi ESP8266 AT podatkovne strukture vedno pričakujte odgovor, ki je lahko sporočilo o uspehu ali napaki. Ko pride do napake, se modul znova zažene in se znova zažene in če sporočilo kaže, da je operacija pravilna, se premakne na naslednjo.
Ko se povežete s strežnikom, se vzorec spremeni. V tem primeru je treba ① poslati dolžino podatkovnega paketa, ki ga želite prenesti, in ② sestaviti zahtevo HTTP s fiksnim besedilom in vrednostjo (temperature), ki se pošlje v shranjevanje na strežnik. Priprava teh podatkov poteka v vsaki pošiljki in jo je potrebno razdeliti na dve (sporočite dolžino) ali tri (pošljite zahtevo) HTTP) On ESP8266 AT naročilo. Samo zadnji od delov, na katere je operacija razdeljena, bo čakal na odgovor.
V tem primeru bo deloval brez težav (mogoče opozorilo, da je modul zaseden), ko pa bo dolžina podatkov večja, bo treba podatkovne bloke razdeliti na manjše dele in morda celo implementirati čakanje, kot se opravi z odčitkom temperature, da ima modul čas za pošiljanje podatkov, ne da bi jih zapolnil varovalni.
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(); |
Skupaj z drugimi makri, ki so bili že razloženi prej, zgornji primer kode prikazuje, kako so definirana različna stanja, s katerimi je treba določiti, ali je treba počakati na odgovor in, če je primerno, katero sporočilo označuje, da se je končalo.
Ker bo na različnih mestih v kodi poslana operacija (ko je čas za pošiljanje povprečne temperature, če je čakalni čas operacije presežen, ko je trenutna operacija uspešno zaključena ...), toda kako to storiti je uveljavljena globalno, je bila opredeljena kot makro ENVIAR_OPERACION
ki združuje korake, vključene v pošiljanje.
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(); |
Sledi koda glavnega programa primera. Najbolj zunanja naloga je tista, ki je zadolžena za vzorčenje temperature za izračun povprečja in se vsako določeno časovno obdobje pošlje strežniku s pomočjo ESP8266 wifi modul. Ko je posamezna operacija poslana, se odgovor analizira, da se ugotovi, katera je naslednja ali ali je bila naloga pošiljanja informacij opravljena.
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 } } } |
Logično je, da je na prejšnji kodi mogoče izvesti več optimizacijskih dejanj, vendar, ker je to primer za razumevanje, kako se ESP8266 Splošno gledano se je vredno osredotočiti le na nekatere vidike, prvi je struktura podatkov. Zdi se, da je logično uporabite podatkovno strukturo programskega jezika (struct
), ki predstavlja podatke, ki se obdelujejo: the Ukazi ESP8266 AT in sporočila, ki se analizirajo.
Uporabite strukturo (struct
) shranjevanje podatkov namesto vzorčnih nizov (temelji na njih) je trivialno in, čeprav lahko povzroči bolj elegantno kodo, ne pomeni nobene izboljšave rezultata. Prava alternativa, ki jo predstavlja uporaba struct
izvajati, kot je razloženo spodaj, spremenljive dolžine v strukturah, ki vsebujejo "notranje" podatke ki jih omenjajo. Na ta način na primer ne bi bilo potrebno, da bi imela operacija določeno število odgovorov za analizo.
Ta pristop nakazuje, da je to najboljši način za implementacijo rešitve, pomanjkljivost pa je, da bi bil potreben uporaba dinamičnega dodeljevanja pomnilnika, kar je tvegana praksa dela z a mikrokrmilnik kar zahteva natančno merjenje količine pomnilnika, ki bo uporabljen med izvajanjem, saj nas prevajalnik na to težko opozori in obstaja določena možnost izčrpanja pomnilnika (ali sklada) z usodnimi posledicami za izvajanje programa.
V vrstici optimizacije kode je zanimivo spomniti, da v programu te vrste, ki uporablja veliko količino besedila, lahko prihrani pomnilniški prostor SRAM shranjevanje besedilnih nizov v programski pomnilnik (bliskavico) z makrom F()
. Na naslednjih posnetkih zaslona lahko vidite različne programe in dinamično porazdelitev pomnilnika z običajno uporabo besedila in uporabo makra F()
.
V zvezi z dejanji, ki se izvajajo po informacijah, ki prihajajo iz ESP8266 wifi modul, kot alternativo preverjanju sporočila iz kode in izvedbi enega ali drugega glede na prejeto, lahko shranite v to podatkovno strukturo kazalci na funkcije, ki izvajajo vsako nalogo namesto indikatorjev statusa (zastavice), ki opozarjajo na določeno stanje, za upravljanje katerega je odgovorna aplikacija, na primer znotraj glavne zanke.
Sledi primer struktur za shranjevanje podatkov zahtev za ESP8266 (tip podatkov operacion_esp8266
) in njihovi odgovori (vrsta podatkov 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; |
Kot struktura, ki predstavlja operacijo (podatki, ki so poslani v ESP8266 wifi modul) se nanaša na strukturo, s katero so definirani odzivi, struktura odzivov pa na strukturo operacij, najprej je treba deklarirati oboje, tako da definirate nov podatkovni tip in nato definirate njegovo vsebino.
Prejšnji primer meni, da se je program, ki ga vključuje, odločil za uporabo a indikator stanja, ki mora ustrezati spremenljivki, dostopni iz kode, ki je odgovorna za izvajanje ene ali drugih operacij, kot je navedeno z omenjeno vrednostjo. Če v odgovoru na ESP8266 Pri analizi določenega besedila stanje zavzame vrednost, ki označuje strukturo ustreznega odgovora.
Kot je bilo že omenjeno, bi bila druga alternativa zamenjava ali dopolnitev indikatorja stanja shrani funkcijo v referenčno strukturo (kazalec), ki bi bil poklican, ko bi v odgovoru naletel na določeno besedilo ESP8266 wifi modul.
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; |
V prejšnjem primeru je bil dodan podatkovni strukturi, ki se uporablja za obdelavo odgovora iz ESP8266 wifi modul kazalec na (domnevno) funkcijo, ki vrne podatek tipa float
(lahko je utežena vrednost analognega odčitka) in kateremu sta dva bajta podana kot argumenta (dva unsigned char
ki je lahko pin, s katerega se bere analogni vhod, in tisti, ki aktivira ENABLE hipotetičnega integriranega).
V razvoju za MCU, v nasprotju s tem, kar se dogaja v razvojnem slogu za večje sisteme, ni tako redkost uporaba globalnih spremenljivk pri definiranju (globalnega) obnašanja aplikacije, ki nadzoruje sklop, zato ne bo posebej redko najti tovrstnih definicij kot funkcije brez parametrov in ne vrnejo vrednosti, nekaj podobnega void (*accion)();
Če delate s tem načinom predstavljanja podatkov, uporabite struct
podatkov spremenljive dolžine bo treba dinamično dodeliti pomnilnik malloc()
(o new()
, če so uporabljeni predmeti), ki bo uporabil količino dodeljenega pomnilnika kot parameter in vrnil kazalec na začetek območja pomnilnika, ki je rezerviran. z sizeof()
Glede na vrsto, ki je shranjena, pomnoženo s številom uporabljenih elementov, lahko dobite potrebno količino pomnilnika. Primer z in brez uporabe si lahko ogledate na spodnjih posnetkih zaslona. malloc()
; Bodite previdni pri pomnilniku, ki ga uporablja program, v prvem primeru morate naložiti knjižnico, ki vsebuje to funkcijo.
Če operacije na ESP8266 wifi modul spreminja med izvajanjem programa, bo treba sprostiti pomnilnik, ki se ne uporablja z free()
(o delete()
, če gre za predmete). Čeprav je razumno pričakovati, da prevajalnik (GCC) bo optimiziral program, da se izogne particioniranju pomnilnika, zmogljivost zagotovo ne bo tako optimalna kot pri delu s statično dodeljenim pomnilnikom.
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 } } } |
Čeprav v tem primeru (v obeh izvedbah) nima velikega smisla, je treba za posplošitev operacije, da bi jo lahko uporabili v drugih primerih, upoštevati, da pošiljanje podatkov vedno ponavlja isti protokol: sporočite število bajtov, ki bodo poslani, počakajte na indikator (>) in pošljite podatke.
Ker je v tem primeru uporabljen samo enkrat (celotna zahteva je narejena v enem paketu), se ne zdi zelo uporaben, vendar bo na splošno morda treba izvesti več pošiljanj v isti operaciji, vključno s primeri, ko morajo prenašati znatne količine podatkov, ki morajo biti razdrobljeni, da se prepreči prepolnitev pomnilnika ESP8266.
Za izvedbo tega vedenja se lahko uporabita zadnja dva elementa povezave, tako da se vsakič, ko so podatki poslani, podatki napolnijo z ustreznimi vrednostmi: v prvem primeru število poslanih bajtov, v drugem pa ( del) zahteve, ki se posreduje.
Za ponovitev dodelitve in pošiljanja različnih elementov, ki jih je treba prenesti, je mogoče shraniti v vektor. Ta novi vektor bo tisti, ki bo določal konec kompleksne operacije in ne zadnja operacija kot do zdaj.
1 komentar