การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

กาลครั้งหนึ่ง ระบบอัตโนมัติภายในบ้านหรือ "บ้านอัจฉริยะ" ตามที่มักเรียกกันว่ามีราคาแพงมาก และมีเพียงคนรวยเท่านั้นที่สามารถซื้อได้ ปัจจุบันในตลาดคุณจะพบกับชุดอุปกรณ์ที่มีราคาไม่แพงนักซึ่งมีเซ็นเซอร์ ปุ่ม/สวิตช์ และตัวกระตุ้นสำหรับควบคุมแสงสว่าง ปลั๊กไฟ การระบายอากาศ การจ่ายน้ำ และผู้บริโภคอื่นๆ และแม้กระทั่งคน DIY ที่คดโกงที่สุดก็สามารถมีส่วนร่วมในความงามและประกอบอุปกรณ์สำหรับบ้านอัจฉริยะในราคาที่ไม่แพง

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

โดยทั่วไป อุปกรณ์ที่นำเสนอจะเป็นเซ็นเซอร์หรือแอคทูเอเตอร์ ช่วยให้ปรับใช้สถานการณ์ต่างๆ ได้ง่าย เช่น “เมื่อเซ็นเซอร์ตรวจจับความเคลื่อนไหวถูกกระตุ้น ให้เปิดไฟ” หรือ “สวิตช์ใกล้ทางออกจะปิดไฟทั้งอพาร์ตเมนต์” แต่อย่างใดสิ่งต่าง ๆ ไม่ได้ผลกับการวัดทางไกล ที่ดีที่สุดคือกราฟของอุณหภูมิและความชื้น หรือกำลังไฟฟ้าที่เกิดขึ้นทันทีที่เต้าเสียบเฉพาะ

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

ด้านล่างสุดคืออุปกรณ์เวอร์ชันของฉันที่ใช้ ESP8266 ซึ่งนับพัลส์จากมาตรวัดน้ำและส่งการอ่านผ่าน MQTT ไปยังเซิร์ฟเวอร์สมาร์ทโฮม เราจะเขียนโปรแกรมใน micropython โดยใช้ไลบรารี uasyncio เมื่อสร้างเฟิร์มแวร์ ฉันพบปัญหาที่น่าสนใจหลายประการ ซึ่งฉันจะกล่าวถึงในบทความนี้ด้วย ไป!

โครงการนี้

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

หัวใจของวงจรทั้งหมดคือโมดูลบนไมโครคอนโทรลเลอร์ ESP8266 เดิมที ESP-12 นั้นมีการวางแผนไว้ แต่ปรากฏว่าของฉันมีข้อบกพร่อง เราต้องพอใจกับโมดูล ESP-07 ซึ่งมีจำหน่าย โชคดีที่พวกมันเหมือนกันทั้งในแง่ของพินและฟังก์ชันการทำงาน ความแตกต่างเพียงอย่างเดียวคือในเสาอากาศ - ESP-12 มีเสาอากาศในตัวในขณะที่ ESP-07 มีเสาอากาศภายนอก อย่างไรก็ตาม ถึงแม้จะไม่มีเสาอากาศ WiFi แต่สัญญาณในห้องน้ำของฉันก็รับสัญญาณได้ตามปกติ

การเดินสายไฟโมดูลมาตรฐาน:

  • ปุ่มรีเซ็ตพร้อมพูลอัพและตัวเก็บประจุ (แม้ว่าทั้งคู่จะอยู่ภายในโมดูลแล้วก็ตาม)
  • สัญญาณเปิดใช้งาน (CH_PD) ถูกดึงขึ้นมาเพื่อรับพลังงาน
  • GPIO15 ถูกดึงลงพื้น นี่จำเป็นแค่ตอนเริ่มต้น แต่ฉันยังไม่มีอะไรติดขานี้ ฉันไม่ต้องการมันอีกต่อไป

หากต้องการให้โมดูลเข้าสู่โหมดเฟิร์มแวร์ คุณจะต้องลัดวงจร GPIO2 ลงกราวด์ และเพื่อให้สะดวกยิ่งขึ้น ฉันจึงได้จัดเตรียมปุ่มบูตไว้ ในสภาวะปกติ พินนี้จะถูกดึงเข้าสู่กระแสไฟ

สถานะของสาย GPIO2 จะถูกตรวจสอบเฉพาะเมื่อเริ่มต้นการทำงาน - เมื่อมีการจ่ายไฟหรือทันทีหลังจากการรีเซ็ต ดังนั้นโมดูลจะบู๊ตตามปกติหรือเข้าสู่โหมดเฟิร์มแวร์ เมื่อโหลดแล้ว พินนี้สามารถใช้เป็น GPIO ทั่วไปได้ เนื่องจากมีปุ่มอยู่แล้ว คุณจึงสามารถแนบฟังก์ชันที่มีประโยชน์บางอย่างเข้าไปได้

สำหรับการเขียนโปรแกรมและการดีบัก ฉันจะใช้ UART ซึ่งส่งออกไปยังหวี เมื่อจำเป็น ฉันเพียงเชื่อมต่ออะแดปเตอร์ USB-UART ที่นั่น คุณเพียงแค่ต้องจำไว้ว่าโมดูลนั้นใช้พลังงานจาก 3.3V หากคุณลืมเปลี่ยนอะแดปเตอร์เป็นแรงดันไฟฟ้านี้และจ่ายไฟ 5V โมดูลมักจะไหม้

ฉันไม่มีปัญหาเรื่องไฟฟ้าในห้องน้ำ ปลั๊กไฟอยู่ห่างจากเมตรประมาณ 220 เมตร ดังนั้นฉันจึงใช้ไฟ XNUMXV เป็นแหล่งพลังงานฉันก็จะมีขนาดเล็ก บล็อก HLK-PM03 โดย Tenstar Robot โดยส่วนตัวแล้วฉันมีช่วงเวลาที่ยากลำบากกับแอนะล็อกและอุปกรณ์อิเล็กทรอนิกส์กำลัง แต่นี่คือแหล่งจ่ายไฟสำเร็จรูปในเคสขนาดเล็ก

