在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何創建一個具有當前天氣狀況的氣象站

454398 ? 來源:網絡整理 ? 作者:網絡整理 ? 2019-11-29 09:56 ? 次閱讀

概述

太高興了

天氣怎么樣?

海龜,“高興起來”

在此項目中,Adafruit Metro M4 Express Airlift和Tri-Color ePaper Shield可以愉快地共同創建一個具有當前天氣狀況的氣象站

發布Metro M4 Express AirLift時發生了一件很棒的事情。它打開了Arduino草圖到互聯網,使它們擺脫了封閉環境的限制。現在,它正式成為物聯網IoT)世界中的“事物”。在此項目中,Metro M4 Express AirLift將從互聯網上獲取您當地的天氣數據,并在易于閱讀的ePaper顯示屏上顯示

與該項目一起使用的Adafruit三色ePaper Shield是一款2.7“ ePaper防護罩,顯示黑色,白色和紅色像素。由于接頭已經在Metro和ePaper防護罩上組裝好,因此無需焊接即可輕松連接到Metro。

功能

DIY氣象站顯示您所在地區的天氣信息ePaper顯示屏上的攝氏度或華氏度單位。顯示的信息包括:

當前日期

當前溫度

當前天氣狀況

接下來的12小時的預報數據,以6小時為增量

天氣圖標以顯示天氣情況和天氣預報

城市名稱

顯示當前和預測的高溫紅色

日出和日落時間

當前月相的描述和圖標

零件

構建該項目無需焊接,僅使用兩個零件:Adafruit Metro M4 Express AirLift Lite和Adafruit 2.7“ Tri-Color eInk/ePaper用SRAM屏蔽。要使該項目可移植,您可以添加USB電池組或Adafruit PowerBoost 500 Shield和一塊Li-Po電池,然后將其插入Metro和ePaper屏蔽之間。

如果您對其他項目的ePaper顯示器感興趣,查看Adafruit的ePaper顯示的整個行。

Adafruit Metro M4 Express AirLift(WiFi) -Lite

產品ID:4000

使用AirLift為您的下一個項目提供電梯-我們為這個Metro M4增添了ESP32處理器的機智名稱。您已經知道theAdafruit Metro 。..

$ 34.95

入庫存

添加到購物車

Adafruit 2.7“三色電子墨水/ePaper帶有Shield的SRAM

產品ID:4229

輕松的電子紙終于出現在微控制器上,這種突破的目的是使添加三色電子墨水顯示器變得輕而易舉。您是否看到過其中之一。..

$ 39.95

入庫存

添加到購物車

USB電纜-USB A到Micro -B

產品ID:592

這是您的標準A到Micro-B USB電纜,用于USB 1.1或2.0。非常適合將PC連接到Metro,Feather, Raspberry Pi或其他開發板或。..

$ 2.95

入庫存

添加到購物車

工作原理

Metro M4 Express配備了AirLift協處理器板!好吧,至少對于此草圖,它將獲取您的本地天氣并將其顯示在ePaper防護板上。有許多具有天氣API的站點可以獲取您當地的當前狀況和即將到來的預報。這些網站中有較少的網站出于非商業目的免費提供此數據。我們使用了OpenWeatherMap.com API,該API提供了有關當前狀況和天氣預報的免費天氣數據。

對于天氣和月亮圖標,我們利用了Adafruit GFX庫對自定義字體的支持。字體是此類顯示的理想選擇,因為我們需要簡單,不同點大小的線形圖標,這些圖標在單色ePaper顯示器上看起來不錯。對于天氣圖標,我們選擇了Meteocon字體集,這是一種包含40多個天氣圖標的免費字體。對于月相字體,我們使用了Curtis Clark的Moon Phases字體,該字體可免費用于非商業用途。字體文件需要進行轉換才能與GFX庫一起使用,但是請放心,我們已經以各種磅數完成了此操作,因此您可以在本項目和其他項目中使用這些字體。

月相,OpenWeatherMap在其API中不包含月相。但是,這不是問題,因為我們使用數學來計算當前的月相。我們知道,平均每29.5305882天就有一個新月。從OpenWeatherMap中獲取當前日期和時間,如果我們知道新月發生的時間點,則可以計算當前日期的月相。

OpenWeatherMap中的天氣數據包括當前未來天氣預報的時間和時間,例如距現在3小時的天氣預報。這次是UTC格式。我們需要將其轉換為本地時間,這樣才能準確顯示時間。幸運的是,OpenWeatherMap最近將數據添加了本地時區偏移量(實際上,正如我們編寫本指南一樣!),因此現在可以通過采用UTC的當前時間并添加本地時區偏移量來輕松確定本地時間。

Meteoicon字體包含40多種與天氣相關的字體。

下一步是將它們放在一起:獲取天氣數據并格式化顯示。我們修改了Daniel Eichhorn出色的Weather Station項目中的部分代碼,因此它并不是特定于微控制器的專有代碼。這樣,包括空運協處理器在內的其他處理器就可以從Internet檢索天氣數據。

Arduino設置

如果尚未安裝,則需要在計算機上安裝Arduino IDE才能將此草圖上傳到Metro M4 Express AirLift。在本學習指南中找到有關安裝Arduino的信息。您還需要配置Metro M4 Express AirLift開發板以與Arduino IDE配合使用。 《 Adafruit學習指南》網站上有一篇很棒的文章,介紹了如何使用Arduino和其他環境設置此開發板。/span》

安裝庫

對于此草圖,您將需要安裝以下庫:

Adafruit EPD(電子紙顯示)庫

Adafruit GFX庫

Adafruit NeoPixel庫

Arduino JSON庫

Adafruit的變體WiFiNINA庫

這些庫應位于最新的Arduino IDE(1.8.7及更高版本)的Arduino庫管理器中。

打開Arduino庫管理器:

搜索 Adafruit EPD 庫并安裝

搜索 Adafruit GFX 庫并安裝它

搜索 Adafruit NeoPixel 庫并安裝

搜索 ArduinoJSON 庫并安裝

您必須通過下面的鏈接手動安裝WiFiNina

下載Adafruit版本的WiFiNina

注冊OpenWeatherMap API密鑰

您需要OpenWeatherMap的API密鑰才能下載來自其站點的天氣數據。您可以在OpenWeatherMap.org上免費注冊。免費帳戶允許訪問當前天氣和5天/3小時天氣API。

下載代碼并修改secrets.h文件

此下載可在GitHub上獲得。它包含天氣和月相圖標的代碼和字體文件。

必須修改 secrets.h ,使其包括WiFi設置,OpenWeatherMap API密鑰以及所在城市您需要天氣數據。如果您的城市名稱出現在多個國家(例如意大利威尼斯和佛羅里達州威尼斯),則可能需要包含2個字母的國家/地區代碼。因此,例如,佛羅里達州的威尼斯將被輸入為“美國威尼斯”。

此文件中的其他選項包括選擇溫度的公制或英語單位以及天氣描述的語言。

下載:Project Zip 或 secrets.h | 在Github上查看

復制代碼

#pragma once

// secrets.h

// Define your WIFI and OpenWeatherMap API key and location in this file

#define WIFI_SSID “{wifi ssid}”

#define WIFI_PASSWORD “{wifi password}”

#define OWM_KEY “{OpenWeatherMap.com key}”

#define OWM_LOCATION “Your City”

//example

//#define OWM_LOCATION “New York,US”

// update the weather at this interval, in minutes

#define UPDATE_INTERVAL 15

// Set to true to show temperatures in Celsius, false for Fahrenheit

#define OWM_METRIC false

// temperature will display in red at or above this temperature

// set to a high number (i.e. 》200) to not show temperatures in red

#define METRIC_HOT 32

#define ENGLISH_HOT 90

/*

Arabic - ar, Bulgarian - bg, Catalan - ca, Czech - cz, German - de, Greek - el,

English - en, Persian (Farsi) - fa, Finnish - fi, French - fr, Galician - gl,

Croatian - hr, Hungarian - hu, Italian - it, Japanese - ja, Korean - kr,

Latvian - la, Lithuanian - lt, Macedonian - mk, Dutch - nl, Polish - pl,

Portuguese - pt, Romanian - ro, Russian - ru, Swedish - se, Slovak - sk,

Slovenian - sl, Spanish - es, Turkish - tr, Ukrainian - ua, Vietnamese - vi,

Chinese Simplified - zh_cn, Chinese Traditional - zh_tw.

*/

#define OWM_LANGUAGE “en”

#pragma once

// secrets.h

// Define your WIFI and OpenWeatherMap API key and location in this file

#define WIFI_SSID “{wifi ssid}”

#define WIFI_PASSWORD “{wifi password}”

#define OWM_KEY “{OpenWeatherMap.com key}”

#define OWM_LOCATION “Your City”

//example

//#define OWM_LOCATION “New York,US”

// update the weather at this interval, in minutes

#define UPDATE_INTERVAL 15

// Set to true to show temperatures in Celsius, false for Fahrenheit

#define OWM_METRIC false

// temperature will display in red at or above this temperature

// set to a high number (i.e. 》200) to not show temperatures in red

#define METRIC_HOT 32

#define ENGLISH_HOT 90

