IoTTalk 物聯網應用操作入門手冊     -- by tsaiwn@cs.nctu.edu.tw 交大資工 蔡文能        
IoTtalk 使用者詳細使用手冊:
      http://liny.cs.nctu.edu.tw/#IoTtalk   (林一平教授網頁)

      點入後在 Document 下方, 有中文版和英文版;     or     點這看更多手冊
* 如果想連接"真"的設備, 建議也 先看看 ArduTalk 操作手冊
以便多了解一些概念。
    裡面的"安裝手冊"是告訴你如何把程式碼燒錄到 NodeMCU V2/V3 內, "操作手冊"則是教你如何在 IoTtalk 的專案使用NodeMCU設備。
    如果你還不清楚啥是 NodeMCU, 可以點這裡先往下跳到後面看看NodeMCU/ESP8266相關資料後, 再跳回這繼續看。
  ** 點這到後面(三)關於Joint函數   點這到(四)如何用Python寫Dummy Device + (五) + (六)    
  ** 點這到後面(七)新增可用設備   點這到(八)如何用 IoTtalk 連接真實設備...     (九) 搭配 Raspberry Pi 3 B+     (十) 搭配 Rabboni 小玩具
注意NodeMCU V2 開發板比較小方便使用麵包板, 但是經實測發現對 D0 做 PWM 輸出會讓網路斷線,
    所以, 如果用 NodeMCU V2 開發板, 要燒錄不同的程式碼, 請點這看關於我改過的 t7 版本

* 如果你對這 IoTTalk 物聯網應用平台到底怎麼做出來的有興趣, 可以點這到 iottalk.vip/0 看看開發者手冊
    ** 如果你不太懂 Python 和 Flask 但想快速懂一些, 可以點這開新窗看看我寫的如何快速用 Python + Flask 弄出一個網站
在, 好玩的IoT物聯網應用 ..
要.. 開始囉 . . .
可以用哪個 IoTtalk Server 來體驗物聯網(IoT)的實驗操作?
★★★ 如果你在資策會上課:
     請點這 http://125.227.255.81 連到 IoT 互聯網實驗網站 (這機器 在資策會)
    或 http://192.168.20.101/ (資策會內部網路; 注意上面 Public IP 可能被防火牆擋住)

 萬一不能用, 用以下給 Guest 測試的:點這 https://demo.iottalk.tw 連到交大 IoT 互聯網實驗網站 (這機器 140.113.199.200 在交大電算中心)
 * 或 另一部機器  test.iottalk.tw  (注意如果用到 NodeMCU 則要注意使用的 IoTtalk Server IP)

(一)用電腦 或 手機控制開關燈, 虛擬燈泡(Bulb)可以在自己或別人電腦上, 甚至也可以在手機上
準備建立一個 IoTtalk 物聯網應用專案(Project)
(1)點交大 http://demo.iottalk.tw 連到物聯網互動練習網站後,
      或 點 http://125.227.255.81 連到物聯網互動練習網站後,
      或資策會內網: http://192.168.20.101/
      會出現如下網頁, 在網頁內, 點那個 Project
      此時會彈出新網頁(...:7788/connection),
      此時會彈出新網頁,

      此時會彈出新網頁, (說三次免得有些人沒看到:-)
請在新窗點 Select a project 然後可選 add project 建立新專案,或選以前你建立過的專案名稱。



(2)在彈出的小窗輸入專案名稱(Project Name), 和 對應這專案的密碼 (以後開啟這專案要用)
   建議專案名稱用你學號後三碼開頭比較能認出是你做的專案,
   密碼要打自己容易記住不會忘掉,但別人又不容易猜到的 :-)

  * 也可以不打密碼免得忘掉, 但這樣別人也可點入把你專案砍掉, 不過那也沒關係, 反正不是很重要 :-)

(3)這時專案是空的, 左上角會出現你專案名稱
   要開始加入這專案的設備(Model), 滑鼠移動到 Model 上, 捲動滾輪選取設備
   選錯了還可以砍掉(Delete), 所以不用怕選錯 :-)
   以下我們先加入一個燈泡 (Bulb):


(4)為選到的設備勾選零件(就是選擇要哪些功能 Feature)
   這燈泡有兩個零件(功能,Feature)可以選: Color-O (顏色) 和  Luminance (亮度, Lumen=流明)
   至少要選一樣後 Save 才會出現設備的圖示; 如果設備只有單一功能則會自動出現在視窗內;
   之後隨時可以點圖示左上角的齒輪重新設定零件, 所以也是不用怕選錯 :-)
   以下我們先只選 Luminance (亮度) 用來控制燈泡的亮度:



(5)接下來, 再生出一個設備(Model) "Remote_Control" (遙控器)
   剛才那 Bulb 燈炮是 "輸出" 設備 (就是會被控制的),
   再來我們加入一個"輸入"(Input)設備, 就是讓使用者可以操作 "輸入" 一些資料(例如按開關, 旋轉轉輪, 甚至打字等);現在來加入遙控器:
   遙控器功能很多, 可以只選轉輪(Knob)一個即可, 或你要順便選一個開關(Switch)也不錯 :-)
   選定之後, 點按 Save 就會在視窗內左邊出現該設備(Remote Control)
 
 現在應該有發現: 左邊會放"輸入"設備, 右邊會放 "輸出"設備 !
   在上圖中, 燈泡設備圖示的右上方格子內出現26.Bulb表示已經關聯到 26號燈泡,
   這 26 號燈泡可能是你之前打開的, 也可能是別人打開的燈泡,
   你隨時可以點該處重新關聯到別的燈泡 !

(6)把遙控器的零件和燈泡的零件連接起來
   第一個連接點會自動叫做 Join 1, 在連接點上可以寫簡單 Python 程式碼做處理
   不過, 通常不必寫就可以使用 :-)
   連接好之後記得要按右上角的 Save
 
    在上圖中, 有沒注意到: 有編號 1, 2, 3, 5, 奇怪怎沒有 4 ? 
    因為 4 是要選擇把 Bulb 關聯到一個可用的 Bulb 啦。

(7)現在物聯網專案已經完成, 剩下的是要把代表設備的"設備圖示"關聯到真正的設備;
   (a)請到同一部Server(例如demo.iottalk.tw)物聯網首頁點 Bulb 開啟一個網頁燈泡, 注意上方的編號;
      就是到剛才一開始開啟的網頁點 Bulb, 當然現在另外開新網頁只要連到同一部伺服器(Server)也是 OK 啦 !
      如果你一開始還沒建立專案就先開啟一個 Bulb 備用也可以, 順序不重要, 重要的是該 Bulb 的編號 !
   (b)用手機的 Chrome 或任意瀏覽器連到同一部伺服器(例如 demo.iottalk.tw)然後點那個 Remote_Control(Mobile)
      也是注意此遙控器的編號 
      ** 沒手機也可以在電腦上點開那 Remote_Control(Mobile) 也是注意其編號
   (c)把燈泡窗拉開到永遠看得見, 若用電腦點開 Remote_Control(Mobile), 也拉開方便控制
   (d)接著可以回到你剛開的專案(Project)視窗, 準備把設備關聯到代表設備的"設備圖示"
      就是告訴電腦: 遙控器是在誰手上的遙控器, 燈炮又是在哪邊的燈炮(Bulb)


(8)更改設備屬性(功能)以及改變設備的關聯對象
     ** 隨時可以點齒輪更改該設備的零件
     ** 也可以隨時重新做關聯
   (a)你可以隨時點設備左上角的齒輪改變設備的屬性(功能), 也可以隨時把專案內設備關聯到不同的"設備"
     注意: 改變屬性功能之後, 有可能需要重新連連看連線(如果你砍了某功能則其連線也會自動被砍掉, 廢話 :-) 
   (b)針對每個設備, 點它齒輪右邊的方格, 然後在右半邊視窗點選你要連接的設備(例如燈泡26.Bulb)
     注意關聯之後, 在該方格內會用藍色字體顯示其關聯到的設備編號。
   (c)為了確定有弄懂, 可以請兩位同學幫你測試, 要他們用手機或電腦連到同一部 Server,
      然後請他們也點選那個 Remote_Control(Mobile)  (注意, 要有 Mobile 那項喔 !)
      當然也要請他們把編號告訴你, 讓你到你的專案視窗中點 Remote_Control 圖示齒輪右方的格子,
      先改"關聯"到第一位同學, 請他開關燈, 再次改"關聯"到另一位同學, 也請他開關燈,
      確定你有弄懂可以在你的物聯網專案(Project)快速切換設備物件的真正關聯對象 !
   (d)你也可以讓一位同學的手機用那 Remote_Control(Mobile) 控制另一位同學手機上的 Bulb 喔! 
   (e)注意 ! 要連到同一部 IoTtalk 的 server 才有辦法互相控制喔 !
      老是有同學連到不同 Server 才來問說怎都沒效果 :-( !!! 

                  >
(9)測試、模擬輸入、監看進出連結點(Join)的資料
   (a)現在, 可以用手機上的 Remote_Control(Mobile)控制電腦上的燈泡(Bulb),
   (b)也可改把燈泡關聯到另一位同學手機上的燈泡, 變成用手機遙控器控制另一位同學手機上的燈泡 :-)
***(c)模擬輸入: 即使沒關聯到輸入設備, 仍可用模擬方式觀看燈泡開關(ㄚ當然輸出設備一定要關聯啦) !
           即使沒關聯到輸入設備, 仍可用模擬方式觀看燈泡開關(ㄚ當然輸出設備一定要關聯啦) !
           即使沒關聯到輸入設備, 仍可用模擬方式觀看燈泡開關(ㄚ當然輸出設備一定要關聯啦),
           請參看以下圖片右上角, 在圖片中的 Simulation 是點亮的 (ON綠燈) !
   (d)監看資料: 還有, 用滑鼠右鍵點 Join 點, 可以監看進出該節點的資料(在右半邊視窗)
 出問題 !? 出現黃色驚嘆號 !  或 Flush 右邊綠燈變紅燈 怎麼辦?!怎麼辦 ?!
 出問題 !? 出現黃色驚嘆號 !  或 Flush 右邊綠燈變紅燈 怎麼辦?!怎麼辦 ?!
 出問題 !? 出現黃色驚嘆號 !  或 Flush 右邊綠燈變紅燈 怎麼辦?!怎麼辦 ?!
   (e)刷新 Flush: 萬一出問題出現黃色驚嘆號, 可以點驚嘆號大略看看是啥錯誤,
      不過通常點一下 Flush 讓它紅燈又回到綠燈阿就可解決問題 !
      參看下圖:  (注意圖片中左上角的 Flush 以及右上角的 Simulation 之說明 ! )

(二)用你手機控制丟球, 球建議選 Ball-Throw2
      建立一個新專案(Project), 或用原專案修改也可以, 一個專案可以有無數個輸入設備和無數個輸出設備 !
      * 這次輸入設備改選 Smartphone (智慧型手機),
       Smartphone 智慧型手機的功能很多, 這次只要選加速器(Accelerator)一項功能即可;
      你也可以故意把智慧型手機的加速器連到燈泡控制看會怎樣 !? (hint: 到時候甩動手機會讓燈炮會忽明忽暗)
      不過這次主要是要連到 Ball-throw2 這個丟球小遊戲(球打到木板上紅色那處會發聲音說 Good Job)
      當然你要回首頁 點 Ball-throw2 開啟一個丟球畫面來用(在往下的 VPython List: 大約第 10 個),
      下圖中已經關聯到 60.Ball-Throw2 (加入Ball-Throw2 時如只有一個會自動關聯)
      也是要找一位同學用手機連入同樣網站, 這次點 Smartphone 注意編號, 例如下圖:
      已經把 Smartphone 關聯到 61.Smartphone
  ** 補充說明:
      在 IoTtalk 系統首頁 VPython List 之下的視覺化網頁應用(Visual Web Application)
      都是用 VPython 視覺化的語言(其實是程式庫)寫的應用 !
      在 VPython 的首頁就說:
      VPython 是 3D Programming for Ordinary Mortals (凡人用的 3D 程式設計)

      * 想體驗 VPython programming,
          請點這連到 https://EduTalk4.NCTU.edu.tw/ (免費註冊 Sign up)

      * 想看更多 VPython programming 範例,
          請點這連到 VPython 官方的 Examples

      VPythonPython中一個稱為 Visual 的模組所擴充而來。其原始架構是由Carnegie Mellon University的大學生 David Scherer 在2000年開發出來,核心為Python 這個易學的程式語言,以其建構的三維繪圖模組來描述虛擬的物理世界,讓使用者可以輕易的利用程式來做三 維空間的物理繪圖,具備有動態的時間演進和不同的視角觀察,讓學生可以更直覺的利用程式來學習物理,尤其是需要視覺化和動態觀察的物理現象。

      VPython 目前有多個版本被廣泛使用,除了原本的單機版的 VPython 之外,另有在網頁上可執行的GlowScript,與新世代的vpython-jupyter計畫(就是提供 API 讓 VPython 可以在> 網頁執行,在 IoTtalk 系統裡面的 VPython List 各應用 以及在 EduTalk4.NCTU.edu.tw 都是在瀏覽器執行 VPython 程式碼)。

 
   
如果發現手機不動也會自動丟球,
先看看是否你點亮了模擬(Simulation), 如沒有, 那可能手機加速度的取值設定被改錯了,
正常狀況預設是採用"變動量"(variant)抓取三軸加速度, 看下圖:
  
萬一不小心被改為 "sample" 取樣當時看到的值, 則可能導致手機不動, 但球卻自己亂丟 !    
注意.. 注意..
這時手機很耗電 ! 還有.. 
注意千萬不要把你手機丟出去, 丟出去摔壞了別怪我沒先講喔 :-) 

**思考: 如何用"手機"控制開關前面(一)的 Bulb 燈泡?
     例如: 手機正面朝上關燈, 正面朝下時開燈, 或反過來 !

(三)也可以玩三個人分別用手機控制同一個燈(Bulb)的顏色
一個控制紅色
一個控制綠色
一個控制藍色

請參考以下各圖的連接方式, (a)注意專案中, 選用三次的 Remote_Control, 各自都只勾選一個 Knob 就 Save, (b)專案中也選用一個 Bulb, 只勾選亮度 (Luminance) (c)再注意專案中三個 Remote_Control 遙控器都接到 Join 1 就是其中一個連接燈泡後, 其他兩個遙控器要連到 Join 接點 (d)在電腦上也是要開啟一個 Bulb 燈泡拉成永遠看得到, 並把該編號燈泡 關聯到 專案內的 Bulb 代表圖示 (e)然後請三位同學各自用手機連到 demo.iottalk.tw 點裡面的 Remote_Control (Mobile) (f)請三位分別報出手機上遙控器的編號以便關聯到專案中三個遙控器代表圖示; 再來, 點專案中的連接點(Join 1)要寫一個合成的程式碼(以下我稱 haha), 以及三個拆開給紅綠藍使用的 rr, gg, bb 三個函數; haha( ) 函數的內容是 return [ args[0], args[1], args[2] ] # 把三個參數組合成一個串列 rr( ) 函數的內容是 return args[0][0]*255 gg( ) 函數的內容是 return args[0][1]*255 bb( ) 函數的內容是 return args[0][2]*255 然後三位同學就可以用手機上遙控器的 Knob 轉輪控制網頁上的燈泡囉 ! *** 注意各函數的頭部都是 def run(*args):
    *** 提醒: joint function 一定要 Save, 套用, 再 Save *** 上圖中的 haha( ) 把三個參數組合成一個串列
*** 下圖中的 gg( ) 從串列中取出第2個; 注意 [1] 是第 2 個不是第一個 ** rr( ), gg( ), bb( ) 是想分別取出 紅(Red) 綠(Green) 藍(Blue)的成份

*參看下圖, 點 Join 1 連結點, 注意右邊各個函數套用設定是否正確 ! 最後記得要按右上角那個 "Save" 喔 !如果發現上方 Flush 右邊是亮紅燈, 表示出問題了, 要點一下 Flush 讓它變綠燈 !

再把前面說明重複貼這方便查看 : 
請參考上面各圖的連接方式, 
(a)注意專案中, 選用三次的 Remote_Control, 各自都只勾選一個 Knob 就 Save,
(b)專案中也選用一個 Bulb, 這次只勾選顏色 (Color-O) 
(c)再注意專案中三個 Remote_Control 遙控器都接到 Join 1
   就是其中一個連接燈泡後, 其他兩個遙控器要連到 Join 接點
(d)在電腦上也是要開啟一個 Bulb 燈泡拉成永遠看得到,
   並把該編號燈泡 關聯到 專案內的 Bulb 代表圖示
(e)然後請三位同學各自用手機連到 demo.iottalk.tw 點裡面的 Remote_Control
(f)請三位分別報出手機上遙控器的編號以便關聯到專案中三個遙控器代表圖示;
   再來, 點專案中的連接點(Join 1)要寫一個合成的程式碼(以下我稱 haha),
   以及三個拆開給紅綠藍使用的 rr, gg, bb 三個函數; 
   haha( ) 函數的內容是  return [ args[0], args[1], args[2] ]
   rr( )  函數的內容是  return args[0][0]*255
   gg( )  函數的內容是  return args[0][1]*255
   bb( )  函數的內容是  return args[0][2]*255
