Try   HackMD

Internet echo server application on MT7687 with Linkit SDK

tags: Linkit SDK, MT7687, Toolchain, Internet echo server, arm, Cortex-M4, MCU

為了量測移動、轉動物體的相關數值,如果還要從待測物連接通訊線到外界設備,是件不切實際的事。因此使用無線通訊,將量測數值傳回就是不可或缺的技術。

在尋找合適的晶片/開發板過程中,有找到例如 ESP32/8266 等相關資源。但考量到它是中國晶片,便不選擇它。剛好之前運氣好,抽到一張 Linkit 7697 的開發板,搭載的晶片是臺灣在地公司聯發科 MediaTek 的 MT7697 控制器,是屬 Cortex-M4 MCU。寫個 Echo server 放到 Linkit 7697 上,來做網路通訊 PoC 好像不錯。

準備 Toolchain

工欲善其事,必先利其器!

Linkit SDK 環境

相較於其他晶片商,MediaTek 的商業模式是較封閉的。不像許多晶片商,可以在公開的網路找到免費詳細技術資訊。如果要詳細資訊/支援,就必須要有商業往來()。所以如果要在 Linkit 上開發一些應用,無法從零開始,必須使用 MediaTek 打包好的 Linkit SDK 環境,再往上發展。也可以從官網上看到,Linkit SDK 是採用 FreeRTOS 當作 kernel。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

可以從官網下載 https://docs.labs.mediatek.com/resource/mt7687-mt7697/en/downloads
SDK for Public Users -> SDK for GCC/IAR (因為我是在 Archlinux 上用 GCC)

又因為我的電腦作業系統是 Archlinux 64 bits

$ uname -a
Linux starnight 5.6.13-arch1-1 #1 SMP PREEMPT Thu, 14 May 2020 06:52:53 +0000 x86_64 GNU/Linux

但 linkit sdk v4.6.1 裡附的編譯器是 32 bits 程式,無法在我的電腦上直接執行。

$ file tools/gcc/gcc-arm-none-eabi/bin/arm-none-eabi-gcc
tools/gcc/gcc-arm-none-eabi/bin/arm-none-eabi-gcc: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.8, stripped

曾經有人說過:「這年頭有誰的電腦是 32 bits!?」無奈 所以我在社群上有反應這問題,希望 MediaTek 可以盡快更新 SDK。而目前暫時的解決方法,就是先額外裝支援 32 bits 的 libc library pacman -S lib32-glibc

Flash tool

Linkit SDK 有 MT76x7 FlashTool,用來把編譯好的程式是載入 Linkit 7697。但根據 LinkIt 7697 不用能使用官方的 MT76X7 FLASH TOOL的問題和想用單純指令/不用 GUI 的需求,所以我改用文章中提到的 mt76x7-uploader。mt76x7-uploader 的 README.md 有詳述其運作機制。尷尬的是,它是用 Python 2.7 寫出來的程式(截至2020/05/23,其 master branch 仍是 Python 2.7),但 Python 2.7 已在 2020/1/1 EOL。為了確保之後可以持續有工具可以用,只好自己想辦法改程式,把 mt76x7-uploader 轉到 Python 3。

在 trace code 之後,發現:

  • 主要的程式是 upload.py,預設用 Python 2.7 執行
  • 需要第三方 Python libraries:serial、xmodem 和 pyprind。MediaTek 已整理這些 libraries 放到 mt76x7-uploader 路徑下。

從上述的追蹤/分析結果,需要從 Python 2 porting 到 Python 3 的部份只有 upload.py 和說明文件(Windows 不在我的考量範圍)。所以我發了個 pull request (PR),且該 PR 已被 merge 到 mt76x7-uploader 的 python-3 branch

終於,準備好 toolchain 包含 Linkit SDK 和 flash tool,接下來可以做一些簡單的測試。

為方便說明,我分別將下載的 Linkit SDK 接壓縮放到家目錄下;同樣的,mt76x7-uploader 也放到家目錄下。

Serial Echo Application

在 Linkit SDK v4.6.1 裡,有個 uart_loopback_data_dma 的範例,其實就是個 serial echo application,適合當做 Hello World 起手式。

看程式 Trace

他的路徑在project/linkit7697_hdk/hal_examples/uart_loopback_data_dma,進到該資料夾,看一下有什麼。

$ cd ~/linkitsdk/project/linkit7697_hdk/hal_examples/uart_loopback_data_dma
$ ls *
readme.txt

EWARM:
flash.icf  startup_mt7687.s  uart_loopback_data_dma.ewd  uart_loopback_data_dma.ewp  uart_loopback_data_dma.eww

GCC:
feature.mk  Makefile  mt7687_flash.ld  mt7687_hdk.cmm  startup_mt7687.s  syscalls.c

inc:
flash_map.h  hal_feature_config.h

MDK-ARM:
flash.sct  startup_mt7687.s  uart_loopback_data_dma.uvoptx  uart_loopback_data_dma.uvprojx

