2015年10月28日 星期三

ESP8266 NodeMCU 使用 Lua 自動連網

ESP8266 NodeMCU 使用 Lua 自動連網

在使用Arduino的時候,有時候會需要將TxRx的連線訊息透過 WiFi 傳送到一個指定的網站。這時就可以使用 ESP8266 with nodeMCU 來達成。
在ESP8266裡面刷 nodeMCU 來跑 lua script 的方式可以參考之前的文章。
所以這邊就直接把範例程式碼寫出來如下:
ssid = "My_AP"
passwd = "12345678"
scratch_ip = "192.168.0.1"
scratch_port = 12345
wifi.setmode(wifi.STATION)
wifi.sta.config(ssid,passwd,1)

tmr.alarm(1,1000, 1, function() 
    if wifi.sta.getip()==nil then 
        print(" Wait to IP address! for " .. ssid) 
    else 
        print("New IP address is "..wifi.sta.getip())
        tmr.stop(1)
        sck = net.createConnection(net.UDP)
        sck:on('receive', function(sck,pl) uart.write(0,pl) end)
        uart.on('data',0, function(data) sck:send(data) end,0)
        sck:connect(scratch_port,scratch_ip)
        sck:send("START UART Tunnel\n")
        uart.setup(0,38400,8,0,1,0)
    end 
end)
將這個 script 用 "init.lua" 的檔名存到 ESP8266 晶片之中之後,之後 ESP8266 開機就會自動去和指定的無線 AP 連線,然後把從 UART port 所收到的訊息用 UDP Socket 的方式送到指定的 ip 位置裡的指定port。
其中 ssid 變數指定無線 AP 的 SSID ,passwd 變數為 AP 登入密碼。 scratch_ip 和 scratch_port 則是連線之後 UART 訊息要收發的伺服器位置和 port。
其中 tmr.alarm() 函示用來設定 timer 每隔 1 秒就執行一次 wifi.sta.getip() 確認是否有正確取得IP位置。如果確認取得IP位置就執行另一段程式碼將timer 關掉,並且和伺服器建立一個 UDP 的 socket ,將UART收到的封包傳送到伺服器,並將伺服器傳送過來的封包透過 UART 發送出去。
該段程式碼是用來把 S4A 的板子所發出來的封包透過 wifi 連線和指定的伺服器做連線。

前進校園計畫投影片-- 用Raspberry Pi 結合 Arduino 實現 Wi-Fi 遙控車 -- 用 Python 玩 WiFi + S4A







投影片中用到的 Code 都放在 GitHub 上面:

https://github.com/itrobotics/Python_Control_Motoduino




ESP8266 NodeMCU 使用 ESPLorer 寫入 Lua Script

ESP8266 NodeMCU 韌體設計 Lua Script

上一篇文章中有提到刷新 ESP8266 韌體的做法,這次說明的是使用開發工具 ESPlorer 來寫入Lua Script 進去ESP8266(NodeMCU)的做法。
而nodeMCU所提供了API查詢網頁可以查詢其中所支援的 API 與其定義。

ESPlorer

ESPLorer 是一款用 java 寫的開發工具(for ESP8266)。在 mac os x 或 Linux 底下需先安裝 JDK 套件之後才能執行它。打開 command line 之後,跑到 ESPLorer 的目錄底下鍵入:
$> java -jar ESPlorer.jar
之後就會出現ESPlorer的使用者介面如下:

和 ESP8266 連線

ESP8266 連線時的 pin 腳連線設置有兩個模式,一個是燒錄模式,另一個是一般模式。

燒錄模式接線接法

  • CH_PD --> 3.3v
  • VCC --> 3.3V
  • UTXD --> RXD (白色線)
  • URXD --> TXD (綠色線)
  • GPIO0 --> GND <重點>
  • GND --> GND

一般模式接線接法

  • CH_PD --> 3.3v
  • VCC --> 3.3V
  • UTXD --> RXD (USB-TTL 轉接線:白色線)
  • URXD --> TXD (USB-TTL 轉接線:綠色線)
  • GPIO0 --> 不要接 <重點>
  • GND --> GND