然後三位同學就可以用手機上遙控器的 Knob 轉輪控制網頁上的燈泡囉 !
*** 注意各函數的頭部都是 def run(*args): 
   
(四)練習從 github 抓 Python 虛擬裝置來用 (就用來控制燈泡開關好了)
    * 在PC/Windows不安裝 git 仍可選用"Download ZIP"方式下載 !
還沒有 git 但想安裝 git 的可以點這到 https://git-scm.com/downloads下載安裝
 
到 https://desktop.github.com/ 抓 Desktop github
  * 在PC/Windows不安裝 git 仍可選用"Download ZIP"方式下載 !
(1)用 Google 搜尋 dummy + iottalk + python
   應該會在前幾篇找到 GitHub - IoTtalk/Dummy_Device_IoTtalk_v1_py
     (Hint: 我搜尋結果是有時在第一篇, 或第二篇, 或 在第 三 篇)
(2)點進去, 稍微看一下說明, 它有說會用到網頁相關的 python module 'requests'
   就是說萬一有錯誤訊息可能你沒用 pip 安裝過 requests 模組啦 !
   到時候, 當然就是要照它說的安裝囉: (requests 是負責處理網頁溝通用的模組)
       pip  install  requests 
   別緊張, 不用現在 pip install requests 喔, 因為說不定你的 Python 已經有安裝過該模組了 :-)
(3)點 Clone or download 準備下載這 Dummy_Device專案壓縮檔
   如果你電腦有 git 則可複製顯示的網址以便做 "git clone 網址"  抓回來
   在 Windows 上也可以點 Download ZIP  抓回來解壓縮
   (註: PC 上也可以安裝 git 以便用來 git clone 網址)
    ** 注意這時複製到的網址尾巴應該是 .git
    ** 如果你是在 Download ZIP 上按滑鼠右鍵選複製網址則尾巴應是 .zip 可用 wget 抓回 .zip 檔案
(4)如果抓 ZIP 檔案請解壓縮(Linux 上可用 unzip 命令)
   如果用 git clone 則是抓回放在子目錄 Dummy_Device_IoTtalk_v1_py
   用 cd 命令進入該子目錄
   Hint: 在 CMD 命令視窗內要善用 TAB 按鍵, 就是說只要打開始幾個字母就可按 TAB, 不對就再按一次 TAB, ...

(5)修改 DAI.py 檔案, 其他 DAN.py 和 csmapi.py 都不要管, 只要放同一目錄即可;
   阿 DAI.py 也只要改第三列那個 IP, 改為你在用的 iottalk 伺服器, 例如 125.227.255.81 或 demo.iottalk.tw
   這時變成類似如下:(紅色部分就是你正在用的 IoTtalk 伺服器, 也可以用 IP address)
     ServerURL = 'http://125.227.255.81:9999' #with no secure connection
    或交大 demo 伺服器:
     ServerURL = 'http://demo.iottalk.tw:9999' #with no secure connection
(6)仿照前面(一)用遙控器開關燈的專案,
   只是把 Remote_Control (遙控器)改為選用 Dummy_Device (虛擬裝置),
   提醒 Dummy_Device  的 IDF (輸入功能), 就是感測器(Sensor)
   
(7)執行 DAI.py 以便產生虛擬裝置並註冊到 iottalk 伺服器
   開啟 CMD 命令窗 進入到該目錄, 像以下這樣執行:
      python DAI.py    #  如果在 Ubuntu Linux 則用 python3 DAI.py
   注意看印出信息顯示的裝置編號 (亂數產生的), 關聯時要用到!

(8)回到 iottalk 專案網頁, 把虛擬裝置關聯起來, 當然虛擬燈炮也要關聯起來喔 !

(9)仔細看看虛擬燈炮會怎樣?
說明:
   這個 DAI.py 其實包括 IDF, 本來該讀取 IoT 的感測器(Sensor), 以便丟去給伺服器,
   現在用亂數產生一個值模擬讀到的 Sensor value;
   另外, 也包括一個 ODF 叫做 'Dummy_Control', 那是從 Server 送來的串列(Python 的 List 就是 Array 啦)
   這 DAI.py 只是從 Server "拉回"這 ODF 的串列, 然後如果收到的不是 None, 則只印出第一個值: 
        print (value1[0]) 
   真正的裝置可能要拿這值來做控制某些設備或做其他事情 !
   目前我們沒用到這 ODF, 所以你專案內可以不勾選 ODF, 或勾選了不關聯到任何設備 !

確定都有聯接好且關聯到虛擬裝置和燈炮, 應該有看到變化吧 !? 燈好像忽開忽關忽明忽暗對不對 !? 那是因為 DAI.py 是幫 'Dummy_Sensor' 產生亂數送出去 ! 接著來改為從鍵盤讀入再送出 ..
(五)修改 (四) 的 DAI.py 改為從鍵盤讀取要送出的值
    # 新版的 DAI.py (建議用 DAI2.py 做名稱) 如下: (不要 Mark 到左方的每列編號 !)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# DAI2.py  -- new version of Dummy Device DAI.py, modified by tsaiwn@cs.nctu.edu.tw
# you can get from here:  https://goo.gl/6jtP41   ; Search dummy + iottalk  for other files
import time, DAN, requests, random 
import threading, sys

# ServerURL = 'http://Your_server_IP_or_DomainName:9999' #with no secure connection
# ServerURL = 'http://192.168.20.101:9999' #with no secure connection
#  注意你用的 IoTtalk 伺服器網址或 IP 
ServerURL = 'https://demo.iottalk.tw' #with SSL secure connection
# ServerURL = 'https://Your_DomainName' #with SSL connection  (IP can not be used with https)
Reg_addr = None #if None, Reg_addr = MAC address

mac_addr = 'C860008BD249'  # put here for easy to modify;;  the mac_addr in DAN.py is NOT used
# Copy DAI.py to DAI2.py and then modify the above mac_addr, then you can have two dummy devices
Reg_addr = mac_addr   # Otherwise, the mac addr generated in DAN.py will always be the same !

DAN.profile['dm_name']='Dummy_Device'   # you can change this but should also add the DM in server
DAN.profile['df_list']=['Dummy_Sensor', 'Dummy_Control']
#DAN.profile['d_name']= None # None for autoNaming
DAN.device_registration_with_retry(ServerURL, Reg_addr)

# global gotInput, theInput
gotInput=False
theInput="haha"
allDead=False

def doRead( ):
    global gotInput, theInput, allDead
    while True:
        if gotInput:
           time.sleep(0.1)
           continue  # go back to while
        try:
           theInput = input("Give me data: ")
        except Exception:    ##  KeyboardInterrupt:
           allDead = True
           print("\n\nDeregister " + DAN.profile['d_name'] + " !!!\n",  flush=True)
           DAN.deregister()
           sys.stdout = sys.__stdout__
           print(" Thread say Bye bye ---------------", flush=True)
           sys.exit( );   ## break  # raise   #  ?
        gotInput=True
        if theInput !='quit' and theInput != "exit":
           print("Will send " + theInput, end="   , ")

#creat a thread to do Input data from keyboard, by tsaiwn@cs.nctu.edu.tw
threadx = threading.Thread(target=doRead)
threadx.daemon = True
threadx.start()

while True:
    try:
    #Pull data from a device feature called "Dummy_Control"
        value1=DAN.pull('Dummy_Control')
        if value1 != None:
            print (value1[0])
    #Push data to a device feature called "Dummy_Sensor" 
        if gotInput:
           if theInput =='quit' or theInput=="exit":
              break;  #  sys.exit( );
           #value2=random.uniform(1, 10)
           try:
              value2=float( theInput )
           except:
              value2=0
           gotInput=False   # so that you can input again 
           if(allDead): break;
           DAN.push ('Dummy_Sensor', value2,  value2)

    except Exception as e:
        print(e)
        if str(e).find('mac_addr not found:') != -1:
            print('Reg_addr is not found. Try to re-register...')
            DAN.device_registration_with_retry(ServerURL, Reg_addr)
        else:
            print('Connection failed due to unknow reasons.')
            time.sleep(1)    
    try:
       time.sleep(0.2)
    except KeyboardInterrupt:
       break
time.sleep(0.5)
try: 
   DAN.deregister()
except Exception as e:
   print("===")
print("Bye ! --------------", flush=True)
sys.exit( );
這個新版的 DAI2.py 改為派一個 Thread (執行緒) 同時(平行)做事, 負責讀取使用者從鍵盤輸入;
請注意, 在等輸入的同時, 主程式仍不斷的 "Pull" ODF ('Dummy_Control')
並且在 "小弟" 有幫忙讀到一個值之時, 把該值 (value2) push 送去給 Server :
      DAN.push ('Dummy_Sensor', value2,  value2)  
在這是故意把 value2 寫兩次表示送出兩個值, 也可以送更多個 !  
補充關於用 Thread 平行做事:
 (a)Line 27-44 定義一個函數 doRead( )
    這函數利用一個全域變數 gotInput 讓 "主人" 知道是否有讀到輸入值(放在 theInput 內)
 (b)Line 47-49 設定並啟動 Thread 執行緒, 執行的是 doRead( )
 (c)Line 58-68 是主人發現 gotInput 亮燈表示有資料時, 取出資料, 
         並把 gotInput 弄為 False 以便讓小弟知道主人已經把資料 theInput 拿走
         請注意, 在 Line 59-60 還有檢查輸入值, 如果是 'quit' 或 'exit' 則結束程式 !

(六)修改專案, 改為放兩組 Dummy Device, 然後互相傳遞資料
   (1)複製並修改 (五)的 DAI2.py  為 DAI3.py,
      這 DAI3.py 只是改一下 第13列的 mac_addr, 把裡面 249 改為 你喜歡的其他三位數,
      因為如果 mac_addr 相同, 則會被 Server 看作同一個裝置 !
   (2)你必須開兩個命令視窗, 一個跑 DAI2.py, 另一個跑 DAI3.py
      執行時請注意看各自印出的裝置號碼, 關聯時要用到 !
   (3)也可以找同學合作, 一個跑 DAI2.py, 另一個跑 DAI3.py
      專案當然是由誰建立都可以, 只要把代表 DAI2.py 和代表 DAI3.py 的虛擬裝置關聯到即可,
      注意, 在專案中加入兩組 Dummy_Device 兩次都有勾選 IDF 與 ODF, 則共會有四個 Dummy_Device,
      兩個輸入用, 兩個輸出用 !
   (4)這時, DAI2.py 和 DAI3.py 都各自身兼兩個設備: 輸入(IDF) 和 輸出(ODF)
      所以關聯設備時, DAI2.py 和 DAI3.py 各會被用到兩次
      其實你也可以再複製為 DAI4.py 和 DAI5.py (當然要改 mac_addr), 開四個 CMD 窗各自執行,
      這樣負責輸入的就不必負責輸出 !
   (5)測試時,
      各自都打一個數值然後按 Enter 送出 ;可以打實數因為我用 :
          value2=float( theInput )
   (6)做習題的同學注意: 測試過程也要 CTRL_PrintScr 抓取畫面繳交 !
Top        
(七) 如何增加/修改 Device Model 物聯網設備類別 ?                 
    (0)阿這是IoTtalk使用者該知道的喔, 在使用者手冊內有詳細解說 !
          這與如何增加/修改/刪除 Device Features(設備功能屬性)在同一個操作介面,
        都是從IoTtalk首頁點選 Device Feature Management進入頁面;
        這時 左上角是Device Feature, 用滑鼠點一下就可切換 Device FeatureDevice Model 管理頁面!

    (1) 在iottalk 首頁點選 Device Feature Management
          這時可以增加或刪除 Device Feature 設備功能, 功能可用於各種設備(Model),
        如果某功能(Feature)已經被至少一種設備(Model)使用, 則無法刪除(會警告且不給刪除)。

        想增刪或修改設備(Model)則請讓我們跟新聞主播盛竹如繼續看下去..
    (2) 點左上角的 Device Feature 切換為 Device Model 畫面 (參考下圖)
     
    (3) 右邊視窗中點選 add new DM 以便新增 Device Model (設備)
    (4) 然後要選 想要哪些 IDF 以及/或者 哪些 ODF, 選擇類別 Category, 然後選你要的功能
    (5) 最後記得要點 Save 存檔, 它會問你 Device Model Name (設備名稱)
    (6)注意 Device Model name 若與已經有的重複會警告你要蓋掉 !
    (7)成功加入之後, 就可在 Project 中新增這個Device Model 設備類了
   
   
(八) 如果要用 IoTtalk 連接真實設備(例如真的燈泡)那要如何做 ?
      (關於 ArduTalk 與 NodeMCU 以及IoTtalk物聯網應用)
    A: 這時需要一個 ES8266 或 NodeMCU 的微控器(Micro controller),
         裡面要有程式可以和 IoTTalk 系統溝通, 又可以控制真實燈泡等物聯網設備。
         ( 註: ESP8266 是指 WiFi 模組(當然含SoC), NodeMCU 通常指 ESP8266 開發板 )
         ( 還有, ESP8266 是樂鑫公司開發, NodeMCU 是安信可公司(為ESP8266第三方公司)基於ESP8266開發 )
         ( 目前 NodeMCU 除了 NodeMCU ESP8266之外, 還有增加支援藍芽的 NodeMCU ESP32S(基於樂鑫的ESP32開發) )
     於要放 NodeMCU ESP8266 裡面的程式我們已經寫好一個ESP8266的通用範例(C++),
      你可以用 Arduino IDENodeMCU ESP8266 所需要的 C++ 程式碼燒錄進去,  
      那要怎樣"燒錄"進去呢?
     阿這在本網頁最前面說過喔, 就是要 到Github ArduTalk-for-NodeMCU 網頁點入 Documents 目錄,
    看裡面 ArduTalk安裝教學(通常只須要燒錄一次)ArduTalk 操作教學

* 通常使用 Arduino IDE 開發程式碼會用 Serial.print( ) 來印訊息到序列埠(serial Port)以便用序列埠監視窗查看;
    很多人以為把多個 serial.print( ) 利用字串串接方式合併成一個 Serial.print( ) 比較省時間 !?
    其實錯了, 不但沒省時間, 而且也沒省空間(記憶體)!! 詳細可以點這看我以前寫的這篇點這看更多我寫的Arduino相關文章
    也可以點這看我以前在另一個也是很多人用的 Arduino 社群發表的文章(都用 tsaiwn 帳號:-)
** 國字小常識: "埠" (Port) 的讀音 ..
          序列埠(Serial Port)的"埠"念作 "步", Port("埠")意思是港口; 共匪把Serial Port翻譯為"串口"好像比較貼切:-)
          所以 Serial Port Monitor (序列埠監視器)共匪翻譯為 "串口監視窗"。

  Q: 那該買 ESP8266 模組 還是買 NodeMCU ESP8266 開發板
  A: 如果你自己會焊接電路板, 且要燒錄非常多片, 那可買 ESP8266 模組 比較便宜(但還要另外買 USB to TTL 轉接器才能燒錄!)
    否則當然應該買 NodeMCU ESP8266 開發板 (大約台幣 100元 ~ 120元), 後面會補充 !
    甚至大片的 V3 版 NodeMCU 開發板只賣 NT$90 元, 且它的小麵包板也只要每片 NT$8元
    (注意大片的放麵包板要兩塊麵包板併用! 或要另外買 V3 擴充底板大約台幣50元到85元)
    (有寫 LoLin 的就是 V3 版; V3 開發板早期使用 CH340 接口晶片, 但後來其實也有用 CP2102 晶片的比較小片就不必用擴充底板)
    如果你跟代理商買, 當然就會發現 NodeMCU 開發板不論 V2 或 V3 都很貴竟然一片要大約台幣三百元上下 !
    購買 NodeMCU 之前, 建議先看看這篇"第一次購買 NODEMCU 就上手"
  * 注意 NodeMCU V2 板比 V3 板多一顆 On Board LED(和 D0, 即 GPIO 16 連在一起)
  * 再注意, NodeMCU V2 開發板的 D0 如果做 analogWrite( ) 會影響 WiFi 連線,
      所以 NodeMCU 的程式碼必須稍微修改(看後面說的 t7 版本) !
