四博 AI 智能音箱 + ESPC3 Tasmota 计量通断器方案

四博 AI 智能音箱 + ESPC3 Tasmota 计量通断器方案

1. 方案定位

本方案面向:

复制代码
1. 智能插座 / 计量通断器
2. 智能空开 / 智能继电器
3. 电工照明类计量开关
4. 酒店 / 公寓 / 门店能耗管控
5. AI 音箱语音控制家电
6. 客户自有云平台 / 私有化系统接入

推荐架构:

复制代码
┌──────────────────────────────────────────────────────────────┐
│                    四博 AI 智能音箱 / AI 中控                 │
│  ESP32-S3 / AI-C3 / AI-02 + VB6824                           │
│  - 语音唤醒 / AI 对话                                         │
│  - AT+MCP 语义控制                                            │
│  - MQTT / HTTP / WebSocket                                    │
│  - 客户系统 API 接入                                          │
│  - 远程运维 / OTA / 日志                                      │
└───────────────┬──────────────────────────────────────────────┘
                │ MQTT / HTTP / 局域网 / 云平台
                ▼
┌──────────────────────────────────────────────────────────────┐
│              ESPC3 Tasmota 计量通断器节点                    │
│  ESPC3-02 / ESPC3-05 / ESPC3-12 / ESPC3-20 / ESPC3-32         │
│  - Relay 通断控制                                             │
│  - 电压 / 电流 / 功率 / 电量采集                              │
│  - Tasmota Web UI / MQTT / HTTP / Rule                        │
│  - 自动校准                                                   │
│  - 过载 / 欠压 / 过压 / 漏电外部保护扩展                      │
└──────────────────────────────────────────────────────────────┘

ESPC3 系列适合这类电工产品:四博资料中,ESPC3-02 是 15×17.3 mm 金手指封装,定位电工类应用;ESPC3-05 是 16×20 mm 照明类应用,内置高温 Flash;ESPC3-12 / 12E、ESPC3-20 / 20E 和 ESPC3-32 / 32E 则分别面向通用模组、兼容 ESP32-C3-WROOM-02 / WROOM-32 等形态。

Tasmota 侧建议使用 ESP32-C3 对应的 tasmota32c3- 固件;Tasmota 官方文档说明 ESP32-C3 是单核 Wi-Fi + Bluetooth 5 LE、RISC-V 架构芯片,并要求 C3 系列使用 tasmota32c3- binaries。(Tasmota)


2. 为什么不是"单颗 ESPC3 同时做 AI 音箱 + Tasmota 通断器"

ESPC3 可以做 Wi-Fi、BLE、Tasmota、继电器和计量,但它不是最适合做大模型音箱的主控。更推荐两种落地方式:

方案 A:双设备架构,最稳

复制代码
AI 音箱:
  ESP32-S3 / AI-C3 / AI-02
  负责语音、大模型、客户系统、MCP、远程管理

通断器:
  ESPC3 + Tasmota
  负责电工控制、计量、校准、MQTT / HTTP

优点是开发快、风险低、Tasmota 生态完整、客户系统容易接入。

方案 B:单设备通断器 AI 化,低成本

复制代码
ESPC3 + Tasmota
  仅做通断器、计量、远程管理

AI 能力放在云端或音箱端:
  语音指令 → 云端 / 音箱 → MQTT / HTTP → Tasmota

适合低成本量产,不在通断器本体上跑复杂语音模型。

方案 C:AI-C3 / AI-02 直接做小型语音开关

四博 AI-C3 / AI-02 是 ESP32-C3 + VB6824 组合,开发宝典中描述其支持 2.4 GHz Wi-Fi、Bluetooth 5 LE、400K SRAM、最大 16MB 片外 Flash,并且 VB6824 负责离线语音识别、降噪、更远距离唤醒、更低误唤醒、更强抗噪和免联网离线识别。

这个版本可以做"带语音的电工面板",但计量和 Tasmota 生态需要另外移植或改固件,不如 Tasmota 节点方案成熟。


3. 硬件方案

3.1 ESPC3 计量通断器节点

推荐硬件框图:

复制代码
AC L / N 输入
  │
  ├── AC-DC 隔离电源 3.3V
  │       ↓
  │    ESPC3 模组
  │       ├── Relay GPIO
  │       ├── Button GPIO
  │       ├── LED GPIO
  │       ├── Metering IC UART / CF / CF1 / SEL
  │       └── NTC / 温度 / 过零检测,可选
  │
  ├── 继电器 / 磁保持继电器
  │       ↓
  │    AC L 输出
  │
  └── 计量芯片
          ├── 电压采样
          ├── 电流采样:锰铜 / CT
          └── 功率 / 电量计算

推荐器件:

复制代码
主控:
  ESPC3-02:金手指电工类模组,适合替换 WB2S / ESP8685-WROOM-03 类产品
  ESPC3-05:照明类产品,白色耐高温,适合墙开 / 灯控
  ESPC3-12 / 20:通用型,适合插座 / 通断器
  ESPC3-32:IO 更宽裕,适合多路继电器 / 多功能面板

