2023寒假在家练-基于Sipeed M1s Dock综合应用-网络相机
一、目标
完成一个基于Sipeed M1s Dock 的网络相机
二、具体要求:
完成相机驱动,定时拍摄图片,并将图片通过网络传到电脑或服务器,实现长时间拍摄并通过电脑端编程将图片合成为一个视频。
三、介绍
Sipeed M1s Dock 是基于 Sipeed M1s 模组来设计的一款核心板,引出了 MIPI CSI、SPI LCD 等 FPC 接口,免去接线难的烦恼。
四、主要代码片段及说明
1.phython文件
import socket
import numpy as np
import cv2
import datetime
from pathlib import Path
if __name__ == '__main__':
# 创建tcp服务端套接字
# 参数同客户端配置一致,这里不再重复
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,让程序退出端口号立即释放,否则的话在30秒-2分钟之内这个端口是不会被释放的,这是TCP的为了保证传输可靠性的机制。
tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 给客户端绑定端口号,客户端需要知道服务器的端口号才能进行建立连接。IP地址不用设置,默认就为本机的IP地址。
tcp_server.bind(("192.168.43.104", 9000))
print(tcp_server)
# 设置监听
# 128:最大等待建立连接的个数, 提示: 目前是单任务的服务端,同一时刻只能服务与一个客户端,后续使用多任务能够让服务端同时服务与多个客户端
# 不需要让客户端进行等待建立连接
# listen后的这个套接字只负责接收客户端连接请求,不能收发消息,收发消息使用返回的这个新套接字tcp_client来完成
tcp_server.listen(128)
# 等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞,代码才能继续往下执行
# 1. 专门和客户端通信的套接字: tcp_client
# 2. 客户端的ip地址和端口号: tcp_client_address
tcp_client, tcp_client_address = tcp_server.accept()
print("3")
# 代码执行到此说明连接建立成功
print("客户端的ip地址和端口号:", tcp_client_address)
count=100
while True:
# 接收客户端发送的数据, 这次接收数据的最大字节数是4
recv_data = tcp_client.recv(4)
mjpeg_len = int.from_bytes(recv_data, 'little')
print("recv len: ", mjpeg_len)
tcp_client.send(recv_data)
recv_data_mjpeg = b''
remained_bytes = mjpeg_len
while remained_bytes > 0:
recv_data_mjpeg += tcp_client.recv(remained_bytes)
remained_bytes = mjpeg_len - len(recv_data_mjpeg)
print("recv stream success")
if recv_data_mjpeg[:2] != b'\xff\xd8' \
or recv_data_mjpeg[-2:] != b'\xff\xd9':
continue
mjpeg_data = np.frombuffer(recv_data_mjpeg, 'uint8')
img = cv2.imdecode(mjpeg_data, cv2.IMREAD_COLOR)
img_dir = "./image"
img_path = "F:/project/image/%d.jpg" % count
cv2.imwrite(str(img_path),img)
count=count+1
cv2.imshow('stream', img)
if cv2.waitKey(1) == 32:
break
# 关闭服务与客户端的套接字, 终止和客户端通信的服务
tcp_client.close()
# 关闭服务端的套接字, 终止和客户端提供建立连接请求的服务 但是正常来说服务器的套接字是不需要关闭的,因为服务器需要一直运行。
# tcp_server.close()
def makeVideo( img_dir: Path ,fps:int):
video_path: Path =img_dir/"output.mp4"
video = cv2.VideoWriter(str(video_path), cv2.VideoWriter_fourcc(
'M','P','4','V'),fps,(800,600))
for image in img_dir.iterdir():
print(image)
image=cv2.imread(str(image))
video.write(image)
video.release()
#生成视频
def main ():
fps = 30
dir_name : Path = Path(__file__).parent / ("./image")
makeVideo(dir_name, fps)
if __name__ == '__main__':
main()
2.c文件
#include <stdbool.h>
#include <stdio.h>
/* FreeRTOS */
#include <FreeRTOS.h>
#include <task.h>
/* bl808 c906 std driver */
#include <bl808_glb.h>
#include <bl_cam.h>
#include <m1s_c906_xram_wifi.h>
void main()
{
vTaskDelay(1);
bl_cam_mipi_mjpeg_init();
m1s_xram_wifi_init();
m1s_xram_wifi_connect("Jibril", "fcbbarcelona010826");
m1s_xram_wifi_upload_stream("192.168.43.104", 9000);
}
#设置好wifi与密码,ip地址与端口,将拍摄的图片上传
五、遇到的困难
在实践过程中,我遇到了许多问题,比如 Firmware文件无法生成bin文件,板卡无法连接至电脑,cv2函数库调用失败。前两个问题我都通过在sipeed wiki学习到的串口烧录进行了重新烧录解决,最后一个问题通过重新安装cv2函数库解决。
六、学习后的一些理解
1.板子上为什么有一个额外的 BL702 芯片
BL702 芯片在这里担任着 USB 转双串口芯片功能。因此有时候串口异常的话,可以上电前按住 BOOT 按键来给 BL702 重新烧录一下 USB 转双串口的固件。
2.为什么要用双串口
根据 BL808 的手册可以知道里面有三颗核心,双串口分别连接到了其中的两颗核心,可以自己体验异构核心的执行顺序。
3.为什么用虚拟 U 盘
虚拟 U 盘是基于固件出来的,目的是方便用户快速烧录体验 Demo。把 BL808 里面的全部固件擦除后,就不会再有虚拟 U 盘了。想要再次使用虚拟 U 盘的话,按照文档重新使用串口烧录的方法重新烧录一下默认的固件就行了。
4.为什么要说明串口烧录
虚拟 U 盘是基于固件的,想要烧录这个固件就必须有一种烧录方式将它烧录进板子。这里我们用串口烧录的方法来烧录最基础的固件到芯片里面的 FLASH 中。
七、项目过程及思路
首先我先跟着老师在直播中的介绍在虚拟机中对编译环境进行了搭建,第一先安装编译所需要的相关软件,获取 SDK 需要用到 git 这个软件,编译 SDK 需要用到 make 这个软件,对应着后面文档检查自己的目录结构配置的时候需要用到 tree 这个软件。对它们进行安装后再用git clone https://gitee.com/Sipeed/ M1s_BL808_example.git获取例程仓库,再用git clone https://gitee.com/ sipeed/M1s_BL808_SDK.git 获得 SDK 仓库,再在在 SDK 仓库文件夹下,获取编译工具链,根据例程仓库里面的 readme 的要求,工具链应存放在 M1s_BL808_ SDK/toolchain 目录下,最后再配置编译工具链路径,这样我就可以对一些demo进行编译。
本实验我是基于c906里面的WIFI 串流摄像头demo进行修改并进行了编译,并将生成的bin文件拖到已经进行过串口烧录的板卡内,然后使用vscode对main.py进行编译,从而达成目标效果。
八、软件流程图
九、项目总结
总之,在寒假进行了硬禾课堂的寒假一起练项目,让我收获良多,对板卡,虚拟机,ai的了解与使用都上了一个台阶,电子森林与硬禾学堂的老师的讲解也都非常详细与有趣,在未来,我也想多多了解并学习相关方面的内容,也希望能够多多参与到电子森林与硬禾课堂的课程中。