2017年8月30日 星期三

MicroPython on ESP8266 (二十一) : 使用 ThingTweet 傳送推文

去年在玩 Arduino + ESP8266 時曾測試過利用 ThingSpeak App 裡面的 ThingTweet 功能, 透過 ESP8266 連結 WiFi 從 Arduino 傳送推文到自己的推特帳號, 今天我想改用 MicroPython on ESP8266 來泡製一番.

在此之前必須先註冊一個推特帳號, 然後在其 App 功能的 ThingTweet 下授權 ThingSpeak 利用自己的推特帳號發送推文, 產生一個 API Key 放在 HTTPS 要求中讓 ThingTweet 知道要傳送到哪一個推特帳號, 過程參考 :

使用 ESP8266 傳送 Twitter 訊息

本系列 MicroPython on ESP8266 測試文章參考 :

MicroPython on ESP8266 (一) : 燒錄韌體
MicroPython on ESP8266 (二) : 數值型別測試
MicroPython on ESP8266 (三) : 序列型別測試
MicroPython on ESP8266 (四) : 字典與集合型別測試
MicroPython on ESP8266 (五) : WiFi 連線與 WebREPL 測試
MicroPython on ESP8266 (六) : 檔案系統測試
MicroPython on ESP8266 (七) : 時間日期測試
MicroPython on ESP8266 (八) : GPIO 測試
MicroPython on ESP8266 (九) : PIR 紅外線移動偵測
MicroPython v1.9.1 版韌體測試
MicroPython on ESP8266 (十) : socket 模組測試
MicroPython on ESP8266 (十一) : urllib.urequest 模組測試
MicroPython on ESP8266 (十二) : urequests 模組測試
MicroPython on ESP8266 (十三) : DHT11 溫溼度感測器測試
MicroPython 使用 ampy 突然無法上傳檔案問題
MicroPython on ESP8266 (十四) : 網頁伺服器測試
WeMOS D1 Mini 開發板測試
MicroPython on ESP8266 (十五) : 光敏電阻與 ADC 測試
MicroPython on ESP8266 (十六) : 蜂鳴器測試
MicroPython on ESP8266 (十七) : 液晶顯示器 1602A 測試
MicroPython on ESP8266 (十八) : SSD1306 液晶顯示器測試
MicroPython v1.9.2 版釋出
MicroPython on ESP8266 (十九) : 太陽能測候站與移動偵測控制照明
MicroPython on ESP8266 (二十) : 從 ThingSpeak 讀取資料

MicroPython 文件參考 :

MicroPython tutorial for ESP8266  (官方教學)
http://docs.micropython.org/en/latest/micropython-esp8266.pdf
http://docs.micropython.org/en/latest/pyboard/library/usocket.html#class-socket
http://docs.micropython.org/en/v1.8.7/esp8266/library/usocket.html#module-usocket
https://gist.github.com/xyb/9a4c8d7fba92e6e3761a (驅動程式)


下面的 Python 程式碼複製了上面這篇文章的測試, 每分鐘向自己推特帳號傳送 'Hello! xx times.' 的推文. 注意, 推文有如下限制 :
  1. 推送週期必須大於 1 分鐘
  2. 前後兩篇推文內容不可相同
因此在下面測試中我用增量變數 i 來讓前後篇推文前後內容不同.


測試 1 : 每分鐘發送 'Hello! xx times' 推文 (GET 方法)

import urequests
import time

host='https://api.thingspeak.com'
api_key='QC73W06AWWX27AW1'
i=1

while True:
    msg='Hello! %d times.' %(i)
    url='%s/apps/thingtweet/1/statuses/update?api_key=%s&status=%s' \
         %(host,api_key,msg)
    try:
        r=urequests.get(url)
        print(type(r.text))
        if r.text == '1':
            print('Message is successfully sent.')
        else:
            print('Message-sending failed.')
    except:
        print('urequests.post() exception occurred!')
    i=i+1
    time.sleep(60)



注意, 這裡 urequests() 的傳回值 r 是一個物件, 其 text 屬性類型是 str (字串), 若傳送成功會傳回 '1', 可用來判別是否傳送成功, 例如 :

PYB: soft reboot
#11 ets_task(40100164, 3, 3fff837c, 4)
Connecting to AP ...
Connected:IP= 192.168.43.252
<class 'str'>
Message is successfully sent.

如果傳送失敗, text 屬性會傳回錯誤原因字串, 例如 :

PYB: soft reboot
#10 ets_task(40100164, 3, 3fff837c, 4)
Connecting to AP ...
Connected:IP= 192.168.43.252
Invalid API Key
Message-sending failed.

以上是使用 GET 方法提出 HTTPS 要求, 在 ThingTweet 網頁右下角的範例使用的是 POST 方法, 其 URL 為 "https://api.thingspeak.com/apps/thingtweet/1/statuses/update", 傳送參數為 api_key 與 status (即要傳送之訊息), 我參考下列這篇傳送 POST 要求 :

