零基礎物聯網開發,踩坑無數,得到這份寶典 | 原力計劃

作者 | Haor.L

責編 | 王曉曼

出品 | CSDN博客

筆者最近參加了校內的一場物聯網開發競賽,從零開始,踩坑無數,感覺很多時候事情都不像預料的一樣發展,離開了美好的IDE,太多事情要在板子上一步步摸索。運行失敗還好,運行成功但BUG了,簡直不知道從何查起


但認識了M5Stack簡單的線上系統,學習了MQTT,HTTP等數據傳輸方式,入門Arduino和MIrcopython等開發語言,接觸ESP32的板子,感受最深的一點就是,物聯網的准入門檻並沒有那麼高,成本也沒有那麼高,但這還是一片混沌未開的區域,進場者真的可以大有所爲,起碼方便自己的日常生活是綽綽有餘的。

總的來說,這次物聯網入坑,值得!

閱讀本文後你將收穫:

  • 瞭解M5STACK這款新興的片上系統

  • 瞭解這款板子的開發環境如何配置

  • 掌握ONENET數據傳輸

  • 掌握HTTP的GET方法

  • 會搭建筆者前幾天做的項目雛形:健康碼追蹤系統


開發準備


本博客作爲學習物聯網開發的筆記,也簡單的將自己的片上系統作爲例程寫出來。

  • ESP-IDF工具鏈配置:樂鑫文檔

  • UIflow在線IDE

  • M5STACK的官方開發文檔( 很全,如果你也打算使用M5STACK系列的開發板,強烈建議從此入門)

  • 本博客項目的代碼倉庫(歡迎賞星):https://github.com/haoruilee/M5Stack_Healthy_code_tracer


M5STACK簡介


M5STACK是一款對初學者非常友好的ESP32開發板,可以用Micropython和C編程,也可以用很簡單的拖拽式編程,擁有龐大的官方文檔,只是國內只在最近開始流行,油管和B站上都有官方的教學視頻,也在常常更新,很有創造力的一個產品。

結構上主要分爲Core和Unit,Core作爲核心控件,Unit作爲傳感器採集數據。

下圖圖就是筆者自己做的產品雛形,中間的小屏幕是Core,作爲主控,周圍有GPS和RFID,攝像頭等單元用來採集數據。

設計雛形:


由於所有的M5產品都預設了樂高塊,所有你可以把他們拼在樂高模組上,油管上還有不少人用它做樂高機器人,確實是很有創意的一款產品,顏值也不錯,所以筆者選擇它用來開發。


環境配置


UIFlow環境

在線編程地址:UIFlow

前幾天GIthub發佈了遠程編譯器Codespace,可以看出遠程編譯確實是大勢所趨。

這裏不得不說UIFlow把物聯網的門檻大大降低了,筆者配置ESP-IDF用了將近一天,而使用UIFlow可以免去一切環境配置的痛苦。

支持拖拽編程,可視化UI設計,自帶例程,確實是良心產品。


ESP-IDF環境

方法一

筆者配置ESP-IDF工具鏈時,一開始是使用Windows系統的工具安裝器。

ESP-IDF 工具安裝器可在“開始”菜單中,創建一個打開ESP-IDF 命令提示符窗口的快捷方式。本快捷方式可以打開 Windows 命令提示符(即 cmd.exe),並運行 export.bat 腳本以設置各環境變量(比如 PATH,IDF_PATH 等)。

(不得不說,這個安裝器的健壯性非常差!!)

安裝心得:上網技巧+把系統的PATH重新檢查一遍,有些卸載殘餘的PATH會導致玄學問題!

方法二(更推薦)

宇宙第一VS code中有插件 ESPRESSIF

可以更清楚每時每刻在幹什麼,也更清楚出了BUG去哪裏修補。



感知模塊


M5開發了很多環境傳感器,包括溫溼度,人體感應,RFID,攝像頭等等,在官方開發文檔上也都給出了相關例程和GIthub的源碼鏈接。