/*

Arabic - ar, Bulgarian - bg, Catalan - ca, Czech - cz, German - de, Greek - el,

English - en, Persian (Farsi) - fa, Finnish - fi, French - fr, Galician - gl,

Croatian - hr, Hungarian - hu, Italian - it, Japanese - ja, Korean - kr,

Latvian - la, Lithuanian - lt, Macedonian - mk, Dutch - nl, Polish - pl,

Portuguese - pt, Romanian - ro, Russian - ru, Swedish - se, Slovak - sk,

Slovenian - sl, Spanish - es, Turkish - tr, Ukrainian - ua, Vietnamese - vi,

Chinese Simplified - zh_cn, Chinese Traditional - zh_tw.

*/

#define OWM_LANGUAGE “en”

下載:項目Zip 或 adafruit_epd_weather.ino | 在Github上查看

復制代碼

#include

#include // Core graphics library

#include

#include

#include //https://github.com/bblanchon/ArduinoJson

#include

#include

#include “secrets.h”

#include “OpenWeatherMap.h”

#include “Fonts/meteocons48pt7b.h”

#include “Fonts/meteocons24pt7b.h”

#include “Fonts/meteocons20pt7b.h”

#include “Fonts/meteocons16pt7b.h”

#include “Fonts/moon_phases20pt7b.h”

#include “Fonts/moon_phases36pt7b.h”

#include

#include

#include

#include

#include

#define SRAM_CS 8

#define EPD_CS 10

#define EPD_DC 9

#define EPD_RESET -1

#define EPD_BUSY -1

#define NEOPIXELPIN 40

// This is for the 2.7“ tricolor EPD

Adafruit_IL91874 gfx(264, 176 ,EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY);

AirliftOpenWeatherMap owclient(&Serial);

OpenWeatherMapCurrentData owcdata;

OpenWeatherMapForecastData owfdata[3];

Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(1, NEOPIXELPIN, NEO_GRB + NEO_KHZ800);

const char *moonphasenames[29] = {

”New Moon“,

”Waxing Crescent“,

”Waxing Crescent“,

”Waxing Crescent“,

”Waxing Crescent“,

”Waxing Crescent“,

”Waxing Crescent“,

”Quarter“,

”Waxing Gibbous“,

”Waxing Gibbous“,

”Waxing Gibbous“,

”Waxing Gibbous“,

”Waxing Gibbous“,

”Waxing Gibbous“,

”Full Moon“,

”Waning Gibbous“,

”Waning Gibbous“,

”Waning Gibbous“,

”Waning Gibbous“,

”Waning Gibbous“,

”Waning Gibbous“,

”Last Quarter“,

”Waning Crescent“,

”Waning Crescent“,

”Waning Crescent“,

”Waning Crescent“,

”Waning Crescent“,

”Waning Crescent“,

”Waning Crescent“

};

int8_t readButtons(void) {

uint16_t reading = analogRead(A3);

//Serial.println(reading);

if (reading 》 600) {

return 0; // no buttons pressed

}

if (reading 》 400) {

return 4; // button D pressed

}

if (reading 》 250) {

return 3; // button C pressed

}

if (reading 》 125) {

return 2; // button B pressed

}

return 1; // Button A pressed

}

bool wifi_connect(){

Serial.print(”Connecting to WiFi.。. “);

WiFi.setPins(SPIWIFI_SS, SPIWIFI_ACK, ESP32_RESETN, ESP32_GPIO0, &SPIWIFI);

// check for the WiFi module:

if(WiFi.status() == WL_NO_MODULE) {

Serial.println(”Communication with WiFi module failed!“);

displayError(”Communication with WiFi module failed!“);

while(true);

}

String fv = WiFi.firmwareVersion();

if (fv 《 ”1.0.0“) {

Serial.println(”Please upgrade the firmware“);

}

neopixel.setPixelColor(0, neopixel.Color(0, 0, 255));

neopixel.show();

if(WiFi.begin(WIFI_SSID, WIFI_PASSWORD) == WL_CONNECT_FAILED)

{

Serial.println(”WiFi connection failed!“);

displayError(”WiFi connection failed!“);

return false;

}

int wifitimeout = 15;

int wifistatus;

while ((wifistatus = WiFi.status()) != WL_CONNECTED && wifitimeout 》 0) {

delay(1000);

Serial.print(”。“);

wifitimeout--;

}

if(wifitimeout == 0)

{

Serial.println(”WiFi connection timeout with error “ + String(wifistatus));

displayError(”WiFi connection timeout with error “ + String(wifistatus));

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

return false;

}

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

Serial.println(”Connected“);

return true;

}

void wget(String &url, int port, char *buff)

{

int pos1 = url.indexOf(”/“,0);

int pos2 = url.indexOf(”/“,8);

String host = url.substring(pos1+2,pos2);

String path = url.substring(pos2);

Serial.println(”to wget(“ + host + ”,“ + path + ”,“ + port + ”)“);

wget(host, path, port, buff);

}

void wget(String &host, String &path, int port, char *buff)

{

//WiFiSSLClient client;

WiFiClient client;

neopixel.setPixelColor(0, neopixel.Color(0, 0, 255));

neopixel.show();

client.stop();

if (client.connect(host.c_str(), port)) {

Serial.println(”connected to server“);

// Make a HTTP request:

client.println(String(”GET “) + path + String(” HTTP/1.0“));

client.println(”Host: “ + host);

client.println(”Connection: close“);

client.println();

uint32_t bytes = 0;

int capturepos = 0;

bool capture = false;

int linelength = 0;

char lastc = ‘’;

while(true)

{

while (client.available()) {

char c = client.read();

//Serial.print(c);

if((c == ‘ ’) && (lastc == ‘ ’))

{

if(linelength == 0)

{

capture = true;

}

linelength = 0;

}

else if(capture)

{

buff[capturepos++] = c;

//Serial.write(c);

}

else

{

if((c != ‘ ’) && (c != ‘ ’))

linelength++;

}

lastc = c;

bytes++;

}

// if the server‘s disconnected, stop the client:

if (!client.connected()) {

//Serial.println();

Serial.println(”disconnecting from server.“);

client.stop();

buff[capturepos] = ’‘;

Serial.println(”captured “ + String(capturepos) + ” bytes“);

break;

}

}

}

else

{

Serial.println(”problem connecting to “ + host + ”:“ + String(port));

buff[0] = ’‘;

}

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

int getStringLength(String s)

{

int16_t x = 0, y = 0;

uint16_t w, h;

gfx.getTextBounds(s, 0, 0, &x, &y, &w, &h);

return w + x;

}

/*

return value is percent of moon cycle ( from 0.0 to 0.999999), i.e.:

0.0: New Moon

0.125: Waxing Crescent Moon

0.25: Quarter Moon

0.375: Waxing Gibbous Moon

0.5: Full Moon

0.625: Waning Gibbous Moon

0.75: Last Quarter Moon

0.875: Waning Crescent Moon

*/

float getMoonPhase(time_t tdate)

{

time_t newmoonref = 1263539460; //known new moon date (2010-01-15 07:11)

// moon phase is 29.5305882 days, which is 2551442.82048 seconds

float phase = abs( tdate - newmoonref) / (double)2551442.82048;

phase -= (int)phase; // leave only the remainder

if(newmoonref 》 tdate)

phase = 1 - phase;

return phase;

}

void displayError(String str)

{

// show error on display

neopixel.setPixelColor(0, neopixel.Color(255, 0, 0));

neopixel.show();

Serial.println(str);

gfx.setTextColor(EPD_BLACK);

gfx.powerUp();

gfx.clearBuffer();

gfx.setTextWrap(true);

gfx.setCursor(10,60);

gfx.setFont(&FreeSans12pt7b);

gfx.print(str);

gfx.display();

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

void displayHeading(OpenWeatherMapCurrentData &owcdata)

{

time_t local = owcdata.observationTime + owcdata.timezone;

struct tm *timeinfo = gmtime(&local);

char datestr[80];

// date

//strftime(datestr,80,”%a, %d %b %Y“,timeinfo);

strftime(datestr,80,”%a, %b %d“,timeinfo);

gfx.setFont(&FreeSans18pt7b);

gfx.setCursor((gfx.width()-getStringLength(datestr))/2,30);

gfx.print(datestr);

// city

strftime(datestr,80,”%A“,timeinfo);

gfx.setFont(&FreeSansBold12pt7b);

gfx.setCursor((gfx.width()-getStringLength(owcdata.cityName))/2,60);

gfx.print(owcdata.cityName);

}

void displayForecastDays(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3)

{

for(int i=0; i 《 count; i++)

{

// day

time_t local = owfdata[i].observationTime + owcdata.timezone;

struct tm *timeinfo = gmtime(&local);

char strbuff[80];

strftime(strbuff,80,”%I“,timeinfo);

String datestr = String(atoi(strbuff));

strftime(strbuff,80,”%p“,timeinfo);

// convert AM/PM to lowercase

strbuff[0] = tolower(strbuff[0]);

strbuff[1] = tolower(strbuff[1]);

datestr = datestr + ” “ + String(strbuff);

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(datestr))/2,94);

gfx.print(datestr);

// weather icon

String wicon = owclient.getMeteoconIcon(owfdata[i].icon);

gfx.setFont(&meteocons20pt7b);

gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(wicon))/2,134);

gfx.print(wicon);