ในการส่งสัญญาณโหมดการทำงาน ฉันจัดเตรียม LED ที่เชื่อมต่อกับ GPIO2 ไว้ อย่างไรก็ตาม ฉันไม่ได้ขายมัน เพราะ... โมดูล ESP-07 มี LED อยู่แล้วและเชื่อมต่อกับ GPIO2 ด้วย แต่ปล่อยให้มันอยู่บนบอร์ด เผื่อฉันต้องการเอาท์พุต LED นี้ไปที่เคส

เรามาดูส่วนที่น่าสนใจที่สุดกันดีกว่า มาตรวัดน้ำไม่มีตรรกะ คุณไม่สามารถขอให้อ่านค่าปัจจุบันได้ สิ่งเดียวที่มีสำหรับเราคือแรงกระตุ้น - ปิดหน้าสัมผัสของสวิตช์กกทุก ๆ ลิตร เอาต์พุตสวิตช์รีดของฉันเชื่อมต่อกับ GPIO12/GPIO13 ฉันจะเปิดใช้งานตัวต้านทานแบบดึงขึ้นโดยทางโปรแกรมภายในโมดูล

ตอนแรกฉันลืมเตรียมตัวต้านทาน R8 และ R9 มาให้ แต่บอร์ดเวอร์ชันของฉันไม่มีมาให้ แต่เนื่องจากฉันได้โพสต์ไดอะแกรมให้ทุกคนได้เห็นแล้ว จึงคุ้มค่าที่จะแก้ไขการกำกับดูแลนี้ จำเป็นต้องใช้ตัวต้านทานเพื่อไม่ให้พอร์ตไหม้หากเฟิร์มแวร์ขัดข้องและตั้งค่าพินเป็นหนึ่ง และสวิตช์กกทำให้สายนี้สั้นลงกราวด์ (โดยตัวต้านทานจะไหลสูงสุด 3.3V/1000Ohm = 3.3mA)

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

ดังนั้นฉันจึงตัดสินใจใช้การบันทึกค่าตัวนับในชิปหน่วยความจำที่เชื่อมต่อผ่าน I2C ฉันไม่มีข้อกำหนดพิเศษสำหรับขนาดของหน่วยความจำแฟลช - คุณจะต้องบันทึกตัวเลข 2 ตัวเท่านั้น (จำนวนลิตรตามมาตรวัดน้ำร้อนและน้ำเย็น) แม้แต่โมดูลที่เล็กที่สุดก็ยังทำได้ แต่คุณต้องใส่ใจกับจำนวนรอบการบันทึกด้วย สำหรับโมดูลส่วนใหญ่ อัตรานี้จะอยู่ที่ 100 รอบ หรืออาจสูงถึงหนึ่งล้านรอบ

ดูเหมือนล้านจะเยอะมาก แต่ในช่วง 4 ปีที่อาศัยอยู่ในอพาร์ทเมนต์ของฉัน ฉันใช้น้ำมากกว่า 500 ลูกบาศก์เมตรเล็กน้อย นั่นคือ 500 ลิตร! และบันทึก 500 รายการในแฟลช และนั่นเป็นเพียงน้ำเย็น แน่นอนคุณสามารถจำหน่ายชิปต่อได้ทุกสองสามปี แต่ปรากฎว่ามีชิป FRAM จากมุมมองของการเขียนโปรแกรม นี่คือ I2C EEPROM เดียวกัน แต่มีรอบการเขียนซ้ำจำนวนมากเท่านั้น (หลายร้อยล้าน) ฉันแค่ยังไปที่ร้านด้วยไมโครวงจรดังกล่าวไม่ได้ดังนั้นตอนนี้ 24LC512 ปกติจะยังคงอยู่

แผงวงจรพิมพ์

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

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

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

นอกจากนี้ยังมีปัญหาใหญ่ประการหนึ่งกับการเดินสายทางเดียว เพราะ บอร์ดถูกวาดให้เป็นด้านเดียว ดังนั้นจึงมีการวางแผนให้รางและส่วนประกอบ SMD อยู่ที่ด้านหนึ่ง และส่วนประกอบเอาต์พุต ขั้วต่อ และแหล่งจ่ายไฟอยู่อีกด้านหนึ่ง เมื่อฉันได้รับบอร์ดในอีกหนึ่งเดือนต่อมา ฉันลืมแผนเดิมและบัดกรีส่วนประกอบทั้งหมดที่ด้านหน้า และเฉพาะเมื่อถึงเวลาบัดกรีแหล่งจ่ายไฟกลับกลายเป็นว่ามีการต่อสายบวกและลบกลับกัน ฉันต้องทำฟาร์มด้วยจัมเปอร์ ในภาพด้านบนฉันได้เปลี่ยนสายไฟแล้ว แต่กราวด์ถูกถ่ายโอนจากส่วนหนึ่งของบอร์ดไปยังอีกส่วนหนึ่งผ่านหมุดของปุ่ม Boot (แม้ว่าจะเป็นไปได้ที่จะวาดแทร็กบนเลเยอร์ที่สองก็ตาม)

มันกลับกลายเป็นแบบนี้

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

การเคหะ

ขั้นตอนต่อไปคือร่างกาย หากคุณมีเครื่องพิมพ์ 3D ก็ไม่ใช่ปัญหา ฉันไม่ได้กังวลอะไรมากนัก - ฉันแค่วาดกล่องที่มีขนาดเหมาะสมและทำพิลึกในตำแหน่งที่ถูกต้อง ฝาครอบติดอยู่กับตัวเครื่องด้วยสกรูเกลียวปล่อยขนาดเล็ก

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

ฉันได้กล่าวไปแล้วว่าปุ่ม Boot สามารถใช้เป็นปุ่มใช้งานทั่วไปได้ - ดังนั้นเราจะแสดงไว้ที่แผงด้านหน้า เมื่อต้องการทำเช่นนี้ ฉันวาด "บ่อ" พิเศษที่มีปุ่มอยู่

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