* NodeMCU V2 開發板插在一塊麵包板上仍可用杜邦線連接物聯網裝置
* NodeMCU V3 開發板如果是用CH340接扣晶片的因較大片則需兩片麵包板併用
* 關於 ESP8266, 點這看 GitHub 上的 ESP8266 core for Arduino 以及 Documentation 文件
* 關於 ESP32, 點這看 GitHub 上的 ESP32 core for Arduino
* 關於 String 類別, 點這看 Arduino 的 WString.cpp 以及 點這看 ESP8266 用的 WString.cpp
簡介 ArduTalk (Arduino + iotTalk) 與 NodeMCU
   ArduTalk 是為了讓物聯網設備(IoT Device)可以很方便利用 IoTtalk 的專案連接,
   主要包括 ArduTalk-for-Arduino Yun 雲版本(Python), 和 ArduTalk-for-NodeMCU 版本
   那既然是強調 Arduino, 怎會用 NodeMCU ESP8266 ESP12E 呢?
   其實 NodeMCU ESP12E 不但和 Arduino 相容且多了 WiFi 模組, 可以用 Arduino IDE 開發軟體!
   ArduTalk 本來是考慮 Arduino-Yun 和 Arduino-Uno 兩個開發板 !
       其中 Arduino-Yun 雖然一片大約台幣 2000 ~ 2500, 但因為它同時有一般Arduino 的 MCU ATmega32u4,
   又有可以支援 OpenWRT(一種 Linux)的 Atheros AR9331 MCU SoC,
   可以灌 Python 且又有 Ethernet 和 WiFi 且可以用 C/C++ 寫程式碼到 ATmega32u4 的Flash記憶體,
   所以並不算貴; 但很普及的 Arduino-Uno/或Nano 還要另外買 WiFi 模組才方便使用, 加起來大約五百元ㄟ!
   而 NodeMCU 開發板就相當於 Arduino-Nano + WiFi 模組, 且只要台幣120元上下!!
   所以, NodeMCU 是不錯的選擇(Analog Input 和 GPIO 較少), 於是就有了 ArduTalk-for-NodeMCU囉 ! 
   ** 如果你是要用 Arduino Yun 來連接 IoTtalk, 則要看 ArduTalk-for-ArduinoYun
      可以點這看如何把 ArduinoYun Rev.2 連接到 IoTtalk 的操作手冊 
   ** 這邊有 NT$2280 的 Arduino Yun 開發板 Rev.2, 也有限時五月份特價 NT$1800元喔!(同一家)
* 再次提醒在上面以及 Github ArduTalk 說的 NodeMCU 是 ESP8266 ESP12E
NodeMCU  == Node  MicroController Unit 
**嚴格說來, NodeMCU 是指開源的SoC韌體(大陸稱固件), 點這看 NodeMCU 官方網站
   但是, 現在提到 NodeMCU 通常指開發板, 包括 NodeMCU 8266 V2 和 8266 V3, 以及 NodeMCU ESP32(多藍芽)** ESP8266 和 ESP32 的原廠是 樂鑫 espressif.com
** NodeMCU 開發板的原廠是安信可 AI-Thinker 公司, 可以點這看 NodeMCU ESP8266 介紹
** NodeMCU 也是 Ai-Thinker(安信可)公司發起的開源專案(Project)(不是樂鑫喔!)
**關於 NodeMCU 的文件, 可以 點這看 https://nodemcu.readthedocs.io/
**一般提到 NodeMCU 開發板主要分兩大類: ESP8266 和 NodeMCU-32S ESP32 (有很多開發板)
  NodeMCU 開發板 使用的 CPU --
        NodeMCU ESP8266 是用 Tensilica L106 32-bit RISC 處理器
        NodeMCU-32S ESP32 是用 Tensilica Xtensa LX6
     (Tensilica 公司在 2013年被 Cadence益華電腦 併購)
  * NodeMCU ESP8266 ==. Arduino Nano + WiFi 模組; ESP8266 有十幾種版本,
     各版本 ESP8266 請看 https://en.wikipedia.org/wiki/ESP8266NodeMCU-32S ESP32 ==.  ESP8266 + 藍芽 4.2 版本更亂, ESP32 製造廠商眾多,
     各版本 ESP32 請看  https://en.wikipedia.org/wiki/ESP32
     所以, ESP32 是樂鑫公司的帶 WiFi 和 藍芽 4.2 之 SoC, 但 ESP32S 是安信可公司產品;
     安信可的 ESP32S (注意 32 後面有 S) 和 樂鑫公司的 ESP-WROOM-32 完全相容。
  * NodeMCU 的硬體及韌體 和 Arduino 一樣是開源, 若要說原廠則是上海的樂鑫(Espressif):
      https://www.espressif.com/
     但是真正讓 ESP8266(做成 NodeMCU 開發板)普及的則是深圳市的安信可(Ai-Thinker)公司:
      https://www.ai-thinker.com/
     所以, 一般都會說"安信可原廠" NodeMCU 開發板(因為 NodeMCU 開發板是安信可公司做的), 例如:
      安信可 NodeMCU 開發板 V.2 (基於樂鑫 ESP8266 WiFi模組)(淘寶網安信可Ai-Thinker)
   ** 另外, 比較新的 NodeMCU 開發板則是搭載 ESP-12F; 也是有 V2 版和 V3 版
      (安信可公司NodeMCU開發板一開始用 ESP-12(V1), 後來 V2 開始用 ESP-12E; 12F 是 12E 的加強版)
   *** 這是蝦皮上賣的 ESP-12F 模組, 每片 NT$78元 (安信可AI-Thinker原廠)
    @ 不過, 安信可原廠在淘寶網上 ESP-12F 模塊(模組)只賣人民幣 RMB$11 元(約台幣 50元)
    % 這是用 ESP-12F 做的白牌 NodeMCU V3 每個約台幣 55元(注意天線的樣子!)
   *** 目前還有更新使用 ESP-12N 的 NodeMCU V3 開發板
* 關於 樂鑫 Espressif.com 可以點這看該公司在知乎的帳號發表的文章
* 關於 安信可 AI-Thinker 公司(深圳)

  要體驗 ArduTalk,
    你需要買個 NodeMCU 開發板, 大約 NT$120, 有更貴的但也有更便宜的, 等下會列出我辛苦搜尋結果給大家參考:-)
    還有, 需要一個或兩個麵包板(ㄟ..不是可以吃的那個麵包喔, 沒聽過的自己 Google 一下啦:-)
    至於還需要哪些零組件呢?
    就請看剛剛說的 ArduTalk 操作手冊, 點入之後, 先點看 ArduTalk操作教學.pdf
    在 P.5 有列出基本上該有的材料, 這樣就可以體驗該操作教學文件內設計的六個實驗範例 !
   以下把該操作教學文件內 P.5 列的材料複製過來:

  ⊚ ↑ 以上這些材料總價大約 NT$310元:
          120 + 15*2 + 18 + 10 + 25 + 5 + 2 + 35 + 運費 60 (預估)

* 關於 NodeMCU 開發板
    購買 NodeMCU 開發板 之前建議可以先看看這篇 "第一次購買 NODEMCU 就上手" 經驗文
    再次提醒, 雖然建議買 NodeMCU V2 開發板比較小, 方便使用麵包板, 但是經實測發現對 D0 做 PWM 輸出會讓網路斷線,
    所以, 如果用 NodeMCU V2 開發板, 要燒錄不同的程式碼, 請點這看關於我改過的 t7 版本

* 如果你買的光敏電阻不是模組而是很便宜的一個只有兩端接腳的小光敏電阻(PhotoResistor),
    那還需要串一個 1K 歐姆的電阻再接地(這端並聯到 A0), 可以參考下圖的連接方法:  
    (NodeMCU 沒有 像 Arduino 的 5V, 所以另一端連接到 3.3V 即可)
Q: 我連接簡易光敏電阻控制網頁上 Bulb 燈泡, 發現遮住光敏電阻 Bulb 接近關燈, 放開光敏電阻則 Bulb 接近全亮 !?
      可是這樣不是我要的啊 !? 怎辦 ?
A: 在 Project 專案內, 點從光敏電阻(例如 A0) 連接到 Bulb 亮度的 Join 連接點,
      這時右半邊視窗出現資料進出使用的函數, 可以寫一個小Python函數來處理 !
      當然你要先知道你光敏電阻的變化值, 可以先用滑鼠右鍵點 Join 連接點觀察資料進出變化 !
      或是你另外先寫個小程式燒錄進 NodeMCU 用 Serial.print( ) 印出讀取 A0 的值仔細比較各種變化 !
Hint: 提醒點Join 連接點寫 Python 小函數, 記得要存檔, 然後記得重新選取要套用的函數 !
      主要是修改函數並存檔並不表示要自動套用該函數 !
      還有, 套用完畢後, 建議再次用滑鼠右鍵點 Join 連接點觀察資料進出變化 !

Q: 那程式碼到底要如何寫呢 ?
A: 假設觀察到的 A0 數值在 500 到 825 之間, 希望反過來控制明亮, 參考以下 Code: (當然可以有別的寫法)
          gg = int(args[0]) - 500;
          if(gg < 0): gg = 0;   # 防止負數 < 0
          gg = int ( gg * 1.0 / (825 - 500) * 1024 );   # map 到 0..1023
          if(gg > 1023): gg = 1023;   # 防止超出上限
          return gg
          # 如果希望控制在 0..255; 把上面 1024 改為 256, 把 1023 改為 255 即可

* 單色LED 燈的接腳有「長短腳」之分,長腳接「高電位」(或數字接腳),短腳接「低電位」(接地或電源負極)。
* 在ArduTalk操作教學文件裡面提到的 RGB LED 的規格可以參考:
      https://reurl.cc/Ga8NG
主要就是要注意, 這 LED 的紅光電壓大約 2V, 但 NodeMCU 接腳輸出是 3.3V,
所以要串接一個 220歐姆(大約即可)的電阻以免燒壞 LED; 萬一手邊沒電阻, 可以串接一顆單色LED代替,
就是說, Signal Pin(數字接腳) --> 單色 LED 長腳; 單色 LED 短腳 --> 三色 LED 的 R 腳;
阿不過, 反正一顆三色 LED 才台幣兩塊錢燒壞也不會心疼 :-)
最長那支腳要接地(因為共陰極, 若是共陽則要接 3.3V); 在這最長腳的一邊只一支的是 R 紅光;
另一邊有兩支的, 由這支最長腳往外依序為 G 綠光, B 藍光, 參考下圖。



果你買的是三色LED 模組(比較貴)那就不需要再接電阻(因模組版子上已經有連接電阻)!
例如 這個 RGB 模組(NT$20)(蝦皮allen_6833)
這個 三色 LED 模組(NT$40)(蝦皮allen_6833)
(注意這是"共陽極"的, 寫 V 那接腳要接 3.3V,
    RGB 三支腳的值(用 analogWrite )則 0 是最亮, 1023不亮, 剛好反過來 !
    (Arduino 的 PWM(Pulse Width Modulation) 是 0 ~ 255; 但 NodeMCU 的 PWM 是 0 ~ 1023)
    不過, 你可以在 IoTtalk 專案的連結點寫個小 Python 把 Knob 傳的值用 1023 減去其值!)


這個 三色 LED 模組(NT$16)(注意這個是"共陰極"的) 或 這個 NT$15元也是共陰極的
    也有只賣 NT$8元的三色 LED不知道會不會亮 :-)
* 如果你買的是像這七彩自閃 LED 因工作電壓是 3.2V 就不必再串接電阻了  
* 至於小麵包板, 蝦皮上價格從 NT$7 到 NT$35 都有人賣(都是同樣的 170孔的)
還要買哪些材料來玩呢 ?
    很多啊, 溫濕度感測器, 蜂鳴器(Buzzer), 可調變電阻等都是很容易測試 !
** 如果你買了 Buzzer 蜂鳴器 則可以把 NodeMCU 燒錄我這個 t8Songs 版本
    點入後, 進入後點入 ArduTalk,
    然後, 再點入 ESP12E_modified_tsaiwn 子目錄, 裡面有 t8Songs 版本 以及 t5 和 t7 版本的壓縮檔。
    ( t5 和 t7 版本說明請在這網頁敲 CTRL_F 搜尋 t5 和 t7 )
* 使用這 t8Songs 版本, 把蜂鳴器(Buzzer)的信號接腳(Pin)接在 NodeMCU 的 D2;
    當然, 在 IoTtalk 專案內, D2~ 沒用了, 同時 D0~ 和 D5 及 D8 也取消沒用了, 簡單說明如下:
      * D5 可接 Local 的 Button 按鈕, 按了可撥放音樂 (從 D2)
      * D6 有效, digital, 0 / 1
      * D7 有效, 在專案內仍如以前用, 但在 NodeMCU 已經改 analogWrite(D7, 1023*從Server抓下的 D7 值);
      * D8 在專案內沒用, 但 D1~ 的資料會同時寫到 NodeMCU 的 D1 接腳和 D8 接腳 !
** 至於可調變電阻當然要把信號腳(中間那支)接到 NodeMCU 的 A0, 左接 GND 右接 3.3V
*聽說 analogRead( ) 很慢? 這是相對其他動作很慢! 可參考我以前寫的 Arduino 文章
  如果想知道關於 PWM 輸出與 analogWrite( ), 也可以點這看我以前寫的 Arduino 的 PWM 輸出
    不過, NodeMCU ESP8266 的 PWM 預設是用純軟體靠 CPU 的 Interrupt 做的 !
    ( Arduino 是靠 Timer 定時器做, UNO 板有三個 Timer 各管控兩個 PWM pin 所以有六個 PWM pin~ )
    NodeMCU 每一支 GPIO pin 都可以做 PWM 輸出
    主要因為 Aduino 的 Clock 只有 16MHz, 而 NodeMCU 有 80MHz 甚至有 160MHz 版本 !
* 注意: 不過, 實測發現 NodeMCU V2 的 D0 如果做 PWM 輸出會讓 WiFi 斷線! (V3 沒問題)
   

    * 關於DHT-11等溫濕度感測器可以參靠我在共匪的社群發表的文章, 可以讓你學到更深入的概念
        如果要了解簡單的使用, 可以點這看這篇溫濕度感測器實驗
        如果你使用 Arduino, 這篇我以前在育達上課寫的應該對你也有些幫助
        如果你想了解如何從電腦送資料去 Arduino, 這篇應該可幫助你理解(也是我以前在育達上課講義)
    * 可調變電阻的連接方式可以參考這篇關於 Arduino 實驗的文章

    基本上建議先把該操作教學文件內設計的六個實驗範例做過之後, 再來考慮還要買哪些零件來玩 :-)
    沒概念或沒靈感?
    你可以用 Google 搜尋 arduino教學實作;
    你也可以在 youtube 打入 Arduino 專題 (或只打 arduino) 搜尋別人做過的來尋求靈感 !!
      ( 當然你也可以搜尋 Arduino project 找老外弄的:-)
    啥!?
    可不可以搜尋 NodeMCU 專題NodeMCU Project ?

    當然可以啊, 因為 NodeMCU (通常指 ESP12E 開發板) 和 Arduino (通常指 Arduino Uno 開發板)是相容的,
    而且硬體設計和軟體也都是開源(Open Source), 意思就是大家都可以合法 Copy/製作(只有註冊商標要錢)!

    使用 ESP8266 的 ESP12E 開發板有個好處, 就是它已經內建了 WiFi, 不需要另買 WiFi 模組 !
    可以把它當作簡單版的 Arduino 加上 WiFi 模組來用,
    你只要把你手機開無線網路熱點(無線基地台)分享給該 ESP12E 就可以實現聯網功能 !

* 再談關於 NodeMCU 開發板
    購買NodeMCU之前建議可以先看看這篇 "第一次購買 NODEMCU 就上手" 經驗文;
    也可以點這看看 NodeMCU 及其DEVKIT 開發板介紹
所以提醒大家, NodeMCU ESP8266 開發板建議購買 內建 CP210x 晶片的 ESP12E 開發板 !
如果帶有 CH340 晶片的(通常是 V3)雖然通常比較便宜,
但因為體積較大, 必須使用兩塊小麵包板併起來用, 或另買 V3 底座才有辦法連接材料!

    (當然你也可以說那我用杜邦線帶母頭的直接插到 MCU 接腳也可啦 !)

* 再次提醒, 雖然建議買 NodeMCU V2 開發板比較小, 方便使用麵包板, 但是經實測發現V2板對 D0 做 PWM 輸出會讓網路斷線,
    所以, 如果用 NodeMCU V2 開發板, 要燒錄不同的程式碼, 請點這看關於我改過的 t7 版本
* 蝦皮上找到一個很便宜的 NodeMCU V2 開發板, 一個只要 NT$96元  
      點這連到蝦皮 bopultd 賣場

  (蝦皮, 雖然有些店家寫來自海外, 但五天到七天就可寄到也還好, 超商取貨付款運費 NT$60元 和國內店家相同)  
* 單買這項最多可買十個(因為重量與體積還是海外運輸關係, 我試過無法一次買11個)則平均每個運費台幣 6 元 :-)
* 這家也有賣小麵包板 (比較貴, 一片要 19元, 但和 NodeMCU 一起買只要一份運費:-)
      建議買400孔(台幣30元)或 830孔(台幣42元)的比較好用 !
    https://shopee.tw/search?keyword=%E9%BA%B5%E5%8C%85%E6%9D%BF&order=asc&page=0&shop=41998737&sortBy=price
** 有一片只要 NT$8 元或7元的小麵包板: (與剛 19元的完全相同, 問題是不同家買要另外運費 !)
    https://shopee.tw/search?keyword=%E9%BA%B5%E5%8C%85%E6%9D%BF&order=asc&page=0&sortBy=price
***當然還需要有 Micro USB 數據線才能把程式碼燒錄進去 NodeMCU 這家賣 27元(很貴)(往下捲有allen_6833賣18元)
      (用 Android 手機的 USB 線即可, 當然要可以傳資料的! 有些只能充電用!)
    https://shopee.tw/Micro-USB-Cable-wire-1m-for-NodeMcu-i.41998737.2070403488    

