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

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)在多個國家(例如意大利威尼斯和佛羅里達(dá)州威尼斯),則可能需要包含2個字母的國家/地區(qū)代碼。因此,例如,佛羅里達(dá)州的威尼斯將被輸入為“美國威尼斯”。

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

下載: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

    文章

    766

    瀏覽量

    16051
收藏 人收藏

    評論

    相關(guān)推薦
    熱點推薦

    給孩子堂自然課:氣象站如何讓科學(xué)觸手可及?

    氣象站
    奕帆科技
    發(fā)布于 :2025年05月06日 14:57:01

    解密交通氣象站:數(shù)據(jù)驅(qū)動的交通氣象服務(wù)革新

    在當(dāng)今數(shù)字化時代,數(shù)據(jù)成為了推動各行業(yè)變革的核心動力,交通氣象領(lǐng)域也不例外。交通氣象站作為交通氣象服務(wù)的關(guān)鍵設(shè)施,正借助數(shù)據(jù)的力量實現(xiàn)著革新,為交通出行與順暢提供著更強大的支持。 交通氣象站
    的頭像 發(fā)表于 03-27 15:36 ?161次閱讀
    解密交通<b class='flag-5'>氣象站</b>:數(shù)據(jù)驅(qū)動的交通<b class='flag-5'>氣象</b>服務(wù)革新

    自動氣象站的工作原理與應(yīng)用

    。 自動氣象站的工作原理 自動氣象站的工作原理基于系列高精度傳感器和數(shù)據(jù)采集技術(shù)。這些傳感器能夠?qū)崟r監(jiān)測和記錄多種氣象要素,包括溫度、濕度、氣壓、風(fēng)速、風(fēng)向、降水量以及太陽輻射等。這
    的頭像 發(fā)表于 12-18 13:56 ?600次閱讀

    綠色校園,智慧氣象:走進我們的氣象站

    在蔚藍(lán)天空與蔥郁綠意的交相輝映下,我們的校園不僅是片知識的沃土,更是綠色生.態(tài)與智慧科技融合的典范。在這片充滿活力的土地上,座現(xiàn)代化的氣象站悄然矗立,它不僅是我們探索自然奧秘的窗口,更是綠色校園
    的頭像 發(fā)表于 12-10 11:58 ?326次閱讀

    自動超聲波氣象站:智慧氣象的重要組成部分

    自動超聲波氣象站款高度集成、低功耗、可快速安裝、便于野外監(jiān)測使用的高精度自動氣象觀測設(shè)備。以下是對自動超聲波氣象站的詳細(xì)介紹: 、概述
    的頭像 發(fā)表于 12-06 11:51 ?518次閱讀

    多功能自動氣象站-支持定制監(jiān)測數(shù)據(jù)

    多功能自動氣象站支持定制監(jiān)測數(shù)據(jù)具有諸多優(yōu)勢和特點 可定制的監(jiān)測要素 : 常規(guī)氣象要素 :般來說,溫度、濕度、氣壓、風(fēng)速、風(fēng)向、降雨量等是自動氣象
    的頭像 發(fā)表于 10-31 15:30 ?411次閱讀

    交通氣象站:實時監(jiān)測道路氣象環(huán)境

    在現(xiàn)代交通體系中,安全與效率始終是人們關(guān)注的焦點。而交通氣象站,如同位無聲的守護者,實時監(jiān)測著道路氣象環(huán)境,為交通的順暢運行和出行者的安全提供著至關(guān)重要的保障。 交通氣象站通常分布在
    的頭像 發(fā)表于 10-23 18:28 ?501次閱讀

    負(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 ?387次閱讀

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

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

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

    校園氣象站高校科普論文氣象站多功能氣象站種集氣象監(jiān)測、數(shù)據(jù)傳輸、數(shù)據(jù)處理、信息發(fā)布等功能于
    的頭像 發(fā)表于 08-06 17:53 ?621次閱讀
    校園<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 ?814次閱讀
    什么是自動<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 ?800次閱讀
    主站蜘蛛池模板: 午夜tv| 特黄特黄一级高清免费大片 | 天天摸天天看 | 人人做人人爽国产视 | 亚洲二区在线观看 | 成人五级毛片免费播放 | 国产成人啪精品午夜在线观看 | 亚洲二区在线观看 | 国产精品成人在线播放 | 四虎影院.com| 婷婷网五月天天综合天天爱 | 欧美系列在线 | 夜夜骑日日射 | 午夜三级在线 | 5566精品资源在线播放 | 色多多高清在线观看视频www | 奇米影色777四色在线首页 | 性精品| 日本吻胸抓胸激烈视频网站 | 欧美成人高清性色生活 | 韩国男女无遮挡高清性视频 | tom影院亚洲国产一区二区 | 视频一区中文字幕 | 亚洲精品久久久久久久蜜桃 | 国产精品久久精品福利网站 | 波多野结衣第一页 | 一个色在线 | 天天综合天天添夜夜添狠狠添 | 奇米影视亚洲四色8888 | 久久这里只有精品任你色 | 日本人69xxxxxxx69 | 亚洲午夜免费 | 天天草夜夜 | 国产一区二区三区乱码 | 在线视频免费观看 | 91大神大战高跟丝袜美女 | 国产精品资源手机在线播放 | 久久久久国产午夜 | 777欧美午夜精品影院 | 亚洲伦理中文字幕一区 | 老司机亚洲精品影院在线 |