基于 Sipeed M1s Dock 实现网络相机
使用 Sipeed M1s Dock 的板载摄像头定时捕获当前画面,并通过 WiFi 将图片发送到同一网络下的 PC 服务程序。
标签
嵌入式系统
2023寒假在家练
topgear
更新2023-03-28
751

2023寒假一起练 - 基于 Sipeed M1s Dock 实现网络相机

 

一、项目描述

Sipeed M1s Dock 基于 BL808 芯片组,同时集成了显示屏和摄像头。BL808 是一款高度集成的 AIoT 芯片组,具有 Wi-Fi/BT/BLE/Zigbee 等无线互联单元,包含多个 CPU 以及音频编码译码器、视频编码译码器和 AI 硬件加速器,适用于各种高性能和低功耗应用领域。

BL808 系列芯片主要包含无线和多媒体两个子系统:

  • 无线子系统包含一颗 RISC-V 32-bit 高性能 CPU - 内核 E907,集成 Wi-Fi/BT/Zigbee 无线子系统,可以实现多种无线连接和数据传输,提供多样化的连接与传输体验。
  • 多媒体子系统包含一颗 RISC-V 64-bit 超高性能 CPU - 内核 C906,集成 DVP/CSI/ H264/NPU 等视频处理模块,可以广泛应用于视频监控/智能音箱等多种 AI 领域。

在本项目中,使用内核 E907 实现:

  • WIFI 网络连接
  • 摄像头捕获图像并发送到远端服务器

使用内核 C906 实现任务控制。

二、设计思路

BL808 内部集成了两颗 RSIC-V CPU和NPU,本项目不需要使用 NPU,两颗 CPU 之间通过 XRAM 进行命令交互。具体实现思路如下:

  • 控制命令 - 使用内核 C906 发送控制命令给内核 E907,包括“WIFI 连接”、“开始相机数据 TCP 串流(支持时间周期)"、“停止相机数据TCP串流”、“开始相机数据 UDP 串流"和“停止相机数据UDP串流”
  • WiFi 连接、串流任务 - 在 E907 在执行
  • 在 PC 上使用 C# 实现一个可以接收 TCP/UDP 串流数据的服务程序

框图如下:

FgqM7R_sR6YWnEtA09G66Z3QgmN-

三、开发环境的搭建

BL808 的 CPU 是基于平头哥的 RSIC-V 内核设开发的,平头哥官方提供了 Linux 和 Windows 平台的工具链,选择 Linux 平台开发会更方便一些。我选择的是 Linux 开发环境 - WSL2 Ubuntu20.04。固件升级工具可以跨平台,因为我的 WSL2 Ubuntu20.04 没有配置图形界面,所以使用了 Windows 版本的升级工具。

开发环境配置方法如下:

  • 创建BL808开发目录:
    mkdir ~/bl808
  • 下载 SDK 和 示例代码:
    cd ~/bl808
    git clone https://gitee.com/Sipeed/M1s_BL808_SDK.git
    git clone https://gitee.com/Sipeed/M1s_BL808_example.git
    cd M1s_BL808_example && ln -s ../M1s_BL808_SDK ./
  • 下载平头哥工具链:

https://dl.sipeed.com/shareURL/others/toolchain

下载 Xuantie-900-gcc-elf-newlib-x86_64-V2.2.4-20211227.tar.gz

  • 安装工具链:
    # 创建工具链目录
    cd ~/bl808 && mkdir M1s_BL808_SDK/toolchain
    
    # 拷贝工具链到开发目录并解压到 toolchain 目录
    mv Xuantie-900-gcc-elf-newlib-x86_64-V2.2.4-20220715.tar.gz ~/bl808
    tar -zxvf Xuantie-900-gcc-elf-newlib-x86_64-V2.2.4-20220715.tar.gz -C M1s_BL808_SDK/toolchain/
    
    # 工具链目录重命名
    cd M1s_BL808_SDK/toolchain && mv Xuantie-900-gcc-elf-newlib-x86_64-V2.2.4/ Linux_x86_64 && cd -
  • 编译示例工程:
    # 编译在 C906 上运行的程序
    cd ~/bl808/M1s_BL808_example/c906_app/
    
    # 配置 SDK 路径环境变量
    export BL_SDK_PATH=$(pwd)/../M1s_BL808_SDK
    
    # 编译 hello_world 项目,编译成功后可以在 build_out 目录找到 hello_world.bin
    ./build.sh hello_world
    
    # 编译在 E907 上运行的程序
    cd ~/bl808/M1s_BL808_example/e907_app/
    
    # 编译 firmware,编译成功后可以在 build_out 目录找到 firmware.bin
    ./build.sh firmware
    

