数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 164|回复: 6

[Arduino] AHT20+BMP280制作温湿度气压+天气预报12864点阵显示

[复制链接]
发表于 7 天前 | 显示全部楼层 |阅读模式

需要在arduino ide-管理库-自己安装一下对应的库,LCD用的是瑞立德12864 i2c接口,程序基本上兼容大部分arduino比如uno r3 r4 due
LCD库是我自己修改过的,厂家提供那个没有直接使用wire库,导致多个i2c使用有问题,而且不能用在avr以外的mcu





#include <Wire.h>
#include <Adafruit_AHTX0.h>      // AHT20 专用库
#include <Adafruit_BMP280.h>     // BMP280 库
#include "RSCG12864B.h"
#include "Arduino_LED_Matrix.h"


ArduinoLEDMatrix matrix;

Adafruit_AHTX0 aht;              // 创建 AHT20 对象
Adafruit_BMP280 bmp;             // 创建 BMP280 对象

float temp_aht,humidity,temp_bmp,pressure,altitude,dewPoint ;

// 全局变量 - 可以在任何函数中访问
float currentTemperature = 0;
float currentHumidity = 0;
// 多个定时任务同时执行
unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;
unsigned long previousMillis3 = 0;
unsigned long previousMillis4 = 0;

const long interval1 = 500;   // 任务1: ms秒
const long interval2 = 2500;  // 任务2: ms秒
const long interval3 = 500;  // 任务3: ms秒
const long interval4 = 5000;  // 任务4: ms秒

// 定义存储历史数据的数组大小 (例如:存储60个点,每5秒一个,即5分钟窗口)
const int HISTORY_SIZE = 60;
float pressureHistory[HISTORY_SIZE];
int currentIndex = 0;
bool historyFull = false;




  //定义字符串用于中文显示
  char f1[]={0XBC,0XB4,0XBD,0XAB,0X20,0XCF,0XC2,0XD3,0XEA,0,};//即将 下雨
  char f2[]={0XBC,0XB4,0XBD,0XAB,0X20,0XCF,0XC2,0XD3,0XEA,0,};//即将 晴天
  char f3[]={0XD7,0XAA,0XD2,0XF5,0X20,0XCF,0XC2,0XD3,0XEA,0,};//转阴 下雨
  char f4[]={0XCC,0XEC,0XC6,0XF8,0X20,0XBA,0XC3,0XD7,0XAA,0,};//天气 好转
  char f5[]={0XCC,0XEC,0XC6,0XF8,0X20,0XC6,0XBD,0XCE,0XC8,0,};//天气 平稳
    char f6[]={0XB8,0XC9,0XD4,0XEF,0,};//干燥
    char f7[]={0XC6,0XAB,0XB8,0XC9,0,};//偏干
    char f8[]={0XCA,0XE6,0XCA,0XCA,0,};//舒适
    char f9[]={0XC2,0XD4,0XCA,0XAA,0,};//略湿
    char f10[]={0XB3,0XB1,0XCA,0XAA,0,};//潮湿
    char f11[]={0XC3,0XC6,0XC8,0XC8,0,};//闷热
    char f12[]={0XB5,0XE7,0XB3,0XD8,0XB5,0XE7,0XC1,0XBF,0XB2,0XE2,0XC1,0XBF,0,};//电池电量测量
    char f13[]={0XC7,0XEB,0XC1,0XAC,0XBD,0XD3,0XB5,0XE7,0XB3,0XD8,0,};//请链接电池
    char f14[]={0XB2,0XE2,0XC1,0XBF,0X20,0X20,0XCD,0XEA,0XB3,0XC9,0,};//测量 完成
      
   

void setup() {
  Serial.begin(9600);
  // 初始化 I2C
  Wire.begin();
analogReadResolution(12);
  // --- 初始化 AHT20 (使用 Adafruit_AHTX0 库)---
aht.begin();
bmp.begin(0x77);
  
  // 可选:配置 BMP280 采样模式
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,// 普通模式
                  Adafruit_BMP280::SAMPLING_X16,// 温度过采样 ×2
                  Adafruit_BMP280::SAMPLING_X16,// 气压过采样 ×16
                  Adafruit_BMP280::FILTER_X16,// IIR滤波系数 ×4
                  Adafruit_BMP280::STANDBY_MS_500);// 间隔250ms
                  
  // 初始化LCD
  delay(500);
  RSCG12864B.begin();
  RSCG12864B.display_on();
  RSCG12864B.clear();
  //初始化LED点阵
  matrix.begin();
  // 加载并播放开机动画
  matrix.loadSequence(LEDMATRIX_ANIMATION_TETRIS_INTRO);
  matrix.play(true);  // true = 无限循环

}

