# Arduino UNO Q 实战:温湿度检测 + 自动穿衣提醒 Demo

yunchang0612 2026-09-20 14:45:25

拿到 Arduino UNO Q 之后,如果只是写一个 Blink,多少有点浪费这块板子的“双脑架构”。

这次尝试做一个比较生活化的小 Demo:

实时检测环境温度、湿度,并根据当前环境自动给出穿衣建议。

例如环境为:

温度:16.8℃
湿度:82.4%

环境状态:偏凉
穿衣建议:建议长袖 + 薄外套;当前湿度较高,体感可能偏湿冷,可适当增加一层。

整个项目并不复杂,但比较适合用来理解 Arduino UNO Q 和普通 Arduino 最大的区别:

STM32 负责“感知”,Qualcomm Linux 负责“决策”。


1. 先看最终实现效果

这个 Demo 使用一个 SHT31 温湿度传感器实时采集室内环境数据。

数据首先进入 Arduino UNO Q 上的 STM32U585,然后通过 UNO Q 的 Bridge RPC 传递给运行 Debian Linux 的 Qualcomm QRB2210。

Linux 侧 Python 程序拿到温湿度以后,根据预设规则生成穿衣建议。

整体数据流如下:

                 SHT31 温湿度传感器
                         │
                        I2C
                         │
                         ▼
                ┌─────────────────┐
                │   STM32U585     │
                │ Arduino / Zephyr│
                │                 │
                │  温湿度采集     │
                └────────┬────────┘
                         │
                     Bridge RPC
                         │
                         ▼
                ┌─────────────────┐
                │ Qualcomm QRB2210│
                │  Debian Linux   │
                │                 │
                │ Python规则引擎  │
                └────────┬────────┘
                         │
                         ▼
                 自动生成穿衣提醒

Arduino UNO Q 本身就是这样的异构架构:QRB2210 负责完整 Linux 环境和高层应用,STM32U585 则负责实时控制和 Arduino Sketch。两颗处理器之间可以通过 Arduino Router/Bridge RPC 通信。

这也是为什么这个项目我没有直接在 STM32 中写一堆 if...else

因为后面如果要增加:

语音提醒
Web 页面
手机通知
天气 API
用户画像
AI Agent
大模型自然语言建议

这些逻辑放在 Linux 侧明显更加灵活。


2. 硬件准备

这次用到的东西很少。

硬件数量作用
Arduino UNO Q1主控开发板
SHT31 温湿度传感器1获取温度、相对湿度
杜邦线4I2C 接线
USB-C 电源/数据线1UNO Q 供电及开发

我这里选择 SHT31,而没有使用比较常见的 DHT11。

主要原因是 SHT31 本身使用 I²C,UNO Q 官方已经提供标准 Wire 支持,我们甚至不用安装第三方传感器库,直接操作 I²C 就能读取数据。

UNO Q 标准 Arduino 接口中的 I²C 对应:

UNO Q功能
D20SDA
D21SCL

官方用户手册明确给出了 Wire.begin() 对应 D20/D21,而板载 Qwiic 则使用另外一路 Wire1

SHT31-DIS 本身支持 2.15V~5.5V 供电,并提供数字 I²C 温湿度输出。为了统一逻辑电平,这里直接使用 UNO Q 的 3.3V 供电。

接线如下:

SHT31Arduino UNO Q
VIN / VCC3.3V
GNDGND
SDAD20 / SDA
SCLD21 / SCL

3. 为什么这个 Demo 要拆成两部分?

这是这篇文章最重要的地方。

如果使用传统 Arduino UNO,我们可能会这么写:

SHT31
 ↓
Arduino
 ↓
if 温度 < 15
 ↓
输出“穿厚外套”

但到了 UNO Q,我更建议利用它原生的异构架构:

STM32 MCU
负责:
温湿度采集
I2C时序
传感器状态
实时任务

        ↓ Bridge RPC

Qualcomm Linux
负责:
数据分析
穿衣规则
日志
网络
UI
语音
AI

这样做的好处是硬件层和业务层被分开了。

以后即使把:

if temp < 15

升级成:

温湿度
+
天气预报
+
风力
+
降雨概率
+
个人耐寒程度
+
当天活动类型
+
大模型 Agent

STM32 那边基本不用动。


4. Arduino App Lab 创建项目

打开 Arduino App Lab,新建一个 App。

我们的项目目录可以简单理解成:

ClothingReminder/
│
├── python/
│   └── main.py
│
└── sketch/
    └── sketch.ino

其中:

sketch.ino
    ↓
运行在 STM32U585

main.py
    ↓
运行在 Qualcomm QRB2210 / Debian Linux

两者通过:

Arduino Router
+
Bridge RPC

完成数据交换。

Arduino 官方提供的 UNO Q 示例同样采用这种方式,例如 Linux Python 可以直接:

Bridge.call("set_led_state", True)

调用 MCU 中通过 Bridge.provide() 注册的函数。


5. STM32 侧:读取 SHT31 温湿度

首先编写:

sketch/sketch.ino

为了减少第三方依赖,这里直接使用 Arduino 自带的 Wire 操作 SHT31。

完整代码如下:

#include <Wire.h>
#include <Arduino_RouterBridge.h>
#include <math.h>

#define SHT31_ADDR 0x44

float lastTemperature = 0.0f;
float lastHumidity = 0.0f;

bool sensorOK = false;

unsigned long lastReadTime = 0;
const unsigned long SENSOR_INTERVAL = 2000;


// ----------------------------------------------------
// SHT31 CRC8
// Polynomial: 0x31
// Initialization: 0xFF
// ----------------------------------------------------
uint8_t crc8(const uint8_t *data, int len) {

  uint8_t crc = 0xFF;

  for (int j = 0; j < len; j++) {

    crc ^= data[j];

    for (int i = 0; i < 8; i++) {

      if (crc & 0x80) {
        crc = (crc << 1) ^ 0x31;
      } else {
        crc <<= 1;
      }
    }
  }

  return crc;
}


// ----------------------------------------------------
// 读取 SHT31
// ----------------------------------------------------
bool readSHT31(float &temperature, float &humidity) {

  // 0x2400
  // Single Shot
  // High Repeatability
  // Clock Stretching Disabled

  Wire.beginTransmission(SHT31_ADDR);

  Wire.write(0x24);
  Wire.write(0x00);

  if (Wire.endTransmission() != 0) {
    return false;
  }

  // 高重复性测量最大约需要 15ms
  delay(20);

  Wire.requestFrom(SHT31_ADDR, 6);

  if (Wire.available() != 6) {
    return false;
  }

  uint8_t data[6];

  for (int i = 0; i < 6; i++) {
    data[i] = Wire.read();
  }

  // CRC 检查
  if (crc8(data, 2) != data[2]) {
    return false;
  }

  if (crc8(data + 3, 2) != data[5]) {
    return false;
  }

  uint16_t rawTemperature =
      ((uint16_t)data[0] << 8) | data[1];

  uint16_t rawHumidity =
      ((uint16_t)data[3] << 8) | data[4];


  // SHT31 官方转换公式

  temperature =
      -45.0f +
      175.0f *
      ((float)rawTemperature / 65535.0f);

  humidity =
      100.0f *
      ((float)rawHumidity / 65535.0f);


  return true;
}


// ----------------------------------------------------
// 给 Linux 提供 RPC 接口
// 乘以100转换成整数,避免传输浮点带来的不确定性
// ----------------------------------------------------

int32_t get_temperature_x100() {

  if (!sensorOK) {
    return -32768;
  }

  return (int32_t)lroundf(
      lastTemperature * 100.0f
  );
}


int32_t get_humidity_x100() {

  if (!sensorOK) {
    return -1;
  }

  return (int32_t)lroundf(
      lastHumidity * 100.0f
  );
}


bool get_sensor_status() {

  return sensorOK;
}


// ----------------------------------------------------
// Setup
// ----------------------------------------------------

void setup() {

  Serial.begin(115200);

  // UNO Q 标准 I2C
  // SDA = D20
  // SCL = D21

  Wire.begin();

  // 初始化 Bridge
  Bridge.begin();


  // 注册 RPC

  Bridge.provide_safe(
      "get_temperature_x100",
      get_temperature_x100
  );

  Bridge.provide_safe(
      "get_humidity_x100",
      get_humidity_x100
  );

  Bridge.provide_safe(
      "get_sensor_status",
      get_sensor_status
  );


  Serial.println(
      "UNO Q Temperature & Humidity Sensor Ready"
  );
}


// ----------------------------------------------------
// Loop
// ----------------------------------------------------

void loop() {

  unsigned long now = millis();

  if (now - lastReadTime >= SENSOR_INTERVAL) {

    lastReadTime = now;

    float temperature;
    float humidity;

    sensorOK = readSHT31(
        temperature,
        humidity
    );


    if (sensorOK) {

      lastTemperature = temperature;
      lastHumidity = humidity;

      Serial.print("Temperature: ");
      Serial.print(lastTemperature);
      Serial.print(" C  ");

      Serial.print("Humidity: ");
      Serial.print(lastHumidity);
      Serial.println(" %");

    } else {

      Serial.println(
          "Failed to read SHT31"
      );
    }
  }
}

SHT31 默认 I²C 地址为 0x44,也可以通过 ADDR 引脚切换为 0x45。单次测量使用 0x2400 可以进行高重复性、关闭 clock stretching 的测量;高重复性模式最长测量时间约 15ms。

传感器返回:

Temperature MSB
Temperature LSB
Temperature CRC

Humidity MSB
Humidity LSB
Humidity CRC

因此总共读取 6 Byte。

SHT3x 官方给出的转换公式为:

Temperature
=
-45 + 175 × ST / 65535

Humidity
=
100 × SRH / 65535

CRC8 使用多项式 0x31、初始值 0xFF

这里没有省略 CRC 检查。

因为 Demo 虽然简单,但如果以后真正用于环境监测,不能把一次 I²C 通信异常的数据当成真实温湿度。


6. 为什么 Bridge 传输的是整数?

代码里我没有直接:

return temperature;

而是:

return temperature * 100;

例如:

23.56℃

转换成:

2356

Linux 收到以后再:

2356 / 100

还原。

这样做不是因为 Bridge 不支持浮点数,而是一个比较常见的嵌入式处理习惯:

MCU
 ↓
定点整数
 ↓
IPC / 网络
 ↓
业务系统
 ↓
恢复小数

可以让协议更加明确,也方便以后扩展 MQTT、CAN、串口或其他通信方式。


7. Linux 侧:实现自动穿衣提醒

接下来编写:

python/main.py

这里我们先不引入 AI。

先做一个透明、可解释的规则引擎。

from arduino.app_utils import App, Bridge, Logger
import time


logger = Logger(
    "UNOQ-ClothingReminder"
)


# ----------------------------------------------------
# 穿衣建议
# ----------------------------------------------------

def get_clothing_advice(
    temperature,
    humidity
):

    # 根据温度确定基础建议

    if temperature >= 30:

        status = "炎热"

        advice = (
            "建议短袖、短裤或轻薄透气衣物,"
            "尽量选择吸汗、速干材质。"
        )

    elif temperature >= 26:

        status = "偏热"

        advice = (
            "建议短袖、薄衬衫等轻薄衣物。"
        )

    elif temperature >= 22:

        status = "舒适"

        advice = (
            "短袖或薄长袖均可,"
            "早晚温差较大时可准备一件薄外套。"
        )

    elif temperature >= 18:

        status = "微凉"

        advice = (
            "建议长袖、卫衣或薄外套。"
        )

    elif temperature >= 14:

        status = "偏凉"

        advice = (
            "建议长袖 + 薄外套,"
            "怕冷的话可以增加一层。"
        )

    elif temperature >= 8:

        status = "较冷"

        advice = (
            "建议毛衣、卫衣搭配较厚外套。"
        )

    else:

        status = "寒冷"

        advice = (
            "建议厚外套、羽绒服等保暖衣物,"
            "注意头部和手部保暖。"
        )


    # ------------------------------------------------
    # 湿度修正
    # ------------------------------------------------

    if humidity >= 80:

        if temperature <= 20:

            advice += (
                " 当前湿度较高,"
                "环境可能偏湿冷,"
                "可适当增加一层并选择易干衣物。"
            )

        elif temperature >= 26:

            advice += (
                " 当前处于高温高湿环境,"
                "建议优先选择透气、速干衣物。"
            )

    elif humidity <= 30:

        advice += (
            " 当前空气较干燥,"
            "可注意补水和皮肤保湿。"
        )


    return status, advice


# ----------------------------------------------------
# 主循环
# ----------------------------------------------------

