既然我們使用LoRa來傳輸和接收資料,就必須記住該技術的設計用途:小容量資料的遠距離傳輸。這意味著我們要儘量精簡我們的指令,因為每一個位元組都會被計數。我們可以只發送一些字串,例如“馬達左側50”來將左側的馬達設定為50%的轉速,但是這其實非常浪費空間。這個字串佔用了14個位元組的資料容量,但是僅僅傳輸了三個區塊:該指令的子系統目標(“馬達”)中哪些馬達會被影響(“左側”)以及它們將會被設定的轉速所占最大轉速的百分比(“50”)。可以肯定地說,我們可以做得更好。這種類型指令的優點是非常易於閱讀,但其實,再重申一下,只要系統運行正常,在指令執行這一塊是不需要人為干預的。
所以,我決定不使用那些長的字串,而是研發一種簡單的指令系統,在每個指令包中使用的指令最長僅為4個位元組。指令包中的第一個位元組叫做首位址,將會被進一步分為兩半:高四位元包含來自控制系統的指令分類,低四位元用來作為機器人的響應。0表示成功,非零值是具有已知含義的錯誤代碼。
第1個位元組 | 指令描述 |
0x00 | 停止所有馬達 |
0x10 | 設定所有馬達的轉速和轉向 |
0x20 | 設定所有左側馬達的轉速和轉向 |
0x30 | 設定所有右側馬達的轉速和轉向 |
0x40 | 設定相機傾斜角度 |
0x50 | 設定相機平移距離 |
0x60 | 使用相機拍攝圖片 |
0x70 | 開始JPEG 傳輸 |
0x80 | 強制啟動新感測器測量 |
0x90 | 獲取最新感測器資料 |
0xA0 | 重新發送最終指令包 |
0xB0 | 設定LoRa調製穩壓器配置 |
0xC0 | 獲取當前所有存在的錯誤資訊 |
0xD0 | 未被使用 |
0xE0 | 未被使用 |
0xF0 | 未被使用 |
指令包的下一部分內容是有效負載—這也是在有關頭檔中指令的所有附加資訊存儲的地方。這部分比首位址更簡單一些,因為它的內容取決於指令類型:有些指令不需要任何附加資訊,比如說0x00(停止兩側馬達)。所以這些指令的有效負載為空。然而,一些命令要求額外資料不能超過三個位元組,比如指令0xD0(LoRa配置),要求每個主要設定,如頻寬、擴頻因數、以及編碼率(有關LoRa細節請參考 LoRaLib課程 或者 GitHub wiki)只占一個位元組。下表描述了所有指令包的結構。
第1個位元組 | 第2個位元組 | 第3個位元組 | 第4個位元組 |
0x00 | – | – | – |
0x10 | 左側PWM控制轉速 | 右側PWM控制轉速 | 轉向 |
0x20 | 左側PWM控制轉速 | 轉向 | – |
0x30 | 右側PWM控制轉速 | 轉向 | – |
0x40 | 傾斜角度 | – | – |
0x50 | 平移距離 | – | – |
0x60 | – | – | – |
0x70 | 圖片編碼 | – | – |
0x80 | 感測器 ID(s) | – | – |
0x90 | 感測器 ID(s) | – | – |
0xA0 | – | – | – |
0xB0 | 頻寬 | 擴頻因數 | 編碼率 |
0xC0 | – | – | – |
0xD0 | – | – | – |
0xE0 | – | – | – |
0xF0 | – | – | – |
當然,這些只是由控制系統發送然後機器人來接收的指令包。我們也希望機器人能夠作出響應。對於某些指令,這種響應可能非常簡單,僅僅是為了讓控制系統知道指令是否被成功執行。如果您仔細看上文中的表格,會注意到所有的指令都只用了第一個指令位元組的高四位元,這樣剩下的低四位可以用於響應。這種方法有兩個顯著的優勢:首先,始終可以追蹤到每個響應所屬的指令,因為指令位元總是作為響應值的一部分返回;其次,對於每一個指令,都有足夠的空間用於16種不同的響應。
讓我們用一個例子來進行說明。假設我們想用相機拍攝一張圖片,那麼我們會發送指令0x60。那麼機器人有16種不同的方式來響應。如果響應是0x60,這意味指令已經被成功執行。所有其他類型的響應,0x61到0x6F,都意味著出現了錯誤。這樣我們不僅知道指令執行失敗了,還會知道為什麼失敗並且進行修正。當然,這些響應不一定總是只有一個位元組的長度,有些指令會要求機器發回一些附加資訊,比如感測器資料。下表顯示的是所有針對不同指令的響應包。
指令 | 第1個位元組 | 第2個位元組 | 第3個位元組 | 第4~240位元組 |
0x00 | 0x00 | – | – | – |
0x10 | 0x10 | – | – | – |
0x20 | 0x20 | – | – | – |
0x30 | 0x30 | – | – | – |
0x40 | 0x40 | – | – | – |
0x50 | 0x50 | – | – | – |
0x60 | 0x6_ | – | – | – |
0x70 | 0x7_ | 圖像資料 | 圖像資料 | 圖像資料 |
0x80 | 0x8_ | 發生錯誤的感測器 | 感測器資料 | 感測器資料 |
0x90 | 0x9_ | 發生錯誤的感測器 | 感測器資料 | 感測器資料 |
0xA0 | 0xA0 | – | – | – |
0xB0 | 0xB0 | – | – | – |
0xC0 | 0xC_ | 錯誤標誌 | 錯誤標誌 | – |
0xD0 | 0xD0 | – | – | – |
0xE0 | 0xE0 | – | – | – |
0xF0 | 0xF0 | – | – | – |
您可以注意到某些指令(主要是關於馬達和伺服器的那些指令)只能返回0(成功)。這是因為當前版本中的板載電子裝置無法判斷這些指令是否被成功執行。為了達到驗證的目的,我們需要增加新的裝置來檢查馬達是否在運轉,或者伺服器是否在正確的位置。這應該不太困難,讓我們暫時忽略它,來看看那些可能會有報錯響應的指令:
在解釋完所有內容後,讓我們回到最開始的內容。我們想要把左側馬達的轉速設定為50%。我們已經知道了指令首位址為0x20。所以根據表格內容,我們可以填寫剩下的位元組,從控制器發送的指令包如下所示:
0x20 0x7F 0x00 0x00
第一個位元組顯然是首位址—設定左側馬達轉速。下一個位元組是轉速。馬達驅動器透過PWM調製來改變馬達轉速,所以數位0xF7(十進位為127)對應50%占空比或50%轉速。第三個位元組是轉向—0x00表示正向,0x01表示逆向。最後一個位元組僅用來占位元—添加該位元組來使指令包始終為四位元組長。一旦指令被接收並被機器人成功執行,會返回以下指令包:
0x20
對於機器人上所運行的Arduino程式,其指令系統的實現都可以在我的GitHub中查看。通常我在這上面發佈實際代碼。不幸的是,代碼檔太大無法放在本文中。所以如果您對於代碼實現的細節有興趣的話,請查看實際代碼中的注釋。現在,我們來繼續看一些有趣的東西吧,那就是實現遠端操控的應用程式!