ภายในเคสยังมีสตั๊ดสำหรับติดตั้งบอร์ดและยึดด้วยสกรู M3 ตัวเดียว (ไม่มีที่ว่างบนบอร์ดอีกต่อไป)

ฉันเลือกจอแสดงผลแล้วเมื่อฉันพิมพ์เคสเวอร์ชันตัวอย่างแรก เครื่องอ่านสองบรรทัดมาตรฐานไม่เหมาะกับในกรณีนี้ แต่ที่ด้านล่างมีจอแสดงผล OLED SSD1306 128×32 มันเล็กน้อย แต่ฉันไม่จำเป็นต้องจ้องมองมันทุกวัน มันมากเกินไปสำหรับฉัน

เมื่อทราบวิธีการนี้และวิธีเดินสายไฟ ฉันจึงตัดสินใจติดจอแสดงผลไว้ตรงกลางเคส แน่นอนว่าการยศาสตร์นั้นต่ำกว่ามาตรฐาน - ปุ่มอยู่ด้านบน จอแสดงผลอยู่ด้านล่าง แต่ฉันบอกไปแล้วว่าความคิดที่จะติดจอแสดงผลมาช้าเกินไป และฉันขี้เกียจเกินไปที่จะเดินสายบอร์ดใหม่เพื่อย้ายปุ่ม

ประกอบอุปกรณ์แล้ว โมดูลจอแสดงผลติดกาวกับน้ำมูกด้วยกาวร้อน

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

ผลลัพธ์สุดท้ายสามารถดูได้บน KDPV

การแทรก

เรามาดูส่วนของซอฟต์แวร์กันดีกว่า สำหรับงานฝีมือเล็กๆ แบบนี้ ฉันชอบใช้ Python มาก (ไมโครไพธอน) - โค้ดมีขนาดกะทัดรัดและเข้าใจได้มาก โชคดีที่ไม่จำเป็นต้องลงไปที่ระดับการลงทะเบียนเพื่อบีบไมโครวินาที - ทุกอย่างสามารถทำได้จาก Python

ดูเหมือนว่าทุกอย่างจะเรียบง่าย แต่ไม่ง่ายนัก - อุปกรณ์มีฟังก์ชั่นอิสระหลายประการ:

  • ผู้ใช้สะกิดปุ่มแล้วดูที่หน้าจอ
  • ลิตรทำเครื่องหมายและอัปเดตค่าในหน่วยความจำแฟลช
  • โมดูลจะตรวจสอบสัญญาณ WiFi และเชื่อมต่อใหม่หากจำเป็น
  • หากไม่มีหลอดไฟกะพริบก็เป็นไปไม่ได้

คุณไม่สามารถสรุปได้ว่าฟังก์ชันหนึ่งใช้งานไม่ได้หากฟังก์ชันอื่นค้างอยู่ด้วยเหตุผลบางประการ ฉันได้เติมกระบองเพชรในโครงการอื่นแล้ว และตอนนี้ฉันยังคงเห็นข้อบกพร่องในลักษณะ "พลาดไปอีกหนึ่งลิตรเนื่องจากจอแสดงผลกำลังอัปเดตในขณะนั้น" หรือ "ผู้ใช้ไม่สามารถทำอะไรได้ในขณะที่โมดูลกำลังเชื่อมต่อกับ ไวไฟ” แน่นอนว่า บางสิ่งสามารถทำได้ผ่านการขัดจังหวะ แต่คุณอาจพบข้อจำกัดด้านระยะเวลา การซ้อนกันของการโทร หรือการเปลี่ยนแปลงตัวแปรที่ไม่ใช่แบบอะตอมมิก รหัสที่ทำทุกอย่างอย่างรวดเร็วกลายเป็นข้าวต้ม

В โครงการที่จริงจังยิ่งขึ้น ฉันใช้มัลติทาสก์แบบยึดเอาเสียก่อนแบบคลาสสิกและ FreeRTOS แต่ในกรณีนี้โมเดลนี้ดูเหมาะสมกว่ามาก coroutines และไลบรารี uasync . ยิ่งไปกว่านั้น การใช้งาน Python ของ coroutines นั้นน่าทึ่งมาก - ทุกอย่างทำได้ง่ายและสะดวกสำหรับโปรแกรมเมอร์ แค่เขียนตรรกะของคุณเอง แค่บอกฉันว่าคุณสามารถสลับระหว่างสตรีมได้ที่ไหน

ฉันขอแนะนำให้ศึกษาความแตกต่างระหว่างการทำงานหลายอย่างพร้อมกันแบบยึดถือและแข่งขันเป็นวิชาเลือก ในที่สุดเรามาดูโค้ดกันดีกว่า

#####################################
# Counter class - implements a single water counter on specified pin
#####################################
class Counter():
    debounce_ms = const(25)
    
    def __init__(self, pin_num, value_storage):
        self._value_storage = value_storage
        
        self._value = self._value_storage.read()
        self._value_changed = False

        self._pin = Pin(pin_num, Pin.IN, Pin.PULL_UP)

        loop = asyncio.get_event_loop()
        loop.create_task(self._switchcheck())  # Thread runs forever

ตัวนับแต่ละตัวได้รับการจัดการโดยอินสแตนซ์ของคลาสตัวนับ ก่อนอื่นค่าตัวนับเริ่มต้นจะถูกลบออกจาก EEPROM (value_storage) - นี่คือวิธีการกู้คืนหลังจากเกิดไฟฟ้าดับ

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

มีการเปิดตัวงานแยกต่างหากที่นี่ ซึ่งจะสำรวจพิน แต่ละเคาน์เตอร์จะทำงานของตัวเอง นี่คือรหัสของเธอ

    """ Poll pin and advance value when another litre passed """
    async def _switchcheck(self):
        last_checked_pin_state = self._pin.value()  # Get initial state

        # Poll for a pin change
        while True:
            state = self._pin.value()
            if state != last_checked_pin_state:
                # State has changed: act on it now.
                last_checked_pin_state = state
                if state == 0:
                    self._another_litre_passed()

            # Ignore further state changes until switch has settled
            await asyncio.sleep_ms(Counter.debounce_ms)

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

    def _another_litre_passed(self):
        self._value += 1
        self._value_changed = True

        self._value_storage.write(self._value)