def loop():

    try:

        sensor_ok = Bridge.call(
            "get_sensor_status"
        )

        if not sensor_ok:

            logger.warning(
                "SHT31 sensor is not ready"
            )

            time.sleep(3)
            return


        temperature_raw = Bridge.call(
            "get_temperature_x100"
        )

        humidity_raw = Bridge.call(
            "get_humidity_x100"
        )


        if temperature_raw == -32768:
            raise RuntimeError(
                "Invalid temperature data"
            )

        if humidity_raw < 0:
            raise RuntimeError(
                "Invalid humidity data"
            )


        temperature = (
            temperature_raw / 100.0
        )

        humidity = (
            humidity_raw / 100.0
        )


        status, advice = get_clothing_advice(
            temperature,
            humidity
        )


        logger.info(
            "=============================="
        )

        logger.info(
            f"Temperature: "
            f"{temperature:.2f} °C"
        )

        logger.info(
            f"Humidity: "
            f"{humidity:.2f} %RH"
        )

        logger.info(
            f"Environment: {status}"
        )

        logger.info(
            f"Clothing Advice: {advice}"
        )


    except Exception as e:

        logger.error(
            f"Failed to get environment data: {e}"
        )


    time.sleep(5)


# ----------------------------------------------------
# 启动 App
# ----------------------------------------------------

App.run(
    user_loop=loop
)

UNO Q Python 侧的 Bridge.call() 会等待 MCU 返回结果,所以:

temperature = Bridge.call(
    "get_temperature_x100"
)

实际上就是在 Linux 上远程调用 STM32 中注册的 C++ 函数。Arduino 当前 Python Bridge 实现也明确支持将远程调用返回值直接作为 Python 对象返回。

这就是 UNO Q 比传统 MCU 有意思的地方。

Python 不需要:

打开串口
解析字符串
判断换行符
自己设计协议

而是直接:

Linux Python
      ↓
Bridge.call()
      ↓
STM32 Function
      ↓
return

看起来就像在调用一个本地函数。


8. 穿衣判断规则

这个 Demo 的规则故意没有做得特别复杂。

目前基础逻辑如下:

温度环境判断穿衣建议
≥30℃炎热短袖、短裤、透气衣物
26~29.9℃偏热短袖、薄衬衫
22~25.9℃舒适短袖或薄长袖
18~21.9℃微凉长袖、卫衣、薄外套
14~17.9℃偏凉长袖 + 薄外套
8~13.9℃较冷毛衣、卫衣 + 较厚外套
<8℃寒冷厚外套、羽绒服

湿度不是简单地转换成所谓的“体感温度”。

这里采用更容易解释的修正规则:

温度 ≤ 20℃
+
湿度 ≥ 80%
        ↓
增加“湿冷”提醒


温度 ≥ 26℃
+
湿度 ≥ 80%
        ↓
增加“闷热、透气速干”提醒


湿度 ≤ 30%
        ↓
增加“空气干燥”提醒

这套规则主要用于 Demo 展示,并不是医学或专业气象穿衣指数。

真正做产品的话,还需要加入:

风速
太阳辐射
降雨
室内 / 室外
活动强度
年龄
个人耐寒程度
衣物保温能力

9. 运行项目

Arduino App Lab 中点击:

Run

以后,首先可以看到 STM32 侧不断采集温湿度。

例如下面是假设环境数据下的示例日志:

Temperature: 23.62 C
Humidity: 55.84 %

随后 Linux Python 会通过 Bridge 获取数据。

Arduino App Lab Console 中可以看到类似:

==============================

Temperature: 23.62 °C

Humidity: 55.84 %RH

Environment: 舒适

Clothing Advice:
短袖或薄长袖均可,
早晚温差较大时可准备一件薄外套。

如果把传感器放到湿度较大的环境,假设读取到:

Temperature: 16.80 °C
Humidity: 84.20 %RH

逻辑就会变成:

Environment: 偏凉

Clothing Advice:

建议长袖 + 薄外套,
怕冷的话可以增加一层。

当前湿度较高,
环境可能偏湿冷,
可适当增加一层并选择易干衣物。

这里的数据只是为了展示程序输出格式,最终实际数值以你的 SHT31 实测结果为准。


10. 遇到 Sensor Not Ready 怎么办?

如果日志一直出现:

SHT31 sensor is not ready

首先检查接线:

3.3V
GND
D20 / SDA
D21 / SCL

