OpenHarmony鸿蒙硬件-Kaihong_Hi3861土壤湿度感应板、土壤检测探头获取土壤湿度

应用场景

在物联网智能农业、盆栽监测等场景中,土壤湿度是核心监测指标之一。本文基于OpenHarmony 1.0 轻量级系统(Hi3861_WiFi_IoT开发板),搭配电阻式土壤湿度传感器,实现土壤湿度的精准采集、ADC模拟信号转数字信号、电压值到湿度百分比的换算,并输出标准化采集数据,为后续的智能浇水、湿度阈值报警等功能提供基础。

开发核心需求:

  1. 基于OpenHarmony 1.0标准驱动,实现ADC采集功能,匹配Hi3861硬件特性;
  2. 完成模拟电压到土壤湿度百分比的转换,解决传感器实际特性与理论公式的匹配问题;
  3. 保证代码符合OpenHarmony开发规范,结构精简、可维护性强;
  4. 输出纯英文打印日志,避免串口乱码,适配嵌入式开发调试场景。

二、硬件准备与原理说明

2.1 硬件

2.2 传感器工作原理

本文使用的电阻式土壤湿度传感器为模拟输出型,核心原理是:土壤湿度不同,导电性不同,传感器电极间的电阻值会发生变化——土壤湿度越高,电阻越小,输出的模拟电压越低;土壤湿度越低,电阻越大,输出的模拟电压越高。

传感器输出 0~3.3V(5V供电时)的模拟电压,需通过开发板的ADC(模拟/数字转换器)将模拟信号转换为0~4095的数字信号,再通过公式换算为 0~100% 的湿度百分比,实现量化监测。

三、开发环境准备

  1. 系统环境:Ubuntu 18.04/20.04(推荐,OpenHarmony官方编译环境);
  2. 开发工具:VS Code(代码编写)、MobaXterm/Xshell(串口调试)、HarmonyOS DevEco Device Tool(可选);
  3. 固件版本:OpenHarmony 1.0(Hi3861专用版,已适配ADC、GPIO驱动);
  4. 串口配置:波特率115200、数据位8、停止位1、无校验、无流控。

四、核心代码实现

4.1 代码设计思路

  1. 分层设计:按“硬件初始化、数据采集、数值换算”分层,函数职责单一,符合高内聚低耦合原则;
  2. 规范适配:严格遵循OpenHarmony 1.0开发规范,头文件、宏定义、函数命名、注释均符合嵌入式开发标准;
  3. 异常处理:对ADC读取结果做校验,对湿度值做范围限制,避免程序崩溃或输出异常数据;
  4. 无冗余设计:仅保留土壤湿度采集核心功能,移除无关的GPIO控制、水泵驱动等代码,降低资源占用。

4.2 代码

创建目录,如:

\applications\sample\wifi-iot\app\code_v2.0\std_30_土壤湿度

创建 main.c文件

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

// OpenHarmony系统初始化头文件
#include "ohos_init.h"
// 操作系统任务创建头文件
#include "cmsis_os2.h"
// ADC驱动头文件(核心:读取模拟信号)
#include "hi_adc.h"
// GPIO配置头文件(配置ADC引脚功能)
#include "hi_io.h"

// ===================== 硬件配置(匹配你的开发板) =====================
// 土壤湿度传感器ADC通道:GPIO9对应ADC4(硬件文档指定)
#define SOIL_ADC_CHANNEL        HI_ADC_CHANNEL_4

/**
 * @brief  初始化ADC采集引脚(GPIO9)
 * @note   仅配置ADC采集所需的GPIO,移除水泵/继电器相关配置
 */
static void ADC_Pin_Init(void)
{
    // 配置GPIO9为ADC功能(关闭默认的上下拉电阻,避免干扰采集)
    hi_io_set_func(HI_IO_NAME_GPIO_9, HI_IO_FUNC_GPIO_9_GPIO);
    hi_io_set_pull(HI_IO_NAME_GPIO_9, HI_IO_PULL_NONE);
}

/**
 * @brief  读取并计算土壤湿度(核心函数)
 * @return 湿度百分比(0~100%)
 * @note   电压越低 → 湿度越高(0V=100%,3.3V=0%)
 */
static uint8_t Read_Soil_Humidity(void)
{
    uint16_t adc_raw = 0;    // 原始ADC值(0~4095)
    float voltage = 0.0f;    // 转换后的电压值(0~3.3V)
    uint8_t humidity = 0;    // 最终湿度百分比

    // 读取ADC原始值:通道4、8次平均滤波、默认偏置电流
    if (hi_adc_read(SOIL_ADC_CHANNEL, &adc_raw, HI_ADC_EQU_MODEL_8, 
        HI_ADC_CUR_BAIS_DEFAULT, 0) == HI_ERR_SUCCESS)
    {
        // ADC值转换为实际电压(公式:电压 = ADC值 * 3.3V / 4095)
        voltage = hi_adc_convert_to_voltage(adc_raw);
        // 计算湿度:电压越高湿度越低,最终结果限制在0~100%
        humidity = 100 - (uint8_t)((voltage / 3.3f) * 100);
        humidity = (humidity > 100) ? 100 : humidity;

        // 打印结果(纯英文,避免乱码)
        printf("ADC raw: %d | Voltage: %.2fV | Soil humidity: %d%%\n", adc_raw, voltage, humidity);
    }
    else
    {
        // ADC读取失败时的提示
        printf("Failed to read ADC data!\n");
        humidity = 0;
    }

    return humidity;
}

/**
 * @brief  土壤湿度采集主任务
 * @param  arg: 任务入参(未使用)
 * @note   死循环持续采集,每秒刷新一次
 */
void Soil_Humi_Collect_Task(void *arg)
{
    (void)arg;  // 忽略未使用参数,避免编译警告

    // 初始化ADC引脚
    ADC_Pin_Init();
    // 任务启动提示(纯英文)
    printf("Soil humidity collection task started!\n");

    // 持续采集湿度
    while (1)
    {
        Read_Soil_Humidity();  // 读取并打印湿度
        sleep(1);              // 延时1秒,降低采集频率
    }
}

/**
 * @brief  系统初始化入口
 * @note   创建并启动湿度采集任务,系统开机自动运行
 */
void Soil_Humi_Init(void)
{
    // 任务属性配置:名称、栈大小、优先级
    osThreadAttr_t task_attr = {
        .name = "SoilHumiTask",    // 任务名称
        .stack_size = 1024 * 2,    // 简化栈大小(仅采集功能,2KB足够)
        .priority = osPriorityNormal,  // 普通优先级
    };

    // 创建任务并启动
    if (osThreadNew(Soil_Humi_Collect_Task, NULL, &task_attr) == NULL)
    {
        printf("Failed to create soil humidity task!\n");
    }
}

// 注册系统启动入口,开机自动执行Soil_Humi_Init
SYS_RUN(Soil_Humi_Init);

同目录创建 BUILD.gn 文件:

# static_library中指定业务模块的编译结果.为静态库文件libmyapp.a,开发者根据实际情况完成填写。
#sources中指定静态库.a所依赖的.c文件及其路径,若路径中包含"//"则表示绝对路径(此处为代码根路径),若不包含"//"则表示相对路径。
#include_dirs中指定source所需要依赖的.h文件路径。
static_library("app") { 
    sources = [ 
        "main.c" 
    ]
    include_dirs = [
        "//utils/native/lite/include",
        "//base/iot_hardware/interfaces/kits/wifiiot_lite",
        "//kernel/liteos_m/components/cmsis/2.0",
    ] 
}

4.3 核心代码解析

4.3.1 头文件引入

仅引入核心必要头文件,遵循“最小依赖”原则:

  • 标准库头文件(stdio.h/stdint.h/unistd.h):实现打印、固定宽度类型、延时功能;
  • OpenHarmony系统头文件(ohos_init.h/cmsis_os2.h):实现系统启动、任务创建;
  • 硬件驱动头文件(hi_adc.h/hi_io.h):实现ADC采集、GPIO引脚配置。