在燒錄 lua script 進去 ESP8266 的時候是使用一般模式的接線法。pin 設置做好之後就可以準備開始和 ESP8266 連線了。
nodeMCU 預設連線的UART設定是 9600,8,N,1 ,使用 ESPlorer 的時候要設定畫面右半邊的連線區域上的 Baudrate 選項設定為 9600 ,然後指定連線設備為 "/dev/tty.usbserial" (在 Linux 系統上應該是 "/dev/ttyUSB0")
設置完按下 "Open" 按鍵就開始連線。這時如果把ESP8266重新上電的話應該會看到類似下面的訊息:
NodeMCU custom build by frightanic.com
branch: master
commit: bb9dd62882b89d40f5d0df2c009c972f64b7f965
SSL: false
modules: node,file,gpio,wifi,net,tmr,uart
built on: 2015-09-18 02:26
powered by Lua 5.1.4

編輯 Lua Script 檔 & 燒錄 Lua Script

開發工具的左半邊是 Lua script 的程式碼編輯區,在此區編輯要燒入ESP8266模組的Lua script檔。要在連線的狀態之下才能燒入 Lua Script。
假設我們先寫了一個印出 "hello world" 的程式碼:
print("hello world")
然後按下編輯區下方的 "Send to ESP" 按鈕,會看到開發工具會把該行程式碼傳送給ESP後馬上執行。如果有程式碼不只一行的話會看到,傳一行執行一行,結束後才執行下一行的動作狀況。
開發工具也支援將lua script 用檔案的形式儲存在晶片之中,之後再呼叫出來運行的方式。做法是先在編輯區之中把程式碼打好,然後按上面的 "Save" -> 指定存檔檔名(副檔名要為.lua)。最後再按下下方的 "Save to ESP" 按鈕,就會看到開發工具會用 lua 的 file open 的方式來把程式碼檔案寫入晶片之中的檔案系統,並且用剛剛存檔的檔名。之後就可以使用 lua 的 dofile("檔名") 來執行已存在晶片之中的 lua script檔。
dofile("script.lua")

開機後執行的第一個lua script檔

nodeMCU的韌體預設在晶片開機的時候,會先自動呼叫 "init.lua" 這個檔案來執行。 如果要有開機後執行初始化設定的需求的時候,就把初始化動作寫好之後存指定檔名為 "init.lua" 之後,再按 "Save to ESP"。

2015年10月18日 星期日

使用 Google fusion tables

存取google 的服務,都會用到OAuth 的認證機制,所以使用 Google fusion tables當然也不例外,底下說明的使用的是OAuth 2.0 for Devices過程,一旦取得Access Token,就可以對Google fusion tables 執行新增資料、查詢資料、刪除資料等操作。


OAuth 2.0 for Devices的認證流程
The user logs in on a separate device that has a browser.

使用 OAuth 2.0 , 必須先到Google Developers Console 建立一個專案以取得client ID及client secret. 註: 須於建立用戶端ID頁面, [已安裝的應用程式類型]請選擇"其他"

注意:curl 必須支援HTTPS (安裝說明)

取得Authorization Code


root@raspberrypi:~# curl -d "client_id=530304002742-55mhfghci5o59tur1tgt183p38ceh08t.apps.googleusercontent.com&scope=https://www.googleapis.com/auth/fusiontables" https://accounts.google.com/o/oauth2/device/code
{
  "device_code" : "ZMUM-BZYM4/b1grhomQLBtifJxKrATFJ-m8AoYV7tr3RfN1BLvOTlo",
  "user_code" : "ZMUM-BZYM",
  "verification_url" : "https://www.google.com/device",
  "expires_in" : 1800,
  "interval" : 5
  
} 
  
The user_code and verification_url from the JSON object should be shown to your user. The idea is to ask the user to go to a browser, navigate to the verification_url URL, and enter the user_code. The user_code is case sensitive, so the user will need to enter the code exactly as it appears in the response.



  
=======================================

獲得access tokens

code為上一個請求所傳回的device code

root@raspberrypi:~# curl -d "client_id=530304002742-55mhfghci5o59tur1tgt183p38ceh08t.apps.googleusercontent.com&client_secret=xtogS1gi_MSl_7apvzfrsvmK&code=ZMUM-BZYM4/b1grhomQLBtifJxKrATFJ-m8AoYV7tr3RfN1BLvOTlo&grant_type=http://oauth.net/grant_type/device/1.0" https://www.googleapis.com/oauth2/v3/token