***這家蝦皮桃園(allen_6833)的 USB 數據線(1公尺) 只要 NT$18元且品質很好 (我買過:-)
      https://reurl.cc/eVg97
***這家蝦皮桃園的也有賣 NodeMCU, V2 小片(帶 CP2102接口京片)的 NT$120元;
          NodeMCU V3 大片的 NT$100元(帶CH340 USB to TTL 轉接晶片的):
      https://shopee.tw/search?keyword=nodemcu&shop=4877344
      如果你買了 V3 大片的NodeMCU, 使用時需要兩塊麵包板併起來用, 或者也可另買 V3 擴充底板

***這家蝦皮桃園的有賣很多材料, 不過它的 NodeMCU 小片要價 NT$120 元稍微貴一點點,
    但是, 幾乎你想要的實驗材料這家通通有 :-) 且店家在桃園蘆竹通常一兩天就可收到貨 !
    https://shopee.tw/search?keyword=arduino&order=asc&page=0&shop=4877344&sortBy=price
***例如, 麵包, 阿不是, 我是說 麵包板:   點這看該店 allen_6833 賣 麵包板
***例如, 光敏電阻 (Light Sensor):   點這看該店 allen_6833 賣 光敏電阻
***例如, 繼電器 (Relay):   點這看該店 allen_6833 賣 繼電器
***例如, 杜邦線:   點這看該店 allen_6833 賣 各種杜邦線
***例如, 各種 LED:   點這看該店 allen_6833 賣 各種 LED 零件

*** 如果你買的都是 "模組", 那其實就不需用到 額外的電阻 了 ! !
          點這看該店 allen_6833 賣 RGB LED 模組

阿我好像應該跟這店家( allen_6833 蝦皮 ) 要廣告費才對 .. 呵呵 :-)

* 如果要更便宜的 NodeMCU, 那要到淘寶網找 (不過買少量連運費可能不伐算 !)
  (a)最便宜的在淘寶網 "全球易創客", 每個約 NT$70, (人民幣13.9); 库存2665321件
       https://reurl.cc/gnOqQ
     (原價 人民幣 13.9, 買第一個特價 10.68元, 注意第2個起每個是人民幣 13.9元)
     *該家另有賣 USB 傳輸線可一起買, 每條約台幣 10元
     *該家要另加運費人民幣至少6元寄送到海岸集運中心, 你再上網付海運費/空運費約每公斤人民幣23元
     *就是說到淘寶網買東西要付兩次錢, 一次付給店家的, 另一次付給物流到台灣的"集運"公司
  (b)淘寶網"8266模組的家", 也是每個約 NT$70, (人民幣14.0);; 库存22138878件)
       https://reurl.cc/dqE3M
      (原價 人民幣 14.0, 買第一個特價 10.10, 第2個開始每個 14.0元)
     *該家要另加運費人民幣30元(選官方集運是人民幣8元)寄送到海岸集運中心, 你再上網付海運費/空運費約每公斤人民幣23元
以下是我試著從 淘寶網 "全球易創客" 這店家購買要結帳的畫面:
  (通常很多店家提供如果不到一公斤可以勾選連全部運費一次結算)


* NodeMCU 開發板 須要燒錄一個會和 IoTtalk Server 溝通的程式碼 !
* 再說一次,
    一般提到 NodeMCU 是指 ESP8266 ESP12E 開發板, 因這最普遍;
    ESP8266 是樂鑫公司開發的 WiFi SoC; NodeMCU 則是安信可公司基於ESP8266 ESP12E 的開發板;
    (安信可公司是 ESP8266 的第三方開發商, 其 ESP8266 產品有 ESP-01, ESP-02, ... ESP12, ESP12E, ESP12F, ...)
    其實 ESP8266 現在有很多廠商做很多版本型號, 可以點這看看 ESP8266點這看 ESP32 (多了藍芽4.2)

   面已經說過, 你可以用 Arduino IDE 把 NodeMCU 所需要的 C++ 程式碼燒錄進去,
