Use clang to make codestyle consistent

This commit is contained in:
Djuri Baars 2023-11-30 22:38:01 +01:00
parent 239415c7aa
commit 4aab02a040
31 changed files with 10588 additions and 10871 deletions

View File

@ -1,482 +1,484 @@
#include <Adafruit_GFX.h>
#include <Arduino.h>
const uint8_t Antonio_SemiBold20pt7bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x66, 0x66,
0x66, 0x66, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7B, 0xDE, 0xF7,
0x98, 0xC6, 0x00, 0x03, 0x8F, 0x01, 0xC7, 0x80, 0xE3, 0x80, 0xF1, 0xC0,
0x70, 0xE0, 0x38, 0x70, 0x1C, 0x78, 0x0E, 0x3C, 0x07, 0x1C, 0x07, 0x8E,
0x03, 0x87, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x38, 0xE0, 0x3C,
0x70, 0x1E, 0x38, 0x0E, 0x1C, 0x07, 0x0E, 0x03, 0x8F, 0x0F, 0xFF, 0xF7,
0xFF, 0xF8, 0xF1, 0xC0, 0x70, 0xE0, 0x38, 0x70, 0x1C, 0x78, 0x0E, 0x3C,
0x0F, 0x1C, 0x07, 0x8E, 0x03, 0x87, 0x01, 0xC3, 0x80, 0xE3, 0xC0, 0x71,
0xE0, 0x78, 0xE0, 0x00, 0x06, 0x00, 0x30, 0x01, 0x80, 0x0C, 0x00, 0x60,
0x1F, 0xC1, 0xFF, 0x9F, 0xFC, 0xFB, 0xF7, 0x87, 0xBC, 0x3D, 0xE1, 0xEF,
0x0F, 0x78, 0x7B, 0xE3, 0xDF, 0x00, 0x7C, 0x01, 0xF8, 0x0F, 0xE0, 0x3F,
0x80, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x3E, 0xF0, 0xF7, 0x87, 0xFC, 0x3F,
0xE1, 0xFF, 0x0F, 0xFC, 0x7B, 0xFF, 0xCF, 0xFE, 0x3F, 0xE0, 0xFE, 0x01,
0xC0, 0x0E, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0x1F, 0x80, 0x01, 0xC0,
0x07, 0xFE, 0x00, 0x3C, 0x00, 0x7F, 0xE0, 0x03, 0xC0, 0x0F, 0x9F, 0x00,
0x38, 0x00, 0xF0, 0xF0, 0x07, 0x80, 0x0F, 0x0F, 0x00, 0x70, 0x00, 0xF0,
0xF0, 0x0F, 0x00, 0x0F, 0x0F, 0x00, 0xF0, 0x00, 0xF0, 0xF0, 0x0E, 0x00,
0x0F, 0x0F, 0x01, 0xE0, 0x00, 0xF0, 0xF0, 0x1C, 0x00, 0x0F, 0x0F, 0x03,
0xC0, 0x60, 0xF0, 0xF0, 0x3C, 0x3F, 0xCF, 0x0F, 0x03, 0x87, 0xFE, 0xF0,
0xF0, 0x78, 0xFF, 0xEF, 0x0F, 0x07, 0x8F, 0x0E, 0xF0, 0xF0, 0xF0, 0xF0,
0xFF, 0x0F, 0x0F, 0x0E, 0x0F, 0xF0, 0xF0, 0xE0, 0xE0, 0xF7, 0xDF, 0x1E,
0x0E, 0x0F, 0x7F, 0xE1, 0xE0, 0xE0, 0xF3, 0xFC, 0x1C, 0x0E, 0x0F, 0x1F,
0x83, 0xC0, 0xE0, 0xF0, 0x00, 0x38, 0x0E, 0x0F, 0x00, 0x07, 0x80, 0xE0,
0xF0, 0x00, 0x78, 0x0E, 0x0F, 0x00, 0x07, 0x00, 0xE0, 0xF0, 0x00, 0xF0,
0x0E, 0x0F, 0x00, 0x0E, 0x00, 0xE0, 0xF0, 0x01, 0xE0, 0x0F, 0x0F, 0x00,
0x1E, 0x00, 0xF1, 0xE0, 0x01, 0xC0, 0x0F, 0xFE, 0x00, 0x3C, 0x00, 0x7F,
0xC0, 0x03, 0x80, 0x03, 0xF8, 0x03, 0xE0, 0x03, 0xFE, 0x00, 0xFF, 0x80,
0x7C, 0xF0, 0x1E, 0x1C, 0x07, 0x87, 0x01, 0xE1, 0xC0, 0x78, 0x70, 0x1E,
0x1C, 0x07, 0x8F, 0x00, 0xF3, 0x80, 0x3D, 0xE0, 0x0F, 0x78, 0x01, 0xFC,
0x00, 0x7E, 0x00, 0x1F, 0x80, 0x07, 0xC0, 0x03, 0xF0, 0x00, 0xFE, 0x08,
0x7F, 0x86, 0x3F, 0xE3, 0x8F, 0x3C, 0xF7, 0x8F, 0x79, 0xE1, 0xFC, 0x78,
0x7F, 0x3C, 0x1F, 0x8F, 0x03, 0xE3, 0xC0, 0xF0, 0xF0, 0x3E, 0x1E, 0x1F,
0x87, 0xFF, 0xF1, 0xFF, 0xFC, 0x3F, 0xE7, 0x87, 0xF1, 0xE0, 0xFF, 0xFF,
0xF7, 0x76, 0x66, 0x66, 0x3E, 0xFF, 0xFF, 0xCF, 0x1E, 0x3C, 0x78, 0xF1,
0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C,
0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xFB, 0xF7,
0xE1, 0xC0, 0xF1, 0xF3, 0xF1, 0xE3, 0xC7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9,
0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E,
0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7B, 0xF7, 0xEF, 0x98, 0x00,
0x07, 0x00, 0x1C, 0x04, 0x71, 0x39, 0xCE, 0xFB, 0x7D, 0xFF, 0xC1, 0xFC,
0x03, 0xE0, 0x3F, 0xE3, 0xEF, 0xFF, 0x73, 0x99, 0xC6, 0x07, 0x08, 0x1C,
0x00, 0x70, 0x00, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0xFF, 0xFF, 0xFF,
0xFF, 0xF0, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0xFF, 0xFF, 0xFF,
0x8C, 0xE7, 0x31, 0x9C, 0xC0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0x00,
0xF8, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0E, 0x00, 0xF0, 0x07, 0x80, 0x3C,
0x01, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x38, 0x03, 0xC0, 0x1E, 0x00,
0xF0, 0x07, 0x80, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x0F, 0x00, 0x78,
0x03, 0xC0, 0x1E, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x3C, 0x01,
0xE0, 0x0F, 0x00, 0x78, 0x07, 0x80, 0x00, 0x0F, 0xC1, 0xFF, 0x9F, 0xFC,
0xFF, 0xF7, 0x8F, 0xFC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F, 0xC3, 0xFE,
0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F, 0xC3,
0xFE, 0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F,
0xC3, 0xFE, 0x1F, 0xF0, 0xFF, 0x87, 0xBC, 0x7D, 0xF7, 0xEF, 0xFE, 0x3F,
0xF0, 0xFF, 0x00, 0x03, 0x83, 0xC3, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F,
0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC3,
0xE1, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8,
0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC0, 0x0F, 0x81, 0xFF, 0x1F, 0xF8, 0xFF,
0xEF, 0x8F, 0x7C, 0x7B, 0xC3, 0xDE, 0x1E, 0xF0, 0xFF, 0x87, 0xFC, 0x3F,
0xE1, 0xEF, 0x0F, 0x78, 0x78, 0x07, 0xC0, 0x3C, 0x03, 0xE0, 0x1E, 0x01,
0xF0, 0x0F, 0x00, 0xF8, 0x07, 0x80, 0x7C, 0x07, 0xC0, 0x3E, 0x03, 0xE0,
0x1F, 0x00, 0xF0, 0x07, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0xFE, 0xFF, 0xF7,
0xFF, 0x80, 0x0F, 0xC0, 0xFF, 0x87, 0xFF, 0x1F, 0xFE, 0x78, 0xFB, 0xE1,
0xEF, 0x87, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xE0, 0x07, 0x80, 0x1E, 0x00,
0x78, 0x03, 0xE0, 0x3F, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0xC0, 0x0F, 0x00,
0x3E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0xBC, 0x1E, 0xF0, 0x7F, 0xC1, 0xFF,
0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xE1, 0xE7, 0xDF, 0x9F, 0xFC, 0x3F, 0xE0,
0x7F, 0x00, 0x01, 0xF0, 0x07, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x1F,
0xC0, 0x7F, 0x01, 0xFC, 0x06, 0xF0, 0x3B, 0xC0, 0xEF, 0x03, 0xBC, 0x1C,
0xF0, 0x73, 0xC1, 0xCF, 0x0F, 0x3C, 0x38, 0xF0, 0xE3, 0xC7, 0x8F, 0x1C,
0x3C, 0x70, 0xF1, 0xC3, 0xCF, 0x0F, 0x38, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0,
0x03, 0xC0, 0xFF, 0xEF, 0xFE, 0xFF, 0xEF, 0xFE, 0xF0, 0x0F, 0x00, 0xF0,
0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF3, 0x0F, 0xFC, 0xFF, 0xEF, 0xFE, 0xF9,
0xEF, 0x1F, 0xF0, 0xF1, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00,
0xF0, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x1F, 0xFB,
0xEF, 0xFE, 0x7F, 0xC3, 0xF8, 0x0F, 0xC0, 0xFF, 0x83, 0xFF, 0x1F, 0xFC,
0x78, 0xF9, 0xE1, 0xEF, 0x87, 0xBE, 0x1E, 0xF8, 0x7B, 0xE1, 0xEF, 0x80,
0x3E, 0x00, 0xF8, 0x03, 0xEF, 0x8F, 0xFF, 0x3F, 0xFE, 0xFF, 0xFB, 0xE1,
0xEF, 0x87, 0xBE, 0x1E, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8,
0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x79, 0xE1, 0xE7, 0xFF, 0x9F,
0xFE, 0x3F, 0xF0, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x7C, 0x01, 0xE0, 0x0F, 0x80, 0x3C, 0x00, 0xF0, 0x07, 0xC0, 0x1E,
0x00, 0x78, 0x03, 0xE0, 0x0F, 0x80, 0x3C, 0x00, 0xF0, 0x07, 0xC0, 0x1E,
0x00, 0x78, 0x03, 0xE0, 0x0F, 0x80, 0x3C, 0x00, 0xF0, 0x07, 0xC0, 0x1F,
0x00, 0x7C, 0x01, 0xF0, 0x07, 0x80, 0x1E, 0x00, 0xF8, 0x03, 0xE0, 0x0F,
0x80, 0x3E, 0x00, 0xF8, 0x00, 0x1F, 0xC1, 0xFF, 0x1F, 0xFC, 0xFB, 0xFF,
0x8F, 0xFC, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0,
0xFF, 0x87, 0xBC, 0x79, 0xFF, 0xC7, 0xFC, 0x3F, 0xE3, 0xFF, 0x9E, 0x3D,
0xF1, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
0x1F, 0xE0, 0xFF, 0x07, 0xFC, 0x3F, 0xF7, 0xEF, 0xFE, 0x3F, 0xF0, 0xFE,
0x00, 0x1F, 0x81, 0xFF, 0x1F, 0xFD, 0xFF, 0xEF, 0x8F, 0x78, 0x7F, 0xC3,
0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F,
0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0xC7, 0xDF, 0xFE, 0xFF, 0xF3, 0xFF, 0x80,
0x3C, 0x01, 0xE0, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x0F,
0xFC, 0x79, 0xF7, 0xCF, 0xFE, 0x3F, 0xE0, 0xFE, 0x00, 0xFF, 0xFF, 0xF0,
0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF3, 0x9C, 0xCE, 0x73, 0x98,
0x00, 0x20, 0x0C, 0x07, 0x81, 0xF0, 0xFE, 0x3F, 0x9F, 0xC7, 0xF0, 0xF8,
0x1C, 0x03, 0xE0, 0x7E, 0x07, 0xF0, 0x3F, 0x03, 0xF8, 0x1F, 0x01, 0xE0,
0x0C, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x0F, 0xFF,
0xFF, 0xFF, 0xC0, 0x80, 0x18, 0x03, 0x80, 0x7C, 0x0F, 0xC0, 0xFE, 0x07,
0xF0, 0x7F, 0x03, 0xE0, 0x3C, 0x0F, 0x83, 0xF1, 0xFC, 0xFE, 0x3F, 0x87,
0xC0, 0xF0, 0x18, 0x02, 0x00, 0x00, 0x1F, 0x87, 0xFC, 0x7F, 0xEF, 0xFE,
0xF1, 0xEF, 0x1F, 0xF1, 0xFF, 0x1F, 0xF1, 0xFF, 0x1F, 0xF1, 0xFF, 0x1F,
0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x3E, 0x03, 0xE0, 0xFC, 0x3F, 0x83, 0xF0,
0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x00, 0x3F, 0x80,
0x00, 0xFF, 0xF0, 0x00, 0xFF, 0xFC, 0x00, 0xF8, 0x0F, 0x00, 0xF0, 0x03,
0xC0, 0xF0, 0x00, 0xF0, 0xF0, 0x00, 0x38, 0x78, 0x00, 0x1C, 0x38, 0x1F,
0x87, 0x3C, 0x3F, 0xE3, 0x9E, 0x1E, 0xF1, 0xCE, 0x1C, 0x38, 0xE7, 0x0E,
0x1C, 0x77, 0x8E, 0x0E, 0x1F, 0xC7, 0x07, 0x0F, 0xE3, 0x83, 0x87, 0xF1,
0xC1, 0xC3, 0xF8, 0xE0, 0xE1, 0xFC, 0x70, 0x70, 0xFE, 0x38, 0x38, 0x7F,
0x1C, 0x1C, 0x77, 0x8E, 0x0E, 0x39, 0xC7, 0x07, 0x1C, 0xE3, 0xC7, 0xCE,
0x70, 0xF6, 0xFE, 0x3C, 0x7F, 0x3F, 0x1E, 0x1F, 0x0F, 0x07, 0x81, 0x00,
0x03, 0xC0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1F, 0x80,
0x70, 0x07, 0xFF, 0xF8, 0x00, 0xFF, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0x07,
0xC0, 0x07, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F,
0xE0, 0x0F, 0xF0, 0x0E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E,
0xF0, 0x1E, 0x70, 0x1E, 0x78, 0x1C, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C,
0x78, 0x3C, 0x78, 0x3C, 0x3C, 0x3C, 0x3C, 0x7C, 0x3C, 0x78, 0x3C, 0x7F,
0xFC, 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFE, 0x78, 0x1E, 0xF8, 0x1E, 0xF8,
0x1E, 0xF0, 0x1E, 0xF0, 0x1F, 0xF0, 0x1F, 0xFF, 0x03, 0xFF, 0x8F, 0xFF,
0x3F, 0xFC, 0xF0, 0xFB, 0xC3, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1,
0xEF, 0x07, 0xBC, 0x3E, 0xF0, 0xFB, 0xC3, 0xCF, 0xFF, 0x3F, 0xF8, 0xFF,
0xF3, 0xFF, 0xCF, 0x0F, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xFC,
0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC3, 0xEF,
0xFF, 0xBF, 0xFC, 0xFF, 0xE3, 0xFF, 0x00, 0x0F, 0xC0, 0x7F, 0xC3, 0xFF,
0x9F, 0xFE, 0x7C, 0x79, 0xE1, 0xFF, 0x87, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0,
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8,
0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x1F, 0x78, 0x7D, 0xE1, 0xE7,
0xEF, 0x8F, 0xFE, 0x3F, 0xF0, 0x3F, 0x80, 0xFF, 0x07, 0xFF, 0x3F, 0xFD,
0xFF, 0xEF, 0x0F, 0xF8, 0x7F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83,
0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F,
0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x7F, 0xFF, 0xDF, 0xFE, 0xFF,
0xE7, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x0F, 0x03,
0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F,
0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03,
0xC0, 0xF0, 0x3C, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0xF0,
0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F,
0x03, 0xC0, 0xF0, 0x3C, 0x00, 0x0F, 0xE0, 0x7F, 0xC3, 0xFF, 0x9F, 0xFE,
0x7C, 0x7D, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83,
0xFE, 0x0F, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF9, 0xFF, 0xE7,
0xFF, 0x9F, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0x78, 0x3D, 0xE1, 0xF7, 0xEF, 0xCF,
0xFF, 0x3F, 0xEC, 0x3F, 0x30, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F,
0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07,
0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0,
0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC,
0x1F, 0xF0, 0x7F, 0xC1, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF8,
0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00,
0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F,
0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0xF8, 0x7F, 0xC3, 0xFE,
0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xEF, 0x0F, 0x78, 0x7B, 0xF7,
0xCF, 0xFC, 0x7F, 0xE1, 0xFE, 0x00, 0xF0, 0x3D, 0xE0, 0xFB, 0xC1, 0xE7,
0x83, 0xCF, 0x0F, 0x9E, 0x1E, 0x3C, 0x3C, 0x78, 0xF8, 0xF1, 0xE1, 0xE7,
0xC3, 0xCF, 0x87, 0x9E, 0x0F, 0x7C, 0x1E, 0xF8, 0x3D, 0xE0, 0x7F, 0xC0,
0xFF, 0x01, 0xFE, 0x03, 0xFE, 0x07, 0xBC, 0x0F, 0x7C, 0x1E, 0xF8, 0x3C,
0xF0, 0x79, 0xF0, 0xF1, 0xE1, 0xE3, 0xC3, 0xC7, 0xC7, 0x87, 0x8F, 0x0F,
0x9E, 0x1F, 0x3C, 0x1E, 0x78, 0x3E, 0xF0, 0x7D, 0xE0, 0x7C, 0xF0, 0x3C,
0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03,
0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0,
0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F,
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x03,
0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0,
0x7F, 0xFE, 0x07, 0xFF, 0xE0, 0x7F, 0xFE, 0x07, 0xFE, 0xE0, 0x7F, 0xEE,
0x0F, 0xFE, 0xF0, 0xFF, 0xEF, 0x0E, 0xFE, 0x70, 0xEF, 0xE7, 0x0E, 0xFE,
0x70, 0xEF, 0xE7, 0x1E, 0xFE, 0x79, 0xCF, 0xE3, 0x9C, 0xFF, 0x39, 0xCF,
0xF3, 0x9C, 0xFF, 0x3B, 0xCF, 0xF3, 0xF8, 0xFF, 0x1F, 0x8F, 0xF1, 0xF8,
0xFF, 0x1F, 0x8F, 0xF1, 0xF8, 0xFF, 0x1F, 0x8F, 0xF0, 0xF0, 0xFF, 0x0F,
0x0F, 0xF0, 0xF0, 0xFF, 0x0F, 0x0F, 0xE0, 0x3F, 0x80, 0xFF, 0x03, 0xFC,
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFF, 0x0F, 0xFC, 0x3F, 0xF0, 0xFF,
0xE3, 0xFF, 0x8F, 0xFE, 0x3F, 0xFC, 0xFF, 0xF3, 0xFD, 0xCF, 0xF7, 0xBF,
0xDE, 0xFF, 0x3B, 0xFC, 0xFF, 0xF3, 0xDF, 0xC7, 0x7F, 0x1F, 0xFC, 0x7F,
0xF0, 0xFF, 0xC3, 0xFF, 0x0F, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x03,
0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0x70, 0x0F, 0xE0, 0x7F, 0xC3, 0xFF, 0x9F,
0xFE, 0x7C, 0x7D, 0xE1, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F,
0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F,
0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0x78, 0x7D, 0xE1, 0xF7, 0xFF,
0x8F, 0xFE, 0x3F, 0xF0, 0x3F, 0x80, 0xFF, 0x87, 0xFF, 0x3F, 0xFD, 0xFF,
0xEF, 0x0F, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x1F, 0xF0, 0xFF, 0xFF, 0xBF,
0xFD, 0xFF, 0xCF, 0xF8, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80,
0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07,
0x80, 0x00, 0x0F, 0xE0, 0x7F, 0xC3, 0xFF, 0x9F, 0xFE, 0x7C, 0x7D, 0xE1,
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
0x83, 0xFE, 0x0F, 0x78, 0x7D, 0xE1, 0xF7, 0xFF, 0x8F, 0xFE, 0x3F, 0xF0,
0x3F, 0x80, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x01, 0xE0, 0x03, 0x00, 0xFF,
0x83, 0xFF, 0x8F, 0xFF, 0x3F, 0xFE, 0xF0, 0xFB, 0xC1, 0xEF, 0x07, 0xBC,
0x1E, 0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC3, 0xEF,
0x1F, 0x3F, 0xFC, 0xFF, 0xE3, 0xFF, 0xCF, 0x0F, 0xBC, 0x1E, 0xF0, 0x7B,
0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x1E,
0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xF0, 0x0F,
0xC0, 0xFF, 0xC3, 0xFF, 0x1F, 0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE,
0x1F, 0xF8, 0x7D, 0xE1, 0xF7, 0x87, 0xDF, 0x1F, 0x7E, 0x00, 0xFC, 0x01,
0xF8, 0x03, 0xF0, 0x0F, 0xE0, 0x1F, 0x80, 0x3F, 0x00, 0x7E, 0x00, 0xF9,
0xE1, 0xF7, 0x87, 0xDE, 0x0F, 0x78, 0x3D, 0xE0, 0xF7, 0x83, 0xDE, 0x0F,
0x78, 0x3D, 0xE1, 0xF7, 0xEF, 0x8F, 0xFE, 0x3F, 0xF0, 0x3F, 0x80, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xF8, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0,
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
0x83, 0xFE, 0x0F, 0x78, 0x3D, 0xE1, 0xF7, 0xEF, 0xCF, 0xFE, 0x3F, 0xF8,
0x7F, 0x80, 0xF0, 0x1F, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF8, 0x1E,
0x78, 0x1E, 0x78, 0x1E, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x7C, 0x3C,
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x78,
0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x70, 0x1E, 0xF0,
0x0E, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xE0,
0x0F, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0xF0, 0x3C,
0x0F, 0xF0, 0x3C, 0x0F, 0xF0, 0x3C, 0x0F, 0xF0, 0x3C, 0x1E, 0xF0, 0x3C,
0x1E, 0x78, 0x3E, 0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E,
0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E, 0x1E, 0x38, 0x7F,
0x1C, 0x38, 0xF7, 0x1C, 0x3C, 0xF7, 0x3C, 0x3C, 0xE7, 0x3C, 0x3C, 0xE7,
0x3C, 0x3C, 0xE7, 0x3C, 0x3C, 0xE7, 0x3C, 0x3C, 0xE7, 0xBC, 0x1D, 0xE3,
0xB8, 0x1D, 0xC3, 0xB8, 0x1D, 0xC3, 0xB8, 0x1F, 0xC3, 0xB8, 0x1F, 0xC3,
0xB8, 0x1F, 0xC3, 0xF8, 0x1F, 0xC3, 0xF8, 0x1F, 0xC1, 0xF8, 0x0F, 0x81,
0xF8, 0x0F, 0x81, 0xF0, 0x0F, 0x81, 0xF0, 0x0F, 0x81, 0xF0, 0x0F, 0x81,
0xF0, 0x0F, 0x81, 0xF0, 0xF0, 0x3F, 0xC0, 0xF7, 0x07, 0x9E, 0x1E, 0x78,
0x78, 0xE3, 0xE3, 0xCF, 0x0F, 0x3C, 0x3C, 0xF0, 0x7F, 0x81, 0xFE, 0x07,
0xF8, 0x0F, 0xE0, 0x3F, 0x00, 0xFC, 0x01, 0xF0, 0x07, 0x80, 0x3E, 0x00,
0xF8, 0x03, 0xF0, 0x0F, 0xC0, 0x7F, 0x01, 0xFE, 0x07, 0xF8, 0x3F, 0xE0,
0xF3, 0xC3, 0xCF, 0x0F, 0x3C, 0x78, 0x71, 0xE1, 0xE7, 0x87, 0x9E, 0x0E,
0xF0, 0x3F, 0xC0, 0xF0, 0xF8, 0x0F, 0x78, 0x1F, 0x78, 0x1E, 0x7C, 0x1E,
0x3C, 0x1E, 0x3C, 0x3E, 0x3C, 0x3C, 0x3E, 0x3C, 0x1E, 0x3C, 0x1E, 0x78,
0x1E, 0x78, 0x0F, 0x78, 0x0F, 0x78, 0x0F, 0xF0, 0x0F, 0xF0, 0x07, 0xF0,
0x07, 0xF0, 0x07, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0,
0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0,
0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0,
0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x3E,
0x03, 0xE0, 0x3C, 0x07, 0xC0, 0x7C, 0x07, 0xC0, 0x78, 0x0F, 0x80, 0xF8,
0x0F, 0x80, 0xF0, 0x1F, 0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x3E, 0x03, 0xE0,
0x3C, 0x07, 0xC0, 0x7C, 0x07, 0xC0, 0x78, 0x0F, 0x80, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF,
0xFF, 0xFF, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x07, 0x00, 0xF0, 0x1E, 0x03,
0xC0, 0x78, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x3C, 0x07, 0x80, 0xF0,
0x1E, 0x01, 0xC0, 0x3C, 0x07, 0x80, 0xF0, 0x0E, 0x01, 0xE0, 0x3C, 0x07,
0x80, 0xF0, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0x78, 0x0F, 0x01, 0xE0,
0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF,
0x0F, 0xC0, 0x1F, 0x80, 0x3F, 0x80, 0x7F, 0x01, 0xFE, 0x03, 0xDC, 0x07,
0xBC, 0x0E, 0x78, 0x3C, 0xF0, 0x79, 0xE0, 0xF1, 0xE1, 0xE3, 0xC7, 0x87,
0x8F, 0x0F, 0x1E, 0x0F, 0x3C, 0x1E, 0xF8, 0x3D, 0xE0, 0x7C, 0xFF, 0xFF,
0xFF, 0xFF, 0xF0, 0xF9, 0xE7, 0x8E, 0x38, 0x71, 0xC3, 0x0F, 0xC0, 0xFF,
0xC3, 0xFF, 0x1F, 0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE, 0x1E, 0xF8,
0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x7F, 0x0F, 0xFC, 0x7F, 0xF3, 0xE7, 0xDE,
0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF,
0x87, 0xFE, 0x1F, 0xF8, 0x7D, 0xE3, 0xF7, 0xFF, 0xDF, 0xFF, 0x3F, 0x7C,
0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x79, 0xE3, 0xDF, 0x9F,
0xFE, 0xFF, 0xFF, 0xC7, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1,
0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F,
0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xFC,
0x7F, 0xF7, 0xFF, 0xFE, 0xF7, 0xF7, 0x9F, 0x00, 0x0F, 0xC1, 0xFF, 0x8F,
0xFC, 0xFF, 0xF7, 0x87, 0xBC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F, 0xC3,
0xFE, 0x1F, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07,
0xC0, 0x3E, 0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xEF, 0x0F, 0x78,
0x7B, 0xE7, 0xDF, 0xFC, 0x7F, 0xE1, 0xFE, 0x00, 0x00, 0x7C, 0x01, 0xF0,
0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x7D, 0xF3, 0xFF, 0xDF, 0xFF, 0x7F, 0xFD,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F,
0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87,
0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7D, 0xE1,
0xF7, 0xDF, 0xDF, 0xFF, 0x3F, 0xFC, 0x7D, 0xF0, 0x0F, 0xC0, 0x7F, 0xC3,
0xFF, 0x1F, 0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE, 0x1E, 0xF8, 0x7B,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00,
0xF8, 0x03, 0xE0, 0x0F, 0x87, 0xBE, 0x1E, 0xF8, 0x7B, 0xE1, 0xEF, 0x87,
0x9E, 0x1E, 0x78, 0x79, 0xF3, 0xE7, 0xFF, 0x0F, 0xFC, 0x1F, 0xE0, 0x07,
0xC3, 0xF1, 0xFC, 0x7C, 0x1E, 0x07, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1,
0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78,
0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07,
0x81, 0xE0, 0x78, 0x1E, 0x07, 0x80, 0x1F, 0x7C, 0xFF, 0xF7, 0xFF, 0xDF,
0xFF, 0x78, 0x7D, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF,
0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7D, 0xE1, 0xF7, 0xCF, 0xDF, 0xFF,
0x3F, 0xFC, 0x7D, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x79, 0xC3, 0xE7, 0xFF,
0xBF, 0xFC, 0x3F, 0xC0, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00,
0x79, 0xF3, 0xDF, 0xDF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFC, 0x1F, 0xE0, 0xFF,
0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0,
0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xC0,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x78, 0xF1, 0xE0, 0x00, 0x0F,
0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3,
0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C,
0xF9, 0xFF, 0xDF, 0xBE, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00,
0xF0, 0x03, 0xC1, 0xFF, 0x07, 0xBC, 0x1E, 0xF0, 0xFB, 0xC3, 0xCF, 0x1F,
0x3C, 0x78, 0xF1, 0xE3, 0xCF, 0x8F, 0x3C, 0x3D, 0xF0, 0xF7, 0x83, 0xDE,
0x0F, 0xF8, 0x3F, 0xC0, 0xFF, 0x83, 0xDE, 0x0F, 0x78, 0x3D, 0xF0, 0xF3,
0xC3, 0xCF, 0x8F, 0x1E, 0x3C, 0x7C, 0xF0, 0xF3, 0xC3, 0xCF, 0x0F, 0xBC,
0x1E, 0xF0, 0x7F, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xE1,
0xF3, 0xDF, 0xCF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFC, 0x3F,
0xC1, 0xE0, 0xFF, 0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1,
0xE0, 0xFF, 0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0,
0xFF, 0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0, 0xFF,
0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0, 0xFF, 0x07,
0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0, 0xFF, 0x07, 0x83,
0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3C, 0xF3, 0xE7, 0xBF, 0xBF, 0xFF, 0xFF,
0xFF, 0x87, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83,
0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0x80, 0x0F, 0xC0, 0x7F, 0x83, 0xFF, 0x1F,
0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF,
0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xDE, 0x1E,
0x78, 0x79, 0xF3, 0xE7, 0xFF, 0x0F, 0xFC, 0x1F, 0xE0, 0xF3, 0xC7, 0xBF,
0xBF, 0xFD, 0xFF, 0xFF, 0x8F, 0xF8, 0x7F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F,
0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0,
0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F,
0xF8, 0xFF, 0xEF, 0xFF, 0xFD, 0xFF, 0xEF, 0x7E, 0x78, 0x03, 0xC0, 0x1E,
0x00, 0xF0, 0x07, 0x80, 0x00, 0x1E, 0x7C, 0xFF, 0xF7, 0xFF, 0xDF, 0xFF,
0x78, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87,
0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1,
0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0x78,
0x7D, 0xF7, 0xF7, 0xFF, 0xCF, 0xFF, 0x1F, 0x7C, 0x01, 0xF0, 0x07, 0xC0,
0x1F, 0x00, 0x7C, 0x01, 0xF0, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xC7, 0x83,
0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0,
0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E,
0x0F, 0x00, 0x1F, 0x83, 0xFE, 0x1F, 0xF9, 0xFF, 0xCF, 0x0F, 0x78, 0x7B,
0xC3, 0xDE, 0x1E, 0xF0, 0xF7, 0xC7, 0xBE, 0x00, 0xF8, 0x07, 0xE0, 0x1F,
0x80, 0x7E, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0x80, 0x7D, 0xE1, 0xEF, 0x0F,
0xF8, 0x7F, 0xC3, 0xFE, 0x1F, 0xF0, 0xF7, 0xEF, 0x9F, 0xFC, 0xFF, 0xC1,
0xFC, 0x00, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x8F, 0xFF, 0xFF,
0xFF, 0xCF, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F,
0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0,
0xF8, 0x3E, 0x0F, 0x83, 0xF0, 0xFF, 0x1F, 0xC3, 0xF0, 0xF8, 0x3F, 0xE0,
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
0x83, 0xFE, 0x0F, 0x78, 0x7D, 0xF3, 0xF7, 0xFF, 0xCF, 0xEF, 0x1F, 0x3C,
0xF0, 0x7F, 0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0x78, 0x79, 0xE1, 0xE7, 0x87,
0x9E, 0x1C, 0x78, 0x71, 0xE3, 0xC7, 0x8F, 0x0E, 0x3C, 0x3C, 0xF0, 0xF3,
0xC3, 0xCE, 0x0F, 0x38, 0x3C, 0xE0, 0xF3, 0x81, 0xDE, 0x07, 0x78, 0x1F,
0xE0, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x3F, 0x00, 0xFC, 0x03,
0xF0, 0x0F, 0x80, 0xF0, 0x78, 0x7F, 0x87, 0xC3, 0xFC, 0x3E, 0x1F, 0xE1,
0xF0, 0xF7, 0x0F, 0x87, 0xB8, 0x7C, 0x39, 0xC3, 0xE1, 0xCE, 0x1F, 0x8E,
0x79, 0xDC, 0x73, 0xCE, 0xE7, 0x9E, 0x77, 0x3C, 0xF3, 0xB9, 0xE3, 0x9D,
0xCF, 0x1C, 0xE6, 0x70, 0xE7, 0x3B, 0x87, 0x71, 0xDC, 0x3B, 0x8E, 0xE1,
0xDC, 0x77, 0x0F, 0xE3, 0xB8, 0x7F, 0x1D, 0xC3, 0xF8, 0xFE, 0x0F, 0x83,
0xE0, 0x7C, 0x1F, 0x03, 0xE0, 0xF8, 0x1F, 0x07, 0xC0, 0xF8, 0x3E, 0x07,
0xC1, 0xF0, 0x3E, 0x0F, 0x81, 0xE0, 0x7C, 0x00, 0xF0, 0x7B, 0xC1, 0xE7,
0x87, 0x9E, 0x3C, 0x78, 0xF0, 0xF3, 0xC3, 0xCE, 0x0F, 0x78, 0x1F, 0xE0,
0x7F, 0x01, 0xFC, 0x03, 0xF0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0,
0x0F, 0x80, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x3D, 0xE0, 0xF7, 0x83, 0xDE,
0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xCF, 0x07, 0xBC, 0x1E, 0xF0, 0x7C, 0xF0,
0x3F, 0xC0, 0xFF, 0x07, 0xFE, 0x1E, 0x78, 0x79, 0xE1, 0xE7, 0x87, 0x9E,
0x1E, 0x78, 0x79, 0xE1, 0xC3, 0x87, 0x0F, 0x3C, 0x3C, 0xF0, 0xF3, 0xC3,
0xCF, 0x0F, 0x3C, 0x1C, 0xE0, 0x73, 0x81, 0xCE, 0x07, 0xB8, 0x1F, 0xE0,
0x7F, 0x80, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0,
0x07, 0x80, 0x1E, 0x00, 0xF8, 0x1F, 0xE0, 0x7F, 0x01, 0xFC, 0x00, 0x7F,
0xEF, 0xFD, 0xFF, 0xBF, 0xF0, 0x1E, 0x07, 0xC0, 0xF0, 0x1E, 0x07, 0xC0,
0xF0, 0x1E, 0x03, 0xC0, 0xF8, 0x1E, 0x03, 0xC0, 0xF8, 0x1E, 0x03, 0xC0,
0xF8, 0x1F, 0x03, 0xC0, 0x78, 0x1F, 0x03, 0xC0, 0x78, 0x1F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFE, 0x07, 0xC3, 0xF1, 0xFC, 0x7E, 0x1E, 0x07, 0x81, 0xE0,
0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0xF8, 0x7E,
0x3F, 0x0F, 0x83, 0xF0, 0xFE, 0x0F, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81,
0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0xC1, 0xFC, 0x3F,
0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x3F, 0x0F,
0xC1, 0xF8, 0x3E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78,
0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1F, 0x83, 0xF0, 0x7C, 0x3F, 0x1F, 0xC7,
0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0,
0x78, 0x3E, 0x0F, 0x8F, 0xC3, 0xF0, 0xF8, 0x00, 0x1E, 0x07, 0x9F, 0xF3,
0xDF, 0xFF, 0xDF, 0xFF, 0xEF, 0xFF, 0xE1, 0x03, 0xE0 };
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x66, 0x66,
0x66, 0x66, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7B, 0xDE, 0xF7,
0x98, 0xC6, 0x00, 0x03, 0x8F, 0x01, 0xC7, 0x80, 0xE3, 0x80, 0xF1, 0xC0,
0x70, 0xE0, 0x38, 0x70, 0x1C, 0x78, 0x0E, 0x3C, 0x07, 0x1C, 0x07, 0x8E,
0x03, 0x87, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x38, 0xE0, 0x3C,
0x70, 0x1E, 0x38, 0x0E, 0x1C, 0x07, 0x0E, 0x03, 0x8F, 0x0F, 0xFF, 0xF7,
0xFF, 0xF8, 0xF1, 0xC0, 0x70, 0xE0, 0x38, 0x70, 0x1C, 0x78, 0x0E, 0x3C,
0x0F, 0x1C, 0x07, 0x8E, 0x03, 0x87, 0x01, 0xC3, 0x80, 0xE3, 0xC0, 0x71,
0xE0, 0x78, 0xE0, 0x00, 0x06, 0x00, 0x30, 0x01, 0x80, 0x0C, 0x00, 0x60,
0x1F, 0xC1, 0xFF, 0x9F, 0xFC, 0xFB, 0xF7, 0x87, 0xBC, 0x3D, 0xE1, 0xEF,
0x0F, 0x78, 0x7B, 0xE3, 0xDF, 0x00, 0x7C, 0x01, 0xF8, 0x0F, 0xE0, 0x3F,
0x80, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x3E, 0xF0, 0xF7, 0x87, 0xFC, 0x3F,
0xE1, 0xFF, 0x0F, 0xFC, 0x7B, 0xFF, 0xCF, 0xFE, 0x3F, 0xE0, 0xFE, 0x01,
0xC0, 0x0E, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0x1F, 0x80, 0x01, 0xC0,
0x07, 0xFE, 0x00, 0x3C, 0x00, 0x7F, 0xE0, 0x03, 0xC0, 0x0F, 0x9F, 0x00,
0x38, 0x00, 0xF0, 0xF0, 0x07, 0x80, 0x0F, 0x0F, 0x00, 0x70, 0x00, 0xF0,
0xF0, 0x0F, 0x00, 0x0F, 0x0F, 0x00, 0xF0, 0x00, 0xF0, 0xF0, 0x0E, 0x00,
0x0F, 0x0F, 0x01, 0xE0, 0x00, 0xF0, 0xF0, 0x1C, 0x00, 0x0F, 0x0F, 0x03,
0xC0, 0x60, 0xF0, 0xF0, 0x3C, 0x3F, 0xCF, 0x0F, 0x03, 0x87, 0xFE, 0xF0,
0xF0, 0x78, 0xFF, 0xEF, 0x0F, 0x07, 0x8F, 0x0E, 0xF0, 0xF0, 0xF0, 0xF0,
0xFF, 0x0F, 0x0F, 0x0E, 0x0F, 0xF0, 0xF0, 0xE0, 0xE0, 0xF7, 0xDF, 0x1E,
0x0E, 0x0F, 0x7F, 0xE1, 0xE0, 0xE0, 0xF3, 0xFC, 0x1C, 0x0E, 0x0F, 0x1F,
0x83, 0xC0, 0xE0, 0xF0, 0x00, 0x38, 0x0E, 0x0F, 0x00, 0x07, 0x80, 0xE0,
0xF0, 0x00, 0x78, 0x0E, 0x0F, 0x00, 0x07, 0x00, 0xE0, 0xF0, 0x00, 0xF0,
0x0E, 0x0F, 0x00, 0x0E, 0x00, 0xE0, 0xF0, 0x01, 0xE0, 0x0F, 0x0F, 0x00,
0x1E, 0x00, 0xF1, 0xE0, 0x01, 0xC0, 0x0F, 0xFE, 0x00, 0x3C, 0x00, 0x7F,
0xC0, 0x03, 0x80, 0x03, 0xF8, 0x03, 0xE0, 0x03, 0xFE, 0x00, 0xFF, 0x80,
0x7C, 0xF0, 0x1E, 0x1C, 0x07, 0x87, 0x01, 0xE1, 0xC0, 0x78, 0x70, 0x1E,
0x1C, 0x07, 0x8F, 0x00, 0xF3, 0x80, 0x3D, 0xE0, 0x0F, 0x78, 0x01, 0xFC,
0x00, 0x7E, 0x00, 0x1F, 0x80, 0x07, 0xC0, 0x03, 0xF0, 0x00, 0xFE, 0x08,
0x7F, 0x86, 0x3F, 0xE3, 0x8F, 0x3C, 0xF7, 0x8F, 0x79, 0xE1, 0xFC, 0x78,
0x7F, 0x3C, 0x1F, 0x8F, 0x03, 0xE3, 0xC0, 0xF0, 0xF0, 0x3E, 0x1E, 0x1F,
0x87, 0xFF, 0xF1, 0xFF, 0xFC, 0x3F, 0xE7, 0x87, 0xF1, 0xE0, 0xFF, 0xFF,
0xF7, 0x76, 0x66, 0x66, 0x3E, 0xFF, 0xFF, 0xCF, 0x1E, 0x3C, 0x78, 0xF1,
0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C,
0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xFB, 0xF7,
0xE1, 0xC0, 0xF1, 0xF3, 0xF1, 0xE3, 0xC7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9,
0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E,
0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7B, 0xF7, 0xEF, 0x98, 0x00,
0x07, 0x00, 0x1C, 0x04, 0x71, 0x39, 0xCE, 0xFB, 0x7D, 0xFF, 0xC1, 0xFC,
0x03, 0xE0, 0x3F, 0xE3, 0xEF, 0xFF, 0x73, 0x99, 0xC6, 0x07, 0x08, 0x1C,
0x00, 0x70, 0x00, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0xFF, 0xFF, 0xFF,
0xFF, 0xF0, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0xFF, 0xFF, 0xFF,
0x8C, 0xE7, 0x31, 0x9C, 0xC0, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0x00,
0xF8, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0E, 0x00, 0xF0, 0x07, 0x80, 0x3C,
0x01, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x38, 0x03, 0xC0, 0x1E, 0x00,
0xF0, 0x07, 0x80, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x0F, 0x00, 0x78,
0x03, 0xC0, 0x1E, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x3C, 0x01,
0xE0, 0x0F, 0x00, 0x78, 0x07, 0x80, 0x00, 0x0F, 0xC1, 0xFF, 0x9F, 0xFC,
0xFF, 0xF7, 0x8F, 0xFC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F, 0xC3, 0xFE,
0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F, 0xC3,
0xFE, 0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F,
0xC3, 0xFE, 0x1F, 0xF0, 0xFF, 0x87, 0xBC, 0x7D, 0xF7, 0xEF, 0xFE, 0x3F,
0xF0, 0xFF, 0x00, 0x03, 0x83, 0xC3, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F,
0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC3,
0xE1, 0xF0, 0xF8, 0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC3, 0xE1, 0xF0, 0xF8,
0x7C, 0x3E, 0x1F, 0x0F, 0x87, 0xC0, 0x0F, 0x81, 0xFF, 0x1F, 0xF8, 0xFF,
0xEF, 0x8F, 0x7C, 0x7B, 0xC3, 0xDE, 0x1E, 0xF0, 0xFF, 0x87, 0xFC, 0x3F,
0xE1, 0xEF, 0x0F, 0x78, 0x78, 0x07, 0xC0, 0x3C, 0x03, 0xE0, 0x1E, 0x01,
0xF0, 0x0F, 0x00, 0xF8, 0x07, 0x80, 0x7C, 0x07, 0xC0, 0x3E, 0x03, 0xE0,
0x1F, 0x00, 0xF0, 0x07, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0xFE, 0xFF, 0xF7,
0xFF, 0x80, 0x0F, 0xC0, 0xFF, 0x87, 0xFF, 0x1F, 0xFE, 0x78, 0xFB, 0xE1,
0xEF, 0x87, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xE0, 0x07, 0x80, 0x1E, 0x00,
0x78, 0x03, 0xE0, 0x3F, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0xC0, 0x0F, 0x00,
0x3E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0xBC, 0x1E, 0xF0, 0x7F, 0xC1, 0xFF,
0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xE1, 0xE7, 0xDF, 0x9F, 0xFC, 0x3F, 0xE0,
0x7F, 0x00, 0x01, 0xF0, 0x07, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0, 0x1F,
0xC0, 0x7F, 0x01, 0xFC, 0x06, 0xF0, 0x3B, 0xC0, 0xEF, 0x03, 0xBC, 0x1C,
0xF0, 0x73, 0xC1, 0xCF, 0x0F, 0x3C, 0x38, 0xF0, 0xE3, 0xC7, 0x8F, 0x1C,
0x3C, 0x70, 0xF1, 0xC3, 0xCF, 0x0F, 0x38, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0,
0x03, 0xC0, 0xFF, 0xEF, 0xFE, 0xFF, 0xEF, 0xFE, 0xF0, 0x0F, 0x00, 0xF0,
0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF3, 0x0F, 0xFC, 0xFF, 0xEF, 0xFE, 0xF9,
0xEF, 0x1F, 0xF0, 0xF1, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00,
0xF0, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x1F, 0xFB,
0xEF, 0xFE, 0x7F, 0xC3, 0xF8, 0x0F, 0xC0, 0xFF, 0x83, 0xFF, 0x1F, 0xFC,
0x78, 0xF9, 0xE1, 0xEF, 0x87, 0xBE, 0x1E, 0xF8, 0x7B, 0xE1, 0xEF, 0x80,
0x3E, 0x00, 0xF8, 0x03, 0xEF, 0x8F, 0xFF, 0x3F, 0xFE, 0xFF, 0xFB, 0xE1,
0xEF, 0x87, 0xBE, 0x1E, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8,
0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x79, 0xE1, 0xE7, 0xFF, 0x9F,
0xFE, 0x3F, 0xF0, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x7C, 0x01, 0xE0, 0x0F, 0x80, 0x3C, 0x00, 0xF0, 0x07, 0xC0, 0x1E,
0x00, 0x78, 0x03, 0xE0, 0x0F, 0x80, 0x3C, 0x00, 0xF0, 0x07, 0xC0, 0x1E,
0x00, 0x78, 0x03, 0xE0, 0x0F, 0x80, 0x3C, 0x00, 0xF0, 0x07, 0xC0, 0x1F,
0x00, 0x7C, 0x01, 0xF0, 0x07, 0x80, 0x1E, 0x00, 0xF8, 0x03, 0xE0, 0x0F,
0x80, 0x3E, 0x00, 0xF8, 0x00, 0x1F, 0xC1, 0xFF, 0x1F, 0xFC, 0xFB, 0xFF,
0x8F, 0xFC, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0,
0xFF, 0x87, 0xBC, 0x79, 0xFF, 0xC7, 0xFC, 0x3F, 0xE3, 0xFF, 0x9E, 0x3D,
0xF1, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
0x1F, 0xE0, 0xFF, 0x07, 0xFC, 0x3F, 0xF7, 0xEF, 0xFE, 0x3F, 0xF0, 0xFE,
0x00, 0x1F, 0x81, 0xFF, 0x1F, 0xFD, 0xFF, 0xEF, 0x8F, 0x78, 0x7F, 0xC3,
0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F,
0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0xC7, 0xDF, 0xFE, 0xFF, 0xF3, 0xFF, 0x80,
0x3C, 0x01, 0xE0, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x0F,
0xFC, 0x79, 0xF7, 0xCF, 0xFE, 0x3F, 0xE0, 0xFE, 0x00, 0xFF, 0xFF, 0xF0,
0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF3, 0x9C, 0xCE, 0x73, 0x98,
0x00, 0x20, 0x0C, 0x07, 0x81, 0xF0, 0xFE, 0x3F, 0x9F, 0xC7, 0xF0, 0xF8,
0x1C, 0x03, 0xE0, 0x7E, 0x07, 0xF0, 0x3F, 0x03, 0xF8, 0x1F, 0x01, 0xE0,
0x0C, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x0F, 0xFF,
0xFF, 0xFF, 0xC0, 0x80, 0x18, 0x03, 0x80, 0x7C, 0x0F, 0xC0, 0xFE, 0x07,
0xF0, 0x7F, 0x03, 0xE0, 0x3C, 0x0F, 0x83, 0xF1, 0xFC, 0xFE, 0x3F, 0x87,
0xC0, 0xF0, 0x18, 0x02, 0x00, 0x00, 0x1F, 0x87, 0xFC, 0x7F, 0xEF, 0xFE,
0xF1, 0xEF, 0x1F, 0xF1, 0xFF, 0x1F, 0xF1, 0xFF, 0x1F, 0xF1, 0xFF, 0x1F,
0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x3E, 0x03, 0xE0, 0xFC, 0x3F, 0x83, 0xF0,
0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x00, 0x3F, 0x80,
0x00, 0xFF, 0xF0, 0x00, 0xFF, 0xFC, 0x00, 0xF8, 0x0F, 0x00, 0xF0, 0x03,
0xC0, 0xF0, 0x00, 0xF0, 0xF0, 0x00, 0x38, 0x78, 0x00, 0x1C, 0x38, 0x1F,
0x87, 0x3C, 0x3F, 0xE3, 0x9E, 0x1E, 0xF1, 0xCE, 0x1C, 0x38, 0xE7, 0x0E,
0x1C, 0x77, 0x8E, 0x0E, 0x1F, 0xC7, 0x07, 0x0F, 0xE3, 0x83, 0x87, 0xF1,
0xC1, 0xC3, 0xF8, 0xE0, 0xE1, 0xFC, 0x70, 0x70, 0xFE, 0x38, 0x38, 0x7F,
0x1C, 0x1C, 0x77, 0x8E, 0x0E, 0x39, 0xC7, 0x07, 0x1C, 0xE3, 0xC7, 0xCE,
0x70, 0xF6, 0xFE, 0x3C, 0x7F, 0x3F, 0x1E, 0x1F, 0x0F, 0x07, 0x81, 0x00,
0x03, 0xC0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1F, 0x80,
0x70, 0x07, 0xFF, 0xF8, 0x00, 0xFF, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0x07,
0xC0, 0x07, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F, 0xE0, 0x0F,
0xE0, 0x0F, 0xF0, 0x0E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E,
0xF0, 0x1E, 0x70, 0x1E, 0x78, 0x1C, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C,
0x78, 0x3C, 0x78, 0x3C, 0x3C, 0x3C, 0x3C, 0x7C, 0x3C, 0x78, 0x3C, 0x7F,
0xFC, 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFE, 0x78, 0x1E, 0xF8, 0x1E, 0xF8,
0x1E, 0xF0, 0x1E, 0xF0, 0x1F, 0xF0, 0x1F, 0xFF, 0x03, 0xFF, 0x8F, 0xFF,
0x3F, 0xFC, 0xF0, 0xFB, 0xC3, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1,
0xEF, 0x07, 0xBC, 0x3E, 0xF0, 0xFB, 0xC3, 0xCF, 0xFF, 0x3F, 0xF8, 0xFF,
0xF3, 0xFF, 0xCF, 0x0F, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xFC,
0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC3, 0xEF,
0xFF, 0xBF, 0xFC, 0xFF, 0xE3, 0xFF, 0x00, 0x0F, 0xC0, 0x7F, 0xC3, 0xFF,
0x9F, 0xFE, 0x7C, 0x79, 0xE1, 0xFF, 0x87, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0,
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8,
0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x1F, 0x78, 0x7D, 0xE1, 0xE7,
0xEF, 0x8F, 0xFE, 0x3F, 0xF0, 0x3F, 0x80, 0xFF, 0x07, 0xFF, 0x3F, 0xFD,
0xFF, 0xEF, 0x0F, 0xF8, 0x7F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83,
0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F,
0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x7F, 0xFF, 0xDF, 0xFE, 0xFF,
0xE7, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x0F, 0x03,
0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F,
0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03,
0xC0, 0xF0, 0x3C, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0xF0,
0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F,
0x03, 0xC0, 0xF0, 0x3C, 0x00, 0x0F, 0xE0, 0x7F, 0xC3, 0xFF, 0x9F, 0xFE,
0x7C, 0x7D, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83,
0xFE, 0x0F, 0xF8, 0x03, 0xE0, 0x0F, 0x80, 0x3E, 0x00, 0xF9, 0xFF, 0xE7,
0xFF, 0x9F, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0x78, 0x3D, 0xE1, 0xF7, 0xEF, 0xCF,
0xFF, 0x3F, 0xEC, 0x3F, 0x30, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F,
0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07,
0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0,
0x7F, 0xC1, 0xFF, 0x07, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xFC,
0x1F, 0xF0, 0x7F, 0xC1, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF8,
0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00,
0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F,
0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0xF8, 0x7F, 0xC3, 0xFE,
0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xEF, 0x0F, 0x78, 0x7B, 0xF7,
0xCF, 0xFC, 0x7F, 0xE1, 0xFE, 0x00, 0xF0, 0x3D, 0xE0, 0xFB, 0xC1, 0xE7,
0x83, 0xCF, 0x0F, 0x9E, 0x1E, 0x3C, 0x3C, 0x78, 0xF8, 0xF1, 0xE1, 0xE7,
0xC3, 0xCF, 0x87, 0x9E, 0x0F, 0x7C, 0x1E, 0xF8, 0x3D, 0xE0, 0x7F, 0xC0,
0xFF, 0x01, 0xFE, 0x03, 0xFE, 0x07, 0xBC, 0x0F, 0x7C, 0x1E, 0xF8, 0x3C,
0xF0, 0x79, 0xF0, 0xF1, 0xE1, 0xE3, 0xC3, 0xC7, 0xC7, 0x87, 0x8F, 0x0F,
0x9E, 0x1F, 0x3C, 0x1E, 0x78, 0x3E, 0xF0, 0x7D, 0xE0, 0x7C, 0xF0, 0x3C,
0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03,
0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0,
0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F,
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x01, 0xFF, 0x80, 0x1F, 0xF8, 0x03,
0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xC0,
0x7F, 0xFE, 0x07, 0xFF, 0xE0, 0x7F, 0xFE, 0x07, 0xFE, 0xE0, 0x7F, 0xEE,
0x0F, 0xFE, 0xF0, 0xFF, 0xEF, 0x0E, 0xFE, 0x70, 0xEF, 0xE7, 0x0E, 0xFE,
0x70, 0xEF, 0xE7, 0x1E, 0xFE, 0x79, 0xCF, 0xE3, 0x9C, 0xFF, 0x39, 0xCF,
0xF3, 0x9C, 0xFF, 0x3B, 0xCF, 0xF3, 0xF8, 0xFF, 0x1F, 0x8F, 0xF1, 0xF8,
0xFF, 0x1F, 0x8F, 0xF1, 0xF8, 0xFF, 0x1F, 0x8F, 0xF0, 0xF0, 0xFF, 0x0F,
0x0F, 0xF0, 0xF0, 0xFF, 0x0F, 0x0F, 0xE0, 0x3F, 0x80, 0xFF, 0x03, 0xFC,
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFF, 0x0F, 0xFC, 0x3F, 0xF0, 0xFF,
0xE3, 0xFF, 0x8F, 0xFE, 0x3F, 0xFC, 0xFF, 0xF3, 0xFD, 0xCF, 0xF7, 0xBF,
0xDE, 0xFF, 0x3B, 0xFC, 0xFF, 0xF3, 0xDF, 0xC7, 0x7F, 0x1F, 0xFC, 0x7F,
0xF0, 0xFF, 0xC3, 0xFF, 0x0F, 0xFC, 0x1F, 0xF0, 0x7F, 0xC1, 0xFF, 0x03,
0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0x70, 0x0F, 0xE0, 0x7F, 0xC3, 0xFF, 0x9F,
0xFE, 0x7C, 0x7D, 0xE1, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F,
0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F,
0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0x78, 0x7D, 0xE1, 0xF7, 0xFF,
0x8F, 0xFE, 0x3F, 0xF0, 0x3F, 0x80, 0xFF, 0x87, 0xFF, 0x3F, 0xFD, 0xFF,
0xEF, 0x0F, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x1F, 0xF0, 0xFF, 0xFF, 0xBF,
0xFD, 0xFF, 0xCF, 0xF8, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80,
0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07,
0x80, 0x00, 0x0F, 0xE0, 0x7F, 0xC3, 0xFF, 0x9F, 0xFE, 0x7C, 0x7D, 0xE1,
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
0x83, 0xFE, 0x0F, 0x78, 0x7D, 0xE1, 0xF7, 0xFF, 0x8F, 0xFE, 0x3F, 0xF0,
0x3F, 0x80, 0x1E, 0x00, 0x3C, 0x00, 0x78, 0x01, 0xE0, 0x03, 0x00, 0xFF,
0x83, 0xFF, 0x8F, 0xFF, 0x3F, 0xFE, 0xF0, 0xFB, 0xC1, 0xEF, 0x07, 0xBC,
0x1E, 0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC3, 0xEF,
0x1F, 0x3F, 0xFC, 0xFF, 0xE3, 0xFF, 0xCF, 0x0F, 0xBC, 0x1E, 0xF0, 0x7B,
0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x1E,
0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xF0, 0x0F,
0xC0, 0xFF, 0xC3, 0xFF, 0x1F, 0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE,
0x1F, 0xF8, 0x7D, 0xE1, 0xF7, 0x87, 0xDF, 0x1F, 0x7E, 0x00, 0xFC, 0x01,
0xF8, 0x03, 0xF0, 0x0F, 0xE0, 0x1F, 0x80, 0x3F, 0x00, 0x7E, 0x00, 0xF9,
0xE1, 0xF7, 0x87, 0xDE, 0x0F, 0x78, 0x3D, 0xE0, 0xF7, 0x83, 0xDE, 0x0F,
0x78, 0x3D, 0xE1, 0xF7, 0xEF, 0x8F, 0xFE, 0x3F, 0xF0, 0x3F, 0x80, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xF8, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0,
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
0x83, 0xFE, 0x0F, 0x78, 0x3D, 0xE1, 0xF7, 0xEF, 0xCF, 0xFE, 0x3F, 0xF8,
0x7F, 0x80, 0xF0, 0x1F, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF8, 0x1E,
0x78, 0x1E, 0x78, 0x1E, 0x78, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x7C, 0x3C,
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x78, 0x3C, 0x78, 0x3C, 0x78,
0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x70, 0x1E, 0xF0,
0x0E, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xE0,
0x0F, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0xF0, 0x3C,
0x0F, 0xF0, 0x3C, 0x0F, 0xF0, 0x3C, 0x0F, 0xF0, 0x3C, 0x1E, 0xF0, 0x3C,
0x1E, 0x78, 0x3E, 0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E,
0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E, 0x1E, 0x78, 0x7E, 0x1E, 0x38, 0x7F,
0x1C, 0x38, 0xF7, 0x1C, 0x3C, 0xF7, 0x3C, 0x3C, 0xE7, 0x3C, 0x3C, 0xE7,
0x3C, 0x3C, 0xE7, 0x3C, 0x3C, 0xE7, 0x3C, 0x3C, 0xE7, 0xBC, 0x1D, 0xE3,
0xB8, 0x1D, 0xC3, 0xB8, 0x1D, 0xC3, 0xB8, 0x1F, 0xC3, 0xB8, 0x1F, 0xC3,
0xB8, 0x1F, 0xC3, 0xF8, 0x1F, 0xC3, 0xF8, 0x1F, 0xC1, 0xF8, 0x0F, 0x81,
0xF8, 0x0F, 0x81, 0xF0, 0x0F, 0x81, 0xF0, 0x0F, 0x81, 0xF0, 0x0F, 0x81,
0xF0, 0x0F, 0x81, 0xF0, 0xF0, 0x3F, 0xC0, 0xF7, 0x07, 0x9E, 0x1E, 0x78,
0x78, 0xE3, 0xE3, 0xCF, 0x0F, 0x3C, 0x3C, 0xF0, 0x7F, 0x81, 0xFE, 0x07,
0xF8, 0x0F, 0xE0, 0x3F, 0x00, 0xFC, 0x01, 0xF0, 0x07, 0x80, 0x3E, 0x00,
0xF8, 0x03, 0xF0, 0x0F, 0xC0, 0x7F, 0x01, 0xFE, 0x07, 0xF8, 0x3F, 0xE0,
0xF3, 0xC3, 0xCF, 0x0F, 0x3C, 0x78, 0x71, 0xE1, 0xE7, 0x87, 0x9E, 0x0E,
0xF0, 0x3F, 0xC0, 0xF0, 0xF8, 0x0F, 0x78, 0x1F, 0x78, 0x1E, 0x7C, 0x1E,
0x3C, 0x1E, 0x3C, 0x3E, 0x3C, 0x3C, 0x3E, 0x3C, 0x1E, 0x3C, 0x1E, 0x78,
0x1E, 0x78, 0x0F, 0x78, 0x0F, 0x78, 0x0F, 0xF0, 0x0F, 0xF0, 0x07, 0xF0,
0x07, 0xF0, 0x07, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0,
0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0,
0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0, 0x03, 0xE0,
0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x3E,
0x03, 0xE0, 0x3C, 0x07, 0xC0, 0x7C, 0x07, 0xC0, 0x78, 0x0F, 0x80, 0xF8,
0x0F, 0x80, 0xF0, 0x1F, 0x01, 0xF0, 0x1E, 0x01, 0xE0, 0x3E, 0x03, 0xE0,
0x3C, 0x07, 0xC0, 0x7C, 0x07, 0xC0, 0x78, 0x0F, 0x80, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF,
0xFF, 0xFF, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x07, 0x00, 0xF0, 0x1E, 0x03,
0xC0, 0x78, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x3C, 0x07, 0x80, 0xF0,
0x1E, 0x01, 0xC0, 0x3C, 0x07, 0x80, 0xF0, 0x0E, 0x01, 0xE0, 0x3C, 0x07,
0x80, 0xF0, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0x78, 0x0F, 0x01, 0xE0,
0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF,
0x0F, 0xC0, 0x1F, 0x80, 0x3F, 0x80, 0x7F, 0x01, 0xFE, 0x03, 0xDC, 0x07,
0xBC, 0x0E, 0x78, 0x3C, 0xF0, 0x79, 0xE0, 0xF1, 0xE1, 0xE3, 0xC7, 0x87,
0x8F, 0x0F, 0x1E, 0x0F, 0x3C, 0x1E, 0xF8, 0x3D, 0xE0, 0x7C, 0xFF, 0xFF,
0xFF, 0xFF, 0xF0, 0xF9, 0xE7, 0x8E, 0x38, 0x71, 0xC3, 0x0F, 0xC0, 0xFF,
0xC3, 0xFF, 0x1F, 0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE, 0x1E, 0xF8,
0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x7F, 0x0F, 0xFC, 0x7F, 0xF3, 0xE7, 0xDE,
0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF,
0x87, 0xFE, 0x1F, 0xF8, 0x7D, 0xE3, 0xF7, 0xFF, 0xDF, 0xFF, 0x3F, 0x7C,
0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x79, 0xE3, 0xDF, 0x9F,
0xFE, 0xFF, 0xFF, 0xC7, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1,
0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F,
0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xFC,
0x7F, 0xF7, 0xFF, 0xFE, 0xF7, 0xF7, 0x9F, 0x00, 0x0F, 0xC1, 0xFF, 0x8F,
0xFC, 0xFF, 0xF7, 0x87, 0xBC, 0x3F, 0xE1, 0xFF, 0x0F, 0xF8, 0x7F, 0xC3,
0xFE, 0x1F, 0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07,
0xC0, 0x3E, 0x1F, 0xF0, 0xFF, 0x87, 0xFC, 0x3F, 0xE1, 0xEF, 0x0F, 0x78,
0x7B, 0xE7, 0xDF, 0xFC, 0x7F, 0xE1, 0xFE, 0x00, 0x00, 0x7C, 0x01, 0xF0,
0x07, 0xC0, 0x1F, 0x00, 0x7C, 0x7D, 0xF3, 0xFF, 0xDF, 0xFF, 0x7F, 0xFD,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F,
0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87,
0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7D, 0xE1,
0xF7, 0xDF, 0xDF, 0xFF, 0x3F, 0xFC, 0x7D, 0xF0, 0x0F, 0xC0, 0x7F, 0xC3,
0xFF, 0x1F, 0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE, 0x1E, 0xF8, 0x7B,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00,
0xF8, 0x03, 0xE0, 0x0F, 0x87, 0xBE, 0x1E, 0xF8, 0x7B, 0xE1, 0xEF, 0x87,
0x9E, 0x1E, 0x78, 0x79, 0xF3, 0xE7, 0xFF, 0x0F, 0xFC, 0x1F, 0xE0, 0x07,
0xC3, 0xF1, 0xFC, 0x7C, 0x1E, 0x07, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1,
0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78,
0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07,
0x81, 0xE0, 0x78, 0x1E, 0x07, 0x80, 0x1F, 0x7C, 0xFF, 0xF7, 0xFF, 0xDF,
0xFF, 0x78, 0x7D, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF,
0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7D, 0xE1, 0xF7, 0xCF, 0xDF, 0xFF,
0x3F, 0xFC, 0x7D, 0xF0, 0x07, 0xC0, 0x1F, 0x00, 0x79, 0xC3, 0xE7, 0xFF,
0xBF, 0xFC, 0x3F, 0xC0, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00,
0x79, 0xF3, 0xDF, 0xDF, 0xFF, 0xFF, 0xFF, 0xC3, 0xFC, 0x1F, 0xE0, 0xFF,
0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0,
0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xC0,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x78, 0xF1, 0xE0, 0x00, 0x0F,
0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3,
0xE7, 0xCF, 0x9F, 0x3E, 0x7C, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0x3E, 0x7C,
0xF9, 0xFF, 0xDF, 0xBE, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00,
0xF0, 0x03, 0xC1, 0xFF, 0x07, 0xBC, 0x1E, 0xF0, 0xFB, 0xC3, 0xCF, 0x1F,
0x3C, 0x78, 0xF1, 0xE3, 0xCF, 0x8F, 0x3C, 0x3D, 0xF0, 0xF7, 0x83, 0xDE,
0x0F, 0xF8, 0x3F, 0xC0, 0xFF, 0x83, 0xDE, 0x0F, 0x78, 0x3D, 0xF0, 0xF3,
0xC3, 0xCF, 0x8F, 0x1E, 0x3C, 0x7C, 0xF0, 0xF3, 0xC3, 0xCF, 0x0F, 0xBC,
0x1E, 0xF0, 0x7F, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xE1,
0xF3, 0xDF, 0xCF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xFC, 0x3F,
0xC1, 0xE0, 0xFF, 0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1,
0xE0, 0xFF, 0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0,
0xFF, 0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0, 0xFF,
0x07, 0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0, 0xFF, 0x07,
0x83, 0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3F, 0xC1, 0xE0, 0xFF, 0x07, 0x83,
0xFC, 0x1E, 0x0F, 0xF0, 0x78, 0x3C, 0xF3, 0xE7, 0xBF, 0xBF, 0xFF, 0xFF,
0xFF, 0x87, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F,
0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83,
0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0x80, 0x0F, 0xC0, 0x7F, 0x83, 0xFF, 0x1F,
0xFE, 0x78, 0x79, 0xE1, 0xEF, 0x87, 0xBE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF,
0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F,
0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xDE, 0x1E,
0x78, 0x79, 0xF3, 0xE7, 0xFF, 0x0F, 0xFC, 0x1F, 0xE0, 0xF3, 0xC7, 0xBF,
0xBF, 0xFD, 0xFF, 0xFF, 0x8F, 0xF8, 0x7F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F,
0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0,
0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F,
0xF8, 0xFF, 0xEF, 0xFF, 0xFD, 0xFF, 0xEF, 0x7E, 0x78, 0x03, 0xC0, 0x1E,
0x00, 0xF0, 0x07, 0x80, 0x00, 0x1E, 0x7C, 0xFF, 0xF7, 0xFF, 0xDF, 0xFF,
0x78, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87,
0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1,
0xFF, 0x87, 0xFE, 0x1F, 0xF8, 0x7F, 0xE1, 0xFF, 0x87, 0xFE, 0x1F, 0x78,
0x7D, 0xF7, 0xF7, 0xFF, 0xCF, 0xFF, 0x1F, 0x7C, 0x01, 0xF0, 0x07, 0xC0,
0x1F, 0x00, 0x7C, 0x01, 0xF0, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xC7, 0x83,
0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0,
0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E,
0x0F, 0x00, 0x1F, 0x83, 0xFE, 0x1F, 0xF9, 0xFF, 0xCF, 0x0F, 0x78, 0x7B,
0xC3, 0xDE, 0x1E, 0xF0, 0xF7, 0xC7, 0xBE, 0x00, 0xF8, 0x07, 0xE0, 0x1F,
0x80, 0x7E, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0x80, 0x7D, 0xE1, 0xEF, 0x0F,
0xF8, 0x7F, 0xC3, 0xFE, 0x1F, 0xF0, 0xF7, 0xEF, 0x9F, 0xFC, 0xFF, 0xC1,
0xFC, 0x00, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x8F, 0xFF, 0xFF,
0xFF, 0xCF, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F,
0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0, 0xF8, 0x3E, 0x0F, 0x83, 0xE0,
0xF8, 0x3E, 0x0F, 0x83, 0xF0, 0xFF, 0x1F, 0xC3, 0xF0, 0xF8, 0x3F, 0xE0,
0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8,
0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE,
0x0F, 0xF8, 0x3F, 0xE0, 0xFF, 0x83, 0xFE, 0x0F, 0xF8, 0x3F, 0xE0, 0xFF,
0x83, 0xFE, 0x0F, 0x78, 0x7D, 0xF3, 0xF7, 0xFF, 0xCF, 0xEF, 0x1F, 0x3C,
0xF0, 0x7F, 0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0x78, 0x79, 0xE1, 0xE7, 0x87,
0x9E, 0x1C, 0x78, 0x71, 0xE3, 0xC7, 0x8F, 0x0E, 0x3C, 0x3C, 0xF0, 0xF3,
0xC3, 0xCE, 0x0F, 0x38, 0x3C, 0xE0, 0xF3, 0x81, 0xDE, 0x07, 0x78, 0x1F,
0xE0, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x1F, 0xC0, 0x3F, 0x00, 0xFC, 0x03,
0xF0, 0x0F, 0x80, 0xF0, 0x78, 0x7F, 0x87, 0xC3, 0xFC, 0x3E, 0x1F, 0xE1,
0xF0, 0xF7, 0x0F, 0x87, 0xB8, 0x7C, 0x39, 0xC3, 0xE1, 0xCE, 0x1F, 0x8E,
0x79, 0xDC, 0x73, 0xCE, 0xE7, 0x9E, 0x77, 0x3C, 0xF3, 0xB9, 0xE3, 0x9D,
0xCF, 0x1C, 0xE6, 0x70, 0xE7, 0x3B, 0x87, 0x71, 0xDC, 0x3B, 0x8E, 0xE1,
0xDC, 0x77, 0x0F, 0xE3, 0xB8, 0x7F, 0x1D, 0xC3, 0xF8, 0xFE, 0x0F, 0x83,
0xE0, 0x7C, 0x1F, 0x03, 0xE0, 0xF8, 0x1F, 0x07, 0xC0, 0xF8, 0x3E, 0x07,
0xC1, 0xF0, 0x3E, 0x0F, 0x81, 0xE0, 0x7C, 0x00, 0xF0, 0x7B, 0xC1, 0xE7,
0x87, 0x9E, 0x3C, 0x78, 0xF0, 0xF3, 0xC3, 0xCE, 0x0F, 0x78, 0x1F, 0xE0,
0x7F, 0x01, 0xFC, 0x03, 0xF0, 0x0F, 0x80, 0x3E, 0x00, 0xF8, 0x03, 0xE0,
0x0F, 0x80, 0x7F, 0x01, 0xFC, 0x07, 0xF0, 0x3D, 0xE0, 0xF7, 0x83, 0xDE,
0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xCF, 0x07, 0xBC, 0x1E, 0xF0, 0x7C, 0xF0,
0x3F, 0xC0, 0xFF, 0x07, 0xFE, 0x1E, 0x78, 0x79, 0xE1, 0xE7, 0x87, 0x9E,
0x1E, 0x78, 0x79, 0xE1, 0xC3, 0x87, 0x0F, 0x3C, 0x3C, 0xF0, 0xF3, 0xC3,
0xCF, 0x0F, 0x3C, 0x1C, 0xE0, 0x73, 0x81, 0xCE, 0x07, 0xB8, 0x1F, 0xE0,
0x7F, 0x80, 0xFC, 0x03, 0xF0, 0x0F, 0xC0, 0x3F, 0x00, 0xFC, 0x03, 0xF0,
0x07, 0x80, 0x1E, 0x00, 0xF8, 0x1F, 0xE0, 0x7F, 0x01, 0xFC, 0x00, 0x7F,
0xEF, 0xFD, 0xFF, 0xBF, 0xF0, 0x1E, 0x07, 0xC0, 0xF0, 0x1E, 0x07, 0xC0,
0xF0, 0x1E, 0x03, 0xC0, 0xF8, 0x1E, 0x03, 0xC0, 0xF8, 0x1E, 0x03, 0xC0,
0xF8, 0x1F, 0x03, 0xC0, 0x78, 0x1F, 0x03, 0xC0, 0x78, 0x1F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFE, 0x07, 0xC3, 0xF1, 0xFC, 0x7E, 0x1E, 0x07, 0x81, 0xE0,
0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0xF8, 0x7E,
0x3F, 0x0F, 0x83, 0xF0, 0xFE, 0x0F, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81,
0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0xC1, 0xFC, 0x3F,
0x07, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0x3F, 0x0F,
0xC1, 0xF8, 0x3E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78,
0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1F, 0x83, 0xF0, 0x7C, 0x3F, 0x1F, 0xC7,
0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x07, 0x81, 0xE0,
0x78, 0x3E, 0x0F, 0x8F, 0xC3, 0xF0, 0xF8, 0x00, 0x1E, 0x07, 0x9F, 0xF3,
0xDF, 0xFF, 0xDF, 0xFF, 0xEF, 0xFF, 0xE1, 0x03, 0xE0};
const GFXglyph Antonio_SemiBold20pt7bGlyphs[] PROGMEM = {
{ 0, 1, 1, 8, 0, 0 }, // 0x20 ' '
{ 1, 4, 34, 10, 3, -33 }, // 0x21 '!'
{ 18, 5, 13, 16, 6, -33 }, // 0x22 '"'
{ 27, 17, 34, 20, 2, -33 }, // 0x23 '#'
{ 100, 13, 39, 16, 2, -33 }, // 0x24 '$'
{ 164, 36, 34, 42, 3, -33 }, // 0x25 '%'
{ 317, 18, 34, 19, 1, -33 }, // 0x26 '&'
{ 394, 4, 12, 8, 2, -33 }, // 0x27 '''
{ 400, 7, 38, 11, 3, -33 }, // 0x28 '('
{ 434, 7, 38, 11, 2, -33 }, // 0x29 ')'
{ 468, 14, 15, 17, 2, -32 }, // 0x2A '*'
{ 495, 12, 12, 14, 1, -20 }, // 0x2B '+'
{ 513, 5, 12, 9, 2, -4 }, // 0x2C ','
{ 521, 9, 3, 13, 2, -16 }, // 0x2D '-'
{ 525, 4, 4, 10, 3, -3 }, // 0x2E '.'
{ 527, 13, 34, 15, 1, -33 }, // 0x2F '/'
{ 583, 13, 34, 18, 2, -33 }, // 0x30 '0'
{ 639, 9, 34, 15, 2, -33 }, // 0x31 '1'
{ 678, 13, 34, 17, 3, -33 }, // 0x32 '2'
{ 734, 14, 34, 17, 2, -33 }, // 0x33 '3'
{ 794, 14, 34, 17, 2, -33 }, // 0x34 '4'
{ 854, 12, 34, 17, 3, -33 }, // 0x35 '5'
{ 905, 14, 34, 17, 2, -33 }, // 0x36 '6'
{ 965, 14, 34, 17, 2, -33 }, // 0x37 '7'
{ 1025, 13, 34, 17, 2, -33 }, // 0x38 '8'
{ 1081, 13, 34, 17, 2, -33 }, // 0x39 '9'
{ 1137, 4, 20, 10, 3, -24 }, // 0x3A ':'
{ 1147, 5, 27, 11, 3, -24 }, // 0x3B ';'
{ 1164, 11, 19, 14, 1, -23 }, // 0x3C '<'
{ 1191, 10, 9, 14, 2, -19 }, // 0x3D '='
{ 1203, 11, 19, 14, 2, -23 }, // 0x3E '>'
{ 1230, 12, 34, 17, 3, -33 }, // 0x3F '?'
{ 1281, 25, 35, 29, 2, -33 }, // 0x40 '@'
{ 1391, 16, 34, 18, 1, -33 }, // 0x41 'A'
{ 1459, 14, 34, 18, 3, -33 }, // 0x42 'B'
{ 1519, 14, 34, 18, 2, -33 }, // 0x43 'C'
{ 1579, 13, 34, 19, 3, -33 }, // 0x44 'D'
{ 1635, 10, 34, 15, 3, -33 }, // 0x45 'E'
{ 1678, 10, 34, 15, 3, -33 }, // 0x46 'F'
{ 1721, 14, 34, 18, 2, -33 }, // 0x47 'G'
{ 1781, 14, 34, 19, 3, -33 }, // 0x48 'H'
{ 1841, 4, 34, 10, 3, -33 }, // 0x49 'I'
{ 1858, 13, 34, 17, 2, -33 }, // 0x4A 'J'
{ 1914, 15, 34, 18, 3, -33 }, // 0x4B 'K'
{ 1978, 10, 34, 14, 3, -33 }, // 0x4C 'L'
{ 2021, 20, 34, 26, 3, -33 }, // 0x4D 'M'
{ 2106, 14, 34, 20, 3, -33 }, // 0x4E 'N'
{ 2166, 14, 34, 19, 2, -33 }, // 0x4F 'O'
{ 2226, 13, 34, 17, 3, -33 }, // 0x50 'P'
{ 2282, 14, 39, 19, 2, -33 }, // 0x51 'Q'
{ 2351, 14, 34, 18, 3, -33 }, // 0x52 'R'
{ 2411, 14, 34, 16, 1, -33 }, // 0x53 'S'
{ 2471, 12, 34, 13, 0, -33 }, // 0x54 'T'
{ 2522, 14, 34, 19, 2, -33 }, // 0x55 'U'
{ 2582, 16, 34, 17, 1, -33 }, // 0x56 'V'
{ 2650, 24, 34, 26, 1, -33 }, // 0x57 'W'
{ 2752, 14, 34, 16, 1, -33 }, // 0x58 'X'
{ 2812, 16, 34, 16, 0, -33 }, // 0x59 'Y'
{ 2880, 12, 34, 14, 1, -33 }, // 0x5A 'Z'
{ 2931, 8, 35, 13, 3, -33 }, // 0x5B '['
{ 2966, 11, 34, 15, 2, -33 }, // 0x5C '\'
{ 3013, 8, 35, 13, 2, -33 }, // 0x5D ']'
{ 3048, 15, 18, 18, 2, -33 }, // 0x5E '^'
{ 3082, 12, 3, 14, 1, 2 }, // 0x5F '_'
{ 3087, 6, 8, 8, 1, -38 }, // 0x60 '`'
{ 3093, 14, 29, 18, 2, -28 }, // 0x61 'a'
{ 3144, 13, 34, 18, 3, -33 }, // 0x62 'b'
{ 3200, 13, 29, 17, 2, -28 }, // 0x63 'c'
{ 3248, 14, 34, 18, 2, -33 }, // 0x64 'd'
{ 3308, 14, 29, 17, 2, -28 }, // 0x65 'e'
{ 3359, 10, 34, 13, 1, -33 }, // 0x66 'f'
{ 3402, 14, 33, 18, 2, -28 }, // 0x67 'g'
{ 3460, 13, 34, 19, 3, -33 }, // 0x68 'h'
{ 3516, 4, 35, 10, 3, -34 }, // 0x69 'i'
{ 3534, 7, 39, 10, 1, -34 }, // 0x6A 'j'
{ 3569, 14, 34, 18, 3, -33 }, // 0x6B 'k'
{ 3629, 4, 34, 10, 3, -33 }, // 0x6C 'l'
{ 3646, 22, 29, 28, 3, -28 }, // 0x6D 'm'
{ 3726, 13, 29, 19, 3, -28 }, // 0x6E 'n'
{ 3774, 14, 29, 18, 2, -28 }, // 0x6F 'o'
{ 3825, 13, 34, 18, 3, -28 }, // 0x70 'p'
{ 3881, 14, 34, 18, 2, -28 }, // 0x71 'q'
{ 3941, 9, 29, 13, 3, -28 }, // 0x72 'r'
{ 3974, 13, 29, 16, 2, -28 }, // 0x73 's'
{ 4022, 10, 34, 12, 1, -33 }, // 0x74 't'
{ 4065, 14, 29, 19, 2, -28 }, // 0x75 'u'
{ 4116, 14, 29, 15, 1, -28 }, // 0x76 'v'
{ 4167, 21, 29, 23, 1, -28 }, // 0x77 'w'
{ 4244, 14, 29, 15, 1, -28 }, // 0x78 'x'
{ 4295, 14, 34, 16, 1, -28 }, // 0x79 'y'
{ 4355, 11, 29, 13, 1, -28 }, // 0x7A 'z'
{ 4395, 10, 37, 14, 2, -33 }, // 0x7B '{'
{ 4442, 4, 37, 10, 3, -34 }, // 0x7C '|'
{ 4461, 10, 37, 14, 2, -33 }, // 0x7D '}'
{ 4508, 17, 6, 21, 2, -21 } }; // 0x7E '~'
{0, 1, 1, 8, 0, 0}, // 0x20 ' '
{1, 4, 34, 10, 3, -33}, // 0x21 '!'
{18, 5, 13, 16, 6, -33}, // 0x22 '"'
{27, 17, 34, 20, 2, -33}, // 0x23 '#'
{100, 13, 39, 16, 2, -33}, // 0x24 '$'
{164, 36, 34, 42, 3, -33}, // 0x25 '%'
{317, 18, 34, 19, 1, -33}, // 0x26 '&'
{394, 4, 12, 8, 2, -33}, // 0x27 '''
{400, 7, 38, 11, 3, -33}, // 0x28 '('
{434, 7, 38, 11, 2, -33}, // 0x29 ')'
{468, 14, 15, 17, 2, -32}, // 0x2A '*'
{495, 12, 12, 14, 1, -20}, // 0x2B '+'
{513, 5, 12, 9, 2, -4}, // 0x2C ','
{521, 9, 3, 13, 2, -16}, // 0x2D '-'
{525, 4, 4, 10, 3, -3}, // 0x2E '.'
{527, 13, 34, 15, 1, -33}, // 0x2F '/'
{583, 13, 34, 18, 2, -33}, // 0x30 '0'
{639, 9, 34, 15, 2, -33}, // 0x31 '1'
{678, 13, 34, 17, 3, -33}, // 0x32 '2'
{734, 14, 34, 17, 2, -33}, // 0x33 '3'
{794, 14, 34, 17, 2, -33}, // 0x34 '4'
{854, 12, 34, 17, 3, -33}, // 0x35 '5'
{905, 14, 34, 17, 2, -33}, // 0x36 '6'
{965, 14, 34, 17, 2, -33}, // 0x37 '7'
{1025, 13, 34, 17, 2, -33}, // 0x38 '8'
{1081, 13, 34, 17, 2, -33}, // 0x39 '9'
{1137, 4, 20, 10, 3, -24}, // 0x3A ':'
{1147, 5, 27, 11, 3, -24}, // 0x3B ';'
{1164, 11, 19, 14, 1, -23}, // 0x3C '<'
{1191, 10, 9, 14, 2, -19}, // 0x3D '='
{1203, 11, 19, 14, 2, -23}, // 0x3E '>'
{1230, 12, 34, 17, 3, -33}, // 0x3F '?'
{1281, 25, 35, 29, 2, -33}, // 0x40 '@'
{1391, 16, 34, 18, 1, -33}, // 0x41 'A'
{1459, 14, 34, 18, 3, -33}, // 0x42 'B'
{1519, 14, 34, 18, 2, -33}, // 0x43 'C'
{1579, 13, 34, 19, 3, -33}, // 0x44 'D'
{1635, 10, 34, 15, 3, -33}, // 0x45 'E'
{1678, 10, 34, 15, 3, -33}, // 0x46 'F'
{1721, 14, 34, 18, 2, -33}, // 0x47 'G'
{1781, 14, 34, 19, 3, -33}, // 0x48 'H'
{1841, 4, 34, 10, 3, -33}, // 0x49 'I'
{1858, 13, 34, 17, 2, -33}, // 0x4A 'J'
{1914, 15, 34, 18, 3, -33}, // 0x4B 'K'
{1978, 10, 34, 14, 3, -33}, // 0x4C 'L'
{2021, 20, 34, 26, 3, -33}, // 0x4D 'M'
{2106, 14, 34, 20, 3, -33}, // 0x4E 'N'
{2166, 14, 34, 19, 2, -33}, // 0x4F 'O'
{2226, 13, 34, 17, 3, -33}, // 0x50 'P'
{2282, 14, 39, 19, 2, -33}, // 0x51 'Q'
{2351, 14, 34, 18, 3, -33}, // 0x52 'R'
{2411, 14, 34, 16, 1, -33}, // 0x53 'S'
{2471, 12, 34, 13, 0, -33}, // 0x54 'T'
{2522, 14, 34, 19, 2, -33}, // 0x55 'U'
{2582, 16, 34, 17, 1, -33}, // 0x56 'V'
{2650, 24, 34, 26, 1, -33}, // 0x57 'W'
{2752, 14, 34, 16, 1, -33}, // 0x58 'X'
{2812, 16, 34, 16, 0, -33}, // 0x59 'Y'
{2880, 12, 34, 14, 1, -33}, // 0x5A 'Z'
{2931, 8, 35, 13, 3, -33}, // 0x5B '['
{2966, 11, 34, 15, 2, -33}, // 0x5C '\'
{3013, 8, 35, 13, 2, -33}, // 0x5D ']'
{3048, 15, 18, 18, 2, -33}, // 0x5E '^'
{3082, 12, 3, 14, 1, 2}, // 0x5F '_'
{3087, 6, 8, 8, 1, -38}, // 0x60 '`'
{3093, 14, 29, 18, 2, -28}, // 0x61 'a'
{3144, 13, 34, 18, 3, -33}, // 0x62 'b'
{3200, 13, 29, 17, 2, -28}, // 0x63 'c'
{3248, 14, 34, 18, 2, -33}, // 0x64 'd'
{3308, 14, 29, 17, 2, -28}, // 0x65 'e'
{3359, 10, 34, 13, 1, -33}, // 0x66 'f'
{3402, 14, 33, 18, 2, -28}, // 0x67 'g'
{3460, 13, 34, 19, 3, -33}, // 0x68 'h'
{3516, 4, 35, 10, 3, -34}, // 0x69 'i'
{3534, 7, 39, 10, 1, -34}, // 0x6A 'j'
{3569, 14, 34, 18, 3, -33}, // 0x6B 'k'
{3629, 4, 34, 10, 3, -33}, // 0x6C 'l'
{3646, 22, 29, 28, 3, -28}, // 0x6D 'm'
{3726, 13, 29, 19, 3, -28}, // 0x6E 'n'
{3774, 14, 29, 18, 2, -28}, // 0x6F 'o'
{3825, 13, 34, 18, 3, -28}, // 0x70 'p'
{3881, 14, 34, 18, 2, -28}, // 0x71 'q'
{3941, 9, 29, 13, 3, -28}, // 0x72 'r'
{3974, 13, 29, 16, 2, -28}, // 0x73 's'
{4022, 10, 34, 12, 1, -33}, // 0x74 't'
{4065, 14, 29, 19, 2, -28}, // 0x75 'u'
{4116, 14, 29, 15, 1, -28}, // 0x76 'v'
{4167, 21, 29, 23, 1, -28}, // 0x77 'w'
{4244, 14, 29, 15, 1, -28}, // 0x78 'x'
{4295, 14, 34, 16, 1, -28}, // 0x79 'y'
{4355, 11, 29, 13, 1, -28}, // 0x7A 'z'
{4395, 10, 37, 14, 2, -33}, // 0x7B '{'
{4442, 4, 37, 10, 3, -34}, // 0x7C '|'
{4461, 10, 37, 14, 2, -33}, // 0x7D '}'
{4508, 17, 6, 21, 2, -21}}; // 0x7E '~'
const GFXfont Antonio_SemiBold20pt7b PROGMEM = {
(uint8_t *)Antonio_SemiBold20pt7bBitmaps,
(GFXglyph *)Antonio_SemiBold20pt7bGlyphs,
0x20, 0x7E, 51 };
(uint8_t *)Antonio_SemiBold20pt7bBitmaps,
(GFXglyph *)Antonio_SemiBold20pt7bGlyphs, 0x20, 0x7E, 51};
// Approx. 5193 bytes

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,4 +3,4 @@ dependencies:
idf: ">=4.4"
esp_littlefs:
git: https://github.com/joltwallet/esp_littlefs.git
git: https://github.com/joltwallet/esp_littlefs.git