其次检查地址。

我们代码默认:

#define SHT31_ADDR 0x44

SHT3x-DIS 默认地址是 0x44,当 ADDR 配置为高电平时也可以使用 0x45

因此部分传感器模块如果修改过地址,需要把代码改成:

#define SHT31_ADDR 0x45

11. Bridge 为什么偶尔获取不到函数?

如果出现类似:

Function not found

建议先确认:

Bridge.begin();

是否正常运行,以及对应 RPC 是否已经注册:

Bridge.provide_safe(
    "get_temperature_x100",
    get_temperature_x100
);

函数名称必须和 Python 完全一致:

Bridge.call(
    "get_temperature_x100"
)

Arduino 官方还特别提醒,Bridge.provide() 注册的函数运行在 RPC 后台线程,而 provide_safe() 会让回调在 Arduino 主循环上下文中安全执行;如果回调涉及 Arduino API,优先使用 provide_safe() 会更加稳妥。


12. 为什么不直接用串口传温湿度?

当然也可以。

例如 MCU:

Serial.println(
    "23.5,65.2"
);

Linux 再:

line.split(",")

但如果都已经使用 UNO Q 了,再自己维护串口协议其实有点可惜。

Bridge 最大的优势就是我们可以直接把 STM32 看成 Linux 的“硬件服务”。

例如以后还可以继续注册:

get_temperature
get_humidity
get_light
get_pm25
get_co2
get_noise
set_fan
set_heater
open_window

Linux 侧只需要:

Bridge.call(...)

即可。

这样整个系统会逐渐变成:

                   Linux
                     │
        ┌────────────┼────────────┐
        │            │            │
    环境判断      AI Agent      Web UI
        │            │            │
        └────────────┼────────────┘
                     │
                  Bridge
                     │
                  STM32
                     │
     ┌───────────────┼───────────────┐
     │               │               │
   SHT31           CO₂             PM2.5
     │
  温湿度

这已经非常接近一个小型智能家居或者 AIoT 系统的架构了。


13. 这个 Demo 其实还能继续玩

做到这里只算 Version 1.0。

下一步我认为最值得增加的是:

当前室内温湿度
        +
互联网天气预报
        +
当天最低/最高温
        +
降雨概率
        +
风力
        ↓
      AI Agent
        ↓
“今天成都 18~26℃,
 上午可能下雨,
 当前室内23℃,
 建议穿短袖并带一件轻薄防水外套。”

这样就不再只是:

温度 → 查表

而变成:

实时传感器
+
外部环境
+
个人偏好
+
AI推理
=
个性化生活建议

再进一步甚至可以给用户设置:

怕冷
正常
怕热

三个档位。

例如相同的:

18℃

对于不同用户:

怕冷用户
→ 毛衣 + 外套

普通用户
→ 长袖 + 薄外套

怕热用户
→ 长袖即可

这样这个小 Demo 就开始真正具备“智能助手”的感觉了。


14. 总结

这个案例本身其实非常简单:

读取温湿度,然后根据条件推荐衣服。

但如果放到 Arduino UNO Q 上,它真正值得学习的并不是几个 if...else

而是:

Sensor
 ↓
STM32U585
 ↓
Bridge RPC
 ↓
Qualcomm QRB2210
 ↓
Linux / Python
 ↓
应用逻辑

STM32 处理底层硬件和实时任务。

Qualcomm Linux 处理规则、网络、UI 和未来的 AI。

两边各做自己擅长的事情。

从这个 Demo 继续往后扩展,可以很自然地演变成:

家庭环境助手
办公室舒适度助手
老人穿衣提醒
儿童穿衣提醒
户外运动建议
智能衣柜
智能家居 Agent

这也是我觉得 Arduino UNO Q 比传统 Arduino 更有意思的一点:

让 Arduino 的算力变强,也让 MCU 世界和 Linux / AI 应用真正运行在了同一块开发板上。

...全文
53 回复 打赏 收藏 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

7,697

社区成员

发帖
与我相关
我的任务
社区描述
本论坛以AI、IoT、PC 、XR、Auto等核心板块组成,为开发者提供便捷及高效的学习和交流平台。 高通开发者专区主页:https://qualcomm.csdn.net/
物联网人工智能开源 企业社区 北京·东城区
社区管理员
  • csdnsqst0050
  • chipseeker
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