0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何創(chuàng)建一個具有當(dāng)前天氣狀況的氣象站

454398 ? 來源:網(wǎng)絡(luò)整理 ? 作者:網(wǎng)絡(luò)整理 ? 2019-11-29 09:56 ? 次閱讀

概述

太高興了

天氣怎么樣?

海龜,“高興起來”

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

發(fā)布Metro M4 Express AirLift時發(fā)生了一件很棒的事情。它打開了Arduino草圖到互聯(lián)網(wǎng),使它們擺脫了封閉環(huán)境的限制。現(xiàn)在,它正式成為物聯(lián)網(wǎng)IoT)世界中的“事物”。在此項目中,Metro M4 Express AirLift將從互聯(lián)網(wǎng)上獲取您當(dāng)?shù)氐奶鞖鈹?shù)據(jù),并在易于閱讀的ePaper顯示屏上顯示

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

功能

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

當(dāng)前日期

當(dāng)前溫度

當(dāng)前天氣狀況

接下來的12小時的預(yù)報數(shù)據(jù),以6小時為增量

天氣圖標(biāo)以顯示天氣情況和天氣預(yù)報

城市名稱

顯示當(dāng)前和預(yù)測的高溫紅色

日出和日落時間

當(dāng)前月相的描述和圖標(biāo)

零件

構(gòu)建該項目無需焊接,僅使用兩個零件: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

產(chǎn)品ID:4000

使用AirLift為您的下一個項目提供電梯-我們?yōu)檫@個Metro M4增添了ESP32協(xié)處理器的機智名稱。您已經(jīng)知道theAdafruit Metro 。..

$ 34.95

入庫存

添加到購物車

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

產(chǎn)品ID:4229

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

$ 39.95

入庫存

添加到購物車

USB電纜-USB A到Micro -B

產(chǎn)品ID:592

這是您的標(biāo)準(zhǔn)A到Micro-B USB電纜,用于USB 1.1或2.0。非常適合將PC連接到Metro,F(xiàn)eather, Raspberry Pi或其他開發(fā)板或。..

$ 2.95

入庫存

添加到購物車

工作原理

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

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

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

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

Meteoicon字體包含40多種與天氣相關(guān)的字體。

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

Arduino設(shè)置

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

安裝庫

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

Adafruit EPD(電子紙顯示)庫

Adafruit GFX庫

Adafruit NeoPixel庫

Arduino JSON庫

Adafruit的變體WiFiNINA庫

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

打開Arduino庫管理器:

搜索 Adafruit EPD 庫并安裝

搜索 Adafruit GFX 庫并安裝它

搜索 Adafruit NeoPixel 庫并安裝

搜索 ArduinoJSON 庫并安裝

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

下載Adafruit版本的WiFiNina

注冊O(shè)penWeatherMap API密鑰

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

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

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

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

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

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

復(fù)制代碼

#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上查看

復(fù)制代碼

#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 ”O(jiān)penWeatherMap.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上查看

復(fù)制代碼

#pragma once

#include “secrets.h”

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

typedef struct OpenWeatherMapCurrentData {

// “l(fā)on”: 8.54,

float lon;

// “l(fā)at”: 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 {

// “l(fā)on”: 8.54,

float lon;

// “l(fā)at”: 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上查看復(fù)制代碼

#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”][“l(fā)at”];

data.lon = (float) doc[“coord”][“l(fā)on”];

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[“l(fā)ist”][day][“dt”];

data.temp = (float) doc[“l(fā)ist”][day][“main”][“temp”];

data.pressure = (uint16_t) doc[“l(fā)ist”][day][“main”][“pressure”];

data.humidity = (uint8_t) doc[“l(fā)ist”][day][“main”][“humidity”];

data.tempMin = (float) doc[“l(fā)ist”][day][“main”][“temp_min”];

data.tempMax = (float) doc[“l(fā)ist”][day][“main”][“temp_max”];

data.main = (const char*) doc[“l(fā)ist”][day][“weather”][0][“main”];

data.description = (const char*) doc[“l(fā)ist”][day][“weather”][0][“description”];

data.icon = (const char*) doc[“l(fā)ist”][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”][“l(fā)at”];

data.lon = (float) doc[“coord”][“l(fā)on”];

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[“l(fā)ist”][day][“dt”];

data.temp = (float) doc[“l(fā)ist”][day][“main”][“temp”];

data.pressure = (uint16_t) doc[“l(fā)ist”][day][“main”][“pressure”];

data.humidity = (uint8_t) doc[“l(fā)ist”][day][“main”][“humidity”];

data.tempMin = (float) doc[“l(fā)ist”][day][“main”][“temp_min”];

data.tempMax = (float) doc[“l(fā)ist”][day][“main”][“temp_max”];

data.main = (const char*) doc[“l(fā)ist”][day][“weather”][0][“main”];

data.description = (const char*) doc[“l(fā)ist”][day][“weather”][0][“description”];

data.icon = (const char*) doc[“l(fā)ist”][day][“weather”][0][“icon”];

return true;

}

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

使用

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

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

狀態(tài)LED

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

A 藍(lán)色 NeoPixel狀態(tài)表示草圖當(dāng)前正在訪問Internet,或者連接到WiFi熱點或獲取日期和時間。 。

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

A 紅色 NeoPixel狀態(tài)表示存在網(wǎng)絡(luò)通信問題。如果NeoPixel顯示這些狀態(tài)顏色之一,則按按鈕將無效。等待NeoPixel關(guān)閉,然后按其中一個按鈕。

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

按下按鈕A或啟動設(shè)備會顯示當(dāng)前和天氣預(yù)報。

按下按鈕B顯示當(dāng)前的天氣情況。

