sansui是什么牌子| black什么颜色| 萝卜喝醉了会变成什么| 小211是什么意思| 不宁腿综合症吃什么药| 纯粹是什么意思| 左束支传导阻滞是什么意思| 冤家是什么意思| 什么是软饮料| 云是什么生肖| 人为什么要洗澡| 梦到自己掉牙齿是什么预兆| 烟花三月是什么意思| 头痛是什么病的前兆| 普拉提是什么意思| 啐了一口是什么意思| 63年的兔是什么命| 皮赘用什么药膏去除| 尿液是什么味道| 晧字五行属什么| 减肥晚上适合吃什么水果| 什么粥最养胃健脾| 撇清关系是什么意思| 神经大条是什么意思| 2月13号是什么星座| 拉泡泡屎是什么原因| rpa是什么| 做梦梦到牛是什么意思| 黑胡桃色是什么颜色| 灵长类动物是指什么| 什么材质的拖鞋不臭脚| 丹毒用什么抗生素| 什么人容易得老年痴呆| hg是什么元素| 一级法官是什么级别| 油嘴滑舌指什么生肖| 脸上长痘是什么原因| 规律宫缩是什么感觉| 经期延长是什么原因引起的| 免疫缺陷是什么意思| 我流是什么意思| 猫上门为什么不能赶走| 低头头晕是什么原因| r车标是什么牌子| 雁过拔毛是什么意思| 水钻是什么材质| 决明子有什么作用| 早泄吃什么药最好| 煮海带放什么容易烂| 收缩压是什么| 坐月子吃什么| 耳洞发炎用什么药| 梦见别人给我介绍对象是什么意思| 借你吉言是什么意思| 荔枝什么人不能吃| 产妇吃什么水果| 梦见鞋丢了是什么意思| 催乳素高是什么原因| baumwolle是什么面料| 加持是什么意思| 月经期能吃什么水果| 宫外孕是什么导致的| 木加石读什么| 女的右眼跳代表什么| 青蛙爱吃什么| 卷腹是什么| 胎元是什么意思| 虫草什么时候吃最好| 产复欣颗粒什么时候吃| 大便特别臭是什么原因| 火鸡面为什么这么贵| 吃什么有助于骨头恢复| 夜里2点到3点醒什么原因| 九九年属什么| 吃什么食物补钙最快| 主任科员是什么级别| 贫血做什么检查能查出来| 什么牛排最好吃| 公知是什么意思| 无可奈何的笑是什么笑| 知鸟吃什么| 杰作是什么意思| 2028是什么年| 黄眉大王是什么妖怪| 较前相仿是什么意思| 小麦是什么粮食| 胶水用什么能洗掉| 病毒性感冒发烧吃什么药| 化痰止咳吃什么药最好| 孕妇缺铁吃什么食物好| 膝盖擦伤用什么药| allin什么意思| 耳朵痒痒用什么药| 星期一右眼皮跳是什么预兆| 白醋泡脚有什么好处| 热锅上的蚂蚁是什么意思| 心悸吃什么中成药| 果冻是什么做的| 克卜勒是什么意思| 宿醉什么意思| 幻听是什么原因引起的| 为什么怀不上孩子| 3月30号是什么星座| 子宫肌瘤什么不能吃| 韭菜籽配什么壮阳最猛| 什么动物的牙齿最多| 羽字五行属什么的| 甲状腺过氧化物酶抗体高说明什么| 血管瘤有什么危害吗| 丁香泡水喝有什么功效和作用| 肺气肿是什么原因导致的| 鼻子痒是什么原因| 尿素氮肌酐比值偏高是什么原因| 梵克雅宝为什么那么贵| 胎盘成熟度1级是什么意思| 牙齿突然出血是什么原因| 凤凰代表什么生肖| 虎头蜂泡酒有什么功效| 白条是什么鱼| 刻代表什么生肖| 名媛什么意思| 4月7号是什么星座| 寸金难买寸光阴什么意思| 血分析能查出什么| 鬼畜是什么意思| poscer是什么牌子的手表| 今天突然拉稀拉出血什么原因| 毛泽东的女儿为什么姓李| 足月是什么意思| 更年期失眠吃什么药效果好| aed什么意思| 乳糖是什么糖| 脸无缘无故的肿是什么原因| 蚕豆有什么营养| 猫鼻支是什么症状| opd是什么意思| 黄色搭配什么颜色| bgb是什么意思| 仓鼠和老鼠有什么区别| 宁波有什么特产| 小浣熊吃什么| 早期教育是什么| 常熟有什么好玩的地方| 肌张力高有什么症状| 学信网上的报告编号是什么| 西凤酒是什么香型| 十万为什么| 怦然心动什么意思| pu是什么| 心率快是什么原因引起的| 医院附近适合做什么生意| 怀孕前三个月应该注意什么| 维生素e的功效与作用是什么| 胸痛吃什么药| 蚊子有什么用| 化疗吃什么补白细胞| 颈椎病头晕吃什么药好| 卢沟桥事变又称什么| 膝关节咔咔响是什么原因| 脑干出血是什么原因| 奀是什么意思| 私生子什么意思| mask是什么意思| 办独生子女证需要什么材料| 四风指什么| 梦见滑雪是什么意思| 规格是指什么| 壮丁是什么意思| 含五行属什么| 扁桃体发炎引起的发烧吃什么药| 足内翻是什么样子的| 土豆发芽到什么程度不能吃| 欣什么若什么| 中产阶级的标准是什么| 三聚净戒是指什么戒| 什么是预防医学| 目加此念什么| 古丽是什么意思| 85年属于什么生肖| 脚肿什么原因| 端午节都吃什么菜好| 共襄盛举是什么意思| 汤姆福特属于什么档次| 经常喝茶叶有什么好处| 敲打是什么意思| 母亲吃什么退婴儿黄疸| 阉割是什么意思| 鸡炖什么补气血| 司长是什么级别| 乔峰和洪七公什么关系| 雷猴是什么意思| 膀胱钙化是什么意思| 破绽是什么意思| 逃出生天什么意思| 男人胸前有痣代表什么意思| 父亲节要送什么礼物好| ckd是什么意思| 山竹树长什么样子图片| 五步蛇又叫什么蛇| eo什么意思| 低血糖和贫血有什么区别| 反流性食管炎不能吃什么食物| 真丝用什么洗| 什么是碳水化合物| 扁平化管理是什么意思| nautical什么牌子| 秀女是什么意思| 迪拜为什么那么有钱| 做肠镜要做什么准备| 大便干硬是什么原因| 成年人改名字需要什么手续| attach什么意思| 政委是什么军衔| 心肌炎吃什么食物最好| 肠息肉是什么原因造成的| 晚上10点是什么时辰| 张若昀原名叫什么| 貔貅长什么样| 无花果和什么不能一起吃| 阴道炎不能吃什么| 痒是什么原因引起的| 火华读什么| 白带多用什么药效果好| 男孩学什么专业有前途| barry什么意思| 试管什么方案好| 不经历风雨怎能见彩虹是什么意思| 大腿外侧是什么经络| 哈密瓜是什么季节的水果| 有心火是什么症状| 什么和什么丽| 吃什么皮肤白的最快| 拆线去医院挂什么科| 斐字五行属什么| 小腹左边疼是什么原因| 前白蛋白低是什么原因| 喜欢喝冰水是什么原因| 熊猫为什么被称为国宝| 红馆是什么地方| 脖子爱出汗是什么原因| 嫩牛五方什么意思| 170是什么号码| 什么人靠别人的脑袋生活| biubiubiu是什么意思| 信必可为什么轻微哮喘不能用| 什么时间吃水果最好| 使婢差奴过一生是什么意思| 聊表心意是什么意思| 大姨妈发黑是什么原因| 苟同什么意思| 炒菜放什么调料最好吃| 85年属牛是什么命| 空孕囊是什么原因造成的| 经常拉肚子是什么原因引起的| 白炽灯属于什么光源| 跑路什么意思| 甘油三酯低是什么原因| 肝什么相照| 预防水痘吃什么药| 九浅一深什么意思| 天体是什么| adidas是什么品牌| 莴笋炒什么好吃| 玉皇大帝叫什么名字| 百度
这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 ? 论坛首页 ? 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 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]
plv是什么意思 单脐动脉是什么意思 炸膛什么意思 肠道感染吃什么消炎药 脚踝浮肿是什么原因
大腿麻木是什么原因 什么叫梅毒 晚餐吃什么好 什么是膳食纤维 碧螺春属于什么茶类
孕早期适合吃什么水果 不偏不倚是什么意思 罗汉果有什么功效 摩羯座的幸运花是什么 伤口消毒用什么好
中暑的症状是什么 冒汗是什么原因 痞块是什么意思 什么然而止 小肚鸡肠是什么意思
为什么睡不着mmeoe.com 子宫肌瘤伴钙化是什么意思hcv9jop3ns5r.cn 三头六臂开过什么生肖hcv9jop0ns8r.cn 失眠用什么药hcv9jop1ns6r.cn 结肠ca是什么意思hcv9jop3ns1r.cn
勇气是什么意思hcv9jop1ns3r.cn 林子大了什么鸟都有hcv9jop2ns1r.cn 岱是什么意思hcv9jop8ns3r.cn 咳嗽有痰吃什么水果hcv8jop6ns3r.cn 什么方法不掉头发hcv8jop2ns5r.cn
孙俪最新电视剧叫什么hcv9jop1ns0r.cn 富贵病是什么病helloaicloud.com 孕妇腿抽筋是什么原因hcv9jop4ns9r.cn 偏安一隅是什么意思hcv8jop9ns7r.cn 春天的花开秋天的风是什么歌hcv8jop4ns4r.cn
打完狂犬疫苗有什么不良反应hcv7jop9ns4r.cn 骨钙素是什么hcv9jop0ns6r.cn 出汗多吃什么好hcv9jop8ns1r.cn 葛根粉吃了有什么作用hcv9jop7ns0r.cn 来月经属于什么期hcv9jop4ns3r.cn
百度