Browse Source

Merge branch 'dAjaY85-development03' into development03

pull/735/head^2
lumapu 2 years ago
parent
commit
501daf04bc
  1. 11
      src/app.cpp
  2. 30
      src/app.h
  3. 83
      src/config/settings.h
  4. 1
      src/platformio.ini
  5. 125
      src/plugins/Display/Display.h
  6. 144
      src/plugins/Display/Display_Mono.cpp
  7. 41
      src/plugins/Display/Display_Mono.h
  8. 192
      src/plugins/Display/Display_ePaper.cpp
  9. 51
      src/plugins/Display/Display_ePaper.h
  10. 642
      src/plugins/Display/imagedata.cpp
  11. 5
      src/plugins/Display/imagedata.h
  12. 217
      src/plugins/MonochromeDisplay/MonochromeDisplay.h
  13. 117
      src/web/RestApi.h
  14. 107
      src/web/web.h

11
src/app.cpp

@ -4,13 +4,14 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#include "app.h" #include "app.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "utils/sun.h" #include "utils/sun.h"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
app::app() : ah::Scheduler() {} app::app() : ah::Scheduler() {}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::setup() { void app::setup() {
Serial.begin(115200); Serial.begin(115200);
@ -78,13 +79,12 @@ void app::setup() {
// Plugins // Plugins
if (mConfig->plugin.display.type != 0) if (mConfig->plugin.display.type != 0)
mMonoDisplay.setup(&mConfig->plugin.display, &mSys, &mTimestamp, 0xff, mVersion); mDisplay.setup(&mConfig->plugin.display, &mSys, &mTimestamp, 0xff, mVersion);
mPubSerial.setup(mConfig, &mSys, &mTimestamp); mPubSerial.setup(mConfig, &mSys, &mTimestamp);
regularTickers(); regularTickers();
// DBGPRINTLN("--- end setup"); // DBGPRINTLN("--- end setup");
// DBGPRINTLN(String(ESP.getFreeHeap())); // DBGPRINTLN(String(ESP.getFreeHeap()));
// DBGPRINTLN(String(ESP.getHeapFragmentation())); // DBGPRINTLN(String(ESP.getHeapFragmentation()));
@ -155,8 +155,7 @@ void app::onWifi(bool gotIp) {
mMqttEnabled = false; mMqttEnabled = false;
everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL"); everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL");
} }
} } else {
else {
mInnerLoopCb = std::bind(&app::loopWifi, this); mInnerLoopCb = std::bind(&app::loopWifi, this);
everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL"); everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL");
} }
@ -168,7 +167,7 @@ void app::regularTickers(void) {
everySec(std::bind(&WebType::tickSecond, &mWeb), "webSc"); everySec(std::bind(&WebType::tickSecond, &mWeb), "webSc");
// Plugins // Plugins
if (mConfig->plugin.display.type != 0) if (mConfig->plugin.display.type != 0)
everySec(std::bind(&MonoDisplayType::tickerSecond, &mMonoDisplay), "disp"); everySec(std::bind(&DisplayType::tickerSecond, &mDisplay), "disp");
every(std::bind(&PubSerialType::tick, &mPubSerial), mConfig->serial.interval, "uart"); every(std::bind(&PubSerialType::tick, &mPubSerial), mConfig->serial.interval, "uart");
} }

30
src/app.h

@ -6,30 +6,25 @@
#ifndef __APP_H__ #ifndef __APP_H__
#define __APP_H__ #define __APP_H__
#include "utils/dbg.h"
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h>
#include <RF24.h> #include <RF24.h>
#include <RF24_config.h> #include <RF24_config.h>
#include <ArduinoJson.h>
#include "appInterface.h" #include "appInterface.h"
#include "config/settings.h" #include "config/settings.h"
#include "defines.h" #include "defines.h"
#include "utils/crc.h"
#include "utils/scheduler.h"
#include "hm/hmSystem.h"
#include "hm/hmPayload.h" #include "hm/hmPayload.h"
#include "hm/hmSystem.h"
#include "hm/miPayload.h" #include "hm/miPayload.h"
#include "wifi/ahoywifi.h"
#include "web/web.h"
#include "web/RestApi.h"
#include "publisher/pubMqtt.h" #include "publisher/pubMqtt.h"
#include "publisher/pubSerial.h" #include "publisher/pubSerial.h"
#include "utils/crc.h"
#include "utils/dbg.h"
#include "utils/scheduler.h"
#include "web/RestApi.h"
#include "web/web.h"
#include "wifi/ahoywifi.h"
// convert degrees and radians for sun calculation // convert degrees and radians for sun calculation
#define SIN(x) (sin(radians(x))) #define SIN(x) (sin(radians(x)))
@ -46,9 +41,8 @@ typedef PubMqtt<HmSystemType> PubMqttType;
typedef PubSerial<HmSystemType> PubSerialType; typedef PubSerial<HmSystemType> PubSerialType;
// PLUGINS // PLUGINS
#include "plugins/MonochromeDisplay/MonochromeDisplay.h" #include "plugins/Display/Display.h"
typedef MonochromeDisplay<HmSystemType> MonoDisplayType; typedef Display<HmSystemType> DisplayType;
class app : public IApp, public ah::Scheduler { class app : public IApp, public ah::Scheduler {
public: public:
@ -203,7 +197,7 @@ class app : public IApp, public ah::Scheduler {
mMqtt.payloadEventListener(cmd); mMqtt.payloadEventListener(cmd);
#endif #endif
if (mConfig->plugin.display.type != 0) if (mConfig->plugin.display.type != 0)
mMonoDisplay.payloadEventListener(cmd); mDisplay.payloadEventListener(cmd);
} }
void mqttSubRxCb(JsonObject obj); void mqttSubRxCb(JsonObject obj);
@ -274,7 +268,7 @@ class app : public IApp, public ah::Scheduler {
uint32_t mSunrise, mSunset; uint32_t mSunrise, mSunset;
// plugins // plugins
MonoDisplayType mMonoDisplay; DisplayType mDisplay;
}; };
#endif /*__APP_H__*/ #endif /*__APP_H__*/

83
src/config/settings.h

@ -7,11 +7,12 @@
#define __SETTINGS_H__ #define __SETTINGS_H__
#include <Arduino.h> #include <Arduino.h>
#include <LittleFS.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <LittleFS.h>
#include "../defines.h"
#include "../utils/dbg.h" #include "../utils/dbg.h"
#include "../utils/helper.h" #include "../utils/helper.h"
#include "../defines.h"
/** /**
* More info: * More info:
@ -19,7 +20,6 @@
* */ * */
#define DEF_PIN_OFF 255 #define DEF_PIN_OFF 255
#define PROT_MASK_INDEX 0x0001 #define PROT_MASK_INDEX 0x0001
#define PROT_MASK_LIVE 0x0002 #define PROT_MASK_LIVE 0x0002
#define PROT_MASK_SERIAL 0x0004 #define PROT_MASK_SERIAL 0x0004
@ -38,7 +38,6 @@
#define DEF_PROT_API 0x0000 #define DEF_PROT_API 0x0000
#define DEF_PROT_MQTT 0x0000 #define DEF_PROT_MQTT 0x0000
typedef struct { typedef struct {
uint8_t ip[4]; // ip address uint8_t ip[4]; // ip address
uint8_t mask[4]; // sub mask uint8_t mask[4]; // sub mask
@ -124,12 +123,17 @@ typedef struct {
bool pwrSaveAtIvOffline; bool pwrSaveAtIvOffline;
bool logoEn; bool logoEn;
bool pxShift; bool pxShift;
bool rot180; uint8_t rot;
uint16_t period;
uint16_t wakeUp; uint16_t wakeUp;
uint16_t sleepAt; uint16_t sleepAt;
uint8_t contrast; uint8_t contrast;
uint8_t pin0; uint8_t disp_data;
uint8_t pin1; uint8_t disp_clk;
uint8_t disp_cs;
uint8_t disp_reset;
uint8_t disp_busy;
uint8_t disp_dc;
} display_t; } display_t;
typedef struct { typedef struct {
@ -177,8 +181,7 @@ class settings {
DPRINTLN(DBG_INFO, F(".. failed")); DPRINTLN(DBG_INFO, F(".. failed"));
} }
} } else
else
DPRINTLN(DBG_INFO, F(" .. done")); DPRINTLN(DBG_INFO, F(" .. done"));
readSettings("/settings.json"); readSettings("/settings.json");
@ -233,8 +236,7 @@ class settings {
jsonLed(root[F("led")]); jsonLed(root[F("led")]);
jsonPlugin(root[F("plugin")]); jsonPlugin(root[F("plugin")]);
jsonInst(root[F("inst")]); jsonInst(root[F("inst")]);
} } else {
else {
Serial.println(F("failed to parse json, using default config")); Serial.println(F("failed to parse json, using default config"));
} }
@ -291,8 +293,7 @@ class settings {
} }
// erase all settings and reset to default // erase all settings and reset to default
memset(&mCfg, 0, sizeof(settings_t)); memset(&mCfg, 0, sizeof(settings_t));
mCfg.sys.protectionMask = DEF_PROT_INDEX | DEF_PROT_LIVE | DEF_PROT_SERIAL | DEF_PROT_SETUP mCfg.sys.protectionMask = DEF_PROT_INDEX | DEF_PROT_LIVE | DEF_PROT_SERIAL | DEF_PROT_SETUP | DEF_PROT_UPDATE | DEF_PROT_SYSTEM | DEF_PROT_API | DEF_PROT_MQTT;
| DEF_PROT_UPDATE | DEF_PROT_SYSTEM | DEF_PROT_API | DEF_PROT_MQTT;
mCfg.sys.darkMode = false; mCfg.sys.darkMode = false;
// restore temp settings // restore temp settings
if (keepWifi) if (keepWifi)
@ -343,9 +344,14 @@ class settings {
mCfg.plugin.display.contrast = 60; mCfg.plugin.display.contrast = 60;
mCfg.plugin.display.logoEn = true; mCfg.plugin.display.logoEn = true;
mCfg.plugin.display.pxShift = true; mCfg.plugin.display.pxShift = true;
mCfg.plugin.display.rot180 = false; mCfg.plugin.display.rot = 0;
mCfg.plugin.display.pin0 = DEF_PIN_OFF; // SCL mCfg.plugin.display.period = 10000;
mCfg.plugin.display.pin1 = DEF_PIN_OFF; // SDA mCfg.plugin.display.disp_data = DEF_PIN_OFF; // SDA
mCfg.plugin.display.disp_clk = DEF_PIN_OFF; // SCL
mCfg.plugin.display.disp_cs = DEF_PIN_OFF;
mCfg.plugin.display.disp_reset = DEF_PIN_OFF;
mCfg.plugin.display.disp_busy = DEF_PIN_OFF;
mCfg.plugin.display.disp_dc = DEF_PIN_OFF;
} }
void jsonWifi(JsonObject obj, bool set = false) { void jsonWifi(JsonObject obj, bool set = false) {
@ -357,11 +363,16 @@ class settings {
obj[F("adm")] = mCfg.sys.adminPwd; obj[F("adm")] = mCfg.sys.adminPwd;
obj[F("prot_mask")] = mCfg.sys.protectionMask; obj[F("prot_mask")] = mCfg.sys.protectionMask;
obj[F("dark")] = mCfg.sys.darkMode; obj[F("dark")] = mCfg.sys.darkMode;
ah::ip2Char(mCfg.sys.ip.ip, buf); obj[F("ip")] = String(buf); ah::ip2Char(mCfg.sys.ip.ip, buf);
ah::ip2Char(mCfg.sys.ip.mask, buf); obj[F("mask")] = String(buf); obj[F("ip")] = String(buf);
ah::ip2Char(mCfg.sys.ip.dns1, buf); obj[F("dns1")] = String(buf); ah::ip2Char(mCfg.sys.ip.mask, buf);
ah::ip2Char(mCfg.sys.ip.dns2, buf); obj[F("dns2")] = String(buf); obj[F("mask")] = String(buf);
ah::ip2Char(mCfg.sys.ip.gateway, buf); obj[F("gtwy")] = String(buf); ah::ip2Char(mCfg.sys.ip.dns1, buf);
obj[F("dns1")] = String(buf);
ah::ip2Char(mCfg.sys.ip.dns2, buf);
obj[F("dns2")] = String(buf);
ah::ip2Char(mCfg.sys.ip.gateway, buf);
obj[F("gtwy")] = String(buf);
} else { } else {
snprintf(mCfg.sys.stationSsid, SSID_LEN, "%s", obj[F("ssid")].as<const char *>()); snprintf(mCfg.sys.stationSsid, SSID_LEN, "%s", obj[F("ssid")].as<const char *>());
snprintf(mCfg.sys.stationPwd, PWD_LEN, "%s", obj[F("pwd")].as<const char *>()); snprintf(mCfg.sys.stationPwd, PWD_LEN, "%s", obj[F("pwd")].as<const char *>());
@ -376,8 +387,7 @@ class settings {
ah::ip2Arr(mCfg.sys.ip.gateway, obj[F("gtwy")].as<const char *>()); ah::ip2Arr(mCfg.sys.ip.gateway, obj[F("gtwy")].as<const char *>());
if (mCfg.sys.protectionMask == 0) if (mCfg.sys.protectionMask == 0)
mCfg.sys.protectionMask = DEF_PROT_INDEX | DEF_PROT_LIVE | DEF_PROT_SERIAL | DEF_PROT_SETUP mCfg.sys.protectionMask = DEF_PROT_INDEX | DEF_PROT_LIVE | DEF_PROT_SERIAL | DEF_PROT_SETUP | DEF_PROT_UPDATE | DEF_PROT_SYSTEM | DEF_PROT_API | DEF_PROT_MQTT;
| DEF_PROT_UPDATE | DEF_PROT_SYSTEM | DEF_PROT_API | DEF_PROT_MQTT;
} }
} }
@ -469,26 +479,34 @@ class settings {
JsonObject disp = obj.createNestedObject("disp"); JsonObject disp = obj.createNestedObject("disp");
disp[F("type")] = mCfg.plugin.display.type; disp[F("type")] = mCfg.plugin.display.type;
disp[F("pwrSafe")] = (bool)mCfg.plugin.display.pwrSaveAtIvOffline; disp[F("pwrSafe")] = (bool)mCfg.plugin.display.pwrSaveAtIvOffline;
disp[F("logo")] = (bool)mCfg.plugin.display.logoEn; disp[F("period")] = mCfg.plugin.display.period;
disp[F("pxShift")] = (bool)mCfg.plugin.display.pxShift; disp[F("pxShift")] = (bool)mCfg.plugin.display.pxShift;
disp[F("rot180")] = (bool)mCfg.plugin.display.rot180; disp[F("rotation")] = mCfg.plugin.display.rot;
disp[F("wake")] = mCfg.plugin.display.wakeUp; disp[F("wake")] = mCfg.plugin.display.wakeUp;
disp[F("sleep")] = mCfg.plugin.display.sleepAt; disp[F("sleep")] = mCfg.plugin.display.sleepAt;
disp[F("contrast")] = mCfg.plugin.display.contrast; disp[F("contrast")] = mCfg.plugin.display.contrast;
disp[F("pin0")] = mCfg.plugin.display.pin0; disp[F("data")] = mCfg.plugin.display.disp_data;
disp[F("pin1")] = mCfg.plugin.display.pin1; disp[F("clock")] = mCfg.plugin.display.disp_clk;
disp[F("cs")] = mCfg.plugin.display.disp_cs;
disp[F("reset")] = mCfg.plugin.display.disp_reset;
disp[F("busy")] = mCfg.plugin.display.disp_busy;
disp[F("dc")] = mCfg.plugin.display.disp_dc;
} else { } else {
JsonObject disp = obj["disp"]; JsonObject disp = obj["disp"];
mCfg.plugin.display.type = disp[F("type")]; mCfg.plugin.display.type = disp[F("type")];
mCfg.plugin.display.pwrSaveAtIvOffline = (bool)disp[F("pwrSafe")]; mCfg.plugin.display.pwrSaveAtIvOffline = (bool)disp[F("pwrSafe")];
mCfg.plugin.display.logoEn = (bool) disp[F("logo")]; mCfg.plugin.display.period = disp[F("period")];
mCfg.plugin.display.pxShift = (bool)disp[F("pxShift")]; mCfg.plugin.display.pxShift = (bool)disp[F("pxShift")];
mCfg.plugin.display.rot180 = (bool) disp[F("rot180")]; mCfg.plugin.display.rot = disp[F("rotation")];
mCfg.plugin.display.wakeUp = disp[F("wake")]; mCfg.plugin.display.wakeUp = disp[F("wake")];
mCfg.plugin.display.sleepAt = disp[F("sleep")]; mCfg.plugin.display.sleepAt = disp[F("sleep")];
mCfg.plugin.display.contrast = disp[F("contrast")]; mCfg.plugin.display.contrast = disp[F("contrast")];
mCfg.plugin.display.pin0 = disp[F("pin0")]; mCfg.plugin.display.disp_data = disp[F("data")];
mCfg.plugin.display.pin1 = disp[F("pin1")]; mCfg.plugin.display.disp_clk = disp[F("clock")];
mCfg.plugin.display.disp_cs = disp[F("cs")];
mCfg.plugin.display.disp_reset = disp[F("reset")];
mCfg.plugin.display.disp_busy = disp[F("busy")];
mCfg.plugin.display.disp_dc = disp[F("dc")];
} }
} }
@ -498,8 +516,7 @@ class settings {
obj[F("rstMidNight")] = (bool)mCfg.inst.rstYieldMidNight; obj[F("rstMidNight")] = (bool)mCfg.inst.rstYieldMidNight;
obj[F("rstNotAvail")] = (bool)mCfg.inst.rstValsNotAvail; obj[F("rstNotAvail")] = (bool)mCfg.inst.rstValsNotAvail;
obj[F("rstComStop")] = (bool)mCfg.inst.rstValsCommStop; obj[F("rstComStop")] = (bool)mCfg.inst.rstValsCommStop;
} } else {
else {
mCfg.inst.enabled = (bool)obj[F("en")]; mCfg.inst.enabled = (bool)obj[F("en")];
mCfg.inst.rstYieldMidNight = (bool)obj["rstMidNight"]; mCfg.inst.rstYieldMidNight = (bool)obj["rstMidNight"];
mCfg.inst.rstValsNotAvail = (bool)obj["rstNotAvail"]; mCfg.inst.rstValsNotAvail = (bool)obj["rstNotAvail"];

1
src/platformio.ini

@ -41,6 +41,7 @@ lib_deps =
bblanchon/ArduinoJson bblanchon/ArduinoJson
https://github.com/JChristensen/Timezone https://github.com/JChristensen/Timezone
olikraus/U8g2 olikraus/U8g2
zinggjm/GxEPD2@^1.5.0
;esp8266/DNSServer ;esp8266/DNSServer
;esp8266/EEPROM ;esp8266/EEPROM
;esp8266/ESP8266WiFi ;esp8266/ESP8266WiFi

125
src/plugins/Display/Display.h

@ -0,0 +1,125 @@
#ifndef __DISPLAY__
#define __DISPLAY__
#include <Timezone.h>
#include <U8g2lib.h>
#include "../../hm/hmSystem.h"
#include "../../utils/helper.h"
#include "Display_Mono.h"
#include "Display_ePaper.h"
#include "imagedata.h"
template <class HMSYSTEM>
class Display {
public:
Display() {}
void setup(display_t *cfg, HMSYSTEM *sys, uint32_t *utcTs, uint8_t disp_reset, const char *version) {
mCfg = cfg;
mSys = sys;
mUtcTs = utcTs;
mNewPayload = false;
mLoopCnt = 0;
mVersion = version;
if (mCfg->type == 0) {
return;
} else if (1 < mCfg->type < 11) {
switch (mCfg->rot) {
case 0:
DisplayMono.disp_rotation = U8G2_R0;
break;
case 1:
DisplayMono.disp_rotation = U8G2_R1;
break;
case 2:
DisplayMono.disp_rotation = U8G2_R2;
break;
case 3:
DisplayMono.disp_rotation = U8G2_R3;
break;
}
DisplayMono.enablePowerSafe = mCfg->pwrSaveAtIvOffline;
DisplayMono.enableScreensaver = mCfg->pxShift;
DisplayMono.contrast = mCfg->contrast;
DisplayMono.init(mCfg->type, mCfg->disp_cs, mCfg->disp_dc, mCfg->disp_reset, mCfg->disp_busy, mCfg->disp_clk, mCfg->disp_data, mVersion);
} else if (mCfg->type > 10) {
DisplayEPaper.displayRotation = mCfg->rot;
counterEPaper = 0;
DisplayEPaper.init(mCfg->type, mCfg->disp_cs, mCfg->disp_dc, mCfg->disp_reset, mCfg->disp_busy, mCfg->disp_clk, mCfg->disp_data, mVersion);
}
}
void payloadEventListener(uint8_t cmd) {
mNewPayload = true;
}
void tickerSecond() {
if (mNewPayload || ((++mLoopCnt % 10) == 0)) {
mNewPayload = false;
mLoopCnt = 0;
DataScreen();
}
}
private:
void DataScreen() {
if (mCfg->type == 0)
return;
if (*mUtcTs == 0)
return;
if ((millis() - _lastDisplayUpdate) > mCfg->period) {
float totalPower = 0;
float totalYieldDay = 0;
float totalYieldTotal = 0;
uint8_t isprod = 0;
Inverter<> *iv;
record_t<> *rec;
for (uint8_t i = 0; i < mSys->getNumInverters(); i++) {
iv = mSys->getInverterByPos(i);
rec = iv->getRecordStruct(RealTimeRunData_Debug);
if (iv == NULL)
continue;
if (iv->isProducing(*mUtcTs))
uint8_t isprod = 0;
totalPower += iv->getChannelFieldValue(CH0, FLD_PAC, rec);
totalYieldDay += iv->getChannelFieldValue(CH0, FLD_YD, rec);
totalYieldTotal += iv->getChannelFieldValue(CH0, FLD_YT, rec);
}
if (1 < mCfg->type < 11) {
DisplayMono.loop(totalPower, totalYieldDay, totalYieldTotal, isprod);
} else if (mCfg->type > 10) {
DisplayEPaper.loop(totalPower, totalYieldDay, totalYieldTotal, isprod);
counterEPaper++;
}
_lastDisplayUpdate = millis();
}
if (counterEPaper > 480) {
DisplayEPaper.fullRefresh();
counterEPaper = 0;
}
}
// private member variables
bool mNewPayload;
uint8_t mLoopCnt;
uint32_t *mUtcTs;
const char *mVersion;
display_t *mCfg;
HMSYSTEM *mSys;
uint16_t counterEPaper;
uint32_t _lastDisplayUpdate = 0;
};
#endif /*__DISPLAY__*/

144
src/plugins/Display/Display_Mono.cpp

@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Display_Mono.h"
#include <time.h>
#include <map>
#include "WiFi.h"
#include "imagedata.h"
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
std::map<uint8_t, std::function<U8G2*(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)>> mono_types = {
{1, [](uint8_t reset, uint8_t clock, uint8_t data, uint8_t cs, uint8_t dc) { return new U8G2_PCD8544_84X48_F_4W_SW_SPI(U8G2_R2, clock, data, cs, dc, reset); }},
{2, [](uint8_t reset, uint8_t clock, uint8_t data, uint8_t cs, uint8_t dc) { return new U8G2_SSD1306_128X64_NONAME_F_HW_I2C(U8G2_R0, reset, clock, data); }},
{3, [](uint8_t reset, uint8_t clock, uint8_t data, uint8_t cs, uint8_t dc) { return new U8G2_SH1106_128X64_NONAME_F_HW_I2C(U8G2_R0, reset, clock, data); }},
};
DisplayMonoClass::DisplayMonoClass() {
}
DisplayMonoClass::~DisplayMonoClass() {
delete _display;
}
void DisplayMonoClass::calcLineHeights() {
uint8_t yOff = 0;
for (uint8_t i = 0; i < 4; i++) {
setFont(i);
yOff += (_display->getMaxCharHeight());
mLineOffsets[i] = yOff;
}
}
inline void DisplayMonoClass::setFont(uint8_t line) {
switch (line) {
case 0:
_display->setFont((_mIsLarge) ? u8g2_font_ncenB14_tr : u8g2_font_logisoso16_tr);
break;
case 3:
_display->setFont(u8g2_font_5x8_tr);
break;
default:
_display->setFont((_mIsLarge) ? u8g2_font_ncenB10_tr : u8g2_font_5x8_tr);
break;
}
}
void DisplayMonoClass::printText(const char* text, uint8_t line, uint8_t dispX = 5) {
if (!_mIsLarge) {
dispX = (line == 0) ? 5 : 0;
} else {
dispX = (line == 0) ? 20 : 5;
}
setFont(line);
dispX += enableScreensaver ? (_mExtra % 7) : 0;
_display->drawStr(dispX, mLineOffsets[line], text);
}
void DisplayMonoClass::init(uint8_t _type, uint8_t _CS, uint8_t _DC, uint8_t _RST, uint8_t _BUSY, uint8_t _SCK, uint8_t _MOSI, const char* version) {
if (0 < _type < 4) {
auto constructor = mono_types[_type];
_display = constructor(_RST, _SCK, _MOSI, _CS, _DC);
_display->begin();
_display->setDisplayRotation(disp_rotation);
_mIsLarge = (_display->getWidth() > 100);
calcLineHeights();
_display->clearBuffer();
if (contrast < 255) {
_display->setContrast(contrast);
}
printText("AHOY!", 0, 35);
printText("ahoydtu.de", 2, 20);
printText(version, 3, 46);
_display->sendBuffer();
}
}
void DisplayMonoClass::loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) {
_display->clearBuffer();
// set Contrast of the Display to raise the lifetime
if (contrast < 255) {
_display->setContrast(contrast);
}
//=====> Actual Production ==========
if ((totalPower > 0) && (isprod > 0)) {
_display->setPowerSave(false);
if (totalPower > 999) {
snprintf(_fmtText, sizeof(_fmtText), "%2.2f kW", (totalPower / 1000));
} else {
snprintf(_fmtText, sizeof(_fmtText), "%3.0f W", totalPower);
}
printText(_fmtText, 0);
_previousMillis = millis();
}
//<=======================
//=====> Offline ===========
else {
printText("offline", 0);
// check if it's time to enter power saving mode
if (millis() - _previousMillis >= (_mTimeout * 2)) {
_display->setPowerSave(enablePowerSafe);
}
}
//<=======================
//=====> Today & Total Production =======
snprintf(_fmtText, sizeof(_fmtText), "today: %4.0f Wh", totalYieldDay);
printText(_fmtText, 1);
snprintf(_fmtText, sizeof(_fmtText), "total: %.1f kWh", totalYieldTotal);
printText(_fmtText, 2);
//<=======================
//=====> IP or Date-Time ========
if (!(_mExtra % 10) && WiFi.localIP()) {
printText(WiFi.localIP().toString().c_str(), 3);
} else if (!(_mExtra % 5)) {
snprintf(_fmtText, sizeof(_fmtText), "#%d Inverter online", isprod);
printText(_fmtText, 3);
} else {
time_t now = time(nullptr);
strftime(_fmtText, sizeof(_fmtText), "%d.%m.%Y %H:%M", localtime(&now));
printText(_fmtText, 3);
}
_display->sendBuffer();
_dispY = 0;
_mExtra++;
}
DisplayMonoClass DisplayMono;

41
src/plugins/Display/Display_Mono.h

@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <U8g2lib.h>
#define DISP_DEFAULT_TIMEOUT 60000 // in milliseconds
class DisplayMonoClass {
public:
DisplayMonoClass();
~DisplayMonoClass();
void init(uint8_t type, uint8_t _CS, uint8_t _DC, uint8_t _RST, uint8_t _BUSY, uint8_t _SCK, uint8_t _MOSI, const char* version);
void loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod);
bool enablePowerSafe = true;
bool enableScreensaver = true;
const u8g2_cb_t* disp_rotation = U8G2_R2;
uint8_t contrast = 60;
private:
void calcLineHeights();
void setFont(uint8_t line);
void printText(const char* text, uint8_t line, uint8_t dispX);
U8G2* _display;
bool _mIsLarge = false;
uint8_t mLoopCnt;
uint32_t* mUtcTs;
uint8_t mLineOffsets[5];
uint16_t _dispY = 0;
uint32_t _previousMillis = 0;
uint8_t _mExtra;
uint16_t _mTimeout = DISP_DEFAULT_TIMEOUT; // interval at which to power save (milliseconds)
char _fmtText[32];
};
extern DisplayMonoClass DisplayMono;

192
src/plugins/Display/Display_ePaper.cpp

@ -0,0 +1,192 @@
#include "Display_ePaper.h"
#include "WiFi.h"
#include "imagedata.h"
static const uint32_t spiClk = 4000000; // 4 MHz
#if defined(ESP32) && defined(USE_HSPI_FOR_EPD)
SPIClass hspi(HSPI);
#endif
std::map<uint8_t, std::function<GxEPD2_GFX *(uint8_t, uint8_t, uint8_t, uint8_t)>> _ePaperTypes = {
// DEPG0150BN 200x200, SSD1681, TTGO T5 V2.4.1
{11, [](uint8_t _CS, uint8_t _DC, uint8_t _RST, uint8_t _BUSY) { return new GxEPD2_BW<GxEPD2_150_BN, GxEPD2_150_BN::HEIGHT>(GxEPD2_150_BN(_CS, _DC, _RST, _BUSY)); }},
// GDEW027C44 2.7 " b/w/r 176x264, IL91874
//{DisplayType_t::ePaper270, [](uint8_t _CS, uint8_t _DC, uint8_t _RST, uint8_t _BUSY)
// F { return new GxEPD2_3C<GxEPD2_270c, GxEPD2_270c::HEIGHT>(GxEPD2_270c(_CS, _DC, _RST, _BUSY)); }},
};
DisplayEPaperClass::DisplayEPaperClass() {
}
DisplayEPaperClass::~DisplayEPaperClass() {
delete _display;
}
//***************************************************************************
void DisplayEPaperClass::init(uint8_t type, uint8_t _CS, uint8_t _DC, uint8_t _RST, uint8_t _BUSY, uint8_t _SCK, uint8_t _MOSI, const char *version) {
if (type > 3) {
Serial.begin(115200);
auto constructor = _ePaperTypes[type];
_display = constructor(_CS, _DC, _RST, _BUSY);
hspi.begin(_SCK, _BUSY, _MOSI, _CS);
#if defined(ESP32) && defined(USE_HSPI_FOR_EPD)
_display->epd2.selectSPI(hspi, SPISettings(spiClk, MSBFIRST, SPI_MODE0));
#endif
_display->init(115200, true, 2, false);
_display->setRotation(displayRotation);
_display->setFullWindow();
// Logo
_display->fillScreen(GxEPD_BLACK);
_display->drawBitmap(0, 0, AhoyLogo, 200, 200, GxEPD_WHITE);
//_display->drawBitmap(0, 0, OpenDTULogo, 200, 200, GxEPD_WHITE);
while (_display->nextPage())
;
// clean the screen
delay(2000);
_display->fillScreen(GxEPD_WHITE);
while (_display->nextPage())
;
headlineIP();
// call the PowerPage to change the PV Power Values
actualPowerPaged(0, 0, 0, 0);
}
}
//***************************************************************************
void DisplayEPaperClass::fullRefresh() {
// screen complete black
_display->fillScreen(GxEPD_BLACK);
while (_display->nextPage())
;
delay(2000);
// screen complete white
_display->fillScreen(GxEPD_WHITE);
while (_display->nextPage())
;
}
//***************************************************************************
void DisplayEPaperClass::headlineIP() {
int16_t tbx, tby;
uint16_t tbw, tbh;
_display->setFont(&FreeSans9pt7b);
_display->setTextColor(GxEPD_WHITE);
_display->setPartialWindow(0, 0, _display->width(), headfootline);
_display->fillScreen(GxEPD_BLACK);
do {
if ((WiFi.isConnected() == true) && (WiFi.localIP() > 0)) {
snprintf(_fmtText, sizeof(_fmtText), "%s", WiFi.localIP().toString().c_str());
} else {
snprintf(_fmtText, sizeof(_fmtText), "WiFi not connected");
}
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
uint16_t x = ((_display->width() - tbw) / 2) - tbx;
_display->setCursor(x, (headfootline - 2));
_display->println(_fmtText);
} while (_display->nextPage());
}
//***************************************************************************
void DisplayEPaperClass::lastUpdatePaged() {
int16_t tbx, tby;
uint16_t tbw, tbh;
_display->setFont(&FreeSans9pt7b);
_display->setTextColor(GxEPD_WHITE);
_display->setPartialWindow(0, _display->height() - headfootline, _display->width(), headfootline);
_display->fillScreen(GxEPD_BLACK);
do {
time_t now = time(nullptr);
strftime(_fmtText, sizeof(_fmtText), "%d.%m.%Y %H:%M", localtime(&now));
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
uint16_t x = ((_display->width() - tbw) / 2) - tbx;
_display->setCursor(x, (_display->height() - 3));
_display->println(_fmtText);
} while (_display->nextPage());
}
//***************************************************************************
void DisplayEPaperClass::actualPowerPaged(float _totalPower, float _totalYieldDay, float _totalYieldTotal, uint8_t _isprod) {
int16_t tbx, tby;
uint16_t tbw, tbh, x, y;
_display->setFont(&FreeSans24pt7b);
_display->setTextColor(GxEPD_BLACK);
_display->setPartialWindow(0, headfootline, _display->width(), _display->height() - (headfootline * 2));
_display->fillScreen(GxEPD_WHITE);
do {
if (_totalPower > 9999) {
snprintf(_fmtText, sizeof(_fmtText), "%.1f kW", (_totalPower / 10000));
_changed = true;
} else if ((_totalPower > 0) && (_totalPower <= 9999)) {
snprintf(_fmtText, sizeof(_fmtText), "%.0f W", _totalPower);
_changed = true;
} else {
snprintf(_fmtText, sizeof(_fmtText), "offline");
}
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
x = ((_display->width() - tbw) / 2) - tbx;
_display->setCursor(x, headfootline + tbh + 10);
_display->print(_fmtText);
_display->setFont(&FreeSans12pt7b);
y = _display->height() / 2;
_display->setCursor(0, y);
_display->print("today:");
snprintf(_fmtText, _display->width(), "%.0f", _totalYieldDay);
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
x = ((_display->width() - tbw) / 2) - tbx;
_display->setCursor(x, y);
_display->print(_fmtText);
_display->setCursor(_display->width() - 33, y);
_display->println("Wh");
y = y + tbh + 7;
_display->setCursor(0, y);
_display->print("total:");
snprintf(_fmtText, _display->width(), "%.1f", _totalYieldTotal);
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
x = ((_display->width() - tbw) / 2) - tbx;
_display->setCursor(x, y);
_display->print(_fmtText);
_display->setCursor(_display->width() - 45, y);
_display->println("kWh");
_display->setCursor(0, _display->height() - (headfootline + 10));
snprintf(_fmtText, sizeof(_fmtText), "#%d Inverter online", _isprod);
_display->println(_fmtText);
} while (_display->nextPage());
}
//***************************************************************************
void DisplayEPaperClass::loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) {
// check if the IP has changed
if (_settedIP != WiFi.localIP().toString().c_str()) {
// save the new IP and call the Headline Funktion to adapt the Headline
_settedIP = WiFi.localIP().toString().c_str();
headlineIP();
}
// call the PowerPage to change the PV Power Values
actualPowerPaged(totalPower, totalYieldDay, totalYieldTotal, isprod);
// if there was an change and the Inverter is producing set a new Timestam in the footline
if ((isprod > 0) && (_changed)) {
_changed = false;
lastUpdatePaged();
}
_display->powerOff();
}
//***************************************************************************
DisplayEPaperClass DisplayEPaper;

51
src/plugins/Display/Display_ePaper.h

@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// uncomment next line to use HSPI for EPD (and VSPI for SD), e.g. with Waveshare ESP32 Driver Board
#define USE_HSPI_FOR_EPD
/// uncomment next line to use class GFX of library GFX_Root instead of Adafruit_GFX, to use less code and ram
// #include <GFX.h>
// base class GxEPD2_GFX can be used to pass references or pointers to the display instance as parameter, uses ~1.2k more code
// enable GxEPD2_GFX base class
#define ENABLE_GxEPD2_GFX 1
#include <GxEPD2_3C.h>
#include <GxEPD2_BW.h>
#include <SPI.h>
#include <map>
// FreeFonts from Adafruit_GFX
#include <Fonts/FreeSans12pt7b.h>
#include <Fonts/FreeSans18pt7b.h>
#include <Fonts/FreeSans24pt7b.h>
#include <Fonts/FreeSans9pt7b.h>
#include "imagedata.h"
// GDEW027C44 2.7 " b/w/r 176x264, IL91874
// GDEH0154D67 1.54" b/w 200x200
class DisplayEPaperClass {
public:
DisplayEPaperClass();
~DisplayEPaperClass();
void fullRefresh();
void init(uint8_t type, uint8_t _CS, uint8_t _DC, uint8_t _RST, uint8_t _BUSY, uint8_t _SCK, uint8_t _MOSI, const char* version);
void loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod);
uint8_t displayRotation = 2;
private:
void headlineIP();
void actualPowerPaged(float _totalPower, float _totalYieldDay, float _totalYieldTotal, uint8_t _isprod);
void lastUpdatePaged();
bool _changed = false;
char _fmtText[35];
const char* _settedIP;
uint8_t headfootline = 16;
GxEPD2_GFX* _display;
};
extern DisplayEPaperClass DisplayEPaper;

642
src/plugins/Display/imagedata.cpp

