海欧的博客

  • Navidrome
  1. 首页
  2. 嵌入式
  3. 正文

ESP32部署开源物联网平台ThingsBoard

2026年4月13日 88点热度 0人点赞 0条评论

ESP32部署开源物联网平台ThingsBoard

image-20260414000007047

1. 前言

之前使用阿里云物联网平台搭建的电能表温表IOT采集并前端展示的项目,由于阿里云IOT Studio体验版到期了,前端展示被关闭了。继续使用要收取一笔高昂费用,我自然消费不起,那么现在我的目标就是使用开源物联网平台来代替阿里云物联网,这意味着你要自己部署物联网MQTT消息服务器,不过这个也不难,搞一台linux机器就行,可以是自己购买公网服务器,也可以家里的内网x86小主机。好吧,开始开干!

image-20260412000008062
image-202604120000058075

PS: 阿里云物联网前端应用无法展示

2. 部署MQTT服务器

2.1 目前有什么选择

EMQX自带Web后台,Mosquitto没有Web后台

EMQX多节点集群是需要购买商业许可证的,但是单节点部署是免费的,高性能MQTT服务器

ThingsBoard是自带mqtt服务器,完善的可视化以及设备管理,内置低配MQTT服务器

2.2 部署EMQX

version: '3.8'
services:
  emqx:  
    image: emqx/emqx:latest
    container_name: emqx
    restart: unless-stopped
    hostname: emqx-node
    mem_limit: 1024M
    environment:
      - EMQX_NODE_NAME=emqx@emqx-node
      - EMQX_LOG__FILE__DEFAULT__ENABLE=true
      # 禁用遥测,可选
      - EMQX_TELEMETRY__ENABLE=false
      - TZ=Asia/Shanghai
    volumes:
      # 数据持久化
      - ./data:/opt/emqx/data
      - ./log:/opt/emqx/log
    ports:
      # MQTT 标准端口
      - "1883:1883"
      # WebSocket 端口
      #- "8083:8083"
      # WebSocket 安全端口
      #- "8084:8084"
      # MQTT over TLS
      #- "8883:8883"
      # Dashboard Web 管理后台
      - "18083:18083"
    healthcheck:
      test: ["CMD", "/opt/emqx/bin/emqx", "ctl", "status"]
      interval: 10s
      timeout: 10s
      retries: 5
image-20260412000058335
image-20260414000013915

PS: 登录EMQX之后的界面

2.3 EMQX与阿里云物联网区别

阿里云物联网平台解决了什么问题:一站式解决方案,提供了从设备连接到应用开发的全套服务,除了消息中转,还集成了设备管理、物模型、规则引擎、数据存储、安全认证等功能,并连接阿里云庞大的生态服务。开箱即用,需要付费。

EMQX解决了什么问题:MQTT消息服务器,是一个高性能、开源的 MQTT 消息服务器。核心功能是完成接收和分发来自设备的消息。但它本身不提供设备管理、数据存储或应用开发等一体化能力。但是可以与外部数据库、可视化平台等系统的完美集成。

使用标准的mqtt协议连接emqx: 修改MQTT服务器地址和端口

使用开源数据可视化工具做前端展示:ThingsBoard ,是开源物联网平台,为IoT方案提供设备管理、数据采集、处理与可视化,内置轻量级

2.4 小结

因为目前家里机器性能以及资源有限,内存条才4G, 如果要跑EMQX作为高性能MQTT Broker,加上ThingsBoard作为可视化应用服务,根本不可能,容器服务会把最后的系统内存都占满,系统开机就宕机。最后选择ThingsBoard部署这个项目,这个已经完全够用,因为设备连接数很少,用不上高性能MQTT Broker,低配的内置MQTT Broker也可以用。当前日后如果真的需要EMQX,那么编写规则引擎让接入Grafana可视化工具,这就非常完美。不废话,下一步部署ThingsBoard!

3. 部署ThingsBoard

3.1 部署方式

不采用微服务方式部署,不需要分别部署多个组件,常用单体部署方式更加省内存,所有核心功能都打包在同一个Java虚拟机进程中运行

使用thingsboard/tb-postgres:latest镜像,内置PostgreSQL数据库

默认账号:

Systen Administrator: [email protected] / sysadmin

Tenant Administrator: [email protected]/ tenant

Customer User: [email protected]/ customer

租户管理员可以创建设备,创建仪表板

系统管理员可以创建租户,客户用户只能查看

部署编排:

version: '3.8'
services:
  thingsboard:
    image: thingsboard/tb-postgres:latest
    container_name: thingsboard
    restart: unless-stopped
    mem_limit: 1536M  # 限制最大内存 1GB+512M+512M 小于它会宕机
    ports:
      - "18080:9090"      # Web UI 端口
      - "11883:1883"      # MQTT 设备接入端口
    environment:
      TB_QUEUE_TYPE: in-memory  # 使用内存队列,降低资源占用,指定消息队列类型
    volumes:
      - ./tb-data:/data
      - ./tb-logs:/var/log/thingsboard
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9090/api/noauth/health"]
      interval: 30s
      timeout: 10s
      retries: 3

3.2 容器权限问题

解决容器权限的问题:

日志报错如下:

initdb: error: could not create directory "/data/db": Permission denied

授予操作文件权限:

docker run --rm thingsboard/tb-postgres cat /etc/passwd  #查看容器所有用户
#输出: thingsboard:x:799:799:Thingsboard application,,,:/home/thingsboard:/usr/sbin/nologin
#ps: 用户名:密码占位符:用户ID(UID):组ID(GID):用户描述:家目录:登录Shell  
#目前确定了UID是799

#授予权限
sudo chown -R 799:799 ./tb-data
sudo chown -R 799:799 ./tb-logs

日志显示如下:

Installation finished successfully!

Starting ThingsBoard ...

表示安装成功,服务启动了,经过测试1024M内存会导致访问被Killed ,增加到1024+256M+256M 可以运行

3.3 创建设备

image-20260413000000910

登录租户,创建设备连接参数如下:

image-20260413000056067

使用树莓派完成连通性测试:

image-20260413000039306

之前阿里云的MQTT链接参数:

client ID 是系统自动拼接的

user name 是系统自动拼接的

password 是自动随机生成的

image-20260413000057149

PS: 之前阿里云MQTT的连接参数

在thingsboard中,client ID、 user name 、 password 这些MQTT基本连接参数可以自定义或者自动生成

3.4 数据类型

thingsboard中的数据类型:

属性(Attributes):用于描述设备相对静态的特征。比如设备固件版本、运行模式等。在平台里只保留最新的一个值。

遥测(Telemetry):用于上报随时间连续变化的测量数据,比如温度、湿度、电压、电流等。每次上报都会在数据库里生成一条带有时间戳的新记录,方便后续做历史趋势分析。

主题:

v1/devices/me/telemetry(用于遥测)

v1/devices/me/attributes(用于属性)

v1/devices/me/attributes/response/+(<request_id>)(订阅主题)

平台收到上报的数据:

image-20260414000033539

关键代码:

/* thingsboard mqtt */
#define TB_BROKER "192.168.1.10"
#define TB_PORT 11883 // Thingsboard MQTT 非加密端口,使用加密请改为 8883并配置证书
#define TB_CLIENT_ID "xnn7fo32lwpzgz56ks7h"
#define TB_USERNAME "sbu0bic9smatdufeh5st"
#define TB_PASSWORD "p8fg43v4f5rk545vaxxd"
#define TB_TOPIC_TELEMETRY "v1/devices/me/telemetry"

// Thingsboard MQTT 连接参数,不需要生成,已经硬编码
snprintf(clientId, sizeof(clientId), TB_CLIENT_ID);
snprintf(username, sizeof(username), TB_USERNAME);
snprintf(password, sizeof(password), TB_PASSWORD);

// 配置 MQTT 客户端,生成 host
// snprintf(ali_broker, sizeof(ali_broker), "%s.iot-as-mqtt.%s.aliyuncs.com", 
//          MQTT_CRE_PRODUCT_KEY, MQTT_CRE_REGION_ID);      
ESP_LOGI(TAG, "broker: %s", TB_BROKER);
ESP_LOGI(TAG, "port: %d", TB_PORT);
ESP_LOGI(TAG, "clientId: %s", clientId);
ESP_LOGI(TAG, "username: %s", username);
ESP_LOGI(TAG, "password: %s", password);   // 注意:生产环境不要打印密码

// 配置MQTT客户端
esp_mqtt_client_config_t mqtt_cfg = {
    .broker.address.uri = NULL,                 // 不使用uri方式,而是分别设置hostname和port
    .broker.address.hostname = TB_BROKER,          // Thingsboard MQTT接入地址
    .broker.address.port = TB_PORT,                 // 非加密端口,如需加密请使用8883并配置证书
    .broker.address.transport = MQTT_TRANSPORT_OVER_TCP,
    .credentials.client_id = clientId,
    .credentials.username = username,
    .credentials.authentication.password = password,
    .session.keepalive = 60,                     // 心跳间隔60秒
};

/**
 * @brief 从 Modbus 数据队列读取数据,并调用处理函数发布到 MQTT
 * 
 * @param client 
 * @param modbus_queue_handle 
 */