การประมวลผลลิตรถัดไปนั้นไม่สำคัญ - ตัวนับจะเพิ่มขึ้น เป็นการดีที่จะเขียนค่าใหม่ลงในแฟลชไดรฟ์

เพื่อความสะดวกในการใช้งานจึงมี “อุปกรณ์เสริม” เตรียมไว้ให้

    def value(self):
        self._value_changed = False
        return self._value

    def set_value(self, value):
        self._value = value
        self._value_changed = False

ตอนนี้เรามาใช้ประโยชน์จากความสุขของ Python และไลบรารี uasync และสร้างวัตถุที่รอได้ (เราจะแปลสิ่งนี้เป็นภาษารัสเซียได้อย่างไร สิ่งที่คุณคาดหวังได้?)

    def __await__(self):
        while not self._value_changed:
            yield from asyncio.sleep(0)

        return self.value()

    __iter__ = __await__  

นี่เป็นฟังก์ชันที่สะดวกสบายที่รอจนกว่าจะอัปเดตค่าตัวนับ - ฟังก์ชันจะเริ่มทำงานเป็นครั้งคราวและตรวจสอบแฟล็ก _value_changed สิ่งที่ยอดเยี่ยมเกี่ยวกับฟังก์ชันนี้คือ รหัสการโทรสามารถหลับได้ในขณะที่เรียกใช้ฟังก์ชันนี้ และจะเข้าสู่โหมดสลีปจนกว่าจะได้รับค่าใหม่

แล้วการหยุดชะงักล่ะ?ใช่ เมื่อถึงจุดนี้ คุณหลอกฉันได้เลย โดยบอกว่าคุณเองก็พูดเรื่องการขัดจังหวะ แต่จริงๆ แล้ว คุณทำโพลพินที่โง่เขลา จริงๆ แล้วการขัดจังหวะคือสิ่งแรกที่ฉันพยายาม ใน ESP8266 คุณสามารถจัดระเบียบ Edge Interrupt และแม้แต่เขียนตัวจัดการสำหรับการขัดจังหวะนี้ใน Python ในการขัดจังหวะนี้ ค่าของตัวแปรสามารถอัพเดตได้ อาจเพียงพอแล้วหากตัวนับเป็นอุปกรณ์ทาส - อุปกรณ์ที่รอจนกว่าจะมีการถามค่านี้

น่าเสียดาย (หรือโชคดี?) อุปกรณ์ของฉันเปิดใช้งานอยู่ โดยจะต้องส่งข้อความผ่านโปรโตคอล MQTT และเขียนข้อมูลไปยัง EEPROM และนี่คือข้อ จำกัด ที่เข้ามามีบทบาท - คุณไม่สามารถจัดสรรหน่วยความจำในการขัดจังหวะและใช้สแต็กขนาดใหญ่ซึ่งหมายความว่าคุณสามารถลืมเกี่ยวกับการส่งข้อความผ่านเครือข่ายได้ มีขนมปังเช่น micropython.schedule() ที่ให้คุณเรียกใช้ฟังก์ชันบางอย่าง "โดยเร็วที่สุด" แต่คำถามก็เกิดขึ้นว่า "มีประโยชน์อะไร" จะเกิดอะไรขึ้นถ้าเราส่งข้อความบางประเภทตอนนี้ แล้วมีการขัดจังหวะเข้ามาและทำให้ค่าของตัวแปรเสียหาย หรือ ตัวอย่างเช่น ค่าตัวนับใหม่มาจากเซิร์ฟเวอร์ในขณะที่เรายังไม่ได้จดค่าเก่าไว้ โดยทั่วไปคุณต้องบล็อกการซิงโครไนซ์หรือออกจากการซิงโครไนซ์ด้วยวิธีอื่น

และบางครั้ง RuntimeError: schedule stack full ล่ม และใครจะรู้ว่าเพราะเหตุใด

ด้วยการโพลที่ชัดเจนและ uasync ในกรณีนี้มันจะดูสวยงามและเชื่อถือได้มากขึ้น

ฉันนำงานที่มี EEPROM มาสู่ชั้นเรียนขนาดเล็ก

class EEPROM():
    i2c_addr = const(80)

    def __init__(self, i2c):
        self.i2c = i2c
        self.i2c_buf = bytearray(4) # Avoid creation/destruction of the buffer on each call


    def read(self, eeprom_addr):
        self.i2c.readfrom_mem_into(self.i2c_addr, eeprom_addr, self.i2c_buf, addrsize=16)
        return ustruct.unpack_from("<I", self.i2c_buf)[0]    
        
    
    def write(self, eeprom_addr, value):
        ustruct.pack_into("<I", self.i2c_buf, 0, value)
        self.i2c.writeto_mem(self.i2c_addr, eeprom_addr, self.i2c_buf, addrsize=16)

ใน Python เป็นการยากที่จะทำงานกับไบต์โดยตรง แต่เป็นไบต์ที่เขียนลงในหน่วยความจำ ฉันต้องกั้นการแปลงระหว่างจำนวนเต็มและไบต์โดยใช้ไลบรารี ustruct

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

class EEPROMValue():
    def __init__(self, i2c, eeprom_addr):
        self._eeprom = EEPROM(i2c)
        self._eeprom_addr = eeprom_addr
        

    def read(self):
        return self._eeprom.read(self._eeprom_addr)


    def write(self, value):
        self._eeprom.write(self._eeprom_addr, value)

วัตถุ I2C นั้นถูกสร้างขึ้นด้วยพารามิเตอร์เหล่านี้

i2c = I2C(freq=400000, scl=Pin(5), sda=Pin(4))

เรามาถึงส่วนที่น่าสนใจที่สุด - การใช้งานการสื่อสารกับเซิร์ฟเวอร์ผ่าน MQTT ไม่จำเป็นต้องใช้โปรโตคอลเอง - ฉันพบมันบนอินเทอร์เน็ต การใช้งานแบบอะซิงโครนัสสำเร็จรูป. นี่คือสิ่งที่เราจะใช้

