前言

网上写得比较乱,这里记一下我自己的步骤。

本文主要是面向有软件开发基础和计算机基本知识,但是没接过线、第一次接线,或者第一次接触AM2302这个小传感器的。在撰写本文之前,笔者甚至不清楚杜邦线接线到什么程度才算接紧了。再加上香橙派的资源真的很少,现成的库硬是调用不了,我觉得网上的教程和官网的教程对于小白都非常不友好。

我的设备:OrangePi AIpro 8G,做活动送的,不是特意买的,没有卖家也没有客服所以也没有什么社群什么的,但好在拿到手的时候系统已经烧录好了。
我连接香橙派的方式:

  1. Windows:安装RealVNC Viewer共享桌面。【具体怎么操作你们可以自行检索】
  2. 香橙派:自带VNC并默认打开,且是root权限,我调整为了用户权限,开放端口为5901。【如果你发现并没有自带可以参考我的记录【记录】 香橙派|VNC Server 和 VNC Viewer (最终发现它默认自带一个:1)-CSDN社区进行配置】
  3. 同时配合了SSH连接用来传输文件,这个简单就略过不提了。

基础资源

主要以下三方面:

  1. 官方文档:Orange Pi Download- Orangepi
    在这里插入图片描述

  2. 大模型:我主要问Deepseek(不深度思考版本)。

  3. AM2302说明书:这种廉价且常见的传感器一般厂家都不会提供说明书,但网上一搜就能搜出来。这里提供一个在线下载地址http://www.aosong.com/userfiles/files/media/AM2302%E8%AF%A6%E7%BB%86%E7%89%88%E8%AF%B4%E6%98%8E%E4%B9%A6-V1_2.pdf

通过文档搞清楚自己需要了解什么具体概念,比如GPIO、电源和GND引脚,SDA、SCL、UTX、URX等通信协议,AM2302这个传感器模块的工作电压、接线要求、传输方案和是否需要并联电阻等,这些边做边查,就不放在开头全写完了。

1 接线

1.1 接线注意事项

  1. 杜邦线颜色主要是为了两端好区分,如果你可以判断哪端对应哪端,那颜色乱接也行。我是新人,所以按标准去接线。
  2. 插入杜邦线尽量暴露的那个口统一方向,一致对外。【我自己感觉这样会好插进去一点,不是网上看到的经验,单纯是我自己觉得是这样,如果你觉得不是这样,那就忽略这条】
  3. 整个插入全部而非只接一点点或一半。

1.2 DHT22 模块侧

通常红色接VCC(电源正极),黑色/咖色等深色接GND(负极),黄色/橙色/绿色或其他颜色接DATA(OUT)。我只有红色、咖色和橙色线,所以是红色正极、咖色负极、橙色OUT,后文叙述接线颜色也与这里保持一致。

接线的事情具体可以看Arduino的教程,非常详细,还有接线视频:Arduino - DHT22 | Arduino Tutorial
在这里插入图片描述

我的接完之后是这样:

在这里插入图片描述

1.3 香橙派侧

1.3.1 查询接口

香橙派手册上写了GPIO接口(注意本图是接口在板子的左下角时的方向,不要把左右看反了):

在这里插入图片描述

如果你死活找不到手册,也可以在安装了wiringPi后运行以下指令读取接口的信息(具体怎么装就略了):

gpio readall

输出的这个结果包含左右两张表,名称与上面的手册一一对应。
在这里插入图片描述

1.3.2 选择接口

接口选择也是有讲究的,官方文档有说 40 pin 接口使用注意事项:

  1. 40 pin 接口中总共有 26 个 GPIO 口,但 8 号和 10 号引脚默认是用于调试串口功能的,并且这两个引脚和 Micro USB 调试串口是连接在一起的,所以这两个引脚请不要设置为 GPIO 等功能。【笔者注:怀疑官方文档打错了,因为图上面根本没有8号和10号,倒是翻到前面有写具体位置,我看是14号和15号】在这里插入图片描述
  2. 所有的 GPIO 口的电压都是 3.3v。
  3. 40 pin 接口中 27 号和 28 号引脚只有 I2C 的功能,没有 GPIO 等其他复用功能,另外这两个引脚的电压默认都为 1.8v。

就算去掉了注意事项里不准接的,还是有很多接口,具体选哪种呢?我问了大模型解释如下:

香橙派AI Pro的GPIO支持多种协议(如1-Wire、I2C、SPI等),引脚功能可通过gpio readall命令查看。关键分类:

  • 数字输入/输出:普通GPIO(如GPIO4、GPIO17),用于开关信号或数字传感器(如DHT22)。
  • 调试专用:UTXD7和URXD2那两个
  • 通信协议专用:
    • I2C:SCL(时钟)、SDA(数据)引脚,适用于I2C传感器(如BME280)。
    • SPI:MOSI、MISO、SCLK引脚,用于高速数据传输。
    • 1-Wire:如GPIO4,用于DS18B20等单总线设备。
  • 电源引脚:
    • 3.3V(引脚1/17):为传感器供电(优先选择,兼容性更好)。
    • 5V(引脚2/4):需注意传感器是否支持5V电平。
    • GND(引脚6/9等):必须共地。

根据大模型的建议,最好接在没有特定传输协议的GPIO接口上,为插线方便我的选择是:红色接3.3V,橙色GPIO1_06(对应wPi编号为7,物理编号为13),咖色GND。

1.3.3 接线

确定接口后,最好给香橙派断电,再接线,避免一下子接错了或者碰到不对的地方造成元件损坏。【当然,对于老手来说可能这一步是不必须的,但是我看Arduino也是断电接线的,感觉这应该是个非常需要强调的好习惯】

连接完成后应如下图:
在这里插入图片描述

我的实物图是这样:

在这里插入图片描述

2 读取数据

2.1 用不了的代码

网上直接查得到的Adafriuit_DHT这个Python库只支持树莓派等最常见的。香橙派甚至pip install时build会失败,即使按照它的要求--force-pi成功安装了,也运行不了。

pip install Adafriuit_DHT

如果你可以成功安装,那你可以使用如下代码去完成数据读取(代码参考:树莓派使用 DHT11 / DHT22 温湿度传感器 - 知乎):

#!/usr/bin/python

import Adafruit_DHT

#两个选一个填写
# sensor = Adafruit_DHT.DHT11 #调用DHT11温湿度传感器
sensor = Adafruit_DHT.DHT22 #调用DHT22温湿度传感器


pin = 26  #使用GPIO-26口

humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)

if humidity is not None and temperature is not None:

    print('Temp={0:0.1f}*C  Humidity={1:0.1f}%'.format(temperature, humidity))

else:

    print('Failed to get reading. Try again!')

根据官网文档我感觉好像只有wiringPi能用,只好查相关代码。

看到搜索引擎最上面是wookookie/DHT22: DHT22 for Raspberry Pi with WiringPi,下载来用了下发现运行时毫无反应,疑惑不解的我调了一下具体的,我感觉这个传参还有代码形式很晦涩,也没有调试打印输出……最关键的时我总感觉这个代码和我自己的传感器没什么关联:

在这里插入图片描述

2.2 用得了的代码

网上找了一圈并没找到能用的wiringPi接口的代码,但我突然找到了AM2302的通信过程,才注意到我可以直接找AM2302的说明书。

原文写法:AM2302器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由数据线完成。设备(微处理器)通过一个漏极开路或三态端口连接至该数据线,以允许设备在不发送数据时能够释放总线;单总线通常要求外接一个约5.1K欧姆的上拉电阻,这样,当总线闲置时,其状态为高电平。由于他们是主从结构,只有在主机呼叫传感器时,传感器才会应答,因此主机访问传感器必须遵循单总线序列,如果出现序列混乱,传感器将不再响应主机。单总线信号格式如下:在这里插入图片描述

说实话那篇博客在原理介绍的时候有点乱,我都没看懂是怎么做到“一次性串出数据”的。但感谢它让我有思路去找说明书。

由于我感觉这个协议有点小复杂,要控制好多个过程,所以干脆说明书发大模型,让大模型写一个,然后我调试了一下,改了改bug就能用了:

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>

#define DHT_PIN 7  // 使用GPIO7(根据你自己的实际连接调整,改成wPi号)

// 全局变量存储温湿度数据
int humidity = 0;
int temperature = 0;

