正视是什么意思| 为什么左手会发麻| 带下病是什么病| 肝区疼痛吃什么药| 吃降压药有什么副作用| 目赤什么意思| 红斑狼疮吃什么药最好| 五行大林木是什么意思| 锦衣卫是干什么的| 什么是色盲| 合疗和医保有什么区别| 荨麻疹是什么引起| 手脚抽筋是什么原因| 拔牙可以吃什么| 农历六月十二是什么日子| 924是什么星座| 腿硬邦邦的是什么原因| 铝中毒有什么症状| 知进退明得失什么意思| 月牙是什么| 天灾人祸什么意思| 人流需要准备什么东西| 易孕体质是什么意思| 8.11是什么星座| 人流需要准备什么东西| 血糖高喝什么豆浆好| 16周检查什么项目| 属鼠男和什么属相最配| 火牛命五行缺什么| cook什么意思| 榆字五行属什么| 彩铅是什么| 支行行长什么级别| 打开图片用什么软件| 冰山一角是什么生肖| 千张是什么| 贤良淑德后半句是什么| lhrh是什么激素| 白带多吃什么药| 血管夹层是什么病| 母的第三笔是什么| 尿潜血1十是什么原因| 眼睛痛是什么原因| 哔哩哔哩是干什么的| 滂沱是什么意思| 风团是什么原因引起的| 肝病有什么征兆| ck医学上是什么意思| 凌晨两点是什么时辰| 老虎五行属什么| 检车需要什么手续| 牙齿出血是什么病征兆| 鹅吃什么草| 孙悟空原名叫什么| 打醮是什么意思| 北京中秋节有什么活动| 向日葵为什么会随着太阳转动| 为什么人会得抑郁症| 赛治是什么药| 梦见老虎是什么意思| 六月初六是什么星座| 干咳无痰是什么原因引起的| 灵芝有什么功效| 舌头发白是什么情况| 97年是属什么的| 街道办事处属于什么单位| 伯邑考为什么不姓姬| 忽悠什么意思| 萤火虫为什么会发光简单回答| 孕妇便秘吃什么最快排便| 胃左边疼是什么原因| 莆田荔枝什么时候成熟| 属猴和什么属相相冲| 天上的月亮是什么生肖| 2.13是什么星座| 天生丽质什么意思| 美尼尔综合症吃什么药| 吃了就吐是什么原因| 橙子皮泡水喝有什么好处| 弟弟的女儿叫什么| 五指毛桃不能和什么一起吃| facebook是什么| 四川九寨沟什么时候去最好| 什么地指挥| 跳蚤吃什么| 尿胆红素阳性是什么意思| 咽喉痒干咳吃什么药| 愿君多采撷是什么意思| 倒数是什么| 吃番茄有什么好处| camel什么意思| 什么是命运| 白细胞降低是什么原因| 牛肉配什么菜包饺子好吃| tips是什么意思| 阴道发炎用什么药| 梦见自己被抢劫了预示什么| 小孩鼻子出血什么原因| 胶原蛋白的成分是什么| 三轮体空是什么意思| 学架子鼓有什么好处| 鸡血藤手镯有什么功效| 什么是同比| 客服是什么意思| 什么是211大学| 什么值得买怎么用| 什么面粉最好| 西泮片是什么药| 猫五行属什么| 5月出生是什么星座| 植物纤维是什么面料| cindy英文名什么意思| 感冒吃什么消炎药| 秦朝为什么那么快灭亡| 漏尿是什么原因造成的| 茴香豆是什么豆| 低置胎盘有什么危险| 82年的拉菲是什么意思| 柏拉图爱情是什么意思| 狗狗流鼻涕吃什么药| rh血型D阳性是什么意思| 玉兰花什么时候开| 明朝为什么会灭亡| 什么补血补气最好最快| ooc是什么意思| 腋下疼痛什么原因| 吃什么可以帮助睡眠| 鼻子冒热气是什么原因| 耳呜吃什么药最好| 孕吐是什么时候开始| 轻浮的女人是什么意思| 蓝莓什么季节成熟| 喜怒无常是什么意思| 卧室养什么花好| 帕金森病是什么病| 云南是什么民族| 上唇肿胀是什么原因| 咳嗽能吃什么水果| 箨是什么意思| 我在你心里是什么颜色| 阴中求阳是什么意思| 巧克力囊肿有什么症状表现| 麻药过敏什么症状| 扫把和什么是一套的| 有时候会感到莫名的难过是什么歌| 弱点是什么意思| 腺瘤样增生是什么意思| 梦见大蛇是什么预兆| 什么病不能吃西洋参| 头疼恶心吃什么药| 做梦梦见猪是什么意思| 肝硬化失代偿期是什么意思| 做梦梦到大蟒蛇是什么意思| 梦见小麦粒是什么意思| 什么都别说| 独角戏什么意思| 肌酐高用什么药| 七手八脚是什么意思| 转氨酶高是什么原因| 房性心动过速是什么意思| cd8高是什么原因| 重要是什么意思| 做人流挂什么科| me too是什么意思| 红萝卜什么时候种| 同房出血什么原因| 更年期什么年龄开始| 查甲状腺功能挂什么科| 早上起来心慌是什么原因| 诸葛亮长什么样| dw手表属于什么档次| 莫桑钻和钻石有什么区别| 鸡眼长什么样子图片| 什么的亮光| 定增股票是什么意思| 沵是什么意思| 大圆脸适合什么发型| 哪些动物的尾巴有什么作用| 怕冷的女人是什么原因| 直辖市市长是什么级别| 梅干菜是什么菜做成的| 梦到洗衣服是什么意思| 为什么一个月来两次月经| 补气补血吃什么好| 挖苦是什么意思| 巧克力囊肿是什么| 扦插是什么意思| 花旦是什么意思| 血小板太高会导致什么| 什么的火车| 那英姓什么| 印度人为什么用手抓饭吃| 6月26是什么星座| 什么是历史虚无主义| 拒服兵役是什么意思| 省亲是什么意思| 乳腺ca是什么意思| 山茶花什么颜色| 海绵体充血不足吃什么药| 中国最大的海是什么海| 小孩儿咳嗽有什么妙招| 奄奄一息的息是什么意思| 咖啡有什么功效| 切脉切的是什么脉| 纳囊是什么妇科病| 尿酸高什么意思| bnp是什么| 牙龈炎吃什么消炎药| 至是什么意思| 肚脐眼左边疼是什么原因| 9月9日是什么星座| 身体发麻是什么原因| 蜂蜜有什么作用与功效| 看痘痘挂什么科| 三月份什么星座| yet是什么意思| 河南人喜欢吃什么菜| 清胃火吃什么药| 越什么越什么的词语| 邹去掉耳朵旁读什么| 鼻基底用什么填充最好| 什么是滑脉| veromoda是什么牌子| 尿失禁吃什么药最好| 馋肉是身体里缺什么| 学医需要什么条件| 中耳炎吃什么药最有效| 包皮发炎用什么药| 垂体催乳素高是什么原因| 心烦意乱吃什么药| May什么意思| 生孩子前做什么检查| 怎么知道自己五行属什么| 白带多是什么情况| 玻尿酸是什么| 压寨夫人是什么意思| 小孩上吐下泻吃什么药| 梦见红薯是什么意思| 喝茶有什么好处| 青光眼有什么症状| 表哥的儿子叫什么| 神仙是什么意思| 害怕是什么意思| 一朝一夕是什么意思| 尿素偏高是什么意思| 鼻子旁边长痘是什么原因| 小名是什么意思| 海带和什么菜搭配好吃| 后续是什么意思| 安宫牛黄丸治什么病| 基诺浦鞋属于什么档次| 嘴唇起水泡是什么原因| 什么是玄关| 大健康是什么| 化疗前吃什么补充营养| 吴孟达什么时候去世的| 彩铃是什么意思| 马来玉是什么玉| 二尖瓣反流吃什么药| 舌头两边锯齿状是什么原因| 按摩spa是什么意思| 脚背抽筋是什么原因引起的| 小傻瓜是什么意思| 化疗期间吃什么| 百度
这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 ? 论坛首页 ? DIY与开源设计 ? 电子DIY ? [物联网系列][Aduino]Arduino智能灌溉系统(二) 完整版

