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