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

09/17/ BB   #目#   color         點入上列網址後在 Document 下方, 有中文版和英文版(English version); or   點這看更多手冊 ..   17pdf
* 簡介:
IoTtalk 系統交通大學林一平博士帶領研發團隊開發的智慧物聯網應用開發平台, 提供業者一個整合、開放、低成本、高效率, 且能串聯不同規格標準的物聯網應用,例如智慧家庭、智慧校園、智慧農場、互動藝術(含元宇宙)等實際場域林一平教授曾任科技部次長以及交大副校長現任交大資工系終身講座教授,研究主題包含:物聯網(Internet of Things)、行動計算(Mobile Computing)、系統模擬(System Simulation)等。林一平教授興趣多元,喜好藝術繪畫寫作遨遊於科技與人文間自得其樂 ,參看林一平教授的臉書(fB),以及林一平教授的Digitimes專欄文章。(點這看林一平教授寫的『大橋驟雨』以及關於歌川廣重的『大橋安宅驟雨』;還有,看看林教授該書中提到晚年住在亞爾Arles的梵谷模仿大橋驟雨的『雨中橋』油畫)
    目前這 IoTTalk 系統平台已經成功用於智慧校園智慧農業, 例如用於種植薑黃(5分鐘影片) (參看 今週刊報導(2019.01.23)農譯科技公司 AgritalkTech.com/ 以及分家出去的 AI_農業 Agritalk.com.tw/)(現已合併回來) (arduino.cc 分家出 arduino.org 訴訟後又和解+合併)
啊?IoTTalk 系統平台還能做甚麼喔?那就看大家的創意了 :-) (藝術家和賈伯斯都說: 創意, 偷就有了!)
*** 林ㄧ平教授講解 IoTtalk 物聯網商業化應用經驗(五個影片) 
    使用 IoTtalk 系統的好處是你可以利用 GUI 快速建立物聯網應用專案(在首頁點 Project 進入 GUI 畫面),針對任一個應用專案,可以加入一個或多個輸入和輸出的設備模型(Device Model),用滑鼠點按即可連接輸入設備的 IDF和輸出設備的 ODF(可以單點功能連接或多點功能連接),當物聯網設備連接至 IoTtalk 系統時,針對各種感測器,IoTtalk 會自動產生或使用應用軟體來處理,因此每一個輸入設備可以相當方便地連接至輸出設備,必要時還可用 Python 在連結點寫簡單前處理或後處理函數;如果是多點連接, 甚至可在連接點撰寫較複雜的處理函數。

點左邊三角型看看chatGPT 回答 IoTtalk 是啥碗糕...
      chat.openAI.com/chat
如果... 如果想連接"真"的設備,可點左邊三角型先大略看看這的簡單說明           《國樂狂響曲》  

* 如果想連接"真"的設備, 建議也 先看看 ArduTalk 操作手冊(在Github) 以便多了解一些概念。
      (可用Google 搜尋 ardutalk + github 找到(應該第一篇)點入裡面的 Documents 子目錄)         《說好不哭》(周杰倫/阿信)
  裡面包括:                                    
      (a)《安裝手冊》是告訴你如何利用Arduino IDE 把程式碼燒錄到 NodeMCU V2/V3 內,
     可點這跳到後面看更多關於燒錄到NodeMCU(及應用)的參考資料;
            也可以點這跳到後面看關於Arduino Yún如何燒錄與使用的參考資料;
            ** 我們的智慧農業系統終端都使用 Arduino Yún 開發板連接物聯網裝置(蒐集資訊, 控制設備)。
            可以點這參看林一平教授和生科系陳文亮教授合開的農翻譯科技公司(奉科技部命令價創計畫開立)
            (註: 目前(2020年) 官方網站 arduino.cc 上 Arduino Yún Rev.2 開發板售價美金 50元。)
      (b)《操作手冊》則是教你如何在 IoTtalk 的專案使用NodeMCU設備搭配感測器等
          => 至於IoTtalk可以如何應用, 那取決於你的想像力和創意(賈伯斯說創意偷就有了)!
      (c)《驅動程式 for USB to TTL (USB 轉 COM Port)》:
            包括 CP210x (for V2 與 V3 窄板) 和 CH34x ( for V3 寬板)

* 如果你還不清楚啥是 NodeMCU,
          可以點這裡先往下跳到後面看看NodeMCU/ESP8266相關資料後, 再跳回這繼續看。

      註: Arduino Yún 的 ú衍生拉丁字母, Unicode 250;
            如何打出: 壓著左邊 ALT不放, 在右邊數字鍵區按 250 然後放掉 ALT鍵。
* * * 請注意 ! 請注意 ! 請注意 ! ( 德國哲學家尼采說過, 很重要, 所以要說三次 :-)
* 這是 IoTTalk 物聯網應用平台 入門使用手冊,
    原則上建議依序往下看並練習操作(至少依序看到第(八)項或這網頁的一半以上 :-)

如... 如果你對這 IoTTalk 物聯網應用平台到底怎麼做出來的有興趣,請點左邊三角型
* 如果你對這 IoTTalk 物聯網應用平台到底怎麼做出來的有興趣,
    可以點這到 iottalk.vip/0 看看 IoTtalk 開發者手冊
    ** IoTtalk 物聯網應用平台是一個Web Application, 採用 RESTful API(簡單說就是用 HTTP 網址形式溝通傳資料);
          --- 我們 IoTtalk 物聯網應用平台主要是用 Python + Flask + DHTML 實作。(包括Server端 Javascript)
            * 資料庫則使用 SQLiteMySQL (Map Device 用這)。
            * Flask 會用到 模板引擎 Jinja(目前第 3 版)。

    ** 如果不知道 DHTML, 請先看以下這篇: (很短, 稍微看一下:-)
          http://www.differencebetween.info/difference-between-dhtml-and-html5

    ** 如果你不太懂 PythonFlask 但想快速懂一些(註: Flask 是一個簡單的 Web應用框架),
          可以點這開新窗看看我寫的如何快速用 Python + Flask 弄出一個網站(大一計概講義)。

          --- 沒安裝 Python 之前, (Python 版本建議選 Python 3.7.7 版)
              可以點這到 REPL.IT 用瀏覽器就可線上測試/開發 Python 程式
 
* 再說一次, 這是 IoTtalk 入門使用手冊, 不是參考手冊!
    原則上建議依序往下看並練習操作(至少依序看到第(八)大項或看到這網頁的一半:-)
      好啦, 為了有些沒耐心慢慢依序看的, 給一些往下跳的連結:

  ** 點這到後面(三)看關於Joint函數;
       不給(一)和(二), 因這兩項一定要看 :-) 請往下捲動ㄧ些就會到(ㄧ)
  ** 點這到(四)如何用Python寫Dummy Device + ()進階Dummy Device + (六) More練習..    
  ** 點這到後面看(七)新增可用設備模型(管理Device Model);  
  ** 點這到(八)如何用 IoTtalk 連接真實設備...
(Ardutalk, ESP8266, Arduino Yún);
  ** 點這到(九) 搭配 Raspberry Pi 3 B+;    
  ** (十) 關於 Rabboni 小玩具 以及Rabboni 搭配 IoTtalk
  ** 藝術家和賈伯斯都說: 創意, 偷就有了!(往下跳到第(八)項結束之後)
*** 林ㄧ平教授講解 IoTtalk 物聯網商業化應用經驗(五個影片)  
可點左邊三角型看 補充關於 (八)如何用 IoTtalk 連接真實設備
**補充關於 (八)如何用 IoTtalk 連接真實設備...
關於 Ardutalk-for-NodeMCU 注意事項.. (用 Google 搜尋 github + iottalk+nodemcu)
    另外, 目前 demo.iottalk.tw (140.113.199.200) 的 port 9999 已經關閉,
    所以現行版本 ArduTalk 因為不支援 https 變成無法(用 :9999)與 demo 這台溝通;
    你可以把 demo 改為 class (IP 自己查:-) 就可以通過 port 9999 與IoTTalk Server 溝通。
  ==> 注意 NodeMCU/ESP8266 V2 開發板(搭載 CP210x USB-to-TTL 晶片)
          比較窄小, 方便使用一個麵包板,
    但是經實測發現對 V2 開發板的 D0 做 PWM 輸出,
      (做 PWM 就是指用 analogWrite( ) 這句啦)會導致WiFi網路斷線!

    所以, 如果用 NodeMCU V2 開發板,
    要稍微修改程式碼, 請點這看關於我改過的 t7 版本
  ==> V3 開發板通常比較寬, 需要用兩個麵包板;
          但是後來也有瘦窄的 V3 板(搭載 CP210x 晶片); 這是寬大的V3(搭CH34x晶片)

  ==> 台灣物聯公司通常賣比較貴; 飆機器人公司相對較公道; 蝦皮通常可找到較便宜的; 也可去淘寶找更便宜的(注意運費)!
  TOP
在, 好玩的IoT物聯網應用 ..
要.. 開始囉 . . .
可以用哪個 IoTtalk Server 來體驗物聯網(IoT)的實驗操作呢?
  *** 交大 IoT 物聯網實驗網站 https://demo.iottalk.tw (140.113.199.200 在交大電算中心)
    ** 這機器關閉Port 80; 不可以直接打 140.113.199.200 使用網頁, 請使用 demo.iottalk.tw 網址。

    * 或用 交大另一部備用機器 test.iottalk.tw (140.113.199.199; 這port 80沒關閉, 請注意 IoTtalk Server IP)
如果你在資策會上課,請點左邊三角型
★★★ 如果你在資策會上課:
     請點這 http://125.227.255.81 連到 IoT 物聯網實驗網站 (這機器 在資策會)
    或 http://192.168.20.101/ (資策會內部網路; 注意上面 Public IP 可能被防火牆擋住)

 萬一不能用, 用以下交大給 Guest 測試的:點這 https://demo.iottalk.tw 連到交大 IoT 互聯網實驗網站 (這機器 140.113.199.200 在交大電算中心)
 
提醒: 這 https://demo.iottalk.tw 這機器在以下兩時段會暫時不能用:
          (1) 清晨 04:30 ~ 04:33   約三分鐘
          (2) 中午 12:15 ~ 12:18   約三分鐘
TOP       www.w3schools.com    Learn to Code HTML/CSS/JavaScript/Python/JQuery and ...      

(一)用"虛擬遙控器"(Remote_control) 控制開關燈,
      遙控器和虛擬燈泡(Bulb)都可以在自己或別人電腦上, 甚至也可以在手機上


準備建立一個 IoTtalk 物聯網應用專案(Project)
      ( 懶得看文字敘述的..往下捲到(9)再往下捲ㄧ些有12分鐘影片)
注意因為 IoTtalk Server 改版有些還沒穩定, 請仔細看這補充說明 ---
因為IoTtalk server 改版仍未完整, 你可能需要:
(1)網頁上的 Bulb 如果不能用,
   請點這 https://bit.Ly/Bulb_HTTP 抓 Bulb_HTTP.zip 到自己電腦解壓縮, 
   看裡面*Readme*說明 改 js 子目錄內 ida.js 內的 Server 網址,
   然後雙擊 index.html 這樣就可以啦。
(2)網頁上的 Smartphone 如果不能用(就是不會去跟Server註冊),
   就改點那個 Smartphone_HTTP
(3)網頁上已經沒有 Remote_control(mobile),
   請點這 抓 Remote_control(mobile).zip 到自己電腦解壓縮, 
   修改裡面的 sa.txt 內的 Server 網址,
   然後雙擊 index.html 這樣就可以啦。
   或者, 可以改用手動打入網址+自定名稱生出的神秘遙控器;
   (需要開新頁面打入網址生出自定名稱遙控器, 回 Project 頁面綁定它, 再回到神秘遙控器網頁按 F5 刷新)。
   可以參看稍後關於控制虛擬燈泡顏色 Color 關於神秘遙控器的說明。
(4)如果你用 iPhone 抓不到感測器訊息, 請參看以下做設定:
    https://bit.Ly/iPhoneSet (.pdf)

(1)點交大 https://demo.iottalk.tw 連到物聯網互動練習網站,
      ( 或點 交大 https://test.iottalk.tw 連到另一個備用備用的的練習網站 )
      連入 IoTtalk 伺服器網站後會出現iottalk首頁, 在網頁內, 點那個 Project
      此時會彈出新網頁(...:7788/connection),
      此時會彈出新網頁,

      此時會彈出新網頁, (說三次免得有些人沒看到:-)
請在新窗點 Select a project, 然後可選 add project 建立新專案,或選以前你建立過的專案名稱。
** 剛才那 iottalk 首頁(如下圖)請不要關閉,等下還要回來點 Bulb 和 Remote_control(mobile)



(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)。
   以後仍可點畫面上該設備左上角齒輪增加或刪除功能(DF)。
 
 現在應該有發現: 左邊會放"輸入"設備, 右邊會放 "輸出"設備 !
   在上圖中, 燈泡設備圖示的右上方格子內出現26.Bulb表示已經綁定(關聯)到 26號燈泡,
   這 26 號燈泡可能是你之前打開的, 也可能是別人打開的燈泡,
   你隨時可以點該處(設備圖示的右上方格子)重新綁定/關聯到別的燈泡 !

(6)把遙控器的零件和燈泡的零件連接起來並注意右半窗中的連線參數
   第一個連接點會自動叫做 Join 1 (其實該叫 Joint), 在連接點上可以寫簡單 Python 程式碼做處理
   不過, 通常不必寫就可以使用 :-)
   連接好之後記得要按右上角的 Save
      也要注意連線參數,就是點連線中間那圓點(下圖中 Join 1)後,
   注意右半邊視窗 Type 那欄 是否為 Sample,
   如果是 Variant 就把它改為 Sample 在這開關燈的應用比較合裡。
   改好後記得再按右邊的 Save 存起來。
   Sample 表示讀取的值就送出,Variant 表示變化值,
   就是說讀第一次不會送出, 讀取第二次的值會減去第一個值再送出,
   以此類推, 每次讀取的值要減去上ㄧ次的值再送出。
      有些應用選 Sample(取樣) 比較合理(例如開關燈),
   有些則選 Variant 比較合理,例如以下(二)手機隔空丟球。
   另外 Function 那欄是指要套用的函式,disabled 表示不套用,
   這遙控器開關燈的範例不必套用任何函式。	
 
    在上圖中, 有沒注意到: 有編號 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), 也拉開方便控制
***如果, 如果網頁上找不到Remote_control(mobile)
  那是因為最近在改版, 把該 Remote_control(mobile) 拿掉了;
 這裡有一個該通用遙控器的壓縮檔, 把Remote_control(mobile).zip抓回自己電腦解壓縮後,
 進入子目錄, 修改 sa.txt 檔案內看要連哪個 server (不改就是連 demo.iottalk.tw );
 改完 sa.txt 並存檔後, 滑鼠雙極擊 index.html 啟動網頁。
 這樣就有普通的通用遙控器可用 ! 
 不過手機就沒辦法用這遙控器啦 :-( 
 (如果希望手機可以用, 可以在自己電腦上用 Python 弄個簡單網站 host 這"通用遙控器"的網頁;
  然後修改成可以讓連線者手動輸入要連的 server 或者可用下拉選單選擇 Server,
  這樣就可用手機或者任何電腦連到你這電腦上的普通遙控器以便連去 IoTtalk Server了) 
   (d)接著可以回到你剛開的專案(Project)視窗, 準備把設備綁定(關聯)到代表設備的"設備圖示"
      就是告訴電腦: 遙控器是在誰手上的遙控器, 燈炮又是在哪邊的燈炮(Bulb)

再說一次, 因為 IoTtalk server 改版把 Remote_control(mobile) Cyber Device 拿掉了, 所以首頁上沒辦法點Remote_control(mobile) 來生出網頁版通用遙控啟啦; 這裡有一個該 "通用遙控器 "的壓縮檔, 把Remote_control_mobile.zip抓回自己電腦解壓縮後, 進入子目錄, 修改 sa.txt 檔案內看要連哪個 server (不改就是連 demo.iottalk.tw ); 改完 sa.txt 並存檔後, 滑鼠雙擊(double click) index.html 啟動網頁。 這樣就有普通的"通用遙控器"可用 ! 不過手機就沒辦法用這遙控器啦 :-(
(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)的資料 -- 往下捲ㄧ些有給初學者看的 Demo 影片 (a)現在, 可以用手機上的 Remote_control(mobile)控制電腦上的燈泡(Bulb), (b)也可改把燈泡綁定關聯到另一位同學手機上的燈泡, 變成用手機遙控器控制另一位同學手機上的燈泡 :-) ***(c)模擬輸入: 即使沒綁定關聯到輸入設備, 仍可用模擬方式觀看燈泡開關(ㄚ當然輸出設備一定要關聯啦) ! 即使沒關聯到輸入設備, 仍可用模擬方式觀看燈泡開關(ㄚ當然輸出設備一定要關聯啦) ! 即使沒綁定關聯到輸入設備, 仍可用模擬方式觀看燈泡開關(ㄚ當然輸出設備一定要關聯啦), 請參看以下圖片右上角, 在圖片中的 Simulation 是點亮的 (ON綠燈) ! **注意: 如果你點亮Simulation綠燈,則你的 Input Device 就沒效了。           所以模擬後記得關閉 Simulation !           所以模擬後記得關閉 Simulation !           所以模擬後記得關閉 Simulation !       (德國哲學家尼采說過, 很重要所以說三次!) (d)監看資料: 還有, 滑鼠右鍵點 Join 點, 可以監看進出該節點的資料(在右半邊視窗) 出問題 !? 出現黃色驚嘆號 ! 或 Flush 右邊綠燈變紅燈 怎麼辦?!怎麼辦 ?! 出問題 !? 出現黃色驚嘆號 ! 或 Flush 右邊綠燈變紅燈 怎麼辦?!怎麼辦 ?! 出問題 !? 出現黃色驚嘆號 ! 或 Flush 右邊綠燈變紅燈 怎麼辦?!怎麼辦 ?! (e)刷新 Flush: 萬一出問題出現黃色驚嘆號, 可以點驚嘆號大略看看是啥錯誤, 不過通常點一下 Flush 讓它紅燈又回到綠燈阿就可解決問題 ! 參看下圖: (注意圖片中左上角的 Flush 以及右上角的 Simulation 之說明 ! )

* 以下是給初學者看的 Demo 影片 (可開啟字幕):
     (用 Remote_control 遙控器 控制 燈泡 Bulb) (12分鐘)  
   
(建議在 Youtube 內設定 1080P 全螢幕觀看, 可開啟字幕)
手機可以直接掃以下 QR code 或自己先打開手機上的瀏覽器再打入網址 ! 注意project(專案)在哪個 Server, IoT Device 設備就要在哪個 Server 喔 !
QR code Generator OnLine
 
  TOP   幾個英文字讀音     www.w3schools.com    Learn to Code HTML/CSS/JavaScript/Python/JQuery and ...

  三種控制燈泡的方法 ..
首先你專案內的 Bulb ㄧ定要勾選 Color-O 的功能(Feature) !!! (提醒 ODF 出現在畫面右邊)
(1)專案內Remote_control 要勾選 Color-I 的功能, 就用它來控制 Bulb 的 Color-O
      確認 Bulb (ODF) 窗中 Color-O 的 y1, y2, y3 是否分別套用預設的 x1, x2, x3
      該 x1, x2, x3 三個function 分別是 return args[0]; return args[1]; return args[2]
      不過這時 Remote_control 不能綁定通用的遙控器 Remote_control(mobile) 喔!
      因為, 那個 Remote_control(mobile) 沒提供(就是沒寫) Color-I 這個 IDF 啦 !
     要改 綁定到手動生出的神秘遙控器:
              (另開網頁打入以下網址, 注意 Server網址 要用同一個 IoTtalk Server)
          Server網址/RemoteControl/anyName自定名稱
      綁定後, 回到手動生的神秘遙控器網頁按 F5 刷新網頁可看到 Color-I 顏色按鈕。
(2)專案內Remote_control 勾選三個 Knob 旋鈕, 這次故意不使用 Color-I 功能,
      三個 Knob 連到同一個連結點(Joint)再連到Bulb 的 Color-O
      連結點寫簡單 Python 函數整合三個Knob的值;
      也要確認 Bulb (ODF) 窗中 Color-O 的 y1, y2, y3 分別套用正確的 function
      把 Project 內 Remote_control 綁定到手動生出的神秘遙控器:
          Server網址/RemoteControl/anyName自定名稱
      回到手動生的遙控器網頁按 F5 刷新網頁可看到三個分開的 Knob 旋鈕。
(3)專案內Remote_control 也不勾選 Color-I, 但要勾選有三個 Knob 旋鈕,
      三個 Knob 連到同一個連結點(Joint)再連到Bulb 的 Color-O
      在聯合三個 Knob 連結點不寫function 直接選 disabled,此時自動相當於模擬 Color-I
      也要確認 Bulb (ODF) 窗中 Color-O 的 y1, y2, y3 分別套用預設x1,x2,x3或你寫的正確的 function
      但這次把 Remote_control 關連綁定到普通遙控器 Remote_control(mobile),
      普通遙控器 Remote_control(mobile)的三個 Knob 旋鈕看起來只有一個,要切換編號變換!
 
以上詳細三種方法請參考以下影片:
用手動生出的神秘遙控器控制燈泡顏色(25分鐘影片)
      ( 也包括用普通遙控器 Remote_control(mobile) )
      (建議在 Youtube 內設定 1080P 全螢幕觀看)

  手動生出神秘遙控器的方法: (參考上述影片,善用影片下方,有各主題的時間點連結。)
      1)在新網頁打入 IoTtalk_Server 網址/RemoteControl/自取神秘遙控器名稱
          例如:   demo.iottalk.tw/RemoteControl/張大千
              (下圖中我打 "老大" 不是打 "張大千" )
      2)回到你的 Project 網頁, 綁定該 自取神秘遙控器名稱的神秘遙控器
      3)再到 .../RemoteControl/自取神秘遙控器名稱 那網頁 按 F5 刷新網頁