计量芯片:
  BL0937 / HLW8012:成本低,脉冲计量
  BL0942 / CSE7766:UART 计量,数据读取方便
  外部参考表:产测自动校准使用

继电器:
  普通继电器:成本低
  磁保持继电器:低功耗、适合长期通断器

安全:
  保险丝 / 压敏 / TVS / RC 吸收
  AC-DC 隔离电源
  继电器触点爬电距离
  强弱电隔离槽
  产测夹具隔离

强电设备调试必须使用隔离电源、封闭夹具和合规产测流程,不能在裸板带电时连接 USB 串口或触碰 GPIO。Tasmota 模板社区中也经常提醒部分计量开关的数字地可能与市电 L 端相连,GPIO 在工作时可能带电,这类设计必须按强电安全处理。(Tasmota 设备库)


3.2 AI 音箱 / 语音中控端

推荐两种:

高端版:ESP32-S3 + VB6824

复制代码
ESP32-S3R8 + 16M Flash + 8M PSRAM
VB6824 语音前端
麦克风 + 喇叭
1.3 / 1.54 / 2.0 寸屏幕
Wi-Fi + BLE BluFi
WebSocket AI 后端
MQTT / HTTP 控制 Tasmota

AI 硬件选型表中,AI-S3 相关方案采用 ESP32S3R8 + 16M Flash + VB6824,可选小智、豆包、ChatGPT 等主流大模型,并且全开源、支持二次开发,适合品牌 B 端客户或方案商客户。

中低成本版:AI-C3 / AI-02

复制代码
ESP32-C3 + VB6824
1.28 寸屏,可选
麦克风 + 喇叭
BluFi 配网
小智 / Coze 智能体
MQTT / HTTP 控制 Tasmota

开发宝典中 AI-C3 提到:AI-C3 集成 BluFi 蓝牙配网,可通过微信小程序给芯片联网,并支持"小智小助理"微信小程序切换多家 AI 服务;同时 AI-C3 已实现 Coze / 扣子智能体硬件化分身,适合接入客户自己的 Agent 流程。


4. Tasmota 固件与基础配置

Tasmota 官方文档说明,它是面向 ESP 设备的开源固件,支持 Web UI、OTA、规则 / 定时器自动化,并可通过 MQTT、HTTP、串口或 KNX 做本地控制。(Tasmota)

4.1 烧录建议

复制代码
芯片:ESP32-C3 / ESP8685
固件:tasmota32c3.factory.bin 或项目定制编译版本
烧录方式:
  1. Tasmota Web Installer
  2. esptool.py
  3. 量产烧录治具

Tasmota ESP32 文档说明,ESP32 设备可以通过 Web Installer 烧录,也可以使用 esptool.py write_flash 0x0 tasmota32.factory.bin 方式写入固件;ESP32-C3 使用 tasmota32c3- 系列 binaries。(Tasmota)

4.2 Tasmota 模板示例

GPIO 必须按最终 PCB 修改。下面只是示例结构:

复制代码
{
  "NAME": "SIBO-ESPC3-Meter-Relay",
  "GPIO": [
    1, 1, 1, 1,
    32,       // Relay1
    224,      // Button1
    288,      // Led1i
    1,
    2720,     // BL0937 CF / or metering input
    2656,     // BL0937 CF1
    2624,     // BL0937 SEL
    1,
    1, 1, 1, 1
  ],
  "FLAG": 0,
  "BASE": 1
}

实际项目中建议做三套模板:

复制代码
Template A:BL0937 / HLW8012 脉冲计量版
Template B:BL0942 UART 计量版
Template C:双路继电器 + 单路计量版

Tasmota 配置命令示例:

复制代码
Backlog Module 0; Template {"NAME":"SIBO-ESPC3-Meter-Relay","GPIO":[1,1,1,1,32,224,288,1,2720,2656,2624,1,1,1,1,1],"FLAG":0,"BASE":1}; Restart 1

然后配置 MQTT 与远程管理参数:

复制代码
Backlog MqttHost 192.168.1.10; MqttPort 1883; MqttUser sibo; MqttPassword 123456; Topic sibo_meter_001; TelePeriod 10; PowerRetain 1; SensorRetain 1

Tasmota 命令支持 MQTT、HTTP Web Request、WebUI Console 和串口;MQTT 命令主题形态为 cmnd/%topic%/<command>,HTTP 则可以通过 /cm?cmnd=Power%20On 这类 URL 执行命令。(GitHub)


5. 计量自动校准方案

5.1 先说明一个关键点

"自动校准"不能凭空完成。通断器必须拿到一个可信参考值,来源可以是:

复制代码
1. 产测夹具上的标准功率计;
2. 标准电压 + 标准阻性负载;
3. 上位机手动录入的功率 / 电压 / 电流;
4. Modbus / 串口参考表实时读取;
5. 已校准的基准通断器作为对照。

也就是说,自动校准的本质是:

复制代码
参考仪表读数
  ↓
校准服务器计算目标值
  ↓