按下按鈕C顯示當(dāng)前的月相以及日出和日落時間。
責(zé)任編輯:wv

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 氣象站
    +關(guān)注

    關(guān)注

    1

    文章

    732

    瀏覽量

    15595
收藏 人收藏

    評論

    相關(guān)推薦

    負(fù)氧離子氣象站

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

    車載小型氣象站:實時、準(zhǔn)確地監(jiān)測氣象

    車載小型氣象站種高度集成化的氣象監(jiān)測設(shè)備,專為移動環(huán)境設(shè)計,如車輛、船只或移動觀測站等,能夠?qū)崟r、準(zhǔn)確地監(jiān)測多種關(guān)鍵氣象要素。 集成度高:車載小型
    的頭像 發(fā)表于 09-13 17:24 ?151次閱讀

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

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

    校園氣象站高??破照撐?b class='flag-5'>氣象站多功能氣象站

    校園氣象站高??破照撐?b class='flag-5'>氣象站多功能氣象站種集氣象監(jiān)測、數(shù)據(jù)傳輸、數(shù)據(jù)處理、信息發(fā)布等功能于
    的頭像 發(fā)表于 08-06 17:53 ?369次閱讀
    校園<b class='flag-5'>氣象站</b>高校科普論文<b class='flag-5'>氣象站</b>多功能<b class='flag-5'>氣象站</b>

    便攜式氣象站:未來氣象監(jiān)測的便攜新選擇

    氣象站
    萬象環(huán)境科技
    發(fā)布于 :2024年06月23日 10:39:24

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

    氣象站
    萬象環(huán)境科技
    發(fā)布于 :2024年06月22日 21:14:07

    什么是自動氣象站?

    在科學(xué)技術(shù)迅猛發(fā)展的今天,自動氣象站以其高效、精準(zhǔn)、實時的特點,逐漸成為現(xiàn)代氣象觀測的先鋒。它不僅極大地提升了氣象觀測的效率和準(zhǔn)確性,還為農(nóng)業(yè)生產(chǎn)、交通運輸、環(huán)境保護等領(lǐng)域提供了重要的氣象
    的頭像 發(fā)表于 06-14 09:15 ?443次閱讀
    什么是自動<b class='flag-5'>氣象站</b>?

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

    小型氣象站可以監(jiān)測多種氣象參數(shù),包括但不限于溫度、濕度、氣壓、風(fēng)速、風(fēng)向、降水量等。這些數(shù)據(jù)對于農(nóng)業(yè)、氣象預(yù)測、交通運輸、環(huán)境監(jiān)測等領(lǐng)域都至關(guān)重要。例如,在農(nóng)業(yè)領(lǐng)域,農(nóng)民可以根據(jù)氣象站
    的頭像 發(fā)表于 05-29 17:03 ?509次閱讀

    微型氣象站的性能表現(xiàn)如何?

    微型氣象站種小型化、輕便化的氣象監(jiān)測設(shè)備,通常用于戶外環(huán)境的氣象監(jiān)測和數(shù)據(jù)采集。其性能表現(xiàn)主要包括準(zhǔn)確性、穩(wěn)定性、靈敏度和易用性等方面。 首先,微型
    的頭像 發(fā)表于 05-10 16:22 ?317次閱讀

    小型農(nóng)業(yè)氣象站廠家:數(shù)據(jù)分析與決策支持

    ,助力農(nóng)業(yè)增產(chǎn)致富。 、智能化農(nóng)業(yè)氣象站——助力農(nóng)民科學(xué)種植 小型農(nóng)業(yè)氣象站廠家推出的智能化農(nóng)業(yè)氣象站,集成了先進的氣象傳感器
    的頭像 發(fā)表于 04-02 16:35 ?382次閱讀

    罐區(qū)防爆氣象站:科技保障,安全生產(chǎn)

    JD-FBCQ罐區(qū)防爆氣象站是指在石油、化工等工業(yè)罐區(qū)內(nèi)設(shè)置的氣象監(jiān)測設(shè)備,旨在監(jiān)測和分析當(dāng)?shù)?b class='flag-5'>氣象變化,提供關(guān)鍵氣象信息,以保障罐區(qū)安全
    的頭像 發(fā)表于 03-19 16:40 ?531次閱讀

    小型手持氣象站:它是否能夠提供準(zhǔn)確的天氣預(yù)報?

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

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

      JD-CQX7科研用的小型氣象站種專門設(shè)計用于科學(xué)研究領(lǐng)域的氣象監(jiān)測設(shè)備,其主要特點是小巧輕便、靈活易用,并具有高精度的測量性能。這種氣象站
    的頭像 發(fā)表于 03-15 15:26 ?328次閱讀

    校園氣象站組成與作用

    教育也擺到了相應(yīng)重要的位置。 校園氣象站屬地面小型氣象站般學(xué)??梢园?b class='flag-5'>氣象站安裝在空曠的操場上和教學(xué)樓頂,周圍也加配些護欄以確保儀器的安
    的頭像 發(fā)表于 10-31 16:28 ?603次閱讀

    小型氣象站數(shù)據(jù)采集網(wǎng)關(guān)——準(zhǔn)確監(jiān)測雨量、風(fēng)速

    隨著科技的發(fā)展,人們對天氣信息的需求也愈發(fā)增加。天氣是每個人生活中不可或缺的部分,了解準(zhǔn)確的天氣信息有助于我們做出合理的決策和安排。在小型氣象站
    的頭像 發(fā)表于 10-27 16:03 ?470次閱讀
    小型<b class='flag-5'>氣象站</b>數(shù)據(jù)采集網(wǎng)關(guān)——準(zhǔn)確監(jiān)測雨量、風(fēng)速