กลับไปหน้ารวมไฟล์
mkr-wan-1310-iot-operating-at-0-92ma-f13209.md

คำนำ

สเก็ตช์/โปรเจกต์นี้ยังมีโค้ดสำหรับเมนู Arduino และการเก็บค่าตั้งค่าไว้ในแฟลชของ SAMD21 ด้วยนะ เมนูนี้เข้าถึงได้ผ่านพอร์ต USB อยากรู้รายละเอียดเพิ่มเติมก็ลองไปหาอ่านดูได้ มีคนแชร์ไว้เยอะแยะ

เป้าหมาย

ตอนนั้นพี่มีของเหลืออยู่คือบอร์ด MKR WAN 1310 LoRa แบตเตอรี่ LiPo 2600mAh และ Shield MKR ENV (Environment) ที่สามารถวัดค่าต่างๆ ได้ดังนี้:

  • อุณหภูมิ
  • ความชื้น
  • ความกดอากาศ
  • รังสี UV-A และ UV-B
  • ดัชนี UV

พี่เลยอยากสร้างสถานีตรวจอากาศ IoT เอาไปตั้งนอกบ้าน แล้วดึงค่ามาดูบนสมาร์ทโฟนได้เลย จำได้มั้ยที่ Arduino เคยประกาศเรื่องแอปควบคุม IoT จากที่ไหนก็ได้ เป้าหมายของพี่คือลดกระแสไฟขณะทำงานให้ต่ำที่สุดเท่าที่จะทำได้ วัดแรงดันแบตเตอรี่ และใช้ระบบ Arduino IoT Cloud ให้เป็นประโยชน์

บทความนี้จะไม่สอนวิธีประกอบเซนเซอร์วัดสภาพแวดล้อมนอกบ้านตั้งแต่ต้นจนจบนะ แต่น้องก็จะพบสเก็ตช์เต็มๆ พร้อมคำแนะนำและทิปส์ในบทความนี้อยู่ดี สิ่งที่พี่อยากแชร์จริงๆ คือวิธีออกแบบส่วน LoRa และโหมดสแตนด์บายให้ดีที่สุดสำหรับโปรเจกต์นี้ เพราะฉะนั้นอ่านบทความต่อจากนี้ด้วย mindset นี้ไว้เลย

มีอย่างหนึ่งต้องอธิบาย เพราะมองข้ามได้ง่ายเวลาวัด ความกดอากาศ เซนเซอร์จะให้ค่าความดันที่ถูกต้องออกมา แต่ถ้าเอาไปเทียบกับค่าความดันจากพยากรณ์อากาศล่าสุด ค่ามันจะต่างกันอยู่ดี! นั่นเป็นเพราะในแผนที่อากาศ การพยากรณ์ และข่าวต่างๆ ค่าความดันจะถูกปรับทางคณิตศาสตร์ให้เป็นค่าที่ระดับน้ำทะเลปานกลาง (MSL) เสมอ สำหรับการวิเคราะห์อากาศ การเปรียบเทียบความดันจากความสูงต่างระดับกันมันไม่เมกเซนส์ เราเลยไม่เห็นว่าพื้นที่ความกดอากาศสูงหรือต่ำอยู่ตรงไหน! สิ่งที่เราต้องทำเพื่อให้ได้ผลลัพธ์ที่เทียบกันได้คือคำนวณทางคณิตศาสตร์นี่แหละ พี่ได้ไปปรับปรุงไลบรารี MKRENV ไว้แล้ว น้องสามารถหาอ่านรายละเอียดเพิ่มเติมได้จากฟอรั่มต่างๆ เอาไปใช้แบบนี้เลย...

ENV.readPressure(MILLIBAR_MSL, 460));

460 คือความสูงจากระดับน้ำทะเล (หน่วยเมตร) ที่ตำแหน่งของน้องนะ อุณหภูมิก็สำคัญสำหรับการคำนวณที่แม่นยำ แต่ส่วนนี้จะดึงค่าจากเซนเซอร์วัดอุณหภูมิบนชีลด์โดยอัตโนมัติอยู่แล้ว