通过 Tasmota HTTP / MQTT 下发 VoltageSet / PowerSet / CurrentSet
  ↓
读取 Tasmota Status 8 / Telemetry
  ↓
判断误差
  ↓
写入 SN、校准结果和批次记录

Tasmota 官方功率监控校准文档明确要求使用支持功率监控的 Tasmota 设备、正确模板、AC 校准万用表,以及功率因数尽量接近 1 的已知阻性负载;文档也提示不要使用开关电源类负载、LED 灯、电脑、电机等感性 / 容性负载做校准。(Tasmota)


5.2 Tasmota v14.2.0.1+ 推荐校准流程

官方文档中,Tasmota v14.2.0.1 及以上版本推荐流程是:

复制代码
1. VoltageSet <voltage>
2. PowerSet <watts>, <voltage>
3. 检查 Power Factor 接近 1.00

其中 VoltageSet <value> 用目标电压校准电压读数,PowerSet <value>,<voltage> 可同时按目标功率和电压校准功率与电流。(Tasmota)

示例:

复制代码
VoltRes 1
WattRes 2
VoltageSet 230.0
PowerSet 60.0,230.0
Status 8

Tasmota 命令文档也说明,PowerSet<value>,<voltage> 参数可同时校准功率和基于电压 / 负载计算出的电流,VoltageSet 用于把电压校准到目标 V 值,Status 8 用于显示用电量 / 传感信息。(GitHub)


5.3 旧版兼容流程

如果使用旧版本 Tasmota,流程如下:

复制代码
PowerSet 60.0
VoltageSet 230.0
CurrentSet 260.87

电流计算:

复制代码
I(mA) = 1000 × P(W) / V(V)

例如:
P = 60 W
V = 230 V

I = 1000 × 60 / 230 = 260.87 mA

官方旧流程文档也给出 PowerSetVoltageSetCurrentSet 的校准顺序,并说明 CurrentSet 使用毫安值。(Tasmota)


6. 产测自动校准流程

6.1 工装结构

复制代码
PC / 工控机
  ├── Python 校准程序
  ├── MQTT Broker
  ├── 标准功率计 Modbus / Serial
  └── 工装扫码枪

标准 AC 输入
  ↓
安全隔离 / 漏保 / 急停
  ↓
待测 ESPC3 Tasmota 通断器
  ↓
标准阻性负载,例如 60W / 100W 白炽灯或电阻箱

6.2 自动校准步骤

复制代码
1. 扫描设备 SN / MAC。
2. 工装连接待测设备 Wi-Fi 或设备连接工厂路由器。
3. 校准程序发现设备 IP。
4. 下发 Tasmota Template / MQTT / TelePeriod。
5. 打开 Relay。
6. 等待电压、功率、电流稳定。
7. 从标准功率计读取 V / W / A。
8. 下发 VoltageSet。
9. 下发 PowerSet W,V。
10. 读取 Status 8。
11. 判断误差:
    电压误差 ≤ ±1%
    功率误差 ≤ ±2%
    电流误差 ≤ ±2%
12. 写入生产数据库。
13. 打印合格标签。

7. Python 自动校准程序

下面代码通过 Tasmota HTTP API 对 ESPC3 通断器进行自动校准。Tasmota 官方文档给出的 HTTP 命令方式是 /cm?cmnd=Power%20On 这类格式,空格和特殊字符需要 URL 编码。(GitHub)

复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
ESPC3 Tasmota 计量通断器自动校准工具

功能:
1. 打开继电器;
2. 等待负载稳定;
3. 下发 VoltageSet;
4. 下发 PowerSet W,V;
5. 读取 Status 8;
6. 判断电压 / 功率 / 电流误差;
7. 输出校准结果。