共4条 1/1 1 跳转至

[物联网系列][Aduino]Arduino智能灌溉系统(二) 完整版

工程师
2025-08-04 23:28:23     打赏
百度 林郑月娥还说,李克强总理给她很大的信心,深信香港会在中央支持下融入国家的发展大局,亦可以与国家共繁荣及共发展,我们这届政府在这方面的工作亦会加倍努力。

简介

在上一篇文章中我对当前系统的整个构成进行了概述, 虽然系统的传感器使用的比较多,但是使用的是Arduino 平台,所以对每个传感器的驱动并不复杂, 因此在本篇文章中我将完成所有组件的构建.  


传感器外围部件清单

1 - 土壤湿度传感器(Analog)

2- 水流量传感器YF-S401

3- OLED 0.96

4- SHT30

5- LTR-329

6- NMOS 用于PWM驱动LED灯板

7- LED灯板

8- 继电器 (如果使用5V可以使用NMOS控制开关, 不需要使用继电器)

9- 12V 水泵 (可以调整为5V)


实物俯视图

image.png

实物平视图

image.png


OLED 显示image.png


Flask上位机监控界面

Snipaste_2025-08-04_14-04-53.png图图表一为系统上电时 PID动态调整灯光亮度的曲线图.


系统稳定时即PID调整后的光照输出 (稳定在了亮度200)

