Grunnleggende operasjoner på en ESP8266 wifi-modul fra Arduino
Når Espressif lanserte de første modulene på markedet wifi med det integrerte ESP8266 og firmware for å håndtere det ved hjelp av AT-kommandoer, det vi brukere var interessert i var å integrere det i sammenstillinger med mikrokontrollere og problemene ble redusert til å kjenne det (tidligere) mørket ESP8266 AT kommandotabell, fôringsbehov eller ESP8266 fastvareoppdatering.
Da kom det raskt alternativer for å programmere ESP8266 og modulimplementeringer wifi av svært forskjellige formater som vakte andre bekymringer: hvilken ESP8266 wifi-modul å velge avhengig av rekkevidden til de forskjellige antennene (inkludert eksterne) eller den fysiske integreringen av disse nye modulene i våre forsamlinger.
På grunn av alle disse endringene har det sikkert ikke blitt lagt vekt på de mest grunnleggende aspektene, den mest grunnleggende styringen av ESP8266 wifi-modul. Selv om polaritet.es Du kan finne informasjon om bruken av ESP8266 og det er noen applikasjoner ment å forklare på en generisk måte driften av ESP8266 wifi-modul ved hjelp av AT-kommandoer, spesielt i artikkelen om bibliotek for å lage HTTP-spørringer fra Arduino med ESP8266 wifi-modulen, antyder lesernes inntrykk at det ville være nyttig å legge til litt mer grunnleggende informasjon for å hjelpe brukere av ESP8266 å gjennomføre sine egne implementeringer.
Diskuter de grunnleggende operasjonene for å jobbe med ESP8266 og å foreslå generiske løsninger er et mål for flere vidt forskjellige deler; For å hjelpe deg med å følge innholdet i artikkelen kan følgende indeks tjene som en guide:
- Kontroller ESP8266 wifi-modulen fra datamaskinen gjennom den serielle porten
- Oppdater firmware med esptool
- Send bestillinger til modulen
- Motta data fra ESP8266
- Analyser responsen ved å søke etter tekster i innholdet
- Begrens ventetiden for å motta svar
- Utfør en kompleks operasjon definert av flere AT-kommandoer
Kontroller ESP8266 wifi-modulen fra datamaskinen gjennom den serielle porten
Fra en tallerken Arduino og bruke din IDE det er mulig å overvåke driften av en ESP8266 wifi-modul, Send ESP8266 AT-kommandoer og se svaret, men det er mye mer praktisk å gjøre det fra en datamaskin med en terminaltypeapplikasjon.
Avhengig av hvilket brett Arduino brukt, kan det hende at bare én maskinvareserieport er tilgjengelig, noe som gir litt ulempe for sending og mottak. Å endre kommunikasjonshastigheten er mye mer behagelig i en seriell kommunikasjonsapplikasjon fra en datamaskin og noen hovedkort. Arduino (og under noen omstendigheter) ikke støtter de høyeste hastighetene for seriell kommunikasjon godt, spesielt 115200 baud, som er standardhastigheten til de nyeste versjonene av firmware.
På Hvilket program du skal bruke for å overvåke ESP8266 bruker seriell port, det er mange å velge mellom i henhold til behov og preferanser; i det siste har jeg brukt klassikeren mer CuteCom (den i skjermbildet ovenfor) fordi det er veldig behagelig for meg å gjenta visse ESP8266 wifi-modul AT-bestillinger i prosjekttesting.
Noen anbefalinger er allerede gitt her om programmer som fungerer som en seriell konsoll; For eksempel når man snakker om PuTTY for å kontrollere UART serielle enheter fra datamaskinen. PuTTYI tillegg til å være et utmerket program, er det tilgjengelig for de fleste stasjonære operativsystemer. Videre, som PuTTY kan brukes til å fungere som en konsoll med både serieporten og Internett-protokollfamilie (TCP/IP), inkludert de som opererer TLS, blir et vanlig verktøy som mer enn tilbakebetaler den (litte) tiden som brukes på å konfigurere den og venne seg til bruken.
I tillegg til seriell kommunikasjonsprogramvare, å koble til ESP8266 wifi-modul til havnen USB En datamaskin krever også en omformer USB til serier TTL. Som i tilfellet med programvare, er det flere versjoner, hvorfra de kun brukes til å konvertere porten USB på en seriell port TTL (som kan fås fra én euro) til de som kan emulere forskjellige protokoller (som f.eks SPI o I2C).
Akkurat som et program som fungerer som en seriell konsoll, maskinvaren for å kommunisere datamaskinen via USB med en logisk krets (ikke bare ESP8266) vil være et vanlig verktøy i arbeidet til en mikrokontrollert applikasjonsutvikler, det er verdt å ha det i verktøykassen så snart som mulig og jobbe med det ESP8266 wifi-modul Det er en utmerket mulighet til å få en.
Omformeren USB a UART TTL Den kan også brukes til å overvåke oppførselen til en krets som bruker ESP8266, for å gjøre dette, kobles utgangene du vil overvåke i serie til datainngangen (RX) til omformeren med en hurtigdiode (den 1N4148, for eksempel) og en motstand (2K2, for eksempel) parallelt med hverandre. Et slikt oppsett fungerer som en maskinvareseriesniffer.
Selv om snifferen i bildet ovenfor absolutt er rudimentær (blant annet har den ikke buffer) er tilstrekkelig til å overvåke driften av en sammenstilling med Arduino og ESP8266.
Fjerne snifferen fra den forrige ordningen, den skjematisk som viser hvordan du kobler til en ESP8266 wifi-modul til en tallerken Arduino. I tillegg til å mate den på 3V3, må tilbakestillingspinnen og aktiveringspinnen til den integrerte være koblet til et høyt nivå (aktiver). Selvfølgelig må RX-pinnen til den ene kobles til TX-en til den andre.
For å forenkle det forrige diagrammet er en plate representert Arduino drevet på 3V3 og for hvilken en spenning på serieporten også antas å være 3V3. Hvis du bruker en mikrokontroller med et annet signalnivå på serieporten (normalt 5 V) vil være nødvendig, for ikke å skade ESP8266, bruk en nivåomformer som i diagrammene nedenfor. Denne kretsen finnes ofte i mange kommersielle modulimplementeringer.
Oppdater ESP8266 firmware
Las ESP8266 AT-kommandoer, dens avslutning, standardhastigheten til modulen... avhenger av versjonen av ESP8266 firmware. Det er best å sørge for at du har samme versjon i alle moduler, og om mulig at det er den nyeste versjonen.
Dessverre er det meste av ESP8266 wifi-modulmodeller De har bare 4 Mbit, så den nyeste versjonen kan ikke installeres på dem. Den siste (offisielle) versjonen av fastvaren som kan installeres på ESP8266 wifi-moduler med 4 Mbit (mest) er 0.9.4 som inkluderer versjon 0.2 av ESP8266 AT-kommandoer.
Oppsummert, for å oppdatere fastvaren trenger du:
-
Last ned den tilsvarende fastvareversjonen. Den siste (offisielle) versjon for en modul som har 4 Mbit minne, ligger i Espressif-mappen på github. I Espressifs nettsted Du kan laste ned den nyeste versjonen av fastvaren, men det er veldig viktig å kontrollere at modulen den er installert på har nok minne.
-
Last ned den nyeste versjonen av fastvareinstallasjonsverktøyet. Min favoritt er esptool som er skrevet inn Python, så det fungerer på alle plattformer. I tillegg til å lastes ned, kan den også installeres med
pip install esptool
(opip2
opython -m pip
…). Selvfølgelig, Espressif Den tilbyr også sitt eget verktøy, men er foreløpig bare tilgjengelig for Windows. -
Forbered nedlastede filer; pakk dem ut i en tilgjengelig mappe og gjør verktøyet kjørbart om nødvendig esptool, i mitt tilfelle, siden GNU / Linux, Med
chmod +x esptool
-
Koble modulen til datamaskinen ved hjelp av en omformer USB UART TTL som fungerer på 3V3 eller bruk en nivåomformer hvis den fungerer på 5 V. I tillegg til strømmen må du koble TX til RX på omformeren USB UART TTL, RX til TX, GPIO0 på lavt nivå (GND) og kanskje GPIO2 på høyt nivå (i mine tester har det fungert både å koble den på lavt nivå og koble den fra). Hvis modulen har GPIO15-tilkoblingen fri (som skjer i ESP-12), må den kobles til et lavt nivå. RESET, som normalt vil være på et høyt nivå under drift, kan stå ukoblet eller koblet til et høyt nivå ved hjelp av en motstand (10K, for eksempel), siden før du starter opptaket kan det være nødvendig å tilbakestille enheten ved å koble den til til et lavt nivå.
Ved å slå på modulen vil den være tilgjengelig for oppdatering, men Hvis en tilkoblingsfeil vises, vil det være nødvendig å tilbakestille den koble til RESET på et lavt nivå et øyeblikk og deretter la den være i luften (uten å koble til) for oppdateringsprosessen.
Modulen har halv ampere forbruket topper (opptil 600 mA, ifølge noen brukere) så det er viktig å bruke en strømforsyning som er i stand til å støtte dette forbruket, spesielt for oppdatering av fastvare. -
Kjør verktøyet for å oppdatere fastvaren. I mitt tilfelle har jeg lagret verktøyet og fastvaredokumentene i trinn 3 i samme mappe, så jeg kjører fra konsollen:
cd ~/Datos/firmwareESP8266
(bytt til mappen som inneholder verktøyet og fastvaren)./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
angir hastigheten på ESP8266 (115200 baud i mitt tilfelle) og--port
serieporten den kobles til (i mitt tilfelle, emulert, den første USB). De forskjellige dokumentene som utgjør fastvaren går bakwrite_flash
foran med adressen, med user1.bin-dokumentet som inneholder oppdateringsnyttelasten.
Send kommandoer til ESP8266 wifi-modul
For å kontrollere ESP8266 fra en datamaskin vi må begynne med konfigurer appen som det vil være nok å ① velge porten som omformeren er koblet til USB UART TTL, noe som /dev/USB0
i GNU/Linux og lignende eller noe sånt COM6
i Windows, ② velg hastigheten som ESP8266, sannsynligvis 115200 baud, ③ sette 8 databiter pluss en stoppbit, uten paritet eller håndtrykk, og ④ angi slutten av linjen, avhengig av firmware, nesten alltid CR+LF.
Når applikasjonen er konfigurert (eller, der det er relevant, lagret og valgt), er den det åpne tilkoblingen ("åpen enhet" og "åpen", henholdsvis i skjermbildene av eksemplene ovenfor med CuteCom y PuTTY) og du kan begynne å sende bestillinger til ESP8266.
Som kan sees i ESP8266 AT kommandotabell, formatet for å aktivere, deaktivere, angi en verdi og henvise til den er ganske forutsigbar, men generelt er det ikke lett å huske dem alle, og du må sannsynligvis ha den for hånden for å referere til den.
Måten å send AT-ordrer al ESP8266 wifi-modul fra Arduino Det er veldig enkelt: ① konfigurere kommunikasjon med Serial.begin(115200);
(eller Serial1, Serial2... på kort med flere seriell maskinvareporter) og ② send kommandoene ved å bruke formatet 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()
{
}
|
Eksemplet ovenfor viser hvordan du sender ESP8266 wifi-modul AT-bestillinger fra Arduino. I dette tilfellet er det illustrert AT+CWJAP
, som brukes til å koble til et tilgangspunkt. Denne kommandoen bruker tilgangspunktidentifikatoren som argumenter (SSID) og nøkkelen, begge i anførselstegn, slik at de blir et objekt Srtring
og omslutt dem i anførselstegn ved å bruke escape-koden (\"
). For å fullføre bestillingen, bruk \r\n
som tilsvarer CR
y LF
.
For å huske at den serielle porten ikke alltid identifiseres med Serial
(på enkelte plater kan det være Serial1
, Serial2
…) portobjektet som brukes er definert ved å tilordne det til makroen PUERTO_SERIE
. Å oppdage hvilken type brett som brukes kan legge til litt intelligens til valg av serieport; Senere skal vi gå gjennom hvordan du kan finne ut hvilken type Arduino. Resten av definisjonene er de vanlige som lar deg "navngi" konstantverdiene for å unngå å gjenta dem (og gjøre feil) og gjøre det lettere å endre dem.
Eksempelet ovenfor er ment å koble til ESP8266 wifi-modul til det angitte tilgangspunktet, men var det allerede koblet til før? Har forbindelsen fungert? For å vite dette, må vi "lytte" til hva ESP8266
Motta data fra ESP8266 wifi-modul
Ved å koble datasnifferen forklart ovenfor til datamaskinen kan du se hva Arduino har sendt til ESP8266 og hans svar. Å lese fra Arduino og behandle informasjonen i den det vil være nødvendig å oppdage med Serial.available()
hvis noen data har kommet, og i så fall last den med Serial.read()
. Følgende eksempel viser hvordan du leser svaret fra AT+CWJAP?
, som vil rapportere om det er en tilkobling til et tilgangspunkt.
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();
}
}
|
Som på en tallerken Arduino Uno (og i andre) åpning av den serielle skjermen tilbakestiller programmet, den kan brukes til å se i den serielle konsollen Arduino informasjonen du sender til ESP8266 som skjermbildet av bildet nedenfor viser.
Analyser svaret sendt av ESP8266 wifi-modulen
Vi har allerede sett hvordan man leser informasjonen som når Arduino fra ESP8266. Problemet du må forholde deg til er at du ikke vet når det begynner å komme, hvor lang tid det vil ta før det kommer, hvor lenge det vil være... og det er lite effektivt å vente på svar fra ESP8266 mottas uten å la mikrokontroller utføre andre oppgaver i mellomtiden.
En enkel måte å håndtere denne omstendigheten på er iterere på dataene som er mottatt på jakt etter konkrete svar som for eksempel aktiverer indikatorer (flagg eller boolske variabler) som vil avgjøre om det skal fortsette å søke i den mottatte teksten og hvilke handlinger som skal utføres basert på informasjonen som kommer fra ESP8266. Mens svaret kommer mikrokontroller kan dedikere til andre oppgaver, for eksempel å motta data fra sensorer og behandle dem.
Søk etter en tekst i informasjonen mottatt fra ESP8266
For å søke i teksten som kommer fra ESP8266 kan sammenligne hvert mottatt brev med det som tilsvarer meldingen du leter etter. Det vil være nødvendig å bruke en teller (eller en peker) som peker på bokstaven som skal sammenlignes; Hvis tegnet som kommer fra ESP8266 er den samme som den som undersøkes i meldingen, går telleren frem, hvis den er annerledes initialiseres den.
For å vite at slutten er nådd, konsulteres neste tegn i den søkte meldingen, som vil være null (\0
) eller lengden på meldingen er lagret for å, ved å sammenligne den med telleren, vite om sammenligningen er fullført og derfor ESP8266 wifi-modul har sendt ønsket melding.
Følgende eksempel bruker kommandoen AT+CWLAP
som vil returnere en liste over tilgangspunkter og innenfor dem søkes det etter et som heter "wifi polaridad.es". Selv om vi har valgt å bekrefte at det siste tegnet er null, da buffer Den lagrer kun den søkte teksten og lengden er kjent, det kan også sjekkes om et slikt antall korrekte bokstaver er mottatt. Med en LED koblet til pinne 2 rapporteres det at forventet tekst er funnet.
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
}
}
}
}
|
I koden til forrige eksempel kan du også se en måte å velg serieport avhengig av type brett Arduino brukt. Dette eksemplet forutsetter at du har tre typer brett for prosjektet: en Arduino Uno, One Arduino Mega 2560 og en arduino leonardo. Hvis du jobber med en Arduino Uno den vil bli brukt Serial
og ellers Serial1
.
Hvis du jobber med en tallerken arduino leonardo Du kan bruke samme metode for å stoppe programmet og vente på konsollen (den serielle porten knyttet til Serial
) er tilgjengelig.
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
|
Søk i ulike tekster i ESP8266-svaret
Koden i forrige eksempel brukes til å søke etter tekst i informasjonen som sendes av ESP8266 men svaret kan inneholde forskjellig informasjon avhengig av operasjonen. Anta, for å starte med et enkelt tilfelle i neste eksempel, at teksten sendt av MCU ESP8266 es OK
når operasjonen er utført riktig og ERROR
Ellers som med bestillingen AT+CWJAP?
, som tjener til å verifisere om ESP8266 wifi-modul er allerede koblet til et tilgangspunkt.
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
}
}
}
}
}
|
Denne nye implementeringen av samme metode, som søker etter et samsvar med flere mulige meldinger, lar deg velge mellom ulike handlinger avhengig av svaret mottatt fra ESP8266, bare slå på LED tilsvarende.
Begrens tiden det tar å motta et svar
Til nå har det ikke blitt henvist til en relevant problemstilling: maksimal ventetid (timeout) før vurdering av en operasjon mislyktes. Hvis av en eller annen grunn forbindelsen med ESP8266 wifi-modul, modulen med tilgangspunktet, tilgangspunktet med Internett eller, for eksempel, en hypotetisk server er ikke tilgjengelig, programmet kan blokkeres på et tidspunkt og venter på ubestemt tid, så et svar må artikuleres til slike omstendigheter. Maksimal ventetid kan konfigureres for hele applikasjonen, vanligvis vil den i så fall være mer "generøs", eller individuelle ventetider kan programmeres for hver operasjon.
For å sjekke at (minst) et visst tidsintervall har gått "Tidspunktet" for øyeblikket kontoen startes trekkes vanligvis fra gjeldende "tidspunkt", og det bekreftes at forskjellen er større enn ønsket grense. Denne "tiden" trenger ikke å være sanntid, den tilsvarer vanligvis intervallet som har gått siden MCU begynn å telle tid; Dette påvirker ikke programmet siden det som er interessant er medgått tid og ikke den absolutte tiden.
Vanligvis, for å sjekke om et visst intervall har gått, brukes et uttrykk av typen:
1
|
(unsigned long)(millis()–milisegundos_al_empezar)>intervalo_de_tiempo
|
variabel milisegundos_al_empezar
inneholder verdien av millis()
av et bestemt øyeblikk i utførelsen som den er tidsbestemt fra, så det er ikke uvanlig at navnet refererer til ordet "kronometer." Variabelen intervalo_de_tiempo
inneholder det maksimale antallet millisekunder som gjør det forrige uttrykket sant, det vil si at det representerer tidsavbruddet; Det er vanligvis en konstant (eller en makro), og som i forrige tilfelle, vises ordet "TIMEOUT" ofte i navnet. Hvis du jobber med veldig korte intervaller kan du bruke micros()
i stedet for millis()
(mikrosekunder i stedet for millisekunder) selv om det er mye mindre vanlig og mye mindre presist.
1
|
(unsigned long)(millis()–cronometro)>TIMEOUT
|
Et langt heltall i Arduino (unsigned long
) opptar 4 byte (32 biter), så den største verdien den kan representere er 4294967295 (2 i potensen 32 minus én, fordi den starter på null). på en tallerken Arduino Mens den kjører kontinuerlig vil millisekundtelleren tilbakestilles (tilbake til null) omtrent hver 50. dag. Når du trekker fra med usignerte datatyper, reproduseres den samme oppførselen (snu telleren) slik at det er mulig å kontrollere tidsavbruddet på ubestemt tid.
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;
}
}
}
|
Koden ovenfor viser en helt grunnleggende implementering av tidsavbruddsbegrensning inkorporerer linjene merket med hensyn til eksemplet som går foran det. Siden tidsavbruddsbekreftelsen utføres etter behandling av dataene som kommer fra ESP8266 wifi-modul, kan operasjonen anses som vellykket selv om mottak tar lengre tid enn den pålagte ventetiden.
Utfør en kompleks operasjon definert av flere AT-kommandoer
For å ha et eksempel på formålet med applikasjonen som utnytter ESP8266 wifi-modul, antar at det er det lagre informasjon i en database som du får tilgang til via en nettjeneste for å holde styr på temperaturen. Følgende kode leser en sensor koblet til en analog inngang hvert bestemt tidsintervall, beregner gjennomsnittsverdien og sender den til webserveren etter et lengre tidsintervall (stil IOT) gjennom en underskriftskampanje HTTP (POST, FÅ...).
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”);
}
}
|
I dette temperaturregistreringseksemplet åpnes en webserver hvert femte minutt. Selv om tilgjengeligheten ikke er spesielt høy, er det å forvente at forslaget vil fungere, men dersom en høyere opptaksfrekvens var nødvendig, måtte andre ressurser settes i verk, f.eks. databuffer venter på å bli sendt, for å sende flere når serveren kan delta og lagre dem når den ikke er tilgjengelig. Hvis frekvensen som data må registreres med var enda større, ville andre typer protokoller måtte foreslås som et alternativ til HTTP eller til og med erstatte TCP av UDP for å kunne sende mesteparten av dataene med nødvendig hastighet selv på bekostning av å miste noen.
Operasjonene som utgjør oppgaven som skal utføres for å sende temperaturen vil være:
- Tilbakestill wifi-modulen
- Koble fra det gjeldende tilgangspunktet (i tilfelle en standardtilkobling eksisterer)
- Angi innstillingene. For eksempelet antas det at tilkoblingsmodusen (enkel) og rollen i Wi-Fi-kommunikasjon (stasjon) må konfigureres.
- Koble til tilgangspunktet
- Bekreft at tilkoblingen er riktig (faktisk er dette inngangspunktet) Hvis det ikke er noen tilkobling, start prosessen fra begynnelsen
- Koble til serveren
- Send forespørselen HTTP med dataene som skal lagres
Rekkefølgen på operasjoner trenger ikke å være nøyaktig slik (selv om operasjonen er det), og hvert trinn kan kreve flere ESP8266 AT-kommandoerFor eksempel vil konfigurasjonen ovenfor trenge to: AT+CIPMUX=0
y AT+CWMODE=1
.
En datastruktur for å representere operasjoner på ESP8266
I de foregående eksemplene, selv om det er på en veldig grunnleggende måte, er en generisk løsning på problemet allerede foreslått: bruke en datastruktur som lagrer mulige svar og handlinger som må iverksettes i hvert enkelt tilfelle; send en handling, vent på svar, og fortsett i henhold til hva svaret betyr. Siden hver kompleks operasjon vil kreve flere ESP8266 AT-kommandoer, må datastrukturen koble en operasjon med andre, etterfølgende eller tidligere, som må utføres i hvert enkelt tilfelle avhengig av responsen fra ESP8266.
I de forrige eksemplene ble det søkt etter en melding i svaret til ESP8266 og det ble tolket som suksess eller feil. I tillegg til en mottakelse (og analyse) av all den mottatte teksten, For å ha et generisk minimum, er det tilrådelig å også ivareta fullføringen av meldingen eller, med andre ord, til tilgjengeligheten av ESP8266 wifi-modul for å motta nye bestillinger. På denne måten kan endringen til en tilstand som vi kan kalle, for eksempel "wifi tilgjengelig", være å motta navnet på tilgangspunktet og å motta teksten ERROR
eller teksten OK
vil bety at ESP8266 du er ferdig med svaret, og du kan nå sende det neste AT-kommando til 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;
|
Koden ovenfor bruker en vektor (operacion
) for å lagre teksten til de påfølgende operasjonene som utgjør hele oppgaven. En todimensjonal matrise brukes (mensaje
) med de tre svarene som er analysert. Som forklart ovenfor, er det nødvendig å se etter meldingene som representerer slutten av svaret i tillegg til meldingen som representerer et riktig eller feil svar. Ikke alle operasjoner vil ha like mange mulige svar; Når det er færre svar, kan en tom melding brukes som bruker minst mulig antall sykluser i analysen (selv om det ikke er den mest optimale måten). Logisk sett vil det være nødvendig at minimum antall svar som søkes (tre i eksempelet) inkluderer alle operasjonsmulighetene, selv om de ikke er alle mulige.
Når du snakker om mulige svar, kan det allerede sees at dette eksemplet ikke er veldig nyttig for å motta data med et vilkårlig format fra en ESP8266 wifi-modul, men saken er at, i sammenheng med bruk med mikrokontrollere det er ikke vanlig; Det vanligste er å sende data samlet inn av sensorene de har koblet til og/eller motta informasjon om hva de skal gjøre med aktuatorene den styrer. Meget verdifull informasjon, som kan forutses veldig godt.
I den forrige datastrukturen, akkurat som det er gjort for å uttrykke de mulige svarene som analyseres, brukes en todimensjonal matrise også for å bestemme operasjonen som må utføres i hvert tilfelle (siguiente_operacion
). Konkret har vi valgt å svare på tre typer meldinger: ① en vilkårlig tekst (LITERAL
) for å bekrefte om det er en tilkobling til Wi-Fi-tilgangspunktet og serveren, ② en tekst for å oppdage feil i prosessen (FALLO
) og ③ en tekst som indikerer at operasjonen ble fullført (ACIERTO
).
Til slutt er det ytterligere to vektorer for å angi maksimal ventetid før du gir opp (timeout
) og spesifiser (configuracion
) hvis operasjonen avsluttes uten å vente på svar (ESPERAR_RESPUESTA
) og meldinger som indikerer slutten på kommunikasjonen. Denne siste vektoren, for å illustrere et eksempel på hvordan minne kan lagres, arbeider med bitene i en konfigurasjonsbyte for å indikere de forskjellige tilstandene.
Den første ESP8266 AT-kommandoer av datastrukturen alltid forvente et svar, som kan være suksessen eller feilmeldingen. Når det oppstår en feil, startes modulen på nytt og den starter igjen, og hvis meldingen indikerer at operasjonen er riktig, går den videre til neste.
Når du har koblet til serveren endres mønsteret. I dette tilfellet er det nødvendig å ① sende lengden på datapakken som skal overføres og ② skrive forespørselen HTTP med en fast tekst pluss verdien (av temperaturen) som sendes for å lagres på serveren. Forberedelsen av disse dataene utføres i hver forsendelse, og det er nødvendig å dele dem i to (varsle lengden) eller tre (send forespørselen HTTP) Til ESP8266 AT-bestilling. Bare den siste av delene som operasjonen er delt inn i vil vente på svar.
I dette tilfellet vil det fungere uten problemer (kanskje advarsel om at modulen er opptatt), men når lengden på dataene er større vil det være nødvendig å dele opp datablokkene i mindre biter og det kan til og med være nødvendig å implementere en venting, som gjøres med temperaturavlesningen, for å gi modulen tid til å sende dataene uten å fylle den buffer.
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();
|
Sammen med andre makroer som allerede er forklart før, viser eksempelkoden ovenfor hvordan de forskjellige tilstandene er definert for å spesifisere om man skal vente på svar og eventuelt hvilken melding som indikerer at den er ferdig.
Som på forskjellige punkter i koden vil en operasjon bli sendt (når det er på tide å sende gjennomsnittstemperaturen, hvis ventetiden for en operasjon overskrides, når den nåværende operasjonen er vellykket fullført...), men hvordan gjøres det er etablert globalt, har det blitt definert som en makro ENVIAR_OPERACION
som grupperer trinnene som er involvert i frakt.
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();
|
Følgende er koden til hovedprogrammet i eksemplet. Den mest eksterne oppgaven er den som har ansvaret for å ta prøver av temperaturen for å beregne gjennomsnittet, og hver bestemt tidsperiode sendes den til serveren ved å bruke ESP8266 wifi-modul. Når hver operasjon er sendt, analyseres svaret for å avgjøre hva som er neste eller om oppgaven med å sende informasjon er fullført.
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
}
}
}
|
Logisk sett kan flere optimaliseringshandlinger utføres på den forrige koden, men siden dette er et eksempel for å forstå hvordan ESP8266 På en generisk måte er det bare verdt å fokusere på noen aspekter, den første er datastrukturen. Det virker som det logiske er bruk en datastruktur for programmeringsspråk (struct
) for å representere informasjonen som behandles: den ESP8266 AT-kommandoer og meldingene som analyseres.
Bruk en struktur (struct
) å lagre dataene i stedet for eksempelmatrisene (basert på dem) er trivielt, og selv om det kan resultere i mer elegant kode, innebærer det ingen forbedring av resultatet. Det sanne alternativet som utgjøres av bruken av struct
er å implementere, som forklart nedenfor, variable lengder i strukturer som inneholder "indre" data som er referert til av dem. På denne måten vil det for eksempel ikke være nødvendig for en operasjon å ha et fast antall svar å analysere.
Denne tilnærmingen antyder at det er den beste måten å implementere løsningen på, men ulempen er at det ville være nødvendig bruke dynamisk minneallokering, en risikabel praksis å jobbe med en mikrokontroller som krever nøye måling av hvor mye minne som vil bli brukt under kjøring, siden kompilatoren neppe vil kunne advare oss om dette og det er en viss mulighet for å tømme minnet (eller stabelen) med fatale konsekvenser for gjennomføringen av programmet.
I linjen med å optimalisere koden, er det interessant å huske at i et program av denne typen, som bruker en stor mengde tekst, kan spare minneplass SRAM lagring av tekststrenger i programminnet (blitz) med makroen F()
. I de følgende skjermbildene kan du se det forskjellige programmet og den dynamiske minnedistribusjonen med normal bruk av tekst og ved bruk av makro F()
.
Med hensyn til handlingene som utføres i henhold til informasjonen som kommer fra ESP8266 wifi-modul, som et alternativ til å sjekke meldingen fra koden og utføre det ene eller det andre i henhold til det som mottas, kan lagres i denne datastrukturen pekere til funksjoner som utfører hver oppgave i stedet for statusindikatorer (flagg) som advarer om en viss tilstand som applikasjonen er ansvarlig for å administrere, for eksempel innenfor hovedsløyfen.
Følgende er et eksempel på strukturer for å lagre dataene til forespørslene til ESP8266 (datatypen operacion_esp8266
) og deres svar (datatypen 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;
|
Som strukturen som representerer operasjonen (dataene som sendes til ESP8266 wifi-modul) refererer til strukturen som responsene er definert med, og strukturen til responsene til strukturen til operasjonene, det er nødvendig å erklære begge først, ved å definere den nye datatypen, og deretter definere innholdet.
Det forrige eksemplet vurderer at programmet som inkluderer det har valgt å bruke en statusindikator, som må tilsvare en variabel tilgjengelig fra koden som er ansvarlig for å utføre en eller andre operasjoner som indikert av nevnte verdi. Hvis i svaret fra ESP8266 Når en bestemt tekst analyseres, tar staten verdien som indikerer strukturen til den tilsvarende responsen.
Som sagt før, vil et annet alternativ, enten å erstatte eller utfylle en statusindikator, være lagre en funksjon i referansestrukturen (en peker) som ville bli kalt ved å møte bestemt tekst i svaret fra 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;
|
I forrige eksempel er det lagt til datastrukturen som brukes til å behandle svaret fra ESP8266 wifi-modul en peker til en (antatt) funksjon som returnerer data av typen float
(kan være den vektede verdien av en analog avlesning) og som to byte er gitt som argumenter (to unsigned char
som kan være pinnen som den analoge inngangen leses fra og den som aktiverer ENABLE for en hypotetisk integrert).
Under utvikling for MCU, i motsetning til det som skjer i utviklingsstilen for større systemer, er det ikke så uvanlig å bruke globale variabler når man definerer den (globale) oppførselen til applikasjonen som kontrollerer en sammenstilling, så det vil ikke være spesielt sjelden å finne denne typen definisjoner som funksjoner uten parametere og som ikke returnerer verdier, noe sånt som void (*accion)();
Hvis du jobber med denne måten å representere dataene på, ved hjelp av struct
av data med variabel lengde, vil det være nødvendig å dynamisk allokere minne med malloc()
(o new()
, hvis objekter brukes), som vil bruke mengden minne som er tildelt som en parameter og returnere en peker til begynnelsen av minneområdet som er reservert. Med sizeof()
På typen som er lagret, multiplisert med antall elementer som brukes, kan du få mengden minne som trengs. Et eksempel med og uten bruk kan ses i skjermbildene nedenfor. malloc()
; Vær forsiktig med minnet som brukes av programmet i det første tilfellet, du må laste inn biblioteket som inneholder denne funksjonen.
Hvis operasjonene på ESP8266 wifi-modul vil variere gjennom gjennomføringen av programmet, vil det være nødvendig å frigjøre minnet som ikke brukes med free()
(o delete()
, i tilfelle av å være objekter). Selv om det er rimelig å forvente at kompilatoren (GCC) vil optimere programmet for å unngå minnepartisjonering, ytelsen vil sikkert ikke være like optimal som å jobbe med statisk allokert minne.
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
}
}
}
|
Selv om det i dette eksemplet (i begge implementeringene) ikke gir mye mening, for å generalisere operasjonen for å kunne bruke den på andre tilfeller, bør det bemerkes at å sende data gjentar alltid den samme protokollen: gi beskjed om antall byte som skal sendes, vent på indikatoren (>) og send dataene.
Siden det i dette eksemplet bare brukes ved én anledning (hele forespørselen er laget i én pakke), virker det ikke særlig nyttig, men generelt kan det være nødvendig å utføre flere sendinger i samme operasjon, inkludert tilfeller der de må overføres betydelige mengder data som må fragmenteres for å unngå å flyte over minnet ESP8266.
For å implementere denne oppførselen, kan de to siste elementene i forbindelsen brukes slik at hver gang dataene sendes, fylles dataene med de tilsvarende verdiene: i det første tilfellet, antall byte sendt og i det andre, ( del av forespørselen som skal overføres.
For å gjenta tilordning og sending av de forskjellige elementene som må overføres kan lagres i en vektor. Denne nye vektoren vil være den som bestemmer slutten på den komplekse operasjonen og ikke den siste operasjonen som til nå.
1 kommentar