View File

@ -48,152 +48,141 @@ uint currentBlockHeight = 816000;
// ew==
// -----END CERTIFICATE-----)";
void setupBlockNotify()
{
//currentBlockHeight = preferences.getUInt("blockHeight", 816000);
void setupBlockNotify() {
// currentBlockHeight = preferences.getUInt("blockHeight", 816000);
IPAddress result;
IPAddress result;
int dnsErr = -1;
String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
int dnsErr = -1;
String mempoolInstance =
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
while (dnsErr != 1)
{
dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result);
while (dnsErr != 1) {
dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result);
if (dnsErr != 1)
{
Serial.print(mempoolInstance);
Serial.println(F("mempool DNS could not be resolved"));
WiFi.reconnect();
vTaskDelay(pdMS_TO_TICKS(1000));
}
if (dnsErr != 1) {
Serial.print(mempoolInstance);
Serial.println(F("mempool DNS could not be resolved"));
WiFi.reconnect();
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// Get current block height through regular API
HTTPClient *http = new HTTPClient();
http->begin("https://" + mempoolInstance + "/api/blocks/tip/height");
int httpCode = http->GET();
if (httpCode > 0 && httpCode == HTTP_CODE_OK) {
String blockHeightStr = http->getString();
currentBlockHeight = blockHeightStr.toInt();
// xTaskNotifyGive(blockUpdateTaskHandle);
if (workQueue != nullptr) {
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
}
}
// std::strcpy(wsServer, String("wss://" + mempoolInstance +
// "/api/v1/ws").c_str());
esp_websocket_client_config_t config = {
.uri = "wss://mempool.space/api/v1/ws",
// .task_stack = (6*1024),
// .cert_pem = mempoolWsCert,
.user_agent = USER_AGENT,
};
blockNotifyClient = esp_websocket_client_init(&config);
esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY,
onWebsocketEvent, blockNotifyClient);
esp_websocket_client_start(blockNotifyClient);
}
void onWebsocketEvent(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data) {
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
const String sub = "{\"action\": \"want\", \"data\":[\"blocks\"]}";
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
Serial.println(F("Connected to Mempool.space WebSocket"));
Serial.println(sub);
if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(),
sub.length(), portMAX_DELAY) == -1) {
Serial.println(F("Mempool.space WS Block Subscribe Error"));
}
// Get current block height through regular API
HTTPClient *http = new HTTPClient();
http->begin("https://" + mempoolInstance + "/api/blocks/tip/height");
int httpCode = http->GET();
break;
case WEBSOCKET_EVENT_DATA:
onWebsocketMessage(data);
break;
case WEBSOCKET_EVENT_ERROR:
Serial.println(F("Mempool.space WS Connnection error"));
break;
case WEBSOCKET_EVENT_DISCONNECTED:
Serial.println(F("Mempool.space WS Connnection Closed"));
break;
}
}
if (httpCode > 0 && httpCode == HTTP_CODE_OK)
{
String blockHeightStr = http->getString();
currentBlockHeight = blockHeightStr.toInt();
// xTaskNotifyGive(blockUpdateTaskHandle);
if (workQueue != nullptr)
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
void onWebsocketMessage(esp_websocket_event_data_t *event_data) {
SpiRamJsonDocument doc(event_data->data_len);
deserializeJson(doc, (char *)event_data->data_ptr);
if (doc.containsKey("block")) {
JsonObject block = doc["block"];
currentBlockHeight = block["height"].as<uint>();
Serial.printf("New block found: %d\r\n", block["height"].as<uint>());
preferences.putUInt("blockHeight", currentBlockHeight);
if (workQueue != nullptr) {
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
// xTaskNotifyGive(blockUpdateTaskHandle);
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT &&
preferences.getBool("stealFocus", true)) {
uint64_t timerPeriod = 0;
if (isTimerActive()) {
// store timer periode before making inactive to prevent artifacts
timerPeriod = getTimerSeconds();
esp_timer_stop(screenRotateTimer);
}
}
// std::strcpy(wsServer, String("wss://" + mempoolInstance + "/api/v1/ws").c_str());
esp_websocket_client_config_t config = {
.uri = "wss://mempool.space/api/v1/ws",
// .task_stack = (6*1024),
// .cert_pem = mempoolWsCert,
.user_agent = USER_AGENT,
};
blockNotifyClient = esp_websocket_client_init(&config);
esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY, onWebsocketEvent, blockNotifyClient);
esp_websocket_client_start(blockNotifyClient);
}
void onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
const String sub = "{\"action\": \"want\", \"data\":[\"blocks\"]}";
switch (event_id)
{
case WEBSOCKET_EVENT_CONNECTED:
Serial.println(F("Connected to Mempool.space WebSocket"));
Serial.println(sub);
if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(), sub.length(), portMAX_DELAY) == -1)
{
Serial.println(F("Mempool.space WS Block Subscribe Error"));
setCurrentScreen(SCREEN_BLOCK_HEIGHT);
if (timerPeriod > 0) {
esp_timer_start_periodic(screenRotateTimer,
timerPeriod * usPerSecond);
}
}
break;
case WEBSOCKET_EVENT_DATA:
onWebsocketMessage(data);
break;
case WEBSOCKET_EVENT_ERROR:
Serial.println(F("Mempool.space WS Connnection error"));
break;
case WEBSOCKET_EVENT_DISCONNECTED:
Serial.println(F("Mempool.space WS Connnection Closed"));
break;
if (getCurrentScreen() == SCREEN_BLOCK_HEIGHT &&
preferences.getBool("ledFlashOnUpd", false)) {
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated
queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
}
}
}
doc.clear();
}
void onWebsocketMessage(esp_websocket_event_data_t *event_data)
{
SpiRamJsonDocument doc(event_data->data_len);
uint getBlockHeight() { return currentBlockHeight; }
deserializeJson(doc, (char *)event_data->data_ptr);
if (doc.containsKey("block"))
{
JsonObject block = doc["block"];
currentBlockHeight = block["height"].as<uint>();
Serial.printf("New block found: %d\r\n", block["height"].as<uint>());
preferences.putUInt("blockHeight", currentBlockHeight);
if (workQueue != nullptr)
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
// xTaskNotifyGive(blockUpdateTaskHandle);
if (getCurrentScreen() != SCREEN_BLOCK_HEIGHT && preferences.getBool("stealFocus", true))
{
uint64_t timerPeriod = 0;
if (isTimerActive()) {
// store timer periode before making inactive to prevent artifacts
timerPeriod = getTimerSeconds();
esp_timer_stop(screenRotateTimer);
}
setCurrentScreen(SCREEN_BLOCK_HEIGHT);
if (timerPeriod > 0) {
esp_timer_start_periodic(screenRotateTimer, timerPeriod * usPerSecond);
}
}
if (getCurrentScreen() == SCREEN_BLOCK_HEIGHT && preferences.getBool("ledFlashOnUpd", false))
{
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated
queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
}
}
}
doc.clear();
void setBlockHeight(uint newBlockHeight) {
currentBlockHeight = newBlockHeight;
}
uint getBlockHeight()
{
return currentBlockHeight;
bool isBlockNotifyConnected() {
if (blockNotifyClient == NULL)
return false;
return esp_websocket_client_is_connected(blockNotifyClient);
}
void setBlockHeight(uint newBlockHeight)
{
currentBlockHeight = newBlockHeight;
}
bool isBlockNotifyConnected()
{
if (blockNotifyClient == NULL)
return false;
return esp_websocket_client_is_connected(blockNotifyClient);
}
void stopBlockNotify()
{
esp_websocket_client_stop(blockNotifyClient);
esp_websocket_client_destroy(blockNotifyClient);
void stopBlockNotify() {
esp_websocket_client_stop(blockNotifyClient);
esp_websocket_client_destroy(blockNotifyClient);
}

View File

@ -1,22 +1,23 @@
#pragma once
#include <cstring>
#include <string>
#include "lib/led_handler.hpp"
#include "lib/screen_handler.hpp"
#include "lib/shared.hpp"
#include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "shared.hpp"
#include "esp_timer.h"
#include "esp_websocket_client.h"
#include "screen_handler.hpp"
#include "led_handler.hpp"
#include <HTTPClient.h>
#include <cstring>
#include <esp_timer.h>
#include <esp_websocket_client.h>
#include <string>
//using namespace websockets;
// using namespace websockets;
void setupBlockNotify();
void onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data);
void onWebsocketMessage(esp_websocket_event_data_t* event_data);
void onWebsocketEvent(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data);
void onWebsocketMessage(esp_websocket_event_data_t *event_data);
void setBlockHeight(uint newBlockHeight);
uint getBlockHeight();

View File

@ -4,64 +4,54 @@ TaskHandle_t buttonTaskHandle = NULL;
const TickType_t debounceDelay = pdMS_TO_TICKS(50);
TickType_t lastDebounceTime = 0;
void buttonTask(void *parameter)
{
while (1)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
std::lock_guard<std::mutex> lock(mcpMutex);
void buttonTask(void *parameter) {
while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
std::lock_guard<std::mutex> lock(mcpMutex);
TickType_t currentTime = xTaskGetTickCount();
if ((currentTime - lastDebounceTime) >= debounceDelay)
{
lastDebounceTime = currentTime;
TickType_t currentTime = xTaskGetTickCount();
if ((currentTime - lastDebounceTime) >= debounceDelay) {
lastDebounceTime = currentTime;
if (!digitalRead(MCP_INT_PIN))
{
uint pin = mcp1.getLastInterruptPin();
if (!digitalRead(MCP_INT_PIN)) {
uint pin = mcp1.getLastInterruptPin();
switch (pin)
{
case 3:
toggleTimerActive();
break;
case 2:
nextScreen();
break;
case 1:
previousScreen();
break;
case 0:
showSystemStatusScreen();
break;
}
}
mcp1.clearInterrupts();
}
else
{
}
// Very ugly, but for some reason this is necessary
while (!digitalRead(MCP_INT_PIN))
{
mcp1.clearInterrupts();
switch (pin) {
case 3:
toggleTimerActive();
break;
case 2:
nextScreen();
break;
case 1:
previousScreen();
break;
case 0:
showSystemStatusScreen();
break;
}
}
mcp1.clearInterrupts();
} else {
}
}
void IRAM_ATTR handleButtonInterrupt()
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(buttonTaskHandle, 0, eNoAction, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE)
{
portYIELD_FROM_ISR();
// Very ugly, but for some reason this is necessary
while (!digitalRead(MCP_INT_PIN)) {
mcp1.clearInterrupts();
}
}
}
void setupButtonTask()
{
xTaskCreate(buttonTask, "ButtonTask", 4096, NULL, tskIDLE_PRIORITY, &buttonTaskHandle); // Create the FreeRTOS task
// Use interrupt instead of task
attachInterrupt(MCP_INT_PIN, handleButtonInterrupt, CHANGE);
void IRAM_ATTR handleButtonInterrupt() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(buttonTaskHandle, 0, eNoAction, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
void setupButtonTask() {
xTaskCreate(buttonTask, "ButtonTask", 4096, NULL, tskIDLE_PRIORITY,
&buttonTaskHandle); // Create the FreeRTOS task
// Use interrupt instead of task
attachInterrupt(MCP_INT_PIN, handleButtonInterrupt, CHANGE);
}

View File

@ -1,8 +1,8 @@
#pragma once
#include "lib/screen_handler.hpp"
#include "lib/shared.hpp"
#include <Arduino.h>
#include "shared.hpp"
#include "screen_handler.hpp"
extern TaskHandle_t buttonTaskHandle;

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,23 @@
#pragma once;
#include <WiFiManager.h>
#include "base64.h"
#include <WiFiClientSecure.h>
#include <Preferences.h>
#include <Adafruit_MCP23X17.h>
#include <Arduino.h>
#include "shared.hpp"
#include "epd.hpp"
#include "improv.hpp"
#include "esp_task_wdt.h"
#include <Preferences.h>
#include <WiFiClientSecure.h>
#include <WiFiManager.h>
#include <base64.h>
#include <esp_task_wdt.h>
#include <map>
#include "ota.hpp"
#include "lib/screen_handler.hpp"
#include "lib/webserver.hpp"
#include "lib/block_notify.hpp"
#include "lib/price_notify.hpp"
#include "lib/button_handler.hpp"
#include "lib/epd.hpp"
#include "lib/improv.hpp"
#include "lib/led_handler.hpp"
#include "lib/ota.hpp"
#include "lib/price_notify.hpp"
#include "lib/screen_handler.hpp"
#include "lib/shared.hpp"
#include "lib/webserver.hpp"
#define NTP_SERVER "pool.ntp.org"
#define DEFAULT_MEMPOOL_INSTANCE "mempool.space"

View File

@ -16,21 +16,12 @@ Native_Pin EPD_CS[NUM_SCREENS] = {
#endif
};
Native_Pin EPD_BUSY[NUM_SCREENS] = {
Native_Pin(3),
Native_Pin(5),
Native_Pin(7),
Native_Pin(9),
Native_Pin(37),
Native_Pin(18),
Native_Pin(16),
Native_Pin(3), Native_Pin(5), Native_Pin(7), Native_Pin(9),
Native_Pin(37), Native_Pin(18), Native_Pin(16),
};
MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = {
MCP23X17_Pin(mcp1, 8),
MCP23X17_Pin(mcp1, 9),
MCP23X17_Pin(mcp1, 10),
MCP23X17_Pin(mcp1, 11),
MCP23X17_Pin(mcp1, 12),
MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 8), MCP23X17_Pin(mcp1, 9), MCP23X17_Pin(mcp1, 10),
MCP23X17_Pin(mcp1, 11), MCP23X17_Pin(mcp1, 12), MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 14),
};
@ -39,36 +30,20 @@ Native_Pin EPD_DC = Native_Pin(14);
Native_Pin EPD_DC = Native_Pin(38);
MCP23X17_Pin EPD_BUSY[NUM_SCREENS] = {
MCP23X17_Pin(mcp1, 8),
MCP23X17_Pin(mcp1, 9),
MCP23X17_Pin(mcp1, 10),
MCP23X17_Pin(mcp1, 11),
MCP23X17_Pin(mcp1, 12),
MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 14),
MCP23X17_Pin(mcp1, 4),
MCP23X17_Pin(mcp1, 8), MCP23X17_Pin(mcp1, 9), MCP23X17_Pin(mcp1, 10),
MCP23X17_Pin(mcp1, 11), MCP23X17_Pin(mcp1, 12), MCP23X17_Pin(mcp1, 13),
MCP23X17_Pin(mcp1, 14), MCP23X17_Pin(mcp1, 4),
};
MCP23X17_Pin EPD_CS[NUM_SCREENS] = {
MCP23X17_Pin(mcp2, 8),
MCP23X17_Pin(mcp2, 10),
MCP23X17_Pin(mcp2, 12),
MCP23X17_Pin(mcp2, 14),
MCP23X17_Pin(mcp2, 0),
MCP23X17_Pin(mcp2, 2),
MCP23X17_Pin(mcp2, 4),
MCP23X17_Pin(mcp2, 6)
};
MCP23X17_Pin(mcp2, 8), MCP23X17_Pin(mcp2, 10), MCP23X17_Pin(mcp2, 12),
MCP23X17_Pin(mcp2, 14), MCP23X17_Pin(mcp2, 0), MCP23X17_Pin(mcp2, 2),
MCP23X17_Pin(mcp2, 4), MCP23X17_Pin(mcp2, 6)};
MCP23X17_Pin EPD_RESET_MPD[NUM_SCREENS] = {
MCP23X17_Pin(mcp2, 9),
MCP23X17_Pin(mcp2, 11),
MCP23X17_Pin(mcp2, 13),
MCP23X17_Pin(mcp2, 15),
MCP23X17_Pin(mcp2, 1),
MCP23X17_Pin(mcp2, 3),
MCP23X17_Pin(mcp2, 5),
MCP23X17_Pin(mcp2, 7),
MCP23X17_Pin(mcp2, 9), MCP23X17_Pin(mcp2, 11), MCP23X17_Pin(mcp2, 13),
MCP23X17_Pin(mcp2, 15), MCP23X17_Pin(mcp2, 1), MCP23X17_Pin(mcp2, 3),
MCP23X17_Pin(mcp2, 5), MCP23X17_Pin(mcp2, 7),
};
#endif
@ -108,369 +83,329 @@ std::mutex epdMutex[NUM_SCREENS];
uint8_t qrcode[800];
void forceFullRefresh()
{
for (uint i = 0; i < NUM_SCREENS; i++)
{
lastFullRefresh[i] = NULL;
}
void forceFullRefresh() {
for (uint i = 0; i < NUM_SCREENS; i++) {
lastFullRefresh[i] = NULL;
}
}
void refreshFromMemory()
{
for (uint i = 0; i < NUM_SCREENS; i++)
{
int *taskParam = new int;
*taskParam = i;
void refreshFromMemory() {
for (uint i = 0; i < NUM_SCREENS; i++) {
int *taskParam = new int;
*taskParam = i;
xTaskCreate([](void *pvParameters)
{
const int epdIndex = *(int *)pvParameters;
delete (int *)pvParameters;
displays[epdIndex].refresh(false);
vTaskDelete(NULL); },
"PrepareUpd", 4096, taskParam, tskIDLE_PRIORITY, NULL);
}
xTaskCreate(
[](void *pvParameters) {
const int epdIndex = *(int *)pvParameters;
delete (int *)pvParameters;
displays[epdIndex].refresh(false);
vTaskDelete(NULL);
},
"PrepareUpd", 4096, taskParam, tskIDLE_PRIORITY, NULL);
}
}
void setupDisplays()
{
std::lock_guard<std::mutex> lockMcp(mcpMutex);
void setupDisplays() {
std::lock_guard<std::mutex> lockMcp(mcpMutex);
for (uint i = 0; i < NUM_SCREENS; i++)
{
displays[i].init(0, true, 30);
}
for (uint i = 0; i < NUM_SCREENS; i++) {
displays[i].init(0, true, 30);
}
updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem));
updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem));
xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", 4096, NULL, 11, NULL);
xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", 4096, NULL, 11, NULL);
for (uint i = 0; i < NUM_SCREENS; i++)
{
// epdUpdateSemaphore[i] = xSemaphoreCreateBinary();
// xSemaphoreGive(epdUpdateSemaphore[i]);
for (uint i = 0; i < NUM_SCREENS; i++) {
// epdUpdateSemaphore[i] = xSemaphoreCreateBinary();
// xSemaphoreGive(epdUpdateSemaphore[i]);
int *taskParam = new int;
*taskParam = i;
int *taskParam = new int;
*taskParam = i;
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 2048, taskParam, 11, &tasks[i]); // create task
}
xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), 2048, taskParam,
11, &tasks[i]); // create task
}
epdContent = {"B",
"T",
"C",
"L",
"O",
"C",
"K"};
epdContent = {"B", "T", "C", "L", "O", "C", "K"};
setEpdContent(epdContent);
setEpdContent(epdContent);
}
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent)
{
setEpdContent(newEpdContent, false);
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent) {
setEpdContent(newEpdContent, false);
}
void setEpdContent(std::array<std::string, NUM_SCREENS> newEpdContent) {
std::array<String, NUM_SCREENS> conv;
std::array<String, NUM_SCREENS> conv;
for (size_t i = 0; i < newEpdContent.size(); ++i) {
conv[i] = String(newEpdContent[i].c_str());
}
for (size_t i = 0; i < newEpdContent.size(); ++i) {
conv[i] = String(newEpdContent[i].c_str());
}
return setEpdContent(conv);
return setEpdContent(conv);
}
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent, bool forceUpdate)
{
std::lock_guard<std::mutex> lock(epdUpdateMutex);
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent,
bool forceUpdate) {
std::lock_guard<std::mutex> lock(epdUpdateMutex);
waitUntilNoneBusy();
waitUntilNoneBusy();
for (uint i = 0; i < NUM_SCREENS; i++) {
if (newEpdContent[i].compareTo(currentEpdContent[i]) != 0 || forceUpdate) {
epdContent[i] = newEpdContent[i];
UpdateDisplayTaskItem dispUpdate = {i};
xQueueSend(updateQueue, &dispUpdate, portMAX_DELAY);
}
}
}
void prepareDisplayUpdateTask(void *pvParameters) {
UpdateDisplayTaskItem receivedItem;
while (1) {
// Wait for a work item to be available in the queue
if (xQueueReceive(updateQueue, &receivedItem, portMAX_DELAY)) {
uint epdIndex = receivedItem.dispNum;
std::lock_guard<std::mutex> lock(epdMutex[epdIndex]);
// displays[epdIndex].init(0, false); // Little longer reset duration
// because of MCP
bool updatePartial = true;
if (strstr(epdContent[epdIndex].c_str(), "/") != NULL) {
String top = epdContent[epdIndex].substring(
0, epdContent[epdIndex].indexOf("/"));
String bottom = epdContent[epdIndex].substring(
epdContent[epdIndex].indexOf("/") + 1);
splitText(epdIndex, top, bottom, updatePartial);
} else if (epdContent[epdIndex].startsWith(F("qr"))) {
renderQr(epdIndex, epdContent[epdIndex], updatePartial);
} else if (epdContent[epdIndex].length() > 5) {
renderText(epdIndex, epdContent[epdIndex], updatePartial);
} else {
if (epdContent[epdIndex].length() > 1) {
showChars(epdIndex, epdContent[epdIndex], updatePartial,
&FONT_MEDIUM);
} else {
showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial,
&FONT_BIG);
}
}
xTaskNotifyGive(tasks[epdIndex]);
}
}
}
extern "C" void updateDisplay(void *pvParameters) noexcept {
const int epdIndex = *(int *)pvParameters;
delete (int *)pvParameters;
for (;;) {
// Wait for the task notification
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
std::lock_guard<std::mutex> lock(epdMutex[epdIndex]);
for (uint i = 0; i < NUM_SCREENS; i++)
{
if (newEpdContent[i].compareTo(currentEpdContent[i]) != 0 || forceUpdate)
{
epdContent[i] = newEpdContent[i];
UpdateDisplayTaskItem dispUpdate = {i};
xQueueSend(updateQueue, &dispUpdate, portMAX_DELAY);
}
std::lock_guard<std::mutex> lockMcp(mcpMutex);
displays[epdIndex].init(0, false, 40);
}
}
void prepareDisplayUpdateTask(void *pvParameters)
{
UpdateDisplayTaskItem receivedItem;
while (1)
{
// Wait for a work item to be available in the queue
if (xQueueReceive(updateQueue, &receivedItem, portMAX_DELAY))
{
uint epdIndex = receivedItem.dispNum;
std::lock_guard<std::mutex> lock(epdMutex[epdIndex]);
// displays[epdIndex].init(0, false); // Little longer reset duration because of MCP
bool updatePartial = true;
if (strstr(epdContent[epdIndex].c_str(), "/") != NULL)
{
String top = epdContent[epdIndex].substring(0, epdContent[epdIndex].indexOf("/"));
String bottom = epdContent[epdIndex].substring(epdContent[epdIndex].indexOf("/") + 1);
splitText(epdIndex, top, bottom, updatePartial);
}
else if (epdContent[epdIndex].startsWith(F("qr")))
{
renderQr(epdIndex, epdContent[epdIndex], updatePartial);
}
else if (epdContent[epdIndex].length() > 5)
{
renderText(epdIndex, epdContent[epdIndex], updatePartial);
}
else
{
if (epdContent[epdIndex].length() > 1)
{
showChars(epdIndex, epdContent[epdIndex], updatePartial, &FONT_MEDIUM);
}
else
{
showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, &FONT_BIG);
}
}
xTaskNotifyGive(tasks[epdIndex]);
}
uint count = 0;
while (EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10) {
vTaskDelay(pdMS_TO_TICKS(100));
count++;
}
}
extern "C" void updateDisplay(void *pvParameters) noexcept
{
const int epdIndex = *(int *)pvParameters;
delete (int *)pvParameters;
bool updatePartial = true;
for (;;)
{
// Wait for the task notification
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
std::lock_guard<std::mutex> lock(epdMutex[epdIndex]);
{
std::lock_guard<std::mutex> lockMcp(mcpMutex);
displays[epdIndex].init(0, false, 40);
}
uint count = 0;
while (EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10)
{
vTaskDelay(pdMS_TO_TICKS(100));
count++;
}
bool updatePartial = true;
// Full Refresh every x minutes
if (!lastFullRefresh[epdIndex] || (millis() - lastFullRefresh[epdIndex]) > (preferences.getUInt("fullRefreshMin", DEFAULT_MINUTES_FULL_REFRESH) * 60 * 1000))
{
updatePartial = false;
}
char tries = 0;
while (tries < 3)
{
if (displays[epdIndex].displayWithReturn(updatePartial))
{
displays[epdIndex].powerOff();
currentEpdContent[epdIndex] = epdContent[epdIndex];
if (!updatePartial)
lastFullRefresh[epdIndex] = millis();
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
break;
}
vTaskDelay(pdMS_TO_TICKS(100));
tries++;
}
// Full Refresh every x minutes
if (!lastFullRefresh[epdIndex] ||
(millis() - lastFullRefresh[epdIndex]) >
(preferences.getUInt("fullRefreshMin",
DEFAULT_MINUTES_FULL_REFRESH) *
60 * 1000)) {
updatePartial = false;
}
}
void splitText(const uint dispNum, const String &top, const String &bottom, bool partial)
{
displays[dispNum].setRotation(2);
displays[dispNum].setFont(&FONT_SMALL);
displays[dispNum].setTextColor(getFgColor());
char tries = 0;
while (tries < 3) {
if (displays[epdIndex].displayWithReturn(updatePartial)) {
displays[epdIndex].powerOff();
currentEpdContent[epdIndex] = epdContent[epdIndex];
if (!updatePartial)
lastFullRefresh[epdIndex] = millis();
// Top text
int16_t ttbx, ttby;
uint16_t ttbw, ttbh;
displays[dispNum].getTextBounds(top, 0, 0, &ttbx, &ttby, &ttbw, &ttbh);
uint16_t tx = ((displays[dispNum].width() - ttbw) / 2) - ttbx;
uint16_t ty = ((displays[dispNum].height() - ttbh) / 2) - ttby - ttbh / 2 - 12;
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
// Bottom text
int16_t tbbx, tbby;
uint16_t tbbw, tbbh;
displays[dispNum].getTextBounds(bottom, 0, 0, &tbbx, &tbby, &tbbw, &tbbh);
uint16_t bx = ((displays[dispNum].width() - tbbw) / 2) - tbbx;
uint16_t by = ((displays[dispNum].height() - tbbh) / 2) - tbby + tbbh / 2 + 12;
break;
}
// Make separator as wide as the shortest text.
uint16_t lineWidth, lineX;
if (tbbw < ttbh)
lineWidth = tbbw;
else
lineWidth = ttbw;
lineX = round((displays[dispNum].width() - lineWidth) / 2);
displays[dispNum].fillScreen(getBgColor());
displays[dispNum].setCursor(tx, ty);
displays[dispNum].print(top);
displays[dispNum].fillRoundRect(lineX, displays[dispNum].height() / 2 - 3, lineWidth, 6, 3, getFgColor());
displays[dispNum].setCursor(bx, by);
displays[dispNum].print(bottom);
}
void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font)
{
String str(chr);
displays[dispNum].setRotation(2);
displays[dispNum].setFont(font);
displays[dispNum].setTextColor(getFgColor());
int16_t tbx, tby;
uint16_t tbw, tbh;
displays[dispNum].getTextBounds(str, 0, 0, &tbx, &tby, &tbw, &tbh);
// center the bounding box by transposition of the origin:
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
displays[dispNum].fillScreen(getBgColor());
displays[dispNum].setCursor(x, y);
displays[dispNum].print(str);
}
void showChars(const uint dispNum, const String &chars, bool partial, const GFXfont *font)
{
displays[dispNum].setRotation(2);
displays[dispNum].setFont(font);
displays[dispNum].setTextColor(getFgColor());
int16_t tbx, tby;
uint16_t tbw, tbh;
displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
// center the bounding box by transposition of the origin:
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
displays[dispNum].fillScreen(getBgColor());
displays[dispNum].setCursor(x, y);
displays[dispNum].print(chars);
}
int getBgColor()
{
return bgColor;
}
int getFgColor()
{
return fgColor;
}
void setBgColor(int color)
{
bgColor = color;
}
void setFgColor(int color)
{
fgColor = color;
}
std::array<String, NUM_SCREENS> getCurrentEpdContent()
{
return currentEpdContent;
}
void renderText(const uint dispNum, const String &text, bool partial)
{
displays[dispNum].setRotation(2);
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height());
displays[dispNum].fillScreen(GxEPD_WHITE);
displays[dispNum].setTextColor(GxEPD_BLACK);
displays[dispNum].setCursor(0, 50);
std::stringstream ss;
ss.str(text.c_str());
std::string line;
while (std::getline(ss, line, '\n'))
{
if (line.rfind("*", 0) == 0)
{
line.erase(std::remove(line.begin(), line.end(), '*'), line.end());
displays[dispNum].setFont(&FreeSansBold9pt7b);
displays[dispNum].println(line.c_str());
}
else
{
displays[dispNum].setFont(&FreeSans9pt7b);
displays[dispNum].println(line.c_str());
}
vTaskDelay(pdMS_TO_TICKS(100));
tries++;
}
}
}
void renderQr(const uint dispNum, const String &text, bool partial)
{
void splitText(const uint dispNum, const String &top, const String &bottom,
bool partial) {
displays[dispNum].setRotation(2);
displays[dispNum].setFont(&FONT_SMALL);
displays[dispNum].setTextColor(getFgColor());
// Top text
int16_t ttbx, ttby;
uint16_t ttbw, ttbh;
displays[dispNum].getTextBounds(top, 0, 0, &ttbx, &ttby, &ttbw, &ttbh);
uint16_t tx = ((displays[dispNum].width() - ttbw) / 2) - ttbx;
uint16_t ty =
((displays[dispNum].height() - ttbh) / 2) - ttby - ttbh / 2 - 12;
// Bottom text
int16_t tbbx, tbby;
uint16_t tbbw, tbbh;
displays[dispNum].getTextBounds(bottom, 0, 0, &tbbx, &tbby, &tbbw, &tbbh);
uint16_t bx = ((displays[dispNum].width() - tbbw) / 2) - tbbx;
uint16_t by =
((displays[dispNum].height() - tbbh) / 2) - tbby + tbbh / 2 + 12;
// Make separator as wide as the shortest text.
uint16_t lineWidth, lineX;
if (tbbw < ttbh)
lineWidth = tbbw;
else
lineWidth = ttbw;
lineX = round((displays[dispNum].width() - lineWidth) / 2);
displays[dispNum].fillScreen(getBgColor());
displays[dispNum].setCursor(tx, ty);
displays[dispNum].print(top);
displays[dispNum].fillRoundRect(lineX, displays[dispNum].height() / 2 - 3,
lineWidth, 6, 3, getFgColor());
displays[dispNum].setCursor(bx, by);
displays[dispNum].print(bottom);
}
void showDigit(const uint dispNum, char chr, bool partial,
const GFXfont *font) {
String str(chr);
displays[dispNum].setRotation(2);
displays[dispNum].setFont(font);
displays[dispNum].setTextColor(getFgColor());
int16_t tbx, tby;
uint16_t tbw, tbh;
displays[dispNum].getTextBounds(str, 0, 0, &tbx, &tby, &tbw, &tbh);
// center the bounding box by transposition of the origin:
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
displays[dispNum].fillScreen(getBgColor());
displays[dispNum].setCursor(x, y);
displays[dispNum].print(str);
}
void showChars(const uint dispNum, const String &chars, bool partial,
const GFXfont *font) {
displays[dispNum].setRotation(2);
displays[dispNum].setFont(font);
displays[dispNum].setTextColor(getFgColor());
int16_t tbx, tby;
uint16_t tbw, tbh;
displays[dispNum].getTextBounds(chars, 0, 0, &tbx, &tby, &tbw, &tbh);
// center the bounding box by transposition of the origin:
uint16_t x = ((displays[dispNum].width() - tbw) / 2) - tbx;
uint16_t y = ((displays[dispNum].height() - tbh) / 2) - tby;
displays[dispNum].fillScreen(getBgColor());
displays[dispNum].setCursor(x, y);
displays[dispNum].print(chars);
}
int getBgColor() { return bgColor; }
int getFgColor() { return fgColor; }
void setBgColor(int color) { bgColor = color; }
void setFgColor(int color) { fgColor = color; }
std::array<String, NUM_SCREENS> getCurrentEpdContent() {
return currentEpdContent;
}
void renderText(const uint dispNum, const String &text, bool partial) {
displays[dispNum].setRotation(2);
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(),
displays[dispNum].height());
displays[dispNum].fillScreen(GxEPD_WHITE);
displays[dispNum].setTextColor(GxEPD_BLACK);
displays[dispNum].setCursor(0, 50);
std::stringstream ss;
ss.str(text.c_str());
std::string line;
while (std::getline(ss, line, '\n')) {
if (line.rfind("*", 0) == 0) {
line.erase(std::remove(line.begin(), line.end(), '*'), line.end());
displays[dispNum].setFont(&FreeSansBold9pt7b);
displays[dispNum].println(line.c_str());
} else {
displays[dispNum].setFont(&FreeSans9pt7b);
displays[dispNum].println(line.c_str());
}
}
}
void renderQr(const uint dispNum, const String &text, bool partial) {
#ifdef USE_QR
uint8_t tempBuffer[800];
bool ok = qrcodegen_encodeText(text.substring(2).c_str(), tempBuffer, qrcode, qrcodegen_Ecc_LOW,
qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
uint8_t tempBuffer[800];
bool ok = qrcodegen_encodeText(
text.substring(2).c_str(), tempBuffer, qrcode, qrcodegen_Ecc_LOW,
qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
const int size = qrcodegen_getSize(qrcode);
const int size = qrcodegen_getSize(qrcode);
const int padding = floor(float(displays[dispNum].width() - (size * 4)) / 2);
const int paddingY = floor(float(displays[dispNum].height() - (size * 4)) / 2);
displays[dispNum].setRotation(2);
const int padding = floor(float(displays[dispNum].width() - (size * 4)) / 2);
const int paddingY =
floor(float(displays[dispNum].height() - (size * 4)) / 2);
displays[dispNum].setRotation(2);
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), displays[dispNum].height());
displays[dispNum].fillScreen(GxEPD_WHITE);
const int border = 0;
displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(),
displays[dispNum].height());
displays[dispNum].fillScreen(GxEPD_WHITE);
const int border = 0;
for (int y = -border; y < size * 4 + border; y++)
{
for (int x = -border; x < size * 4 + border; x++)
{
displays[dispNum].drawPixel(padding + x, paddingY + y, qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4)) ? GxEPD_BLACK : GxEPD_WHITE);
}
for (int y = -border; y < size * 4 + border; y++) {
for (int x = -border; x < size * 4 + border; x++) {
displays[dispNum].drawPixel(
padding + x, paddingY + y,
qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4))
? GxEPD_BLACK
: GxEPD_WHITE);
}
}
#endif
}
void waitUntilNoneBusy()
{
for (int i = 0; i < NUM_SCREENS; i++)
{
uint count = 0;
while (EPD_BUSY[i].digitalRead())
{
count++;
vTaskDelay(10);
if (count == 200)
{
// displays[i].init(0, false);
vTaskDelay(100);
}
else if (count > 205)
{
Serial.printf("Busy timeout %d", i);
break;
}
}
void waitUntilNoneBusy() {
for (int i = 0; i < NUM_SCREENS; i++) {
uint count = 0;
while (EPD_BUSY[i].digitalRead()) {
count++;
vTaskDelay(10);
if (count == 200) {
// displays[i].init(0, false);
vTaskDelay(100);
} else if (count > 205) {
Serial.printf("Busy timeout %d", i);
break;
}
}
}
}

View File

@ -1,34 +1,35 @@
#pragma once
#include <GxEPD2_BW.h>
#include <native_pin.hpp>
#include <mcp23x17_pin.hpp>
#include "shared.hpp"
#include "config.hpp"
#include "fonts/fonts.hpp"
#include <Fonts/FreeSansBold9pt7b.h>
#include "lib/config.hpp"
#include "lib/shared.hpp"
#include <Fonts/FreeSans9pt7b.h>
#include <regex>
#include <Fonts/FreeSansBold9pt7b.h>
#include <GxEPD2_BW.h>
#include <mcp23x17_pin.hpp>
#include <mutex>
#include <native_pin.hpp>
#include <regex>
#ifdef USE_QR
#include "qrcodegen.h"
#endif
// extern TaskHandle_t epdTaskHandle;
typedef struct
{
char dispNum;
typedef struct {
char dispNum;
} UpdateDisplayTaskItem;
void forceFullRefresh();
void refreshFromMemory();
void setupDisplays();
void splitText(const uint dispNum, const String& top, const String& bottom, bool partial);
void splitText(const uint dispNum, const String &top, const String &bottom,
bool partial);
void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font);
void showChars(const uint dispNum, const String& chars, bool partial, const GFXfont *font);
void showChars(const uint dispNum, const String &chars, bool partial,
const GFXfont *font);
extern "C" void updateDisplay(void *pvParameters) noexcept;
void updateDisplayAlt(int epdIndex);
@ -39,10 +40,11 @@ int getFgColor();
void setBgColor(int color);
void setFgColor(int color);
void renderText(const uint dispNum, const String& text, bool partial);
void renderQr(const uint dispNum, const String& text, bool partial);
void renderText(const uint dispNum, const String &text, bool partial);
void renderQr(const uint dispNum, const String &text, bool partial);
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent, bool forceUpdate);
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent,
bool forceUpdate);
void setEpdContent(std::array<String, NUM_SCREENS> newEpdContent);
void setEpdContent(std::array<std::string, NUM_SCREENS> newEpdContent);