สิ่งที่น่าสนใจที่สุดทั้งหมดจะถูกรวบรวมไว้ในคลาส CounterMQTTClient ซึ่งอิงตามไลบรารี MQTTClient เริ่มจากบริเวณรอบนอกกันก่อน

#####################################
# Class handles both counters and sends their status to MQTT
#####################################
class CounterMQTTClient(MQTTClient):

    blue_led = Pin(2, Pin.OUT, value = 1)
    button = Pin(0, Pin.IN)

    hot_counter = Counter(12, EEPROMValue(i2c, EEPROM_ADDR_HOT_VALUE))
    cold_counter = Counter(13, EEPROMValue(i2c, EEPROM_ADDR_COLD_VALUE))

ที่นี่คุณสามารถสร้างและกำหนดค่าหมุดและปุ่มของหลอดไฟ รวมถึงวัตถุมาตรวัดน้ำเย็นและน้ำร้อนได้

ด้วยการเริ่มต้นไม่ใช่ทุกอย่างจะเป็นเรื่องเล็กน้อย

    def __init__(self):
        self.internet_outage = True
        self.internet_outages = 0
        self.internet_outage_start = ticks_ms()

        with open("config.txt") as config_file:
            config['ssid'] = config_file.readline().rstrip()
            config['wifi_pw'] = config_file.readline().rstrip()
            config['server'] = config_file.readline().rstrip()
            config['client_id'] = config_file.readline().rstrip()
            self._mqtt_cold_water_theme = config_file.readline().rstrip()
            self._mqtt_hot_water_theme = config_file.readline().rstrip()
            self._mqtt_debug_water_theme = config_file.readline().rstrip()

        config['subs_cb'] = self.mqtt_msg_handler
        config['wifi_coro'] = self.wifi_connection_handler
        config['connect_coro'] = self.mqtt_connection_handler
        config['clean'] = False
        config['clean_init'] = False
        super().__init__(config)

        loop = asyncio.get_event_loop()
        loop.create_task(self._heartbeat())
        loop.create_task(self._counter_coro(self.cold_counter, self._mqtt_cold_water_theme))
        loop.create_task(self._counter_coro(self.hot_counter, self._mqtt_hot_water_theme))
        loop.create_task(self._display_coro())

ในการตั้งค่าพารามิเตอร์การทำงานของไลบรารี mqtt_as จะใช้พจนานุกรมขนาดใหญ่ที่มีการตั้งค่าต่างกัน - config. การตั้งค่าเริ่มต้นส่วนใหญ่นั้นใช้ได้สำหรับเรา แต่การตั้งค่าหลายอย่างจำเป็นต้องตั้งค่าอย่างชัดเจน เพื่อไม่ให้เขียนการตั้งค่าลงในโค้ดโดยตรง ฉันจะจัดเก็บไว้ในไฟล์ข้อความ config.txt สิ่งนี้ช่วยให้คุณสามารถเปลี่ยนรหัสโดยไม่คำนึงถึงการตั้งค่า เช่นเดียวกับการตรึงอุปกรณ์ที่เหมือนกันหลายตัวด้วยพารามิเตอร์ที่แตกต่างกัน

บล็อกสุดท้ายของโค้ดจะเริ่มต้นคอร์รูทีนหลายตัวเพื่อรองรับฟังก์ชันต่างๆ ของระบบ ตัวอย่างเช่น นี่คือ Coroutine ที่ให้บริการเคาน์เตอร์

    async def _counter_coro(self, counter, topic):
        # Publish initial value
        value = counter.value()
        await self.publish(topic, str(value))

        # Publish each new value
        while True:
            value = await counter
            await self.publish_msg(topic, str(value))

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

MQTTClient คลาสพื้นฐานทำหน้าที่เอง เริ่มต้นการเชื่อมต่อ WiFi และเชื่อมต่อใหม่เมื่อการเชื่อมต่อขาดหาย เมื่อมีการเปลี่ยนแปลงสถานะการเชื่อมต่อ WiFi ห้องสมุดจะแจ้งให้เราทราบโดยโทรไปที่ wifi_connection_handler

    async def wifi_connection_handler(self, state):
        self.internet_outage = not state
        if state:
            self.dprint('WiFi is up.')
            duration = ticks_diff(ticks_ms(), self.internet_outage_start) // 1000
            await self.publish_debug_msg('ReconnectedAfter', duration)
        else:
            self.internet_outages += 1
            self.internet_outage_start = ticks_ms()
            self.dprint('WiFi is down.')
            
        await asyncio.sleep(0)

ฟังก์ชันนี้คัดลอกมาจากตัวอย่างโดยสุจริต ในกรณีนี้ จะนับจำนวนการหยุดทำงาน (internet_outages) และระยะเวลา เมื่อการเชื่อมต่อกลับคืนมา เวลาว่างจะถูกส่งไปยังเซิร์ฟเวอร์

อย่างไรก็ตาม จำเป็นต้องใช้โหมดสลีปครั้งสุดท้ายเพื่อทำให้ฟังก์ชันอะซิงโครนัสเท่านั้น - ในไลบรารีจะถูกเรียกผ่าน await และเฉพาะฟังก์ชันที่เนื้อหามีการ await อื่นเท่านั้นที่สามารถเรียกได้

นอกจากการเชื่อมต่อ WiFi แล้ว คุณยังต้องสร้างการเชื่อมต่อกับโบรกเกอร์ MQTT (เซิร์ฟเวอร์) ด้วย ห้องสมุดก็ทำเช่นนี้เช่นกัน และเราได้รับโอกาสในการทำสิ่งที่มีประโยชน์เมื่อทำการเชื่อมต่อแล้ว

    async def mqtt_connection_handler(self, client):
        await client.subscribe(self._mqtt_cold_water_theme)
        await client.subscribe(self._mqtt_hot_water_theme)