@ -0,0 +1,642 @@
// GxEPD2_ESP32_ESP8266_WifiData_V1_und_V2
#include "imagedata.h"
#if defined(__AVR__) || defined(ARDUINO_ARCH_SAMD)
#include <avr/pgmspace.h>
#elif defined(ESP8266) || defined(ESP32)
#include <pgmspace.h>
#endif
// 'OpenDTU', 200x200px
const unsigned char OpenDTULogo[] PROGMEM = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xf8, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x3f, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xf8, 0x03, 0x80, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xf0,
0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
0x7f, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xfe, 0x01, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x01, 0xff, 0xfe, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0x07,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff,
0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x07, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff,
0xfc, 0x7f, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xfc, 0x0f, 0xe0, 0x0f, 0xc0, 0xfe, 0x03, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81,
0xf8, 0x07, 0xc0, 0x0f, 0xc0, 0x3e, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xf0, 0x07, 0xc0, 0x0f, 0x80, 0x1e, 0x03,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x01, 0xe0, 0x07, 0xc0, 0x07, 0x80, 0x0f, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xe0, 0x07, 0xc0, 0x0f, 0x80,
0x0f, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x03, 0xe0, 0x07, 0xc0, 0x0f, 0xc0, 0x1f, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xe0, 0x0f, 0xe0,
0x0f, 0xc0, 0x1f, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfe, 0x03, 0xf0, 0x3f, 0xf8, 0x3f, 0xf0, 0x7f, 0x81, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x07, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
0x07, 0xf0, 0x7f, 0xfc, 0xff, 0xf0, 0x7f, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x07, 0x80, 0x0f, 0xe0, 0x1f, 0xc0, 0x07,
0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfc, 0x0f, 0x80, 0x07, 0xe0, 0x0f, 0xc0, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0f, 0x00, 0x0f, 0xc0, 0x0f,
0xc0, 0x03, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf8, 0x0f, 0x00, 0x0f, 0xc0, 0x07, 0xc0, 0x03, 0xe0, 0x7f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0x00, 0x0f,
0xc0, 0x07, 0xe0, 0x03, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0x80, 0x0f, 0xc0, 0x0f, 0xe0, 0x03, 0xe0, 0x7f, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f,
0x80, 0x1f, 0xe0, 0x0f, 0xf0, 0x07, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xe0, 0x3f, 0xf8, 0x3f, 0xfc, 0x1f, 0xf0,
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x03, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1f,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3f, 0xff, 0xff, 0xff, 0x80, 0x00,
0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x0f, 0xff, 0xf8, 0x00, 0x00, 0x7f, 0x8f, 0xff,
0xf3, 0xfe, 0x00, 0x0f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x01, 0xff, 0xf0, 0x00, 0x00, 0x3f, 0x07, 0xff, 0xe1, 0xfc, 0x00, 0x03, 0xff, 0xff, 0xff,
0xf0, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00, 0x1f,
0x03, 0xff, 0xc0, 0xf8, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x00, 0x1f, 0x03, 0xff, 0xc0, 0xf0, 0x00, 0x00, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1f, 0xf0, 0x00,
0x00, 0x1f, 0x03, 0xff, 0xc0, 0xe0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x7f, 0x03, 0xff, 0xc0, 0xc0, 0x3f,
0x80, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xf0, 0x0f,
0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0xc0, 0x7f, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xfc, 0x07, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0,
0xc0, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03,
0xfe, 0x03, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x81, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0x03, 0xff, 0xf0, 0x3f, 0xff, 0x03,
0xff, 0xc0, 0x81, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x03, 0xff, 0x03, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x81, 0xff, 0xf8, 0x1f, 0xf0,
0x00, 0x7f, 0xff, 0xf0, 0x03, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0x03, 0xff, 0x81, 0xff, 0xf0, 0x3f,
0xff, 0x03, 0xff, 0xc0, 0x83, 0xff, 0xf8, 0x1f, 0xe0, 0x00, 0x1f, 0xff, 0xc0, 0x00, 0xff, 0xe0,
0x00, 0x1f, 0xff, 0x03, 0xff, 0x81, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x03, 0xff, 0xf8,
0x1f, 0xe0, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x0f, 0xff, 0x03, 0xff, 0x81, 0xff,
0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x03, 0xff, 0xfc, 0x1f, 0xe0, 0x00, 0x07, 0xff, 0x00, 0x00,
0x3f, 0xe0, 0x00, 0x07, 0xff, 0x03, 0xff, 0x81, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x03,
0xff, 0xfc, 0x1f, 0xe0, 0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x07, 0xff, 0x03, 0xff,
0xc0, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x03, 0xff, 0xfc, 0x1f, 0xe0, 0x3c, 0x01, 0xfe,
0x01, 0xe0, 0x1f, 0xe0, 0x1c, 0x03, 0xff, 0x03, 0xff, 0xc0, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff,
0xc0, 0x03, 0xff, 0xfc, 0x1f, 0xe0, 0x7f, 0x01, 0xfc, 0x07, 0xf8, 0x1f, 0xe0, 0x7f, 0x03, 0xff,
0x03, 0xff, 0xc0, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x03, 0xff, 0xfc, 0x1f, 0xe0, 0x7f,
0x80, 0xfc, 0x0f, 0xf8, 0x1f, 0xe0, 0x7f, 0x83, 0xff, 0x03, 0xff, 0xc0, 0xff, 0xf0, 0x3f, 0xff,
0x03, 0xff, 0xc0, 0x03, 0xff, 0xfc, 0x1f, 0xe0, 0x7f, 0xc0, 0xfc, 0x0f, 0xe0, 0x1f, 0xe0, 0x7f,
0x83, 0xff, 0x03, 0xff, 0xc0, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x03, 0xff, 0xfc, 0x1f,
0xe0, 0x7f, 0xc0, 0xfc, 0x0c, 0x00, 0x1f, 0xe0, 0x7f, 0x83, 0xff, 0x03, 0xff, 0xc0, 0xff, 0xf0,
0x3f, 0xff, 0x03, 0xff, 0xc0, 0x03, 0xff, 0xfc, 0x1f, 0xe0, 0x7f, 0xc0, 0xfc, 0x00, 0x00, 0x1f,
0xe0, 0x7f, 0x83, 0xff, 0x03, 0xff, 0xc0, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x03, 0xff,
0xfc, 0x1f, 0xe0, 0x7f, 0xc0, 0xfc, 0x00, 0x00, 0x1f, 0xe0, 0x7f, 0x83, 0xff, 0x03, 0xff, 0xc1,
0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x03, 0xff, 0xfc, 0x1f, 0xe0, 0x7f, 0xc0, 0xfc, 0x00,
0x00, 0x3f, 0xe0, 0x7f, 0x83, 0xff, 0x03, 0xff, 0x81, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0,
0x03, 0xff, 0xf8, 0x1f, 0xe0, 0x7f, 0xc0, 0xfc, 0x00, 0x03, 0xff, 0xe0, 0x7f, 0x83, 0xff, 0x03,
0xff, 0x81, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x83, 0xff, 0xf8, 0x1f, 0xe0, 0x7f, 0xc0,
0xfc, 0x00, 0x7f, 0xff, 0xe0, 0x7f, 0x83, 0xff, 0x03, 0xff, 0x81, 0xff, 0xf0, 0x3f, 0xff, 0x03,
0xff, 0xc0, 0x81, 0xff, 0xf8, 0x1f, 0xe0, 0x7f, 0xc0, 0xfc, 0x07, 0xff, 0xff, 0xe0, 0x7f, 0x83,
0xff, 0x03, 0xff, 0x81, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0x81, 0xff, 0xf8, 0x3f, 0xe0,
0x7f, 0xc0, 0xfc, 0x0f, 0xff, 0xff, 0xe0, 0x7f, 0x83, 0xff, 0x03, 0xff, 0x03, 0xff, 0xf0, 0x3f,
0xff, 0x03, 0xff, 0xc0, 0x81, 0xff, 0xf0, 0x3f, 0xe0, 0x7f, 0xc0, 0xfc, 0x0f, 0xff, 0xff, 0xe0,
0x7f, 0x83, 0xff, 0x03, 0xff, 0x03, 0xff, 0xf0, 0x3f, 0xff, 0x03, 0xff, 0xc0, 0xc0, 0xff, 0xf0,
0x3f, 0xe0, 0x7f, 0xc0, 0xfc, 0x0f, 0xff, 0xff, 0xe0, 0x7f, 0x83, 0xff, 0x03, 0xfe, 0x07, 0xff,
0xf0, 0x3f, 0xff, 0x03, 0xff, 0x80, 0xc0, 0x7f, 0xe0, 0x3f, 0xe0, 0x7f, 0x81, 0xfc, 0x0f, 0xff,
0xff, 0xe0, 0x7f, 0x83, 0xff, 0x03, 0xfc, 0x07, 0xff, 0xf0, 0x3f, 0xff, 0x01, 0xff, 0x81, 0xc0,
0x3f, 0x80, 0x7f, 0xe0, 0x7e, 0x01, 0xfe, 0x07, 0xf0, 0x7f, 0xe0, 0x7f, 0x83, 0xff, 0x03, 0xe0,
0x0f, 0xff, 0xf0, 0x3f, 0xff, 0x80, 0xfe, 0x01, 0xe0, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x03, 0xfe,
0x00, 0x00, 0x3f, 0xe0, 0x7f, 0x83, 0xff, 0x00, 0x00, 0x0f, 0xff, 0xf0, 0x3f, 0xff, 0x80, 0x00,
0x03, 0xf0, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x03, 0xff, 0x00, 0x00, 0x3f, 0xe0, 0x7f, 0x83, 0xff,
0x00, 0x00, 0x1f, 0xff, 0xf0, 0x3f, 0xff, 0xc0, 0x00, 0x03, 0xf8, 0x00, 0x01, 0xff, 0xe0, 0x00,
0x07, 0xff, 0x00, 0x00, 0x7f, 0xe0, 0x7f, 0x83, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xf0, 0x3f, 0xff,
0xe0, 0x00, 0x07, 0xfc, 0x00, 0x03, 0xff, 0xe0, 0x00, 0x0f, 0xff, 0x80, 0x00, 0x7f, 0xe0, 0x7f,
0x83, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xf0, 0x3f, 0xff, 0xf0, 0x00, 0x0f, 0xff, 0x00, 0x0f, 0xff,
0xe0, 0x00, 0x3f, 0xff, 0xe0, 0x01, 0xff, 0xf0, 0xff, 0x83, 0xff, 0x00, 0x03, 0xff, 0xff, 0xf8,
0x7f, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x00, 0xff, 0xff, 0xf8, 0x07, 0xff,
0xf9, 0xff, 0xe7, 0xff, 0xc0, 0x1f, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
// 'Logo', 200x200px
const unsigned char AhoyLogo[] PROGMEM = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00,
0x0b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xfe, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x06,
0x0f, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x7e, 0x0f, 0xff, 0xff, 0xfc, 0x03, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
0x03, 0xfe, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x19, 0xfe, 0x07, 0xff, 0xff, 0xff, 0xfe,
0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xe0, 0x70, 0x7f, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe0, 0x3f, 0x07, 0xff, 0xff,
0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xfc, 0x0f, 0xe0, 0x3f, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xe0, 0x1f, 0x83,
0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, 0xe0, 0x1f, 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xe0,
0x0f, 0x83, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xe0, 0x0f, 0x83, 0xff, 0xff, 0xff, 0xff, 0xfe,
0x07, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
0xff, 0xc1, 0x07, 0x80, 0x07, 0xfe, 0xff, 0xff, 0xfc, 0x07, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xe1, 0x07, 0xc0, 0x01, 0xe0, 0x0f,
0xff, 0xfc, 0x0f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfc, 0xff, 0xe1, 0x83, 0xc0, 0x01, 0xc0, 0x07, 0xff, 0xf8, 0x0f, 0xfc, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xe1, 0x83, 0xc0, 0x00,
0xc0, 0x07, 0x8f, 0xf8, 0x1f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xfe, 0x7f, 0xe0, 0x01, 0xc0, 0x00, 0x81, 0x83, 0x07, 0xf0, 0x3f, 0xf9, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xe0, 0x01,
0xe0, 0xe0, 0x87, 0xe3, 0x0f, 0xf0, 0x3f, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xe0, 0x00, 0xe0, 0xe0, 0x87, 0xe1, 0x0c, 0x60, 0x7f,
0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f,
0xe0, 0x00, 0xe1, 0xf0, 0x87, 0xe1, 0x08, 0x60, 0x7f, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xe0, 0xe0, 0xe0, 0xe0, 0x87, 0xc2, 0x00,
0x40, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x8f, 0xc0, 0xe0, 0x60, 0xe0, 0xc0, 0x82, 0x00, 0xc0, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xc0, 0xe0, 0x60, 0xe0, 0xc0,
0x06, 0x01, 0x81, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xcf, 0xe0, 0xe0, 0x20, 0xe0, 0xe0, 0x0c, 0x03, 0x81, 0xff, 0x1f, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc0, 0xf0, 0x30,
0xe1, 0xf8, 0x18, 0x07, 0xe1, 0xfe, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xc0, 0xf0, 0x7f, 0xff, 0xff, 0xf0, 0x1f, 0xf3, 0xfe, 0x01,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xc0,
0xfb, 0xff, 0xff, 0xff, 0xe0, 0x3e, 0x1f, 0xfc, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xfc, 0x0f,
0xf8, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
0x33, 0xef, 0xff, 0xff, 0xff, 0xff, 0x81, 0xfc, 0x0f, 0xf1, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xf1, 0xff, 0xff, 0xa0, 0x00, 0x7f, 0xe3,
0xfc, 0x0f, 0xf3, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf1, 0xf9, 0xff, 0xf0, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x0f, 0xe7, 0xff, 0xe0, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xf9, 0xff, 0x80, 0x3f, 0xff,
0xe0, 0x0f, 0xfe, 0x1f, 0xc7, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xcf, 0xf8, 0xf0, 0x07, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0x8f, 0xff, 0xfc,
0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xfc, 0x70, 0x3f,
0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0x1f, 0xff, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xfc, 0x63, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0x3f,
0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfe,
0x23, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7e, 0x3f, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xfe, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
0x0c, 0x7f, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
0x7f, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xf1, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfc, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xf8,
0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0x87, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfc, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf8, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x00, 0x3f, 0xff, 0xf8, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xfc, 0x00, 0x00, 0x01, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x55, 0x00, 0x3f, 0xf8, 0x00,
0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xff,
0xff, 0xff, 0x01, 0xff, 0xff, 0xf8, 0x0f, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x9f, 0xff, 0xf8, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0x03,
0xfc, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xe3, 0xf1, 0xff,
0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xe0, 0xfe, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xe7, 0xf9, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff,
0xff, 0xf8, 0x7e, 0x06, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xcf,
0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0x03, 0x3f, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff,
0xff, 0xff, 0xff, 0xff, 0x1f, 0x23, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f,
0xff, 0x9f, 0xfe, 0x7f, 0xff, 0xf3, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf1, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0x9f, 0xfe, 0x7f, 0xff, 0xe3, 0xf8,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xfe, 0x7f, 0xff, 0x9f, 0xff, 0x0f, 0xff, 0x8f, 0xf1, 0xff, 0xff, 0xff, 0xfe, 0xf5, 0x90, 0x07,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x9f, 0xff, 0x03, 0xff,
0x1f, 0xe3, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xfe, 0x7f, 0xff, 0x3f, 0xfe, 0x31, 0xfe, 0x7f, 0xe7, 0xff, 0x80, 0x00, 0x40, 0x00,
0x07, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0x3f, 0x3c,
0xf9, 0xfc, 0xff, 0xe7, 0xfe, 0x3f, 0xc9, 0xff, 0xf1, 0x1f, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0x3f, 0x3c, 0xf9, 0xf9, 0xff, 0xc7, 0xfc, 0xff, 0x90,
0x7f, 0xf3, 0x03, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff,
0x3f, 0x39, 0xfd, 0xf3, 0xff, 0xcf, 0xfc, 0xff, 0x90, 0x3f, 0xf3, 0x83, 0xf8, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0x3f, 0x39, 0xf9, 0xc7, 0xff, 0xcf, 0xfc,
0xff, 0x32, 0x7f, 0xe4, 0x77, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
0xff, 0xff, 0x7f, 0x33, 0xf9, 0x8f, 0xff, 0xcf, 0xf9, 0xff, 0x00, 0x7f, 0xe0, 0x67, 0xfc, 0x7f,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0x7f, 0xb3, 0xf3, 0xbf, 0xff,
0xcf, 0xf9, 0xff, 0x00, 0xff, 0xfe, 0x47, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf9, 0xff, 0xff, 0x7f, 0xf7, 0xf3, 0xff, 0xff, 0xcf, 0xf9, 0xff, 0xe0, 0xff, 0xfc, 0x0f,
0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0x7f, 0xe7, 0xe7,
0xff, 0xff, 0xcf, 0xf9, 0xff, 0xe1, 0xff, 0xfc, 0x1f, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0x3f, 0xef, 0xe7, 0xef, 0xff, 0xc7, 0xf9, 0xff, 0xc3, 0xff,
0xfc, 0x3f, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0x3f,
0xef, 0xef, 0xc0, 0xff, 0xe7, 0xf9, 0xff, 0xc3, 0xff, 0xf8, 0x3f, 0xff, 0x3f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0x3f, 0xef, 0xcf, 0xf0, 0x01, 0xe7, 0xf1, 0xff,
0x87, 0xff, 0xf8, 0x7f, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff,
0xff, 0xbf, 0xcf, 0xe7, 0xff, 0xc1, 0xe3, 0xe1, 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0x9f, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0x9f, 0xef, 0xe7, 0xff, 0xff, 0xf3,
0xc1, 0xff, 0x96, 0xaf, 0xf9, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf9, 0xff, 0xff, 0x9f, 0xe7, 0xe3, 0xff, 0xff, 0xf1, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff,
0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xcf, 0xe7, 0xf3, 0xff,
0xff, 0xf8, 0xc0, 0x00, 0x4a, 0x90, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xf9, 0xff, 0xff, 0xef, 0xf3, 0xf3, 0x9f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xe7, 0xf1,
0xe7, 0xc7, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xf3, 0xf0, 0x07, 0xe3, 0xff, 0xff, 0x81, 0xff, 0xff,
0xff, 0xff, 0xfe, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff,
0xf8, 0x07, 0x1f, 0xf1, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xfc, 0x1f, 0x9f, 0xf8, 0xff, 0xff, 0xc3,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9,
0xff, 0xff, 0xf8, 0xff, 0x9f, 0xfe, 0x7f, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xf9, 0xff, 0x9f, 0xfe, 0x3f,
0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfd, 0xff, 0xff, 0xf1, 0xff, 0x9f, 0xff, 0x9f, 0xff, 0xf3, 0xff, 0x3f, 0x3f, 0xff, 0xff,
0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xe1, 0xff, 0xcf,
0xff, 0xc7, 0xff, 0xf3, 0xff, 0x3f, 0x9f, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xe1, 0xff, 0x8f, 0xff, 0xe7, 0xff, 0xf3, 0xff, 0x3f, 0x9f,
0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xc1,
0xff, 0xcf, 0xff, 0xf3, 0xff, 0xf3, 0xff, 0x3f, 0x9f, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0x81, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xf3, 0xff,
0x3f, 0x9f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff,
0xff, 0x91, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xf3, 0xff, 0x3f, 0x9f, 0xff, 0xff, 0xfe, 0x3f, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0x11, 0xff, 0x9f, 0xff, 0xff, 0xff,
0xf3, 0xff, 0x1f, 0x9f, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xfe, 0x7f, 0xff, 0x21, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xbf, 0x9f, 0xff, 0xff, 0xfe,
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xfe, 0x20, 0xff, 0x9f, 0xff,
0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xfe, 0x7f, 0xfe, 0x60, 0x7f, 0x9f, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x64, 0x3f,
0x1f, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0xe7, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff,
0xff, 0x3f, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xfc,
0xe7, 0x80, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xf1, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xf8, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3,
0xff, 0xff, 0xfe, 0x7f, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x9f, 0xf9, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xe7, 0xff, 0xfe, 0x7f, 0xff, 0xc3, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xf9, 0xe7, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf3, 0xf3, 0xff, 0xfc, 0x7f, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xcf, 0xf9, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf3, 0xff, 0xf8, 0xff, 0xff,
0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xf9, 0xe7, 0xff, 0xff,
0xff, 0xff, 0xff, 0xf3, 0xf9, 0xff, 0xe1, 0xff, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xe7, 0xf3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xfc, 0x3f, 0x07,
0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xf3, 0xe7,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xfe, 0x00, 0x1f, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff,
0xe0, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1,
0xf3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xe3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf7, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf8, 0x83, 0xe7, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xc7, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x13, 0xe7, 0xff, 0xfc, 0x03,
0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xfc, 0x31, 0xe7, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xfe,
0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x39, 0xe3, 0xff,
0xfc, 0x00, 0x1f, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x31, 0xf3, 0xff, 0xfc, 0x00, 0x1f, 0xff, 0xc7, 0xff, 0xff,
0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x19,
0xf3, 0xff, 0xfc, 0x00, 0x07, 0xff, 0x87, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xf3, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x07,
0xff, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x83, 0xf3, 0xff, 0xff, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xf3, 0xff, 0xff, 0xff, 0xff,
0xf8, 0x07, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xe3, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xfe, 0x1f, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xe1, 0xff, 0xfe,
0x01, 0xff, 0xfe, 0x07, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xe1, 0xff, 0xf0, 0x00, 0x3f, 0x80, 0x07, 0xff, 0xff, 0xf0,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x4c,
0xff, 0xf0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0c, 0xff, 0xf0, 0x00, 0x00, 0x0b, 0x87, 0xff,
0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x0e, 0x7f, 0xf8, 0x00, 0x3f, 0xff, 0xc7, 0xff, 0xfe, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x86, 0x7f, 0xfe, 0x00, 0xff, 0xff,
0xc3, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x80, 0x7f, 0xff, 0x87, 0xff, 0xff, 0xf3, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff,
0xff, 0xff, 0xf3, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xfe, 0x07, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03,
0xff, 0xff, 0xff, 0xff, 0xf3, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xf3, 0xc0, 0x7f,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xf8, 0x1f, 0xff, 0xff, 0xff, 0xe3, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0xe0,
0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x03, 0xff,
0xfe, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xf0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x17, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

5
src/plugins/Display/imagedata.h

@ -0,0 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
extern const unsigned char AhoyLogo[];
extern const unsigned char OpenDTULogo[];

217
src/plugins/MonochromeDisplay/MonochromeDisplay.h

@ -1,217 +0,0 @@
#ifndef __MONOCHROME_DISPLAY__
#define __MONOCHROME_DISPLAY__
#include <U8g2lib.h>
#include <Timezone.h>
#include "../../utils/helper.h"
#include "../../hm/hmSystem.h"
#define DISP_DEFAULT_TIMEOUT 60 // in seconds
static uint8_t bmp_logo[] PROGMEM = {
B00000000, B00000000, // ................
B11101100, B00110111, // ..##.######.##..
B11101100, B00110111, // ..##.######.##..
B11100000, B00000111, // .....######.....
B11010000, B00001011, // ....#.####.#....
B10011000, B00011001, // ...##..##..##...
B10000000, B00000001, // .......##.......
B00000000, B00000000, // ................
B01111000, B00011110, // ...####..####...
B11111100, B00111111, // ..############..
B01111100, B00111110, // ..#####..#####..
B00000000, B00000000, // ................
B11111100, B00111111, // ..############..
B11111110, B01111111, // .##############.
B01111110, B01111110, // .######..######.
B00000000, B00000000 // ................
};
static uint8_t bmp_arrow[] PROGMEM = {
B00000000, B00011100, B00011100, B00001110, B00001110, B11111110, B01111111,
B01110000, B01110000, B00110000, B00111000, B00011000, B01111111, B00111111,
B00011110, B00001110, B00000110, B00000000, B00000000, B00000000, B00000000
};
template<class HMSYSTEM>
class MonochromeDisplay {
public:
MonochromeDisplay() {}
void setup(display_t *cfg, HMSYSTEM *sys, uint32_t *utcTs, uint8_t disp_reset, const char *version) {
mCfg = cfg;
mSys = sys;
mUtcTs = utcTs;
mNewPayload = false;
mLoopCnt = 0;
mTimeout = DISP_DEFAULT_TIMEOUT; // power off timeout (after inverters go offline)
u8g2_cb_t *rot = (u8g2_cb_t *)((mCfg->rot180) ? U8G2_R2 : U8G2_R0);
if(mCfg->type) {
switch(mCfg->type) {
case 1:
mDisplay = new U8G2_PCD8544_84X48_F_4W_HW_SPI(rot, mCfg->pin0, mCfg->pin1, disp_reset);
break;
case 2:
mDisplay = new U8G2_SSD1306_128X64_NONAME_F_HW_I2C(rot, disp_reset, mCfg->pin0, mCfg->pin1);
break;
case 3:
mDisplay = new U8G2_SH1106_128X64_NONAME_F_HW_I2C(rot, disp_reset, mCfg->pin0, mCfg->pin1);
break;
}
mDisplay->begin();
mIsLarge = ((mDisplay->getWidth() > 120) && (mDisplay->getHeight() > 60));
calcLineHeights();
mDisplay->clearBuffer();
mDisplay->setContrast(mCfg->contrast);
printText("Ahoy!", 0, 35);
printText("ahoydtu.de", 2, 20);
printText(version, 3, 46);
mDisplay->sendBuffer();
}
}
void payloadEventListener(uint8_t cmd) {
mNewPayload = true;
}
void tickerSecond() {
if(mCfg->pwrSaveAtIvOffline) {
if(mTimeout != 0)
mTimeout--;
}
if(mNewPayload || ((++mLoopCnt % 10) == 0)) {
mNewPayload = false;
mLoopCnt = 0;
DataScreen();
}
}
private:
void DataScreen() {
if (mCfg->type == 0)
return;
if(*mUtcTs == 0)
return;
float totalPower = 0;
float totalYieldDay = 0;
float totalYieldTotal = 0;
bool isprod = false;
Inverter<> *iv;
record_t<> *rec;
for (uint8_t i = 0; i < mSys->getNumInverters(); i++) {
iv = mSys->getInverterByPos(i);
rec = iv->getRecordStruct(RealTimeRunData_Debug);
if (iv == NULL)
continue;
if (iv->isProducing(*mUtcTs))
isprod = true;
totalPower += iv->getChannelFieldValue(CH0, FLD_PAC, rec);
totalYieldDay += iv->getChannelFieldValue(CH0, FLD_YD, rec);
totalYieldTotal += iv->getChannelFieldValue(CH0, FLD_YT, rec);
}
mDisplay->clearBuffer();
// Logos
// pxMovement +x (0 - 6 px)
uint8_t ex = (_mExtra % 7);
if (isprod) {
mDisplay->drawXBMP(5 + ex, 1, 8, 17, bmp_arrow);
if (mCfg->logoEn)
mDisplay->drawXBMP(mDisplay->getWidth() - 24 + ex, 2, 16, 16, bmp_logo);
}
if ((totalPower > 0) && isprod) {
mTimeout = DISP_DEFAULT_TIMEOUT;
mDisplay->setPowerSave(false);
mDisplay->setContrast(mCfg->contrast);
if (totalPower > 999)
snprintf(_fmtText, sizeof(_fmtText), "%2.1f kW", (totalPower / 1000));
else
snprintf(_fmtText, sizeof(_fmtText), "%3.0f W", totalPower);
printText(_fmtText, 0, 20);
} else {
printText("offline", 0, 25);
if(mCfg->pwrSaveAtIvOffline) {
if(mTimeout == 0)
mDisplay->setPowerSave(true);
}
}
snprintf(_fmtText, sizeof(_fmtText), "today: %4.0f Wh", totalYieldDay);
printText(_fmtText, 1);
snprintf(_fmtText, sizeof(_fmtText), "total: %.1f kWh", totalYieldTotal);
printText(_fmtText, 2);
IPAddress ip = WiFi.localIP();
if (!(_mExtra % 10) && (ip)) {
printText(ip.toString().c_str(), 3);
} else {
// Get current time
if(mIsLarge)
printText(ah::getDateTimeStr(gTimezone.toLocal(*mUtcTs)).c_str(), 3);
else
printText(ah::getTimeStr(gTimezone.toLocal(*mUtcTs)).c_str(), 3);
}
mDisplay->sendBuffer();
_mExtra++;
}
void calcLineHeights() {
uint8_t yOff = 0;
for(uint8_t i = 0; i < 4; i++) {
setFont(i);
yOff += (mDisplay->getMaxCharHeight() + 1);
mLineOffsets[i] = yOff;
}
}
inline void setFont(uint8_t line) {
switch (line) {
case 0: mDisplay->setFont((mIsLarge) ? u8g2_font_ncenB14_tr : u8g2_font_lubBI14_tr); break;
case 3: mDisplay->setFont(u8g2_font_5x8_tr); break;
default: mDisplay->setFont((mIsLarge) ? u8g2_font_ncenB10_tr : u8g2_font_5x8_tr); break;
}
}
void printText(const char* text, uint8_t line, uint8_t dispX = 5) {
if(!mIsLarge)
dispX = (line == 0) ? 10 : 5;
setFont(line);
if(mCfg->pxShift)
dispX += (_mExtra % 7); // add pixel movement
mDisplay->drawStr(dispX, mLineOffsets[line], text);
}
// private member variables
U8G2* mDisplay;
uint8_t _mExtra;
uint16_t mTimeout; // interval at which to power save (milliseconds)
char _fmtText[32];
bool mNewPayload;
bool mIsLarge;
uint8_t mLoopCnt;
uint32_t *mUtcTs;
uint8_t mLineOffsets[5];
display_t *mCfg;
HMSYSTEM *mSys;
};
#endif /*__MONOCHROME_DISPLAY__*/