依赖:
pip install requests
"""

import time
import json
import math
import argparse
import urllib.parse
from dataclasses import dataclass
from typing import Any, Dict, Optional

import requests


@dataclass
class RefMeterValue:
    voltage_v: float
    power_w: float
    current_a: Optional[float] = None


@dataclass
class TasmotaReading:
    voltage_v: float
    power_w: float
    current_a: float
    factor: Optional[float] = None


class TasmotaClient:
    def __init__(self, ip: str, username: str = "", password: str = "", timeout: float = 5.0):
        self.ip = ip
        self.username = username
        self.password = password
        self.timeout = timeout

    def command(self, cmd: str) -> Dict[str, Any]:
        params = {"cmnd": cmd}
        if self.username:
            params["user"] = self.username
            params["password"] = self.password

        url = f"http://{self.ip}/cm"
        resp = requests.get(url, params=params, timeout=self.timeout)
        resp.raise_for_status()
        return resp.json()

    def backlog(self, commands: list[str]) -> Dict[str, Any]:
        return self.command("Backlog " + "; ".join(commands))

    def power_on(self):
        return self.command("Power On")

    def power_off(self):
        return self.command("Power Off")

    def status8(self) -> Dict[str, Any]:
        return self.command("Status 8")

    def voltage_set(self, voltage: float):
        return self.command(f"VoltageSet {voltage:.2f}")

    def power_set_new(self, power: float, voltage: float):
        """
        Tasmota v14.2.0.1+ 推荐方式:
        PowerSet <watts>,<voltage>
        """
        return self.command(f"PowerSet {power:.2f},{voltage:.2f}")

    def current_set_old(self, current_ma: float):
        """
        旧版兼容方式:
        CurrentSet <mA>
        """
        return self.command(f"CurrentSet {current_ma:.2f}")


def extract_energy_reading(status_json: Dict[str, Any]) -> TasmotaReading:
    """
    常见 Status 8 返回里会有 StatusSNS -> ENERGY。
    不同 Tasmota 版本 / 计量驱动字段略有差异,此处做容错解析。
    """
    energy = None

    if "StatusSNS" in status_json:
        sns = status_json["StatusSNS"]
        if "ENERGY" in sns:
            energy = sns["ENERGY"]

    if energy is None and "ENERGY" in status_json:
        energy = status_json["ENERGY"]

    if energy is None:
        raise RuntimeError(f"Cannot find ENERGY in response: {json.dumps(status_json, ensure_ascii=False)}")

    voltage = float(energy.get("Voltage", 0))
    power = float(energy.get("Power", 0))
    current = float(energy.get("Current", 0))
    factor = energy.get("Factor")

    return TasmotaReading(
        voltage_v=voltage,
        power_w=power,
        current_a=current,
        factor=float(factor) if factor is not None else None,
    )


def percent_error(actual: float, target: float) -> float:
    if target == 0:
        return 999.0
    return abs(actual - target) * 100.0 / abs(target)


def wait_stable_reading(client: TasmotaClient, samples: int = 5, interval: float = 1.0) -> TasmotaReading:
    readings: list[TasmotaReading] = []

    for _ in range(samples):
        status = client.status8()
        reading = extract_energy_reading(status)
        readings.append(reading)
        time.sleep(interval)

    avg_voltage = sum(r.voltage_v for r in readings) / len(readings)
    avg_power = sum(r.power_w for r in readings) / len(readings)
    avg_current = sum(r.current_a for r in readings) / len(readings)

    factor_values = [r.factor for r in readings if r.factor is not None]
    avg_factor = sum(factor_values) / len(factor_values) if factor_values else None

    return TasmotaReading(avg_voltage, avg_power, avg_current, avg_factor)


def calibrate(
    client: TasmotaClient,
    ref: RefMeterValue,
    use_new_method: bool = True,
    voltage_tolerance_pct: float = 1.0,
    power_tolerance_pct: float = 2.0,
    current_tolerance_pct: float = 2.0,
) -> bool:
    print("[1] Configure resolution and telemetry")
    client.backlog([
        "VoltRes 2",
        "WattRes 2",
        "TelePeriod 10",
    ])

    print("[2] Turn relay on")
    client.power_on()
    time.sleep(5)

    print("[3] Before calibration")
    before = wait_stable_reading(client)
    print(before)

    print("[4] Apply calibration")
    client.voltage_set(ref.voltage_v)
    time.sleep(2)

    if use_new_method:
        client.power_set_new(ref.power_w, ref.voltage_v)
    else:
        current_ma = 1000.0 * ref.power_w / ref.voltage_v
        client.command(f"PowerSet {ref.power_w:.2f}")
        time.sleep(1)
        client.current_set_old(current_ma)

    time.sleep(5)

    print("[5] After calibration")
    after = wait_stable_reading(client)
    print(after)

    target_current_a = ref.current_a if ref.current_a is not None else ref.power_w / ref.voltage_v

    v_err = percent_error(after.voltage_v, ref.voltage_v)
    p_err = percent_error(after.power_w, ref.power_w)
    i_err = percent_error(after.current_a, target_current_a)

    print(f"[6] Error voltage={v_err:.2f}% power={p_err:.2f}% current={i_err:.2f}% factor={after.factor}")

    ok = (
        v_err <= voltage_tolerance_pct and
        p_err <= power_tolerance_pct and
        i_err <= current_tolerance_pct
    )

    if after.factor is not None and after.factor < 0.95:
        print("[WARN] Power factor is not close to 1.00. Check load type and wiring.")

    print("[7] Calibration result:", "PASS" if ok else "FAIL")
    return ok


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--ip", required=True, help="Tasmota device IP")
    parser.add_argument("--voltage", type=float, required=True, help="Reference voltage, V")
    parser.add_argument("--power", type=float, required=True, help="Reference power, W")
    parser.add_argument("--current", type=float, default=None, help="Reference current, A")
    parser.add_argument("--user", default="")
    parser.add_argument("--password", default="")
    parser.add_argument("--old", action="store_true", help="Use old PowerSet + VoltageSet + CurrentSet flow")
    args = parser.parse_args()

    client = TasmotaClient(args.ip, args.user, args.password)

    ref = RefMeterValue(
        voltage_v=args.voltage,
        power_w=args.power,
        current_a=args.current,
    )

    ok = calibrate(client, ref, use_new_method=not args.old)
    raise SystemExit(0 if ok else 2)


if __name__ == "__main__":
    main()

运行示例:

复制代码
python calibrate_tasmota.py --ip 192.168.1.88 --voltage 230.4 --power 60.2

旧版 Tasmota 兼容:

复制代码
python calibrate_tasmota.py --ip 192.168.1.88 --voltage 230.4 --power 60.2 --old

8. AI 音箱到 Tasmota 的控制协议

8.1 语音指令设计

用户可以这样说:

复制代码
"小博,打开客厅插座"
"小博,关闭热水器通断器"
"小博,查看空调插座功率"
"小博,今天用了多少电"
"小博,把这个插座校准到 230 伏、60 瓦"
"小博,超过 1500 瓦自动断电"
"小博,把门店所有插座断开"

AI 音箱 / AI 中控需要把自然语言解析成标准动作:

复制代码
{
  "device_id": "sibo_meter_001",
  "action": "power_on"
}

{
  "device_id": "sibo_meter_001",
  "action": "calibrate",
  "voltage": 230.0,
  "power": 60.0
}

{
  "device_id": "sibo_meter_001",
  "action": "set_power_limit",
  "power_high": 1500
}

8.2 使用开发宝典 AT+MCP 做语义到控制

开发宝典中的 AT+MCP 非常适合这类"AI 音箱控制通断器"的场景。文档说明,AT+MCP 使用标准 UART,默认 115200, 8N1,所有指令以回车换行结束,合法指令统一回复 OK;通过 AT+ADDMCP 可以把"人话"映射为 MCU 可执行的二进制控制帧;模块还会上报开机、配网、联网、监听、说话、升级、激活等状态。

协议帧格式:

复制代码
AI 模组 -> MCU:

0x55 0xAA LEN CMD DATA... 0xAA 0x55

开发宝典还说明,当 MCU 收到 55 AA 01 FC AA 55 时,需要重启 AI 模组并重新发送 MCP 映射;AT+RESTORE 可清空 Wi-Fi,AT+CONNECT 可一键回连或进入配网。


9. AI 音箱端 MCP 注册代码

下面代码用于音箱端或中控端:启动后向 AI 模组注册"控制 Tasmota 通断器"的 MCP 能力。

复制代码
/*
 * ai_mcp_tasmota_register.c
 *
 * 功能:
 * 1. 注册 Tasmota 通断器控制能力;
 * 2. 将语音指令映射为二进制帧;
 * 3. 后续由 mcp_rx_task 解析帧并转 MQTT / HTTP。
 */

static void mcp_send_line(const char *line)
{
    uart_write_bytes(AI_UART, line, strlen(line));
    uart_write_bytes(AI_UART, "\r\n", 2);
}

static void register_tasmota_mcp_tools(void)
{
    mcp_send_line("AT");
    mcp_send_line("AT+WIFICFG=0");
    mcp_send_line("AT+CONNECT");

    /*
     * 用户:"打开客厅插座"
     * AI -> MCU: 55 AA 03 F1 device_id 01 AA 55
     */
    mcp_send_line(
        "AT+ADDMCP=1,tasmota_power_control,控制Tasmota通断器开关,F1,2,device_id,power"
    );

    /*
     * 用户:"查看插座功率"
     * AI -> MCU: 55 AA 02 F2 device_id AA 55
     */
    mcp_send_line(
        "AT+ADDMCP=1,tasmota_query_energy,查询Tasmota通断器电压电流功率,F2,1,device_id"
    );

    /*
     * 用户:"把插座校准到230伏60瓦"
     * AI -> MCU: 55 AA 05 F3 device_id voltage_hi voltage_lo power_hi power_lo AA 55
     * 为节省字节,这里 voltage/power 可以按 0.1 单位传输。
     */
    mcp_send_line(
        "AT+ADDMCP=1,tasmota_calibrate,校准Tasmota通断器计量,F3,5,device_id,voltage_hi,voltage_lo,power_hi,power_lo"
    );

    /*
     * 用户:"超过1500瓦自动断电"
     */
    mcp_send_line(
        "AT+ADDMCP=1,tasmota_set_power_limit,设置通断器过载断电阈值,F4,3,device_id,power_hi,power_lo"
    );

    /*
     * 用户:"同步通断器状态到客户系统"
     */
    mcp_send_line(
        "AT+ADDMCP=1,customer_sync_meter,同步计量数据到客户系统,F5,1,device_id"
    );
}

10. AI 音箱端:MCP 帧转 MQTT 控制 Tasmota

复制代码
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "esp_log.h"
#include "mqtt_client.h"

#define TAG "MCP_TASMOTA"

#define CMD_TASMOTA_POWER        0xF1
#define CMD_TASMOTA_QUERY        0xF2
#define CMD_TASMOTA_CALIBRATE    0xF3
#define CMD_TASMOTA_LIMIT        0xF4
#define CMD_CUSTOMER_SYNC        0xF5
#define CMD_STATUS               0xFF
#define CMD_RECOVER              0xFC

static esp_mqtt_client_handle_t s_mqtt = NULL;

static const char *device_id_to_topic(uint8_t device_id)
{
    /*
     * 量产时建议从 NVS / 云端配置中读取:
     * device_id -> Tasmota Topic 映射。
     */
    switch (device_id) {
    case 1:
        return "sibo_meter_001";
    case 2:
        return "sibo_meter_002";
    case 3:
        return "sibo_meter_003";
    default:
        return "sibo_meter_unknown";
    }
}

static void mqtt_publish_cmd(const char *topic, const char *cmd)
{
    char mqtt_topic[128];

    snprintf(mqtt_topic, sizeof(mqtt_topic), "cmnd/%s/%s", topic, cmd);

    /*
     * 对于 Power 这类命令,Tasmota 标准格式是:
     * topic: cmnd/<Topic>/Power
     * payload: On / Off / Toggle
     *
     * 为了通用,这里支持 cmd 里不带 payload 的模式;
     * 更严谨的实现可拆 command 和 payload。
     */
}

static void tasmota_mqtt_command(const char *device_topic, const char *command, const char *payload)
{
    char topic[128];

    snprintf(topic, sizeof(topic), "cmnd/%s/%s", device_topic, command);

    ESP_LOGI(TAG, "MQTT publish topic=%s payload=%s", topic, payload);

    esp_mqtt_client_publish(
        s_mqtt,
        topic,
        payload,
        0,
        1,
        0
    );
}

static void tasmota_power_control(uint8_t device_id, uint8_t power)
{
    const char *topic = device_id_to_topic(device_id);

    if (power) {
        tasmota_mqtt_command(topic, "Power", "On");
    } else {
        tasmota_mqtt_command(topic, "Power", "Off");
    }
}

static void tasmota_query_energy(uint8_t device_id)
{
    const char *topic = device_id_to_topic(device_id);

    /*
     * Status 8 用于查询功率 / 传感信息。
     */
    tasmota_mqtt_command(topic, "Status", "8");
}

static void tasmota_calibrate(uint8_t device_id, uint16_t voltage_x10, uint16_t power_x10)
{
    const char *topic = device_id_to_topic(device_id);

    float voltage = voltage_x10 / 10.0f;
    float power = power_x10 / 10.0f;

    char payload[64];

    /*
     * 新版本 Tasmota 推荐:
     * VoltageSet <voltage>
     * PowerSet <power>,<voltage>
     */
    snprintf(payload, sizeof(payload), "%.1f", voltage);
    tasmota_mqtt_command(topic, "VoltageSet", payload);

    snprintf(payload, sizeof(payload), "%.1f,%.1f", power, voltage);
    tasmota_mqtt_command(topic, "PowerSet", payload);
}

static void tasmota_set_power_limit(uint8_t device_id, uint16_t power_w)
{
    const char *topic = device_id_to_topic(device_id);

    char payload[32];

    snprintf(payload, sizeof(payload), "%u", power_w);

    /*
     * PowerHigh 可设置功率高阈值。
     * 后续配合 Rule 可实现超功率断电。
     */
    tasmota_mqtt_command(topic, "PowerHigh", payload);
}

static void customer_sync_meter(uint8_t device_id)
{
    /*
     * TODO:
     * 1. 从缓存的 Tasmota telemetry 中取电压 / 电流 / 功率 / 电量;
     * 2. HTTP / MQTT 上报到客户平台;
     * 3. 支持客户 project_id / product_id / device_id。
     */
    ESP_LOGI(TAG, "sync meter data to customer system, device_id=%u", device_id);
}

static void handle_mcp_frame(uint8_t cmd, const uint8_t *data, uint8_t len)
{
    switch (cmd) {
    case CMD_TASMOTA_POWER:
        if (len >= 2) {
            tasmota_power_control(data[0], data[1]);
        }
        break;

    case CMD_TASMOTA_QUERY:
        if (len >= 1) {
            tasmota_query_energy(data[0]);
        }
        break;

    case CMD_TASMOTA_CALIBRATE:
        if (len >= 5) {
            uint8_t device_id = data[0];
            uint16_t voltage_x10 = ((uint16_t)data[1] << 8) | data[2];
            uint16_t power_x10 = ((uint16_t)data[3] << 8) | data[4];

            tasmota_calibrate(device_id, voltage_x10, power_x10);
        }
        break;

    case CMD_TASMOTA_LIMIT:
        if (len >= 3) {
            uint8_t device_id = data[0];
            uint16_t power_w = ((uint16_t)data[1] << 8) | data[2];

            tasmota_set_power_limit(device_id, power_w);
        }
        break;

    case CMD_CUSTOMER_SYNC:
        if (len >= 1) {
            customer_sync_meter(data[0]);
        }
        break;

    default:
        ESP_LOGW(TAG, "unknown MCP cmd=0x%02X len=%u", cmd, len);
        break;
    }
}

11. Tasmota Rule:过载自动断电

Tasmota 可以直接在节点侧做本地保护,不依赖云端。

示例逻辑:

复制代码
当功率超过 1500W:
  1. 关闭继电器;
  2. 发布过载事件;
  3. 点亮告警灯;
  4. 需要人工或远程复位。

Tasmota Console 示例:

复制代码
Backlog PowerHigh 1500; Rule1 ON ENERGY#Power>1500 DO Backlog Power Off; Publish stat/%topic%/ALARM OVER_POWER ENDON; Rule1 1

也可以用 AI 音箱设置:

复制代码
"小博,把热水器通断器过载保护设为 1800 瓦"

音箱端转成:

复制代码
cmnd/sibo_meter_001/PowerHigh 1800
cmnd/sibo_meter_001/Rule1 ON ENERGY#Power>1800 DO Backlog Power Off; Publish stat/%topic%/ALARM OVER_POWER ENDON
cmnd/sibo_meter_001/Rule1 1

12. 远程管理平台设计

12.1 设备 Topic 设计

复制代码
Tasmota 原生:
  cmnd/sibo_meter_001/Power
  cmnd/sibo_meter_001/Status
  stat/sibo_meter_001/RESULT
  tele/sibo_meter_001/SENSOR

客户平台统一:
  customer/{project_id}/{device_id}/cmd
  customer/{project_id}/{device_id}/state
  customer/{project_id}/{device_id}/event
  customer/{project_id}/{device_id}/ota

12.2 远程管理功能

复制代码
1. 设备绑定:
   SN、MAC、Tasmota Topic、客户 device_id 绑定。

2. 实时状态:
   开关状态、电压、电流、功率、今日电量、总电量、RSSI、固件版本。

3. 远程控制:
   开 / 关 / 定时 / 倒计时 / 场景联动。

4. 远程校准:
   下发参考电压、参考功率、参考电流,执行自动校准。

5. 阈值保护:
   过压、欠压、过载、过温、过流。

6. OTA:
   Tasmota 固件 OTA;
   AI 音箱固件 OTA;
   配置模板 OTA。

7. 日志:
   校准记录、继电器动作、异常断电、客户系统调用记录。

Tasmota 原生支持 MQTT、Web UI、HTTP、serial 等控制方式,因此客户平台既可以直接管理 Tasmota,也可以通过四博 AI 音箱中控做一层网关和权限管理。(Tasmota)


13. 客户系统 HTTP 接入示例

音箱端收到 Tasmota 的 telemetry 后,可以上报客户平台:

复制代码
#include "esp_http_client.h"
#include "esp_log.h"

#define TAG "CUSTOMER_API"

typedef struct {
    const char *device_id;
    float voltage;
    float current;
    float power;
    float energy_today;
    float energy_total;
    int relay_on;
} meter_state_t;

esp_err_t customer_upload_meter_state(const meter_state_t *s)
{
    char body[256];

    snprintf(body, sizeof(body),
        "{"
        "\"device_id\":\"%s\","
        "\"voltage\":%.2f,"
        "\"current\":%.3f,"
        "\"power\":%.2f,"
        "\"energy_today\":%.3f,"
        "\"energy_total\":%.3f,"
        "\"relay_on\":%d"
        "}",
        s->device_id,
        s->voltage,
        s->current,
        s->power,
        s->energy_today,
        s->energy_total,
        s->relay_on
    );

    esp_http_client_config_t config = {
        .url = "https://customer.example.com/api/v1/meter/state",
        .method = HTTP_METHOD_POST,
        .timeout_ms = 8000,
    };

    esp_http_client_handle_t client = esp_http_client_init(&config);

    esp_http_client_set_header(client, "Content-Type", "application/json");
    esp_http_client_set_header(client, "Authorization", "Bearer ${token}");
    esp_http_client_set_post_field(client, body, strlen(body));

    esp_err_t err = esp_http_client_perform(client);
    int status = esp_http_client_get_status_code(client);

    ESP_LOGI(TAG, "upload status=%d err=%s", status, esp_err_to_name(err));

    esp_http_client_cleanup(client);
    return err;
}

14. 后端服务建议

结合开发宝典的小智后端思路,可以部署一个统一后端:

复制代码
Nginx / Caddy
  ├── AI WebSocket Gateway
  ├── OTA Server
  ├── MQTT Broker
  ├── Tasmota Device Manager
  ├── Calibration Service
  ├── Customer API Gateway
  ├── Rule Engine
  └── Web Admin

开发宝典中说明,设备默认可连接官方小智服务,也可以运行开源后端服务,把设备连接到自己的后端;后端包含 OTA 接口和 WebSocket 接口,并可配置 LLM、TTS、人设等参数。

推荐后端数据表:

复制代码
CREATE TABLE device_meter (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    device_id VARCHAR(64) NOT NULL,
    tasmota_topic VARCHAR(64) NOT NULL,
    mac VARCHAR(32),
    sn VARCHAR(64),
    product_id VARCHAR(64),
    project_id VARCHAR(64),
    firmware_version VARCHAR(32),
    calibration_version INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE meter_calibration_log (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    device_id VARCHAR(64) NOT NULL,
    ref_voltage DECIMAL(10,3),
    ref_power DECIMAL(10,3),
    ref_current DECIMAL(10,4),
    read_voltage DECIMAL(10,3),
    read_power DECIMAL(10,3),
    read_current DECIMAL(10,4),
    voltage_error DECIMAL(8,3),
    power_error DECIMAL(8,3),
    current_error DECIMAL(8,3),
    result VARCHAR(16),
    operator_id VARCHAR(64),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

15. 量产测试方案

15.1 工厂测试项

复制代码
ESPC3:
  - Flash 读写
  - Wi-Fi MAC
  - RSSI
  - Tasmota 固件版本
  - Template 是否正确
  - MQTT 是否在线

继电器:
  - 开 / 关动作
  - 触点粘连检测
  - 上电默认状态
  - 断电恢复状态

计量:
  - 空载功率
  - 标准电压
  - 标准负载功率
  - 电流读数
  - 功率因数
  - 电量累计

校准:
  - VoltageSet
  - PowerSet W,V
  - Status 8 验证
  - 误差判定
  - 结果写数据库

安全:
  - 高压测试
  - 绝缘测试
  - 老化测试
  - 过载断电测试

AI 音箱联动:
  - 语音开关
  - 查询功率
  - 设置过载阈值
  - 远程校准
  - 客户系统上报

15.2 产测串口 / 平台命令

复制代码
FACTORY_SCAN
FACTORY_BIND_SN=SBOMETER20260001
FACTORY_SET_TEMPLATE
FACTORY_MQTT_CONFIG
FACTORY_RELAY_ON
FACTORY_RELAY_OFF
FACTORY_READ_STATUS8
FACTORY_CALIBRATE=230.0,60.0
FACTORY_CHECK_ERROR
FACTORY_SET_POWER_LIMIT=1500
FACTORY_UPLOAD_RESULT
FACTORY_PASS
FACTORY_FAIL

16. 推荐版本

16.1 ESPC3 基础计量通断器

复制代码
ESPC3-02 / ESPC3-05
1 路继电器
BL0937 / HLW8012
Tasmota32-C3
MQTT / HTTP
手动校准 / 产测自动校准

适合低成本智能插座、墙开、灯控。

16.2 ESPC3 高精度计量通断器

复制代码
ESPC3-12 / ESPC3-20
1 路继电器或磁保持继电器
BL0942 / CSE7766 UART 计量
Tasmota32-C3 定制固件
远程自动校准
过载 / 过压 / 欠压保护
客户云平台

适合能耗管理、酒店、公寓、门店。

16.3 AI 音箱 + 通断器套装

复制代码
AI 音箱:
  ESP32-S3 + VB6824
  屏幕 + 麦克风 + 喇叭
  小智 / Coze / 客户 LLM
  AT+MCP

通断器:
  ESPC3 + Tasmota
  Relay + 计量芯片
  MQTT / HTTP

能力:
  语音控制
  语音查询功率
  远程管理
  自动校准
  客户系统接入

17. 方案总结

这套方案建议采用 "四博 AI 智能音箱作为语音与客户系统入口,ESPC3 Tasmota 计量通断器作为电工执行节点" 的架构。ESPC3 负责继电器通断、功率计量、Tasmota Web UI / MQTT / HTTP、自动校准和本地保护;AI 音箱端基于四博 AI 开发宝典的 DOIT_AI、AI-C3 / AI-S3、VB6824、BluFi 和 AT+MCP 能力,把"打开插座、查询功率、校准计量、设置过载保护"等自然语言转成 Tasmota 命令;客户平台通过 MQTT / HTTP 接收数据和下发策略,实现远程管理、批量 OTA、产测自动校准和私有化系统集成。

关键落地点是:自动校准必须有参考仪表或标准负载,Tasmota 负责执行 VoltageSet / PowerSet / CurrentSet,四博 AI 音箱或客户云平台负责编排流程、记录校准结果和接入业务系统。

相关推荐
dllmayday1 小时前
Milvus在LangChain中使用方法
人工智能·ai·langchain·milvus
xiaoduo AI1 小时前
客服机器人问题解决率怎么统计?Agent系统自动判断是否解决,比人工回访准?
大数据·人工智能·机器人
AI周红伟1 小时前
周红伟:GPT-Image-2深度解析:从技术原理到实战教程,为什么它能让整个AI圈炸锅?
人工智能·gpt·深度学习·机器学习·语言模型·openclaw
hecgaoyuan1 小时前
浅谈个人在人工智能方面的应用情况
人工智能
*Lisen2 小时前
从零手写 FlashAttention(PyTorch实现 + 原理推导)
人工智能·pytorch·python
字节跳动数据库2 小时前
数据孤岛难打通、权限怕失控?DBW 助“小龙虾”落地最后一公里
人工智能
俊哥V2 小时前
AI一周事件 · 2026-04-22 至 2026-04-28
人工智能·ai
Black蜡笔小新2 小时前
AI大模型训练工作站/私有化本地化AI模型训推工作站DLTM为农业生产装上AI“慧眼”
人工智能·ai大模型
小星AI2 小时前
Claude Code Agent SDK 从入门到精通,一步到位
人工智能·agent·cursor