ที่นี่เราสมัครรับข้อความหลายข้อความ - ขณะนี้เซิร์ฟเวอร์มีความสามารถในการตั้งค่าตัวนับปัจจุบันโดยการส่งข้อความที่เกี่ยวข้อง

    def mqtt_msg_handler(self, topic, msg):
        topicstr = str(topic, 'utf8')
        self.dprint("Received MQTT message topic={}, msg={}".format(topicstr, msg))

        if topicstr == self._mqtt_cold_water_theme:
            self.cold_counter.set_value(int(msg))

        if topicstr == self._mqtt_hot_water_theme:
            self.hot_counter.set_value(int(msg))

ฟังก์ชั่นนี้ประมวลผลข้อความขาเข้าและขึ้นอยู่กับหัวข้อ (ชื่อข้อความ) ค่าของตัวนับตัวใดตัวหนึ่งจะได้รับการอัปเดต

ฟังก์ชั่นผู้ช่วยสองสามอย่าง

    # Publish a message if WiFi and broker is up, else discard
    async def publish_msg(self, topic, msg):
        self.dprint("Publishing message on topic {}: {}".format(topic, msg))
        if not self.internet_outage:
            await self.publish(topic, msg)
        else:
            self.dprint("Message was not published - no internet connection")

ฟังก์ชันนี้จะส่งข้อความหากมีการเชื่อมต่อเกิดขึ้น หากไม่มีการเชื่อมต่อ ข้อความจะถูกละเว้น

และนี่เป็นเพียงฟังก์ชันที่สะดวกสบายที่สร้างและส่งข้อความแก้ไขจุดบกพร่อง

    async def publish_debug_msg(self, subtopic, msg):
        await self.publish_msg("{}/{}".format(self._mqtt_debug_water_theme, subtopic), str(msg))

ข้อความเยอะมาก และเรายังไม่ได้กระพริบไฟ LED เลย ที่นี่

    # Blink flash LED if WiFi down
    async def _heartbeat(self):
        while True:
            if self.internet_outage:
                self.blue_led(not self.blue_led()) # Fast blinking if no connection
                await asyncio.sleep_ms(200) 
            else:
                self.blue_led(0) # Rare blinking when connected
                await asyncio.sleep_ms(50)
                self.blue_led(1)
                await asyncio.sleep_ms(5000)

ฉันมีโหมดกะพริบ 2 โหมด หากการเชื่อมต่อขาดหาย (หรือเพิ่งเริ่มต้น) อุปกรณ์จะกะพริบอย่างรวดเร็ว หากสร้างการเชื่อมต่อแล้ว อุปกรณ์จะกะพริบทุกๆ 5 วินาที หากจำเป็น สามารถใช้โหมดการกะพริบอื่นๆ ได้ที่นี่

แต่ LED เป็นเพียงการผ่อนคลาย เรายังมุ่งเป้าไปที่การแสดงผลด้วย

    async def _display_coro(self):
        display = SSD1306_I2C(128,32, i2c)
    
        while True:
            display.poweron()
            display.fill(0)
            display.text("COLD: {:.3f}".format(self.cold_counter.value() / 1000), 16, 4)
            display.text("HOT:  {:.3f}".format(self.hot_counter.value() / 1000), 16, 20)
            display.show()
            await asyncio.sleep(3)
            display.poweroff()

            while self.button():
                await asyncio.sleep_ms(20)

นี่คือสิ่งที่ฉันกำลังพูดถึง - coroutines นั้นง่ายและสะดวกเพียงใด ฟังก์ชั่นเล็กๆ นี้อธิบายประสบการณ์การใช้งานทั้งหมดของผู้ใช้ โครูทีนเพียงรอให้กดปุ่มแล้วเปิดจอแสดงผลเป็นเวลา 3 วินาที หน้าจอแสดงค่ามิเตอร์ปัจจุบัน

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

   async def main(self):
        while True:
            try:
                await self._connect_to_WiFi()
                await self._run_main_loop()
                    
            except Exception as e:
                self.dprint('Global communication failure: ', e)
                await asyncio.sleep(20)

    async def _connect_to_WiFi(self):
        self.dprint('Connecting to WiFi and MQTT')
        sta_if = network.WLAN(network.STA_IF)
        sta_if.connect(config['ssid'], config['wifi_pw'])
        
        conn = False
        while not conn:
            await self.connect()
            conn = True

        self.dprint('Connected!')
        self.internet_outage = False

    async def _run_main_loop(self):
        # Loop forever
        mins = 0
        while True:
            gc.collect()  # For RAM stats.
            mem_free = gc.mem_free()
            mem_alloc = gc.mem_alloc()

            try:
                await self.publish_debug_msg("Uptime", mins)
                await self.publish_debug_msg("Repubs", self.REPUB_COUNT)
                await self.publish_debug_msg("Outages", self.internet_outages)
                await self.publish_debug_msg("MemFree", mem_free)
                await self.publish_debug_msg("MemAlloc", mem_alloc)
            except Exception as e:
                self.dprint("Exception occurred: ", e)
            mins += 1

            await asyncio.sleep(60)

มีการตั้งค่าและค่าคงที่เพิ่มเติมอีกสองสามรายการเพื่อทำให้คำอธิบายสมบูรณ์

#####################################
# Constants and configuration
#####################################


config['keepalive'] = 60
config['clean'] = False
config['will'] = ('/ESP/Wemos/Water/LastWill', 'Goodbye cruel world!', False, 0)

MQTTClient.DEBUG = True

EEPROM_ADDR_HOT_VALUE = const(0)
EEPROM_ADDR_COLD_VALUE = const(4)

ทุกอย่างเริ่มต้นเช่นนี้

client = CounterMQTTClient()
loop = asyncio.get_event_loop()
loop.run_until_complete(client.main())

มีบางอย่างเกิดขึ้นกับความทรงจำของฉัน

ดังนั้นรหัสทั้งหมดจึงอยู่ที่นั่น ฉันอัปโหลดไฟล์โดยใช้ยูทิลิตี้ ampy - ช่วยให้คุณสามารถอัปโหลดไฟล์เหล่านั้นไปยังแฟลชไดรฟ์ภายใน (ที่อยู่ใน ESP-07 เอง) จากนั้นเข้าถึงจากโปรแกรมเป็นไฟล์ปกติ ที่นั่นฉันยังอัปโหลด mqtt_as, uasyncio, ssd1306 และไลบรารีคอลเลกชันที่ฉันใช้ (ใช้ภายใน mqtt_as)