筆者自己的項目使用的是RFID、GPS和Camera模塊,其中除了Camera只能用ESP-IDF編程,其他單元都支持在UIFlow上在線編程,再加上,Micropython確實比C++舒服太多。


網絡連接


Wi-Fi鏈接

M5Stack已經自己預置了Wi-Fi鏈接,開機就是,不過在Mircopython裏寫起來也很簡單,下面是一個範例:

import network
SSID="YOUR-WIFI-NAME"
PASSWORD="YOUR-WIFI-PASSWORD"
wlan=None
s=None

def connectWifi(ssid,passwd):
  '''
  連接指定wifi
  '''
  global wlan
  wlan=network.WLAN(network.STA_IF)
  wlan.active(True)
  wlan.disconnect()
  wlan.connect(ssid,passwd)
  while(wlan.ifconfig()[0]=='0.0.0.0'):
    time.sleep(1)
  return True

使用Mircoython的Wi-Fi模塊,connect(ssid,password),就可以進入可愛的Wi-Fi連接界面了,M5GO的封裝還是不錯的。

NB-IOT
IOT模組需要額外配一張NB卡,某寶20就可以拿下一年500M流量,不過筆者沒有繼續嘗試,已經有可愛的M5GO和穩定的Wi-Fi,就沒探索用3G進行通訊。


HTTP通訊


接下來介紹物聯網的精髓,“聯網”,採集到了數據,那數據上報和傳輸是重中之重。

筆者覺得HTTP是最好理解,好入門的通訊協議,這裏也先介紹這種方法。

建議參考視頻:B站:接入中國移動ONENET平臺

筆者嘗試過諸如阿里雲和一些外國的平臺,最後發現都不如中國移動專門爲物聯網開發的ONENET,一來穩定,不用擔心服務商跑路,而來阿里雲顯得太過臃腫,對入門者很不友好,最終選擇了ONENET,他的HTTP封裝好了很好看的數據流模板,如:

  • 位置信息可視化:

  • 自動折線圖

POST上傳數據

創建流模板

首先需要創建一個數據流模板,用於接收傳過來的數據:


發送請求

請求方式:POST

URL:http://api.heclouds.com/devices/device_id/datapoints

其中,device_id:需要替換爲設備ID

注意:ONENET默認post的數據叫Datastreams,參數配置見表:


請求實例:

{
    "datastreams": [{
            "id": "temperature",
            "datapoints": [{
                    "at": "2013-04-22T00:35:43",
                    "value": "bacd"
                },
                {
                    "at": "2013-04-22T00:55:43",
                    "value": 84
                }
            ]
        },
        {
            "id": "key",
            "datapoints": [{
                    "at": "2013-04-22T00:35:43",
                    "value": {
                        "x": 123,
                        "y": 123.994
                    }
                },
                {
                    "at": "2013-04-22T00:35:43",
                    "value": 23.001
                }
            ]
        }
    ]
}

使用ONENET的模擬API調用可以快速熟悉數據的模式:

其中,URL的device_id,和下面的API_key換成自己設備的,可以在設備列表找到,就可以往自己設備的數據流模板傳一個值爲3的value。


Micropython用HTTP

UIFlow裏自帶了HTTP模塊,但是很玄學,很難用!

這裏筆者用Micropython自己寫了一份HTTP的傳輸:

def http_put_data(data):
 #post data into onenet
    url='http://api.heclouds.com/devices/'+DEVICE_ID+'/datapoints'
    values={'datastreams':[{"id":"temperature","datapoints":[{"value":data}]}]}
    jdata = json.dumps(values)                 
    r=urequests.post(url,data=jdata,headers={"api-key":API_KEY})
    return r

親測還是很好用的,可能是UIFlow自己的json封裝比較奇怪?

使用模組交互的數據傳輸

既然M5STACK可以同時使用多個模組,那麼自然也就可以使用模組來控制數據傳輸:

如:使用RFID卡,控制POST:

rfid0 = unit.get(unit.RFID, unit.PORTA)

while(True):
  if rfid0.isCardOn():
    rsp = http_put_data(12)
    emoji0.show_map([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,1,1,1,1,1,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]], 0xff0000)
  else:
    #wlan.disconnect()
    #wlan.active(False)
    emoji0.show_map([[0,0,0,1,0,0,0],[0,0,0,1,0,0,0],[0,0,0,1,0,0,0],[0,0,0,1,0,0,0],[0,0,0,1,0,0,0],[0,0,0,1,0,0,0],[0,0,0,1,0,0,0]], 0xff0000)
  wait(1)

第一行的rfid0 = unit.get(unit.RFID, unit.PORTA)是指定從PORTA讀取RFID的射頻信息


使用HTTP的GET獲取b站信息


有POST就要有GET,這裏介紹一個B站大佬“正負加減”的例程,獲取自己的粉絲數,裏面講解也很細緻,大佬也是老Geek了,關注不虧!

UIFlow的GET結構:

核心思想是知道哪個key是follow,可以在F5審查源代碼找到:


此處的URL是

http://api.bilibili.com/x/relation/stat?vmid=99566555

UP和DOWN是控制RGB等的函數。



我的片上系統設計全過程


設計初衷

在疫情管控最嚴格的的時候,經常能看到觸目驚心的“尋找x月x日乘坐xxxx的乘客”,如果能追尋乘坐公共交通者的足跡,會給疫情管控帶來很大便利。

設計方法

筆者希望結合GPS,RFID和攝像頭功能,做個車載的識別信息-上報信息的小系統。

具體涉及到二維碼識別,RFID識別,GPS獲取和HTTP上報。

然而,這個設計一開始就遇到了難題。網上購買的測溫槍,他的藍牙數據我無法解包,好像是和騰訊連連有自己的相關配置。


騰訊連連界面:

既然騰訊連連已經寫到那麼好了,那就不搶他的飯碗了。

既然沒辦法解析內容,那下面筆者將用ENV單元採集的環境溫度代替乘客溫度進行收發。

分模塊實現

RFID控制塊

首先聲明RFID模塊的串口:

rfid0 = unit.get(unit.RFID, unit.PORTA)

該段指從A口串入RFID模組

【注:M5STACK使用顏色標明瞭A,B,C三個Grove口,見下圖】

上圖中RFID的串口是紅色,就對應到M5GO左側的紅色接口:


GRIVE HUB

由於筆者同時還需要使用紅色串口的ENV單元,因此額外購買了GROVE HUB


這樣就有兩個A口了。

訪問設置時不需要加以區分,都從PORTA引入:

rfid0 = unit.get(unit.RFID, unit.PORTA)
env0 = unit.get(unit.ENV, unit.PORTA)

使用Emoji模塊做可視化:

筆者使用循環判斷是否有卡片接近,無則顯示待機Emoji,有則顯示✅

 #循環判斷是否有卡片接近
  while(True):
    #熄滅RGB燈
    rgb.setBrightness(0)
    #清空Emoji顯示
    emoji0.show_map([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]], 0x000000)
    #判斷是否有卡片接近
    if rfid0.isCardOn():
      #有卡片接近顯示對號
      emoji0.show_map([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,1],[0,0,0,0,0,1,1],[1,0,0,0,1,1,0],[0,1,1,1,1,0,0],[0,0,1,1,0,0,0]], 0x33ff33)
      #發送RFID讀取到的卡片ID
      rsp_RFID = http_put_RFID((str(rfid0.readUid())))
      #完成後指示燈變綠
      rgb.setColorAll(0x33ff33)
      rgb.setBrightness(10)
    else:
      #可選是否斷開wifi
      #wlan.disconnect()
      #wlan.active(False)
      #等待卡片接近時,Emoji展示"。。。"
      emoji0.show_map([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,1,0,1,0,1,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]], 0x000000)
    wait(1)