Snipaste_2025-08-04_14-05-38.png


Arduino程序设计

#include <Wire.h>
#include "Adafruit_SSD1306.h"
#include "Adafruit_GFX.h"
#include "Adafruit_LTR329_LTR303.h"
#include "Adafruit_SHT4x.h"
#include <ArduinoJson.h>


// OLED 屏幕配置
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire1, OLED_RESET);

// 传感器对象
Adafruit_LTR329 ltr = Adafruit_LTR329();
Adafruit_SHT4x sht4;

// PID 控制相关
const int pwmPin = 3;         // IO2 PWM 输出
const int targetCh0 = 200;    // 目标 CH0 值

float Kp = 0.18, Ki = 0.0825, Kd = 0.002;
float integral = 0, last_error = 0;
unsigned long lastTime = 0;

volatile unsigned int pulseCount = 0;
unsigned long lastFlowCalcTime = 0;
float flowRate_mLps = 0;   // 毫升每秒
float totalMilliLiters = 0;


const int pumpPin = 4;  // IO4 控制继电器
const int soilThreshold = 600;  // 土壤湿度阈值(需根据实际情况调整)
bool pumping = false;
float pumpStartVolume = 0;
volatile bool isTrunonLight = true;

#define FLOW_SENSOR_PIN 2

void flowPulseISR() {
  pulseCount++;
}


void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("Initializing...");

  pinMode(pwmPin, OUTPUT);

  // 初始化 OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 initialization failed!"));
    while (true);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);

  // 初始化 LTR329
  if (!ltr.begin(&Wire1)) {
    Serial.println("Couldn't find LTR sensor!");
    while (1) delay(10);
  }
  ltr.setGain(LTR3XX_GAIN_2);
  ltr.setIntegrationTime(LTR3XX_INTEGTIME_100);
  ltr.setMeasurementRate(LTR3XX_MEASRATE_200);

  // 初始化 SHT40
  if (!sht4.begin(&Wire1)) {
    Serial.println(F("SHT40 sensor not found!"));
    while (1);
  }
  sht4.setPrecision(SHT4X_HIGH_PRECISION);
  sht4.setHeater(SHT4X_NO_HEATER);

  pinMode(FLOW_SENSOR_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(FLOW_SENSOR_PIN), flowPulseISR, RISING);

  pinMode(pumpPin, OUTPUT);
  digitalWrite(pumpPin, LOW);  // 默认关闭水泵

}