การติดตั้งและทดสอบครั้งแรก

1. ต่อ ENV Shield เข้ากับ MKR WAN 1310 (LoRa) อันนี้จริงๆ แล้วง่ายมากและเหมาะสำหรับการทดสอบ แต่ถ้าเอาไปใช้งานจริง ตัว MKR ที่อยู่ใต้ ENV Shield จะทำให้ความร้อนขึ้นไปรบกวนเซนเซอร์ ทำให้การวัดอุณหภูมิและความชื้นคลาดเคลื่อนได้

ในกล่องของพี่ พี่แยกชีลด์ออกจากบอร์ด MKR และใส่ฉนวนกั้นความร้อนระหว่างกลางเข้าไป ทำให้ได้ผลลัพธ์ที่แม่นยำขึ้น

2. วัดแรงดันแบตเตอรี่

พี่อยากเห็นระดับแรงดันแบตเตอรี่ปัจจุบันบน IoT Dashboard ด้วย อันนี้จะทำให้พี่มีโอกาสใช้ webhooks ส่งอีเมลแจ้งเตือนได้ถ้าแรงดันตกต่ำกว่าเกณฑ์ที่กำหนด งานนี้บนบอร์ด MKR ส่วนใหญ่ทำง่ายมาก มีพินของ SAMD21 (PB09) จองไว้สำหรับงานนี้โดยเฉพาะ และวงจรแบ่งแรงดันก็มีให้เรียบร้อยแล้ว

จากแผนภาพ MKR WiFi 1010

แต่บนโมดูล MKR LoRa นี่ไม่มีหรอกนะ พิน PB09 ถูกใช้ควบคุมชิป LoRa ไปแล้ว เลยต้องเลือกพินอินพุตอนาล็อกอื่นที่ว่างอยู่แทน พี่ใช้พิน A1 ในตัวอย่างนี้ ส่วนวงจรแบ่งแรงดันต้องสร้างเพิ่มเองข้างนอกเลย

วงจรแบ่งแรงดันของพี่
// For battery measurements
int LiPo_BATTERY = A1;
// The MKR WAN 1310 3.3V reference voltage for battery measurements
 analogReference(AR_DEFAULT);
// read the input on analog pin 0 (A1) and calculate the voltage
BatteryLevel = analogRead(LiPo_BATTERY) * 3.3f / 1023.0f / 1.2f * (1.2f+0.33f);

3. หน้าปัด (Dashboard) และโค้ด (Sketch) ออกแบบและสร้างได้ง่ายมาก ดูโค้ดเต็มๆ ด้านล่างเลย...

4. ทดสอบครั้งแรก (The initial tests)

หลังจากเขียนโค้ดเสร็จ พี่ก็เริ่มทดสอบครั้งแรกเลย ต้องบอกตามตรงว่า... ไม่มีข้อความอะไรวิ่งไปหา Arduino Dashboard เลยสักนิด! พี่เลยเปิดโหมดดีบั๊กสุดพลัง (ระดับ 4) ตามโค้ดด้านล่างนี้ เพื่อดูว่ามันติดขัดอะไรกันแน่:

void setup() {
   setDebugMessageLevel(4);

ทีนี้ใน Serial Monitor ก็เห็นข้อความ LoRa แบบนี้: ข้อมูลเยอะเกินไปจริงดิ?

Message length is bigger than max LoRa packet!
Message length is bigger than max LoRa packet!
Message length is bigger than max LoRa packet!

ในไฟล์ thingProperties.h นี่แหละที่เรากำหนดตัวแปรต่างๆ ที่ต้องส่งผ่าน LoRa ไปตรวจสอบกันหน่อย:

ข้อมูลดิบ 32 ไบต์

แค่ 32 ไบต์เองนะ แต่เดี๋ยวก่อน... มันต้องมีส่วนหัว (overhead) เพื่อห่อหุ้มข้อมูลนี้ในแพ็กเกจด้วยสิ เซิร์ฟเวอร์ Arduino IoT Cloud ต้องสามารถแยกข้อมูลและส่งค่าที่ถูกต้องไปยังวิดเจ็ตบนแดชบอร์ดได้ ลองประมาณคร่าวๆ คูณสองไปเลย = 64 ไบต์ ก็ยังไม่เยอะมาก แต่... เยอะเกินไปสำหรับ LoRa ไปดูตารางจาก Semtech กัน:

อุ้ย...

นี่คือสำหรับภูมิภาค EU นะ ใน US ยังแย่กว่านี้อีก วิธีแก้คือพี่ลดขนาดข้อมูลและแบ่งส่งเป็นสองแพ็กเกจ ใช่แล้ว ตัวแปร integer บน SAMD21 ก็ใช้ 4 ไบต์เหมือน float แต่เลขศูนย์นำหน้าจะถูกตัดออกก่อนส่ง (ที่ขอบเขตไบต์) เลยช่วยลดขนาดข้อมูลลงได้นิดหน่อย

คราวนี้เราจะส่งสองแพ็กเกจ...

Serial Monitor แสดงให้เห็นว่าไลบรารี ArduinoIoTCloud ทำอะไรบ้างในการลองครั้งต่อไป:

Message length is bigger than max LoRa packet!
Message sent correctly!
Message sent correctly!
  • วินาทีที่ 0 - พยายามส่งทั้งสองแพ็กเกจ และขนาดก็ยังใหญ่เกินอีก
  • วินาทีที่ 15 - แพ็กเกจแรก (ข้อมูล 12 ไบต์) ส่งสำเร็จ!
  • วินาทีที่ 22 - นี่คือแพ็กเกจที่สอง!

แต่มันก็สร้างปัญหาอีกอย่างนึง เรากำลังส่งข้อมูลด้วยจังหวะแบบนี้:

15   22
30
45   44
60
75   66

44 กับ 45 ห่างกันแค่ 1 วินาที พอถึงเวลาส่ง 45 แพ็กเกจ 44 ยังส่งไม่เสร็จดีเลย มันก็จะสร้างข้อความผิดพลาด LoRa อีก:

LoRa chip is busy (LORA_ERROR_BUSY)

แต่สำหรับโปรเจกต์นี้ไม่ใช่ปัญหาหรอก เพราะพี่ตั้งใจจะให้บอร์ด MKR LoRa นอนหลับ (sleep) ก่อนที่โค้ดจะทำงานถึงจุดนั้นอยู่แล้ว

ลดการกินกระแส (Reducing current consumption)

ใน datasheet ของตระกูล SAMD21 พี่เจอตารางโหมดสลีปแบบนี้:

โอเค มาลองเปิดใช้โหมด Standby นี่กันเป็นเวลา 15 นาทีดีกว่า พี่ไปตรวจสอบไลบรารี Adafruit_SleepyDog แล้วพบว่าคำสั่ง Watchdog.sleep(); ใช้โหมดสแตนบายน์นี้แหละ - เยี่ยมเลย ด้วย () เราจะได้เวลาสลีปสูงสุดที่รองรับคือ 16 วินาที ดังนั้นพี่ต้องสั่งให้มันหลับต่อเนื่องกัน 51 ครั้งเพื่อให้ครบ 15 นาที นี่คือโค้ด:

// Go to STANDBY Mode for 51 times x 16,5s sleep = 841,5s + 60s
// from the start phase give us a total of ~900s = 15 minutes...
if (sleep_now == true) {
 for (int i = 0; i < sleep_cycles; i++) {
 // Enter the Standby Mode for 16 seconds (MKR WAN 1310 specific)
 Watchdog.sleep();
}

พี่คิดว่าพอครบ 15 นาทีแล้วจะส่งแพ็กเก็ตถัดไปได้สบายๆ แต่ปรากฏว่า LoRa และ/หรือการเชื่อมต่อ Arduino Cloud มันหายไปซะงั้น! พี่ลองรีสตาร์ทการเชื่อมต่อ IoT Cloud ดูแล้วก็ไม่สำเร็จอีก

เลยตัดสินใจว่า หลังออกจากโหมดสแตนด์บายแล้ว จะรีบูต Arduino MKR ซะเลย วิธีนี้จะแก้ปัญหา IoT Cloud ได้แน่นอน และยังรีเซ็ตจังหวะการส่งข้อมูล (15, 22 วินาที ตามที่อธิบายไปก่อนหน้า) ให้กลับมาเริ่มใหม่ด้วย

โค้ดเต็มๆ ก็เลยเป็นแบบนี้จ้า:

if (sleep_now == true) {
  for (int i = 0; i < sleep_cycles; i++) {
    Watchdog.sleep();
  }
  // Now restart the MKR
  int countdownMS = Watchdog.enable(100);   // WatchDog timeout in 100ms
  delay(countdownMS+1000);                  // Wait for 1,1 second!!!
  Watchdog.disable();                       // The WatchDog WILL time out
                                            // and restart the CPU
}

อธิบายเพิ่มนิดนึงนะ ตอนแรกพี่อยากใช้ฟีเจอร์ในคลาวด์ที่ชื่อ "When the value changes" สำหรับทุกๆ property ของพี่ มันเป็นตัวเลือกที่ดีมากๆ ในการลดจำนวนและความถี่ในการส่งข้อมูล แต่เพราะพี่ต้องรีสตาร์ท Arduino ทุก 15 นาที เลยทำให้ฟีเจอร์นี้ไม่เหมาะกับสถานการณ์นี้ โค้ดมีเวลาแค่ ~30 วินาทีในการตรวจจับการเปลี่ยนแปลงเท่านั้น...

การประหยัดพลังงานขั้นสูง (Low-Power Optimization)

บอร์ด Arduino MKR WAN 1310 ออกแบบมาสำหรับงาน LoRa ระยะไกลและประหยัดพลังงานโดยเฉพาะ การจะทำให้กระแสสแตนด์บายเหลือแค่ 0.92mA นี่ต้องใช้เทคนิคระดับเฟิร์มแวร์เลยล่ะ

  • โหมดสลีป (Sleep Modes): สคริปต์ใช้ไลบรารี่ ArduinoLowPower เพื่อให้ไมโครคอนโทรลเลอร์ SAMD21 เข้าสู่โหมดดีพสลีประหว่างรอบการส่งข้อมูล
  • การตัดวงจรฮาร์ดแวร์ (Hardware gating): มันจัดการกับส่วนประกอบที่กินไฟเยอะ (เช่น เซนเซอร์หรือวิทยุ LoRa) โดยปิดหรือให้เข้าสู่โหมดสลีปเมื่อไม่ได้ใช้งาน

การเชื่อมต่อและสถาปัตยกรรม

  • โปรโตคอล LoRaWAN: อุปกรณ์เชื่อมต่อกับเกตเวย์ (เช่น The Things Network) เพื่อส่งแพ็กเก็ตข้อมูลเล็กๆ ไปได้หลายกิโลเมตร
  • การส่งข้อมูลระยะไกล (Telemetry): ประสิทธิภาพด้านพลังงานระดับนี้ทำให้มันเหมาะมากสำหรับการตรวจสอบระยะไกล ที่อุปกรณ์ต้องใช้แบตเตอรี่ได้นานหลายปี ข้อมูลมักจะถูกส่งไปที่ Arduino IoT Cloud เพื่อดูผลแบบเรียลไทม์และจัดการการแจ้งเตือน

วัดกระแสและคำนวณอายุแบตเตอรี่

เพราะพี่ไม่มีโพรบวัดกระแสสำหรับออสซิลโลสโคป เลยต้องใช้มัลติมิเตอร์อย่างเดียวในการวัดระดับกระแส ซึ่งมันง่ายสำหรับช่วงสแตนด์บายยาวๆ แต่ยากมากสำหรับช่วงที่บอร์ดทำงาน พี่เห็นช่วงสั้นๆ ที่กระแสสูงขึ้น แต่ไม่ชัดเจนว่ามันนานเท่าไหร่ พี่เลยใช้ออสซิลโลสโคปช่วยดูการดรอปของแรงดันในช่วงที่กระแสสูงด้วย มันจะทำให้รู้เวลาที่แม่นยำขึ้น ตั้งตู้แล็บกันแบบนี้:

ทิป: ใช้โพรบวัดให้ชิดขั้วแบตเตอรี่ให้มากที่สุด

ด้วยแอมป์มิเตอร์ พี่วัดค่าได้ดังนี้:

เริ่มรัน Arduino...

  • 0 - 20 วินาที กระแสต่อเนื่อง 15mA
  • 20 - 42 วินาที มีพีค 2 ครั้ง 49mA
  • 42 - 99 วินาที กระแสต่อเนื่อง 25µA
Range = 50mV per division

พอวัดด้วยออสซิลโลสโคปแล้วได้กราฟออกมาแบบนี้ พี่เองยังเซอร์ไพรส์เลยว่ามันดึงข้อมูลออกมาได้เยอะแค่ไหน โดยทั่วไป เมื่อแรงดันอยู่ที่ 3.995V (แรงดันวงจรเปิด) แสดงว่าไม่มีการโหลด (ปิดสวิตช์) หรือมีโหลดแค่ 25µA ในช่วงสแตนด์บาย แรงดันจะดรอปลงเมื่อ Arduino หรือชิลด์ต้องการกระแสมากขึ้น ยิ่งแรงดันดรอปมาก กระแสก็ยิ่งไหลมาก มาดูรายละเอียดกันเลยดีกว่า:

  • [0] หลังจากเริ่มต้น จะเห็นว่ามีการดรอปเล็กๆ สองครั้ง นี่น่าจะเป็นการอ่านค่าพารามิเตอร์ระบบจากแฟลชของ SAMD21 นั่นแหละ
  • [1] เพราะว่าการดรอปตรงนี้มีระดับพลังงานเท่ากับดรอปใหญ่ๆ ในจุด [6] และ [8] รุ่นพี่คาดว่านี่น่าจะเป็นช่วงที่ส่งคำขอเข้าร่วมเครือข่าย LoRa (LoRa join request)
  • [3] หลังจากนั้นไม่นาน ก็จะเห็นพีคของการรับข้อความตอบรับเข้าร่วมเครือข่าย LoRa (LoRa join accept) โผล่มา
  • [4] ตอนนี้เฟส `setup()` จบลงแล้ว `loop()` ก็เริ่มทำงาน สไปก์เล็กๆ ที่เห็นทุกวินาที น่าจะมาจากการตรวจเช็คเซนเซอร์และแบตเตอรี่ ตรวจถี่เกินไปหน่อยนะตัวนี้ ไว้ค่อยมาปรับปรุงกันทีหลังนะน้อง :-)
  • [5] ตอนนี้รุ่นพี่เจอข้อผิดพลาด LoRa "Message length is bigger..." เลยไม่มีดรอปเกิดขึ้น ซึ่งมันก็ถูกต้องแล้ว
  • [6] หลังจากผ่านไป 15 วินาทีเป๊ะๆ ก็จะเห็นการส่งข้อมูล LoRa ครั้งแรก - ส่งคุณสมบัติ IoT สามตัวแรก (ดูในตารางเอ็กเซลด้านบนที่ไฮไลท์สีเหลือง) ใช้เวลา 1.95 วินาที - สำหรับข้อมูล 12 ไบต์ (บวกค่าโสหุ้ยอื่นๆ)
  • [7] นี่คือการยืนยันรับข้อมูล (Acknowledgement) จาก LoRa แล้ว เครือข่าย LoRa-WAN จะเป็นคนดูแลข้อมูลให้เรา ซึ่งดีมาก เพราะ Arduino ของเราสามารถลืมเรื่องนี้ไปได้เลย แม้แพ็กเกจจะยังไปไม่ถึงเซิร์ฟเวอร์ Arduino IoT Cloud ก็ตาม บน Serial Monitor ก็จะขึ้นข้อความ "Message sent correctly!" ทันที จบงาน!
  • [8] และไม่นานหลังจากนั้น การส่ง LoRa ครั้งต่อไปก็ตามมา พีคนี้ใช้เวลา 2.6 วินาที เพราะคราวนี้เราส่งคุณสมบัติ 5 ตัว (ด้านบนสีเขียว) และก็มีการตอบรับ (ACK) ตามมาอย่างที่คาด
  • [9] ก่อนที่รอบการส่งนี้จะเริ่มใหม่อีกครั้ง (ทุก 30 วินาที) สเก็ตช์จะเข้าสู่โหมด STANDBY
  • [10] หลังจากผ่านไป 16 วินาที จะเห็นสไปก์จิ๋วๆ เป็นสัญญาณการตื่นขึ้นของระบบ ตามด้วยการหลับอีก 16 วินาที หลังจากทำแบบนี้ 51 ครั้ง Watchdog จะทำการรีสตาร์ท CPU และรอบใหม่ก็เริ่มต้นขึ้น วนไปเรื่อยๆ...

ตอนนี้เรามีข้อมูลครบแล้ว มาคำนวณกันว่าแบตเตอรี่จะอยู่ได้นานแค่ไหนภายใต้โหลดแบบนี้ รุ่นพี่ใช้แบตเตอรี่ LiPo ขนาด 2600mAh นะ

กรณีไม่ใช้ Arduino STANDBY
กรณีใช้ Arduino STANDBY

เห็นความแตกต่างชัดเจนเลยใช่ไหมล่ะ? พัฒนาการที่ก้าวกระโดดมาก!

LoRa Duty Cycle

เพราะว่าคลื่นความถี่ LoRa ใช้ได้ฟรีๆ เราจึงต้องมีข้อจำกัดบางอย่าง หรือจะเรียกว่าเป็น "นโยบายการเข้าถึงที่ยุติธรรม" ก็ได้ นี่เป็นสิ่งที่ดีและสำคัญสำหรับเราทุกคน เพราะไม่อย่างนั้นเครือข่าย LoRa จะกลายเป็นที่จอแจไปหมด เศร้าใจสุดๆ Duty cycle ก็คือ

ข้อมูล Frontmatter ดั้งเดิม

title: "MKR WAN 1310 IoT - งานง่ายแต่หล่อ แถมประหยัดไฟสุดตึงที่ 0.92mA!"
description: "การลดกระแสไฟให้ต่ำสุดเป็นเรื่องจำเป็นมากๆ โดยเฉพาะในบอร์ดตระกูล MKR รุ่น LoRa นี้ แต่ก็มีด่านหลุมพรางบางอย่างที่รุ่นพี่เตือนไว้เลยว่าห้ามเผลอไปเหยียบ!"
author: "andreas_waldherr"
category: "Internet of Things, BT & Wireless"
tags:
  - "lora-wan"
  - "energy efficiency"
  - "internet of things"
views: 13503
likes: 4
price: 2450
difficulty: "Intermediate"
components:
  - "1x Digital Multimeter"
  - "1x Arduino MKR WAN 1310"
  - "1x ARDUINO PRO GATEWAY"
  - "1x Analog Discovery 2 (Oscilloscope)"
  - "1x LiPo 3,7V 2600mAh Battery"
  - "1x MKR ENV Shield"
  - "1x 1200K Ohm Resistor"
  - "1x 330K Ohm Resistor"
tools: []
apps:
  - "1x Arduino IoT Cloud"
downloadableFiles:
  - "https://create.arduino.cc/editor/andreas_waldherr/9612677b-9e10-4991-8cd9-d4b30560aec3"
documentationLinks: []
passwordHash: "af65e42592a3decab1b76bcb181863bed74994f1f64ff8a0ac0cf0680e834256"
encryptedPayload: "U2FsdGVkX19tVDaMoS3kYqvT+y2hkcJgnQWWBUsBOBKVg+Uzk+ufV0+RueMY6DXmeTJh7Xb9Ln923WA+BNE6JBDwfpj15JeiigVmrfCL0gI="
seoDescription: "Learn to reduce current drain for MKR WAN 1310 IoT to 0.92mA. Tips and pitfalls for the LoRa Version of MKR Series."
videoLinks: []
heroImage: "https://cdn.jsdelivr.net/gh/bigboxthailand/arduino-assets@main/images/projects/mkr-wan-1310-iot-operating-at-0-92ma-f13209_cover.jpg"
lang: "en"