เราเปิดตัวและ... เราได้รับ MemoryError ยิ่งไปกว่านั้น ยิ่งฉันพยายามทำความเข้าใจว่าหน่วยความจำรั่วตรงจุดไหน ฉันก็ยิ่งพิมพ์ดีบั๊กมากขึ้นเท่านั้น ข้อผิดพลาดนี้ก็ยิ่งปรากฏขึ้นเร็วขึ้นเท่านั้น การค้นหาโดย Google สั้นๆ ทำให้ฉันเข้าใจว่าโดยหลักการแล้วไมโครคอนโทรลเลอร์มีหน่วยความจำเพียง 30 kB ซึ่งโค้ดขนาด 65 kB (รวมถึงไลบรารีด้วย) ไม่สามารถใส่ได้พอดี

แต่มีทางออก ปรากฎว่า micropython ไม่ได้รันโค้ดโดยตรงจากไฟล์ .py - ไฟล์นี้จะถูกคอมไพล์ก่อน ยิ่งไปกว่านั้น มันถูกคอมไพล์โดยตรงบนไมโครคอนโทรลเลอร์ และกลายเป็นโค้ดไบต์ ซึ่งจากนั้นจะถูกจัดเก็บไว้ในหน่วยความจำ เพื่อให้คอมไพเลอร์ทำงานได้คุณต้องมี RAM จำนวนหนึ่งด้วย

เคล็ดลับคือการบันทึกไมโครคอนโทรลเลอร์จากการคอมไพล์ที่ใช้ทรัพยากรมาก คุณสามารถรวบรวมไฟล์บนคอมพิวเตอร์ขนาดใหญ่และอัปโหลดรหัสไบต์สำเร็จรูปลงในไมโครคอนโทรลเลอร์ ในการดำเนินการนี้ คุณจะต้องดาวน์โหลดเฟิร์มแวร์และบิลด์ micropython ยูทิลิตี้ mpy-cross.

ฉันไม่ได้เขียน Makefile แต่ได้ดำเนินการและรวบรวมไฟล์ที่จำเป็นทั้งหมด (รวมถึงไลบรารี) ด้วยตนเองในลักษณะนี้

mpy-cross water_counter.py

สิ่งที่เหลืออยู่คือการอัปโหลดไฟล์ที่มีนามสกุล .mpy โดยไม่ลืมที่จะลบ .py ที่เกี่ยวข้องออกจากระบบไฟล์ของอุปกรณ์ก่อน

ฉันทำการพัฒนาทั้งหมดในโปรแกรม (IDE?) ESPlorer ช่วยให้คุณสามารถอัปโหลดสคริปต์ไปยังไมโครคอนโทรลเลอร์และดำเนินการได้ทันที ในกรณีของฉัน ตรรกะและการสร้างออบเจ็กต์ทั้งหมดจะอยู่ในไฟล์ water_counter.py (.mpy) แต่เพื่อให้ทั้งหมดนี้เริ่มต้นโดยอัตโนมัติ จะต้องมีไฟล์ชื่อ main.py ในตอนเริ่มต้นด้วย ยิ่งไปกว่านั้น มันควรจะเป็น .py ทุกประการ และไม่ใช่ .mpy ที่คอมไพล์ไว้ล่วงหน้า นี่คือเนื้อหาเล็กน้อย

import water_counter

เราเปิดตัวมัน - ทุกอย่างใช้งานได้ แต่หน่วยความจำว่างมีขนาดเล็กจนน่าตกใจ - ประมาณ 1kb ฉันยังมีแผนที่จะขยายฟังก์ชันการทำงานของอุปกรณ์ และเห็นได้ชัดว่ากิโลไบต์นี้ไม่เพียงพอสำหรับฉัน แต่กลับกลายเป็นว่ามีทางออกสำหรับคดีนี้ด้วย

นี่คือสิ่งที่ แม้ว่าไฟล์จะถูกคอมไพล์เป็นไบต์โค้ดและอยู่ในระบบไฟล์ภายใน แต่ในความเป็นจริง ไฟล์เหล่านั้นยังคงโหลดเข้าสู่ RAM และดำเนินการจากที่นั่น แต่ปรากฎว่า micropython สามารถรันโค้ดไบต์ได้โดยตรงจากหน่วยความจำแฟลช แต่สำหรับสิ่งนี้คุณต้องสร้างมันลงในเฟิร์มแวร์โดยตรง ไม่ใช่เรื่องยาก แม้ว่าจะใช้เวลาบนเน็ตบุ๊กของฉันค่อนข้างนาน (มีเพียงฉันเท่านั้นที่มี Linux)

อัลกอริทึมมีดังนี้:

  • ดาวน์โหลดและติดตั้ง ESP เปิด SDK. สิ่งนี้ประกอบคอมไพเลอร์และไลบรารีสำหรับโปรแกรมสำหรับ ESP8266 ประกอบตามคำแนะนำในหน้าหลักของโครงการ (ฉันเลือกการตั้งค่า STANDALONE=yes)
  • ดาวน์โหลด ไมโครไพธอนเรียงลำดับ
  • วางไลบรารีที่จำเป็นใน ports/esp8266/modules ภายในแผนผัง micropython
  • เราประกอบเฟิร์มแวร์ตามคำแนะนำในไฟล์ พอร์ต/esp8266/README.md
  • เราอัปโหลดเฟิร์มแวร์ไปยังไมโครคอนโทรลเลอร์ (ฉันทำบน Windows โดยใช้โปรแกรม ESP8266Flasher หรือ Python esptool)