void loop() {
   if(Serial.available() >0)
    {
      char c = Serial.read();
      Serial.println(c);
      if(c == '1')
      {
        isTrunonLight = !isTrunonLight;
      }
    
    }
  uint16_t ch0 = 0, ch1 = 0;
  float temperature = 0, humidity = 0;

  // 读取光照
  if (ltr.newDataAvailable()) {
     StaticJsonDocument<256> doc;
    bool valid = ltr.readBothChannels(ch0, ch1);
    if (valid && isTrunonLight) {
      // PID 计算
      float error = targetCh0 - ch0;
      unsigned long now = millis();
      float dt = (now - lastTime) / 1000.0;
      lastTime = now;

      integral += error * dt;
      float derivative = (error - last_error) / dt;
      last_error = error;

      float output = Kp * error + Ki * integral + Kd * derivative;
      output = constrain(output, 0, 255);
      analogWrite(pwmPin, (int)output);
      // 每 1000ms 计算一次水流量
      if (millis() - lastFlowCalcTime >= 1000) {
        noInterrupts();
        unsigned int pulses = pulseCount;
        pulseCount = 0;
        interrupts();

        // 每脉冲 ≈ 2.22 毫升(YF401)
        flowRate_mLps = pulses * 2.22;
        totalMilliLiters += flowRate_mLps;

        lastFlowCalcTime = millis();
      }
          
        display.print("PWM: ");
        display.println((int)output);
        doc["pwm"] = (int)output;

    }else{
       analogWrite(pwmPin, 0);
    }
     // 读取温湿度
      sensors_event_t temp_event, hum_event;
      sht4.getEvent(&hum_event, &temp_event);
      temperature = temp_event.temperature;
      humidity = hum_event.relative_humidity;

      // 读取土壤湿度
      int soilValue = analogRead(A5);

      // 显示到 OLED
      display.clearDisplay();
      display.setCursor(0, 0);
      display.print("Temp: ");
      display.print(temperature, 1);
      display.println(" C");

      display.print("Humidity: ");
      display.print(humidity, 1);
      display.println(" %");

      display.print("Light CH0: ");
      display.println(ch0);

      display.print("Target: ");
      display.println(targetCh0);


      display.print("Flow: ");
      display.print(flowRate_mLps, 1);
      display.println(" mL/s");

      display.print("Total: ");
      display.print(totalMilliLiters, 0);
      display.println(" mL");

      if (!pumping && soilValue > soilThreshold) {
        digitalWrite(pumpPin, HIGH);
        pumping = true;
        pumpStartVolume = totalMilliLiters;
      }

      // 已在泵水 -> 检查是否达到100mL
      if (pumping && (totalMilliLiters - pumpStartVolume >= 100.0)) {
        digitalWrite(pumpPin, LOW);
        pumping = false;
        
      }

      int barLength = map(soilValue, 1023, 0, 0, 100);  // 显示湿度条(反映湿度)
      display.drawRect(0, 58, 100, 5, SSD1306_WHITE);      // 条边框
      display.fillRect(0, 58, barLength, 5, SSD1306_WHITE); // 填充条
      display.display();
     

      doc["temperature"] = temperature;
      doc["humidity"] = humidity;
      doc["light_ch0"] = ch0;
      doc["light_ch1"] = ch1;
      doc["target"] = targetCh0;
      doc["flow_mLps"] = flowRate_mLps;
      doc["total_mL"] = totalMilliLiters;
      doc["soil"] = soilValue;
      doc["pump"] = pumping;

      serializeJson(doc, Serial);
      Serial.println();
  }

  delay(500);  // 避免刷新过快
}


在上述的程序中对PID的调整稍微有一点麻烦, 这里有一个调整的思路就是, 首先将KI和KD都设置为0, 首先调整KP, 这里调整KP的时候可能会出现抖动的情况. 当抖动并不是很大的时候(LED闪烁频率没有那么大), 然后调整KI用来消除稳态误差. 当光照逐渐变稳定的时候再尝试调整KD来减缓超调与振荡.  此时LED的亮度已经已经稳定了. 如果觉得LED的亮度上升太慢或者下降太慢的话再逐渐改变KP的值. 到最后我系统中所调整的值为:

float Kp = 0.18, Ki = 0.0825, Kd = 0.002;

使其程序在运行的时候光照强度(可见光 + 红外光) 的亮度稳定在了 200. (上述的PID算法同样可以用于温控系统, 比如说恒温箱等)

核心代码如下所示

      float error = targetCh0 - ch0;
      unsigned long now = millis();
      float dt = (now - lastTime) / 1000.0;
      lastTime = now;

      integral += error * dt;
      float derivative = (error - last_error) / dt;
      last_error = error;

      float output = Kp * error + Ki * integral + Kd * derivative;
      output = constrain(output, 0, 255);
      analogWrite(pwmPin, (int)output);


