Funpack3-4 MCX N94实现基于MQTT的多传感器测控
该项目使用了MCXN947开发板,实现了多传感器测控的设计,它的主要功能为:基于MQTT的多传感器测控。
标签
Funpack活动
传感器
rt-thread
MQTT
FunPack3-4
MCXN947
枫雪天
更新2024-09-09
80

任务介绍

    本项目实现了Funpack第3-4期活动的任务二,在MCXN947开发板及扩展板上,实现了基于MQTT的多传感器测控。

硬件平台

    首先介绍本次用到的开发板:来自恩智浦的FRDM-MCXN947,这是一块性能很强的板卡,主控是主频高达150MHz的双核ARM Cortex-M33。我把它理解成AI增强版的LPC55S69,它们使用相同的内核,最不一样的地方是MCXN947配备了一个用于AI加速的协处理器,并且在安全、存储资源等各方面都做了增强。这次正好借着Funpack活动上车体验一下

任务分析与实现

这次主办方出了三种任务

  • 任务一是做一个多协议通信HUB,使用到USB协议栈、串口和CAN总线,他们之间互相做输入输出转换,需要准备一个额外的CAN模块。
  • 任务二是使用板卡的以太网网口,实现远程读取板卡和外部的的传感器和输入设备,并控制板载的三色LED,以及外部的执行设备。
  • 任务三是使用板卡的AI加速,实现音频或图像分类。

    这次我选择了任务二。因为题目要求增加额外的传感器和执行器模块,我手上正好有一块带可燃气体传感器、蜂鸣器和OLED显示屏的扩展板。

    所以,项目最终涉及板载网口的网络通信、板载温度传感器、外接OLED屏幕的I2C通信、蜂鸣器的PWM输出、三色LED的GPIO输出、板载触摸开关的触摸输入、板载按键的输入、外接可燃气体传感器MQ-2的ADC模拟输入,可以说硬件功能覆盖的相当全面。

    功能定好了,接下来就是分模块调通,最后合并联调。

    因为这块板卡年初的时候在RT-Thread社区的活动中已经做好了大部分的功能移植和测试,相比于裸机开发,在RT-Thread平台上可以更容易的集成相对复杂的能力。于是我选择在RT-Thread平台上进行开发。

    我们首先调最核心最复杂的网络模块,首先使能网口驱动,在RT-Thread Components -> Device Drivers中选择Using ethernet phy device drivers。

Administrator_ cmd - menuconfig 2024-08-31 20_50_52.png

随后在RT-Thread Components -> Network中选择网络相关的组件。

Administrator_ cmd - menuconfig 2024-08-31 20_51_32.png
    这两个基础组件打开后,接上网线,就能通过ifconfig命令看到板卡的ip地址了。

    如果只使用LWIP做最基础的Socket通信,有点过于底层。我们选择更高层的MQTT协议来做通信,到IoT列表打开RyanMQTT选项,就可以在RT-Thread的命令行使用基本的命令来测试连接MQTT服务器,以及发布和监听Topic。

    基础的网络功能调好了,我们继续调其他模块,对于两个I2C设备,OLED屏幕很好接入,我们在RT-Thread的软件包里引入SSD1306的驱动,同时在硬件列表使能I2C1,简单的配置后就可以正常驱动。

    板载温度传感器就要略微复杂一点,因为它对接了主控的I3C引脚,我们需要单独引入fsl_i3c这个库文件来驱动i3c接口,好在本身的读取逻辑很简单,在调试的过程我们需要持续观察逻辑分析仪,来确保引脚正常输出了波形和时序。

Scopy - v1.4.1 - f4beeb1 2024-07-21 2_30_28.png

    随后是触摸模块,我们可以从官网下载一个触摸例程来移植,例程中驱动触摸模块的代码文件比较多,同时还附带了FreeMaster调试工具的代码,因为FreeMaster在我们的程序里不是必须的,这里需要做一下裁剪。

    蜂鸣器和ADC的移植比较相似,都是先找一个例程跑通,然后移植初始化以及驱动代码,MCXN947有很多种方式来实现PWM,这里我选择使用FlexIO的PWM输出功能,这一过程中也可以通过示波器来观察引脚有没有正常输出波形。