View File

@ -2,13 +2,15 @@
namespace improv {
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data, bool check_checksum) {
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data,
bool check_checksum) {
return parse_improv_data(data.data(), data.size(), check_checksum);
}
ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_checksum) {
ImprovCommand parse_improv_data(const uint8_t *data, size_t length,
bool check_checksum) {
ImprovCommand improv_command;
Command command = (Command) data[0];
Command command = (Command)data[0];
uint8_t data_length = data[1];
if (data_length != length - 2 - check_checksum) {
@ -24,7 +26,7 @@ ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_c
calculated_checksum += data[i];
}
if ((uint8_t) calculated_checksum != checksum) {
if ((uint8_t)calculated_checksum != checksum) {
improv_command.command = BAD_CHECKSUM;
return improv_command;
}
@ -48,8 +50,10 @@ ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_c
return improv_command;
}
bool parse_improv_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer,
std::function<bool(ImprovCommand)> &&callback, std::function<void(Error)> &&on_error) {
bool parse_improv_serial_byte(size_t position, uint8_t byte,
const uint8_t *buffer,
std::function<bool(ImprovCommand)> &&callback,
std::function<void(Error)> &&on_error) {
if (position == 0)
return byte == 'I';
if (position == 1)
@ -94,7 +98,9 @@ bool parse_improv_serial_byte(size_t position, uint8_t byte, const uint8_t *buff
return false;
}
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::string> &datum, bool add_checksum) {
std::vector<uint8_t> build_rpc_response(Command command,
const std::vector<std::string> &datum,
bool add_checksum) {
std::vector<uint8_t> out;
uint32_t length = 0;
out.push_back(command);
@ -118,7 +124,9 @@ std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::
}
#ifdef ARDUINO
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<String> &datum, bool add_checksum) {
std::vector<uint8_t> build_rpc_response(Command command,
const std::vector<String> &datum,
bool add_checksum) {
std::vector<uint8_t> out;
uint32_t length = 0;
out.push_back(command);
@ -140,6 +148,6 @@ std::vector<uint8_t> build_rpc_response(Command command, const std::vector<Strin
}
return out;
}
#endif // ARDUINO
#endif // ARDUINO
} // namespace improv
} // namespace improv

View File

@ -2,7 +2,7 @@
#ifdef ARDUINO
#include <Arduino.h>
#endif // ARDUINO
#endif // ARDUINO
#include <cstdint>
#include <functional>
@ -14,9 +14,12 @@ namespace improv {
static const char *const SERVICE_UUID = "00467768-6228-2272-4663-277478268000";
static const char *const STATUS_UUID = "00467768-6228-2272-4663-277478268001";
static const char *const ERROR_UUID = "00467768-6228-2272-4663-277478268002";
static const char *const RPC_COMMAND_UUID = "00467768-6228-2272-4663-277478268003";
static const char *const RPC_RESULT_UUID = "00467768-6228-2272-4663-277478268004";
static const char *const CAPABILITIES_UUID = "00467768-6228-2272-4663-277478268005";
static const char *const RPC_COMMAND_UUID =
"00467768-6228-2272-4663-277478268003";
static const char *const RPC_RESULT_UUID =
"00467768-6228-2272-4663-277478268004";
static const char *const CAPABILITIES_UUID =
"00467768-6228-2272-4663-277478268005";
enum Error : uint8_t {
ERROR_NONE = 0x00,
@ -61,16 +64,23 @@ struct ImprovCommand {
std::string password;
};
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data, bool check_checksum = true);
ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_checksum = true);
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data,
bool check_checksum = true);
ImprovCommand parse_improv_data(const uint8_t *data, size_t length,
bool check_checksum = true);
bool parse_improv_serial_byte(size_t position, uint8_t byte, const uint8_t *buffer,
std::function<bool(ImprovCommand)> &&callback, std::function<void(Error)> &&on_error);
bool parse_improv_serial_byte(size_t position, uint8_t byte,
const uint8_t *buffer,
std::function<bool(ImprovCommand)> &&callback,
std::function<void(Error)> &&on_error);
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::string> &datum,
std::vector<uint8_t> build_rpc_response(Command command,
const std::vector<std::string> &datum,
bool add_checksum = true);
#ifdef ARDUINO
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<String> &datum, bool add_checksum = true);
#endif // ARDUINO
std::vector<uint8_t> build_rpc_response(Command command,
const std::vector<String> &datum,
bool add_checksum = true);
#endif // ARDUINO
} // namespace improv
} // namespace improv

