diff --git a/data b/data index 8389ed8..033fe09 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 8389ed8e36a9a1a7a39ca31f53324a0949aedb1d +Subproject commit 033fe098295ab6da6568d6298b4380e51bec0b98 diff --git a/dependencies.lock b/dependencies.lock index 2274b85..c338e6c 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -4,6 +4,6 @@ dependencies: source: type: idf version: 4.4.7 -manifest_hash: 1d4ef353a86901733b106a1897b186dbf9fc091a4981f0560ea2f6899b7a3d44 +manifest_hash: cd2f3ee15e776d949eb4ea4eddc8f39b30c2a7905050850eed01ab4928143cff target: esp32s3 version: 1.0.0 diff --git a/include/timezone_data.hpp b/include/timezone_data.hpp deleted file mode 100644 index 1ebb5db..0000000 --- a/include/timezone_data.hpp +++ /dev/null @@ -1,721 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace timezone_data { - -// Enum for unique timezone strings -enum class TimezoneValue : uint8_t { - plus000plus02_2M350_1M1050_3, - plus01_1, - plus02_2, - plus0330_330, - plus03_3, - plus0430_430, - plus04_4, - plus0530_530, - plus0545_545, - plus05_5, - plus0630_630, - plus06_6, - plus07_7, - plus0845_845, - plus08_8, - plus09_9, - plus1030_1030plus11_11M1010M410, - plus10_10, - plus11_11, - plus11_11plus12M1010M410_3, - plus1245_1245plus1345M950_245M410_345, - plus12_12, - plus13_13, - plus14_14, - _011, - _011plus00M350_0M1050_1, - _022, - _022_01M350__1M1050_0, - _033, - _033_02M320M1110, - _044, - _044_03M1010_0M340_0, - _044_03M916_24M416_24, - _055, - _066, - _066_05M916_22M416_22, - _077, - _088, - _0930930, - _099, - _1010, - _1111, - _1212, - ACST_930, - ACST_930ACDTM1010M410_3, - AEST_10, - AEST_10AEDTM1010M410_3, - AKST9AKDTM320M1110, - AST4, - AST4ADTM320M1110, - AWST_8, - CAT_2, - CET_1, - CET_1CESTM350M1050_3, - CST_8, - CST5CDTM320_0M1110_1, - CST6, - CST6CDTM320M1110, - ChST_10, - EAT_3, - EET_2, - EET_2EESTM344_50M1044_50, - EET_2EESTM350M1050_3, - EET_2EESTM350_0M1050_0, - EET_2EESTM350_3M1050_4, - EET_2EESTM455_0M1054_24, - EST5, - EST5EDTM320M1110, - GMT0, - GMT0BSTM350_1M1050, - HKT_8, - HST10, - HST10HDTM320M1110, - IST_1GMT0M1050M350_1, - IST_2IDTM344_26M1050, - IST_530, - JST_9, - KST_9, - MSK_3, - MST7, - MST7MDTM320M1110, - NST330NDTM320M1110, - NZST_12NZDTM950M410_3, - PKT_5, - PST_8, - PST8PDTM320M1110, - SAST_2, - SST11, - UTC0, - WAT_1, - WET0WESTM350_1M1050, - WIB_7, - WIT_9, - WITA_8, -}; - -// Key-value pair type -using TimezoneEntry = std::pair; - -// Lookup table -constexpr std::array TIMEZONE_DATA = {{ - {"Africa/Abidjan", TimezoneValue::GMT0}, - {"Africa/Accra", TimezoneValue::GMT0}, - {"Africa/Addis_Ababa", TimezoneValue::EAT_3}, - {"Africa/Algiers", TimezoneValue::CET_1}, - {"Africa/Asmara", TimezoneValue::EAT_3}, - {"Africa/Bamako", TimezoneValue::GMT0}, - {"Africa/Bangui", TimezoneValue::WAT_1}, - {"Africa/Banjul", TimezoneValue::GMT0}, - {"Africa/Bissau", TimezoneValue::GMT0}, - {"Africa/Blantyre", TimezoneValue::CAT_2}, - {"Africa/Brazzaville", TimezoneValue::WAT_1}, - {"Africa/Bujumbura", TimezoneValue::CAT_2}, - {"Africa/Cairo", TimezoneValue::EET_2EESTM455_0M1054_24}, - {"Africa/Casablanca", TimezoneValue::plus01_1}, - {"Africa/Ceuta", TimezoneValue::CET_1CESTM350M1050_3}, - {"Africa/Conakry", TimezoneValue::GMT0}, - {"Africa/Dakar", TimezoneValue::GMT0}, - {"Africa/Dar_es_Salaam", TimezoneValue::EAT_3}, - {"Africa/Djibouti", TimezoneValue::EAT_3}, - {"Africa/Douala", TimezoneValue::WAT_1}, - {"Africa/El_Aaiun", TimezoneValue::plus01_1}, - {"Africa/Freetown", TimezoneValue::GMT0}, - {"Africa/Gaborone", TimezoneValue::CAT_2}, - {"Africa/Harare", TimezoneValue::CAT_2}, - {"Africa/Johannesburg", TimezoneValue::SAST_2}, - {"Africa/Juba", TimezoneValue::CAT_2}, - {"Africa/Kampala", TimezoneValue::EAT_3}, - {"Africa/Khartoum", TimezoneValue::CAT_2}, - {"Africa/Kigali", TimezoneValue::CAT_2}, - {"Africa/Kinshasa", TimezoneValue::WAT_1}, - {"Africa/Lagos", TimezoneValue::WAT_1}, - {"Africa/Libreville", TimezoneValue::WAT_1}, - {"Africa/Lome", TimezoneValue::GMT0}, - {"Africa/Luanda", TimezoneValue::WAT_1}, - {"Africa/Lubumbashi", TimezoneValue::CAT_2}, - {"Africa/Lusaka", TimezoneValue::CAT_2}, - {"Africa/Malabo", TimezoneValue::WAT_1}, - {"Africa/Maputo", TimezoneValue::CAT_2}, - {"Africa/Maseru", TimezoneValue::SAST_2}, - {"Africa/Mbabane", TimezoneValue::SAST_2}, - {"Africa/Mogadishu", TimezoneValue::EAT_3}, - {"Africa/Monrovia", TimezoneValue::GMT0}, - {"Africa/Nairobi", TimezoneValue::EAT_3}, - {"Africa/Ndjamena", TimezoneValue::WAT_1}, - {"Africa/Niamey", TimezoneValue::WAT_1}, - {"Africa/Nouakchott", TimezoneValue::GMT0}, - {"Africa/Ouagadougou", TimezoneValue::GMT0}, - {"Africa/Porto-Novo", TimezoneValue::WAT_1}, - {"Africa/Sao_Tome", TimezoneValue::GMT0}, - {"Africa/Tripoli", TimezoneValue::EET_2}, - {"Africa/Tunis", TimezoneValue::CET_1}, - {"Africa/Windhoek", TimezoneValue::CAT_2}, - {"America/Adak", TimezoneValue::HST10HDTM320M1110}, - {"America/Anchorage", TimezoneValue::AKST9AKDTM320M1110}, - {"America/Anguilla", TimezoneValue::AST4}, - {"America/Antigua", TimezoneValue::AST4}, - {"America/Araguaina", TimezoneValue::_033}, - {"America/Argentina/Buenos_Aires", TimezoneValue::_033}, - {"America/Argentina/Catamarca", TimezoneValue::_033}, - {"America/Argentina/Cordoba", TimezoneValue::_033}, - {"America/Argentina/Jujuy", TimezoneValue::_033}, - {"America/Argentina/La_Rioja", TimezoneValue::_033}, - {"America/Argentina/Mendoza", TimezoneValue::_033}, - {"America/Argentina/Rio_Gallegos", TimezoneValue::_033}, - {"America/Argentina/Salta", TimezoneValue::_033}, - {"America/Argentina/San_Juan", TimezoneValue::_033}, - {"America/Argentina/San_Luis", TimezoneValue::_033}, - {"America/Argentina/Tucuman", TimezoneValue::_033}, - {"America/Argentina/Ushuaia", TimezoneValue::_033}, - {"America/Aruba", TimezoneValue::AST4}, - {"America/Asuncion", TimezoneValue::_044_03M1010_0M340_0}, - {"America/Atikokan", TimezoneValue::EST5}, - {"America/Bahia", TimezoneValue::_033}, - {"America/Bahia_Banderas", TimezoneValue::CST6}, - {"America/Barbados", TimezoneValue::AST4}, - {"America/Belem", TimezoneValue::_033}, - {"America/Belize", TimezoneValue::CST6}, - {"America/Blanc-Sablon", TimezoneValue::AST4}, - {"America/Boa_Vista", TimezoneValue::_044}, - {"America/Bogota", TimezoneValue::_055}, - {"America/Boise", TimezoneValue::MST7MDTM320M1110}, - {"America/Cambridge_Bay", TimezoneValue::MST7MDTM320M1110}, - {"America/Campo_Grande", TimezoneValue::_044}, - {"America/Cancun", TimezoneValue::EST5}, - {"America/Caracas", TimezoneValue::_044}, - {"America/Cayenne", TimezoneValue::_033}, - {"America/Cayman", TimezoneValue::EST5}, - {"America/Chicago", TimezoneValue::CST6CDTM320M1110}, - {"America/Chihuahua", TimezoneValue::CST6}, - {"America/Costa_Rica", TimezoneValue::CST6}, - {"America/Creston", TimezoneValue::MST7}, - {"America/Cuiaba", TimezoneValue::_044}, - {"America/Curacao", TimezoneValue::AST4}, - {"America/Danmarkshavn", TimezoneValue::GMT0}, - {"America/Dawson", TimezoneValue::MST7}, - {"America/Dawson_Creek", TimezoneValue::MST7}, - {"America/Denver", TimezoneValue::MST7MDTM320M1110}, - {"America/Detroit", TimezoneValue::EST5EDTM320M1110}, - {"America/Dominica", TimezoneValue::AST4}, - {"America/Edmonton", TimezoneValue::MST7MDTM320M1110}, - {"America/Eirunepe", TimezoneValue::_055}, - {"America/El_Salvador", TimezoneValue::CST6}, - {"America/Fort_Nelson", TimezoneValue::MST7}, - {"America/Fortaleza", TimezoneValue::_033}, - {"America/Glace_Bay", TimezoneValue::AST4ADTM320M1110}, - {"America/Godthab", TimezoneValue::_022_01M350__1M1050_0}, - {"America/Goose_Bay", TimezoneValue::AST4ADTM320M1110}, - {"America/Grand_Turk", TimezoneValue::EST5EDTM320M1110}, - {"America/Grenada", TimezoneValue::AST4}, - {"America/Guadeloupe", TimezoneValue::AST4}, - {"America/Guatemala", TimezoneValue::CST6}, - {"America/Guayaquil", TimezoneValue::_055}, - {"America/Guyana", TimezoneValue::_044}, - {"America/Halifax", TimezoneValue::AST4ADTM320M1110}, - {"America/Havana", TimezoneValue::CST5CDTM320_0M1110_1}, - {"America/Hermosillo", TimezoneValue::MST7}, - {"America/Indiana/Indianapolis", TimezoneValue::EST5EDTM320M1110}, - {"America/Indiana/Knox", TimezoneValue::CST6CDTM320M1110}, - {"America/Indiana/Marengo", TimezoneValue::EST5EDTM320M1110}, - {"America/Indiana/Petersburg", TimezoneValue::EST5EDTM320M1110}, - {"America/Indiana/Tell_City", TimezoneValue::CST6CDTM320M1110}, - {"America/Indiana/Vevay", TimezoneValue::EST5EDTM320M1110}, - {"America/Indiana/Vincennes", TimezoneValue::EST5EDTM320M1110}, - {"America/Indiana/Winamac", TimezoneValue::EST5EDTM320M1110}, - {"America/Inuvik", TimezoneValue::MST7MDTM320M1110}, - {"America/Iqaluit", TimezoneValue::EST5EDTM320M1110}, - {"America/Jamaica", TimezoneValue::EST5}, - {"America/Juneau", TimezoneValue::AKST9AKDTM320M1110}, - {"America/Kentucky/Louisville", TimezoneValue::EST5EDTM320M1110}, - {"America/Kentucky/Monticello", TimezoneValue::EST5EDTM320M1110}, - {"America/Kralendijk", TimezoneValue::AST4}, - {"America/La_Paz", TimezoneValue::_044}, - {"America/Lima", TimezoneValue::_055}, - {"America/Los_Angeles", TimezoneValue::PST8PDTM320M1110}, - {"America/Lower_Princes", TimezoneValue::AST4}, - {"America/Maceio", TimezoneValue::_033}, - {"America/Managua", TimezoneValue::CST6}, - {"America/Manaus", TimezoneValue::_044}, - {"America/Marigot", TimezoneValue::AST4}, - {"America/Martinique", TimezoneValue::AST4}, - {"America/Matamoros", TimezoneValue::CST6CDTM320M1110}, - {"America/Mazatlan", TimezoneValue::MST7}, - {"America/Menominee", TimezoneValue::CST6CDTM320M1110}, - {"America/Merida", TimezoneValue::CST6}, - {"America/Metlakatla", TimezoneValue::AKST9AKDTM320M1110}, - {"America/Mexico_City", TimezoneValue::CST6}, - {"America/Miquelon", TimezoneValue::_033_02M320M1110}, - {"America/Moncton", TimezoneValue::AST4ADTM320M1110}, - {"America/Monterrey", TimezoneValue::CST6}, - {"America/Montevideo", TimezoneValue::_033}, - {"America/Montreal", TimezoneValue::EST5EDTM320M1110}, - {"America/Montserrat", TimezoneValue::AST4}, - {"America/Nassau", TimezoneValue::EST5EDTM320M1110}, - {"America/New_York", TimezoneValue::EST5EDTM320M1110}, - {"America/Nipigon", TimezoneValue::EST5EDTM320M1110}, - {"America/Nome", TimezoneValue::AKST9AKDTM320M1110}, - {"America/Noronha", TimezoneValue::_022}, - {"America/North_Dakota/Beulah", TimezoneValue::CST6CDTM320M1110}, - {"America/North_Dakota/Center", TimezoneValue::CST6CDTM320M1110}, - {"America/North_Dakota/New_Salem", TimezoneValue::CST6CDTM320M1110}, - {"America/Nuuk", TimezoneValue::_022_01M350__1M1050_0}, - {"America/Ojinaga", TimezoneValue::CST6CDTM320M1110}, - {"America/Panama", TimezoneValue::EST5}, - {"America/Pangnirtung", TimezoneValue::EST5EDTM320M1110}, - {"America/Paramaribo", TimezoneValue::_033}, - {"America/Phoenix", TimezoneValue::MST7}, - {"America/Port-au-Prince", TimezoneValue::EST5EDTM320M1110}, - {"America/Port_of_Spain", TimezoneValue::AST4}, - {"America/Porto_Velho", TimezoneValue::_044}, - {"America/Puerto_Rico", TimezoneValue::AST4}, - {"America/Punta_Arenas", TimezoneValue::_033}, - {"America/Rainy_River", TimezoneValue::CST6CDTM320M1110}, - {"America/Rankin_Inlet", TimezoneValue::CST6CDTM320M1110}, - {"America/Recife", TimezoneValue::_033}, - {"America/Regina", TimezoneValue::CST6}, - {"America/Resolute", TimezoneValue::CST6CDTM320M1110}, - {"America/Rio_Branco", TimezoneValue::_055}, - {"America/Santarem", TimezoneValue::_033}, - {"America/Santiago", TimezoneValue::_044_03M916_24M416_24}, - {"America/Santo_Domingo", TimezoneValue::AST4}, - {"America/Sao_Paulo", TimezoneValue::_033}, - {"America/Scoresbysund", TimezoneValue::_022_01M350__1M1050_0}, - {"America/Sitka", TimezoneValue::AKST9AKDTM320M1110}, - {"America/St_Barthelemy", TimezoneValue::AST4}, - {"America/St_Johns", TimezoneValue::NST330NDTM320M1110}, - {"America/St_Kitts", TimezoneValue::AST4}, - {"America/St_Lucia", TimezoneValue::AST4}, - {"America/St_Thomas", TimezoneValue::AST4}, - {"America/St_Vincent", TimezoneValue::AST4}, - {"America/Swift_Current", TimezoneValue::CST6}, - {"America/Tegucigalpa", TimezoneValue::CST6}, - {"America/Thule", TimezoneValue::AST4ADTM320M1110}, - {"America/Thunder_Bay", TimezoneValue::EST5EDTM320M1110}, - {"America/Tijuana", TimezoneValue::PST8PDTM320M1110}, - {"America/Toronto", TimezoneValue::EST5EDTM320M1110}, - {"America/Tortola", TimezoneValue::AST4}, - {"America/Vancouver", TimezoneValue::PST8PDTM320M1110}, - {"America/Whitehorse", TimezoneValue::MST7}, - {"America/Winnipeg", TimezoneValue::CST6CDTM320M1110}, - {"America/Yakutat", TimezoneValue::AKST9AKDTM320M1110}, - {"America/Yellowknife", TimezoneValue::MST7MDTM320M1110}, - {"Antarctica/Casey", TimezoneValue::plus08_8}, - {"Antarctica/Davis", TimezoneValue::plus07_7}, - {"Antarctica/DumontDUrville", TimezoneValue::plus10_10}, - {"Antarctica/Macquarie", TimezoneValue::AEST_10AEDTM1010M410_3}, - {"Antarctica/Mawson", TimezoneValue::plus05_5}, - {"Antarctica/McMurdo", TimezoneValue::NZST_12NZDTM950M410_3}, - {"Antarctica/Palmer", TimezoneValue::_033}, - {"Antarctica/Rothera", TimezoneValue::_033}, - {"Antarctica/Syowa", TimezoneValue::plus03_3}, - {"Antarctica/Troll", TimezoneValue::plus000plus02_2M350_1M1050_3}, - {"Antarctica/Vostok", TimezoneValue::plus05_5}, - {"Arctic/Longyearbyen", TimezoneValue::CET_1CESTM350M1050_3}, - {"Asia/Aden", TimezoneValue::plus03_3}, - {"Asia/Almaty", TimezoneValue::plus05_5}, - {"Asia/Amman", TimezoneValue::plus03_3}, - {"Asia/Anadyr", TimezoneValue::plus12_12}, - {"Asia/Aqtau", TimezoneValue::plus05_5}, - {"Asia/Aqtobe", TimezoneValue::plus05_5}, - {"Asia/Ashgabat", TimezoneValue::plus05_5}, - {"Asia/Atyrau", TimezoneValue::plus05_5}, - {"Asia/Baghdad", TimezoneValue::plus03_3}, - {"Asia/Bahrain", TimezoneValue::plus03_3}, - {"Asia/Baku", TimezoneValue::plus04_4}, - {"Asia/Bangkok", TimezoneValue::plus07_7}, - {"Asia/Barnaul", TimezoneValue::plus07_7}, - {"Asia/Beirut", TimezoneValue::EET_2EESTM350_0M1050_0}, - {"Asia/Bishkek", TimezoneValue::plus06_6}, - {"Asia/Brunei", TimezoneValue::plus08_8}, - {"Asia/Chita", TimezoneValue::plus09_9}, - {"Asia/Choibalsan", TimezoneValue::plus08_8}, - {"Asia/Colombo", TimezoneValue::plus0530_530}, - {"Asia/Damascus", TimezoneValue::plus03_3}, - {"Asia/Dhaka", TimezoneValue::plus06_6}, - {"Asia/Dili", TimezoneValue::plus09_9}, - {"Asia/Dubai", TimezoneValue::plus04_4}, - {"Asia/Dushanbe", TimezoneValue::plus05_5}, - {"Asia/Famagusta", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Asia/Gaza", TimezoneValue::EET_2EESTM344_50M1044_50}, - {"Asia/Hebron", TimezoneValue::EET_2EESTM344_50M1044_50}, - {"Asia/Ho_Chi_Minh", TimezoneValue::plus07_7}, - {"Asia/Hong_Kong", TimezoneValue::HKT_8}, - {"Asia/Hovd", TimezoneValue::plus07_7}, - {"Asia/Irkutsk", TimezoneValue::plus08_8}, - {"Asia/Jakarta", TimezoneValue::WIB_7}, - {"Asia/Jayapura", TimezoneValue::WIT_9}, - {"Asia/Jerusalem", TimezoneValue::IST_2IDTM344_26M1050}, - {"Asia/Kabul", TimezoneValue::plus0430_430}, - {"Asia/Kamchatka", TimezoneValue::plus12_12}, - {"Asia/Karachi", TimezoneValue::PKT_5}, - {"Asia/Kathmandu", TimezoneValue::plus0545_545}, - {"Asia/Khandyga", TimezoneValue::plus09_9}, - {"Asia/Kolkata", TimezoneValue::IST_530}, - {"Asia/Krasnoyarsk", TimezoneValue::plus07_7}, - {"Asia/Kuala_Lumpur", TimezoneValue::plus08_8}, - {"Asia/Kuching", TimezoneValue::plus08_8}, - {"Asia/Kuwait", TimezoneValue::plus03_3}, - {"Asia/Macau", TimezoneValue::CST_8}, - {"Asia/Magadan", TimezoneValue::plus11_11}, - {"Asia/Makassar", TimezoneValue::WITA_8}, - {"Asia/Manila", TimezoneValue::PST_8}, - {"Asia/Muscat", TimezoneValue::plus04_4}, - {"Asia/Nicosia", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Asia/Novokuznetsk", TimezoneValue::plus07_7}, - {"Asia/Novosibirsk", TimezoneValue::plus07_7}, - {"Asia/Omsk", TimezoneValue::plus06_6}, - {"Asia/Oral", TimezoneValue::plus05_5}, - {"Asia/Phnom_Penh", TimezoneValue::plus07_7}, - {"Asia/Pontianak", TimezoneValue::WIB_7}, - {"Asia/Pyongyang", TimezoneValue::KST_9}, - {"Asia/Qatar", TimezoneValue::plus03_3}, - {"Asia/Qyzylorda", TimezoneValue::plus05_5}, - {"Asia/Riyadh", TimezoneValue::plus03_3}, - {"Asia/Sakhalin", TimezoneValue::plus11_11}, - {"Asia/Samarkand", TimezoneValue::plus05_5}, - {"Asia/Seoul", TimezoneValue::KST_9}, - {"Asia/Shanghai", TimezoneValue::CST_8}, - {"Asia/Singapore", TimezoneValue::plus08_8}, - {"Asia/Srednekolymsk", TimezoneValue::plus11_11}, - {"Asia/Taipei", TimezoneValue::CST_8}, - {"Asia/Tashkent", TimezoneValue::plus05_5}, - {"Asia/Tbilisi", TimezoneValue::plus04_4}, - {"Asia/Tehran", TimezoneValue::plus0330_330}, - {"Asia/Thimphu", TimezoneValue::plus06_6}, - {"Asia/Tokyo", TimezoneValue::JST_9}, - {"Asia/Tomsk", TimezoneValue::plus07_7}, - {"Asia/Ulaanbaatar", TimezoneValue::plus08_8}, - {"Asia/Urumqi", TimezoneValue::plus06_6}, - {"Asia/Ust-Nera", TimezoneValue::plus10_10}, - {"Asia/Vientiane", TimezoneValue::plus07_7}, - {"Asia/Vladivostok", TimezoneValue::plus10_10}, - {"Asia/Yakutsk", TimezoneValue::plus09_9}, - {"Asia/Yangon", TimezoneValue::plus0630_630}, - {"Asia/Yekaterinburg", TimezoneValue::plus05_5}, - {"Asia/Yerevan", TimezoneValue::plus04_4}, - {"Atlantic/Azores", TimezoneValue::_011plus00M350_0M1050_1}, - {"Atlantic/Bermuda", TimezoneValue::AST4ADTM320M1110}, - {"Atlantic/Canary", TimezoneValue::WET0WESTM350_1M1050}, - {"Atlantic/Cape_Verde", TimezoneValue::_011}, - {"Atlantic/Faroe", TimezoneValue::WET0WESTM350_1M1050}, - {"Atlantic/Madeira", TimezoneValue::WET0WESTM350_1M1050}, - {"Atlantic/Reykjavik", TimezoneValue::GMT0}, - {"Atlantic/South_Georgia", TimezoneValue::_022}, - {"Atlantic/St_Helena", TimezoneValue::GMT0}, - {"Atlantic/Stanley", TimezoneValue::_033}, - {"Australia/Adelaide", TimezoneValue::ACST_930ACDTM1010M410_3}, - {"Australia/Brisbane", TimezoneValue::AEST_10}, - {"Australia/Broken_Hill", TimezoneValue::ACST_930ACDTM1010M410_3}, - {"Australia/Currie", TimezoneValue::AEST_10AEDTM1010M410_3}, - {"Australia/Darwin", TimezoneValue::ACST_930}, - {"Australia/Eucla", TimezoneValue::plus0845_845}, - {"Australia/Hobart", TimezoneValue::AEST_10AEDTM1010M410_3}, - {"Australia/Lindeman", TimezoneValue::AEST_10}, - {"Australia/Lord_Howe", TimezoneValue::plus1030_1030plus11_11M1010M410}, - {"Australia/Melbourne", TimezoneValue::AEST_10AEDTM1010M410_3}, - {"Australia/Perth", TimezoneValue::AWST_8}, - {"Australia/Sydney", TimezoneValue::AEST_10AEDTM1010M410_3}, - {"Etc/GMT", TimezoneValue::GMT0}, - {"Etc/GMT+0", TimezoneValue::GMT0}, - {"Etc/GMT+1", TimezoneValue::_011}, - {"Etc/GMT+10", TimezoneValue::_1010}, - {"Etc/GMT+11", TimezoneValue::_1111}, - {"Etc/GMT+12", TimezoneValue::_1212}, - {"Etc/GMT+2", TimezoneValue::_022}, - {"Etc/GMT+3", TimezoneValue::_033}, - {"Etc/GMT+4", TimezoneValue::_044}, - {"Etc/GMT+5", TimezoneValue::_055}, - {"Etc/GMT+6", TimezoneValue::_066}, - {"Etc/GMT+7", TimezoneValue::_077}, - {"Etc/GMT+8", TimezoneValue::_088}, - {"Etc/GMT+9", TimezoneValue::_099}, - {"Etc/GMT-0", TimezoneValue::GMT0}, - {"Etc/GMT-1", TimezoneValue::plus01_1}, - {"Etc/GMT-10", TimezoneValue::plus10_10}, - {"Etc/GMT-11", TimezoneValue::plus11_11}, - {"Etc/GMT-12", TimezoneValue::plus12_12}, - {"Etc/GMT-13", TimezoneValue::plus13_13}, - {"Etc/GMT-14", TimezoneValue::plus14_14}, - {"Etc/GMT-2", TimezoneValue::plus02_2}, - {"Etc/GMT-3", TimezoneValue::plus03_3}, - {"Etc/GMT-4", TimezoneValue::plus04_4}, - {"Etc/GMT-5", TimezoneValue::plus05_5}, - {"Etc/GMT-6", TimezoneValue::plus06_6}, - {"Etc/GMT-7", TimezoneValue::plus07_7}, - {"Etc/GMT-8", TimezoneValue::plus08_8}, - {"Etc/GMT-9", TimezoneValue::plus09_9}, - {"Etc/GMT0", TimezoneValue::GMT0}, - {"Etc/Greenwich", TimezoneValue::GMT0}, - {"Etc/UCT", TimezoneValue::UTC0}, - {"Etc/UTC", TimezoneValue::UTC0}, - {"Etc/Universal", TimezoneValue::UTC0}, - {"Etc/Zulu", TimezoneValue::UTC0}, - {"Europe/Amsterdam", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Andorra", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Astrakhan", TimezoneValue::plus04_4}, - {"Europe/Athens", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Belgrade", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Berlin", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Bratislava", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Brussels", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Bucharest", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Budapest", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Busingen", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Chisinau", TimezoneValue::EET_2EESTM350M1050_3}, - {"Europe/Copenhagen", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Dublin", TimezoneValue::IST_1GMT0M1050M350_1}, - {"Europe/Gibraltar", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Guernsey", TimezoneValue::GMT0BSTM350_1M1050}, - {"Europe/Helsinki", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Isle_of_Man", TimezoneValue::GMT0BSTM350_1M1050}, - {"Europe/Istanbul", TimezoneValue::plus03_3}, - {"Europe/Jersey", TimezoneValue::GMT0BSTM350_1M1050}, - {"Europe/Kaliningrad", TimezoneValue::EET_2}, - {"Europe/Kiev", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Kirov", TimezoneValue::MSK_3}, - {"Europe/Lisbon", TimezoneValue::WET0WESTM350_1M1050}, - {"Europe/Ljubljana", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/London", TimezoneValue::GMT0BSTM350_1M1050}, - {"Europe/Luxembourg", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Madrid", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Malta", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Mariehamn", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Minsk", TimezoneValue::plus03_3}, - {"Europe/Monaco", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Moscow", TimezoneValue::MSK_3}, - {"Europe/Oslo", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Paris", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Podgorica", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Prague", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Riga", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Rome", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Samara", TimezoneValue::plus04_4}, - {"Europe/San_Marino", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Sarajevo", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Saratov", TimezoneValue::plus04_4}, - {"Europe/Simferopol", TimezoneValue::MSK_3}, - {"Europe/Skopje", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Sofia", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Stockholm", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Tallinn", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Tirane", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Ulyanovsk", TimezoneValue::plus04_4}, - {"Europe/Uzhgorod", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Vaduz", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Vatican", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Vienna", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Vilnius", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Volgograd", TimezoneValue::MSK_3}, - {"Europe/Warsaw", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Zagreb", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Zaporozhye", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Zurich", TimezoneValue::CET_1CESTM350M1050_3}, - {"Indian/Antananarivo", TimezoneValue::EAT_3}, - {"Indian/Chagos", TimezoneValue::plus06_6}, - {"Indian/Christmas", TimezoneValue::plus07_7}, - {"Indian/Cocos", TimezoneValue::plus0630_630}, - {"Indian/Comoro", TimezoneValue::EAT_3}, - {"Indian/Kerguelen", TimezoneValue::plus05_5}, - {"Indian/Mahe", TimezoneValue::plus04_4}, - {"Indian/Maldives", TimezoneValue::plus05_5}, - {"Indian/Mauritius", TimezoneValue::plus04_4}, - {"Indian/Mayotte", TimezoneValue::EAT_3}, - {"Indian/Reunion", TimezoneValue::plus04_4}, - {"Pacific/Apia", TimezoneValue::plus13_13}, - {"Pacific/Auckland", TimezoneValue::NZST_12NZDTM950M410_3}, - {"Pacific/Bougainville", TimezoneValue::plus11_11}, - {"Pacific/Chatham", TimezoneValue::plus1245_1245plus1345M950_245M410_345}, - {"Pacific/Chuuk", TimezoneValue::plus10_10}, - {"Pacific/Easter", TimezoneValue::_066_05M916_22M416_22}, - {"Pacific/Efate", TimezoneValue::plus11_11}, - {"Pacific/Enderbury", TimezoneValue::plus13_13}, - {"Pacific/Fakaofo", TimezoneValue::plus13_13}, - {"Pacific/Fiji", TimezoneValue::plus12_12}, - {"Pacific/Funafuti", TimezoneValue::plus12_12}, - {"Pacific/Galapagos", TimezoneValue::_066}, - {"Pacific/Gambier", TimezoneValue::_099}, - {"Pacific/Guadalcanal", TimezoneValue::plus11_11}, - {"Pacific/Guam", TimezoneValue::ChST_10}, - {"Pacific/Honolulu", TimezoneValue::HST10}, - {"Pacific/Kiritimati", TimezoneValue::plus14_14}, - {"Pacific/Kosrae", TimezoneValue::plus11_11}, - {"Pacific/Kwajalein", TimezoneValue::plus12_12}, - {"Pacific/Majuro", TimezoneValue::plus12_12}, - {"Pacific/Marquesas", TimezoneValue::_0930930}, - {"Pacific/Midway", TimezoneValue::SST11}, - {"Pacific/Nauru", TimezoneValue::plus12_12}, - {"Pacific/Niue", TimezoneValue::_1111}, - {"Pacific/Norfolk", TimezoneValue::plus11_11plus12M1010M410_3}, - {"Pacific/Noumea", TimezoneValue::plus11_11}, - {"Pacific/Pago_Pago", TimezoneValue::SST11}, - {"Pacific/Palau", TimezoneValue::plus09_9}, - {"Pacific/Pitcairn", TimezoneValue::_088}, - {"Pacific/Pohnpei", TimezoneValue::plus11_11}, - {"Pacific/Port_Moresby", TimezoneValue::plus10_10}, - {"Pacific/Rarotonga", TimezoneValue::_1010}, - {"Pacific/Saipan", TimezoneValue::ChST_10}, - {"Pacific/Tahiti", TimezoneValue::_1010}, - {"Pacific/Tarawa", TimezoneValue::plus12_12}, - {"Pacific/Tongatapu", TimezoneValue::plus13_13}, - {"Pacific/Wake", TimezoneValue::plus12_12}, - {"Pacific/Wallis", TimezoneValue::plus12_12}, -}}; - -// Helper function to find timezone value -inline TimezoneValue find_timezone_value(std::string_view key) { - for (const auto& entry : TIMEZONE_DATA) { - if (entry.first == key) { - return entry.second; - } - } - return TimezoneValue::plus000plus02_2M350_1M1050_3; // Default fallback -} - -// Overload for String -inline TimezoneValue find_timezone_value(const String& key) { - return find_timezone_value(std::string_view(key.c_str())); -} - -// Overload for std::string -inline TimezoneValue find_timezone_value(const std::string& key) { - return find_timezone_value(std::string_view(key)); -} - -// Helper function to convert TimezoneValue to string representation -inline String get_timezone_string(TimezoneValue value) { - for (const auto& entry : TIMEZONE_DATA) { - if (entry.second == value) { - return String(entry.first.data(), entry.first.length()); - } - } - return String("GMT0"); // Default fallback -} - -// Overload for std::string -inline std::string get_timezone_string_std(TimezoneValue value) { - for (const auto& entry : TIMEZONE_DATA) { - if (entry.second == value) { - return std::string(entry.first.data(), entry.first.length()); - } - } - return "GMT0"; // Default fallback -} - -// Helper function to convert TimezoneValue enum to its string representation -inline String get_timezone_value_string(TimezoneValue value) { - switch (value) { - case TimezoneValue::plus000plus02_2M350_1M1050_3: return String("+00:00+02:00,M3.5.0/1,M10.5.0/3"); - case TimezoneValue::plus01_1: return String("+01:00"); - case TimezoneValue::plus02_2: return String("+02:00"); - case TimezoneValue::plus0330_330: return String("+03:30"); - case TimezoneValue::plus03_3: return String("+03:00"); - case TimezoneValue::plus0430_430: return String("+04:30"); - case TimezoneValue::plus04_4: return String("+04:00"); - case TimezoneValue::plus0530_530: return String("+05:30"); - case TimezoneValue::plus0545_545: return String("+05:45"); - case TimezoneValue::plus05_5: return String("+05:00"); - case TimezoneValue::plus0630_630: return String("+06:30"); - case TimezoneValue::plus06_6: return String("+06:00"); - case TimezoneValue::plus07_7: return String("+07:00"); - case TimezoneValue::plus0845_845: return String("+08:45"); - case TimezoneValue::plus08_8: return String("+08:00"); - case TimezoneValue::plus09_9: return String("+09:00"); - case TimezoneValue::plus1030_1030plus11_11M1010M410: return String("+10:30+11:00,M10.1.0,M4.1.0"); - case TimezoneValue::plus10_10: return String("+10:00"); - case TimezoneValue::plus11_11: return String("+11:00"); - case TimezoneValue::plus11_11plus12M1010M410_3: return String("+11:00+12:00,M10.1.0,M4.1.0/3"); - case TimezoneValue::plus1245_1245plus1345M950_245M410_345: return String("+12:45+13:45,M9.5.0/2:45,M4.1.0/3:45"); - case TimezoneValue::plus12_12: return String("+12:00"); - case TimezoneValue::plus13_13: return String("+13:00"); - case TimezoneValue::plus14_14: return String("+14:00"); - case TimezoneValue::_011: return String("-01:00"); - case TimezoneValue::_011plus00M350_0M1050_1: return String("-01:00+00:00,M3.5.0/0,M10.5.0/1"); - case TimezoneValue::_022: return String("-02:00"); - case TimezoneValue::_022_01M350__1M1050_0: return String("-02:00-01:00,M3.5.0/-1,M10.5.0/0"); - case TimezoneValue::_033: return String("-03:00"); - case TimezoneValue::_033_02M320M1110: return String("-03:00-02:00,M3.2.0,M11.1.0"); - case TimezoneValue::_044: return String("-04:00"); - case TimezoneValue::_044_03M1010_0M340_0: return String("-04:00-03:00,M10.1.0/0,M3.4.0/0"); - case TimezoneValue::_044_03M916_24M416_24: return String("-04:00-03:00,M9.1.6/24,M4.1.6/24"); - case TimezoneValue::_055: return String("-05:00"); - case TimezoneValue::_066: return String("-06:00"); - case TimezoneValue::_066_05M916_22M416_22: return String("-06:00-05:00,M9.1.6/22,M4.1.6/22"); - case TimezoneValue::_077: return String("-07:00"); - case TimezoneValue::_088: return String("-08:00"); - case TimezoneValue::_0930930: return String("-09:30"); - case TimezoneValue::_099: return String("-09:00"); - case TimezoneValue::_1010: return String("-10:00"); - case TimezoneValue::_1111: return String("-11:00"); - case TimezoneValue::_1212: return String("-12:00"); - case TimezoneValue::ACST_930: return String("ACST-9:30"); - case TimezoneValue::ACST_930ACDTM1010M410_3: return String("ACST-9:30ACDT,M10.1.0,M4.1.0/3"); - case TimezoneValue::AEST_10: return String("AEST-10"); - case TimezoneValue::AEST_10AEDTM1010M410_3: return String("AEST-10AEDT,M10.1.0,M4.1.0/3"); - case TimezoneValue::AKST9AKDTM320M1110: return String("AKST9AKDT,M3.2.0,M11.1.0"); - case TimezoneValue::AST4: return String("AST4"); - case TimezoneValue::AST4ADTM320M1110: return String("AST4ADT,M3.2.0,M11.1.0"); - case TimezoneValue::AWST_8: return String("AWST-8"); - case TimezoneValue::CAT_2: return String("CAT-2"); - case TimezoneValue::CET_1: return String("CET-1"); - case TimezoneValue::CET_1CESTM350M1050_3: return String("CET-1CEST,M3.5.0,M10.5.0/3"); - case TimezoneValue::CST_8: return String("CST-8"); - case TimezoneValue::CST5CDTM320_0M1110_1: return String("CST5CDT,M3.2.0/0,M11.1.0/1"); - case TimezoneValue::CST6: return String("CST6"); - case TimezoneValue::CST6CDTM320M1110: return String("CST6CDT,M3.2.0,M11.1.0"); - case TimezoneValue::ChST_10: return String("ChST-10"); - case TimezoneValue::EAT_3: return String("EAT-3"); - case TimezoneValue::EET_2: return String("EET-2"); - case TimezoneValue::EET_2EESTM344_50M1044_50: return String("EET-2EEST,M3.4.4/50,M10.4.4/50"); - case TimezoneValue::EET_2EESTM350M1050_3: return String("EET-2EEST,M3.5.0,M10.5.0/3"); - case TimezoneValue::EET_2EESTM350_0M1050_0: return String("EET-2EEST,M3.5.0/0,M10.5.0/0"); - case TimezoneValue::EET_2EESTM350_3M1050_4: return String("EET-2EEST,M3.5.0/3,M10.5.0/4"); - case TimezoneValue::EET_2EESTM455_0M1054_24: return String("EET-2EEST,M4.5.5/0,M10.5.4/24"); - case TimezoneValue::EST5: return String("EST5"); - case TimezoneValue::EST5EDTM320M1110: return String("EST5EDT,M3.2.0,M11.1.0"); - case TimezoneValue::GMT0: return String("GMT0"); - case TimezoneValue::GMT0BSTM350_1M1050: return String("GMT0BST,M3.5.0/1,M10.5.0"); - case TimezoneValue::HKT_8: return String("HKT-8"); - case TimezoneValue::HST10: return String("HST10"); - case TimezoneValue::HST10HDTM320M1110: return String("HST10HDT,M3.2.0,M11.1.0"); - case TimezoneValue::IST_1GMT0M1050M350_1: return String("IST-1GMT0,M10.5.0,M3.5.0/1"); - case TimezoneValue::IST_2IDTM344_26M1050: return String("IST-2IDT,M3.4.4/26,M10.5.0"); - case TimezoneValue::IST_530: return String("IST-5:30"); - case TimezoneValue::JST_9: return String("JST-9"); - case TimezoneValue::KST_9: return String("KST-9"); - case TimezoneValue::MSK_3: return String("MSK-3"); - case TimezoneValue::MST7: return String("MST7"); - case TimezoneValue::MST7MDTM320M1110: return String("MST7MDT,M3.2.0,M11.1.0"); - case TimezoneValue::NST330NDTM320M1110: return String("NST3:30NDT,M3.2.0,M11.1.0"); - case TimezoneValue::NZST_12NZDTM950M410_3: return String("NZST-12NZDT,M9.5.0,M4.1.0/3"); - case TimezoneValue::PKT_5: return String("PKT-5"); - case TimezoneValue::PST_8: return String("PST-8"); - case TimezoneValue::PST8PDTM320M1110: return String("PST8PDT,M3.2.0,M11.1.0"); - case TimezoneValue::SAST_2: return String("SAST-2"); - case TimezoneValue::SST11: return String("SST11"); - case TimezoneValue::UTC0: return String("UTC0"); - case TimezoneValue::WAT_1: return String("WAT-1"); - case TimezoneValue::WET0WESTM350_1M1050: return String("WET0WEST,M3.5.0/1,M10.5.0"); - case TimezoneValue::WIB_7: return String("WIB-7"); - case TimezoneValue::WIT_9: return String("WIT-9"); - case TimezoneValue::WITA_8: return String("WITA-8"); - default: return String("GMT0"); // Default fallback - } -} - -// Overload for std::string -inline std::string get_timezone_value_string_std(TimezoneValue value) { - return std::string(get_timezone_value_string(value).c_str()); -} - -} // namespace timezone_data diff --git a/platformio.ini b/platformio.ini index 98192fe..b65defd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,7 +15,7 @@ default_envs = lolin_s3_mini_213epd, lolin_s3_mini_29epd, btclock_rev_b_213epd, [env] [btclock_base] -platform = espressif32 @ ^6.10.0 +platform = espressif32 @ ^6.9.0 framework = arduino, espidf monitor_speed = 115200 monitor_filters = esp32_exception_decoder, colorize @@ -30,17 +30,17 @@ build_flags = -DLAST_BUILD_TIME=$UNIX_TIME -DARDUINO_USB_CDC_ON_BOOT -DCORE_DEBUG_LEVEL=0 - -D CONFIG_ASYNC_TCP_STACK_SIZE=16384 + -D DEFAULT_BOOT_TEXT=\"BTCLOCK\" -fexceptions build_unflags = -Werror=all -fno-exceptions lib_deps = - https://github.com/joltwallet/esp_littlefs.git#v1.16.4 - bblanchon/ArduinoJson@^7.4.1 - esp32async/ESPAsyncWebServer @ 3.7.7 - robtillaart/MCP23017@^0.9.1 - adafruit/Adafruit NeoPixel@^1.12.5 + https://github.com/joltwallet/esp_littlefs.git + bblanchon/ArduinoJson@^7.2.1 + mathieucarbou/ESPAsyncWebServer @ 3.3.23 + robtillaart/MCP23017@^0.8.0 + adafruit/Adafruit NeoPixel@^1.12.3 https://github.com/dsbaars/universal_pin#feature/mcp23017_rt https://github.com/dsbaars/GxEPD2#universal_pin https://github.com/tzapu/WiFiManager.git#v2.0.17 @@ -79,10 +79,9 @@ build_flags = -D I2C_SDA_PIN=35 -D I2C_SCK_PIN=36 -D HAS_FRONTLIGHT - -D PCA_OE_PIN=48 + -D PCA_OE_PIN=45 -D PCA_I2C_ADDR=0x42 -D IS_HW_REV_B - lib_deps = ${btclock_base.lib_deps} robtillaart/PCA9685@^0.7.1 @@ -101,7 +100,6 @@ build_flags = -D USE_QR -D VERSION_EPD_2_13 -D HW_REV=\"REV_A_EPD_2_13\" - -D CONFIG_ARDUINO_MAIN_TASK_STACK_SIZE=16384 platform_packages = platformio/tool-mklittlefs@^1.203.210628 earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216 @@ -114,7 +112,6 @@ build_flags = -D USE_QR -D VERSION_EPD_2_13 -D HW_REV=\"REV_B_EPD_2_13\" - -D CONFIG_ARDUINO_MAIN_TASK_STACK_SIZE=16384 platform_packages = platformio/tool-mklittlefs@^1.203.210628 earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216 diff --git a/src/fonts/sats-symbol.h b/src/fonts/sats-symbol.h index 182c6ea..d21a9a5 100644 --- a/src/fonts/sats-symbol.h +++ b/src/fonts/sats-symbol.h @@ -1,235 +1,201 @@ -#pragma once - -#include -#include -#include "fonts.hpp" - -const uint8_t Satoshi_Symbol90pt7bBitmaps_Gzip[] = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x63, 0x60, - 0x60, 0x60, 0xe0, 0xff, 0xc7, 0x00, 0x05, 0xcc, 0xff, 0x1b, 0x60, 0xcc, - 0xff, 0x0f, 0x60, 0x2c, 0xfb, 0x1f, 0x30, 0xd6, 0x60, 0x53, 0x38, 0x28, - 0x81, 0xfd, 0x7f, 0x3a, 0x83, 0x81, 0xf6, 0x30, 0x1a, 0xe0, 0xa7, 0xb7, - 0xff, 0x0f, 0x0c, 0xb4, 0x8f, 0x51, 0x01, 0x33, 0xbd, 0xfd, 0xff, 0x61, - 0xa0, 0x7d, 0x8c, 0x17, 0x0c, 0x8e, 0x92, 0x82, 0x44, 0x85, 0x54, 0x8f, - 0x23, 0x88, 0xe1, 0xec, 0xff, 0xa1, 0x56, 0xf2, 0xff, 0xff, 0x03, 0x61, - 0xc8, 0xff, 0x87, 0x3a, 0xc7, 0x1e, 0x16, 0x8d, 0xf5, 0x30, 0xa7, 0xc2, - 0x12, 0x36, 0xe3, 0x7f, 0xa8, 0xeb, 0x98, 0x47, 0x8d, 0x19, 0x35, 0x66, - 0x78, 0x1b, 0x43, 0x65, 0xf0, 0x00, 0x00, 0xc7, 0x63, 0x9f, 0x4b, 0xde, - 0x08, 0x00, 0x00 -}; -// unsigned int satoshi_bin_gz_len = 123; - - -// const uint8_t Satoshi_Symbol90pt7bBitmaps[] PROGMEM = { -// 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, -// 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, -// 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, -// 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, -// 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, -// 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, -// 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, -// 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, -// 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -// 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, -// 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, -// 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, -// 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, -// 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, -// 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, -// 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, -// 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, -// 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, -// 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, -// 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, -// 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, -// 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, -// 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xE0 }; +const uint8_t Satoshi_Symbol90pt7bBitmaps[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, + 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, + 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, + 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, + 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE0 }; const GFXglyph Satoshi_Symbol90pt7bGlyphs[] PROGMEM = { { 0, 82, 127, 99, 8, -126 }, { 1302, 71, 109, 93, 0, -117 } }; // 0x53 'S' -// const GFXfont Satoshi_Symbol90pt7b PROGMEM = { -// (uint8_t *)Satoshi_Symbol90pt7bBitmaps, -// (GFXglyph *)Satoshi_Symbol90pt7bGlyphs, -// 0x53, 0x53, 192 }; - - // Font properties -static constexpr FontData Satoshi_Symbol90pt7b_Properties = { - Satoshi_Symbol90pt7bBitmaps_Gzip, - Satoshi_Symbol90pt7bGlyphs, - sizeof(Satoshi_Symbol90pt7bBitmaps_Gzip), - 2270, // Original size - 0x53, // First char - 0x53, // Last char - 192 // yAdvance -}; - +const GFXfont Satoshi_Symbol90pt7b PROGMEM = { + (uint8_t *)Satoshi_Symbol90pt7bBitmaps, + (GFXglyph *)Satoshi_Symbol90pt7bGlyphs, + 0x53, 0x53, 192 }; // Approx. 2284 bytes diff --git a/src/icons/icons.cpp b/src/icons/icons.cpp index d9ae65c..2a4ff93 100644 --- a/src/icons/icons.cpp +++ b/src/icons/icons.cpp @@ -1,542 +1,65 @@ #include "icons.h" -// 'lightning-bolt', 122x122px -const unsigned char epd_icons_lightning_bolt [] PROGMEM = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0 -}; -// 'rocket-launch', 122x122px -const unsigned char epd_icons_rocket_launch [] PROGMEM = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xfe, 0xf8, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xf1, 0xf0, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xc1, 0xf0, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0x03, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xfc, 0x03, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xf0, 0x07, 0xc0, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0x80, 0x0f, 0x80, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xfe, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xf8, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xe0, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x80, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x80, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xe0, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xf8, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xc0, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xfc, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xf0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xe0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0x81, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0x03, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xfe, 0x07, 0xe7, 0xe0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xfc, 0x0f, 0xc3, 0xf0, 0x00, 0x00, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xf8, 0x1f, 0x81, 0xf8, 0x00, 0x03, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xf0, 0x3f, 0x00, 0xfc, 0x00, 0x0f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xe0, 0x7e, 0x00, 0x7e, 0x00, 0x3f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xc0, 0xfc, 0x00, 0x3f, 0x80, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x81, 0xf8, 0x00, 0x7f, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x03, 0xf0, 0x00, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x07, 0xe0, 0x01, 0xf3, 0xff, 0xe0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x0f, 0xc0, 0x03, 0xe0, 0xff, 0x80, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x1f, 0x80, 0x07, 0xc0, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x3f, 0x00, 0x0f, 0x81, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x7e, 0x00, 0x1f, 0x03, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xfc, 0x00, 0x3e, 0x07, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xf8, 0x00, 0x7c, 0x0f, 0xff, 0x80, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xf0, 0x00, 0xf8, 0x1f, 0xff, 0xc0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xe0, 0x01, 0xf0, 0x3f, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xc0, 0x03, 0xe0, 0x7f, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x80, 0x07, 0xc0, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x00, 0x0f, 0x81, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x00, 0x1f, 0x03, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x00, 0x3e, 0x07, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x00, 0x7c, 0x0f, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x00, 0xf8, 0x1f, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x01, 0xf0, 0x3f, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0 -}; -// 'pickaxe', 122x122px -const unsigned char epd_icons_pickaxe [] PROGMEM = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x31, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xe0, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xf8, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xfd, 0x80, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xf8, 0x00, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0x80, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xf8, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xf0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xe0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x80, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0x80, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0 +const unsigned char epd_icons_lightning_bolt[] PROGMEM = { + 0x1f,0x8b,0x08,0x00,0xa5,0xc7,0x7a,0x67,0x02,0xff,0xbd,0xd5,0xb1,0x0d,0xc3,0x20,0x10,0x40,0x51,0x23,0x17,0x2e,0x19,0x81,0x51,0x18,0x0d,0x6f,0x16,0x46,0x61,0x04,0x97,0x14,0x08,0x92,0x60,0x25,0x24,0xd6,0x1d,0xbf,0xf3,0x75,0x4f,0x3a,0x8a,0xdf,0x70,0xad,0xfd,0x4d,0x6c,0x37,0xf9,0x71,0xb1,0x9f,0xbb,0x82,0x0b,0x38,0x83,0x0f,0x70,0x02,0x47,0xf0,0x0e,0x5e,0xe6,0xae,0xe0,0x02,0xce,0xe0,0x03,0x9c,0xc0,0x11,0xbc,0x83,0x97,0xb9,0x2b,0xb8,0xb0,0x5f,0xe3,0xbe,0xce,0x17,0x1f,0xdd,0x5e,0x75,0xea,0x0e,0xaa,0x63,0x77,0x53,0xbd,0xbf,0x69,0x74,0xf7,0xf5,0x55,0x75,0xed,0xde,0x54,0x9f,0x79,0x56,0xf5,0xc8,0x95,0x3d,0x72,0x65,0x8f,0x5c,0xd9,0x23,0x57,0xf6,0xc8,0x95,0x3d,0x72,0x45,0xff,0xe4,0x8a,0x3e,0x9f,0x58,0xb0,0x03,0x7b,0x70,0x00,0xb7,0xb9,0x0d,0x78,0x05,0x6f,0x60,0x0b,0x76,0x60,0xfa,0x8f,0x02,0xb8,0xcd,0x6d,0xc0,0x2b,0x78,0x03,0x5b,0xb0,0x03,0x7b,0x70,0xb8,0xfb,0x3e,0x7e,0xfc,0x04,0x18,0x55,0x3a,0x4a,0xa0,0x07,0x00,0x00 }; -const unsigned char epd_icons_bitaxe_logo [] PROGMEM = { - // 'bitaxe_dark copy', 88x220px - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3e, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0x00, 0xff, 0xff, 0xfe, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0xff, 0x00, - 0x07, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, - 0xf8, 0x00, 0x3f, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x1f, 0xff, 0xff, - 0xf0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x0f, 0xff, 0xff, 0xf8, 0x03, 0xff, 0xff, 0xff, - 0xff, 0xf0, 0x00, 0x07, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, - 0xff, 0xfc, 0x00, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, - 0xff, 0xff, 0xe0, 0x07, 0xf8, 0x7f, 0xff, 0xfd, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xfe, - 0x3f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0x07, - 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xcf, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0x80, 0x0f, - 0xff, 0xe7, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xf3, 0xff, 0xff, 0xf0, - 0x01, 0xff, 0xff, 0xff, 0xc0, 0x1f, 0xff, 0xf3, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xff, 0xff, 0xf0, - 0x1f, 0xff, 0xf9, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xfd, 0xff, 0xff, - 0xe0, 0x07, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xfd, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, - 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, - 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, - 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfc, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xfe, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xfe, 0x7f, - 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xfc, 0x7f, 0xff, 0xff, - 0xff, 0xfc, 0x00, 0x00, 0x7f, 0x3f, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x7e, - 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x0f, 0xff, - 0xff, 0xff, 0xf0, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, - 0x3c, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, - 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xf8, - 0x1f, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xf0, 0x03, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x07, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, - 0x0f, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xfc, 0xff, 0xff, - 0xff, 0xff, 0xfc, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, - 0x00, 0x3f, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, - 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, - 0x01, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xc0, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, - 0xe0, 0x0f, 0xff, 0xf1, 0xf8, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xf9, 0xfc, - 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x3f, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xfd, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xfe, 0x3f, - 0xff, 0xf8, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xfc, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, - 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, - 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xf7, 0xff, 0xff, 0xfc, 0x3f, 0xff, - 0xff, 0xff, 0xe0, 0x07, 0xff, 0xf3, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, - 0xf3, 0xff, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xf3, 0xff, 0xff, 0xf8, 0x01, - 0xff, 0xff, 0xff, 0x80, 0x0f, 0xff, 0xf9, 0xff, 0xff, 0xf0, 0x01, 0xff, 0xff, 0xff, 0x80, 0x1f, - 0xff, 0xf9, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xff, 0xff, 0xc0, 0x1f, 0xff, 0xfc, 0xff, 0xff, 0xf0, - 0x03, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xfe, 0x3f, 0xff, 0xe0, 0x03, 0xff, 0xff, 0xff, 0xf8, - 0x3f, 0xff, 0x7e, 0x1f, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xfc, 0xff, 0x03, 0xff, - 0xc0, 0x07, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x80, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, - 0xff, 0x80, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x80, 0x03, 0xff, 0xe0, - 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0xff, - 0xff, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xff, - 0xf8, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xf8, 0x00, 0x00, 0x3f, 0xff, - 0xff, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xf8, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, - 0xff, 0xfc, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x7f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, - 0xff, 0x83, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, - 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xf8, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xf8, 0x7f, - 0xff, 0xf9, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xf0, 0xff, 0xf8, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xe0, 0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0xff, 0xc0, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x80, 0x1f, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x80, 0x1f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0xff, 0xc0, 0x3f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x3f, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xf0, 0x7f, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1f, 0xff, 0xf9, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, - 0xff, 0xf0, 0x1f, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xfd, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xff, - 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x1f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, - 0xef, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xfc, 0x00, - 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, - 0x00, 0x07, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xf8, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x60, 0x00, - 0x00, 0x00, 0x03, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, - 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, - 0xff, 0xe0, 0x07, 0xff, 0xff, 0x80, 0x0f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xe0, 0x07, 0xff, 0xff, - 0x80, 0x1f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xc0, 0x07, 0xff, 0xff, 0x80, 0x1f, 0xff, 0xff, 0xf8, - 0x0f, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xf8, 0x07, 0xff, 0x80, 0x0f, 0xff, - 0xff, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x03, 0xff, 0x80, 0x1f, 0xff, 0xff, 0x80, 0x3f, 0xff, 0xff, - 0xf0, 0x01, 0xff, 0xc0, 0x1f, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xf0, 0x01, 0xff, 0xf0, 0x1f, - 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xf0, 0x03, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xf8, 0x7f, 0xff, - 0xff, 0xe0, 0x03, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xfe, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, - 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, - 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0xff, 0x80, 0x00, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0xfe, 0x00, 0x7f, 0xff, 0x80, 0x03, 0xff, - 0xff, 0xff, 0xf8, 0xff, 0xfe, 0x00, 0xff, 0xff, 0xc0, 0x07, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xfe, - 0x00, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xfe, 0x00, 0xff, 0xff, 0xe0, 0x1f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0x01, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xfc, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0x03, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, - 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, - 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, - 0xbf, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xe0, - 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0x9f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x3f, 0xe7, 0xff, 0xff, 0xe7, 0xfd, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0x00, 0x1f, 0xff, 0xff, 0xfc, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff +const unsigned char epd_icons_rocket_launch[] PROGMEM = { + 0x1f,0x8b,0x08,0x00,0xa5,0xc7,0x7a,0x67,0x02,0xff,0xed,0xd4,0xb1,0x6e,0xc4,0x20,0x0c,0x06,0x60,0x47,0x0c,0x8c,0x3c,0x02,0x2f,0x52,0x5d,0x5e,0xab,0xc3,0xe9,0xc2,0xbd,0x19,0x55,0x5f,0x84,0xad,0x2b,0x55,0x17,0x2a,0x51,0x5c,0x43,0x0e,0x0c,0x5e,0x3a,0xde,0xd2,0x6c,0x9f,0x94,0xf8,0xb7,0xc1,0x0a,0xe2,0xf2,0x78,0x7c,0x96,0xa3,0x59,0x9c,0x01,0x8e,0xd9,0x1e,0x60,0x9f,0x0d,0xab,0xb3,0x70,0x14,0xf6,0xc2,0x8e,0x6c,0xd9,0x05,0x56,0x67,0xe1,0x28,0x1c,0x84,0xbd,0xb0,0x13,0x86,0xd5,0x45,0x38,0x0b,0x27,0xd8,0xd2,0xdc,0x6f,0x04,0x5d,0x66,0x07,0x30,0x38,0xdb,0xd3,0xb7,0x6e,0xb2,0x13,0xae,0xef,0x7a,0x3e,0xcf,0xf2,0x87,0xf3,0xe9,0x7e,0x1f,0x25,0xad,0xfe,0x8c,0xcd,0x5b,0xf7,0x9b,0xb0,0x0a,0x75,0x16,0xd7,0x9d,0x15,0x9d,0x0e,0xf5,0xab,0x1e,0x8e,0x9a,0x4c,0xf3,0x74,0x3b,0x43,0xa7,0xa5,0x32,0xe8,0xd3,0x05,0x4c,0x3b,0x3d,0x78,0xec,0x43,0x02,0x0b,0xb3,0x43,0xb7,0x3d,0xed,0xe0,0x72,0x7a,0xef,0x7e,0x59,0x1c,0xe0,0x7a,0xfa,0xe8,0xf5,0x1e,0xee,0xfb,0x07,0xaf,0x8d,0x5b,0xb7,0x4f,0xab,0x63,0xbb,0xfc,0xb3,0xdd,0x36,0xdf,0x69,0x3d,0x1c,0xb8,0xbd,0xc9,0x56,0x78,0x17,0x3e,0x84,0x71,0xf5,0x86,0x6b,0x9e,0x1a,0xce,0x91,0xe3,0xaa,0x53,0xe2,0xb8,0xd6,0xef,0x95,0xe3,0xaa,0xc3,0x8d,0xcb,0xb7,0xf9,0x90,0xcb,0x57,0xdf,0xeb,0xb6,0x6a,0xb6,0x42,0x3f,0xca,0xd7,0xfb,0xd0,0x1f,0x61,0x94,0x23,0x67,0xf3,0x4e,0x0d,0xe0,0xd1,0x9d,0xec,0x3d,0x81,0x2a,0xc3,0x71,0x87,0x0c,0x26,0x8d,0xf7,0x29,0xfe,0x06,0x7b,0x18,0xf6,0x19,0x76,0x47,0xa1,0xdd,0xf4,0xf5,0x81,0x85,0xfb,0x51,0x91,0x7a,0x4d,0x6c,0x1d,0xb6,0x2f,0x0c,0x6a,0xd8,0x78,0x15,0xd0,0xb1,0xad,0xd3,0x34,0x11,0xf7,0xbf,0x83,0xb9,0xcf,0xbe,0x81,0x55,0xb4,0x63,0xdc,0x3f,0x5c,0xf4,0x6c,0xda,0x07,0x83,0xce,0x0e,0x47,0x48,0x16,0x3d,0x3b,0x6c,0x91,0x96,0x9b,0xe7,0xa7,0xf8,0x03,0x03,0xbb,0xc6,0xcf,0xae,0xf1,0x18,0x0f,0x36,0xc5,0x2f,0xa6,0x78,0x8c,0xfc,0xff,0xab,0xf1,0x98,0x26,0x53,0x3c,0x7e,0xb3,0x6b,0x3c,0xfe,0x3c,0xff,0xff,0xfc,0xef,0xa7,0xf8,0x17,0xa1,0x5d,0x0c,0xd4,0xa0,0x07,0x00,0x00 +}; + +const unsigned char epd_icons_pickaxe[] PROGMEM = { + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xed, 0xd3, + 0xb1, 0x71, 0xc5, 0x30, 0x08, 0x06, 0x60, 0x7c, 0x2e, 0x5c, 0x7a, 0x04, + 0xcf, 0x90, 0x09, 0x3c, 0x9a, 0x35, 0x1a, 0xa9, 0xb2, 0x86, 0xba, 0x94, + 0x51, 0xa9, 0x5c, 0x14, 0x2b, 0x08, 0x64, 0x09, 0xe8, 0x53, 0x25, 0xaf, + 0xfb, 0xce, 0x98, 0x1f, 0xfc, 0xa4, 0x5a, 0xcd, 0x0f, 0xeb, 0x6f, 0xf8, + 0xdd, 0xf9, 0xd5, 0x19, 0x4f, 0xe7, 0xdd, 0x3a, 0x6c, 0xce, 0x8b, 0x35, + 0xc0, 0x65, 0x9f, 0xc3, 0x61, 0x1c, 0x61, 0x37, 0x4e, 0xb0, 0x1a, 0x17, + 0x58, 0x8c, 0xa9, 0xe1, 0x97, 0x71, 0x80, 0x97, 0x0f, 0xed, 0x08, 0xf0, + 0xa6, 0x9d, 0xa0, 0x77, 0xec, 0xce, 0xd0, 0x3b, 0x76, 0xdf, 0x00, 0xa0, + 0x4d, 0x01, 0x32, 0xf2, 0xe3, 0xe0, 0x8c, 0xae, 0x3e, 0xd9, 0x7e, 0x6d, + 0x80, 0xea, 0xea, 0xfd, 0xfb, 0xa7, 0xab, 0x37, 0xcf, 0x93, 0x7b, 0x3f, + 0x3b, 0x17, 0x57, 0x7f, 0xbb, 0x7e, 0xd5, 0xbb, 0x7f, 0xf2, 0x61, 0x94, + 0x4f, 0xfe, 0xec, 0xff, 0xfc, 0x05, 0xdd, 0xe5, 0xca, 0xb0, 0x29, 0xe7, + 0xf3, 0x3b, 0xac, 0xca, 0xe9, 0xa8, 0xa8, 0x1d, 0xf7, 0x1a, 0x17, 0x65, + 0xdc, 0x68, 0x03, 0x65, 0x3a, 0x20, 0x59, 0x9b, 0xc2, 0x32, 0x2f, 0x80, + 0x7d, 0xfa, 0x83, 0x36, 0x9a, 0x2e, 0x34, 0xfc, 0xcd, 0x0b, 0x60, 0xdf, + 0xfe, 0x32, 0xe6, 0xde, 0xbc, 0x50, 0x3f, 0x3f, 0x2d, 0x3b, 0x4c, 0xf3, + 0x6c, 0x61, 0x1f, 0xe6, 0xf3, 0x89, 0xd3, 0xbc, 0x6b, 0x1c, 0xbe, 0xb9, + 0x55, 0xdc, 0x1e, 0x17, 0x8e, 0x4a, 0xc3, 0x32, 0x6a, 0x1e, 0xf5, 0xb2, + 0x5a, 0x1e, 0xf3, 0xc8, 0xea, 0x65, 0xec, 0x87, 0xcf, 0xe9, 0xed, 0x9e, + 0xd7, 0x43, 0x3c, 0x4e, 0xbb, 0xf8, 0x1e, 0xb7, 0x41, 0x2c, 0xf1, 0xd3, + 0x79, 0xdc, 0x1e, 0xb1, 0xc4, 0x4f, 0xc7, 0xc5, 0x5a, 0xc5, 0xb3, 0x55, + 0x3c, 0x5b, 0xc5, 0x37, 0xeb, 0xf8, 0x66, 0x1d, 0xdf, 0xac, 0xe3, 0x9b, + 0x75, 0x7c, 0xb3, 0x8e, 0x6f, 0xd6, 0xf1, 0xcd, 0x3a, 0xbe, 0x59, 0xc7, + 0x93, 0x4d, 0x3c, 0xd9, 0xc4, 0x93, 0x4d, 0x3c, 0x39, 0xe9, 0x38, 0x72, + 0x5c, 0xad, 0x71, 0xb3, 0x0e, 0xbb, 0xf3, 0xe1, 0xea, 0x4f, 0xeb, 0x78, + 0x59, 0xa7, 0x6a, 0xfd, 0xe9, 0x5c, 0xff, 0xfd, 0x97, 0xfd, 0x03, 0x1c, + 0xb6, 0xaf, 0x78, 0xa0, 0x07, 0x00, 0x00 +}; + +const unsigned char epd_icons_bitaxe_logo[] PROGMEM = { + 0x1f,0x8b,0x08,0x00,0xa5,0xc7,0x7a,0x67,0x02,0xff,0xa5,0x96,0x4d,0x6a,0xdc,0x40,0x10,0x85,0x7b,0xd0,0x62,0x36,0x81,0xbe,0x40,0xa0,0x73,0x90,0xe0,0xbe,0x4a,0x8e,0x10,0xc8,0xde,0x52,0xf0,0x22,0xcb,0x1c,0x21,0x47,0x89,0x42,0x0e,0x90,0x2b,0x68,0xe7,0xa5,0x65,0x66,0xe1,0x16,0xb4,0x54,0xae,0xdf,0x2e,0x8d,0x70,0x88,0x4d,0x04,0xf6,0x7c,0x8c,0xa4,0x56,0xbf,0x7a,0xf5,0x4a,0x03,0xf0,0xcf,0xa3,0x3a,0x6e,0xbd,0xe3,0x0d,0xc0,0xaa,0x18,0xf0,0xa2,0x10,0x0d,0x37,0xc1,0x1a,0xf0,0xe2,0x70,0x16,0xcc,0x00,0x23,0x63,0x21,0x9c,0x14,0x13,0xc0,0xdc,0x11,0xce,0x74,0x4b,0x51,0xc4,0x93,0xe5,0x24,0xd8,0xd1,0xbd,0x84,0x53,0x08,0xba,0x22,0xde,0x5c,0x7a,0x58,0x6b,0x66,0xdc,0xf0,0xa3,0xe2,0x32,0x30,0x46,0xc0,0x8f,0x72,0x16,0xfc,0xa3,0x4b,0x0c,0x11,0xee,0x0d,0x13,0x5c,0x70,0x49,0xc2,0x51,0x90,0x1e,0x37,0x27,0x58,0x14,0x4b,0x26,0x3d,0xbc,0xb5,0xcd,0x11,0x02,0x1f,0x8c,0x03,0x63,0x3c,0xe0,0xc8,0x98,0x0e,0x38,0x1d,0x31,0x1f,0x70,0x3e,0x62,0xdf,0xaa,0x6d,0x55,0x5e,0xaf,0x0a,0x0e,0x19,0x5e,0x7d,0xac,0x7e,0x7b,0x35,0x0b,0x33,0xd4,0x9e,0xbd,0x08,0x7d,0x26,0xb1,0x64,0x40,0xb8,0xd5,0x4d,0x2a,0x46,0xd9,0xcd,0x8d,0x2a,0x46,0xfc,0x88,0x78,0x92,0x9d,0xbf,0x47,0xec,0x44,0xe5,0x3b,0xbd,0x40,0xca,0xc0,0xb7,0x8d,0x8e,0xa2,0x52,0xd6,0x4d,0xac,0x8e,0x9e,0x86,0x36,0x89,0xd9,0xbc,0x17,0x29,0x2e,0x1f,0x3b,0x1c,0x77,0x18,0x5f,0x42,0x34,0xd4,0xb4,0xa1,0xa1,0xa6,0x0d,0x1f,0x65,0xda,0x82,0x6b,0x0b,0xae,0x2d,0xb8,0xb6,0xa0,0x2a,0x08,0x3f,0x35,0x6d,0xe1,0x73,0xd3,0x16,0x66,0xd7,0x56,0x9a,0xb6,0x53,0x91,0xc5,0xa6,0x08,0x8f,0x45,0x04,0xa1,0xb6,0xa5,0x8a,0x6f,0x95,0xdb,0xc8,0xb4,0xad,0xf0,0xa6,0x83,0x2f,0xdf,0x3c,0x6a,0x68,0x1a,0x7e,0xc5,0x82,0xb0,0x11,0x0b,0x88,0xa0,0xcd,0x05,0x55,0x17,0x54,0xbd,0x3d,0x15,0x4f,0xae,0xb8,0x73,0x3c,0xbb,0xf8,0x5d,0x1d,0x92,0x63,0x76,0xdf,0x24,0x64,0xf0,0x84,0x4b,0x4a,0xc8,0x28,0x2d,0x16,0xb2,0x8b,0x87,0xec,0xe2,0x21,0x5b,0x34,0x59,0x83,0xc7,0x69,0x64,0x7b,0x2c,0x64,0x28,0x64,0xd2,0x90,0xdd,0x26,0x0b,0x59,0x85,0x4e,0x1d,0xc7,0x74,0x0f,0x96,0xac,0x13,0x95,0x5b,0xb0,0xa3,0x2d,0x45,0x6d,0x81,0xc9,0xe2,0xc4,0x2d,0x25,0x98,0xa1,0x18,0xf6,0x50,0x2c,0x4e,0x3b,0x94,0x7e,0x3f,0x20,0x17,0x7a,0x3f,0xca,0xde,0x1a,0xb2,0x9f,0x8e,0xdf,0xdd,0xc2,0x6f,0x6e,0xe1,0x1d,0x34,0x0b,0x11,0x0b,0x57,0x52,0x0d,0xe4,0xfd,0x6a,0x6e,0x48,0x9b,0x16,0x9d,0xa7,0x5e,0x68,0x16,0x56,0x47,0xbb,0xaf,0x6b,0xc3,0x88,0xdd,0xf4,0x15,0x6c,0xb5,0xe4,0x98,0xdb,0x83,0xc1,0xb7,0xc3,0xc7,0x6f,0xdf,0xfa,0x05,0xfe,0xe3,0x58,0xdb,0xbf,0x96,0x42,0x74,0x1e,0x27,0xf3,0x62,0x7b,0xcf,0x58,0xf2,0x16,0xbd,0xa9,0x6f,0x8d,0x38,0xe6,0xd9,0xb4,0x0d,0x69,0x87,0x93,0xc9,0x1c,0xf3,0x68,0xd7,0x4e,0xb9,0x4d,0x98,0xb9,0x6f,0x32,0x17,0x1b,0x36,0x59,0x9a,0xcb,0x64,0x62,0x20,0x4d,0xe6,0xf6,0xea,0x61,0xf9,0x97,0xb7,0x5b,0x6e,0xf5,0x21,0xd4,0x27,0x6c,0x49,0x4d,0x55,0xc4,0x33,0x0f,0x6c,0x50,0x57,0xc4,0x8a,0x2a,0x83,0x82,0x0c,0xba,0x46,0x1a,0x2a,0x66,0xf1,0x2c,0x66,0x16,0x19,0x40,0x86,0x1f,0xc4,0xed,0xd9,0xba,0xe4,0x0a,0xad,0x49,0xb0,0x7f,0x29,0x3b,0x43,0xe4,0x87,0x33,0x26,0xce,0xe6,0xa8,0x58,0x22,0x4f,0xcb,0x20,0xef,0x42,0xba,0x8e,0x8a,0x84,0x6f,0x56,0x3a,0x39,0x64,0x8e,0x29,0x26,0x13,0x6b,0xcc,0x38,0x27,0x8e,0x0b,0xc5,0x94,0x86,0x0c,0xda,0x47,0x31,0x25,0x5d,0x55,0x92,0xdf,0x9a,0xf6,0xdc,0x3a,0x2e,0xec,0xbe,0x8d,0xad,0x51,0xf7,0x98,0x5a,0x27,0x87,0xe4,0x2a,0xb2,0xab,0xc8,0x66,0x20,0x09,0x1a,0x02,0x27,0x79,0x23,0xe4,0x11,0xc1,0x3f,0x22,0x78,0x2e,0xac,0x82,0xb1,0xe1,0x94,0xa4,0xf8,0x54,0xa8,0xdc,0xb0,0xf4,0x0d,0xab,0xa3,0xba,0xb9,0x75,0x6e,0x6c,0xe7,0x01,0x27,0xfc,0xe1,0xf8,0x20,0xf8,0x15,0xff,0x9e,0x1c,0x17,0x89,0xfa,0x2f,0x69,0x13,0xc2,0x89,0x4e,0xc9,0x6a,0x54,0x37,0x5d,0xa2,0x64,0xfc,0x95,0x71,0x2f,0x73,0x9c,0x4a,0x5f,0xbf,0xc8,0x7b,0xab,0x7f,0xb9,0xe5,0x9e,0x01,0x52,0xde,0x2e,0xf4,0x74,0x09,0x00,0x00 +}; + +const size_t epd_icons_compressed_sizes[epd_icons_allArray_LEN] = { + 331, + 381, + 183, + 733 +}; + +const size_t epd_icons_original_sizes[epd_icons_allArray_LEN] = { + 1952, // 122x122 pixels / 8 bits per byte + padding + 1952, + 1952, + 2420 // 88x220 pixels / 8 bits per byte + padding }; -// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 8032) -const int epd_icons_allArray_LEN = 4; const unsigned char* epd_icons_allArray[epd_icons_allArray_LEN] = { - epd_icons_pickaxe, - epd_icons_rocket_launch, - epd_icons_lightning_bolt, - epd_icons_bitaxe_logo + epd_icons_pickaxe, + epd_icons_rocket_launch, + epd_icons_lightning_bolt, + epd_icons_bitaxe_logo }; diff --git a/src/icons/icons.h b/src/icons/icons.h index 136b4cc..7313d7b 100644 --- a/src/icons/icons.h +++ b/src/icons/icons.h @@ -1,10 +1,13 @@ #pragma once -#ifndef ICONS_H -#define ICONS_H - #include -extern const unsigned char* epd_icons_allArray[]; +extern const unsigned char epd_icons_pickaxe[] PROGMEM; +extern const unsigned char epd_icons_rocket_launch[] PROGMEM; +extern const unsigned char epd_icons_lightning_bolt[] PROGMEM; +extern const unsigned char epd_icons_bitaxe_logo[] PROGMEM; -#endif // ICONS_H +constexpr int epd_icons_allArray_LEN = 4; +extern const unsigned char* epd_icons_allArray[epd_icons_allArray_LEN]; +extern const size_t epd_icons_original_sizes[epd_icons_allArray_LEN]; +extern const size_t epd_icons_compressed_sizes[epd_icons_allArray_LEN]; \ No newline at end of file diff --git a/src/lib/bitaxe_fetch.cpp b/src/lib/bitaxe_fetch.cpp index 8fa1db0..d2dae13 100644 --- a/src/lib/bitaxe_fetch.cpp +++ b/src/lib/bitaxe_fetch.cpp @@ -1,19 +1,24 @@ #include "bitaxe_fetch.hpp" -void BitAxeFetch::taskWrapper(void* pvParameters) { - BitAxeFetch::getInstance().task(); +TaskHandle_t bitaxeFetchTaskHandle; + +uint64_t bitaxeHashrate; +uint64_t bitaxeBestDiff; + +uint64_t getBitAxeHashRate() +{ + return bitaxeHashrate; } -uint64_t BitAxeFetch::getHashRate() const { - return hashrate; +uint64_t getBitaxeBestDiff() +{ + return bitaxeBestDiff; } -uint64_t BitAxeFetch::getBestDiff() const { - return bestDiff; -} - -void BitAxeFetch::task() { - for (;;) { +void taskBitaxeFetch(void *pvParameters) +{ + for (;;) + { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); HTTPClient http; @@ -23,38 +28,46 @@ void BitAxeFetch::task() { int httpCode = http.GET(); - if (httpCode == 200) { + if (httpCode == 200) + { String payload = http.getString(); JsonDocument doc; deserializeJson(doc, payload); // Convert GH/s to H/s (multiply by 10^9) float hashRateGH = doc["hashRate"].as(); - hashrate = static_cast(std::round(hashRateGH * std::pow(10, getHashrateMultiplier('G')))); + bitaxeHashrate = static_cast(std::round(hashRateGH * std::pow(10, getHashrateMultiplier('G')))); // Parse difficulty string and convert to uint64_t std::string diffStr = doc["bestDiff"].as(); char diffUnit = diffStr[diffStr.length() - 1]; if (std::isalpha(diffUnit)) { float diffValue = std::stof(diffStr.substr(0, diffStr.length() - 1)); - bestDiff = static_cast(std::round(diffValue * std::pow(10, getDifficultyMultiplier(diffUnit)))); + bitaxeBestDiff = static_cast(std::round(diffValue * std::pow(10, getDifficultyMultiplier(diffUnit)))); } else { - bestDiff = std::stoull(diffStr); + bitaxeBestDiff = std::stoull(diffStr); } - if (workQueue != nullptr && (ScreenHandler::getCurrentScreen() == SCREEN_BITAXE_HASHRATE || ScreenHandler::getCurrentScreen() == SCREEN_BITAXE_BESTDIFF)) { + if (workQueue != nullptr && (ScreenHandler::getCurrentScreen() == SCREEN_BITAXE_HASHRATE || ScreenHandler::getCurrentScreen() == SCREEN_BITAXE_BESTDIFF)) + { WorkItem priceUpdate = {TASK_BITAXE_UPDATE, 0}; xQueueSend(workQueue, &priceUpdate, portMAX_DELAY); } - } else { - Serial.print(F("Error retrieving BitAxe data. HTTP status code: ")); + } + else + { + Serial.print( + F("Error retrieving BitAxe data. HTTP status code: ")); Serial.println(httpCode); Serial.println(bitaxeApiUrl); } } } -void BitAxeFetch::setup() { - xTaskCreate(taskWrapper, "bitaxeFetch", (3 * 1024), NULL, tskIDLE_PRIORITY, &taskHandle); - xTaskNotifyGive(taskHandle); +void setupBitaxeFetchTask() +{ + xTaskCreate(taskBitaxeFetch, "bitaxeFetch", (3 * 1024), NULL, tskIDLE_PRIORITY, + &bitaxeFetchTaskHandle); + + xTaskNotifyGive(bitaxeFetchTaskHandle); } \ No newline at end of file diff --git a/src/lib/bitaxe_fetch.hpp b/src/lib/bitaxe_fetch.hpp index 0dd98d9..8e1da37 100644 --- a/src/lib/bitaxe_fetch.hpp +++ b/src/lib/bitaxe_fetch.hpp @@ -7,28 +7,10 @@ #include "lib/config.hpp" #include "lib/shared.hpp" -class BitAxeFetch { -public: - static BitAxeFetch& getInstance() { - static BitAxeFetch instance; - return instance; - } +extern TaskHandle_t bitaxeFetchTaskHandle; - void setup(); - uint64_t getHashRate() const; - uint64_t getBestDiff() const; - static void taskWrapper(void* pvParameters); - TaskHandle_t getTaskHandle() const { return taskHandle; } +void setupBitaxeFetchTask(); +void taskBitaxeFetch(void *pvParameters); -private: - BitAxeFetch() = default; - ~BitAxeFetch() = default; - BitAxeFetch(const BitAxeFetch&) = delete; - BitAxeFetch& operator=(const BitAxeFetch&) = delete; - - void task(); - - TaskHandle_t taskHandle = nullptr; - uint64_t hashrate = 0; - uint64_t bestDiff = 0; -}; \ No newline at end of file +uint64_t getBitAxeHashRate(); +uint64_t getBitaxeBestDiff(); \ No newline at end of file diff --git a/src/lib/block_notify.cpp b/src/lib/block_notify.cpp index 20e6267..ec72138 100644 --- a/src/lib/block_notify.cpp +++ b/src/lib/block_notify.cpp @@ -1,14 +1,13 @@ #include "block_notify.hpp" -// Initialize static members -esp_websocket_client_handle_t BlockNotify::wsClient = nullptr; -uint32_t BlockNotify::currentBlockHeight = 878000; -uint16_t BlockNotify::blockMedianFee = 1; -bool BlockNotify::notifyInit = false; -unsigned long int BlockNotify::lastBlockUpdate = 0; -TaskHandle_t BlockNotify::taskHandle = nullptr; +char *wsServer; +esp_websocket_client_handle_t blockNotifyClient = NULL; +uint32_t currentBlockHeight = 873400; +uint16_t blockMedianFee = 1; +bool blockNotifyInit = false; +unsigned long int lastBlockUpdate; -const char* BlockNotify::mempoolWsCert = R"EOF( +const char *mempoolWsCert = R"EOF( -----BEGIN CERTIFICATE----- MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl @@ -43,255 +42,256 @@ VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG jjxDah2nGN59PRbxYvnKkKj9 -----END CERTIFICATE----- - ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 -WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu -ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc -h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ -0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U -A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW -T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH -B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC -B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv -KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn -OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn -jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw -qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI -rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq -hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ -3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK -NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 -ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur -TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC -jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc -oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq -4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA -mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d -emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= ------END CERTIFICATE----- )EOF"; -void BlockNotify::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; - BlockNotify& instance = BlockNotify::getInstance(); +void setupBlockNotify() +{ + IPAddress result; - switch (event_id) { - case WEBSOCKET_EVENT_CONNECTED: - { - notifyInit = true; - Serial.print(F("Connected to ")); - Serial.println(preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE)); + int dnsErr = -1; + String mempoolInstance = + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); - JsonDocument doc; - doc["action"] = "want"; - JsonArray dataArray = doc.createNestedArray("data"); - dataArray.add("blocks"); - dataArray.add("mempool-blocks"); - - String sub; - serializeJson(doc, sub); - esp_websocket_client_send_text(wsClient, sub.c_str(), sub.length(), portMAX_DELAY); - break; - } - case WEBSOCKET_EVENT_DATA: - instance.onWebsocketMessage(data); - break; + while (dnsErr != 1 && !strchr(mempoolInstance.c_str(), ':')) + { + dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result); - case WEBSOCKET_EVENT_DISCONNECTED: - Serial.println(F("Mempool.space WS Connection Closed")); - break; - - case WEBSOCKET_EVENT_ERROR: - Serial.println(F("Mempool.space WS Connection Error")); - break; - } -} - -void BlockNotify::onWebsocketMessage(esp_websocket_event_data_t *data) { - JsonDocument doc; - JsonDocument filter; - filter["block"]["height"] = true; - filter["mempool-blocks"][0]["medianFee"] = true; - - deserializeJson(doc, (char*)data->data_ptr, DeserializationOption::Filter(filter)); - - if (doc["block"].is()) { - JsonObject block = doc["block"]; - if (block["height"].as() != currentBlockHeight) { - processNewBlock(block["height"].as()); - } - } - else if (doc["mempool-blocks"].is()) { - JsonArray blockInfo = doc["mempool-blocks"].as(); - uint medianFee = (uint)round(blockInfo[0]["medianFee"].as()); - processNewBlockFee(medianFee); - } -} - -void BlockNotify::setup() { - IPAddress result; - int dnsErr = -1; - String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); - - while (dnsErr != 1 && !strchr(mempoolInstance.c_str(), ':')) { - 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)); - } - } - - // Get current block height through regular API - int blockFetch = fetchLatestBlock(); - - if (blockFetch > currentBlockHeight) - currentBlockHeight = blockFetch; - - if (currentBlockHeight != -1) { - lastBlockUpdate = esp_timer_get_time() / 1000000; - } - - if (workQueue != nullptr) { - WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; - xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); - } - - const bool useSSL = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE); - const String protocol = useSSL ? "wss" : "ws"; - String wsUri = protocol + "://" + mempoolInstance + "/api/v1/ws"; - - esp_websocket_client_config_t config = { - .task_stack = (6*1024), - .user_agent = USER_AGENT - }; - - if (useSSL) { - config.cert_pem = mempoolWsCert; - } - - config.uri = wsUri.c_str(); - - Serial.printf("Connecting to %s\r\n", mempoolInstance.c_str()); - - wsClient = esp_websocket_client_init(&config); - esp_websocket_register_events(wsClient, WEBSOCKET_EVENT_ANY, onWebsocketEvent, wsClient); - esp_websocket_client_start(wsClient); -} - - - -void BlockNotify::processNewBlock(uint32_t newBlockHeight) { - if (newBlockHeight <= currentBlockHeight) + if (dnsErr != 1) { - return; + 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 + int blockFetch = getBlockFetch(); + + if (blockFetch > currentBlockHeight) + currentBlockHeight = blockFetch; + + if (currentBlockHeight != -1) + { + lastBlockUpdate = esp_timer_get_time() / 1000000; + } + + 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()); + + const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "wss" : "ws"; + + String mempoolUri = protocol + "://" + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE) + "/api/v1/ws"; + + esp_websocket_client_config_t config = { + // .uri = "wss://mempool.space/api/v1/ws", + .task_stack = (6*1024), + .user_agent = USER_AGENT + }; + + if (preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE)) { + config.cert_pem = mempoolWsCert; + } + + config.uri = mempoolUri.c_str(); + + Serial.printf("Connecting to %s\r\n", preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE)); + + blockNotifyClient = esp_websocket_client_init(&config); + esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY, + onWebsocketBlockEvent, blockNotifyClient); + esp_websocket_client_start(blockNotifyClient); +} + +void onWebsocketBlockEvent(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\", \"mempool-blocks\"]}"; + switch (event_id) + { + case WEBSOCKET_EVENT_CONNECTED: + blockNotifyInit = true; + + 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")); } - currentBlockHeight = newBlockHeight; + break; + case WEBSOCKET_EVENT_DATA: + onWebsocketBlockMessage(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; + } +} + +void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data) +{ + JsonDocument doc; + + JsonDocument filter; + filter["block"]["height"] = true; + filter["mempool-blocks"][0]["medianFee"] = true; + + deserializeJson(doc, (char *)event_data->data_ptr, DeserializationOption::Filter(filter)); + + // if (error) { + // Serial.print("deserializeJson() failed: "); + // Serial.println(error.c_str()); + // return; + // } + + if (doc.containsKey("block")) + { + JsonObject block = doc["block"]; + + if (block["height"].as() == currentBlockHeight) { + return; + } + + processNewBlock(block["height"].as()); + } + else if (doc.containsKey("mempool-blocks")) + { + JsonArray blockInfo = doc["mempool-blocks"].as(); + + uint medianFee = (uint)round(blockInfo[0]["medianFee"].as()); + + processNewBlockFee(medianFee); + } + + doc.clear(); +} + +void processNewBlock(uint32_t newBlockHeight) { + if (newBlockHeight < currentBlockHeight) + return; + + currentBlockHeight = newBlockHeight; + + // Serial.printf("New block found: %d\r\n", block["height"].as()); + preferences.putUInt("blockHeight", currentBlockHeight); lastBlockUpdate = esp_timer_get_time() / 1000000; if (workQueue != nullptr) { - WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; - xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); - } + WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; + xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); + // xTaskNotifyGive(blockUpdateTaskHandle); - if (ScreenHandler::getCurrentScreen() != SCREEN_BLOCK_HEIGHT && - preferences.getBool("stealFocus", DEFAULT_STEAL_FOCUS)) - { + if (ScreenHandler::getCurrentScreen() != SCREEN_BLOCK_HEIGHT && + preferences.getBool("stealFocus", DEFAULT_STEAL_FOCUS)) + { uint64_t timerPeriod = 0; if (isTimerActive()) { - timerPeriod = getTimerSeconds(); - esp_timer_stop(screenRotateTimer); + // store timer periode before making inactive to prevent artifacts + timerPeriod = getTimerSeconds(); + esp_timer_stop(screenRotateTimer); } ScreenHandler::setCurrentScreen(SCREEN_BLOCK_HEIGHT); if (timerPeriod > 0) { - esp_timer_start_periodic(screenRotateTimer, + esp_timer_start_periodic(screenRotateTimer, timerPeriod * usPerSecond); } vTaskDelay(pdMS_TO_TICKS(315*NUM_SCREENS)); // Extra delay because of screen switching - } + } - if (preferences.getBool("ledFlashOnUpd", DEFAULT_LED_FLASH_ON_UPD)) - { + if (preferences.getBool("ledFlashOnUpd", DEFAULT_LED_FLASH_ON_UPD)) + { vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated - getLedHandler().queueEffect(LED_FLASH_BLOCK_NOTIFY); + queueLedEffect(LED_FLASH_BLOCK_NOTIFY); + } } } -void BlockNotify::processNewBlockFee(uint16_t newBlockFee) { - if (blockMedianFee == newBlockFee) +void processNewBlockFee(uint16_t newBlockFee) { + if (blockMedianFee == newBlockFee) { - return; + return; } + // Serial.printf("New median fee: %d\r\n", medianFee); blockMedianFee = newBlockFee; if (workQueue != nullptr) { - WorkItem blockUpdate = {TASK_FEE_UPDATE, 0}; - xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); + WorkItem blockUpdate = {TASK_FEE_UPDATE, 0}; + xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); } } -uint32_t BlockNotify::getBlockHeight() const { - return currentBlockHeight; -} +uint32_t getBlockHeight() { return currentBlockHeight; } -void BlockNotify::setBlockHeight(uint32_t newBlockHeight) +void setBlockHeight(uint32_t newBlockHeight) { - currentBlockHeight = newBlockHeight; + currentBlockHeight = newBlockHeight; } -uint16_t BlockNotify::getBlockMedianFee() const { - return blockMedianFee; -} +uint16_t getBlockMedianFee() { return blockMedianFee; } -void BlockNotify::setBlockMedianFee(uint16_t newBlockMedianFee) +void setBlockMedianFee(uint16_t newBlockMedianFee) { - blockMedianFee = newBlockMedianFee; + blockMedianFee = newBlockMedianFee; } -bool BlockNotify::isConnected() const +bool isBlockNotifyConnected() { - if (wsClient == NULL) - return false; - return esp_websocket_client_is_connected(wsClient); + if (blockNotifyClient == NULL) + return false; + return esp_websocket_client_is_connected(blockNotifyClient); } -bool BlockNotify::isInitialized() const +bool getBlockNotifyInit() { - return notifyInit; + return blockNotifyInit; } -void BlockNotify::stop() +void stopBlockNotify() { - if (wsClient == NULL) - return; + if (blockNotifyClient == NULL) + return; - esp_websocket_client_close(wsClient, portMAX_DELAY); - esp_websocket_client_stop(wsClient); - esp_websocket_client_destroy(wsClient); - wsClient = NULL; + esp_websocket_client_close(blockNotifyClient, pdMS_TO_TICKS(5000)); + esp_websocket_client_stop(blockNotifyClient); + esp_websocket_client_destroy(blockNotifyClient); + + blockNotifyClient = NULL; } -void BlockNotify::restart() +void restartBlockNotify() { - stop(); - setup(); + stopBlockNotify(); + + if (blockNotifyClient == NULL) { + setupBlockNotify(); + return; + } + + // esp_websocket_client_close(blockNotifyClient, pdMS_TO_TICKS(5000)); + // esp_websocket_client_stop(blockNotifyClient); + // esp_websocket_client_start(blockNotifyClient); } -int BlockNotify::fetchLatestBlock() { + +int getBlockFetch() { try { String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "https" : "http"; @@ -314,12 +314,12 @@ int BlockNotify::fetchLatestBlock() { return 2203; // B-T-C } -uint BlockNotify::getLastBlockUpdate() const +uint getLastBlockUpdate() { - return lastBlockUpdate; + return lastBlockUpdate; } -void BlockNotify::setLastBlockUpdate(uint lastUpdate) +void setLastBlockUpdate(uint lastUpdate) { - lastBlockUpdate = lastUpdate; + lastBlockUpdate = lastUpdate; } \ No newline at end of file diff --git a/src/lib/block_notify.hpp b/src/lib/block_notify.hpp index 15aabee..9c41bf0 100644 --- a/src/lib/block_notify.hpp +++ b/src/lib/block_notify.hpp @@ -5,6 +5,7 @@ #include #include #include + #include #include @@ -13,53 +14,28 @@ #include "lib/timers.hpp" #include "lib/shared.hpp" -class BlockNotify { -public: - static BlockNotify& getInstance() { - static BlockNotify instance; - return instance; - } +// using namespace websockets; - // Delete copy constructor and assignment operator - BlockNotify(const BlockNotify&) = delete; - void operator=(const BlockNotify&) = delete; +void setupBlockNotify(); - // Block notification setup and control - void setup(); - void stop(); - void restart(); - bool isConnected() const; - bool isInitialized() const; +void onWebsocketBlockEvent(void *handler_args, esp_event_base_t base, + int32_t event_id, void *event_data); +void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data); - // Block height management - void setBlockHeight(uint32_t newBlockHeight); - uint32_t getBlockHeight() const; +void setBlockHeight(uint32_t newBlockHeight); +uint32_t getBlockHeight(); - // Block fee management - void setBlockMedianFee(uint16_t blockMedianFee); - uint16_t getBlockMedianFee() const; +void setBlockMedianFee(uint16_t blockMedianFee); +uint16_t getBlockMedianFee(); - // Block processing - void processNewBlock(uint32_t newBlockHeight); - void processNewBlockFee(uint16_t newBlockFee); +bool isBlockNotifyConnected(); +void stopBlockNotify(); +void restartBlockNotify(); - // Block fetch and update tracking - int fetchLatestBlock(); - uint getLastBlockUpdate() const; - void setLastBlockUpdate(uint lastUpdate); +void processNewBlock(uint32_t newBlockHeight); +void processNewBlockFee(uint16_t newBlockFee); -private: - BlockNotify() = default; // Private constructor for singleton - - void setupTask(); - static void onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data); - void onWebsocketMessage(esp_websocket_event_data_t *data); - - static const char* mempoolWsCert; - static esp_websocket_client_handle_t wsClient; - static uint32_t currentBlockHeight; - static uint16_t blockMedianFee; - static bool notifyInit; - static unsigned long int lastBlockUpdate; - static TaskHandle_t taskHandle; -}; \ No newline at end of file +bool getBlockNotifyInit(); +uint32_t getLastBlockUpdate(); +int getBlockFetch(); +void setLastBlockUpdate(uint32_t lastUpdate); diff --git a/src/lib/config.cpp b/src/lib/config.cpp index 908a5a6..57c1c28 100644 --- a/src/lib/config.cpp +++ b/src/lib/config.cpp @@ -1,5 +1,6 @@ #include "config.hpp" -#include "led_handler.hpp" +#include "icon_manager.hpp" +#include "../icons/icons.h" #define MAX_ATTEMPTS_WIFI_CONNECTION 20 @@ -48,11 +49,10 @@ void setup() setupPreferences(); setupHardware(); - EPDManager::getInstance().initialize(); + setupDisplays(); if (preferences.getBool("ledTestOnPower", DEFAULT_LED_TEST_ON_POWER)) { - auto& ledHandler = getLedHandler(); - ledHandler.queueEffect(LED_POWER_TEST); + queueLedEffect(LED_POWER_TEST); } { std::lock_guard lockMcp(mcpMutex); @@ -62,8 +62,7 @@ void setup() preferences.remove("txPower"); WiFi.eraseAP(); - auto& ledHandler = getLedHandler(); - ledHandler.queueEffect(LED_EFFECT_WIFI_ERASE_SETTINGS); + queueLedEffect(LED_EFFECT_WIFI_ERASE_SETTINGS); } } @@ -79,8 +78,7 @@ void setup() else if (mcp1.read1(1) == LOW) { preferences.clear(); - auto& ledHandler = getLedHandler(); - ledHandler.queueEffect(LED_EFFECT_WIFI_ERASE_SETTINGS); + queueLedEffect(LED_EFFECT_WIFI_ERASE_SETTINGS); nvs_flash_erase(); delay(1000); @@ -104,53 +102,37 @@ void setup() if (preferences.getBool("bitaxeEnabled", DEFAULT_BITAXE_ENABLED)) { - BitAxeFetch::getInstance().setup(); + setupBitaxeFetchTask(); } if (preferences.getBool("miningPoolStats", DEFAULT_MINING_POOL_STATS_ENABLED)) { - MiningPoolStatsFetch::getInstance().setup(); + setupMiningPoolStatsFetchTask(); } ButtonHandler::setup(); setupOTA(); - EPDManager::getInstance().waitUntilNoneBusy(); + waitUntilNoneBusy(); #ifdef HAS_FRONTLIGHT if (!preferences.getBool("flAlwaysOn", DEFAULT_FL_ALWAYS_ON)) { - auto& ledHandler = getLedHandler(); - ledHandler.frontlightFadeOutAll(preferences.getUInt("flEffectDelay"), true); + frontlightFadeOutAll(preferences.getUInt("flEffectDelay"), true); flArray.allOFF(); } #endif - EPDManager::getInstance().forceFullRefresh(); + forceFullRefresh(); + } void setupWifi() { WiFi.onEvent(WiFiEvent); - - // wifi_country_t country = { - // .cc = "NL", - // .schan = 1, - // .nchan = 13, - // .policy = WIFI_COUNTRY_POLICY_MANUAL - // }; - - // esp_err_t err = esp_wifi_set_country(&country); - // if (err != ESP_OK) { - // Serial.printf("Failed to set country: %d\n", err); - // } - WiFi.setAutoConnect(true); WiFi.setAutoReconnect(true); WiFi.begin(); - - - if (preferences.getInt("txPower", DEFAULT_TX_POWER)) { if (WiFi.setTxPower( @@ -164,8 +146,7 @@ void setupWifi() // if (!preferences.getBool("wifiConfigured", DEFAULT_WIFI_CONFIGURED) { - auto& ledHandler = getLedHandler(); - ledHandler.queueEffect(LED_EFFECT_WIFI_WAIT_FOR_CONFIG); + queueLedEffect(LED_EFFECT_WIFI_WAIT_FOR_CONFIG); bool buttonPress = false; { @@ -188,7 +169,6 @@ void setupWifi() wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", DEFAULT_WP_TIMEOUT)); wm.setWiFiAutoReconnect(false); wm.setDebugOutput(false); - wm.setCountry("NL"); wm.setConfigPortalBlocking(true); wm.setAPCallback([&](WiFiManager *wifiManager) @@ -198,8 +178,8 @@ void setupWifi() wifiManager->getConfigPortalSSID().c_str(), softAP_password.c_str()); // delay(6000); - EPDManager::getInstance().setForegroundColor(GxEPD_BLACK); - EPDManager::getInstance().setBackgroundColor(GxEPD_WHITE); + setFgColor(GxEPD_BLACK); + setBgColor(GxEPD_WHITE); const String qrText = "qrWIFI:S:" + wifiManager->getConfigPortalSSID() + ";T:WPA;P:" + softAP_password.c_str() + ";;"; const String explainText = "*SSID: *\r\n" + @@ -229,22 +209,58 @@ void setupWifi() #endif "\r\n\r\n*FW build date:*\r\n" + formattedDate, qrText}; - - EPDManager::getInstance().setContent(epdContent); }); + setEpdContent(epdContent); }); wm.setSaveConfigCallback([]() { preferences.putBool("wifiConfigured", true); delay(1000); - // just restart after success + // just restart after succes ESP.restart(); }); bool ac = wm.autoConnect(softAP_SSID.c_str(), softAP_password.c_str()); + + // waitUntilNoneBusy(); + // std::array epdContent = {"Welcome!", + // "Bienvenidos!", "Use\r\nweb-interface\r\nto configure", "Use\r\nla + // interfaz web\r\npara configurar", "Or + // restart\r\nwhile\r\nholding\r\n2nd button\r\r\nto start\r\n QR-config", + // "O reinicie\r\nmientras\r\n mantiene presionado\r\nel segundo + // botón\r\r\npara iniciar\r\nQR-config", ""}; setEpdContent(epdContent); + // esp_task_wdt_init(30, false); + // uint count = 0; + // while (WiFi.status() != WL_CONNECTED) + // { + // if (Serial.available() > 0) + // { + // uint8_t b = Serial.read(); + + // if (parse_improv_serial_byte(x_position, b, x_buffer, + // onImprovCommandCallback, onImprovErrorCallback)) + // { + // x_buffer[x_position++] = b; + // } + // else + // { + // x_position = 0; + // } + // } + // count++; + + // if (count > 2000000) { + // queueLedEffect(LED_EFFECT_HEARTBEAT); + // count = 0; + // } + // } + // esp_task_wdt_deinit(); + // esp_task_wdt_reset(); } - EPDManager::getInstance().setForegroundColor(preferences.getUInt("fgColor", isWhiteVersion() ? GxEPD_BLACK : GxEPD_WHITE)); - EPDManager::getInstance().setBackgroundColor(preferences.getUInt("bgColor", isWhiteVersion() ? GxEPD_WHITE : GxEPD_BLACK)); + + + setFgColor(preferences.getUInt("fgColor", isWhiteVersion() ? GxEPD_BLACK : GxEPD_WHITE)); + setBgColor(preferences.getUInt("bgColor", isWhiteVersion() ? GxEPD_WHITE : GxEPD_BLACK)); } // else // { @@ -259,39 +275,29 @@ void setupWifi() void syncTime() { - configTime(0, 0, + configTime(preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS), 0, NTP_SERVER); struct tm timeinfo; while (!getLocalTime(&timeinfo)) { - auto& ledHandler = getLedHandler(); - ledHandler.queueEffect(LED_EFFECT_CONFIGURING); - configTime(0, 0, + queueLedEffect(LED_EFFECT_CONFIGURING); + configTime(preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS), 0, NTP_SERVER); delay(500); Serial.println(F("Retry set time")); } - setTimezone(get_timezone_value_string(timezone_data::find_timezone_value(preferences.getString("tzString", DEFAULT_TZ_STRING)))); - lastTimeSync = esp_timer_get_time() / 1000000; } -void setTimezone(String timezone) { - Serial.printf(" Setting Timezone to %s\n",timezone.c_str()); - setenv("TZ",timezone.c_str(),1); // Now adjust the TZ. Clock settings are adjusted to show the new local time - tzset(); -} - - void setupPreferences() { preferences.begin("btclock", false); - EPDManager::getInstance().setForegroundColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR)); - EPDManager::getInstance().setBackgroundColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR)); - BlockNotify::getInstance().setBlockHeight(preferences.getUInt("blockHeight", INITIAL_BLOCK_HEIGHT)); + setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR)); + setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR)); + setBlockHeight(preferences.getUInt("blockHeight", INITIAL_BLOCK_HEIGHT)); setPrice(preferences.getUInt("lastPrice", INITIAL_LAST_PRICE), CURRENCY_USD); if (!preferences.isKey("enableDebugLog")) { @@ -370,10 +376,25 @@ void setupPreferences() if (preferences.getBool("miningPoolStats", DEFAULT_MINING_POOL_STATS_ENABLED)) { addScreenMapping(SCREEN_MINING_POOL_STATS_HASHRATE, "Mining Pool Hashrate"); - if (MiningPoolStatsFetch::getInstance().getPool()->supportsDailyEarnings()) { + if (getMiningPool()->supportsDailyEarnings()) { addScreenMapping(SCREEN_MINING_POOL_STATS_EARNINGS, "Mining Pool Earnings"); } } + + // Load icons based on preferences + if (preferences.getBool("bitaxeEnabled", DEFAULT_BITAXE_ENABLED)) { + IconManager::loadIcon("bitaxe", epd_icons_bitaxe_logo, epd_icons_compressed_sizes[3], epd_icons_original_sizes[3], 88, 220); + IconManager::loadIcon("rocket", epd_icons_rocket_launch, epd_icons_compressed_sizes[1], epd_icons_original_sizes[1], 122, 122); + IconManager::loadIcon("pickaxe", epd_icons_pickaxe, epd_icons_compressed_sizes[0], epd_icons_original_sizes[0], 122, 122); + } + + if (preferences.getBool("nostrZapNotify", DEFAULT_ZAP_NOTIFY_ENABLED)) { + IconManager::loadIcon("lightning", epd_icons_lightning_bolt, epd_icons_compressed_sizes[2], epd_icons_original_sizes[2], 122, 122); + } + + if (preferences.getBool("miningPoolStats", DEFAULT_MINING_POOL_STATS_ENABLED)) { + IconManager::loadIcon("pickaxe", epd_icons_pickaxe, epd_icons_compressed_sizes[0], epd_icons_original_sizes[0], 122, 122); + } } String replaceAmbiguousChars(String input) @@ -399,7 +420,7 @@ void setupWebsocketClients(void *pvParameters) } else if (dataSource == THIRD_PARTY_SOURCE) { - BlockNotify::getInstance().setup(); + setupBlockNotify(); setupPriceNotify(); } @@ -416,14 +437,13 @@ void setupTimers() void finishSetup() { - auto& ledHandler = getLedHandler(); if (preferences.getBool("ledStatus", DEFAULT_LED_STATUS)) { - ledHandler.restoreLedState(); + restoreLedState(); } else { - ledHandler.clear(); + clearLeds(); } } @@ -472,9 +492,7 @@ void setupHardware() Serial.println(F("Error loading WebUI")); } - // Initialize LED handler - auto& ledHandler = getLedHandler(); - ledHandler.setup(); + setupLeds(); WiFi.setHostname(getMyHostname().c_str()); if (!psramInit()) @@ -486,6 +504,8 @@ void setupHardware() Wire.begin(I2C_SDA_PIN, I2C_SCK_PIN, 400000); + + if (!mcp1.begin()) { Serial.println(F("Error MCP23017 1")); } else { @@ -532,8 +552,7 @@ void setupHardware() #endif #ifdef HAS_FRONTLIGHT - // Initialize frontlight through LedHandler - ledHandler.initializeFrontlight(); + setupFrontlight(); Wire.beginTransmission(0x5C); byte error = Wire.endTransmission(); @@ -555,7 +574,6 @@ void setupHardware() void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { static bool first_connect = true; - auto& ledHandler = getLedHandler(); // Get ledHandler reference once at the start Serial.printf("[WiFi-event] event: %d\n", event); @@ -581,7 +599,7 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) if (!first_connect) { Serial.println(F("Disconnected from WiFi access point")); - ledHandler.queueEffect(LED_EFFECT_WIFI_CONNECT_ERROR); + queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR); uint8_t reason = info.wifi_sta_disconnected.reason; if (reason) Serial.printf("Disconnect reason: %s, ", @@ -597,13 +615,13 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) Serial.print("Obtained IP address: "); Serial.println(WiFi.localIP()); if (!first_connect) - ledHandler.queueEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS); + queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS); first_connect = false; break; } case ARDUINO_EVENT_WIFI_STA_LOST_IP: Serial.println(F("Lost IP address and IP address is reset to 0")); - ledHandler.queueEffect(LED_EFFECT_WIFI_CONNECT_ERROR); + queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR); WiFi.reconnect(); break; case ARDUINO_EVENT_WIFI_AP_START: @@ -653,6 +671,30 @@ uint getLastTimeSync() } #ifdef HAS_FRONTLIGHT +void setupFrontlight() +{ + if (!flArray.begin(PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL, PCA9685_MODE2_TOTEMPOLE)) + { + Serial.println(F("FL driver error")); + return; + } + flArray.setFrequency(200); + Serial.println(F("FL driver active")); + + if (!preferences.isKey("flMaxBrightness")) + { + preferences.putUInt("flMaxBrightness", DEFAULT_FL_MAX_BRIGHTNESS); + } + if (!preferences.isKey("flEffectDelay")) + { + preferences.putUInt("flEffectDelay", DEFAULT_FL_EFFECT_DELAY); + } + + if (!preferences.isKey("flFlashOnUpd")) + { + preferences.putBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE); + } +} float getLightLevel() { diff --git a/src/lib/config.hpp b/src/lib/config.hpp index c8d5336..d8e27b6 100644 --- a/src/lib/config.hpp +++ b/src/lib/config.hpp @@ -33,7 +33,7 @@ #include "shared.hpp" #include "defaults.hpp" -#include "timezone_data.hpp" + #define NTP_SERVER "pool.ntp.org" #define DEFAULT_TIME_OFFSET_SECONDS 3600 #ifndef MCP_DEV_ADDR @@ -43,7 +43,6 @@ void setup(); void syncTime(); -void setTimezone(String timezone); uint getLastTimeSync(); void setupPreferences(); void setupWebsocketClients(void *pvParameters); @@ -53,10 +52,10 @@ void setupTimers(); void finishSetup(); void setupMcp(); #ifdef HAS_FRONTLIGHT -extern BH1750 bh1750; -extern bool hasLuxSensor; +void setupFrontlight(); float getLightLevel(); bool hasLightLevel(); +extern PCA9685 flArray; #endif String getMyHostname(); @@ -99,10 +98,6 @@ extern MCP23017 mcp1; extern MCP23017 mcp2; #endif -#ifdef HAS_FRONTLIGHT -extern PCA9685 flArray; -#endif - // Expose DataSourceType enum extern DataSourceType getDataSource(); extern void setDataSource(DataSourceType source); \ No newline at end of file diff --git a/src/lib/defaults.hpp b/src/lib/defaults.hpp index 00d8bc0..29d7eee 100644 --- a/src/lib/defaults.hpp +++ b/src/lib/defaults.hpp @@ -24,7 +24,6 @@ #define DEFAULT_V2_SOURCE_CURRENCY CURRENCY_USD #define DEFAULT_TIME_OFFSET_SECONDS 3600 -#define DEFAULT_TZ_STRING "Europe/Amsterdam" #define DEFAULT_HOSTNAME_PREFIX "btclock" #define DEFAULT_MEMPOOL_INSTANCE "mempool.space" @@ -47,8 +46,8 @@ #define DEFAULT_LUX_LIGHT_TOGGLE 128 #define DEFAULT_FL_OFF_WHEN_DARK true -#define DEFAULT_FL_ALWAYS_ON true -#define DEFAULT_FL_FLASH_ON_UPDATE true +#define DEFAULT_FL_ALWAYS_ON false +#define DEFAULT_FL_FLASH_ON_UPDATE false #define DEFAULT_LED_STATUS false #define DEFAULT_TIMER_ACTIVE true @@ -61,7 +60,6 @@ #define DEFAULT_MINING_POOL_STATS_ENABLED false #define DEFAULT_MINING_POOL_NAME "ocean" #define DEFAULT_MINING_POOL_USER "38Qkkei3SuF1Eo45BaYmRHUneRD54yyTFy" // Random actual Ocean hasher -#define DEFAULT_LOCAL_POOL_ENDPOINT "umbrel.local:2019" #define DEFAULT_ZAP_NOTIFY_ENABLED false #define DEFAULT_ZAP_NOTIFY_PUBKEY "b5127a08cf33616274800a4387881a9f98e04b9c37116e92de5250498635c422" diff --git a/src/lib/epd.cpp b/src/lib/epd.cpp index fa389c6..097268f 100644 --- a/src/lib/epd.cpp +++ b/src/lib/epd.cpp @@ -1,278 +1,459 @@ #include "epd.hpp" -// Initialize static members #ifdef IS_BTCLOCK_REV_B -Native_Pin EPDManager::EPD_DC(14); -std::array EPDManager::EPD_CS = { - Native_Pin(2), Native_Pin(4), Native_Pin(6), Native_Pin(10), - Native_Pin(38), Native_Pin(21), Native_Pin(17) +Native_Pin EPD_CS[NUM_SCREENS] = { + Native_Pin(2), + Native_Pin(4), + Native_Pin(6), + Native_Pin(10), + Native_Pin(38), + Native_Pin(21), + Native_Pin(17), }; -std::array EPDManager::EPD_BUSY = { - Native_Pin(3), Native_Pin(5), Native_Pin(7), Native_Pin(9), - Native_Pin(37), Native_Pin(18), Native_Pin(16) +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), }; -std::array EPDManager::EPD_RESET = { - 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 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, 14), }; -#elif defined(IS_BTCLOCK_V8) -Native_Pin EPDManager::EPD_DC(38); -std::array EPDManager::EPD_BUSY = { - 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) + +Native_Pin EPD_DC = Native_Pin(14); +#elif IS_BTCLOCK_V8 +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), }; -std::array EPDManager::EPD_CS = { + +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) -}; -std::array EPDManager::EPD_RESET = { - 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, 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), }; #else -Native_Pin EPDManager::EPD_DC(14); -std::array EPDManager::EPD_CS = { - Native_Pin(2), Native_Pin(4), Native_Pin(6), Native_Pin(10), - Native_Pin(33), Native_Pin(21), Native_Pin(17) +Native_Pin EPD_CS[NUM_SCREENS] = { + Native_Pin(2), + Native_Pin(4), + Native_Pin(6), + Native_Pin(10), + Native_Pin(33), + Native_Pin(21), + Native_Pin(17), +#if NUM_SCREENS == 9 + // MCP23X17_Pin(mcp2, 7), + Native_Pin(-1), + Native_Pin(-1), +#endif }; -std::array EPDManager::EPD_BUSY = { - Native_Pin(3), Native_Pin(5), Native_Pin(7), Native_Pin(9), - Native_Pin(37), Native_Pin(18), Native_Pin(16) +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), }; -std::array EPDManager::EPD_RESET = { - 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 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, 14), }; + +Native_Pin EPD_DC = Native_Pin(14); #endif -EPDManager& EPDManager::getInstance() { - static EPDManager instance; - return instance; -} +GxEPD2_BW displays[NUM_SCREENS] = { + EPD_CLASS(&EPD_CS[0], &EPD_DC, &EPD_RESET_MPD[0], &EPD_BUSY[0]), + EPD_CLASS(&EPD_CS[1], &EPD_DC, &EPD_RESET_MPD[1], &EPD_BUSY[1]), + EPD_CLASS(&EPD_CS[2], &EPD_DC, &EPD_RESET_MPD[2], &EPD_BUSY[2]), + EPD_CLASS(&EPD_CS[3], &EPD_DC, &EPD_RESET_MPD[3], &EPD_BUSY[3]), + EPD_CLASS(&EPD_CS[4], &EPD_DC, &EPD_RESET_MPD[4], &EPD_BUSY[4]), + EPD_CLASS(&EPD_CS[5], &EPD_DC, &EPD_RESET_MPD[5], &EPD_BUSY[5]), + EPD_CLASS(&EPD_CS[6], &EPD_DC, &EPD_RESET_MPD[6], &EPD_BUSY[6]), +#ifdef IS_BTCLOCK_V8 + EPD_CLASS(&EPD_CS[7], &EPD_DC, &EPD_RESET_MPD[7], &EPD_BUSY[7]), +#endif +}; -EPDManager::EPDManager() - : currentContent{} - , content{} - , lastFullRefresh{} - , tasks{} - , updateQueue{nullptr} - , antonioFonts{nullptr, nullptr, nullptr} - , oswaldFonts{nullptr, nullptr, nullptr} - , fontSmall{nullptr} - , fontBig{nullptr} - , fontMedium{nullptr} - , fontSatsymbol{nullptr} - , bgColor{GxEPD_BLACK} - , fgColor{GxEPD_WHITE} - , displays{ - #ifdef IS_BTCLOCK_V8 - EPD_CLASS(&EPD_CS[0], &EPD_DC, &EPD_RESET[0], &EPD_BUSY[0]), - EPD_CLASS(&EPD_CS[1], &EPD_DC, &EPD_RESET[1], &EPD_BUSY[1]), - EPD_CLASS(&EPD_CS[2], &EPD_DC, &EPD_RESET[2], &EPD_BUSY[2]), - EPD_CLASS(&EPD_CS[3], &EPD_DC, &EPD_RESET[3], &EPD_BUSY[3]), - EPD_CLASS(&EPD_CS[4], &EPD_DC, &EPD_RESET[4], &EPD_BUSY[4]), - EPD_CLASS(&EPD_CS[5], &EPD_DC, &EPD_RESET[5], &EPD_BUSY[5]), - EPD_CLASS(&EPD_CS[6], &EPD_DC, &EPD_RESET[6], &EPD_BUSY[6]), - EPD_CLASS(&EPD_CS[7], &EPD_DC, &EPD_RESET[7], &EPD_BUSY[7]) - #else - EPD_CLASS(&EPD_CS[0], &EPD_DC, &EPD_RESET[0], &EPD_BUSY[0]), - EPD_CLASS(&EPD_CS[1], &EPD_DC, &EPD_RESET[1], &EPD_BUSY[1]), - EPD_CLASS(&EPD_CS[2], &EPD_DC, &EPD_RESET[2], &EPD_BUSY[2]), - EPD_CLASS(&EPD_CS[3], &EPD_DC, &EPD_RESET[3], &EPD_BUSY[3]), - EPD_CLASS(&EPD_CS[4], &EPD_DC, &EPD_RESET[4], &EPD_BUSY[4]), - EPD_CLASS(&EPD_CS[5], &EPD_DC, &EPD_RESET[5], &EPD_BUSY[5]), - EPD_CLASS(&EPD_CS[6], &EPD_DC, &EPD_RESET[6], &EPD_BUSY[6]) - #endif - } +std::array currentEpdContent; +std::array epdContent; +uint32_t lastFullRefresh[NUM_SCREENS]; +TaskHandle_t tasks[NUM_SCREENS]; +// TaskHandle_t epdTaskHandle = NULL; + +#define UPDATE_QUEUE_SIZE 14 +QueueHandle_t updateQueue; + +// SemaphoreHandle_t epdUpdateSemaphore[NUM_SCREENS]; + +int fgColor = GxEPD_WHITE; +int bgColor = GxEPD_BLACK; + +struct FontFamily { + GFXfont* big; + GFXfont* medium; + GFXfont* small; +}; + +FontFamily antonioFonts = {nullptr, nullptr, nullptr}; +FontFamily oswaldFonts = {nullptr, nullptr, nullptr}; + +const GFXfont *FONT_SMALL; +const GFXfont *FONT_BIG; +const GFXfont *FONT_MEDIUM; +const GFXfont *FONT_SATSYMBOL; + +std::mutex epdUpdateMutex; +std::mutex epdMutex[NUM_SCREENS]; + +uint8_t qrcode[800]; + +#ifdef IS_BTCLOCK_V8 +#define EPD_TASK_STACK_SIZE 4096 +#else +#define EPD_TASK_STACK_SIZE 2048 +#endif + +#define BUSY_TIMEOUT_COUNT 200 +#define BUSY_RETRY_DELAY pdMS_TO_TICKS(10) + +void forceFullRefresh() { + for (uint i = 0; i < NUM_SCREENS; i++) + { + lastFullRefresh[i] = NULL; + } } -EPDManager::~EPDManager() { - // Clean up tasks - for (auto& task : tasks) { - if (task != nullptr) { - vTaskDelete(task); - } - } +GFXfont font90; - // Clean up queue - if (updateQueue != nullptr) { - vQueueDelete(updateQueue); - } +void loadFonts(const String& fontName) { + if (fontName == FontNames::ANTONIO) { + // Load Antonio fonts + antonioFonts.big = FontLoader::loadCompressedFont(Antonio_SemiBold90pt7b_Properties); + antonioFonts.medium = FontLoader::loadCompressedFont(Antonio_SemiBold40pt7b_Properties); + antonioFonts.small = FontLoader::loadCompressedFont(Antonio_SemiBold20pt7b_Properties); - // Clean up fonts - delete antonioFonts.big; - delete antonioFonts.medium; - delete antonioFonts.small; - delete oswaldFonts.big; - delete oswaldFonts.medium; - delete oswaldFonts.small; + FONT_BIG = antonioFonts.big; + FONT_MEDIUM = antonioFonts.medium; + FONT_SMALL = antonioFonts.small; + } else if (fontName == FontNames::OSWALD) { + // Load Oswald fonts + oswaldFonts.big = FontLoader::loadCompressedFont(Oswald_Medium80pt7b_Properties); + oswaldFonts.medium = FontLoader::loadCompressedFont(Oswald_Medium30pt7b_Properties); + oswaldFonts.small = FontLoader::loadCompressedFont(Oswald_Medium20pt7b_Properties); + + FONT_BIG = oswaldFonts.big; + FONT_MEDIUM = oswaldFonts.medium; + FONT_SMALL = oswaldFonts.small; + } + + FONT_SATSYMBOL = FontLoader::loadCompressedFont(Satoshi_Symbol90pt7b_Properties); } -void EPDManager::initialize() { - // Load fonts based on preference - String fontName = preferences.getString("fontName", DEFAULT_FONT_NAME); - loadFonts(fontName); +void setupDisplays() { + // Load fonts based on preference + String fontName = preferences.getString("fontName", DEFAULT_FONT_NAME); + loadFonts(fontName); - // Initialize displays - std::lock_guard lockMcp(mcpMutex); - for (auto& display : displays) { - display.init(0, true, 30); + // Initialize displays + std::lock_guard lockMcp(mcpMutex); + for (uint i = 0; i < NUM_SCREENS; i++) { + displays[i].init(0, true, 30); + } + + // Create update queue and task + updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem)); + xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", EPD_TASK_STACK_SIZE * 2, NULL, 11, NULL); + + // Create display update tasks + for (uint i = 0; i < NUM_SCREENS; i++) { + int *taskParam = new int; + *taskParam = i; + xTaskCreate(updateDisplay, ("EpdUpd" + String(i)).c_str(), EPD_TASK_STACK_SIZE, taskParam, 11, &tasks[i]); + } + + // Check for storage mode (prevents burn-in) + if (mcp1.read1(0) == LOW) { + setFgColor(GxEPD_BLACK); + setBgColor(GxEPD_WHITE); + epdContent.fill(""); + } else { + // Initialize with custom text or default + String customText = preferences.getString("displayText", DEFAULT_BOOT_TEXT); + std::array newContent; + newContent.fill(" "); + + for (size_t i = 0; i < std::min(customText.length(), (size_t)NUM_SCREENS); i++) { + newContent[i] = String(customText[i]); } + + epdContent = newContent; + } - // Create update queue and task - updateQueue = xQueueCreate(UPDATE_QUEUE_SIZE, sizeof(UpdateDisplayTaskItem)); - xTaskCreate(prepareDisplayUpdateTask, "PrepareUpd", EPD_TASK_STACK_SIZE * 2, nullptr, 11, nullptr); - - // Create display update tasks - for (size_t i = 0; i < NUM_SCREENS; i++) { - auto* taskParam = new int(i); - xTaskCreate(updateDisplayTask, ("EpdUpd" + String(i)).c_str(), EPD_TASK_STACK_SIZE, - taskParam, 11, &tasks[i]); - } - - // Check for storage mode (prevents burn-in) - if (mcp1.read1(0) == LOW) { - setForegroundColor(GxEPD_BLACK); - setBackgroundColor(GxEPD_WHITE); - content.fill(""); - } else { - // Initialize with custom text or default - String customText = preferences.getString("displayText", DEFAULT_BOOT_TEXT); - std::array newContent; - newContent.fill(" "); - - for (size_t i = 0; i < std::min(customText.length(), (size_t)NUM_SCREENS); i++) { - newContent[i] = String(customText[i]); - } - - content = newContent; - } - - setContent(content); + setEpdContent(epdContent); } -void EPDManager::loadFonts(const String& fontName) { - if (fontName == FontNames::ANTONIO) { - // Load Antonio fonts - antonioFonts.big = FontLoader::loadCompressedFont(Antonio_SemiBold90pt7b_Properties); - antonioFonts.medium = FontLoader::loadCompressedFont(Antonio_SemiBold40pt7b_Properties); - antonioFonts.small = FontLoader::loadCompressedFont(Antonio_SemiBold20pt7b_Properties); +void setEpdContent(std::array newEpdContent) +{ + setEpdContent(newEpdContent, false); +} - fontBig = antonioFonts.big; - fontMedium = antonioFonts.medium; - fontSmall = antonioFonts.small; - } else if (fontName == FontNames::OSWALD) { - // Load Oswald fonts - oswaldFonts.big = FontLoader::loadCompressedFont(Oswald_Medium80pt7b_Properties); - oswaldFonts.medium = FontLoader::loadCompressedFont(Oswald_Medium30pt7b_Properties); - oswaldFonts.small = FontLoader::loadCompressedFont(Oswald_Medium20pt7b_Properties); +void setEpdContent(std::array newEpdContent) +{ + std::array conv; - fontBig = oswaldFonts.big; - fontMedium = oswaldFonts.medium; - fontSmall = oswaldFonts.small; + for (size_t i = 0; i < newEpdContent.size(); ++i) + { + conv[i] = String(newEpdContent[i].c_str()); } - fontSatsymbol = FontLoader::loadCompressedFont(Satoshi_Symbol90pt7b_Properties); + return setEpdContent(conv); } -void EPDManager::forceFullRefresh() { - std::fill(lastFullRefresh.begin(), lastFullRefresh.end(), 0); -} +void setEpdContent(std::array newEpdContent, + bool forceUpdate) +{ + std::lock_guard lock(epdUpdateMutex); -void EPDManager::setContent(const std::array& newContent, bool forceUpdate) { - std::lock_guard lock(updateMutex); waitUntilNoneBusy(); - for (size_t i = 0; i < NUM_SCREENS; i++) { - if (newContent[i].compareTo(currentContent[i]) != 0 || forceUpdate) { - content[i] = newContent[i]; - UpdateDisplayTaskItem dispUpdate{static_cast(i)}; + 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 EPDManager::setContent(const std::array& newContent) { - std::array conv; - for (size_t i = 0; i < newContent.size(); ++i) { - conv[i] = String(newContent[i].c_str()); - } - setContent(conv); -} +void prepareDisplayUpdateTask(void *pvParameters) +{ + UpdateDisplayTaskItem receivedItem; -std::array EPDManager::getCurrentContent() const { - return currentContent; -} + 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 lock(epdMutex[epdIndex]); + // displays[epdIndex].init(0, false); // Little longer reset duration + // because of MCP -void EPDManager::waitUntilNoneBusy() { - for (size_t i = 0; i < NUM_SCREENS; i++) { - uint32_t count = 0; - while (EPD_BUSY[i].digitalRead()) { - count++; - vTaskDelay(BUSY_RETRY_DELAY); + bool updatePartial = true; - if (count == BUSY_TIMEOUT_COUNT) { - vTaskDelay(pdMS_TO_TICKS(100)); - } else if (count > BUSY_TIMEOUT_COUNT + 5) { - log_e("Display %d busy timeout", i); - break; + if (epdContent[epdIndex].length() > 1 && 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].startsWith(F("mdi"))) + { + bool updated = renderIcon(epdIndex, epdContent[epdIndex], updatePartial); + if (!updated) + { + continue; + } + } + else if (epdContent[epdIndex].length() > 5) + { + renderText(epdIndex, epdContent[epdIndex], updatePartial); + } + else + { + if (epdContent[epdIndex].length() == 2) + { + showChars(epdIndex, epdContent[epdIndex], updatePartial, FONT_BIG); + } + else if (epdContent[epdIndex].length() > 1 && epdContent[epdIndex].indexOf(".") == -1) + { + if (epdContent[epdIndex].equals("STS")) + { + showDigit(epdIndex, 'S', updatePartial, + FONT_SATSYMBOL); + } + else + { + showChars(epdIndex, epdContent[epdIndex], updatePartial, + FONT_MEDIUM); + } + } + else + { + + showDigit(epdIndex, epdContent[epdIndex].c_str()[0], updatePartial, + FONT_BIG); + } + } + + xTaskNotifyGive(tasks[epdIndex]); } } } -void EPDManager::setupDisplay(uint dispNum, const GFXfont* font) { - displays[dispNum].setRotation(2); - displays[dispNum].setFont(font); - displays[dispNum].setTextColor(fgColor); - displays[dispNum].fillScreen(bgColor); +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 lock(epdMutex[epdIndex]); + + { + std::lock_guard 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++; + } + } } -void EPDManager::splitText(uint dispNum, const String& top, const String& bottom, bool partial) { - if (preferences.getBool("verticalDesc", DEFAULT_VERTICAL_DESC) && dispNum == 0) { +void splitText(const uint dispNum, const String &top, const String &bottom, + bool partial) +{ + if (preferences.getBool("verticalDesc", DEFAULT_VERTICAL_DESC) && dispNum == 0) + { displays[dispNum].setRotation(1); - } else { + } + else + { displays[dispNum].setRotation(2); } - displays[dispNum].setFont(fontSmall); - displays[dispNum].setTextColor(fgColor); + 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; + 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; + uint16_t by = + ((displays[dispNum].height() - tbbh) / 2) - tbby + tbbh / 2 + 12; - // Make separator as wide as the shortest text - uint16_t lineWidth = (tbbw < ttbh) ? tbbw : ttbw; - uint16_t lineX = round((displays[dispNum].width() - lineWidth) / 2); + // 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(bgColor); + 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, fgColor); + lineWidth, 6, 3, getFgColor()); displays[dispNum].setCursor(bx, by); displays[dispNum].print(bottom); } -void EPDManager::showDigit(uint dispNum, char chr, bool partial, const GFXfont* font) { +// Consolidate common display setup code into a helper function +void setupDisplay(const uint dispNum, const GFXfont *font) +{ + displays[dispNum].setRotation(2); + displays[dispNum].setFont(font); + displays[dispNum].setTextColor(getFgColor()); + displays[dispNum].fillScreen(getBgColor()); +} + +void showDigit(const uint dispNum, char chr, bool partial, const GFXfont *font) +{ String str(chr); - if (chr == '.') { + if (chr == '.') + { str = "!"; } @@ -288,34 +469,56 @@ void EPDManager::showDigit(uint dispNum, char chr, bool partial, const GFXfont* displays[dispNum].setCursor(x, y); displays[dispNum].print(str); - if (chr == '.') { + if (chr == '.') + { displays[dispNum].fillRect(0, 0, displays[dispNum].width(), - round(displays[dispNum].height() * 0.67), bgColor); + round(displays[dispNum].height() * 0.67), getBgColor()); } } -void EPDManager::showChars(uint dispNum, const String& chars, bool partial, const GFXfont* font) { +int16_t calculateDescent(const GFXfont *font) +{ + int16_t maxDescent = 0; + for (uint16_t i = font->first; i <= font->last; i++) + { + GFXglyph *glyph = &font->glyph[i - font->first]; + int16_t descent = glyph->yOffset; + if (descent > maxDescent) + { + maxDescent = descent; + } + } + return maxDescent; +} + +void showChars(const uint dispNum, const String &chars, bool partial, + const GFXfont *font) +{ setupDisplay(dispNum, font); 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 + // 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; - for (size_t i = 0; i < chars.length(); i++) { + for (int i = 0; i < chars.length(); i++) + { char c = chars[i]; - if (c == '.' || c == ',') { + if (c == '.' || c == ',') + { // For the dot, calculate its specific descent - GFXglyph* dotGlyph = &font->glyph[c - font->first]; + GFXglyph *dotGlyph = &font->glyph[c - font->first]; int16_t dotDescent = dotGlyph->yOffset; // Draw the dot with adjusted y-position displays[dispNum].setCursor(x, y + dotDescent + dotGlyph->height + 8); displays[dispNum].print(c); - } else { + } + else + { // For other characters, use the original y-position displays[dispNum].setCursor(x, y); displays[dispNum].print(c); @@ -326,212 +529,169 @@ void EPDManager::showChars(uint dispNum, const String& chars, bool partial, cons } } -bool EPDManager::renderIcon(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(bgColor); - displays[dispNum].setTextColor(fgColor); +int getBgColor() { return bgColor; } - uint iconIndex = 0; - uint width = 122; - uint height = 122; +int getFgColor() { return fgColor; } - if (text.endsWith("rocket")) { - iconIndex = 1; - } else if (text.endsWith("lnbolt")) { - iconIndex = 2; - } else if (text.endsWith("bitaxe")) { - width = 88; - height = 220; - iconIndex = 3; - } else if (text.endsWith("miningpool")) { - LogoData logo = MiningPoolStatsFetch::getInstance().getLogo(); - if (logo.size == 0) { - Serial.println(F("No logo found")); - return false; - } +void setBgColor(int color) { bgColor = color; } - int x_offset = (displays[dispNum].width() - logo.width) / 2; - int y_offset = (displays[dispNum].height() - logo.height) / 2; - displays[dispNum].drawInvertedBitmap(x_offset, y_offset, logo.data, - logo.width, logo.height, fgColor); - return true; - } +void setFgColor(int color) { fgColor = color; } - int x_offset = (displays[dispNum].width() - width) / 2; - int y_offset = (displays[dispNum].height() - height) / 2; - displays[dispNum].drawInvertedBitmap(x_offset, y_offset, epd_icons_allArray[iconIndex], - width, height, fgColor); - return true; +std::array getCurrentEpdContent() +{ + return currentEpdContent; } - -void EPDManager::renderText(uint dispNum, const String& text, bool partial) { +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].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) { + 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); - } else { - displays[dispNum].setFont(&FreeSans9pt7b); + displays[dispNum].println(line.c_str()); + } + else + { + displays[dispNum].setFont(&FreeSans9pt7b); + displays[dispNum].println(line.c_str()); } - displays[dispNum].println(line.c_str()); } } -void EPDManager::renderQr(uint dispNum, const String& text, bool partial) { -#ifdef USE_QR - // Dynamically allocate QR buffer - uint8_t* qrcode = (uint8_t*)malloc(qrcodegen_BUFFER_LEN_MAX); - if (!qrcode) { - log_e("Failed to allocate QR buffer"); - return; +bool renderIcon(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(getBgColor()); + displays[dispNum].setTextColor(getFgColor()); + + // Handle mining pool logo separately + if (text.endsWith("miningpool")) + { + LogoData logo = getMiningPoolLogo(); + if (logo.size == 0) + { + Serial.println(F("No logo found")); + return false; + } + + int x_offset = (displays[dispNum].width() - logo.width) / 2; + int y_offset = (displays[dispNum].height() - logo.height) / 2; + displays[dispNum].drawInvertedBitmap(x_offset, y_offset, logo.data, logo.width, logo.height, getFgColor()); + + if (!partial) + { + displays[dispNum].display(); + } + return true; } + const char* iconName = nullptr; + if (text.endsWith("rocket")) { + iconName = "rocket"; + } else if (text.endsWith("lnbolt")) { + iconName = "lightning"; + } else if (text.endsWith("bitaxe")) { + iconName = "bitaxe"; + } else if (text.endsWith("pickaxe")) { + iconName = "pickaxe"; + } + + if (!iconName) { + return false; + } + + IconData* icon = IconManager::getIcon(iconName); + if (!icon) { + Serial.printf("Icon %s not loaded\n", iconName); + return false; + } + + // Calculate center position + int x_offset = (displays[dispNum].width() - icon->width) / 2; + int y_offset = (displays[dispNum].height() - icon->height) / 2; + + // Draw the icon + displays[dispNum].drawInvertedBitmap(x_offset, y_offset, icon->data, icon->width, icon->height, getFgColor()); + + if (!partial) + { + displays[dispNum].display(); + } + + return true; +} + +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); - if (ok) { - 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); - displays[dispNum].setPartialWindow(0, 0, displays[dispNum].width(), - displays[dispNum].height()); - displays[dispNum].fillScreen(GxEPD_WHITE); + const int size = qrcodegen_getSize(qrcode); - for (int y = 0; y < size * 4; y++) { - for (int x = 0; x < size * 4; x++) { - displays[dispNum].drawPixel( - padding + x, paddingY + y, - qrcodegen_getModule(qrcode, floor(float(x) / 4), floor(float(y) / 4)) - ? GxEPD_BLACK - : GxEPD_WHITE); - } + 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; + + 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); } } - - free(qrcode); #endif } -int16_t EPDManager::calculateDescent(const GFXfont* font) { - int16_t maxDescent = 0; - for (uint16_t i = font->first; i <= font->last; i++) { - GFXglyph* glyph = &font->glyph[i - font->first]; - int16_t descent = glyph->yOffset; - if (descent > maxDescent) { - maxDescent = descent; - } - } - return maxDescent; -} - -void EPDManager::updateDisplayTask(void* pvParameters) noexcept { - auto& instance = EPDManager::getInstance(); - const int epdIndex = *(int*)pvParameters; - delete (int*)pvParameters; - - for (;;) { - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - - std::lock_guard lock(instance.displayMutexes[epdIndex]); +void waitUntilNoneBusy() +{ + for (int i = 0; i < NUM_SCREENS; i++) + { + uint count = 0; + while (EPD_BUSY[i].digitalRead()) { - std::lock_guard lockMcp(mcpMutex); - instance.displays[epdIndex].init(0, false, 40); - } - - uint32_t count = 0; - while (instance.EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10) { - vTaskDelay(pdMS_TO_TICKS(100)); count++; - } + vTaskDelay(BUSY_RETRY_DELAY); - bool updatePartial = true; - if (!instance.lastFullRefresh[epdIndex] || - (millis() - instance.lastFullRefresh[epdIndex]) > - (preferences.getUInt("fullRefreshMin", DEFAULT_MINUTES_FULL_REFRESH) * 60 * 1000)) { - updatePartial = false; - } - - char tries = 0; - while (tries < 3) { - if (instance.displays[epdIndex].displayWithReturn(updatePartial)) { - instance.displays[epdIndex].powerOff(); - instance.currentContent[epdIndex] = instance.content[epdIndex]; - if (!updatePartial) { - instance.lastFullRefresh[epdIndex] = millis(); - } - - if (eventSourceTaskHandle != nullptr) { - xTaskNotifyGive(eventSourceTaskHandle); - } + if (count == BUSY_TIMEOUT_COUNT) + { + vTaskDelay(pdMS_TO_TICKS(100)); + } + else if (count > BUSY_TIMEOUT_COUNT + 5) + { + log_e("Display %d busy timeout", i); break; } - - vTaskDelay(pdMS_TO_TICKS(100)); - tries++; - } - } -} - -void EPDManager::prepareDisplayUpdateTask(void* pvParameters) { - auto& instance = EPDManager::getInstance(); - UpdateDisplayTaskItem receivedItem; - - for (;;) { - if (xQueueReceive(instance.updateQueue, &receivedItem, portMAX_DELAY)) { - uint epdIndex = receivedItem.dispNum; - std::lock_guard lock(instance.displayMutexes[epdIndex]); - - bool updatePartial = true; - - if (instance.content[epdIndex].length() > 1 && - strstr(instance.content[epdIndex].c_str(), "/") != nullptr) { - String top = instance.content[epdIndex].substring( - 0, instance.content[epdIndex].indexOf("/")); - String bottom = instance.content[epdIndex].substring( - instance.content[epdIndex].indexOf("/") + 1); - instance.splitText(epdIndex, top, bottom, updatePartial); - } else if (instance.content[epdIndex].startsWith(F("qr"))) { - instance.renderQr(epdIndex, instance.content[epdIndex], updatePartial); - } else if (instance.content[epdIndex].startsWith(F("mdi"))) { - if (!instance.renderIcon(epdIndex, instance.content[epdIndex], updatePartial)) { - continue; - } - } else if (instance.content[epdIndex].length() > 5) { - instance.renderText(epdIndex, instance.content[epdIndex], updatePartial); - } else { - if (instance.content[epdIndex].length() == 2) { - instance.showChars(epdIndex, instance.content[epdIndex], updatePartial, instance.fontBig); - } else if (instance.content[epdIndex].length() > 1 && - instance.content[epdIndex].indexOf(".") == -1) { - if (instance.content[epdIndex].equals("STS")) { - instance.showDigit(epdIndex, 'S', updatePartial, instance.fontSatsymbol); - } else { - instance.showChars(epdIndex, instance.content[epdIndex], updatePartial, - instance.fontMedium); - } - } else { - instance.showDigit(epdIndex, instance.content[epdIndex].c_str()[0], - updatePartial, instance.fontBig); - } - } - - xTaskNotifyGive(instance.tasks[epdIndex]); } } } \ No newline at end of file diff --git a/src/lib/epd.hpp b/src/lib/epd.hpp index 80bed83..035806c 100644 --- a/src/lib/epd.hpp +++ b/src/lib/epd.hpp @@ -9,14 +9,13 @@ #include #include #include -#include -#include #include "fonts/fonts.hpp" #include "lib/config.hpp" #include "lib/shared.hpp" #include "icons/icons.h" #include "mining_pool_stats_fetch.hpp" +#include "icon_manager.hpp" // Font includes #include "../fonts/antonio-semibold20.h" @@ -34,102 +33,39 @@ #include "qrcodegen.h" #endif -struct UpdateDisplayTaskItem { - char dispNum; -}; +typedef struct { + char dispNum; +} UpdateDisplayTaskItem; -struct FontFamily { - GFXfont* big; - GFXfont* medium; - GFXfont* small; -}; +void forceFullRefresh(); +void setupDisplays(); +void loadFonts(const String& fontName); -class EPDManager { -public: - static EPDManager& getInstance(); +void splitText(const uint dispNum, const String &top, const String &bottom, + bool partial); - // Delete copy constructor and assignment operator - EPDManager(const EPDManager&) = delete; - EPDManager& operator=(const EPDManager&) = delete; +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 initialize(); - void forceFullRefresh(); - void loadFonts(const String& fontName); - void setContent(const std::array& newContent, bool forceUpdate = false); - void setContent(const std::array& newContent); - std::array getCurrentContent() const; +extern "C" void updateDisplay(void *pvParameters) noexcept; +void updateDisplayAlt(int epdIndex); +void prepareDisplayUpdateTask(void *pvParameters); - int getBackgroundColor() const { return bgColor; } - int getForegroundColor() const { return fgColor; } - void setBackgroundColor(int color) { bgColor = color; } - void setForegroundColor(int color) { fgColor = color; } - void waitUntilNoneBusy(); +int getBgColor(); +int getFgColor(); +void setBgColor(int color); +void setFgColor(int color); -private: - EPDManager(); // Private constructor for singleton - ~EPDManager(); // Private destructor +bool renderIcon(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 setupDisplay(uint dispNum, const GFXfont* font); - void splitText(uint dispNum, const String& top, const String& bottom, bool partial); - void showDigit(uint dispNum, char chr, bool partial, const GFXfont* font); - void showChars(uint dispNum, const String& chars, bool partial, const GFXfont* font); - bool renderIcon(uint dispNum, const String& text, bool partial); - void renderText(uint dispNum, const String& text, bool partial); - void renderQr(uint dispNum, const String& text, bool partial); - int16_t calculateDescent(const GFXfont* font); +void setEpdContent(std::array newEpdContent, + bool forceUpdate); +void setEpdContent(std::array newEpdContent); - static void updateDisplayTask(void* pvParameters) noexcept; - static void prepareDisplayUpdateTask(void* pvParameters); +void setEpdContent(std::array newEpdContent); - // Member variables - std::array currentContent; - std::array content; - std::array lastFullRefresh; - std::array tasks; - QueueHandle_t updateQueue; - - FontFamily antonioFonts; - FontFamily oswaldFonts; - const GFXfont* fontSmall; - const GFXfont* fontBig; - const GFXfont* fontMedium; - const GFXfont* fontSatsymbol; - - int bgColor; - int fgColor; - - std::mutex updateMutex; - std::array displayMutexes; - - // Pin configurations based on board version - #ifdef IS_BTCLOCK_REV_B - static Native_Pin EPD_DC; - static std::array EPD_CS; - static std::array EPD_BUSY; - static std::array EPD_RESET; - #elif defined(IS_BTCLOCK_V8) - static Native_Pin EPD_DC; - static std::array EPD_BUSY; - static std::array EPD_CS; - static std::array EPD_RESET; - #else - static Native_Pin EPD_DC; - static std::array EPD_CS; - static std::array EPD_BUSY; - static std::array EPD_RESET; - #endif - - // Display array - std::array, NUM_SCREENS> displays; - - static constexpr size_t UPDATE_QUEUE_SIZE = 14; - static constexpr uint32_t BUSY_TIMEOUT_COUNT = 200; - static constexpr TickType_t BUSY_RETRY_DELAY = pdMS_TO_TICKS(10); - static constexpr size_t EPD_TASK_STACK_SIZE = - #ifdef IS_BTCLOCK_V8 - 4096 - #else - 2048 - #endif - ; -}; \ No newline at end of file +std::array getCurrentEpdContent(); +void waitUntilNoneBusy(); \ No newline at end of file diff --git a/src/lib/icon_manager.cpp b/src/lib/icon_manager.cpp new file mode 100644 index 0000000..6c3532a --- /dev/null +++ b/src/lib/icon_manager.cpp @@ -0,0 +1,59 @@ +#include "icon_manager.hpp" +#include "gzip_decompressor.hpp" + +std::map IconManager::loadedIcons; + +void IconManager::loadIcon(const char* name, const unsigned char* compressedData, size_t compressedSize, size_t originalSize, uint width, uint height) { + // Check if icon is already loaded + if (loadedIcons.find(name) != loadedIcons.end()) { + return; + } + + // Allocate memory for decompressed data + uint8_t* decompressedData = new uint8_t[originalSize]; + if (!decompressedData) { + Serial.printf("Failed to allocate memory for icon %s\n", name); + return; + } + + // Decompress the icon + size_t decompressedSize = originalSize; + if (!GzipDecompressor::decompressData(compressedData, compressedSize, decompressedData, &decompressedSize)) { + Serial.printf("Failed to decompress icon %s\n", name); + delete[] decompressedData; + return; + } + + Serial.printf("Decompressed icon %s to %zu bytes\n", name, decompressedSize); + + // Store the icon data + IconData iconData = { + .data = decompressedData, + .width = width, + .height = height + }; + loadedIcons[name] = iconData; +} + +void IconManager::unloadIcon(const char* name) { + auto it = loadedIcons.find(name); + if (it != loadedIcons.end()) { + delete[] it->second.data; + loadedIcons.erase(it); + } +} + +IconData* IconManager::getIcon(const char* name) { + auto it = loadedIcons.find(name); + if (it != loadedIcons.end()) { + return &it->second; + } + return nullptr; +} + +void IconManager::cleanup() { + for (auto& pair : loadedIcons) { + delete[] pair.second.data; + } + loadedIcons.clear(); +} \ No newline at end of file diff --git a/src/lib/icon_manager.hpp b/src/lib/icon_manager.hpp new file mode 100644 index 0000000..30cc29d --- /dev/null +++ b/src/lib/icon_manager.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +struct IconData { + uint8_t* data; + uint width; + uint height; +}; + +class IconManager { +public: + static void loadIcon(const char* name, const unsigned char* compressedData, size_t compressedSize, size_t originalSize, uint width, uint height); + static void unloadIcon(const char* name); + static IconData* getIcon(const char* name); + static void cleanup(); + +private: + static std::map loadedIcons; +}; \ No newline at end of file diff --git a/src/lib/led_handler.cpp b/src/lib/led_handler.cpp index 453b067..806ee9c 100644 --- a/src/lib/led_handler.cpp +++ b/src/lib/led_handler.cpp @@ -1,431 +1,227 @@ #include "led_handler.hpp" -// Singleton instance -LedHandler& LedHandler::getInstance() { - static LedHandler instance; - return instance; -} +TaskHandle_t ledTaskHandle = NULL; +QueueHandle_t ledTaskQueue = NULL; +Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); +uint ledTaskParams; -LedHandler::LedHandler() - : pixels(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800) - , ledTaskHandle(nullptr) - , ledTaskQueue(nullptr) - , ledTaskParams(0) - , dndEnabled(false) - , dndTimeBasedEnabled(false) - , dndTimeRange{23, 0, 7, 0} // Default: 23:00 to 07:00 #ifdef HAS_FRONTLIGHT - , frontlightOn(false) - , flInTransition(false) -#endif +constexpr uint16_t FL_FADE_STEP = 25; + +bool frontlightOn = false; +bool flInTransition = false; + +void frontlightFlash(int flDelayTime) { + if (preferences.getBool("flDisable")) + return; + + if (frontlightOn) + { + frontlightFadeOutAll(flDelayTime, true); + frontlightFadeInAll(flDelayTime, true); + } + else + { + frontlightFadeInAll(flDelayTime, true); + frontlightFadeOutAll(flDelayTime, true); + } } -void LedHandler::setup() { - loadDNDSettings(); - pixels.begin(); - pixels.setBrightness(preferences.getUInt("ledBrightness", DEFAULT_LED_BRIGHTNESS)); - pixels.clear(); - pixels.show(); - setupTask(); - - if (preferences.getBool("ledTestOnPower", DEFAULT_LED_TEST_ON_POWER)) { - while (!ledTaskQueue) { - delay(1); - } - queueEffect(LED_POWER_TEST); +void frontlightFadeInAll() +{ + frontlightFadeInAll(preferences.getUInt("flEffectDelay")); +} + +void frontlightFadeOutAll() +{ + frontlightFadeOutAll(preferences.getUInt("flEffectDelay")); +} + +void frontlightFadeIn(uint num) +{ + frontlightFadeIn(num, preferences.getUInt("flEffectDelay")); +} + +void frontlightFadeOut(uint num) +{ + frontlightFadeOut(num, preferences.getUInt("flEffectDelay")); +} + +void frontlightSetBrightness(uint brightness) +{ + if (isDNDActive()) { + return; // Don't change brightness during DND mode + } + if (brightness > 4096) + { + return; + } + + for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++) + { + flArray.setPWM(ledPin, 0, brightness); + } +} + +void frontlightFadeInAll(int flDelayTime) +{ + frontlightFadeInAll(flDelayTime, false); +} + +void frontlightFadeInAll(int flDelayTime, bool staggered) +{ + if (preferences.getBool("flDisable") || frontlightIsOn() || flInTransition) + return; + + flInTransition = true; + + const int maxBrightness = preferences.getUInt("flMaxBrightness"); + + if (staggered) + { + int step = FL_FADE_STEP; + int staggerDelay = flDelayTime / NUM_SCREENS; + + for (int dutyCycle = 0; dutyCycle <= maxBrightness + (NUM_SCREENS - 1) * maxBrightness / NUM_SCREENS; dutyCycle += step) + { + for (int ledPin = 0; ledPin < NUM_SCREENS; ledPin++) + { + int ledBrightness = dutyCycle - ledPin * maxBrightness / NUM_SCREENS; + if (ledBrightness < 0) + ledBrightness = 0; + else if (ledBrightness > maxBrightness) + ledBrightness = maxBrightness; + + flArray.setPWM(ledPin + 1, 0, ledBrightness); + } + vTaskDelay(pdMS_TO_TICKS(staggerDelay)); } + } + else + { + for (int dutyCycle = 0; dutyCycle <= maxBrightness; dutyCycle += FL_FADE_STEP) + { + for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++) + { + flArray.setPWM(ledPin, 0, dutyCycle); + } + vTaskDelay(pdMS_TO_TICKS(flDelayTime)); + } + } + frontlightOn = true; + flInTransition = false; } -void LedHandler::setupTask() { - ledTaskQueue = xQueueCreate(5, sizeof(uint)); - xTaskCreate(ledTask, "LedTask", 2048, this, 10, &ledTaskHandle); +void frontlightFadeOutAll(int flDelayTime) +{ + frontlightFadeOutAll(flDelayTime, false); } -void LedHandler::ledTask(void* pvParameters) { - auto* handler = static_cast(pvParameters); - while (true) { - if (handler->ledTaskQueue != nullptr) { - if (xQueueReceive(handler->ledTaskQueue, &handler->ledTaskParams, portMAX_DELAY) == pdPASS) { - if (preferences.getBool("disableLeds", DEFAULT_DISABLE_LEDS)) { - continue; - } +void frontlightFadeOutAll(int flDelayTime, bool staggered) +{ + if (preferences.getBool("flDisable")) + return; + if (!frontlightIsOn()) + return; + if (flInTransition) + return; + flInTransition = true; - std::array oldLights; - for (int i = 0; i < NEOPIXEL_COUNT; i++) { - oldLights[i] = handler->pixels.getPixelColor(i); - } + if (staggered) + { + int maxBrightness = preferences.getUInt("flMaxBrightness"); + int step = FL_FADE_STEP; + int staggerDelay = flDelayTime / NUM_SCREENS; -#ifdef HAS_FRONTLIGHT - uint flDelayTime = preferences.getUInt("flEffectDelay"); + for (int dutyCycle = maxBrightness; dutyCycle >= 0; dutyCycle -= step) + { + for (int ledPin = 0; ledPin < NUM_SCREENS; ledPin++) + { + int ledBrightness = dutyCycle - (NUM_SCREENS - 1 - ledPin) * maxBrightness / NUM_SCREENS; + if (ledBrightness < 0) + ledBrightness = 0; + else if (ledBrightness > maxBrightness) + ledBrightness = maxBrightness; + + flArray.setPWM(ledPin + 1, 0, ledBrightness); + } + vTaskDelay(pdMS_TO_TICKS(staggerDelay)); + } + } + else + { + for (int dutyCycle = preferences.getUInt("flMaxBrightness"); dutyCycle >= 0; dutyCycle -= FL_FADE_STEP) + { + for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++) + { + flArray.setPWM(ledPin, 0, dutyCycle); + } + vTaskDelay(pdMS_TO_TICKS(flDelayTime)); + } + } + + flArray.allOFF(); + frontlightOn = false; + flInTransition = false; +} + +std::vector frontlightGetStatus() +{ + std::vector statuses; + for (int ledPin = 1; ledPin <= NUM_SCREENS; ledPin++) + { + uint16_t a = 0, b = 0; + flArray.getPWM(ledPin, &a, &b); + statuses.push_back(round(b - a / 4096)); + } + + return statuses; +} + +bool frontlightIsOn() +{ + return frontlightOn; +} + +void frontlightFadeIn(uint num, int flDelayTime) +{ + if (isDNDActive()) { + return; // Don't change brightness during DND mode + } + if (preferences.getBool("flDisable")) + return; + for (int dutyCycle = 0; dutyCycle <= preferences.getUInt("flMaxBrightness"); dutyCycle += 5) + { + flArray.setPWM(num, 0, dutyCycle); + vTaskDelay(pdMS_TO_TICKS(flDelayTime)); + } +} + +void frontlightFadeOut(uint num, int flDelayTime) +{ + if (isDNDActive()) { + return; // Don't change brightness during DND mode + } + if (preferences.getBool("flDisable")) + return; + if (!frontlightIsOn()) + return; + + for (int dutyCycle = preferences.getUInt("flMaxBrightness"); dutyCycle >= 0; dutyCycle -= 5) + { + flArray.setPWM(num, 0, dutyCycle); + vTaskDelay(pdMS_TO_TICKS(flDelayTime)); + } +} #endif - switch (handler->ledTaskParams) { - case LED_POWER_TEST: -#ifdef HAS_FRONTLIGHT - handler->frontlightFadeInAll(preferences.getUInt("flEffectDelay"), true); -#endif - handler->rainbow(20); - handler->pixels.clear(); - break; +// Do Not Disturb mode variables +bool dndEnabled = false; +bool dndTimeBasedEnabled = false; +DNDTimeRange dndTimeRange = {23, 0, 7, 0}; // Default: 23:00 to 07:00 - case LED_EFFECT_WIFI_CONNECT_ERROR: - handler->blinkDelayTwoColor(100, 3, handler->pixels.Color(8, 161, 236), - handler->pixels.Color(255, 0, 0)); - break; - - case LED_EFFECT_CONFIGURING: - for (int i = NEOPIXEL_COUNT; i--; i > 0) { - for (int j = NEOPIXEL_COUNT; j--; j > 0) { - uint32_t c = handler->pixels.Color(0, 0, 0); - if (i == j) - c = handler->pixels.Color(0, 0, 255); - handler->pixels.setPixelColor(j, c); - } - handler->pixels.show(); - delay(100); - } - handler->pixels.clear(); - handler->pixels.show(); - break; - - case LED_FLASH_ERROR: - handler->blinkDelayColor(250, 3, 255, 0, 0); - break; - - case LED_EFFECT_HEARTBEAT: - handler->blinkDelayColor(150, 2, 0, 0, 255); - break; - - case LED_DATA_BLOCK_ERROR: - handler->blinkDelayColor(150, 2, 128, 0, 128); - break; - - case LED_DATA_PRICE_ERROR: - handler->blinkDelayColor(150, 2, 177, 90, 31); - break; - - case LED_FLASH_IDENTIFY: - handler->blinkDelayTwoColor(100, 2, handler->pixels.Color(255, 0, 0), - handler->pixels.Color(0, 255, 255)); - handler->blinkDelayTwoColor(100, 2, handler->pixels.Color(0, 255, 0), - handler->pixels.Color(0, 0, 255)); - break; - - case LED_EFFECT_WIFI_CONNECT_SUCCESS: - case LED_FLASH_SUCCESS: - handler->blinkDelayColor(150, 3, 0, 255, 0); - break; - - case LED_PROGRESS_100: - handler->pixels.setPixelColor(0, handler->pixels.Color(0, 255, 0)); - [[fallthrough]]; - case LED_PROGRESS_75: - handler->pixels.setPixelColor(1, handler->pixels.Color(0, 255, 0)); - [[fallthrough]]; - case LED_PROGRESS_50: - handler->pixels.setPixelColor(2, handler->pixels.Color(0, 255, 0)); - [[fallthrough]]; - case LED_PROGRESS_25: - handler->pixels.setPixelColor(3, handler->pixels.Color(0, 255, 0)); - handler->pixels.show(); - break; - - case LED_EFFECT_NOSTR_ZAP: - { -#ifdef HAS_FRONTLIGHT - bool frontlightWasOn = false; - if (preferences.getBool("flFlashOnZap", DEFAULT_FL_FLASH_ON_ZAP)) { - if (handler->frontlightOn) { - frontlightWasOn = true; - handler->frontlightFadeOutAll(flDelayTime, true); - } else { - handler->frontlightFadeInAll(flDelayTime, true); - } - } -#endif - for (int flash = 0; flash < random(7, 10); flash++) { - handler->lightningStrike(); - delay(random(50, 150)); - } -#ifdef HAS_FRONTLIGHT - if (preferences.getBool("flFlashOnZap", DEFAULT_FL_FLASH_ON_ZAP)) { - vTaskDelay(pdMS_TO_TICKS(10)); - if (frontlightWasOn) { - handler->frontlightFadeInAll(flDelayTime, true); - } else { - handler->frontlightFadeOutAll(flDelayTime, true); - } - } -#endif - break; - } - - case LED_FLASH_UPDATE: - handler->blinkDelayTwoColor(250, 3, handler->pixels.Color(0, 230, 0), - handler->pixels.Color(230, 230, 0)); - break; - - case LED_FLASH_BLOCK_NOTIFY: - { -#ifdef HAS_FRONTLIGHT - bool frontlightWasOn = false; - if (preferences.getBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE)) { - if (handler->frontlightOn) { - frontlightWasOn = true; - handler->frontlightFadeOutAll(flDelayTime, true); - } else { - handler->frontlightFadeInAll(flDelayTime, true); - } - } -#endif - handler->blinkDelayTwoColor(250, 3, handler->pixels.Color(224, 67, 0), - handler->pixels.Color(8, 2, 0)); -#ifdef HAS_FRONTLIGHT - if (preferences.getBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE)) { - vTaskDelay(pdMS_TO_TICKS(10)); - if (frontlightWasOn) { - handler->frontlightFadeInAll(flDelayTime, true); - } else { - handler->frontlightFadeOutAll(flDelayTime, true); - } - } -#endif - break; - } - - case LED_EFFECT_WIFI_WAIT_FOR_CONFIG: - handler->blinkDelayTwoColor(100, 1, handler->pixels.Color(8, 161, 236), - handler->pixels.Color(156, 225, 240)); - break; - - case LED_EFFECT_WIFI_ERASE_SETTINGS: - handler->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) { - handler->pixels.setPixelColor(i, handler->pixels.Color(16, 197, 236)); - } else { - handler->pixels.setPixelColor(j, handler->pixels.Color(0, 0, 0)); - } - } - handler->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 = handler->pixels.Color(0, 0, 0); - if (i == j) - c = handler->pixels.Color(0, 255, 0); - handler->pixels.setPixelColor(j, c); - } - handler->pixels.show(); - delay(100); - } - handler->pixels.setPixelColor(0, handler->pixels.Color(255, 0, 0)); - handler->pixels.show(); - delay(900); - handler->pixels.clear(); - handler->pixels.show(); - break; - - case LED_EFFECT_START_TIMER: - handler->pixels.clear(); - handler->pixels.setPixelColor((NEOPIXEL_COUNT - 1), handler->pixels.Color(255, 0, 0)); - handler->pixels.show(); - delay(900); - for (int i = NEOPIXEL_COUNT; i--; i > 0) { - for (int j = NEOPIXEL_COUNT; j--; j > 0) { - uint32_t c = handler->pixels.Color(0, 0, 0); - if (i == j) - c = handler->pixels.Color(0, 255, 0); - handler->pixels.setPixelColor(j, c); - } - handler->pixels.show(); - delay(100); - } - handler->pixels.clear(); - handler->pixels.show(); - break; - } - - // Restore previous state unless power test - for (int i = 0; i < NEOPIXEL_COUNT; i++) { - handler->pixels.setPixelColor(i, oldLights[i]); - } - handler->pixels.show(); - } - } - } -} - -bool LedHandler::queueEffect(uint effect) { - if (isDNDActive()) { - return false; - } - if (ledTaskQueue == nullptr) { - return false; - } - xQueueSend(ledTaskQueue, &effect, portMAX_DELAY); - return true; -} - -void LedHandler::clear() { - preferences.putBool("ledStatus", false); - pixels.clear(); - pixels.show(); -} - -void LedHandler::setLights(int r, int g, int b) { - setLights(pixels.Color(r, g, b)); -} - -void LedHandler::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 LedHandler::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); - } - xTaskNotifyGive(eventSourceTaskHandle); -} - -void LedHandler::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); - } - pixels.show(); -} - -void LedHandler::rainbow(int wait) { - for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256) { - pixels.rainbow(firstPixelHue); - pixels.show(); - delayMicroseconds(wait); - } -} - -void LedHandler::theaterChase(uint32_t color, int wait) { - for (int a = 0; a < 10; a++) { - for (int b = 0; b < 3; b++) { - pixels.clear(); - for (int c = b; c < pixels.numPixels(); c += 3) { - pixels.setPixelColor(c, color); - } - pixels.show(); - vTaskDelay(pdMS_TO_TICKS(wait)); - } - } -} - -void LedHandler::theaterChaseRainbow(int wait) { - int firstPixelHue = 0; - for (int a = 0; a < 30; a++) { - for (int b = 0; b < 3; b++) { - pixels.clear(); - for (int c = b; c < pixels.numPixels(); c += 3) { - int hue = firstPixelHue + c * 65536L / pixels.numPixels(); - uint32_t color = pixels.gamma32(pixels.ColorHSV(hue)); - pixels.setPixelColor(c, color); - } - pixels.show(); - vTaskDelay(pdMS_TO_TICKS(wait)); - firstPixelHue += 65536 / 90; - } - } -} - -void LedHandler::lightningStrike() { - uint32_t PURPLE = pixels.Color(128, 0, 128); - uint32_t YELLOW = pixels.Color(255, 226, 41); - - for (int i = 0; i < pixels.numPixels(); i++) { - pixels.setPixelColor(i, random(2) == 0 ? YELLOW : PURPLE); - } - pixels.show(); - delay(random(10, 50)); -} - -void LedHandler::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 LedHandler::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 LedHandler::blinkDelayTwoColor(int d, int times, const uint32_t& c1, const 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(); -} - -// DND Implementation -void LedHandler::loadDNDSettings() { +void loadDNDSettings() { dndEnabled = preferences.getBool("dndEnabled", false); dndTimeBasedEnabled = preferences.getBool("dndTimeEnabled", false); @@ -435,29 +231,29 @@ void LedHandler::loadDNDSettings() { dndTimeRange.endMinute = preferences.getUChar("dndEndMin", 0); } -void LedHandler::setDNDEnabled(bool enabled) { +void setDNDEnabled(bool enabled) { dndEnabled = enabled; preferences.putBool("dndEnabled", enabled); if (enabled && isDNDActive()) { - clear(); -#ifdef HAS_FRONTLIGHT + clearLeds(); + #ifdef HAS_FRONTLIGHT frontlightFadeOutAll(); -#endif + #endif } } -void LedHandler::setDNDTimeBasedEnabled(bool enabled) { +void setDNDTimeBasedEnabled(bool enabled) { dndTimeBasedEnabled = enabled; preferences.putBool("dndTimeEnabled", enabled); if (enabled && isDNDActive()) { - clear(); -#ifdef HAS_FRONTLIGHT + clearLeds(); + #ifdef HAS_FRONTLIGHT frontlightFadeOutAll(); -#endif + #endif } } -void LedHandler::setDNDTimeRange(uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute) { +void setDNDTimeRange(uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute) { dndTimeRange.startHour = startHour; dndTimeRange.startMinute = startMinute; dndTimeRange.endHour = endHour; @@ -469,19 +265,21 @@ void LedHandler::setDNDTimeRange(uint8_t startHour, uint8_t startMinute, uint8_t preferences.putUChar("dndEndMin", endMinute); } -bool LedHandler::isTimeInDNDRange(uint8_t hour, uint8_t minute) const { +bool isTimeInDNDRange(uint8_t hour, uint8_t minute) { uint16_t currentTime = hour * 60 + minute; uint16_t startTime = dndTimeRange.startHour * 60 + dndTimeRange.startMinute; uint16_t endTime = dndTimeRange.endHour * 60 + dndTimeRange.endMinute; if (startTime <= endTime) { + // Simple case: start time is before end time (e.g., 09:00 to 17:00) return currentTime >= startTime && currentTime < endTime; } else { + // Complex case: start time is after end time (e.g., 23:00 to 07:00) return currentTime >= startTime || currentTime < endTime; } } -bool LedHandler::isDNDActive() const { +bool isDNDActive() { if (dndEnabled) { return true; } @@ -497,174 +295,518 @@ bool LedHandler::isDNDActive() const { return false; } +void ledTask(void *parameter) +{ + while (1) + { + if (ledTaskQueue != NULL) + { + if (xQueueReceive(ledTaskQueue, &ledTaskParams, portMAX_DELAY) == + pdPASS) + { + + if (preferences.getBool("disableLeds", DEFAULT_DISABLE_LEDS)) + { + continue; + } + + std::array oldLights; + + // get current state + for (int i = 0; i < NEOPIXEL_COUNT; i++) + { + oldLights[i] = pixels.getPixelColor(i); + } #ifdef HAS_FRONTLIGHT -// Frontlight implementation -void LedHandler::frontlightFlash(int flDelayTime) { - if (preferences.getBool("flDisable")) { - return; - } - - if (frontlightOn) { - frontlightFadeOutAll(flDelayTime, true); - frontlightFadeInAll(flDelayTime, true); - } else { - frontlightFadeInAll(flDelayTime, true); - frontlightFadeOutAll(flDelayTime, true); - } -} - -void LedHandler::frontlightFadeInAll() { - frontlightFadeInAll(preferences.getUInt("flEffectDelay")); -} - -void LedHandler::frontlightFadeOutAll() { - frontlightFadeOutAll(preferences.getUInt("flEffectDelay")); -} - -void LedHandler::frontlightFadeIn(uint num) { - frontlightFadeIn(num, preferences.getUInt("flEffectDelay")); -} - -void LedHandler::frontlightFadeOut(uint num) { - frontlightFadeOut(num, preferences.getUInt("flEffectDelay")); -} - -void LedHandler::frontlightSetBrightness(uint brightness) { - if (isDNDActive() || brightness > 4096) { - return; - } - - for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++) { - flArray.setPWM(ledPin + 1, 0, brightness); - } -} - -std::vector LedHandler::frontlightGetStatus() { - std::vector statuses; - for (int ledPin = 1; ledPin <= NUM_SCREENS; ledPin++) { - uint16_t a = 0, b = 0; - flArray.getPWM(ledPin + 1, &a, &b); - statuses.push_back(round(b - a / 4096)); - } - return statuses; -} - -void LedHandler::frontlightFadeInAll(int flDelayTime, bool staggered) { - if (preferences.getBool("flDisable") || frontlightIsOn() || flInTransition) { - return; - } - - flInTransition = true; - const int maxBrightness = preferences.getUInt("flMaxBrightness"); - - if (staggered) { - int step = FL_FADE_STEP; - int staggerDelay = flDelayTime / NUM_SCREENS; - - for (int dutyCycle = 0; dutyCycle <= maxBrightness + (NUM_SCREENS - 1) * maxBrightness / NUM_SCREENS; dutyCycle += step) { - for (int ledPin = 0; ledPin < NUM_SCREENS; ledPin++) { - int ledBrightness = dutyCycle - ledPin * maxBrightness / NUM_SCREENS; - if (ledBrightness < 0) { - ledBrightness = 0; - } else if (ledBrightness > maxBrightness) { - ledBrightness = maxBrightness; - } - flArray.setPWM(ledPin + 1, 0, ledBrightness); - } - vTaskDelay(pdMS_TO_TICKS(staggerDelay)); - } - } else { - for (int dutyCycle = 0; dutyCycle <= maxBrightness; dutyCycle += FL_FADE_STEP) { - for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++) { - flArray.setPWM(ledPin + 1, 0, dutyCycle); - } - vTaskDelay(pdMS_TO_TICKS(flDelayTime)); - } - } - frontlightOn = true; - flInTransition = false; -} - -void LedHandler::frontlightFadeOutAll(int flDelayTime, bool staggered) { - if (preferences.getBool("flDisable") || !frontlightIsOn() || flInTransition) { - return; - } - - flInTransition = true; - if (staggered) { - int maxBrightness = preferences.getUInt("flMaxBrightness"); - int step = FL_FADE_STEP; - int staggerDelay = flDelayTime / NUM_SCREENS; - - for (int dutyCycle = maxBrightness; dutyCycle >= 0; dutyCycle -= step) { - for (int ledPin = 0; ledPin < NUM_SCREENS; ledPin++) { - int ledBrightness = dutyCycle - (NUM_SCREENS - 1 - ledPin) * maxBrightness / NUM_SCREENS; - if (ledBrightness < 0) { - ledBrightness = 0; - } else if (ledBrightness > maxBrightness) { - ledBrightness = maxBrightness; - } - flArray.setPWM(ledPin + 1, 0, ledBrightness); - } - vTaskDelay(pdMS_TO_TICKS(staggerDelay)); - } - } else { - for (int dutyCycle = preferences.getUInt("flMaxBrightness"); dutyCycle >= 0; dutyCycle -= FL_FADE_STEP) { - for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++) { - flArray.setPWM(ledPin + 1, 0, dutyCycle); - } - vTaskDelay(pdMS_TO_TICKS(flDelayTime)); - } - } - - flArray.allOFF(); - frontlightOn = false; - flInTransition = false; -} - -void LedHandler::frontlightFadeIn(uint num, int flDelayTime) { - if (isDNDActive() || preferences.getBool("flDisable")) { - return; - } - - for (int dutyCycle = 0; dutyCycle <= preferences.getUInt("flMaxBrightness"); dutyCycle += 5) { - flArray.setPWM(num + 1, 0, dutyCycle); - vTaskDelay(pdMS_TO_TICKS(flDelayTime)); - } -} - -void LedHandler::frontlightFadeOut(uint num, int flDelayTime) { - if (isDNDActive() || preferences.getBool("flDisable") || !frontlightIsOn()) { - return; - } - - for (int dutyCycle = preferences.getUInt("flMaxBrightness"); dutyCycle >= 0; dutyCycle -= 5) { - flArray.setPWM(num + 1, 0, dutyCycle); - vTaskDelay(pdMS_TO_TICKS(flDelayTime)); - } -} - -void LedHandler::initializeFrontlight() { - if (!flArray.begin(PCA9685_MODE1_AUTOINCR | PCA9685_MODE1_ALLCALL, PCA9685_MODE2_TOTEMPOLE)) - { - Serial.println(F("FL driver error")); - return; - } - flArray.setFrequency(200); - Serial.println(F("FL driver active")); - - if (!preferences.isKey("flMaxBrightness")) - { - preferences.putUInt("flMaxBrightness", DEFAULT_FL_MAX_BRIGHTNESS); - } - if (!preferences.isKey("flEffectDelay")) - { - preferences.putUInt("flEffectDelay", DEFAULT_FL_EFFECT_DELAY); - } - - if (!preferences.isKey("flFlashOnUpd")) - { - preferences.putBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE); - } -} + uint flDelayTime = preferences.getUInt("flEffectDelay"); #endif + switch (ledTaskParams) + { + case LED_POWER_TEST: +#ifdef HAS_FRONTLIGHT + frontlightFadeInAll(preferences.getUInt("flEffectDelay"), true); +#endif + 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_EFFECT_CONFIGURING: + 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, 0, 255); + + pixels.setPixelColor(j, c); + } + + pixels.show(); + + delay(100); + } + + pixels.clear(); + pixels.show(); + 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_DATA_BLOCK_ERROR: + blinkDelayColor(150, 2, 128, 0, 128); + break; + case LED_DATA_PRICE_ERROR: + blinkDelayColor(150, 2, 177, 90, 31); + break; + case LED_FLASH_IDENTIFY: + blinkDelayTwoColor(100, 2, pixels.Color(255, 0, 0), + pixels.Color(0, 255, 255)); + blinkDelayTwoColor(100, 2, pixels.Color(0, 255, 0), + pixels.Color(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_EFFECT_NOSTR_ZAP: + { +#ifdef HAS_FRONTLIGHT + bool frontlightWasOn = false; + + if (preferences.getBool("flFlashOnZap", DEFAULT_FL_FLASH_ON_ZAP)) + { + if (frontlightOn) + { + frontlightWasOn = true; + frontlightFadeOutAll(flDelayTime, true); + } + else + { + frontlightFadeInAll(flDelayTime, true); + } + } +#endif + for (int flash = 0; flash < random(7, 10); flash++) + { + lightningStrike(); + delay(random(50, 150)); + } + // blinkDelayColor(250, 3, 142, 48, 235); + // blinkDelayTwoColor(250, 3, pixels.Color(142, 48, 235), + // pixels.Color(169, 21, 255)); +#ifdef HAS_FRONTLIGHT + if (preferences.getBool("flFlashOnZap", DEFAULT_FL_FLASH_ON_ZAP)) + { + vTaskDelay(pdMS_TO_TICKS(10)); + if (frontlightWasOn) + { + frontlightFadeInAll(flDelayTime, true); + } + else + { + frontlightFadeOutAll(flDelayTime, true); + } + } +#endif + break; + } + case LED_FLASH_UPDATE: + blinkDelayTwoColor(250, 3, pixels.Color(0, 230, 0), + pixels.Color(230, 230, 0)); + break; + case LED_FLASH_BLOCK_NOTIFY: + { +#ifdef HAS_FRONTLIGHT + bool frontlightWasOn = false; + + if (preferences.getBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE)) + { + if (frontlightOn) + { + frontlightWasOn = true; + frontlightFadeOutAll(flDelayTime, true); + } + else + { + frontlightFadeInAll(flDelayTime, true); + } + } +#endif + blinkDelayTwoColor(250, 3, pixels.Color(224, 67, 0), + pixels.Color(8, 2, 0)); +#ifdef HAS_FRONTLIGHT + if (preferences.getBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE)) + { + vTaskDelay(pdMS_TO_TICKS(10)); + if (frontlightWasOn) + { + frontlightFadeInAll(flDelayTime, true); + } + else + { + frontlightFadeOutAll(flDelayTime, true); + } + } +#endif + 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(); + } + } + } +} + +void setupLeds() +{ + loadDNDSettings(); + pixels.begin(); + pixels.setBrightness(preferences.getUInt("ledBrightness", DEFAULT_LED_BRIGHTNESS)); + pixels.clear(); + pixels.show(); + setupLedTask(); + if (preferences.getBool("ledTestOnPower", DEFAULT_LED_TEST_ON_POWER)) + { + 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, 10, &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, const uint32_t& c1, const 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); + } + 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); + } + + 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); + } + + pixels.show(); +} + +QueueHandle_t getLedTaskQueue() { return ledTaskQueue; } + +bool queueLedEffect(uint effect) { + if (isDNDActive()) { + return false; // Don't queue any effects during DND mode + } + if (ledTaskQueue == NULL) + { + return false; + } + + uint flashType = effect; + xQueueSend(ledTaskQueue, &flashType, portMAX_DELAY); + return true; +} + +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 + } + } +} + +void lightningStrike() +{ + uint32_t PURPLE = pixels.Color(128, 0, 128); + uint32_t YELLOW = pixels.Color(255, 226, 41); + + // Randomly choose which LEDs to light up + for (int i = 0; i < pixels.numPixels(); i++) + { + if (random(2) == 0) + { // 50% chance for each LED + pixels.setPixelColor(i, YELLOW); + } + else + { + pixels.setPixelColor(i, PURPLE); + } + } + pixels.show(); + + delay(random(10, 50)); // Flash duration + + // Return to purple background + // setAllPixels(PURPLE); +} + +Adafruit_NeoPixel getPixels() { return pixels; } diff --git a/src/lib/led_handler.hpp b/src/lib/led_handler.hpp index e599e2a..fe72ffa 100644 --- a/src/lib/led_handler.hpp +++ b/src/lib/led_handler.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include "lib/shared.hpp" #include "lib/webserver.hpp" @@ -16,11 +15,12 @@ #define NEOPIXEL_COUNT 4 #endif -// LED effect constants const int LED_FLASH_ERROR = 0; const int LED_FLASH_SUCCESS = 1; const int LED_FLASH_UPDATE = 2; + const int LED_EFFECT_CONFIGURING = 10; + const int LED_FLASH_BLOCK_NOTIFY = 4; const int LED_EFFECT_START_TIMER = 5; const int LED_EFFECT_PAUSE_TIMER = 6; @@ -30,15 +30,61 @@ const int LED_EFFECT_WIFI_CONNECTING = 101; const int LED_EFFECT_WIFI_CONNECT_ERROR = 102; const int LED_EFFECT_WIFI_CONNECT_SUCCESS = 103; const int LED_EFFECT_WIFI_ERASE_SETTINGS = 104; + const int LED_PROGRESS_25 = 200; const int LED_PROGRESS_50 = 201; const int LED_PROGRESS_75 = 202; const int LED_PROGRESS_100 = 203; + const int LED_DATA_PRICE_ERROR = 300; const int LED_DATA_BLOCK_ERROR = 301; + const int LED_EFFECT_NOSTR_ZAP = 400; + const int LED_FLASH_IDENTIFY = 990; const int LED_POWER_TEST = 999; +extern TaskHandle_t ledTaskHandle; +extern Adafruit_NeoPixel pixels; + +void ledTask(void *pvParameters); +void setupLeds(); +void setupLedTask(); +void blinkDelay(int d, int times); +void blinkDelayColor(int d, int times, uint r, uint g, uint b); +void blinkDelayTwoColor(int d, int times, const uint32_t& c1, const uint32_t& c2); +void clearLeds(); +void saveLedState(); +void restoreLedState(); +QueueHandle_t getLedTaskQueue(); +bool queueLedEffect(uint effect); +void setLights(int r, int g, int b); +void setLights(uint32_t color); +void ledRainbow(int wait); +void ledTheaterChaseRainbow(int wait); +void ledTheaterChase(uint32_t color, int wait); +Adafruit_NeoPixel getPixels(); +void lightningStrike(); + +#ifdef HAS_FRONTLIGHT +void frontlightFlash(int flDelayTime); +void frontlightFadeInAll(); +void frontlightFadeOutAll(); +void frontlightFadeIn(uint num); +void frontlightFadeOut(uint num); + +std::vector frontlightGetStatus(); + +void frontlightSetBrightness(uint brightness); +bool frontlightIsOn(); + +void frontlightFadeInAll(int flDelayTime); +void frontlightFadeInAll(int flDelayTime, bool staggered); +void frontlightFadeOutAll(int flDelayTime); +void frontlightFadeOutAll(int flDelayTime, bool staggered); + +void frontlightFadeIn(uint num, int flDelayTime); +void frontlightFadeOut(uint num, int flDelayTime); +#endif // Do Not Disturb mode settings struct DNDTimeRange { @@ -48,88 +94,12 @@ struct DNDTimeRange { uint8_t endMinute; }; -class LedHandler { -public: - static LedHandler& getInstance(); - - // Delete copy constructor and assignment operator - LedHandler(const LedHandler&) = delete; - LedHandler& operator=(const LedHandler&) = delete; +extern bool dndEnabled; +extern bool dndTimeBasedEnabled; +extern DNDTimeRange dndTimeRange; - void setup(); - void setupTask(); - bool queueEffect(uint effect); - void clear(); - void setLights(int r, int g, int b); - void setLights(uint32_t color); - void saveLedState(); - void restoreLedState(); - QueueHandle_t getTaskQueue() const { return ledTaskQueue; } - Adafruit_NeoPixel& getPixels() { return pixels; } - - // DND methods - void setDNDEnabled(bool enabled); - void setDNDTimeBasedEnabled(bool enabled); - void setDNDTimeRange(uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute); - bool isDNDActive() const; - bool isTimeInDNDRange(uint8_t hour, uint8_t minute) const; - - // DND getters - bool isDNDEnabled() const { return dndEnabled; } - bool isDNDTimeBasedEnabled() const { return dndTimeBasedEnabled; } - uint8_t getDNDStartHour() const { return dndTimeRange.startHour; } - uint8_t getDNDStartMinute() const { return dndTimeRange.startMinute; } - uint8_t getDNDEndHour() const { return dndTimeRange.endHour; } - uint8_t getDNDEndMinute() const { return dndTimeRange.endMinute; } - - // Effect methods - void rainbow(int wait); - void theaterChase(uint32_t color, int wait); - void theaterChaseRainbow(int wait); - void lightningStrike(); - void blinkDelay(int d, int times); - void blinkDelayColor(int d, int times, uint r, uint g, uint b); - void blinkDelayTwoColor(int d, int times, const uint32_t& c1, const uint32_t& c2); - -#ifdef HAS_FRONTLIGHT - void frontlightFlash(int flDelayTime); - void frontlightFadeInAll(); - void frontlightFadeOutAll(); - void frontlightFadeIn(uint num); - void frontlightFadeOut(uint num); - std::vector frontlightGetStatus(); - void frontlightSetBrightness(uint brightness); - bool frontlightIsOn() const { return frontlightOn; } - void frontlightFadeInAll(int flDelayTime, bool staggered = false); - void frontlightFadeOutAll(int flDelayTime, bool staggered = false); - void frontlightFadeIn(uint num, int flDelayTime); - void frontlightFadeOut(uint num, int flDelayTime); - void initializeFrontlight(); -#endif - -private: - LedHandler(); // Private constructor for singleton - void loadDNDSettings(); - static void ledTask(void* pvParameters); - - Adafruit_NeoPixel pixels; - TaskHandle_t ledTaskHandle; - QueueHandle_t ledTaskQueue; - uint ledTaskParams; - - // DND members - bool dndEnabled; - bool dndTimeBasedEnabled; - DNDTimeRange dndTimeRange; - -#ifdef HAS_FRONTLIGHT - static constexpr uint16_t FL_FADE_STEP = 25; - bool frontlightOn; - bool flInTransition; -#endif -}; - -// Global accessor function -inline LedHandler& getLedHandler() { - return LedHandler::getInstance(); -} \ No newline at end of file +void setDNDEnabled(bool enabled); +void setDNDTimeBasedEnabled(bool enabled); +void setDNDTimeRange(uint8_t startHour, uint8_t startMinute, uint8_t endHour, uint8_t endMinute); +bool isDNDActive(); +bool isTimeInDNDRange(uint8_t hour, uint8_t minute); \ No newline at end of file diff --git a/src/lib/mining_pool/pool_factory.cpp b/src/lib/mining_pool/pool_factory.cpp index 45bc6ce..c353996 100644 --- a/src/lib/mining_pool/pool_factory.cpp +++ b/src/lib/mining_pool/pool_factory.cpp @@ -5,7 +5,6 @@ const char* PoolFactory::MINING_POOL_NAME_NODERUNNERS = "noderunners"; const char* PoolFactory::MINING_POOL_NAME_BRAIINS = "braiins"; const char* PoolFactory::MINING_POOL_NAME_SATOSHI_RADIO = "satoshi_radio"; const char* PoolFactory::MINING_POOL_NAME_PUBLIC_POOL = "public_pool"; -const char* PoolFactory::MINING_POOL_NAME_LOCAL_PUBLIC_POOL = "local_public_pool"; const char* PoolFactory::MINING_POOL_NAME_GOBRRR_POOL = "gobrrr_pool"; const char* PoolFactory::MINING_POOL_NAME_CKPOOL = "ckpool"; const char* PoolFactory::MINING_POOL_NAME_EU_CKPOOL = "eu_ckpool"; @@ -18,7 +17,6 @@ std::unique_ptr PoolFactory::createPool(const std::string& {MINING_POOL_NAME_BRAIINS, []() { return std::make_unique(); }}, {MINING_POOL_NAME_SATOSHI_RADIO, []() { return std::make_unique(); }}, {MINING_POOL_NAME_PUBLIC_POOL, []() { return std::make_unique(); }}, - {MINING_POOL_NAME_LOCAL_PUBLIC_POOL, []() { return std::make_unique(); }}, {MINING_POOL_NAME_GOBRRR_POOL, []() { return std::make_unique(); }}, {MINING_POOL_NAME_CKPOOL, []() { return std::make_unique(); }}, {MINING_POOL_NAME_EU_CKPOOL, []() { return std::make_unique(); }} diff --git a/src/lib/mining_pool/pool_factory.hpp b/src/lib/mining_pool/pool_factory.hpp index 9885d74..951dbe5 100644 --- a/src/lib/mining_pool/pool_factory.hpp +++ b/src/lib/mining_pool/pool_factory.hpp @@ -10,7 +10,6 @@ #include "ocean/ocean_pool.hpp" #include "satoshi_radio/satoshi_radio_pool.hpp" #include "public_pool/public_pool.hpp" -#include "public_pool/local_public_pool.hpp" #include "gobrrr_pool/gobrrr_pool.hpp" #include "ckpool/ckpool.hpp" #include "ckpool/eu_ckpool.hpp" @@ -29,7 +28,6 @@ class PoolFactory { MINING_POOL_NAME_SATOSHI_RADIO, MINING_POOL_NAME_BRAIINS, MINING_POOL_NAME_PUBLIC_POOL, - MINING_POOL_NAME_LOCAL_PUBLIC_POOL, MINING_POOL_NAME_GOBRRR_POOL, MINING_POOL_NAME_CKPOOL, MINING_POOL_NAME_EU_CKPOOL @@ -57,7 +55,6 @@ class PoolFactory { static const char* MINING_POOL_NAME_BRAIINS; static const char* MINING_POOL_NAME_SATOSHI_RADIO; static const char* MINING_POOL_NAME_PUBLIC_POOL; - static const char* MINING_POOL_NAME_LOCAL_PUBLIC_POOL; static const char* MINING_POOL_NAME_GOBRRR_POOL; static const char* MINING_POOL_NAME_CKPOOL; static const char* MINING_POOL_NAME_EU_CKPOOL; diff --git a/src/lib/mining_pool/public_pool/local_public_pool.cpp b/src/lib/mining_pool/public_pool/local_public_pool.cpp deleted file mode 100644 index 048197c..0000000 --- a/src/lib/mining_pool/public_pool/local_public_pool.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "local_public_pool.hpp" -#include "lib/shared.hpp" -#include "lib/defaults.hpp" - -std::string LocalPublicPool::getEndpoint() const { - return preferences.getString("localPoolEndpoint", DEFAULT_LOCAL_POOL_ENDPOINT).c_str(); -} - -std::string LocalPublicPool::getApiUrl() const { - return "http://" + getEndpoint() + "/api/client/" + poolUser; -} \ No newline at end of file diff --git a/src/lib/mining_pool/public_pool/local_public_pool.hpp b/src/lib/mining_pool/public_pool/local_public_pool.hpp deleted file mode 100644 index a9e37ad..0000000 --- a/src/lib/mining_pool/public_pool/local_public_pool.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "public_pool.hpp" - -class LocalPublicPool : public PublicPool { -public: - std::string getApiUrl() const override; - std::string getDisplayLabel() const override { return "LOCAL/POOL"; } -private: - std::string getEndpoint() const; -}; \ No newline at end of file diff --git a/src/lib/mining_pool_stats_fetch.cpp b/src/lib/mining_pool_stats_fetch.cpp index e04f208..a35a00e 100644 --- a/src/lib/mining_pool_stats_fetch.cpp +++ b/src/lib/mining_pool_stats_fetch.cpp @@ -1,98 +1,95 @@ #include "mining_pool_stats_fetch.hpp" -void MiningPoolStatsFetch::taskWrapper(void* pvParameters) { - MiningPoolStatsFetch::getInstance().task(); +TaskHandle_t miningPoolStatsFetchTaskHandle; + +std::string miningPoolName; +std::string miningPoolStatsHashrate; +int miningPoolStatsDailyEarnings; + +std::string getMiningPoolStatsHashRate() +{ + return miningPoolStatsHashrate; } -void MiningPoolStatsFetch::downloadLogoTaskWrapper(void* pvParameters) { - MiningPoolStatsFetch::getInstance().downloadLogoTask(); +int getMiningPoolStatsDailyEarnings() +{ + return miningPoolStatsDailyEarnings; } -std::string MiningPoolStatsFetch::getHashRate() const { - return hashrate; -} - -int MiningPoolStatsFetch::getDailyEarnings() const { - return dailyEarnings; -} - -MiningPoolInterface* MiningPoolStatsFetch::getPool() { - if (!currentPool) { - std::string poolName = preferences.getString("miningPoolName", DEFAULT_MINING_POOL_NAME).c_str(); - currentPool = PoolFactory::createPool(poolName); - } - return currentPool.get(); -} - -const MiningPoolInterface* MiningPoolStatsFetch::getPool() const { - return currentPool.get(); -} - -LogoData MiningPoolStatsFetch::getLogo() const { - if (const auto* pool = getPool()) { - return pool->getLogo(); - } - return LogoData{}; -} - -void MiningPoolStatsFetch::task() { +void taskMiningPoolStatsFetch(void *pvParameters) +{ std::string poolName = preferences.getString("miningPoolName", DEFAULT_MINING_POOL_NAME).c_str(); - auto* poolInterface = getPool(); - if (!poolInterface) return; + auto poolInterface = PoolFactory::createPool(poolName); std::string poolUser = preferences.getString("miningPoolUser", DEFAULT_MINING_POOL_USER).c_str(); // Main stats fetching loop - for (;;) { + for (;;) + { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); HTTPClient http; http.setUserAgent(USER_AGENT); - + + + poolInterface->setPoolUser(poolUser); std::string apiUrl = poolInterface->getApiUrl(); http.begin(apiUrl.c_str()); - if (debugLogEnabled()) { + if (debugLogEnabled()) + { Serial.printf("Fetching mining pool stats from %s\r\n", apiUrl.c_str()); } poolInterface->prepareRequest(http); int httpCode = http.GET(); - if (httpCode == 200) { + if (httpCode == 200) + { String payload = http.getString(); JsonDocument doc; deserializeJson(doc, payload); - if (debugLogEnabled()) { + if (debugLogEnabled()) + { Serial.printf("Mining pool stats response: %s\r\n", payload.c_str()); } PoolStats stats = poolInterface->parseResponse(doc); - hashrate = stats.hashrate; - if (debugLogEnabled()) { + miningPoolStatsHashrate = stats.hashrate; + + if (debugLogEnabled()) + { Serial.printf("Mining pool stats parsed hashrate: %s\r\n", stats.hashrate.c_str()); } - dailyEarnings = stats.dailyEarnings ? *stats.dailyEarnings : 0; + if (stats.dailyEarnings) + { + miningPoolStatsDailyEarnings = *stats.dailyEarnings; + } + else + { + miningPoolStatsDailyEarnings = 0; // or any other default value + } - if (workQueue != nullptr && (ScreenHandler::getCurrentScreen() == SCREEN_MINING_POOL_STATS_HASHRATE || - ScreenHandler::getCurrentScreen() == SCREEN_MINING_POOL_STATS_EARNINGS)) { + if (workQueue != nullptr && (ScreenHandler::getCurrentScreen() == SCREEN_MINING_POOL_STATS_HASHRATE || ScreenHandler::getCurrentScreen() == SCREEN_MINING_POOL_STATS_EARNINGS)) + { WorkItem priceUpdate = {TASK_MINING_POOL_STATS_UPDATE, 0}; xQueueSend(workQueue, &priceUpdate, portMAX_DELAY); } - } else { - Serial.print(F("Error retrieving mining pool data. HTTP status code: ")); + } + else + { + Serial.print( + F("Error retrieving mining pool data. HTTP status code: ")); Serial.println(httpCode); } } } -void MiningPoolStatsFetch::downloadLogoTask() { +void downloadMiningPoolLogoTask(void *pvParameters) { std::string poolName = preferences.getString("miningPoolName", DEFAULT_MINING_POOL_NAME).c_str(); - auto* poolInterface = getPool(); - if (!poolInterface) return; - - PoolFactory::downloadPoolLogo(poolName, poolInterface); + auto poolInterface = PoolFactory::createPool(poolName); + PoolFactory::downloadPoolLogo(poolName, poolInterface.get()); // If we're on the mining pool stats screen, trigger a display update if (ScreenHandler::getCurrentScreen() == SCREEN_MINING_POOL_STATS_HASHRATE) { @@ -100,22 +97,41 @@ void MiningPoolStatsFetch::downloadLogoTask() { xQueueSend(workQueue, &priceUpdate, portMAX_DELAY); } - xTaskNotifyGive(taskHandle); + xTaskNotifyGive(miningPoolStatsFetchTaskHandle); vTaskDelete(NULL); } -void MiningPoolStatsFetch::setup() { - xTaskCreate(downloadLogoTaskWrapper, +void setupMiningPoolStatsFetchTask() +{ + xTaskCreate(downloadMiningPoolLogoTask, "logoDownload", (6 * 1024), NULL, tskIDLE_PRIORITY, NULL); - xTaskCreate(taskWrapper, + xTaskCreate(taskMiningPoolStatsFetch, "miningPoolStatsFetch", (6 * 1024), NULL, tskIDLE_PRIORITY, - &taskHandle); + &miningPoolStatsFetchTaskHandle); +} + +std::unique_ptr& getMiningPool() +{ + static std::unique_ptr currentMiningPool; + + if (!currentMiningPool) { + std::string poolName = preferences.getString("miningPoolName", DEFAULT_MINING_POOL_NAME).c_str(); + currentMiningPool = PoolFactory::createPool(poolName); + } + + return currentMiningPool; +} + +LogoData getMiningPoolLogo() +{ + LogoData logo = getMiningPool()->getLogo(); + return logo; } diff --git a/src/lib/mining_pool_stats_fetch.hpp b/src/lib/mining_pool_stats_fetch.hpp index 9ea351a..7a84454 100644 --- a/src/lib/mining_pool_stats_fetch.hpp +++ b/src/lib/mining_pool_stats_fetch.hpp @@ -2,44 +2,18 @@ #include #include -#include -#include +#include "mining_pool/pool_factory.hpp" #include "lib/config.hpp" #include "lib/shared.hpp" -#include "lib/mining_pool/mining_pool_interface.hpp" -#include "mining_pool/pool_factory.hpp" -class MiningPoolStatsFetch { -public: - static MiningPoolStatsFetch& getInstance() { - static MiningPoolStatsFetch instance; - return instance; - } +extern TaskHandle_t miningPoolStatsFetchTaskHandle; - void setup(); - std::string getHashRate() const; - int getDailyEarnings() const; - TaskHandle_t getTaskHandle() const { return taskHandle; } - static void taskWrapper(void* pvParameters); - static void downloadLogoTaskWrapper(void* pvParameters); - - // Pool interface methods - MiningPoolInterface* getPool(); - const MiningPoolInterface* getPool() const; - LogoData getLogo() const; +void setupMiningPoolStatsFetchTask(); +void taskMiningPoolStatsFetch(void *pvParameters); -private: - MiningPoolStatsFetch() = default; - ~MiningPoolStatsFetch() = default; - MiningPoolStatsFetch(const MiningPoolStatsFetch&) = delete; - MiningPoolStatsFetch& operator=(const MiningPoolStatsFetch&) = delete; +std::string getMiningPoolStatsHashRate(); +int getMiningPoolStatsDailyEarnings(); - void task(); - void downloadLogoTask(); - - TaskHandle_t taskHandle = nullptr; - std::string hashrate; - int dailyEarnings = 0; - std::unique_ptr currentPool; -}; \ No newline at end of file +std::unique_ptr& getMiningPool(); +LogoData getMiningPoolLogo(); \ No newline at end of file diff --git a/src/lib/nostr_notify.cpp b/src/lib/nostr_notify.cpp index dda24e1..b9a7a46 100644 --- a/src/lib/nostr_notify.cpp +++ b/src/lib/nostr_notify.cpp @@ -1,5 +1,4 @@ #include "nostr_notify.hpp" -#include "led_handler.hpp" std::vector pools; nostr::Transport *transport; @@ -41,7 +40,7 @@ void setupNostrNotify(bool asDatasource, bool zapNotify) {relay}, {// First filter { - {"kinds", {"12203"}}, + {"kinds", {"1"}}, {"since", {String(getMinutesAgo(60))}}, {"authors", {pubKey}}, }}, @@ -79,9 +78,8 @@ void nostrTask(void *pvParameters) { DataSourceType dataSource = getDataSource(); if(dataSource == NOSTR_SOURCE) { - auto& blockNotify = BlockNotify::getInstance(); - int blockFetch = blockNotify.fetchLatestBlock(); - blockNotify.processNewBlock(blockFetch); + int blockFetch = getBlockFetch(); + processNewBlock(blockFetch); } while (1) @@ -146,7 +144,6 @@ void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *even // Use direct value access instead of multiple comparisons String typeValue; uint medianFee = 0; - uint blockHeight = 0; for (JsonArray tag : tags) { if (tag.size() != 2) continue; @@ -167,11 +164,6 @@ void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *even medianFee = tag[1].as(); } break; - case 'b': // blockHeight - if (strcmp(key, "block") == 0) { - blockHeight = tag[1].as(); - } - break; } } @@ -179,19 +171,13 @@ void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *even if (!typeValue.isEmpty()) { if (typeValue == "priceUsd") { processNewPrice(obj["content"].as(), CURRENCY_USD); - if (blockHeight != 0) { - auto& blockNotify = BlockNotify::getInstance(); - blockNotify.processNewBlock(blockHeight); - } } else if (typeValue == "blockHeight") { - auto& blockNotify = BlockNotify::getInstance(); - blockNotify.processNewBlock(obj["content"].as()); + processNewBlock(obj["content"].as()); } if (medianFee != 0) { - auto& blockNotify = BlockNotify::getInstance(); - blockNotify.processNewBlockFee(medianFee); + processNewBlockFee(medianFee); } } } @@ -296,31 +282,15 @@ void handleNostrZapCallback(const String &subId, nostr::SignedNostrEvent *event) } ScreenHandler::setCurrentScreen(SCREEN_CUSTOM); - EPDManager::getInstance().setContent(textEpdContent); + setEpdContent(textEpdContent); vTaskDelay(pdMS_TO_TICKS(315 * NUM_SCREENS) + pdMS_TO_TICKS(250)); if (preferences.getBool("ledFlashOnZap", DEFAULT_LED_FLASH_ON_ZAP)) { - getLedHandler().queueEffect(LED_EFFECT_NOSTR_ZAP); + queueLedEffect(LED_EFFECT_NOSTR_ZAP); } if (timerPeriod > 0) { esp_timer_start_periodic(screenRotateTimer, timerPeriod * usPerSecond); } -} - -// void onNostrEvent(const String &subId, const nostr::Event &event) { -// // This is the callback that will be called when a new event is received -// if (event.kind == 9735) { -// // Parse the zap amount from the event -// uint16_t amount = parseZapAmount(event); -// if (amount > 0) { -// std::array zapContent = parseZapNotify(amount, true); -// EPDManager::getInstance().setContent(zapContent); - -// if (preferences.getBool("ledFlashOnUpd", DEFAULT_LED_FLASH_ON_UPD)) { -// getLedHandler().queueEffect(LED_FLASH_BLOCK_NOTIFY); -// } -// } -// } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/lib/ota.cpp b/src/lib/ota.cpp index 2d94d48..c481b72 100644 --- a/src/lib/ota.cpp +++ b/src/lib/ota.cpp @@ -1,5 +1,4 @@ #include "ota.hpp" -#include "led_handler.hpp" TaskHandle_t taskOtaHandle = NULL; bool isOtaUpdating = false; @@ -32,9 +31,6 @@ void setupOTA() void onOTAProgress(unsigned int progress, unsigned int total) { uint percentage = progress / (total / 100); - auto& ledHandler = getLedHandler(); - auto& pixels = ledHandler.getPixels(); - pixels.fill(pixels.Color(0, 255, 0)); if (percentage < 100) { @@ -57,10 +53,10 @@ void onOTAProgress(unsigned int progress, unsigned int total) void onOTAStart() { - EPDManager::getInstance().forceFullRefresh(); + forceFullRefresh(); std::array epdContent = {"U", "P", "D", "A", "T", "E", "!"}; - EPDManager::getInstance().setContent(epdContent); + setEpdContent(epdContent); // Stop all timers esp_timer_stop(screenRotateTimer); esp_timer_stop(minuteTimer); @@ -74,8 +70,8 @@ void onOTAStart() ButtonHandler::suspendTask(); // stopWebServer(); - auto& blockNotify = BlockNotify::getInstance(); - blockNotify.stop(); + stopBlockNotify(); + stopPriceNotify(); } void handleOTATask(void *parameter) @@ -88,15 +84,15 @@ void handleOTATask(void *parameter) { if (msg.updateType == UPDATE_ALL) { isOtaUpdating = true; - getLedHandler().queueEffect(LED_FLASH_UPDATE); + queueLedEffect(LED_FLASH_UPDATE); int resultWebUi = downloadUpdateHandler(UPDATE_WEBUI); - getLedHandler().queueEffect(LED_FLASH_UPDATE); + queueLedEffect(LED_FLASH_UPDATE); int resultFw = downloadUpdateHandler(UPDATE_FIRMWARE); if (resultWebUi == 0 && resultFw == 0) { ESP.restart(); } else { - getLedHandler().queueEffect(LED_FLASH_ERROR); + queueLedEffect(LED_FLASH_ERROR); vTaskDelay(pdMS_TO_TICKS(3000)); ESP.restart(); } diff --git a/src/lib/price_notify.cpp b/src/lib/price_notify.cpp index c851e85..2c6fe94 100644 --- a/src/lib/price_notify.cpp +++ b/src/lib/price_notify.cpp @@ -1,74 +1,104 @@ #include "price_notify.hpp" -const char *wsServerPrice = "wss://ws.kraken.com/v2"; +const char *wsServerPrice = "wss://ws.coincap.io/prices?assets=bitcoin"; -WebSocketsClient webSocket; +// WebsocketsClient client; +esp_websocket_client_handle_t clientPrice = NULL; +esp_websocket_client_config_t config; uint currentPrice = 90000; unsigned long int lastPriceUpdate; bool priceNotifyInit = false; std::map currencyMap; std::map lastUpdateMap; -TaskHandle_t priceNotifyTaskHandle; - -void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length); +WebSocketsClient priceNotifyWs; void setupPriceNotify() { - webSocket.beginSSL("ws.kraken.com", 443, "/v2"); - webSocket.onEvent([](WStype_t type, uint8_t * payload, size_t length) { - onWebsocketPriceEvent(type, payload, length); - }); - webSocket.setReconnectInterval(5000); - webSocket.enableHeartbeat(15000, 3000, 2); + config = {.uri = wsServerPrice, + .user_agent = USER_AGENT}; + config.cert_pem = isrg_root_x1cert; - setupPriceNotifyTask(); + config.task_stack = (6*1024); + + + clientPrice = esp_websocket_client_init(&config); + esp_websocket_register_events(clientPrice, WEBSOCKET_EVENT_ANY, + onWebsocketPriceEvent, clientPrice); + esp_websocket_client_start(clientPrice); + + // priceNotifyWs.beginSSL("ws.coincap.io", 443, "/prices?assets=bitcoin"); + // priceNotifyWs.onEvent(onWebsocketPriceEvent); + // priceNotifyWs.setReconnectInterval(5000); + // priceNotifyWs.enableHeartbeat(15000, 3000, 2); } -void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length) { - switch(type) { - case WStype_DISCONNECTED: - Serial.println(F("Price WS Connection Closed")); - break; - case WStype_CONNECTED: - { - Serial.println("Connected to " + String(wsServerPrice)); - JsonDocument doc; - doc["method"] = "subscribe"; - JsonObject params = doc["params"].to(); - params["channel"] = "ticker"; - params["symbol"][0] = "BTC/USD"; - - webSocket.sendTXT(doc.as().c_str()); - break; - } - case WStype_TEXT: - { - JsonDocument doc; - deserializeJson(doc, (char *)payload); +// void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length) { +// switch(type) { +// case WStype_DISCONNECTED: +// Serial.printf("[WSc] Disconnected!\n"); +// break; +// case WStype_CONNECTED: +// { +// Serial.printf("[WSc] Connected to url: %s\n", payload); - if (doc["data"][0].is()) - { - float price = doc["data"][0]["last"].as(); - uint roundedPrice = round(price); - if (currentPrice != roundedPrice) - { - processNewPrice(roundedPrice, CURRENCY_USD); - } - } - break; - } - case WStype_BIN: - break; - case WStype_ERROR: - case WStype_FRAGMENT_TEXT_START: - case WStype_FRAGMENT_BIN_START: - case WStype_FRAGMENT: - case WStype_PING: - case WStype_PONG: - case WStype_FRAGMENT_FIN: - break; + +// break; +// } +// case WStype_TEXT: +// String message = String((char*)payload); +// onWebsocketPriceMessage(message); +// break; +// case WStype_BIN: +// break; +// case WStype_ERROR: +// case WStype_FRAGMENT_TEXT_START: +// case WStype_FRAGMENT_BIN_START: +// case WStype_FRAGMENT: +// case WStype_PING: +// case WStype_PONG: +// case WStype_FRAGMENT_FIN: +// break; +// } +// } + +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("Connected to " + String(config.uri) + " WebSocket"); + priceNotifyInit = true; + + 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) +{ + JsonDocument doc; + + deserializeJson(doc, (char *)event_data->data_ptr); + + if (doc.containsKey("bitcoin")) + { + if (currentPrice != doc["bitcoin"].as()) + { + processNewPrice(doc["bitcoin"].as(), CURRENCY_USD); } + } } void processNewPrice(uint newPrice, char currency) @@ -145,7 +175,9 @@ void setPrice(uint newPrice, char currency) bool isPriceNotifyConnected() { - return webSocket.isConnected(); + if (clientPrice == NULL) + return false; + return esp_websocket_client_is_connected(clientPrice); } bool getPriceNotifyInit() @@ -155,30 +187,24 @@ bool getPriceNotifyInit() void stopPriceNotify() { - webSocket.disconnect(); - if (priceNotifyTaskHandle != NULL) { - vTaskDelete(priceNotifyTaskHandle); - priceNotifyTaskHandle = NULL; - } + if (clientPrice == NULL) + return; + esp_websocket_client_close(clientPrice, pdMS_TO_TICKS(5000)); + esp_websocket_client_stop(clientPrice); + esp_websocket_client_destroy(clientPrice); + + clientPrice = NULL; } void restartPriceNotify() { stopPriceNotify(); - setupPriceNotify(); -} - -void taskPriceNotify(void *pvParameters) -{ - for (;;) + if (clientPrice == NULL) { - webSocket.loop(); - vTaskDelay(10 / portTICK_PERIOD_MS); + setupPriceNotify(); + return; } -} - -void setupPriceNotifyTask() -{ - xTaskCreate(taskPriceNotify, "priceNotify", (6 * 1024), NULL, tskIDLE_PRIORITY, - &priceNotifyTaskHandle); + // esp_websocket_client_close(clientPrice, pdMS_TO_TICKS(5000)); + // esp_websocket_client_stop(clientPrice); + // esp_websocket_client_start(clientPrice); } \ No newline at end of file diff --git a/src/lib/price_notify.hpp b/src/lib/price_notify.hpp index 6c8c6df..3591d40 100644 --- a/src/lib/price_notify.hpp +++ b/src/lib/price_notify.hpp @@ -2,22 +2,24 @@ #include #include -#include +#include +#include "block_notify.hpp" #include #include "lib/screen_handler.hpp" -extern TaskHandle_t priceNotifyTaskHandle; - void setupPriceNotify(); -void setupPriceNotifyTask(); -void taskPriceNotify(void *pvParameters); -void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length); +void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base, + int32_t event_id, void *event_data); +//void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length); + +void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data); uint getPrice(char currency); void setPrice(uint newPrice, char currency); +//void processNewPrice(uint newPrice); void processNewPrice(uint newPrice, char currency); bool isPriceNotifyConnected(); diff --git a/src/lib/screen_handler.cpp b/src/lib/screen_handler.cpp index 75e59aa..4d8d1b9 100644 --- a/src/lib/screen_handler.cpp +++ b/src/lib/screen_handler.cpp @@ -203,7 +203,7 @@ void ScreenHandler::showSystemStatusScreen() { String((int)round(ESP.getFreeHeap() / 1024)) + "/" + (int)round(ESP.getHeapSize() / 1024); setCurrentScreen(SCREEN_CUSTOM); - EPDManager::getInstance().setContent(sysStatusEpdContent); + setEpdContent(sysStatusEpdContent); } // Keep these as free functions @@ -220,9 +220,9 @@ void workerTask(void *pvParameters) { currentScreenValue != SCREEN_BITAXE_BESTDIFF) break; taskEpdContent = (currentScreenValue == SCREEN_BITAXE_HASHRATE) ? - parseBitaxeHashRate(BitAxeFetch::getInstance().getHashRate()) : - parseBitaxeBestDiff(BitAxeFetch::getInstance().getBestDiff()); - EPDManager::getInstance().setContent(taskEpdContent); + parseBitaxeHashRate(getBitAxeHashRate()) : + parseBitaxeBestDiff(getBitaxeBestDiff()); + setEpdContent(taskEpdContent); break; } @@ -231,11 +231,10 @@ void workerTask(void *pvParameters) { currentScreenValue != SCREEN_MINING_POOL_STATS_EARNINGS) break; taskEpdContent = (currentScreenValue == SCREEN_MINING_POOL_STATS_HASHRATE) ? - parseMiningPoolStatsHashRate(MiningPoolStatsFetch::getInstance().getHashRate(), *MiningPoolStatsFetch::getInstance().getPool()) : - parseMiningPoolStatsDailyEarnings(MiningPoolStatsFetch::getInstance().getDailyEarnings(), - MiningPoolStatsFetch::getInstance().getPool()->getDailyEarningsLabel(), - *MiningPoolStatsFetch::getInstance().getPool()); - EPDManager::getInstance().setContent(taskEpdContent); + parseMiningPoolStatsHashRate(getMiningPoolStatsHashRate(), *getMiningPool()) : + parseMiningPoolStatsDailyEarnings(getMiningPoolStatsDailyEarnings(), + getMiningPool()->getDailyEarningsLabel(), *getMiningPool()); + setEpdContent(taskEpdContent); break; } @@ -251,33 +250,31 @@ void workerTask(void *pvParameters) { } else if (currentScreenValue == SCREEN_SATS_PER_CURRENCY) { taskEpdContent = parseSatsPerCurrency(price, currency, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL)); } else { - auto& blockNotify = BlockNotify::getInstance(); - taskEpdContent = parseMarketCap(blockNotify.getBlockHeight(), price, currency, preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR)); + taskEpdContent = + parseMarketCap(getBlockHeight(), price, currency, + preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR)); } - EPDManager::getInstance().setContent(taskEpdContent); + setEpdContent(taskEpdContent); break; } case TASK_FEE_UPDATE: { if (currentScreenValue == SCREEN_BLOCK_FEE_RATE) { - auto& blockNotify = BlockNotify::getInstance(); - taskEpdContent = parseBlockFees(static_cast(blockNotify.getBlockMedianFee())); - EPDManager::getInstance().setContent(taskEpdContent); + taskEpdContent = parseBlockFees(static_cast(getBlockMedianFee())); + setEpdContent(taskEpdContent); } break; } case TASK_BLOCK_UPDATE: { if (currentScreenValue != SCREEN_HALVING_COUNTDOWN) { - auto& blockNotify = BlockNotify::getInstance(); - taskEpdContent = parseBlockHeight(blockNotify.getBlockHeight()); + taskEpdContent = parseBlockHeight(getBlockHeight()); } else { - auto& blockNotify = BlockNotify::getInstance(); - taskEpdContent = parseHalvingCountdown(blockNotify.getBlockHeight(), preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN)); + taskEpdContent = parseHalvingCountdown(getBlockHeight(), preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN)); } if (currentScreenValue == SCREEN_HALVING_COUNTDOWN || currentScreenValue == SCREEN_BLOCK_HEIGHT) { - EPDManager::getInstance().setContent(taskEpdContent); + setEpdContent(taskEpdContent); } break; } @@ -304,7 +301,7 @@ void workerTask(void *pvParameters) { for (uint i = 1; i < NUM_SCREENS; i++) { taskEpdContent[i] = timeString[i]; } - EPDManager::getInstance().setContent(taskEpdContent); + setEpdContent(taskEpdContent); } break; @@ -332,6 +329,8 @@ void setupTasks() { xTaskCreate(taskScreenRotate, "rotateScreen", 4096, NULL, tskIDLE_PRIORITY, &taskScreenRotateTaskHandle); + waitUntilNoneBusy(); + if (findScreenIndexByValue(preferences.getUInt("currentScreen", DEFAULT_CURRENT_SCREEN)) != -1) ScreenHandler::setCurrentScreen(preferences.getUInt("currentScreen", DEFAULT_CURRENT_SCREEN)); } diff --git a/src/lib/shared.cpp b/src/lib/shared.cpp index b8efc0c..a776750 100644 --- a/src/lib/shared.cpp +++ b/src/lib/shared.cpp @@ -40,39 +40,39 @@ // "MrY=\n" // "-----END CERTIFICATE-----\n"; -// const char* isrg_root_x1cert = R"EOF( -// -----BEGIN CERTIFICATE----- -// MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw -// TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -// cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 -// WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu -// ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY -// MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc -// h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ -// 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U -// A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW -// T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH -// B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC -// B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv -// KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn -// OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn -// jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw -// qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI -// rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -// HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq -// hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -// ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ -// 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK -// NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 -// ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur -// TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC -// jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc -// oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq -// 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA -// mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d -// emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= -// -----END CERTIFICATE----- -// )EOF"; +const char* isrg_root_x1cert = R"EOF( +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)EOF"; #ifdef TEST_SCREENS @@ -179,5 +179,4 @@ void HttpHelper::end(HTTPClient* http) { http->end(); delete http; } -} - +} \ No newline at end of file diff --git a/src/lib/shared.hpp b/src/lib/shared.hpp index 54d48aa..78fb9ea 100644 --- a/src/lib/shared.hpp +++ b/src/lib/shared.hpp @@ -16,8 +16,6 @@ #include #include -#include -#include #include "defaults.hpp" @@ -68,7 +66,7 @@ const int usPerSecond = 1000000; const int usPerMinute = 60 * usPerSecond; // extern const char *github_root_ca; -// extern const char *isrg_root_x1cert; +extern const char *isrg_root_x1cert; extern const uint8_t rootca_crt_bundle_start[] asm("_binary_x509_crt_bundle_start"); // extern const uint8_t ocean_logo_comp[] asm("_binary_ocean_gz_start"); @@ -120,5 +118,4 @@ private: static WiFiClientSecure secureClient; static bool certBundleSet; static WiFiClient insecureClient; -}; - +}; \ No newline at end of file diff --git a/src/lib/timers.cpp b/src/lib/timers.cpp index 850ead3..2fdb71c 100644 --- a/src/lib/timers.cpp +++ b/src/lib/timers.cpp @@ -1,5 +1,4 @@ #include "timers.hpp" -#include "led_handler.hpp" esp_timer_handle_t screenRotateTimer; esp_timer_handle_t minuteTimer; @@ -50,11 +49,11 @@ void setTimerActive(bool status) { if (status) { esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond); - getLedHandler().queueEffect(LED_EFFECT_START_TIMER); + queueLedEffect(LED_EFFECT_START_TIMER); preferences.putBool("timerActive", true); } else { esp_timer_stop(screenRotateTimer); - getLedHandler().queueEffect(LED_EFFECT_PAUSE_TIMER); + queueLedEffect(LED_EFFECT_PAUSE_TIMER); preferences.putBool("timerActive", false); } @@ -69,14 +68,12 @@ void IRAM_ATTR minuteTimerISR(void *arg) { WorkItem timeUpdate = {TASK_TIME_UPDATE, 0}; xQueueSendFromISR(workQueue, &timeUpdate, &xHigherPriorityTaskWoken); - TaskHandle_t bitaxeHandle = BitAxeFetch::getInstance().getTaskHandle(); - if (bitaxeHandle != NULL) { - vTaskNotifyGiveFromISR(bitaxeHandle, &xHigherPriorityTaskWoken); + if (bitaxeFetchTaskHandle != NULL) { + vTaskNotifyGiveFromISR(bitaxeFetchTaskHandle, &xHigherPriorityTaskWoken); } - TaskHandle_t miningPoolHandle = MiningPoolStatsFetch::getInstance().getTaskHandle(); - if (miningPoolHandle != NULL) { - vTaskNotifyGiveFromISR(miningPoolHandle, &xHigherPriorityTaskWoken); + if (miningPoolStatsFetchTaskHandle != NULL) { + vTaskNotifyGiveFromISR(miningPoolStatsFetchTaskHandle, &xHigherPriorityTaskWoken); } if (xHigherPriorityTaskWoken == pdTRUE) { diff --git a/src/lib/v2_notify.cpp b/src/lib/v2_notify.cpp index b915518..469e6e1 100644 --- a/src/lib/v2_notify.cpp +++ b/src/lib/v2_notify.cpp @@ -106,11 +106,6 @@ namespace V2Notify JsonDocument doc; DeserializationError error = deserializeMsgPack(doc, payload, length); - if (error) { - Serial.println(F("Error deserializing message")); - break; - } - V2Notify::handleV2Message(doc); break; } @@ -127,33 +122,24 @@ namespace V2Notify void handleV2Message(JsonDocument doc) { - if (doc["blockheight"].is()) + if (doc.containsKey("blockheight")) { uint newBlockHeight = doc["blockheight"].as(); - if (newBlockHeight == BlockNotify::getInstance().getBlockHeight()) + if (newBlockHeight == getBlockHeight()) { return; } - if (debugLogEnabled()) { - Serial.print(F("processNewBlock ")); - Serial.println(newBlockHeight); - } - BlockNotify::getInstance().processNewBlock(newBlockHeight); + processNewBlock(newBlockHeight); } - else if (doc["blockfee"].is()) + else if (doc.containsKey("blockfee")) { uint medianFee = doc["blockfee"].as(); - if (debugLogEnabled()) { - Serial.print(F("processNewBlockFee ")); - Serial.println(medianFee); - } - - BlockNotify::getInstance().processNewBlockFee(medianFee); + processNewBlockFee(medianFee); } - else if (doc["price"].is()) + else if (doc.containsKey("price")) { // Iterate through the key-value pairs of the "price" object @@ -172,7 +158,7 @@ namespace V2Notify for (;;) { webSocket.loop(); - vTaskDelay(pdMS_TO_TICKS(10)); + vTaskDelay(10 / portTICK_PERIOD_MS); } } diff --git a/src/lib/webserver.cpp b/src/lib/webserver.cpp index 6ee62f9..2ba0a4d 100644 --- a/src/lib/webserver.cpp +++ b/src/lib/webserver.cpp @@ -1,11 +1,9 @@ #include "webserver.hpp" -#include "lib/led_handler.hpp" -#include "lib/shared.hpp" static const char* JSON_CONTENT = "application/json"; static const char *const PROGMEM strSettings[] = { - "hostnamePrefix", "mempoolInstance", "nostrPubKey", "nostrRelay", "bitaxeHostname", "miningPoolName", "miningPoolUser", "nostrZapPubkey", "httpAuthUser", "httpAuthPass", "gitReleaseUrl", "poolLogosUrl", "ceEndpoint", "fontName", "localPoolEndpoint", "tzString"}; + "hostnamePrefix", "mempoolInstance", "nostrPubKey", "nostrRelay", "bitaxeHostname", "miningPoolName", "miningPoolUser", "nostrZapPubkey", "httpAuthUser", "httpAuthPass", "gitReleaseUrl", "poolLogosUrl", "ceEndpoint", "fontName"}; static const char *const PROGMEM uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness", "flMaxBrightness", "flEffectDelay", "luxLightToggle", "wpTimeout"}; @@ -30,8 +28,7 @@ TaskHandle_t eventSourceTaskHandle; void setupWebserver() { events.onConnect([](AsyncEventSourceClient *client) - { client->send("welcome", NULL, millis(), 1000); - }); + { client->send("welcome", NULL, millis(), 1000); }); server.addHandler(&events); AsyncStaticWebHandler &staticHandler = server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html"); @@ -234,7 +231,6 @@ void asyncFirmwareUpdateHandler(AsyncWebServerRequest *request, String filename, JsonDocument getStatusObject() { - auto& ledHandler = getLedHandler(); JsonDocument root; root["currentScreen"] = ScreenHandler::getCurrentScreen(); @@ -242,22 +238,25 @@ JsonDocument getStatusObject() root["timerRunning"] = isTimerActive(); root["isOTAUpdating"] = getIsOTAUpdating(); root["espUptime"] = esp_timer_get_time() / 1000000; + // root["currentPrice"] = getPrice(); + // root["currentBlockHeight"] = getBlockHeight(); root["espFreeHeap"] = ESP.getFreeHeap(); root["espHeapSize"] = ESP.getHeapSize(); + // root["espFreePsram"] = ESP.getFreePsram(); + // root["espPsramSize"] = ESP.getPsramSize(); JsonObject conStatus = root["connectionStatus"].to(); conStatus["price"] = isPriceNotifyConnected(); - auto& blockNotify = BlockNotify::getInstance(); - conStatus["blocks"] = blockNotify.isConnected(); + conStatus["blocks"] = isBlockNotifyConnected(); conStatus["V2"] = V2Notify::isV2NotifyConnected(); + conStatus["nostr"] = nostrConnected(); root["rssi"] = WiFi.RSSI(); root["currency"] = getCurrencyCode(ScreenHandler::getCurrentCurrency()); - #ifdef HAS_FRONTLIGHT - std::vector statuses = ledHandler.frontlightGetStatus(); + std::vector statuses = frontlightGetStatus(); uint16_t arr[NUM_SCREENS]; std::copy(statuses.begin(), statuses.end(), arr); @@ -271,24 +270,22 @@ JsonDocument getStatusObject() #endif // Add DND status - root["dnd"]["enabled"] = ledHandler.isDNDEnabled(); - root["dnd"]["timeBasedEnabled"] = ledHandler.isDNDTimeBasedEnabled(); - root["dnd"]["startTime"] = String(ledHandler.getDNDStartHour()) + ":" + - (ledHandler.getDNDStartMinute() < 10 ? "0" : "") + String(ledHandler.getDNDStartMinute()); - root["dnd"]["endTime"] = String(ledHandler.getDNDEndHour()) + ":" + - (ledHandler.getDNDEndMinute() < 10 ? "0" : "") + String(ledHandler.getDNDEndMinute()); - root["dnd"]["active"] = ledHandler.isDNDActive(); + root["dnd"]["enabled"] = dndEnabled; + root["dnd"]["timeBasedEnabled"] = dndTimeBasedEnabled; + root["dnd"]["startTime"] = String(dndTimeRange.startHour) + ":" + + (dndTimeRange.startMinute < 10 ? "0" : "") + String(dndTimeRange.startMinute); + root["dnd"]["endTime"] = String(dndTimeRange.endHour) + ":" + + (dndTimeRange.endMinute < 10 ? "0" : "") + String(dndTimeRange.endMinute); + root["dnd"]["active"] = isDNDActive(); return root; } JsonDocument getLedStatusObject() { - auto& ledHandler = getLedHandler(); - auto& pixels = ledHandler.getPixels(); - JsonDocument root; JsonArray colors = root["data"].to(); + // Adafruit_NeoPixel pix = getPixels(); for (uint i = 0; i < pixels.numPixels(); i++) { @@ -298,7 +295,7 @@ JsonDocument getLedStatusObject() uint blue = pixColor & 0xFF; char hexColor[8]; snprintf(hexColor, sizeof(hexColor), "#%02X%02X%02X", red, green, blue); - // colors.add(hexColor); + JsonObject object = colors.add(); object["red"] = red; @@ -313,18 +310,14 @@ JsonDocument getLedStatusObject() void eventSourceUpdate() { if (!events.count()) return; - static JsonDocument doc; - doc.clear(); - - JsonDocument root = getStatusObject(); - - root["leds"] = getLedStatusObject()["data"]; + JsonDocument doc = getStatusObject(); + doc["leds"] = getLedStatusObject()["data"]; // Get current EPD content directly as array - std::array epdContent = EPDManager::getInstance().getCurrentContent(); + std::array epdContent = getCurrentEpdContent(); // Add EPD content arrays - JsonArray data = root["data"].to(); + JsonArray data = doc["data"].to(); // Copy array elements directly for(const auto& content : epdContent) { @@ -332,7 +325,7 @@ void eventSourceUpdate() { } String buffer; - serializeJson(root, buffer); + serializeJson(doc, buffer); events.send(buffer.c_str(), "status"); } @@ -348,7 +341,7 @@ void onApiStatus(AsyncWebServerRequest *request) JsonDocument root = getStatusObject(); // Get current EPD content directly as array - std::array epdContent = EPDManager::getInstance().getCurrentContent(); + std::array epdContent = getCurrentEpdContent(); // Add EPD content arrays JsonArray data = root["data"].to(); @@ -390,9 +383,11 @@ void onApiActionTimerRestart(AsyncWebServerRequest *request) */ void onApiFullRefresh(AsyncWebServerRequest *request) { - EPDManager::getInstance().forceFullRefresh(); - std::array newEpdContent = EPDManager::getInstance().getCurrentContent(); - EPDManager::getInstance().setContent(newEpdContent, true); + forceFullRefresh(); + std::array newEpdContent = getCurrentEpdContent(); + + setEpdContent(newEpdContent, true); + request->send(HTTP_OK); } @@ -439,7 +434,7 @@ void onApiShowText(AsyncWebServerRequest *request) textEpdContent[i] = t[i]; } - EPDManager::getInstance().setContent(textEpdContent); + setEpdContent(textEpdContent); } ScreenHandler::setCurrentScreen(SCREEN_CUSTOM); request->send(HTTP_OK); @@ -457,7 +452,7 @@ void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json) i++; } - EPDManager::getInstance().setContent(epdContent); + setEpdContent(epdContent); ScreenHandler::setCurrentScreen(SCREEN_CUSTOM); request->send(HTTP_OK); @@ -485,13 +480,13 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) if (inverted) { preferences.putUInt("fgColor", GxEPD_WHITE); preferences.putUInt("bgColor", GxEPD_BLACK); - EPDManager::getInstance().setForegroundColor(GxEPD_WHITE); - EPDManager::getInstance().setBackgroundColor(GxEPD_BLACK); + setFgColor(GxEPD_WHITE); + setBgColor(GxEPD_BLACK); } else { preferences.putUInt("fgColor", GxEPD_BLACK); preferences.putUInt("bgColor", GxEPD_WHITE); - EPDManager::getInstance().setForegroundColor(GxEPD_BLACK); - EPDManager::getInstance().setBackgroundColor(GxEPD_WHITE); + setFgColor(GxEPD_BLACK); + setBgColor(GxEPD_WHITE); } Serial.printf("Setting invertedColor to %d\r\n", inverted); settingsChanged = true; @@ -624,28 +619,26 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) } // Handle DND settings - if (settings["dnd"].is()) { + if (settings.containsKey("dnd")) { JsonObject dndObj = settings["dnd"]; - auto& ledHandler = getLedHandler(); - - if (dndObj["timeBasedEnabled"].is()) { - ledHandler.setDNDTimeBasedEnabled(dndObj["timeBasedEnabled"].as()); + if (dndObj.containsKey("timeBasedEnabled")) { + setDNDTimeBasedEnabled(dndObj["timeBasedEnabled"].as()); } - if (dndObj["startHour"].is() && dndObj["startMinute"].is() && - dndObj["endHour"].is() && dndObj["endMinute"].is()) { - ledHandler.setDNDTimeRange( - dndObj["startHour"].as(), - dndObj["startMinute"].as(), - dndObj["endHour"].as(), - dndObj["endMinute"].as()); + if (dndObj.containsKey("startHour") && dndObj.containsKey("startMinute") && + dndObj.containsKey("endHour") && dndObj.containsKey("endMinute")) { + setDNDTimeRange( + dndObj["startHour"].as(), + dndObj["startMinute"].as(), + dndObj["endHour"].as(), + dndObj["endMinute"].as() + ); } } request->send(HTTP_OK); if (settingsChanged) { - auto& ledHandler = getLedHandler(); - ledHandler.queueEffect(LED_FLASH_SUCCESS); + queueLedEffect(LED_FLASH_SUCCESS); } } @@ -666,8 +659,7 @@ void onApiRestart(AsyncWebServerRequest *request) void onApiIdentify(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); - ledHandler.queueEffect(LED_FLASH_IDENTIFY); + queueLedEffect(LED_FLASH_IDENTIFY); request->send(HTTP_OK); } @@ -690,7 +682,7 @@ void onApiSettingsGet(AsyncWebServerRequest *request) JsonDocument root; root["numScreens"] = NUM_SCREENS; - root["invertedColor"] = preferences.getBool("invertedColor", EPDManager::getInstance().getForegroundColor() == GxEPD_WHITE); + root["invertedColor"] = preferences.getBool("invertedColor", getFgColor() == GxEPD_WHITE); root["timerSeconds"] = getTimerSeconds(); root["timerRunning"] = isTimerActive(); root["minSecPriceUpd"] = preferences.getUInt( @@ -698,9 +690,8 @@ void onApiSettingsGet(AsyncWebServerRequest *request) root["fullRefreshMin"] = preferences.getUInt("fullRefreshMin", DEFAULT_MINUTES_FULL_REFRESH); root["wpTimeout"] = preferences.getUInt("wpTimeout", DEFAULT_WP_TIMEOUT); - //root["tzOffset"] = preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS) / 60; - root["tzString"] = preferences.getString("tzString", DEFAULT_TZ_STRING); - + root["tzOffset"] = preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS) / 60; + // Add data source settings root["dataSource"] = preferences.getUChar("dataSource", DEFAULT_DATA_SOURCE); @@ -708,9 +699,6 @@ void onApiSettingsGet(AsyncWebServerRequest *request) root["mempoolInstance"] = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); root["mempoolSecure"] = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE); - // Local pool settings - root["localPoolEndpoint"] = preferences.getString("localPoolEndpoint", DEFAULT_LOCAL_POOL_ENDPOINT); - // Nostr settings (used for NOSTR_SOURCE or when zapNotify is enabled) root["nostrPubKey"] = preferences.getString("nostrPubKey", DEFAULT_NOSTR_NPUB); root["nostrRelay"] = preferences.getString("nostrRelay", DEFAULT_NOSTR_RELAY); @@ -809,13 +797,12 @@ void onApiSettingsGet(AsyncWebServerRequest *request) root["ceDisableSSL"] = preferences.getBool("ceDisableSSL", DEFAULT_CUSTOM_ENDPOINT_DISABLE_SSL); // Add DND settings - auto& ledHandler = getLedHandler(); - root["dnd"]["enabled"] = ledHandler.isDNDEnabled(); - root["dnd"]["timeBasedEnabled"] = ledHandler.isDNDTimeBasedEnabled(); - root["dnd"]["startHour"] = ledHandler.getDNDStartHour(); - root["dnd"]["startMinute"] = ledHandler.getDNDStartMinute(); - root["dnd"]["endHour"] = ledHandler.getDNDEndHour(); - root["dnd"]["endMinute"] = ledHandler.getDNDEndMinute(); + root["dnd"]["enabled"] = dndEnabled; + root["dnd"]["timeBasedEnabled"] = dndTimeBasedEnabled; + root["dnd"]["startHour"] = dndTimeRange.startHour; + root["dnd"]["startMinute"] = dndTimeRange.startMinute; + root["dnd"]["endHour"] = dndTimeRange.endHour; + root["dnd"]["endMinute"] = dndTimeRange.endMinute; AsyncResponseStream *response = request->beginResponseStream(JSON_CONTENT); @@ -832,7 +819,7 @@ bool processEpdColorSettings(AsyncWebServerRequest *request) const AsyncWebParameter *fgColor = request->getParam("fgColor", true); uint32_t color = strtol(fgColor->value().c_str(), NULL, 16); preferences.putUInt("fgColor", color); - EPDManager::getInstance().setForegroundColor(color); + setFgColor(color); // Serial.print(F("Setting foreground color to ")); // Serial.println(fgColor->value().c_str()); settingsChanged = true; @@ -843,7 +830,7 @@ bool processEpdColorSettings(AsyncWebServerRequest *request) uint32_t color = strtol(bgColor->value().c_str(), NULL, 16); preferences.putUInt("bgColor", color); - EPDManager::getInstance().setBackgroundColor(color); + setBgColor(color); // Serial.print(F("Setting background color to ")); // Serial.println(bgColor->value().c_str()); settingsChanged = true; @@ -922,7 +909,7 @@ void onApiStopDataSources(AsyncWebServerRequest *request) request->beginResponseStream(JSON_CONTENT); stopPriceNotify(); - BlockNotify::getInstance().stop(); + stopBlockNotify(); request->send(response); } @@ -933,15 +920,16 @@ void onApiRestartDataSources(AsyncWebServerRequest *request) request->beginResponseStream(JSON_CONTENT); restartPriceNotify(); - BlockNotify::getInstance().restart(); + restartBlockNotify(); + // setupPriceNotify(); + // setupBlockNotify(); request->send(response); } void onApiLightsOff(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); - ledHandler.setLights(0, 0, 0); + setLights(0, 0, 0); request->send(HTTP_OK); } @@ -956,15 +944,13 @@ void onApiLightsSetColor(AsyncWebServerRequest *request) if (rgbColor.compareTo("off") == 0) { - auto& ledHandler = getLedHandler(); - ledHandler.setLights(0, 0, 0); + setLights(0, 0, 0); } else { uint r, g, b; sscanf(rgbColor.c_str(), "%02x%02x%02x", &r, &g, &b); - auto& ledHandler = getLedHandler(); - ledHandler.setLights(r, g, b); + setLights(r, g, b); } JsonDocument doc; @@ -982,9 +968,6 @@ void onApiLightsSetColor(AsyncWebServerRequest *request) void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json) { - auto& ledHandler = getLedHandler(); - auto& pixels = ledHandler.getPixels(); - JsonArray lights = json.as(); if (lights.size() != pixels.numPixels()) @@ -1033,7 +1016,7 @@ void onApiLightsSetJson(AsyncWebServerRequest *request, JsonVariant &json) } pixels.show(); - ledHandler.saveLedState(); + saveLedState(); request->send(HTTP_OK); } @@ -1097,21 +1080,19 @@ void onApiShowCurrency(AsyncWebServerRequest *request) #ifdef HAS_FRONTLIGHT void onApiFrontlightOn(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); - ledHandler.frontlightFadeInAll(); + frontlightFadeInAll(); request->send(HTTP_OK); } void onApiFrontlightStatus(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); AsyncResponseStream *response = request->beginResponseStream(JSON_CONTENT); JsonDocument root; - std::vector statuses = ledHandler.frontlightGetStatus(); + std::vector statuses = frontlightGetStatus(); uint16_t arr[NUM_SCREENS]; std::copy(statuses.begin(), statuses.end(), arr); @@ -1124,8 +1105,7 @@ void onApiFrontlightStatus(AsyncWebServerRequest *request) void onApiFrontlightFlash(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); - ledHandler.frontlightFlash(preferences.getUInt("flEffectDelay")); + frontlightFlash(preferences.getUInt("flEffectDelay")); request->send(HTTP_OK); } @@ -1134,8 +1114,7 @@ void onApiFrontlightSetBrightness(AsyncWebServerRequest *request) { if (request->hasParam("b")) { - auto& ledHandler = getLedHandler(); - ledHandler.frontlightSetBrightness(request->getParam("b")->value().toInt()); + frontlightSetBrightness(request->getParam("b")->value().toInt()); request->send(HTTP_OK); } else @@ -1146,51 +1125,21 @@ void onApiFrontlightSetBrightness(AsyncWebServerRequest *request) void onApiFrontlightOff(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); - ledHandler.frontlightFadeOutAll(); + frontlightFadeOutAll(); request->send(HTTP_OK); } #endif -void onApiDNDTimeBasedEnable(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); - ledHandler.setDNDTimeBasedEnabled(true); - request->send(200); -} - -void onApiDNDTimeBasedDisable(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); - ledHandler.setDNDTimeBasedEnabled(false); - request->send(200); -} - -void onApiDNDSetTimeRange(AsyncWebServerRequest *request) { - if (request->hasParam("startHour") && request->hasParam("startMinute") && - request->hasParam("endHour") && request->hasParam("endMinute")) { - auto& ledHandler = getLedHandler(); - uint8_t startHour = request->getParam("startHour")->value().toInt(); - uint8_t startMinute = request->getParam("startMinute")->value().toInt(); - uint8_t endHour = request->getParam("endHour")->value().toInt(); - uint8_t endMinute = request->getParam("endMinute")->value().toInt(); - - ledHandler.setDNDTimeRange(startHour, startMinute, endHour, endMinute); - request->send(200); - } else { - request->send(400); - } -} - void onApiDNDStatus(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); JsonDocument doc; - doc["enabled"] = ledHandler.isDNDEnabled(); - doc["timeBasedEnabled"] = ledHandler.isDNDTimeBasedEnabled(); - doc["startTime"] = String(ledHandler.getDNDStartHour()) + ":" + - (ledHandler.getDNDStartMinute() < 10 ? "0" : "") + String(ledHandler.getDNDStartMinute()); - doc["endTime"] = String(ledHandler.getDNDEndHour()) + ":" + - (ledHandler.getDNDEndMinute() < 10 ? "0" : "") + String(ledHandler.getDNDEndMinute()); - doc["active"] = ledHandler.isDNDActive(); + doc["enabled"] = dndEnabled; + doc["timeBasedEnabled"] = dndTimeBasedEnabled; + doc["startTime"] = String(dndTimeRange.startHour) + ":" + + (dndTimeRange.startMinute < 10 ? "0" : "") + String(dndTimeRange.startMinute); + doc["endTime"] = String(dndTimeRange.endHour) + ":" + + (dndTimeRange.endMinute < 10 ? "0" : "") + String(dndTimeRange.endMinute); + doc["active"] = isDNDActive(); String response; serializeJson(doc, response); @@ -1198,71 +1147,11 @@ void onApiDNDStatus(AsyncWebServerRequest *request) { } void onApiDNDEnable(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); - ledHandler.setDNDEnabled(true); + setDNDEnabled(true); request->send(200); } void onApiDNDDisable(AsyncWebServerRequest *request) { - auto& ledHandler = getLedHandler(); - ledHandler.setDNDEnabled(false); - request->send(200); -} - -void onApiLightsGet(AsyncWebServerRequest *request) -{ - auto& ledHandler = getLedHandler(); - auto& pixels = ledHandler.getPixels(); - - JsonDocument doc; - JsonArray lights = doc.createNestedArray("lights"); - - for (uint i = 0; i < pixels.numPixels(); i++) - { - uint32_t pixColor = pixels.getPixelColor(pixels.numPixels() - i - 1); - JsonObject light = lights.createNestedObject(); - light["r"] = (uint8_t)(pixColor >> 16); - light["g"] = (uint8_t)(pixColor >> 8); - light["b"] = (uint8_t)pixColor; - } - - String output; - serializeJson(doc, output); - request->send(200, "application/json", output); -} - -void onApiLightsPost(AsyncWebServerRequest *request, uint8_t *data, size_t len, - size_t index, size_t total) -{ - auto& ledHandler = getLedHandler(); - auto& pixels = ledHandler.getPixels(); - - JsonDocument doc; - DeserializationError error = deserializeJson(doc, data); - if (error) - { - request->send(400); - return; - } - - JsonArray lights = doc["lights"]; - if (lights.size() != pixels.numPixels()) - { - request->send(400); - return; - } - - for (uint i = 0; i < pixels.numPixels(); i++) - { - JsonObject light = lights[i]; - uint8_t red = light["r"]; - uint8_t green = light["g"]; - uint8_t blue = light["b"]; - - pixels.setPixelColor((pixels.numPixels() - i - 1), - pixels.Color(red, green, blue)); - } - pixels.show(); - + setDNDEnabled(false); request->send(200); } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 08d38ad..4943a51 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,8 +18,6 @@ #define WEBSERVER_H #include "ESPAsyncWebServer.h" #include "lib/config.hpp" -#include "lib/led_handler.hpp" -#include "lib/block_notify.hpp" uint wifiLostConnection; uint priceNotifyLostConnection = 0; @@ -50,8 +48,7 @@ void handleBlockNotifyDisconnection() { if ((getUptime() - blockNotifyLostConnection) > 300) { // 5 minutes timeout Serial.println(F("Block notification connection lost for 5 minutes, restarting handler...")); - auto& blockNotify = BlockNotify::getInstance(); - blockNotify.restart(); + restartBlockNotify(); blockNotifyLostConnection = 0; } } @@ -61,14 +58,13 @@ void handleFrontlight() { if (hasLightLevel() && preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE) != 0) { uint lightLevel = getLightLevel(); uint luxThreshold = preferences.getUInt("luxLightToggle", DEFAULT_LUX_LIGHT_TOGGLE); - auto& ledHandler = getLedHandler(); if (lightLevel <= 1 && preferences.getBool("flOffWhenDark", DEFAULT_FL_OFF_WHEN_DARK)) { - if (ledHandler.frontlightIsOn()) ledHandler.frontlightFadeOutAll(); - } else if (lightLevel < luxThreshold && !ledHandler.frontlightIsOn()) { - ledHandler.frontlightFadeInAll(); - } else if (ledHandler.frontlightIsOn() && lightLevel > luxThreshold) { - ledHandler.frontlightFadeOutAll(); + if (frontlightIsOn()) frontlightFadeOutAll(); + } else if (lightLevel < luxThreshold && !frontlightIsOn()) { + frontlightFadeInAll(); + } else if (frontlightIsOn() && lightLevel > luxThreshold) { + frontlightFadeOutAll(); } } #endif @@ -94,18 +90,19 @@ void checkWiFiConnection() { void checkMissedBlocks() { Serial.println(F("Long time (45 min) since last block, checking if I missed anything...")); - auto& blockNotify = BlockNotify::getInstance(); - int currentBlock = blockNotify.fetchLatestBlock(); + int currentBlock = getBlockFetch(); if (currentBlock != -1) { - if (currentBlock != blockNotify.getBlockHeight()) { + if (currentBlock != getBlockHeight()) { Serial.println(F("Detected stuck block height... restarting block handler.")); - blockNotify.restart(); + restartBlockNotify(); } - blockNotify.setLastBlockUpdate(getUptime()); + setLastBlockUpdate(getUptime()); } } + void monitorDataConnections() { + // Price notification monitoring if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE) && !isPriceNotifyConnected()) { handlePriceNotifyDisconnection(); @@ -114,10 +111,9 @@ void monitorDataConnections() { } // Block notification monitoring - auto& blockNotify = BlockNotify::getInstance(); - if (blockNotify.isInitialized() && !blockNotify.isConnected()) { + if (getBlockNotifyInit() && !isBlockNotifyConnected()) { handleBlockNotifyDisconnection(); - } else if (blockNotifyLostConnection > 0 && blockNotify.isConnected()) { + } else if (blockNotifyLostConnection > 0 && isBlockNotifyConnected()) { blockNotifyLostConnection = 0; } @@ -129,7 +125,7 @@ void monitorDataConnections() { } // Check for missed blocks - if ((blockNotify.getLastBlockUpdate() - getUptime()) > 45 * 60) { + if ((getLastBlockUpdate() - getUptime()) > 45 * 60) { checkMissedBlocks(); } } @@ -141,6 +137,7 @@ extern "C" void app_main() { bool thirdPartySource = getDataSource() == THIRD_PARTY_SOURCE; + while (true) { if (eventSourceTaskHandle != NULL) { xTaskNotifyGive(eventSourceTaskHandle);