src:
main.c  system_mt7687.c
  • Source files 在 src
  • Header files 在 inc
  • Makefile 在 GCC

編譯 Compile

於是進到 GCC 資料夾,編譯範例程式:

$ cd GCC
$ make
rm -f ...linkitsdk/project/linkit7697_hdk/hal_examples/uart_loopback_data_dma/GCC/build/*.log
Build... cos_api.o
Build... cos_api.o PASS
...
Build... verno.o
Build... verno.o PASS
Linking...
Done
   text	   data	    bss	    dec	    hex	filename
   9016	    772	   1420	  11208	   2bc8	...linkitsdk/project/linkit7697_hdk/hal_examples/uart_loopback_data_dma/GCC/build/uart_loopback_data_dma.elf
copy_firmware.sh....
BOARD=linkit7697_hdk
bin filename is uart_loopback_data_dma.bin
...linkitsdk/project/linkit7697_hdk/hal_examples/uart_loopback_data_dma/GCC/build/mt7697_bootloader.bin doesn't exist. copy default bootloader done.
cp ...linkitsdk/project/linkit7697_hdk/hal_examples/uart_loopback_data_dma/GCC/mt7687_hdk.cmm to ...linkitsdk/project/linkit7697_hdk/hal_examples/uart_loopback_data_dma/GCC/build/

編譯出來的 binary file 是 uart_loopback_data_dma.bin,在 build 資料夾下。

燒錄 Flash

Linkit 7697 接上電腦後,它在我的系統上會註冊一個 serial port /dev/ttyUSB0。將 binary file 載入 Linkit 7697:

$ ls -l /dev/ttyUSB0 
crw-rw---- 1 root uucp 188, 0  5月 24 11:05 /dev/ttyUSB0
$ python3 ~/mt76x7-uploader/upload.py -c /dev/ttyUSB0 -p mt7697 -n ~/mt76x7-uploader/da97.bin -f build/uart_loopback_data_dma.bin 
Start uploading the download agent
0%                          100%
[############################# ] | ETA: 00:00:00DA uploaded, start uploading the user bin
0%       100%
[########## ] | ETA: 00:00:00
Bin file uploaded. The board reboots now.

測試 Test

接下來可以用 serial terminal 透過 /dev/ttyUSB0,Baud Rate 是 115200 和 Linkit 7697 通訊。因為我習慣使用 picocom,所以我就用 picocom

$ sudo picocom -b 115200 /dev/ttyUSB0
picocom v3.1

port is        : /dev/ttyUSB0
flowcontrol    : none
baudrate is    : 115200
parity is      : none
databits are   : 8
stopbits are   : 1
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
hangup is      : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv -E
imap is        : 
omap is        : 
emap is        : crcrlf,delbs,
logfile is     : none
initstring     : none
exit_after is  : not set
exit is        : no

Type [C-a] [C-h] to see available commands
Terminal ready


Please input data to this UART port and watch it's output:
Hello World!!!

按下 Linkit 7697 上的 RST 鍵。它會重開機,接著會透過 /dev/ttyUSB0 print 出字串:"Please input data to this UART port and watch it's output:",我輸入 "Hello World!!!",Linkit 7697 也真的 echo print 出 "Hello World!!!" => Serial Echo Application 測試成功!

Internet Echo Server Application

在 Linkit 7697 上成功測試 Serial Echo Application 後,可以進一步測試 TCP 通訊。
剛好可以修改既有的範例程式 lwip_socket project/linkit7697_hdk/apps/lwip_socket

修改程式

為了簡化、專注在 TCP echo server 的部份,我修改了 lwip_socket 範例,修改內容如:https://gist.github.com/starnight/9cd376bcd8895127ec3ee6217fcc7d3c#file-lwip-socket-diff
所以可以直接 patch 該 diff 快速修改:

$ cd ~/Desktop/linkitsdk/project/linkit7697_hdk/apps/lwip_socket/
[zack@starnight lwip_socket]$ patch -p1 < /tmp/lwip-socket.diff 
patching file src/main.c
  • 再依據所在環境,於 src/main.c 設定 WIFI SSID、密碼
  • src/main.c 設定 Server 想要 listen 的 port,範例程式是 port 6500
#define WIFI_SSID                ("<WIFI SSID>")
#define WIFI_PASSWORD            ("<WIFI Password>")

#define SOCK_TCP_SRV_PORT        6500

編譯 Compile

$ cd GCC
$ make
../../../../../middleware/MTK/wifi_service/combo/module.mk:21: WIFI_LIB_FOLDER=wifi_supp
Prebuilt WIFI_LIB_FOLDER=wifi_supp
../../../../../middleware/MTK/wifi_service/combo/module.mk:21: WIFI_LIB_FOLDER=wifi_supp
Prebuilt WIFI_LIB_FOLDER=wifi_supp
rm -f ...project/linkit7697_hdk/apps/lwip_socket/GCC/build/*.log
Build... cos_api.o
Build... cos_api.o PASS
...
Build... verno.o
Build... verno.o PASS
Linking...
Done
   text	   data	    bss	    dec	    hex	filename
 338428	   1060	 233042	 572530	  8bc72	...project/linkit7697_hdk/apps/lwip_socket/GCC/build/lwip_socket.elf
Generate Assembly from elf:
copy_firmware.sh....
BOARD=linkit7697_hdk
bin filename is lwip_socket.bin
...project/linkit7697_hdk/apps/lwip_socket/GCC/build/mt7697_bootloader.bin doesn't exist. copy default bootloader done.
cp ...project/linkit7697_hdk/apps/lwip_socket/GCC/mt7687_hdk.cmm to ...project/linkit7697_hdk/apps/lwip_socket/GCC/build/

編譯出來的 binary file 是 lwip_socket.bin,在 build 資料夾下。

燒錄 Flash

$ python3 ~/mt76x7-uploader/upload.py -c /dev/ttyUSB0 -p mt7697 -n ~/mt76x7-uploader/da97.bin -f build/lwip_socket.bin 
Start uploading the download agent
0%                          100%
[############################# ] | ETA: 00:00:00DA uploaded, start uploading the user bin
0%                          100%
[############################# ] | ETA: 00:00:00
Bin file uploaded. The board reboots now.

測試 Test

一樣使用 picocom 透過 serial console 看 Linkit 7697 執行時的資訊。按下 Linkit 7697 上的 RST 鍵。它會重開機:

$ sudo picocom -b 115200 /dev/ttyUSB0 
picocom v3.1

port is        : /dev/ttyUSB0
flowcontrol    : none
baudrate is    : 115200
parity is      : none
databits are   : 8
stopbits are   : 1
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
hangup is      : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv -E
imap is        : 
omap is        : 
emap is        : crcrlf,delbs,
logfile is     : none
initstring     : none
exit_after is  : not set
exit is        : no

Type [C-a] [C-h] to see available commands
Terminal ready
[T: 30 M: lwip_socket_example C: info F: main L: 196]: start to create task.
[T: 244 M: wifi C: error F: wifi_wlan_evt_handler L: 296]: Supplicant is not ready to receive event from interface(=0) yet.
[T: 245 M: inband C: warning F: inband_queue_evt_handler L: 791]: u2PacketType(0xe000), ucEID(0x30), ucSeqNum(0x0) not handled!
[T: 1237 M: minisupp C: error F: wpa_supplicant_entry L: 414]: ========= Supplicant Ready =======
[T: 2290 M: inband C: warning F: inband_queue_evt_handler L: 791]: u2PacketType(0xe000), ucEID(0x76), ucSeqNum(0x0) not handled!
[T: 2386 M: common C: info F: wifi_station_port_secure_event_handler L: 114]: wifi connected
[T: 3387 M: common C: info F: ip_ready_callback L: 75]: ************************
[T: 3387 M: common C: info F: ip_ready_callback L: 76]: DHCP got IP:192.168.1.123
[T: 3387 M: common C: info F: ip_ready_callback L: 77]: ************************
[T: 3387 M: lwip_socket_example C: info F: user_entry L: 161]: Begin to create socket_sample_task
[T: 3387 M: lwip_socket_example C: info F: user_entry L: 172]: Finish to create socket_sample_task
[T: 3387 M: lwip_socket_example C: info F: tcp_server_test L: 81]: tcp_server_test starts
[T: 6762 M: lwip_socket_example C: info F: tcp_server_test L: 116]: TCP server waiting for data... 

可以看到:

  1. 連上所在環境的 Wifi
  2. 分配到的 IP 是:192.168.1.123
  3. TCP echo server 啟動了!等待 client 中

開啟另一個 terminal,使用 telnet 當作 client,連到 TCP echo server,並傳送一些字串:

$ telnet 192.168.1.123 6500
Trying 192.168.1.123...
Connected to 192.168.1.123.
Escape character is '^]'.
Hello Linkit 7697!  LWIP!
Hello Linkit 7697!  LWIP!
^]
telnet> 
Connection closed.

Client Side

  1. 成功連到位於 192.168.1.123:6500 的 TCP echo server。
  2. 傳送出 "Hello Linkit 7697! LWIP!" 字串,TCP echo server 也正確回傳 "Hello Linkit 7697! LWIP!" 字串。
[T: 29664 M: lwip_socket_example C: info F: tcp_server_test L: 123]: TCP
server received data:Hello Linkit 7697!  LWIP!

[T: 36716 M: lwip_socket_example C: info F: tcp_server_test L: 133]: TCP server s close:ret = 0
[T: 36716 M: lwip_socket_example C: info F: tcp_server_test L: 135]: TCP server test completed

Terminating...
Thanks for using picocom

Server Side
從 TCP echo server 也可以看到和上述相同的紀錄。=> Internet Echo Server Application 測試成功!