詳細的步驟在 ArduTalk安裝教學(NodeMCU).pdf
該檔案在 用 google 搜尋 Github + ardutalk + nodemcu 的 Repository ,
就是在 https://github.com/IoTtalk/ArduTalk-for-NodeMCU 裡面,
點入後, 程式碼在 ArduTalk_ESP12e_1 目錄內,
    至於剛剛說的安裝教學文件則請點入 Documents 目錄, 點那有"安裝"的.pdf檔案 ArduTalk安裝教學(NodeMCU).pdf; 簡單說就是要有 Arduino IDE (到 Arduino.cc 抓壓縮檔來解壓縮即可使用; 然後要安裝驅動程式, 之後才能把程式碼燒錄進去 !
    燒錄好 NodeMCU (ESP8266 ESP12E) 之後, 如何使用來連接電子零件原則上類似 Arduino 操作, 這樣說是因為網路上有非常多的 Arduino 教學或專題心得等, 自己問 Google 大神和 搜尋 Youtube 就可找到一堆; 不過我們是要和 IoTtalk Server 結合, 所以請先看 Ardutalk操作教學.pdf, 裡面提供了使用前面說的一些材料(在操作手冊內P.5)六個可操作練習範例, 至少先練習其中的兩個 !
以下先略述該 NodeMCU 程式碼 (ArduTalk_ESP12e_1.ino) 的行為:
(0)開機, 設定 GPIO 0 為 INPUT_PULLUP (for 長按 Flash 按鈕清除 EEPROM 網路資料)
 設定以下七個 GPIO 為輸出接腳:
    pinMode(16, OUTPUT);// D0~    
    pinMode(5, OUTPUT); // D1~    
    pinMode(4, OUTPUT); // D2~
    pinMode(14, OUTPUT);// D5
    pinMode(12, OUTPUT);// D6    
    pinMode(13, OUTPUT);// D7        
    pinMode(15, OUTPUT);// D8        
  * 另外 A0 接腳是做 analogRead( ) 用的不必設定
    analogRead 是類比輸入, 大陸翻譯為模擬輸入, 可以讀到的值 0 ~ 1023
(1) 讀取 EEPROM 內網路相關設定 (SSID, PASS, IoTtalkServer IP)
     如讀取到資料則做 CALL (2) connect_to_wifi(wifissid, wifipass);
     否則做 CALL (3) wifi_setting();
    Goto (4)
(2) function connect_to_wifi(wifissid, wifipass);  // LINE 185
    試著連線, 只試 10 秒, 如果 timeout 無法成功就 CALL (3)
    如果連線成功就設定為 STA mode, 就是 wifimode = 0
    return;  // 成功 或 10秒 Timeout
(3) function wifi_setting();  // LINE 165
    進入 AP Server mode (wifimode = 1)
    等待 User 用手機連入 192.168.0.1
    連入後會做 handleRoot(0);
    在該函數內會先掃描所有的 WiFi SSID,
    吐出網頁讓使用者選擇 SSID 並且輸入其密碼
     還要輸入正確的 IoTtalk Server IP
    輸入完成之後, 點按 Submit, 這時會跳去網頁 /setup 
     也就是執行  saveInfoAndConnectToWiFi( )  // LINE 137
     它會把 SSID 和 PASSwortd 以及 IoTtalk Server IP 存入 EEPROM
     接著會 CALL (2) connect_to_wifi(_SSID_, _PASS_); 
     return;

(4) while( 在 AP server mode) {  // 就是說  wifimode != 0 
       delay(10);  一直等等等.. 等 User 用手機 瀏覽器設定網路並且連線(會設 wifimode=0;)
    }

(5) 已經連線成功 wifimode == 0
    向 IoTtalk Server 做 iottalk_register( )
    while(註冊失敗) { delay(3000); 繼續 try 註冊; }
    // 注意, 如果註冊不成功, 將陷入這 while Loop
(6) 已經向 IoTtalk Server 註冊成功, 做一些 I/O 準備的設定, 結束 setup( ) { }
(7) 這裡開始是在 loop( ) { ... } 函數內 
  用 cycleTimestamp 來檢查時間以便每 0.2 秒才做一次 Push/Pull 
  // 注意只 Push 一個資料(從 A0 讀取的 0 ~ 1023 的值), 但會 Pull 七個資料下來
  // Pull 下來的資料有可能代表 "沒資料", 這時不會 Write 到 GPIO pin
  // D0~, D1~, D2~  這三個對應到 PWM(Pulse Width Modulation) 的 pin (其值是 0 ~ 1023);
  // (Arduino 的 PWM 是 0 ~ 255; 但 NodeMCU 的 PWM 是 0 ~ 1023)
 /// On ESP12E, PWM may be used on GPIO pins 0 to 16 using analogWrite(pin, value). 
 /// analogWrite 翻譯為類比輸出, 大陸翻譯為模擬輸出
 /// https://iotbyhvm.ooo/gpio-pins-esp8266/
  // PWM range may be changed by calling analogWriteRange(new_range).
  // PWM frequency is 1kHz by default. Call analogWriteFreq(new_frequency) to change the frequency.
  // Pin interrupts are supported through attachInterrupt, detachInterrupt functions. 
  // Interrupts may be attached to any GPIO pin, except GPIO16. 
  // The ESP8266 has 17 GPIO pins (0-16), however, you can only use 11 of them, 
  //  .. because 6 pins (GPIO 6 - 11) are used to connect the Flash Memory chip.
  //  .. And, usually TX (GPIO 1) and RX (GPIO 3) are used to do communication job
  // GPIO16 has a built-in pull-down resistor. (NOT Pull-Up)
  // GPIO15 is always pulled low, so you can’t use the internal pull-up resistor.
  // GPIO2 (on board LED) can NOT be LOW at boot, so you can NOT connect a switch to it.
  loop( ) {
    a. 如果發現 Flash Button 被按下就跳去準備清除 EEPROM
       必須有長按住 Flash Button 超過 3 秒才會做否則會返回此處
       如果有做清除動作則重開機 ESP.reset( );  // 官方建議用 ESP.restart( );
    b. if( cycleTimestaml 已經過了 0.2秒){
        b1. 用 analogRead( ) 讀取 A0 然後 把 A0 push 去 IoTtalk Server
            // A0 為類比輸入(大陸稱模擬輸入), 讀取的值為 0 ~ 1023 
        b2. 處理三個 PWM 的資料
           依序從 IoTtalk Server PULL 資料並且用 analogWrite( ) 寫到對應接腳
             Pull D0~   寫到 GPIO 16
             Pull D1~   寫到 GPIO 05
             Pull D2~   寫到 GPIO 04
        b3. 處理四個 digital 的資料
           依序從 IoTtalk Server PULL 資料並且用 digitalWrite( ) 寫到對應接腳
             Pull D5   寫到 GPIO 14
             Pull D6   寫到 GPIO 12
             Pull D7   寫到 GPIO 13
             Pull D8   寫到 GPIO 15
        b4. 重設 cycleTimestamp = millis( );
       } // 每 0.2秒做 Push/Pull  
    c. 確保每 2 秒左右可以閃一次 LED
  } // loop(
   
Q: 我看網路上說 GPIO 16 不能做 PWM 輸出, 真的嗎 ?
A: 你看的資料太舊啦 ! 自己試驗從 D0 (GPIO 16) 用 analogWrite( ) 控制 LED 不就知道了 !
      點這裡面有 PWM 範例剛好就是使用 GPIO 16
      阿不過, 如果是 NodeMCU V2 則對 GPIO 16 做 PWM 確實會影響 WiFi 網路(V3 則沒影響!)
      為此, 我改了一個 t7 版本, 請往下看完 t5 說明再看或點這跳到後面直接看我的說明

Q: 要如何把 NodeMCU 程式碼燒錄進去 NodeMCU ESP8266/ESP-12E 開發板?
A: 前面有說過資料在 Github 上的 ArduTalk-for-NodeMCU 內啊 !
就是說要在你的 Arduino IDE 安裝(a)NodeMCU ESP8266 的驅動程式以及(b)USB to TTL 的 CP210x 或 CH34x 的驅動程式, 然後開發板選 NodeMCU1.0, 並確定選到正確的 COM Port, 然後開啟程式碼專案, 就可以把程式碼編譯並"上傳"進去 NodeMCU 開發板; 詳細步驟在前面說過了, 就是要看在 ArduTalk for NodeMCU 內的ArduTalk安裝教學(NodeMCU).pdf
* 關於 NodeMCU1.0驅動程式, 也可以點這看 Github 上的 ESP8266/Arduino/ 裡面的說明
* 關於 CP210x 驅動程式(USB to TTL), 也可以點這到官方網站抓
* 關於 CH340 驅動程式(USB to TTL), 也可以點這進去抓 CH341SER.EXE



小片(with CP210x的) NodeMCU 其時也有擴充底板(注意天線圖樣不同!)
* 有人每片賣 NT$89 (露天拍賣店家); 也有人很誇張賣每張 NT$180 (注意真的只有底板喔!)

* 大片(V3, with CH34x) NodeMCU 用的擴充底板相對就比較便宜,
    有人每片只賣 NT$49 (我確定這片是 for V3 with CH34x 的, 雖然它沒寫 !); 甚至每片 NT$44元(海外寄通常也只要五天)
    其他可以點這看看蝦皮一些賣家各自賣不同價格 !
** 注意看底板上印的天線的樣子就可以分辨是 for V3 (LoLin) 還是 for V2 (都是用 ESP-12E 或 ESP-12F)
** 注意 NodeMCU V2 板比 V3 板多一顆 On Board LED(和 D0, 即 GPIO 16 連在一起)

ESP-12F 是 ESP-12E 加強版, WiFi 距離更遠 !
ESP-12E 在 Arduino IDE 內 pin 腳 定義:
      https://github.com/esp8266/Arduino/blob/master/variants/wifinfo/pins_arduino.h
* 注意 D3 與 Flash 按鈕共用 GPIO 0; 而 D4 是 on board LED ( GPIO 2 )
另外,
我有修改了另一個版本, 用手機連上設定 AP 時比較好用(可指定當時不存在的 AP 之 SSID);
放在 https://goo.gl/6jtP41
點入該網頁之後, 點 ArduTalk 目錄兩下, 進入再點入 ESP12E_modified_tsaiwn 子目錄,
裡面有一個壓縮檔案 ArduTalk_ESP12e_1_t5.zip
抓回來用 Arduno IDE 打開專案之後, 裡面有詳細說明如何從 Arduino IDE 安裝驅動程式, 包括 ESP12E (NodeMCU 1.0) 的驅動程式, 以及燒錄所需的 CH340 或 CP210x 驅動程式;

為了方便你查看程式碼, 在該處我也放了一個 .pdf 檔案, 就是 該 C++ 程式碼的全部 !
以下略述到底改了蝦密 (這些有寫在程式碼內):
////////// This is a modified version. current version number is  t5
///   Version t5, modified by tsaiwn@cs.nctu.edu.tw
///(0)When Power On and/or Reset
///   LED on for 2 seconds and then quickly falsh around one second
///(1)Read the network Info (SSID, PASS, IoTtalkServerIP) from EEPROM
///   If there is NO data, goto (3) to enter AP server mode
///   If it Got netword info, the on board LED will Quickly Flash twice.
///(2)Try to connect to the AP specifined in the network Info.
///   Enter STA (station) mode, will try to connect the AP for 25 seconds
///   During this time, It will flash the on board LED quickly 6 times every 3 seconds
///   After timeout (25 seconds), enter AP server mode with IP 192.168.0.1
/// ** try 25 秒如無法連上, 會切換到 AP server mode 等待連線 (如果EEPROM沒網路資料就不會 try 25秒)
///(3)Enter AP server mode, Server IP address is 192.168.0.1
///   LED will flash twice slowly every 5 seconds.
///  ** 此時 on board LED 燈會每隔約五秒連續慢閃燈2下, 表示在 AP mode 等待連入做設定
///   User can use a smart Phone to connect to the ESP WiFi AP which SSID begins with "MCU"
///   And then open a Browser to connect to 192.168.0.1 to do wifi setting/Configuration
///  *** AP 的 SSID 為 MCU- 開頭, 無需密碼; 用你手機開啟 WiFi 選該 MCU- 開頭的即可連上網路
///(4)ESP MCU will DO SCAN all available WiFi SSID and add them into the web Page
///   You can choose One of them; You may have to enter the Password for that AP.
///   Or you can even Enter a SSID name of an AP you prefered (This will be used if any)
///  ** 也可以自己手動指定 AP 與密碼 (這有填寫的話就會優先用這!!), 
///   Also enter its Password if required
///   Then click the Submit Button to save the network Information
///(5)ESP MCU will respond with a sucessful Page and then enter WiFi Station mode
///   It will Goto (2) to try to connect to the specified AP (for 25 seconds)
///(*)More about (4)
///  ****** 例如可填你手機分享的 AP, 或你實驗室自己的 AP, 甚或學校的 AP (這通常不能用! 理由如下);
///  ***      注意很多學校的 AP 連入之後還要用網頁做帳號密碼驗證, 這樣不能給 MCU 用!!
///  ***      所以, 不要填連上後還要網頁認證一次的 AP, 否則無法使用 ! (誰能從 MCU 內去開網頁認證?!)
///  ***      還有, 當然也可從網頁內修改要連的 IoTtalk Server IP (也可填 FQDN 網址)
///  ***** *** 不論是填入指定或選擇 AP, 好了之後點按 Submit 按鈕即可存起來並透過 AP 連出去 Internet
///(6)About RESET and How to clear the network Info in EEPROM ?
///   Press the RESET (RST) button will restart the MCU, 相當於拔電源重新插入 (廢話 :-)
///   Flash 按鈕 (USB 接頭旁邊有兩個小按鈕, 左邊 Reset, 右邊 Flash, 有寫字)
///// Long Press the Flash button (more than 5 seconds) will cause the network Info in EEPROM been erased !
///// ESP will restart( ) after clearing the EEPROM network Info.
/// ** 按住不放(LED燈會亮)超過五秒, 會把 EEPROM 內網路連線資料刪除, 重新開機,
///    (* Note that the data in EEPROM are  AP SSID, PassWord, IoTtalkServer IP ! *)
/// ---(** 如果後悔可在燈亮著還沒五秒之前放掉Flash 按鈕 ! **)---
///    這時自動重開後, 因為沒之前網路連線資料, 會立即進入 AP server 模式等你用手機連入做網路設定!
////// 自動重開 LED 應該會先亮 3 秒左右, 如果沒有亮則可能 Reboot 失敗, 請手動按 Reset 按鈕!!
////// (那是 ESP8266 ESP-12E 以及各版本的 Bug)
////// 關於 NodeMCU ESP8266 各版本請看 https://en.wikipedia.org/wiki/ESP8266 
////// NodeMCU 另外有 ESP32 (比 ESP8266 多了藍芽), 請看  https://en.wikipedia.org/wiki/ESP32
//////============== ========================================================================
///// Original Version by Jyneda (Dr. Yun-Wei Lin) :  Jyneda@Gmail.com
/// 用 Google 搜尋  iottalk + ardutalk + nodemcu 可找到以下網址
// See  https://github.com/IoTtalk/ArduTalk-for-NodeMCU
// Doc in  https://github.com/IoTtalk/ArduTalk-for-NodeMCU/tree/master/Documents
// === Installing Drivers with Boards Manager ===
//(0)Start Arduino and open Preferences window(偏好設定).
//     https://github.com/esp8266/Arduino
//(1)Enter the following URL into "Additional Board Manager URLs" field.
//     http://arduino.esp8266.com/stable/package_esp8266com_index.json 
//(2)Open Boards Manager from Tools
//   Tools 工具  >  Board  開發板 >  Board Manager  開發板管理員
//(3)在 開發板管理員 找到 或搜尋 ESP8266, 安裝最新版 (目前 2.5.0)
///// Documentation for the ESP8266: 
///   https://arduino-esp8266.readthedocs.io/en/2.5.0/
///===== Or use git clone to install the Library
// Clone git hub repository into hardware/esp8266com/esp8266 directory
/////  cd hardware;  mkdir esp8266com ; cd esp8266com
/////  git clone https://github.com/esp8266/Arduino.git esp8266
///// cd esp8266
///// git submodule update --init
//////////////////////////////////////////////////////////////////
//(4) USB to TTL Driver : CH34x or CP210x depends on your Development Board
/// You will also need CH34x Driver  OR  CP210x  Driver !
/// See https://github.com/IoTtalk/ArduTalk-for-NodeMCU/tree/master/Documents 
/// CH34x and Cp210x Drivers can be found there
///  CLick ArduTalk安裝教學(NodeMCU).pdf
///// https://sparks.gogo.co.nz/ch340.html
///// http://www.wch.cn/download/CH341SER_EXE.html
///Cp2102 Usb-to-Serial Driver Installation 
/////  https://exploreembedded.com/wiki/Cp2102_Usb-to-Serial_Driver_Installation
////////
//(5)Choose correct Board and COM port .. Before you starting to Burn the program ...
/// Board:  Tools 工具 > 開發板 Board  >  選  NodeMCU 1.0(ESP-12E Module)
/// COM:   Tools  工具 > Serial Port 序列埠  >  Choose CORRECT port 
////////////////////////////////////////////////////////////////////////// 

另外, 修改了原程式一個小 Bug:
原程式在 loop( ) 函數程式碼在接近最後的地方,
    if (millis()-LEDonCycle > 1) digitalWrite(2, 1);
這句會不斷的丟 HIGH(1)給 on board LED (GPIO 2), 現在修改為如下:
    (雖不重要, 但沒必要一直送 1 給 GPIO 2)
// 在 loop( ) 函數之前加入一個變數 int LEDisON = 0;
if(!LEDhadFlashed) {
  ... 讓 LED 亮 .. 改 LEDhadFlashed 狀態 
  LEDonCycle = millis( );   // 為了每 2 秒要點亮一次 LED
  LEDisON = 1;   // 新增的
}
if( LEDisON && (millis() - LEDonCycle > 5) ) {  // 增加 檢查 LEDisON     
  ... 關閉  LED (就是丟 1 給 GPIO 2)
  LEDisON = 0; // 新增
}
////////////////////////////////
* 原 ArduTalk 的 NodeMCU ESP-12E/F 程式碼在以下網址:
      https://github.com/IoTtalk/ArduTalk-for-NodeMCU
* 修改過的 NodeMCU ESP-12E/F 程式碼在: (包括 t5 版本和 t7 版本)
      https://GOO.GL/6jtP41
    (進入後在 ArduTalk 目錄內的 ESP12E_modified_tsaiwn 子目錄內)
* 關於在 Arduino IDE 用開發板管理員安裝 NodeMCU 驅動程式, 這篇裡面的圖片比較清楚
 
* 再次提醒, 購買NodeMCU開發板之前, 建議可以先看看這篇 "第一次購買 NODEMCU 就上手" 經驗文
* 關於 NodeMCU 的文件 : https://nodemcu.readthedocs.io/
* 關於 ESP8266 的官方技術文件 :   (樂鑫官方網站)
      https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf

* 關於ADC(Analog to Digital Converter), 可以點這看用 analogRead(A0)讀取 ESP8266 的 ADC 引腳
* 關於 analogWrite( ) 與 PWM 的原理, 可以點這看以前我寫的關於 Arduino PWM 的文章
      不過 NodeMCU 的 PWM 是用軟體做出來的, 不像 Arduino 的 PWM 是利用計時器用硬體實作出來的 !
* 關於使用 millis( ) 請點這看 millis( ) 注意事項, 也可以點這看我以前寫的關於 Arduino 的 millis( )與 timer 的秘密
    也可以點這看 Arduino 的 millis( ) 原始碼 以及 ESP8266 用的 millis( ) 原始碼
* 關於Arduino 的定時做些事, 竟然有人把我寫的一些文章整理成一篇較長的文章。 不過似乎漏掉了我這另外一篇
* 關於 String 的用法, 建議要看看這篇文章, 以及我寫的這篇關於 String.reserve( ) 函數(有被收入精華篇)
    如果你要研究 String 類別, 可以點這看 String class 的 C++ 原始碼
* 順便說一下, 把很多個 print 和 println 利用 String 併成一個 println( ) 並沒有省時間(也是我寫的:-)
* 關於連接 YeeLink 雲端使用經驗with ESP8266, 這也是用ESP8266接入yeelink
* 關於在ESP8266使用MicroPython, 點這看基于MicoPython如何下载代码到ESP8266中
* 關於 使用 ESP32 開發板 結合 Arduino IDE, 要看Github ESP32 重要資料 也可看 Node32s 的相關資料
* 注意 NodeMCU V2 板比 V3 板多一顆 On Board LED(和 D0, 即 GPIO 16 連在一起)
* 很多文章討論說 NodeMCU ESP32 網路比 ESP8266慢, 尤其用 https 又特別慢 !

**前面說過, NodeMCU V2 版本如果對 D0 (GPIO 16)做 analogWrite( ) 會導致 WiFi 網路斷線,
      為此, 我改了一個 t7 版本, 這 t7 版本會把 IoTtalk 專案上看到的 D0~ Pull 下來, 但不會寫到 D0,
      會改用 analogWrite(D8, 抓下的值)寫到 NodeMCU V2 開發板上的 D8;(專案內NodeMCU的D8變沒用);
      所以, 在 NodeMCU V2開發板上接線時, 請把原先接在 D0 的改接到 D8接腳上。
      另外, 為了方便測試, D7 接腳改用 analogWrite(D7, int(抓下D7值 * 1023) );
      請點這抓我改的 t7 版本, 進入後點入 ArduTalk,
      然後, 再點入 ESP12E_modified_tsaiwn 子目錄, 裡面有 t5 和 t7 以及 t8Sounds 版本的壓縮檔。
      關於 NodeMCU 的 GPIO, 可以點這看 ESP8266 GPIO 相關資料; 和 GPIO0 與 ESP8266 的啟動模式
** 關於 t8Sounds 版本則是為了使用蜂鳴器Buzzer修改了 analogWrite( ) 以便執行 tone( )
** NodeMCU 很類似 Arduino, 可以點這看我以前寫的關於 Arduino 的文章。(包括使用 Timer)
** 簡單使用 ESP8266 Timer 可以點這看 ESP8266 Timer and Ticker Example (或 這中文版本)
** 更多關於 ESP8266 Timer 可以點這看比較專業的說明
There are two types of timers on the ESP8266. There is os_timer, which is a software based interval timer and the os_timer apparently only has capacity to have seven timers set at one time. The second type of timer is a hardware based timer, hw_timer, of which there is apparently only one.
** ESP8266 ESP-12E Specs.   ESP8266 ESP-12E 規格書   ESP8266 ESP-12S 規格書   Docs.
** ESP8266 ESP-12F Specs.   ESP8266 ESP-12F 規格書  ESP8266 Wifi 模組使用手冊   ESP-12F
** ESP8266 core for Arduino
** ESP32 core for Arduino (Arduino core for ESP32 WiFi chip)
*** NodeMCU V3 _CH340 淘寶網   NodeMCU V3 _CP2102 淘寶網  
** 為協助台灣產業邁入AIoT(人工智慧+物聯網)時代台灣RISC-V產業聯盟日前正式成立(2019/03/11)
** ARM 霸權江山不保?晶心領軍開源指令集 RISC-V 趁勢而起(2019/03/19) RISC-V 是啥?(Wiki)
** 林百里:廣達AI財 30年也賺不完(2019/04/27)中美貿易戰也扯 AI (2019/05/21)
** 新思科技推動AIoT設計發展 開啟台灣IC設計產業新商機(2019/05/14)
** 【中美貿易戰】中國緊急宣布積體電路及軟體業 2 年免稅!(2019/05/23) 中美貿易戰對台影響(2019/05/16)
** 【稀土】到底是蝦秘碗糕?(2019/05/15)   【稀土】美國有備胎?(2019/05/21)

 
(九) 使用 Raspberry Pi 做為真實設備 取代(四)(五)的虛擬裝置
連到 IoT 互聯網實驗網站http://192.168.20.101 (資策會 內網)連到 IoT 互聯網實驗網站http://125.227.255.81 (資策會 public IP)認識 Raspberry Pi GPIO 腳位    (Board板子上的編號 vs. BCM 編號)
*第一次把玩樹莓派的新手 (到 Raspberry PI 官方網站 下載NOOBS作業系統並安裝到 Micro SD 卡)
*Maker入門手冊-Raspberry Pi3安裝及環境建置Raspberry Pi台灣樹莓派(教學)    (3B+ vs. 3B)

*LED blink using Raspberry Pi and Python  ( 其他 很多方法閃爍 LED  )
# ledBlink.py  -- #這個程式是 Raspberry Pi 的基本測試程式
#請先把實體編號 12 的 pin 腳連接到一個電阻再連接到 LED 的長腳,
# 然後 LED 的短腳連接到 GND
#執行程式後, 該 LED 將會不斷的閃爍 
import RPi.GPIO as GPIO    # Import Raspberry Pi GPIO library
from time import sleep     # Import the sleep function from the time module
ledPin = 12
GPIO.setwarnings(False)    # Ignore warning for now
GPIO.setmode(GPIO.BOARD)   # Use physical pin numbering
GPIO.setup(ledPin, GPIO.OUT, initial=GPIO.LOW)   # Set pin 12 to be an output pin
try:
   print('按下 Ctrl-C 可停止程式')
   while True: # Run forever
      GPIO.output(ledPin, GPIO.HIGH) # Turn on
      sleep(1)                  # Sleep for 1 second
      GPIO.output(ledPin, GPIO.LOW)  # Turn off
      sleep(1)                  # Sleep for 1 second
except KeyboardInterrupt:
    print('Bye bye 程式')
finally:
    GPIO.cleanup()   # 索有用到的 GPIO pin 恢復到 Input 狀態
在 RASPBERRY PI 3 MODEL B 建立 PYTHON 3.6 環境(測試 led.py) *RASPBERRY PI 3 MODEL B 與 DHT11 溫溼度感應器之應用(用 Python 3.6 + dht11.py) * 關於DHT-11等溫濕度感測器可以參看我在共匪的社群發表的文章, 可以讓你學到更深入的概念。 *關於讀取按鈕 DIY- 2 Ways to Add a Button to Your Raspberry Pi Project。 *也是按鈕R-Pi 讀取按鈕用 GPIO.add_event_detect( )。 *Raspberry-pi-3-model-b-與 瞬時按鈕(用 Python 3.6 + button_poll.py,button_event.py)
# button_poll.py
# 如何測試:  注意, 這範例故意把實體 Pin 12 用做按鈕, LED 改到實體 pin 16 喔!
#  (a) 實體 pin 12 插入杜邦公母線, 公的那端可以碰觸 GND 或網路線連接頭的金屬(也算 GND)
#  (b) 實體 pin 16 連接到一個電阻再連到 LED 的長腳, 並把短腳連到 GND
import time
import RPi.GPIO as GPIO   #引入 GPIO 套件

BUTTON_PIN = 18  #實體編號 12 的腳位,其 BCM 編號為 18。
LED_PIN = 23  #控制 LED 的腳位,實體編號 16 的腳位,其 BCM 編號為 23。

def my_callback(channel):
    print('按下按鈕') 
    GPIO.output(LED_PIN, GPIO.HIGH)
    time.sleep(0.1)
    GPIO.output(LED_PIN, GPIO.LOW) 

GPIO.setmode(GPIO.BCM)   ##使用 BCM 編號方式。  
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# pull_up_down=GPIO.PUD_UP用來開啟內建上拉電阻  
GPIO.setup(LED_PIN, GPIO.OUT)   #腳位設定為輸出模式。 

try:
    print('按下 Ctrl-C 可停止程式')
    while True:  # 以下檢察按鈕是否被按下
        if GPIO.input(BUTTON_PIN) == GPIO.LOW:
            my_callback(BUTTON_PIN)
        time.sleep(0.25)
except KeyboardInterrupt:
    print('關閉程式')
finally:
    GPIO.cleanup()  #確保程式結束後,用到的腳位皆回復到預設狀態。
############################################################################

# button_event.py
# 如何測試:  與上面那個 button_pool.py 相同連接方式
#  (a) 實體 pin 12 插入杜邦公母線, 公的那端可以碰觸 GND 或網路線連接頭的金屬(也算 GND)
#   (b) 實體 pin 16 連接到一個電阻再連到 LED 的長腳, 並把短腳連到 GND
import time
import RPi.GPIO as GPIO

BUTTON_PIN = 18
LED_PIN = 23

def my_callback(channel):
    print('按下按鈕')
    GPIO.output(LED_PIN, GPIO.HIGH)
    time.sleep(0.1)
    GPIO.output(LED_PIN, GPIO.LOW)

GPIO.setmode(GPIO.BCM)  
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)   
GPIO.setup(LED_PIN, GPIO.OUT) 
### 註冊當輸入腳位由高電位變成低電位 (GPIO.FALLING) 時,執行 my_callback 函數。
GPIO.add_event_detect(BUTTON_PIN, GPIO.FALLING, callback=my_callback, bouncetime=250) 

try:
    print('按下 Ctrl-C 可停止程式')
    while True:
        time.sleep(0.258)
except KeyboardInterrupt:
    print('關閉程式')
finally:
    GPIO.cleanup() # 用到的腳位皆回復到 INPUT
# https://reurl.cc/oEkpD 

# btnPollLED.py  -- 利用瞬時按鈕 Toggle LED 燈 開/關
# 如何測試: 注意, 這範例故意把 按鈕和 Led 的 pin 腳 跟前面兩範例對調 !!
#   (a) 實體 pin 16 插入杜邦公母線, 公的那端可以碰觸 GND 或網路線連接頭的金屬(也算 GND)
#   (b) 實體 pin 12 連接到一個電阻再連到 LED 的長腳, 並把短腳連到 GND
# 你當然可以改用其他 pin 
import RPi.GPIO as GPIO   #引入 GPIO 套件
import time

btnPin = 23  #實體編號 16 的腳位,其 BCM 編號為 23。
ledPin = 18  #控制 LED 的腳位,實體編號 12 的腳位,其 BCM 編號為 18。

def my_callback(channel):
    print('按下按鈕') 
    tmp = not GPIO.input(ledPin)   #  On / Off
    GPIO.output(ledPin, tmp)

GPIO.setmode(GPIO.BCM)   ##使用 BCM 編號方式。  
GPIO.setup(btnPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)   
     ### pull_up_down=GPIO.PUD_UP用來開啟內建上拉電阻  
