本页内容 接收与去重支持的仪表素材终端接入Wi-Fi 控制与数据配置示例与回调签名素材结构与返回值接收与播放顺序广告数据通道接口
HARDWARE SDK / FreeRTOS

仪表广告

接入素材接收、持久回执、解码与一次播放。

本章使用 FreeRTOS 托管入口接入仪表媒体广告。SDK 处理已认证的接收流程;你提供存储、持久回执、解码与播放器。

接收与去重

每台设备在自己的持久存储中按广告 ID 去重,最多启动一次播放。完整接收、校验并持久保存回执后,APP 才记录接收成功。修改素材需新建广告 ID。

接收成功与播放成功不同。断电、离开当前认证会话、播放失败或播放中断均不触发重新发送或下次开机补播。广告只能在当前安全空闲会话中播放,仪表启动本身不会触发播放。

支持的仪表素材

用途 输入约束
仪表静态图 ≤ 5 MiB;实际宽高不能超过终端上报能力
仪表 GIF ≤ 10 MiB,≤ 10 秒,≤ 300 帧,累计解码像素 ≤ 1 亿,≤ 1920×1080
仪表 MP4 H.264,≤ 50 MiB,≤ 15 秒,≤ 30fps,≤ 1920×1080;转码时移除音轨

设备可声明更小的容量与尺寸上限;仅声明已实现解码和播放的格式。

终端接入

调用 moto_terminal_config_init 后设置 config.advertising。端口类型为 moto/advertising.h 中的 moto_ad_port。

配置 填写要求
max_size / max_chunk 素材/分片字节上限;协议最大 50 MiB / 1024 字节
mime_mask 用 MOTO_AD_MIME_MASK(...) 声明已实现的格式
max_width / max_height 解码原图的最大尺寸,不是缩放后的显示尺寸
8 个回调 全部实现;签名见下方示例,逐项返回值见“素材结构与返回值”

回调在 SDK 任务执行,必须有界、不可阻塞,不得重入 SDK。哈希建议随写入增量计算;finish 只有验证和持久记录完成才返回成功。素材可使用 RAM 或 Flash,但接收回执必须跨重启、素材清理与解绑保留。存储满时返回错误,不得清理回执后继续接收旧广告。

启用广告需要完整的广告端口与设备云认证。

Wi-Fi 控制与数据

控制复用 Wi-Fi 主业务 TLS 会话;可选 bulk TLS 通道传输素材,失败后可在主业务通道按确认偏移继续。data.wifi.purpose=2 表示广告,data.wifi.task 为广告 UUID;OTA 使用 purpose=1。

配置 config.bulk 的 TCP 收发端口后设置 wifi_advertising=true。收到 purpose 2 事件后复用主业务热点、核对 IP/DHCP 并准备该任务的 TCP 监听,再调用 moto_terminal_bulk_ready;SDK 生成随机 PSK 并管理 TLS,同一接口也支持 OTA purpose 1。bulk 通道只能写入当前授权广告,状态、完成和播放仍走 Wi-Fi 主业务通道。断开、停止或会话切换时清理任务 TCP 与播放资源,保留主业务热点和发现监听。

广告传输和播放与导航、视频、仪表样式切换、OTA 互斥。safe_idle 应根据车辆和显示状态决定是否允许播放,不中断进行中的导航。

配置示例与回调签名

广告素材配置使用 moto_terminal_config.advertising;moto_terminal_advertising(t, out) 用于生成 BLE 发现广播。

以下示例仅启用 PNG。board_ad_* 由板级驱动实现;容量和尺寸按实际硬件填写。在 moto_terminal_config_init 后、moto_terminal_create 前调用 configure_advertising。

#include <moto_freertos/terminal.h>
#include <moto/advertising.h>

int board_ad_lookup(void *u, const uint8_t ad[16],
                    uint8_t hash[32], bool *consumed);
int board_ad_begin(void *u, const moto_ad_media *media);
int board_ad_write(void *u, uint32_t offset,
                   const uint8_t *bytes, size_t size);
int board_ad_finish(void *u, const moto_ad_media *media);
int board_ad_consume(void *u, const uint8_t ad[16]);
int board_ad_play(void *u, const moto_ad_media *media);
void board_ad_stop(void *u);
bool board_ad_safe_idle(void *u);

void configure_advertising(moto_terminal_config *c, void *board_context) {
    c->advertising = (moto_ad_port){
        .user = board_context,
        .max_size = 256u * 1024u,
        .max_chunk = 512,
        .mime_mask = MOTO_AD_MIME_MASK(MOTO_AD_PNG),
        .max_width = 800,
        .max_height = 480,
        .lookup = board_ad_lookup,
        .begin = board_ad_begin,
        .write = board_ad_write,
        .finish = board_ad_finish,
        .consume = board_ad_consume,
        .play = board_ad_play,
        .stop = board_ad_stop,
        .safe_idle = board_ad_safe_idle,
    };
    c->wifi_advertising = false;
}

广告端口全零表示关闭。回调不完整或上限无效时,create 在 CONFIG 阶段返回 INVALID;可用 moto_ad_port_validate 预先检查端口。