這裏的Emoji模塊是先在UIFlow裏敲好,再進Micropython裏查看的。

HTTP傳輸塊

HTTP傳輸在上文已經講了不少,這裏優先介紹GPS的傳輸:

GPS的命令也是要加“value:”的,形如value:{“lon”:lon,“lat”:lat},這個地方一開始把筆者坑的不輕

def http_put_location(lon,lat):
      '''
      傳輸地理位置數據點至ONENET平臺的location數據流
      lon:longitude,經度
      lat: latitude,緯度
      url:http的post地址
      values:請參考https://open.iot.10086.cn/doc/multiprotocol/  文檔中的"HTTP協議上傳數據點模塊,配置json格式信息"
      API_KEY:設備發送HTTP請求的證書,請參考https://open.iot.10086.cn/doc/multiprotocol/book/develop/http/api/api-usage.html  文檔中的"鑑權說明"
      '''
      url='http://api.heclouds.com/devices/YOUR-DEVICE-ID/datapoints'
      values={'datastreams':[{"id":"location","datapoints":[{"value":{"lon":lon,"lat":lat}}]}]}
      jdata = json.dumps(values)                 
      r=urequests.post(url,data=jdata,headers={"api-key":API_KEY})
      return r

這裏請把YOUR-DEVICE-ID替換爲自己的數據流中設備編號,參考ONENET手冊。

Wi-Fi連接塊

UIflow裏封裝了很好的Wi-Fi-connect:

wifiCfg.doConnect(SSID, PASSWORD)

如果你的Micropython裏沒有一鍵Wi-Fi連接,筆者自己也實現了一個:

  def connectWifi(ssid,passwd):
    '''
    如果您的Micropython不攜帶WifiCfg.doconnect,請參考本函數
    函數作用:連接至名稱爲SSID,密碼爲passwd的wifi
    '''
    global wlan
    wlan=network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.disconnect()
    wlan.connect(ssid,passwd)
    while(wlan.ifconfig()[0]=='0.0.0.0'):
      time.sleep(1)
    return True

二維碼識別塊

  • 本部分使用ESP-IDF編程

  • 使用的攝像頭是M5CAMERA

  • 屬性是OV2640

參考官方例程

  //拍攝
camera_fb_t* esp_camera_fb_get()
{
    if (s_state == NULL) {
        return NULL;
    }
    if(!I2S0.conf.rx_start) {
        if(s_state->config.fb_count > 1) {
            ESP_LOGD(TAG, "i2s_run");
        }
        if (i2s_run() != 0) {
            return NULL;
        }
    }
    if(s_state->config.fb_count == 1) {
        xSemaphoreTake(s_state->frame_ready, portMAX_DELAY);
    }
    if(s_state->config.fb_count == 1) {
        return (camera_fb_t*)s_state->fb;
    }
    camera_fb_int_t * fb = NULL;
    if(s_state->fb_out) {
        xQueueReceive(s_state->fb_out, &fb, portMAX_DELAY);
    }
    return (camera_fb_t*)fb;
}

//二維碼識別
void qr_recoginze(void *pdata) {

    camera_fb_t *camera_config = pdata;

    if(pdata==NULL)
    {
        ESP_LOGI(TAG,"Camera Size err");
        return;
    }

    struct quirc *q;
    struct quirc_data qd;
    uint8_t *image;
    q = quirc_new();

    if (!q) {
        printf("can't create quirc object\r\n");
        vTaskDelete(NULL) ;
    }
    //printf("begin to quirc_resize\r\n");

    if (quirc_resize(q, camera_config->width, camera_config->height)< 0)
    {
        printf("quirc_resize err\r\n");
        quirc_destroy(q);
        vTaskDelete(NULL) ;
    }

    image = quirc_begin(q, NULL, NULL);
    memcpy(image, camera_config->buf, camera_config->len);
    quirc_end(q);

    int id_count = quirc_count(q);
    if (id_count == 0) {
        quirc_destroy(q);
        return;
    }

    struct quirc_code code;
    quirc_extract(q, 0, &code);
    quirc_decode(&code, &qd);
    dump_info(q);
    quirc_destroy(q);
}

