物联网项目开发实战-第3章-自动浇花项目迭代2

本节我们在迭代一的基础上增加采集土壤湿度数据,并根据湿度数据来决定是否自动进行浇水动作。

3.3迭代二:基于土壤湿度自动浇水

3.3.1迭代目标

本节我们在迭代一的基础上增加采集土壤湿度数据,并根据湿度数据来决定是否自动进行浇水动作。

3.3.2需求分析

针对本节的目标,分析如下:

1. 需要获取土壤湿度数据,土壤湿度是模拟数据,每3分钟采集一次数据;

2. 判断湿度数据是否小于预定阀值,如果小于阀值就进行浇水;

3. 为避免数据采集问题导致误动作, 使用对最新5次采集数据进行综合判断;

3.3.3知识点

知识点3-4:通过GPIO读取土壤湿度传感器数据

本开发套件中的土壤湿度传感器是通过测量土壤的导电性来判断水分含量的,传感器读数是模拟值,导电性越大,表示越湿润,传感器读数越小;导电性越小,表示越干燥,传感器读数越大。

物联网项目开发实战-第3章-自动浇花项目迭代2

在Arduino-ESP32中使用analogRead来读取模拟数值,默认精度(分辨率)是12位,即数值范围是0到4095。

在日常生活中,湿度一般用0-100来表示,对于土壤湿度传感数值,可以使用map函数将该值映射到1到100的区间内。

实例6:获取土壤湿度传感器数据

const int SOIL_MOISTURE_PIN = 35;

int collectData() {
  int originVal = analogRead(SOIL_MOISTURE_PIN);
  int sensorVal = 0;
  if (originVal > 0) {
    // 将值映射到1-100
    sensorVal = map(originVal, 1, 4095, 100, 1);
  }
  return sensorVal; 
}
 
void setup() {
  Serial.begin(115200);
  pinMode(SOIL_MOISTURE_PIN, INPUT);
}

void loop() {
  int val = collectData();
  Serial.printf("土壤湿度:%d\n", val);

  delay(180000); //3分钟
}

知识点3-5:使用高级数据结构:ArrayList

在本节的需求分析中,为避免单次的数据导致误动作,我们设计对最新5次采集数据进行综合判断来决定是否浇水。

当我们要存储连续采集的传感器数据用于后续处理时,可以使用数组来存储,但数组在动态调整大小和删除元素等操作时比较繁琐,另一个选择是使用列表(ArrayList)数据结构,通过使用ArrayList类,可以方便的只保存最新5条数据。

Arduino并未提供ArrayList类,这里我们使用第三方提供的ArrayList类库,安装步骤如下:

1.点击菜单”工具”->”管理库...”;

2.输入”ArrayList”进行筛选;

物联网项目开发实战-第3章-自动浇花项目迭代2

3.找到”ArrayList by Brayden Anderson”进行安装,这里安装的是1.0.2版本;

实例7:使用高级数据结构ArrayList

#include <ArrayList.h>

ArrayList<int> valList;

// 列表数值比较
int compareAll(int cmpValue) {
  for (int i=0; i<valList.size(); i++) {
    if (valList.get(i) > cmpValue) {
      // 只要有一个值高于阀值,即判定不满足。
      return 0;
    }
  }
  return 1;
}

void setup() {
  Serial.begin(115200);

  valList.add(35);
  valList.add(38);
  valList.add(41);
  valList.add(45);
  valList.add(48);

  if (compareAll(50)) {
    Serial.println("全部数值都小于阀值。");
  }

  if (compareAll(45)) {
    Serial.println("有数值大于阀值");
  }
}

void loop() {

  delay(100);
}

到目前为此,我们可以在迭代一代码上实现迭代二了,合并后的loop()代码将会如下:

void loop() {
  if (buttonClicked()) {
    // 浇水5秒
    watering(5);
  }

  int val = collectData();
  Serial.printf("土壤湿度:%d\n", val);
  // 此处是列表数值比较处理

  delay(180000); //3分钟
  //delay(10);
}

那么问题就来了,当delay(180000)执行时,此时按键状态是检测不到的!

导致此问题的原因是因为按键检测和数据采集在loop内是顺序执行(串行)的,要解决这个问题,需要把这两个操作做并行执行的处理。

