網頁最後修改時間:2019/05/10
套件所包含的學習部分可由下面這張圖看出。
其中,本篇網頁主要說明的是圖中 雲台手機控制 (右下,綠色線和框)的部分:
搭配自製的手機 APP 與 ESP8255 通訊,傳送與接收兩軸伺服馬達的設定,並且以手機螢幕中的搖桿圖示來控制雲台的兩軸動作。詳細的操作和說明,請看網頁中的影片介紹。
*********************************************************************************
學習套件可至下面網址購買:
*********************************************************************************
【學習套件的基本電路】
不管使用哪一個程式,學習套件只用到下面這個佈線電路圖;即便沒有用到 MPU6050 的程式,測試的時候也不需要拆,直接就能使用
不過這裡有一個很重要!必須要注意的地方,要特別提出來:電源!電源!電源!
實際測試用的麵包板電源模組,它的外接電源輸入是 5V/2A 的手機充電器。
如果電源(有試過 5V/1A)功率不夠的話,接好電路通電的結果,就會看到伺服馬達不受使喚;此時,就是告訴你(妳),請更換一顆功率較大的就沒問題了!
學習套件參考接線圖 |
學習套件參考接線實際佈線圖例 |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 燒錄接線:
ESP8285 燒錄的接線與 ESP8266 沒有什麼不同,只要將 <GP0> 接 <GND> 然後再將模組通電就會自動進入到燒錄模式。
下面提供兩種燒錄接線圖。第二種方式是比較推薦的方法,不需重覆插拔線,馬上燒錄、馬上測試!
** USB 轉 TTL 模組 / 線:
USB 轉 UART 模組燒錄接線方式 |
不要去質疑下面的電路為什麼 <TXD> 接 <TX0>,就是要這樣接!
ESP-01/01S 通訊/燒錄底板燒錄接線方式 |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 韌體燒錄選項:
Arduino IDE 和 Flash Download Tool 韌體燒錄的選項,如下圖所示。
紅色框框的部分是不能做更改的部分,這是屬於 ESP8285 專用的設定。沒出現在 Arduino IDE 的選項中,表示這是內定的預設值,不應該被修改。
Upload Speed 燒錄速度設定要看使用的 UART 模組而定,115200 是大多都適用的速度選項,若使用的是入門套件中的燒錄底板,則速度可拉至 921600(或更高?)。
Flash Size 這基本的大小(1M)是不能變的,但是 SPIFFS 的大小卻是可以。盡量不要選 no SPIFFS,因為有些函式庫會用到此部分。對於這個選項,我都是選擇 1M(512k SPIFFS) 比較多。
其他沒說到的選項就用預設值就可以。如果要改,請先弄清楚該選項再去做,否則只會讓自己徒增麻煩而已!
ESP8285, ESP-01M 燒錄選項設定 |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 完全清除韌體與紀錄 :
ESP8285(或某些函式庫)在執行期間會用到 SPIFFS 來儲存資料,因此韌體上傳時若是沒有將其清除的話,它們就會繼續保留在 SPIFFS 中讓程式取用。
不清楚什麼是 SPIFFS 的話,這裡有篇說明 ESP8266 Arduino IDE 開發問與答 Q&A ( 2 )。
雖然大部分的時候,沒清除 SPIFFS 再進行燒錄都讓人覺得方便(當然有時候在燒錄的同時,或是燒錄前將其清除是有其必要性的,例如無線網路連線資料),端看應用與自己的選擇。
Erase Flash 提供三個清除選擇在 Arduino IDE 的選單:Only Sketch、Sketch + WiFi Settings 和 All Flash Contents,除了第一個選項只清除韌體之外,其他兩個選項都可以選,第三個則是清除 SPIFFS 最徹底的一個。
另外一種清除的方法是使用 esptool.py 直接下指令(系統內要有 python 2.7 和 esptool)
$> pip install esptool
$> esptool.py -p com8 -b 74880 erase_flash
其中,com8 是 USB 轉 UART 模組的 COM Port 號碼;74880 是通訊速度; erase_flase 表示清除 flash。
下面的示意圖,除了說明處理的流程,也將手機端與 ESP8285 之間的通訊方式與傳輸的資料格式都列在上面,只要與程式相對照,不難了解其中的流程與原理。
程式架構示意圖,點擊看超大圖 |
將手機設定到與 ESP8285 同一個網路,打開 APP 進入 WiFi 連線設定 頁面,將該 IP 地址輸入到欄位中,即可開始操作其他畫面中的東西。
IP 地址輸入後就會自動被記錄,下一次重新啟動 APP 就不需要再重新輸入!
程式架構示意圖裡面沒有特別畫出無線網路設定的部分,這部分請看影片中的操作說明。
APP 與 ESP8285 之間的通訊採非連續性的通訊,也就是說:只有需要的時候才會與 ESP8285 通訊,其餘時候都是斷開的。
雲台初始化、雲台資料上傳和下載,都是使用 HTTP POST 將所需要的資料封裝進 JSON 字串裡發送和被接收,之後 APP 和 ESP8285 解析 JSON 字串後,再取出資料進行相對應的動作。
- action:"initial"
進行雲台初始化。 - action:"upload"
將雲台設定值寫入到 EEPROM 中,雲台初始化就會使用這個值,否則就採用預設值。 - action:"download"
EEPROM 儲存的雲台資料若有效則使用這些數據做為回傳給 APP,否則採用預設值回傳。
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
第一次啟動 APP有兩個動作一定要先做(先進入到 雲台組態設定 頁面中,設定好雲台各軸的各項參數值,再繼續下面的動作(基本上使用 APP 預設值上傳就可以,不用特別修改!)):
第一次啟動 APP有兩個動作一定要先做(先進入到 雲台組態設定 頁面中,設定好雲台各軸的各項參數值,再繼續下面的動作(基本上使用 APP 預設值上傳就可以,不用特別修改!)):
- 按下 "上傳設定值" 按鈕,上傳資料到 ESP8285 並儲存至 EEPROM;
沒有執行這動作,ESP8285 就不知道有 EEPROM 的資料可以用,所執行的動作都會採程式預設值;但只要執行過一次之後,ESP8285 就會自己去檢查 EEPROM 裡面的資料,程式執行就會按照設定值動作。 - 按下 "儲存設定值" 按鈕,儲存設定值到手機;
儲存之後,每次按下 "恢復初始值" 按鈕,就會以此儲存的設定值恢復頁面欄位值。
進入 雲台操作 頁面後,APP 會自動建立與 ESP8285 的 WebSocket 連接,其連線的狀態會顯示在頁面的上方;只有連線成功,搖桿才會出現。
一但離開該頁面,就會馬上關閉 WebSocket 連線。
所以只有在需要連線的時候才建立連線,任何的傳送都帶有回覆,沒有回覆就代表連線不存在,需要檢查雙方的連線或是 IP 有沒設錯。
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* Android APP GUI:
APP 1.84MB 左右,安裝之後打開可看到有四個頁面,如下所示。
Android 手機 APP 操作頁面,點擊看原圖 |
- WiFi 連線設定
ESP8285 在網路中的 IP 地址。 - 雲台組態設定
雲台各軸的脈波寬度範圍以及初始化的位置設定。設定錯誤的脈波寬度範圍,會導致伺服馬達動作不正確,詳情可看此篇網頁的說明。 - 雲台操作
左邊搖桿控制水平(PAN)旋轉;右邊搖桿控制上下(TILT)旋轉,搖桿往上頭部往下,搖桿往下頭部往上。 - 雲台初始化
根據 ESP8285 EEPROM 裡的設定或是預設值,進行雲台初始化的動作。
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 手動測試:
* 手動測試:
那麼怎麼在沒 APP 的情況之下做測試呢 ?
HTTP POST 可使用 Postman;WebSocket 則上 chrome 線上應用程式商店安裝 Simple WebSocket Client。
** HTTP POST 通訊測試:
雲台初始化的測試,切換到頁面 Body 選擇 x-www0form-urlencoded、輸入 KEY 為 action 和設定 VALUE 為 initial,按下 "Send" 後觀察雲台的動作;此時應該要回到設定的初始化位置。
雲台初始化格式 |
從 ESP8285 下載雲台資料 |
- panmin, panmax, panpos
PAN servo 最小與最大的脈衝寬度值(µs),以及初始化的位置(degree)。 - tiltmin, tiltmax, tiltpos
TILT servo 最小與最大的脈衝寬度值(µs),以及初始化的位置(degree)
PAN-SERVO, INITIAL DATA若是 APP,就會出現提示的視窗,表示資料已成功上傳;這些都可以在影片中看到。
MIN: 700 MAX: 2400 POS: 90 CKSUM: 0xd6
TILT-SERVO, INITIAL DATA
MIN: 700 MAX: 2400 POS: 90 CKSUM: 0xd6
Got platform data from APP
從遠端上傳雲台資料到 ESP8285 |
Web Socket 的通訊主要是,要傳送 APP 左右兩支搖桿的移動位置和方向。ESP8285 接收到之後,進行簡單的運算,就能給出兩軸需要的轉動角度。
Server Location 輸入 ws://192.168.11.12:81,表示 ESP8285 的 IP 是 192.168.11.12,Web Socket port number 是 81(這個值設定在 ESP8285 的程式裡)。
Request 輸入的是各軸的角度以及方位,這跟 APP 搖桿的移動方向以及位移值有關,ESP8285 的程式是根據這個來撰寫的。所以,不需要糾結為什麼是這樣寫,完全可以創造自己想要的呈現方式。
格式是這樣定義的(有些部分是保留以後擴充用的,在解析時不會用到):
p(l|r)(a|s)(degree)
t(u|d)(a|s)(degree)其中,
- p, t:代表控制的是雲台水平或是上下轉動的伺服馬達;
- l, r:代表搖桿的水平移動是左轉還是右轉,現在是保留,沒用到;
- u, d:代表搖桿的上下移動是往上還是往下,現在是保留,沒用到;
- a, s:代表是角度還是速度模式,s 現在是保留,沒用到;
- degree:代表角度值。原本應該是 0 .. 180 度,但是因為 ESP8285 轉換失敗所回傳的是 0,所以從 APP 發送出去的值都是取值之後加一再送出的。ESP8285 接收到之後會先做轉換,確認轉換成功之後就會減一再輸出給伺服馬達,所以下面會看到發送值與回傳值會相差一,就是這個原因。
成功發送並被接收之後,ESP8285 會以 JSON 格式的字串回傳給 APP 現在雲台兩軸的轉動角度。
遠端 Web Socket 與 ESP8285 的雲台動作測試 |
下面說明程式碼的部分。要知道雲端資料夾裡的程式碼幾乎沒有包含註解,所以要了解程式碼要看這裡的說明。
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
有購買商品的使用者,網頁中所需相關資料已放置於雲端硬碟,請自行與網頁中的內容相互對照。
其他的使用者,程式碼可以給,但是不提供標頭檔定義以及所使用的函式庫名稱;從網頁內文以及影片中可以找到提示。
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
再把程式結構圖拉過來看一下。
程式架構示意圖,點擊看超大圖 |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 全局變數:
- line 3...4:softAP 的名稱與密碼;
- line 6:建立 Web Server,port number 80;
- line 7:建立 Web Socket,port number 81;
- line 10...11:ESP8285 可選擇 <IO4>、<IO12>、<IO14> 和 <IO15> 這四支接腳來控制伺服馬達;此處選用的是水平 <IO12>、上下 <IO14>;
- line 12:列舉一些常數,PAN = 0, TILT = 1, SERVOITEMS = 2,方便陣列使用常數而不是數字,容易識別;
- line 13:建立一個可控制兩個伺服馬達的陣列,大小為 2;
- line 15...20:儲存伺服馬達脈衝寬度範圍和初始化位置的結構,EEPROM 儲存的就是這個部分;checksum 是用來驗證該筆數據是否有效的根據。
- line 22...25:用於儲存伺服馬達初始化數據以及位置的結構,並同時建立和初始化一個儲存雲台兩軸預設值的結構陣列。
ESPWebServoControl.ino, 1 of 6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //* 全局變數 * // ESP8266 AP 名稱與連線密碼 const char* ESP_AP_NAME = "espPanTilt"; const char* ESP_AP_PASS = "123456789"; // ESP8266 Web Server ESP8266WebServer WEBSERVER(80); // ESP8266 Web Socket Server WebSocketsServer WEBSOCKET(81); // Two-Axis Servo Gimbal const int PIN_PAN_SERVO = 12; // Horizontal rotation const int PIN_TILT_SERVO = 14; // Vertical rotation enum {PAN, TILT, SERVOITEMS}; Servo platform[SERVOITEMS]; struct init_t { uint16_t min_pulse_width; // pulse width in microseconds uint16_t max_pulse_width; uint8_t pos; // initial position, 0...180 degree uint8_t checksum; }; struct servo_t { init_t init; uint8_t currpos; } servo[SERVOITEMS] = { { {700, 2400, 90, 0xD6}, 90 }, { {700, 2400, 90, 0xD6}, 90 } }; |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 雲台初始化:
雲台兩軸的初始化函式。從這個函式也可以看出,為什麼全局變數那麼宣告?以及使用的方式。
這裡要特別說明的是 line 9...10。attach 時必須同時指定脈衝寬度的大小範圍,否則伺服馬達的轉動只會是預設的 90 度。再者,脈衝寬度的範圍以現在的設定就是 180 度的轉動量,雖然說可以改,但是錯誤的修改會導致伺服馬達損壞或動作不正常,所以除非知道自己在做什麼,否則不建議去做變更!
ESPWebServoControl.ino, 2 of 6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /**-------------------------* * 伺 服 馬 達 輔 助 函 式 * * ------------------------*/ void platformInit() { // detach servo platform[PAN].detach(); platform[TILT].detach(); // attach servo platform[PAN].attach( PIN_PAN_SERVO, servo[PAN].init.min_pulse_width, servo[PAN].init.max_pulse_width ); platform[TILT].attach( PIN_TILT_SERVO, servo[TILT].init.min_pulse_width, servo[TILT].init.max_pulse_width ); // 回預設的初始點 platform[PAN].write( servo[PAN].init.pos ); platform[TILT].write( servo[TILT].init.pos ); // 紀錄現在伺服馬達的位置 servo[PAN].currpos = servo[PAN].init.pos; servo[TILT].currpos = servo[TILT].init.pos; } |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* EEPROM 輔助函式:
下面幾個函式,是用來讀取與寫入資料到 EEPROM、計算校驗和以及輸出雲台資料的,其中:
- line 4...8:校驗和計算。針對 init_t 結構裡面前三項進行 8 位元計算,然後全部加總在一起,返回最後計算結果。實例可以看全局變數小節 line 25 的初始化宣告,它的校驗和就是經過計算所得出的結果。
- line 10...19:取回兩個伺服馬達的初始值設定,並且驗證校驗和是否正確;只有正確的校驗和才能確保讀取的資料是正確且有效的!
- line 21...34:EEPROM 的寫入分兩個步驟,首先將要寫入的資料 put() 到緩衝記憶體(line 22...27),然後再 commit() 將記憶體裡的資料寫入到 EEPROM(line 29);前提是欲寫入的資料的校驗和是正確的(line 15...16),否則就會馬上中斷寫入的動作。
- line 36...50:輸出雲台初始值設定與現在的轉動角度。
這幾個函式實際的使用方法請看 setup() 裡的程式碼。
ESPWebServoControl.ino, 3 of 6
1 2 3 4 5 6 7 8 9 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 | /**---------------------* * EEPROM 輔 助 函 式 * * --------------------*/ uint8_t calculateChecksum( uint8_t *cb , uint8_t size ) { uint8_t sum = 0x55; while( --size ) sum += *cb++; return sum; } bool readGlobalSet() { // Get EEPROM data into our local copy size_t sz = sizeof( init_t ); for( int i = 0; i < SERVOITEMS; i++ ) { EEPROM.get( 0 + ( i * sz ), servo[i].init ); if( calculateChecksum( (uint8_t*)&servo[i].init, sz ) != servo[i].init.checksum ) return false; } return true; } bool writeGlobalSet() { size_t sz = sizeof( init_t ); // Set the EEPROM data ready for writing for( int i = 0; i < SERVOITEMS; i++ ) { servo[i].init.checksum = calculateChecksum( (uint8_t*)&servo[i].init, sz ); EEPROM.put( 0 + ( i * sz ), servo[i].init ); } // Write the data to EEPROM bool ok = EEPROM.commit(); if( !ok ) return false; Serial.println( (ok) ? "Commit OK" : "Commit failed" ); return true; } void printGlobalSet() { Serial.println(); Serial.println( F("PAN-SERVO, INITIAL DATA") ); Serial.printf( " MIN: %d", servo[PAN].init.min_pulse_width); Serial.printf( " MAX: %d", servo[PAN].init.max_pulse_width); Serial.printf( " POS: %d", servo[PAN].init.pos); Serial.printf(" CKSUM: 0x%x", servo[PAN].init.checksum); Serial.println(); Serial.println( F("TILT-SERVO, INITIAL DATA") ); Serial.printf( " MIN: %d", servo[TILT].init.min_pulse_width); Serial.printf( " MAX: %d", servo[TILT].init.max_pulse_width); Serial.printf( " POS: %d", servo[TILT].init.pos); Serial.printf(" CKSUM: 0x%x", servo[TILT].init.checksum); Serial.println(); } |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* setup():
setup() 裡面包含的不只是初始化:整個程式的處理中心也在這裡,Web Server 客戶端請求、Web Socket 的事件處理分配也在這裡,所以程式碼有點多。為了方便說明,將此處的程式碼分成四段,分段說明。
下面是關於無線網路組態的設定說明:
- line 4:初始化 UART,通訊速率為 115200 bps;
- line 9...17:無線網路的連線或配置;
- line 10:設定最小的訊號品質,預設 < 8% 的訊號品質不輸出;
- line 11:連線逾時的時間設定(秒);
- line 12:自動連線。若失敗首先啟用 SoftAP 模式,SoftAP 模式下的名稱為 ESP_AP_NAME,密碼為 ESP_AP_PASS 變數所指定的字串,若再失敗則重啟 ESP8266。
- line 19:無線網路設置如果成功,輸出 WiFi Connected.,這時就可以切換網路連線了。
ESPWebServoControl.ino, setup() 4-1 of 6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | void setup() { delay(3000); Serial.begin( 115200 ); Serial.println(); Serial.println(); /** WiFiManager */ WiFiManager wifiManager; wifiManager.setMinimumSignalQuality(); wifiManager.setTimeout(120); if ( !wifiManager.autoConnect( ESP_AP_NAME, ESP_AP_PASS ) ) { Serial.println("failed to connect and hit timeout"); delay(3000); ESP.reset(); delay(5000); } // 到這裡已經完成無線網路的連線 Serial.print( "\nWiFi Connected.\n" ); |
下面是關於 Web Server 的客戶端請求處理,處理關於 http://<IP>/platform 的客戶端請求的部分(line 22...65)。
連線到此網址後,Web Server 會去接收來自客戶端的資料,依據 action 後面所帶的值,再去區分處理的動作是雲台初始化、雲台資料上傳或雲台資料下載。
而從手機 APP 這端,實際所丟的資料是 JSON 格式字串。但經過處理之後,在 Web Server 這端,可以很方便的使用 arg( String key ) 函式輕鬆取出 key 所對應的值,不需要再做 JSON 字串解析的動作。
上面各部分的程式碼說明如下:
- line 22:設定要處理的客戶端請求 URI 名稱;
- line 24:取出 action 的值;
- line 26...29:雲台初始化;
- line 26:重新設定伺服馬達脈衝寬度的範圍,並且轉動到指定的角度。
- line 28:輸出雲台各軸的資料。
- line 29:回覆一個沒資料的狀態值,表示處理成功,手機端也會出現相對應的提示視窗,否則過一段時間後會出現錯誤的提示。
- line 31...47:回傳雲台的資料;
- line 32...44:建立一個要回傳給客戶端的 JSON 字串
{"panpos":"...","tiltpos":"...","panmin":"...","panmax":"...","tiltmin":"...","tiltmax":"..."} - line 46:以 text/plain 的 HTTP 內文類型(Content-Type)傳送 JSON 字串,HTTP 狀態碼(Status Code) 200。
- line 49...61:儲存手機送過來的雲台資料;
- line 50...57:取出手機傳送來的雲台資料值(同時轉換為整數),並暫時存放在 servo 結構陣列中,同時要將數據進行校驗和的計算並一併放入到結構陣列中。
- line 58:將 servo 結構陣列中的資料寫入到 EEPROM。
- line 60:處理完畢後回覆狀態碼給客戶端。
ESPWebServoControl.ino, setup() 4-2 of 6
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 | /** Web Server Events Handle */ WEBSERVER.on( "/platform", HTTP_POST, []() { if( WEBSERVER.args() > 0 ) { String action = WEBSERVER.arg( "action" ); if( action == "initial" ) { // 雲台初始化 platformInit(); Serial.println( F("Platform initialization") ); printGlobalSet(); WEBSERVER.send( 200, "text/plain", "" ); } else if ( action == "download" ) { // 回傳雲台的資料 String json = "{\"panpos\":\""; //** pan initial position json += String( servo[PAN].init.pos ) + "\",\"tiltpos\":\""; //** tilt initial position json += String( servo[TILT].init.pos ) + "\",\"panmin\":\""; //** pan min pulse width json += String( servo[PAN].init.min_pulse_width ) + "\",\"panmax\":\""; //** pan max pulse width json += String( servo[PAN].init.max_pulse_width ) + "\",\"tiltmin\":\""; //** tile min pulse width json += String( servo[TILT].init.min_pulse_width ) + "\",\"tiltmax\":\""; ///** tile max pulse width json += String( servo[TILT].init.max_pulse_width ) + "\"}"; Serial.println(json); WEBSERVER.send( 200, "text/plain", json ); Serial.println( F("Send platform data to APP") ); } else if( action == "upload" ) { // 儲存手機送過來的雲台資料 servo[PAN].init.min_pulse_width = WEBSERVER.arg( "panmin" ).toInt(); servo[PAN].init.max_pulse_width = WEBSERVER.arg( "panmax" ).toInt(); servo[PAN].init.pos = WEBSERVER.arg( "panpos" ).toInt(); servo[PAN].init.checksum = calculateChecksum( (uint8_t*)&servo[PAN].init, sizeof( init_t ) ); servo[TILT].init.min_pulse_width = WEBSERVER.arg( "tiltmin" ).toInt(); servo[TILT].init.max_pulse_width = WEBSERVER.arg( "tiltmax" ).toInt(); servo[TILT].init.pos = WEBSERVER.arg( "tiltpos" ).toInt(); servo[TILT].init.checksum = calculateChecksum( (uint8_t*)&servo[TILT].init, sizeof( init_t ) ); writeGlobalSet(); printGlobalSet(); WEBSERVER.send( 200, "text/plain", "" ); Serial.println( F("Got platform data from APP") ); } else {} } }); // /platform |
- line 67...69:是 Web Server 的客戶端請求處理,處理任何不符合前述 http://<IP>/platform 的 HTTP POST 要求的部分(line 67...69)。
- line 71:啟動 Web Server;
- line 75:啟動 Web Socket;
- line 76:設定 Web Socket 的事件處理函式名稱;
ESPWebServoControl.ino, setup() 4-3 of 6
67 68 69 70 71 72 73 74 75 76 77 78 | WEBSERVER.onNotFound([](){ WEBSERVER.send( 404, "test/plain", "File not found." ); }); WEBSERVER.begin(); Serial.println( "HTTP server started" ); /** Web Socket Events Handle */ WEBSOCKET.begin(); WEBSOCKET.onEvent( wsEventsHandle ); Serial.println( "Web socket server started " ); |
下面是關於開機之後的 ESP8285 EEPROM 裡的雲台資料讀取,尤其是燒錄之後第一次的讀取,必須確保 EEPROM 在沒有任何資料的情況下,先行將全局變數的預設值確實寫入,這樣之後每一次開機讀取和寫入才能確保沒有問題;否則永遠只會使用預設值,所設定的值完全都沒有用。
- line 80:初始化 EEPROM,並且設定要使用的大小,也就是 2 個 init_t 結構的大小;
- line 82...103:看程式碼裡面的註解;
- line 107:進行開機之後的雲台初始化;
ESPWebServoControl.ino, setup() 4-4 of 6
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 | /** ESP8285 EEPROM */ EEPROM.begin( 2 * sizeof( init_t ) ); // 檢查 EEPROM 是否已有之前儲存的資料,如果是,讀出來看看是否有效! if( EEPROM.percentUsed() >= 0 ) { Serial.println( F("EEPROM has data from a previous run.") ); Serial.print( EEPROM.percentUsed() ); Serial.println( F("% of ESP flash space currently used" ) ); // 讀取並確認 EEPROM 裡面的資料是否有效?如果有效,用它做為預設值。 if( readGlobalSet() ) { Serial.println( "Use global set from EEPROM" ); printGlobalSet(); } else { //** 讀取失敗,寫入預設的結構與數值 Serial.println( "Failed to read global set from EEPROM, recover it!" ); if( writeGlobalSet() ) { Serial.println( "Use default global set" ); printGlobalSet(); } else { Serial.println( "Global set read/write failure, check your code!" ); } } } else { Serial.println( F("EEPROM size changed - EEPROM data zeroed - commit() to make permanent" ) ); } /** Servo */ // 雲台伺服馬達初始化 platformInit(); } |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* Web Socket 事件處理函式:
Web Socket 通訊中有很多事件會產生,但並不是所有的事件都需要去處理才行,其實只要處理感興趣和必須的事件就可以,如 wsEventHandle() 事件處理函式只處理了 Web Socket 的已斷線(line 3...5)、已連線(line 6...9)和接收到訊息(line 10...13)這三個事件,而最後一個事件(WStype_TEXT) 就是接收到手機搖桿資料的地方,要對其分配相對應的處理程式碼;這裡指定了另外一個函式 wsservohandle() 來處理。
搖桿的資料格式在上面 ** Web Socket 通訊測試: 小節已做過說明,所以可知 wsservohandle() 函式裡的字串 cmd 的第三個字元之後的數字,代表的就是角度值,對其轉換(line 18)為整數,然後將其值減一,並驗證其轉換是否有效,無效則馬上退出接下來的處理(line 19)。
- cmd[0]:控制的是 PAN 或 TILT 伺服馬達;
- cmd[1]:上下左右轉動的方向,這位元保留不使用;
- cmd[2]:角度或速度模式,速度模式保留不使用,故只能是 'a';
接著根據第一個字元(line 22, line 26)決定控制的伺服馬達是 PAN 還是 TILT,然後寫入角度值(line23, line 27)並記錄下當前角度值(line 24, line 28)。
最後,要把執行的結果以 JSON 字串格式發送回 APP(line 32...34),這樣 APP 才能知道現在的雲台轉動角度值,以及是否已被執行。
返回的值會顯示在 APP 的 雲台操作 頁面的 R 欄位後面,也能容易的用來判別搖桿的動作是否已被 ESP8285 執行過了的依據。
ESPWebServoControl.ino, 5 of 6
1 2 3 4 5 6 7 8 9 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 | void wsEventsHandle( uint8_t num, WStype_t type, uint8_t * payload, size_t length) { switch(type) { case WStype_DISCONNECTED: Serial.printf("[%u] Disconnected!\n", num); break; case WStype_CONNECTED: { IPAddress ip = WEBSOCKET.remoteIP(num); Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); } break; case WStype_TEXT: Serial.printf("[%u] get Text: %s\n", num, payload); wsservohandle( num, String((char*)payload) ); break; } } void wsservohandle( uint8_t client_id, const String& cmd ) { int val = (cmd.substring(3)).toInt(); if( val-- <= 0 ) return; if( cmd[2] == 'a' ) { if( cmd[0] == 'p' ) { platform[PAN].write( val ); servo[PAN].currpos = val; } else if( cmd[0] == 't' ) { platform[TILT].write( val ); servo[TILT].currpos = val; } } String json = "{\"panangle\":\"" + String(servo[PAN].currpos) + "\",\"tiltangle\":\"" + String(servo[TILT].currpos) + "\"}"; WEBSOCKET.sendTXT( client_id, json ); } |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* loop():
Web Server 和 Web Socket 在 loop() 函式裡必須要加的程式碼,沒加這兩行就不會正常執行,就是把它們加上去了就好!
ESPWebServoControl.ino, 6 of 6
void loop() { WEBSERVER.handleClient(); WEBSOCKET.loop(); }
【展示影片】
程式碼的說明不一定能夠清楚的呈現出實際動作的效果,但經由下面的展示影片,完全可以呼應出上面網頁的說明,它完整展示了雲台手機控制操作的整個流程。
【結論】
對於想要用手機來控制東西的想法已經很久,但是按鈕按一按開個燈、開個電風扇等...類的 APP 網路上很多,有些也不需要特別去寫 APP,用其他的 APP 也能達到同樣的效果,所以一直沒特別針對部落格的項目寫這部分的東西。
但是就是突然萌現了這個想法:是不是可以用手機來控制雲台的動作,單純的轉動動作?想想,用來練練手也不錯,就寫了這 APP!
雖然用的是 ESP8285 晶片模組,但是這個程式碼 ESP8266 晶片模組也可以用。
另外,即便沒有 APP,網頁也提供了其他的連線測試方法。若是真的想自己寫操作介面的話,可以用 HTML + JS 來做也能達到相同的效果。
另外,若是不想用韌體所提供的無線網路組態函式庫的話,可以把 setup() 裡面關於此部分的程式碼全部移除,然後用一般的連線方式連線到想連線的無線網路去,這樣也不失為另一種方法。
接著,繼續這個學習套件的系列,要再引入另一個模組:MPU6050(三軸加速度計與三軸陀螺儀模組),後面要用它實現雲台跟隨與自穩的運動控制。而要達到這個目的,首先要做的就是 MPU6050 的校正,也是下一篇部落格網頁的主題。
<< 部落格相關文章 >>
- ESP8285 兩軸伺服馬達雲台運動控制學習套件{4 of 4}雲台自穩與跟隨運動控制
- ESP8285 兩軸伺服馬達雲台運動控制學習套件{3 of 4}MPU6050 的 DMP 輸出測試
- ESP8285 兩軸伺服馬達雲台運動控制學習套件{2 of 4}MPU6050 的偏移值校正(offset calibration)
- 小型兩軸伺服馬達 (舵機) 雲台 (Pan-Tilt Kit) 動作展示
沒有留言:
張貼留言
留言屬名為"Unknown"或"不明"的用戶,大多這樣的留言都會直接被刪除掉,不會得到任何回覆!
發問問題,請描述清楚你(妳)的問題,別人回答前不會想去 "猜" 問題是什麼?
不知道怎麼發問,請看 [公告] 部落格提問須知 - 如何問問題 !