advertising.user 独立于 port.user,创建时只复制指针,应存活到 SDK 销毁。media、ad、bytes 等输入只在回调期间有效;异步显示须复制元数据或字节,不能保存 SDK 借出的地址。

素材结构与返回值

moto_ad_media 为已经过协议校验的素材描述:

字段 类型与含义
ad[16] 广告 UUID 的 16 个原始字节,是持久去重键;不是以 \0 结尾的字符串
task[16] 本次传输任务 UUID 的原始字节;不能替代广告 ID 去重
sha256[32] 整个素材文件的 SHA-256;驱动须实际核验,不能只复制成“校验成功”
size uint32_t,完整文件字节数,范围 1 到端口 max_size
mime uint8_t:1 PNG、2 JPEG、3 GIF、4 H.264 MP4;mime_mask 使用 1u << mime
width / height uint16_t,素材原始像素尺寸,须在声明的解码上限内
duration_ms uint32_t,最长 15000 毫秒;0 时 SDK 使用 3000 毫秒停止计时;驱动可在素材自然结束时提前结束

端口 max_size 为 1–52428800 字节,max_chunk 为 1–1024 字节,宽高上限必须非零。上述协议上限不代表板端有相应内存、存储或解码性能。

回调 成功返回值 失败处理
lookup 0 不存在;1 已接收,并填入 hash、consumed 存储读取失败返回负错误码,不能按“不存在”处理
begin / write / finish / play MOTO_OK(0),本步实际完成后返回 返回负 moto_result,如 MOTO_IO、MOTO_FULL、MOTO_UNSUPPORTED
consume 0 本次首次消费;1 已消费 持久化失败返回负错误码
stop 无返回值;可重复调用,无活动素材时也应安全 清理接收/播放及其专用资源,始终保留持久回执
safe_idle true 当前允许展示;false 拒绝开始或停止正在播放的广告 使用车辆和显示系统的真实状态

这里的 lookup/consume 返回 1 是各自的业务结果,不是异步等待。广告回调不支持返回 MOTO_AGAIN 后再调用 moto_terminal_complete 完成;也没有 MOTO_EVENT_AD_* 供应用事件循环接收广告内容。回调在 SDK 任务同步执行,必须有界,不能等待另一任务长时间擦写 Flash 或等待整段播放结束。write 应增量计算哈希,finish 完成核验及原子持久提交;耗时存储平台需先评估这一同步接口是否满足目标时延。

接收与播放顺序

lookup → begin → write → finish → consume → play → stop

成功接收与实际曝光分别处理。finish 后的记录必须能由 lookup 立即读回同一哈希;consume 在 play 之前提交。即使播放失败或这两步之间掉电,也不清除消费记录,不在下次连接或启动时补播。

write 的 offset 为连续接收偏移。finish 后保留回执;再次查询已接收素材时只返回记录,不重新播放。

广告数据通道接口

注册 config.bulk.send/close 并启用 wifi_advertising。板级负责热点、IP/DHCP、TCP 监听和有序字节收发,SDK 管理 TLS 认证与数据接收。

接口 用法和返回值
moto_terminal_bulk_ready(t, &e, &ap) 保存的 purpose 1/2 WIFI 事件,AP/IP/TCP 监听实际就绪后提交;ap.port 为 TCP 端口。OK 仅表示入队,FULL 未提交,原参数重试
moto_terminal_bulk_connected(t, &e, &connection) TCP accept 后传监听器保存的原 WIFI 事件;OK 输出新的非零连接代次;BUSY 已有连接;STATE 尚未处理 ready 或事件已失效,关闭此次 socket
moto_terminal_bulk_receive(t, connection, bytes, size) 每次 1–512 字节 TLS 密文,OK 全部复制,FULL 完全未接收;保持顺序重试,不可跳过或重复
moto_terminal_bulk_disconnected(t, connection) TCP 断开,直接撤销该代次,不依赖接收队列空闲;旧代次 STATE
config.bulk.send(user, connection, bytes, size) 在 SDK 任务调用;复制实际接收字节并返回数量,0 暂忙,负数失败;不得阻塞
config.bulk.close(user, connection) 在 SDK 清理时异步、幂等地关闭指定 TCP 代次;不阻塞等待,不关闭后来建立的连接
moto_terminal_complete(t, &e, MOTO_FAILED, reason) 热点准备失败;不用此接口确认广告写入或播放

bulk.user 由板级持有至销毁;输入数据只借用至回调返回。bulk_rx_slots 默认 8,范围 1–128,仅启用 bulk 时分配。所有入口在任务调用,不用于 ISR。监听器必须复制原 WIFI 事件,不能用最新会话替换。

SDK 同时接受一条 bulk TCP 连接。断开、WIFI_STOP 或会话失效后清理连接;重新传输需使用新的 WIFI 事件完成就绪流程。异步失败通过错误事件和 get_status().error 查询。关闭终端前停止 BLE/TCP 输入和事件消费者。

从 bulk 回到主业务通道前,关闭 bulk 并查询设备已确认的接收偏移;不得同时向两条链路写入。