四、开发之前的填坑

Sipeed M1s Dock 出厂时固化的 E907 内核程序有 bug,会导致系统使用固定IP地址,解决方法有两个:

  • 从 Sipeed 官方下载最新的 E907 固件
  • 使用最新的 SDK 仓库版本,编译可供 E907 使用的 firmware.bin。

这里提供直接去官方下载固件更新的方法:

https://dl.sipeed.com/shareURL/MAIX/M1s/M1s_Dock/7_Firmware/factory

下载 firmware_20230227.bin

https://dl.sipeed.com/shareURL/MAIX/M1s/M1s_Dock/7_Firmware/partition

下载 partition_cfg_16M_m1sdock.toml

https://dev.bouffalolab.com/download

下载 Bouffalo Lab Dev Cube,使用 BLDevCube.exe

FgwVkkb6Nw4HgkJjdLb77_WSV513

关于固件升级的详细描述,可以参考 Sipeed 的官方 Wiki:

https://wiki.sipeed.com/hardware/zh/maix/m1s/other/start.html#%E4%B8%B2%E5%8F%A3%E7%83%A7%E5%BD%95

  • 板卡有两个USB接口:OTG和UART:
    • 连接 UART 后,PC上会出现两个串口,小端口号对应的是 C906,大端口号对应的是 E907,串口波特率均为 2000000 bps;
    • 连接 OTG 后,保持按住板卡两侧按钮,然后按 RST,PC上会出现虚拟出来的U盘,复制固件程序到 U盘后,会自动对 C906进 行升级。