// 读取AM2302数据
int read_dht22() {
    uint8_t data[5] = {0};
    uint8_t last_state = HIGH;
    uint8_t counter = 0;
    uint8_t j = 0, i;

    // 初始化GPIO
    pinMode(DHT_PIN, OUTPUT);
    digitalWrite(DHT_PIN, HIGH);
    delay(10);

    // 发送开始信号(拉低18ms)
    digitalWrite(DHT_PIN, LOW);
    delay(18);

    // 切换为输入模式,等待传感器响应
    digitalWrite(DHT_PIN, HIGH);
    delayMicroseconds(40);
    pinMode(DHT_PIN, INPUT);

    // 等待传感器拉低80us
    while (digitalRead(DHT_PIN) == HIGH) {
        delayMicroseconds(1);
    }

    // 等待传感器拉高80us
    while (digitalRead(DHT_PIN) == LOW) {
        delayMicroseconds(1);
    }

    // 开始读取40bit数据
    for (i = 0; i < 40; ++i) {
    	// 等待高电平结束
        counter = 0;
        while (digitalRead(DHT_PIN) == HIGH) {
            delayMicroseconds(1);
            if (++counter == 255) break;  // 超时退出
        }
        
        // 等待高电平开始
        counter = 0;
        while (digitalRead(DHT_PIN) == LOW) {
            delayMicroseconds(1);
            if (++counter == 255) break;  // 超时退出
        }
        
        // 高电平开始后,等待30us,作为0/1的区分阈值
        delayMicroseconds(30);
        last_state = digitalRead(DHT_PIN);
        
        // for debug: 输出last_state (如果全0就错了)
        // printf("%d\n", last_state);

        if (last_state == HIGH) {
            data[j/8] |= (1 << (7 - (j % 8)));  // 写入1
        } else {
            data[j/8] &= ~(1 << (7 - (j % 8)));  // 写入0
        }
        j++;
    }

    // 校验数据
    if ((j >= 40) && (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF))) {
        humidity = (data[0] << 8) + data[1];
        temperature = (data[2] << 8) + data[3];
        return 0;  // 成功
    } else {
        return -1;  // 失败
    }
}

int main() {
    printf("AM2302温湿度传感器测试\n");

    if (wiringPiSetup() == -1) {
        printf("WiringPi初始化失败!\n");
        return -1;
    }

    if (read_dht22() == 0) {
        printf("湿度: %.1f%%\n", humidity / 10.0);
        printf("温度: %.1f°C\n", temperature / 10.0);
    } else {
        printf("读取失败,请检查接线或重试。\n");
    }

    return 0;
}

编译:

gcc -o dht22.out dht22.c -lwiringPi

运行:

sudo ./dht22.out

输出结果如下(忽略我终端的乱码):

在这里插入图片描述

2.3 升级版代码

不知道为什么,数据读取的时候总是时不时丢几个数据,我总感觉是卖元件的有问题,但是我又没有证据。为了解决丢失的问题,我重新写了一个代码,增加了重试的机制,还增加了统计成功率的代码(被我注释掉了),如果有遇到和我一样问题的可以参考一下,我这里也就不做过多的整理了:

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>

// #define DEBUG
#define DHT_PIN 7  // 使用GPIO7(根据实际连接调整)

// 全局变量存储温湿度数据
int humidity = 0;
int temperature = 0;