void loop() {

  unsigned long currentMillis = millis();
  
  // 声明事件变量
  sensors_event_t humidity_event, temp_event;
  // 任务1
  if (currentMillis - previousMillis1 >= interval1) {
    previousMillis1 = currentMillis;
//     --- 读取 AHT20 数据(Adafruit_AHTX0 的简洁方式)---
  sensors_event_t humidity, temp;
  aht.getEvent(&humidity, &temp);   // 一次性获取温湿度
  // 将温湿度结果赋值给自定义变量

  currentTemperature = temp.temperature;
  currentHumidity = humidity.relative_humidity;

  // 调用函数计算露点
    dewPoint = calculateDewPoint(currentTemperature, currentHumidity);
// Serial.print(F("AHT20 - 温度: "));
// Serial.print(temp.temperature, 1);
// Serial.println(F(" °C"));
// Serial.print(F("AHT20 - 湿度: "));
// Serial.print(humidity.relative_humidity, 1);
// Serial.println(F(" %"));
//digitalWrite(12,HIGH);
  }
// 任务2
  if (currentMillis - previousMillis2 >= interval2) {
    previousMillis2 = currentMillis;
  // --- 读取 BMP280 数据 ---
  temp_bmp = bmp.readTemperature();
  pressure = bmp.readPressure() / 100.0F;



// Serial.print(F("BMP280 - 温度: "));
  //Serial.print(temp_bmp, 1);
// Serial.println(F(" °C"));
  //Serial.print(F("BMP280 - 气压: "));
// Serial.print(pressure, 2);
// Serial.println(F("hPa"));

   // 你也可以从 BMP280 读取并计算近似海拔 (需要输入当前海平面气压作为参考)
  altitude = bmp.readAltitude(1013.25); // 1013.25 hPa 是标准海平面气压
//  Serial.print(F("BMP280 - 海拔: "));
//  Serial.print(altitude, 1);
// Serial.println(F(" 米"));
  
  }
  // 任务3
if (currentMillis - previousMillis3 >= interval3) {
    previousMillis3 = currentMillis;
lcd();
  }
    // 任务4
    if (currentMillis - previousMillis4 >= interval4) {
    previousMillis4 = currentMillis;
    addPressureSample(pressure); // 将新数据加入环形缓冲区
      // 当数据足够时,进行趋势分析和预测
  if (historyFull) {
    float slope = calculateTrend(pressureHistory, HISTORY_SIZE);
    String weather = getWeatherPrediction(slope);
   
    Serial.print("气压变化斜率 (hPa/h): ");
    Serial.println(slope * 12); // 因为我们每5秒采一个点,斜率需要换算成每小时的变化率
    Serial.print("天气预测: ");
    Serial.println(weather);
if (weather == "气压快速下降,可能下雨或风暴") {
    RSCG12864B.print_string_12_xy(62, 42, f1);
} else if (weather == "气压持续上升,天气将转晴") {
    RSCG12864B.print_string_12_xy(62, 42, f2);
} else if (weather == "气压下降,天气可能转阴或有雨") {
    RSCG12864B.print_string_12_xy(62, 42, f3);
} else if (weather == "气压上升,天气正在好转") {
    RSCG12864B.print_string_12_xy(62, 42, f4);
} else if (weather == "气压稳定,天气无明显变化") {
    RSCG12864B.print_string_12_xy(62, 42, f5);
}
  }
  }   
}