Scopy - v1.4.1 - f4beeb1 2024-08-29 2_15_55.png

    基础设备都驱动好了,就可以开始做MQTT数据发送和接收了,因为每个设备都是在各自的线程控制工作的,这里我用操作系统的消息队列来实现它们和MQTT线程的信息交互。ADC、温度的数据会循环采集,触摸、按键则只在状态变化时触发发送,MQTT线程收到它们的消息后,转换成json字符串,通过Topic为uplink的上行通道传给上位机。上位机在页面上控制三色LED和蜂鸣器,通过Topic为downlink的下行通道通知开发板。

MQTT线程代码

// 处理消息的通用函数
static void process_message(struct rt_messagequeue *queue, const char *type, void (*add_value)(cJSON *, data_t))
{
    data_t msg;
    if (rt_mq_recv(queue, &msg, sizeof(data_t), RT_WAITING_NO) > 0)
    {
        if (client != NULL)
        {
            cJSON *root = cJSON_CreateObject();
            cJSON_AddStringToObject(root, "type", type);
            add_value(root, msg);
            char *payload_str = cJSON_PrintUnformatted(root);
            RyanMqttPublish(client, "uplink", payload_str, strlen(payload_str), RyanMqttQos0, 0);
            cJSON_free(payload_str);
            cJSON_Delete(root);
        }
    }
}

static void my_mqtt_task(void)
{
    // 处理adc消息
    process_message(&adc_queue, "adc", add_double_value);


    // 处理temp消息
    process_message(&temp_queue, "temp", add_double_value);


    // 处理btn消息
    process_message(&btn_queue, "btn", add_uint8_value);


    // 处理touch消息
    process_message(&touch_queue, "touch", add_uint8_value);


    //    rt_kprintf("my_mqtt_task.\r\n");
}

static void mqtt_entry(void *paremeter)
{
    MY_MQTT_Init();
    while (1)
    {
        my_mqtt_task();
        rt_thread_mdelay(100);
    }
}

int run_mqtt(void)
{
    mqtt_thread = rt_thread_create("mqtt_task", mqtt_entry, RT_NULL, 4096, 15, 20);
    if (mqtt_thread != RT_NULL)
    {
        rt_thread_startup(mqtt_thread);
    }
    return 0;
}

    上位机交互界面使用PyQT开发,用GPT可以快速的生成一个原型程序,界面很简单但功能够用,简单修改debug一下就可以跑起来。

1725173223537.png

上位机代码

import sys
import json
from PyQt5.QtWidgets import QApplication, QGroupBox, QWidget, QLabel, QPushButton, QSlider, QVBoxLayout, QHBoxLayout
from PyQt5.QtCore import Qt
import paho.mqtt.client as mqtt

# MQTT 相关参数
broker_address = "localhost"  # MQTT Broker 地址
port = 1883
topic_subscribe = "uplink"
topic_publish = "downlink"