顺序执行就好比两辆汽车在一个车道上只能一前一后跑,并行执行就是设计出多个车道,让车在车道上各跑各的,互不影响。

要实现多任务并行,可以使用Arduino-ESP32内置的FreeRTOS来实现。

知识点3-6:使用FreeRTOS实现多任务并行

FreeRTOS是一个用于嵌入式的微型实时操作系统内核,功能包括:任务管理、时间管理、信号量、消息队列等,可满足较小系统的多任务运行需求。要了解更多关于FreeRTOS的内容,可查阅FreeRTOS手册。

Arduino-ESP32开发包集成了FreeRTOS内核,通过使用FreeRTOS的任务机制,可以方便的开发多任务(多线程)的应用。

实例8:使用FreeRTOS实现多任务运行

void buttonTask(void *pvParam) {
  while(1) {
    Serial.println("按键状态检测");
    vTaskDelay(10 / portTICK_PERIOD_MS);
  }
}

void collectTask(void *pvParam) {
  while(1) {
    Serial.println("按键状态检测");
    vTaskDelay(180000 / portTICK_PERIOD_MS); // 180秒
  }
}

void setup() {
  Serial.begin(115200);

  // 创建按键检测任务
  xTaskCreate(
    buttonTask,    // 任务函数
    "Button_Task",   // 任务名称
    10000,          // 堆栈大小(字节)
    NULL,           // 参数
    1,              // 优先级
    NULL            // 任务句柄
  );

  // 创建数据采集任务
  xTaskCreate( collectTask, "Collect_Task", 10000, NULL, 1, NULL );
}

void loop() {
  // ...
  vTaskDelay(10 / portTICK_PERIOD_MS);
} 

3.3.4实现

掌握了所需的知识点后,我们来实现迭代二的目标。

1.流程图

迭代二的程序流程图如下:
物联网项目开发实战-第3章-自动浇花项目迭代2

通过流程图可以看到按键任务和土壤湿度数据采集任务是各自独立的,适合通过多任务程序的方式来实现。

2.电路搭建

物联网项目开发实战-第3章-自动浇花项目迭代2

3.程序开发

#include <ArrayList.h>

const int RELAY_PIN = 12;
const int BUTTON_PIN = 13;
const int SOIL_MOISTURE_PIN = 35;
boolean button_pressed = 0;

ArrayList<int> valList;

// 浇水子程序
void watering(int seconds) {
  digitalWrite(RELAY_PIN, HIGH);
  delay(seconds * 1000);
  digitalWrite(RELAY_PIN, LOW);
  Serial.printf("浇水 %d秒。\n", seconds);
}

boolean buttonClicked() {
  // 检测按钮是否已按下
  if (digitalRead(BUTTON_PIN) == LOW) { // 已按下
    delay(10); // 延时10ms,用于防止机械抖动
    if (digitalRead(BUTTON_PIN)==LOW) { // 再次读取
      button_pressed = 1;
      Serial.print("按键已按下。");
    } 
  }

  // 检测按键是否已释放
  if (button_pressed==1 && digitalRead(BUTTON_PIN) == HIGH) { // 已释放
    delay(10); // 延时10ms,用于防止机械抖动
    if (button_pressed==1 && digitalRead(BUTTON_PIN) == HIGH) {
      button_pressed = 0;
      Serial.print("按键已释放。");
      Serial.print("按键单击。");
      return true;
    }
  }

  return false;
}

// 数据采集子程序
int collectData() {
  int originVal = analogRead(SOIL_MOISTURE_PIN);
  int sensorVal = 0;
  if (originVal > 0) {
    // 将值映射到1-100
    sensorVal = map(originVal, 1, 4095, 100, 1);
  }
  return sensorVal; 
}

// 列表数值比较
int compareAll(int cmpValue) {
  for (int i=0; i<valList.size(); i++) {
    if (valList.get(i) > cmpValue) {
      // 只要有一个值高于阀值,即判定不满足。
      return 0;
    }
  }
  return 1;
}

// 数据采集任务
void collectTask(void *pvParam) {
  while(1) {
    int val = collectData();
    Serial.printf("土壤湿度:%d\n", val);
    if (val>0) {
      valList.add(val);
      if (valList.size() > 5) {
        // 超过5条,移除最早的数据。
        valList.remove(0);
      }
      if (valList.size()==5 && compareAll(50)) {
        // 有5条数据,并且都小于阀值,则浇水
        watering(5);
      }
    }

    delay(180000); //3分钟
  }
}