這裏依賴了太多官方庫…建議去代碼倉庫翻一翻。

K210的二維碼識別

上文是ESP32類型的開發板識別代碼,可以看到非常繁瑣,改起來簡直要了老命

使用Micropython的話,代碼就變的可愛多了~

【此部分使用Maxipy編程】

 import sensor
import image
import lcd
import time

clock = time.clock()
lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1)
sensor.run(1)
sensor.skip_frames(30)
while True:
    clock.tick()
    img = sensor.snapshot()
    res = img.find_qrcodes()
    fps =clock.fps()
    if len(res) > 0:
        img.draw_string(2,2, res[0].payload(), color=(0,128,0), scale=2)
        print(res[0].payload())
    lcd.display(img)

這裏Maxipy的官方文檔還給出了修正圖像的方法:

如果使用了鏡頭,畫面會有扭曲,需要矯正畫面使用 lens_corr 函數來矯正,比如 2.8mm, img.lens_corr(1.8)

(沒事翻一翻這個Maxipy的文檔還是很有啓發的,大概兩小時就能通讀一遍)

GPS傳輸塊

室內經常沒有信號(實測室外也很少有…)因此在傳遞GPS的時候筆者額外設計了防止無信號的代碼。

GPS的http傳輸函數已經在上文講了,此處和RFID卡片控制進行結合:

 if rfid0.isCardOn():
      #有卡片接近顯示對號
      emoji0.show_map([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,1],[0,0,0,0,0,1,1],[1,0,0,0,1,1,0],[0,1,1,1,1,0,0],[0,0,1,1,0,0,0]], 0x33ff33)
      #發送RFID讀取到的卡片ID
      rsp_RFID = http_put_RFID((str(rfid0.readUid())))
      #防止測試時無信號
      if str(gps0.pos_quality) != "1" and str(gps0.pos_quality) != "6":
        lon=116.39137751349433
        lat=39.8969585128568
      else:
        #默認北京
        lon=gps0.longitude
        lat=gps0.latitude
      try:
        rsp_LOCATION=http_put_location(float(lon),float(lat))
      except:
        emoji0.show_map([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,1],[0,0,0,0,0,1,1],[1,0,0,0,1,1,0],[0,1,1,1,1,0,0],[0,0,1,1,0,0,0]], 0xff0000)
      #傳輸溫度
      rsp = http_put_data(env0.temperature)
      #完成後指示燈變綠
      rgb.setColorAll(0x33ff33)
      rgb.setBrightness(10)
    else:
      #可選是否斷開wifi
      #wlan.disconnect()
      #wlan.active(False)
      #等待卡片接近時,Emoji展示"。。。"
      emoji0.show_map([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,1,0,1,0,1,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]], 0x000000)
    wait(1)



後記


參賽時間倉促,沒能完全搞明白每個引腳的用途,沒有好好觸摸一遍C和開發板,沒有用NB-IOT通訊。沒有做藍牙通訊解包…還是有很多遺憾的,還好設備還在,可以繼續探索物聯網的神奇。
成果效果:

版權聲明:本文爲CSDN博主「Haor.L」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。





原文鏈接:https://blog.csdn.net/weixin_46233323/article/details/106054434」

更多精彩推薦
☞程序員在家辦公沒顯示屏,我被領導鄙視了
☞Get!讀懂數據科學和機器學習,看這文就夠了
☞深度剖析數據庫國產化遷移之路
☞Go遠超Python,機器學習人才極度稀缺,全球16,655位程序員告訴你這些真相
☞我佛了!用KNN實現驗證碼識別,又 Get 到一招
☞超級賬本Hyperledger Fabric中的Protobuf到底是什麼?
你點的每個“在看”,我都認真當成了喜歡
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章