補充手動打字生出神秘遙控器過程畫面 ...
TOP     www.w3schools.com    Learn to Code HTML/CSS/JavaScript/Python/JQuery and ...  
*網頁版 IoTtalk 物聯網裝置程式(DA)實作
( 建議先練習後面(四)Python 版的 Dummy Device 了解觀念。)
(也可先練習比較簡單的Web 版本的 Dummy_Device ) (要修改 ida.js 內連線網址)
* 進階 DA (Device Application)練習:
    把燈泡 Bulb 的程式碼全抓回自己電腦上跑(Bulb 的 Device Application)
    說明:
      (1)用 Google 搜尋 github + iottalk + bulb 找到 IoTtalk/Bulb 抓回自己電腦
          ==> (目前應該是 https://github.com/IoTtalk/Bulb )
      (2)檔案共一個 HTML, 五個 .js 檔案(包括一個是jquery程式庫), 一個 style.css
      (3)確認 ida.js 內(LINE 2)伺服器正確, 例如可用 :
          csmapi.set_endpoint ('https://demo.iottalk.tw');

      (4)若要改變 d_name, 可以修改 dan.js 大約在 LINE 35 處
      ==> 若覺得燈炮反應有點慢, 可改 LINE 4 : (建議不要少於 100)
          var POLLING_INTERVAL = 500; // 把 500 改為 100; 注意不要改太小增加網路負擔
      (5)若要改變燈泡顏色, 可以把 ida.js 的 r, g, b 初始值改變(在很前面幾列),
          例如試把 g 和 b 都改為 128 (r 保留 255)看看啥顏色!?(LINE 10, 11)
      (6) dai.js 內重要的只有 mac_addr, 不用改
      (7) 另外 csmapi.js 和 jquery.js 兩個檔案都不用看 :-)

*** 可以點這參看教學影片(45分鐘)
  (Week05-P2 -- IoTtalk平台介紹與使用(二) - Part 2/5 自己電腦上的 Bulb)

*** 林ㄧ平教授講解 IoTtalk 物聯網商業化應用經驗(五個影片) 
> ※ 劍橋字典 dictionary.cambridge.org 劍橋字典
 
TOP     www.w3schools.com    Learn to Code HTML/CSS/JavaScript/Python/JQuery and ...
   
(二)用你手機控制丟球, 球建議選 Ball-throw2
    注意如果你是用蘋果 iOS 手機, 可能要先做設定,
    請先點這看看這短短說明並設定你手機允許讀取感測器(sensor)。(.pdf)

      建立一個新專案(Project), 或用原專案修改也可以, 一個專案可以有無數個輸入設備和無數個輸出設備 !
      * 這次輸入設備改選 Smartphone (智慧型手機), 不要選 Remote_control
       Smartphone 智慧型手機的功能很多, 這次只要選加速器(Accelerator)一項功能即可;
      你也可以故意把智慧型手機的加速器連到燈泡控制看會怎樣 !? (hint: 到時候甩動手機會讓燈炮會忽明忽暗)
      * 不過這次主要是要連到 Ball-throw2 這個丟球小遊戲(球打到木板上紅色那處會發聲音說 Good Job)
      當然你要回首頁 點 Ball-throw2 開啟一個丟球畫面來用(在往下的 VPython List: 大約第 10 個),
      注意 Ball-throw2 的編號名稱在網頁 Tag 上,
      如開太多網頁看不見Ball-throw2 的編號名稱可把滑鼠移到該頁 Tag 上看!
      * 再來, 回到你的 Project 那頁, 點 Ball-throw2 設備右上角四方型以便綁定關聯要控制的 Ball-throw2 畫面。

      (還有,如果此時 只有一個 Ball-throw2 連入此Server註冊會自動綁定關聯)

      用自己手機或也可以找一位同學用手機連入同樣網站, 這次點 Smartphone 注意編號,
也是回到你的 Project 那頁, 點 Smartphone 設備右上角四方型以便綁定關聯 Smartphone !
例如下圖:
      已經把 Smartphone 綁定關聯到 61.Smartphone
   
如果你是蘋果手機開瀏覽器點iottalk首頁Smartphone後,
發現感測值都是 0, 要先做ㄧ些設定:
請點這先看看這說明再繼續。
如果發現手機不動也會自動丟球,
先看看是否你點亮了模擬(Simulation), 如沒有, 那可能手機加速度的取值設定被改錯了,
正常狀況預設是採用"變動量"(variant)抓取三軸加速度, 看下圖: 萬一不小心被改為 "sample" 取樣當時看到的值, 則可能導致手機不動, 但球卻自己亂丟 ! (可能被其他同學改了! 因為這系統是大家共用 :-)
其實我們可以不要管 Input DM 上 IDF 的 TYPE (input type) 是 Sample 還是 Variant,
因為我可以點IDF 連到 ODF 上的連結點(Joint),然後在右邊視窗修改 input TYPE 以及各套用函式,
然後記得 SAVE 即可。所以這手機隔空丟球最好的連結點(Joint)設定如下圖:

Have Fun !

** 關於用遙控器控制 Ball-throw2
以及用 Smartphone 手機控制 Ball-throw2 也可點這看影片解說
(善用影片下方的說明與連結)
說明:
    影片的下方有各個小主題的連結時間點,
    例如若要跳到用手機隔空丟球Ball_Throw2,
    就點影片下方的 "35:30 改用"智慧手機"(Smart Phone)丟球"直接從影片35:30處開始看。
   
注意.. 注意..
這時手機很耗電 ! 還有.. 
注意千萬不要把你手機丟出去, 丟出去摔壞了別怪我沒先講喔 :-) 
  ** 補充說明:
      在 IoTtalk 系統首頁 VPython List 之下的視覺化網頁應用(Visual Web Application)
      都是用 VPython 視覺化的語言(其實是程式庫)寫的應用 !
      在 VPython 的首頁 就說:
      VPython 是 3D Programming for Ordinary Mortals (凡人用的 3D 程式設計)

      * 想體驗 VPython programming,
          請點這連到 https://EduTalk6.NCTU.edu.tw/ (免費註冊 Sign up)
  (1)註冊, 登入
  (2)點 行星運動
  (3) 點 Program 可看到程式碼
  (4)點 Animation 秀出執行畫面
  (5)點 Animation 右邊那個 QR code 產生鈕
  (6)拿出你手機, 確定網路有通, 叫出 QR code 掃描功能掃畫面上 QR code
  (7)關掉螢幕上 QR code 子畫面免得礙眼!
  (8)用你手機控制重力 和 速度

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

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

      VPython 目前有多個版本被廣泛使用,除了原本的單機版的 VPython 之外,另有在網頁上可執行的GlowScript,與新世代的vpython-jupyter計畫(就是提供 API 讓 VPython 可以在 網頁執行。

      在 IoTtalk 系統裡面的 VPython List 各應用 以及在 EduTalk6.NCTU.edu.tw 都是在瀏覽器執行 VPython 程式碼)。

 
再補充說明 iottalk 首頁上的 VPython List
所有的 VPython Device 的 DA 都使用相同的一支 HTML
  o 也都使用相同的 .js 程式庫(廢話, 因為 HTML 同一支)
  o 根據網址 HTML結尾#WhichDevice 抓 WhichDevice.py
    ==> 這由 dai.js 負責
  o 注意 dan.js 內重點: (內有向iottalk註冊時用到的 d_name)
  o 注意 dai.js 內重點:   (注意剛說了 d_name 在 dan.js 內)
    ==> 要連去哪個 IoTtalk server
    ==> 要去哪個地方抓 .wav 檔案
    ==> 要抓哪個 .py 檔案 (根據網址結尾抓 VPython DA 的 .py 檔案)
南港高慧君老師 DEMO 用旋鈕 Knob 控制 單擺 Simple Pendulum(影片)
VPython VPython DA VPython
* 進階練習:   (把 Ball-throw1 抓回自己電腦必要時可略作修改) * 想體驗在自己電腦上跑 VPython DA (Device Application) 嗎?
        ==> 請點這看給學生的加分題說明網頁 /b1/"
        也可以 點這看我拍給學生加分題說明影片
                        (看影片時,善用影片下方說明與子題連結)
      ==> 也可把 Ball-throw2 抓回自己電腦上跑; 請點看這網頁 /b2/"
    ===> 更多 VPython DA 與 EDUTalk, 可以請點看這網頁 /b3/"
    **=> 關於 蛇擺(Pendulum Wave) 的 VPython DA 與 EDUTalk, 請點看這網頁 /b38/"
    **=> 關於 烏龜繪圖(Turtle Graphics) 的 純 Python DA, 請點看這網頁 /b49/"
VPython DA VPython DA VPython DA
補充上述說的進階練習(把 iottalk 首頁的Ball-throw1 或 Ball-throw2 偷回自己電腦上執行),
瀏覽器基於安全的理由,如果你直接執行 HTML 檔案則無法成功,
必須如下先開啟"不要管安全"的瀏覽器:
  start chrome --user-data-dir="." --disable-web-security
如果你把Ball-throw1 或 Ball-throw2 這類 VPython DA 放入自己電腦網站就沒這問題;
詳細的說明(包括用 Python + Flask 弄個網站)在 iottalk.vip/b1/
在自己電腦上跑一個 Python + Flask 網站
   (可搭配 VPython DA 例如Ball-throw1; 在網頁 https://iottalk.vip/b1/ 內)
     也看看把 Ball-throw2 偷回稍稍改後放入 Edutalk 的行星運動用手機控制。
  ==> 參考這影片-VPython Device Application__加分題
      (善用影片下方說明與子題連結;;投影片檔案在這 )
  ==> ※關於該加分題的說明在 https://iottalk.vip/b3/#bonus0523
     (就是做 Flask + Python + 抓Edutalk的 code 回來做 IoTtalk 的 VPython DA)
※※ (影片)補充加分題說明 VPDA -- VPython Device Application revisited
      (善用影片下方說明與子題連結)
      ※※ (影片)再補充從 Edutalk 偷 VPython 程式回來自己電腦上Python_Flask的網站上跑
      (善用影片下方說明與子題連結)
          且也參考 https://iottalk.vip/b3/https://iottalk.vip/b1/
※※※ (影片) Python + Flask + 整合氣象網路爬蟲做微氣象站
      (善用影片下方說明與子題連結) 且也參考 https://iottalk.vip/b3/

**思考: 如何用"手機"控制開關前面(一)的 Bulb 燈泡?
例如: 手機正面朝上開燈, 正面朝下時關燈, 或反過來 !
Hint:
      (1) 加速度感測器的 Z 軸值在手機正面朝上約 9.8; 正面朝下約 -9.8
      (2) 顯然這時須把加速度值從 Variant 改為 Sample (可以只改 Z 軸), 就是 Smart Phone (IDF) 的 x3 屬性。
      (3) 需要在 Join 連結點寫個簡單小 Python 程式,只把加速度感測器的 Z 軸值送去給燈泡。

 
網站相關的實用小常識
如何讓內網(Intranet)的網站可以讓外網全世界的人連進來?
就是你的網站跑在 Private IP (例如 192.168開頭的)電腦上, 但想讓別人用啦!
很簡單.. 不要錢(當然付錢可有更多服務),
  ==> 用 ngrok 服務即可, 抓 ngrok 來安裝, 然後設定一下就可用了!
o Download ngrok:
      https://ngrok.com/download
o ngrok on github:
      https://github.com/inconshreveable/ngrok
o 請參考: (大一計概練習題 -- 自己電腦上跑網站, 用 ngrok 讓別人可以連入你網站)
      https://iottalk.vip/static/604/index.html#NGROK
Windows 如何開啟那個黑黑的 CMD 命令視窗?
Ans:
有兩種方法 .. (網路上有人提供了號稱有十種方法可開啟 CMD 視窗:-)
      (1)壓住左下角那個視窗鍵(左 CTRL 的右邊), 敲入 cmd 然後按下 ENTER 即可
      (2a)Windows 8 以前: 用滑鼠點左下角 開始 => 所有程式(或程式集) ==> 附屬應用程式 ==> CMD命令提示字元
      (2b)Windows 10: 用滑鼠點左下角 開始 => 往下捲找到 Windows 系統... (有三個, 找一下, 在其中一個裡面!)
** Unix/Linux 愛好者, 也可以:
      下載 cmder 來用   ( cmder in github官方網站 )
#TOP
*** 林ㄧ平教授講解 IoTtalk 物聯網商業化應用經驗(五個影片) 
 
(三)也可以玩三個人分別用手機控制同一個燈(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) !! 因為 IoTtalk server 改版, 首頁已經拿掉 Remote_control(mobile) 可以自己下載該遙控器的 Remote_control_mobile.zip 到自己電腦解壓縮使用。 (解壓縮後必要時須修改裡面 sa.txt 內要連結的 IoTtalk Server 網址) (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):
    Top  幾個英文字讀音 *** 提醒: 改變 function後一定要 Save, Close關掉編輯窗, 套用, 再 Save   * 提醒: 改變 function後一定要 Save, Close關掉編輯窗, 套用, 再 Save   * 提醒: 改變 function後一定要 Save, Close關掉編輯窗, 套用, 再 Save (很重要所以要說三次; 要修改function也是要先點 add new function) *** 上圖中的 haha( ) 把三個參數組合成一個串列 (其實這 Join 1 可以不必套用函數, 當然這樣後面對 Color-O 的三個函數寫法就不一樣啦!)
*** 下圖中的 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): 
*** 測試與思考:    ==> 把那個套用 haha( ) 的 joint 結點改為 disabled 不套用任何函數,      按 FLUSH, 轉動各 knobs, 看看會怎樣 !? 答案:    會看到三角型驚嘆號警告標誌 ! 點入看看訊息 .. 稍為研究一下!   ==> 解決方案, 必須更換那三個轉資料給 Color-O ODF 的函數 rr( ); gg( ); bb( );       各自改為 return args[0]; / return args[1]; / return args[2];    ***哦 .. 這樣好像比較簡單ㄟ .. 哈哈 .. 阿就是要讓你先麻煩一些才可以學到比較多啊 :-) ***提醒, 萬一看不到顏色變化, 點 joint 連接點再點右方窗上方Delete把連線\砍掉,     然後重新把三個 Knob 連一起再連去 Bulb 的 Color-O, 仔細檢查套用函數; SAVE; 點按 FLUSH; 測試 ! *** 這原因是因為前面 Bulb 的 Color-O 若已經被送入 255 會記住上限為 255;     但剛說的新函數若只送 args[0]; args[1]; args[2] 等knobs 傳的值則上限是 1; 所以必須砍掉連線讓它 RESET ,     這樣使用 knob 旋鈕轉動時 1 會變新上限, 經自動 Scale 會對應到 255 (否則 1/255 太小看不出亮度!) *** 阿那如果重新連線之後仍然看不出亮度呢?     那就看看 Bulb ODF Monitor 窗中 Color-O 的 Scaling 值是如何變化?!     如果發現沒 Scaling 可選(就是只有 Function),那可能 Bulb 的 Color-O 屬性被其他同學改掉了 :-(     通常是被改成 Min 和 Max 都是 0 表示不會 auto scaling 啦;     這時要想辦法把它改為會 scaling; 可先點你 project 窗中Bulb 的 Color-O 看可否改屬性,     若不行,要從首頁Device Feature Management進入以便修改 Bulb 的 Color-O 屬性, 如下圖:         改完後, 回到你 project, 把連線砍掉重新連, 確認 sample/variant 以及套用的 function 正確, 點 Save 再重新測試。
補充說明:
其實要控制燈泡顏色的方法有很多種 !
上述只是其中一種,
不同的方法點 Joint 連結點後,
在右半邊視窗內設定以及要寫的小 Python 函數也都不太ㄧ樣 !!
最簡單的方法是在 Project 內的 Remote_control 要選定 Color-I 功能(DF),
把它 Color-I 連到 Bulb 的 Color-O
不過我們的網頁上那個普通遙控器 Remote_control(mobile) 並沒有 Color-I,
必須使用手動生出神秘遙控器(會依據綁定 Project 內的遙控器功能生出在神秘遙控器網頁上。)
當然也要點連線 Joint 連結點確認ㄧ下各個參數和套用的函數內容是否正確!
預設都是正確的,但有時會被同學改了,
因為我們這教學用系統內所有 DM (Device Model)大家都共用,
有人改了參數就可能影響到其他人 :-(
在後面還有關於控制燈泡顏色的說明

** 既然可以在連結點寫簡單的 Python 程式, 我們當然可以加入一些"人工智慧"(AI; Artificial Intelligence) ! 例如, 可以在偵測到溫度高過 25度就開啟電扇, 或是高過 28度就開啟冷氣機:-) 再如, 當發現二氧化碳 或 一氧化碳濃度過高, 就自動開啟窗簾以及窗戶, 甚至開啟排風機; 等空氣品質變好再依序關閉! 那這"人工智慧(AI)"要寫在哪呢? 通常, 如果很複雜就把"AI 人工智慧"寫在伺服器內, 甚至可能把資料送去雲端幫忙計算處理再送回來使用! 若不是很複雜可以寫在 DAI (例如用 Arduino Yún) 或寫在 Joint Function; 若很簡單甚至可寫在 IDA (IoT Device 內的韌體)! (參考IoTTalk 系統開發者手冊) 註:現在我們已經把 IDA (Iot Device Application) 改稱 SA (Sensor / Actuator) * 推薦閱讀: ** 當人工智慧遇上物聯網 迎接AIoT智慧時代(今週刊2018.02.09) ** 2019未來科技展 再造台灣「隱形冠軍」(交大轉載MoneyDJ) | 2019未來科技展 ** AIoT浪潮席捲全球 Edge AI晶片設計思維大不同(DigiTimes 2019.10.31) 註:現在我們已經把 IDA (Iot Device Application) 改稱 SA (Sensor / Actuator)
  TOP   幾個英文字讀音
       
> ※ 劍橋字典 dictionary.cambridge.org 劍橋字典/thyme

#TOP
再次補充說明IDF到ODF 連線的函數(函式)套用
(1) 套用後記得按右上角Save
(2) 連結點處的叫Join function 
    其左邊從 IDF 出來後的叫 IDF function;
    其右邊要送去ODF 之前會做的叫 ODF  function 
(3) Join function 如果選disabled 系統會自動把所有連接過來的IDF 值組成串列 list ,
    也就是類似陣列array [x1, x2, x3];
    當然不一定是三個,要看有幾個IDF連過來。
(4)同樣的功能可以有很多方法做到,不是只一種答案。
    例如可把本來想在 IDF function 做的工作改併到 Join function 時一起做,
    當然這樣你的Join function 就變複雜一些些。
(5)大部份情況下選disabled 就是不做任何處理,
     但是若ODF 只是一個值,
     如果用[串列]送往它用disabled 會
     自動取第一個(即 args[0])
(6)通往燈泡Bulb 的Color-O 的 ODF function 
     預設是系統事先寫好的x1, x2, x3
     分別是return args[0] return args[1] 以及 return args[2]
   你可以故意把該三個改為 disabledSave 後測試看看顏色會怎樣?!
(7)想查看或修改 function 也是要先點 add new function
(8)再提醒一次,修改 function 之後的Save只是把function 存入資料庫,
   這與是否套用無關,所以在按 Close 把函數管理視窗關閉之後,
   記得要再套用,檢視每個要套用 function 處看是否都選擇正確,
   確定都正確後按右上角的Save存檔。

*IoTtalk 物聯網裝置程式(DA)實作入門 ..入門 ...入門 !

>>> *** 以下 (四)(五)(六)(七) 對於要寫程式與 IoTtalk 溝通很重要!
>>> 這種程式我們稱為 DA (Device Application) 程式, DA 可以用任何電腦語言寫,可放在任何物聯網裝置或電腦上;
>>> 這裡我們先在自己電腦用 Python 練習撰寫 IoTtalk 物聯網裝置程式(DA)。

>**     (你的電腦要有安裝 Python)

>**   o 沒用過 Python 的可參考網路上的 Python 介紹與安裝 教學影片
      o 也可以點這開新窗看看我寫的如何快速用 Python + Flask 弄出一個網站(大一計概講義)。
如果系統已經有Python 但是你要建立隔離的 Python 虛擬環境
(如果你沒有系統管理員的權限就必須這麼做!
  因沒管理員的權限就無法做 pip install requests 安裝這 requests 模組。 )

可點這跳到後面先看看再回到這繼續練習(四)(五)(六)
Windows 如何開啟那個黑黑的 CMD 命令視窗?
Ans:
有兩種方法 .. (網路上有人提供了號稱有十種方法可開啟 CMD 視窗:-)
      (1)壓住左下角那個視窗鍵(左 CTRL 的右邊), 敲入 cmd 然後按下 ENTER 即可
      (2a)Windows 8 以前: 用滑鼠點左下角 開始 => 所有程式(或程式集) ==> 附屬應用程式 ==> CMD命令提示字元
      (2b)Windows 10: 用滑鼠點左下角 開始 => 往下捲找到 Windows 系統... (有三個, 找一下, 在其中一個裡面!)
** Unix/Linux 愛好者, 也可以:
      下載 cmder 來用   ( cmder in github官方網站 )
** 更多關於 IoTtalk平台介紹與使用 (影片)
      (1+五個影片; 包括以下(四)和(五)關於 Python Dummy Device練習 以及 50分鐘 Python 簡介)  
  
(四)練習從 github 抓 Python 虛擬裝置(Dummy devie)來用 (就用來控制燈泡開關好了)
    * 在PC/Windows不安裝 git 仍可點Code按鈕選用"Download ZIP"方式下載 !
還沒有 git 但想安裝 git 的可以點這到 https://git-scm.com/downloads下載安裝
 
到 https://desktop.github.com/ 抓 Desktop github
  * 在PC/Windows不安裝 git 仍可點Code按鈕 選用"Download ZIP"方式下載 !
(1)用 Google 搜尋 dummy + iottalk + python   (懶得copy/paste點這)
   應該會在前幾篇找到 GitHub - IoTtalk/Dummy_Device_IoTtalk_v1_py
     (Hint: 我搜尋結果是有時在第一篇, 或第二篇, 或 在第 三 篇) 找不到的點這裡:-)
   請注意, 要 _v1_py 版本的, 不要 _v2_py 喔! (那是 for IoTtalk v.2; 目前沒開放 v.2 Server!)