{
 "access_token": "ya29.tAH13KudNYGAmG9rtdXLJjO_QNPjbJcvEkJEeG_C3MxqWh5T9sFbbrguWh77llT6JbVL",
 "token_type": "Bearer",
 "expires_in": 3600,
 "refresh_token": "1/97_rdY40n2PusItQPVPENp5ECTZYqFvUobJZB09nOxlIgOrJDtdun6zK6XiATCKT"
}

==================================

#Access Token 過期時, 出現 401 "Invalid Credentials" 錯誤

root@raspberrypi:~# curl -H "Authorization: Bearer ya29.tAH13KudNYGAmG9rtdXLJjO_QNPjbJcvEkJEeG_C3MxqWh5T9sFbbrguWh77llT6JbVL" -d "sql=SELECT * FROM 1fV5mXuKgG5cCck1cAVQX0G7HVTjfdm1SqeYSdmXU WHERE number=43" "https://www.googleapis.com/fusiontables/v2/query" { "error": { "errors": [ { "domain": "global", "reason": "authError", "message": "Invalid Credentials", "locationType": "header", "location": "Authorization" } ], "code": 401, "message": "Invalid Credentials" } } =======================================

# Refresh Token

root@raspberrypi:~# curl -d "client_id=530304002742-55mhfghci5o59tur1tgt183p38ceh08t.apps.googleusercontent.com&client_secret=xtogS1gi_MSl_7apvzfrsvmK&refresh_token=1/97_rdY40n2PusItQPVPENp5ECTZYqFvUobJZB09nOxlIgOrJDtdun6zK6XiATCKT&grant_type=refresh_token" https://www.googleapis.com/oauth2/v3/token
{
 "access_token": "ya29.tAHsfamfZ4CUSiMwXG0TztY9DYkcj3Zs988p9Wr_GrZz5vRbxkGHFoQiHxDMNsuEZf8-",
 "token_type": "Bearer",
 "expires_in": 3600
}


一旦取得Access Token並知道過期後如何更新Access Token, 接下接下來就是對Google fusion tables 執行新增資料、查詢資料、刪除資料對表格的操作。使用的方式使用HTTP POST,但必須在每一次的HTTPS請求,在Header中加入Access Token

#新增 "sql=INSERT INTO {table} (number,Address)VALUES(43,'IT robotics lab')"

root@raspberrypi:~# curl  -H "Authorization: Bearer ya29.tAH13KudNYGAmG9rtdXLJjO_QNPjbJcvEkJEeG_C3MxqWh5T9sFbbrguWh77llT6JbVL" -d "sql=INSERT INTO 1fV5mXuKgG5cCck1cAVQX0G7HVTjfdm1SqeYSdmXU (number,Address)VALUES(43,'IT robotics lab')" "https://www.googleapis.com/fusiontables/v2/query"
{
 "kind": "fusiontables#sqlresponse",
 "columns": [
  "rowid"
 ],
 "rows": [
  [
   "5002"
  ]
 ]
}
==================================

#查詢 "sql=SELECT * FROM {table} WHERE number=43"

root@raspberrypi:~# curl  -H "Authorization: Bearer ya29.tAH13KudNYGAmG9rtdXLJjO_QNPjbJcvEkJEeG_C3MxqWh5T9sFbbrguWh77llT6JbVL" -d "sql=SELECT * FROM 1fV5mXuKgG5cCck1cAVQX0G7HVTjfdm1SqeYSdmXU WHERE number=43" "https://www.googleapis.com/fusiontables/v2/query"
{
 "kind": "fusiontables#sqlresponse",
 "columns": [
  "Date",
  "Time",
  "number",
  "Location",
  "Address"
 ],
 "rows": [
  [
   "",
   "",
   "43",
   "",
   "IT robotics lab"
  ]
 ]
}
==================================

#查詢ROWID SELECT ROWID FROM {table} WHERE number=43