เพียงเท่านี้ตอนนี้ 'import ssd1306' จะยกโค้ดโดยตรงจากเฟิร์มแวร์และ RAM จะไม่ถูกใช้ในการดำเนินการนี้ ด้วยเคล็ดลับนี้ ฉันอัปโหลดเฉพาะโค้ดไลบรารีลงในเฟิร์มแวร์ ในขณะที่โค้ดโปรแกรมหลักดำเนินการจากระบบไฟล์ สิ่งนี้ช่วยให้คุณแก้ไขโปรแกรมได้อย่างง่ายดายโดยไม่ต้องคอมไพล์เฟิร์มแวร์ใหม่ ในขณะนี้ฉันมี RAM ว่างประมาณ 8.5kb สิ่งนี้จะทำให้เราสามารถใช้ฟังก์ชันที่มีประโยชน์ต่างๆ มากมายได้ในอนาคต ถ้ามีหน่วยความจำไม่เพียงพอคุณสามารถดันโปรแกรมหลักเข้าไปในเฟิร์มแวร์ได้

แล้วเราควรทำอย่างไรกับเรื่องนี้ตอนนี้?

ตกลง ฮาร์ดแวร์ถูกบัดกรี เขียนเฟิร์มแวร์ พิมพ์กล่อง อุปกรณ์ติดอยู่บนผนัง และหลอดไฟกะพริบอย่างมีความสุข แต่สำหรับตอนนี้มันเป็นกล่องดำทั้งหมด (ตามตัวอักษรและเป็นรูปเป็นร่าง) และยังไม่ค่อยมีประโยชน์ ถึงเวลาที่ต้องทำอะไรบางอย่างกับข้อความ MQTT ที่ส่งไปยังเซิร์ฟเวอร์

“บ้านอัจฉริยะ” ของฉันกำลังหมุนอยู่ ระบบเมเจอร์โดโม. โมดูล MQTT แกะกล่องหรือติดตั้งได้ง่ายจากตลาดส่วนเสริม ฉันจำไม่ได้ว่าได้มาจากไหน MQTT ไม่ใช่สิ่งที่พึ่งพาตนเองได้ คุณต้องมีสิ่งที่เรียกว่า นายหน้า - เซิร์ฟเวอร์ที่รับ จัดเรียง และส่งต่อข้อความ MQTT ไปยังไคลเอนต์ ฉันใช้ยุงซึ่ง (เช่น Majordomo) ทำงานบนเน็ตบุ๊กเครื่องเดียวกัน

หลังจากที่เครื่องส่งข้อความอย่างน้อย XNUMX ครั้ง ค่าจะปรากฏในรายการทันที

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

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

ผมจะแสดงกราฟสองสามกราฟให้คุณดู นี่คือกราฟอย่างง่ายของมูลค่ารายวัน

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

จากกราฟนี้ ฉันได้เรียนรู้ว่าการเข้าห้องน้ำต้องใช้น้ำ 6-7 ลิตร การอาบน้ำต้องใช้ 20-30 ลิตร การล้างจานต้องใช้ประมาณ 20 ลิตร และการอาบน้ำต้องใช้ 160 ลิตร ครอบครัวของฉันใช้ประมาณ 500-600 ลิตรต่อวัน

สำหรับผู้ที่สงสัยเป็นพิเศษ คุณสามารถดูบันทึกสำหรับแต่ละค่าได้

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

จากที่นี่ฉันได้เรียนรู้ว่าเมื่อเปิดก๊อกน้ำ น้ำจะไหลด้วยความเร็วประมาณ 1 ลิตรต่อ 5 วินาที

แต่ในรูปแบบนี้สถิติคงดูไม่ค่อยสะดวกนัก Majordomo ยังมีความสามารถในการดูแผนภูมิการบริโภคตามวัน สัปดาห์ และเดือน ตัวอย่างเช่น นี่คือกราฟปริมาณการใช้เป็นแท่ง

การเชื่อมต่อมิเตอร์น้ำกับบ้านอัจฉริยะ

จนถึงตอนนี้ฉันมีข้อมูลเพียงหนึ่งสัปดาห์เท่านั้น ในหนึ่งเดือน กราฟนี้จะแสดงให้เห็นมากขึ้น โดยในแต่ละวันจะมีคอลัมน์แยกกัน รูปภาพเสียหายเล็กน้อยจากการปรับค่าที่ฉันป้อนด้วยตนเอง (คอลัมน์ที่ใหญ่ที่สุด) และยังไม่ชัดเจนว่าฉันตั้งค่าแรกสุดไม่ถูกต้องหรือน้อยกว่าเกือบลูกบาศก์หรือนี่เป็นข้อบกพร่องในเฟิร์มแวร์และไม่ได้นับลิตรทั้งหมด ต้องการเวลามากขึ้น

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

ข้อสรุป

วันนี้อพาร์ทเมนต์ของฉันฉลาดขึ้นเล็กน้อย ด้วยอุปกรณ์ขนาดเล็กเช่นนี้ ฉันจะตรวจสอบปริมาณการใช้น้ำในบ้านได้สะดวกยิ่งขึ้น ถ้าเมื่อก่อนโกรธที่ “เดือนหนึ่งเรากินน้ำเยอะอีกแล้ว” ทีนี้ก็หาที่มาของการบริโภคนี้ได้แล้ว

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

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

ในบทความฉันได้อธิบายเวอร์ชันอุปกรณ์ของฉันที่ใช้ ESP8266 ในความคิดของฉัน ฉันพบเฟิร์มแวร์ micropython เวอร์ชันที่น่าสนใจมากโดยใช้ coroutines - เรียบง่ายและสวยงาม ฉันพยายามอธิบายความแตกต่างและข้อบกพร่องหลายประการที่ฉันพบระหว่างการรณรงค์ บางทีฉันอาจอธิบายทุกอย่างอย่างละเอียดเกินไป โดยส่วนตัวแล้ว ในฐานะผู้อ่าน การข้ามสิ่งที่ไม่จำเป็นไปง่ายกว่าการมาคิดถึงสิ่งที่ยังไม่ได้พูดในภายหลัง

เช่นเคย ฉันเปิดรับคำวิจารณ์เชิงสร้างสรรค์

รหัสที่มา
วงจรและบอร์ด
เคสรุ่น

ที่มา: will.com

เพิ่มความคิดเห็น