// weather main description

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(owfdata[i].main))/2,154);

gfx.print(owfdata[i].main);

// temperature

int itemp = (int)(owfdata[i].temp + .5);

int color = EPD_BLACK;

if((OWM_METRIC && itemp 》= METRIC_HOT)|| (!OWM_METRIC && itemp 》= ENGLISH_HOT))

color = EPD_RED;

gfx.setTextColor(color);

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2,172);

gfx.print(itemp);

gfx.drawCircle(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2 + getStringLength(String(itemp)) + 6,163,3,color);

gfx.drawCircle(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2 + getStringLength(String(itemp)) + 6,163,2,color);

gfx.setTextColor(EPD_BLACK);

}

}

void displayForecast(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3)

{

gfx.powerUp();

gfx.clearBuffer();

neopixel.setPixelColor(0, neopixel.Color(0, 255, 0));

neopixel.show();

gfx.setTextColor(EPD_BLACK);

displayHeading(owcdata);

displayForecastDays(owcdata, owfdata, count);

gfx.display();

gfx.powerDown();

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

void displayAllWeather(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3)

{

gfx.powerUp();

gfx.clearBuffer();

neopixel.setPixelColor(0, neopixel.Color(0, 255, 0));

neopixel.show();

gfx.setTextColor(EPD_BLACK);

// date string

time_t local = owcdata.observationTime + owcdata.timezone;

struct tm *timeinfo = gmtime(&local);

char datestr[80];

// date

//strftime(datestr,80,”%a, %d %b %Y“,timeinfo);

strftime(datestr,80,”%a, %b %d %Y“,timeinfo);

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor((gfx.width()-getStringLength(datestr))/2,14);

gfx.print(datestr);

// weather icon

String wicon = owclient.getMeteoconIcon(owcdata.icon);

gfx.setFont(&meteocons24pt7b);

gfx.setCursor((gfx.width()/3-getStringLength(wicon))/2,56);

gfx.print(wicon);

// weather main description

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor((gfx.width()/3-getStringLength(owcdata.main))/2,72);

gfx.print(owcdata.main);

// temperature

gfx.setFont(&FreeSansBold24pt7b);

int itemp = owcdata.temp + .5;

int color = EPD_BLACK;

if((OWM_METRIC && (int)itemp 》= METRIC_HOT)|| (!OWM_METRIC && (int)itemp 》= ENGLISH_HOT))

color = EPD_RED;

gfx.setTextColor(color);

gfx.setCursor(gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2,58);

gfx.print(itemp);

gfx.setTextColor(EPD_BLACK);

// draw temperature degree as a circle (not available as font character

gfx.drawCircle(gfx.width()/3 + (gfx.width()/3 + getStringLength(String(itemp)))/2 + 8, 58-30,4,color);

gfx.drawCircle(gfx.width()/3 + (gfx.width()/3 + getStringLength(String(itemp)))/2 + 8, 58-30,3,color);

// draw moon

// draw Moon Phase

float moonphase = getMoonPhase(owcdata.observationTime);

int moonage = 29.5305882 * moonphase;

//Serial.println(”moon age: “ + String(moonage));

// convert to appropriate icon

String moonstr = String((char)((int)’A‘ + (int)(moonage*25./30)));

gfx.setFont(&moon_phases20pt7b);

// font lines look a little thin at this size, drawing it a few times to thicken the lines

gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2,56);

gfx.print(moonstr);

gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2+1,56);

gfx.print(moonstr);

gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2,56-1);

gfx.print(moonstr);

// draw moon phase name

int currentphase = moonphase * 28. + .5;

gfx.setFont(); // system font (smallest available)

gfx.setCursor(2*gfx.width()/3 + max(0,(gfx.width()/3 - getStringLength(moonphasenames[currentphase]))/2),62);

gfx.print(moonphasenames[currentphase]);

displayForecastDays(owcdata, owfdata, count);

gfx.display();

gfx.powerDown();

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

void displayCurrentConditions(OpenWeatherMapCurrentData &owcdata)

{

gfx.powerUp();

gfx.clearBuffer();

neopixel.setPixelColor(0, neopixel.Color(0, 255, 0));

neopixel.show();

gfx.setTextColor(EPD_BLACK);

displayHeading(owcdata);

// weather icon

String wicon = owclient.getMeteoconIcon(owcdata.icon);

gfx.setFont(&meteocons48pt7b);

gfx.setCursor((gfx.width()/2-getStringLength(wicon))/2,156);

gfx.print(wicon);

// weather main description

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor(gfx.width()/2 + (gfx.width()/2-getStringLength(owcdata.main))/2,160);

gfx.print(owcdata.main);

// temperature

gfx.setFont(&FreeSansBold24pt7b);

int itemp = owcdata.temp + .5;

int color = EPD_BLACK;

if((OWM_METRIC && (int)itemp 》= METRIC_HOT)|| (!OWM_METRIC && (int)itemp 》= ENGLISH_HOT))

color = EPD_RED;

gfx.setTextColor(color);

gfx.setCursor(gfx.width()/2 + (gfx.width()/2-getStringLength(String(itemp)))/2,130);

gfx.print(itemp);

gfx.setTextColor(EPD_BLACK);

// draw temperature degree as a circle (not available as font character

gfx.drawCircle(gfx.width()/2 + (gfx.width()/2 + getStringLength(String(itemp)))/2 + 10, 130-26,4,color);

gfx.drawCircle(gfx.width()/2 + (gfx.width()/2 + getStringLength(String(itemp)))/2 + 10, 130-26,3,color);

gfx.display();

gfx.powerDown();

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

void displaySunMoon(OpenWeatherMapCurrentData &owcdata)

{

gfx.powerUp();

gfx.clearBuffer();

neopixel.setPixelColor(0, neopixel.Color(0, 255, 0));

neopixel.show();

gfx.setTextColor(EPD_BLACK);

displayHeading(owcdata);

// draw Moon Phase

float moonphase = getMoonPhase(owcdata.observationTime);

int moonage = 29.5305882 * moonphase;

// convert to appropriate icon

String moonstr = String((char)((int)’A‘ + (int)(moonage*25./30)));

gfx.setFont(&moon_phases36pt7b);

gfx.setCursor((gfx.width()/3-getStringLength(moonstr))/2,140);

gfx.print(moonstr);

// draw moon phase name

int currentphase = moonphase * 28. + .5;

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor(gfx.width()/3 + max(0,(gfx.width()*2/3 - getStringLength(moonphasenames[currentphase]))/2),110);

gfx.print(moonphasenames[currentphase]);

// draw sunrise/sunset

// sunrise/sunset times

// sunrise

time_t local = owcdata.sunrise + owcdata.timezone + 30; // round to nearest minute

struct tm *timeinfo = gmtime(&local);

char strbuff[80];

strftime(strbuff,80,”%I“,timeinfo);

String datestr = String(atoi(strbuff));

strftime(strbuff,80,”:%M %p“,timeinfo);

datestr = datestr + String(strbuff) + ” - “;

// sunset

local = owcdata.sunset + owcdata.timezone + 30; // round to nearest minute

timeinfo = gmtime(&local);

strftime(strbuff,80,”%I“,timeinfo);

datestr = datestr + String(atoi(strbuff));

strftime(strbuff,80,”:%M %p“,timeinfo);

datestr = datestr + String(strbuff);

gfx.setFont(&FreeSans9pt7b);

int datestrlen = getStringLength(datestr);

int xpos = (gfx.width() - datestrlen)/2;

gfx.setCursor(xpos,166);

gfx.print(datestr);

// draw sunrise icon

// sun icon is ”B“

String wicon = ”B“;

gfx.setFont(&meteocons16pt7b);

gfx.setCursor(xpos - getStringLength(wicon) - 12,174);

gfx.print(wicon);

// draw sunset icon

// sunset icon is ”A“

wicon = ”A“;

gfx.setCursor(xpos + datestrlen + 12,174);

gfx.print(wicon);

gfx.display();

gfx.powerDown();

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

void setup() {

neopixel.begin();

neopixel.show();

gfx.begin();

Serial.println(”ePaper display initialized“);

gfx.setRotation(2);

gfx.setTextWrap(false);

}

void loop() {

char data[4000];

static uint32_t timer = millis();

static uint8_t lastbutton = 1;

static bool firsttime = true;

int button = readButtons();

// update weather data at specified interval or when button 4 is pressed

if((millis() 》= (timer + 1000*60*UPDATE_INTERVAL)) || (button == 4) || firsttime)

{

Serial.println(”getting weather data“);

firsttime = false;

timer = millis();

int retry = 6;

while(!wifi_connect())

{

delay(5000);

retry--;

if(retry 《 0)

{

displayError(”Can not connect to WiFi, press reset to restart“);

while(1);

}

}

String urlc = owclient.buildUrlCurrent(OWM_KEY,OWM_LOCATION);

Serial.println(urlc);

retry = 6;

do

{

retry--;

wget(urlc,80,data);

if(strlen(data) == 0 && retry 《 0)

{

displayError(”Can not get weather data, press reset to restart“);

while(1);

}

}

while(strlen(data) == 0);

Serial.println(”data retrieved:“);

Serial.println(data);

retry = 6;

while(!owclient.updateCurrent(owcdata,data))

{

retry--;

if(retry 《 0)

{

displayError(owclient.getError());

while(1);

}

delay(5000);

}

String urlf = owclient.buildUrlForecast(OWM_KEY,OWM_LOCATION);

Serial.println(urlf);

wget(urlf,80,data);

Serial.println(”data retrieved:“);

Serial.println(data);

if(!owclient.updateForecast(owfdata[0],data,0))

{

displayError(owclient.getError());

while(1);

}

if(!owclient.updateForecast(owfdata[1],data,2))

{

displayError(owclient.getError());

while(1);

}

if(!owclient.updateForecast(owfdata[2],data,4))

{

displayError(owclient.getError());

while(1);

}

switch(lastbutton)

{

case 1:

displayAllWeather(owcdata,owfdata,3);

break;

case 2:

displayCurrentConditions(owcdata);

break;

case 3:

displaySunMoon(owcdata);

break;

}

}

if (button == 0) {

return;

}

Serial.print(”Button “); Serial.print(button); Serial.println(” pressed“);

if (button == 1) {

displayAllWeather(owcdata,owfdata,3);

lastbutton = button;

}

if (button == 2) {

//displayForecast(owcdata,owfdata,3);

displayCurrentConditions(owcdata);

lastbutton = button;

}

if (button == 3) {

displaySunMoon(owcdata);

lastbutton = button;

}

// wait until button is released

while (readButtons()) {

delay(10);

}

}

#include

#include // Core graphics library

#include

#include

#include //https://github.com/bblanchon/ArduinoJson

#include

#include

#include ”secrets.h“

#include ”OpenWeatherMap.h“

#include ”Fonts/meteocons48pt7b.h“

#include ”Fonts/meteocons24pt7b.h“

#include ”Fonts/meteocons20pt7b.h“

#include ”Fonts/meteocons16pt7b.h“

#include ”Fonts/moon_phases20pt7b.h“

#include ”Fonts/moon_phases36pt7b.h“

#include

#include

#include

#include

#include

#define SRAM_CS 8

#define EPD_CS 10

#define EPD_DC 9

#define EPD_RESET -1

#define EPD_BUSY -1

#define NEOPIXELPIN 40

// This is for the 2.7” tricolor EPD

Adafruit_IL91874 gfx(264, 176 ,EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY);

AirliftOpenWeatherMap owclient(&Serial);

OpenWeatherMapCurrentData owcdata;

OpenWeatherMapForecastData owfdata[3];

Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(1, NEOPIXELPIN, NEO_GRB + NEO_KHZ800);

const char *moonphasenames[29] = {

“New Moon”,

“Waxing Crescent”,

“Waxing Crescent”,

“Waxing Crescent”,

“Waxing Crescent”,

“Waxing Crescent”,

“Waxing Crescent”,

“Quarter”,

“Waxing Gibbous”,

“Waxing Gibbous”,

“Waxing Gibbous”,

“Waxing Gibbous”,

“Waxing Gibbous”,

“Waxing Gibbous”,

“Full Moon”,

“Waning Gibbous”,

“Waning Gibbous”,

“Waning Gibbous”,

“Waning Gibbous”,

“Waning Gibbous”,

“Waning Gibbous”,

“Last Quarter”,

“Waning Crescent”,

“Waning Crescent”,

“Waning Crescent”,

“Waning Crescent”,

“Waning Crescent”,

“Waning Crescent”,

“Waning Crescent”

};

int8_t readButtons(void) {

uint16_t reading = analogRead(A3);

//Serial.println(reading);

if (reading 》 600) {

return 0; // no buttons pressed

}

if (reading 》 400) {

return 4; // button D pressed

}

if (reading 》 250) {

return 3; // button C pressed

}

if (reading 》 125) {

return 2; // button B pressed

}

return 1; // Button A pressed

}

bool wifi_connect(){

Serial.print(“Connecting to WiFi.。. ”);

WiFi.setPins(SPIWIFI_SS, SPIWIFI_ACK, ESP32_RESETN, ESP32_GPIO0, &SPIWIFI);

// check for the WiFi module:

if(WiFi.status() == WL_NO_MODULE) {

Serial.println(“Communication with WiFi module failed!”);

displayError(“Communication with WiFi module failed!”);

while(true);

}

String fv = WiFi.firmwareVersion();

if (fv 《 “1.0.0”) {

Serial.println(“Please upgrade the firmware”);

}

neopixel.setPixelColor(0, neopixel.Color(0, 0, 255));

neopixel.show();

if(WiFi.begin(WIFI_SSID, WIFI_PASSWORD) == WL_CONNECT_FAILED)

{

Serial.println(“WiFi connection failed!”);

displayError(“WiFi connection failed!”);

return false;

}

int wifitimeout = 15;

int wifistatus;

while ((wifistatus = WiFi.status()) != WL_CONNECTED && wifitimeout 》 0) {

delay(1000);

Serial.print(“。”);

wifitimeout--;

}

if(wifitimeout == 0)

{

Serial.println(“WiFi connection timeout with error ” + String(wifistatus));

displayError(“WiFi connection timeout with error ” + String(wifistatus));

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

return false;

}

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

Serial.println(“Connected”);

return true;

}

void wget(String &url, int port, char *buff)

{

int pos1 = url.indexOf(“/”,0);

int pos2 = url.indexOf(“/”,8);

String host = url.substring(pos1+2,pos2);

String path = url.substring(pos2);

Serial.println(“to wget(” + host + “,” + path + “,” + port + “)”);

wget(host, path, port, buff);

}

void wget(String &host, String &path, int port, char *buff)

{

//WiFiSSLClient client;

WiFiClient client;

neopixel.setPixelColor(0, neopixel.Color(0, 0, 255));

neopixel.show();

client.stop();

if (client.connect(host.c_str(), port)) {

Serial.println(“connected to server”);

// Make a HTTP request:

client.println(String(“GET ”) + path + String(“ HTTP/1.0”));

client.println(“Host: ” + host);

client.println(“Connection: close”);

client.println();

uint32_t bytes = 0;

int capturepos = 0;

bool capture = false;

int linelength = 0;

char lastc = ’‘;

while(true)

{

while (client.available()) {

char c = client.read();

//Serial.print(c);

if((c == ’ ‘) && (lastc == ’ ‘))

{

if(linelength == 0)

{

capture = true;

}

linelength = 0;

}

else if(capture)

{

buff[capturepos++] = c;

//Serial.write(c);

}

else

{

if((c != ’ ‘) && (c != ’ ‘))

linelength++;

}

lastc = c;

bytes++;

}

// if the server’s disconnected, stop the client:

if (!client.connected()) {

//Serial.println();

Serial.println(“disconnecting from server.”);

client.stop();

buff[capturepos] = ‘’;

Serial.println(“captured ” + String(capturepos) + “ bytes”);

break;

}

}

}

else

{

Serial.println(“problem connecting to ” + host + “:” + String(port));

buff[0] = ‘’;

}

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

int getStringLength(String s)

{

int16_t x = 0, y = 0;

uint16_t w, h;

gfx.getTextBounds(s, 0, 0, &x, &y, &w, &h);

return w + x;

}

/*

return value is percent of moon cycle ( from 0.0 to 0.999999), i.e.:

0.0: New Moon

0.125: Waxing Crescent Moon

0.25: Quarter Moon

0.375: Waxing Gibbous Moon

0.5: Full Moon

0.625: Waning Gibbous Moon

0.75: Last Quarter Moon

0.875: Waning Crescent Moon

*/

float getMoonPhase(time_t tdate)

{

time_t newmoonref = 1263539460; //known new moon date (2010-01-15 07:11)

// moon phase is 29.5305882 days, which is 2551442.82048 seconds

float phase = abs( tdate - newmoonref) / (double)2551442.82048;

phase -= (int)phase; // leave only the remainder

if(newmoonref 》 tdate)

phase = 1 - phase;

return phase;

}

void displayError(String str)

{

// show error on display

neopixel.setPixelColor(0, neopixel.Color(255, 0, 0));

neopixel.show();

Serial.println(str);

gfx.setTextColor(EPD_BLACK);

gfx.powerUp();

gfx.clearBuffer();

gfx.setTextWrap(true);

gfx.setCursor(10,60);

gfx.setFont(&FreeSans12pt7b);

gfx.print(str);

gfx.display();

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

void displayHeading(OpenWeatherMapCurrentData &owcdata)

{

time_t local = owcdata.observationTime + owcdata.timezone;

struct tm *timeinfo = gmtime(&local);

char datestr[80];

// date

//strftime(datestr,80,“%a, %d %b %Y”,timeinfo);

strftime(datestr,80,“%a, %b %d”,timeinfo);

gfx.setFont(&FreeSans18pt7b);

gfx.setCursor((gfx.width()-getStringLength(datestr))/2,30);

gfx.print(datestr);

// city

strftime(datestr,80,“%A”,timeinfo);

gfx.setFont(&FreeSansBold12pt7b);

gfx.setCursor((gfx.width()-getStringLength(owcdata.cityName))/2,60);

gfx.print(owcdata.cityName);

}

void displayForecastDays(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3)

{

for(int i=0; i 《 count; i++)

{

// day

time_t local = owfdata[i].observationTime + owcdata.timezone;

struct tm *timeinfo = gmtime(&local);

char strbuff[80];

strftime(strbuff,80,“%I”,timeinfo);

String datestr = String(atoi(strbuff));

strftime(strbuff,80,“%p”,timeinfo);

// convert AM/PM to lowercase

strbuff[0] = tolower(strbuff[0]);

strbuff[1] = tolower(strbuff[1]);

datestr = datestr + “ ” + String(strbuff);

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(datestr))/2,94);

gfx.print(datestr);

// weather icon

String wicon = owclient.getMeteoconIcon(owfdata[i].icon);

gfx.setFont(&meteocons20pt7b);

gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(wicon))/2,134);

gfx.print(wicon);

// weather main description

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(owfdata[i].main))/2,154);

gfx.print(owfdata[i].main);

// temperature

int itemp = (int)(owfdata[i].temp + .5);

int color = EPD_BLACK;

if((OWM_METRIC && itemp 》= METRIC_HOT)|| (!OWM_METRIC && itemp 》= ENGLISH_HOT))

color = EPD_RED;

gfx.setTextColor(color);

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2,172);

gfx.print(itemp);

gfx.drawCircle(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2 + getStringLength(String(itemp)) + 6,163,3,color);

gfx.drawCircle(i*gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2 + getStringLength(String(itemp)) + 6,163,2,color);

gfx.setTextColor(EPD_BLACK);

}

}

void displayForecast(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3)

{

gfx.powerUp();

gfx.clearBuffer();

neopixel.setPixelColor(0, neopixel.Color(0, 255, 0));

neopixel.show();

gfx.setTextColor(EPD_BLACK);

displayHeading(owcdata);

displayForecastDays(owcdata, owfdata, count);

gfx.display();

gfx.powerDown();

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

void displayAllWeather(OpenWeatherMapCurrentData &owcdata, OpenWeatherMapForecastData owfdata[], int count = 3)

{

gfx.powerUp();

gfx.clearBuffer();

neopixel.setPixelColor(0, neopixel.Color(0, 255, 0));

neopixel.show();

gfx.setTextColor(EPD_BLACK);

// date string

time_t local = owcdata.observationTime + owcdata.timezone;

struct tm *timeinfo = gmtime(&local);

char datestr[80];

// date

//strftime(datestr,80,“%a, %d %b %Y”,timeinfo);

strftime(datestr,80,“%a, %b %d %Y”,timeinfo);

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor((gfx.width()-getStringLength(datestr))/2,14);

gfx.print(datestr);

// weather icon

String wicon = owclient.getMeteoconIcon(owcdata.icon);

gfx.setFont(&meteocons24pt7b);

gfx.setCursor((gfx.width()/3-getStringLength(wicon))/2,56);

gfx.print(wicon);

// weather main description

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor((gfx.width()/3-getStringLength(owcdata.main))/2,72);

gfx.print(owcdata.main);

// temperature

gfx.setFont(&FreeSansBold24pt7b);

int itemp = owcdata.temp + .5;

int color = EPD_BLACK;

if((OWM_METRIC && (int)itemp 》= METRIC_HOT)|| (!OWM_METRIC && (int)itemp 》= ENGLISH_HOT))

color = EPD_RED;

gfx.setTextColor(color);

gfx.setCursor(gfx.width()/3 + (gfx.width()/3-getStringLength(String(itemp)))/2,58);

gfx.print(itemp);

gfx.setTextColor(EPD_BLACK);

// draw temperature degree as a circle (not available as font character

gfx.drawCircle(gfx.width()/3 + (gfx.width()/3 + getStringLength(String(itemp)))/2 + 8, 58-30,4,color);

gfx.drawCircle(gfx.width()/3 + (gfx.width()/3 + getStringLength(String(itemp)))/2 + 8, 58-30,3,color);

// draw moon

// draw Moon Phase

float moonphase = getMoonPhase(owcdata.observationTime);

int moonage = 29.5305882 * moonphase;

//Serial.println(“moon age: ” + String(moonage));

// convert to appropriate icon

String moonstr = String((char)((int)‘A’ + (int)(moonage*25./30)));

gfx.setFont(&moon_phases20pt7b);

// font lines look a little thin at this size, drawing it a few times to thicken the lines

gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2,56);

gfx.print(moonstr);

gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2+1,56);

gfx.print(moonstr);

gfx.setCursor(2*gfx.width()/3 + (gfx.width()/3-getStringLength(moonstr))/2,56-1);

gfx.print(moonstr);

// draw moon phase name

int currentphase = moonphase * 28. + .5;

gfx.setFont(); // system font (smallest available)

gfx.setCursor(2*gfx.width()/3 + max(0,(gfx.width()/3 - getStringLength(moonphasenames[currentphase]))/2),62);

gfx.print(moonphasenames[currentphase]);

displayForecastDays(owcdata, owfdata, count);

gfx.display();

gfx.powerDown();

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

void displayCurrentConditions(OpenWeatherMapCurrentData &owcdata)

{

gfx.powerUp();

gfx.clearBuffer();

neopixel.setPixelColor(0, neopixel.Color(0, 255, 0));

neopixel.show();

gfx.setTextColor(EPD_BLACK);

displayHeading(owcdata);

// weather icon

String wicon = owclient.getMeteoconIcon(owcdata.icon);

gfx.setFont(&meteocons48pt7b);

gfx.setCursor((gfx.width()/2-getStringLength(wicon))/2,156);

gfx.print(wicon);

// weather main description

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor(gfx.width()/2 + (gfx.width()/2-getStringLength(owcdata.main))/2,160);

gfx.print(owcdata.main);

// temperature

gfx.setFont(&FreeSansBold24pt7b);

int itemp = owcdata.temp + .5;

int color = EPD_BLACK;

if((OWM_METRIC && (int)itemp 》= METRIC_HOT)|| (!OWM_METRIC && (int)itemp 》= ENGLISH_HOT))

color = EPD_RED;

gfx.setTextColor(color);

gfx.setCursor(gfx.width()/2 + (gfx.width()/2-getStringLength(String(itemp)))/2,130);

gfx.print(itemp);

gfx.setTextColor(EPD_BLACK);

// draw temperature degree as a circle (not available as font character

gfx.drawCircle(gfx.width()/2 + (gfx.width()/2 + getStringLength(String(itemp)))/2 + 10, 130-26,4,color);

gfx.drawCircle(gfx.width()/2 + (gfx.width()/2 + getStringLength(String(itemp)))/2 + 10, 130-26,3,color);

gfx.display();

gfx.powerDown();

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

void displaySunMoon(OpenWeatherMapCurrentData &owcdata)

{

gfx.powerUp();

gfx.clearBuffer();

neopixel.setPixelColor(0, neopixel.Color(0, 255, 0));

neopixel.show();

gfx.setTextColor(EPD_BLACK);

displayHeading(owcdata);

// draw Moon Phase

float moonphase = getMoonPhase(owcdata.observationTime);

int moonage = 29.5305882 * moonphase;

// convert to appropriate icon

String moonstr = String((char)((int)‘A’ + (int)(moonage*25./30)));

gfx.setFont(&moon_phases36pt7b);

gfx.setCursor((gfx.width()/3-getStringLength(moonstr))/2,140);

gfx.print(moonstr);

// draw moon phase name

int currentphase = moonphase * 28. + .5;

gfx.setFont(&FreeSans9pt7b);

gfx.setCursor(gfx.width()/3 + max(0,(gfx.width()*2/3 - getStringLength(moonphasenames[currentphase]))/2),110);

gfx.print(moonphasenames[currentphase]);

// draw sunrise/sunset

// sunrise/sunset times

// sunrise

time_t local = owcdata.sunrise + owcdata.timezone + 30; // round to nearest minute

struct tm *timeinfo = gmtime(&local);

char strbuff[80];

strftime(strbuff,80,“%I”,timeinfo);

String datestr = String(atoi(strbuff));

strftime(strbuff,80,“:%M %p”,timeinfo);

datestr = datestr + String(strbuff) + “ - ”;

// sunset

local = owcdata.sunset + owcdata.timezone + 30; // round to nearest minute

timeinfo = gmtime(&local);

strftime(strbuff,80,“%I”,timeinfo);

datestr = datestr + String(atoi(strbuff));

strftime(strbuff,80,“:%M %p”,timeinfo);

datestr = datestr + String(strbuff);

gfx.setFont(&FreeSans9pt7b);

int datestrlen = getStringLength(datestr);

int xpos = (gfx.width() - datestrlen)/2;

gfx.setCursor(xpos,166);

gfx.print(datestr);

// draw sunrise icon

// sun icon is “B”

String wicon = “B”;

gfx.setFont(&meteocons16pt7b);

gfx.setCursor(xpos - getStringLength(wicon) - 12,174);

gfx.print(wicon);

// draw sunset icon

// sunset icon is “A”

wicon = “A”;

gfx.setCursor(xpos + datestrlen + 12,174);

gfx.print(wicon);

gfx.display();

gfx.powerDown();

neopixel.setPixelColor(0, neopixel.Color(0, 0, 0));

neopixel.show();

}

void setup() {

neopixel.begin();

neopixel.show();

gfx.begin();

Serial.println(“ePaper display initialized”);

gfx.setRotation(2);

gfx.setTextWrap(false);

}

void loop() {

char data[4000];

static uint32_t timer = millis();

static uint8_t lastbutton = 1;

static bool firsttime = true;

int button = readButtons();

// update weather data at specified interval or when button 4 is pressed

if((millis() 》= (timer + 1000*60*UPDATE_INTERVAL)) || (button == 4) || firsttime)

{

Serial.println(“getting weather data”);

firsttime = false;

timer = millis();

int retry = 6;

while(!wifi_connect())

{

delay(5000);

retry--;

if(retry 《 0)

{

displayError(“Can not connect to WiFi, press reset to restart”);

while(1);

}

}

String urlc = owclient.buildUrlCurrent(OWM_KEY,OWM_LOCATION);

Serial.println(urlc);

retry = 6;

do

{

retry--;

wget(urlc,80,data);

if(strlen(data) == 0 && retry 《 0)

{

displayError(“Can not get weather data, press reset to restart”);

while(1);

}

}

while(strlen(data) == 0);

Serial.println(“data retrieved:”);

Serial.println(data);

retry = 6;

while(!owclient.updateCurrent(owcdata,data))

{

retry--;

if(retry 《 0)

{

displayError(owclient.getError());

while(1);

}

delay(5000);

}

String urlf = owclient.buildUrlForecast(OWM_KEY,OWM_LOCATION);

Serial.println(urlf);

wget(urlf,80,data);

Serial.println(“data retrieved:”);

Serial.println(data);

if(!owclient.updateForecast(owfdata[0],data,0))

{

displayError(owclient.getError());

while(1);

}

if(!owclient.updateForecast(owfdata[1],data,2))

{

displayError(owclient.getError());

while(1);

}

if(!owclient.updateForecast(owfdata[2],data,4))

{

displayError(owclient.getError());

while(1);

}

switch(lastbutton)

{

case 1:

displayAllWeather(owcdata,owfdata,3);

break;

case 2:

displayCurrentConditions(owcdata);

break;

case 3:

displaySunMoon(owcdata);

break;

}

}

if (button == 0) {

return;

}

Serial.print(“Button ”); Serial.print(button); Serial.println(“ pressed”);

if (button == 1) {

displayAllWeather(owcdata,owfdata,3);

lastbutton = button;

}

if (button == 2) {

//displayForecast(owcdata,owfdata,3);

displayCurrentConditions(owcdata);

lastbutton = button;

}

if (button == 3) {

displaySunMoon(owcdata);

lastbutton = button;

}

// wait until button is released

while (readButtons()) {

delay(10);

}

}

下載:Project Zip 或 OpenWeatherMap.h | 在Github上查看

復制代碼

#pragma once

#include “secrets.h”

#include //https://github.com/bblanchon/ArduinoJson

typedef struct OpenWeatherMapCurrentData {

// “lon”: 8.54,

float lon;

// “lat”: 47.37

float lat;

// “id”: 521,

uint16_t weatherId;

// “main”: “Rain”,

String main;

// “description”: “shower rain”,

String description;

// “icon”: “09d”

String icon;

String iconMeteoCon;

// “temp”: 290.56,

float temp;

// “pressure”: 1013,

uint16_t pressure;

// “humidity”: 87,

uint8_t humidity;

// “temp_min”: 289.15,

float tempMin;

// “temp_max”: 292.15

float tempMax;

// visibility: 10000,

uint16_t visibility;

// “wind”: {“speed”: 1.5},

float windSpeed;

// “wind”: {deg: 226.505},

float windDeg;

// “clouds”: {“all”: 90},

uint8_t clouds;

// “dt”: 1527015000,

time_t observationTime;

// “country”: “CH”,

String country;

// “sunrise”: 1526960448,

time_t sunrise;

// “sunset”: 1527015901

time_t sunset;

// “name”: “Zurich”,

String cityName;

time_t timezone;

} OpenWeatherMapCurrentData;

typedef struct OpenWeatherMapForecastData {

// {“dt”:1527066000,

time_t observationTime;

// “main”:{

// “temp”:17.35,

float temp;

// “temp_min”:16.89,

float tempMin;

// “temp_max”:17.35,

float tempMax;

// “pressure”:970.8,

float pressure;

// “sea_level”:1030.62,

float pressureSeaLevel;

// “grnd_level”:970.8,

float pressureGroundLevel;

// “humidity”:97,

uint8_t humidity;

// “temp_kf”:0.46

// },“weather”:[{

// “id”:802,

uint16_t weatherId;

// “main”:“Clouds”,

String main;

// “description”:“scattered clouds”,

String description;

// “icon”:“03d”

String icon;

String iconMeteoCon;

// }],“clouds”:{“all”:44},

uint8_t clouds;

// “wind”:{

// “speed”:1.77,

float windSpeed;

// “deg”:207.501

float windDeg;

// rain: {3h: 0.055},

float rain;

// },“sys”:{“pod”:“d”}

// dt_txt: “2018-05-23 09:00:00”

String observationTimeText;

} OpenWeatherMapForecastData;

class AirliftOpenWeatherMap{

private:

Stream *Serial;

String currentKey;

String currentParent;

//OpenWeatherMapCurrentData *data;

uint8_t weatherItemCounter = 0;

bool metric = true;

String language;

String _error;

public:

AirliftOpenWeatherMap(Stream *serial){Serial = serial;};

String buildUrlCurrent(String appId, String locationParameter);

String buildUrlForecast(String appId, String locationParameter);

bool updateCurrent(OpenWeatherMapCurrentData &data,String json);

bool updateForecast(OpenWeatherMapForecastData &data,String json, int day = 0);

void setMetric(bool metric) {this-》metric = metric;}

bool isMetric() { return metric; }

void setLanguage(String language) { this-》language = language; }

String getLanguage() { return language; }

void setError(String error){_error = error;}

String getError(){return _error;}

String getMeteoconIcon(String icon);

};

#pragma once

#include “secrets.h”

#include //https://github.com/bblanchon/ArduinoJson

typedef struct OpenWeatherMapCurrentData {

// “lon”: 8.54,

float lon;

// “lat”: 47.37

float lat;

// “id”: 521,

uint16_t weatherId;

// “main”: “Rain”,

String main;

// “description”: “shower rain”,

String description;

// “icon”: “09d”

String icon;

String iconMeteoCon;

// “temp”: 290.56,

float temp;

// “pressure”: 1013,

uint16_t pressure;

// “humidity”: 87,

uint8_t humidity;

// “temp_min”: 289.15,

float tempMin;

// “temp_max”: 292.15

float tempMax;

// visibility: 10000,

uint16_t visibility;

// “wind”: {“speed”: 1.5},

float windSpeed;

// “wind”: {deg: 226.505},

float windDeg;

// “clouds”: {“all”: 90},

uint8_t clouds;

// “dt”: 1527015000,

time_t observationTime;

// “country”: “CH”,

String country;

// “sunrise”: 1526960448,

time_t sunrise;

// “sunset”: 1527015901

time_t sunset;

// “name”: “Zurich”,

String cityName;

time_t timezone;

} OpenWeatherMapCurrentData;

typedef struct OpenWeatherMapForecastData {

// {“dt”:1527066000,

time_t observationTime;

// “main”:{

// “temp”:17.35,

float temp;

// “temp_min”:16.89,

float tempMin;

// “temp_max”:17.35,

float tempMax;

// “pressure”:970.8,

float pressure;

// “sea_level”:1030.62,

float pressureSeaLevel;

// “grnd_level”:970.8,

float pressureGroundLevel;

// “humidity”:97,

uint8_t humidity;

// “temp_kf”:0.46

// },“weather”:[{

// “id”:802,

uint16_t weatherId;

// “main”:“Clouds”,

String main;

// “description”:“scattered clouds”,

String description;

// “icon”:“03d”

String icon;

String iconMeteoCon;

// }],“clouds”:{“all”:44},

uint8_t clouds;

// “wind”:{

// “speed”:1.77,

float windSpeed;

// “deg”:207.501

float windDeg;

// rain: {3h: 0.055},

float rain;

// },“sys”:{“pod”:“d”}

// dt_txt: “2018-05-23 09:00:00”

String observationTimeText;

} OpenWeatherMapForecastData;

class AirliftOpenWeatherMap{

private:

Stream *Serial;

String currentKey;

String currentParent;

//OpenWeatherMapCurrentData *data;

uint8_t weatherItemCounter = 0;

bool metric = true;

String language;

String _error;

public:

AirliftOpenWeatherMap(Stream *serial){Serial = serial;};

String buildUrlCurrent(String appId, String locationParameter);

String buildUrlForecast(String appId, String locationParameter);

bool updateCurrent(OpenWeatherMapCurrentData &data,String json);

bool updateForecast(OpenWeatherMapForecastData &data,String json, int day = 0);

void setMetric(bool metric) {this-》metric = metric;}

bool isMetric() { return metric; }

void setLanguage(String language) { this-》language = language; }

String getLanguage() { return language; }

void setError(String error){_error = error;}

String getError(){return _error;}

String getMeteoconIcon(String icon);

};

下載:項目Zip 或 OpenWeatherMap.cpp | 在Github上查看復制代碼

#include “OpenWeatherMap.h”

String AirliftOpenWeatherMap::buildUrlCurrent(String appId, String location) {

String units = OWM_METRIC ? “metric” : “imperial”;

return “http://api.openweathermap.org/data/2.5/weather?q=” + location + “&appid=” + appId + “&units=” + units + “&lang=” + String(OWM_LANGUAGE);

}

String AirliftOpenWeatherMap::buildUrlForecast(String appId, String location) {

String units = OWM_METRIC ? “metric” : “imperial”;

return “http://api.openweathermap.org/data/2.5/forecast?q=” + location + “&cnt=6&appid=” + appId + “&units=” + units + “&lang=” + String(OWM_LANGUAGE);

}

String AirliftOpenWeatherMap::getMeteoconIcon(String icon) {

// clear sky

// 01d

if (icon == “01d”) {

return “B”;

}

// 01n

if (icon == “01n”) {

return “C”;

}

// few clouds

// 02d

if (icon == “02d”) {

return “H”;

}

// 02n

if (icon == “02n”) {

return “4”;

}

// scattered clouds

// 03d

if (icon == “03d”) {

return “N”;

}

// 03n

if (icon == “03n”) {

return “5”;

}

// broken clouds

// 04d

if (icon == “04d”) {

return “Y”;

}

// 04n

if (icon == “04n”) {

return “%”;

}

// shower rain

// 09d

if (icon == “09d”) {

return “R”;

}

// 09n

if (icon == “09n”) {

return “8”;

}

// rain

// 10d

if (icon == “10d”) {

return “Q”;

}

// 10n

if (icon == “10n”) {

return “7”;

}

// thunderstorm

// 11d

if (icon == “11d”) {

return “P”;

}

// 11n

if (icon == “11n”) {

return “6”;

}

// snow

// 13d

if (icon == “13d”) {

return “W”;

}

// 13n

if (icon == “13n”) {

return “#”;

}

// mist

// 50d

if (icon == “50d”) {

return “M”;

}

// 50n

if (icon == “50n”) {

return “M”;

}

// Nothing matched: N/A

return “)”;

}

bool AirliftOpenWeatherMap::updateCurrent(OpenWeatherMapCurrentData &data, String json)

{

Serial-》println(“updateCurrent()”);

DynamicJsonDocument doc(2000);

//StaticJsonDocument《2000》 doc;

DeserializationError error = deserializeJson(doc, json);

if (error) {

Serial-》println(String(“deserializeJson() failed: ”) + (const char *)error.c_str());

Serial-》println(json);

setError(String(“deserializeJson() failed: ”) + error.c_str());

return false;

}

int code = (int) doc[“cod”];

if(code != 200)

{

Serial-》println(String(“OpenWeatherMap error: ”) + (const char *)doc[“message”]);

setError(String(“OpenWeatherMap error: ”) + (const char *)doc[“message”]);

return false;

}

data.lat = (float) doc[“coord”][“lat”];

data.lon = (float) doc[“coord”][“lon”];

data.main = (const char*) doc[“weather”][0][“main”];

data.description = (const char*) doc[“weather”][0][“description”];

data.icon = (const char*) doc[“weather”][0][“icon”];

data.cityName = (const char*) doc[“name”];

data.visibility = (uint16_t) doc[“visibility”];

data.timezone = (time_t) doc[“timezone”];

data.country = (const char*) doc[“sys”][“country”];

data.observationTime = (time_t) doc[“dt”];

data.sunrise = (time_t) doc[“sys”][“sunrise”];

data.sunset = (time_t) doc[“sys”][“sunset”];

data.temp = (float) doc[“main”][“temp”];

data.pressure = (uint16_t) doc[“main”][“pressure”];

data.humidity = (uint8_t) doc[“main”][“humidity”];

data.tempMin = (float) doc[“main”][“temp_min”];

data.tempMax = (float) doc[“main”][“temp_max”];

data.windSpeed = (float) doc[“wind”][“speed”];

data.windDeg = (float) doc[“wind”][“deg”];

return true;

}

bool AirliftOpenWeatherMap::updateForecast(OpenWeatherMapForecastData &data, String json, int day)

{

Serial-》println(“updateForecast()”);

DynamicJsonDocument doc(5000);

//StaticJsonDocument《5000》 doc;

DeserializationError error = deserializeJson(doc, json);

if (error) {

Serial-》println(String(“deserializeJson() failed: ”) + (const char *)error.c_str());

Serial-》println(json);

setError(String(“deserializeJson() failed: ”) + error.c_str());

return false;

}

int code = (int) doc[“cod”];

if(code != 200)

{

Serial-》println(String(“OpenWeatherMap error: ”) + (const char *)doc[“message”]);

setError(String(“OpenWeatherMap error: ”) + (const char *)doc[“message”]);

return false;

}

data.observationTime = (time_t) doc[“list”][day][“dt”];

data.temp = (float) doc[“list”][day][“main”][“temp”];

data.pressure = (uint16_t) doc[“list”][day][“main”][“pressure”];

data.humidity = (uint8_t) doc[“list”][day][“main”][“humidity”];

data.tempMin = (float) doc[“list”][day][“main”][“temp_min”];

data.tempMax = (float) doc[“list”][day][“main”][“temp_max”];

data.main = (const char*) doc[“list”][day][“weather”][0][“main”];

data.description = (const char*) doc[“list”][day][“weather”][0][“description”];

data.icon = (const char*) doc[“list”][day][“weather”][0][“icon”];

return true;

}

#include “OpenWeatherMap.h”

String AirliftOpenWeatherMap::buildUrlCurrent(String appId, String location) {

String units = OWM_METRIC ? “metric” : “imperial”;

return “http://api.openweathermap.org/data/2.5/weather?q=” + location + “&appid=” + appId + “&units=” + units + “&lang=” + String(OWM_LANGUAGE);

}

String AirliftOpenWeatherMap::buildUrlForecast(String appId, String location) {

String units = OWM_METRIC ? “metric” : “imperial”;

return “http://api.openweathermap.org/data/2.5/forecast?q=” + location + “&cnt=6&appid=” + appId + “&units=” + units + “&lang=” + String(OWM_LANGUAGE);

}

String AirliftOpenWeatherMap::getMeteoconIcon(String icon) {

// clear sky

// 01d

if (icon == “01d”) {

return “B”;

}

// 01n

if (icon == “01n”) {

return “C”;

}

// few clouds

// 02d

if (icon == “02d”) {

return “H”;

}

// 02n

if (icon == “02n”) {

return “4”;

}

// scattered clouds

// 03d

if (icon == “03d”) {

return “N”;

}

// 03n

if (icon == “03n”) {

return “5”;

}

// broken clouds

// 04d

if (icon == “04d”) {

return “Y”;

}

// 04n

if (icon == “04n”) {

return “%”;

}

// shower rain

// 09d

if (icon == “09d”) {

return “R”;

}

// 09n

if (icon == “09n”) {

return “8”;

}

// rain

// 10d

if (icon == “10d”) {

return “Q”;

}

// 10n

if (icon == “10n”) {

return “7”;

}

// thunderstorm

// 11d

if (icon == “11d”) {

return “P”;

}

// 11n

if (icon == “11n”) {

return “6”;

}

// snow

// 13d

if (icon == “13d”) {

return “W”;

}

// 13n

if (icon == “13n”) {

return “#”;

}

// mist

// 50d

if (icon == “50d”) {

return “M”;

}

// 50n

if (icon == “50n”) {

return “M”;

}

// Nothing matched: N/A

return “)”;

}

bool AirliftOpenWeatherMap::updateCurrent(OpenWeatherMapCurrentData &data, String json)

{

Serial-》println(“updateCurrent()”);

DynamicJsonDocument doc(2000);

//StaticJsonDocument《2000》 doc;

DeserializationError error = deserializeJson(doc, json);

if (error) {

Serial-》println(String(“deserializeJson() failed: ”) + (const char *)error.c_str());

Serial-》println(json);

setError(String(“deserializeJson() failed: ”) + error.c_str());

return false;

}

int code = (int) doc[“cod”];

if(code != 200)

{

Serial-》println(String(“OpenWeatherMap error: ”) + (const char *)doc[“message”]);

setError(String(“OpenWeatherMap error: ”) + (const char *)doc[“message”]);

return false;

}

data.lat = (float) doc[“coord”][“lat”];

data.lon = (float) doc[“coord”][“lon”];

data.main = (const char*) doc[“weather”][0][“main”];

data.description = (const char*) doc[“weather”][0][“description”];

data.icon = (const char*) doc[“weather”][0][“icon”];

data.cityName = (const char*) doc[“name”];

data.visibility = (uint16_t) doc[“visibility”];

data.timezone = (time_t) doc[“timezone”];

data.country = (const char*) doc[“sys”][“country”];

data.observationTime = (time_t) doc[“dt”];

data.sunrise = (time_t) doc[“sys”][“sunrise”];

data.sunset = (time_t) doc[“sys”][“sunset”];

data.temp = (float) doc[“main”][“temp”];

data.pressure = (uint16_t) doc[“main”][“pressure”];

data.humidity = (uint8_t) doc[“main”][“humidity”];

data.tempMin = (float) doc[“main”][“temp_min”];

data.tempMax = (float) doc[“main”][“temp_max”];

data.windSpeed = (float) doc[“wind”][“speed”];

data.windDeg = (float) doc[“wind”][“deg”];

return true;

}

bool AirliftOpenWeatherMap::updateForecast(OpenWeatherMapForecastData &data, String json, int day)

{

Serial-》println(“updateForecast()”);

DynamicJsonDocument doc(5000);

//StaticJsonDocument《5000》 doc;

DeserializationError error = deserializeJson(doc, json);

if (error) {

Serial-》println(String(“deserializeJson() failed: ”) + (const char *)error.c_str());

Serial-》println(json);

setError(String(“deserializeJson() failed: ”) + error.c_str());

return false;

}

int code = (int) doc[“cod”];

if(code != 200)

{

Serial-》println(String(“OpenWeatherMap error: ”) + (const char *)doc[“message”]);

setError(String(“OpenWeatherMap error: ”) + (const char *)doc[“message”]);

return false;

}

data.observationTime = (time_t) doc[“list”][day][“dt”];

data.temp = (float) doc[“list”][day][“main”][“temp”];

data.pressure = (uint16_t) doc[“list”][day][“main”][“pressure”];

data.humidity = (uint8_t) doc[“list”][day][“main”][“humidity”];

data.tempMin = (float) doc[“list”][day][“main”][“temp_min”];

data.tempMax = (float) doc[“list”][day][“main”][“temp_max”];

data.main = (const char*) doc[“list”][day][“weather”][0][“main”];

data.description = (const char*) doc[“list”][day][“weather”][0][“description”];

data.icon = (const char*) doc[“list”][day][“weather”][0][“icon”];

return true;

}

字體文件可在此處的GitHub存儲庫中找到。

使用

防護罩包括4個可編程按鈕(標有“ A”至“ D”)和一個復位按鈕。這些按鈕已被編程以顯示不同的天氣顯示。首次啟動設備時,顯示屏將顯示當前和天氣預報。您也可以通過按“ A”或重置按鈕查看此顯示。按鈕“ B”顯示當前的天氣狀況,按鈕“ C”顯示當前的月相以及一天中的日出和日落時間。 “ D”按鈕將從互聯網上檢索最新的天氣數據,并使用最后的顯示模式進行顯示。如果更新間隔較長,并且想用最新的天氣數據更新顯示,則此功能特別有用。

使用按鈕顯示不同的天氣視圖。

狀態LED

TheMetro M4 Express Airlift還配備了一個NeoPixel。此項目使用NeoPixel作為項目狀態指示器。

A 藍色 NeoPixel狀態表示草圖當前正在訪問Internet,或者連接到WiFi熱點或獲取日期和時間。 。

A 綠色 NeoPixel狀態表示草圖當前正在更新顯示,完成后將關閉。由于ePaper顯示屏刷新屏幕的速度不是很快,因此可能要花費幾秒鐘。

A 紅色 NeoPixel狀態表示存在網絡通信問題。如果NeoPixel顯示這些狀態顏色之一,則按按鈕將無效。等待NeoPixel關閉,然后按其中一個按鈕。

NeoPixel用作狀態指示器。此處顯示為綠色表示正在更新顯示。

按下按鈕A或啟動設備會顯示當前和天氣預報。

按下按鈕B顯示當前的天氣情況。

按下按鈕C顯示當前的月相以及日出和日落時間。
責任編輯:wv

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 氣象站
    +關注

    關注

    1

    文章

    756

    瀏覽量

    15695
收藏 人收藏

    評論

    相關推薦

    自動氣象站的工作原理與應用

    。 自動氣象站的工作原理 自動氣象站的工作原理基于系列高精度傳感器和數據采集技術。這些傳感器能夠實時監測和記錄多種氣象要素,包括溫度、濕度、氣壓、風速、風向、降水量以及太陽輻射等。這
    的頭像 發表于 12-18 13:56 ?161次閱讀

    負氧離子氣象站

    氣象站
    pingao141378
    發布于 :2024年10月19日 08:52:28

    車載小型氣象站:實時、準確地監測氣象

    車載小型氣象站種高度集成化的氣象監測設備,專為移動環境設計,如車輛、船只或移動觀測站等,能夠實時、準確地監測多種關鍵氣象要素。 集成度高:車載小型
    的頭像 發表于 09-13 17:24 ?255次閱讀

    小型車載自動氣象站:提供全面、及時的氣象信息

    和記錄車輛周圍的氣象數據,配有超聲波多合環境監測傳感器,集成度較高,可對風速、風向、空氣溫度、空氣濕度、噪聲、PM2.5、PM10、二氧化碳等氣象要素進行實時監測,為駕駛員提供即時的天氣狀況
    的頭像 發表于 09-12 16:52 ?297次閱讀

    校園氣象站高校科普論文氣象站多功能氣象站

    校園氣象站高校科普論文氣象站多功能氣象站種集氣象監測、數據傳輸、數據處理、信息發布等功能于
    的頭像 發表于 08-06 17:53 ?458次閱讀
    校園<b class='flag-5'>氣象站</b>高校科普論文<b class='flag-5'>氣象站</b>多功能<b class='flag-5'>氣象站</b>

    便攜式氣象站:未來氣象監測的便攜新選擇

    氣象站
    萬象環境科技
    發布于 :2024年06月23日 10:39:24

    便攜式氣象站:科技與便捷的完美融合

    氣象站
    萬象環境科技
    發布于 :2024年06月22日 21:14:07

    什么是自動氣象站

    在科學技術迅猛發展的今天,自動氣象站以其高效、精準、實時的特點,逐漸成為現代氣象觀測的先鋒。它不僅極大地提升了氣象觀測的效率和準確性,還為農業生產、交通運輸、環境保護等領域提供了重要的氣象
    的頭像 發表于 06-14 09:15 ?557次閱讀
    什么是自動<b class='flag-5'>氣象站</b>?

    小型氣象站是什么?有哪些作用?

    小型氣象站可以監測多種氣象參數,包括但不限于溫度、濕度、氣壓、風速、風向、降水量等。這些數據對于農業、氣象預測、交通運輸、環境監測等領域都至關重要。例如,在農業領域,農民可以根據氣象站
    的頭像 發表于 05-29 17:03 ?583次閱讀

    微型氣象站的性能表現如何?

    微型氣象站種小型化、輕便化的氣象監測設備,通常用于戶外環境的氣象監測和數據采集。其性能表現主要包括準確性、穩定性、靈敏度和易用性等方面。 首先,微型
    的頭像 發表于 05-10 16:22 ?429次閱讀

    農業小型自動氣象站應用

    小型自動氣象站作為現代農業的重要組成部分,其應用價值不可忽視。在農業生產中,氣象條件是影響作物生長和產量的重要因素之。因此,小型自動氣象站通過提供準確、及時的
    的頭像 發表于 04-24 16:42 ?460次閱讀

    小型農業氣象站廠家:數據分析與決策支持

    ,助力農業增產致富。 、智能化農業氣象站——助力農民科學種植 小型農業氣象站廠家推出的智能化農業氣象站,集成了先進的氣象傳感器
    的頭像 發表于 04-02 16:35 ?463次閱讀

    罐區防爆氣象站:科技保障,安全生產

    JD-FBCQ罐區防爆氣象站是指在石油、化工等工業罐區內設置的氣象監測設備,旨在監測和分析當地氣象變化,提供關鍵氣象信息,以保障罐區安全
    的頭像 發表于 03-19 16:40 ?614次閱讀

    小型手持氣象站:它是否能夠提供準確的天氣預報?

    小型手持氣象站種方便攜帶、操作簡便的氣象監測設備,旨在為用戶提供實時、準確的氣象信息,適用于戶外探險、登山、露營等活動場合。以下是該設備的主要特點和功能: 首先,小型手持
    的頭像 發表于 03-18 10:41 ?377次閱讀

    什么是科研用的小型氣象站

      JD-CQX7科研用的小型氣象站種專門設計用于科學研究領域的氣象監測設備,其主要特點是小巧輕便、靈活易用,并具有高精度的測量性能。這種氣象站
    的頭像 發表于 03-15 15:26 ?397次閱讀
    主站蜘蛛池模板: 欧美成人26uuu欧美毛片| 人人爱操| 国产你懂的在线| 搞逼综合网| 躁天天躁中文字幕在线| 午夜性福| 奇米一区二区三区四区久久| 久久深夜福利| 国产伦一区二区三区免费| 99xxxx开心| sesese在线播放| 韩国美女丝袜一区二区| 亚洲国产成人精品不卡青青草原| 天天爱天天色| 欧美色图中文字幕| 国产一级毛片外aaaa| 1000部啪啪未满十八勿入 | 五月婷婷色网| 日本黄色大片免费观看| 两性午夜欧美高清做性| www视频在线观看天堂| 天堂在线www天堂中文在线| 国产成人综合自拍| 一级美女片| 亚洲色图欧美在线| 日本三级香港三级三级人!妇久| 久久久免费精品视频| 夜夜爽天天爽| 狠狠干天天操| 一区二区视频网| 亚洲成人精品在线| 男女爱爱是免费看| 97干干| 久久久久久久国产精品影院| 69日本xxxxxxxxx98| 亚洲成a人v在线观看| 欧美三级第一页| 亚洲精品午夜久久aaa级久久久| 黄色理伦| 中文字幕第8页| 免费亚洲成人|