View File

@ -5,375 +5,322 @@ QueueHandle_t ledTaskQueue = NULL;
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
uint ledTaskParams;
void ledTask(void *parameter)
{
while (1)
{
if (ledTaskQueue != NULL)
{
if (xQueueReceive(ledTaskQueue, &ledTaskParams, portMAX_DELAY) == pdPASS)
{
uint32_t oldLights[NEOPIXEL_COUNT];
void ledTask(void *parameter) {
while (1) {
if (ledTaskQueue != NULL) {
if (xQueueReceive(ledTaskQueue, &ledTaskParams, portMAX_DELAY) ==
pdPASS) {
uint32_t oldLights[NEOPIXEL_COUNT];
// get current state
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
oldLights[i] = pixels.getPixelColor(i);
}
// get current state
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
oldLights[i] = pixels.getPixelColor(i);
}
switch (ledTaskParams)
{
case LED_POWER_TEST:
ledRainbow(20);
pixels.clear();
break;
case LED_EFFECT_WIFI_CONNECT_ERROR:
blinkDelayTwoColor(100, 3, pixels.Color(8, 161, 236), pixels.Color(255, 0, 0));
break;
case LED_FLASH_ERROR:
blinkDelayColor(250, 3, 255, 0, 0);
break;
case LED_EFFECT_HEARTBEAT:
blinkDelayColor(150, 2, 0, 0, 255);
break;
case LED_EFFECT_WIFI_CONNECT_SUCCESS:
case LED_FLASH_SUCCESS:
blinkDelayColor(150, 3, 0, 255, 0);
break;
case LED_PROGRESS_100:
pixels.setPixelColor(0, pixels.Color(0, 255, 0));
case LED_PROGRESS_75:
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
case LED_PROGRESS_50:
pixels.setPixelColor(2, pixels.Color(0, 255, 0));
case LED_PROGRESS_25:
pixels.setPixelColor(3, pixels.Color(0, 255, 0));
pixels.show();
break;
case LED_FLASH_UPDATE:
break;
case LED_FLASH_BLOCK_NOTIFY:
blinkDelayTwoColor(250, 3, pixels.Color(224, 67, 0), pixels.Color(8, 2, 0));
break;
case LED_EFFECT_WIFI_WAIT_FOR_CONFIG:
blinkDelayTwoColor(100, 1, pixels.Color(8, 161, 236), pixels.Color(156, 225, 240));
break;
case LED_EFFECT_WIFI_ERASE_SETTINGS:
blinkDelay(100, 3);
break;
case LED_EFFECT_WIFI_CONNECTING:
for (int i = NEOPIXEL_COUNT; i >= 0; i--)
{
for (int j = NEOPIXEL_COUNT; j >= 0; j--)
{
if (j == i)
{
pixels.setPixelColor(i, pixels.Color(16, 197, 236));
}
else
{
pixels.setPixelColor(j, pixels.Color(0, 0, 0));
}
}
pixels.show();
vTaskDelay(pdMS_TO_TICKS(100));
}
break;
case LED_EFFECT_PAUSE_TIMER:
for (int i = NEOPIXEL_COUNT; i >= 0; i--)
{
for (int j = NEOPIXEL_COUNT; j >= 0; j--)
{
uint32_t c = pixels.Color(0, 0, 0);
if (i == j)
c = pixels.Color(0, 255, 0);
pixels.setPixelColor(j, c);
}
pixels.show();
delay(100);
}
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.show();
delay(900);
pixels.clear();
pixels.show();
break;
case LED_EFFECT_START_TIMER:
pixels.clear();
pixels.setPixelColor((NEOPIXEL_COUNT - 1), pixels.Color(255, 0, 0));
pixels.show();
delay(900);
for (int i = NEOPIXEL_COUNT; i--; i > 0)
{
for (int j = NEOPIXEL_COUNT; j--; j > 0)
{
uint32_t c = pixels.Color(0, 0, 0);
if (i == j)
c = pixels.Color(0, 255, 0);
pixels.setPixelColor(j, c);
}
pixels.show();
delay(100);
}
pixels.clear();
pixels.show();
break;
}
// revert to previous state unless power test
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, oldLights[i]);
}
pixels.show();
switch (ledTaskParams) {
case LED_POWER_TEST:
ledRainbow(20);
pixels.clear();
break;
case LED_EFFECT_WIFI_CONNECT_ERROR:
blinkDelayTwoColor(100, 3, pixels.Color(8, 161, 236),
pixels.Color(255, 0, 0));
break;
case LED_FLASH_ERROR:
blinkDelayColor(250, 3, 255, 0, 0);
break;
case LED_EFFECT_HEARTBEAT:
blinkDelayColor(150, 2, 0, 0, 255);
break;
case LED_EFFECT_WIFI_CONNECT_SUCCESS:
case LED_FLASH_SUCCESS:
blinkDelayColor(150, 3, 0, 255, 0);
break;
case LED_PROGRESS_100:
pixels.setPixelColor(0, pixels.Color(0, 255, 0));
case LED_PROGRESS_75:
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
case LED_PROGRESS_50:
pixels.setPixelColor(2, pixels.Color(0, 255, 0));
case LED_PROGRESS_25:
pixels.setPixelColor(3, pixels.Color(0, 255, 0));
pixels.show();
break;
case LED_FLASH_UPDATE:
break;
case LED_FLASH_BLOCK_NOTIFY:
blinkDelayTwoColor(250, 3, pixels.Color(224, 67, 0),
pixels.Color(8, 2, 0));
break;
case LED_EFFECT_WIFI_WAIT_FOR_CONFIG:
blinkDelayTwoColor(100, 1, pixels.Color(8, 161, 236),
pixels.Color(156, 225, 240));
break;
case LED_EFFECT_WIFI_ERASE_SETTINGS:
blinkDelay(100, 3);
break;
case LED_EFFECT_WIFI_CONNECTING:
for (int i = NEOPIXEL_COUNT; i >= 0; i--) {
for (int j = NEOPIXEL_COUNT; j >= 0; j--) {
if (j == i) {
pixels.setPixelColor(i, pixels.Color(16, 197, 236));
} else {
pixels.setPixelColor(j, pixels.Color(0, 0, 0));
}
}
pixels.show();
vTaskDelay(pdMS_TO_TICKS(100));
}
break;
case LED_EFFECT_PAUSE_TIMER:
for (int i = NEOPIXEL_COUNT; i >= 0; i--) {
for (int j = NEOPIXEL_COUNT; j >= 0; j--) {
uint32_t c = pixels.Color(0, 0, 0);
if (i == j)
c = pixels.Color(0, 255, 0);
pixels.setPixelColor(j, c);
}
pixels.show();
delay(100);
}
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.show();
delay(900);
pixels.clear();
pixels.show();
break;
case LED_EFFECT_START_TIMER:
pixels.clear();
pixels.setPixelColor((NEOPIXEL_COUNT - 1), pixels.Color(255, 0, 0));
pixels.show();
delay(900);
for (int i = NEOPIXEL_COUNT; i--; i > 0) {
for (int j = NEOPIXEL_COUNT; j--; j > 0) {
uint32_t c = pixels.Color(0, 0, 0);
if (i == j)
c = pixels.Color(0, 255, 0);
pixels.setPixelColor(j, c);
}
pixels.show();
delay(100);
}
pixels.clear();
pixels.show();
break;
}
}
}
void setupLeds()
{
pixels.begin();
pixels.setBrightness(preferences.getUInt("ledBrightness", 128));
pixels.clear();
pixels.show();
setupLedTask();
if (preferences.getBool("ledTestOnPower", true))
{
while (!ledTaskQueue)
{
delay(1);
// wait until queue is available
}
queueLedEffect(LED_POWER_TEST);
}
}
// revert to previous state unless power test
void setupLedTask()
{
ledTaskQueue = xQueueCreate(5, sizeof(uint));
xTaskCreate(ledTask, "LedTask", 2048, NULL, tskIDLE_PRIORITY, &ledTaskHandle);
}
void blinkDelay(int d, int times)
{
for (int j = 0; j < times; j++)
{
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
pixels.setPixelColor(2, pixels.Color(255, 0, 0));
pixels.setPixelColor(3, pixels.Color(0, 255, 0));
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
pixels.setPixelColor(0, pixels.Color(255, 255, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 255));
pixels.setPixelColor(2, pixels.Color(255, 255, 0));
pixels.setPixelColor(3, pixels.Color(0, 255, 255));
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
}
pixels.clear();
pixels.show();
}
void blinkDelayColor(int d, int times, uint r, uint g, uint b)
{
for (int j = 0; j < times; j++)
{
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, pixels.Color(r, g, b));
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
pixels.setPixelColor(i, oldLights[i]);
}
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
pixels.clear();
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
}
}
}
}
void setupLeds() {
pixels.begin();
pixels.setBrightness(preferences.getUInt("ledBrightness", 128));
pixels.clear();
pixels.show();
setupLedTask();
if (preferences.getBool("ledTestOnPower", true)) {
while (!ledTaskQueue) {
delay(1);
// wait until queue is available
}
queueLedEffect(LED_POWER_TEST);
}
}
void setupLedTask() {
ledTaskQueue = xQueueCreate(5, sizeof(uint));
xTaskCreate(ledTask, "LedTask", 2048, NULL, tskIDLE_PRIORITY, &ledTaskHandle);
}
void blinkDelay(int d, int times) {
for (int j = 0; j < times; j++) {
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
pixels.setPixelColor(2, pixels.Color(255, 0, 0));
pixels.setPixelColor(3, pixels.Color(0, 255, 0));
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
pixels.setPixelColor(0, pixels.Color(255, 255, 0));
pixels.setPixelColor(1, pixels.Color(0, 255, 255));
pixels.setPixelColor(2, pixels.Color(255, 255, 0));
pixels.setPixelColor(3, pixels.Color(0, 255, 255));
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
}
pixels.clear();
pixels.show();
}
void blinkDelayColor(int d, int times, uint r, uint g, uint b) {
for (int j = 0; j < times; j++) {
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
pixels.setPixelColor(i, pixels.Color(r, g, b));
}
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
pixels.clear();
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
}
pixels.clear();
pixels.show();
}
void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2)
{
for (int j = 0; j < times; j++)
{
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, c1);
}
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, c2);
}
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
}
pixels.clear();
pixels.show();
}
void clearLeds()
{
preferences.putBool("ledStatus", false);
pixels.clear();
pixels.show();
}
void setLights(int r, int g, int b)
{
setLights(pixels.Color(r, g, b));
}
void setLights(uint32_t color)
{
bool ledStatus = true;
for (int i = 0; i < NEOPIXEL_COUNT; i++)
{
pixels.setPixelColor(i, color);
void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2) {
for (int j = 0; j < times; j++) {
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
pixels.setPixelColor(i, c1);
}
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
if (color == pixels.Color(0, 0, 0))
{
ledStatus = false;
} else {
saveLedState();
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
pixels.setPixelColor(i, c2);
}
preferences.putBool("ledStatus", ledStatus);
pixels.show();
vTaskDelay(pdMS_TO_TICKS(d));
}
pixels.clear();
pixels.show();
}
void clearLeds() {
preferences.putBool("ledStatus", false);
pixels.clear();
pixels.show();
}
void setLights(int r, int g, int b) { setLights(pixels.Color(r, g, b)); }
void setLights(uint32_t color) {
bool ledStatus = true;
for (int i = 0; i < NEOPIXEL_COUNT; i++) {
pixels.setPixelColor(i, color);
}
pixels.show();
if (color == pixels.Color(0, 0, 0)) {
ledStatus = false;
} else {
saveLedState();
}
preferences.putBool("ledStatus", ledStatus);
}
void saveLedState() {
for (int i = 0; i < pixels.numPixels(); i++)
{
int pixelColor = pixels.getPixelColor(i);
char key[12];
snprintf(key, 12, "%s%d", "ledColor_", i);
preferences.putUInt(key, pixelColor);
}
for (int i = 0; i < pixels.numPixels(); i++) {
int pixelColor = pixels.getPixelColor(i);
char key[12];
snprintf(key, 12, "%s%d", "ledColor_", i);
preferences.putUInt(key, pixelColor);
}
xTaskNotifyGive(eventSourceTaskHandle);
xTaskNotifyGive(eventSourceTaskHandle);
}
void restoreLedState() {
for (int i = 0; i < pixels.numPixels(); i++)
{
char key[12];
snprintf(key, 12, "%s%d", "ledColor_", i);
uint pixelColor = preferences.getUInt(key, pixels.Color(0,0,0));
pixels.setPixelColor(i, pixelColor);
for (int i = 0; i < pixels.numPixels(); i++) {
char key[12];
snprintf(key, 12, "%s%d", "ledColor_", i);
uint pixelColor = preferences.getUInt(key, pixels.Color(0, 0, 0));
pixels.setPixelColor(i, pixelColor);
}
pixels.show();
}
QueueHandle_t getLedTaskQueue() { return ledTaskQueue; }
bool queueLedEffect(uint effect) {
if (ledTaskQueue == NULL) {
return false;
}
uint flashType = effect;
xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY);
}
void ledRainbow(int wait) {
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this loop:
for (long firstPixelHue = 0; firstPixelHue < 5 * 65536;
firstPixelHue += 256) {
// strip.rainbow() can take a single argument (first pixel hue) or
// optionally a few extras: number of rainbow repetitions (default 1),
// saturation and value (brightness) (both 0-255, similar to the
// ColorHSV() function, default 255), and a true/false flag for whether
// to apply gamma correction to provide 'truer' colors (default true).
pixels.rainbow(firstPixelHue);
// Above line is equivalent to:
// strip.rainbow(firstPixelHue, 1, 255, 255, true);
pixels.show(); // Update strip with new contents
delayMicroseconds(wait);
// vTaskDelay(pdMS_TO_TICKS(wait)); // Pause for a moment
}
}
void ledTheaterChase(uint32_t color, int wait) {
for (int a = 0; a < 10; a++) { // Repeat 10 times...
for (int b = 0; b < 3; b++) { // 'b' counts from 0 to 2...
pixels.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for (int c = b; c < pixels.numPixels(); c += 3) {
pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
pixels.show(); // Update strip with new contents
vTaskDelay(pdMS_TO_TICKS(wait)); // Pause for a moment
}
pixels.show();
}
}
QueueHandle_t getLedTaskQueue()
{
return ledTaskQueue;
}
bool queueLedEffect(uint effect)
{
if (ledTaskQueue == NULL)
{
return false;
void ledTheaterChaseRainbow(int wait) {
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for (int a = 0; a < 30; a++) { // Repeat 30 times...
for (int b = 0; b < 3; b++) { // 'b' counts from 0 to 2...
pixels.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for (int c = b; c < pixels.numPixels(); c += 3) {
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / pixels.numPixels();
uint32_t color = pixels.gamma32(pixels.ColorHSV(hue)); // hue -> RGB
pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
pixels.show(); // Update strip with new contents
vTaskDelay(pdMS_TO_TICKS(wait)); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
uint flashType = effect;
xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY);
}
}
void ledRainbow(int wait)
{
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this loop:
for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256)
{
// strip.rainbow() can take a single argument (first pixel hue) or
// optionally a few extras: number of rainbow repetitions (default 1),
// saturation and value (brightness) (both 0-255, similar to the
// ColorHSV() function, default 255), and a true/false flag for whether
// to apply gamma correction to provide 'truer' colors (default true).
pixels.rainbow(firstPixelHue);
// Above line is equivalent to:
// strip.rainbow(firstPixelHue, 1, 255, 255, true);
pixels.show(); // Update strip with new contents
delayMicroseconds(wait);
// vTaskDelay(pdMS_TO_TICKS(wait)); // Pause for a moment
}
}
void ledTheaterChase(uint32_t color, int wait)
{
for (int a = 0; a < 10; a++)
{ // Repeat 10 times...
for (int b = 0; b < 3; b++)
{ // 'b' counts from 0 to 2...
pixels.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for (int c = b; c < pixels.numPixels(); c += 3)
{
pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
pixels.show(); // Update strip with new contents
vTaskDelay(pdMS_TO_TICKS(wait)); // Pause for a moment
}
}
}
void ledTheaterChaseRainbow(int wait)
{
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for (int a = 0; a < 30; a++)
{ // Repeat 30 times...
for (int b = 0; b < 3; b++)
{ // 'b' counts from 0 to 2...
pixels.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for (int c = b; c < pixels.numPixels(); c += 3)
{
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / pixels.numPixels();
uint32_t color = pixels.gamma32(pixels.ColorHSV(hue)); // hue -> RGB
pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
pixels.show(); // Update strip with new contents
vTaskDelay(pdMS_TO_TICKS(wait)); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
Adafruit_NeoPixel getPixels()
{
return pixels;
}
Adafruit_NeoPixel getPixels() { return pixels; }

View File

@ -1,11 +1,11 @@
#pragma once
#include <Arduino.h>
#include "lib/shared.hpp"
#include "lib/webserver.hpp"
#include <Adafruit_NeoPixel.h>
#include <Arduino.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "webserver.hpp"
#include "shared.hpp"
#ifndef NEOPIXEL_PIN
#define NEOPIXEL_PIN 34

View File

@ -2,10 +2,8 @@
TaskHandle_t taskOtaHandle = NULL;
void setupOTA()
{
if (preferences.getBool("otaEnabled", true))
{
void setupOTA() {
if (preferences.getBool("otaEnabled", true)) {
ArduinoOTA.onStart(onOTAStart);
ArduinoOTA.onProgress(onOTAProgress);
@ -16,39 +14,35 @@ void setupOTA()
ArduinoOTA.setMdnsEnabled(false);
ArduinoOTA.setRebootOnSuccess(false);
ArduinoOTA.begin();
//downloadUpdate();
// downloadUpdate();
xTaskCreate(handleOTATask, "handleOTA", 4096, NULL, tskIDLE_PRIORITY, &taskOtaHandle);
xTaskCreate(handleOTATask, "handleOTA", 4096, NULL, tskIDLE_PRIORITY,
&taskOtaHandle);
}
}
void onOTAProgress(unsigned int progress, unsigned int total)
{
void onOTAProgress(unsigned int progress, unsigned int total) {
uint percentage = progress / (total / 100);
pixels.fill(pixels.Color(0, 255, 0));
if (percentage < 100)
{
if (percentage < 100) {
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
}
if (percentage < 75)
{
if (percentage < 75) {
pixels.setPixelColor(1, pixels.Color(0, 0, 0));
}
if (percentage < 50)
{
if (percentage < 50) {
pixels.setPixelColor(2, pixels.Color(0, 0, 0));
}
if (percentage < 25)
{
if (percentage < 25) {
pixels.setPixelColor(3, pixels.Color(0, 0, 0));
}
pixels.show();
}
void onOTAStart()
{
void onOTAStart() {
forceFullRefresh();
std::array<String, NUM_SCREENS> epdContent = {"U", "P", "D", "A", "T", "E", "!"};
std::array<String, NUM_SCREENS> epdContent = {"U", "P", "D", "A",
"T", "E", "!"};
setEpdContent(epdContent);
// Stop all timers
esp_timer_stop(screenRotateTimer);
@ -68,17 +62,14 @@ void onOTAStart()
stopPriceNotify();
}
void handleOTATask(void *parameter)
{
for (;;)
{
void handleOTATask(void *parameter) {
for (;;) {
ArduinoOTA.handle(); // Allow OTA updates to occur
vTaskDelay(pdMS_TO_TICKS(2500));
}
}
void downloadUpdate()
{
void downloadUpdate() {
WiFiClientSecure client;
client.setInsecure();
HTTPClient http;
@ -87,12 +78,12 @@ void downloadUpdate()
// Send HTTP request to CoinGecko API
http.useHTTP10(true);
http.begin(client, "https://api.github.com/repos/btclock/btclock_v3/releases/latest");
http.begin(client,
"https://api.github.com/repos/btclock/btclock_v3/releases/latest");
int httpCode = http.GET();
if (httpCode == 200)
{
// WiFiClient * stream = http->getStreamPtr();
if (httpCode == 200) {
// WiFiClient * stream = http->getStreamPtr();
StaticJsonDocument<64> filter;
@ -102,18 +93,17 @@ void downloadUpdate()
SpiRamJsonDocument doc(1536);
DeserializationError error = deserializeJson(doc, http.getStream(), DeserializationOption::Filter(filter));
DeserializationError error = deserializeJson(
doc, http.getStream(), DeserializationOption::Filter(filter));
if (error)
{
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
return;
}
String downloadUrl;
for (JsonObject asset : doc["assets"].as<JsonArray>())
{
for (JsonObject asset : doc["assets"].as<JsonArray>()) {
if (asset["name"].as<String>().compareTo("firmware.bin") == 0) {
downloadUrl = asset["browser_download_url"].as<String>();
break;
@ -122,8 +112,6 @@ void downloadUpdate()
Serial.printf("Download update from %s", downloadUrl);
// esp_http_client_config_t config = {
// .url = CONFIG_FIRMWARE_UPGRADE_URL,
// };
@ -138,8 +126,7 @@ void downloadUpdate()
}
}
void onOTAError(ota_error_t error)
{
void onOTAError(ota_error_t error) {
Serial.println("\nOTA update error, restarting");
Wire.end();
SPI.end();
@ -147,8 +134,7 @@ void onOTAError(ota_error_t error)
ESP.restart();
}
void onOTAComplete()
{
void onOTAComplete() {
Serial.println("\nOTA update finished");
Wire.end();
SPI.end();

View File

@ -1,7 +1,7 @@
#include "lib/config.hpp"
#include "lib/shared.hpp"
#include <Arduino.h>
#include <ArduinoOTA.h>
#include "config.hpp"
#include "shared.hpp"
void setupOTA();
void onOTAStart();

View File

@ -1,60 +1,56 @@
#include "price_fetch.hpp"
const PROGMEM char *cgApiUrl = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd%2Ceur";
const PROGMEM char *cgApiUrl = "https://api.coingecko.com/api/v3/simple/"
"price?ids=bitcoin&vs_currencies=usd%2Ceur";
TaskHandle_t priceFetchTaskHandle;
void taskPriceFetch(void *pvParameters)
{
WiFiClientSecure *client = new WiFiClientSecure;
client->setInsecure();
for (;;)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
void taskPriceFetch(void *pvParameters) {
WiFiClientSecure *client = new WiFiClientSecure;
client->setInsecure();
for (;;) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
HTTPClient *http = new HTTPClient();
http->setUserAgent(USER_AGENT);
HTTPClient *http = new HTTPClient();
http->setUserAgent(USER_AGENT);
// Send HTTP request to CoinGecko API
http->begin(*client, cgApiUrl);
// Send HTTP request to CoinGecko API
http->begin(*client, cgApiUrl);
int httpCode = http->GET();
int httpCode = http->GET();
// Parse JSON response and extract average price
uint usdPrice, eurPrice;
if (httpCode == 200)
{
String payload = http->getString();
StaticJsonDocument<96> doc;
deserializeJson(doc, payload);
// usdPrice = doc["bitcoin"]["usd"];
eurPrice = doc["bitcoin"]["eur"].as<uint>();
// Parse JSON response and extract average price
uint usdPrice, eurPrice;
if (httpCode == 200) {
String payload = http->getString();
StaticJsonDocument<96> doc;
deserializeJson(doc, payload);
// usdPrice = doc["bitcoin"]["usd"];
eurPrice = doc["bitcoin"]["eur"].as<uint>();
setPrice(eurPrice);
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER || getCurrentScreen() == SCREEN_MSCW_TIME || getCurrentScreen() == SCREEN_MARKET_CAP))
{
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
}
setPrice(eurPrice);
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
getCurrentScreen() == SCREEN_MSCW_TIME ||
getCurrentScreen() == SCREEN_MARKET_CAP)) {
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
}
preferences.putUInt("lastPrice", eurPrice);
}
else
{
Serial.print(F("Error retrieving BTC/USD price (CoinGecko). HTTP status code: "));
Serial.println(httpCode);
if (httpCode == -1)
{
WiFi.reconnect();
}
}
preferences.putUInt("lastPrice", eurPrice);
} else {
Serial.print(
F("Error retrieving BTC/USD price (CoinGecko). HTTP status code: "));
Serial.println(httpCode);
if (httpCode == -1) {
WiFi.reconnect();
}
}
}
}
void setupPriceFetchTask()
{
xTaskCreate(taskPriceFetch, "priceFetch", (6*1024), NULL, tskIDLE_PRIORITY, &priceFetchTaskHandle);
xTaskNotifyGive(priceFetchTaskHandle);
void setupPriceFetchTask() {
xTaskCreate(taskPriceFetch, "priceFetch", (6 * 1024), NULL, tskIDLE_PRIORITY,
&priceFetchTaskHandle);
xTaskNotifyGive(priceFetchTaskHandle);
}

View File

@ -1,7 +1,7 @@
#include "lib/config.hpp"
#include "lib/shared.hpp"
#include <Arduino.h>
#include <HTTPClient.h>
#include "config.hpp"
#include "shared.hpp"
extern TaskHandle_t priceFetchTaskHandle;

View File

@ -38,86 +38,82 @@ esp_websocket_client_handle_t clientPrice = NULL;
uint currentPrice = 30000;
unsigned long int lastPriceUpdate;
void setupPriceNotify()
{
// currentPrice = preferences.get("lastPrice", 30000);
void setupPriceNotify() {
// currentPrice = preferences.get("lastPrice", 30000);
esp_websocket_client_config_t config = {
.uri = wsServerPrice,
// .task_stack = (7*1024),
// .cert_pem = coinCapWsCert,
.user_agent = USER_AGENT
};
esp_websocket_client_config_t config = {.uri = wsServerPrice,
// .task_stack = (7*1024),
// .cert_pem = coinCapWsCert,
.user_agent = USER_AGENT};
clientPrice = esp_websocket_client_init(&config);
esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY, onWebsocketPriceEvent, clientPrice);
esp_websocket_client_start(clientPrice);
clientPrice = esp_websocket_client_init(&config);
esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY,
onWebsocketPriceEvent, clientPrice);
esp_websocket_client_start(clientPrice);
}
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data) {
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
switch (event_id)
{
case WEBSOCKET_EVENT_CONNECTED:
Serial.println(F("Connected to CoinCap.io WebSocket"));
break;
case WEBSOCKET_EVENT_DATA:
onWebsocketPriceMessage(data);
break;
case WEBSOCKET_EVENT_ERROR:
Serial.println(F("Price WS Connnection error"));
break;
case WEBSOCKET_EVENT_DISCONNECTED:
Serial.println(F("Price WS Connnection Closed"));
break;
}
switch (event_id) {
case WEBSOCKET_EVENT_CONNECTED:
Serial.println(F("Connected to CoinCap.io WebSocket"));
break;
case WEBSOCKET_EVENT_DATA:
onWebsocketPriceMessage(data);
break;
case WEBSOCKET_EVENT_ERROR:
Serial.println(F("Price WS Connnection error"));
break;
case WEBSOCKET_EVENT_DISCONNECTED:
Serial.println(F("Price WS Connnection Closed"));
break;
}
}
void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data)
{
SpiRamJsonDocument doc(event_data->data_len);
void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data) {
SpiRamJsonDocument doc(event_data->data_len);
deserializeJson(doc, (char *)event_data->data_ptr);
deserializeJson(doc, (char *)event_data->data_ptr);
if (doc.containsKey("bitcoin")) {
if (currentPrice != doc["bitcoin"].as<long>()) {
if (doc.containsKey("bitcoin")) {
if (currentPrice != doc["bitcoin"].as<long>()) {
uint minSecPriceUpd = preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
uint currentTime = esp_timer_get_time() / 1000000;
uint minSecPriceUpd = preferences.getUInt(
"minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
uint currentTime = esp_timer_get_time() / 1000000;
if (lastPriceUpdate == 0 || (currentTime - lastPriceUpdate) > minSecPriceUpd) {
// const unsigned long oldPrice = currentPrice;
currentPrice = doc["bitcoin"].as<uint>();
preferences.putUInt("lastPrice", currentPrice);
lastPriceUpdate = currentTime;
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER || getCurrentScreen() == SCREEN_MSCW_TIME || getCurrentScreen() == SCREEN_MARKET_CAP)) {
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
}
//}
}
if (lastPriceUpdate == 0 ||
(currentTime - lastPriceUpdate) > minSecPriceUpd) {
// const unsigned long oldPrice = currentPrice;
currentPrice = doc["bitcoin"].as<uint>();
preferences.putUInt("lastPrice", currentPrice);
lastPriceUpdate = currentTime;
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
getCurrentScreen() == SCREEN_MSCW_TIME ||
getCurrentScreen() == SCREEN_MARKET_CAP)) {
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
}
//}
}
}
}
}
uint getPrice() {
return currentPrice;
}
uint getPrice() { return currentPrice; }
void setPrice(uint newPrice) {
currentPrice = newPrice;
}
void setPrice(uint newPrice) { currentPrice = newPrice; }
bool isPriceNotifyConnected() {
if (clientPrice == NULL)
return false;
return esp_websocket_client_is_connected(clientPrice);
if (clientPrice == NULL)
return false;
return esp_websocket_client_is_connected(clientPrice);
}
void stopPriceNotify() {
esp_websocket_client_stop(clientPrice);
esp_websocket_client_destroy(clientPrice);
esp_websocket_client_stop(clientPrice);
esp_websocket_client_destroy(clientPrice);
}

View File

@ -1,18 +1,19 @@
#pragma once
#include <string>
#include <Arduino.h>
#include <ArduinoJson.h>
#include <string>
#include "esp_websocket_client.h"
#include "screen_handler.hpp"
#include <esp_websocket_client.h>
#include "lib/screen_handler.hpp"
//using namespace websockets;
// using namespace websockets;
void setupPriceNotify();
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data);
void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data);
void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data);
void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data);
uint getPrice();
void setPrice(uint newPrice);

View File

@ -8,7 +8,8 @@ TaskHandle_t workerTaskHandle;
esp_timer_handle_t screenRotateTimer;
esp_timer_handle_t minuteTimer;
std::array<std::string, NUM_SCREENS> taskEpdContent = {"", "", "", "", "", "", ""};
std::array<std::string, NUM_SCREENS> taskEpdContent = {"", "", "", "",
"", "", ""};
std::string priceString;
// typedef enum
@ -29,327 +30,280 @@ QueueHandle_t workQueue = NULL;
uint currentScreen;
void workerTask(void *pvParameters)
{
WorkItem receivedItem;
void workerTask(void *pvParameters) {
WorkItem receivedItem;
while (1)
{
// Wait for a work item to be available in the queue
if (xQueueReceive(workQueue, &receivedItem, portMAX_DELAY))
{
uint firstIndex = 0;
while (1) {
// Wait for a work item to be available in the queue
if (xQueueReceive(workQueue, &receivedItem, portMAX_DELAY)) {
uint firstIndex = 0;
// Process the work item based on its type
switch (receivedItem.type)
{
case TASK_PRICE_UPDATE:
{
uint price = getPrice();
char priceSymbol = '$';
if (preferences.getBool("fetchEurPrice", false))
{
priceSymbol = '[';
}
if (getCurrentScreen() == SCREEN_BTC_TICKER)
{
taskEpdContent = parsePriceData(price, priceSymbol);
}
else if (getCurrentScreen() == SCREEN_MSCW_TIME)
{
taskEpdContent = parseSatsPerCurrency(price, priceSymbol);
}
else
{
taskEpdContent = parseMarketCap(getBlockHeight(), price, priceSymbol, preferences.getBool("mcapBigChar", true));
}
setEpdContent(taskEpdContent);
break;
}
case TASK_BLOCK_UPDATE:
{
if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN)
{
taskEpdContent = parseBlockHeight(getBlockHeight());
}
else
{
taskEpdContent = parseHalvingCountdown(getBlockHeight());
}
if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN || getCurrentScreen() == SCREEN_BLOCK_HEIGHT)
{
setEpdContent(taskEpdContent);
}
break;
}
case TASK_TIME_UPDATE:
{
if (getCurrentScreen() == SCREEN_TIME)
{
time_t currentTime;
struct tm timeinfo;
time(&currentTime);
localtime_r(&currentTime, &timeinfo);
std::string timeString;
String minute = String(timeinfo.tm_min);
if (minute.length() < 2)
{
minute = "0" + minute;
}
timeString = std::to_string(timeinfo.tm_hour) + ":" + minute.c_str();
timeString.insert(timeString.begin(), NUM_SCREENS - timeString.length(), ' ');
taskEpdContent[0] = std::to_string(timeinfo.tm_mday) + "/" + std::to_string(timeinfo.tm_mon + 1);
for (uint i = 1; i < NUM_SCREENS; i++)
{
taskEpdContent[i] = timeString[i];
}
setEpdContent(taskEpdContent);
}
break;
}
// Add more cases for additional task types
}
// Process the work item based on its type
switch (receivedItem.type) {
case TASK_PRICE_UPDATE: {
uint price = getPrice();
char priceSymbol = '$';
if (preferences.getBool("fetchEurPrice", false)) {
priceSymbol = '[';
}
}
}
void taskScreenRotate(void *pvParameters)
{
for (;;)
{
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
int nextScreen = (currentScreen + 1) % SCREEN_COUNT;
String key = "screen" + String(nextScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
nextScreen = (nextScreen + 1) % SCREEN_COUNT;
key = "screen" + String(nextScreen) + "Visible";
if (getCurrentScreen() == SCREEN_BTC_TICKER) {
taskEpdContent = parsePriceData(price, priceSymbol);
} else if (getCurrentScreen() == SCREEN_MSCW_TIME) {
taskEpdContent = parseSatsPerCurrency(price, priceSymbol);
} else {
taskEpdContent =
parseMarketCap(getBlockHeight(), price, priceSymbol,
preferences.getBool("mcapBigChar", true));
}
setCurrentScreen(nextScreen);
setEpdContent(taskEpdContent);
break;
}
case TASK_BLOCK_UPDATE: {
if (getCurrentScreen() != SCREEN_HALVING_COUNTDOWN) {
taskEpdContent = parseBlockHeight(getBlockHeight());
} else {
taskEpdContent = parseHalvingCountdown(getBlockHeight());
}
if (getCurrentScreen() == SCREEN_HALVING_COUNTDOWN ||
getCurrentScreen() == SCREEN_BLOCK_HEIGHT) {
setEpdContent(taskEpdContent);
}
break;
}
case TASK_TIME_UPDATE: {
if (getCurrentScreen() == SCREEN_TIME) {
time_t currentTime;
struct tm timeinfo;
time(&currentTime);
localtime_r(&currentTime, &timeinfo);
std::string timeString;
String minute = String(timeinfo.tm_min);
if (minute.length() < 2) {
minute = "0" + minute;
}
timeString = std::to_string(timeinfo.tm_hour) + ":" + minute.c_str();
timeString.insert(timeString.begin(),
NUM_SCREENS - timeString.length(), ' ');
taskEpdContent[0] = std::to_string(timeinfo.tm_mday) + "/" +
std::to_string(timeinfo.tm_mon + 1);
for (uint i = 1; i < NUM_SCREENS; i++) {
taskEpdContent[i] = timeString[i];
}
setEpdContent(taskEpdContent);
}
break;
}
// Add more cases for additional task types
}
}
}
}
void IRAM_ATTR minuteTimerISR(void *arg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken);
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
xQueueSendFromISR(workQueue, &timeUpdate, &xHigherPriorityTaskWoken);
if (priceFetchTaskHandle != NULL)
{
vTaskNotifyGiveFromISR(priceFetchTaskHandle, &xHigherPriorityTaskWoken);
}
if (xHigherPriorityTaskWoken == pdTRUE)
{
portYIELD_FROM_ISR();
void taskScreenRotate(void *pvParameters) {
for (;;) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
int nextScreen = (currentScreen + 1) % SCREEN_COUNT;
String key = "screen" + String(nextScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true)) {
nextScreen = (nextScreen + 1) % SCREEN_COUNT;
key = "screen" + String(nextScreen) + "Visible";
}
setCurrentScreen(nextScreen);
}
}
void IRAM_ATTR screenRotateTimerISR(void *arg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(taskScreenRotateTaskHandle, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE)
{
portYIELD_FROM_ISR();
}
void IRAM_ATTR minuteTimerISR(void *arg) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// vTaskNotifyGiveFromISR(timeUpdateTaskHandle, &xHigherPriorityTaskWoken);
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
xQueueSendFromISR(workQueue, &timeUpdate, &xHigherPriorityTaskWoken);
if (priceFetchTaskHandle != NULL) {
vTaskNotifyGiveFromISR(priceFetchTaskHandle, &xHigherPriorityTaskWoken);
}
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
void setupTasks()
{
workQueue = xQueueCreate(WORK_QUEUE_SIZE, sizeof(WorkItem));
// xTaskCreate(taskPriceUpdate, "updatePrice", 1024, NULL, tskIDLE_PRIORITY, &priceUpdateTaskHandle);
// xTaskCreate(taskBlockUpdate, "updateBlock", 1024, NULL, tskIDLE_PRIORITY, &blockUpdateTaskHandle);
// xTaskCreate(taskTimeUpdate, "updateTime", 1024, NULL, tskIDLE_PRIORITY, &timeUpdateTaskHandle);
xTaskCreate(workerTask, "workerTask", 4096, NULL, tskIDLE_PRIORITY, &workerTaskHandle);
xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY, &taskScreenRotateTaskHandle);
waitUntilNoneBusy();
setCurrentScreen(preferences.getUInt("currentScreen", 0));
void IRAM_ATTR screenRotateTimerISR(void *arg) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(taskScreenRotateTaskHandle, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
void setupTimeUpdateTimer(void *pvParameters)
{
const esp_timer_create_args_t minuteTimerConfig = {
.callback = &minuteTimerISR,
.name = "minute_timer"};
void setupTasks() {
workQueue = xQueueCreate(WORK_QUEUE_SIZE, sizeof(WorkItem));
esp_timer_create(&minuteTimerConfig, &minuteTimer);
// xTaskCreate(taskPriceUpdate, "updatePrice", 1024, NULL, tskIDLE_PRIORITY,
// &priceUpdateTaskHandle); xTaskCreate(taskBlockUpdate, "updateBlock", 1024,
// NULL, tskIDLE_PRIORITY, &blockUpdateTaskHandle);
// xTaskCreate(taskTimeUpdate, "updateTime", 1024, NULL, tskIDLE_PRIORITY,
// &timeUpdateTaskHandle);
xTaskCreate(workerTask, "workerTask", 4096, NULL, tskIDLE_PRIORITY,
&workerTaskHandle);
time_t currentTime;
struct tm timeinfo;
time(&currentTime);
localtime_r(&currentTime, &timeinfo);
uint32_t secondsUntilNextMinute = 60 - timeinfo.tm_sec;
xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY,
&taskScreenRotateTaskHandle);
if (secondsUntilNextMinute > 0)
vTaskDelay(pdMS_TO_TICKS((secondsUntilNextMinute * 1000)));
waitUntilNoneBusy();
setCurrentScreen(preferences.getUInt("currentScreen", 0));
}
esp_timer_start_periodic(minuteTimer, usPerMinute);
void setupTimeUpdateTimer(void *pvParameters) {
const esp_timer_create_args_t minuteTimerConfig = {
.callback = &minuteTimerISR, .name = "minute_timer"};
esp_timer_create(&minuteTimerConfig, &minuteTimer);
time_t currentTime;
struct tm timeinfo;
time(&currentTime);
localtime_r(&currentTime, &timeinfo);
uint32_t secondsUntilNextMinute = 60 - timeinfo.tm_sec;
if (secondsUntilNextMinute > 0)
vTaskDelay(pdMS_TO_TICKS((secondsUntilNextMinute * 1000)));
esp_timer_start_periodic(minuteTimer, usPerMinute);
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
xQueueSend(workQueue, &timeUpdate, portMAX_DELAY);
// xTaskNotifyGive(timeUpdateTaskHandle);
vTaskDelete(NULL);
}
void setupScreenRotateTimer(void *pvParameters) {
const esp_timer_create_args_t screenRotateTimerConfig = {
.callback = &screenRotateTimerISR, .name = "screen_rotate_timer"};
esp_timer_create(&screenRotateTimerConfig, &screenRotateTimer);
if (preferences.getBool("timerActive", true)) {
esp_timer_start_periodic(screenRotateTimer,
getTimerSeconds() * usPerSecond);
}
vTaskDelete(NULL);
}
uint getTimerSeconds() { return preferences.getUInt("timerSeconds", 1800); }
bool isTimerActive() { return esp_timer_is_active(screenRotateTimer); }
void setTimerActive(bool status) {
if (status) {
esp_timer_start_periodic(screenRotateTimer,
getTimerSeconds() * usPerSecond);
queueLedEffect(LED_EFFECT_START_TIMER);
preferences.putBool("timerActive", true);
} else {
esp_timer_stop(screenRotateTimer);
queueLedEffect(LED_EFFECT_PAUSE_TIMER);
preferences.putBool("timerActive", false);
}
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
}
void toggleTimerActive() { setTimerActive(!isTimerActive()); }
uint getCurrentScreen() { return currentScreen; }
void setCurrentScreen(uint newScreen) {
if (newScreen != SCREEN_CUSTOM) {
preferences.putUInt("currentScreen", newScreen);
}
currentScreen = newScreen;
switch (currentScreen) {
case SCREEN_TIME: {
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
xQueueSend(workQueue, &timeUpdate, portMAX_DELAY);
// xTaskNotifyGive(timeUpdateTaskHandle);
// xTaskNotifyGive(timeUpdateTaskHandle);
break;
}
case SCREEN_HALVING_COUNTDOWN:
case SCREEN_BLOCK_HEIGHT: {
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
// xTaskNotifyGive(blockUpdateTaskHandle);
break;
}
case SCREEN_MARKET_CAP:
case SCREEN_MSCW_TIME:
case SCREEN_BTC_TICKER: {
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
// xTaskNotifyGive(priceUpdateTaskHandle);
break;
}
}
vTaskDelete(NULL);
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
}
void setupScreenRotateTimer(void *pvParameters)
{
const esp_timer_create_args_t screenRotateTimerConfig = {
.callback = &screenRotateTimerISR,
.name = "screen_rotate_timer"};
void nextScreen() {
int newCurrentScreen = (getCurrentScreen() + 1) % SCREEN_COUNT;
String key = "screen" + String(newCurrentScreen) + "Visible";
esp_timer_create(&screenRotateTimerConfig, &screenRotateTimer);
if (preferences.getBool("timerActive", true))
{
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
}
vTaskDelete(NULL);
while (!preferences.getBool(key.c_str(), true)) {
newCurrentScreen = (newCurrentScreen + 1) % SCREEN_COUNT;
key = "screen" + String(newCurrentScreen) + "Visible";
}
setCurrentScreen(newCurrentScreen);
}
uint getTimerSeconds()
{
return preferences.getUInt("timerSeconds", 1800);
void previousScreen() {
int newCurrentScreen = modulo(getCurrentScreen() - 1, SCREEN_COUNT);
String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true)) {
newCurrentScreen = modulo(newCurrentScreen - 1, SCREEN_COUNT);
key = "screen" + String(newCurrentScreen) + "Visible";
}
setCurrentScreen(newCurrentScreen);
}
bool isTimerActive()
{
return esp_timer_is_active(screenRotateTimer);
}
void showSystemStatusScreen() {
std::array<String, NUM_SCREENS> sysStatusEpdContent = {"", "", "", "",
"", "", ""};
void setTimerActive(bool status)
{
if (status)
{
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
queueLedEffect(LED_EFFECT_START_TIMER);
preferences.putBool("timerActive", true);
}
else
{
esp_timer_stop(screenRotateTimer);
queueLedEffect(LED_EFFECT_PAUSE_TIMER);
preferences.putBool("timerActive", false);
}
String ipAddr = WiFi.localIP().toString();
String subNet = WiFi.subnetMask().toString();
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
}
sysStatusEpdContent[0] = "IP/Subnet";
void toggleTimerActive()
{
setTimerActive(!isTimerActive());
}
int ipAddrPos = 0;
int subnetPos = 0;
for (int i = 0; i < 4; i++) {
sysStatusEpdContent[1 + i] = ipAddr.substring(0, ipAddr.indexOf('.')) +
"/" + subNet.substring(0, subNet.indexOf('.'));
ipAddrPos = ipAddr.indexOf('.') + 1;
subnetPos = subNet.indexOf('.') + 1;
ipAddr = ipAddr.substring(ipAddrPos);
subNet = subNet.substring(subnetPos);
}
sysStatusEpdContent[NUM_SCREENS - 2] = "RAM/Status";
uint getCurrentScreen()
{
return currentScreen;
}
void setCurrentScreen(uint newScreen)
{
if (newScreen != SCREEN_CUSTOM)
{
preferences.putUInt("currentScreen", newScreen);
}
currentScreen = newScreen;
switch (currentScreen)
{
case SCREEN_TIME:
{
WorkItem timeUpdate = {TASK_TIME_UPDATE, 0};
xQueueSend(workQueue, &timeUpdate, portMAX_DELAY);
// xTaskNotifyGive(timeUpdateTaskHandle);
break;
}
case SCREEN_HALVING_COUNTDOWN:
case SCREEN_BLOCK_HEIGHT:
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
// xTaskNotifyGive(blockUpdateTaskHandle);
break;
}
case SCREEN_MARKET_CAP:
case SCREEN_MSCW_TIME:
case SCREEN_BTC_TICKER:
{
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
// xTaskNotifyGive(priceUpdateTaskHandle);
break;
}
}
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
}
void nextScreen()
{
int newCurrentScreen = (getCurrentScreen() + 1) % SCREEN_COUNT;
String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
newCurrentScreen = (newCurrentScreen + 1) % SCREEN_COUNT;
key = "screen" + String(newCurrentScreen) + "Visible";
}
setCurrentScreen(newCurrentScreen);
}
void previousScreen()
{
int newCurrentScreen = modulo(getCurrentScreen() - 1, SCREEN_COUNT);
String key = "screen" + String(newCurrentScreen) + "Visible";
while (!preferences.getBool(key.c_str(), true))
{
newCurrentScreen = modulo(newCurrentScreen - 1, SCREEN_COUNT);
key = "screen" + String(newCurrentScreen) + "Visible";
}
setCurrentScreen(newCurrentScreen);
}
void showSystemStatusScreen()
{
std::array<String, NUM_SCREENS> sysStatusEpdContent = {"", "", "", "", "", "", ""};
String ipAddr = WiFi.localIP().toString();
String subNet = WiFi.subnetMask().toString();
sysStatusEpdContent[0] = "IP/Subnet";
int ipAddrPos = 0;
int subnetPos = 0;
for (int i = 0; i < 4; i++)
{
sysStatusEpdContent[1 + i] = ipAddr.substring(0, ipAddr.indexOf('.')) + "/" + subNet.substring(0, subNet.indexOf('.'));
ipAddrPos = ipAddr.indexOf('.') + 1;
subnetPos = subNet.indexOf('.') + 1;
ipAddr = ipAddr.substring(ipAddrPos);
subNet = subNet.substring(subnetPos);
}
sysStatusEpdContent[NUM_SCREENS - 2] = "RAM/Status";
sysStatusEpdContent[NUM_SCREENS - 1] = String((int)round(ESP.getFreeHeap() / 1024)) + "/" + (int)round(ESP.getHeapSize() / 1024);
setCurrentScreen(SCREEN_CUSTOM);
setEpdContent(sysStatusEpdContent);
sysStatusEpdContent[NUM_SCREENS - 1] =
String((int)round(ESP.getFreeHeap() / 1024)) + "/" +
(int)round(ESP.getHeapSize() / 1024);
setCurrentScreen(SCREEN_CUSTOM);
setEpdContent(sysStatusEpdContent);
}

View File

@ -2,13 +2,13 @@
#include <esp_timer.h>
#include <data_handler.hpp>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <data_handler.hpp>
#include "price_fetch.hpp"
#include "shared.hpp"
#include "lib/epd.hpp"
#include "lib/price_fetch.hpp"
#include "lib/shared.hpp"
// extern TaskHandle_t priceUpdateTaskHandle;
// extern TaskHandle_t blockUpdateTaskHandle;
@ -21,17 +21,15 @@ extern esp_timer_handle_t minuteTimer;
extern QueueHandle_t workQueue;
typedef enum
{
TASK_PRICE_UPDATE,
TASK_BLOCK_UPDATE,
TASK_TIME_UPDATE
typedef enum {
TASK_PRICE_UPDATE,
TASK_BLOCK_UPDATE,
TASK_TIME_UPDATE
} TaskType;
typedef struct
{
TaskType type;
char data;
typedef struct {
TaskType type;
char data;
} WorkItem;
void workerTask(void *pvParameters);
@ -45,8 +43,8 @@ void showSystemStatusScreen();
void setupTimeUpdateTimer(void *pvParameters);
void setupScreenRotateTimer(void *pvParameters);
void IRAM_ATTR minuteTimerISR(void* arg);
void IRAM_ATTR screenRotateTimerISR(void* arg);
void IRAM_ATTR minuteTimerISR(void *arg);
void IRAM_ATTR screenRotateTimerISR(void *arg);
// void taskPriceUpdate(void *pvParameters);
// void taskBlockUpdate(void *pvParameters);

View File

@ -1,13 +1,13 @@
#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <Adafruit_MCP23X17.h>
#include <ArduinoJson.h>
#include <Preferences.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <mutex>
#include "utils.hpp"
#include <utils.hpp>
extern Adafruit_MCP23X17 mcp1;
#ifdef IS_BTCLOCK_S3
@ -26,23 +26,22 @@ const PROGMEM int SCREEN_MARKET_CAP = 5;
const PROGMEM int SCREEN_COUNTDOWN = 98;
const PROGMEM int SCREEN_CUSTOM = 99;
const int SCREEN_COUNT = 6;
const PROGMEM int screens[SCREEN_COUNT] = { SCREEN_BLOCK_HEIGHT, SCREEN_MSCW_TIME, SCREEN_BTC_TICKER, SCREEN_TIME, SCREEN_HALVING_COUNTDOWN, SCREEN_MARKET_CAP };
const PROGMEM int screens[SCREEN_COUNT] = {
SCREEN_BLOCK_HEIGHT, SCREEN_MSCW_TIME, SCREEN_BTC_TICKER,
SCREEN_TIME, SCREEN_HALVING_COUNTDOWN, SCREEN_MARKET_CAP};
const int usPerSecond = 1000000;
const int usPerMinute = 60 * usPerSecond;
struct SpiRamAllocator {
void* allocate(size_t size) {
void *allocate(size_t size) {
return heap_caps_malloc(size, MALLOC_CAP_SPIRAM);
}
void deallocate(void* pointer) {
heap_caps_free(pointer);
}
void deallocate(void *pointer) { heap_caps_free(pointer); }
void* reallocate(void* ptr, size_t new_size) {
void *reallocate(void *ptr, size_t new_size) {
return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM);
}
};
using SpiRamJsonDocument = BasicJsonDocument<SpiRamAllocator>;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
#pragma once
#include "WebServer.h"
#include "ESPAsyncWebServer.h"
#include <ArduinoJson.h>
@ -7,10 +8,19 @@
#include "AsyncJson.h"
#include <iostream>
// #include "AsyncJson.h"
// #include "ESPAsyncWebServer.h"
// #include "WebServer.h"
// #include <ArduinoJson.h>
// #include <ESPmDNS.h>
// #include <LittleFS.h>
// #include <iostream>
#include "lib/block_notify.hpp"
#include "lib/led_handler.hpp"
#include "lib/price_notify.hpp"
#include "lib/screen_handler.hpp"
#include "lib/led_handler.hpp"
#include "webserver/OneParamRewrite.hpp"
@ -40,7 +50,6 @@ void onApiLightsOff(AsyncWebServerRequest *request);
void onApiLightsSetColor(AsyncWebServerRequest *request);
void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json);
void onApiRestart(AsyncWebServerRequest *request);
void onIndex(AsyncWebServerRequest *request);

View File

@ -1,43 +1,31 @@
#include "OneParamRewrite.hpp"
OneParamRewrite::OneParamRewrite(const char *from, const char *to)
: AsyncWebRewrite(from, to)
{
: AsyncWebRewrite(from, to) {
_paramIndex = _from.indexOf('{');
_paramIndex = _from.indexOf('{');
if (_paramIndex >= 0 && _from.endsWith("}"))
{
_urlPrefix = _from.substring(0, _paramIndex);
int index = _params.indexOf('{');
if (index >= 0)
{
_params = _params.substring(0, index);
}
if (_paramIndex >= 0 && _from.endsWith("}")) {
_urlPrefix = _from.substring(0, _paramIndex);
int index = _params.indexOf('{');
if (index >= 0) {
_params = _params.substring(0, index);
}
else
{
_urlPrefix = _from;
}
_paramsBackup = _params;
} else {
_urlPrefix = _from;
}
_paramsBackup = _params;
}
bool OneParamRewrite::match(AsyncWebServerRequest *request)
{
if (request->url().startsWith(_urlPrefix))
{
if (_paramIndex >= 0)
{
_params = _paramsBackup + request->url().substring(_paramIndex);
}
else
{
_params = _paramsBackup;
}
return true;
}
else
{
return false;
bool OneParamRewrite::match(AsyncWebServerRequest *request) {
if (request->url().startsWith(_urlPrefix)) {
if (_paramIndex >= 0) {
_params = _paramsBackup + request->url().substring(_paramIndex);
} else {
_params = _paramsBackup;
}
return true;
} else {
return false;
}
};

View File

@ -2,14 +2,13 @@
#include "ESPAsyncWebServer.h"
class OneParamRewrite : public AsyncWebRewrite
{
class OneParamRewrite : public AsyncWebRewrite {
protected:
String _urlPrefix;
int _paramIndex;
String _paramsBackup;
String _urlPrefix;
int _paramIndex;
String _paramsBackup;
public:
OneParamRewrite(const char *from, const char *to);
bool match(AsyncWebServerRequest *request) override;
OneParamRewrite(const char *from, const char *to);
bool match(AsyncWebServerRequest *request) override;
};

View File

@ -1,50 +1,60 @@
/*
* Copyright 2023 Djuri Baars
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Arduino.h"
#include <WiFiManager.h>
#define WEBSERVER_H
#include "ESPAsyncWebServer.h"
#include "ESPAsyncWebServer.h"
#include "lib/config.hpp"
//char ptrTaskList[400];
uint wifiLostConnection;
extern "C" void app_main()
{
initArduino();
extern "C" void app_main() {
initArduino();
Serial.begin(115200);
setup();
Serial.begin(115200);
setup();
while (true)
{
// vTaskList(ptrTaskList);
// Serial.println(F("**********************************"));
// Serial.println(F("Task State Prio Stack Num"));
// Serial.println(F("**********************************"));
// Serial.print(ptrTaskList);
// Serial.println(F("**********************************"));
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
while (true) {
// vTaskList(ptrTaskList);
// Serial.println(F("**********************************"));
// Serial.println(F("Task State Prio Stack Num"));
// Serial.println(F("**********************************"));
// Serial.print(ptrTaskList);
// Serial.println(F("**********************************"));
if (eventSourceTaskHandle != NULL)
xTaskNotifyGive(eventSourceTaskHandle);
if (!WiFi.isConnected()) {
if (!wifiLostConnection) {
wifiLostConnection = esp_timer_get_time() / 1000000;
Serial.println("Lost WiFi connection, trying to reconnect...");
}
if (!WiFi.isConnected()) {
if (!wifiLostConnection) {
wifiLostConnection = esp_timer_get_time() / 1000000;
Serial.println("Lost WiFi connection, trying to reconnect...");
}
if (((esp_timer_get_time() / 1000000) - wifiLostConnection) > 600) {
Serial.println("Still no connection after 10 minutes, restarting...");
delay(2000);
ESP.restart();
}
if (((esp_timer_get_time() / 1000000) - wifiLostConnection) > 600) {
Serial.println("Still no connection after 10 minutes, restarting...");
delay(2000);
ESP.restart();
}
WiFi.begin();
} else if (wifiLostConnection) {
wifiLostConnection = 0;
Serial.println("Connection restored, reset timer.");
}
vTaskDelay(pdMS_TO_TICKS(5000));
WiFi.begin();
} else if (wifiLostConnection) {
wifiLostConnection = 0;
Serial.println("Connection restored, reset timer.");
}
vTaskDelay(pdMS_TO_TICKS(5000));
}
}