static void mqtt_update_modbus_sensor_data(esp_mqtt_client_handle_t client, QueueHandle_t modbus_queue_handle)
{
    modbus_data_msg_t msg;
    // 循环接收所有可用消息,直到队列空
    while (xQueueReceive(modbus_queue_handle, &msg, 0) == pdTRUE) {
        if (msg.id == DEVICE_ENERGY) {
            ESP_LOGI(TAG, "Received Modbus data for Energy Meter: current=%.2f, power=%.2f, energy=%.2f", 
                    msg.current, msg.power, msg.energy);
        } else if (msg.id == DEVICE_SHT30) {
            ESP_LOGI(TAG, "Received Modbus data for SHT30 Sensor: temp=%.2f, hum=%.2f", 
                    msg.temp_f, msg.hum_f);
        } else {
            ESP_LOGI(TAG, "Received Modbus data for Unknown Device ID %d", msg.id);
            continue; // 跳过未知设备
        }
        // 每次接收后立即上报最新数据   
        mqtt_publish_modbus_data_msg(client, &msg, TB_TOPIC_TELEMETRY);
    }
}

/**
 * @brief 处理 Modbus 传感器数据,并发布到 MQTT 上报 Topic
 * 
 * @param client 
 * @param msg 
 * @param topic 
 */
static void mqtt_publish_modbus_data_msg(esp_mqtt_client_handle_t client, modbus_data_msg_t *msg, const char* topic)
{
    static uint16_t msg_id = 0;

    if( topic == NULL || strlen(topic) == 0 ) {
        ESP_LOGE(TAG, "Invalid topic for publishing modbus sensor data");
        return;
    }

    cJSON *root = cJSON_CreateObject();
    if(msg->id == DEVICE_ENERGY){
        cJSON_AddNumberToObject(root, "current", ((int)(msg->current * 100)) / 100.0f); // 保留两位小数
        cJSON_AddNumberToObject(root, "power", ((int)(msg->power * 100)) / 100.0f); // 保留两位小数
        cJSON_AddNumberToObject(root, "energy", ((int)(msg->energy * 100)) / 100.0f); // 保留两位小数
        cJSON_AddNumberToObject(root, "energy_online", msg->is_online);

    }else if(msg->id == DEVICE_SHT30){
        // 保留两位小数
        cJSON_AddNumberToObject(root, "temp", ((int)(msg->temp_f * 100)) / 100.0f);
        cJSON_AddNumberToObject(root, "hum", ((int)(msg->hum_f * 100)) / 100.0f);
        cJSON_AddNumberToObject(root, "temp_online", msg->is_online);  
    }

    char *json_str = cJSON_PrintUnformatted(root);
    esp_mqtt_client_publish(client, topic, json_str, 0, 1, 0);
    cJSON_free(json_str);
    cJSON_Delete(root);

}

3.5 数据展示

前端展示:

image-20260414000051982

前端展示地址:

https://tbui.hainiu1220.cn/dashboard/7cd30bb0-3760-11f1-961c-ef9f1b85e573?publicId=e3d97ad0-36b4-11f1-961c-ef9f1b85e573

4. 最后

4.1 代码

#克隆指定分支上的tag, -b指定动态静态引用
#克隆 b-thingsboard-v0.1 标签代码
git clone -b b-thingsboard-v0.1 https://github.com/haiou1220/esp32c3-mini-mqtt.git

4.2总结

目前使用ThingsBoard解决了,阿里云物联网平台在展示数据时候的收费问题。借助ThingsBoard可以完全放弃商用的阿里云物联网的解决方案,虽然阿里云物联网MQTT消息服务器是有免费额度的,但是其中连接设备数也是有一定的限制的,所以想要免费使用,还是得花时间部署开源物联网平台。

目前连接到ThingsBoard 局域网mqtt服务器是使用mqtt协议连接,之后考虑使用纯服务端方式的 商用cpolar / 自建frp 。这样在户外4G模块也能连接局域网的mqtt服务器了。

标签: EMQX ESP-IDF ESP32 MQTT ThingsBoard 物联网
最后更新:2026年4月13日

haiou

理工男极客工程师

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

归档

  • 2026 年 6 月
  • 2026 年 4 月
  • 2026 年 3 月
  • 2026 年 2 月

分类

  • APP
  • 嵌入式
  • 服务器

00:00
目录
  • ESP32部署开源物联网平台ThingsBoard
    • 1. 前言
    • 2. 部署MQTT服务器
      • 2.1 目前有什么选择
      • 2.2 部署EMQX
      • 2.3 EMQX与阿里云物联网区别
      • 2.4 小结
    • 3. 部署ThingsBoard
      • 3.1 部署方式
      • 3.2 容器权限问题
      • 3.3 创建设备
      • 3.4 数据类型
      • 3.5 数据展示
    • 4. 最后
      • 4.1 代码
      • 4.2总结

COPYRIGHT © 2026 海欧的博客. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang