Βασικές λειτουργίες σε μια μονάδα wifi ESP8266 από το Arduino
Όταν Espressif κυκλοφόρησε τις πρώτες ενότητες στην αγορά Wi-Fi με το ενσωματωμένο ESP8266 και firmware για να το χειριστούμε χρησιμοποιώντας εντολές AT, αυτό που μας ενδιέφερε οι χρήστες ήταν η ενσωμάτωσή του σε συγκροτήματα με μικροελεγκτές και τα προβλήματα περιορίστηκαν στη γνώση του (πρώην) σκοτεινού Πίνακας εντολών ESP8266 AT, ανάγκες σίτισης ή Ενημέρωση υλικολογισμικού ESP8266.
Στη συνέχεια έφτασαν γρήγορα εναλλακτικές για να προγραμματίσουν το ESP8266 και υλοποιήσεις λειτουργικών μονάδων Wi-Fi πολύ διαφορετικών μορφών που προκάλεσαν άλλες ανησυχίες: ποια μονάδα wifi ESP8266 να επιλέξετε ανάλογα με το εύρος των διαφορετικών κεραιών (συμπεριλαμβανομένων των εξωτερικών) ή τη φυσική ενσωμάτωση αυτών των νέων μονάδων στις συναρμολογήσεις μας.
Σίγουρα, λόγω όλων αυτών των αλλαγών, μπορεί να μην έχει δοθεί έμφαση στις πιο βασικές πτυχές, στην πιο βασική διαχείριση του Μονάδα wifi ESP8266. Παρόλο πολικότητα.εσ Μπορείτε να βρείτε πληροφορίες σχετικά με τη χρήση του 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 baud, που είναι η προεπιλεγμένη ταχύτητα των τελευταίων εκδόσεων του firmware.
Περίπου Τι πρόγραμμα να χρησιμοποιήσετε για την παρακολούθηση ESP8266 χρησιμοποιώντας τη σειριακή θύρα, υπάρχουν πολλά για να διαλέξετε ανάλογα με τις ανάγκες και τις προτιμήσεις. τον τελευταίο καιρό χρησιμοποιώ περισσότερο το κλασικό CuteCom (αυτό στο στιγμιότυπο οθόνης παραπάνω) γιατί είναι πολύ άνετο για μένα να επαναλάβω ορισμένα ESP8266 μονάδα wifi AT παραγγελίες σε δοκιμές έργου.
Ορισμένες συστάσεις έχουν ήδη δοθεί εδώ για προγράμματα που λειτουργούν ως σειριακή κονσόλα. Για παράδειγμα, όταν μιλάμε για PuTTY για τον έλεγχο σειριακών συσκευών UART από τον υπολογιστή. PuTTYΕκτός από εξαιρετική εφαρμογή, είναι διαθέσιμη για τα περισσότερα λειτουργικά συστήματα επιτραπέζιων υπολογιστών. Επιπλέον, όπως PuTTY μπορεί να χρησιμοποιηθεί για να λειτουργήσει ως κονσόλα τόσο με τη σειριακή θύρα όσο και με το Οικογένεια πρωτοκόλλων Διαδικτύου (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, για παράδειγμα) παράλληλα μεταξύ τους. Μια τέτοια εγκατάσταση λειτουργεί σαν σειριακός ανιχνευτής υλικού.
Αν και το sniffer στην παραπάνω εικόνα είναι σίγουρα υποτυπώδες (μεταξύ άλλων δεν έχει ρυθμιστικό) επαρκεί για την παρακολούθηση της λειτουργίας ενός συγκροτήματος με Arduino και ESP8266.
Αφαιρώντας το sniffer από το προηγούμενο σχήμα, το σχηματικό που δείχνει πώς να συνδέσετε ένα Μονάδα wifi ESP8266 σε ένα πιάτο Arduino. Εκτός από την τροφοδοσία του στα 3V3, ο ακροδέκτης επαναφοράς και ο ακροδέκτης ενεργοποίησης του ενσωματωμένου πρέπει να είναι συνδεδεμένοι σε υψηλό επίπεδο (ενεργοποίηση). Φυσικά, η ακίδα RX του ενός πρέπει να συνδεθεί στο TX του άλλου.
Για απλοποίηση του προηγούμενου διαγράμματος, έχει αναπαρασταθεί μια πλάκα Arduino τροφοδοτείται στα 3V3 και για τα οποία η τάση στη σειριακή θύρα θεωρείται επίσης ότι είναι 3V3. Εάν χρησιμοποιείτε α μικροελεγκτής με διαφορετικό επίπεδο σήματος στη σειριακή θύρα (συνήθως 5 V) θα είναι απαραίτητο, ώστε να μην προκληθεί ζημιά στο ESP8266, χρησιμοποίησε ένα μετατροπέας στάθμης όπως αυτά στα παρακάτω διαγράμματα. Αυτό το κύκλωμα βρίσκεται συχνά σε πολλές εμπορικές εφαρμογές εκτός ραφιού.
Ενημερώστε το υλικολογισμικό ESP8266
ο Εντολές ESP8266 AT, ο τερματισμός του, η προεπιλεγμένη ταχύτητα του module... εξαρτώνται από την έκδοση του Υλικολογισμικό ESP8266. Είναι καλύτερο να βεβαιωθείτε ότι έχετε την ίδια έκδοση σε όλες τις μονάδες και, εάν είναι δυνατόν, ότι είναι η πιο πρόσφατη έκδοση.
Δυστυχώς, τα περισσότερα από τα Μοντέλα μονάδας wifi ESP8266 Έχουν μόνο 4 Mbit, επομένως δεν μπορεί να εγκατασταθεί η τελευταία έκδοση σε αυτά. Η τελευταία (επίσημη) έκδοση υλικολογισμικού που μπορεί να εγκατασταθεί ESP8266 μονάδες wifi με 4 Mbit (τα περισσότερα) είναι 0.9.4 που περιλαμβάνει την έκδοση 0.2 του Εντολές ESP8266 AT.
Συνοπτικά, για να ενημερώσετε το υλικολογισμικό χρειάζεστε:
-
Κατεβάστε την αντίστοιχη έκδοση υλικολογισμικού. Ο τελευταία (επίσημη) έκδοση για μονάδα με μνήμη 4 Mbit, που βρίσκεται στο φάκελο Espressif στο github. Στο Ιστοσελίδα Espressif Μπορείτε να κάνετε λήψη της πιο πρόσφατης έκδοσης του υλικολογισμικού, αλλά είναι πολύ σημαντικό να επαληθεύσετε ότι η μονάδα στην οποία είναι εγκατεστημένη έχει αρκετή μνήμη.
-
Κάντε λήψη της πιο πρόσφατης έκδοσης του εργαλείου εγκατάστασης υλικολογισμικού. Το αγαπημένο μου είναι esptool που είναι γραμμένο στο Python, επομένως λειτουργεί σε οποιαδήποτε πλατφόρμα. Εκτός από τη λήψη, μπορεί επίσης να εγκατασταθεί με
pip install esptool
(opip2
opython -m pip
…). Φυσικά, Espressif Προσφέρει επίσης το δικό του εργαλείο, αλλά προς το παρόν είναι διαθέσιμο μόνο για Windows. -
Προετοιμάστε τα ληφθέντα αρχεία; αποσυμπιέστε τα σε έναν προσβάσιμο φάκελο και, εάν χρειάζεται, κάντε το εργαλείο εκτελέσιμο esptool, στην περίπτωσή μου, από τότε GNU / Linux, Με
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 σε χαμηλό επίπεδο για μια στιγμή και μετά αφήνοντάς το on air (χωρίς σύνδεση) για τη διαδικασία ενημέρωσης.
Η ενότητα έχει Η κατανάλωση μισού αμπέρ κορυφώνεται (έως 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 baud στην περίπτωσή μου) και--port
τη σειριακή θύρα στην οποία συνδέεται (στην περίπτωσή μου, προσομοιωμένη, το πρώτο USB). Τα διάφορα έγγραφα που απαρτίζουν το υλικολογισμικό είναι πίσωwrite_flash
πριν από τη διεύθυνση, με το έγγραφο user1.bin να περιέχει το ωφέλιμο φορτίο ενημέρωσης.
Στείλτε εντολές στη μονάδα wifi ESP8266
Για τον έλεγχο του ESP8266 από έναν υπολογιστή με τον οποίο θα πρέπει να ξεκινήσουμε διαμορφώστε την εφαρμογή για την οποία θα αρκεί να ① επιλέξετε τη θύρα στην οποία είναι συνδεδεμένος ο μετατροπέας USB UART TTL, κάτι όπως /dev/USB0
σε GNU/Linux και παρόμοια ή κάτι παρόμοιο COM6
στα Windows, ② επιλέξτε την ταχύτητα με την οποία το ESP8266, πιθανώς 115200 baud, ③ ορίστε 8 bit δεδομένων συν ένα bit stop, χωρίς ισοτιμία ή χειραψία και ④ ορίστε το τέλος της γραμμής, ανάλογα με το firmware, σχεδόν πάντα CR+LF.
Μόλις διαμορφωθεί η εφαρμογή (ή, όπου χρειάζεται, αποθηκευτεί και επιλεγεί), είναι ανοίξτε τη σύνδεση ("open device" και "open", αντίστοιχα, στα στιγμιότυπα οθόνης των παραπάνω παραδειγμάτων με CuteCom y PuTTY) και μπορείτε να αρχίσετε να στέλνετε παραγγελίες στο ESP8266.
Όπως φαίνεται στο Πίνακας εντολών ESP8266 AT, η μορφή ενεργοποίησης, απενεργοποίησης, ρύθμισης μιας τιμής και αναφοράς σε αυτήν είναι αρκετά προβλέψιμη, αλλά γενικά δεν είναι εύκολο να τις θυμάστε όλες και μάλλον θα χρειαστεί να το έχετε στη διάθεσή σας για να ανατρέξετε σε αυτήν.
Ο τρόπος στείλετε 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
Συνδέοντας το sniffer δεδομένων που εξηγήθηκε παραπάνω στον υπολογιστή, μπορείτε να δείτε τι 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 Uno (και σε άλλα) το άνοιγμα της σειριακής οθόνης επαναφέρει το πρόγραμμα, μπορεί να χρησιμοποιηθεί για να δει στη σειριακή κονσόλα Arduino τις πληροφορίες που στέλνετε ESP8266 όπως δείχνει το στιγμιότυπο οθόνης της παρακάτω εικόνας.
Αναλύστε την απάντηση που στάλθηκε από τη μονάδα wifi ESP8266
Έχουμε ήδη δει πώς διαβάζουμε τις πληροφορίες που φτάνουν Arduino από ESP8266. Το πρόβλημα που πρέπει να αντιμετωπίσετε είναι ότι δεν ξέρετε πότε θα αρχίσει να φτάνει, πόσο καιρό θα πάρει για να φτάσει, πόσο θα είναι... και δεν είναι πολύ αποτελεσματικό να περιμένετε την απάντηση από το ESP8266 λαμβάνεται χωρίς να αφήσει το μικροελεγκτής εκτελέστε άλλες εργασίες στο μεταξύ.
Ένας απλός τρόπος διαχείρισης αυτής της κατάστασης είναι επαναλάβετε τα δεδομένα που ελήφθησαν αναζητώντας συγκεκριμένες απαντήσεις με τα οποία, για παράδειγμα, ενεργοποιούν δείκτες (σημαίες ή μεταβλητές Boolean) που θα καθορίσουν εάν θα συνεχιστεί η αναζήτηση στο ληφθέν κείμενο και ποιες ενέργειες θα πρέπει να πραγματοποιηθούν με βάση τις πληροφορίες που προέρχονται από το ESP8266. Ενώ φτάνει η απάντηση μικροελεγκτής μπορεί να αφιερωθεί σε άλλες εργασίες, για παράδειγμα, λήψη δεδομένων από αισθητήρες και επεξεργασία τους.
Αναζητήστε ένα κείμενο στις πληροφορίες που λάβατε από το ESP8266
Για αναζήτηση στο κείμενο που προέρχεται από το ESP8266 μπορείτε συγκρίνετε κάθε γράμμα που λάβατε με αυτό που αντιστοιχεί στο μήνυμα που αναζητάτε. Θα χρειαστεί να χρησιμοποιήσετε έναν μετρητή (ή έναν δείκτη) που δείχνει το γράμμα που πρόκειται να συγκριθεί. Αν ο χαρακτήρας που προέρχεται από το ESP8266 είναι το ίδιο με αυτό που εξετάζεται στο μήνυμα, ο μετρητής προχωρά, εάν είναι διαφορετικός αρχικοποιείται.
Για να μάθετε ότι έχει φτάσει το τέλος, συμβουλευτείτε τον επόμενο χαρακτήρα του μηνύματος που αναζητήσατε, ο οποίος θα είναι μηδέν (\0
) ή το μήκος του μηνύματος αποθηκεύεται ώστε, συγκρίνοντάς το με τον μετρητή, να γνωρίζουμε εάν η σύγκριση έχει ολοκληρωθεί και επομένως η Μονάδα wifi 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 } } } } |
Στον κώδικα του προηγούμενου παραδείγματος μπορείτε επίσης να δείτε έναν τρόπο επιλέξτε τη σειριακή θύρα ανάλογα με τον τύπο της πλακέτας Arduino μεταχειρισμένος. Αυτό το παράδειγμα προϋποθέτει ότι έχετε τρεις τύπους πινάκων για το έργο: ένα Arduino Uno, Ένα Arduino Mega 2560 και ένα arduino leonardo. Εάν εργάζεστε με ένα Arduino Uno θα χρησιμοποιηθεί Serial
και αλλιώς Serial1
.
Εάν εργάζεστε με ένα πιάτο arduino leonardo Μπορείτε να χρησιμοποιήσετε την ίδια μέθοδο για να σταματήσετε το πρόγραμμα και να περιμένετε την κονσόλα (τη σειριακή θύρα που σχετίζεται με 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, απλά ενεργοποιήστε το LED αντίστοιχος.
Περιορίστε το χρόνο που χρειάζεται για να λάβετε μια απάντηση
Μέχρι στιγμής δεν έχει γίνει αναφορά σε σχετικό θέμα: το μέγιστος χρόνος αναμονής (timeout) πριν θεωρηθεί ότι μια λειτουργία απέτυχε. Εάν για οποιοδήποτε λόγο η σύνδεση με το Μονάδα 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 |
Ένας μακρύς ακέραιος in Arduino (unsigned long
) καταλαμβάνει 4 byte (32 bit), επομένως η μεγαλύτερη τιμή που μπορεί να αντιπροσωπεύει είναι 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, ας υποθέσουμε ότι είναι αποθήκευση πληροφοριών σε μια βάση δεδομένων στην οποία έχει πρόσβαση μέσω μιας υπηρεσίας Ιστού για να παρακολουθείτε τη θερμοκρασία. Ο παρακάτω κώδικας διαβάζει έναν αισθητήρα συνδεδεμένο σε μια αναλογική είσοδο κάθε συγκεκριμένο χρονικό διάστημα, υπολογίζει τη μέση τιμή και, μετά από μεγαλύτερο χρονικό διάστημα, τον στέλνει στον διακομιστή web (στυλ 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”); } } |
Σε αυτό το παράδειγμα καταγραφής θερμοκρασίας, γίνεται πρόσβαση σε έναν διακομιστή web κάθε πέντε λεπτά. Αν και η διαθεσιμότητα δεν είναι ιδιαίτερα υψηλή, αναμένεται ότι η πρόταση θα λειτουργούσε, αλλά εάν χρειαζόταν μεγαλύτερη συχνότητα εγγραφής, θα έπρεπε να χρησιμοποιηθούν άλλοι πόροι, για παράδειγμα, προσωρινή μνήμη δεδομένων περιμένει να σταλεί, για να στείλετε πολλά όταν ο διακομιστής μπορεί να παρευρεθεί και να τα αποθηκεύσετε για όταν δεν είναι διαθέσιμο. Εάν η συχνότητα με την οποία πρέπει να καταγράφονται τα δεδομένα ήταν ακόμη μεγαλύτερη, θα έπρεπε να προταθούν άλλοι τύποι πρωτοκόλλων ως εναλλακτική λύση 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
) και μηνύματα που υποδεικνύουν το τέλος της επικοινωνίας. Αυτό το τελευταίο διάνυσμα, για να επεξηγήσει ένα παράδειγμα του πώς θα μπορούσε να αποθηκευτεί η μνήμη, λειτουργεί με τα bit ενός byte διαμόρφωσης για να υποδείξει τις διαφορετικές καταστάσεις.
Ο πρώτος Εντολές ESP8266 AT της δομής δεδομένων αναμένετε πάντα μια απάντηση, η οποία μπορεί να είναι το μήνυμα επιτυχίας ή σφάλματος. Όταν παρουσιαστεί κάποιο σφάλμα, η μονάδα επανεκκινείται και ξεκινά ξανά και εάν το μήνυμα υποδεικνύει ότι η λειτουργία είναι σωστή, προχωρά στην επόμενη.
Όταν συνδεθείτε στον διακομιστή, το μοτίβο αλλάζει. Σε αυτήν την περίπτωση, είναι απαραίτητο να ① να στείλετε το μήκος του πακέτου δεδομένων που πρόκειται να μεταδοθεί και ② να συντάξετε το αίτημα 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(); |
Ακολουθεί ο κώδικας του κύριου προγράμματος του παραδείγματος. Η πιο εξωτερική εργασία είναι αυτή που είναι επιφορτισμένη με τη δειγματοληψία της θερμοκρασίας για τον υπολογισμό του μέσου όρου και, κάθε συγκεκριμένη χρονική περίοδο, αποστέλλεται στον διακομιστή χρησιμοποιώντας το Μονάδα 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
(θα μπορούσε να είναι η σταθμισμένη τιμή μιας αναλογικής ανάγνωσης) και στην οποία παρέχονται δύο byte ως ορίσματα (δύο unsigned char
που θα μπορούσε να είναι η ακίδα από την οποία διαβάζεται η αναλογική είσοδος και αυτή που ενεργοποιεί το ΕΝΕΡΓΟΠΟΙΗΣΗ ενός υποθετικού ολοκληρωμένου).
Σε ανάπτυξη για 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 } } } |
Αν και σε αυτό το παράδειγμα (και στις δύο υλοποιήσεις) δεν έχει πολύ νόημα, για να γενικεύσουμε τη λειτουργία για να μπορέσουμε να την εφαρμόσουμε σε άλλες περιπτώσεις, θα πρέπει να σημειωθεί ότι η αποστολή δεδομένων επαναλαμβάνει πάντα το ίδιο πρωτόκολλο: ειδοποιήστε τον αριθμό των byte που θα σταλούν, περιμένετε την ένδειξη (>) και στείλτε τα δεδομένα.
Δεδομένου ότι σε αυτό το παράδειγμα χρησιμοποιείται μόνο μία φορά (ολόκληρο το αίτημα γίνεται σε ένα πακέτο), δεν φαίνεται πολύ χρήσιμο, αλλά, γενικά, μπορεί να είναι απαραίτητο να πραγματοποιηθούν πολλές αποστολές στην ίδια λειτουργία, συμπεριλαμβανομένων των περιπτώσεων στις οποίες πρέπει να μεταδοθούν σημαντικές ποσότητες δεδομένων που πρέπει να κατακερματιστούν για να αποφευχθεί η υπερχείλιση της μνήμης του ESP8266.
Για την υλοποίηση αυτής της συμπεριφοράς, τα δύο τελευταία στοιχεία της σύνδεσης μπορούν να χρησιμοποιηθούν έτσι ώστε κάθε φορά που αποστέλλονται τα δεδομένα, τα δεδομένα να συμπληρώνονται με τις αντίστοιχες τιμές: στην πρώτη περίπτωση, ο αριθμός των byte που αποστέλλονται και στη δεύτερη, το (( μέρος του) αιτήματος.να διαβιβαστεί.
Για να επαναλάβετε την ανάθεση και την αποστολή των διαφορετικών στοιχείων που πρέπει να μεταδοθούν μπορούν να αποθηκευτούν σε ένα διάνυσμα. Αυτό το νέο διάνυσμα θα είναι εκείνο που καθορίζει το τέλος της σύνθετης πράξης και όχι η τελευταία πράξη όπως μέχρι τώρα.
1 σχόλιο