void lcd(){
  
  //int br=map(analogRead(A0),0,4095,0,255);
    RSCG12864B.brightness(255); // 设置中等亮度
char str1[20];char str2[20];char str3[20];char str4[20];
char str5[20];char str6[20];
  sprintf(str1, "%3.2f", currentTemperature);  // 调整为8宽度
  sprintf(str2, "%3.2f", currentHumidity);  // 调整为8宽度
  sprintf(str3, "%4.2f", temp_bmp);  // 调整为8宽度
  sprintf(str4, "%4.2f", pressure);  // 调整为8宽度
  sprintf(str5, "%4.2f", altitude);  // 调整为8宽度
  sprintf(str6, "%4.2f", dewPoint);  // 调整为8宽度
  RSCG12864B.print_string_5x7_xy(2,2,"AHT20");
  RSCG12864B.print_string_5x7_xy(2,12,"T");
  RSCG12864B.print_string_5x7_xy(2,22,"RH");
  RSCG12864B.print_string_5x7_xy(46,12,"'C");
  RSCG12864B.print_string_5x7_xy(46,22,"%");
  RSCG12864B.print_string_5x7_xy(2,32,"DP");
  RSCG12864B.print_string_5x7_xy(46,32,"'C");
   
  RSCG12864B.print_string_5x7_xy(63,2,"BMP280");
  RSCG12864B.print_string_5x7_xy(114,12,"'C");
  RSCG12864B.print_string_5x7_xy(108,22,"hpa");
  RSCG12864B.print_string_5x7_xy(120,32,"m");

  RSCG12864B.draw_line(60, 0, 60, 62);     // 垂直线
  RSCG12864B.draw_line(0, 10, 127, 10);     // 横线
  RSCG12864B.draw_line(0, 20, 127, 20);     // 横线  
  RSCG12864B.draw_line(0, 30, 127, 30);     // 横线
  RSCG12864B.draw_line(0, 40, 127, 40);     // 横线  
  RSCG12864B.draw_rectangle(0, 0, 127, 63);//矩形 边框
  
  RSCG12864B.print_string_5x7_xy(16,12,str1);
  RSCG12864B.print_string_5x7_xy(16,22,str2);
  RSCG12864B.print_string_5x7_xy(63,12,str3);
  RSCG12864B.print_string_5x7_xy(63,22,str4);
  RSCG12864B.print_string_5x7_xy(16,32,str6);
  RSCG12864B.print_string_5x7_xy(63,32,str5);
if(dewPoint<=5){
  RSCG12864B.print_string_12_xy(2,42,f6);
  }
  else if(dewPoint>5&&dewPoint<=10){
  RSCG12864B.print_string_12_xy(2,42,f7);
  }
    else if(dewPoint>10&&dewPoint<=16){
  RSCG12864B.print_string_12_xy(2,42,f8);
  }
    else if(dewPoint>16&&dewPoint<=18){
  RSCG12864B.print_string_12_xy(2,42,f9);
  }
    else if(dewPoint>18&&dewPoint<21){
  RSCG12864B.print_string_12_xy(2,42,f10);
  }
    else if(dewPoint>21){
  RSCG12864B.print_string_12_xy(2,42,f11);
  }
  }
  

  // 计算露点温度(摄氏度)
double calculateDewPoint(double temp, double humidity) {
    // Magnus公式中的常数
    double a = 17.67;
    double b = 243.5; // 单位:°C

    // 计算中间变量 γ (gamma)
    double gamma = (a * temp / (b + temp)) + log(humidity / 100.0);

    // 计算露点温度 Td
    double dewPoint = (b * gamma) / (a - gamma);

    return dewPoint;
}
// 将新气压值加入环形缓冲区
void addPressureSample(float pressure) {
  pressureHistory[currentIndex] = pressure;
  currentIndex = (currentIndex + 1) % HISTORY_SIZE;
  if (currentIndex == 0) historyFull = true;
}

// 最小二乘法计算斜率
float calculateTrend(float* data, int size) {
  if (!historyFull) return 0;
  
  float sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0;
  int n = size;
  
  for (int i = 0; i < n; i++) {
    // 从环形缓冲区中按时间顺序取出数据
    int idx = (currentIndex + i) % n;
    float x = i; // 用索引作为时间轴
    float y = data[idx];
   
    sum_x += x;
    sum_y += y;
    sum_xy += x * y;
    sum_x2 += x * x;
  }
  
  float denominator = (n * sum_x2 - sum_x * sum_x);
  if (denominator == 0) return 0;
  
  // 斜率公式: (n*Σxy - Σx*Σy) / (n*Σx² - (Σx)²)
  float slope = (n * sum_xy - sum_x * sum_y) / denominator;
  return slope;
}

// 根据斜率预测天气
String getWeatherPrediction(float slopePerPoint) {
  // 将斜率换算为每小时的变化量 (hPa/h)
  // 因为我们每个点间隔5秒,每小时有720个点。所以 slope_per_hour = slope_per_point * 720 / 采样间隔(秒)?
  // 更简单的方法:slope_per_hour = slope_per_point * (3600 / SAMPLE_INTERVAL_SECONDS)
  float slope_per_hour = slopePerPoint * (3600.0 / 5.0); // 这里5是采样间隔(秒)
  
  if (slope_per_hour < -1.0) {
    return "气压快速下降,可能下雨或风暴";      
  } else if (slope_per_hour > 1.0) {
    return "气压持续上升,天气将转晴";
  } else if (slope_per_hour < -0.5) {
    return "气压下降,天气可能转阴或有雨";
  } else if (slope_per_hour > 0.5) {
    return "气压上升,天气正在好转";
  } else {
    return "气压稳定,天气无明显变化";
  }
}



////////////////////////////////////////////////////////////


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x

打赏

参与人数 1家元 +90 收起 理由
家睦 + 90

查看全部打赏

发表于 7 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 7 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 7 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 7 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

 楼主| 发表于 7 天前 来自手机浏览器 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 昨天 11:16 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 微信登录

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2026-4-28 00:50 , Processed in 0.265201 second(s), 13 queries , Gzip On, Redis On.

Powered by Discuz!

© MyDigit.Net Since 2006

快速回复 返回顶部 返回列表