117
src/web/RestApi.h

@ -12,12 +12,11 @@
#else #else
#include "ESPAsyncTCP.h" #include "ESPAsyncTCP.h"
#endif #endif
#include "ESPAsyncWebServer.h" #include "../appInterface.h"
#include "AsyncJson.h"
#include "../hm/hmSystem.h" #include "../hm/hmSystem.h"
#include "../utils/helper.h" #include "../utils/helper.h"
#include "AsyncJson.h"
#include "../appInterface.h" #include "ESPAsyncWebServer.h"
#if defined(F) && defined(ESP32) #if defined(F) && defined(ESP32)
#undef F #undef F
@ -44,8 +43,7 @@ class RestApi {
mSys = sys; mSys = sys;
mConfig = config; mConfig = config;
mSrv->on("/api", HTTP_GET, std::bind(&RestApi::onApi, this, std::placeholders::_1)); mSrv->on("/api", HTTP_GET, std::bind(&RestApi::onApi, this, std::placeholders::_1));
mSrv->on("/api", HTTP_POST, std::bind(&RestApi::onApiPost, this, std::placeholders::_1)).onBody( mSrv->on("/api", HTTP_POST, std::bind(&RestApi::onApiPost, this, std::placeholders::_1)).onBody(std::bind(&RestApi::onApiPostBody, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
std::bind(&RestApi::onApiPostBody, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
mSrv->on("/get_setup", HTTP_GET, std::bind(&RestApi::onDwnldSetup, this, std::placeholders::_1)); mSrv->on("/get_setup", HTTP_GET, std::bind(&RestApi::onDwnldSetup, this, std::placeholders::_1));
} }
@ -78,22 +76,38 @@ class RestApi {
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
String path = request->url().substring(5); String path = request->url().substring(5);
if(path == "html/system") getHtmlSystem(root); if (path == "html/system")
else if(path == "html/logout") getHtmlLogout(root); getHtmlSystem(root);
else if(path == "html/save") getHtmlSave(root); else if (path == "html/logout")
else if(path == "system") getSysInfo(root); getHtmlLogout(root);
else if(path == "generic") getGeneric(root); else if (path == "html/save")
else if(path == "reboot") getReboot(root); getHtmlSave(root);
else if(path == "statistics") getStatistics(root); else if (path == "system")
else if(path == "inverter/list") getInverterList(root); getSysInfo(root);
else if(path == "index") getIndex(root); else if (path == "generic")
else if(path == "setup") getSetup(root); getGeneric(root);
else if(path == "setup/networks") getNetworks(root); else if (path == "reboot")
else if(path == "live") getLive(root); getReboot(root);
else if(path == "record/info") getRecord(root, InverterDevInform_All); else if (path == "statistics")
else if(path == "record/alarm") getRecord(root, AlarmData); getStatistics(root);
else if(path == "record/config") getRecord(root, SystemConfigPara); else if (path == "inverter/list")
else if(path == "record/live") getRecord(root, RealTimeRunData_Debug); getInverterList(root);
else if (path == "index")
getIndex(root);
else if (path == "setup")
getSetup(root);
else if (path == "setup/networks")
getNetworks(root);
else if (path == "live")
getLive(root);
else if (path == "record/info")
getRecord(root, InverterDevInform_All);
else if (path == "record/alarm")
getRecord(root, AlarmData);
else if (path == "record/config")
getRecord(root, SystemConfigPara);
else if (path == "record/live")
getRecord(root, RealTimeRunData_Debug);
else { else {
if (path.substring(0, 12) == "inverter/id/") if (path.substring(0, 12) == "inverter/id/")
getInverter(root, request->url().substring(17).toInt()); getInverter(root, request->url().substring(17).toInt());
@ -131,13 +145,19 @@ class RestApi {
root[F("success")] = false; root[F("success")] = false;
root[F("error")] = "Path not found: " + path; root[F("error")] = "Path not found: " + path;
} }
} } else {
else {
switch (err.code()) { switch (err.code()) {
case DeserializationError::Ok: break; case DeserializationError::Ok:
case DeserializationError::InvalidInput: root[F("error")] = F("Invalid input"); break; break;
case DeserializationError::NoMemory: root[F("error")] = F("Not enough memory"); break; case DeserializationError::InvalidInput:
default: root[F("error")] = F("Deserialization failed"); break; root[F("error")] = F("Invalid input");
break;
case DeserializationError::NoMemory:
root[F("error")] = F("Not enough memory");
break;
default:
root[F("error")] = F("Deserialization failed");
break;
} }
} }
@ -166,8 +186,7 @@ class RestApi {
if (!fp) { if (!fp) {
DPRINTLN(DBG_ERROR, F("failed to load settings")); DPRINTLN(DBG_ERROR, F("failed to load settings"));
response = request->beginResponse(200, F("application/json; charset=utf-8"), "{}"); response = request->beginResponse(200, F("application/json; charset=utf-8"), "{}");
} } else {
else {
String tmp = fp.readString(); String tmp = fp.readString();
int i = 0; int i = 0;
// remove all passwords // remove all passwords
@ -255,7 +274,6 @@ class RestApi {
getSysInfo(obj.createNestedObject(F("system"))); getSysInfo(obj.createNestedObject(F("system")));
getGeneric(obj.createNestedObject(F("generic"))); getGeneric(obj.createNestedObject(F("generic")));
obj[F("html")] = F("<a href=\"/factory\" class=\"btn\">Factory Reset</a><br/><br/><a href=\"/reboot\" class=\"btn\">Reboot</a>"); obj[F("html")] = F("<a href=\"/factory\" class=\"btn\">Factory Reset</a><br/><br/><a href=\"/reboot\" class=\"btn\">Reboot</a>");
} }
void getHtmlLogout(JsonObject obj) { void getHtmlLogout(JsonObject obj) {
@ -398,25 +416,33 @@ class RestApi {
void getStaticIp(JsonObject obj) { void getStaticIp(JsonObject obj) {
char buf[16]; char buf[16];
ah::ip2Char(mConfig->sys.ip.ip, buf); obj[F("ip")] = String(buf); ah::ip2Char(mConfig->sys.ip.ip, buf);
ah::ip2Char(mConfig->sys.ip.mask, buf); obj[F("mask")] = String(buf); obj[F("ip")] = String(buf);
ah::ip2Char(mConfig->sys.ip.dns1, buf); obj[F("dns1")] = String(buf); ah::ip2Char(mConfig->sys.ip.mask, buf);
ah::ip2Char(mConfig->sys.ip.dns2, buf); obj[F("dns2")] = String(buf); obj[F("mask")] = String(buf);
ah::ip2Char(mConfig->sys.ip.gateway, buf); obj[F("gateway")] = String(buf); ah::ip2Char(mConfig->sys.ip.dns1, buf);
obj[F("dns1")] = String(buf);
ah::ip2Char(mConfig->sys.ip.dns2, buf);
obj[F("dns2")] = String(buf);
ah::ip2Char(mConfig->sys.ip.gateway, buf);
obj[F("gateway")] = String(buf);
} }
void getDisplay(JsonObject obj) { void getDisplay(JsonObject obj) {
obj[F("disp_type")] = (uint8_t)mConfig->plugin.display.type; obj[F("disp_type")] = (uint8_t)mConfig->plugin.display.type;
obj[F("disp_pwr")] = (bool)mConfig->plugin.display.pwrSaveAtIvOffline; obj[F("disp_pwr")] = (bool)mConfig->plugin.display.pwrSaveAtIvOffline;
obj[F("logo_en")] = (bool)mConfig->plugin.display.logoEn;
obj[F("px_shift")] = (bool)mConfig->plugin.display.pxShift; obj[F("px_shift")] = (bool)mConfig->plugin.display.pxShift;
obj[F("rot180")] = (bool)mConfig->plugin.display.rot180; obj[F("rotation")] = (uint8_t)mConfig->plugin.display.rot;
obj[F("period")] = (uint16_t)mConfig->plugin.display.period;
obj[F("contrast")] = (uint8_t)mConfig->plugin.display.contrast; obj[F("contrast")] = (uint8_t)mConfig->plugin.display.contrast;
obj[F("pinDisp0")] = mConfig->plugin.display.pin0; obj[F("data")] = mConfig->plugin.display.disp_data;
obj[F("pinDisp1")] = mConfig->plugin.display.pin1; obj[F("clock")] = mConfig->plugin.display.disp_clk;
obj[F("cs")] = mConfig->plugin.display.disp_cs;
obj[F("reset")] = mConfig->plugin.display.disp_reset;
obj[F("busy")] = mConfig->plugin.display.disp_busy;
obj[F("dc")] = mConfig->plugin.display.disp_dc;
} }
void getIndex(JsonObject obj) { void getIndex(JsonObject obj) {
getGeneric(obj.createNestedObject(F("generic"))); getGeneric(obj.createNestedObject(F("generic")));
obj[F("ts_now")] = mApp->getTimestamp(); obj[F("ts_now")] = mApp->getTimestamp();
@ -600,12 +626,10 @@ class RestApi {
iv->powerLimit[1] = AbsolutNonPersistent; iv->powerLimit[1] = AbsolutNonPersistent;
accepted = iv->setDevControlRequest(ActivePowerContr); accepted = iv->setDevControlRequest(ActivePowerContr);
} } else if (F("dev") == jsonIn[F("cmd")]) {
else if(F("dev") == jsonIn[F("cmd")]) {
DPRINTLN(DBG_INFO, F("dev cmd")); DPRINTLN(DBG_INFO, F("dev cmd"));
iv->enqueCommand<InfoCommand>(jsonIn[F("val")].as<int>()); iv->enqueCommand<InfoCommand>(jsonIn[F("val")].as<int>());
} } else {
else {
jsonOut[F("error")] = F("unknown cmd: '") + jsonIn["cmd"].as<String>() + "'"; jsonOut[F("error")] = F("unknown cmd: '") + jsonIn["cmd"].as<String>() + "'";
return false; return false;
} }
@ -630,8 +654,7 @@ class RestApi {
mTimezoneOffset = jsonIn[F("val")]; mTimezoneOffset = jsonIn[F("val")];
else if (F("discovery_cfg") == jsonIn[F("cmd")]) { else if (F("discovery_cfg") == jsonIn[F("cmd")]) {
mApp->setMqttDiscoveryFlag(); // for homeassistant mApp->setMqttDiscoveryFlag(); // for homeassistant
} } else {
else {
jsonOut[F("error")] = F("unknown cmd"); jsonOut[F("error")] = F("unknown cmd");
return false; return false;
} }

107
src/web/web.h

@ -13,25 +13,22 @@
#else #else
#include "ESPAsyncTCP.h" #include "ESPAsyncTCP.h"
#endif #endif
#include "ESPAsyncWebServer.h"
#include "../appInterface.h" #include "../appInterface.h"
#include "../hm/hmSystem.h" #include "../hm/hmSystem.h"
#include "../utils/helper.h" #include "../utils/helper.h"
#include "ESPAsyncWebServer.h"
#include "html/h/index_html.h"
#include "html/h/login_html.h"
#include "html/h/style_css.h"
#include "html/h/colorDark_css.h"
#include "html/h/colorBright_css.h"
#include "html/h/api_js.h" #include "html/h/api_js.h"
#include "html/h/colorBright_css.h"
#include "html/h/colorDark_css.h"
#include "html/h/favicon_ico.h" #include "html/h/favicon_ico.h"
#include "html/h/setup_html.h" #include "html/h/index_html.h"
#include "html/h/visualization_html.h" #include "html/h/login_html.h"
#include "html/h/update_html.h"
#include "html/h/serial_html.h" #include "html/h/serial_html.h"
#include "html/h/setup_html.h"
#include "html/h/style_css.h"
#include "html/h/system_html.h" #include "html/h/system_html.h"
#include "html/h/update_html.h"
#include "html/h/visualization_html.h"
#define WEB_SERIAL_BUF_SIZE 2048 #define WEB_SERIAL_BUF_SIZE 2048
@ -90,7 +87,6 @@ class Web {
mWeb.on("/serial", HTTP_GET, std::bind(&Web::onSerial, this, std::placeholders::_1)); mWeb.on("/serial", HTTP_GET, std::bind(&Web::onSerial, this, std::placeholders::_1));
mWeb.on("/debug", HTTP_GET, std::bind(&Web::onDebug, this, std::placeholders::_1)); mWeb.on("/debug", HTTP_GET, std::bind(&Web::onDebug, this, std::placeholders::_1));
mEvts.onConnect(std::bind(&Web::onConnect, this, std::placeholders::_1)); mEvts.onConnect(std::bind(&Web::onConnect, this, std::placeholders::_1));
mWeb.addHandler(&mEvts); mWeb.addHandler(&mEvts);
@ -179,8 +175,7 @@ class Web {
if (!mApp->readSettings("tmp.json")) { if (!mApp->readSettings("tmp.json")) {
mUploadFail = true; mUploadFail = true;
DPRINTLN(DBG_ERROR, F("upload JSON error!")); DPRINTLN(DBG_ERROR, F("upload JSON error!"));
} } else
else
mApp->saveSettings(); mApp->saveSettings();
} }
DPRINTLN(DBG_INFO, F("upload finished!")); DPRINTLN(DBG_INFO, F("upload finished!"));
@ -198,8 +193,7 @@ class Web {
strncpy(&mSerialBuf[mSerialBufFill], mApp->getTimeStr(mApp->getTimezoneOffset()).c_str(), 9); strncpy(&mSerialBuf[mSerialBufFill], mApp->getTimeStr(mApp->getTimezoneOffset()).c_str(), 9);
mSerialBufFill += 9; mSerialBufFill += 9;
} }
} } else {
else {
mSerialBufFill = 0; mSerialBufFill = 0;
mEvts.send("webSerial, buffer overflow!<rn>", "serial", millis()); mEvts.send("webSerial, buffer overflow!<rn>", "serial", millis());
return; return;
@ -214,8 +208,7 @@ class Web {
if ((length + mSerialBufFill) < WEB_SERIAL_BUF_SIZE) { if ((length + mSerialBufFill) < WEB_SERIAL_BUF_SIZE) {
strncpy(&mSerialBuf[mSerialBufFill], msg.c_str(), length); strncpy(&mSerialBuf[mSerialBufFill], msg.c_str(), length);
mSerialBufFill += length; mSerialBufFill += length;
} } else {
else {
mSerialBufFill = 0; mSerialBufFill = 0;
mEvts.send("webSerial, buffer overflow!<rn>", "serial", millis()); mEvts.send("webSerial, buffer overflow!<rn>", "serial", millis());
} }
@ -413,14 +406,13 @@ class Web {
content = F("factory reset: success\n\nrebooting ... "); content = F("factory reset: success\n\nrebooting ... ");
else else
content = F("factory reset: failed\n\nrebooting ... "); content = F("factory reset: failed\n\nrebooting ... ");
} } else {
else {
content = F("factory reset: aborted"); content = F("factory reset: aborted");
refresh = 3; refresh = 3;
} }
} } else {
else { content = F(
content = F("<h1>Factory Reset</h1>" "<h1>Factory Reset</h1>"
"<p><a href=\"/factory?reset=1\">RESET</a><br/><br/><a href=\"/factory?reset=0\">CANCEL</a><br/></p>"); "<p><a href=\"/factory?reset=1\">RESET</a><br/><br/><a href=\"/factory?reset=0\">CANCEL</a><br/></p>");
refresh = 120; refresh = 120;
} }
@ -491,7 +483,6 @@ class Web {
request->arg("ipGateway").toCharArray(buf, 20); request->arg("ipGateway").toCharArray(buf, 20);
ah::ip2Arr(mConfig->sys.ip.gateway, buf); ah::ip2Arr(mConfig->sys.ip.gateway, buf);
// inverter // inverter
Inverter<> *iv; Inverter<> *iv;
for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
@ -504,10 +495,20 @@ class Web {
memset(buf, 0, 20); memset(buf, 0, 20);
iv->config->serial.u64 = ah::Serial2u64(buf); iv->config->serial.u64 = ah::Serial2u64(buf);
switch (iv->config->serial.b[4]) { switch (iv->config->serial.b[4]) {
case 0x21: iv->type = INV_TYPE_1CH; iv->channels = 1; break; case 0x21:
case 0x41: iv->type = INV_TYPE_2CH; iv->channels = 2; break; iv->type = INV_TYPE_1CH;
case 0x61: iv->type = INV_TYPE_4CH; iv->channels = 4; break; iv->channels = 1;
default: break; break;
case 0x41:
iv->type = INV_TYPE_2CH;
iv->channels = 2;
break;
case 0x61:
iv->type = INV_TYPE_4CH;
iv->channels = 4;
break;
default:
break;
} }
// name // name
@ -535,11 +536,21 @@ class Web {
for (uint8_t i = 0; i < 5; i++) { for (uint8_t i = 0; i < 5; i++) {
pin = request->arg(String(pinArgNames[i])).toInt(); pin = request->arg(String(pinArgNames[i])).toInt();
switch (i) { switch (i) {
default: mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_CS_PIN); break; default:
case 1: mConfig->nrf.pinCe = ((pin != 0xff) ? pin : DEF_CE_PIN); break; mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_CS_PIN);
case 2: mConfig->nrf.pinIrq = ((pin != 0xff) ? pin : DEF_IRQ_PIN); break; break;
case 3: mConfig->led.led0 = pin; break; case 1:
case 4: mConfig->led.led1 = pin; break; mConfig->nrf.pinCe = ((pin != 0xff) ? pin : DEF_CE_PIN);
break;
case 2:
mConfig->nrf.pinIrq = ((pin != 0xff) ? pin : DEF_IRQ_PIN);
break;
case 3:
mConfig->led.led0 = pin;
break;
case 4:
mConfig->led.led1 = pin;
break;
} }
} }
@ -570,8 +581,7 @@ class Web {
String addr = request->arg("mqttAddr"); String addr = request->arg("mqttAddr");
addr.trim(); addr.trim();
addr.toCharArray(mConfig->mqtt.broker, MQTT_ADDR_LEN); addr.toCharArray(mConfig->mqtt.broker, MQTT_ADDR_LEN);
} } else
else
mConfig->mqtt.broker[0] = '\0'; mConfig->mqtt.broker[0] = '\0';
request->arg("mqttUser").toCharArray(mConfig->mqtt.user, MQTT_USER_LEN); request->arg("mqttUser").toCharArray(mConfig->mqtt.user, MQTT_USER_LEN);
if (request->arg("mqttPwd") != "{PWD}") if (request->arg("mqttPwd") != "{PWD}")
@ -592,14 +602,17 @@ class Web {
// display // display
mConfig->plugin.display.pwrSaveAtIvOffline = (request->arg("dispPwr") == "on"); mConfig->plugin.display.pwrSaveAtIvOffline = (request->arg("dispPwr") == "on");
mConfig->plugin.display.logoEn = (request->arg("logoEn") == "on");
mConfig->plugin.display.pxShift = (request->arg("dispPxSh") == "on"); mConfig->plugin.display.pxShift = (request->arg("dispPxSh") == "on");
mConfig->plugin.display.rot180 = (request->arg("disp180") == "on"); mConfig->plugin.display.rot = request->arg("rotation").toInt();
mConfig->plugin.display.type = request->arg("dispType").toInt(); mConfig->plugin.display.type = request->arg("dispType").toInt();
mConfig->plugin.display.contrast = request->arg("dispCont").toInt(); mConfig->plugin.display.contrast = request->arg("dispCont").toInt();
mConfig->plugin.display.pin0 = request->arg("pinDisp0").toInt(); mConfig->plugin.display.period = request->arg("period").toInt();
mConfig->plugin.display.pin1 = request->arg("pinDisp1").toInt(); mConfig->plugin.display.disp_data = request->arg("data").toInt();
mConfig->plugin.display.disp_clk = request->arg("clock").toInt();
mConfig->plugin.display.disp_cs = request->arg("cs").toInt();
mConfig->plugin.display.disp_reset = request->arg("reset").toInt();
mConfig->plugin.display.disp_busy = request->arg("busy").toInt();
mConfig->plugin.display.disp_dc = request->arg("dc").toInt();
mApp->saveSettings(); mApp->saveSettings();
@ -763,7 +776,11 @@ class Web {
#ifdef ENABLE_PROMETHEUS_EP #ifdef ENABLE_PROMETHEUS_EP
enum { enum {
metricsStateStart, metricsStateInverter, metricStateRealtimeData,metricsStateAlarmData,metricsStateEnd metricsStateStart,
metricsStateInverter,
metricStateRealtimeData,
metricsStateAlarmData,
metricsStateEnd
} metricsStep; } metricsStep;
int metricsInverterId, metricsChannelId; int metricsInverterId, metricsChannelId;
@ -772,8 +789,7 @@ class Web {
metricsStep = metricsStateStart; metricsStep = metricsStateStart;
AsyncWebServerResponse *response = request->beginChunkedResponse(F("text/plain"), AsyncWebServerResponse *response = request->beginChunkedResponse(F("text/plain"),
[this](uint8_t *buffer, size_t maxLen, size_t filledLength) -> size_t [this](uint8_t *buffer, size_t maxLen, size_t filledLength) -> size_t {
{
Inverter<> *iv; Inverter<> *iv;
record_t<> *rec; record_t<> *rec;
statistics_t *stat; statistics_t *stat;
@ -871,8 +887,7 @@ class Web {
// TODO: find the right one channel with the alarm id // TODO: find the right one channel with the alarm id
alarmChannelId = 0; alarmChannelId = 0;
// printf("AlarmData Length %d\n",rec->length); // printf("AlarmData Length %d\n",rec->length);
if (alarmChannelId < rec->length) if (alarmChannelId < rec->length) {
{
// uint8_t channel = rec->assign[alarmChannelId].ch; // uint8_t channel = rec->assign[alarmChannelId].ch;
std::tie(promUnit, promType) = convertToPromUnits(iv->getUnit(alarmChannelId, rec)); std::tie(promUnit, promType) = convertToPromUnits(iv->getUnit(alarmChannelId, rec));
snprintf(type, sizeof(type), "# TYPE ahoy_solar_%s%s %s", iv->getFieldName(alarmChannelId, rec), promUnit.c_str(), promType.c_str()); snprintf(type, sizeof(type), "# TYPE ahoy_solar_%s%s %s", iv->getFieldName(alarmChannelId, rec), promUnit.c_str(), promType.c_str());

Loading…
Cancel
Save