(2)點進去, 稍微看一下說明, 它有說會用到網頁相關的 python module 'requests'
   就是說萬一有錯誤訊息可能你沒用 pip 安裝過 requests 模組啦 !
   到時候, 當然就是要照它說的安裝囉: (requests 是負責處理網頁溝通用的模組)
pip install requests
別緊張, 不用現在 pip install requests 喔, 因為說不定你的 Python 已經有安裝過該模組了 :-) (還有, 如果你用 Mac/Linux 發現不認識 pip, 先改打 pip3 看看, 若還是不認識, 先確認你有安裝 Python 3.x, 若仍不認識 pip 和 pip3, 試用這命令看看: python -m pip install pip 如果還不行, 找個人幫忙或是自己 Google 找答案:-)
另外,再說ㄧ次, 如果要建立隔離的 Python 虛擬環境(如果你沒有系統管理員的權限就必須這麼做!) 可點這跳到後面先看看再點Go Back回到這繼續練習

(3)點 github 上該 Dummy Device 網頁的 Code 按鈕選 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 開啟 CMD 命令提示視窗或類似的 PowerShell 等, 用 cd 命令進入該子目錄(裡面應該有三個 .py 檔和ㄧ個 README.md) Hint: 在 CMD 命令視窗內要善用 TAB 按鍵, 就是說只要打開始幾個字母就可按 TAB, 不對就再按一次 TAB, ...
** 因為 IoTtalk 最近改版支援 MQTT, 新版本 Dummy Device 變肥了, 對初學者來說要研究程式碼比較難一點點, 可先使用這個舊版本Dummy_Device: https://bit.LY/oldDummy 以下的說明是根據這個舊版寫的; 如果使用 Github 上現在 IoTtalk 新版 Dummy_Device, ●(新版就是在 https://github.com/iottalk/Dummy_Device_IoTtalk_v1_py ) 需要修改的已經移到 SA.py 內, 改完SA.py之後仍是執行 python DAI.py
❤❤ .. ❤..❤❤..❤ (5)修改 DAI.py 檔案, 其他 DAN.py 和 csmapi.py 都不要管, 只要放同一目錄即可; 主要是要改成讓它會連入你 Project 用的 IoTtalk Server 機器註冊!` 用任何編輯器或 Python IDE 或 notepad 都可修改該 DAI.py 不過因為 DAI.py 是 Unix 格式直接用 notepad 可能有問題, 很簡單, 先這樣: start wordpad DAI.py 進入後打ㄧ個字再去掉該字然後存檔就會變成 Windows 格式 (有換行), 然後就可用 notepad 改啦!! 阿 DAI.py 也只要改ServerURL 那列那個 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
==>如果使用交大 demo 那部, 建議改用 https 的方式如下: (把 http:// 那列註解掉) ServerURL = 'https://demo.iottalk.tw' #with SSL secure connection ==>或是使用交大備用的 test 那部, 建議改用 https 的方式如下: (把 http:// 那列註解掉) ServerURL = 'https://test.iottalk.tw' #with SSL secure connection 注意, 使用 https 時不要有 :9999 喔 !
(6)仿照前面(一)用遙控器開關燈的專案, 只是把 Remote_Control (遙控器)改為選用 Dummy_Device (虛擬裝置), 提醒 Dummy_Device 的 IDF (輸入功能), 就是感測器(Sensor) 建議 project 如下設置: (7)執行 DAI.py 以便產生虛擬裝置並註冊到 iottalk 伺服器 開啟 CMD 命令窗 進入到該目錄, 像以下這樣執行:
python DAI.py # 如果在 Ubuntu Linux 則用 python3 DAI.py
注意看印出信息顯示的裝置編號 (亂數產生的), 在 Project 內關聯(綁定)時要用到! (8)回到 iottalk 專案(Project)網頁, 把虛擬裝置綁定(關聯)起來, 當然虛擬燈炮也要綁定關聯起來喔 !
如果, 執行 python 出現錯誤, 又或如果你想 pip 安裝一些 python 的程式庫但是沒管理員(Administrator)權限, 那請 點這跳到後面看一下再回到這(使用自己建立的 Python 虛擬隔離環境)!
(9)仔細看看虛擬燈炮Bulb會怎樣? 說明: 這個 DAI.py 會處理ㄧ個 IDF 也會處理ㄧ個 ODF; 阿其實 IDF 本來該去讀取 IoT 的感測器(Sensor), 以便丟去給伺服器, 現在用亂數產生一個值 [1, 10] 模擬讀到的 Sensor value; 另外, 也包括一個 ODF 叫做 'Dummy_Control', 那是從 Server 送來的串列(Python 的 List 就是 Array 啦) 這 DAI.py 只是從 Server "拉回"這 ODF 的串列(list; 用起來類似 array; 請注意 Python 沒有原生的 array), 然後如果收到的不是 None, 則只印出第一個值: print (ODF_data[0]) 真正的裝置可能要拿這值來做控制某些設備或做其他事情 ! 目前我們沒用到這 ODF, 所以你專案內可以不勾選 ODF, 或勾選了不關聯綁定到任何設備 ! 不過建議最好也勾選 ODF, 這樣可把右邊的 Dummy_Device 也綁定以便查看 ODF 結果!
確定都有連接好且關聯(綁定)到虛擬裝置和燈炮, 應該有看到變化吧 !? 燈好像忽開忽關忽明忽暗對不對 !? 那是因為 DAI.py 是幫 'Dummy_Sensor' 產生亂數送出去 ! 接著來改為從鍵盤讀入再送出 .. 參看以下(五)修改(四) .. ** 提醒: DAI.py 會用到 DAN.py, DAN.py 會用到 csmapi.py =====> 以下把 DAI.py 列出方便參考: (不過 LINE 7 的 Reg_addr = None 已經被我改了,這樣同ㄧ部電腦才可跑很多份 !) ** 可以比對 我們放在 Github 上的 DAI.py 範例 現在摘述 DAI.py 該做的事情:(參考以下 DAI.py)
DAI.py 該做的事情:
   (a) import 匯入需要的 Library, 包括 DAN
   (b) 填好 ServerURL (參考以下的 Line 5 ~Line 6)
   (c) 填好 Reg_addr (注意這相當於你 Device 的身分證, 在 IoTtalk 內必須唯一)
         如果用我們放 github 上原版的 Reg_addr = None 則會在 DAN 內自動產生 UUID 確保每部電腦只跑ㄧ份!
         就是說, 後來跑的會蓋掉前面跑的 ! 不論跑幾份, 在 IoTtalk server 看來都是同一份 !
   (d) 填資料, 告訴 IoTtalk What are you 你是啥東東
         就是你的 DM 是啥 (Device Model name) ; DAN.profile['dm_name'] = "What_R_U"
   (e) 填資料, 告訴 IoTtalk What features(IDF + ODF) do you have?
         就是你的 IDF 和 ODF; DAN.profile['df_list']= [ 該 Device 所有的 IDF 和 ODF ]
   (f) 填資料, 告訴 IoTtalk Who are you? 你是誰 (名字是可以重複的)
         就是你的名字 (Device name / Who are you); DAN.profile['d_name']="Who_R_U"
   (g) 向 IoTtalk 註冊, 呼叫 DAN.device_registration_with_retry(ServerURL, Reg_addr)
   (h) Loop the following while True:
          讀取/產生 sensors 資料 for IDF
          呼叫 DAN.push( ) 把資料推送去 IoTtalk
          # 所有的 IDF 都如上做; 所有的 ODF 都如下做:
          呼叫 odfData=DAN.pull("odfName") 把 ODF 資料從 IoTtalk 拉下來
          if odfData 不是 None:
             處理 odfData; 例如印出來看 或送去給 MCU 控制的 GPIO pin 或 analog output pin
          # 小睡片刻免得做太快會被計網中心罵 :-)
          time.sleep(0.2)   # 每秒做大約五次 
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
# DAI.py #coding=utf-8 -- 注意原版 Reg_addr 用 None  會使用 UUID (在 DAN.py內)一部機器只能跑一份
import time, random, requests
import DAN
### 只要修改以下兩列 !
ServerURL = 'http://yourServerIP:9999'     #with non-secure connection;
#ServerURL = 'https://YourServerDomainName' #with SSL connection 若用 IP 則無法用 https:// 
Reg_addr = "AABB3388" + str( random.randint(100,999 )) #None  #if None, Reg_addr = MAC address
##    上列如像原版給 None 則在 DAN.py 內會用 UUID, 這樣一部電腦只能跑一份這程式
DAN.profile['dm_name']='Dummy_Device'    ##  What are you? 你是啥東東 
DAN.profile['df_list']=['Dummy_Sensor', 'Dummy_Control',]   ##  你有哪些功能, 包括 IDF 和 ODF 
DAN.profile['d_name']= str( random.randint(1,199))+'.RealHaha'    ##  who are you? 你是誰 

DAN.device_registration_with_retry(ServerURL, Reg_addr)
#DAN.deregister()  #if you want to deregister this device, uncomment this line
#exit()            #if you want to deregister this device, uncomment this line

while True:
    try:
        IDF_data = random.uniform(1, 10)    ## uniform dist. 1..10; 通常去讀取 sendor 的值 
        DAN.push ('Dummy_Sensor', IDF_data) #Push data to an input device feature "Dummy_Sensor"
        ## 可以 push 很長的字串, 最長可以 14950 個字 (中文英文都算一字)
        #==================================

        ODF_data = DAN.pull('Dummy_Control')#Pull data from an output device feature "Dummy_Control"
        if ODF_data != None:
            print (ODF_data[0])

    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)    

    time.sleep(0.2)

注意這範例為了簡短沒有處理敲 CTRL_C結束要做 DAN.deregister() # 試著解除註冊 結果就是, IoTtalk server 內會有許多 Dummy_Device 的屍體 :-( 參考以下(五)範例處理 except KeyboardInterrupt:
00MyDummy.zip  (已經把範例的 DAI.py 改好連到 demo.iottalk.tw) 所以只要下載解壓縮後在解壓縮後目錄內直接執行 python DAI.py 即可 :-) 可參考這 Dummy Device練習 影片教學(43分鐘)這有 50分鐘 Python 簡介剛要學 Python 的可以點這開新窗看看我寫的如何快速用 Python + Flask 弄出一個網站(大一計概講義)。

o 如果你稍微懂 JavaScript, 也可以參考我們Web 版本的 Dummy_Device (要修改 ida.js 內連線網址)
** 更多關於 IoTtalk平台介紹與使用 (影片) (1+五個影片; 包括上述(四) 和以下(五) Dummy Device練習 以及 50分鐘 Python 簡介)

      Top   幾個英文字讀音
> ※ 劍橋字典 dictionary.cambridge.org 劍橋字典/thyme (百里香) 讀如 time   
複製以下(五)裡面DAI2.py程式碼去你編輯的檔案貼上再修改即可,小心不要 Mark 到編號, 就是..
  在 01 # DAI2.py# 處(含 #)用滑鼠左鍵壓著不放然後開始捲動滾輪往下捲直到程式碼結束處放掉左鍵,
      然後在你標記(mark)起來區域用滑鼠右鍵點下去 複製(Copy)
(五)修改 (四) 的 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 #coding=utf-8 -- new version of Dummy Device DAI.py, modified by tsaiwn@cs.nctu.edu.tw
import time, DAN, requests, random 
import threading, sys # for using a Thread to read keyboard INPUT

# ServerURL = 'http://Your_server_IP_or_DomainName:9999' #with no secure connection
#  注意你用的 IoTtalk 伺服器網址或 IP  #  https://goo.gl/6jtP41
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 #(本來在 DAN.py 要這樣做 :-) 
# Note that Reg_addr 在以下三句會被換掉! # the mac_addr in DAN.py is NOT used
mac_addr = 'CD8600D38' + str( random.randint(100,999 ) )  # put here for easy to modify :-)
# 若希望每次執行這程式都被認為同一個 Dummy_Device, 要把上列 mac_addr 寫死, 不要用亂數。
Reg_addr = mac_addr   # Note that the mac_addr generated in DAN.py always be the same cause using UUID !
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']   # Check IoTtalk to see what IDF/ODF the DM has
DAN.profile['d_name']= "TWN_D."+ str( random.randint(100,999 ) ) +"_"+ DAN.profile['dm_name'] # None
DAN.device_registration_with_retry(ServerURL, Reg_addr) 
print("dm_name is ", DAN.profile['dm_name']) ; print("Server is ", ServerURL);
# global gotInput, theInput, allDead    ## 主程式不必宣告 globel, 但寫了也 OK
gotInput=False
theInput="haha"
allDead=False

def doRead( ):
    global gotInput, theInput, allDead
    while True:   
        while gotInput:   # 老闆還沒把資料拿走
           time.sleep(0.1)    # 小睡 下把 CPU 暫時讓給別人
           continue  # go back to while   
        try:     # 準備讀取資料, 注意程式會卡在這等 User 輸入, 所以要用 Thread
           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(0);   ## break  # raise   #  ?
        if theInput =='quit' or theInput == "exit":    # these are NOT data
           allDead = True
        else:
           print("Will send " + theInput, end="   , ")
           gotInput=True   # notify my master that we have data 
        if allDead: break;   # 離開 while True 這 Loop  

#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:
        if(allDead): break;
    #Pull data from a device feature called "Dummy_Control"
        value1=DAN.pull('Dummy_Control')
        if value1 != None:    # 不等於 None 表示有抓到資料
            print (value1[0])
    #Push data to a device feature called "Dummy_Sensor" 
        #value2=random.uniform(1, 10)    ## original Dummy_Device example
        if gotInput:  # 小弟有讀到資料了 
           # we have data in theInput
           try:
              value2=float( theInput )   # 故意轉成實數 
           except:
              value2=0   # 轉成實數失敗就當作 0.0 
           if(allDead): break;
           gotInput=False   # so that you can input again  # 讓小弟知道我拿走了  
           DAN.push ('Dummy_Sensor', value2,  value2)  #  試這:  DAN.push('Dummy_Sensor', theInput) 

    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.25)
try: 
   DAN.deregister()    # 試著解除註冊
except Exception as e:
   print("===")
print("Bye ! --------------", flush=True)
sys.exit(0);
這個新版的 DAI2.py 改為派一個 Thread (執行緒) 同時(平行)做事, 負責讀取使用者從鍵盤輸入;
請注意, 在等輸入的同時, 主程式仍不斷的 "Pull" ODF ('Dummy_Control')
並且在 "小弟" 有幫忙讀到一個值之時, 把該值 (value2) push 送去給 Server :
    DAN.push ('Dummy_Sensor', value2,  value2)  
在這是故意把 value2 寫兩次表示送出兩個值, 也可以送更多個 !  
甚至, 你也可以故意把輸入的原始字串直接送出去: 
    DAN.push('Dummy_Sensor', theInput) 
* 補充關於用 Thread 平行做事: (a)Line 24-45 定義一個函數 doRead( ) 這函數利用一個全域變數 gotInput 讓 "主人" 知道是否有讀到輸入值(放在 theInput 內) (b)Line 47-49 設定並啟動 Thread 執行緒, 執行的是 doRead( ) (c)Line 59-68 是主人發現 gotInput 亮燈表示有資料時, 取出資料, 並把 gotInput 弄為 False 以便讓小弟知道主人已經把資料 theInput 拿走 請注意, 在 Line 53 和 66 還有檢查是否小弟因 'quit' 或 'exit' 設定 allDead, 是則結束程式 ! ** 更詳細關於 Python thread 執行緒的教學可以看這老外的影片
00MyDummy2.zip  (已經把範例的 DAI2.py 也打入且改好連到 demo.iottalk.tw)

    # 新版的 DAI2idf.py (只有處理 IDF ) 如下: (不要 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
# DAI2idf.py #coding=utf-8 -- 注意原版 Reg_addr 用 None  會使用 UUID (在 DAN.py內)一部機器只能跑一份
import DAN, time, random, requests
import threading, sys # for using a Thread to read keyboard INPUT
### 只要修改以下 ServerURL # Reg_addr 現在改用亂數; 如只想跑一份, 給它 None 
#ServerURL = 'http://yourServerIP:9999'  #with non-secure connection; #若用 IP 則無法用 https://
ServerURL = 'https://6.iottalk.tw' #with SSL connection  
Reg_addr = "AABB3388" + str( random.randint(200,299 )) #None  #if None, Reg_addr = MAC address as ID
##  上列如像原版給 None 則在 DAN.py 內會用 UUID as Reg_addr (ID), 這樣一部電腦只能跑一份這程式
interval = 150  # 150 milli seconds = 0.15 sec
DAN.profile['dm_name']='Dummy_Device'    ##  What are you? 你是啥東東  DM name
DAN.profile['d_name']= "TWN_in." + str( random.randint(200,299))+'.'+DAN.profile['dm_name']  ##  你是誰
DAN.profile['df_list']=['Dummy_Sensor',]   ##  你有哪些功能,  這次不需要  'Dummy_Control', 

sleepTime = 1.0 * interval / 1000.0
allDead = gone = False
def doIDF( ):
  global allDead, gone
  DAN.device_registration_with_retry(ServerURL, Reg_addr)
  print("CTRL_C to kill the program...", end="")
  while True:
    try:
        IDF_data = input("Give me data: ")
        if IDF_data == "quit":
           allDead = True
           break
        try:
           IDF_data = float(IDF_data)
        except:
           IDF_data = 0
        DAN.push ('Dummy_Sensor', IDF_data) #Push data to an input device feature "Dummy_Sensor"
        ## 可以 push 很長的字串, 最長可以 14950 個字 (中文英文都算一字) #=========  =========
    except KeyboardInterrupt:
        allDead = True
        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(sleepTime)
    except:
       allDead = True
       break;
  try:
    print("Bye ! --------")
    DAN.deregister()  #if you want to deregister this device, uncomment this line
    gone=True
  except:
    pass
  exit(0)

if __name__ == '__main__':
  thready = threading.Thread(target=doIDF)
  thready.daemon = True
  thready.start()
  while True:
    if(allDead): break
    try:
       time.sleep(0.15)  # nothing to do in main thread; 留給 Tk GUI 用
    except:
       break;
  time.sleep(0.12)
  if not gone: DAN.deregister() 
  #end of main block

#TOP        
(六)修改專案, 改為放兩組 Dummy Device, 然後互相傳遞資料
   (1)複製並修改 (五)的 DAI2.py  為 DAI3.py,
      這 DAI3.py 只是改一下 第11列的 mac_addr, 把裡面 D38 改為 你喜歡的其他三位數,
      因為如果 mac_addr 相同, 則會被 Server 看作同一個裝置 !
      (更新: 其實現在已經不用改, 因 DAI2.py 的第11列的 mac_addr 已經改加入亂數 :-)
      (意思是現在不需要再 DAI3.py, 開兩個CMD命令窗都直接跑 python DAI2.py 就是不同的 Dummy_Device)
   (2)你必須開兩個命令視窗, 一個跑 DAI2.py, 另一個跑 DAI3.py(更新:如帳上述說的因我已改加入亂數,另一個窗也跑 DAI2.py 即可:-)
      執行時請注意看各自印出的裝置名稱號碼, 關聯時要用到 !
   (3)也可以找同學合作, 一個跑 DAI2.py, 另一個跑 DAI3.py (剛剛說了,現在兩個 CMD 命令窗都跑 python DAI2.py 即可 :-)
      專案當然是由誰建立都可以, 只要把代表 DAI2.py 和代表 DAI3.py 的虛擬裝置關聯到即可,
      注意, 在專案中加入兩組 Dummy_Device 兩次都有勾選 IDF 與 ODF, 則Project內共會看到四個 Dummy_Device,
      兩個輸入用(在 Project 窗的左邊), 兩個輸出用(在右邊) !
   (4)這時, DAI2.py 和 DAI3.py 都各自身兼兩個設備: 輸入(IDF) 和 輸出(ODF)
      所以關聯綁定設備時, DAI2.py 和 DAI3.py 各會被用到兩次
      其實你也可以再複製為 DAI4.py 和 DAI5.py (當然要改 mac_addr), 開四個 CMD 窗各自執行,
      這樣負責輸入的就不必負責輸出 !(更新: 因老是有同學忘了改 mac_addr, 我已把它改為會包含亂數! 不需要修改 DAI2.py 啦!)
   (5)測試時,
      各自都打一個數值然後按 Enter 送出 ;可以打實數因為我用 :
          value2=float( theInput )
   (6)做習題的同學注意: 測試過程也要 CTRL_PrintScr 抓取畫面繳交 !
  以下是讓大家練習的示意圖 (for Week05習題) (四份 Dummy Device + 三份在自己電腦跑的 Bulb)

==> 注意我們有用到 global 變數(外部變數; 函數外面的變數)

    # DAI3odf.py ; 這是改 DAI2.py 刪除 讀取 Sensor 處理 IDF 的 code; 專心做 Pull ODF 下來的事情!
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
# DAI3odf.py #coding=utf-8 --  Dummy Device that handles ODF (pull data) ONLY; by tsaiwn@cs.nctu.edu.tw
# 注意這版本不處理 IDF, 不會送資料去 IoTtalk server, 所以若綁定project左邊 Dummy_Device 沒有用!!
import time, DAN, requests, random 
import threading, sys # for using a Thread to read keyboard INPUT

interval = 88;   #  88 milli seconds = 0.088 seconds
#  注意你用的 IoTtalk 伺服器網址或 IP  #  https://goo.gl/6jtP41
# ServerURL = 'http://Your_server_IP_or_DomainName:9999' #with no secure connection
ServerURL = 'https://demo.iottalk.tw' # with SSL secure connection (IP 不能用 https)

Reg_addr = None #if None, Reg_addr = MAC address #(本來在 DAN.py 要這樣做 :-) 
# Note that Reg_addr 在以下三句會被換掉! # the mac_addr in DAN.py is NOT used
mac_addr = 'Rx-ODF-049' + str( random.randint(300,399 ) )  # put here for easy to modify :-)
# 若希望每次執行這程式都被認為同一個 Dummy_Device, 要把上列 mac_addr 寫死, 不要用亂數。
Reg_addr = mac_addr   # Note that the mac_addr generated in DAN.py always be the same cause using UUID !

DAN.profile['dm_name']='Dummy_Device'   # you can change this but should also add the DM in server
DAN.profile['df_list']=['Dummy_Control']   #  do NOT need Dummy_Sensor now
DAN.profile['d_name']= "TWN_odfD."+ str( random.randint(300,599 ) ) +"_"+ DAN.profile['dm_name'] # None
DAN.device_registration_with_retry(ServerURL, Reg_addr) 
print("dm_name is ", DAN.profile['dm_name'], " -- handle ODF Dummy_Control ONLY") ; 
print("CTRL_C to kill this program.")
print("Server is ", ServerURL);
# global gotInput, theInput, allDead    ## 主程式不必宣告 globel, 但寫了也 OK
allDead=False

sleepTime = 1.0 * interval / 1000.0;
print("sleepTime = ", sleepTime, " second.")
while True:
    try:
        if(allDead): break;
    #Pull data from a device feature called "Dummy_Control"
        value1=DAN.pull('Dummy_Control')
        if value1 != None:    # 不等於 None 表示有抓到資料
            print (value1[0])
        #Push data to a device feature called "Dummy_Sensor" 
        #現在專心處理收取 ODF 資料, 不 push IDF 啦
    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(sleepTime)   # was sleep(0.2)
       start_time = time.monotonic()   # 這招比 sleep( ) 準確
       ggyy=0
       while time.monotonic() - start_time < sleepTime:
          ggyy+=1
          pass; 
       #print("Sleep ", sleepTime, " sec; Loop Count = ", ggyy)
    except:
       break;
    #end of while True loop
try:
   time.sleep(0.025)
   DAN.deregister()    # 試著解除註冊
except Exception as e:
   print("===")
print("Bye ! --------------", flush=True)
sys.exit(0);
##  end of DAI3odf.py
這是Dummy_Device習題的參考影片(Week05-P5)(Part 5/5 Dummy Device練習)
    和 Week05-P2/5 - 自己電腦上的 Bulb ( 就是前面說的進階 DA 練習 )
    Top   幾個英文字讀音
DAI6.py as Dummy_Device (with Tk Slider)
    # DAI6.py (這是改 DAI2.py 加入 Tk 的 Slider): (不要 Mark 到左方的每列編號 !)
( Hint: 滑鼠左鍵先壓住下方01 右邊的 # 並且把滑鼠往右滑一點點(其實不滑也OK但沒Mark感覺:-),
      然後滾動滾輪捲到下面最後一列(就 168 列); 此時先壓著 Shift 按鍵不放,
     滑鼠點最後一列(就 168 列)的最右邊; CTRL_C 按滑鼠右鍵Copy複製
)
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# DAI6.py #coding=utf-8 --  Dummy Device with Tk Slider, 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 # for using a Thread to read keyboard INPUT
from tkinter import *  # 注意是 tkinter (Python 3.x); 不是 Tkinter (Python 2.x) 喔

#  注意你用的 IoTtalk 伺服器網址或 IP 
# ServerURL = 'http://192.168.33.88:9999' #with no secure connection
ServerURL = 'https://demo.iottalk.tw' # with SSL secure connection
myName = "TWN_D."+ str( random.randint(600,999 ) ) 

tk = Tk()
tk.title("007_蔡文能 Tk_Slider_請注意 Tk 必須當老大要在 main thread")
text_widget = Text(tk, height=2, width=36, font=("Simsun", 24), fg="red", bg="lightgreen"); 
text_widget.pack( ); 
text_widget.insert(END, "拉到滿意的值然後放掉滑鼠!")

gotSlider=False
sliderVal = 58  #initVal for the Slider
firstSetSlider=True  # 在一開始 .set(58) 設定 Slider故意不處理 # 已改用滑鼠放掉, 此變數沒用了
def syy_changed(event): 
    global gotSlider, sliderVal  #用來和原先的老大溝通 
    sliderVal=s.get()
    gotSlider=True

s = Scale(tk, from_=0, to=100, length=600, tickinterval=10, bg="lightyellow", fg="red",
    orient=HORIZONTAL)    #:# orient=HORIZONTAL, command=syy_changed)
# Connect the slider to the callback function
s.bind("<ButtonRelease-1>", syy_changed)  # 滑鼠放掉才算
s.set(sliderVal); s.pack()  # 如果 command 寫在 Scale( ... 那句, 則這 s.set( ) 也會執行它

def kill_me():
    global allDead  # 用來通知 thread 自殺
    allDead=True  #通知所有 thread 自殺
    tk.focus_set()
    sayBye( )
    tk.quit()  # close Tk window

button = Button(tk, text="Quit 點這結束程式", anchor="w",
  bg="yellow", fg="green", command=kill_me)
button.pack(side='right')
tk.protocol("WM_DELETE_WINDOW", kill_me)

Reg_addr = None #if None, Reg_addr = MAC address #(本來在 DAN.py 要這樣做 :-) 
# Note that Reg_addr 在以下三句會被換掉! # the mac_addr in DAN.py is NOT used
mac_addr = 'C9870D238' + str( random.randint(100,999 ) )  # put here for easy to modify :-)
# 若希望每次執行這程式都被認為同一個 Dummy_Device, 要把上列 mac_addr 寫死, 不要用亂數。
Reg_addr = mac_addr   # Note that the mac_addr generated in DAN.py always be the same cause using UUID !

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']   # Check IoTtalk to see what IDF/ODF the DM has
def initIoTtalk( ):
   DAN.profile['d_name']= myName + "_"+ DAN.profile['dm_name'] # None
   DAN.device_registration_with_retry(ServerURL, Reg_addr) 
   print("dm_name is ", DAN.profile['dm_name']) ; print("Server is ", ServerURL);
##
theInput="haha"
gotInput = allDead = False
firstRead=True
def doRead( ):
    global gotInput, theInput, allDead, firstRead
    while True:
        if(allDead): break
        if gotInput:
           time.sleep(0.1)
           continue  # go back to while
        try:
           if firstRead:
              print("提醒輸入 quit 會結束 !")  #只在第一次輸入之前才提醒
              firstRead=False
           theInput = input("Give me data: ")
        except KeyboardInterrupt:
           allDead = True
           break
        except Exception:    ##  KeyboardInterrupt:
           allDead = True
           sys.stdout = sys.__stdout__
           print(" Thread say Bye bye ---------------", flush=True)
           break  # raise   #  sys.exit(0);   ## break  # raise   #  ?
        gotInput=True
        if(allDead): kill_me( )
        elif 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

def doDummy( ):  # 因為 Tkinter  必須在 main thread, 所以原先的主程式必須改用 thread (thready)
  global gotInput, theInput, allDead  # do NOT forget these var should be global
  global gotSlider, firstSetSlider  # 沒寫  sliderVal = xxx 就不必寫 global 
  while True:
    if(allDead): break
    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 gotSlider:  # Slider 有被動到
           sss = sliderVal  # 取出 slider value
           gotSlider = False #其實沒用處, 因為我們不管 user 是否會去改變  Slider
           if firstSetSlider:
              firstSetSlider=False
              DAN.push ('Dummy_Sensor', 100);  time.sleep(0.2)  # 怕燈泡來不及收取
              DAN.push ('Dummy_Sensor', 0);  time.sleep(0.2) # 這時 DAI6.py 收不到這兩個喔 ! 想想why?
              print ("Slider 第一次的值: ", sss)  # 可在第一次故意先送 100, 再送 0, 再送 sss
              DAN.push ('Dummy_Sensor', sss)  # 若不送就把這列註解掉
              pass    # 第一次是我們 .set( ) 的, 故意不處理; # 現改check滑鼠放掉不會查到  .set(58)
           else:  # 不是我們一開始 .set( )的 !
              print ("Slider 被拉到 ", sss)  # 可以用  DAN.push( ) 送去 IoTtalk server
              DAN.push ('Dummy_Sensor', sss)
        #end of if gotSlider
        if gotInput:
           if theInput =='quit' or theInput=="exit":
              allDead = True
              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)  #  故意多送一個 
        #end of if gotInput
    except KeyboardInterrupt:
        allDead = True
        break;  # sys.exit( );
    except Exception as e:
        print("allDead: ", allDead)
        if(allDead):
            break  #  do NOT try to re-register !
        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)    
    if(allDead): break
    try:
       time.sleep(0.1)  # was 0.2
    except KeyboardInterrupt:
       break
  print("=== end of thready")
  time.sleep(0.015)
  kill_me( );
  sys.exit(0);

def sayBye( ):   # 用來向 IoTtalk 解除註冊 Deregister
  try: 
     time.sleep(0.025)
     DAN.deregister()
  except Exception as e:
     print("===De-Reg Error")
  print("Bye ! --------------", flush=True)
  # sys.exit(0);

# 以下三列把 doDummy 包成 thready 然後叫它平行啟動
thready = threading.Thread(target=doDummy)
thready.daemon = True

if __name__ == '__main__':
   initIoTtalk( )
   threadx.start()
   thready.start()
   tk.mainloop()  #  tk GUI 必須當老大, 在 main thread
            Top   幾個英文字讀音
DAI7.py as Remote_control (遙控器) -- 建議的 project 連接圖在往下程式碼後面
    (請注意這會用到原先 Dummy_Device 的 Library,
      但 DAN.py 必須修改一列讓它state='RESUME' )

      (就是這個 DAN.py 在大約 Line 15 處, 原先是#state = 'RESUME' 最開始的#要拿掉讓它變不是註解)
## DAI7.py -- 這也是改自 DAI2.py ; 但改叫 Remote_control 遙控器喔 !
說明: 因為包括 Remote_control 的少數 DM 在 server 端不處理 RESUME 和 SUSPEND 狀態改變,
      所以 DAN.py 的 Line 15 要去掉"#"以便假裝都在 RESUME 狀態;
      這個修改對其它Device只影響初始state, 
      使其還沒綁定之前也會Push資料去server或去跟 server Pull 資料, 浪費一些網路資源:-)
    如果 Line 15 像原先註解掉則Device初始狀態為 SUSPEND (在 Line 14做的), 
    那樣對其它 Device 綁定前可節省網路資源(自己研究一下 DAN.py 內的 push 和 pull 函數);
    但對於包括 Remote_control 的少數特定 Device 會因為 Server 不處理它們的狀態改變,
    會導致Device 醒不來(不會 RESUME), 這樣呼叫 DAN.push( ) 和 DAN.pull( )都不做事!
  Q: 為何 Server 不處理 Remote_control 的狀態改變?
  A: Server 日理萬機, 要處理那麼多 project 的 Devices, 可以不用做的就不要做以節省精力 :-)
     考量正常的 Remote_control 沒事不會來跟server溝通, 若跑來溝通了一定是有事,阿就是說
     一定是按了開關或轉動旋鈕或操作了什麼的,應該立即處理;所以不必有SUSPEND/RESUME機制。
     滑鼠先 Mark 以下程式碼第一列的第一個 # (或在該列用滑鼠快速點三下),
      然後用滾輪往下捲到程式碼最後(Line 64), 壓著 Shift 不放, 滑鼠點Line 64 那列最右邊,
     敲 CTEL_C 或按滑鼠右鍵選 Copy 即可複製程式碼。
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

# DAI7.py #coding=utf-8 -- Remote_control; Not Dummy_Device,  by tsaiwn@cs.nctu.edu.tw
# 需要修改 DAN.py 在大約 Line 15 的 #state = 'RESUME' 把該 # 去掉; 因 server 不處理遙控器的 RESUME
import time, DAN, requests, random  ### 這樣修改 DAN.py 後只是一開始預設 RESUME, 之前 DAI 仍可執行
# 以下故意間隔不相同, 示範要如何處理多個需要不同間隔做不同的事
interval = 250  # 250 milli seconds, interval for sending RGB 一秒切換四次
gap = 1500 #  每隔 1.5秒, interval for sending Knob (controls Luminance)

#  注意你用的 IoTtalk 伺服器網址或 IP 
ServerURL = 'https://demo.iottalk.tw' # with SSL secure connection

Reg_addr = None  #if None, Reg_addr = MAC address # ( using UUID in DAN.py  只能跑一份) 
DAN.profile['dm_name']= "Remote_control"    # 注意大小寫
DAN.profile['df_list']= [ 'Knob1', 'Color-I1', 'Switch1']  # Only one Knob and one Color-I and one Switch

DAN.profile['d_name']= "TWN_R."+ str( random.randint(700,799 ) ) +"_"+ DAN.profile['dm_name'] # None

def sendRGB():
    global lastSendRGB   # 注意以下 time.monotonic( ) 是實數, 單位仍是秒
    if time.monotonic()*1000 - lastSendRGB < interval: return  # 時間還沒到不用做
    r, g, b = random.randint(0,5)*51, 51*random.randint(0,5), random.randint(0,5)*51
    if(r+g+b < 38): r=255;  # 若接近不亮的顏色(其實就 0,0,0 ), 強迫改紅色
    ggyy = DAN.push ('Color-I1', r, g, b )  # 遙控器第一個色盤是 Color-I1
    print("DAI: send Color-I1 ", r, g, b, "  ?? Result = ", ggyy, "\n")
    lastSendRGB = 1000* time.monotonic()   # 通常都配合上面 Line 19 這樣做啦 :-)

def sendLum():
    global lastSendLum   # 其實送給 Knob1 開關, 可用於控制燈泡的 Luminance 亮度
    if time.monotonic()*1000 - lastSendLum < gap: return  # 時間還沒到不用做
    kk = random.randint(0, 5) # 沒規定 Knob 一定是 0..1; 這邊故意 0..5
    if random.randint(0, 100) < 35: kk = 5  # 35% 機會給它 5.0
    ggyy = DAN.push ("Knob1", kk)  # DAN.push( ) 會回傳是否成功 True/False
    #print("DAI: send Knob1 : ", kk, "  ==> Result = ", ggyy) # for Debug
    DAN.push("Switch1", int(kk >= 2.5) ) # 順便根據 Knob1 的值, 偷送 Switch1 只給 0 或 1
    lastSendLum = 1000* time.monotonic()

if __name__ == '__main__':   # 這樣如果這檔案被 import 則以下不會做
   DAN.device_registration_with_retry(ServerURL, Reg_addr) 
   print("dm_name is ", DAN.profile['dm_name']) ; print("Server is ", ServerURL);
   input("CTRL_C to quit; Bind your DA then Press ENTER: ")
   DAN.push ('Color-I1', 51, 102, 204);  # 偷送 (51, 102, 204) 參考用, 看 Scaling !
   time.sleep(1.68);   # 停 1.68 秒讓你看得見
   DAN.push ('Color-I1', 255,255,255); time.sleep(0.12); # 先偷送 (255,255,255)
   DAN.push ('Color-I1', 0,0,0); time.sleep(0.12); 
   # 先偷送 (255,255,255), 再偷送 (0, 0, 0) ; 這樣之後就不受 Scaling 影響啦 :-)
   DAN.push ('Color-I1', 38, 168, 58);  # 再偷送 (38, 168, 58) 測試 測試 測試
   time.sleep(5.68)   # 停五秒多 方便觀看
   lastSendLum = lastSendRGB = 1000*time.monotonic()   # 現在時間
   while True:
      try:
         sendLum()   # 每間隔 gap milli sec. 送出 Knob1
         sendRGB()   # 每間隔 interval milli sec 送出 r, g, b
         time.sleep(0.008) # 8 milli seconds; ASAP 但也不要太快 !
      except KeyboardInterrupt:
         break;
      except Exception: 
         break; 
   #end of while True Loop
   try:
      DAN.deregister()
      print("Bye bye -------", flush=True)
   except:
      pass
   exit(0);
#end of the program DAI7.py

用一個 CMD 窗把 DAI7.py 跑起來之後, 先到 project 窗綁定它到 Remote_control,
然後回到 CMD 窗按 ENTER 讓它開始自動送資料去 IoTtalk server (前面8秒送很慢, 再來會快速自動送資料)
(別忘了這 DAI7.py 需要原先 Dummy_Device 的 Library)



Top   幾個英文字讀音        
#TOP
  
(七) 如何增加/修改 Device Model 物聯網設備類別 ?                 
◎ 為何要提供(七)這項修改或增加 DF/DM 的功能?    
因為你的 IoT物聯網裝置或設備 在 IoTtalk Server 內可能找不到符合可用的 Device Model !
如果你寫的 DA application(for your IoT物聯網裝置或設備)的 DF (功能) 是 Smartphone 的 subset,
那你可以在DA程式中騙系統說你的 DM 名稱是 Smartphone, 可以不必設計新的 DM;
就是說只要 IoTtalk Server 內有你的 DA 可以用的 DM 就可以這樣做。

    (0)阿這是IoTtalk使用者該知道的喔, 在使用者手冊內有詳細解說 !
          這與如何增加/修改/刪除 Device Features(DF; 設備功能屬性)在同一個操作介面,
        都是從IoTtalk首頁點選 Device Feature Management進入頁面;
        這時 左上角是Device Feature, 用滑鼠點一下就可切換 Device FeatureDevice Model 管理頁面!

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

      (A)先點選 Type 右邊的 IDF 或 ODF 表示要增刪或修改 Input 或 Output 的 Device Features.
      (B)不論是 IDF 或 ODF 我們都分為五種類別(Category):
          Sight(視覺) 、Hearing(聽覺)、Feeling(感覺)、Motion(動作)、Other(其它)
            (結果, 我們發現大多在 Other , 因為阿就不是其它四類啊 :-)
      ** 當然, 類別(Catelog) 切換是點上方藍底白字各類別名稱 !
          其實, 我們已經建立很多的 Input/Output Device Features, 有些甚至只是名稱不同:-)
      (C)針對每個 IDF / ODF 你可以設定一個或多個參數(parameters);
          設定每個參數的 Type (int, float, boolean, void, string, json),
          以及每個參數的預設(default)的下限(Min) 和上限(Max)。
          這裡說"預設(default)"是因為這上下限在該 DF 被拿去組成 Device Model 時還可以個別改 !
        這個 Min 和 Max 目前 IDF 沒意義, ODF 則是用來 auto scale 用的;
        例如若某 ODF 的 Min 是 0 且 Max 是 255,
        則從 IDF 送 [0, 1] (例如捲輪) 到該 ODF 會被自動 scale 成 [0, 255]
        如果 Min 和 Max 都是 0 表示該 ODF 不做 auto scale !
      (D)不論新增 DF name 或修改 DF 當然要記得按 SAVE 存檔!
          請注意, DF name 名稱的大小寫是有意義的!!
          另外, 要注意如果 DF name 是給 VPython 的程式用的, 必須符合變數函數名稱的規定!
          就是必須字母開頭, 不可以有減號 - 和各種怪符號 !!!

          因為它在 VPython 內是某個函數(function)名稱 !

        想增刪或修改設備模型(Device Model; DM)則請讓我們跟新聞主播盛竹如繼續看下去..
    (2) 點左上角的 Device Feature 切換為 Device Model 畫面 (參考下圖)
     
    (3) 右邊視窗中點選 add new DM 以便新增 Device Model (設備)
    (4) 然後要選 想要哪些 IDF 以及/或者 哪些 ODF, 選擇類別 Category, 然後選你要的功能
    (5) 最後記得要點 Save 存檔, 它會問你 Device Model Name (設備名稱)
    (6)注意 Device Model name 若與已經有的重複會警告你要蓋掉 !
      ※※※ 如果該 Device Model 已經有 Project 在用它, 系統是不允許修改或刪除的 !!
    (7)成功加入之後, 就可在 Project 中新增這個Device Model 設備類了
    (8)關於 Devie Feature Management 設備功能的管理可以點這看 DFM 影片教學
                 
    (9)關於 Devie Model Management 設備模型(類別)的管理可以點這看 DMM 影片教學
                 

                      Top   幾個英文字讀音
*** 林ㄧ平教授講解 IoTtalk 物聯網商業化應用經驗(五個影片) 

#TOP     點這回到(四)如何用Python寫Dummy Device
    真實設備上的物聯網裝置程式(DA)實作--以NodeMCU-ESP8266為例
(八) 如果要用 IoTtalk 連接真實設備(例如真的燈泡)那要如何做 ?
      (關於 ArduTalk 與 NodeMCU 以及IoTtalk物聯網應用)
    Q: 到底要如何讓真實的物聯網裝置(IoT Device)連接到 IoTtalk 平台 ?
    A:
這時需要一個 ESP8266 或 NodeMCU 的微控器(Micro controller),
         裡面要有程式可以和 IoTTalk 系統溝通, 又可以控制真實燈泡等物聯網設備。
         ( 註: ESP8266 是指 WiFi 模組(當然含SoC), NodeMCU 通常指 ESP8266 開發板, 或 NodeMCU-ESP32 開發板 )
         ( 還有, ESP8266 是上海樂鑫公司開發, NodeMCU 是深圳安信可公司(為ESP8266第三方公司)基於ESP8266開發 )
         ( 目前 NodeMCU 除了 NodeMCU ESP8266 (注意, ESP8266 的類比輸入只A0一個, 10bits)之外,
              還有增加支援藍芽以及更多類比輸入(18個, 且是12bits)的 NodeMCU ESP32S(基於樂鑫的ESP32開發);
          台灣物聯科技產品很多但通常比較貴:-(也參考這ESP32手冊ESP32 支援 SPI 傳輸
      ==> 這家飆機器人PlayRobot 賣的ESP8266 和 ESP32 模組和開發板比較便宜; 要更便宜請到蝦皮或淘寶網:-)
)
關於 NodeMCU-ESP8266 與 IoTtalk 連結應用展示請參看:
  (1) https://youtu.be/2TcdsVSiLP4 簡介NodeMCU, 一分鐘
  (2) https://youtu.be/36NR2D90Gh0 使用Arduino IDE Blink範例, 五分鐘
  (3) https://youtu.be/TSglOkFO3oA 使用Arduino IDE Fade範例呼吸燈, 六分鐘
  (4) https://youtu.be/vXE_S6wCiVE 同(3), 但改外接 LED燈,  四分鐘
  (5) https://youtu.be/-Tr5pSL845I 讓 NodeMCU 透過手機連上網路, 六分鐘
  (6) https://youtu.be/kwqV_APzhLw 展示如何讓NodeMCU與IoTtalk平台雙向溝通, 14分鐘

    A2: 如果不使用 NodeMCU 開發板,
          也可以使用 Arduino UNO + ESP8266 - ESP01 WiFi 模組(大約NT$40上下);
        ==>NodeMCU-ESP8266的另一個選擇: 做成類似 Arduino UNO 的 WeMOS D1 WiFi 開發板(也是用 ESP8266-ESP12E)
        **> 注意, WeMOS 公司的 D1 WiFi 只是做成外表很像 Arduino UNO,
          *> 最大差別是多了 WiFi, 但類比輸入只一個(A0) !
                Arduino UNO 則有六個類比輸入(Analog Input)義大利原廠的台灣售價大多NT$七百多元
                不是義大利原廠的 UNO R3 通常在NT$300以下, 蝦皮上可以找到只要台幣 90元的 UNO R3(相同的)

                ** 再次提醒 Arduino UNO 沒有 WiFi, 要便宜的 WiFi 可用 ESP-01 模組(約 NT$40元);
                    或是, 可考慮剛說只一個類比輸入+但外表很像 Arduino UNO 的 WeMOS D1 WiFi 開發板
                ** 更新: 現在有 Arduino UNO Rev4 是帶有 WiFi, 只是有點貴 !!!
          如果你有在玩硬體會焊接, 也可考慮類似的 WeMOS D1 Mini; 這在台灣物聯要賣兩百多元超沒良心 :-(
          => 至於如何使用 WeMOS D1 Mini 則可參考這 "小狐狸事務所" 寫的經驗教學;
            ( 註: WeMOS 是一個公司 https://docs.wemos.cc/en/latest/ )
    A3: 重要工作可考慮用 帶有 Ethernet (有線的網路) 的 Arduino Yún (較貴, 大約台幣兩千元上下)!
            ==> Arduino Yún 開發板, 須搭配Ardutalk-for-Arduino Yún (雲)
          Arduino Yún 開發板內有兩個 CPU: (疫情開始後 竟然連 Rev.1 都大缺貨大家亂報價$99999元擺明來亂的)
            o 一個是類似 Arduino UNO R3 開發板 的 CPU (MCU),
                但是 UNO 用的是 ATmega328, Yún 的是 ATmega32u4 更好(是 Arduino Leonardo開發板用的);
            o 另一個是 SoC (Atheros AR9331) 跑類似 Unix 可以執行 Python 程式碼,
            原則上是 ATmega32u4 用 C/C++ 負責 I/O; 其他在 SoC 的 Unix 上用 Python 寫應用(含網路)!
            兩者之間以共用的記憶體溝通!! ( 使用 Bridge 橋接 程式庫 )

          ==> 可以點這跳到稍微後面看關於Arduino Yún如何燒錄與使用的參考資料

        ==> 可惜的是, 原廠已經放棄 Arduino Yún 這產品, 現在已經很難買到, 可搜尋 alternative to Arduino Yún
        ++> 如果需要, 可考慮Seeduino-Cloud 這相容產品   或者 聯發科的 LinkIt Smart 7688 Duo更便宜 (都和Arduino Yún相容)
    ** 關於要放 NodeMCU ESP8266 裡面搭配 IoTtalk 平台的程式,
          我們已經寫好一個ESP8266的通用範例(C++), ( 搜尋 ArduTalk-for-NodeMCU + github )
          你可以用 Arduino IDENodeMCU ESP8266 所需要的 C++ 程式碼燒錄進去,  
      那要怎樣"燒錄"進去呢?
     阿這在本網頁最前面說過喔, 就是要:
         到Github ArduTalk-for-NodeMCU網頁,
    進入網頁後點入 Documents 目錄,

    看裡面 ArduTalk安裝教學(通常只須要燒錄一次)點這裡查看
    以及 ArduTalk 操作教學 (註: 目錄內還有兩個 USB 轉 COM Port 晶片的驅動程式)。 或 點這查看
後面還有更多關於 NodeMCU 燒錄以及 IoT 應用的參考資料/連結
** 如果你不太知道 Arduino, 建議先看看我以前為育達科大的大一同學寫的 Arduino 入門講義
      (也可看看我以前在老共Arduino社群寫的一些文章, 例如: 【小常識】從按鈕開關看上拉pull-up電阻下拉電阻是蝦密碗糕 )

** 推薦額外閱讀Arduino、Web到IoT(林信良 | 2015-05-15)

使用 Arduino IDE 小常識
* 通常使用 Arduino IDE 開發程式碼會用 Serial.print( ) 來印訊息到序列埠(serial Port)以便用序列埠監視窗查看;
    很多人以為把多個 serial.print( ) 利用字串串接方式合併成一個 Serial.print( ) 比較省時間 !?
    其實錯了, 不但沒省時間, 而且也沒省空間(記憶體)!! 詳細可以點這看我以前寫的這篇點這看更多我寫的Arduino相關文章
    也可以點這看我以前在另一個也是很多人用的 Arduino 社群發表的文章(都用 tsaiwn 帳號:-)
** 國字小常識: "埠" (Port) 的讀音 ..
          序列埠(Serial Port)的"埠"念作 "步", Port("埠")意思是港口; 老共把Serial Port翻譯為"串口"好像比較貼切:-)
          所以 Serial Port Monitor (序列埠監視器)老共則翻譯為 "串口監視窗"。
** 非廣告 (沒付我廣告費)【飆_IoT創客】手機控制與IOT 輕鬆上手22+1堂課(Arduino系列) ;
              WebDuino】-- QNAP QIoT : 物聯網(IoT) -- WEBDUINO 輕鬆實現跨入物聯網的第一步
以下是用手機開瀏覽器連入 NodeMCU 準備設定網路的畫面:
* 當然手機要開啟 WiFi 並連接 NodeMCU 的 WiFi
* 這時 NodeMCU 在 AP Server mode, 按下 Submit 後會切換為 Client mode
* 以下用 192.168.0.7 是因為燒錄進去的程式故意用這網址!
    還有, 注意以下圖片內 IoTTalk Server IP 打 140.113.199.200 (這是 demo.iottalk.tw 的 IP) 現在已經不可以用!
    這是因為 .. 因為目前 demo.iottalk.tw (140.113.199.200) 的 port 9999 已經關閉,
    所以現行版本 ArduTalk 因為不支援 https 變成無法(用 :9999)與 demo 這台溝通;
    你可以把 demo 改為 class (IP 自己查:-) 就可以通過 port 9999 與IoTTalk Server 溝通。
    當然, 你的專案(project)和其他設備也要改用 class.iottalk.tw 這 IoTTalk Server 才可以。

     TOP   幾個英文字讀音
  Q: 那該買 ESP8266 模組 還是買 NodeMCU ESP8266 開發板
  A: 如果你自己會焊接電路板, 且要燒錄非常多片, 那可買 ESP8266 模組 比較便宜(但還要另外買 USB to TTL 轉接器才能燒錄!)
    否則當然應該買 NodeMCU ESP8266 開發板 (大約台幣 100元 ~ 120元), 後面會補充 !
    甚至大片的 V3 版 NodeMCU 開發板只賣 NT$65 元, 且它的小麵包板也只要每片大約 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
* 請注意, ESP8266 的 Timer (有兩個) 使用上要非常小心!
* 關於 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 雖然一片大約台幣 1700 ~ 2500元, 但因為它同時有一般Arduino 的 MCU ATmega32u4,
   又有可以支援 Linino/OpenWRT(一種 Linux)的 Atheros AR9331 MCU SoC, (第一版 Yun 已經降價到 NT$1218)
   可以灌 Python 且又有 Ethernet 和 WiFi 且可以用 C/C++ 寫程式碼到 ATmega32u4 的Flash記憶體,
   所以並不算貴; 但很普及的 Arduino-Uno/UNONano 還要另外買 WiFi 模組才方便使用, 加起來大約五百元ㄟ!
   而 NodeMCU 開發板就相當於 Arduino-Nano + WiFi 模組, 且只要台幣120元上下!!(淘寶網有70元以下的!)
   所以, NodeMCU 是不錯的選擇(Analog Input 和 GPIO 較少), 於是就有了 ArduTalk-for-NodeMCU囉 ! 
      ( 註: 這裡說的 NodeMCU 是指 NodeMCU-ESP8266, 不是 NodeMCU-ESP32NodeMCU-32s )
   ** 如果你是要用 Arduino Yún 來連接 IoTtalk, 則要看 ArduTalk-for-ArduinoYun(Github)
      可以點這看如何把 Arduino Yún Rev.2 連接到 IoTtalk 的操作手冊。
   ** 這邊有 NT$1700 的 Arduino Yún 開發板 Rev.2,
      (原先賣 2200, 2019年五月限時特價 1800; 特賣結束變 1700元!)(同一家)(可能因官網已降到 US$50元:-)
**如果你不太知道 Arduino, 建議先看看我以前寫的 Arduino 入門講義    (也可看看我以前在老共Arduino社群寫的一些文章, 例如: 【小常識】從按鈕開關看上拉pull-up電阻下拉電阻是蝦密碗糕)

* 再次提醒:在上面以及 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 製造廠商眾多,
     各版本 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 公司(深圳)
    ** 如果你不太知道 Arduino, 建議先看看我以前寫的 Arduino 入門講義(用 Arduino Uno; 蝦皮較便宜)
            - - - 還有, 也可以看看我以前在老共Arduino社群寫的一些文章; (還有我在geek極客工坊也有發表一些文章) 例如:
                  【神秘知識】為何 delay(1000); 前後只有 999ms (milli second)?
                  教程為何說定時做事的ISR或中断程序內原则上不可用Serial.print
                  教程Arduino IIC/I2C 實驗示例補充   ;   IIC / I2C (讀 I square C) 簡介(IIC == Inter-Integrated Circuit Bus)
** 關於Arduino Yún (這是Rev.2); 以及Ardutalk-for-Arduino Yun (雲)
      --- 至於 Rev.1 是較舊的版本;
官網 arduino.cc 上強調已經 "RETIRED."
** Arduino ; Yun ; ; ArduinoYún ; Next Yun 含稅1218元 ; Yun2/Yun2;
            OpenWRT ; openWRT ; Linino ; BridgeLibrary ; 物聯網OS

    --- ArduinoYun PWM pin: 3, 5, 6, 9, 10, 11, and 13. Provide 8-bit PWM output with the analogWrite() function.
    --- ArduinoYun 有兩個 CPU, 一個是 Atheros AR9331 跑 Linino/Linux (又稱 Yun OS) 可能須更新 OS + Python ;
            另一個CPU 是個較簡單的 MCU : ATmega32U4, 通常用來跑 C/C++ 做 I/O (讀取趕測器/控制設備開關等)

0.Blink測試教學, 沒用到 Yún的SoC(但有開發板詳細資料) ;
第一次使用Arduino Yun雲 ; More Detail (老共翻譯) ; REST/RESTful
** ArduTalk-for-Arduino Yun 雲版本(Python) ; Yun2.pdf手冊 ( custom.py/DAI.py ; AtMega.ino ; 參考 ) ;
          -- 請注意這不是 ArduTalk-for-NodeMCU ;
 
** Summary about Arduino-Yun and Auduino Yun Rev.2 ...
      (1)Arduino Yun (Arduino 雲) 有兩個 CPU:
     一個是 Atheros AR9331, 跑 Linino/OpenWRT(從Linux改的)作業系統(OS), 通常執行 Python 寫的程式;
     一個是 ATmegaXXXX 與其他如 Arduino Leonado 等相同(Arduino UNO 則類似)的 CPU, 執行 C/C++ 程式

      (2)ArduinoYun 開發板上的兩個 CPU 之間透過共用的記憶體溝通資料, 並且各自負責一些主要工作:
     oo 利用 Bridge 類別程式庫做 read from / write to 到共用的記憶體
       注意, 該共用記憶體區在 Arduino Yun 關機或 Reset AR9331 就會不見; 如只 Reset ATmega CPU 則不會不見!

     o AR9331 CPU, Linino/Linux 上當然可執行 Python 程式, 通常負責較複雜的工作, 以及對外網路通訊的工作;
      -- 既然 Linino 也是 Linux 系統, 編輯程式和傳送檔案當然和一般 Linux 系統相同
     o ATmegaXXXX 的程式碼通常負責從 Arduino Input pin 讀取資料以及把資料寫到 Arduino 的一些 Output pin
      -- 用 Arduino IDE 把程式碼燒錄進去 ATmegaXXXX 的方法與使用其他 Arduino 開發板 (如 UNO / Leonado) 完全相同
  TOP   幾個英文字讀音

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

  ⊚ ↑ 以上這些材料網購總價大約 NT$310元(台灣正規店面購買則約500元~700元):
          120 + 15*2 + 18 + 10 + 25 + 5 + 2 + 35 + 運費 60 (預估)

* 關於 NodeMCU 開發板 (這裡指 ESP8266/NodeMCU 開發板, 不是多了藍芽的 ESP32/NodeMCU 開發板)
    購買 NodeMCU 開發板 之前建議可以先看看這篇 "第一次購買 NODEMCU 就上手" 經驗文
    再次提醒, 目前 NodeMCU 開發板有 V2 和 V3 (比較胖), 通常 V3 比較便宜一些;
      雖然建議買 NodeMCU V2 開發板比較小, 方便使用麵包板, 但是經實測發現對 D0 做 PWM 輸出會讓WiFi網路斷線,
    所以, 如果用 NodeMCU V2 開發板, 要燒錄不同的程式碼, 請點這看關於我改過的 t7 版本

* 如果你買的光敏電阻不是模組而是很便宜的一個只有兩端接腳的小光敏電阻(PhotoResistor),
    那還需要串一個 1K 歐姆的電阻再接地(這端並聯到 A0), 可以參考下圖的連接方法:  
  (NodeMCU 沒有 像 Arduino 的 5V, 所以另一端連接到 3.3V 即可)
* 在 NodeMCU 開發板上有支 VIN 的 Pin, 有些文章說它就是 5V 輸出;
    其實這說法不太對!
    但是, 該 VIN 『可能』 可以拿來當 5V 輸出用 ! 怎麼說『可能』呢 ?
    因為其實它是設計來輸入電源給 NodeMCU 用的(所以叫做 VIN);
    其實它就是和 MicroUSB 直接連在一起!
    所以如果你用 MicroUSB 連接 NodeMCU 供電, 這時 VIN 可拿來做 5V 輸出,
    但是要注意不保證 5V, 因為那由你的 USB 決定, 通常落在 4.6V 到 4.9V 之間。
    當然也無法由 MCU 程式碼控制該電壓!
    另外, 請注意,
    V3 版本(就是在 MicroUSB插孔附近寫 LoLin 的)則即使連接 MicroUSB 供電,
    該 VIN 沒辦法做 5V 用, (所以剛剛我才說 『可能』 可以當 5V 輸出用 :-)
    因此時 VIN 幾乎無電壓, 因為被一個 1N5817 Diode 二極管 隔開了!
** 這 V3 版本把 USB 的電另外直接拉到 A0 pin 附近,
      在 A0 往下數兩支 Pin, 叫做 VU, 參考右圖,
      VU 意思是 Volt 與 USB 相同, 這 VU 可拿來當作 5V輸出 :-)
      (山寨版的 V3 開發板可能把 VU 印成 VV 喔, 我買到的就是印著 VV :-)
      (還有, 山寨版的 V3 開發板可能把 LoLin 印成 LOL1n 喔 :-)
 
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 即可

**如果你不太知道 Arduino, 建議先看看我以前寫的 Arduino 入門講義
      (也可看看我以前在老共Arduino社群寫的一些文章, 例如: 【小常識】從按鈕開關看上拉pull-up電阻下拉電阻是蝦密碗糕 )

* 單色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), 可調變電阻等都是很容易測試 !

**如果你不太知道 Arduino, 建議先看看我以前寫的 Arduino 入門講義
      (也可看看我以前在老共Arduino社群寫的一些文章, 例如: 【小常識】從按鈕開關看上拉pull-up電阻下拉電阻是蝦密碗糕 )
** 如果你買了 Buzzer 蜂鳴器, 則可以把 NodeMCU 燒錄我這個 t8Songs 版本
    點入網頁後, 再點入 ArduTalk,
    然後, 再點入 ESP12E_modified_tsaiwn 子目錄, 裡面有 t8Songs 版本 以及 t5 和 t7 版本的壓縮檔。
    ( t5 和 t7 版本說明請在這網頁敲 CTRL_F 搜尋 t5.t7 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 接腳 !
** 可以參考這我以前寫的三色LED+蜂鳴器在 Arduino Uno 的練習講義
** 至於可調變電阻當然要把信號腳(中間那支)接到 NodeMCU 的 A0, 左接 GND 右接 3.3V
** 這是以前寫的用按鈕搭配可調變電阻練習講義也看看這Arduino送資料去PC的練習講義
**如果沒有按鈕(Button), 可用一條杜邦線或電線連接要偵測的 pin 腳去碰觸 GND(接地)即可!
      但是, 記得要把該 pin 設定為 INPUT_PULLUP 以便利用內建的上拉電阻
**聽說 analogRead( ) 很慢? 這是相對其他動作很慢!
      ==> 關於 analogRead( )的解說, 可參考我以前寫的 Arduino 文章
    如果想知道關於 PWM 輸出與 analogWrite( ),
      ==> 也可以點這看我以前寫的 Arduino 的 PWM 輸出
    請注意, NodeMCU ESP8266 的 PWM 預設是用純軟體靠 CPU 的 Interrupt 做的 !
    ( Arduino 是靠 Timer 定時器做, UNO 板有三個 Timer 各管控兩個 PWM pin 所以有六個 PWM pin~ )
    ( NodeMCU-ESP32 把 analogWrite( ) 拿掉了, 因為它的 PWM 改用 timer 做, 參考 ledcWrite( ) 函數 )
    NodeMCU 每一支 GPIO pin 都可以做 PWM 輸出
    主要因為 Aduino 的 Clock 只有 16MHz, 而 NodeMCU 有 80MHz 甚至有 160MHz 版本 !
* 注意: 不過, 實測發現 NodeMCU V2 的 D0 如果做 PWM 輸出會讓 WiFi 斷線! (V3 沒問題)
   

  ** 如果你不太知道 Arduino, 建議先看看我以前上課寫的Arduino基礎入門講義
          (也可以看看我以前在老共Arduino社群寫的一些文章, 例如: 【小常識】從按鈕開關看上拉pull-up電阻下拉電阻是蝦密碗糕 )
    * 關於DHT-11等溫濕度感測器可以參考我在老共的社群發表的文章, 可以讓你學到更深入的概念
        如要了解dht11簡單的使用,
        可以點這看這篇我以前為科技大學大一課程寫的溫濕度感測器實驗講義

    * 可調變電阻的連接方式也可參考我以前關於Arduino結合Processing講義。 或 點這
          或 參考這篇 Cooper Maa 關於 Arduino 實驗的教學文章

    * 基本上建議先把前述ArduTalk操作教學文件內設計的六個實驗範例做過之後, 再來考慮還要買哪些零件來玩 :-)
  TOP     幾個英文字讀音  
★※* 沒概念或沒靈感?
    賈伯斯說過: 創意, 偷就有了!(其實是畢卡索說的)
    ※※ 賈伯斯在1995遺失的訪談中(注意 1:07:05 處)說借用(偷取)別人的點子並不可恥
      *若影片被youtube移除了, 可改點這 https://www.bilibili.com/video/BV1Gx411P7jp/?t=4021
    號稱智商200的翟神也很認同(他說自己經常"偷學"別人的點子!)
          (這其實是摘自翟神寫的書) (創意並不是想出來的,而是偷來的)

    所以,你可以用 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 就可以實現聯網功能 !
* tc543 看台中高工的創意製作競賽-雲端智慧水耕植物工廠   107得獎   107年的   108年..   更多更多   請Gogle大神找更多類似
模仿 →改良 →創新。   模仿,是創新的第一步!
    賈伯斯做了很多沒用的事,但豐富的經歷成了他的創意來源。

  《聖經》中說:「太陽底下無新事。」(見 傳道書 1:9)      
  《佛經》也說:「如果經驗不足,就應當盡量地模仿。」(參見金剛經)
      這個世界上,不存在真正的原創,創意的過程就像搭積木,
      知識就是積木塊,你的積木越豐富,搭出來的東西就越有趣。 (參考這篇的)
紐約時報暢銷書排行榜中有本書,叫《像藝術家一樣偷竊(點子都是偷來的)》(這是該書中文翻譯版),書中說,所有誠實的藝術家被問到創意是怎麼來的時候,都這麼說: 「我是偷來的」。
      藝術領域如此,科學領域更是如此,成功的科學家都是興趣廣泛,善於在看似不可能的知識點之間建立聯繫。
想要有創意,就必須多積累知識,多看、多想、多記下別人的奇思妙想,擴展和連接才是創意的最重要來源。
猶太教與基督教的區別;   *基督教、猶太教、伊斯蘭教間的關係?; *在 Youtube 看9分鐘解說; 伊斯蘭教的崛起(Youtube)
佛教與印度教的關係以及差別(星雲); *為何印度人大都不信佛教??(Youtube); *佛教在中亞和印度是如何消亡的???(文章)
淨空法師說:每個人都不是孤獨的(Youtube); *證嚴法師說故事(Youtube); *印順法與證嚴(Youtube)
把知識連起來就是創意(文章); *老外看蘭陵王(Youtube); *老外看台灣(Youtube); *老外看中國(Youtube)
後面還有更多關於創意的論述       黃霑 滄海一聲笑   寒山寺為何在三更半夜敲鐘?   如何增強記憶力?   歷史很好玩(唐太宗+武則天+唐高宗)?
  gg543 .. How to Burn NodeMCU code -- board manager -- http://arduino.esp8266.com/stable/package_esp8266com_index.json
* 再談關於 NodeMCU 開發板  
    購買NodeMCU之前建議可以先看看這篇 "第一次購買 NODEMCU 就上手" 經驗文;
    也可以點這看看 NodeMCU 及其DEVKIT 開發板介紹
所以提醒大家, NodeMCU ESP8266 開發板建議購買 內建 CP210x 晶片的 ESP12E 開發板 !
如果帶有 CH340 晶片的(通常是 V3)雖然通常比較便宜,
但因為體積較大, 必須使用兩塊小麵包板併起來用, 或另買 V3 底座才有辦法連接材料!

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

* 再次提醒, 雖然建議買 NodeMCU V2 開發板比較小, 方便使用麵包板,
      但是經實測發現V2開發板對 D0 做 PWM 輸出(analogWrite)會讓WiFi網路斷線,
    所以, 如果用 NodeMCU V2 開發板, 要燒錄不同的程式碼, 請點這看關於我改過的 t7 版本
  * 再談 關於燒錄程式碼到 NodeMCU 開發板 以及 ... 543...
ArduTalk for NodeMCU @ github(林一平教授研發團隊)
      ^^^^^ 進入後 (就是 Ardutalk 的 Documents 目錄) 可看到兩個 教學 檔案。
      -----> 安裝教學 是教你如何用 Arduino IDE把 C/C++ 程式碼燒錄進去 NodeMCU-ESP8266 開發板 。
      -----> 操作教學 是教你如何 使用 IoTtalk 平台 透過 NodeMCU 開發板連接物聯網裝置 。
*** 也可以看看網路上其他好心人士寫的 ...
  * 安裝 NodeMCU 相關驅動程式+ArduinoIDE 教學 .pdf (弘道國中 潘建弘老師)
      (潘建弘老師這篇寫得很仔細, 不過裡面 CP210x 驅動程式的連結是錯的 !)
  * 阿玉maker研究區: 關於安裝 NodeMCU 驅動程式
** 關於安裝 NodeMCU 的驅動程式, 在 Arduino IDE 點偏好設定(Preference),
      -- 然後在偏好設定頁面的下方『額外的開發板管理員網址』格子內輸入:

          http://arduino.esp8266.com/stable/package_esp8266com_index.json
*注意, 除了 NodeMCU 驅動程式, 還需要CH34xCP210x 燒錄用驅動程式(NodeMCU開發板上有印字):
    ** CH34X (USB to TTL) 燒錄用驅動程式 .zip(通常是 V3, 但後來有些 V3 用 CP210x 變窄板)
    ** CP210X (USB to TTL) 燒錄用驅動程式 .zip(瘦小窄的開發板, 通常是 V2)
    ** 以上這兩個 USB-to TTL 的驅動程式,
          也可以在我們放Github 上 Ardutalk-for-NodeMCU 的 Documents 目錄內找到!
NodeMCU 搭配 L298N馬達驅動模組 .pdf (弘道國中 潘建弘老師)   王老師談 L298N ..解惑篇
NodeMCU 搭配 LED + DHT11模組 or LM35 .pdf (弘道國中 潘建弘老師)   關於 LM35 + Uno
  **關於 DHT-11, 可以參考我在老共的社群發表的讀取DHT11溫濕度文章, 可以讓你學到更深入的概念。
更多 IoT 裝置 -- 物聯網程式設計社 (弘道國中 潘建弘老師)
* 再說一次, 創意真的偷就有了:-) 賈伯斯在1995遺失的訪談中(1:07:05)說偷取別人的點子並不可恥

* 蝦皮上找到一個很便宜的 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     (現在 2020年 竟然漲價變 NT$19元 - 還是很便宜 :-)
***剛說的這家蝦皮桃園的也有賣 NodeMCU, V2 小片(帶 CP2102接口京片)的 NT$120元; (現2020年漲價變 NT$126)
          NodeMCU V3 大片的 NT$100元(帶CH340 USB to TTL 轉接晶片的): (現2020年跌價 1元變 NT$99)
      https://shopee.tw/search?keyword=nodemcu&shop=4877344
      如果你買了 V3 大片的NodeMCU, 使用時需要兩塊麵包板併起來用, 或者也可另買 V3 擴充底板

      ==> 現在 V3 開發板 有兩種版本:
            大片的用 CH340 USB-TTL 晶片, 瘦(窄)的用 CP210x USB-TTL 晶片)

***剛說的這家蝦皮桃園的 allen_6833 有賣很多材料,
      不過它的 NodeMCU 小片要價 NT$120 元(變126啦)稍微貴一點點, (V3 的 NT$99)

    但是, 幾乎你想要的實驗材料這家通通有 :-) 且店家在桃園蘆竹通常一兩天就可收到貨 !
    https://shopee.tw/search?keyword=arduino&order=asc&page=0&shop=4877344&sortBy=price
      ==> (註: 該店家沒付我廣告費, 我也不認識該店家,阿蝦皮上隨時可能有賣更便宜的店家; 也可去淘寶網有些更便宜:-)
***例如, 各種 NodeMCU-ESP32: (多了 藍芽) (安信可原廠 的稱 NodeMCU-32S)
      ==> 點這看該店 allen_6833 賣 各種 ESP32 開發板
    # 更多 ESP32 開發板, 請點這看露天拍賣上其他店家 ( 芸庭樹的賣場 )
***例如, 麵包, 阿不是, 我是說 麵包板:   點這看該店 allen_6833 賣 麵包板
***例如, 光敏電阻 (Light Sensor):   點這看該店 allen_6833 賣 光敏電阻
***例如, 繼電器 (Relay):   點這看該店 allen_6833 賣 繼電器
***例如, 杜邦線:   點這看該店 allen_6833 賣 各種杜邦線
***例如, 各種 LED:   點這看該店 allen_6833 賣 各種 LED 零件

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

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

* 柯P說過用腳頭烏想也知道(除非你腳頭烏壞掉)蝦皮上的價格隨時可能變動, 請自己隨時到蝦皮搜尋;
  如果要更便宜的 NodeMCU, 那要到淘寶網找 (不過買少量連運費可能不伐算 !)
  (a)最便宜的在淘寶網 "全球易創客", 每個約 NT$65, (人民幣12.75); 库存1885321件
       https://reurl.cc/gnOqQ        (** 提醒:  帶 CP2102 的是窄板)
     (原價 人民幣 12.75, 買第一個特價 8.00元, 注意第2個起每個是人民幣 12.75元)
     *該家另有賣 USB 傳輸線可一起買, 每條約台幣 10元
     *該家要另加運費人民幣至少6元寄送到海岸集運中心, 你再上網付海運費/空運費約每公斤人民幣23元
     *就是說到淘寶網買東西要付兩次錢, 一次付給店家的, 另一次付給物流到台灣的"集運"公司
  (b)淘寶網"8266模組的家", 也是每個約 NT$65, (人民幣13.3);; 库存22121878件)
       https://reurl.cc/dqE3M       (** 提醒:  帶 CP2102 的是窄板)
      (原價 人民幣 13.3, 買第一個特價 9.38, 第2個開始每個 13.3元)
     *該家要另加運費人民幣30元(選官方集運是人民幣8元)寄送到海岸集運中心, 你再上網付海運費/空運費約每公斤人民幣23元
以下是我試著從 淘寶網 "全球易創客" 這店家購買要結帳的畫面:
  (通常很多店家提供如果不到一公斤可以勾選連全部運費一次結算)


* 以下這個則是帶 CH340x 燒錄晶片 的 NodeMCU V3 版本, 比較胖(寬), 須用兩塊麵包板:
    NodeMCU V3 (帶 CH340x 燒錄晶片) ; 每個台幣不到 50元(人民幣 11.2元; 第一個單價 8.22元)
      ( * 該家要另加運費人民幣30元(選官方集運是人民幣8元)寄送到海岸集運中心, 你再上網付海運費/空運費約每公斤人民幣23元 )

* NodeMCU 開發板 須要燒錄一個會和 IoTtalk Server 溝通的程式碼 !
* 再說一次,
    一般提到 NodeMCU 是指 ESP8266 ESP12E 開發板, 因這最普遍;
    ESP8266 是樂鑫公司開發的 WiFi SoC; NodeMCU 則是安信可(AI-Thinker)公司基於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 目錄,
點那有"安裝"的 檔案 ArduTalk安裝教學(NodeMCU).pdf
      -----> 教你如何用 Arduino IDE 把 NodeMCU 的程式碼燒錄進去 MCU;
  簡單說就是要有 Arduino IDE (到 Arduino.cc 抓壓縮檔來解壓縮即可使用)
  然後要安裝 NodeMCU 驅動程式 和燒錄晶片的驅動程式(CH340x 或 CP210x), 之後才能把程式碼燒錄進去 !
  ==> 燒錄好 NodeMCU (ESP8266 ESP12E) 之後, 如何使用來連接電子零件原則上類似 Arduino 操作,
    這樣說是因為網路上有非常多的 Arduino 教學或專題心得等, 自己問 Google 大神和 搜尋 Youtube 就可找到一堆;
不過我們是要和 IoTtalk Server 結合, 所以, 請接著看 Ardutalk操作教學.pdf,
      -----> 裡面提供了使用前面說的一些材料(在操作手冊內P.5)六個可操作練習範例, 至少先練習其中的兩個 !
* 關於用 Arduino IDE 把程式碼燒錄到 NodeMCU 開發板, 除了 ArduTalk安裝教學(NodeMCU).pdf 有詳細說明,
      網路上也有很多教學文章可以參考 !
    其實前面我已經列出一些, 可以點這跳到前面看看 ! ! (按 PageUp 大約五下, 黃色底的網頁)
        關於 ArduTalk for NodeMCU/ESP8266, and ArduTalk for ArduinoYún 開發板 ..
以下先略述該 NodeMCU 程式碼 (ArduTalk_ESP12e_1.ino) 的行為:
(0)開機, 第一個執行的函數當然是 setup( ) { } // LINE 343
  設定 GPIO 0 為 INPUT_PULLUP (for 長按 Flash 按鈕清除 EEPROM 網路資料)
  設定以下七個 GPIO 為輸出接腳:  (點這看 Github 上程式碼)
    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)    // Line 363
     如讀取到資料則做 CALL (2) connect_to_wifi(wifissid, wifipass);
     否則做 CALL (3) wifi_setting();
      Goto (4)   //  i.e., LINE 372 
    (點這看 Github 上程式碼; Line 357 ~ )
(2) function connect_to_wifi(wifissid, wifipass);  // LINE 185
    試著網路連線, 只試 10 秒,    (點這看 Github 上程式碼; Line 184 ~)
    如果 timeout 無法成功就 CALL (3) 變基地台模式+網站server 讓使用者可用手機或筆電連入做設定。
    如果連線成功就:
       點亮NodeMCU 右上角的小 LED:   digitalWrite(2,LOW);
       設定為 STA mode(就是client 電腦啦), 就是設定 wifimode = 0
    return;  // 成功 或 10秒 Timeout
    // *** 所以, WiFi 連線成功之前 NodeMCU 右上角  OnBoard LED 不會亮 !
    /// 在我改過的 t5, t7 等版本, 則改為 LED 大約每兩秒連續快閃七下 (很用力想連上網路 )
(3) function wifi_setting();  // LINE 165  (點這看 Github 上程式碼; Line 165 ~)
    進入 AP Server mode (wifimode = 1) 阿就是基地台模式
    等待 User 用手機連入 192.168.0.1
    連入後會做 handleRoot(0);   (點這看 handleRoot(int retry) 在 Line 103 ~ Line 131)
    在該函數內會先掃描所有的 WiFi SSID,  
        (在 Line 111 呼叫 Line 88 處的 scan_network( ) 產生下拉式選單) )
    吐出網頁讓使用者選擇 SSID 並且輸入其密碼
     還要輸入正確的 IoTtalk Server IP
    輸入完成之後, 點按 Submit, 這時會跳去網頁 /setup 
     也就是執行  saveInfoAndConnectToWiFi( )  // LINE 137  (點這看程式碼 Line 133 ~)
     它會把 SSID 和 PASSwortd 以及 IoTtalk Server IP 存入 EEPROM
     (點這看 save_netInfo() 點這看 connect_to_wifi() -- 阿就是上面(2)說的啦)
     接著會 CALL (2) connect_to_wifi(_SSID_, _PASS_); 
     return;
(4)  // LINE 372   只要還在基地台模式就要等 user 用瀏覽器連入做網路設定, 設定完會變 STA 電腦模式
    while( 在 AP server mode) {  // 就是說  wifimode != 0  
       server.handleClient(); //waitting for connecting to AP ; // 處理手機連進來設定..
       delay(10);  一直等等等.. 等 User 用手機 瀏覽器設定網路並且連線(會設 wifimode=0;) 
    } 
       (點這看 Line 372 ~     點這看 程式庫 handleClient Line 166 ~ 228)
      (點這看 #include WebServer 程式庫的 .h 檔案;  點這看 宣告/定義 server)
(5) 已經連線成功 wifimode == 0   //   LINE 377    
    向 IoTtalk Server 做 iottalk_register( )
    while(註冊失敗) { 
       delay(3000); 繼續 try 註冊; 
       // ReTry 期間, 順便看看有沒按下 NodeMCU 右下角FLASH按鍵, 有長案三秒就清除 EEPROM
    }
    // 注意, 如果註冊不成功, 將陷入這 while Loop
(6) 已經向 IoTtalk Server 註冊成功, 做一些 I/O 準備的設定, 結束 setup( ) { }
(7) 這裡開始是在 loop( ) { ... } 函數內 
    (點這看 loop( ) 函數)
  Line405~456, 用 cycleTimestamp 來檢查時間以便每 0.2 秒才做一次 Push/Pull 
  // 注意只 Push 一個資料(從 A0 讀取的 0 ~ 1023 的值), 但會 Pull 七個資料下來
  // Pull 的 ODF 包括 D0~, D1~, D2~, D5, D6, D7, D8
  // Pull 下來的資料有可能代表 "沒資料", 這時不會 Write 到 GPIO pin
  // D0~, D1~, D2~  這三個對應到 PWM(Pulse Width Modulation) 的 pin (其值是 0 ~ 255);
  // (Arduino 的 PWM 是 0 ~ 255; 但 NodeMCU 的 PWM 舊版 ESP8266 是 0 ~ 1023; 後來新版已改為預設 0 ~ 255)
  // 所以如不確定新舊版本, 可用 analogWriteRange(new_range);  設定為 0 ~ new_range 
 /// 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. 
  // 根據我(蔡神:-)的經驗, Interrupt 的觸發條件儘量選用 FALLING 會比較好, 我曾選用 CHANGE, 結果偶爾會發生偵測不到!
  // 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 (D0) has a built-in pull-down resistor. (NOT Pull-Up)
  // GPIO15 (D8) 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.
  // Also NOTE that  GPIO2 is connected with D4 Pin. 
  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 不就知道了 !
      這個文件確實是說 D0 (GPIO16) 不支援 PWM 也不支援 I2C 也不支援 Interrupt
      阿不過, 如果是 NodeMCU V2 則對 GPIO 16 做 PWM 確實會影響 WiFi 網路(V3 則沒影響!)
      就是說雖然對 D0 做 PWM 有作用, 但 WiFi 馬上就掛了(沒了網路我怎麼"物聯網"啊!) !!
      為此, 我改了一個 t7 版本, 請往下往下捲 或 點這往下跳 先看完 t5 說明再繼續看關於 t7 版本的說明

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 )
                     
關於 t5 版本的說明.. (t5 t7 類似, t7 主要給 NodeMCU V2 板 -- 因為 V2 板 D0 用 analogWrite( ) 會讓 WiFi 斷線! )
另外 ..
另外, 我有修改了另一個版本 t5, 用手機連上設定 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)
///// 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 驅動程式, 這篇裡面的圖片比較清楚
 
  Top   幾個英文字讀音

** 關於 Ardutalk-for-NodeMCU t7 版本 for NodeMCU/ESP8266 V2 開發板..
    前面說過, NodeMCU V2 版本如果對 D0 做 analogWrite( ) 會導致 WiFi 網路斷線
      為此, 我改了一個 t7 版本(放Google雲端) for ESP8266/NodeMCU 開發板 V2
  ==> ( 點入後, 再點入 ArduTalk 子目錄,
             然後點入 ESP12E_modified_tsaiwn 子目錄, 有 t5, t7, t8 三個版本 )

      (開發者資料下載也可參考官方網站: http://www.nodemcu.com )
* 再次提醒, 購買NodeMCU開發板之前, 建議可以先看看這篇 "第一次購買 NODEMCU 就上手" 經驗文
* 關於 NodeMCU 的文件 : https://nodemcu.readthedocs.io/
* 關於 ESP8266 的官方技術文件 :   (樂鑫官方網站 https://espressif.com )
      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 的定時做些事, 竟然有人把我寫的一些文章整理成一篇較長的文章。 不過似乎漏掉了我另一篇用 Timer 的文章
* 請注意, ESP8266 的 Timer (有兩個) 使用上要非常小心!
還有還有, 如果不太懂Interrupt中斷, 請看我以前寫的這篇『關於中断(Interrupt)的一些五四三..』
* 關於 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 又特別慢 !
    但是, ESP32 還是有很多好處! 包括多了藍芽, 有18個類比輸入等!
    甚至, 它也有可用"硬體" timer 控制的 PWM, 但不是用 analogWrite,
    所以它故意把 analogWrite( ) 程式庫拿掉了; 要使用 ESP32 的 PWM,
    必須改使用 ledcAttachPin(pin, channel ) 搭配 ledcWrite(channel, val);
    以下摘錄一位大學老師外號夜市小霸王的心得:
大部分我上IoT課程都用ESP32(NodeMcu-32s), 最大的好處是藍芽、WIFI都內建在裡面,
程式部分一樣使用Arduino IDE,有些結構或Lib不太相同外, 大部分code都可以直接套用,ESP32價格更是漂亮。
如果真的要說與Arduino UNO哪裡不一樣,大概就是ESP32預設沒有類比輸出, 但是可以加裝lib即可,不會影響真正的開發。
( 關於ESP32類比輸出可參考: https://youyouyou.pixnet.net/blog/post/119687001
ESP32版本很多,我只推薦nodemcu-32s的原因是他有5V輸出, 其他大部分只有3.3V輸出,有5V的話就可以推動servo或relay,等於完全可以取代UNO。
非常建議玩Arduino UNO的人可以改玩ESP32。
文章出處:   https://youyouyou.pixnet.net/blog/post/119410732
    通常, Nodemcu-ESP8266 和 Nodemcu-32s 在蝦皮上可找到比較便宜的, 例如: (自己複製以下連結貼到瀏覽器)
        https://shopee.tw/NodeMCU-32S-Lua-WiFi物聯網開發板-串口WiFi模塊-基于ESP32-i.180400872.5309637592
      (只是舉例, 自己問賣家, 要買哪家的不要來問我:-)
               

**前面說過, 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-ESP8266 的 GPIO,
      可以點這看 ESP8266 GPIO 相關資料; 和 GPIO0 與 ESP8266 的啟動模式
** 關於 t8Sounds 版本則是為了使用蜂鳴器Buzzer修改了 analogWrite( ) 以便執行 tone( )
      ==> 讓連接在 NodeMCU 上的蜂鳴器(Buzzer)唱歌 !
//關於 t8 版本簡單說明: 
////////// This is a modified version, t8Songs; by tsaiwn@cs.nctu.edu.tw
/// === This CODE file can be found here:  https://goo.gl/6jtP41
/// === File Path is  ArduTalk/ESP12E_modified_tsaiwn/ArduTalk_ESP12e_1_t8Songs.zip 
///// Suggest buy NodeMCU 1.0 (ESP12E) with CP210x chip (smaller SIZE), V2 or V3 all OK
// A0 :  OK
// D0~ : Disabled
// D1~ : OK ; PWM output
// D2~ : Disabled;  used to play sound on Buzzer; connect D2 to "IN" pin of Buzzer
// D5 : pin D5 used for Local Button and ODF D5 used to control playing Music
// D6: OK ; digitalWrite( ) to D6; You can connect this to an LED
// D7: OK but changed to use analogWrite(D7, 1023 * vlauePull(D7) );
//     and, D7 is also used to on/off light when playing Music
// D8: Disabled
////////////////////============== === Buzzer  Sound / Song === ==================
  ***把蜂鳴器信號腳接到 NodeMCU 的 D2
   ** pin D5 可接一個按鈕, 每按一下播放下一首歌
   ** 可用 遙控器的 Switch 連接 NodeMCU 的 ODF D5
   ** 送 1 給 ODF D5 播放下一首歌; 與按一下 D5 的按鈕相同
   ** 送 0 給 ODF D5 停止音樂 

** 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)

    TOP   幾個英文字讀音
創意(Idea),真的偷就有了 (copy)
      俗話說:「天下文章一大抄看你會抄不會抄!」 ::平衡報導!     LSE專家
**前面已經提到賈伯斯也說借用別人點子並不可恥, 還說他的創意都是偷來的
想要"創新",就要大膽借用別人的想法,關鍵是要把別人的想法消化與吸收。
你要做的,就是如何幫多數的眾人解決很頭痛的問題(成功八大秘訣裡面的"服務"),至於創不創新,根本不重要。
      講到科技界的"偷點子",臉書fB的故事就是最好的例子:
馬克·祖伯格(Mark Zuckerberg)一開始就是想要解決哈佛校園沒有同學名錄 (faceBook)的問題,想讓學生可以互相認識彼此。可是社交網絡(Social Network)這個概念,在Facebook之前已經有一大堆,1995年就有 Classmates.com(2019年排名14417),然後2002年3月有Friendster(2015年6月14日結束)、跟著2003年8月又有 MySpace(2019年排名3614),在2004年到2009年間 MySpace 是全美最流行的交友網站。
      根據2006年2月新聞報導, 當時 Myspace 的網路流量是 Google 的兩倍
阿那個在2005年我也註冊了一個帳號,但是後來就忘了它的存在,直到2014年它改版Email通之我才想起阿我竟然有Myspace帳號!?
如果你有看電影《社群網戰》(The Social Network)( 電影獲得奧斯卡金像獎八項提名,最終獲得最佳改編劇本、最佳原創音樂及最佳影片剪輯。 ),你就知道其實是馬克·祖伯格在2003/10/28晚上弄了「正妹比一比」網站在數小時內癱瘓了學校網路後, 然後2004年1月受到後來曾去2008北京奧運參加划船比賽的孿生兄弟卡麥隆·溫克沃斯與泰勒·溫克沃斯之賞識與邀請, 想做個哈佛專用的社群網站。然而2004年當時 Myspace 已經是全美最大的社群交友網站, 所以嚴格來講,在2004年當時這根本是一個舊到不行的點子(idea)。 隨後馬克·祖伯格自己偷偷撰寫網站在2004年2月4日開放「Thefacebook.com」(後來改名facebook.com), 當然也因此被溫克沃斯兄弟控告剽竊創意, 這起訴訟最後在2008年以1,200萬股的賠償條件達成和解(在Facebook首次公開募股後約市值3億美元)。 溫克沃斯兄弟後來用這筆錢投資比特幣(BitCoin)據說賺到13億美金。(不過如果他們在2017年底沒賣即使比特幣在2019年前七個月狂飆200%那就只剩不到七億美金囉!)
俗話說:「十年河東,十年河西」。在2005年排名第一的社群網站 Myspace 絕對沒想到它現在快倒了(2019年排名3614),而臉書卻成為排名第一的社群網站!
其實在2005年2月 Myspace 曾想併購剛起步的臉書,但是認為馬克·祖伯格要價七千五百萬美金價格太高(不到一億美金)而作罷!(後來當然很後悔,因為2019年臉書流量排名全球第三名(最近掉到第七)而臉書市值大約六千億美金蘋果電腦Apple市值則超過一兆美金。 )
#  
(九) 使用 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等溫濕度感測器可以參看我在老共的社群發表的文章, 可以讓你學到更深入的概念     (也可以看看這我以前教育達科大大一同學的 DHT11 講義) (還有以前我在老共Arduino社群寫的一些文章) *關於讀取按鈕 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 = 'CD8677D49' + str( random.randint(100,999 ) ) # 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 ; or You can Give it a Name
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:
        while gotInput:     # wait till main thread NOT busy  (has sent data out)
           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 = 'CD8677D49' + str( random.randint(100,999 ) ) # 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:
        while 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:
        wgilwgile 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網路卡(較貴) (可自己找一下其他有開發票的)
Raspberry Pi-4B @ 台灣樹梅派 ;; 開機久最好買個帶風扇的鋁合金外殼 ;; 各種版本 R-Pi ;; 與NVIDIA Jetson Nano比
Raspberry Pi-4B性能 ; Buster OS/更新 ;; Raspberry Pi-4B vs. Pi-3B+ ; Raspberry Pi-3B+ vs. Pi-3B
[R-Pi]完全不接螢幕鍵盤滑鼠,電腦一開始就用網路線遠端連線Pi(CaveEDU)
      -- 要注意的是 R-Pi 的 OS 預設關閉 SSH 連線, 但只要在 boot磁區根目錄下有檔名 SSH 的文字檔就會開啟!
      -- 就是說, 在燒錄 OS 到 MicroSD 後,在Boot的根目錄新建一個名為SSH的無副檔名空檔案( 不是資料夾! )即可。
[R-Pi]一次做好 Raspberry Pi 3B+ 系統安裝 ( 沒螢幕 沒鍵盤 沒滑鼠 )
[R-Pi4B]樹莓派 Raspberry Pi 4 Model B 開箱,搭建 Homeassistant 簡單又穩定(2019.08.28)
[R-Pi] Python + 樹莓派 GPIO ; [ C/C++ 開發 ] 樹莓派 GPIO 功能介紹 ;
貴的USB3.0網卡 ; 貴的USB2.0網卡 ;; 有NT$68的USB網卡這也便宜 (便宜通常要手動安裝驅動) (自己搜尋找一下其他有開發票的)
**USB網路卡NT$80USB網路卡 *USB網路卡 ***免驅動USB網路卡USB3.0 Giga網路卡 or NT$299  網路線
其實所謂免驅動程式有些是偷偷把驅動程式塞在USB內啦!   不過也有真的免驅動, 因已經內建在 Windows/作業系統 內啦!
* 邊緣運算(Edge Computing) -- 特殊工作請旁邊的外接設備幫忙算 :-)
  * Google AI 加速器: Google Coral USB Accelerator(Edge TPU)目前售價約台幣3500元
    --- 這也有賣 Edge TPU; 天瓏賣3496元; 沒想到 PCHome 上有店家(PcStore)竟然只賣 NT$3088

  * [教學] 樹莓派 R-Pi 3B+ 搭配 Google Edge TPU + 樹莓派專用的相機模組實驗教學
  這是NVIDIA Jetson Nano 開發套件(Kit); 這賣NT$3600元(未稅); 最便宜大約要 NT$2100元
      --- 這是可以搭配使用的 NVIDIA JetBot AI 機器人自走車約 NT$1600元
  ** CUDA  是NVIDIA 研發的平行運算平台及編程模型(2007/0623開始)是該公司對 GPGPU技術的正式名稱。
  ** TensorFlow是Google從 DistBelief 改來的一個開源軟體庫,用於人工智慧(AI)的機器學習
  ** TensorFlow API:
    ==> TensorFlow provides stable Python (for version 3.7 across all platforms)and C APIs;
          and without API backwards compatibility guarantee: C++, Go, Java, JavaScript and Swift.
    ==> Third-party packages are available for C#, Haskell, Julia, MATLAB, R, Scala, Rust, OCaml, and Crystal.
  超級比一比:Google Coral Edge TPU v.s. NVIDIA Jetson Nano(2019.0516)
  【AI加速運算】各家專用型 AI 推論晶片同場較勁(2019.0114)
  AI 應用需要怎樣的硬體加速?(2019.0108)   為不同應用慎選AI硬體加速方案!(2018.1005)
      TOP   ※ 看看幾個英文字的讀音  Rabboni 的意思
> ※ 劍橋字典 dictionary.cambridge.org 劍橋字典/thyme 讀如 time
(十)Rabboni 小玩具   (使用 ICM-20689 (類似 MPU6050) 六軸感測器三軸重力加速度 + 三軸陀螺儀  )
           
      Rabboni Rabboni Rabboni (抓有 tsaiwn 的 zip檔案; Python 請用 3.7.3 ~ 3.7.7版; 不支援 3.8.x)
★★ 點左方箭頭看更多.. ***把 Rabboni 當作 Smartphone ***
(1)抓回 RabboniTsaiwn.zip 到自己電腦
  點 IoTtalk 入門手冊後跳到(十)Rabboni小玩具:
      https://iottalk.vip/000/#Rabb
    然後看到  "Rabboni Rabboni Rabboni",
    點它會到 Google雲端, 目錄內有很多檔案,
  其中 RabboniTsaiwn.zip 是把 Rabboni 假裝成 Smartphone 連去 IoTtalk;
  把 RabboniTsaiwn.zip 檔案抓回自己電腦, 放入你工作目錄! 
(2)解壓縮, 注意目錄, 平常要練熟 Windows/CMD/Python 的使用!
(3)修改 DAI.py
   用編輯器(如安裝 Python 帶入的 IDLENotepad++),
   稍微修改 DAI.py (通常改兩列即可): 
     (a)你的 MAC address, (b)要連的 IoTtalk Server)
  說明:
   (a)大約第七列, 以下改用您 Rabboni 的 MAC address
      Rabboni_BLE_address = 'D8:5A:75:76:0D:78'  # 要改成你的 Rabboni 的 MAC (在 Rabboni 背面貼紙) 
   (b)修改 IoTtalk Server 那列(第10列), 
      假設用 demo.iottalk.tw, 則要改成: 
        https://demo.iottalk.tw
(4)執行 python DAI.py   (其它檔案都不用改)
   建議用 CMD 視窗, cd 切換到該目錄,
   執行 python DAI.py
*補充說明:
   這時您的 Rabboni 就是一支 Smartphone, 
   在 IoTtalk project 內要關聯綁定 Smartphone 時,
   可看到名稱為您 MAC address 後六碼 (或你修改設定的 d_name)
-------------------- ---------------- ---------------------
*** 注意:
  使用 USB 版本不需要注意 MAC_address , 只要跟別人不同即可,
  它只是用來做 Reg_addr  (註冊位址)以及 d_name 供系統辨認用!
但, 
如果使用 BLE 藍芽版本, 則該第七列附近的的 MAC address 就必須真的!
 必須跟您 Rabboni 背面小貼紙貼的相同!
因為它還要用來跟您 Rabboni 做藍芽配對與通訊!

**再次補充說明:
   看懂該 DAI.py 程式後,
   應該知道以下這三項可以不相同:
     (1)Rabboni_BLE_address = 'D8:5A:75:76:0D:78' # 要改成你的 Rabboni 的 MAC
        以上這 MAC address 用於藍芽配對時一定要跟你的 Rabboni 背面貼的一樣!
        但該 MAC address 目前在程式碼順便做以下兩用途, 則可以改掉:
     (2)Reg_addr = Rabboni_BLE_address  #跟 IoTtalk 註冊要用到這
        所以 = 等號 右邊可改為任一個唯一的 ID, 例如 "55663388abc"
     (3)DAN.profile['d_name'] = Rabboni_BLE_address  # 例如可改 成 "大老二"

點上面左方紅色箭頭可看到更多說明!
  * 點這抓之前 Rabboni 所需的程式碼 (這跟 IoTtalk 註冊時是騙說這 Rabboni 是 Smart phone (便宜版的手機:-)
  * 點這看今天講義 Rabboni_d5.pdf (關於 Dummy Device 請看前面(四)(五(六)各大項)
  * 點這看 Rabboni API 文件(含BLE/USB範例)    或   Rabboni API 文件 .pdf 檔案   ( .docx 檔案)
  * 點這抓新版 Rabboni API 文件(1.7).pdf   點這抓新版 Rabboni API 文件(1.7).docx  
  * Python 請用 Python 3.7.7 版本(目前不支援 3.8.x 和 小於 3.7.3 版本)
    請搭配 Rabboni 程式庫 1.73 版:
            pip install rabboni==1.73

  **(2020.11.10 注意事項):
            安裝 rabboni 程式庫後如果編譯以下範例(請測試 10-1 簡單範例)有錯誤訊息,
            請把 numpy 版本改為1.19.2舊版:
pip install rabboni
pip show rabboni   # 查看安裝到的 rabboni 的版本訊息
pip show numpy   # 查看安裝到的 numpy 的版本訊息
pip uninstall numpy     # 解除 numpy 程式庫
pip install numpy==999.999   # 故意亂打一個版本則它會告訴我們有哪些版本 :-)
pip install numpy==1.19.2     ## 指定安裝 numpy  1.19.2 版本
  **說明: 因為 rabboni 會用到 numpy 這程式庫,
      阿可是該程式庫改版之後變成與 rabboni 程式庫不相容 :-( 好像 1.19.4 又沒問題了 :-)

  * 萬一 pip install rabboni 抓到的 Library 有問題,
      或者你想用舊版本程式庫 :
      ==> 點這抓 LibRab007.zip   (解壓縮到 你的 Python 程庫所在 Lib\site-packages\ 即可)
          - 正常情況應該是用 pip install rabboni 安裝 Rabboni 的程式庫即可 !
  * 如果要送資料去IoTtalk Server, 點這 抓之前上課用的程式碼 Rabboni.zip(連接 IoTtalk 用)
  ** 0.8版之後程式庫 1 代表 1g = 以前舊版本程式庫(0.7版)的 9.8: (舊版重力加速度 1g 用 9.8 表示, 0.8版開始 1g 用 1.0 表示)
      pip install rabboni==0.7   (線上已經找不到就抓我前面說的 LibRab007.zip 去解壓縮)
    ==> pippippip pip pip
  ** 如果你的電腦不認識 pip 命令, 可利用 Python 內建的 pip 模組 "生" 出 pip 命令:
      python -m pip install --upgrade pip
  (萬一有 Error, 改用以下:)
      python -m pip install --upgrade pip --user    
  ** 如果要建立隔離的 Python 虛擬環境: (如果你沒有系統管理員的權限就必須這麼做!)
在 CMD 命令提示窗或 PowerShell 窗中打如下三列:
python -m venv myvenv
cd  myvenv
Scripts\activate
    ==  現在開始已經在隔離的Python 虛擬環境 myvenv (其實系統只是改了 PATH)
    現在你可以 cd 回你原來的目錄繼續做事啦。
      --- 補充說明剛剛三列做的事:
          python -m venv myvenv 建立子目錄 myvenv 並且
                    從系統把 Python 需要的但是不能共用的都 copy 到該 myvenv 目錄內
          cd myvenv 阿就切換進入該 myvenv 子目錄啊 (其實可以不必 :--)
          Scripts\activate 執行 activate 腳本記住原先 PATH 並把 PATH 改為隔離環境內檔案優先做
          --- 如果不做第二件的 cd myvenv 可以直接打 myvenv\Scripts\activate 即可
  === 要離開該隔離環境 myvenv 只要打 deactivate 即可(其實系統也只是改回了 PATH)
  TOP
※ 建議用 Python 3.7.7 64bits 版本搭配 Rabboni Library 1.73 版。
      pip install rabboni==1.73
      pip install rabboni==1.83
*10-1 簡單範例讀取 Rabboni 六軸資料, 最多讀取100次
###  USB.py   (USB mode)  -- modified by tsaiwn@cs.nctu.edu.tw
# -*- coding: UTF-8 -*-
## 原範例 USB.py  ; 注意, 必要時, 就是如果檔案存檔用的編碼是Big5, 把上列的 UTF-8 改為 Big5
from rabboni import *
gfact = 9.8  # scale factor for 1g   # use 1.0 if version == 0.7 or below
rabbo = Rabboni(mode = "USB") #先宣告一個物件                         
print("Try to connect to the Rabboni ...", end="", flush=True)
try:                        
   rabbo.connect()#連結上rabboni,若沒插上會報錯
except:
    print("\n! No Device: 沒看到你的 Rabboni 喔!")
    exit( )
print ("\nStatus: ", rabbo.Status, "( 1 means OK )")
##rabbo.set_sensor_scale(2, 250);  # Acc range, Gyr range == (2, 250) #(8, 1000) #1.73版本前
##rabbo.set_count_threshold(1568)  #1568 == 1.568g #1.73版本前
###rabbo.read_sensor_config( ) # rabboni 2.0 開始才有
##rabbo.set_sensor_config(2, 250, 10, 1568, threshold=1568, )  # 1.83 版本之後#(8, 1000, ..) 
##rabbo.set_sensor_config(8, 1000, 10, 1568, threshold=1568, )  # 1.83 版本之後#(8, 1000, ..) 
###rabbo.read_sensor_config( ) # rabboni 2.0 開始才有
##rabbo.stop( )
##exit( )
##請將設定與錄製分開進行,設定完後離線再進行錄製。 .set_sensor_config(sensor 參數) :
# sensor 參數: Acc 三軸加速度正負值範圍, Gyr 陀螺儀三軸角加速度正負值範圍, sample rate [1, 5, 10, ... 1000]
# 第四個參數為 Cur_Cnt 計步器計步的門檻值 threshold, 100 表示 0.1g, 為 Acc 三軸之 Scalar
time.sleep(0.5)  # 等 0.5 秒阿不然有時出錯
try:
    rabbo.read_data()  #  try to fix the no data bug ?
    #rabbo.read_sensor_char()   # 1.0 版本以後已經不需要這
    #rabbo.read_data()  # for test ONLY :-)
    print("Max of Acc value = ", rabbo.Acc_char)   #加速度值目前測量最大範圍
    while True:#一直打印資料 直到結束程式  
        rabbo.print_data()#print資料  ; 會 delay 一下
        print (rabbo.data_num)
        #if rabbo.Cnt > 10:      # 0.7 版以前 Rabboni 才能用 .Cnt 
        #   rabbo.rst_count() #重置 計步器 count , 會delay一下
        if rabbo.data_num >= 100:
            break
except KeyboardInterrupt:  # 敲 CTRL_C 結束程式
    print('Shut done!')
print ("所有的", rabbo.data_num, " 個 Accx * ", gfact, ": ")
ggyy = [ x * gfact for x in rabbo.Accx_list ]
print(ggyy) #印出到結束程式時的所有Accx值
#print(rabbo.Accx_list)#印出到結束程式時的所有Accx值
try:
   rabbo.stop()#停止運作
except Exception: pass
print("Max of Acc value = ", rabbo.Acc_char)   #加速度值目前測量最大範圍
print("Max of Gyr 陀螺儀 value = ", rabbo.Gyr_char)   # 陀螺儀值目前測量最大範圍
print("Rabboni.Cur_Cnt 計步器 = ", rabbo.Cur_Cnt)
print("如果 Rabboni 沒怎麼動但Cur_Cnt 計步器仍有增加表示 count_threshold 值太小!")
#print("Rabboni attributes = ", dir(rabbo))  # 列出Rabboni所有的 attributes
print('====== Bye bye')
###USB.py

新版程式庫 1 代表 1g = 以前舊版本程式庫(0.7版)的 9.8:
      pip install rabboni==0.7
※ 建議用 Python 3.7.7 64bits 版本搭配 Rabboni Library 1.73 版。
      pip install rabboni==1.73
*10-2 稍微修改原先的簡單範例, 讀取 Rabboni 六軸資料, 並直接存取六軸的資料, 最多讀取 8 次
### testUSB000.py   (USB mode)
# coding=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
gfact = 9.8  # scale factor for 1g   # use 1.0 if version == 0.7 or below
print("Try to connect to the Rabboni ...", end="", flush=True)
try:
   rabbo = Rabboni(mode = "USB")  #先宣告一個物件 
   rabbo.connect()  #連結上rabboni,若沒插上會報錯
except Exception as e:
   print(e);   print("\n! 可能你沒把 Rabboni 用 USB 連接好 !   ")  
   sys.exit( )
print ("\nRabboni 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( )
#time.sleep(0.5)  # 等 0.5 秒阿不然有時出錯
try:
    rabbo.read_data()  #  try to fix the no data bug ?
    # rabbo.read_data()  # for test ONLY :-)
    while True:#一直打印資料 直到結束程式
        rabbo.print_data()  #print資料, 會delay一下; 可把這列註解掉再測試看看  !!!
        print("---")   
        print("Acc x, y, z: ", rabbo.Accx *gfact, 
            rabbo.Accy * gfact, rabbo.Accz * gfact,  end="    ;  ")
        ggyy =  rabbo.Accx * rabbo.Accx + rabbo.Accy * rabbo.Accy + rabbo.Accz*rabbo.Accz
        print( gfact * math.sqrt( ggyy ) )
        print("Gyr x, y, z: ", rabbo.Gyrx, rabbo.Gyry,rabbo.Gyrz,)
        print ("# ", rabbo.data_num, )  
        # 注意 rabbo.Cnt  要舊版 (0.7) 以前  rabboni 程式庫才能用
        #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("Max of Acc value = ", rabbo.Acc_char)   #加速度值目前測量最大範圍
print("Max of Gyr 陀螺儀 value = ", rabbo.Gyr_char)   # 陀螺儀值目前測量最大範圍
print("Rabboni.Cur_Cnt 計步器 counter = ", rabbo.Cur_Cnt)
print("如果 Rabboni 沒怎麼動但Cur_Cnt 計步器仍有增加表示 count_threshold 值太小!")
print('====== Bye bye, data# =', rabbo.data_num)
### end of the program  testUSB000.py

新版程式庫 1 代表 1g = 以前舊版本程式庫(0.7版)的 9.8:
      pip install rabboni==0.7
※ 建議用 Python 3.7.7 64bits 版本搭配 Rabboni Library 1.73 版。
      pip install rabboni==1.73
*10-3 修改原先的簡單範例, 加入檢查 Rabboni 姿態 (檢查移動的 checkMoving( ) 在此程式後面)
### testUSB002.py  (USB mode)
# coding=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=9.8   # use 1.0 for rabboni 0.7 or below;   
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背面
print("Try to connect to the Rabboni ...", end="", flush=True)
rabbo = Rabboni(mode = "USB")  #先宣告一個物件  
try:
   rabbo.connect()  #連結上rabboni,若沒插上會報錯
except Exception as e:
   print(e);   print("\n! 可能你沒把 Rabboni 用 USB 連接好 !  ") 
   sys.exit( )
print ("\nRabboni 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  
#time.sleep(0.5)  # 等 0.5 秒阿不然有時出錯
try:
    rabbo.read_data()  #  try to fix the no data bug ?
    # rabbo.read_data()  # for test ONLY :-)
    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)    
        # 注意 rabbo.Cnt  要舊版 (0.7) 以前  rabboni 程式庫才能用
        #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: 
   print("You hit CTRL_C, Bye!")
   pass; #結束程式
finally:
   rabbo.stop()#停止運作
   print("=== Bye bye ===")
print("###### ######");
### end of the program  testUSB002.py
###testUSB002.py
### 判斷動作的 function  checkMoving( ) ; 放在上面程式碼適當地方!
gyfact = 8.0  ## Rabboni 0.7 版本請用 1.0  ## 0.8 版本之後要看情況調整, 先用 8.0 測試
def checkMoving(faceNow: int):    # 故意規定 faceNow 是 int 整數
   if(rabbo.Gyrx < -500/gyfact and rabbo.Gyry < -500/gyfact):
      print("向左旋轉")
   if(rabbo.Gyrx > 500/gyfact and rabbo.Gyry > 500/gyfact):
      print("向右旋轉")
   if(faceNow == 1):    # 正面向上
      if(rabbo.Gyrx > 500/gyfact and rabbo.Gyry < -500/gyfact):
         print("正面前端向上向我旋轉")
      if(rabbo.Gyrx < -500/gyfact and rabbo.Gyry > 500/gyfact):
         print("正面前端向下凹旋轉")
   elif(faceNow == 2):   #背面向上
      if(rabbo.Gyrx > 500/gyfact and rabbo.Gyry < -500/gyfact):
         print("背面前端向下凹旋轉")
      if(rabbo.Gyrx < -500/gyfact and rabbo.Gyry > 500/gyfact):
         print("背面前端向上向我旋轉")
##更準確的計算方式請參考交大智慧物聯團隊這Boat Control範例裡面的 Talker Node-USB 程式

新版程式庫 1 代表 1g = 以前舊版本程式庫(0.7版)的 9.8:
      pip install rabboni==0.7
*10-4 這是 Blue Tooth 藍芽版本
### testBLE.py  (BLE mode)
# coding=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("D2:5A:75:76:0D:78")  #依照MAC連接   記得改成你的 MAC 
rabbo.discover_characteristics()#掃描所有服務 可略過
rabbo.print_char()#列出所有服務 可略過
# print (rabbo.characteristics)
# print (rabbo.Status)
time.sleep(3)  # 等 3 秒讓你看 :-)
rabbo.read_data()#讀取資料 必跑
# rabbo.read_data()  #  try to fix the no data bug ?

try:
    while True:#一直打印資料 直到結束程式
        rabbo.print_data()#print資料
        #if rabbo.Cnt == 100:        # 0.7 版以前才有 .Cnt
        #   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 ###

新版程式庫 1 代表 1g = 以前舊版本程式庫(0.7版)的 9.8:
      pip install rabboni==0.7
*10-5 這是 USB 版本, 讀取 168次, 會統計六軸的最大與最小值, 最後會印出讓你參考
### testUSB.py   (USB mode)
# coding=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()
time.sleep(2)  # 等 2 秒阿不然有時出錯
try:
    rabbo.read_data()  #  try to fix the no data bug ?
    # rabbo.read_data()  # for test ONLY :-)
    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 as e: # 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



新版程式庫 1 代表 1g = 以前舊版本程式庫(0.7版)的 9.8:
      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("D2:5A:75:76:0D:78")#依照MAC連接
rabbo.discover_characteristics()#掃描所有服務 可略過
rabbo.print_char() #列出所有服務 可略過
# print (rabbo.characteristics)
# print (rabbo.Status)
time.sleep(2)  # 等 2 秒阿不然有時出錯
rabbo.read_data() #讀取資料 必跑
# rabbo.read_data()  #  try to fix the no data bug ?

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:    # 0.7版本以前才有 .Cnt
        #  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()
   
* 手機搭配 Rabboni -- 可用手機掃瞄以下 QR code:
   
* 手機: 點這安裝 搭配 Rabboni 的 APP (GooglePlay)

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

新版程式庫 1 代表 1g = 以前舊版本程式庫(0.7版)的 9.8:
      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 (字串):
            注意這句: DAN.profile['df_list']=['Acceleration', 'Gyroscope', "Float", "Total"]
            所以, 建立新 Device Model "Rabboni" 時, 要有上述四個 IDF
    ** 請注意看以下紅色字體的 code; 為了與別人的 Rabboni 區別, 請修改 Rabboni_BLE_address

    執行以下程式碼, 測試: (a)把Rabboni正面放著 (b)把 Rabboni 背面向上放著 (c)用力搖 Rabboni
### DAI_Rab5u.py -- Rabboni with IoTtalk  (USB mode)
# coding=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=9.8   # use 9.8 for rabboni 0.8 ;  use 1.0 if Library is rabboni 0.7 
Rabboni_BLE_address = 'C6:98:9D:Hahaha:92'  # 你 Rabboni 的 MAC address  (其實跟 MAC 無關 :-)
ServerURL = 'http://demo.iottalk.tw:9999'  # 請確定 IoTtalk Server 
rabbo = Rabboni(mode = "USB") #先宣告一個物件  # pip install rabboni==1.73
print("Try to connect to Rabboni ..", end="", flush=True)
try:
   rabbo.connect() #連結上rabboni,若沒插上會報錯
except Exception as e:
   print("\n! ",e);   print("可能你沒把 Rabboni 用 USB 連接好 !   ") 
   sys.exit( )
print ("\nStatus:", rabbo.Status)
time.sleep(0.5)  # 等 0.5 秒阿不然有時出錯
rabbo.read_data()
# rabbo.read_data()  #  try to fix the no data bug ?
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['df_list']=['Acceleration', 'Gyroscope', ]   # 如果當作 Smartphone
#DAN.profile['dm_name']='Smartphone'  # 如果當作 Smartphone 
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

print("Rabboni 內部計步器 Store_Cnt = ", rabbo.Store_Cnt, ", Cur_Cnt=", rabbo.Cur_Cnt )
gg = input("現在要不要把 Count Reset 歸 0 ? ")   #Cur_Cnt 要0.8版本後
if gg == "yes" or gg == "y": rabbo.rst_count()
print("Rabboni 內部計步器 Store_Cnt 現在 = ", rabbo.Store_Cnt )
yy = input("每隔幾次印 myFloat ? ")
try:
  yy = int(yy)
except Exception:
  yy=0
if yy==0: yy=300
print("OK! 每 ", yy, "次印一次。")
yyCnt=0
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)  # 若用 Smartphone 註解這列
        ggOLD = [rabbo.Gyrx, rabbo.Gyry, rabbo.Gyrz]
        #tot = (rabbo.Cnt % 23 ) * 1.568   # 亂寫的 :-)  # 0.7 版本才有 .Cnt
        tot = random.randrange(8, 38)   # [8, 38)
        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 是字串  # 若用 Smartphone 註解這列
        yyCnt += 1
        if yyCnt==1 or yyCnt % yy == 0:
           print("#", yyCnt,"\t", end="")
           print("===myFloat: ", myFloat, ", tot: ", tot )
           #print("    Cnt=", rabbo.Cnt)    # 0.7版本才有
    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:
       print("#", yyCnt,"\t", end="")
       print("===myFloat: ", myFloat, ", tot: ", tot )
       #print("    Cnt=", rabbo.Cnt)   # 0.7 版本以前
       print("    Cur_Cnt=", rabbo.Cur_Cnt)   #Cur_Cnt 要0.8版本後
       break;
try: 
   DAN.deregister()
   print("解除註冊 -- ", DAN.profile['d_name'])
except Exception as ee:
   print("!!! ", ee)
finally:
   rabbo.stop() # Rabboni 停止運作
### end of the program  DAI_Rab5u.py    ( USB mode ) 

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

新版程式庫 1 代表 1g = 以前舊版本程式庫(0.7版)的 9.8:
      pip install rabboni==0.7
*10-8 這是 BLE 版本, DAI_Rab5b.py -- 資料會送去 demo.iottalk.tw 的 Rabboni 裝置
### DAI_Rab5b.py -- Rabboni with IoTtalk  (BLE mode)
# coding=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 = 'D2:5A:75:76:0D:78'  # 你 Rabboni 的 MAC address 
ServerURL = 'http://demo.iottalk.tw:9999'  #請確定 Server 

#ServerURL = 'https://demo.iottalk.tw'  # Secure 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)
time.sleep(2)  # 等 2 秒阿不然有時出錯
rabbo.read_data()
# rabbo.read_data()  #  try to fix the no data bug ?
Reg_addr = Rabboni_BLE_address

DAN.profile['dm_name']='Rabboni'   # Devie Model name 
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   # 亂寫的 :-)
        tot =  8+ int( random( ) * 33 )
        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, ")
        #print("    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 小玩具 ======
Top  幾個英文字讀音
關於 Joint 函數的寫法與應用請參考(三)三個人用手機控制同一個燈


Rabboni 內部晶片 -- 採用 ICM-20689 (類似 MPU6050) 六軸感測器
萬有引力 -- 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)
 
 
創意真的偷就有了! --- 把知識連起來就是創意   點子都是偷來的:10個沒人告訴過你的創意撇步
5G尚未商用,6G就已踏上來時路! 下一代地面無線通信的頻段0.12THz (0.12 太赫茲)   台灣5G..2019年12月競標中...
    ( 5G通訊台灣採用的是3.5GHz頻段和28GHz頻段以及1800MHz頻段 ) (據說太赫茲波還可以治療癌症ㄟ)   台灣無線電視頻段

    ★★★ 太赫兹波(TeraHertz, THz)是頻率在0.1THz-10THz (1T = 1 Tera = 10的 12次方)範圍内的電磁波。   光速約每秒30萬公里   暗物質無法通過電磁波觀測   暗能量
    ★★★ 輻射不等於電磁波,但是電磁波會產生輻射   科學家2012年找到上帝粒子   回到未來:蟲洞是是時空的隧道   黑洞其實不是洞   白洞可能不存在   真有平行時空?   救世主
    ★★★ 關於頻率也可看看我以前寫的鬼話連篇談無線網路與電磁波頻率(舊版)。啥?有些很像是亂ㄅㄞ的喔?對啦.. 有些是我亂掰的啦..咬我啊:-)不過大部分都是有根據的喔!  

※※※ 不過, 一個 Tera 也才 10 的 12次方,不但『不可說』和『不可說不可說』都比一個 Tera 大很多,甚至一個 Googol 也遠比它大
⊚ ⊚ 關於小倍數 例如nano:何處惹塵埃? "瞬息"、 "須臾"、 "彈指", 這三個到底哪個最? 哪個最

健康很重要!要顧好身體, 不然沒機會實現你的創意 (醫生的診斷有三成是誤診,by老共醫師紀小龍) 黑心錢 感冒藥vs瘦肉精 黑心才能變名醫(備用)
  ※😷請注意感冒其實沒有藥   珍珠奶茶最好少喝或不要喝! 塑化劑滅國?   脂肪和膽固醇被誤會四十年其實兇手是糖!   糖是兇手! 不是脂肪!

* 號稱飢餓大師劉一鳴醫師強調發燒不該吃退燒藥( 其實是感染科名醫王任賢說的; 建議從頭看該影片!可以把播放速度設 1.25 倍節省時間:-)  
劉醫師說慢性病可以逆轉不用吃藥!   劉醫師又說吃素其實不健康!還說要經常斷食飢餓可治療百病?   說梅干扣肉的扣肉比梅干建康   劉醫師還說喝黑咖啡很好!?   甚至還說要多吃牛肉!?   不同說法!?
      (註:有人批評劉一鳴醫師騙錢大王..因為他診療費每小時四千元且還跟你推銷數萬元到數十萬的維他命!
            (其實他推銷數十萬元的營養品就是抗氧化劑維他命營養素, 去 iherb 買較便宜, 可以參考我寫的 iherb 購買心得教你省更多!)
          -- 不過罵他的主要是罵他斂財$ 對於劉一鳴的書與醫學知識則大多贊同! 請自行多看自己判斷.. 盡信醫生不如不要看醫生! 反正.. 加減看 MED/Youtube 不要錢
:-)
    ※ 其實..斷食..可以從略過一餐開始克里斯汀·德·迪夫博士在1974年獲得諾貝爾醫學獎, 因為他首度發現體內細胞自噬機制(Autophagy = self-eating);
          其追隨者大隅良典(Yoshinori Ohsumi)博士則因為發現一日飢餓(間歇斷食)的秘密得到2016年諾貝爾醫學獎為什麼研究飢餓也可獲獎?
    江坤俊醫師說想餓死癌細胞仍要吃維生素D(要曬太陽或吃D3) 一天一D3 江坤俊醫師說D3吃多了不會中毒! D其實是賀爾蒙+低醣優脂可抗癌! 餓死癌細胞! 癌細胞愛吃糖! 癌細胞怕這些菜!
      -- 所以劉乂鳴醫師一週吃七餐其實也是有理論基礎的ㄟ(2016諾貝爾醫學獎認證)!   越來越多正規醫師開始說維他命真的有用!

    ※ 但是請注意:根據許多醫師研究與統計,若常常早餐不吃已經被證明比較容易得膽結石胰臟癌
      所以,建議斷食期間早上(最好七點半之前)還是要喝些帶有油脂的東東並喝250cc~500cc的室溫開水
光年之外 Marshmello - Alone (孤獨) Alan Walker - Alone (孤單一人) Alan Walker - Faded (解說人間迷走Piano) 2 (滴妹訪問Alan Walker (艾倫·沃克) (Wiki)) 漂向北方
哈佛大學說:風力發電害氣候暖化 不輸火力發電(自由時報) 外媒說沒那麼嚴重 -- 平衡報導   芒種Lyrics 芒種MV 芒種Rap搞笑 MR 國曆6月6附近 立冬 二十四節氣   佛系 一曲
台北市捷運行政大樓之興建史   【玩老外】懂英文! Let It Go 放開手   我喜歡 你不懂 好孤單 西海情歌   涼涼   不一樣 追光者 打了烊   雲朵天長地久 天路
     
* 想學 Python + Flask 請點這     Jinja2 Jinja2 Doc     *點這看我寫的超簡單 HTML 網頁教學(可搜尋 HTML + tsaiwn 找到)   *也有要錢的瀏覽器 (沈修平博士)
* 點這看如何選擇 VPS ?     (建議用 DigitalOcean 或 Hostwinds.com 流量小可以先用免費的 AWS EC2)     C++11 vs. 14   14 vs. 17     OppO R17 vs. R17Pro

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

* 取妻當取李子柒? 不說話的網紅年賺多少館長也很她ㄟ!唱歌好聽的阿冷以及台灣任何網紅還要紅的李子柒李子柒何許人 i 網紅 阿冷演講 沙漠骆驼
   李子柒、辦公室小野谷阿莫...谷阿莫這群人、蔡阿嘎小玉、以及黃氏兄弟讓大家相信努力就可賺到Youtube的錢。 排名點閱排名也是排名   第一名   黃氏兄弟
  *  關於林一平教授可以參看林教授個人網站林教授Digitimes專欄林教授的書『大橋驟雨』 (林一平fB臉書大戰宅神)
      創意真的偷就有了!   👉 沒有是非的時代誰造成的 ( 要有善良的心, 請思考我們與惡的距離 !)