class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("MQTT 上位机")
        self.setGeometry(300, 300, 300, 300)


        # 创建 MQTT 客户端
        self.client = mqtt.Client()
        self.client.on_connect = self.on_connect
        self.client.on_message = self.on_message
        self.client.connect(broker_address, port=port, keepalive=60)
        self.client.loop_start()


        # 创建控件
        self.label_adc = QLabel("ADC值: ")
        self.label_temp = QLabel("温度值: ")
        self.label_touch = QLabel("触摸状态: ")
        self.label_btn = QLabel("按钮状态: ")


        groupBox_sensor = QGroupBox("传感器状态")
        # 创建两个水平布局,分别容纳两个QLabel
        hlayout_sensor1 = QHBoxLayout()
        hlayout_sensor1.addWidget(self.label_adc)
        hlayout_sensor1.addWidget(self.label_temp)


        hlayout_sensor2 = QHBoxLayout()
        hlayout_sensor2.addWidget(self.label_touch)
        hlayout_sensor2.addWidget(self.label_btn)


        # 创建垂直布局,将两个水平布局放入其中
        vlayout_sensor = QVBoxLayout()
        vlayout_sensor.addLayout(hlayout_sensor1)
        vlayout_sensor.addLayout(hlayout_sensor2)


        groupBox_sensor.setLayout(vlayout_sensor)


        # 创建 RGB 滑块组
        groupBox_rgb = QGroupBox("RGB 控制")
        hlayout_rgb = QHBoxLayout()
        for color in ['R', 'G', 'B']:
            label = QLabel(color)
            slider = QSlider(Qt.Horizontal)
            slider.setRange(0, 1)
            slider.valueChanged.connect(lambda value, color=color: self.on_slider_changed(value, color))
            hlayout_rgb.addWidget(label)
            hlayout_rgb.addWidget(slider)
            setattr(self, f"slider_{color.lower()}", slider)  # 动态设置属性
        groupBox_rgb.setLayout(hlayout_rgb)


        # 创建按钮组
        groupBox_control = QGroupBox("蜂鸣器控制")
        hbox_control = QHBoxLayout()
        self.button_buzzer = QPushButton("蜂鸣器")
        self.button_buzzer.clicked.connect(self.on_buzzer_clicked)
        hbox_control.addWidget(self.button_buzzer)
        groupBox_control.setLayout(hbox_control)


        # 创建主布局
        vlayout = QVBoxLayout()
        vlayout.addWidget(groupBox_sensor)
        vlayout.addWidget(groupBox_rgb)
        vlayout.addWidget(groupBox_control)
        self.setLayout(vlayout)


        # 设置样式表
        self.setStyleSheet("""
            QGroupBox {
                border: 1px solid gray;
                border-radius: 5px;
                padding: 5px;
            }
        """)


    def on_connect(self, client, userdata, flags, rc):
        print("Connected with result code "+str(rc))
        self.client.subscribe(topic_subscribe)
        print("subscribe")


    def on_message(self, client, userdata, msg):
        print(msg.payload.decode("utf-8"))
        data = json.loads(msg.payload.decode("utf-8"))
        if data["type"] == "adc":
            self.label_adc.setText("ADC值: {:.2f}".format(data["value"]) + ' V')
        elif data["type"] == "temp":
            self.label_temp.setText("温度值: {:.2f}".format(data["value"]) + ' ℃')
        elif data["type"] == "touch":
            self.label_touch.setText("触摸状态: " + str(data["value"]))
        elif data["type"] == "btn":
            self.label_btn.setText("按钮状态: " + str(data["value"]))


    def on_buzzer_clicked(self):
        data = {"type": "pwm", "value": 1 if self.button_buzzer.text() == "蜂鸣器" else 0}
        self.client.publish(topic_publish, json.dumps(data))
        self.button_buzzer.setText("停止蜂鸣" if data["value"] == 1 else "蜂鸣器")


    def on_slider_changed(self, value, color):
        r = self.slider_r.value()
        g = self.slider_g.value()
        b = self.slider_b.value()
        data = {"type": "rgb", "value": (b << 2) | (g << 1) | r}
        self.client.publish(topic_publish, json.dumps(data))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

效果展示

 

活动感想

    在这次活动中,因为之前就开发过LPC55S69、I.MX RT1062等NXP家的微控制器。另外手上有M2K这个电子调试的大杀器,这次开发的过程可以说很顺利。同时在开发过程中也了解到了FreeMaster工具,对于它以及在MCXN947平台上的AI开发,之后可以通过官方文档和其他伙伴的项目,更深入的进行学习。可以说通过一块板卡既锻炼了开发能力,也带出了更多的未知的知识和学习方向。这就是参加电子活动最有意思的地方。

    感谢硬禾学堂和得捷电子联合举办的Funpack活动,祝硬禾的活动越办越好!

附件下载
程序源码.rar
团队介绍
个人
团队成员
枫雪天
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号