การทำงานพื้นฐานบนโมดูล wifi ESP8266 จาก Arduino
เมื่อ เอสเพรสซิฟ เปิดตัวโมดูลแรกในตลาด อินเตอร์เน็ตไร้สาย ด้วยการบูรณาการ ESP8266 และ y เฟิร์มแว ในการจัดการโดยใช้คำสั่ง AT สิ่งที่ผู้ใช้สนใจคือการรวมเข้ากับชุดประกอบ ไมโครคอนโทรลเลอร์ และปัญหาก็ลดลงจนกลายเป็นการรู้จักความมืด (เดิม) ตารางคำสั่ง 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 คำสั่ง AT และดูคำตอบได้ แต่จะสะดวกกว่ามากถ้าทำจากคอมพิวเตอร์ที่มีแอพพลิเคชั่นประเภทเทอร์มินัล
ขึ้นอยู่กับบอร์ดไหน แพลตฟอร์มฮาร์ดแวร์ ใช้อาจมีพอร์ตอนุกรมฮาร์ดแวร์เพียงพอร์ตเดียวเท่านั้น ซึ่งเพิ่มความไม่สะดวกเล็กน้อยในการส่งและรับ การเปลี่ยนความเร็วการสื่อสารนั้นสะดวกสบายกว่ามากในแอปพลิเคชันการสื่อสารแบบอนุกรมจากคอมพิวเตอร์และมาเธอร์บอร์ดบางรุ่น แพลตฟอร์มฮาร์ดแวร์ (และในบางกรณี) ไม่รองรับความเร็วที่สูงกว่าของการสื่อสารแบบอนุกรมได้ดี โดยเฉพาะ 115200 บอด ซึ่งเป็นความเร็วเริ่มต้นของเวอร์ชันล่าสุด เฟิร์มแว.
sobre ใช้โปรแกรมอะไรในการตรวจสอบ. ESP8266 โดยใช้พอร์ตอนุกรมมีให้เลือกหลากหลายตามความต้องการและความชอบ ช่วงนี้ฉันใช้แบบคลาสสิกมากขึ้น น่ารักคอม (อันที่อยู่ในภาพหน้าจอด้านบน) เพราะมันสะดวกสบายมากสำหรับฉันที่จะทำซ้ำอย่างแน่นอน ESP8266 โมดูล wifi ตามคำสั่งซื้อ ในการทดสอบโครงการ
มีคำแนะนำบางประการเกี่ยวกับโปรแกรมที่ทำงานเป็นคอนโซลอนุกรมแล้ว เช่น เมื่อพูดถึง 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) ขนานกัน การตั้งค่าดังกล่าวทำงานเหมือนกับการดมกลิ่นอนุกรมของฮาร์ดแวร์
แม้ว่าการดมกลิ่นในภาพด้านบนจะถือเป็นเรื่องพื้นฐานอย่างแน่นอน (เหนือสิ่งอื่นใดมันไม่มี) กันชน) ก็เพียงพอที่จะติดตามการทำงานของชุดประกอบด้วย แพลตฟอร์มฮาร์ดแวร์ และ y ESP8266.
การลบดมกลิ่นออกจากโครงการก่อนหน้านี้ แผนผังแสดงวิธีการเชื่อมต่อ โมดูลอินเตอร์เน็ตไร้สาย ESP8266 ไปที่จาน แพลตฟอร์มฮาร์ดแวร์. นอกเหนือจากการป้อนที่ 3V3 แล้ว พินรีเซ็ตและพินการเปิดใช้งานของอินทิเกรตจะต้องเชื่อมต่อกับระดับสูง (เปิดใช้งาน) แน่นอนว่า RX pin ของอันหนึ่งต้องเชื่อมต่อกับ TX ของอีกอัน
เพื่อให้แผนภาพก่อนหน้านี้ง่ายขึ้น จึงได้แสดงแผ่นเพลทไว้ แพลตฟอร์มฮาร์ดแวร์ จ่ายไฟที่ 3V3 และซึ่งแรงดันไฟฟ้าบนพอร์ตอนุกรมจะถือว่าเป็น 3V3 ด้วย ถ้าคุณใช้ก ไมโครคอนโทรลเลอร์ จำเป็นต้องมีระดับสัญญาณที่แตกต่างกันบนพอร์ตอนุกรม (โดยทั่วไปคือ 5 V) เพื่อไม่ให้เกิดความเสียหาย ESP8266, ใช้ ตัวแปลงระดับ เช่นเดียวกับในแผนภาพด้านล่าง วงจรนี้มักพบในการใช้งานโมดูลนอกชั้นวางเชิงพาณิชย์จำนวนมาก
อัพเดตเฟิร์มแวร์ ESP8266
ลา ESP8266 คำสั่ง ATการยุติ ความเร็วเริ่มต้นของโมดูล... ขึ้นอยู่กับเวอร์ชันของโมดูล เฟิร์มแวร์ ESP8266. วิธีที่ดีที่สุดคือตรวจสอบให้แน่ใจว่าคุณมีเวอร์ชันเดียวกันในทุกโมดูล และหากเป็นไปได้ เป็นเวอร์ชันล่าสุด
น่าเสียดายที่ส่วนใหญ่ ESP8266 โมดูล wifi รุ่น มีเพียง 4Mbit เท่านั้น ดังนั้นจึงไม่สามารถติดตั้งเวอร์ชันล่าสุดได้ เฟิร์มแวร์เวอร์ชันล่าสุด (อย่างเป็นทางการ) ที่สามารถติดตั้งได้ โมดูลอินเตอร์เน็ตไร้สาย ESP8266 ด้วย 4 Mbit (ส่วนใหญ่) คือ 0.9.4 ซึ่งรวมถึงเวอร์ชัน 0.2 ของ ESP8266 คำสั่ง AT.
โดยสรุป หากต้องการอัปเดตเฟิร์มแวร์ที่คุณต้องการ:
-
ดาวน์โหลดเวอร์ชันเฟิร์มแวร์ที่เกี่ยวข้อง. เวอร์ชันล่าสุด (เป็นทางการ) สำหรับโมดูลที่มีหน่วยความจำ 4 Mbit พบในโฟลเดอร์ Espressif บน github. ใน เว็บไซต์เอสเพรสซิฟ คุณสามารถดาวน์โหลดเฟิร์มแวร์เวอร์ชันล่าสุดได้ แต่สิ่งสำคัญคือต้องตรวจสอบว่าโมดูลที่ติดตั้งนั้นมีหน่วยความจำเพียงพอ
-
ดาวน์โหลดเครื่องมือติดตั้งเฟิร์มแวร์เวอร์ชันล่าสุด. ที่ชอบคือ เอสตอล ซึ่งเขียนไว้ใน หลามดังนั้นจึงใช้งานได้บนทุกแพลตฟอร์ม นอกจากจะดาวน์โหลดแล้วยังสามารถติดตั้งได้ด้วย
pip install esptool
(opip2
opython -m pip
…) แน่นอน, เอสเพรสซิฟ นอกจากนี้ยังมีเครื่องมือของตัวเอง แต่ปัจจุบันใช้งานได้กับ Windows เท่านั้น -
เตรียมไฟล์ที่ดาวน์โหลด; แตกไฟล์เหล่านั้นในโฟลเดอร์ที่สามารถเข้าถึงได้ และหากจำเป็น ให้ทำให้เครื่องมือนี้ใช้งานได้ เอสตอลในกรณีของฉัน เนื่องจาก 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 ที่ระดับต่ำชั่วครู่แล้วจึงปล่อยทิ้งไว้ (โดยไม่ต้องเชื่อมต่อ) เพื่อดำเนินการอัปเดต
โมดูลมี การบริโภคสูงสุดครึ่งแอมแปร์ (ตามผู้ใช้บางคนระบุว่าสูงถึง 600 mA) ดังนั้นจึงเป็นสิ่งสำคัญที่จะต้องใช้แหล่งจ่ายไฟที่สามารถรองรับการบริโภคนี้ โดยเฉพาะอย่างยิ่งสำหรับการอัปเดตเฟิร์มแวร์ -
เรียกใช้เครื่องมือเพื่ออัพเดตเฟิร์มแวร์. ในกรณีของฉัน ฉันได้บันทึกเครื่องมือและเอกสารเฟิร์มแวร์ในขั้นตอนที่ 3 ไว้ในโฟลเดอร์เดียวกัน ดังนั้นฉันจึงเรียกใช้จากคอนโซล:
cd ~/Datos/firmwareESP8266
(เปลี่ยนเป็นโฟลเดอร์ที่มีเครื่องมือและเฟิร์มแวร์)./esptool.py --baud 115200 --port /dev/ttyUSB0 write_flash \
0x00000 ./boot_v1.1.bin \
0x01000 ./user1.bin \
0x7C000 ./esp_init_data_default.bin \
0x7E000 ./blank.bin
--baud
ตั้งค่าความเร็วของ ESP8266 (115200 บอดในกรณีของฉัน) และ--port
พอร์ตอนุกรมที่เชื่อมต่ออยู่ (ในกรณีของฉันจำลองคือ USB ตัวแรก) เอกสารต่างๆ ที่ประกอบเป็นเฟิร์มแวร์หายไปwrite_flash
นำหน้าด้วยที่อยู่ โดยมีเอกสาร user1.bin ที่มีเพย์โหลดการอัพเดต
ส่งคำสั่งไปยังโมดูล wifi ESP8266
เพื่อควบคุม ESP8266 จากคอมพิวเตอร์เราจะต้องเริ่มต้นด้วย กำหนดค่าแอป ซึ่งก็เพียงพอแล้วที่จะ 1 เลือกพอร์ตที่ตัวแปลงเชื่อมต่ออยู่ USB UART TTL, สิ่งที่ต้องการ /dev/USB0
ใน GNU/Linux และที่คล้ายกันหรืออะไรทำนองนั้น COM6
ใน Windows ② เลือกความเร็วที่ต้องการ ESP8266อาจเป็น 115200 บอด ④ ตั้งค่าบิตข้อมูล 8 บิตบวกหนึ่งบิตหยุด โดยไม่มีพาริตีหรือการแฮนด์เชค และ ④ ตั้งค่าจุดสิ้นสุดของบรรทัด ขึ้นอยู่กับ เฟิร์มแวCR+LF เกือบทุกครั้ง
เมื่อกำหนดค่าแอปพลิเคชันแล้ว (หรือจัดเก็บและเลือกตามความเหมาะสม) ก็จะเป็นเช่นนั้น เปิดการเชื่อมต่อ ("อุปกรณ์เปิด" และ "เปิด" ตามลำดับในภาพหน้าจอของตัวอย่างด้านบนด้วย น่ารักคอม y ฉาบ) และคุณสามารถเริ่มส่งคำสั่งซื้อไปที่ ESP8266.
ดังที่เห็นได้ใน ตารางคำสั่ง AT ESP8266รูปแบบการเปิดใช้งาน ปิดใช้งาน การตั้งค่าและการอ้างอิงนั้นค่อนข้างคาดเดาได้ แต่โดยทั่วไปแล้วมันไม่ง่ายเลยที่จะจดจำทั้งหมดและคุณอาจต้องมีไว้เพื่ออ้างอิง
ทางของ enviar ที่สั่งซื้อ al โมดูลอินเตอร์เน็ตไร้สาย ESP8266 จาก แพลตฟอร์มฮาร์ดแวร์ ง่ายมาก: 1 กำหนดค่าการสื่อสารด้วย Serial.begin(115200);
(หรือ Serial1, Serial2… บนบอร์ดที่มีพอร์ตอนุกรมฮาร์ดแวร์หลายตัว) และ XNUMX ส่งคำสั่งโดยใช้รูปแบบ 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+CWJAP
ซึ่งใช้เชื่อมต่อกับจุดเข้าใช้งาน คำสั่งนี้ใช้เป็นอาร์กิวเมนต์ตัวระบุจุดเข้าใช้งาน (SSID) และคีย์ ทั้งสองอยู่ในเครื่องหมายคำพูด ดังนั้นทั้งสองจึงกลายเป็นวัตถุ Srtring
และใส่ไว้ในเครื่องหมายคำพูดโดยใช้โค้ด Escape (\"
). หากต้องการดำเนินการสั่งซื้อให้เสร็จสิ้น ให้ใช้ \r\n
ซึ่งสอดคล้องกับ CR
y LF
.
ต้องจำไว้ว่าพอร์ตอนุกรมนั้นไม่ได้ระบุด้วยเสมอไป Serial
(บางจานก็ใส่ได้. Serial1
, Serial2
…) วัตถุพอร์ตที่ใช้ถูกกำหนดโดยการกำหนดให้กับแมโคร PUERTO_SERIE
. การตรวจจับประเภทของบอร์ดที่ใช้อาจเพิ่มความชาญฉลาดในการเลือกพอร์ตอนุกรม ต่อไปเราจะอธิบายวิธีการค้นหาประเภทของ แพลตฟอร์มฮาร์ดแวร์. คำจำกัดความที่เหลือเป็นคำจำกัดความปกติที่อนุญาตให้คุณ "ตั้งชื่อ" ค่าคงที่เพื่อหลีกเลี่ยงการทำซ้ำ (และทำผิดพลาด) และทำให้ง่ายต่อการเปลี่ยนแปลง
ตัวอย่างข้างต้นควรจะเชื่อมต่อ โมดูลอินเตอร์เน็ตไร้สาย ESP8266 ไปยังจุดเข้าใช้งานที่ระบุ แต่เคยเชื่อมต่อมาก่อนหรือไม่ การเชื่อมต่อใช้งานได้หรือไม่? หากต้องการทราบเรื่องนี้ เราต้อง "ฟัง" อะไรบ้าง ESP8266
รับข้อมูลจากโมดูล wifi 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();
}
}
|
เหมือนอยู่บนจาน Arduino Uno (และอย่างอื่น) การเปิดมอนิเตอร์แบบอนุกรมจะรีเซ็ตโปรแกรม ซึ่งสามารถใช้เพื่อดูในคอนโซลอนุกรม แพลตฟอร์มฮาร์ดแวร์ ข้อมูลที่คุณส่งไป 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
}
}
}
}
|
ในโค้ดของตัวอย่างก่อนหน้านี้ คุณสามารถดูวิธีการได้เช่นกัน เลือกพอร์ตอนุกรมขึ้นอยู่กับประเภทของบอร์ด แพลตฟอร์มฮาร์ดแวร์ ใช้แล้ว. ตัวอย่างนี้ถือว่าคุณมีบอร์ดสามประเภทสำหรับโปรเจ็กต์: ประเภทหนึ่ง Arduino Uno, Arduino ล้าน 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?
ซึ่งทำหน้าที่ตรวจสอบว่า โมดูลอินเตอร์เน็ตไร้สาย 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 ลบ XNUMX เนื่องจากเริ่มต้นที่ศูนย์) บนจาน แพลตฟอร์มฮาร์ดแวร์ ในขณะที่ทำงานอย่างต่อเนื่อง ตัวนับมิลลิวินาทีจะรีเซ็ต (กลับสู่ศูนย์) ประมาณทุกๆ 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ตัวอย่างเช่น การกำหนดค่าที่แสดงข้างต้นจะต้องมีสองรายการ: AT+CIPMUX=0
y AT+CWMODE=1
.
โครงสร้างข้อมูลที่แสดงถึงการดำเนินงานบน ESP8266
ในตัวอย่างก่อนหน้านี้ แม้ว่าจะเป็นแนวทางพื้นฐาน แต่ก็มีการแนะนำวิธีแก้ปัญหาทั่วไปไปแล้ว: ใช้โครงสร้างข้อมูลที่จัดเก็บการตอบสนองที่เป็นไปได้และการดำเนินการที่ต้องดำเนินการในแต่ละกรณี; ส่งการกระทำ รอการตอบกลับ และดำเนินการตามความหมายของการตอบกลับ เนื่องจากการดำเนินการที่ซับซ้อนแต่ละครั้งจะต้องใช้การดำเนินการหลายอย่าง ESP8266 คำสั่ง ATโครงสร้างข้อมูลจะต้องเชื่อมโยงการดำเนินการกับสิ่งอื่นภายหลังหรือก่อนหน้าซึ่งจะต้องดำเนินการในแต่ละกรณีขึ้นอยู่กับการตอบสนองของ ESP8266.
ในตัวอย่างก่อนหน้านี้ มีการค้นหาข้อความภายในการตอบกลับของ ESP8266 และถูกตีความว่าเป็นความสำเร็จหรือข้อผิดพลาด นอกเหนือจากการรับ (และการวิเคราะห์) ข้อความทั้งหมดที่ได้รับแล้ว หากต้องการให้มีขั้นต่ำทั่วไป ขอแนะนำให้ใส่ใจกับการเติมข้อความให้สมบูรณ์ด้วย หรืออีกนัยหนึ่งคือความพร้อมของ โมดูลอินเตอร์เน็ตไร้สาย ESP8266 เพื่อรับคำสั่งซื้อใหม่ ด้วยวิธีนี้ การเปลี่ยนแปลงสถานะที่เราเรียกได้ เช่น "wifi available" อาจเป็นการรับชื่อจุดเข้าใช้งานและรับข้อความ 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
) พร้อมคำตอบทั้งสามที่ได้รับการวิเคราะห์ ตามที่อธิบายไว้ข้างต้น จำเป็นต้องค้นหาข้อความที่แสดงถึงจุดสิ้นสุดของการตอบกลับ นอกเหนือจากข้อความที่แสดงถึงการตอบกลับที่ถูกต้องหรือไม่ถูกต้อง การดำเนินการทั้งหมดอาจไม่มีจำนวนคำตอบที่เป็นไปได้เท่ากัน เมื่อมีการตอบกลับน้อยลง คุณสามารถใช้ข้อความว่างที่ใช้จำนวนรอบน้อยที่สุดในการวิเคราะห์ (ถึงกระนั้นก็ไม่ใช่วิธีที่ดีที่สุด) ตามหลักเหตุผลแล้ว จำนวนการตอบสนองขั้นต่ำที่ต้องการ (สามรายการในตัวอย่างนี้) จำเป็นจะต้องรวมความเป็นไปได้ในการดำเนินงานทั้งหมด แม้ว่าจะไม่ได้เป็นไปได้ทั้งหมดก็ตาม
เมื่อพูดถึงคำตอบที่เป็นไปได้จะเห็นได้ว่าตัวอย่างนี้ไม่มีประโยชน์มากนักในการรับข้อมูลในรูปแบบที่กำหนดเองจาก โมดูลอินเตอร์เน็ตไร้สาย ESP8266แต่ประเด็นก็คือ ในบริบทของการใช้ with ไมโครคอนโทรลเลอร์ มันไม่ปกติ; สิ่งที่พบบ่อยที่สุดคือการส่งข้อมูลที่รวบรวมโดยเซ็นเซอร์ที่เชื่อมต่อและ/หรือรับข้อมูลเกี่ยวกับสิ่งที่ต้องทำกับแอคชูเอเตอร์ที่ควบคุม ข้อมูลอันทรงคุณค่ามากซึ่งสามารถทำนายได้ดีมาก
ในโครงสร้างข้อมูลก่อนหน้านี้ เช่นเดียวกับที่ทำเพื่อแสดงการตอบสนองที่เป็นไปได้ที่ได้รับการวิเคราะห์ เมทริกซ์สองมิติยังใช้เพื่อกำหนดการดำเนินการที่จะต้องดำเนินการในแต่ละกรณี (siguiente_operacion
). โดยเฉพาะอย่างยิ่ง เราได้เลือกที่จะตอบกลับข้อความสามประเภท: ① ข้อความที่กำหนดเอง (LITERAL
) เพื่อตรวจสอบว่ามีการเชื่อมต่อกับจุดเข้าใช้งาน Wi-Fi และเซิร์ฟเวอร์หรือไม่ ② ข้อความเพื่อตรวจจับข้อผิดพลาดในกระบวนการ (FALLO
) และ 3 ข้อความที่ระบุว่าการดำเนินการเสร็จสมบูรณ์แล้ว (ACIERTO
).
สุดท้ายนี้ มีเวกเตอร์อีกสองตัวเพื่อตั้งเวลารอสูงสุดก่อนที่จะยอมแพ้ (timeout
) และระบุ (configuracion
) หากการดำเนินการสิ้นสุดลงโดยไม่รอการตอบกลับ (ESPERAR_RESPUESTA
) และข้อความแจ้งการสิ้นสุดการสื่อสาร เวกเตอร์สุดท้ายนี้ เพื่อแสดงตัวอย่างวิธีการบันทึกหน่วยความจำ โดยทำงานร่วมกับบิตของไบต์การกำหนดค่าเพื่อระบุสถานะต่างๆ
ครั้งแรก ESP8266 คำสั่ง AT ของโครงสร้างข้อมูลมักคาดหวังการตอบสนองซึ่งอาจเป็นผลสำเร็จหรือข้อความแสดงข้อผิดพลาด เมื่อเกิดข้อผิดพลาด โมดูลจะรีสตาร์ทและเริ่มใหม่อีกครั้ง และหากข้อความระบุว่าการดำเนินการถูกต้อง โมดูลจะย้ายไปยังขั้นตอนถัดไป
เมื่อคุณเชื่อมต่อกับเซิร์ฟเวอร์ รูปแบบจะเปลี่ยนไป ในกรณีนี้ จำเป็นต้อง 1 ส่งความยาวของแพ็กเก็ตข้อมูลที่จะส่ง และ 2 เขียนคำขอ 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 คำสั่ง AT และข้อความที่ได้รับการวิเคราะห์
ใช้โครงสร้าง (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
(อาจเป็นค่าถ่วงน้ำหนักของการอ่านแบบอะนาล็อก) และระบุสองไบต์เป็นอาร์กิวเมนต์ (two unsigned char
ซึ่งอาจเป็นพินที่ใช้อ่านอินพุตแบบอะนาล็อกและพินที่เปิดใช้งานการเปิดใช้งานของการรวมเชิงสมมุติ)
ในการพัฒนาเพื่อ MCUตรงกันข้ามกับสิ่งที่เกิดขึ้นในรูปแบบการพัฒนาสำหรับระบบขนาดใหญ่ ไม่ใช่เรื่องแปลกที่จะใช้ตัวแปรโกลบอลเมื่อกำหนดพฤติกรรม (โกลบอล) ของแอปพลิเคชันที่ควบคุมแอสเซมบลี ดังนั้นจึงไม่ใช่เรื่องแปลกที่จะค้นหาคำจำกัดความประเภทนี้ เป็นฟังก์ชันที่ไม่มีพารามิเตอร์และไม่ส่งคืนค่า บางอย่างเช่น void (*accion)();
หากคุณทำงานด้วยวิธีนี้ในการแสดงข้อมูลให้ใช้ struct
ของข้อมูลที่มีความยาวผันแปรได้ จำเป็นต้องจัดสรรหน่วยความจำแบบไดนามิกด้วย malloc()
(o new()
หากใช้วัตถุ) ซึ่งจะใช้จำนวนหน่วยความจำที่จัดสรรเป็นพารามิเตอร์และส่งคืนตัวชี้ไปที่จุดเริ่มต้นของพื้นที่หน่วยความจำที่สงวนไว้ กับ sizeof()
สำหรับประเภทที่จัดเก็บ คูณด้วยจำนวนองค์ประกอบที่ใช้ คุณจะได้รับจำนวนหน่วยความจำที่ต้องการ ตัวอย่างที่มีและไม่มีการใช้งานสามารถดูได้ในภาพหน้าจอด้านล่าง malloc()
; ระวังหน่วยความจำที่โปรแกรมใช้ในกรณีแรกคุณต้องโหลดไลบรารี่ที่มีฟังก์ชันนี้
หากการดำเนินการเกี่ยวกับ โมดูลอินเตอร์เน็ตไร้สาย ESP8266 จะแตกต่างกันไปตลอดการทำงานของโปรแกรมโดยจะต้องเพิ่มหน่วยความจำที่ไม่ได้ใช้ด้วย free()
(o delete()
ในกรณีเป็นวัตถุ) แม้ว่าจะสมเหตุสมผลที่จะคาดหวังว่าคอมไพเลอร์ (GCC) จะปรับโปรแกรมให้เหมาะสมเพื่อหลีกเลี่ยงการแบ่งพาร์ติชันหน่วยความจำ แน่นอนว่าประสิทธิภาพจะไม่เหมาะสมที่สุดเท่ากับการทำงานกับหน่วยความจำที่จัดสรรแบบคงที่
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
error_operacion.mensaje=“ERROR\r\n”;
error_operacion.termina_operacion=true;
error_operacion.operacion=&reiniciar_esp8266;
error_enlace.mensaje=“link is not\r\n”;
error_enlace.termina_operacion=true;
error_enlace.operacion=&reiniciar_esp8266;
reiniciar_esp8266_correcto.mensaje=“ready\r\n”;
reiniciar_esp8266_correcto.termina_operacion=true;
reiniciar_esp8266_correcto.operacion=&desconectar_wifi;
desconectar_wifi_correcto.mensaje=mensaje_acierto;
desconectar_wifi_correcto.termina_operacion=true;
desconectar_wifi_correcto.operacion=&establecer_modo_estacion;
establecer_modo_estacion_correcto.mensaje=mensaje_acierto;
establecer_modo_estacion_correcto.termina_operacion=true;
establecer_modo_estacion_correcto.operacion=&establecer_modo_simple;
establecer_modo_simple_correcto.mensaje=mensaje_acierto;
establecer_modo_simple_correcto.termina_operacion=true;
establecer_modo_simple_correcto.operacion=&conectar_wifi;
conectar_wifi_correcto.mensaje=mensaje_acierto;
conectar_wifi_correcto.termina_operacion=true;
conectar_wifi_correcto.operacion=&verificar_conexion;
verificar_conexion_correcto.mensaje=“STATUS:5”;
verificar_conexion_correcto.termina_operacion=false;
verificar_conexion_correcto.operacion=&conectar_servidor;
verificar_conexion_terminado.mensaje=mensaje_acierto;
verificar_conexion_terminado.termina_operacion=true;
verificar_conexion_terminado.operacion=&reiniciar_esp8266;
conectar_servidor_correcto.mensaje=“CONNECT”;
conectar_servidor_correcto.termina_operacion=false;
conectar_servidor_correcto.operacion=&informar_cantidad;
conectar_servidor_terminado.mensaje=mensaje_acierto;
conectar_servidor_terminado.termina_operacion=true;
conectar_servidor_terminado.operacion=&reiniciar_esp8266;
informar_cantidad_correcto.mensaje=mensaje_acierto;
informar_cantidad_correcto.termina_operacion=true;
informar_cantidad_correcto.operacion=&enviar_cantidad;
enviar_cantidad_correcto.mensaje=“>”;
enviar_cantidad_correcto.termina_operacion=true;
enviar_cantidad_correcto.operacion=&enviar_prefijo;
enviar_prefijo_correcto.operacion=&enviar_datos;
enviar_datos_correcto.operacion=&enviar_sufijo;
enviar_sufijo_correcto.mensaje=“CLOSED\r\n\r\nOK\r\n”;
enviar_sufijo_correcto.termina_operacion=true;
enviar_sufijo_correcto.operacion=&verificar_conexion;
reiniciar_esp8266.peticion=“AT+RST”;
reiniciar_esp8266.timeout=15000;
reiniciar_esp8266.cantidad_respuestas=2;
reiniciar_esp8266.respuesta=malloc(sizeof(respuesta_esp8266*)*reiniciar_esp8266.cantidad_respuestas);
reiniciar_esp8266.respuesta[FALLO]=&error_operacion;
reiniciar_esp8266.respuesta[ACIERTO]=&reiniciar_esp8266_correcto;
desconectar_wifi.peticion=“AT+CWQAP”;
desconectar_wifi.timeout=2500;
desconectar_wifi.cantidad_respuestas=2;
desconectar_wifi.respuesta=malloc(sizeof(respuesta_esp8266*)*desconectar_wifi.cantidad_respuestas);
desconectar_wifi.respuesta[FALLO]=&error_operacion;
desconectar_wifi.respuesta[ACIERTO]=&desconectar_wifi_correcto;
establecer_modo_estacion.peticion=“AT+CWMODE=1”;
establecer_modo_estacion.timeout=2500;
establecer_modo_estacion.cantidad_respuestas=2;
establecer_modo_estacion.respuesta=malloc(sizeof(respuesta_esp8266*)*establecer_modo_estacion.cantidad_respuestas);
establecer_modo_estacion.respuesta[FALLO]=&error_operacion;
establecer_modo_estacion.respuesta[ACIERTO]=&establecer_modo_estacion_correcto;
establecer_modo_simple.peticion=“AT+CIPMUX=0”;
establecer_modo_simple.timeout=2500;
establecer_modo_simple.cantidad_respuestas=2;
establecer_modo_simple.respuesta=malloc(sizeof(respuesta_esp8266*)*establecer_modo_simple.cantidad_respuestas);
establecer_modo_simple.respuesta[FALLO]=&error_operacion;
establecer_modo_simple.respuesta[ACIERTO]=&establecer_modo_simple_correcto;
conectar_wifi.peticion=“AT+CWJAP=\”polaridad.es\”,\”54lLij1RiTn3MEd3v41C\””;;
conectar_wifi.timeout=20000;
conectar_wifi.cantidad_respuestas=2;
conectar_wifi.respuesta=malloc(sizeof(respuesta_esp8266*)*conectar_wifi.cantidad_respuestas);
conectar_wifi.respuesta[FALLO]=&error_operacion;
conectar_wifi.respuesta[ACIERTO]=&conectar_wifi_correcto;
verificar_conexion.peticion=“AT+CIPSTATUS”;
verificar_conexion.timeout=5000;
verificar_conexion.cantidad_respuestas=3;
verificar_conexion.respuesta=malloc(sizeof(respuesta_esp8266*)*verificar_conexion.cantidad_respuestas);
verificar_conexion.respuesta[FALLO]=&error_operacion;
verificar_conexion.respuesta[ACIERTO]=&verificar_conexion_correcto;
verificar_conexion.respuesta[OTRO_MENSAJE]=&verificar_conexion_terminado;
conectar_servidor.peticion=“AT+CIPSTART=\”TCP\”,\”servidoriot.com\”,80″;
conectar_servidor.timeout=15000;
conectar_servidor.cantidad_respuestas=3;
conectar_servidor.respuesta=malloc(sizeof(respuesta_esp8266*)*conectar_servidor.cantidad_respuestas);
conectar_servidor.respuesta[FALLO]=&error_operacion;
conectar_servidor.respuesta[ACIERTO]=&conectar_servidor_correcto;
conectar_servidor.respuesta[OTRO_MENSAJE]=&conectar_servidor_terminado; // OK, no significa que haya conexión pero sí termina la operación
informar_cantidad.peticion=“AT+CIPSEND=”;
informar_cantidad.timeout=1000;
informar_cantidad.cantidad_respuestas=1;
informar_cantidad.respuesta=malloc(sizeof(respuesta_esp8266*)*informar_cantidad.cantidad_respuestas);
informar_cantidad.respuesta[0]=&informar_cantidad_correcto;
//enviar_cantidad.peticion=””; // Se asigna cuando se conoce el valor que se va a enviar y se puede calcular la longitud que ocupa (número de caracteres)
enviar_cantidad.timeout=5000;
enviar_cantidad.cantidad_respuestas=2;
enviar_cantidad.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_cantidad.cantidad_respuestas);
enviar_cantidad.respuesta[FALLO]=&error_enlace;
enviar_cantidad.respuesta[ACIERTO]=&enviar_cantidad_correcto;
enviar_prefijo.peticion=“GET /frigo03/almacenar_temperatura.php?temperatura=”;
enviar_prefijo.timeout=10000;
enviar_prefijo.cantidad_respuestas=1;
enviar_prefijo.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_prefijo.cantidad_respuestas);
enviar_prefijo.respuesta[0]=&enviar_prefijo_correcto;
//enviar_datos.peticion=””; // Se asigna en cuando se conoce el valor que se va a enviar
enviar_datos.timeout=5000;
enviar_datos.cantidad_respuestas=1;
enviar_datos.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_datos.cantidad_respuestas);
enviar_datos.respuesta[0]=&enviar_datos_correcto;
enviar_sufijo.peticion=” HTTP/1.1\r\nHost: www.servidoriot.com\r\nUser-Agent: ESP8266\r\nConnection: close\r\n\r\n”;
enviar_sufijo.timeout=20000;
enviar_sufijo.cantidad_respuestas=2;
enviar_sufijo.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_sufijo.cantidad_respuestas);
enviar_sufijo.respuesta[FALLO]=&error_enlace;
enviar_sufijo.respuesta[ACIERTO]=&enviar_sufijo_correcto;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define FALLO 0 // Índice del vector de respuestas que representa el mensaje de error
#define FALLO_TERMINA 0B00000001 // 1<<FALLO
#define ACIERTO 1
#define ACIERTO_TERMINA 0B00000010 // 1<<ACIERTO
#define OTRO_MENSAJE 2
#define OTRO_MENSAJE_TERMINA 0B00000100 // 1<<OTRO_MENSAJE
#define CANTIDAD_RESPUESTAS 3 // Cantidad de posibles respuestas que se buscan en el texto recibido desde el ESP8266
#define ESPERAR_RESPUESTA 0B00001000 // 1<<CANTIDAD_RESPUESTAS
#define NO_ESPERAR_RESPUESTA 0B00000000
#define ENVIAR_OPERACION esperando_respuesta=true;SERIE.print((*operacion_actual).peticion);if((*operacion_actual).cantidad_respuestas>1){SERIE.print(“\r\n”);for(unsigned char numero_respuesta=0;numero_respuesta<(*operacion_actual).cantidad_respuestas;numero_respuesta++){numero_caracter[numero_respuesta]=0;}}cronometro_esp8266=millis();
#define PIN_TEMPERATURA A0 // Pin analógico al que se conecta la salida del sensor de temperatura LM35
#define INTERVALO_LECTURA_TEMPERATURA 30000 // Leer la temperatura cada 30 segundos (30*1000)
#define INTERVALO_GRABACION_TEMPERATURA 300000 // Grabar la media de la temperatura cada 5 minutos (5*60*1000)
unsigned long muestras=0; // Número de veces que se ha medido la temperatura (para calcular la media)
float temperatura; // de -55.0 °C a +150 °C | de 0 V a 2.050 V | de -550 mV a +1500 mV | analogRead*5.0/1023.0*100-55.0 analogRead/2.46-55.0
float media_temperaturas=0.0;
unsigned long cronometro_lectura_temperatura;
unsigned long cronometro_grabacion_temperatura;
char *mensaje_acierto=“OK\r\n”;
typedef struct estructura_operacion_esp8266 operacion_esp8266; // Se define el tipo de datos operacion_esp8266 que corresponde con la estructura (struct) llamada estructura_operacion_esp8266 que se define más adelante
typedef struct estructura_respuesta_esp8266 respuesta_esp8266; // Se define el tipo de datos respuesta_esp8266 que corresponde con la estructura (struct) llamada struct estructura_respuesta_esp8266 que se define más adelante
struct estructura_operacion_esp8266
{
char *peticion; // Datos que se envían al ESP8266 para iniciar una operación (como una orden AT, pero también algo como la petición a un servidor…)
unsigned int timeout; // Tiempo (segundos) que se espera la respuesta del ESP8266 antes de desistir
bool espera_respuesta; // Si no espera respuesta en cuanto se termine de enviar la orden se puede pasar a la siguiente
unsigned char cantidad_respuestas; // Número de posibles respuestas del ESP8266 que se van a analizar, que puede ser variable en cada petición (no solo OK y ERROR)
respuesta_esp8266 **respuesta; // Respuestas (estructura) que se esperan de esta operación
};
struct estructura_respuesta_esp8266
{
char *mensaje; // Mensaje que se espera recibir desde el ESP8266
bool termina_operacion; // Cuando se termina de leer el mensaje ha terminado la operación
operacion_esp8266 *operacion; // Puntero a operación que se ejecutará si se encuentra esta respuesta en el mensaje devuelto por el ESP8266
};
operacion_esp8266 *operacion_actual; // Operación sobre el ESP8266 que se está ejecutando actualmente
operacion_esp8266 *proxima_operacion; // Siguiente operación que se procesará cuando termine la actual
unsigned int longitud_peticion; // Número de caracteres que ocupa la petición HTTP
char texto_longitud_peticion[4]; // 3 caracteres para almacenar la longitud de la petición en formato texto
char valor_enviado[9]; // signo + 4 enteros + punto + 2 decimales + \0 = 9
unsigned long cronometro_esp8266; // Cronómetro para controlar el tiempo máximo de respuesta del ESP8266 antes de desistir
char lectura_serie; // buffer con el carácter leído desde el ESP8266
boolean grabando_datos=false; // Verdadero cuando han terminado todas las operaciones necesarias para grabar los datos
boolean esperando_respuesta; // Verdadero si aún no se ha encontrado una de los mensajes que indica que ha terminado la respuesta
unsigned char numero_caracter[CANTIDAD_RESPUESTAS]; // Número de orden de la letra del mensaje-respuesta que se está almacenando (una matriz de, como máximo, el mayor número de respuestas posible)
operacion_esp8266 reiniciar_esp8266; // Reiniciar el módulo wifi ESP8266
operacion_esp8266 desconectar_wifi; // Desconectar del punto de acceso por defecto (si fuera el caso)
operacion_esp8266 establecer_modo_estacion; // Establecer el modo de estación (no punto de acceso)
operacion_esp8266 establecer_modo_simple; // Establecer el modo de conexión simple
operacion_esp8266 conectar_wifi; // Conectar al punto de acceso
operacion_esp8266 verificar_conexion; // Verificar si hay conexión
operacion_esp8266 conectar_servidor; // Conectar al servidor
operacion_esp8266 informar_cantidad; // Avisar de la cantidad de datos que se envían
operacion_esp8266 enviar_cantidad; // Enviar cantidad
operacion_esp8266 enviar_prefijo; // Enviar el prefijo de la petición
operacion_esp8266 enviar_datos; // Enviar la temperatura
operacion_esp8266 enviar_sufijo; // Enviar el sufijo de la petición
respuesta_esp8266 error_operacion; // Todas las respuestas “ERROR” reinician el módulo wifi ESP8266
respuesta_esp8266 error_enlace; // Las respuestas “link is not” también reinician el módulo wifi ESP8266
respuesta_esp8266 reiniciar_esp8266_correcto;
respuesta_esp8266 desconectar_wifi_correcto;
respuesta_esp8266 establecer_modo_estacion_correcto;
respuesta_esp8266 establecer_modo_simple_correcto;
respuesta_esp8266 conectar_wifi_correcto;
respuesta_esp8266 verificar_conexion_correcto;
respuesta_esp8266 verificar_conexion_terminado;
respuesta_esp8266 conectar_servidor_correcto;
respuesta_esp8266 conectar_servidor_terminado;
respuesta_esp8266 informar_cantidad_correcto;
respuesta_esp8266 enviar_cantidad_correcto;
respuesta_esp8266 enviar_prefijo_correcto;
respuesta_esp8266 enviar_datos_correcto;
respuesta_esp8266 enviar_sufijo_correcto;
void setup()
{
#include “inicializar_operaciones.h”
longitud_peticion=strlen(enviar_prefijo.peticion)+strlen(enviar_sufijo.peticion);
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
//delay(8000); // En fase de pruebas se puede introducir un tiempo de espera para conectar una consola/sniffer
cronometro_lectura_temperatura=millis();
cronometro_grabacion_temperatura=millis();
}
void loop()
{
if((unsigned long)(millis()–cronometro_lectura_temperatura)>INTERVALO_LECTURA_TEMPERATURA)
{
cronometro_lectura_temperatura=millis();
temperatura=analogRead(PIN_TEMPERATURA)/2.46–55.0;
muestras++;
media_temperaturas=(float)temperatura/(float)muestras+media_temperaturas*(float)(muestras–1)/(float)(muestras);
}
if(grabando_datos)
{
if((unsigned long)(millis()–cronometro_esp8266)>(unsigned long)(*operacion_actual).timeout) // Si se ha superado el tiempo de espera máximo
{
operacion_actual=(*(*operacion_actual).respuesta[FALLO]).operacion; // Pasar a la operación correspondiente al error
ENVIAR_OPERACION
}
else // Si no se ha superado el tiempo de espera máximo
{
if((*operacion_actual).cantidad_respuestas>1) // Si la siguiente operación depende de la respuesta a la actual desde el ESP8266 hay que leer la información que llegue desde el puerto serie
{
while(SERIE.available())
{
lectura_serie=SERIE.read();
for(unsigned char numero_respuesta=0;numero_respuesta<(*operacion_actual).cantidad_respuestas;numero_respuesta++) // Comparar la letra cargada desde el puerto serie con la correspondiente de los mensajes disponibles
{
if(lectura_serie==(*(*operacion_actual).respuesta[numero_respuesta]).mensaje[numero_caracter[numero_respuesta]]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
numero_caracter[numero_respuesta]++; // Como el carácter coincide, se puede comparar con el siguiente lo próximo que llegue por el puerto serie
if((*(*operacion_actual).respuesta[numero_respuesta]).mensaje[numero_caracter[numero_respuesta]]==0) // Si el carácter que toca es \0 es que se ha terminado de analizar el mensaje
{
if(esperando_respuesta) // Todavía no se ha encontrado un mensaje que determine la siguiente operación
{
proxima_operacion=(*(*operacion_actual).respuesta[numero_respuesta]).operacion; // La próxima operación que habrá que procesar será la que indique el mensaje encontrado para la operación actual
esperando_respuesta=false; // Ya se ha encontrado un mensaje que determina la siguiente operación
}
if((*(*operacion_actual).respuesta[numero_respuesta]).termina_operacion) // Si el mensaje encontrado es uno de los que terminan la operación…
{
if(operacion_actual==&enviar_sufijo) // Se ha completado la última operación de la tarea compleja
{
grabando_datos=false; // Se ha terminado la tarea compleja (grabar datos en el servidor)
}
else // No es la última operación de la tarea compleja, hay que seguir realizando otras operaciones
{
operacion_actual=proxima_operacion; // Ejecutar la siguiente operación
ENVIAR_OPERACION
}
}
}
}
else // Si la letra recibida no es igual que la correspondiente del mensaje
{
numero_caracter[numero_respuesta]=0; // Empezar a comparar desde la primera letra del mensaje
}
}
}
}
else // Si no hay que esperar datos desde el puerto serie
{
operacion_actual=(*(*operacion_actual).respuesta[0]).operacion;
ENVIAR_OPERACION
}
}
}
else
{
if((unsigned long)(millis()–cronometro_grabacion_temperatura)>INTERVALO_GRABACION_TEMPERATURA)
{
cronometro_grabacion_temperatura=millis();
dtostrf(media_temperaturas,4,2,valor_enviado); // snprintf(valor_enviado,9,”%+3.2f”,media_temperaturas); // printf y derivadas no funcionan en AVR
snprintf(texto_longitud_peticion,4,“%d”,longitud_peticion+strlen(valor_enviado));
grabando_datos=true;
operacion_actual=&verificar_conexion;
enviar_datos.peticion=valor_enviado;
enviar_cantidad.peticion=texto_longitud_peticion;
ENVIAR_OPERACION
}
}
}
|
แม้ว่าในตัวอย่างนี้ (ในการใช้งานทั้งสอง) จะไม่สมเหตุสมผลนัก แต่เพื่อที่จะสรุปการดำเนินการเพื่อให้สามารถนำไปใช้กับกรณีอื่น ๆ ได้ก็ควรสังเกตว่า การส่งข้อมูลจะทำซ้ำโปรโตคอลเดิมเสมอ: แจ้งจำนวนไบต์ที่จะส่ง รอตัวบ่งชี้ (>) แล้วส่งข้อมูล.
เนื่องจากในตัวอย่างนี้มีการใช้เพียงครั้งเดียว (คำขอทั้งหมดถูกสร้างขึ้นในแพ็กเก็ตเดียว) ดูเหมือนจะไม่มีประโยชน์มากนัก แต่โดยทั่วไปอาจจำเป็นต้องดำเนินการส่งหลายครั้งในการดำเนินการเดียวกัน รวมถึงกรณีที่ต้องทำการส่งหลายครั้ง ได้รับการส่งข้อมูลจำนวนมากที่ต้องแยกส่วนเพื่อหลีกเลี่ยงการล้นหน่วยความจำของ ESP8266.
หากต้องการใช้ลักษณะการทำงานนี้ คุณสามารถใช้สององค์ประกอบสุดท้ายของการเชื่อมต่อเพื่อให้แต่ละครั้งที่มีการส่งข้อมูล ข้อมูลจะถูกเติมด้วยค่าที่เกี่ยวข้อง: ในกรณีแรกคือจำนวนไบต์ที่ส่ง และในครั้งที่สอง ( ส่วนหนึ่งของ) คำขอ ที่จะส่งต่อ
หากต้องการทำซ้ำการกำหนดและการส่งองค์ประกอบต่างๆ ที่ต้องส่งสามารถจัดเก็บไว้ในเวกเตอร์ได้ เวกเตอร์ใหม่นี้จะเป็นเวกเตอร์ที่กำหนดจุดสิ้นสุดของการดำเนินการที่ซับซ้อน และไม่ใช่การดำเนินการสุดท้ายจนถึงขณะนี้
ความคิดเห็น 1