Python 上位机程序设计

python的上位机是运行在虚拟的树莓派OS中的, 其中和Arduino的通讯主要是采用串口通讯. 其后端框架采用的是Flask, 支持将数据保存到数据库中. 

image.png

其主要的核心逻辑即读取串口的JSON输入, 然后通过HTML的定时器定时发送HTTP请求来读取串口数据,然后使用chart.js进行绘图.

串口线程(读取Arduino串口数据)


# 串口线程函数
def read_serial():
    global latest_data
    try:
        while True:
            line = ser.readline().decode('utf-8').strip()
            if line:
                try:
                    data = json.loads(line)  # 解析 JSON 数据
                    latest_data = data  # 更新最新的数据
                    print("Received:", data)
                    insert_data_to_db(data)  # 插入数据到数据库
                except json.JSONDecodeError:
                    print("Invalid JSON:", line)
    except serial.SerialException as e:
        print("Serial error:", e)


# 启动串口线程
serial_thread = threading.Thread(target=read_serial, daemon=True)
serial_thread.start()


返回串口数据用于前端绘图


@app.route('/data')
def get_data():
    return jsonify(latest_data)


定时请求串口数据:

 async function fetchData() {
        try {
            const res = await fetch('/data');
            const data = await res.json();
            document.getElementById("json").textContent = JSON.stringify(data, null, 2);

            const now = new Date().toLocaleTimeString();
            if (labels.length >= maxDataPoints) {
                labels.shift();
                lightData.shift();
                tempData.shift();
                humidityData.shift();
                soilData.shift();
            }

            labels.push(now);
            lightData.push(data.light_ch0);
            tempData.push(data.temperature);
            humidityData.push(data.humidity);
            soilData.push(data.soil);

            [lightChart, tempChart, humidityChart, soilChart].forEach((chart, i) => {
                chart.data.labels = labels;
                chart.data.datasets[0].data = [lightData, tempData, humidityData, soilData][i];
                chart.update();
            });
        } catch (err) {
            console.error("Failed to fetch data:", err);
        }
    }

    setInterval(() => {
        fetchData();
        fetchStats();
    }, 2000);


附件

project_without_env.zip

注意: 系统并不需要使用上位机程序进行监控, 不运行上位机对Arduino的执行没有任何影响




关键词: Arduino     智能灌溉系统     DIY    

专家
2025-08-04 11:39:07     打赏
2楼

感谢分享


专家
2025-08-04 11:42:57     打赏
3楼

感谢分享


专家
2025-08-04 11:44:44     打赏
4楼

学习一下


共4条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]
杨柳代表什么生肖 慰安妇什么意思 孕吐 吃什么 什么是胎记 双11是什么节日
炒菜锅什么牌子好 推头是什么意思 nf是什么 dpm值阳性什么意思 宠物蛇吃什么
化验血常规能查出什么 付梓什么意思 早筛是检查什么项目 遗传物质是什么 尘埃落定什么意思
胡汉三回来了什么意思 姐姐的老公叫什么 看抑郁症挂什么科 多字五行属什么 花容月貌是什么意思
一品诰命夫人是什么意思hcv9jop3ns7r.cn 哒哒是什么意思hcv8jop4ns5r.cn 世界上最贵的烟是什么烟hcv9jop7ns1r.cn 赡养什么意思weuuu.com 气胸病是什么原因引起的xinmaowt.com
体检喝水了有什么影响hcv8jop9ns9r.cn 东方不败练的什么武功hcv8jop4ns5r.cn 方圆是什么意思adwl56.com silence是什么意思hcv8jop9ns1r.cn 去医院查怀孕挂什么科jinxinzhichuang.com
肩胛骨突出是什么原因luyiluode.com 小腿肚酸胀是什么原因hcv7jop9ns3r.cn 铁锈色痰见于什么病hcv9jop1ns6r.cn 股骨长径是指胎儿什么hcv9jop7ns0r.cn 副县长是什么级别shenchushe.com
左手虎口有痣代表什么hcv9jop5ns3r.cn l表示什么单位hcv7jop7ns4r.cn 放疗什么意思hcv9jop4ns0r.cn 心脏杂音是什么意思hcv8jop8ns8r.cn dx是什么hcv9jop3ns6r.cn
百度