更新时间:2022-01-13 07:33:40
登录IOT平台(https://iot.tuya.com/)选择“可视挖耳勺”产品并创建产品。如图所示:
创建产品过后需要选择产品对应的面板(目前只有该面板支持拉流,如果有其他定制需求可以找对接人员提定制需求)
创建产品选完对应DP点和面板后进入硬件开发。再硬件开发页面中选择对应的芯片平台后,可以下载对应SDK,如图所示:
ty_iot_wf_rtos_sdk_2.3.1
├─ apps #Demo
│ ├─ tuya_demo_elp_1plug
│ ├─ tuya_demo_light_pwm
│ └─ tuya_demo_template
├─ platforms #芯片平台相关文件
└─ sdk #涂鸦Wi-Fi SDK
│ ├─ include #头文件
│ └─ lib #lib库
├─ build_app.h #编译脚本
├─ CHANGELOG.md #迭代说明
├─ README.md #注意事项
目前SDK需要在 Linux 系统下编译,具体的环境搭建可以参考 开发环境
tar -zxvf b6c36suogf0g.tar.gz
目前固件编译的脚本已经写好,直接运行即可。如: sh build_app.sh apps/Bk731u_Video_Test Bk731u_Video_Test 1.0.0
建议编译之前先 clean 一下,清除之前的编译信息。如:sh build_app.sh apps/Bk731u_Video_Test Bk731u_Video_Test 1.0.0 clean
如图:
编译成功后,需要把编译出来的固件上传到IOT平台,上传的目的是获取对应授权码。
编译后的固件生成在\ty_iot_sdk_bk7231u_bk7231t_unencrypted-2.3.4-beta.1\apps\Bk731u_Video_Test\output\1.0.0
目录下
文件名 | 说明 |
---|---|
Bk731u_Video_Test_QIO_1.0.0.bin | 生产固件,用于模组Flash工作方式为QIO的模组 |
Bk731u_Video_Test_UA_1.0.0.bin | 用户区固件,用于芯片烧录工具使用 |
Bk731u_Video_Test_UG_1.0.0.bin | 升级固件,用于上传涂鸦 IoT 平台用户区固件和 OTA 固件 |
在硬件开发处上传编译出来的对应固件,保存并上架。如图: | |
![]() |
|
![]() |
|
![]() |
固件上传完成后即可获取授权码,涂鸦的授权码分为以下三种类型,您可以根据自己使用的烧录授权方案选择合适的授权码目前该SDK仅支持 生产凭证-仅授权
和生产凭证
:
(目前每个账号只可以免费申请2个激活码。后续需要下单购买)
授权码名称 | 适用方案 | 说明 |
---|---|---|
生产凭证 | 适用于烧录授权一体方案 | 加密后的授权码信息,使用涂鸦上位机通过授权协议烧录到设备内,常用于固件烧录和授权一体方案,会对固件 Key 包含的固件标识名、固件版本等信息进行校验,防止出现固件烧录错误、版本错误等现象,是涂鸦标准模组使用的烧录方式 ,也是涂鸦最推荐的烧录方式,不过需要涂鸦生产系统支持相应芯片的烧录,不适用于非涂鸦标准芯片。 |
生产凭证-仅授权 | 适用于烧录授权分立方案 | 加密后的授权码信息,使用涂鸦上位机通过授权协议烧录到设备内,用于固件烧录和授权分立方案,不对固件 Key 包含的信息做校验,适用于非涂鸦标准芯片的授权。 |
授权码清单 | 不建议使用,如有需求,可以先通过工单与涂鸦进行沟通 | 授权码明文信息,由开发者自行开发上位机烧录到设备内,通常不建议使用。 |
目前如果是新得BK7231U芯片或者开发板需要先用 SPI 烧录 QIO 文件之后才能正常用串口烧录 UA 文件。(如果没有用SPI烧录,芯片是没有 bootloader 的,无法正常使用)
擦除状态一定要选择擦除程序空间,如果选择擦除全部空间会把RF标志位擦掉,固件需要重新做RF校验。
该工具可以直接从原厂获取,或者联系涂鸦对接人员购买。
(除以上5个IO以外,还需连VCC和GND)
在用SPI烧录之后,芯片已有 bootloader ,后续开发者在调试时,只需通过串口1烧录对应用户区固件即可。
对应烧录软件可直接从原厂获取或联系涂鸦FAE获取。
目前固件授权有两种方式:1.下单时选择生产凭证授权,此授权方法会检测PID是否和固件匹配,优点是比较简单,不需要走工单。2.下单时选择生产凭证授权-仅授权,此方法一般是针对不同PID的产品授权,生产凭证(Token)中不含固件信息,只用于授权激活;
如果没有生产凭证确定的选项,联系涂鸦的对接人员开通白名单
文档可以参考该文档,按照该文档流程进行即可:
struct video_pix_format v_format;
struct vbq_config video_buff_q_config;
struct sensor_reg_config reg_cfg;
VIDEO_FPS_TYPE fps;
sw_i2c_pin_config(1, 0); //设置I2C引脚, 如果不调用,默认sda---1, scl----0
OPERATE_RET tuya_soc_camera_init(soc_camera_link *tuya_camera_link, BYTE_T num, video_mem_handler *p_video_mem_handler);
ty_video_mem_handler.mem_malloc = tuya_hal_system_malloc;
ty_video_mem_handler.mem_free = tuya_hal_system_free;
tuya_soc_camera_init(tuya_camera_link,1,&ty_video_mem_handler);
soc_video_device = tuya_video_dev_find("ty_video");
if(soc_video_device == NULL){
PR_DEBUG("soc_video_device is null");
return OPRT_COM_ERROR;
}
/*初始化video_device*/
tuya_video_dev_open(soc_video_device);
/*设置格式*/
v_format.p_type = VGA_640_480;
v_format.pixelformat = V4L2_PIX_FMT_JPEG;
v_format.field = V4L2_FIELD_ANY;
v_format.colorspace = V4L2_COLORSPACE_JPEG;
ret = tuya_video_dev_ctl(soc_video_device,CMD_SET_FMT,&v_format);
if(ret != 0) {
PR_NOTICE("CMD_SET_FMT :%d",ret);
}
video_buff_q_config.video_pkg_buffer_len = 1 * 1024; //设置读图一个缓存bug大小
video_buff_q_config.video_pkg_num = 12; // 设置读图缓存个数
ret = tuya_video_dev_ctl(soc_video_device,CMD_STREAM_SET_BUFF,&video_buff_q_config);
if(ret != 0) {
PR_NOTICE("CMD_STREAM_SET_BUFF: %d",ret);
}
/*启动*/
tuya_video_dev_ctl(soc_video_device,CMD_STREAM_START,NULL);
/**
* @brief: tuya_video_dev_read 读取数据
* @param 用户配置
* @retval: none
*/
OPERATE_RET tuya_video_dev_read(video_device *video_dev, struct video_pkg **pkg);
/*释放 buf */
tuya_video_dev_ctl(soc_video_device, CMD_STREAM_DEQUEEU_BUFF,NULL);
INT_T ret;
rtsp_stream_src_t src = {0};
NW_IP_S ip_str;
wf_get_ip(WF_STATION, &ip_str);
PR_NOTICE("ip_str :%x, %s",ip_str.ip, ip_str.ip);
src.get_codec = __get_codec;
src.get_frame = __get_frame_jpeg;
src.get_frame_fragment = __get_frame_fragment_jpeg;
src.get_sample_rate = __get_sample_rate;
src.start = __start;
src.stop = __stop;
src.get_name = __get_name;
tuya_svc_lan_reg_rtsp_stream_src(src);
STATIC INT_T __get_frame_fragment_jpeg(INT_T user_id, rtsp_frame_fragment_t* pframe)
{
//rtsp 拉图回调
return 0;
}
/* rtsp 拉图回调 */
STATIC INT_T __get_frame_jpeg(INT_T user_id, RTSP_MEDIA_TYPE_E type, CHAR_T** buf, INT_T *plen, UINT64_T *pts)
{
//rtsp 拉图回调
return -1;
}
STATIC RTP_CODEC_E __get_codec(RTSP_MEDIA_TYPE_E type)
{
switch (type){
case RTSP_MEDIA_TYPE_VIDEO:
return RTP_CODEC_JPEG;
case RTSP_MEDIA_TYPE_AUDIO:
return RTP_CODEC_INVALID;
default:
return 0;
}
}
STATIC INT_T __get_sample_rate(RTSP_MEDIA_TYPE_E type)
{
switch (type){
case RTSP_MEDIA_TYPE_VIDEO:
return 90000;
case RTSP_MEDIA_TYPE_AUDIO:
return 8000;
default:
return 8000;
}
}
STATIC INT_T __start()
{
int i;
for(i = 0; i < 5; i++){
if (NULL == users[i]){
users[i] = Malloc(sizeof(user_info_s));
users[i]->last_pts[RTSP_MEDIA_TYPE_VIDEO] = 0;
users[i]->last_pts[RTSP_MEDIA_TYPE_AUDIO] = 0;
users[i]->pos[RTSP_MEDIA_TYPE_VIDEO] = 0;
users[i]->pos[RTSP_MEDIA_TYPE_AUDIO] = 0;
return i;
}
}
return -1;
}
STATIC INT_T __stop(INT_T user_id)
{
if (users[user_id]){
Free(users[user_id]);
users[user_id] = NULL;
}
return 0;
}
STATIC INT_T __get_name(CHAR_T* buf, INT_T* buf_len)
{
char name[] = "stream_0";
memcpy(buf, name, sizeof(name));
*buf_len = sizeof(name);
return 0;
}
有两种方式, 一种是自动识别sensor 驱动, 另一种是应用指定。 在初始化时候输入。
/*自动识别sensor*/
#define AUTO_RECOGNIT_SENSOR 1
#if AUTO_RECOGNIT_SENSOR
static struct resource sensor_chip_sourece[]={
{
.name = (BYTE_T *)"i2c", /*未知的sensor 使用 第 0 个I2C去连接*/
.type = 6,
.port = 0,
},
};
/*构建视频系统的参数,支持同时存在多个video_device*/
static soc_camera_link tuya_camera_link[] =
{
{
.name = (BYTE_T *)"ty_video",
/*用于配置sensor_chip驱动所用到的硬件资源*/
.sensor_device = {
.name = (BYTE_T *)SENSOR_ANY, /*不指定哪一款sensor 去自动识别*/
.num_resource = 1, //资源数目
.resource = sensor_chip_sourece, //资源
},
.host_nr = 0,
},
};
#else
static struct resource sensor_chip_sourece[]={
{
.name = (BYTE_T *)"i2c",
.type = 6, /*对应 tuya_driver的 TUYA_DRV_I2C*/
.port = 0, /*使用第0个I2C*/
},
{
.name = (BYTE_T *)"i2c_addr",
.type = 0,
.port = 0x21, /*I2C地址0x21*/
},
};
/*构建视频系统的参数,支持同时存在多个video_device*/
static soc_camera_link tuya_camera_link[] =
{
{
.name = (BYTE_T *)"ty_video",
/*用于配置sensor_chip驱动所用到的硬件资源*/
.sensor_device = {
.name = (BYTE_T *)SENSOR_GC0308,
.num_resource = 2, //资源数目
.resource = sensor_chip_sourece, //资源
},
.host_nr = 0, /*SENSOR_GC0328 与 第0个host总线接着*/
},
};
#endif
video_mem_handler ty_video_mem_handler;
// 初始化接口
OPERATE_RET tuya_soc_camera_init(soc_camera_link *tuya_camera_link, BYTE_T num, video_mem_handler *p_video_mem_handler);
ty_video_mem_handler.mem_malloc = tuya_hal_system_malloc;
ty_video_mem_handler.mem_free = tuya_hal_system_free;
tuya_soc_camera_init(tuya_camera_link,1,&ty_video_mem_handler);
udp 分片拉图, 分片的时间片需要相同。
bk 平台读图数据最后有5个字节数据是多余的。
请参考API说明
#include "ty_video_lib.h"
#include "tuya_hal_network.h"
#include "tuya_hal_semaphore.h"
#include "tuya_svc_lan_rtsp.h"
#include "rtsp_server.h"
typedef struct {
int pos[RTSP_MEDIA_TYPE_NUM];
uint64_t last_pts[RTSP_MEDIA_TYPE_NUM];
} user_info_s;
user_info_s* users[5] = {0};
SEM_HANDLE sem_handle;
SEM_HANDLE sem_handle_rtsp;
struct video_pkg *vb_pkg;
UCHAR_T test_tmp_buf[1025];
UCHAR_T *pstr = NULL;
static UCHAR_T cnt_max = 0;
#include "BkDriverGpio.h"
video_device *soc_video_device = NULL;
STATIC INT_T __get_frame_fragment_jpeg(INT_T user_id, rtsp_frame_fragment_t* pframe)
{
user_info_s* pinfo = users[user_id];
RTSP_MEDIA_TYPE_E type = pframe->type;
INT_T pos = pinfo->pos[type];
INT_T start = -1, end = -1;
INT_T media_len;
UINT_T time_now;
STATIC UINT_T curr_time_now;
STATIC UCHAR_T jpeg_eof = 1;
time_now = rtos_get_time();//uni_time_get_posix_ms();
if(jpeg_eof) {
jpeg_eof = 0;
curr_time_now = time_now;
}
if (pframe == NULL || user_id >=5 || user_id < 0) {
PR_ERR("param invalid");
return -1;
}
static fist_flag = 0;
if(fist_flag == 0) {
fist_flag = 1;
/*启动*/
tuya_video_dev_ctl(soc_video_device,CMD_STREAM_START,NULL);
}
tuya_video_dev_read(soc_video_device,&vb_pkg);
//bk_printf("is_last:%d,no:%d \r\n",vb_pkg->is_last,vb_pkg->pkg_no);
memset(test_tmp_buf, 0, sizeof(test_tmp_buf));
memcpy(test_tmp_buf, vb_pkg->pkg_bufs, vb_pkg->actual_len);
if(vb_pkg->is_last == 1) {
jpeg_eof = 1;
if(vb_pkg->actual_len <= 5) {
tuya_video_dev_ctl(soc_video_device, CMD_STREAM_DEQUEEU_BUFF,NULL);
return -1;
}
media_len = vb_pkg->actual_len - 5;
//tuya_video_dev_ctl(soc_video_device,CMD_STREAM_STOP,NULL);
//tuya_video_dev_ctl(soc_video_device,CMD_STREAM_STOP,NULL);
//fist_flag = 0;
} else {
media_len = vb_pkg->actual_len;
}
tuya_video_dev_ctl(soc_video_device, CMD_STREAM_DEQUEEU_BUFF,NULL);
pframe->buf = test_tmp_buf ;
pframe->len = media_len;
pframe->pts = curr_time_now;
pframe->is_eof = 1;
pinfo->last_pts[type] = curr_time_now;
pinfo->pos[type] = media_len;
if (pinfo->pos[type] > 0){
pinfo->pos[type] = 0;
}
return 0;
}
STATIC INT_T __get_frame_jpeg(INT_T user_id, RTSP_MEDIA_TYPE_E type, CHAR_T** buf, INT_T *plen, UINT64_T *pts)
{
return -1;
}
STATIC RTP_CODEC_E __get_codec(RTSP_MEDIA_TYPE_E type)
{
switch (type){
case RTSP_MEDIA_TYPE_VIDEO:
return RTP_CODEC_JPEG;
case RTSP_MEDIA_TYPE_AUDIO:
return RTP_CODEC_INVALID;
default:
return 0;
}
}
STATIC INT_T __get_sample_rate(RTSP_MEDIA_TYPE_E type)
{
switch (type){
case RTSP_MEDIA_TYPE_VIDEO:
return 90000;
case RTSP_MEDIA_TYPE_AUDIO:
return 8000;
default:
return 8000;
}
}
STATIC INT_T __start()
{
int i;
for(i = 0; i < 5; i++){
if (NULL == users[i]){
users[i] = Malloc(sizeof(user_info_s));
users[i]->last_pts[RTSP_MEDIA_TYPE_VIDEO] = 0;
users[i]->last_pts[RTSP_MEDIA_TYPE_AUDIO] = 0;
users[i]->pos[RTSP_MEDIA_TYPE_VIDEO] = 0;
users[i]->pos[RTSP_MEDIA_TYPE_AUDIO] = 0;
return i;
}
}
return -1;
}
STATIC INT_T __stop(INT_T user_id)
{
if (users[user_id]){
Free(users[user_id]);
users[user_id] = NULL;
}
return 0;
}
STATIC INT_T __get_name(CHAR_T* buf, INT_T* buf_len)
{
char name[] = "stream_0";
memcpy(buf, name, sizeof(name));
*buf_len = sizeof(name);
return 0;
}
/*自动识别sensor*/
#define AUTO_RECOGNIT_SENSOR 0
#if AUTO_RECOGNIT_SENSOR
static struct resource sensor_chip_sourece[]={
{
.name = (BYTE_T *)"i2c", /*未知的sensor 使用 第 0 个I2C去连接*/
.type = 6,
.port = 0,
},
};
/*构建视频系统的参数,支持同时存在多个video_device*/
static soc_camera_link tuya_camera_link[] =
{
{
.name = (BYTE_T *)"ty_video",
/*用于配置sensor_chip驱动所用到的硬件资源*/
.sensor_device = {
.name = (BYTE_T *)SENSOR_ANY, /*不指定哪一款sensor 去自动识别*/
.num_resource = 1, //资源数目
.resource = sensor_chip_sourece, //资源
},
.host_nr = 0,
},
};
#else
static struct resource sensor_chip_sourece[]={
{
.name = (BYTE_T *)"i2c",
.type = 6, /*对应 tuya_driver的 TUYA_DRV_I2C*/
.port = 0, /*使用第0个I2C*/
},
{
.name = (BYTE_T *)"i2c_addr",
.type = 0,
.port = 0x21, /*I2C地址0x21*/
},
};
/*构建视频系统的参数,支持同时存在多个video_device*/
static soc_camera_link tuya_camera_link[] =
{
{
.name = (BYTE_T *)"ty_video",
/*用于配置sensor_chip驱动所用到的硬件资源*/
.sensor_device = {
.name = (BYTE_T *)SENSOR_GC0308,
.num_resource = 2, //资源数目
.resource = sensor_chip_sourece, //资源
},
.host_nr = 0, /*SENSOR_GC0328 与 第0个host总线接着*/
},
};
#endif
video_mem_handler ty_video_mem_handler;
VOID user_rtsp_init()
{
INT_T ret;
rtsp_stream_src_t src = {0};
STREAM_SRC_ID id;
CHAR_T url[256] = {0};
int url_len = 256;
NW_IP_S ip_str;
wf_get_ip(WF_STATION, &ip_str);
PR_NOTICE("ip_str :%x, %s",ip_str.ip, ip_str.ip);
src.get_codec = __get_codec;
src.get_frame = __get_frame_jpeg;
src.get_frame_fragment = __get_frame_fragment_jpeg;
src.get_sample_rate = __get_sample_rate;
src.start = __start;
src.stop = __stop;
src.get_name = __get_name;
tuya_svc_lan_reg_rtsp_stream_src(src);
struct video_pix_format v_format;
struct vbq_config video_buff_q_config;
struct sensor_reg_config reg_cfg;
VIDEO_FPS_TYPE fps;
sw_i2c_pin_config(1, 0); //设置I2C引脚, 如果不调用,默认sda---1, scl----0
/*配置tuya_config.h文件,然后调用tuya_soc_camera_init构建系统,建立设备节点*/
OPERATE_RET tuya_soc_camera_init(soc_camera_link *tuya_camera_link, BYTE_T num, video_mem_handler *p_video_mem_handler);
ty_video_mem_handler.mem_malloc = tuya_hal_system_malloc;
ty_video_mem_handler.mem_free = tuya_hal_system_free;
tuya_soc_camera_init(tuya_camera_link,1,&ty_video_mem_handler);
soc_video_device = tuya_video_dev_find("ty_video");
if(soc_video_device == NULL){
PR_DEBUG("soc_video_device is null");
return OPRT_COM_ERROR;
}
/*初始化video_device*/
tuya_video_dev_open(soc_video_device);
/*设置格式*/
v_format.p_type = VGA_640_480;
v_format.pixelformat = V4L2_PIX_FMT_JPEG;
v_format.field = V4L2_FIELD_ANY;
v_format.colorspace = V4L2_COLORSPACE_JPEG;
ret = tuya_video_dev_ctl(soc_video_device,CMD_SET_FMT,&v_format);
if(ret != 0) {
PR_NOTICE("CMD_SET_FMT %d",ret);
}
/*设置缓存 12个缓存 一帧的大小为 1*1024*/
video_buff_q_config.video_pkg_buffer_len = 1 * 1024;
video_buff_q_config.video_pkg_num = 12;
ret = tuya_video_dev_ctl(soc_video_device,CMD_STREAM_SET_BUFF,&video_buff_q_config);
if(ret != 0) {
PR_NOTICE("CMD_STREAM_SET_BUFF: %d",ret);
}
}
目前平台下载的SDK都是未加密的,也就是需要配合未加密的芯片进行调试。如果编译出来的固件是未加密的,烧录到加密的开发板中,会导致芯片 boot 起不来,没有任何日志。
一般加密的板子配加密的固件,未加密的芯片配未加密的固件。
该问题原因是上传到平台上的固件或者平台上创建的固件标识名和芯片实际烧录的固件名称不一样。所以编译固件的时候一定要和平台创建的固件标识名以及固件版本对的上。
报这种错误的原因是板子的RF没有进行校准。目前BK的芯片都是需要进行RF校准的,校准完成后会再固定的Flash区写入RF标志位,上位机再授权的时候会读一下是否有RF标志位,如果没有就会授权失败。目前写入RF标志位有两种方式:
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