GPIO.setup(ledPin, GPIO.OUT)   #腳位設定為輸出模式。 

try:
    print('按下 Ctrl-C 可停止程式')
    while True:  # 以下檢察按鈕是否被按下
        if GPIO.input(btnPin) == GPIO.LOW:
            time.sleep(0.168)  # simple Debounce
            my_callback(btnPin)
        time.sleep(0.25)
except KeyboardInterrupt:
    print('Bye Bye 程式')
finally:
    GPIO.cleanup() 

RaspBerry Pi Button.py 數位輸入 + 讀取按鈕 *關於 PC/Windows 上安裝使用 Python
想在自己電腦上安裝官方版本的 Python ?
可以 點這連到 Python 官方網站, 建議不要抓最新版(通常 Bug 多多) ! (雖然上圖顯示 3.7.2, 那是我測試安裝一下啦:-)
如果用 Windows 10, 或確定你的 Windows 是 64 位元作業系統 (用滑鼠右鍵點"電腦" 選 "內容"看看),
建議點這抓3.6.8 會連到 python.org/ftp/python/3.6.8/python-3.6.8-amd64.exe

根據官方網站聲明: 3.6.8 是 3.6 版的最後一次修正版 !
也可 點這抓 Anaconda   以及 使用 Jupyter Notebook   點這看 Jupyter Notebook 使用介紹
(這有 台大學生寫的簡易教學老共寫的Anaconda教學)
#* 如何利用 IoTtalk 系統控制 Raspberry Pi 連接的 LED 燈?
# 拿 Dummy_Device 的 Python 程式來修改 .. 注意以下 Code 紅色的部份
#
# DAI777.py  -- new version of Dummy Device DAI.py, modified by tsaiwn@cs.nctu.edu.tw
# you can get from here:  https://goo.gl/6jtP41   ; Search dummy + iottalk  for other files
import time, DAN, requests, random 
import threading, sys    #
import RPi.GPIO as GPIO  
ledPin = 12

GPIO.setmode(GPIO.BOARD)   # Use physical pin numbering
GPIO.setup(ledPin, GPIO.OUT, initial=GPIO.LOW)  # 初始關燈  

# ServerURL = 'http://Your_server_IP_or_DomainName:9999' #with no secure connection
ServerURL = 'http://192.168.20.101:9999' #with no secure connection

# ServerURL = 'https://demo.iottalk.tw' #with SSL secure connection
# ServerURL = 'https://Your_DomainName' #with SSL connection  (IP can not be used with https)
Reg_addr = None #if None, Reg_addr = MAC address

mac_addr = 'C86770773739'  # put here for easy to modify;;  the mac_addr in DAN.py is NOT used
# Copy DAI.py to DAI2.py and then modify the above mac_addr, then you can have two dummy devices
Reg_addr = mac_addr   # Otherwise, the mac addr generated in DAN.py will always be the same !

DAN.profile['dm_name']='Dummy_Device'   # you can change this but should also add the DM in server
DAN.profile['df_list']=['Dummy_Sensor', 'Dummy_Control']
#DAN.profile['d_name']= None # None for autoNaming
DAN.device_registration_with_retry(ServerURL, Reg_addr)

# global gotInput, theInput
gotInput=False
theInput="haha"
allDead=False

def doRead( ):
    global gotInput, theInput, allDead
    while True:
        if gotInput:
           time.sleep(0.1)
           continue  # go back to while
        try:
           theInput = input("Give me data: ")
        except Exception:
           allDead = True  # 通知老闆說我死了喔 :-)
           print("\n\nDeregister " + DAN.profile['d_name'] + " !!!\n",  flush=True)
           DAN.deregister()
           sys.stdout = sys.__stdout__
           print(" Thread say Bye bye ---------------", flush=True)
           sys.exit( );   ## break  # raise   #  ?
        gotInput=True
        if theInput !='quit' and theInput != "exit":
           print("Will send " + theInput, end="   , ")

#creat a thread to do Input data from keyboard, by tsaiwn@cs.nctu.edu.tw
threadx = threading.Thread(target=doRead)
threadx.daemon = True
threadx.start()

while True:
    try:
    #Pull data from a device feature called "Dummy_Control"
        value1=DAN.pull('Dummy_Control')
        if value1 != None:     #   注意這列不可以刪除喔 !
            ggg = value1[0]
            if  ggg ==  0: GPIO.output(ledPin, GPIO.LOW)
            else:  GPIO.output(ledPin, GPIO.HIGH)
    #
    #Push data to a device feature called "Dummy_Sensor" 
        if gotInput:   # 小弟(thread)已經幫忙讀到資料放在 theInput
           if theInput =='quit' or theInput=="exit":
              break;  # sys.exit( );
           #value2=random.uniform(1, 10)
           try:
              value2=float( theInput )
           except:
              value2=0
           gotInput=False   # so that you can input again ; 讓小弟知道我已經取走資料
           if(allDead): break;
           DAN.push ('Dummy_Sensor', value2,  value2)

    except Exception as e:
        print(e)
        if str(e).find('mac_addr not found:') != -1:
            print('Reg_addr is not found. Try to re-register...')
            DAN.device_registration_with_retry(ServerURL, Reg_addr)
        else:
            print('Connection failed due to unknow reasons.')
            time.sleep(1.258) 
    try:
       time.sleep(0.2)
    except KeyboardInterrupt:
       break
time.sleep(0.5)
try: 
   DAN.deregister()
except Exception as e:
   print("===")
print("Bye ! --------------", flush=True)
sys.exit( );   ####### End of DAI777.py 


#* 加入讀取 R-Pi 板子上的瞬時按鈕, (取代原先讀取鍵盤的部份), 把按鈕的值 push 去 IoTtalk 伺服器
# 注意, 以下的 pin 腳編號故意用板子上實際的編號 -- GPIO.setmode(GPIO.BOARD)
## 注意以下 Code 紅色的部份
#
#
# DAI888.py  -- new version of Dummy Device DAI.py, modified by tsaiwn@cs.nctu.edu.tw
#  
import time, DAN, requests, random 
import threading, sys    #
import RPi.GPIO as GPIO  ##
ledPin = 12  # 注意這程式用實體編號 -- mode 用 GPIO.BOARD
btnPin = 16  #實體編號 16 的腳位,其 BCM 編號為 23 
ledLocal = 12   # 如果你有兩個 LED, 把這 12 改為別的值 ; 這用來監看按鈕有沒成功按到 !?

GPIO.setmode(GPIO.BOARD)   # Use physical pin numbering
GPIO.setup(ledPin, GPIO.OUT, initial=GPIO.LOW)  # 初始關燈 
GPIO.setup(ledLocal, GPIO.OUT, initial=GPIO.LOW)  # Local 用的 LED
GPIO.setup(btnPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)   
       ### 上面句子的, pull_up_down=GPIO.PUD_UP用來開啟內建上拉電阻  

# ServerURL = 'http://Your_server_IP_or_DomainName:9999' #with no secure connection
ServerURL = 'http://192.168.20.101:9999' #with no secure connection

# ServerURL = 'https://demo.iottalk.tw' #with SSL secure connection
# ServerURL = 'https://Your_DomainName' #with SSL connection  (IP can not be used with https)
Reg_addr = None #if None, Reg_addr = MAC address

mac_addr = 'C85770738749'  # put here for easy to modify;;  the mac_addr in DAN.py is NOT used
# Copy DAI.py to DAI2.py and then modify the above mac_addr, then you can have two dummy devices
Reg_addr = mac_addr   # Otherwise, the mac addr generated in DAN.py will always be the same !

DAN.profile['dm_name']='Dummy_Device'   # you can change this but should also add the DM in server
DAN.profile['df_list']=['Dummy_Sensor', 'Dummy_Control']
#DAN.profile['d_name']= None # None for autoNaming
DAN.device_registration_with_retry(ServerURL, Reg_addr)

# global gotInput, theInput
gotInput=False
allDead=False
theInput="haha"     # 現在 theInput 沒用了 !  
myState = 0   # 記住按鈕的狀態 

# 以下這個小弟 (thread 執行緒) 不再讀取鍵盤, 改為檢查按鈕
def doRead( ):
    global gotInput, theInput, allDead   # 
    global myState    ### 按鈕狀態
    while True:
        if gotInput:   # 老闆(主程式)還沒拿走
           time.sleep(0.1)
           continue  # go back to while 阿就一直等到老闆拿走啊 
        #theInput = input("Give me data: ")
        try:
           time.sleep(0.053)
           if GPIO.input(btnPin) == GPIO.LOW:    # 按了按鈕
              time.sleep(0.168)  # simple Debounce
              myState = 1 - myState     # Toggle 
              gotInput=True   # 通知老闆 (主程式 thread)
              time.sleep(0.25)   #故意不立即又檢查按鈕
        except Exception:
           allDead = True  # 通知老闆說我死了喔 :-)
           print("\n\nDeregister " + DAN.profile['d_name'] + " !!!\n",  flush=True)
           DAN.deregister()
           sys.stdout = sys.__stdout__
           print(" Thread say Bye bye ---------------", flush=True)
           sys.exit( );   ## break  # raise   #  ?
        ###### 

#creat a thread to do Input data from keyboard, by tsaiwn@cs.nctu.edu.tw
threadx = threading.Thread(target=doRead)
threadx.daemon = True
threadx.start()

while True:
    try:
    #Pull data from a device feature called "Dummy_Control"
        value1=DAN.pull('Dummy_Control')
        if value1 != None:     #  有拉到資料 ! 
            ggg = value1[0]   # 取出資料
            if  ggg ==  0: GPIO.output(ledPin, GPIO.LOW)   # 關燈
            else:  GPIO.output(ledPin, GPIO.HIGH)   ##  開燈
    ##
    #Push data to a device feature called "Dummy_Sensor" 
        if gotInput:    ### 小弟說已經有資料要送去 IoTtalk Server 
           #if theInput =='quit' or theInput=="exit":
           #   sys.exit( );
           #value2=random.uniform(1, 10)
           #value2=float( theInput )
           value2=float( myState )   # 丟實數以後比較有彈性 
           gotInput=False   # so that you can input again (通知負責讀取的小弟 thread)
           if(allDead): break;
           DAN.push ('Dummy_Sensor', value2,  value2)  #
           GPIO.output(ledLocal, myState)   # 故意用 Local 燈對照以便知道有沒按到按鈕 

    except KeyboardInterrupt:
        break;
    except Exception as e:
        print(e)
        if str(e).find('mac_addr not found:') != -1:
            print('Reg_addr is not found. Try to re-register...')
            DAN.device_registration_with_retry(ServerURL, Reg_addr)
        else:
            print('Connection failed due to unknow reasons.')
            time.sleep(1)    
    try:
       time.sleep(0.23)
    except KeyboardInterrupt:
       break
time.sleep(0.5)
try: 
   DAN.deregister()
except Exception as e:
   print("===")
GPIO.cleanup() 
print("Bye ! --------------", flush=True)
sys.exit( );   ########  End of DAI888.py 

用 R-Pi 的 Python 讀取 dip-switch
Using Switch with Raspberry Pi – Python (其實寫得不太好!)
*如果用類似 Dip-Switch, 如何當 發現按鈕有變化才 push 去 IoTtalk Server ?

#修改之前的小弟函數..
allDead = False
myState = GPIO.input(btnPin)
oldState = myState
gotInput=False   # 表示沒變化, 老闆不會送按鈕狀態出去
##第一次, 利用以下設 True 通知老闆 (主程式 thread) 假裝 說按鈕有變化 
gotInput=True   # 如果一開始不要送, 在這列最左邊加 # 註解這列即可
# 以下這個小弟 (thread 執行緒) 不再讀取鍵盤, 改為檢查按鈕
def doRead( ):
    global gotInput, theInput, allDead   # 
    global myState, oldState    ### 按鈕狀態
    while True:
        if gotInput:   # 老闆(主程式)還沒拿走
           time.sleep(0.15)
           continue  # go back to while 阿就一直等到老闆拿走啊 
        #theInput = input("Give me data: ")
        try:
           time.sleep(0.055)
           myState = GPIO.input(btnPin)     # 讀取按鈕
           if myState != oldState:    # 按鈕有改變
              time.sleep(0.168)  # simple Debounce
              oldState = myState  # 記住 以便之後比對
              gotInput=True   # 通知老闆 (主程式 thread) 說按鈕有變化, 請它送出 myState
              time.sleep(0.25)   #故意不立即又檢查按鈕
        except KeyboardInterrupt:
           break;
        except Exception:
           break;    
    ### thread terminate the while Loop .. do clearn up 
    allDead = True   # 通知老闆說我死了喔 :-)
    print("\n\nDeregister " + DAN.profile['d_name'] + " !!!\n",  flush=True)
    try:
       DAN.deregister()
    except Exception:
       pass
    sys.stdout = sys.__stdout__
    print(" Thread say Bye bye ---------------", flush=True)
    sys.exit( );   ## break  # raise   #  ?  
    ###### end of doRead( )  for  Dip-Switch ###### 
   

