-
-
Save wrongbranch/f9344a8a0181519997c1a09ab2167427 to your computer and use it in GitHub Desktop.
himawari_live_M5Stack_Core2.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
https://mag.switch-science.com/2021/07/14/m5stack-core2/ | |
Meteorological Satellite Center (MSC) | Himawari Real-Time Image | |
https://www.data.jma.go.jp/mscweb/data/himawari/sat_img.php?area=jpn | |
Arduino IDE Development (USB Driver) etc | |
https://docs.m5stack.com/en/arduino/arduino_development | |
*/ | |
#include <WiFi.h> | |
#include <HTTPClient.h> | |
#include <M5Core2.h> | |
const char *ssid = ""; //Enter SSID | |
const char *password = ""; //Enter Password | |
const char *ca = | |
// DigiCert Secure Site ECC CA-1 | |
"-----BEGIN CERTIFICATE-----\n" | |
"MIIDyTCCArGgAwIBAgIQC1v2W2un+9CLKQ2QRTfe4DANBgkqhkiG9w0BAQsFADBh\n" | |
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" | |
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" | |
"QTAeFw0xOTAyMTUxMjQ1MjRaFw0yOTAyMTUxMjQ1MjRaMGcxCzAJBgNVBAYTAlVT\n" | |
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" | |
"b20xJjAkBgNVBAMTHURpZ2lDZXJ0IFNlY3VyZSBTaXRlIEVDQyBDQS0xMFkwEwYH\n" | |
"KoZIzj0CAQYIKoZIzj0DAQcDQgAE5rt14WuvLvHn+zZK+KRSigbJRYTGRHg8A8Qk\n" | |
"iFTl8S6JM3rPXQ45S7mZwHGqWnvKEM4w4PEPg9x0e8kZGb3fGqOCAUAwggE8MB0G\n" | |
"A1UdDgQWBBTbNURdK+tTr54L9XE9o5lzrvtcUzAfBgNVHSMEGDAWgBQD3lA1VtFM\n" | |
"u2bwo+IbG8OXsj3RVTAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUH\n" | |
"AwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm\n" | |
"MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0fBDsw\n" | |
"OTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFs\n" | |
"Um9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0\n" | |
"cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzANBgkqhkiG9w0BAQsFAAOCAQEAq/3I\n" | |
"H6J/UvB/6Q9OECLSeul5xkW0PvhDzYJhcRJOZeWYWU575/+8ZnDJ/JhYx4wbEZ+P\n" | |
"VCDLiVhiKU3//F8W7ZdsVLqhdVVrOoZJv+JZar3RZ1rgwhavgHB6Sq142nTSzG5J\n" | |
"3O7+i2NZj4MJVM5uKPDUx659T2m2CsjzzXhFRnacQrN1QFh7+EUKXmxB1oFMcC8k\n" | |
"4BSi4ZYvsAAvb8Xh0g4fHEq8fbAwffNSfEvY3JEbAjeRVA31J1ifBMwliRzPHGLf\n" | |
"eyiYwvPQIUJ9ODieH5vDzLq9XvtdmFzBPXlFnHKI9LphN6sUVXdf4B+dao9drFZE\n" | |
"ifuXbKlQ/2TRZPFeBg==\n" | |
"-----END CERTIFICATE-----\n"; | |
#define DBG_PRINT(s) \ | |
do \ | |
{ \ | |
Serial.print(s); \ | |
M5.Lcd.print(s); \ | |
} while (0); | |
#define DBG_PRINTLN(s) \ | |
do \ | |
{ \ | |
Serial.println(s); \ | |
M5.Lcd.println(s); \ | |
} while (0); | |
#define DBG_PRINTF(s, ...) \ | |
do \ | |
{ \ | |
Serial.printf(s, __VA_ARGS__); \ | |
M5.Lcd.printf(s, __VA_ARGS__); \ | |
} while (0); | |
HTTPClient http; | |
uint8_t http_buff[4096]; | |
TFT_eSprite sprite(&M5.Lcd); | |
void setup() | |
{ | |
M5.begin(); | |
DBG_PRINTLN("Himawari Real-Time Image Viewer for M5Stack Core2"); | |
// Connect to wifi | |
WiFi.begin(ssid, password); | |
DBG_PRINT("WiFi connecting"); | |
// Wait some time to connect to wifi | |
for (int i = 0; i < 30 && WiFi.status() != WL_CONNECTED; i++) | |
{ | |
DBG_PRINT("."); | |
delay(1000); | |
} | |
configTzTime("JST-9", "ntp.nict.jp"); | |
struct tm timeinfo; | |
getLocalTime(&timeinfo, 60000); | |
M5.Lcd.clear(); | |
sprite.createSprite(1024, 1024); | |
http.setReuse(true); | |
} | |
const char *satelliteImageURL(const char *area, const char *band, const char *time) | |
{ | |
static char url[256]; | |
snprintf(url, sizeof(url), "https://www.data.jma.go.jp/mscweb/data/himawari/img/%s/%s_%s_%s.jpg", area, area, band, time); | |
return url; | |
} | |
const char *satelliteImageFilename(const char *area, const char *band, const char *time) | |
{ | |
static char filename[128]; | |
snprintf(filename, sizeof(filename), "/%s_%s_%s.jpg", area, band, time); | |
return filename; | |
} | |
typedef struct | |
{ | |
const char *area; | |
const char *band; | |
const char *describe; | |
uint16_t w, h; | |
} AreaBand; | |
const AreaBand area_and_bands[] = { | |
{"jpn", "trm", "True Color Reproduction (JMA, NOAA/NESDIS, CSU/CIRA)", 801, 641}, | |
{"jpn", "b13", "Band 13 (Infrared)", 801, 641}, | |
{"jpn", "snd", "Sandwich", 801, 641}, | |
}; | |
void loop() | |
{ | |
static unsigned long updateAt = 0; | |
static const AreaBand *areaband = &area_and_bands[0]; | |
static int ofs_x = areaband->w / 2 - M5.Lcd.width() / 2; | |
static int ofs_y = areaband->h / 2 - M5.Lcd.height() / 2; | |
static char image_path[128]; | |
static char image_datetime[32]; | |
bool redraw = false; | |
bool moving = false; | |
M5.update(); | |
M5.Lcd.setTextSize(1); | |
M5.Lcd.setCursor(0, 0); | |
Event &e = M5.Buttons.event; | |
if (e & E_MOVE) | |
{ | |
ofs_x = min(max(ofs_x + (e.from.x - e.to.x), 0), areaband->w - M5.Lcd.width()); | |
ofs_y = min(max(ofs_y + (e.from.y - e.to.y), 0), areaband->h - M5.Lcd.height()); | |
redraw = true; | |
moving = true; | |
} | |
if (e & E_RELEASE) | |
{ | |
redraw = true; | |
moving = false; | |
} | |
if (M5.BtnA.wasReleased()) | |
{ | |
areaband = &area_and_bands[0]; | |
updateAt = 0; | |
} | |
if (M5.BtnB.wasReleased()) | |
{ | |
areaband = &area_and_bands[1]; | |
updateAt = 0; | |
} | |
if (M5.BtnC.wasReleased()) | |
{ | |
areaband = &area_and_bands[2]; | |
updateAt = 0; | |
} | |
M5.update(); | |
unsigned long t = millis(); | |
if (t > updateAt) | |
{ | |
updateAt = t + 60000; | |
M5.Lcd.setCursor(0, 0); | |
M5.Lcd.println("Loading.. "); | |
time_t now; | |
time(&now); | |
time_t adjnow = int((now - 1200) / 600) * 600; | |
struct tm timeinfo; | |
gmtime_r(&adjnow, &timeinfo); | |
char time_short[24]; | |
char time_full[24]; | |
strftime(time_short, sizeof(time_short), "%H%M", &timeinfo); | |
strftime(time_full, sizeof(time_full), "%Y%m%d%H%M00", &timeinfo); | |
const char *filename = satelliteImageFilename(areaband->area, areaband->band, time_full); | |
const char *url = satelliteImageURL(areaband->area, areaband->band, time_short); | |
bool sate_exists = downloadIfNeeded(url, ca, filename); | |
if (sate_exists) | |
{ | |
if (strcmp(image_path, filename) != 0) | |
{ | |
strlcpy(image_path, filename, sizeof(image_path)); | |
sprite.drawJpgFile(SD, image_path, 0, 0, sprite.width(), sprite.height()); | |
} | |
localtime_r(&adjnow, &timeinfo); | |
strftime(image_datetime, sizeof(image_datetime), "%Y/%m/%d %H:%M%Z", &timeinfo); | |
redraw = true; | |
} | |
} | |
if (redraw) | |
{ | |
bblt(&M5.Lcd, &sprite, ofs_x, ofs_y); | |
if (!moving) | |
{ | |
M5.Lcd.setCursor(0, 0); | |
M5.Lcd.println(image_datetime); | |
M5.Lcd.setCursor(M5.Lcd.width() - 6 * strlen(areaband->describe), M5.Lcd.height() - 8); | |
M5.Lcd.println(areaband->describe); | |
} | |
} | |
int wait = -millis() % 33; | |
delay(wait ? wait : 33); | |
} | |
void bblt(TFT_eSPI *dst, TFT_eSprite *src, int16_t ofs_x, int16_t ofs_y) | |
{ | |
uint16_t iw = src->width(); | |
uint16_t *fb = (uint16_t *)src->frameBuffer(16) + iw * ofs_y + ofs_x; | |
bool oldSwapBytes = dst->getSwapBytes(); | |
dst->setSwapBytes(false); | |
for (int i = 0; i < dst->height(); i++) | |
{ | |
dst->pushImage(0, i, dst->width(), 1, fb); | |
fb += iw; | |
} | |
dst->setSwapBytes(oldSwapBytes); | |
} | |
bool downloadIfNeeded(const char *url, const char *ca, const char *save_to) | |
{ | |
bool exists = false; | |
File f = SD.open(save_to, FILE_READ); | |
if (f) | |
{ | |
size_t sz = f.size(); | |
f.close(); | |
if (sz < 1024) | |
{ | |
SD.remove(save_to); | |
} | |
else | |
{ | |
exists = true; | |
} | |
} | |
if (!exists) | |
{ | |
if (!httpget(url, ca, save_to)) | |
{ | |
SD.remove(save_to); | |
} | |
else | |
{ | |
exists = true; | |
} | |
} | |
return exists; | |
} | |
bool httpget(const char *url, const char *ca, const char *save_to) | |
{ | |
bool result = true; | |
DBG_PRINT("[HTTP] begin...\n"); | |
http.begin(url, ca); | |
DBG_PRINTF("[HTTP] GET %s\n", url); | |
int httpCode = http.GET(); | |
if (httpCode == HTTP_CODE_OK) | |
{ | |
DBG_PRINTF("[HTTP] GET... code: %d\n", httpCode); | |
WiFiClient *stream = http.getStreamPtr(); | |
int data_size = http.getSize(); | |
int read_size = 0; | |
File f = SD.open(save_to, FILE_WRITE); | |
if (f) | |
{ | |
int x = M5.Lcd.getCursorX(); | |
int y = M5.Lcd.getCursorY(); | |
while (http.connected() && read_size < data_size) | |
{ | |
int sz = stream->available(); | |
if (sz > 0) | |
{ | |
int rd = stream->readBytes(http_buff, min(sz, sizeof(http_buff))); | |
read_size += rd; | |
M5.Lcd.setCursor(x, y); | |
DBG_PRINTF("[HTTP] read: %d/%d\n", read_size, data_size); | |
f.write(http_buff, rd); | |
} | |
else | |
{ | |
delay(50); | |
} | |
} | |
f.close(); | |
} | |
else | |
{ | |
DBG_PRINTLN("ERROR: OPEN FILE."); | |
result = false; | |
} | |
} | |
else | |
{ | |
DBG_PRINTF("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); | |
result = false; | |
} | |
http.end(); | |
return result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment