العمليات الأساسية على وحدة wifi ESP8266 من Arduino
عندما قهوة اسبريسف أطلقت الوحدات الأولى في السوق واي فاي (WIFI) مع المتكاملة ESP8266 و الثابتة للتعامل معها باستخدام أوامر AT، ما كنا مهتمين به نحن المستخدمين هو دمجه في التجميعات باستخدام ميكروكنترولر واختصرت المشاكل في معرفة الظلام (سابقًا). ESP8266 في جدول الأوامر، احتياجات التغذية أو تحديث البرامج الثابتة ESP8266.
ثم وصلت البدائل بسرعة لبرمجة ESP8266 وتنفيذ الوحدة النمطية واي فاي (WIFI) بأشكال مختلفة جدًا أثارت مخاوف أخرى: أي وحدة واي فاي ESP8266 للاختيار اعتمادًا على نطاق الهوائيات المختلفة (بما في ذلك الهوائيات الخارجية) أو التكامل المادي لهذه الوحدات الجديدة في مجموعاتنا.
بالتأكيد، بسبب كل هذه التغييرات، ربما لم يتم التركيز على الجوانب الأساسية، الإدارة الأساسية للمنظمة وحدة واي فاي ESP8266. برغم من قطبية.es يمكنك العثور على معلومات حول استخدام ESP8266 وهناك بعض التطبيقات التي تهدف إلى شرح تشغيل ملف وحدة واي فاي ESP8266 باستخدام أوامر AT، وخاصة في المادة على مكتبة لإجراء استعلامات HTTP من Arduino باستخدام وحدة wifi ESP8266، تشير انطباعات القراء إلى أنه قد يكون من المفيد إضافة بعض المعلومات الأساسية لمساعدة مستخدمي الموقع ESP8266 لتنفيذ عمليات التنفيذ الخاصة بهم.
مناقشة العمليات الأساسية للعمل مع ESP8266 واقتراح حلول عامة هو هدف يتكون من عدة أجزاء مختلفة للغاية؛ للمساعدة في متابعة محتوى المقالة، يمكن استخدام الفهرس التالي كدليل:
- التحكم في وحدة wifi ESP8266 من الكمبيوتر من خلال المنفذ التسلسلي
- تحديث البرامج الثابتة مع esptool
- إرسال الطلبات إلى الوحدة
- تلقي البيانات من ESP8266
- تحليل الاستجابة من خلال البحث عن النصوص في المحتوى
- تحديد وقت الانتظار لتلقي الرد
- تنفيذ عملية معقدة تحددها أوامر AT متعددة
التحكم في وحدة wifi ESP8266 من الكمبيوتر من خلال المنفذ التسلسلي
من طبق اردوينو واستخدام الخاص بك IDE من الممكن مراقبة تشغيل أ وحدة واي فاي ESP8266، ارسل ESP8266 في الأوامر وشاهد الإجابة ولكن من الملائم جدًا القيام بذلك من جهاز كمبيوتر باستخدام تطبيق من النوع الطرفي.
اعتمادا على أي لوحة اردوينو عند استخدامه، قد يتوفر منفذ تسلسلي واحد فقط للأجهزة، مما يضيف القليل من الإزعاج عند الإرسال والاستقبال. يعد تغيير سرعة الاتصالات أكثر راحة في تطبيق الاتصالات التسلسلية من جهاز كمبيوتر وبعض اللوحات الأم. اردوينو (وفي بعض الظروف) لا تدعم أعلى سرعات الاتصالات التسلسلية بشكل جيد، خاصة 115200 باود، وهي السرعة الافتراضية لأحدث الإصدارات من الثابتة.
في ما هو البرنامج الذي يجب استخدامه لمراقبة ESP8266 باستخدام المنفذ التسلسلي، هناك الكثير للاختيار من بينها حسب الاحتياجات والتفضيلات؛ في الآونة الأخيرة أنا أستخدم الكلاسيكية أكثر كيوتكوم (الموجود في لقطة الشاشة أعلاه) لأنه من المريح جدًا بالنسبة لي أن أكرر بعضًا وحدة واي فاي ESP8266 في الطلبات في اختبار المشروع.
لقد تم بالفعل تقديم بعض التوصيات هنا بشأن البرامج التي تعمل كوحدة تحكم تسلسلية؛ على سبيل المثال، عندما نتحدث عن PuTTY للتحكم في أجهزة UART التسلسلية من الكمبيوتر. المعجونبالإضافة إلى كونه تطبيقًا ممتازًا، فهو متاح لمعظم أنظمة تشغيل سطح المكتب. علاوة على ذلك، كما المعجون يمكن استخدامها لتكون بمثابة وحدة تحكم مع كل من المنفذ التسلسلي و عائلة بروتوكول الإنترنت (TCP/IP)، بما في ذلك تلك التي تعمل عليها TLS، تصبح أداة شائعة تعوض أكثر من الوقت (القليل) الذي أمضيته في تكوينها والتعود على استخدامها.
بالإضافة إلى برامج الاتصالات التسلسلية، لتوصيل وحدة واي فاي ESP8266 الى الميناء ذراع USB يتطلب الكمبيوتر أيضًا محولًا ذراع USB سلسلة TTL. كما هو الحال في البرامج، هناك العديد من الإصدارات، والتي يتم استخدامها فقط لتحويل المنفذ ذراع USB على المنفذ التسلسلي TTL (والتي يمكن الحصول عليها من يورو واحد) إلى تلك التي يمكنها محاكاة بروتوكولات مختلفة (مثل SPI o I2C).
تمامًا مثل البرنامج الذي يعمل كوحدة تحكم تسلسلية، يتم توصيل الأجهزة بالكمبيوتر عبرها ذراع USB مع دائرة منطقية (وليس فقط ESP8266) ستكون أداة شائعة في عمل مطور التطبيقات الذي يتم التحكم فيه بشكل دقيق، ومن الجدير وضعها في صندوق الأدوات في أقرب وقت ممكن والعمل معها وحدة واي فاي ESP8266 إنها فرصة ممتازة للحصول على واحدة.
المحول ذراع USB a UART TTL ويمكن استخدامه أيضًا لمراقبة سلوك الدائرة التي تستخدم ESP8266للقيام بذلك، يتم توصيل المخرجات التي تريد مراقبتها على التوالي بمدخل البيانات (RX) الخاص بالمحول باستخدام الصمام الثنائي السريع ( 1N4148، على سبيل المثال) والمقاوم (2K2، على سبيل المثال) على التوازي مع بعضها البعض. يعمل مثل هذا الإعداد مثل جهاز الشم التسلسلي للأجهزة.
على الرغم من أن أداة الشم الموجودة في الصورة أعلاه بدائية بالتأكيد (من بين أشياء أخرى، فهي لا تحتوي على العازلة) يكفي لمراقبة تشغيل التجميع اردوينو و ESP8266.
إزالة الشم من المخطط السابق، رسم تخطيطي يوضح كيفية توصيل أ وحدة واي فاي ESP8266 إلى لوحة اردوينو. بالإضافة إلى تغذيته عند 3V3، يجب توصيل دبوس إعادة الضبط ودبوس التنشيط الخاصين بالجهاز المدمج بمستوى عالٍ (تمكين). بالطبع، يجب أن يتصل طرف RX الخاص بأحدهما بمنفذ TX الخاص بالآخر.
لتبسيط الرسم التخطيطي السابق، تم تمثيل لوحة اردوينو يتم تشغيله عند 3V3 والذي يُفترض أيضًا أن الجهد الكهربي الموجود على المنفذ التسلسلي له هو 3V3. إذا كنت تستخدم أ متحكم مع مستوى إشارة مختلف على المنفذ التسلسلي (عادة 5 فولت) سيكون من الضروري، حتى لا يتلف ESP8266، إستخدم محول المستوى مثل تلك الموجودة في المخططات أدناه. توجد هذه الدائرة بشكل متكرر في العديد من تطبيقات الوحدات التجارية الجاهزة.
تحديث البرامج الثابتة ESP8266
ال ESP8266 في الأوامر، وإنهائها، والسرعة الافتراضية للوحدة... تعتمد على إصدار البرامج الثابتة ESP8266. من الأفضل التأكد من أن لديك نفس الإصدار في جميع الوحدات، وإذا أمكن، فهو الإصدار الأحدث.
لسوء الحظ ، فإن معظم نماذج وحدة واي فاي ESP8266 لديهم 4 ميجابت فقط، لذلك لا يمكن تثبيت الإصدار الأحدث عليهم. أحدث إصدار (رسمي) من البرنامج الثابت الذي يمكن تثبيته عليه وحدات واي فاي ESP8266 مع 4 ميجابت (الأكثر) هو 0.9.4 والذي يتضمن الإصدار 0.2 من ESP8266 في الأوامر.
باختصار، لتحديث البرنامج الثابت تحتاج إلى:
-
قم بتنزيل إصدار البرنامج الثابت المقابل. ال أحدث إصدار (رسمي) للوحدة التي تحتوي على 4 ميجابت من الذاكرة، موجود في مجلد Espressif على جيثب. في موقع اسبريسيف يمكنك تنزيل أحدث إصدار من البرنامج الثابت، ولكن من المهم جدًا التحقق من أن الوحدة التي تم تثبيت البرنامج عليها بها ذاكرة كافية.
-
قم بتنزيل أحدث إصدار من أداة تثبيت البرامج الثابتة. المفضل لدي esptool الذي هو مكتوب في بايثون، لذلك فهو يعمل على أي منصة. بالإضافة إلى إمكانية تنزيله، يمكن أيضًا تثبيته باستخدام
pip install esptool
(opip2
opython -m pip
…). بالطبع، قهوة اسبريسف كما يقدم أيضًا أداة خاصة به ولكنه متاح حاليًا لنظام التشغيل Windows فقط. -
تحضير الملفات التي تم تنزيلها; قم بفك ضغطها في مجلد يمكن الوصول إليه، وإذا لزم الأمر، اجعل الأداة قابلة للتنفيذ esptool، في حالتي، منذ ذلك الحين جنو / لينكس، مع
chmod +x esptool
-
قم بتوصيل الوحدة بالكمبيوتر باستخدام المحول ذراع USB UART TTL الذي يعمل على 3V3 أو استخدم محول المستوى إذا كان يعمل عند 5 فولت. بالإضافة إلى الطاقة، سيتعين عليك توصيل TX إلى RX للمحول ذراع USB UART TTLو RX إلى TX و GPIO0 على مستوى منخفض (GND) وربما GPIO2 على مستوى عالٍ (في اختباراتي عملت على توصيله على مستوى منخفض وفصله). إذا كانت الوحدة تحتوي على اتصال GPIO15 مجاني (كما يحدث في ESP-12)، فيجب توصيلها بمستوى منخفض. يمكن ترك RESET، الذي يكون عادة على مستوى عالٍ أثناء التشغيل، غير متصل أو متصلاً بمستوى عالٍ عن طريق المقاوم (10K، على سبيل المثال)، لأنه قبل بدء التسجيل قد يكون من الضروري إعادة ضبط الجهاز عن طريق توصيله إلى مستوى منخفض .
من خلال تشغيل الوحدة، ستكون متاحة للتحديث، ولكن، إذا تم عرض خطأ في الاتصال، فسيكون من الضروري إعادة تعيينه توصيل RESET بمستوى منخفض للحظة ثم تركه على الهواء (بدون توصيل) لعملية التحديث.
الوحدة لديها ذروة استهلاك نصف أمبير (حتى 600 مللي أمبير، وفقًا لبعض المستخدمين) لذلك من المهم استخدام مصدر طاقة قادر على دعم هذا الاستهلاك، خاصة لتحديث البرامج الثابتة. -
قم بتشغيل الأداة لتحديث البرامج الثابتة. في حالتي، قمت بحفظ مستندات الأداة والبرامج الثابتة في الخطوة 3 في نفس المجلد، لذلك أقوم بتشغيلها من وحدة التحكم:
cd ~/Datos/firmwareESP8266
(قم بالتغيير إلى المجلد الذي يحتوي على الأداة والبرامج الثابتة)./esptool.py --baud 115200 --port /dev/ttyUSB0 write_flash \
0x00000 ./boot_v1.1.bin \
0x01000 ./user1.bin \
0x7C000 ./esp_init_data_default.bin \
0x7E000 ./blank.bin
--baud
يحدد سرعة ESP8266 (115200 باود في حالتي) و--port
المنفذ التسلسلي الذي يتصل به (في حالتي، تمت محاكاة أول USB). يتم تجاهل المستندات المختلفة التي تشكل البرنامج الثابتwrite_flash
يسبقه العنوان، مع مستند user1.bin الذي يحتوي على حمولة التحديث.
إرسال الأوامر إلى وحدة wifi ESP8266
للسيطرة على ESP8266 من جهاز كمبيوتر سيتعين علينا أن نبدأ به تكوين التطبيق والذي سيكون كافيًا①اختيار المنفذ الذي يتصل به المحول ذراع USB UART TTL، شيء مثل /dev/USB0
في جنو/لينكس وما شابه ذلك أو شيء من هذا القبيل COM6
في نظام التشغيل Windows، ② اختر السرعة التي يتم بها ESP8266، ربما 115200 باود، ③ قم بتعيين 8 بتات بيانات بالإضافة إلى بت توقف واحد، بدون تكافؤ أو مصافحة، و④ قم بتعيين نهاية السطر، اعتمادًا على الثابتة، دائمًا تقريبًا CR+LF.
بمجرد تكوين التطبيق (أو تخزينه واختياره حيثما يكون ذلك مناسبًا)، يصبح كذلك افتح الاتصال ("فتح الجهاز" و"فتح"، على التوالي، في لقطات شاشة الأمثلة أعلاه باستخدام كيوتكوم y المعجون) ويمكنك البدء في إرسال الطلبات إلى ESP8266.
كما يتبين في ESP8266 في جدول الأوامر، تنسيق التنشيط وإلغاء التنشيط وتعيين القيمة والإشارة إليها يمكن التنبؤ به تمامًا، ولكن بشكل عام ليس من السهل تذكرها جميعًا وربما ستحتاج إلى أن تكون في متناول يدك للإشارة إليها.
طريقة إرسال في أوامر al وحدة واي فاي ESP8266 من اردوينو الأمر بسيط للغاية: ① تكوين الاتصالات مع 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 في الطلبات من اردوينو. في هذه الحالة هو موضح AT+CWJAP
، والذي يستخدم للاتصال بنقطة الوصول. يستخدم هذا الأمر معرف نقطة الوصول (SSID) والمفتاح، كلاهما بين علامتي اقتباس، حتى يصبحا كائنًا Srtring
وأرفقها بين علامتي اقتباس باستخدام رمز الهروب (\"
). لإكمال الطلب، استخدم \r\n
الذي يتوافق مع CR
y LF
.
تذكر أن المنفذ التسلسلي لا يتم التعرف عليه دائمًا Serial
(على لوحات معينة يمكن أن يكون Serial1
, Serial2
…) تم تعريف كائن المنفذ المستخدم عن طريق تعيينه للماكرو PUERTO_SERIE
. اكتشاف نوع اللوحة المستخدمة يمكن أن يضيف القليل من الذكاء إلى اختيار المنفذ التسلسلي؛ سنتحدث لاحقًا عن كيفية معرفة نوعه اردوينو. بقية التعريفات هي التعريفات المعتادة التي تسمح لك "بتسمية" القيم الثابتة لتجنب تكرارها (وارتكاب الأخطاء) وتسهيل تغييرها.
من المفترض أن يقوم المثال أعلاه بتوصيل ملف وحدة واي فاي ESP8266 إلى نقطة الوصول المشار إليها ولكن هل كانت متصلة بالفعل من قبل؟ هل نجح الاتصال؟ لمعرفة ذلك، نحن بحاجة إلى "الاستماع" إلى ما ESP8266
تلقي البيانات من وحدة واي فاي ESP8266
من خلال توصيل أداة شم البيانات الموضحة أعلاه بالكمبيوتر، يمكنك معرفة ما يحدث اردوينو قد أرسل إلى ESP8266 ورده. للقراءة من اردوينو ومعالجة المعلومات الموجودة فيه، سيكون من الضروري الكشف عنها 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();
}
}
|
مثل على طبق من ذهب اردوينو أونو (وفي حالات أخرى) يؤدي فتح الشاشة التسلسلية إلى إعادة ضبط البرنامج، ويمكن استخدامه للرؤية في وحدة التحكم التسلسلية اردوينو المعلومات التي ترسلها ESP8266 كما تظهر لقطة الشاشة للصورة أدناه.
قم بتحليل الاستجابة المرسلة بواسطة وحدة wifi ESP8266
لقد رأينا بالفعل كيفية قراءة المعلومات التي تصل اردوينو من ESP8266. المشكلة التي يتعين عليك التعامل معها هي أنك لا تعرف متى ستبدأ في الوصول، وكم من الوقت سيستغرق الوصول، وكم سيكون طوله... وليس من الفعال انتظار الرد من ESP8266 يتم استلامه دون السماح ل متحكم أداء مهام أخرى في هذه الأثناء.
هناك طريقة بسيطة لإدارة هذا الظرف كرر البيانات الواردة بحثًا عن إجابات محددة والتي من خلالها، على سبيل المثال، يتم تنشيط المؤشرات (الأعلام أو المتغيرات المنطقية) التي ستحدد ما إذا كان سيتم مواصلة البحث في النص المستلم وما هي الإجراءات التي يجب تنفيذها بناءً على المعلومات الواردة من ESP8266. بينما يصل الرد متحكم يمكن أن يخصص لمهام أخرىعلى سبيل المثال، تلقي البيانات من أجهزة الاستشعار ومعالجتها.
ابحث عن نص في المعلومات الواردة من ESP8266
للبحث في النص الذي يأتي من ESP8266 يمكن أن يكون قارن كل حرف مستلم بالحرف الذي يتوافق مع الرسالة التي تبحث عنها. سيكون من الضروري استخدام عداد (أو مؤشر) يشير إلى الحرف المراد مقارنته؛ إذا كان الحرف الذي يصل من ESP8266 هو نفسه الذي يتم فحصه في الرسالة، يتقدم العداد، إذا كان مختلفًا يتم تهيئته.
ولمعرفة أنه تم الوصول إلى النهاية يتم الرجوع إلى الحرف التالي للرسالة التي تم البحث عنها والذي سيكون صفراً (\0
) أو يتم تخزين طول الرسالة من خلال مقارنتها بالعداد لمعرفة ما إذا كانت المقارنة قد انتهت وبالتالي وحدة واي فاي ESP8266 لقد أرسل الرسالة المطلوبة.
يستخدم المثال التالي الأمر AT+CWLAP
والتي ستعرض قائمة بنقاط الوصول وداخلها يتم البحث عن نقطة تسمى "wifi Polaridad.es". على الرغم من أننا اخترنا التحقق من أن الحرف الأخير هو صفر، كما هو الحال في العازلة فهو يقوم فقط بتخزين النص الذي تم البحث عنه ويكون طوله معروفًا، ويمكن أيضًا التحقق مما إذا كان قد تم استلام هذا العدد من الحروف الصحيحة. مع LED متصلاً بالدبوس 2، تم الإبلاغ عن العثور على النص المتوقع.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define ORDEN “AT+CWLAP\r\n” // Buscar los puntos de acceso wifi disponibles. (Dependiendo de la versión del firmware) Las órdenes terminan en CR+LF
#define MENSAJE_BUSCADO “wifi polaridad.es” // Ver si está disponible el punto de acceso llamado “wifi polaridad.es”
#define LONGITUD_MENSAJE 18 // Se necesita guardar, al menos, 17 letras y el terminador \0
#define PIN_LED_ENCONTRADO 2 // Pin al que se conecta el LED que informa de que se ha encontrado el texto (el punto de acceso buscado está disponible)
#include <string.h> // strncpy
boolean esperando=true;
char buffer_mensaje;
char mensaje[LONGITUD_MENSAJE];
byte posicion_mensaje=0;
void setup()
{
pinMode(PIN_LED_ENCONTRADO,OUTPUT);
digitalWrite(PIN_LED_ENCONTRADO,LOW); // Apagar el LED (por ahora no se ha encontrado el mensaje)
strncpy(mensaje,MENSAJE_BUSCADO,LONGITUD_MENSAJE); // sizeof(mensaje)
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
SERIE.print(ORDEN); // Enviar la orden (consultar los puntos de acceso disponibles) al módulo wifi ESP8266
}
void loop()
{
if(esperando) // Buscará infinitamente hasta que llegue el texto esperado (y si no existe el punto de acceso nunca llegará ¡Es un ejemplo!)
{
while(SERIE.available()) // Si ha llegado algún dato por el puerto serie…
{
buffer_mensaje=SERIE.read(); // …almacenarlo en el buffer
if(buffer_mensaje==mensaje[posicion_mensaje]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
posicion_mensaje++; // Pasar a la siguiente letra del mensaje
if(mensaje[posicion_mensaje]==0) // ¿Ha terminado de analizarse todo el mensaje? (la última letra de la cadena de texto es \0)
{
esperando=false; // Si se ha terminado de analizar con éxito todo el mensaje ya no se está esperando
digitalWrite(PIN_LED_ENCONTRADO,HIGH); // Encender el LED para indicar que se ha encontrado el texto buscado
}
}
else // Si la letra que ha llegado por el puerto serie no corresponde con la buscada del mensaje…
{
posicion_mensaje=0; // …empezar desde la primera letra del texto buscado
}
}
}
}
|
في كود المثال السابق يمكنك أيضًا رؤية طريقة اختر المنفذ التسلسلي حسب نوع اللوحة اردوينو مستخدم. يفترض هذا المثال أن لديك ثلاثة أنواع من اللوحات للمشروع: واحد اردوينو أونو، واحد اردوينو ميجا 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?
، والذي يعمل على التحقق مما إذا كان وحدة واي فاي ESP8266 متصل بالفعل بنقطة وصول.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define ORDEN “AT+CWJAP?\r\n” // Verificar que está conectado a un punto de acceso
#define CANTIDAD_MENSAJES 2 // Se distingue entre dos mensajes: acierto y error que se identificarán con true y false
#define MENSAJE_ACIERTO “OK”
#define MENSAJE_ERROR “ERROR”
#define LONGITUD_MENSAJE 6 // Se necesita guardar, al menos, 5 letras y el terminador \0 (Un pequeño desperdicio al hacer una matriz en la que todos los elementos ocupan como el mayor. Es admisible porque son muy pocos elementos y así no se complica este ejemplo inicial)
#define PIN_LED_ACIERTO 2 // Pin al que se conecta el LED que informa del acierto
#define PIN_LED_ERROR 3 // Pin al que se conecta el LED que informa del error
#include <string.h> // strncpy
boolean esperando=true; // Todavía no se ha encontrado el final del mensaje
char buffer_mensaje; // Para almacenar la última letra cargada desde el ESP8266
byte led_estado[CANTIDAD_MENSAJES]; // Un LED (pin) para cada estado
char mensaje[CANTIDAD_MENSAJES][LONGITUD_MENSAJE]; // Mensajes de acierto y error
byte posicion_mensaje[CANTIDAD_MENSAJES]; // Un contador de posición para cada mensaje
void setup()
{
led_estado[true]=PIN_LED_ACIERTO;
led_estado[false]=PIN_LED_ERROR;
for(byte numero_mensaje=0;numero_mensaje<CANTIDAD_MENSAJES;numero_mensaje++)
{
pinMode(led_estado[numero_mensaje],OUTPUT); // Establecer el pin del LED
digitalWrite(led_estado[numero_mensaje],LOW); // Apagar el LED
posicion_mensaje[numero_mensaje]=0; // Inicializar a cero el número de letra a analizar de cada mensaje
}
strncpy(mensaje[true],MENSAJE_ACIERTO,LONGITUD_MENSAJE); // Preparar el mensaje de acierto
strncpy(mensaje[false],MENSAJE_ERROR,LONGITUD_MENSAJE); // Preparar el mensaje de error
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
SERIE.print(ORDEN); // Enviar la orden (verificar si existe conexión a un punto de acceso) al módulo wifi ESP8266
}
void loop()
{
if(esperando) // Buscará infinitamente hasta que llegue el texto esperado (y si no existe el punto de acceso nunca llegará ¡Es un ejemplo!)
{
while(SERIE.available()) // Si ha llegado algún dato por el puerto serie…
{
buffer_mensaje=SERIE.read(); // …almacenarlo en el buffer
for(byte numero_mensaje=0;numero_mensaje<CANTIDAD_MENSAJES;numero_mensaje++)
{
if(buffer_mensaje==mensaje[numero_mensaje][posicion_mensaje[numero_mensaje]]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
posicion_mensaje[numero_mensaje]++; // Pasar a la siguiente letra del mensaje
if(mensaje[numero_mensaje][posicion_mensaje[numero_mensaje]]==0) // ¿Ha terminado de analizarse todo el mensaje actual? (la última letra de la cadena de texto es \0)
{
esperando=false; // Si se ha encontrado algún mensaje y ya no se está esperando
digitalWrite(led_estado[numero_mensaje],HIGH); // Encender el LED correspondiente al mensaje encontrado
}
}
else // Si la letra que ha llegado por el puerto serie no corresponde con la buscada del mensaje…
{
posicion_mensaje[numero_mensaje]=0; // …empezar desde la primera letra del texto buscado
}
}
}
}
}
|
يتيح لك هذا التطبيق الجديد لنفس الطريقة، والذي يبحث عن تطابق مع العديد من الرسائل المحتملة، الاختيار بين إجراءات مختلفة اعتمادًا على الاستجابة المستلمة من ESP8266، ببساطة قم بتشغيل LED المقابلة.
تحديد الوقت المستغرق لتلقي الرد
حتى الآن لم تتم الإشارة إلى قضية ذات صلة: وهي الحد الأقصى لوقت الانتظار (المهلة) قبل التفكير في فشل العملية. إذا كان لأي سبب من الأسباب الاتصال مع وحدة واي فاي 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
|
عدد صحيح طويل في اردوينو (unsigned long
) تشغل 4 بايت (32 بت)، وبالتالي فإن أكبر قيمة يمكن أن تمثلها هي 4294967295 (2 أس 32 ناقص واحد، لأنها تبدأ من الصفر). على طبق اردوينو أثناء التشغيل المستمر، سيتم إعادة ضبط عداد المللي ثانية (العودة إلى الصفر) كل 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;
}
}
}
|
يظهر الكود أعلاه أ التنفيذ الأساسي للغاية للحد من المهلة دمج الأسطر المحددة فيما يتعلق بالمثال الذي يسبقه. نظرًا لأنه يتم إجراء التحقق من المهلة بعد معالجة البيانات الواردة من وحدة واي فاي ESP8266يمكن اعتبار العملية ناجحة حتى لو استغرق الاستقبال وقتًا أطول من وقت الانتظار المفروض.
تنفيذ عملية معقدة تحددها أوامر AT متعددة
للحصول على مثال مرجعي للغرض من التطبيق الذي يستغل وحدة واي فاي 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+CIPMUX=0
y AT+CWMODE=1
.
بنية بيانات لتمثيل العمليات على ESP8266
في الأمثلة السابقة، على الرغم من أنه بطريقة أساسية جدًا، تم بالفعل اقتراح حل عام للمشكلة: استخدم بنية بيانات تخزن الاستجابات المحتملة والإجراءات التي يجب اتخاذها في كل حالة; أرسل إجراءً، وانتظر الرد، ثم تابع وفقًا لما يعنيه الرد. لأن كل عملية معقدة سوف تتطلب عدة ESP8266 في الأوامريجب أن تربط بنية البيانات عملية مع أخرى، لاحقة أو سابقة، والتي يجب تنفيذها في كل حالة حسب استجابة ESP8266.
في الأمثلة السابقة تم البحث عن رسالة ضمن رد الـ ESP8266 وتم تفسيره على أنه نجاح أو خطأ. بالإضافة إلى استقبال (وتحليل) جميع النصوص الواردة، للحصول على حد أدنى عام، يُنصح أيضًا بالاهتمام بإكمال الرسالة أو بمعنى آخر مدى توفرها وحدة واي فاي ESP8266 لتلقي الطلبات الجديدة. بهذه الطريقة، التغيير إلى الحالة التي يمكن أن نطلق عليها، على سبيل المثال، "wifi متاح"، يمكن أن يكون تلقي اسم نقطة الوصول واستلام النص ERROR
أو النص OK
قد يعني أن ESP8266 لقد انتهيت من الرد ويمكنك الآن إرسال الرد التالي في الأمر إلى 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
) مع الاستجابات الثلاثة التي تم تحليلها. كما وضحنا أعلاه، من الضروري البحث عن الرسائل التي تمثل نهاية الرد بالإضافة إلى الرسالة التي تمثل إجابة صحيحة أو غير صحيحة. لن تحتوي جميع العمليات على نفس عدد الإجابات المحتملة؛ عندما يكون هناك عدد أقل من الاستجابات، يمكن استخدام رسالة فارغة تستهلك أقل عدد ممكن من الدورات في تحليلها (ومع ذلك، فهي ليست الطريقة الأمثل). ومن الناحية المنطقية، سيكون من الضروري أن يشمل الحد الأدنى لعدد الإجابات المطلوبة (ثلاثة في المثال) جميع الاحتمالات التشغيلية، حتى لو لم تكن كلها ممكنة.
عند الحديث عن الإجابات المحتملة، يمكن أن نرى بالفعل أن هذا المثال ليس مفيدًا جدًا لتلقي البيانات بتنسيق عشوائي من ملف وحدة واي فاي ESP8266، ولكن الشيء هو أنه في سياق الاستخدام مع ميكروكنترولر ليس من المعتاد؛ الشيء الأكثر شيوعًا هو إرسال البيانات التي تم جمعها بواسطة أجهزة الاستشعار التي تم توصيلها و/أو تلقي معلومات حول ما يجب فعله بالمشغلات التي تتحكم فيها. معلومات قيمة للغاية، ويمكن التنبؤ بها بشكل جيد للغاية.
في بنية البيانات السابقة، تمامًا كما يتم التعبير عن الاستجابات المحتملة التي يتم تحليلها، يتم أيضًا استخدام مصفوفة ثنائية الأبعاد لتحديد العملية التي يجب إجراؤها في كل حالة (siguiente_operacion
). على وجه التحديد، اخترنا الرد على ثلاثة أنواع من الرسائل: ① نص عشوائي (LITERAL
) للتحقق مما إذا كان هناك اتصال بنقطة وصول Wi-Fi والخادم، ② نص لاكتشاف الأخطاء في العملية (FALLO
) و③ نص يشير إلى أن العملية قد تمت بنجاح (ACIERTO
).
أخيرًا، هناك متجهان آخران لتعيين الحد الأقصى لوقت الانتظار قبل الاستسلام (timeout
) وحدد (configuracion
) إذا انتهت العملية دون انتظار الرد (ESPERAR_RESPUESTA
) والرسائل التي تشير إلى انتهاء الاتصال. هذا المتجه الأخير، لتوضيح مثال لكيفية حفظ الذاكرة، يعمل مع وحدات بايت التكوين للإشارة إلى الحالات المختلفة.
الأول ESP8266 في الأوامر من بنية البيانات تتوقع دائمًا استجابة، والتي يمكن أن تكون رسالة نجاح أو خطأ. عند حدوث خطأ، تتم إعادة تشغيل الوحدة وتبدأ مرة أخرى وإذا كانت الرسالة تشير إلى أن العملية صحيحة، فسيتم الانتقال إلى العملية التالية.
عندما تتصل بالخادم، يتغير النمط. في هذه الحالة، من الضروري①إرسال طول حزمة البيانات المراد إرسالها و②إنشاء الطلب HTTP بنص ثابت بالإضافة إلى القيمة (درجة الحرارة) التي يتم إرسالها لتخزينها على الخادم. يتم تحضير هذه البيانات في كل شحنة ومن الضروري تقسيمها إلى قسمين (إخطار بالطول) أو ثلاثة (إرسال الطلب) HTTP) ل 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
|
#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();
|
ما يلي هو رمز البرنامج الرئيسي للمثال. المهمة الخارجية هي تلك المسؤولة عن أخذ عينات من درجة الحرارة لحساب المتوسط، ويتم إرسالها كل فترة زمنية معينة إلى الخادم باستخدام وحدة واي فاي 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 في الأوامر والرسائل التي يتم تحليلها.
استخدم البنية (struct
) لتخزين البيانات بدلاً من المصفوفات النموذجية (المبنية عليها) هو أمر تافه، وعلى الرغم من أنه قد يؤدي إلى تعليمات برمجية أكثر أناقة، إلا أنه لا يعني أي تحسن في النتيجة. البديل الحقيقي الذي يطرحه استخدام struct
للتنفيذ كما هو موضح أدناه أطوال متغيرة في الهياكل التي تحتوي على بيانات "داخلية". التي يشيرون إليها. بهذه الطريقة، على سبيل المثال، لن يكون من الضروري أن تحتوي العملية على عدد محدد من الاستجابات لتحليلها.
يشير هذا النهج إلى أنه أفضل طريقة لتنفيذ الحل ولكن عيبه هو أنه سيكون ضروريًا استخدام تخصيص الذاكرة الديناميكية، ممارسة محفوفة بالمخاطر في العمل مع متحكم الأمر الذي يتطلب قياسًا دقيقًا لمقدار الذاكرة التي سيتم استخدامها في وقت التشغيل، نظرًا لأن المترجم لن يتمكن من تحذيرنا بشأن هذا الأمر وهناك احتمال معين لاستنفاد الذاكرة (أو المكدس) مع عواقب وخيمة على تنفيذ البرنامج.
في خط تحسين الكود، من المثير للاهتمام أن نتذكر أنه في برنامج من هذا النوع، والذي يستخدم كمية كبيرة من النص، يمكن توفير مساحة الذاكرة SRAM تخزين السلاسل النصية في ذاكرة البرنامج (فلاش) مع الماكرو F()
. في لقطات الشاشة التالية، يمكنك رؤية البرنامج المختلف وتوزيع الذاكرة الديناميكي مع الاستخدام العادي للنص واستخدام الماكرو F()
.
فيما يتعلق بالإجراءات التي يتم تنفيذها بناءً على المعلومات الواردة من وحدة واي فاي 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;
|
نظرًا لأن الهيكل الذي يمثل العملية (البيانات التي يتم إرسالها إلى ملف وحدة واي فاي ESP8266) يشير إلى الهيكل الذي يتم من خلاله تحديد الاستجابات، وهيكل الاستجابات لهيكل العمليات، من الضروري الإعلان عن كليهما أولاًوذلك من خلال تحديد نوع البيانات الجديد، ومن ثم تحديد محتوياته.
يعتبر المثال السابق أن البرنامج الذي يتضمنه قد اختار استخدام ملف مؤشر الحالة، والذي يجب أن يتوافق مع متغير يمكن الوصول إليه من الكود المسؤول عن تنفيذ عملية أو أخرى كما هو موضح بالقيمة المذكورة. إذا في رد ESP8266 عندما يتم تحليل نص معين، تأخذ الحالة القيمة التي تشير إلى بنية الاستجابة المقابلة.
وكما ذكرنا من قبل، سيكون هناك بديل آخر، إما لاستبدال مؤشر الحالة أو استكماله تخزين وظيفة في البنية المرجعية (مؤشر) سيتم استدعاؤه عند مواجهة نص معين في الرد من ملف وحدة واي فاي 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;
|
في المثال السابق، تمت إضافته إلى بنية البيانات المستخدمة لمعالجة الاستجابة من وحدة واي فاي ESP8266 مؤشر إلى دالة (مفترضة) تقوم بإرجاع بيانات من النوع float
(يمكن أن تكون القيمة المرجحة للقراءة التناظرية) والتي يتم توفير بايتين لها كوسائط (اثنان unsigned char
والذي يمكن أن يكون هو الدبوس الذي تتم قراءة الإدخال التناظري منه والذي يقوم بتنشيط ENABLE للمتكامل الافتراضي).
قيد التطوير ل MCU، على عكس ما يحدث في أسلوب التطوير للأنظمة الأكبر، ليس من غير المألوف استخدام المتغيرات العامة عند تحديد السلوك (العالمي) للتطبيق الذي يتحكم في التجميع، لذلك لن يكون من النادر بشكل خاص العثور على هذا النوع من التعريفات كوظائف بدون معلمات ولا تُرجع قيمًا، شيء من هذا القبيل void (*accion)();
إذا كنت تعمل بهذه الطريقة لتمثيل البيانات، فاستخدم struct
من البيانات ذات الطول المتغير، سيكون من الضروري تخصيص الذاكرة ديناميكيًا malloc()
(o new()
، إذا تم استخدام الكائنات)، والتي ستستخدم مقدار الذاكرة المخصصة كمعلمة وتعيد المؤشر إلى بداية منطقة الذاكرة المحجوزة. مع sizeof()
بالنسبة للنوع الذي يتم تخزينه، مضروبًا في عدد العناصر المستخدمة، يمكنك الحصول على مقدار الذاكرة المطلوبة. يمكن رؤية مثال مع وبدون استخدامه في لقطات الشاشة أدناه. malloc()
; كن حذرا مع الذاكرة التي يستخدمها البرنامج. في الحالة الأولى، تحتاج إلى تحميل المكتبة التي تحتوي على هذه الوظيفة.
إذا كانت العمليات على وحدة واي فاي ESP8266 سوف تختلف طوال تنفيذ البرنامج، سيكون من الضروري تحرير الذاكرة التي لم يتم استخدامها معها free()
(o delete()
، في حالة كونها كائنات). على الرغم من أنه من المعقول أن نتوقع أن المترجم (الخليج) سيعمل على تحسين البرنامج لتجنب تقسيم الذاكرة، وبالتأكيد لن يكون الأداء مثاليًا مثل العمل مع الذاكرة المخصصة بشكل ثابت.
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.
لتنفيذ هذا السلوك، يمكن استخدام العنصرين الأخيرين من الاتصال بحيث في كل مرة يتم إرسال البيانات، يتم ملء البيانات بالقيم المقابلة: في الحالة الأولى، عدد البايتات المرسلة وفي الثانية، ( جزء من) الطلب. ليتم إرساله.
لتكرار المهمة وإرسال العناصر المختلفة التي يجب إرسالها يمكن تخزينها في ناقل. سيكون هذا المتجه الجديد هو الذي يحدد نهاية العملية المعقدة وليس العملية الأخيرة كما هي حتى الآن.
تعليق