本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页
最热资讯


亲,“电路城”已合并升级到更全、更大、更强的「新与非网」。点击查看「新与非网」
该环境监测器由太阳能Wio终端、多个传感器以及一个全球定位系统组成,Wio终端上的液晶显示屏不仅可以显示结果,还可以用来在地图上显示所测量的不同位置。该监测器由太阳能电池板和一个可充电的锂离子电池供电,它们可以与Wio终端连接。
硬件组件:
设备设计:
为了完全确定环境参数,该监测器包括4个不同的传感器;SCD30、模拟紫外线传感器、电容式湿度传感器和温度传感器。
SCD30传感器可用于同时测定空气湿度、温度和CO2浓度;紫外线传感器,用于确定土壤温度和湿度的设备。
数据显示在Wio终端屏幕上,并在位置或环境传感器的情况下自动更新。在土壤测量的情况下,一旦按下按钮,测量就会更新,以避免进行与土壤状态无关的测量。使用Wio终端屏幕的传感器界面如下所示。
运行该项目所需的电力由太阳能电池板提供。该太阳能电池板可以与电池结合,以便连续测量不同的参数。因此,它可以用于现场应用,以确定不同的环境参数并记录它们。
(设备:包括传感器、电池、太阳能充电器)
无线终端接口:
Wio终端设备上的三个按钮,每个按钮上显示不同的屏幕。
当按下第一个按钮上时,设备显示由不同传感器和全球定位系统测量的当前数据。按下设备上的蓝色按钮后,传感器会记录当前数据,包括用户的位置,稍后可以在地图中观察到这些数据。地图上的这个位置可以向用户指示测量的位置。
当按下第二个按钮时,用户可以看到记录的数据,并进行土壤测量。用户最多可以进行10次不同的测量并记录下来,每次测量的次数如下所示显示在屏幕上。当用户切换到带有地图显示的第三个菜单时,还会显示每个样品测量的位置。
最后,在第三个菜单上,可以看到测量区域的地图。在测量前,用户必须下载该地图,并在以下时间上传至Wio终端。图像可以从不同的来源获取,如谷歌地图,经度和纬度的限制必须记录并添加到程序中,以便正确定位彩色圆圈。用户的当前位置由红色圆圈指示,而第二个菜单中指示的过去位置可以用蓝色看到。
(本项目中可用于测量环境 参数的三种不同显示器的图片)
代码:
#include <Seeed_FS.h>
#include "TFT_eSPI.h"
#include <SPI.h>
#include <SD/Seeed_SD.h>
#include <Wire.h>
#include "SparkFun_SCD30_Arduino_Library.h"
#include <RawImage.h>
#include <SoftwareSerial.h>
#include <TinyGPS++.h>
//我们定义类的类型对于我们将在整个项目中使用不同的传感器(gps、光和气体传感器)
SCD30 airSensor;
TinyGPSPlus gps;
//还定义了类的类型对于屏幕中对象的显示
TFT_eSPI tft;
//指出了引脚对于全球定位系统设备
SoftwareSerial mySerial(2, 3);
//定义代码中使用的变量
double dist_LAT = 34.9722899, dist_LONG = 138.3868869;
String p_hour, p_lat, p_lng, p_alt, p_sat, p_date;
String p_my_speed, p_my_course, p_dist_dTo, p_dist_cTo;
String pdist_LAT, pdist_LONG;
String sdist_LAT = String(dist_LAT);
String sdist_LONG = String(dist_LONG);
String Date[10], Dat;
int menu = 1;
byte RX1;
uint8_t Cut = 78;
char buffer[64], Data [64], Message; // 通过串行端口接收数据的缓冲阵列
int count=0; //缓冲区阵列计数器
double Val1, Val2;
int count3 = 0;
int y, Latc, Lonc, i, j, Lat[10], val1, val2, val3, s, p, z, tUV, tlux;
float tTemp, tHum, tCO2, Buff;
uint8_t val[2];
double LatLim1, LatLim2, LonLim1, LonLim2;
String N_Stemp, p_Temp, p_Stemp, N_mois, N_EC, N_CO2, N_Hum, N_Temp, p_STemp, p_mois, p_EC, p_CO2, p_Hum;
int Log_f = 0;
String N_date, hour0;
unsigned long p_time;
int N_y, N_m, N_d;
String hr1, min1, sec1;
String F_name, N_Mois, p_Mois, nhour0[5];
bool sd;
float tEC, tMois, tStemp;
int l, Lng[10];
String N_lux, N_UV, p_lux, p_UV;
int Num = 0;
int k = 1;
float lux[10], Temp[10], Hum[10], CO2[10], Stemp[10], EC[10], Mois[10], UV[10];
String Longitude[10], Latitude[10], Alt[10], Sat[10];
static const int MAX_SATELLITES = 40;
float uUV, ulux, uCO2, uSTemp, uHum, uTemp, uEC, uMois;
//我们将初始化TinyGPS++库中的自定义类型,以确定卫星的位置、高度和数量
TinyGPSCustom ExtLat(gps, "GPGGA", 3);
TinyGPSCustom ExtLng(gps, "GPGGA", 5);
TinyGPSCustom totalGPGSVMessages(gps, "GPGSV", 1);
TinyGPSCustom messageNumber(gps, "GPGSV", 2);
TinyGPSCustom satsInView(gps, "GPGSV", 3);
TinyGPSCustom satNumber[4]; // 待稍后初始化
TinyGPSCustom elevation[4];
TinyGPSCustom azimuth[4];
TinyGPSCustom snr[4];
//定义用于测定温度、电导率和湿度的土壤探针装置的地址
const byte Addr[] = {0x01, 0x04, 0x00, 0x00, 0x00, 0x03, 0xB0, 0x0B};
struct
{
bool active;
int elevation;
int azimuth;
int snr;
int dsp;
} sats[MAX_SATELLITES];
volatile uint32_t newlines = 0UL;
static void handleRxChar( uint8_t c )
{
if (c == '\n')
newlines++;
}
void setup() {
mySerial.begin(9600);
tft.begin();
tft.setRotation(3);
//此命令初始化Wio终端中包含的SD设备
if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
sd = false;
}
else {
SerialUSB.println("initialization done.");
//首先,我们需要初始化将在这个项目中使用的设备,以及串行通信
Wire.begin();
airSensor.begin();
Serial.begin(9600);
sd = true;
}
for (int i=0; i<4; ++i)
{
satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // 偏移量4, 8, 12, 16
elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // 偏移量 5, 9, 13, 17
azimuth[i].begin(gps, "GPGSV", 6 + 4 * i); // 偏移量 6, 10, 14, 18
snr[i].begin(gps, "GPGSV", 7 + 4 * i); // 偏移量7, 11, 15, 19
}
//为整个循环中使用的一些变量设置初始值
p = 0;
j = 0;
p = 0;
s = 1;
Num = 0;
count = 0;
//这些变量表示所显示地图上纬度和经度的最小值和最大值
LatLim1 = 50;
LatLim2 = 55;
LonLim1 = 50;
LonLim2 = 60;
//这些命令将程序上使用的pin设置为输入引脚,用于测量UV光强度
pinMode (A7, INPUT);
pinMode (A4, INPUT);
pinMode (A6, INPUT);
//这些命令初始化Wio终端中的按钮
pinMode(WIO_5S_PRESS, INPUT_PULLUP);
pinMode(WIO_KEY_A, INPUT_PULLUP);
pinMode(WIO_KEY_B, INPUT_PULLUP);
pinMode(WIO_KEY_C, INPUT_PULLUP);
pinMode(WIO_5S_LEFT, INPUT_PULLUP);
pinMode(WIO_5S_RIGHT, INPUT_PULLUP);
}
void loop() {
//这些命令从SCD30传感器获取测量值
if (airSensor.dataAvailable())
{
tTemp = airSensor.getTemperature();
tHum = airSensor.getHumidity();
tCO2 = airSensor.getCO2();
}
//由于紫外线传感器有一个模拟输出,从它连接的引脚读取信号
for (i=0; i<10; i++){
tUV = tUV + analogRead (A7);
}
tUV = tUV/10;
//使用以下命令将环境测量值转换为字符串变量
//这些变量将显示在屏幕上
N_UV = String(tUV);
N_Temp = String(tTemp);
N_Hum = String(tHum);
N_CO2 = String(tCO2);
long N_mjd;
//命令将传感器和GPS的数据保存到SD卡。此功能每6秒执行一次
while (mySerial.available() > 0) {
char c = mySerial.read();
gps.encode(c);
}
//计算纬度和经度值,并将其转换为用于显示的字符串
double lat0 = gps.location.lat();
double lat1 = (lat0 -int(lat0))*60;
double lat2 = (lat1 - int(lat1))*60;
String lat3 = String(int(lat0))+':' + String(int(lat1))+':'+String(lat2) + ' ' + String(ExtLat.value());
double lng0 = gps.location.lng();
double lng1 = (lng0 -int(lng0))*60;
double lng2 = (lng1 - int(lng1))*60;
String lng3 = String(int(lng0))+':' + String(int(lng1))+':'+String(lng2) + ' ' + String(ExtLng.value());
//计算时间并将其转换为字符串以供显示
int hr = gps.time.hour()+9;
if (hr>24) hr -= 24;
String hr0 = '0'+String(hr);
hr1 = hr0.substring(hr0.length()-2);
String min0 = '0'+String(gps.time.minute());
min1 = min0.substring(min0.length()-2);
String sec0 = '0'+String(gps.time.second());
sec1 = sec0.substring(sec0.length()-2);
hour0 = hr1+':'+min1+':'+sec1;
//计算日期并将其转换为字符串以供显示
if (gps.date.isUpdated()) {
int y = gps.date.year();
int m = gps.date.month();
int d = gps.date.day();
if(m<3){y--; m+=12;}
long mjd = int(361.7015*y)+int(y/400)-int(y/100)+int(30.59*(m-2))+d-678912;
if(gps.time.hour()>14)
N_mjd = 58580+(mjd-51412);
else
N_mjd = 58579+(mjd-51412);
long n = N_mjd+678881;
long a = 4*n+3+3*(4*(n+1)/146097+1);
long b = 5*((a % 1461)/4)+2;
N_y = int(a/1461);
N_m = int(b/153+3);
N_d = int((b % 153)/5+1);
if (N_m>12){N_y++; N_m-=12;}
N_date = String(N_y)+'/'+('0'+String(N_m)).substring(('0'+String(N_m)).length()-2)+'/'+('0'+String(N_d)).substring(('0'+String(N_d)).length()-2);
}
//当用户按下Wio终端上的按钮时,将触发土壤测量的RS485通信
//测量位置也固定并显示在Wio终端屏幕上
if (digitalRead(WIO_5S_PRESS) == LOW) {
//只有Wio终端处于数据显示模式时,才会进行土壤测量
if (menu ==1){
//此代码保存初始化测量时的当前位置,以便在lcd屏幕上显示
lng0 = lng0*100;
Lng [p] = map(lng0, LonLim1, LonLim2, 0, 240);
Lat [p] = map (lat0, LatLim1, LatLim2, 0, 320);
lng0 = lng0/100;
//进行土壤温度和湿度测量
for (i=0;i<10;i++){
tStemp = tStemp + analogRead (A4);
tMois = tMois + analogRead (A6);
}
tStemp = tStemp/10;
tStemp = tStemp*(-0.0826) + 108.65;
tMois = tMois/10;
tMois = tMois*5/1024;
N_Stemp = String (tStemp);
N_Mois = String (tMois);
Date[p] = N_date;
nhour0[p] = hour0;
Alt[p] = String(gps.altitude.meters());
Sat[p] = String(gps.satellites.value());
Temp[p] = tTemp;
Hum[p] = tHum;
CO2[p] = tCO2;
UV[p] = tUV;
Stemp[p] = tStemp;
Mois[p] = tMois;
Latitude[p] = lat3;
Longitude[p] = lng3;
//这些线确定将在地图屏幕上显示的点数
p++;
count++;
if (p>10){
p = 0;
count = 10;
}
//s的值决定测量的标题第一次显示在Wio终端屏幕上
s = 1;
}
}
//此条件将显示测量值
if (menu == 1){
//第一次显示菜单时,我们需要初始化屏幕颜色和测量标题
if (s==1){
tft.fillScreen (TFT_BLACK);
tft.setTextSize(3);
tft.drawString("GPS",50,3);
tft.drawString("SCD30",30,150);
tft.drawString("Soil",200,3);
tft.drawString("Light",200,150);
tft.setTextSize(1);
tft.drawString("Date",20,33);
tft.drawString("Time",20,53);
tft.drawString("LAT",20,70);
tft.drawString("LONG",20,87);
tft.drawString("ALT (m)",20,107);
tft.drawString("Satellites",20,127);
tft.drawString("Temp (oC)",20,180);
tft.drawString("Hum (%)",20,200);
tft.drawString("CO2 (ppm)",20,220);
tft.drawString("Temp (oC)",180,33);
tft.drawString("Moisture (V)",180,53);
tft.drawString("UV ",180,180);
}
if (N_date != p_date || s == 1) { //显示日期
tft.fillRect(50,33,70,20,TFT_BLACK);
tft.drawString(N_date,50,33);
p_date = N_date;
}
if (hour0 != p_hour || s == 1) { //显示时间
tft.fillRect(50,53,100,16,TFT_BLACK);
tft.drawString(hour0,50,53);
p_hour = hour0;
}
if (lat3 != p_lat || s == 1) { //显示纬度
tft.fillRect(50,70,110,10,TFT_BLACK);
tft.drawString(lat3,50,70);
p_lat = lat3;
}
if (lng3 != p_lng || s == 1) { //显示经度
tft.fillRect(50,90,87,10,TFT_BLACK);
tft.drawString(lng3,50,90);
p_lng = lng3;
}
if (String(gps.altitude.meters()) != p_alt || s == 1) { //显示高度
tft.fillRect(70,107,107,10,TFT_BLACK);
tft.drawString(String(gps.altitude.meters()),70,107);
p_alt = String(gps.altitude.meters());
}
if (String(gps.satellites.value()) != p_sat || s == 1) { //显示卫星数
tft.fillRect(90,127,110,10, TFT_BLACK);
tft.drawString(String(gps.satellites.value()),90,127);
p_sat = String(gps.satellites.value());
}
tft.fillRect(0,140,320,2,TFT_YELLOW);
tft.fillRect(160,0,2,240,TFT_YELLOW);
if (N_Temp != p_Temp || s == 1){ //显示环境温度
tft.fillRect(80,180,32,16,TFT_BLACK);
tft.drawString(N_Temp, 80, 180);
p_Temp = N_Temp;
}
if (N_Hum != p_Hum || s == 1){ //显示空气湿度
tft.fillRect(90,200,32,16,TFT_BLACK);
tft.drawString(N_Hum, 80, 200);
p_Hum = N_Hum;
}
if (N_CO2 != p_CO2 || s == 1){ //显示CO2浓度
tft.fillRect(80,220,32,16,TFT_BLACK);
tft.drawString(N_CO2, 80, 220);
p_CO2 = N_CO2;
}
if (N_Stemp != p_Stemp || s == 1){ //显示土壤中的温度
tft.fillRect(240,33,32,16,TFT_BLACK);
tft.drawString(N_Stemp, 240, 33);
p_Stemp = N_Stemp;
}
if (N_Mois != p_Mois || s == 1){ //显示土壤体积含水量
tft.fillRect(270,53,32,16,TFT_BLACK);
tft.drawString(N_Mois, 260, 53);
p_Mois = N_Mois;
}
if (N_UV != p_UV || s == 1){ //显示来自UV传感器的强度
tft.fillRect(200,180,80,16,TFT_BLACK);
tft.drawString(N_UV, 200, 180);
p_UV = N_UV;
}
s = 0;
}
if (menu == 2){
if (digitalRead(WIO_5S_RIGHT) == LOW) {
Num = Num + 1;
l = 1;
}
if (digitalRead(WIO_5S_LEFT) == LOW) {
Num = Num - 1;
l = 1;
}
if (Num<0){
Num = count;
}
if (Num>count-1){
Num = 0;
}
Dat = String(Num);
if (l == 1){
tft.fillScreen (TFT_BLACK);
tft.setTextSize(3);
tft.drawString("GPS",50,3);
tft.drawString("SCD30",30,150);
tft.drawString("Soil",170,3);
tft.drawString("Light",200,150);
tft.setTextColor(TFT_YELLOW);
tft.drawString("Sample",170,210);
tft.drawString(Dat,285,210);
tft.setTextColor(TFT_WHITE);
tft.setTextSize(1);
tft.drawString("Date",20,33);
tft.drawString("Time",20,53);
tft.drawString("LAT",20,70);
tft.drawString("LONG",20,87);
tft.drawString("ALT (m)",20,107);
tft.drawString("Satellites",20,127);
tft.drawString("Temp (oC)",20,180);
tft.drawString("Hum (%)",20,200);
tft.drawString("CO2 (ppm)",20,220);
tft.drawString("Temp (oC)",180,33);
tft.drawString("Moisture (%)",180,53);
tft.drawString("UV: ",180,180);
tft.fillRect(0,140,320,2,TFT_YELLOW);
tft.fillRect(160,0,2,240,TFT_YELLOW);
Dat = String (Date[Num]);
//Date
tft.drawString(Dat,50,33);
Dat = String (nhour0[Num]);
tft.drawString(Dat,50,53);
p_hour = hour0;
Dat = String (Latitude[Num]);
tft.drawString(Dat,50,70);
Dat = String (Longitude[Num]);
tft.drawString(Dat,50,90);
Dat = Alt[Num];
tft.drawString(Dat,70,107);
Dat = Sat[Num];
tft.drawString(Dat,90,127);
Dat = String(Temp[Num]);
tft.drawString(Dat, 80, 180);
Dat = String(Hum[Num]);
tft.drawString(Dat, 50, 200);
Dat = String(CO2[Num]);
tft.drawString(Dat, 80, 220);
Dat = String(Stemp[Num]);
tft.drawString(Dat, 240, 33);
Dat = String(Mois[Num]);
tft.drawString(Dat, 260, 53);
Dat = String(UV [Num]);
tft.drawString(Dat, 240, 180);
}
l = 0;
}
//初始化地图显示,以查看当前的位置和测量的地点
if (menu == 3){
delay (100);
if (k==1){
//命令显示地图,存储在SD卡内存中
drawImage<uint16_t>("map.bmp", 0, 0);
//此函数获取坐标,以及用户指示的最大坐标,以将其映射并在屏幕上显示
lng0 = lng0*100;
Latc = map (lat0, LatLim1, LatLim2, 0, 320);
Lonc = map (lng0, LonLim1, LonLim2, 0, 240);
lng0 = lng0/100;
if (l==1){
//在当前位置画一个绿色圆圈
tft.fillCircle(Latc, Lonc, 4, TFT_RED);
}
if (l==0){
//在过去的位置画一个绿色圆圈(仅当在菜单2上时)
tft.fillCircle(Lat[Num], Lng[Num], 4, TFT_BLUE);
}
delay (100);
}
k = 0;
}
//此代码用于检测按钮的输入和菜单之间的变化
if (digitalRead(WIO_KEY_C) == LOW) {
menu = 1;
l = 1;
s = 1;
k = 1;
}
else if (digitalRead(WIO_KEY_B) == LOW) {
menu = 2;
s = 1;
l = 1;
k = 1;
}
else if (digitalRead(WIO_KEY_A) == LOW) {
menu = 3;
s = 1;
k = 1;
}
delay (100);
}
开启“人工智能”——贝尔金WeMo智能插座套装拆解
2014-05-23
iPhone 6 plus最专业传感器深入解析
2014-10-22
智能手机里面都装着哪些传感器?它们都记录了你的什么?
2016-04-11
NXP开发板不知道选什么,看这里就对了!
2016-07-28
盘点全球知名传感器芯片巨头Top10
2015-08-21
这套工业物联网解决方案把乐高变成了一座智慧城市
2019-06-24
[项目实录]全流程LoRaWAN开发
2019-07-22
【视频秀】用STM32和光传感器控制电机马达开关窗户
2019-06-12
一款小尺寸、高性能的智能家居设备
2019-06-21
无线传感器网络——传感器原理与应用系列课程
2019-07-24
讨论