五、代码和功能展示

  1. C906 固件
    • 参考 M1s_BL808_example/c906_app/cli_demo/main.c 的命令行程序实现
    • 代码如下:
      // wifi 连接命令
      static void wifi_connect_cmd(char *buf, int len, int argc, char **argv)
      {
          getopt_env_t getopt_env;
      
          if (3 > argc) {
              goto _ERROUT;
          }
      
          utils_getopt_init(&getopt_env, 0);
      
          if (getopt_env.optind >= argc || argc - getopt_env.optind < 1) {
              printf("Expected ssid and password\r\n");
              goto _ERROUT;
          }
      
          m1s_xram_wifi_init();
          m1s_xram_wifi_connect(argv[getopt_env.optind], argv[getopt_env.optind+1]);
          printf("connect wifi ssid:%s, psk:%s\r\n", argv[getopt_env.optind], argv[getopt_env.optind+1]);
      
          return;
      
      _ERROUT:
          printf("[USAGE]: %s <ssid> <password>\r\n", argv[0]);
          return;
      }
      
      // 其他命令代码见代码附件
      
      // 命令列表
      const static struct cli_command cmds_user[] STATIC_CLI_CMD_ATTRIBUTE = {
          { "wifi_connect", "wifi station connect", wifi_connect_cmd},
          { "start_tcp_upload_stream", "start wifi upload stream (tcp)", wifi_upload_stream_tcp_start_cmd},
          { "stop_tcp_upload_stream", "stop wifi upload stream (tcp)", wifi_upload_stream_tcp_stop_cmd},
          { "start_udp_upload_stream", "start wifi upload stream (udp)", wifi_upload_stream_udp_start_cmd},
          { "stop_udp_upload_stream", "stop wifi upload stream (udp)", wifi_upload_stream_udp_stop_cmd},
      };
  2. E907 固件
    • 基于 Sipeed 提供的 xram_wifi 程序进行修改,直接修改 M1s_BL808_SDK 中的相关代码,修改的文件如下:
      topgear@ubuntu2004:~/bl808/M1s_BL808_example/M1s_BL808_SDK$ git status
      On branch main
      Your branch is up to date with 'origin/main'.
      
      Changes not staged for commit:
        (use "git add <file>..." to update what will be committed)
        (use "git restore <file>..." to discard changes in working directory)
              modified:   components/sipeed/c906/m1s_c906_xram/include/m1s_c906_xram_wifi.h
              modified:   components/sipeed/c906/m1s_c906_xram/src/m1s_c906_xram_wifi.c
              modified:   components/sipeed/e907/m1s_e907_xram/src/m1s_e907_xram_wifi.c
              modified:   components/sipeed/m1s_common_xram/m1s_common_xram_wifi.h
      
      no changes added to commit (use "git add" and/or "git commit -a")
    • 修改 M1s_BL808_SDK/components/sipeed/m1s_common_xram/m1s_common_xram_wifi.h,更新交互命令相关的枚举变量类型
      diff --git a/components/sipeed/m1s_common_xram/m1s_common_xram_wifi.h b/components/sipeed/m1s_common_xram/m1s_common_xram_wifi.h
      index 5a26257..a5d1b27 100644
      --- a/components/sipeed/m1s_common_xram/m1s_common_xram_wifi.h
      +++ b/components/sipeed/m1s_common_xram/m1s_common_xram_wifi.h
      @@ -13,7 +13,10 @@ enum wifi_operation {
           XRAM_WIFI_DEINIT,
           XRAM_WIFI_CONNECT,
           XRAM_WIFI_DISCONNECT,
      -    XRAM_WIFI_UPLOAD_STREAM,
      +    XRAM_WIFI_UPLOAD_STREAM_TCP_START,
      +    XRAM_WIFI_UPLOAD_STREAM_TCP_STOP,
      +    XRAM_WIFI_UPLOAD_STREAM_UDP_START,
      +    XRAM_WIFI_UPLOAD_STREAM_UDP_STOP,
       };
      
       struct m1s_xram_wifi {
      @@ -26,6 +29,7 @@ struct m1s_xram_wifi {
      
               struct {
                   uint32_t port;
      +            uint32_t delay_ms;
                   char ip[16];
               } __attribute__((packed)) upload_stream;
           };
    • 修改 M1s_BL808_SDK/components/sipeed/c906/m1s_c906_xram/src/m1s_c906_xram_wifi.c,修改 TCP 串流命令,增加停止命令和 UDP 串流开始/停止命令
      diff --git a/components/sipeed/c906/m1s_c906_xram/src/m1s_c906_xram_wifi.c b/components/sipeed/c906/m1s_c906_xram/src/m1s_c906_xram_wifi.c
      index aad9c6f..c781c48 100644
      --- a/components/sipeed/c906/m1s_c906_xram/src/m1s_c906_xram_wifi.c
      +++ b/components/sipeed/c906/m1s_c906_xram/src/m1s_c906_xram_wifi.c
      @@ -69,13 +69,35 @@ int m1s_xram_wifi_disconnect(void)
           return m1s_xram_wifi_operation(&op, XRAM_WIFI_DISCONNECT);
       }
      
      -int m1s_xram_wifi_upload_stream(char *ip, uint32_t port)
      +int m1s_xram_wifi_upload_stream(char *ip, uint32_t port, uint32_t delay_ms)
       {
           m1s_xram_wifi_t op = {0};
           strncpy(op.upload_stream.ip, ip, sizeof(op.upload_stream.ip));
           op.upload_stream.port = port;
      -    return m1s_xram_wifi_operation(&op, XRAM_WIFI_UPLOAD_STREAM);
      +    op.upload_stream.delay_ms = delay_ms;
      +    return m1s_xram_wifi_operation(&op, XRAM_WIFI_UPLOAD_STREAM_TCP_START);
       }
      +
      +int m1s_xram_wifi_upload_stream_stop()
      +{
      +    m1s_xram_wifi_t op = {0};
      +    return m1s_xram_wifi_operation(&op, XRAM_WIFI_UPLOAD_STREAM_TCP_STOP);
      +}
      +
      +int m1s_xram_wifi_upload_stream_udp(char *ip, uint32_t port)
      +{
      +    m1s_xram_wifi_t op = {0};
      +    strncpy(op.upload_stream.ip, ip, sizeof(op.upload_stream.ip));
      +    op.upload_stream.port = port;
      +    return m1s_xram_wifi_operation(&op, XRAM_WIFI_UPLOAD_STREAM_UDP_START);
      +}
      +
      +int m1s_xram_wifi_upload_stream_stop_udp()
      +{
      +    m1s_xram_wifi_t op = {0};
      +    return m1s_xram_wifi_operation(&op, XRAM_WIFI_UPLOAD_STREAM_UDP_STOP);
      +}
      +
      diff --git a/components/sipeed/c906/m1s_c906_xram/src/m1s_c906_xram_wifi.c b/components/sipeed/c906/m1s_c906_xram/src/m1s_c906_xram_wifi.c
      index aad9c6f..c781c48 100644
      --- a/components/sipeed/c906/m1s_c906_xram/src/m1s_c906_xram_wifi.c
      +++ b/components/sipeed/c906/m1s_c906_xram/src/m1s_c906_xram_wifi.c
      @@ -69,13 +69,35 @@ int m1s_xram_wifi_disconnect(void)
           return m1s_xram_wifi_operation(&op, XRAM_WIFI_DISCONNECT);
       }
      
      -int m1s_xram_wifi_upload_stream(char *ip, uint32_t port)
      +int m1s_xram_wifi_upload_stream(char *ip, uint32_t port, uint32_t delay_ms)
       {
           m1s_xram_wifi_t op = {0};
           strncpy(op.upload_stream.ip, ip, sizeof(op.upload_stream.ip));
           op.upload_stream.port = port;
      -    return m1s_xram_wifi_operation(&op, XRAM_WIFI_UPLOAD_STREAM);
      +    op.upload_stream.delay_ms = delay_ms;
      +    return m1s_xram_wifi_operation(&op, XRAM_WIFI_UPLOAD_STREAM_TCP_START);
       }
      +
      +int m1s_xram_wifi_upload_stream_stop()
      +{
      +    m1s_xram_wifi_t op = {0};
      +    return m1s_xram_wifi_operation(&op, XRAM_WIFI_UPLOAD_STREAM_TCP_STOP);
      +}
      +
      +int m1s_xram_wifi_upload_stream_udp(char *ip, uint32_t port)
      +{
      +    m1s_xram_wifi_t op = {0};
      +    strncpy(op.upload_stream.ip, ip, sizeof(op.upload_stream.ip));
      +    op.upload_stream.port = port;
      +    return m1s_xram_wifi_operation(&op, XRAM_WIFI_UPLOAD_STREAM_UDP_START);
      +}
      +
      +int m1s_xram_wifi_upload_stream_stop_udp()
      +{
      +    m1s_xram_wifi_t op = {0};
      +    return m1s_xram_wifi_operation(&op, XRAM_WIFI_UPLOAD_STREAM_UDP_STOP);
      +}
      +
    • 修改 M1s_BL808_SDK/components/sipeed/e907/m1s_e907_xram/src/m1s_e907_xram_wifi.c,增加 UDP 串流命令等,具体实现见代码附件 
  3. 发送图片的流程:
    • FglvaPAhh6cMZV6bkMcDYEAkR62Z
  4. C# 程序
    • 实现如下功能:
      • 获取本机 IP 地址
      • 启动/停止 TCP 接收服务
      • 启动/停止 UDP 接收服务
    • Fu10LrA21qnjjdzpD_-jheZh4CF5

六、功能展示

FvwvwoSh8_ZE8BKRtY3Baqy1RzVv

七、未来计划

目前 TCP/UDP 长时间串流会出现卡顿,未来计划优化串流服务的程序,并且增加语音事实传输的功能,并且结合显示屏做视频的同步展示。

八、参考连接

附件下载
m1s_camera.tar.gz
C906/E907 代码及固件bin文件、C# 上位机源码和可执行的程序
团队介绍
在职工程师
团队成员
topgear
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号