root@raspberrypi:~# curl -H "Authorization: Bearer ya29.tAHsfamfZ4CUSiMwXG0TztY9DYkcj3Zs988p9Wr_GrZz5vRbxkGHFoQiHxDMNsuEZf8-" -d "sql=SELECT ROWID FROM 1fV5mXuKgG5cCck1cAVQX0G7HVTjfdm1SqeYSdmXU WHERE number=43" "https://www.googleapis.com/fusiontables/v2/query"
{
 "kind": "fusiontables#sqlresponse",
 "columns": [
  "rowid"
 ],
 "rows": [
  [
   "5002"
  ]
 ]
}
==================================

#更新 sql=Update {table} Set Address='ittraining' WHERE ROWID='5002'

root@raspberrypi:~# curl -H "Authorization: Bearer ya29.tAH13KudNYGAmG9rtdXLJjO_QNPjbJcvEkJEeG_C3MxqWh5T9sFbbrguWh77llT6JbVL" -d "sql=Update 1fV5mXuKgG5cCck1cAVQX0G7HVTjfdm1SqeYSdmXU Set Address='ittraining' WHERE ROWID='5002'" "https://www.googleapis.com/fusiontables/v2/query"


{
 "kind": "fusiontables#sqlresponse",
 "columns": [
  "affected_rows"
 ],
 "rows": [
  [
   "1"
  ]
 ]
}
==================================

#再查詢一次

root@raspberrypi:~# curl -H "Authorization: Bearer ya29.tAH13KudNYGAmG9rtdXLJjO_QNPjbJcvEkJEeG_C3MxqWh5T9sFbbrguWh77llT6JbVL" -d "sql=SELECT * FROM 1fV5mXuKgG5cCck1cAVQX0G7HVTjfdm1SqeYSdmXU WHERE number=43" "https://www.googleapis.com/fusiontables/v2/query"
{
 "kind": "fusiontables#sqlresponse",
 "columns": [
  "Date",
  "Time",
  "number",
  "Location",
  "Address"
 ],
 "rows": [
  [
   "",
   "",
   "43",
   "",
   "ittraining"
  ]
 ]
}

#刪除 sql=Delete {table} WHERE ROWID='5002'

root@raspberrypi:~# curl -H "Authorization: Bearer ya29.tAHsfamfZ4CUSiMwXG0TztY9DYkcj3Zs988p9Wr_GrZz5vRbxkGHFoQiHxDMNsuEZf8-" -d "sql=Delete from 1fV5mXuKgG5cCck1cAVQX0G7HVTjfdm1SqeYSdmXU WHERE ROWID='5002'" "https://www.googleapis.com/fusiontables/v2/query"
{
 "kind": "fusiontables#sqlresponse",
 "columns": [
  "affected_rows"
 ],
 "rows": [
  [
   "1"
  ]
 ]
}

#確認該筆資料(number=43)已刪除

root@raspberrypi:~# curl -H "Authorization: Bearer ya29.tAHsfamfZ4CUSiMwXG0TztY9DYkcj3Zs988p9Wr_GrZz5vRbxkGHFoQiHxDMNsuEZf8-" -d "sql=SELECT ROWID FROM 1fV5mXuKgG5cCck1cAVQX0G7HVTjfdm1SqeYSdmXU WHERE number=43" "https://www.googleapis.com/fusiontables/v2/query"
{
 "kind": "fusiontables#sqlresponse",
 "columns": [
  "rowid"
 ]
}

Access VBA: 查詢方式有3種方式



進入Access 查詢[設計模式]下, 可以設定查詢的連接屬性, 如下。 預設是1:  查詢結果是當兩個個資料表都必須要有記錄才算. 另外2種就是看要以那一個表格為主去查詢另外一個表的記錄, 也就是通常有些記錄,只存在於其中一個表格而已。





Access VBA表單傳遞資料的方式


 Method 1: 從表單找到控制項,取值
  Forms("資料表單").Controls("StudentNo").Value 

 Method 2: 使用Form.OpenArg

  開智表單並傳傳參數
  DoCmd.OpenForm stDocName, , , stLinkCriteria, , , StudentNo

  被開智的表單,利用Form.OpenArgs 取得傳入的參數
   lbl_studentNo.Caption = Me.OpenArgs    ' set studentNo to correct data