# Internet echo server application on MT7687 with Linkit SDK ###### tags: `Linkit SDK`, `MT7687`, `Toolchain`, `Internet echo server`, `arm`, `Cortex-M4`, `MCU` 為了量測移動、轉動物體的相關數值,如果還要從待測物連接通訊線到外界設備,是件不切實際的事。因此使用無線通訊,將量測數值傳回就是不可或缺的技術。 在尋找合適的晶片/開發板過程中,有找到例如 [ESP32](https://www.espressif.com/en/products/socs/esp32/overview)/[8266](https://www.espressif.com/en/products/socs/esp8266/overview) 等相關資源。但考量到它是中國晶片,便不選擇它。剛好之前運氣好,抽到一張 [Linkit 7697](https://labs.mediatek.com/en/platform/linkit-7697) 的開發板,搭載的晶片是臺灣在地公司聯發科 MediaTek 的 [MT7697](https://labs.mediatek.com/en/chipset/MT7697) 控制器,是屬 Cortex-M4 MCU。寫個 Echo server 放到 Linkit 7697 上,來做網路通訊 PoC 好像不錯。 ## 準備 Toolchain 工欲善其事,必先利其器! ### Linkit SDK 環境 相較於其他晶片商,MediaTek 的商業模式是較封閉的。不像許多晶片商,可以在**公開**的網路找到**免費**且**詳細**的**技術資訊**。如果要詳細資訊/支援,就必須要有商業往來(**錢**)。所以如果要在 Linkit 上開發一些應用,無法從零開始,必須使用 MediaTek 打包好的 [Linkit SDK](https://docs.labs.mediatek.com/resource/mt7687-mt7697/en) 環境,再往上發展。也可以從官網上看到,Linkit SDK 是採用 FreeRTOS 當作 kernel。 ![img](https://docs.labs.mediatek.com/resource/mt7687-mt7697/files/en/5799961/9208928/1/1473319599246/sdk_architecture_for_MT7697.png) 可以從官網下載 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!?」無奈 ... 所以我在[社群上有反應這問題](https://www.facebook.com/groups/1641811569367039/permalink/2552673764947477/),希望 MediaTek 可以盡快更新 SDK。而目前暫時的解決方法,就是先額外裝支援 32 bits 的 libc library `pacman -S lib32-glibc`。 ### Flash tool Linkit SDK 有 MT76x7 FlashTool,用來把編譯好的程式是載入 Linkit 7697。但根據 [LinkIt 7697 不用能使用官方的 MT76X7 FLASH TOOL的問題](https://yafaretcs.blogspot.com/2018/12/linkit-7697-mt76x7-flash-tool.html)和想用單純指令/不用 GUI 的需求,所以我改用文章中提到的 [mt76x7-uploader](https://github.com/MediaTek-Labs/mt76x7-uploader)。mt76x7-uploader 的 [README.md](https://github.com/MediaTek-Labs/mt76x7-uploader/blob/master/README.md) 有詳述其運作機制。尷尬的是,它是用 **Python 2.7** 寫出來的程式(截至2020/05/23,其 master branch 仍是 Python 2.7),但 [Python 2.7 已在 2020/1/1 EOL](https://www.python.org/doc/sunset-python-2/)。為了確保之後可以持續有工具可以用,只好自己想辦法改程式,把 mt76x7-uploader 轉到 Python 3。 在 trace code 之後,發現: * 主要的程式是 [upload.py](https://github.com/MediaTek-Labs/mt76x7-uploader/blob/v1.4/upload.py),預設用 Python 2.7 執行 * 需要第三方 Python libraries:serial、xmodem 和 pyprind。MediaTek 已整理這些 libraries 放到 mt76x7-uploader [路徑](https://github.com/MediaTek-Labs/mt76x7-uploader/tree/v1.4)下。 從上述的追蹤/分析結果,需要從 Python 2 porting 到 Python 3 的部份只有 upload.py 和說明文件(Windows 不在我的考量範圍)。所以我發了個 [pull request (PR)](https://github.com/MediaTek-Labs/mt76x7-uploader/pull/2),且該 PR 已被 merge 到 mt76x7-uploader 的 [python-3 branch](https://github.com/MediaTek-Labs/mt76x7-uploader/tree/python-3)。 終於,準備好 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](https://hackmd.io/_uploads/rJ1kqud8n.png) 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](https://hackmd.io/_uploads/SyAe9du83.png) 從 TCP echo server 也可以看到和上述相同的紀錄。=> **Internet Echo Server Application** 測試成功!