// 按键任务
void buttonTask(void *pvParam) {
  while(1) {
    if (buttonClicked()) {
      // 浇水5秒
      watering(5);
    }

    vTaskDelay(10 / portTICK_PERIOD_MS);
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(RELAY_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP); // 使用引脚内置上拉电阻
  pinMode(SOIL_MOISTURE_PIN, INPUT);

  // 创建按键检测任务
  xTaskCreate(
    buttonTask,    // 任务函数
    "Button_Task",   // 任务名称
    10000,          // 堆栈大小(字节)
    NULL,           // 参数
    1,              // 优先级
    NULL           // 任务句柄
  );

  // 创建数据采集任务
  xTaskCreate( collectTask, "Collect_Task", 10000, NULL, 1, NULL );
}

void loop() {
  // ...
  vTaskDelay(10 / portTICK_PERIOD_MS);
}

程序合并了迭代一实现和本节知识点的代码,没有特别的地方,就不详细解释了。

- 本文由用户 老张 发布,文中观点仅代表作者本人,不代表本站立场。
- 如需转载,请联系作者;如有侵权,请联系本站处理。

09-09   阅读(8)   评论(0)
 标签: 创客 ESP32 FreeRTOS 物联网

涨知识
LED点阵屏

LED点阵屏通过LED(发光二极管)组成,以灯珠亮灭来显示文字、图片、动画、视频等,是各部分组件都模块化的显示器件,通常由显示模块、控制系统及电源系统组成。

评论:
相关文章
Arduino-ESP32与ESP-IDF的版本对应表

Arduino-ESP32与ESP-IDF的版本对应表。


Arduino-ESP32文件系统全解析:SPIFFS、LittleFS、SD卡操作

Arduino-ESP32提供了多种文件系统解决方案,本文将深入解析SPIFFS、LittleFS和SD卡三种主流存储方案,帮助你做出最佳选择。


ESP32-P4-WIFI6开发板

ESP32-P4-WIFI6-DEV-KIT是一款微雪(Waveshare)设计的基于 ESP32-P4 的多媒体开发板,并集成 ESP32-C6,支持 Wi-Fi 6 和 BLE 5 无线连接。它提供丰富的人机交互接口,包括 MIPI-CSI (集成图像信号处理器 ISP)、MIPI-DSI、SPI、I2S、I2C、LED PWM、MCPWM、RMT、ADC、UART 和 TWAI 等。


ESP-Hosted 入门介绍 &使用指南

ESP-Hosted 解决方案提供了将 ESP 板用作 Wi-Fi 和 Bluetooth/BLE 连接的通信处理器的方法。


设备上云太麻烦?ESP-Hosted一站触达!

ESP-Hosted 提供了一种将ESP芯片和模组用作通信协处理器的解决方案,该解决方案为主机微处理器或微控制器提供无线连接,使主机能够与其他设备通信。简单来说为网卡方案。


ESP32 + Arduino使用TFT_eSPI库

Arduino+ESP32上使用TFT_eSPI库快速点亮这个屏幕,驱动芯片ST7789


ESP32 利用 SPI 连通 TFT 彩屏

本文给出了一个ESP32与SPI 接口TFT显示屏接线的详细说明,供大家参考。


在Micropython下使用ESPNow功能进行数据传输

本文讲解如何在Micropython环境下使用ESP32的ESPNow功能进行数据传输。


用 ESP32-S3 打造多功能 USB Dongle

ESP-Dongle 是一款基于 ESP32-S3 芯片开发的多功能 USB Device 解决方案。它不仅外形小巧,功能齐全,更集成了无线 U 盘、SD 卡读取以及 USB 无线网卡等多项功能。


利用 ESP32-S3 和 CSI 技术打造智能家居

ESP32 系列芯片可以利用 CSI 数据实现动作检测和存在检测。无论是自动调节灯光、风扇,还是节能控制,CSI 技术为智能家居带来了新的可能性。随着 CSI 技术的发展,未来的智能家居将能够更精确地感知和响应我们的行为,实现更高效、更人性化的控制。