MicroPython on ESP8266 (十二) : urequests 模組測試


測試 2 : 每分鐘發送 'Hello! xx times' 推文 (POST 方法)     

#myapp.py
import urequests
import time

url='https://api.thingspeak.com/apps/thingtweet/1/statuses/update'
i=1

while True:
    msg='Hello! %d times.' %(i)
    data={'api_key':'QC73W06AWWX27AW1','status':msg}
    try:
        r=urequests.post(url,json=data)
        print(r.text)
        if r.text == '1':
            print('Message is successfully sent.')
        else:
            print('Message-sending failed.')
    except:
        print('urequests.post() exception occurred!')
    i=i+1
    time.sleep(60)

但測試結果卻是失敗, 原因是 Invalid API Key, 但我確定 key 是正確無誤的, why?


2017-08-30 補充 :


今天我參考了下面這篇文章修改上面測試 2 程式 :

HTTP POST with Micropython and an ESP8266

除了 body 有送參數外之外, 在 headers 中也傳送 api_key, 同時參數改用字串的 data 傳遞, 如下所示 :


測試 3 : 每分鐘發送 'Hello! xx times' 推文 (POST 方法)


import urequests
import time

url='https://api.thingspeak.com/apps/thingtweet/1/statuses/update'
api_key='BCXIQK4OZO55NP22'
headers={'api_key':api_key,'content-type':'application/json'}
i=1

while True:
    msg='Hello! %d times.' %(i)
    data="{'api_key':'%s','status':'%s'}" % (api_key,msg)
    print(data)
    try:
        r=urequests.post(url,data=data,headers=headers)
        print(r.text)
        if r.text == '1':
            print('Message is successfully sent.')
        else:
            print('Message-sending failed.')
    except:
        print('urequests.post() exception occurred!')
    i=i+1
    time.sleep(30)

但這樣仍然是不行, why?  

ó#5 ets_task(40100164, 3, 3fff837c, 4)
Connecting to AP ...
Connected:IP= 192.168.43.252
{'x-ha-access':'BCXIQK4OZO55NP22','status':'Hello! 1 times.'}

Message-sending failed.
{'x-ha-access':'BCXIQK4OZO55NP22','status':'Hello! 2 times.'}


我比較了 "HTTP POST with Micropython and an ESP8266" 這篇文章中的參數寫法, 發現差別是 data 參數中的屬性與值採用雙引號, 外面標示字串用的是單引號, 上面的測試 3 則是相反, 我抱著姑且一試的心情外單內雙, 測試結果 ~~~  It work!  程式如下 :


測試 4 : 每分鐘發送 'Hello! xx times' 推文 (POST 方法 : 用 data 參數)

import urequests
import time

url='https://api.thingspeak.com/apps/thingtweet/1/statuses/update'
api_key='BCXIQK4OZO55NP22'
headers={'api_key':api_key,'content-type':'application/json'}
i=1

while True:
    msg='Hello! %d times.' %(i)
    data='{"api_key":"%s","status":"%s"}' % (api_key,msg)    
    try:
        r=urequests.post(url,data=data,headers=headers)
        print(r.text)
        if r.text == '1':
            print('Message is successfully sent.')
        else:
            print('Message-sending failed.')
    except:
        print('urequests.post() exception occurred!')
    i=i+1
    time.sleep(60)

REPL 介面輸出正常 :


PYB: soft reboot
#9 ets_task(40100164, 3, 3fff837c, 4)
Connecting to AP ...
Connected:IP= 192.168.43.252
1
Message is successfully sent.

原來一切是引號搞的鬼!  那用 json 參數傳遞只要用雙引號也是 OK 的, 如下所示 :


測試 5 : 每分鐘發送 'Hello! xx times' 推文 (POST 方法 : 用 json 參數)


import urequests
import time

url='https://api.thingspeak.com/apps/thingtweet/1/statuses/update'
api_key='BCXIQK4OZO55NP22'
headers={'api_key':api_key,'content-type':'application/json'}
i=1

while True:
    msg='Hello! %d times.' %(i)
    data={"api_key":api_key,"status":msg}
    try:
        r=urequests.post(url,json=data,headers=headers)
        print(r.text)
        if r.text == '1':
            print('Message is successfully sent.')
        else:
            print('Message-sending failed.')
    except:
        print('urequests.post() exception occurred!')
    i=i+1
    time.sleep(60)

我試著將 headers 參數拿掉, 結果不行, 所以總結以上測試結果, 可以得到以下兩個結論 :

  1. 使用 post 方法時必須同時在 header 與 body 中傳送參數
  2. 參數不論是用字典物件 (json) 或字串 (data) 傳送, 屬性與值必須用雙引號

這項測試也讓我對 urequests 模組的運用有了更深的了解. 

沒有留言 :