// 读取AM2302数据
int read_dht22() {
    uint8_t data[5] = {0};
    // uint8_t last_state = HIGH;
    uint16_t counter = 0;
    uint8_t j = 0, i;
    uint16_t max_wait_time = 255;  // 最大等待时间,单位为us

    // 初始化GPIO,清空总线状态
    pinMode(DHT_PIN, OUTPUT);
    counter = 10;
    while (counter--) {
        digitalWrite(DHT_PIN, HIGH);  // 确保总线处于高电平
        delayMicroseconds(1);
    }

    // 发送开始信号(拉低0.8~20ms)
    digitalWrite(DHT_PIN, LOW);
    delay(1);
    // 主机释放总线,预期时间为25~45us
    digitalWrite(DHT_PIN, HIGH);
    delayMicroseconds(1);

    // 切换为输入模式,等待传感器响应
    pinMode(DHT_PIN, INPUT);
    // printf("等待传感器响应...\n");

    // 等待传感器开始拉低80us
    counter = 0;
    while (digitalRead(DHT_PIN) == HIGH) {
        delayMicroseconds(1);
        if (++counter == max_wait_time) {
#ifdef DEBUG
            printf("等待响应时,传感器拉低超时\n");
#endif
            return -1;  // 超时退出
        }
    }
    // printf("主机释放总线耗时%dus\n", counter);

    // 等待传感器拉高80us
    counter = 0;
    while (digitalRead(DHT_PIN) == LOW) {
        delayMicroseconds(1);
        if (++counter == max_wait_time) {
#ifdef DEBUG
            printf("等待响应时,传感器拉高超时\n");
#endif
            return -1;  // 超时退出
        }
    }
    // printf("传感器拉低%dus\n", counter);

    // 等待高电平结束
    counter = 0;
    while (digitalRead(DHT_PIN) == HIGH) {
        delayMicroseconds(1);
        if (++counter == max_wait_time){
#ifdef DEBUG
            printf("等待高电平结束超时\n");
#endif
            return -1;  // 超时退出
        }
    }
    // printf("传感器拉高%dus\n", counter);

    // 开始读取40bit数据
    for (i = 0; i < 40; ++i) {
        // 等待高电平开始
        counter = 0;
        while (digitalRead(DHT_PIN) == LOW) {
            delayMicroseconds(1);
            if (++counter == max_wait_time){
#ifdef DEBUG
                printf("读取第%d位时等待低电平开始超时\n", i);
#endif
                return -1;  // 超时退出
            }
        }
        // printf("读取第%d位时低电平耗时%dus\n", i, counter);
        // if (counter < 40){
        //     printf("读取第%d位时低电平过短,只有%dus,异常\n", i, counter);
        // }

        // 等待电位切换
        delayMicroseconds(2);

        // 等待高电平结束
        counter = 0;
        while (digitalRead(DHT_PIN) == HIGH) {
            delayMicroseconds(1);
            if (++counter == max_wait_time){
#ifdef DEBUG
                printf("读取第%d位时等待低电平结束超时\n", i);
#endif                
                return -1;  // 超时退出
            }
        }
        // printf("读取第%d位时高电平耗时%dus\n", i, counter);
        // if (counter < 10) {
        //     printf("读取第%d位时高电平过短,只有%dus,异常\n", i, counter);
        // }

        if (counter > 40) {
            data[j/8] |= (1 << (7 - (j % 8)));  // 写入1
        }else{
            data[j/8] &= ~(1 << (7 - (j % 8)));  // 写入0
        }
        j++;

        // 等待电位切换
        delayMicroseconds(1);
    }

    // 校验数据
    if ((j >= 40) && (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF))) {
        humidity = (data[0] << 8) + data[1];
        temperature = (data[2] << 8) + data[3];
        return 0;  // 成功
    } else {
#ifdef DEBUG
        printf("数据校验失败!\n");
#endif
        return -1;  // 失败
    }
}

int main() {

    // 统计错误频率
    // int all=10;
    // int all_backup = all;
    // int success=0;
    // while(all){
    //     if (wiringPiSetup() == -1) {
    //         printf("WiringPi初始化失败!\n");
    //         return -1;
    //     }

    //     if (read_dht22() == 0) {
    //         if ((temperature - 22) / 10.0 < 50){
    //             printf("%.1f\n", (humidity - 220) / 10.0);
    //             printf("%.1f\n", (temperature - 22) / 10.0);
    //             success++;
    //         }
    //         else{
    //             printf("温度异常,跳过输出。\n");
    //         }
    //     } else {
    //         printf("读取失败,请检查接线或重试。\n");
    //     }
    //     printf("------------------------\n");
    //     delay(2000);  // 等待后再次尝试
    //     all--;
    // }

    // // 统计结果
    // printf("总尝试次数: %d\n", all_backup);
    // printf("成功率: %.2f%%\n", (success * 1.0 / all_backup) * 100);

    int all=3;
    if (wiringPiSetup() == -1) {
        printf("WiringPi初始化失败!\n");
        return -1;
    }
    while(all){
        if (read_dht22() == 0) {
            if ((temperature - 22) / 10.0 < 50){
                printf("%.1f\n", (humidity - 220) / 10.0);
                printf("%.1f\n", (temperature - 22) / 10.0);
                return 0;  // 成功退出
            }
        }
        delay(500);  // 等待后再次尝试
        all--;
    }

    return 0;
}

本账号所有文章均为原创,欢迎转载,请注明文章出处:https://shandianchengzi.blog.csdn.net/article/details/149493423。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。

更多推荐