iT邦幫忙

2022 iThome 鐵人賽

DAY 21
2
自我挑戰組

PixelBit 可以這樣玩!系列 第 21

(Day 21)Arduino 簡化上下位機通訊(Part 1)

  • 分享至 

  • xImage
  •  

在前幾篇文章中我們有嘗試使用 PixelBit 的 ESP32 以及 ATmega328p 互相通訊,但有些讀者認為有些複雜與凌亂,這次我們將會嘗試簡化 UART 之間的通訊方式,實作相關 Library,讓大家能更容易使用,在不降低效率前提下讓程式碼可讀性更高。

什麼是上、下位機?

概念上如下

  • 上位機:主要作為系統的規劃控制,屬於決策層。
  • 下位機:主要完成系統規劃層下達的任務。

為何要使用上、下位機方式?

時常在做專案時會因為硬體限制,無法透過單一 MCU 操控所有硬體,例如:

  • 使用ESP32 Cam 開發版,因為大部分 GPIO 都已用在 Camera 上,剩餘 GPIO 非常少,無法整合其他功能。
  • 使用 Raspberry Pi 作為主要系統,卻因為 IO 速度不夠快或 Clock 不夠準,無法勝任需要高速通訊的感測器。

PixelBit 就是屬於第一種情況,需要透過其他方式擴展 IO 數量,常用的擴展方式有幾種:

  • Shift Register(ex: 74HC595)
  • I2C expand(ex: TCA9535)
  • 外掛 MCU

PixelBit 擴展 IO 方案選擇外掛一顆 MCU(ATmega328P),讓我們多了一般 IO 、PWM IO、I2C、SPI、ADC、Interrupt IO,算是一種”都來一點”的概念,優點是可以使用既有的 Library(ex: DHT11 22 Library) 透過各種通訊方式搭配其他感測器,在效率方面也會比較好,讓兩邊 MCU 能專心做各自的工作,缺點是使用者需要寫兩套程式(ESP32、ATmega328P),並透過 UART 互相溝通,當專案功能越多越複雜時對於時序觀念較不熟悉的開發者就會較為困難。

目標

這次我們的目標就是要簡化 ESP32 與 ATmega328P 之間的各種通訊問題,解決問題大致如下:

  • 提高通訊效率,盡可能將時間留給其他程序。
  • 盡可能減少其他程式碼延遲帶來的影響。
  • 提高程式碼可讀性,讓使用更方便且直覺。
  • 方便結合各種使用 UART 通訊的模組。

概念

  • 包裝成 Library。
  • 使用狀態機切分時序。
  • 使用匿名函式簡化程式碼。

使用情境與範例

假設需求

  1. ATmega328P 連接 DHT11溫溼度感測器,ESP32 每秒向 ATmega328P 讀取溫溼度資料並透過 WiFi 上傳雲端。
  2. ATmega328P 連接無段開關,按下開關後通知 ESP32 拍照並透過 WiFi 上傳雲端。
  3. ESP32 作為 Web Server 收到使用者輸入馬達速度,控制 ATmega328P 上的馬達驅動板功率。

基於以上情境範例程式碼如下:

ESP32 code

#include "CircusUart.h"

CircusUart uart(Serial);

void setup()
{
    Serial.begin(57600);

    uart.on("Temp", ':', [](const char *temp) {
        // temp = "24.6"
        // TODO: upload temperature to cloud
    });

    uart.on("Humi", ':', [](const char *humi) {
        // humi = "68.7"
        // TODO: upload humidity to cloud
    });

    uart.on("SW_A", '\0', [](const char *data) {       
        // TODO: Get pictures from camera and upload to cloud
    });
}

void loop()
{
    uart.loop();

    if (收到使用者輸入馬達速度) {
        uart.send("Motor", ':', "128");
    }

	static uint32_t timer = 0;
    if (millis() > timer) {
        timer = millis() + 1000;
        uart.send("Temp");
		uart.send("Humi");
    }
}

ATmega328P code

#include "CircusUart.h"

CircusUart uart(Serial);

void setup()
{
    Serial.begin(57600);

    uart.on("Temp", '\0', [](const char *temp) {
        uart.send("Temp", ':', "24.6");
    });

    uart.on("Humi", ':', [](const char *humi) {
        uart.send("Humi", ':', "68.7");
    });

    uart.on("Motor", ':', [](const char *speed) {
        // speed = "128"
        // TODO: Set Motor Speed 128
    });
}

void loop()
{
    uart.loop();

    if (digitalRead(SWA_PIN)) {
        uart.send("SW_A");
    }
}

Library 內容也很簡單,我們明天再講,今天禮拜天好累明天見

更多有趣系列教學文章


上一篇
(Day 20)整合 LINE Notify 即時拍照通知
下一篇
(Day 22)Arduino 簡化上下位機通訊(Part 2)
系列文
PixelBit 可以這樣玩!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言