4.3.2 宏定义配置

所有硬件配置、任务参数均通过宏定义实现,便于后期修改与移植:

  • SOIL_ADC_CHANNEL:指定ADC采集通道,与硬件接线匹配;
  • 任务栈大小与优先级:根据功能需求配置,避免资源过度分配或不足。

4.3.3 ADC引脚初始化

ADC_Pin_Init函数仅完成ADC采集引脚的核心配置:

  1. hi_io_set_func:将 GPIO9 配置为 ADC 功能,兼容OpenHarmony 1.0底层驱动;
  2. hi_io_set_pull:关闭GPIO9上下拉电阻,防止外部电阻干扰ADC采集信号,保证数据准确性。

4.3.4 土壤湿度采集与换算

Read_Soil_Humidity是核心业务函数,完成三大核心功能:

  1. ADC采集:调用hi_adc_read读取原始值,使用8次平均滤波(HI_ADC_EQU_MODEL_8)提高采集稳定性;
  2. 电压转换:调用官方APIhi_adc_convert_to_voltage将ADC原始值转换为实际电压,避免手动计算带来的误差;
  3. 湿度换算:根据传感器“电压越低湿度越高”的特性,通过理论公式 humidity = 100 - (uint8_t)((voltage / 3.3f) * 100); 将电压值换算为0~100%的湿度百分比,并对电压、湿度值做范围限制,避免异常数据。

同时对ADC读取结果做校验,采集失败时打印错误日志并返回默认值,保证程序健壮性。

4.3.5 任务创建与系统启动

  1. 采集任务:Soil_Humi_Collect_Task为后台死循环任务,初始化引脚后每秒调用一次采集函数,实现持续监测;
  2. 任务创建:Soil_Humi_Init配置任务属性并创建任务,判断创建结果,避免任务创建失败导致程序无响应;
  3. 系统注册:通过SYS_RUN宏注册入口函数,OpenHarmony系统开机后自动执行,无需手动调用。

五、编译与烧录

5.1 编译

详见:

https://www.malaoshi.top/show_1GW1wTyUOMPk.html

5.3 固件烧录

详见:

https://www.malaoshi.top/show_1GW1wWWflGbq.html

六、运行结果与调试

  • 传感器在干燥空气中:ADC值≈1800~4095,电压≈3.0~3.3V,湿度≈0%~10%;
  • 传感器插入湿润土壤:ADC值≈500~1500,电压≈0.5~2.5V,湿度≈20%~80%;
  • 传感器完全浸入水中:ADC值≈0~300,电压≈0~0.5V,湿度≈90%~100%。

注意:经过测试发现,探头前三分之一放到水里,湿度是:25%,探头全部放到水里,湿度是:33%,应该是硬件问题

七、常见问题与调试方案

问题1:ADC值固定不变,湿度无变化

原因:GPIO引脚未配置为ADC功能;接线错误,AO引脚未接GPIO9;传感器供电异常。
调试:检查ADC_Pin_Init函数是否执行;重新核对接线,确保AO接GPIO9;用万用表测量传感器VCC电压,保证5V/3.3V供电正常。

问题2:传感器放入水中,湿度仅20%~30%,无法达到90%+

原因:传感器供电为3.3V,量程压缩;传感器电极氧化/污染;传感器本身质量问题。
调试:将传感器VCC改为5V供电;用酒精棉擦拭电极表面,清除氧化层与污垢;更换全新的电阻式模拟输出传感器。

问题3:串口打印乱码

原因:打印内容包含中文;串口波特率与开发板不匹配。
调试:保证打印内容为纯英文(本文代码已做处理);确认串口波特率为115200,与开发板配置一致。

问题4:ADC采集失败,打印“Failed to read ADC data!”

原因:ADC通道被其他功能占用;GPIO9引脚损坏;底层驱动未适配。
调试:检查工程中是否有其他代码使用ADC4通道;更换开发板其他ADC通道(如ADC5)测试;确认使用的是Hi3861专用的OpenHarmony 1.0固件。


原文出处:/show_1GW32ee3Hbty.html