*RaspberryPi-3B+ Pi3B+&電源+16GB Pi3B+ for OpenCV套件 更多搭配選擇 (德源科技) USB網路卡(較貴) (可自己找一下其他有開發票的)
有NT$68的USB網卡這也便宜 (可自己找一下其他有開發票的)
**USB網路卡NT$50USB網路卡 *USB網路卡 ***免驅動USB網路卡USB3.0 Giga網路卡 or NT$299  網路線

   
(十)Rabboni 小玩具   ( ICM-20689 (類似 MPU6050) 六軸感測器三軸重力加速度 + 三軸陀螺儀  )
  * 點這看今天講義 Rabboni_d5.pdf (關於 Dummy Device 請看前面(四)(五(六)各大項)
  * 點這看 Rabboni API 文件(含BLE/USB範例)    或   Rabboni API 文件 .pdf 檔案   ( .docx 檔案)

  * 如果要送資料去IoTtalk Server, 點這 抓前兩天上課用的程式碼 Rabboni.zip
  ** 如果讀出的數據都很小, 請改用舊的(0.7版)程式庫: (舊版重力加速度 1g 用 9.8 表示, 0.8版開始 1g 用 1.0 表示)
      pip install rabboni==0.7
  ** 如果你的電腦不認識 pip 命令, 可利用 Python 內建的 pip 模組 "生" 出 pip 命令:
      python -m pip install --upgrade pip
  (萬一有 Error, 改用以下:)
      python -m pip install --upgrade pip --user
  ** 如果要建立隔離的 Python 虛擬環境: (如果你沒有系統管理員的權限就必須這麼做!)
      python -m venv myvenv
      cd myvenv
      Scripts\activate
      現在開始已經在隔離的Python 虛擬環境 myvenv
*10-1 簡單範例讀取 Rabboni 六軸資料, 最多讀取100次
###  USB.py   (USB mode)
# -*- coding: UTF-8 -*-
## 原範例 USB.py  ; 注意, 必要時, 就是如果檔案存檔用的編碼是Big5, 把上列的 UTF-8 改為 Big5
from rabboni import *
rabbo = Rabboni(mode = "USB") #先宣告一個物件                         
rabbo.connect()#連結上rabboni,若沒插上會報錯
print ("Status:",rabbo.Status)
try:
    rabbo.read_data()
    while True:#一直打印資料 直到結束程式  
        rabbo.print_data()#print資料  ; 會 delay 一下
        print (rabbo.data_num)
        if rabbo.Cnt > 10:
            rabbo.rst_count() #重置 計步器 count , 會delay一下
        if rabbo.data_num >= 100:
            break
except KeyboardInterrupt:  # 敲 CTRL_C 結束程式
    print('Shut done!')
print ("所有的", rabbo.data_num, " 個 Accx: ")
print(rabbo.Accx_list)#印出到結束程式時的所有Accx值
try:
   rabbo.stop()#停止運作
except Exception: pass
print('====== Bye bye')

如果讀出的數據都很小, 請改用舊的程式庫:
      pip install rabboni==0.7
*10-2 稍維修改原先的簡單範例, 讀取 Rabboni 六軸資料, 並直接存取六軸的資料, 最多讀取 8 次
### testUSB000.py   (USB mode)
# coding=Big5   #UTF-8  #Big5   #看檔案儲存時選哪種編碼; 編碼 先寫先贏
# -*- coding: UTF-8 -*-     # 另一種寫法, 但必須寫在 #LINE 2 或 LINE 1
### testUSB000.py  -- by tsaiwn@cs.nctu.edu.tw
from rabboni import *
import sys, time, math
try:
   rabbo = Rabboni(mode = "USB")  #先宣告一個物件  
   rabbo.connect()  #連結上rabboni,若沒插上會報錯
except Exception as e:
   print(e);   print("可能你沒把 Rabboni 用 USB 連接好 !   ")                              
   sys.exit( )
print ("Rabboni Status: ",rabbo.Status)
print("5 seconds to start:  ", end="", flush=True);
for i in range(1, 6)  :   # 1, 2, 3, 4, 5
   print(6-i, ". ", end="", flush=True);
   time.sleep(1);
print( )
try:
    rabbo.read_data()
    while True:#一直打印資料 直到結束程式
        rabbo.print_data()  #print資料, 會delay一下; 可把這列註解掉再測試看看  !!!
        print("---")   
        print("Acc x, y, z: ", rabbo.Accx, rabbo.Accy, rabbo.Accz,  end="    ;  ")
        print(math.sqrt( rabbo.Accx * rabbo.Accx + rabbo.Accy * rabbo.Accy + rabbo.Accz*rabbo.Accz) )
        print("Gyr x, y, z: ", rabbo.Gyrx, rabbo.Gyry,rabbo.Gyrz,)
        print ("# ", rabbo.data_num, )  
        if rabbo.Cnt > 100: rabbo.rst_count() #重置 計步器 count , 會delay一下
        if rabbo.data_num >= 8: break
except KeyboardInterrupt: pass; #結束程式
finally:
   print("=== Bye ", end="")
try:
   rabbo.stop()#停止運作
except Exception: pass
#print(rabbo.Accx_list) #印出到結束程式時的所有Accx值
print('====== Bye bye, data# =', rabbo.data_num)
### end of the program  testUSB000.py

如果讀出的數據都很小, 請改用舊的程式庫:
      pip install rabboni==0.7
*10-3 修改原先的簡單範例, 加入檢查 Rabboni 姿態 (檢查移動的 checkMoving( ) 在此程式後面)
### testUSB002.py  (USB mode)
# coding=Big5   #UTF-8  #Big5   #看檔案儲存時選哪種編碼; 編碼 先寫先贏
# -*- coding: UTF-8 -*-     # 另一種寫法, 但必須寫在 #LINE 2 或 LINE 1
### testUSB002.py  -- by tsaiwn@cs.nctu.edu.tw
from rabboni import *
import sys, time, math
     ###  checkMoving(  ) 函數可以放這裡 
scale=1.0   # use 9.8 for rabboni 0.8 ;  use 1.0 if Library is rabboni 0.7 
oldAns=0
def pose( ):    # 這 function 依據重力加速度三軸資料判定 Rabboni 的姿態, 回傳 0 到 18
   global oldAns
   ans=oldAns
   if( abs(rabbo.Accx)< 1.98/scale and abs(rabbo.Accy) < 1.98/scale):
      if(rabbo.Accz > 5.568/scale): ans = 1   #正面朝上幾乎平放
      if(rabbo.Accz < -5.68/scale): ans = 2   #背面朝上幾乎平放
   elif( rabbo.Accz > 1.1968/scale ):
      if(rabbo.Accx > 0 and rabbo.Accy < 0): 
         ans = 3   # 正面左傾
         if( abs(rabbo.Accy + rabbo.Accz) < 1.38/scale ): ans=13  # 正面左傾約45度
      if(rabbo.Accx < 0 and rabbo.Accy > 0): 
         ans = 4   # 正面右傾
         if( abs(rabbo.Accy - rabbo.Accz) < 1.38/scale ): ans=14  # 正面右傾約45度
      if(rabbo.Accx > 0 and rabbo.Accy > 0): ans = 7   # 正面前端向上
      if(rabbo.Accx < 0 and rabbo.Accy < 0): ans = 8   # 正面前端向下
   elif( rabbo.Accz < -1.1968/scale ):
      if(rabbo.Accx < 0 and rabbo.Accy > 0):
         ans = 5   # 背面左傾
         if( abs(rabbo.Accy + rabbo.Accz) < 1.38/scale ): ans=15  # 背面左傾約45度
      if(rabbo.Accx > 0 and rabbo.Accy < 0): 
         ans = 6   # 背面右傾
         if( abs(rabbo.Accy - rabbo.Accz) < 1.38/scale ): ans=16  # 背面右傾約45度
      if(rabbo.Accx > 0 and rabbo.Accy > 0): ans = 9   # 背面前端向上
      if(rabbo.Accx < 0 and rabbo.Accy < 0): ans = 10  # 背面前端向下
   elif( abs(rabbo.Accz) <= 1.1968/scale ):    # 差不多 :-)
      if(rabbo.Accx < 0 and rabbo.Accy > 0): ans = 11  # 正面向右垂直
      if(rabbo.Accx > 0 and rabbo.Accy < 0): ans = 12  # 正面向左垂直
      if(rabbo.Accx > 0 and rabbo.Accy > 0): ans = 17  #  前端向上垂直
      if(rabbo.Accx < 0 and rabbo.Accy < 0): ans = 18  # 前端向下垂直
   oldAns=ans
   return ans

msg = ["姿態不知", "正面朝上幾乎平放", "背面朝上幾乎平放", 
       "正面向上左傾", "正面向上右傾", "背面向上左傾", "背面向上右傾", 
       "正面前端向上", "正面前端向下", "背面前端向上", "背面前端向下",
       "正面向右垂直", "正面向左垂直", "正面左傾約45度","正面右傾約45度",
       "背面左傾約45度","背面右傾約45度","前端向上垂直","前端向下垂直",]
faceAB = [0, 1, 2, 1, 1, 2, 2,  1, 1, 2, 2, 1, 1, 1, 1, 2, 2,0,0, ]  # 1 正面; 2背面
try:
   rabbo = Rabboni(mode = "USB")  #先宣告一個物件  
   rabbo.connect()  #連結上rabboni,若沒插上會報錯
except Exception as e:
   print(e);   print("可能你沒把 Rabboni 用 USB 連接好 !   ")                              
   sys.exit( )
print ("Rabboni Status: ",rabbo.Status)
print("3 seconds to start:  ", end="", flush=True);
for i in range(1, 3)  :   # 1, 2, 3, 4, 5
   print(3-i, ". ", end="", flush=True);
   time.sleep(1);
print( )  #   要開始囉 ..
dtn=0    ## data Number 看看程式讀取 Rabboni 有多快
dataNum=-1  # 記住舊的 rabbo.data_num  
try:
    rabbo.read_data()
    while True:#一直打印資料 直到結束程式
        #rabbo.print_data()#print資料 ; 會 delay   ### 若沒叫用 rabbo.print_data()  會太快 !
        print("---")
        #print("Hex: ", rabbo.Hex_data); 
        print("Acc x, y, z: ", rabbo.Accx, rabbo.Accy, rabbo.Accz,  end="    ;  ")
        print(math.sqrt( rabbo.Accx * rabbo.Accx + rabbo.Accy * rabbo.Accy + rabbo.Accz*rabbo.Accz) )
        print("Gyr x, y, z: ", rabbo.Gyrx, rabbo.Gyry,rabbo.Gyrz,)
        print ("# ", rabbo.data_num, )
        face = pose( )   ### 算出姿勢代碼
        print( msg[ face ] )     # 如要檢查動作, Enable 下列, 並把用到的函數放進來前面
        #checkMoving(faceAB[face])   # faceAB[face] 判斷正面向上(1) or 背面向上(2)    
        if rabbo.Cnt > 100: rabbo.rst_count() #重置count 會delay一下
        if rabbo.data_num >= 8: break 
        if( rabbo.data_num != dataNum):
           dataNum = rabbo.data_num
           dtn= dataNum * 1000   ###   看看電腦有多快 ?
        time.sleep(0.05)    #   time.sleep(0.001)   # time.sleep(0.05)  # 或註解掉這列
        dtn += 1
        print("dtn = ", dtn)  # 看看電腦有多快 ?   
except KeyboardInterrupt: pass; #結束程式
finally:
   rabbo.stop()#停止運作
   print("=== Bye bye ===")
print("###### ######");
### end of the program  testUSB002.py

### 判斷動作的 function  checkMoving( ) ; 放在上面程式碼適當地方!
def checkMoving(faceNow: int):
   if(rabbo.Gyrx < -500 and rabbo.Gyry < -500):
      print("向左旋轉")
   if(rabbo.Gyrx > 500 and rabbo.Gyry > 500):
      print("向右旋轉")
   if(faceNow == 1):    # 正面向上
      if(rabbo.Gyrx > 500 and rabbo.Gyry < -500):
         print("正面前端向上向我旋轉")
      if(rabbo.Gyrx < -500 and rabbo.Gyry > 500):
         print("正面前端向下凹旋轉")
   elif(faceNow == 2):   #背面向上
      if(rabbo.Gyrx > 500 and rabbo.Gyry < -500):
         print("背面前端向下凹旋轉")
      if(rabbo.Gyrx < -500 and rabbo.Gyry > 500):
         print("背面前端向上向我旋轉")

如果讀出的數據都很小, 請改用舊的程式庫:
      pip install rabboni==0.7
*10-4 這是 Blue Tooth 藍芽版本
### testBLE.py  (BLE mode)
# coding=Big5   #UTF-8  #Big5   #看檔案儲存時選哪種編碼; 編碼 先寫先贏
# -*- coding: UTF-8 -*-
from rabboni import *
import time, sys
try:
   rabbo = Rabboni(mode="BLE") #先宣告一個物件
   rabbo.scan() #掃描所有藍芽Device
except Exception as e:
   print(e)
   print("可能你沒把 Rabboni 藍芽開啟或 Rabboni 沒電了 !")
   print("也可能缺 BLED112 驅動程式, 或 Dongle 忘了插入!")
   print("參考 http://www.picaxe.com/BLED112-Bluetooth-USB-Dongle/")
   sys.exit( )
rabbo.print_device() # 列出所有藍芽Device
print("====== 以上是掃描到的 BLE MAC =====")
#rabbo.connect("D1:FA:F0:F2:12:29")#依照MAC連接
rabbo.connect("C6:98:9D:BE:62:92")  #依照MAC連接
rabbo.discover_characteristics()#掃描所有服務 可略過
rabbo.print_char()#列出所有服務 可略過
# print (rabbo.characteristics)
# print (rabbo.Status)
time.sleep(3)  # 等 3 秒讓你看 :-)
rabbo.read_data()#讀取資料 必跑

try:
    while True:#一直打印資料 直到結束程式
        rabbo.print_data()#print資料
        if rabbo.Cnt == 100:
            rabbo.rst_count()
except KeyboardInterrupt:#結束程式
    print('Shut done!')
    print (rabbo.Accx_list)#印出到結束程式時的所有Accx值
    rabbo.stop()#停止dongle
    rabbo.write_csv(data = rabbo.Accx_list,file_name ="AccX")#將Accx寫出csv檔
    rabbo.plot_pic(data = rabbo.Accx_list,file_name = "AccX",show = True)#將Accx畫出圖案並存檔
finally:
    rabbo.stop()
### end BLE mode example ###

如果讀出的數據都很小, 請改用舊的程式庫:
      pip install rabboni==0.7
*10-5 這是 USB 版本, 讀取 168次, 會統計六軸的最大與最小值, 最後會印出讓你參考
### testUSB.py   (USB mode)
# coding=Big5   #UTF-8  #Big5   #看檔案儲存時選哪種編碼; 編碼 先寫先贏
# -*- coding: UTF-8 -*-
from rabboni import *
import sys, time, math
CNT_LIMIT = 38   # 當 Cnt > 38 會叫 rabbo.rst_count() 把 Cnt 重置 為 0
DATA_LIMIT = 168   # 讀取幾次 Data 後停止 program ?

try:
   rabbo = Rabboni(mode = "USB")  #先宣告一個物件  
   rabbo.connect()  #連結上rabboni,若沒插上會報錯
except Exception as e:
   print(e)
   print("可能你沒把 Rabboni 用 USB 連接好 !   ")                              
   #print("也可能缺 BLED112 驅動程式, 或 Dongle 忘了插入!")
   #print("參考 http://www.picaxe.com/BLED112-Bluetooth-USB-Dongle/")
   sys.exit( )

print ("Status:",rabbo.Status)
gg = input("要不要把 Count 歸 0 ? ")
if gg == "yes" or gg == "y": rabbo.rst_count()
try:
    rabbo.read_data()
    while True:#一直打印資料 直到結束程式
        rabbo.print_data()#print資料
        print("---")
        print("Acc x, y, z: ", rabbo.Accx, rabbo.Accy, rabbo.Accz,  end="    ;  ")
        print(math.sqrt( rabbo.Accx * rabbo.Accx + rabbo.Accy * rabbo.Accy + rabbo.Accz*rabbo.Accz) )
        print("Gyr x, y, z: ", rabbo.Gyrx, rabbo.Gyry,rabbo.Gyrz,)
        print ("# ", rabbo.data_num, )
        if rabbo.Cnt > CNT_LIMIT:
            rabbo.rst_count() #重置count 會delay一下
        if rabbo.data_num >= DATA_LIMIT:
            break

except KeyboardInterrupt:#結束程式
    print('Shut done!')
    # print (rabbo.Accx_list)#印出到結束程式時的所有Accx值
except Exception: # something wrong
    print(e)
finally:
    rabbo.stop()#停止運作
    print("=== done ===")
    rabbo.write_csv(data = rabbo.Accx_list,file_name ="AccX")#將Accx寫出csv檔
    rabbo.plot_pic(data = rabbo.Accx_list,file_name = "AccX",show = True)#將Accx畫出圖案並存檔
print("====== ====== ======")
xaaB = [max(rabbo.Accx_list), max(rabbo.Accy_list), max(rabbo.Accz_list), ]
xaaS = [min(rabbo.Accx_list), min(rabbo.Accy_list), min(rabbo.Accz_list), ]
xggB = [max(rabbo.Gyrx_list), max(rabbo.Gyry_list), max(rabbo.Gyrz_list), ]
xggS = [min(rabbo.Gyrx_list), min(rabbo.Gyry_list), min(rabbo.Gyrz_list), ]
print("ACC Max:", xaaB)
print("ACC Min:", xaaS)
print("Gyr Max:", xggB)
print("Gyr Min:", xggS)
### end of the program
### end of the program testUSB.py



如果讀出的數據都很小, 請改用舊的程式庫:
      pip install rabboni==0.7
*10-6 這是 BLE 版本, example00.py 搭配 pylive.py
# pylive.py
import matplotlib.pyplot as plt
import numpy as np

# use ggplot style for more sophisticated visuals
plt.style.use('ggplot')

def live_plotter(x_vec,y1_data,line1,identifier='',pause_time=0.1):
    if line1==[]:
        # this is the call to matplotlib that allows dynamic plotting
        plt.ion()
        fig = plt.figure(figsize=(13,6))
        ax = fig.add_subplot(111)
        # create a variable for the line so we can later update it
        line1, = ax.plot(x_vec,y1_data,'-o',alpha=0.8)        
        #update plot label/title
        plt.ylabel('Y Label')
        plt.title('Title: {}'.format(identifier))
        plt.show()
    
    # after the figure, axis, and line are created, we only need to update the y-data
    line1.set_ydata(y1_data)
    # adjust limits if new data goes beyond bounds
    if np.min(y1_data) <= line1.axes.get_ylim()[0] or np.max(y1_data) >= line1.axes.get_ylim()[1]:
        plt.ylim([np.min(y1_data)-np.std(y1_data),np.max(y1_data)+np.std(y1_data)])
    # this pauses the data so the figure/axis can catch up - the amount of pause can be altered above
    plt.pause(pause_time)
    
    # return line so we can update it again in the next iteration
    return line1

# the function below is for updating both x and y values (great for updating dates on the x-axis)
def live_plotter_xy(x_vec,y1_data,line1,identifier='',pause_time=0.01):
    if line1==[]:
        plt.ion()
        fig = plt.figure(figsize=(13,6))
        ax = fig.add_subplot(111)
        line1, = ax.plot(x_vec,y1_data,'r-o',alpha=0.8)
        plt.ylabel('Y Label')
        plt.title('Title: {}'.format(identifier))
        plt.show()
        
    line1.set_data(x_vec,y1_data)
    plt.xlim(np.min(x_vec),np.max(x_vec))
    if np.min(y1_data) <= line1.axes.get_ylim()[0] or np.max(y1_data) >= line1.axes.get_ylim()[1]:
        plt.ylim([np.min(y1_data)-np.std(y1_data),np.max(y1_data)+np.std(y1_data)])

    plt.pause(pause_time)
    
    return line1
   
### example00.py   -- 需要用到 pylive.py ;且請先 pip install rabboni
# -*- coding: UTF-8 -*-
# coding=UTF-8    #  簡單寫法, 如檔案是 Big5 編碼用 coding=Big5
from rabboni import *
from pylive import live_plotter
import numpy as np
import sys, time

try:
   rabbo = Rabboni(mode="BLE") #先宣告一個物件
except Exception as e:
   print(e)
   print("可能你沒把 Rabboni 開啟或 Rabboni 沒電了 !")
   sys.exit( )
rabbo.scan() #掃描所有藍芽Device
rabbo.print_device() # 列出所有藍芽Device
rabbo.connect("DA:E3:4C:8E:F1:6F")#依照MAC連接
rabbo.discover_characteristics()#掃描所有服務 可略過
rabbo.print_char() #列出所有服務 可略過
# print (rabbo.characteristics)
# print (rabbo.Status)
rabbo.read_data() #讀取資料 必跑

size = 100
x_vec = np.linspace(0,1,size+1)[0:-1]
y_vec = np.zeros((100,), dtype=float)
line1 = []

try:
    while True:#一直打印資料 直到結束程式
        #rabbo.print_data() #print資料
        ra=rabbo.data_num
        print('length',ra)
        val = rabbo.Accz
        print('value=',val)
        
        print('y=',y_vec)
        y_vec[-1] = val
        line1 = live_plotter(x_vec,y_vec,line1,pause_time=0.0001)
        y_vec = np.append(y_vec[1:],0.0)
        if rabbo.Cnt == 100: 
           rabbo.rst_count()
        
except KeyboardInterrupt: #結束程式  :  敲 CTRL_C
    print('Shut done!')
    print (rabbo.Accx_list) #印出到結束程式時的所有Accx值
    rabbo.stop() #停止dongle
    rabbo.write_csv(data = rabbo.Accx_list,file_name ="AccX") #將Accx寫出csv檔
    rabbo.plot_pic(data = rabbo.Accx_list,file_name = "AccX",show = True) #將Accx畫出圖案並存檔
finally:
    rabbo.stop()
   

關於 Joint 函數的寫法與應用請參考(三)三個人用手機控制同一個燈
  ** 結合 IoTtalk Server (Rabboni 把資料送去 IoTtalk Server)
  * 點這 抓前兩天上課用的程式碼 Rabboni.zip
          解壓縮後,
          使用子目錄 IoTtalk_v1_USB 內的 DAN.py 和 csmapi.py 來配合以下程式碼 (for Rabboni)

如果讀出的數據都很小, 請改用舊的程式庫:
      pip install rabboni==0.7
*10-7 這是 USB 版本, DAI_Rab5u.py -- 資料會送去 demo.iottalk.tw 的 Rabboni 裝置
    ** 可找一個IDF/ODF接近的 DM (Device Model)來用, 或 參考(七)另行建立一個新的 Device Model (DM) 設備類別
    ** 以下範例是使用我另外建立的一個叫做 Rabboni 的 Device Model, 除了六軸資料, 我另加一個 Float (float) 和一個 Total (字串)
    ** 請注意看以下紅色字體的 code; 為了與別人的 Rabboni 區別, 請修改 Rabboni_BLE_address

    執行以下程式碼, 測試: (a)把Rabboni正面放著 (b)把 Rabboni 背面向上放著 (c)用力搖 Rabboni
### DAI_Rab5u.py -- Rabboni with IoTtalk  (USB mode)
# coding=Big5   #UTF-8  #Big5   ## 另外一種寫法, 但必須寫在 #LINE 2 或 LINE 1
# -*- coding: UTF-8 -*-
### DAI_Rab5u.py    # need DAN.py  and csmapi.py
import time, requests
import DAN
from rabboni import *
import sys, time, math, random

scale=1.0   # use 9.8 for rabboni 0.8 ;  use 1.0 if Library is rabboni 0.7 
Rabboni_BLE_address = 'C6:98:9D:BE:62:92'  # 你 Rabboni 的 MAC address  (其實跟 MAC 無關 :-)
ServerURL = 'http://demo.iottalk.tw:9999'  # 請確定 IoTtalk Server 
try:
   rabbo = Rabboni(mode = "USB") #先宣告一個物件
   rabbo.connect() #連結上rabboni,若沒插上會報錯
except Exception as e:
   print(e);   print("可能你沒把 Rabboni 用 USB 連接好 !   ")                              
   sys.exit( )
print ("Status:", rabbo.Status)
rabbo.read_data()
Reg_addr = Rabboni_BLE_address

DAN.profile['dm_name']='Rabboni'     # 對應到 IoTtalk 系統的 Device Model (DM) name 
DAN.profile['df_list']=['Acceleration', 'Gyroscope', "Float", "Total"]   # Device Feature 可自由增加
DAN.profile['d_name']= Rabboni_BLE_address
DAN.device_registration_with_retry(ServerURL, Reg_addr)  #跟 IoTtalk Server 註冊  
oldAx=0
oldGx=0
yyBBB=88  # 用力搖動 Rabboni 的 Float 這越來越大, 第一次故意限制不要超過這
yyOLD=0   # myFloat 變小的下限  ( 在 Rabboni 的 'Float' )
ggOLD=[0, 0, 0]    # 記住上一次的 Gyr? 陀螺儀 的值  
def calcGG( ):
    global ggOLD, yyOLD, yyBBB
    ans = 0
    if( abs(rabbo.Gyrx - ggOLD[0]) > 200): ans += abs(rabbo.Gyrx - ggOLD[0])/20
    if( abs(rabbo.Gyry - ggOLD[1]) > 300): ans += abs(rabbo.Gyrx - ggOLD[0])/33 
    if( abs(rabbo.Gyrz - ggOLD[2]) > 500): ans += abs(rabbo.Gyrx - ggOLD[0])/55
    yy = math.sqrt( rabbo.Accx * rabbo.Accx + rabbo.Accy * rabbo.Accy + rabbo.Accz*rabbo.Accz)
    if(yy > 18/scale): yy = 18/scale
    ans = ans * yy / 6.1568 *scale 
    if(ans < yyOLD): ans = yyOLD
    if(ans > yyBBB): ans = yyBBB
    if(ans > 1200): ans = 1200      # 限制上限為 1200  
    yyOLD = ans - 33  # 下次最多減少 33
    yyBBB = ans + 88  # 下次最多增加 88   
    if(yyOLD < 0): yyOLD = 0   # 故意不要有負的 :-)  # 因剛剛 ans - 33 可能小於 0
    return ans

gg = input("現在要不要把 Count Reset 歸 0 ? ")
if gg == "yes" or gg == "y": rabbo.rst_count()
while True:
    try: 
        DAN.push('Acceleration', rabbo.Accx, rabbo.Accy, rabbo.Accz) #Push data to an input device feature "Acceleration"
        #print('Acc:', rabbo.Accx, rabbo.Accy, rabbo.Accz) 
        DAN.push('Gyroscope', rabbo.Gyrx, rabbo.Gyry, rabbo.Gyrz)
        #print('Gyr:', rabbo.Gyrx, rabbo.Gyry, rabbo.Gyrz)
        myFloat = round( calcGG( ), 2)  # myFloat = abs(rabbo.Accx) + abs(rabbo.Accy) + abs(rabbo.Accz) 
        DAN.push('Float', myFloat) 
        ggOLD = [rabbo.Gyrx, rabbo.Gyry, rabbo.Gyrz]
        tot = (rabbo.Cnt % 23 ) * 1.568   # 亂寫的 :-)
        if( (rabbo.Accy - oldAx) > 5.0/scale): tot += 3 * abs(rabbo.Accy) * scale   # 這是隨便算的 :-)
        elif( (rabbo.Accy - oldAx) < -3.0/scale): tot -= 1 * abs(rabbo.Accy) * scale
        if( abs(rabbo.Gyrx - oldGx) > 200):  tot +=  abs(rabbo.Gyrx)/5  # 也是隨便算 :-)
        oldAx = rabbo.Accy  # 故意 y
        oldGx = rabbo.Gyrx
        tot = int(tot)  
        scale = random.randrange(3, 6)  # 3, 4, 5   
        if( tot < rabbo.Cnt ): tot = int(rabbo.Cnt / scale) % 38  # tot 比計步器值小, 另外亂算 :-)
        if( myFloat < 100.1 and  rabbo.Accz < 0  ):  #幾乎不動 and 背面向上
           if( tot < 88 ): tot = 88   # 故意最少給 88  
        DAN.push('Total', str(tot) )  # 因為 Total 是字串 
        print("===", myFloat, ", ", tot, ", Cnt=", rabbo.Cnt)
    except KeyboardInterrupt:   # CTRL_C 結束程式
        break;
    except Exception as e:
        print(e)
        if str(e).find('mac_addr not found:') != -1:
            print('Reg_addr is not found. Try to re-register...')
            DAN.device_registration_with_retry(ServerURL, Reg_addr)
        else:
            print('Connection failed due to unknow reasons.')
            time.sleep(1)
    try:
       time.sleep(0.1)
    except KeyboardInterrupt:
       break;
try: 
   DAN.deregister()
except Exception as ee:
   print("!!! ", ee)
finally:
   rabbo.stop() # Rabboni 停止運作
### end of the program  DAI_Rab5u.py    ( USB mode ) 

關於 Joint 函數的寫法與應用請參考(三)三個人用手機控制同一個燈
  * 點這 抓前兩天上課用的程式碼 Rabboni.zip
          解壓縮後, 使用子目錄 IoTtalk_v1_BLE 內的 DAN.py 和 csmapi.py 來配合以下程式碼 (for Rabboni)

如果讀出的數據都很小, 請改用舊的程式庫:
      pip install rabboni==0.7
*10-8 這是 BLE 版本, DAI_Rab5b.py -- 資料會送去 demo.iottalk.tw 的 Rabboni 裝置
### DAI_Rab5b.py -- Rabboni with IoTtalk  (BLE mode)
# coding=Big5   #UTF-8  #Big5   ## 另外一種寫法, 但必須寫在 #LINE 1   或 LINE 2 (這時 Line 1 不可以有中文)
# -*- coding: UTF-8 -*-
### DAI_Rab5b.py    # need DAN.py  and csmapi.py
import time, requests
import DAN
from rabboni import *
import sys, time, math, random

Rabboni_BLE_address = 'C6:98:9D:BE:62:92'  # 你 Rabboni 的 MAC address 
ServerURL = 'http://demo.iottalk.tw:9999'  #請確定 Server 
try:
   rabbo = Rabboni(mode = "BLE") #先宣告一個物件
   rabbo.scan() #掃描所有藍芽Device  ; 知道自己的 MAC 後可註解掉這和以下列
   rabbo.print_device() # 列出所有藍芽Device
   rabbo.connect(Rabboni_BLE_address) #依照MAC連接
except Exception as e:
   print(e);  
   print("可能你沒把 Rabboni 的藍芽打開 或 沒插藍芽接收器 或 根本沒有 Rabboni 裝置 !   ")                            
   sys.exit( )
print ("Status:", rabbo.Status)
rabbo.read_data()
Reg_addr = Rabboni_BLE_address

DAN.profile['dm_name']='Rabboni'
DAN.profile['df_list']=['Acceleration', 'Gyroscope', "Float", "Total"]
DAN.profile['d_name']= Rabboni_BLE_address
DAN.device_registration_with_retry(ServerURL, Reg_addr)  #跟 IoTtalk Server 註冊
oldAx=0
oldGx=0
yyBBB=88  # 越來越大第一次不要超過這
yyOLD=0   # 變小的下限
ggOLD=[0, 0, 0]
def calcGG( ):
    global ggOLD, yyOLD, yyBBB
    ans = 0
    if( abs(rabbo.Gyrx - ggOLD[0]) > 200): ans += abs(rabbo.Gyrx - ggOLD[0])/20
    if( abs(rabbo.Gyry - ggOLD[1]) > 300): ans += abs(rabbo.Gyrx - ggOLD[0])/33 
    if( abs(rabbo.Gyrz - ggOLD[2]) > 500): ans += abs(rabbo.Gyrx - ggOLD[0])/55
    yy = math.sqrt( rabbo.Accx * rabbo.Accx + rabbo.Accy * rabbo.Accy + rabbo.Accz*rabbo.Accz)
    if(yy > 18): yy = 18
    ans = ans * yy / 6.1568
    if(ans < yyOLD): ans = yyOLD
    if(ans > yyBBB): ans = yyBBB
    if(ans > 1200): ans = 1200      # 限制上限為 1200
    yyOLD = ans - 33  # 下次最多減少 33
    yyBBB = ans + 88  # 下次最多增加 88
    if(yyOLD < 0): yyOLD = 0
    return ans

gg = input("現在要不要把 Count Reset 歸 0 ? ")
if gg == "yes" or gg == "y": rabbo.rst_count()
while True:
    try: 
        DAN.push('Acceleration', rabbo.Accx, rabbo.Accy, rabbo.Accz) #Push data to an input device feature "Acceleration"  
        #print('Acc:', rabbo.Accx, rabbo.Accy, rabbo.Accz) 
        DAN.push('Gyroscope', rabbo.Gyrx, rabbo.Gyry, rabbo.Gyrz)  
        #print('Gyr:', rabbo.Gyrx, rabbo.Gyry, rabbo.Gyrz)
        myFloat = round( calcGG( ), 2)  # myFloat = abs(rabbo.Accx) + abs(rabbo.Accy) + abs(rabbo.Accz) 
        DAN.push('Float', myFloat)  
        ggOLD = [rabbo.Gyrx, rabbo.Gyry, rabbo.Gyrz]
        tot = (rabbo.Cnt % 23 ) * 1.568   # 亂寫的 :-)
        if( (rabbo.Accy - oldAx) > 5): tot += 3 * abs(rabbo.Accy)   # 這是隨便算的 :-)
        elif( (rabbo.Accy - oldAx) < -3): tot -= 1 * abs(rabbo.Accy)
        if( abs(rabbo.Gyrx - oldGx) > 200):  tot +=  abs(rabbo.Gyrx)/5  # 也是隨便算 :-)
        oldAx = rabbo.Accy  # 故意 y
        oldGx = rabbo.Gyrx
        tot = int(tot)  
        scale = random.randrange(3, 6)  # 3, 4, 5    
        if( tot < rabbo.Cnt ): tot = int(rabbo.Cnt / scale) % 38   ## tot 比計步器值小, 另外亂算 :-)
        if( myFloat < 100.1 and  rabbo.Accz < 0  ):  #幾乎不動 and 背面向上
           if( tot < 88 ): tot = 88   # 故意最少給 88  
        DAN.push('Total', str(tot) )  # 因為 Total 是字串  
        print("===", myFloat, ", ", tot, ", Cnt=", rabbo.Cnt)
    except KeyboardInterrupt:   # CTRL_C 結束程式
        break;
    except Exception as e:
        print(e)
        if str(e).find('mac_addr not found:') != -1:
            print('Reg_addr is not found. Try to re-register...')
            DAN.device_registration_with_retry(ServerURL, Reg_addr)
        else:
            print('Connection failed due to unknow reasons.')
            time.sleep(1)
    try:
       time.sleep(0.1)
    except KeyboardInterrupt:
       break;
try: 
   DAN.deregister()
except Exception as ee:
   print("!!! ", ee)
finally:
   rabbo.stop() # Rabboni 停止運作
### end of the program  DAI_Rab5b.py    (BLE mode)

#====== END of Rabboni 小玩具 ======
關於 Joint 函數的寫法與應用請參考(三)三個人用手機控制同一個燈


萬有引力 -- g = 物件在地球表面上自由下落的加速度為每平方秒9.8米(Wiki)
    ** 注意, Rabboni 程式庫 0.8 開始, 重力加速度感測器三軸的值改用 1.0 代表 1g; 之前版本用 9.8 代表 1g
          pip install rabboni==0.8
          pip install rabboni==0.7
關於invensense新一代 ICM-20689 DMP Driver六軸感測器驅動程式與範例
六軸傳感器ICM-20689 運動傳感器 資料表     六軸傳感器ICM-20689 Documents
六軸傳感器icm20602的自檢和校準學習
Accelerometer & Gyro Tutorial -- using IMU devices (Inertial Measurement Unit)
如何使用加速度計與陀螺儀(IMU)  或   加速度計和陀螺儀的數學模型和基本演算法
加速度計(Accelerometer),也叫重力感應器(G-Sensor), 可測量 x, y, z 三軸的重力加速度值;
    陀螺儀(Gyroscope、GYRO-Sensor)也叫地感器或者角速度計。陀螺儀通過測量自身的旋轉狀態,判斷出設備
    當前運動狀態,是向前、向後、向上、向下、向左還是向右,是加速(角速度)還是減速(角速度),都可以實現。

認識加速度感測器(G-sensor)的感測值 (Micro:bit)
關於加速度計和陀螺儀傾角測量的物理分析(解說)
關於MPU6050六軸感測器   * 用MPU6050計算角度(C / C++ Arduino)
MPU6050介紹及姿態解算   加速度傳感器的值 與 角度值 轉換原理(解說)
陀螺儀數值 和 加速度計 的數值
多感測器資料融合演算法—9軸慣性感測器
MPU6050 陀螺儀/加速器換算方式
陀螺儀角度計算 (Android Studio開發) (using Java Language)
以陀螺儀 (HiTechnic Gyro)推算物體的旋轉角度的實作測試
感測器原理與無人機...
Raspberry Pi + Python + 六軸感測器 MPU-6050
利用Arduino+MPU6050慣性感測器控制Servo
加速度感測器的原理(.pdf) -- using IMU devices (Inertial Measurement Unit)
Arduino教程:MPU6050的數據獲取、分析與處理
Arduino:加速度計和陀螺儀融合得到精確角度
其他關於六軸感測器的資料

手機陀螺儀 (用 javascript)
   
You are the -th visitors.            
* 想學 Python + Flask 請點這             * 點這看如何選擇 VPS ?
                  (建議用 DigitalOcean 或 Hostwinds.com 流量小可以先用免費的 AWS EC2)

* PuTTY officeal site to Download PuTTY
* PieTTY project official site   Download Pietty0400b14.zip

 
  整理 by 蔡文能 tsaiwn@cs.nctu.edu.tw     交大資工系     tsaiwn.weebly.com     LINE ID: tsaiwn     FB: fb.me/tsaiwn     幾個英文字讀音   TOP
  TOP   Find your FB ID: https://findmyfbid.com/ if you want to Find your Facebook ID for your fb:admins