From b473e88be1bcfe37ab3a93dd2c29e9597c764f4d Mon Sep 17 00:00:00 2001 From: Lurkars Date: Sun, 16 Aug 2020 16:40:05 +0200 Subject: [PATCH] add/update interface --- .gitignore | 4 +- README.md | 15 +- components/ds3231/CMakeLists.txt | 2 +- components/ds3231/{include => }/ds3231.h | 0 components/ena-cwa/CMakeLists.txt | 3 +- components/ena-cwa/ena-cwa.c | 99 ++-- components/ena-cwa/{include => }/ena-cwa.h | 13 +- components/ena/ena-exposure.c | 79 ++- components/ena/ena-storage.c | 16 +- components/ena/ena.c | 5 + components/ena/include/ena-exposure.h | 61 +- components/ena/include/ena-storage.h | 29 +- components/i2c-main/i2c-main.h | 2 +- components/interface/CMakeLists.txt | 14 +- .../interface/include/interface-datetime.h | 37 -- components/interface/include/interface-menu.h | 35 -- .../interface/include/interface-status.h | 25 - components/interface/include/interface.h | 79 --- components/interface/interface-data.c | 195 +++++++ components/interface/interface-datetime.c | 149 ++++- components/interface/interface-input.c | 318 +++++++++++ components/interface/interface-label.c | 139 +++++ components/interface/interface-main.c | 143 +++++ components/interface/interface-menu.c | 88 --- components/interface/interface-report.c | 177 ++++++ components/interface/interface-settings.c | 147 +++++ components/interface/interface-status.c | 37 -- components/interface/interface-wifi.c | 183 ++++++ components/interface/interface.c | 134 +++-- components/interface/interface.h | 193 +++++++ components/ssd1306/CMakeLists.txt | 3 +- components/ssd1306/include/ssd1306-ascii.h | 538 ------------------ components/ssd1306/ssd1306-gfx.c | 307 ++++++++++ components/ssd1306/ssd1306-gfx.h | 62 ++ components/ssd1306/ssd1306.c | 204 ++++++- components/ssd1306/{include => }/ssd1306.h | 41 ++ components/wifi-controller/CMakeLists.txt | 8 + components/wifi-controller/wifi-controller.c | 185 ++++++ components/wifi-controller/wifi-controller.h | 56 ++ main/CMakeLists.txt | 2 - main/Kconfig.projbuild | 20 - main/display-interface.c | 153 ----- main/display-interface.h | 22 - main/main.c | 16 +- main/wifi.c | 172 ------ main/wifi.h | 38 -- 46 files changed, 2785 insertions(+), 1463 deletions(-) rename components/ds3231/{include => }/ds3231.h (100%) rename components/ena-cwa/{include => }/ena-cwa.h (76%) delete mode 100644 components/interface/include/interface-datetime.h delete mode 100644 components/interface/include/interface-menu.h delete mode 100644 components/interface/include/interface-status.h delete mode 100644 components/interface/include/interface.h create mode 100644 components/interface/interface-data.c create mode 100644 components/interface/interface-input.c create mode 100644 components/interface/interface-label.c create mode 100644 components/interface/interface-main.c delete mode 100644 components/interface/interface-menu.c create mode 100644 components/interface/interface-report.c create mode 100644 components/interface/interface-settings.c delete mode 100644 components/interface/interface-status.c create mode 100644 components/interface/interface-wifi.c create mode 100644 components/interface/interface.h delete mode 100644 components/ssd1306/include/ssd1306-ascii.h create mode 100644 components/ssd1306/ssd1306-gfx.c create mode 100644 components/ssd1306/ssd1306-gfx.h rename components/ssd1306/{include => }/ssd1306.h (78%) create mode 100644 components/wifi-controller/CMakeLists.txt create mode 100644 components/wifi-controller/wifi-controller.c create mode 100644 components/wifi-controller/wifi-controller.h delete mode 100644 main/Kconfig.projbuild delete mode 100644 main/display-interface.c delete mode 100644 main/display-interface.h delete mode 100644 main/wifi.c delete mode 100644 main/wifi.h diff --git a/.gitignore b/.gitignore index cd45f94..c3e96ef 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,6 @@ build dependencies.lock sdkconfig -sdkconfig.old \ No newline at end of file +sdkconfig.old + +.local \ No newline at end of file diff --git a/README.md b/README.md index ee05008..94b4e66 100644 --- a/README.md +++ b/README.md @@ -19,23 +19,24 @@ The following acronyms will be used in code and comments: * BLE privacy (change random MAC address in random interval) * permanent storage on flash of last keys, beacons and exposures (storage is limited, see [storage math](#some-storage-math) for details) * parsing of Exposure Key export binaries as defined in [Exposure Key export file format and verification](https://developers.google.com/android/exposure-notifications/exposure-key-file-format) (big thanks to [nanopb](https://github.com/nanopb/nanopb) for making this easier than I thought!) -* calculating exposure risks/scores (after adding reported keys and storing exposure information) as defined in [ENExposureConfiguration (Apple)](https://developer.apple.com/documentation/exposurenotification/enexposureconfiguration) +* calculating exposure risks/scores (after adding reported keys and storing exposure information) as defined in [ENExposureConfiguration (Apple)](https://developer.apple.com/documentation/exposurenotification/enexposureconfiguration/calculating_the_exposure_risk_value_in_exposurenotification_version_1) Additional features for full ENA device * RTC support with DS3231 (for correct system time) * display support with SSD1306 -* interface to - * set time +* interface with a 7 button control (joystick up,down,left,right,enter,cancel,ok) to * show exposure status + * set time + * connect to wifi + * delete data + * set language + * enter tan for infection status (dummy for now) ### Features in development * automatically receive Exposure Key export from web (started with [Corona Warn App](https://github.com/corona-warn-app)) * send infected status (will test [Corona Warn App](https://github.com/corona-warn-app)) * battery support * 3d print case -* interface to - * delete data - * report infection ### Limitations/Problems/Questions * WiFi or other external connection needed for infections status (auto-connect to open WiFis?) @@ -71,7 +72,7 @@ So on average it is possible to meet 38 (24 on a lower boundary) different devic ### Hardware Required -For now just an ESP32 is required. DS3231 RTC and SSD1306 Display are required for a complete device. +For base functionality just an ESP32 is required. DS3231 RTC, SSD1306 Display and 7 buttons are required for a complete device. ### Configure the project diff --git a/components/ds3231/CMakeLists.txt b/components/ds3231/CMakeLists.txt index b84f0cd..7c80e2d 100644 --- a/components/ds3231/CMakeLists.txt +++ b/components/ds3231/CMakeLists.txt @@ -1,7 +1,7 @@ idf_component_register( SRCS "ds3231.c" - INCLUDE_DIRS "include" + INCLUDE_DIRS "." PRIV_REQUIRES i2c-main ) \ No newline at end of file diff --git a/components/ds3231/include/ds3231.h b/components/ds3231/ds3231.h similarity index 100% rename from components/ds3231/include/ds3231.h rename to components/ds3231/ds3231.h diff --git a/components/ena-cwa/CMakeLists.txt b/components/ena-cwa/CMakeLists.txt index 5fd73ef..0f23f8a 100644 --- a/components/ena-cwa/CMakeLists.txt +++ b/components/ena-cwa/CMakeLists.txt @@ -1,11 +1,12 @@ idf_component_register( SRCS "ena-cwa.c" - INCLUDE_DIRS "include" + INCLUDE_DIRS "." PRIV_REQUIRES esp_http_client miniz ena + wifi-controller EMBED_FILES "certs/telekom.pem" ) \ No newline at end of file diff --git a/components/ena-cwa/ena-cwa.c b/components/ena-cwa/ena-cwa.c index 5d89bc4..03f7a5d 100644 --- a/components/ena-cwa/ena-cwa.c +++ b/components/ena-cwa/ena-cwa.c @@ -12,15 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. #include - +#include "time.h" #include "esp_log.h" +#include "esp_event.h" #include "esp_http_client.h" #include "miniz.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "ena-storage.h" +#include "ena-exposure.h" #include "ena-cwa.h" +#include "wifi-controller.h" + +extern uint8_t export_bin_start[] asm("_binary_export_bin_start"); // test data from Google or https://svc90.main.px.t-online.de/version/v1/diagnosis-keys/country/DE/date/2020-07-22 +extern uint8_t export_bin_end[] asm("_binary_export_bin_end"); extern const uint8_t telekom_pem_start[] asm("_binary_telekom_pem_start"); extern const uint8_t telekom__pem_end[] asm("_binary_telekom_pem_end"); @@ -40,7 +47,7 @@ esp_err_t ena_cwa_http_event_handler(esp_http_client_event_t *evt) output_len = 0; if (output_buffer == NULL) { - ESP_LOGE(ENA_CWA_LOG, "Failed to allocate memory for output buffer"); + ESP_LOGE(ENA_CWA_LOG, "Failed to allocate memory for output buffer, memory: %d kB", (xPortGetFreeHeapSize() / 1024)); return ESP_FAIL; } } @@ -53,62 +60,14 @@ esp_err_t ena_cwa_http_event_handler(esp_http_client_event_t *evt) if (output_buffer != NULL) { ESP_LOGD(ENA_CWA_LOG, "memory: %d kB", (xPortGetFreeHeapSize() / 1024)); - - size_t zip_image_size = esp_http_client_get_content_length(evt->client); - // const char *file_name = "export.sig"; - - /* - mz_zip_archive zip_archive; - mz_zip_archive_file_stat file_stat; - mz_uint32 file_index; - - memset(&zip_archive, 0, sizeof(zip_archive)); - ESP_LOGD(ENA_CWA_LOG, "memory: %d kB", (xPortGetFreeHeapSize() / 1024)); - - vTaskDelay(1000 / portTICK_PERIOD_MS); - mz_zip_reader_init_mem(&zip_archive, output_buffer, sizeof(output_buffer), 0); - ESP_LOGD(ENA_CWA_LOG, "memory: %d kB", (xPortGetFreeHeapSize() / 1024)); - mz_zip_reader_locate_file_v2(&zip_archive, file_name, NULL, 0, &file_index); - ESP_LOGD(ENA_CWA_LOG, "memory: %d kB", (xPortGetFreeHeapSize() / 1024)); - mz_zip_reader_file_stat(&zip_archive, file_index, &file_stat); - ESP_LOGD(ENA_CWA_LOG, "memory: %d kB", (xPortGetFreeHeapSize() / 1024)); - size_t extracted_size = file_stat.m_uncomp_size; - - ESP_LOGD(ENA_CWA_LOG, "size of export.sig: %d", extracted_size); - - char *file_buffer = malloc(extracted_size); - - mz_zip_reader_extract_to_mem(&zip_archive, file_index, file_buffer, extracted_size, 0); - ESP_LOGD(ENA_CWA_LOG, "memory: %d kB", (xPortGetFreeHeapSize() / 1024)); - mz_zip_reader_end(&zip_archive); - - ESP_LOG_BUFFER_HEXDUMP(ENA_CWA_LOG, file_buffer, extracted_size, ESP_LOG_DEBUG); - - free(file_buffer); - */ - - mz_zip_archive zip_archive; - ESP_LOGD(ENA_CWA_LOG, "1 memory: %d kB (min %d kB)", (esp_get_free_heap_size() / 1024), (esp_get_minimum_free_heap_size() / 1024)); - memset(&zip_archive, 0, sizeof(zip_archive)); - ESP_LOGD(ENA_CWA_LOG, "2 memory: %d kB (min %d kB)", (esp_get_free_heap_size() / 1024), (esp_get_minimum_free_heap_size() / 1024)); - mz_zip_reader_init_mem(&zip_archive, output_buffer, zip_image_size, 0); - ESP_LOGD(ENA_CWA_LOG, "3 memory: %d kB (min %d kB)", (esp_get_free_heap_size() / 1024), (esp_get_minimum_free_heap_size() / 1024)); - - mz_zip_reader_end(&zip_archive); - - ESP_LOGD(ENA_CWA_LOG, "4 memory: %d kB (min %d kB)", (esp_get_free_heap_size() / 1024), (esp_get_minimum_free_heap_size() / 1024)); - /* - p = mz_zip_reader_extract_file_to_heap(&zip_archive, file_name, &extracted_size, 0); - ESP_LOGD(ENA_CWA_LOG, "4 memory: %d kB", (esp_get_free_heap_size() / 1024)); - mz_zip_reader_end(&zip_archive); - ESP_LOG_BUFFER_HEXDUMP(ENA_CWA_LOG, p, extracted_size, ESP_LOG_DEBUG); - free(p); - */ - free(output_buffer); output_buffer = NULL; output_len = 0; - ESP_LOGD(ENA_CWA_LOG, "memory freed: %d kB (min %d kB)", (esp_get_free_heap_size() / 1024), (esp_get_minimum_free_heap_size() / 1024)); + ESP_ERROR_CHECK_WITHOUT_ABORT(ena_exposure_check_export(export_bin_start, (export_bin_end - export_bin_start))); + } + else + { + return ESP_FAIL; } break; default: @@ -117,7 +76,7 @@ esp_err_t ena_cwa_http_event_handler(esp_http_client_event_t *evt) return ESP_OK; } -void ena_cwa_receive_keys(char *date_string) +esp_err_t ena_cwa_receive_keys(char *date_string) { char *url = malloc(strlen(ENA_CWA_KEYFILES_URL) + strlen(date_string)); sprintf(url, ENA_CWA_KEYFILES_URL, date_string); @@ -126,6 +85,8 @@ void ena_cwa_receive_keys(char *date_string) .cert_pem = (char *)telekom_pem_start, .event_handler = ena_cwa_http_event_handler, }; + + ESP_LOGD(ENA_CWA_LOG, "start memory: %d kB, %s", (xPortGetFreeHeapSize() / 1024), date_string); esp_http_client_handle_t client = esp_http_client_init(&config); esp_err_t err = esp_http_client_perform(client); @@ -136,7 +97,31 @@ void ena_cwa_receive_keys(char *date_string) esp_http_client_get_status_code(client), content_length); } - // free(url); + free(url); esp_http_client_close(client); esp_http_client_cleanup(client); + return err; +} + +void ena_cwa_run(void) +{ + + static time_t current_time = 0; + static uint32_t last_check = 0; + current_time = time(NULL); + last_check = ena_storage_read_last_exposure_date(); + if ((((uint32_t)current_time) - last_check) / (60 * 60 * 24) > 0 && wifi_controller_connection() != NULL) + { + char date_string[11]; + struct tm *time_info; + time_info = localtime(¤t_time); + time_info->tm_mday--; + strftime(date_string, 11, "%Y-%m-%d", time_info); + esp_err_t err = ena_cwa_receive_keys(date_string); + if (err == ESP_OK) + { + ena_storage_write_last_exposure_date((uint32_t)current_time); + ena_exposure_summary(ena_exposure_default_config()); + } + } } \ No newline at end of file diff --git a/components/ena-cwa/include/ena-cwa.h b/components/ena-cwa/ena-cwa.h similarity index 76% rename from components/ena-cwa/include/ena-cwa.h rename to components/ena-cwa/ena-cwa.h index 8c6425a..2014aab 100644 --- a/components/ena-cwa/include/ena-cwa.h +++ b/components/ena-cwa/ena-cwa.h @@ -22,15 +22,22 @@ #ifndef _ena_CWA_H_ #define _ena_CWA_H_ -#define ENA_CWA_LOG "ESP-ENA-corona-warn-app" // TAG for Logging +#include "esp_err.h" -#define ENA_CWA_KEYFILES_URL "http://svc90.main.px.t-online.de/version/v1/diagnosis-keys/country/DE/date/%s" +#define ENA_CWA_LOG "ESP-ENA-cwa" // TAG for Logging + +#define ENA_CWA_KEYFILES_URL "https://svc90.main.px.t-online.de/version/v1/diagnosis-keys/country/DE/date/%s" /** * @brief fetch key export for given date * * @param[in] date_string the date to fetch the data for */ -void ena_cwa_receive_keys(char *date_string); +esp_err_t ena_cwa_receive_keys(char *date_string); + +/** + * @brief start ena CWA + */ +void ena_cwa_run(void); #endif \ No newline at end of file diff --git a/components/ena/ena-exposure.c b/components/ena/ena-exposure.c index 843bc69..fa45c9c 100644 --- a/components/ena/ena-exposure.c +++ b/components/ena/ena-exposure.c @@ -27,6 +27,8 @@ #include "ena-exposure.h" +static ena_exposure_summary_t *current_summary; + static ena_exposure_config_t DEFAULT_ENA_EXPOSURE_CONFIG = { // transmission_risk_values { @@ -77,14 +79,13 @@ static ena_exposure_config_t DEFAULT_ENA_EXPOSURE_CONFIG = { static const char kFileHeader[] = "EK Export v1 "; static size_t kFileHeaderSize = sizeof(kFileHeader) - 1; -extern uint8_t export_bin_start[] asm("_binary_export_bin_start"); // test data from Google or https://svc90.main.px.t-online.de/version/v1/diagnosis-keys/country/DE/date/2020-07-22 -extern uint8_t export_bin_end[] asm("_binary_export_bin_end"); - -int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params) +int ena_exposure_transmission_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params) { - int score = 1; - score *= config->transmission_risk_values[params.report_type]; + return config->transmission_risk_values[params.report_type]; +} +int ena_exposure_duration_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params) +{ // calc duration level int duration_level = MINUTES_0; if (params.duration > 0) @@ -118,8 +119,12 @@ int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_paramete duration_level = MINUTES_LONGER; } } - score *= config->duration_risk_values[duration_level]; + return config->duration_risk_values[duration_level]; +} + +int ena_exposure_days_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params) +{ // calc days level int days_level = DAYS_14; @@ -152,8 +157,11 @@ int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_paramete days_level = DAYS_13; } - score *= config->days_risk_values[days_level]; + return config->days_risk_values[days_level]; +} +int ena_exposure_attenuation_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params) +{ // calc attenuation level int attenuation_level = ATTENUATION_73; @@ -186,7 +194,19 @@ int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_paramete attenuation_level = ATTENUATION_63; } - score *= config->attenuation_risk_values[attenuation_level]; + return config->attenuation_risk_values[attenuation_level]; +} + +int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params) +{ + int score = 1; + score *= ena_exposure_transmission_risk_score(config, params); + + score *= ena_exposure_duration_risk_score(config, params); + + score *= ena_exposure_days_risk_score(config, params); + + score *= ena_exposure_attenuation_risk_score(config, params); if (score > 255) { @@ -196,22 +216,25 @@ int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_paramete return score; } -void ena_exposure_summary(ena_exposure_config_t *config, ena_exposure_summary_t *summary) +void ena_exposure_summary(ena_exposure_config_t *config) { - // XXX TEST key export (should be called on other location though) - ESP_ERROR_CHECK_WITHOUT_ABORT(ena_exposure_check_export(export_bin_start, (export_bin_end - export_bin_start))); - uint32_t count = ena_storage_exposure_information_count(); uint32_t current_time = (uint32_t)time(NULL); - summary->days_since_last_exposure = INT_MAX; - summary->max_risk_score = 0; - summary->risk_score_sum = 0; - summary->num_exposures = count; + if (current_summary == NULL) + { + current_summary = malloc(sizeof(ena_exposure_summary_t)); + } + + current_summary->last_update = ena_storage_read_last_exposure_date(); + current_summary->days_since_last_exposure = INT_MAX; + current_summary->max_risk_score = 0; + current_summary->risk_score_sum = 0; + current_summary->num_exposures = count; if (count == 0) { - summary->days_since_last_exposure = -1; + current_summary->days_since_last_exposure = -1; } ena_exposure_information_t exposure_info; @@ -220,21 +243,31 @@ void ena_exposure_summary(ena_exposure_config_t *config, ena_exposure_summary_t { ena_storage_get_exposure_information(i, &exposure_info); params.days = (current_time - exposure_info.day) / (60 * 60 * 24); // difference in days - if (params.days < summary->days_since_last_exposure) + if (params.days < current_summary->days_since_last_exposure) { - summary->days_since_last_exposure = params.days; + current_summary->days_since_last_exposure = params.days; } params.duration = exposure_info.duration_minutes; params.attenuation = exposure_info.typical_attenuation; int score = ena_exposure_risk_score(config, params); - if (score > summary->max_risk_score) + if (score > current_summary->max_risk_score) { - summary->max_risk_score = score; + current_summary->max_risk_score = score; } - summary->risk_score_sum += score; + current_summary->risk_score_sum += score; } } +ena_exposure_summary_t *ena_exposure_current_summary(void) +{ + if (current_summary == NULL) + { + ena_exposure_summary(ena_exposure_default_config()); + } + + return current_summary; +} + ena_exposure_config_t *ena_exposure_default_config(void) { return &DEFAULT_ENA_EXPOSURE_CONFIG; diff --git a/components/ena/ena-storage.c b/components/ena/ena-storage.c index a53a357..30a355d 100644 --- a/components/ena/ena-storage.c +++ b/components/ena/ena-storage.c @@ -22,7 +22,8 @@ #define BLOCK_SIZE (4096) -const int ENA_STORAGE_TEK_COUNT_ADDRESS = (ENA_STORAGE_START_ADDRESS); // starting address for TEK COUNT +const int ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS = (ENA_STORAGE_START_ADDRESS); +const int ENA_STORAGE_TEK_COUNT_ADDRESS = (ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS + sizeof(uint32_t)); const int ENA_STORAGE_TEK_START_ADDRESS = (ENA_STORAGE_TEK_COUNT_ADDRESS + sizeof(uint32_t)); const int ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS = (ENA_STORAGE_TEK_START_ADDRESS + sizeof(ena_tek_t) * ENA_STORAGE_TEK_MAX); const int ENA_STORAGE_EXPOSURE_INFORMATION_START_ADDRESS = (ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS + sizeof(uint32_t)); @@ -144,6 +145,18 @@ void ena_storage_shift_delete(size_t address, size_t end_address, size_t size) } } +uint32_t ena_storage_read_last_exposure_date(void) +{ + uint32_t timestamp = 0; + ena_storage_read(ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS, ×tamp, sizeof(uint32_t)); + return timestamp; +} + +void ena_storage_write_last_exposure_date(uint32_t timestamp) +{ + ena_storage_write(ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS, ×tamp, sizeof(uint32_t)); +} + uint32_t ena_storage_read_last_tek(ena_tek_t *tek) { uint32_t tek_count = 0; @@ -298,6 +311,7 @@ void ena_storage_erase(void) ESP_LOGI(ENA_STORAGE_LOG, "erased partition %s!", ENA_STORAGE_PARTITION_NAME); uint32_t count = 0; + ena_storage_write(ENA_STORAGE_LAST_EXPOSURE_DATE_ADDRESS, &count, sizeof(uint32_t)); ena_storage_write(ENA_STORAGE_TEK_COUNT_ADDRESS, &count, sizeof(uint32_t)); ena_storage_write(ENA_STORAGE_EXPOSURE_INFORMATION_COUNT_ADDRESS, &count, sizeof(uint32_t)); ena_storage_write(ENA_STORAGE_TEMP_BEACONS_COUNT_ADDRESS, &count, sizeof(uint32_t)); diff --git a/components/ena/ena.c b/components/ena/ena.c index b83f1c4..441acc3 100644 --- a/components/ena/ena.c +++ b/components/ena/ena.c @@ -91,6 +91,11 @@ void ena_start(void) ena_storage_erase(); #endif + if (ena_storage_read_last_exposure_date() == 0xFFFFFFFF) + { + ena_storage_erase(); + } + // init NVS for BLE esp_err_t ret; ret = nvs_flash_init(); diff --git a/components/ena/include/ena-exposure.h b/components/ena/include/ena-exposure.h index e9f8f7f..cb00bb7 100644 --- a/components/ena/include/ena-exposure.h +++ b/components/ena/include/ena-exposure.h @@ -21,7 +21,7 @@ #define _ena_EXPOSURE_H_ #include - +#include "esp_err.h" #include "ena-crypto.h" #define ENA_EXPOSURE_LOG "ESP-ENA-exposure" // TAG for Logging @@ -135,6 +135,7 @@ typedef struct __attribute__((__packed__)) */ typedef struct __attribute__((__packed__)) { + uint32_t last_update; // timestamp of last update of exposure data int days_since_last_exposure; // Number of days since the most recent exposure. int num_exposures; // Number of all exposure information int max_risk_score; // max. risk score of all exposure information @@ -142,10 +143,52 @@ typedef struct __attribute__((__packed__)) } ena_exposure_summary_t; /** - * @brief calculate risk score + * @brief calculate transmission risk score * * @param[in] config the exposure configuration used for calculating score * @param[in] params the exposure parameter to calculate with + * + * @return + */ +int ena_exposure_transmission_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params); + +/** + * @brief calculate duration risk score + * + * @param[in] config the exposure configuration used for calculating score + * @param[in] params the exposure parameter to calculate with + * + * @return + */ +int ena_exposure_duration_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params); + +/** + * @brief calculate days risk score + * + * @param[in] config the exposure configuration used for calculating score + * @param[in] params the exposure parameter to calculate with + * + * @return + */ +int ena_exposure_days_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params); + +/** + * @brief calculate attenuation risk score + * + * @param[in] config the exposure configuration used for calculating score + * @param[in] params the exposure parameter to calculate with + * + * @return + */ +int ena_exposure_attenuation_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params); + +/** + * @brief calculate overall risk score + * + * @param[in] config the exposure configuration used for calculating score + * @param[in] params the exposure parameter to calculate with + * + * @return */ int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params); @@ -153,12 +196,22 @@ int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_paramete * @brief returns the current exposure summary * * @param[in] config the exposure configuration used for calculating scores - * @param[out] summary pointer to exposure summary to write to */ -void ena_exposure_summary(ena_exposure_config_t *config, ena_exposure_summary_t *summary); +void ena_exposure_summary(ena_exposure_config_t *config); + +/** + * @brief return the current exposure summary + * + * @return + * ena_exposure_summary_t pointer to the current exposure summary + */ +ena_exposure_summary_t *ena_exposure_current_summary(void); /** * @brief return a default exposure configuration + * + * @return + * ena_exposure_config_t default exposure configuration */ ena_exposure_config_t *ena_exposure_default_config(void); diff --git a/components/ena/include/ena-storage.h b/components/ena/include/ena-storage.h index 3d660ae..5d45bf9 100644 --- a/components/ena/include/ena-storage.h +++ b/components/ena/include/ena-storage.h @@ -22,11 +22,11 @@ #include "ena-crypto.h" -#define ENA_STORAGE_LOG "ESP-ENA-storage" // TAG for Logging -#define ENA_STORAGE_PARTITION_NAME (CONFIG_ENA_STORAGE_PARTITION_NAME) // name of partition to use for storing -#define ENA_STORAGE_START_ADDRESS (CONFIG_ENA_STORAGE_START_ADDRESS) // start address of storage -#define ENA_STORAGE_TEK_MAX (CONFIG_ENA_STORAGE_TEK_MAX) // Period of storing TEKs // length of a stored beacon -> RPI keysize + AEM size + 4 Bytes for ENIN + 4 Bytes for RSSI -#define ENA_STORAGE_TEMP_BEACONS_MAX (CONFIG_ENA_STORAGE_TEMP_BEACONS_MAX) // Maximum number of temporary stored beacons // length of a stored beacon -> RPI keysize + AEM size + 4 Bytes for ENIN + 4 Bytes for RSSI +#define ENA_STORAGE_LOG "ESP-ENA-storage" // TAG for Logging +#define ENA_STORAGE_PARTITION_NAME (CONFIG_ENA_STORAGE_PARTITION_NAME) // name of partition to use for storing +#define ENA_STORAGE_START_ADDRESS (CONFIG_ENA_STORAGE_START_ADDRESS) // start address of storage +#define ENA_STORAGE_TEK_MAX (CONFIG_ENA_STORAGE_TEK_MAX) // Period of storing TEKs // length of a stored beacon -> RPI keysize + AEM size + 4 Bytes for ENIN + 4 Bytes for RSSI +#define ENA_STORAGE_TEMP_BEACONS_MAX (CONFIG_ENA_STORAGE_TEMP_BEACONS_MAX) // Maximum number of temporary stored beacons // length of a stored beacon -> RPI keysize + AEM size + 4 Bytes for ENIN + 4 Bytes for RSSI #define ENA_STORAGE_EXPOSURE_INFORMATION_MAX (CONFIG_ENA_STORAGE_EXPOSURE_INFORMATION_MAX) // Maximum number of stored exposure information /** @@ -59,7 +59,7 @@ typedef struct __attribute__((__packed__)) uint32_t day; // Day of the exposure, using UTC, encapsulated as the time of the beginning of that day. int typical_attenuation; // Aggregation of the attenuations of all of a given diagnosis key's beacons received during the scan, in dB. int min_attenuation; // Minimum attenuation of all of a given diagnosis key's beacons received during the scan, in dB. - int duration_minutes; //The duration of the exposure in minutes. + int duration_minutes; //The duration of the exposure in minutes. int report_type; // Type of diagnosis associated with a key. } ena_exposure_information_t; @@ -90,6 +90,21 @@ void ena_storage_write(size_t address, void *data, size_t size); */ void ena_storage_shift_delete(size_t address, size_t end_address, size_t size); +/** + * @brief get timestamp of most recent exposure data + * + * @return + * unix timestamp + */ +uint32_t ena_storage_read_last_exposure_date(void); + +/** + * @brief set timestamp of most recent exposure data + * + * @param[in] timestamp unix timestamp + */ +void ena_storage_write_last_exposure_date(uint32_t timestamp); + /** * @brief get last stored TEK * @@ -218,7 +233,6 @@ void ena_storage_erase(void); */ void ena_storage_erase_tek(void); - /** * @brief erase all stored exposure information * @@ -248,7 +262,6 @@ void ena_storage_erase_beacon(void); */ void ena_storage_dump_teks(void); - /** * @brief dump all stored exposure information to serial output * diff --git a/components/i2c-main/i2c-main.h b/components/i2c-main/i2c-main.h index c612357..b9b3dea 100644 --- a/components/i2c-main/i2c-main.h +++ b/components/i2c-main/i2c-main.h @@ -20,7 +20,7 @@ #ifndef _i2c_main_H_ #define _i2c_main_H_ -#define I2C_SDA_PIN (21) +#define I2C_SDA_PIN (23) #define I2C_SCL_PIN (22) #define I2C_CLK_SPEED (1000000) diff --git a/components/interface/CMakeLists.txt b/components/interface/CMakeLists.txt index 301a2bf..9853d7c 100644 --- a/components/interface/CMakeLists.txt +++ b/components/interface/CMakeLists.txt @@ -1,10 +1,18 @@ idf_component_register( SRCS "interface.c" + "interface-main.c" + "interface-data.c" "interface-datetime.c" - "interface-menu.c" - "interface-status.c" - INCLUDE_DIRS "include" + "interface-input.c" + "interface-label.c" + "interface-report.c" + "interface-settings.c" + "interface-wifi.c" + INCLUDE_DIRS "." PRIV_REQUIRES ena + ssd1306 + ds3231 + wifi-controller ) \ No newline at end of file diff --git a/components/interface/include/interface-datetime.h b/components/interface/include/interface-datetime.h deleted file mode 100644 index ca72e07..0000000 --- a/components/interface/include/interface-datetime.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -/** - * @file - * - * @brief interface for changing current date and time - * - */ -#ifndef _interface__DATETIME_H_ -#define _interface__DATETIME_H_ - -typedef enum -{ - INTERFACE_DATETIME_STATE_YEAR = 0, - INTERFACE_DATETIME_STATE_MONTH, - INTERFACE_DATETIME_STATE_DAY, - INTERFACE_DATETIME_STATE_HOUR, - INTERFACE_DATETIME_STATE_MINUTE, - INTERFACE_DATETIME_STATE_SECONDS, -} interface_datetime_state_t; - -void interface_datetime_start(void); - -int interface_datetime_state(void); - -#endif \ No newline at end of file diff --git a/components/interface/include/interface-menu.h b/components/interface/include/interface-menu.h deleted file mode 100644 index 102c5a6..0000000 --- a/components/interface/include/interface-menu.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -/** - * @file - * - * @brief interface menu to navigate through interface - * - */ -#ifndef _interface__MENU_H_ -#define _interface__MENU_H_ - -typedef enum -{ - INTERFACE_MENU_STATE_IDLE = 0, - INTERFACE_MENU_STATE_SELECT_TIME, - INTERFACE_MENU_STATE_SELECT_DEBUG, - INTERFACE_MENU_STATE_SELECT_STATUS, -} interface_menu_state_t; - -void interface_menu_start(void); - -int interface_menu_get_state(void); - -#endif \ No newline at end of file diff --git a/components/interface/include/interface-status.h b/components/interface/include/interface-status.h deleted file mode 100644 index b1e61f6..0000000 --- a/components/interface/include/interface-status.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -/** - * @file - * - * @brief interface to show current ENA status (infection risk etc.) - * - */ -#ifndef _interface__STATUS_H_ -#define _interface__STATUS_H_ - -void interface_status_start(void); - -#endif \ No newline at end of file diff --git a/components/interface/include/interface.h b/components/interface/include/interface.h deleted file mode 100644 index 7b8f472..0000000 --- a/components/interface/include/interface.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -/** - * @file - * - * @brief interface functionality via touch pads for control and setup - * - */ -#ifndef _interface__H_ -#define _interface__H_ - -#define INTERFACE_LOG "INTERFACE" // TAG for Logging - -#define TOUCHPAD_FILTER_TOUCH_PERIOD (10) -#define TOUCH_PAD_COUNT (4) -#define TOUCH_PAD_ESC (TOUCH_PAD_NUM0) -#define TOUCH_PAD_OK (TOUCH_PAD_NUM6) -#define TOUCH_PAD_UP (TOUCH_PAD_NUM4) -#define TOUCH_PAD_DOWN (TOUCH_PAD_NUM3) - -/** - * @brief different interface states - */ -typedef enum -{ - INTERFACE_STATE_IDLE = 0, // ilde state, do nothing - INTERFACE_STATE_MENU, // main menu - INTERFACE_STATE_SET_DATETIME, // set current date and time - INTERFACE_STATE_STATUS, // current status -} interface_state_t; - -/** - * @brief callback function on touch event - */ -typedef void (*interface_touch_callback)(void); - -/** - * @brief register a callback function for touch event - * - * @param[in] touch_pad id of the touchpad to listen touch - * @param[in] callback callback function - */ -void interface_register_touch_callback(int touch_pad, interface_touch_callback callback); - -/** - * @brief get current interface state - * - * @return - * current state the interface is in - */ -int interface_get_state(void); - -/** - * @brief set current interface state - * - * @param[in] state new state to set - */ -void interface_set_state(interface_state_t state); - -/** - * @brief start interface logic - * - * This will initialize the touch controls and start a task to listen to touch - * inputs and calling the callbacks - */ -void interface_start(void); - -#endif \ No newline at end of file diff --git a/components/interface/interface-data.c b/components/interface/interface-data.c new file mode 100644 index 0000000..ab6aae6 --- /dev/null +++ b/components/interface/interface-data.c @@ -0,0 +1,195 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/gpio.h" + +#include "ssd1306.h" +#include "ssd1306-gfx.h" + +#include "ena-storage.h" + +#include "interface.h" + +typedef enum +{ + INTERFACE_DATA_DEL_TEK = 0, + INTERFACE_DATA_DEL_EXPOSURE_INFO, + INTERFACE_DATA_DEL_TEMP_RPI, + INTERFACE_DATA_DEL_RPI, + INTERFACE_DATA_DEL_LAST_EXPOSURE, + INTERFACE_DATA_DEL_ALL, +} interface_data_state_t; + +static int current_interface_data_state; +static int current_data_index; +static bool confirm_current; + +void interface_data_set(void) +{ + if (!confirm_current) + { + interface_main_start(); + } + else + { + confirm_current = false; + ssd1306_clear_line(SSD1306_ADDRESS, 2, false); + ssd1306_clear_line(SSD1306_ADDRESS, 4, false); + ssd1306_clear_line(SSD1306_ADDRESS, 6, false); + } +} + +void interface_data_rst(void) +{ + + if (confirm_current) + { + switch (current_interface_data_state) + { + case INTERFACE_DATA_DEL_TEK: + ena_storage_erase_tek(); + break; + case INTERFACE_DATA_DEL_EXPOSURE_INFO: + ena_storage_erase_exposure_information(); + break; + case INTERFACE_DATA_DEL_TEMP_RPI: + ena_storage_erase_temporary_beacon(); + break; + case INTERFACE_DATA_DEL_RPI: + ena_storage_erase_beacon(); + break; + case INTERFACE_DATA_DEL_LAST_EXPOSURE: + ena_storage_write_last_exposure_date(0); + break; + case INTERFACE_DATA_DEL_ALL: + ena_storage_erase(); + break; + } + + confirm_current = false; + ssd1306_clear_line(SSD1306_ADDRESS, 2, false); + ssd1306_clear_line(SSD1306_ADDRESS, 4, false); + ssd1306_clear_line(SSD1306_ADDRESS, 6, false); + } +} + +void interface_data_lft(void) +{ + interface_wifi_start(); +} + +void interface_data_rht(void) +{ + interface_settings_start(); +} + +void interface_data_mid(void) +{ + if (!confirm_current) + { + ssd1306_clear_line(SSD1306_ADDRESS, 2, false); + ssd1306_clear_line(SSD1306_ADDRESS, 4, false); + ssd1306_clear_line(SSD1306_ADDRESS, 6, false); + confirm_current = true; + } +} + +void interface_data_up(void) +{ + current_interface_data_state--; + if (current_interface_data_state < INTERFACE_DATA_DEL_TEK) + { + current_interface_data_state = INTERFACE_DATA_DEL_ALL; + + current_data_index = 3; + } + else if (current_interface_data_state < current_data_index) + { + current_data_index--; + } + + ssd1306_clear_line(SSD1306_ADDRESS, 2, false); + ssd1306_clear_line(SSD1306_ADDRESS, 4, false); + ssd1306_clear_line(SSD1306_ADDRESS, 6, false); +} + +void interface_data_dwn(void) +{ + current_interface_data_state++; + if (current_interface_data_state > INTERFACE_DATA_DEL_ALL) + { + current_interface_data_state = INTERFACE_DATA_DEL_TEK; + current_data_index = 0; + } + else if (current_interface_data_state >= (current_data_index + 3)) + { + current_data_index++; + } + + ssd1306_clear_line(SSD1306_ADDRESS, 2, false); + ssd1306_clear_line(SSD1306_ADDRESS, 4, false); + ssd1306_clear_line(SSD1306_ADDRESS, 6, false); +} + +void interface_data_display(void) +{ + ssd1306_menu_headline(SSD1306_ADDRESS, interface_get_label_text(&interface_text_headline_data), true, 0); + if (confirm_current) + { + ssd1306_text_line_column(SSD1306_ADDRESS, interface_get_label_text(&interface_text_data_del[current_interface_data_state]), 2, 2, false); + ssd1306_text_line_column(SSD1306_ADDRESS, "?", 2, strlen(interface_get_label_text(&interface_text_data_del[current_interface_data_state])) + 2, false); + + ssd1306_set_button(SSD1306_ADDRESS, interface_get_label_text(&interface_text_button_cancel), true, false); + ssd1306_set_button(SSD1306_ADDRESS, interface_get_label_text(&interface_text_button_ok), false, true); + } + else + { + ssd1306_clear_line(SSD1306_ADDRESS, 5, false); + ssd1306_clear_line(SSD1306_ADDRESS, 7, false); + for (int i = 0; i < 3; i++) + { + int index = i + current_data_index; + if (index <= INTERFACE_DATA_DEL_ALL) + { + if (index == current_interface_data_state) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_right, 8, i * 2 + 2, 8, false); + } + + ssd1306_text_line_column(SSD1306_ADDRESS, interface_get_label_text(&interface_text_data_del[index]), i * 2 + 2, 2, false); + } + } + } +} + +void interface_data_start(void) +{ + current_interface_data_state = INTERFACE_DATA_DEL_TEK; + + interface_register_button_callback(INTERFACE_BUTTON_RST, &interface_data_rst); + interface_register_button_callback(INTERFACE_BUTTON_SET, &interface_data_set); + interface_register_button_callback(INTERFACE_BUTTON_LFT, &interface_data_lft); + interface_register_button_callback(INTERFACE_BUTTON_RHT, &interface_data_rht); + interface_register_button_callback(INTERFACE_BUTTON_MID, &interface_data_mid); + interface_register_button_callback(INTERFACE_BUTTON_UP, &interface_data_up); + interface_register_button_callback(INTERFACE_BUTTON_DWN, &interface_data_dwn); + interface_set_display_function(&interface_data_display); + + ESP_LOGD(INTERFACE_LOG, "start delete data interface"); +} diff --git a/components/interface/interface-datetime.c b/components/interface/interface-datetime.c index df0f5e1..903264a 100644 --- a/components/interface/interface-datetime.c +++ b/components/interface/interface-datetime.c @@ -11,33 +11,58 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#include +#include #include #include - #include "esp_log.h" +#include "driver/gpio.h" + +#include "ds3231.h" +#include "ssd1306.h" +#include "ssd1306-gfx.h" + #include "interface.h" -#include "driver/touch_pad.h" -#include "interface-menu.h" -#include "interface-datetime.h" - -static int current_interface_datetime_state = INTERFACE_DATETIME_STATE_YEAR; - -const uint32_t interface_datetime_steps[6] = { - 31557600, 2629800, 86400, 3600, 60, 1}; - -void interface_datetime_esc(void) +typedef enum { - interface_menu_start(); + INTERFACE_DATETIME_STATE_HOUR = 0, + INTERFACE_DATETIME_STATE_MINUTE, + INTERFACE_DATETIME_STATE_SECONDS, + INTERFACE_DATETIME_STATE_DAY, + INTERFACE_DATETIME_STATE_MONTH, + INTERFACE_DATETIME_STATE_YEAR, +} interface_datetime_state_t; + +static int current_interface_datetime_state; + +const uint32_t interface_datetime_steps[6] = {3600, 60, 1, 86400, 2629800, 31557600}; + +int interface_datetime_state(void) +{ + return current_interface_datetime_state; } -void interface_datetime_ok(void) +void interface_datetime_set(void) +{ + interface_main_start(); +} + +void interface_datetime_lft(void) +{ + interface_settings_start(); +} + +void interface_datetime_rht(void) +{ + interface_wifi_start(); +} + +void interface_datetime_mid(void) { current_interface_datetime_state++; - if (current_interface_datetime_state > INTERFACE_DATETIME_STATE_SECONDS) + if (current_interface_datetime_state > INTERFACE_DATETIME_STATE_YEAR) { - current_interface_datetime_state = INTERFACE_DATETIME_STATE_YEAR; + current_interface_datetime_state = INTERFACE_DATETIME_STATE_HOUR; } ESP_LOGD(INTERFACE_LOG, "datetime to %d", current_interface_datetime_state); } @@ -49,30 +74,100 @@ void interface_datetime_up(void) struct timeval tv = {0}; tv.tv_sec = curtime; settimeofday(&tv, NULL); + ds3231_set_time(gmtime(&curtime)); ESP_LOGD(INTERFACE_LOG, "increment %d about %u %s", current_interface_datetime_state, interface_datetime_steps[current_interface_datetime_state], ctime(&curtime)); } -void interface_datetime_down(void) +void interface_datetime_dwn(void) { time_t curtime = time(NULL); curtime -= interface_datetime_steps[current_interface_datetime_state]; struct timeval tv = {0}; tv.tv_sec = curtime; settimeofday(&tv, NULL); + ds3231_set_time(gmtime(&curtime)); ESP_LOGD(INTERFACE_LOG, "decrement %d about %u %s", current_interface_datetime_state, interface_datetime_steps[current_interface_datetime_state], ctime(&curtime)); } +void interface_datetime_display(void) +{ + static time_t current_timstamp; + static struct tm *current_tm; + static char time_buffer[9]; + static char date_buffer[32]; + + ssd1306_menu_headline(SSD1306_ADDRESS, interface_get_label_text(&interface_text_headline_time), true, 0); + static char edit_char[3]; + static int edit_line = 3; + int edit_length = 2; + int edit_offset = 0; + + ssd1306_clear_line(SSD1306_ADDRESS, edit_line - 1, false); + ssd1306_clear_line(SSD1306_ADDRESS, edit_line + 1, false); + + time(¤t_timstamp); + current_tm = localtime(¤t_timstamp); + + strftime(time_buffer, 16, INTERFACE_FORMAT_TIME, current_tm); + ssd1306_text_line_column(SSD1306_ADDRESS, time_buffer, 3, 4, false); + + sprintf(date_buffer, "%02d %s %02d", + current_tm->tm_mday, + interface_get_label_text(&interface_texts_month[current_tm->tm_mon]), + current_tm->tm_year - 100); + ssd1306_text_line_column(SSD1306_ADDRESS, date_buffer, 6, 4, false); + + switch (interface_datetime_state()) + { + + case INTERFACE_DATETIME_STATE_YEAR: + memcpy(&edit_char, &date_buffer[7], edit_length); + edit_line = 6; + edit_offset = 11; + break; + case INTERFACE_DATETIME_STATE_DAY: + memcpy(&edit_char, &date_buffer[0], edit_length); + edit_line = 6; + edit_offset = 4; + break; + case INTERFACE_DATETIME_STATE_MONTH: + edit_length = 3; + memcpy(&edit_char, &date_buffer[3], edit_length); + edit_line = 6; + edit_offset = 7; + break; + case INTERFACE_DATETIME_STATE_HOUR: + memcpy(&edit_char, &time_buffer[0], edit_length); + edit_line = 3; + edit_offset = 4; + break; + case INTERFACE_DATETIME_STATE_MINUTE: + memcpy(&edit_char, &time_buffer[3], edit_length); + edit_line = 3; + edit_offset = 7; + break; + case INTERFACE_DATETIME_STATE_SECONDS: + memcpy(&edit_char, &time_buffer[6], edit_length); + edit_line = 3; + edit_offset = 10; + break; + } + + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_up, 8, edit_line - 1, edit_offset * 8 + 4, false); + ssd1306_chars(SSD1306_ADDRESS, edit_char, edit_length, edit_line, edit_offset, true); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_down, 8, edit_line + 1, edit_offset * 8 + 4, false); +} + void interface_datetime_start(void) { - interface_set_state(INTERFACE_STATE_SET_DATETIME); - interface_register_touch_callback(TOUCH_PAD_ESC, &interface_datetime_esc); - interface_register_touch_callback(TOUCH_PAD_OK, &interface_datetime_ok); - interface_register_touch_callback(TOUCH_PAD_UP, &interface_datetime_up); - interface_register_touch_callback(TOUCH_PAD_DOWN, &interface_datetime_down); + current_interface_datetime_state = INTERFACE_DATETIME_STATE_HOUR; + interface_register_button_callback(INTERFACE_BUTTON_LFT, &interface_datetime_lft); + interface_register_button_callback(INTERFACE_BUTTON_RHT, &interface_datetime_rht); + interface_register_button_callback(INTERFACE_BUTTON_MID, &interface_datetime_mid); + interface_register_button_callback(INTERFACE_BUTTON_UP, &interface_datetime_up); + interface_register_button_callback(INTERFACE_BUTTON_DWN, &interface_datetime_dwn); + interface_register_button_callback(INTERFACE_BUTTON_SET, &interface_datetime_set); + interface_register_button_callback(INTERFACE_BUTTON_RST, NULL); + interface_set_display_function(&interface_datetime_display); ESP_LOGD(INTERFACE_LOG, "start datetime interface"); -} - -int interface_datetime_state(void) -{ - return current_interface_datetime_state; } \ No newline at end of file diff --git a/components/interface/interface-input.c b/components/interface/interface-input.c new file mode 100644 index 0000000..0851f9d --- /dev/null +++ b/components/interface/interface-input.c @@ -0,0 +1,318 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/gpio.h" + +#include "ssd1306.h" +#include "ssd1306-gfx.h" + +#include "interface.h" + +static interface_input_callback current_input_callback_rst; +static interface_input_callback current_input_callback_set; +static char current_text[255]; +static uint8_t current_cursor; +static char current_char_set[32]; +static uint8_t current_char_index; +static uint8_t current_limit; +static uint8_t current_max_index; + +static char char_set_uppercase[32]; +static char char_set_lowercase[32]; +static char char_set_numeric[32]; +static char char_set_special1[32]; +static char char_set_special_uppercasecase[32]; +static char char_set_special_lowercase[32]; + +void interface_input_set_char_set(void) +{ + + char *ret; + char cur_char = current_text[current_cursor]; + + if ((ret = strchr(char_set_lowercase, cur_char)) != NULL) + { + strcpy(current_char_set, char_set_lowercase); + current_char_index = strlen(current_char_set) - strlen(ret); + } + else if ((ret = strchr(char_set_numeric, cur_char)) != NULL) + { + strcpy(current_char_set, char_set_numeric); + current_char_index = strlen(current_char_set) - strlen(ret); + } + else if ((ret = strchr(char_set_special1, cur_char)) != NULL) + { + strcpy(current_char_set, char_set_special1); + current_char_index = strlen(current_char_set) - strlen(ret); + } + else if ((ret = strchr(char_set_special_uppercasecase, cur_char)) != NULL) + { + strcpy(current_char_set, char_set_special_uppercasecase); + current_char_index = strlen(current_char_set) - strlen(ret); + } + else if ((ret = strchr(char_set_special_lowercase, cur_char)) != NULL) + { + strcpy(current_char_set, char_set_special_lowercase); + current_char_index = strlen(current_char_set) - strlen(ret); + } + else if ((ret = strchr(char_set_uppercase, cur_char)) != NULL) + { + strcpy(current_char_set, char_set_uppercase); + current_char_index = strlen(current_char_set) - strlen(ret); + } + printf("current_char_set: %d %s\n", strlen(current_char_set), current_char_set); +} + +void interface_input_set(void) +{ + if (current_input_callback_rst != NULL) + { + (*current_input_callback_rst)(current_text, current_cursor); + } +} + +void interface_input_rst(void) +{ + if (current_input_callback_set != NULL) + { + (*current_input_callback_set)(current_text, current_cursor); + } +} + +void interface_input_lft(void) +{ + if (current_cursor > 0) + { + current_cursor--; + interface_input_set_char_set(); + } +} + +void interface_input_rht(void) +{ + if (current_cursor < current_limit) + { + current_cursor++; + if (current_cursor > current_max_index) + { + current_max_index = current_cursor; + strcpy(current_char_set, char_set_uppercase); + + current_char_index = 0; + current_text[current_cursor] = current_char_set[current_char_index]; + } + else + { + interface_input_set_char_set(); + } + } +} + +void interface_input_mid(void) +{ + if (current_char_set[0] == char_set_uppercase[0]) + { + strcpy(current_char_set, char_set_lowercase); + } + else if (current_char_set[0] == char_set_lowercase[0]) + { + strcpy(current_char_set, char_set_numeric); + } + else if (current_char_set[0] == char_set_numeric[0]) + { + strcpy(current_char_set, char_set_special1); + } + else if (current_char_set[0] == char_set_special1[0]) + { + strcpy(current_char_set, char_set_special_uppercasecase); + } + else if (current_char_set[0] == char_set_special_uppercasecase[0]) + { + strcpy(current_char_set, char_set_special_lowercase); + } + else if (current_char_set[0] == char_set_special_lowercase[0]) + { + strcpy(current_char_set, char_set_uppercase); + } + current_char_index = 0; + current_text[current_cursor] = current_char_set[current_char_index]; + printf("current_char_set: %d %s\n", strlen(current_char_set), current_char_set); +} +void interface_input_up(void) +{ + if (current_char_index == 0) + { + current_char_index = strlen(current_char_set) - 1; + } + else + { + current_char_index--; + } + + current_text[current_cursor] = current_char_set[current_char_index]; +} + +void interface_input_dwn(void) +{ + if (current_char_index == strlen(current_char_set) - 1) + { + current_char_index = 0; + } + else + { + current_char_index++; + } + + current_text[current_cursor] = current_char_set[current_char_index]; +} + +void interface_input_display(void) +{ + + // buttons + ssd1306_set_button(SSD1306_ADDRESS, interface_get_label_text(&interface_text_button_cancel), true, false); + ssd1306_set_button(SSD1306_ADDRESS, interface_get_label_text(&interface_text_button_ok), false, true); + + size_t start = 0; + uint8_t display_cursor = current_cursor + 1; + if (current_cursor > 13) + { + start = current_cursor - 13; + display_cursor = 14; + } + + // arrow + if (current_cursor > 0) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_left, 8, 2, 0, false); + } + else + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_clear, 8, 2, 0, false); + } + // bounday + ssd1306_text_line_column(SSD1306_ADDRESS, "______________", 2, 1, false); + // arrow + if (current_cursor < current_limit) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_right, 8, 2, 15 * 8, false); + } + else + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_clear, 8, 2, 15 * 8, false); + } + // text + size_t text_length = strlen(current_text); + if (strlen(current_text) > 14) + { + text_length = 14; + } + size_t length = text_length * 8; + uint8_t *textdata = calloc(length, sizeof(uint8_t)); + for (uint8_t i = 0; i < text_length; i++) + { + memcpy(&textdata[i * 8], ssd1306_gfx_font[(uint8_t)current_text[i + start] - 32], 8); + } + + ssd1306_data(SSD1306_ADDRESS, textdata, length, 2, 8, true); + free(textdata); + + // clear + ssd1306_clear_line(SSD1306_ADDRESS, 0, false); + ssd1306_clear_line(SSD1306_ADDRESS, 1, false); + ssd1306_clear_line(SSD1306_ADDRESS, 3, false); + ssd1306_clear_line(SSD1306_ADDRESS, 4, false); + + uint8_t current_char = (uint8_t)current_char_set[current_char_index] - 32; + uint8_t prev_char = (uint8_t)current_char_set[current_char_index - 1] - 32; + uint8_t next_char = (uint8_t)current_char_set[current_char_index + 1] - 32; + + if (current_char_index == 0) + { + prev_char = (uint8_t)current_char_set[strlen(current_char_set) - 1] - 32; + } + + if (current_char_index == strlen(current_char_set) - 1) + { + next_char = (uint8_t)current_char_set[0] - 32; + } + + // arrow + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_up, 8, 0, display_cursor * 8, false); + // upper char + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_font[prev_char], 8, 1, display_cursor * 8, false); + // sel char + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_font[current_char], 8, 2, display_cursor * 8, false); + // lower char + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_font[next_char], 8, 3, display_cursor * 8, false); + // arrow + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_down, 8, 4, display_cursor * 8, false); +} + +void interface_input_set_text(char *text) +{ + ssd1306_utf8_to_ascii(text, current_text); + current_cursor = strlen(current_text) - 1; + current_max_index = current_cursor; + interface_input_set_char_set(); +} + +void interface_input(interface_input_callback callback_rst, interface_input_callback callback_set, uint8_t limit) +{ + current_input_callback_rst = callback_rst; + current_input_callback_set = callback_set; + current_cursor = 0; + current_limit = limit - 1; + + ssd1306_utf8_to_ascii("ABCDEFGHIJKLMNOPQRSTUVWXYZ", char_set_uppercase); + ssd1306_utf8_to_ascii("abcdefghijklmnopqrstuvwxyz", char_set_lowercase); + ssd1306_utf8_to_ascii(" !\"#$%&'()*+,-,&:;<=>@[\\]^_´`{}", char_set_special1); + ssd1306_utf8_to_ascii("0123456789", char_set_numeric); + ssd1306_utf8_to_ascii("ÄÖÜ", char_set_special_uppercasecase); + ssd1306_utf8_to_ascii("äöü", char_set_special_lowercase); + + strcpy(current_char_set, char_set_uppercase); + + printf("char_set_uppercase: %d %s\n", strlen(char_set_uppercase), char_set_uppercase); + printf("char_set_lowercase: %d %s\n", strlen(char_set_lowercase), char_set_lowercase); + printf("char_set_numeric: %d %s\n", strlen(char_set_numeric), char_set_numeric); + printf("char_set_special1: %d %s\n", strlen(char_set_special1), char_set_special1); + printf("char_set_special_uppercasecase: %d %s\n", strlen(char_set_special_uppercasecase), char_set_special_uppercasecase); + printf("char_set_special_lowercase: %d %s\n", strlen(char_set_special_lowercase), char_set_special_lowercase); + + current_char_index = 0; + current_max_index = 0; + if (current_limit == 0) + { + current_limit = 255; + } + + current_text[current_cursor] = current_char_set[current_char_index]; + + interface_register_button_callback(INTERFACE_BUTTON_RST, &interface_input_rst); + interface_register_button_callback(INTERFACE_BUTTON_SET, &interface_input_set); + interface_register_button_callback(INTERFACE_BUTTON_LFT, &interface_input_lft); + interface_register_button_callback(INTERFACE_BUTTON_RHT, &interface_input_rht); + interface_register_button_callback(INTERFACE_BUTTON_MID, &interface_input_mid); + interface_register_button_callback(INTERFACE_BUTTON_UP, &interface_input_up); + interface_register_button_callback(INTERFACE_BUTTON_DWN, &interface_input_dwn); + + interface_set_display_function(&interface_input_display); + ESP_LOGD(INTERFACE_LOG, "start input interface"); +} diff --git a/components/interface/interface-label.c b/components/interface/interface-label.c new file mode 100644 index 0000000..622e466 --- /dev/null +++ b/components/interface/interface-label.c @@ -0,0 +1,139 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/** + * @file + * + * @brief texts for intefaces + * + */ +#include +#include "interface.h" + +static interface_locale_t current_locale = EN; + +void interface_init_label(void) +{ + // EN + interface_text_button_cancel.text[EN] = "CANCEL"; + interface_text_button_ok.text[EN] = "OK"; + interface_text_button_menu.text[EN] = "MENU"; + interface_text_button_report.text[EN] = "REPORT"; + + interface_text_headline_tan.text[EN] = "ENTER TAN"; + interface_text_headline_wifi.text[EN] = "WIFI"; + interface_text_headline_time.text[EN] = "TIME/DATE"; + interface_text_headline_data.text[EN] = "DEL DATA"; + interface_text_headline_settings.text[EN] = "SETTING"; + interface_text_headline_debug.text[EN] = "DEBUG"; + + interface_text_settings_locale.text[EN] = "Language:"; + interface_text_settings_locales[EN].text[EN] = "EN"; + interface_text_settings_locales[DE].text[EN] = "DE"; + + interface_text_settings_timezone.text[EN] = "Timezone:"; + + interface_text_wifi_scanning.text[EN] = "Scanning..."; + + interface_text_data_del[0].text[EN] = "DEL TEK"; + interface_text_data_del[1].text[EN] = "DEL Exp Info"; + interface_text_data_del[2].text[EN] = "DEL Tmp RPI"; + interface_text_data_del[3].text[EN] = "DEL RPI"; + interface_text_data_del[4].text[EN] = "DEL Lst Upd."; + interface_text_data_del[5].text[EN] = "DEL All Data"; + + interface_texts_weekday[0].text[EN] = "Sun"; + interface_texts_weekday[1].text[EN] = "Mon"; + interface_texts_weekday[2].text[EN] = "Tue"; + interface_texts_weekday[3].text[EN] = "Wed"; + interface_texts_weekday[4].text[EN] = "Thu"; + interface_texts_weekday[5].text[EN] = "Fri"; + interface_texts_weekday[6].text[EN] = "Sat"; + + interface_texts_month[0].text[EN] = "Jan"; + interface_texts_month[1].text[EN] = "Feb"; + interface_texts_month[2].text[EN] = "Mar"; + interface_texts_month[3].text[EN] = "Apr"; + interface_texts_month[4].text[EN] = "May"; + interface_texts_month[5].text[EN] = "Jun"; + interface_texts_month[6].text[EN] = "Jul"; + interface_texts_month[7].text[EN] = "Aug"; + interface_texts_month[8].text[EN] = "Sep"; + interface_texts_month[9].text[EN] = "Oct"; + interface_texts_month[10].text[EN] = "Nov"; + interface_texts_month[11].text[EN] = "Dec"; + + // DE + interface_text_button_cancel.text[DE] = "ZURÜCK"; + interface_text_button_ok.text[DE] = "OK"; + interface_text_button_menu.text[DE] = "MENU"; + interface_text_button_report.text[DE] = "MELDEN"; + + interface_text_headline_tan.text[DE] = "TAN EING."; + interface_text_headline_wifi.text[DE] = "WLAN"; + interface_text_headline_time.text[DE] = "ZEIT/DATUM"; + interface_text_headline_data.text[DE] = "DATEN ENTF"; + interface_text_headline_settings.text[DE] = "EINSTEL."; + interface_text_headline_debug.text[DE] = "DEBUG"; + + interface_text_settings_locale.text[DE] = "Sprache:"; + interface_text_settings_locales[EN].text[DE] = "EN"; + interface_text_settings_locales[DE].text[DE] = "DE"; + + interface_text_settings_timezone.text[DE] = "Zeitzone:"; + + interface_text_wifi_scanning.text[DE] = "Scannen..."; + + interface_text_data_del[0].text[DE] = "ENTF TEK"; + interface_text_data_del[1].text[DE] = "ENTF Exp Info"; + interface_text_data_del[2].text[DE] = "ENTF Tmp RPI"; + interface_text_data_del[3].text[DE] = "ENTF RPI"; + interface_text_data_del[4].text[DE] = "ENTF letz. Up"; + interface_text_data_del[5].text[DE] = "ENTF Daten"; + + interface_texts_weekday[0].text[DE] = "So."; + interface_texts_weekday[1].text[DE] = "Mo."; + interface_texts_weekday[2].text[DE] = "Di."; + interface_texts_weekday[3].text[DE] = "Mi."; + interface_texts_weekday[4].text[DE] = "Do."; + interface_texts_weekday[5].text[DE] = "Fr."; + interface_texts_weekday[6].text[DE] = "Sa."; + + interface_texts_month[0].text[DE] = "Jan"; + interface_texts_month[1].text[DE] = "Feb"; + interface_texts_month[2].text[DE] = "Mär"; + interface_texts_month[3].text[DE] = "Apr"; + interface_texts_month[4].text[DE] = "Mai"; + interface_texts_month[5].text[DE] = "Jun"; + interface_texts_month[6].text[DE] = "Jul"; + interface_texts_month[7].text[DE] = "Aug"; + interface_texts_month[8].text[DE] = "Sep"; + interface_texts_month[9].text[DE] = "Okt"; + interface_texts_month[10].text[DE] = "Nov"; + interface_texts_month[11].text[DE] = "Dez"; +} + +interface_locale_t interface_get_locale(void) +{ + return current_locale; +} + +void interface_set_locale(interface_locale_t locale) +{ + current_locale = locale; +} + +char *interface_get_label_text(interface_label_t *label) +{ + return label->text[current_locale]; +} \ No newline at end of file diff --git a/components/interface/interface-main.c b/components/interface/interface-main.c new file mode 100644 index 0000000..8a1904b --- /dev/null +++ b/components/interface/interface-main.c @@ -0,0 +1,143 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include "esp_log.h" +#include "driver/gpio.h" + +#include "ds3231.h" +#include "ssd1306.h" +#include "ssd1306-gfx.h" + +#include "wifi-controller.h" +#include "ena-storage.h" +#include "ena-exposure.h" + +#include "interface.h" + +static time_t current_timstamp; +static struct tm *current_tm; +static char time_buffer[32]; +static char text_buffer[32]; +static bool wifi_connected; + +void interface_main_set(void) +{ + interface_datetime_start(); +} + +void interface_main_rst(void) +{ + interface_report_start(); +} + +void interface_main_display(void) +{ + if (wifi_connected) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_wifi, 8, 0, 0, false); + } + else + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_cross, 8, 0, 0, false); + } + + time(¤t_timstamp); + current_tm = localtime(¤t_timstamp); + + // curent date + sprintf(text_buffer, "%s %s %d", + interface_get_label_text(&interface_texts_weekday[current_tm->tm_wday]), + interface_get_label_text(&interface_texts_month[current_tm->tm_mon]), + current_tm->tm_mday); + ssd1306_text_line_column(SSD1306_ADDRESS, text_buffer, 0, 16 - strlen(text_buffer), false); + + // current time + strftime(time_buffer, 16, INTERFACE_FORMAT_TIME, current_tm); + ssd1306_text_line_column(SSD1306_ADDRESS, time_buffer, 1, 16 - strlen(time_buffer), false); +} + +void interface_main_start(void) +{ + + interface_register_button_callback(INTERFACE_BUTTON_RST, &interface_main_rst); + interface_register_button_callback(INTERFACE_BUTTON_SET, &interface_main_set); + interface_register_button_callback(INTERFACE_BUTTON_LFT, NULL); + interface_register_button_callback(INTERFACE_BUTTON_RHT, NULL); + interface_register_button_callback(INTERFACE_BUTTON_MID, NULL); + interface_register_button_callback(INTERFACE_BUTTON_UP, NULL); + interface_register_button_callback(INTERFACE_BUTTON_DWN, NULL); + + if (wifi_controller_connection() != NULL) + { + wifi_connected = true; + } + else + { + wifi_connected = false; + } + + interface_set_display_function(&interface_main_display); + + ena_exposure_summary_t *current_exposure_summary = ena_exposure_current_summary(); + + time(¤t_timstamp); + uint32_t last_update = ena_storage_read_last_exposure_date(); + + // status unknown if no update or last update older than two days + if (last_update == 0 || ((current_timstamp - last_update) / (60 * 60 * 24)) > 2) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_question[0], 24, 0, 12, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_question[1], 24, 1, 12, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_question[2], 24, 2, 12, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_question[3], 24, 3, 12, false); + } + else if (current_exposure_summary->max_risk_score < 100) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_smile[0], 24, 0, 12, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_smile[1], 24, 1, 12, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_smile[2], 24, 2, 12, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_smile[3], 24, 3, 12, false); + } + else + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_sad[0], 24, 0, 12, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_sad[1], 24, 1, 12, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_sad[2], 24, 2, 12, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_sad[3], 24, 3, 12, false); + } + // clock icon + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_clock, 8, 4, 8, false); + + // last update + struct tm *last_update_tm = localtime((time_t*) &last_update); + + sprintf(time_buffer, "%02d %s %02d:%02d", + last_update_tm->tm_mday, + interface_get_label_text(&interface_texts_month[last_update_tm->tm_mon]), + last_update_tm->tm_hour, + last_update_tm->tm_min); + + if (last_update != 0) + { + ssd1306_text_line_column(SSD1306_ADDRESS, time_buffer, 4, 3, false); + } + + // buttons + ssd1306_set_button(SSD1306_ADDRESS, interface_get_label_text(&interface_text_button_menu), true, false); + ssd1306_set_button(SSD1306_ADDRESS, interface_get_label_text(&interface_text_button_report), false, true); + + ESP_LOGD(INTERFACE_LOG, "start main interface"); +} diff --git a/components/interface/interface-menu.c b/components/interface/interface-menu.c deleted file mode 100644 index 28a0552..0000000 --- a/components/interface/interface-menu.c +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include - -#include "esp_log.h" -#include "driver/touch_pad.h" -#include "interface.h" -#include "interface-datetime.h" -#include "interface-status.h" - -#include "interface-menu.h" - -static int current_interface_menu_state = INTERFACE_MENU_STATE_IDLE; - -void interface_menu_ok(void) -{ - if (current_interface_menu_state == INTERFACE_MENU_STATE_SELECT_TIME) - { - interface_datetime_start(); - } - else if (current_interface_menu_state == INTERFACE_MENU_STATE_SELECT_STATUS) - { - interface_status_start(); - } - else if (current_interface_menu_state == INTERFACE_MENU_STATE_SELECT_DEBUG) - { - } - else if (current_interface_menu_state == INTERFACE_MENU_STATE_IDLE) - { - if (interface_get_state() == INTERFACE_STATE_MENU) - { - interface_set_state(INTERFACE_STATE_IDLE); - interface_register_touch_callback(TOUCH_PAD_UP, NULL); - interface_register_touch_callback(TOUCH_PAD_DOWN, NULL); - } - else - { - interface_menu_start(); - } - } -} - -void interface_menu_up(void) -{ - current_interface_menu_state--; - if (current_interface_menu_state < INTERFACE_MENU_STATE_IDLE) - { - current_interface_menu_state = INTERFACE_MENU_STATE_SELECT_STATUS; - } - ESP_LOGD(INTERFACE_LOG, "menu up to %d", current_interface_menu_state); -} - -void interface_menu_down(void) -{ - current_interface_menu_state++; - if (current_interface_menu_state > INTERFACE_MENU_STATE_SELECT_STATUS) - { - current_interface_menu_state = INTERFACE_MENU_STATE_IDLE; - } - ESP_LOGD(INTERFACE_LOG, "menu down to %d", current_interface_menu_state); -} - -void interface_menu_start(void) -{ - interface_set_state(INTERFACE_STATE_MENU); - interface_register_touch_callback(TOUCH_PAD_ESC, NULL); - interface_register_touch_callback(TOUCH_PAD_OK, &interface_menu_ok); - interface_register_touch_callback(TOUCH_PAD_UP, &interface_menu_up); - interface_register_touch_callback(TOUCH_PAD_DOWN, &interface_menu_down); - - ESP_LOGD(INTERFACE_LOG, "start menu interface"); -} - -int interface_menu_get_state(void) -{ - return current_interface_menu_state; -} \ No newline at end of file diff --git a/components/interface/interface-report.c b/components/interface/interface-report.c new file mode 100644 index 0000000..9b71e63 --- /dev/null +++ b/components/interface/interface-report.c @@ -0,0 +1,177 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/gpio.h" + +#include "ssd1306.h" +#include "ssd1306-gfx.h" + +#include "wifi-controller.h" + +#include "interface.h" + +static int interface_report_tan[10] = {5, 5, 5, 5, 5, 5, 5, 5, 5, 5}; +static int interface_report_tan_index = 0; + +void interface_report_set(void) +{ + interface_main_start(); +} + +void interface_report_rst(void) +{ + // TODO: REPORT here, check tan etc. +} + +void interface_report_lft(void) +{ + if (interface_report_tan_index > 0) + { + interface_report_tan_index--; + } + ESP_LOGD(INTERFACE_LOG, "tan index %d", interface_report_tan_index); +} + +void interface_report_rht(void) +{ + if (interface_report_tan_index < 9) + { + interface_report_tan_index++; + } + ESP_LOGD(INTERFACE_LOG, "tan index %d", interface_report_tan_index); +} + +void interface_report_up(void) +{ + if (interface_report_tan[interface_report_tan_index] > 0) + { + interface_report_tan[interface_report_tan_index]--; + } + else + { + interface_report_tan[interface_report_tan_index] = 9; + } +} + +void interface_report_dwn(void) +{ + if (interface_report_tan[interface_report_tan_index] < 9) + { + interface_report_tan[interface_report_tan_index]++; + } + else + { + interface_report_tan[interface_report_tan_index] = 0; + } +} + +void interface_report_display(void) +{ + ssd1306_menu_headline(SSD1306_ADDRESS, interface_get_label_text(&interface_text_headline_tan), false, 0); + + // buttons + ssd1306_set_button(SSD1306_ADDRESS, interface_get_label_text(&interface_text_button_cancel), true, false); + ssd1306_set_button(SSD1306_ADDRESS, interface_get_label_text(&interface_text_button_ok), false, true); + + static char tan_buffer[10] = {0}; + + if (interface_report_tan_index > 0) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_left, 8, 3, 8, false); + } + else + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_clear, 8, 3, 8, false); + } + + if (interface_report_tan_index < 9) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_right, 8, 3, 112, false); + } + else + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_clear, 8, 3, 112, false); + } + + for (int i = 0; i < interface_report_tan_index + 1; i++) + { + sprintf(&tan_buffer[i], "%d", interface_report_tan[i]); + } + + ssd1306_clear_line(SSD1306_ADDRESS, 2, false); + ssd1306_clear_line(SSD1306_ADDRESS, 4, false); + + ssd1306_text_line_column(SSD1306_ADDRESS, "-", 3, 5, false); + ssd1306_text_line_column(SSD1306_ADDRESS, "-", 3, 9, false); + ssd1306_text_line_column(SSD1306_ADDRESS, "___", 3, 2, false); + ssd1306_text_line_column(SSD1306_ADDRESS, "___", 3, 6, false); + ssd1306_text_line_column(SSD1306_ADDRESS, "____", 3, 10, false); + + int offset = 2; + for (int i = 0; i < 3; i++) + { + if (i < interface_report_tan_index) + { + ssd1306_chars(SSD1306_ADDRESS, &tan_buffer[i], 1, 3, i + offset, true); + } + } + + if (interface_report_tan_index > 2) + { + offset = 3; + for (int i = 3; i < 6; i++) + { + if (i < interface_report_tan_index) + { + ssd1306_chars(SSD1306_ADDRESS, &tan_buffer[i], 1, 3, i + offset, true); + } + } + } + + if (interface_report_tan_index > 5) + { + offset = 4; + for (int i = 6; i < 10; i++) + { + if (i < interface_report_tan_index) + { + ssd1306_chars(SSD1306_ADDRESS, &tan_buffer[i], 1, 3, i + offset, true); + } + } + } + + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_up, 8, 2, interface_report_tan_index * 8 + offset * 8, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_down, 8, 4, interface_report_tan_index * 8 + offset * 8, false); + + ssd1306_chars(SSD1306_ADDRESS, &tan_buffer[interface_report_tan_index], 1, 3, interface_report_tan_index + offset, false); +} + +void interface_report_start(void) +{ + interface_register_button_callback(INTERFACE_BUTTON_RST, &interface_report_rst); + interface_register_button_callback(INTERFACE_BUTTON_SET, &interface_report_set); + interface_register_button_callback(INTERFACE_BUTTON_LFT, &interface_report_lft); + interface_register_button_callback(INTERFACE_BUTTON_RHT, &interface_report_rht); + interface_register_button_callback(INTERFACE_BUTTON_UP, &interface_report_up); + interface_register_button_callback(INTERFACE_BUTTON_DWN, &interface_report_dwn); + interface_register_button_callback(INTERFACE_BUTTON_MID, NULL); + interface_set_display_function(&interface_report_display); + + ESP_LOGD(INTERFACE_LOG, "start report interface"); +} diff --git a/components/interface/interface-settings.c b/components/interface/interface-settings.c new file mode 100644 index 0000000..f6f0428 --- /dev/null +++ b/components/interface/interface-settings.c @@ -0,0 +1,147 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/gpio.h" + +#include "ssd1306.h" +#include "ssd1306-gfx.h" + +#include "ena-storage.h" + +#include "interface.h" + +typedef enum +{ + INTERFACE_SETTINGS_LOCALE = 0, + INTERFACE_SETTINGS_TIMEZONE, +} interface_settings_state_t; + +static int current_interface_settings_state; + +void interface_settings_set(void) +{ + interface_main_start(); +} + +void interface_settings_rst(void) +{ +} + +void interface_settings_lft(void) +{ + interface_data_start(); +} + +void interface_settings_rht(void) +{ + interface_datetime_start(); +} + +void interface_settings_mid(void) +{ + + current_interface_settings_state++; + if (current_interface_settings_state > INTERFACE_SETTINGS_TIMEZONE) + { + current_interface_settings_state = INTERFACE_SETTINGS_LOCALE; + } + + ssd1306_clear_line(SSD1306_ADDRESS, 2, false); + ssd1306_clear_line(SSD1306_ADDRESS, 3, false); + ssd1306_clear_line(SSD1306_ADDRESS, 4, false); + ssd1306_clear_line(SSD1306_ADDRESS, 5, false); + ssd1306_clear_line(SSD1306_ADDRESS, 6, false); + ssd1306_clear_line(SSD1306_ADDRESS, 7, false); +} + +void interface_settings_up(void) +{ + if (current_interface_settings_state == INTERFACE_SETTINGS_LOCALE) + { + if (interface_get_locale() == 0) + { + interface_set_locale(INTERFACE_NUM_LOCALE - 1); + } + else + { + interface_set_locale(interface_get_locale() - 1); + } + } +} + +void interface_settings_dwn(void) +{ + + if (current_interface_settings_state == INTERFACE_SETTINGS_LOCALE) + { + if (interface_get_locale() + 1 == INTERFACE_NUM_LOCALE) + { + interface_set_locale(0); + } + else + { + interface_set_locale(interface_get_locale() + 1); + } + } +} + +void interface_settings_display(void) +{ + ssd1306_menu_headline(SSD1306_ADDRESS, interface_get_label_text(&interface_text_headline_settings), true, 0); + + ssd1306_text_line_column(SSD1306_ADDRESS, interface_get_label_text(&interface_text_settings_locale), 3, 1, false); + + ssd1306_text_line_column(SSD1306_ADDRESS, interface_get_label_text(&interface_text_settings_timezone), 6, 1, false); + + if (current_interface_settings_state == INTERFACE_SETTINGS_LOCALE) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_up, 8, 2, 12 * 8 + 4, false); + ssd1306_text_line_column(SSD1306_ADDRESS, + interface_get_label_text(&interface_text_settings_locales[interface_get_locale()]), 3, 12, true); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_down, 8, 4, 12 * 8 + 4, false); + } + else + { + ssd1306_text_line_column(SSD1306_ADDRESS, + interface_get_label_text(&interface_text_settings_locales[interface_get_locale()]), 3, 12, true); + } + + if (current_interface_settings_state == INTERFACE_SETTINGS_TIMEZONE) + { + + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_up, 8, 5, 12 * 8 + 4, false); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_down, 8, 7, 12 * 8 + 4, false); + } +} + +void interface_settings_start(void) +{ + current_interface_settings_state = INTERFACE_SETTINGS_LOCALE; + + interface_register_button_callback(INTERFACE_BUTTON_RST, &interface_settings_rst); + interface_register_button_callback(INTERFACE_BUTTON_SET, &interface_settings_set); + interface_register_button_callback(INTERFACE_BUTTON_LFT, &interface_settings_lft); + interface_register_button_callback(INTERFACE_BUTTON_RHT, &interface_settings_rht); + interface_register_button_callback(INTERFACE_BUTTON_MID, &interface_settings_mid); + interface_register_button_callback(INTERFACE_BUTTON_UP, &interface_settings_up); + interface_register_button_callback(INTERFACE_BUTTON_DWN, &interface_settings_dwn); + interface_set_display_function(&interface_settings_display); + + ESP_LOGD(INTERFACE_LOG, "start settings interface"); +} diff --git a/components/interface/interface-status.c b/components/interface/interface-status.c deleted file mode 100644 index b275607..0000000 --- a/components/interface/interface-status.c +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include - -#include "esp_log.h" -#include "driver/touch_pad.h" -#include "interface.h" -#include "interface-menu.h" - -#include "interface-status.h" - -void interface_status_esc(void) -{ - interface_menu_start(); -} - -void interface_status_start(void) -{ - interface_set_state(INTERFACE_STATE_STATUS); - interface_register_touch_callback(TOUCH_PAD_ESC, &interface_status_esc); - interface_register_touch_callback(TOUCH_PAD_OK, NULL); - interface_register_touch_callback(TOUCH_PAD_UP, NULL); - interface_register_touch_callback(TOUCH_PAD_DOWN, NULL); - - ESP_LOGD(INTERFACE_LOG, "start status interface"); -} diff --git a/components/interface/interface-wifi.c b/components/interface/interface-wifi.c new file mode 100644 index 0000000..5b941e8 --- /dev/null +++ b/components/interface/interface-wifi.c @@ -0,0 +1,183 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/gpio.h" + +#include "ssd1306.h" +#include "ssd1306-gfx.h" +#include "wifi-controller.h" + +#include "interface.h" + +#define APS_TO_DISPLAY 3 + +wifi_ap_record_t ap_info[10]; +uint16_t ap_count = 0; +int ap_index = 0; +int ap_selected = 0; + +static wifi_config_t current_wifi_config; + +void interface_wifi_input_rst(char *text, uint8_t cursor) +{ + interface_wifi_start(); +} + +void interface_wifi_input_set(char *text, uint8_t cursor) +{ + memcpy(current_wifi_config.sta.password, text, cursor + 1); + + ESP_LOGD(INTERFACE_LOG, "ssid: '%s' password '%s'", current_wifi_config.sta.ssid, current_wifi_config.sta.password); + + if (wifi_controller_connect(current_wifi_config) == ESP_OK) + { + interface_main_start(); + } + else + { + // what happens here? + interface_wifi_start(); + } +} + +void interface_wifi_set(void) +{ + interface_main_start(); +} + +void interface_wifi_lft(void) +{ + interface_datetime_start(); +} + +void interface_wifi_rht(void) +{ + interface_data_start(); +} +void interface_wifi_mid(void) +{ + memset(¤t_wifi_config, 0, sizeof(wifi_config_t)); + memcpy(current_wifi_config.sta.ssid, ap_info[ap_selected].ssid, strlen((char *)ap_info[ap_selected].ssid)); + interface_input(&interface_wifi_input_rst, &interface_wifi_input_set, 64); +} +void interface_wifi_up(void) +{ + ap_selected--; + if (ap_selected < 0) + { + ap_selected = ap_count - 1; + if (ap_count - APS_TO_DISPLAY < 0) + { + ap_index = 0; + } + else + { + ap_index = ap_count - APS_TO_DISPLAY; + } + } + else if (ap_selected < ap_index) + { + ap_index--; + } +} + +void interface_wifi_dwn(void) +{ + ap_selected++; + if (ap_selected >= ap_count) + { + ap_selected = 0; + ap_index = 0; + } + else if (ap_selected >= (ap_index + APS_TO_DISPLAY)) + { + ap_index++; + } +} + +void interface_wifi_display(void) +{ + ssd1306_menu_headline(SSD1306_ADDRESS, interface_get_label_text(&interface_text_headline_wifi), true, 0); + + if (ap_count > 0) + { + ssd1306_clear_line(SSD1306_ADDRESS, 2, false); + ssd1306_clear_line(SSD1306_ADDRESS, 4, false); + ssd1306_clear_line(SSD1306_ADDRESS, 6, false); + for (int i = 0; i < 3; i++) + { + int index = i + ap_index; + if (index < ap_count) + { + if (index == ap_selected) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_arrow_right, 8, i * 2 + 2, 8, false); + } + + if (sizeof(ap_info[i].ssid) > 0) + { + ssd1306_text_line_column(SSD1306_ADDRESS, (char *)ap_info[index].ssid, i * 2 + 2, 2, false); + } + else + { + ssd1306_text_line_column(SSD1306_ADDRESS, " / ", i * 2 + 2, 2, false); + } + + if (ap_info[index].rssi >= -67) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_wifi, 8, i * 2 + 2, 112, false); + } + else if (ap_info[index].rssi >= -80) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_wifi_low, 8, i * 2 + 2, 112, false); + } + else if (ap_info[index].rssi >= -90) + { + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_wifi_lowest, 8, i * 2 + 2, 112, false); + } + } + } + } + else + { + ssd1306_text_line_column(SSD1306_ADDRESS, interface_get_label_text(&interface_text_wifi_scanning), 4, 1, false); + } +} + +void interface_wifi_start(void) +{ + interface_register_button_callback(INTERFACE_BUTTON_SET, &interface_wifi_set); + interface_register_button_callback(INTERFACE_BUTTON_LFT, &interface_wifi_lft); + interface_register_button_callback(INTERFACE_BUTTON_RHT, &interface_wifi_rht); + interface_register_button_callback(INTERFACE_BUTTON_MID, &interface_wifi_mid); + interface_register_button_callback(INTERFACE_BUTTON_UP, &interface_wifi_up); + interface_register_button_callback(INTERFACE_BUTTON_DWN, &interface_wifi_dwn); + interface_register_button_callback(INTERFACE_BUTTON_RST, NULL); + + interface_set_display_function(&interface_wifi_display); + + ESP_LOGD(INTERFACE_LOG, "start wifi interface and scanning"); + + memset(ap_info, 0, sizeof(ap_info)); + ap_count = 0; + ap_index = 0; + ap_selected = 0; + + wifi_controller_scan(ap_info, &ap_count); +} diff --git a/components/interface/interface.c b/components/interface/interface.c index d702b02..761402e 100644 --- a/components/interface/interface.c +++ b/components/interface/interface.c @@ -14,85 +14,101 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "freertos/queue.h" -#include "driver/touch_pad.h" +#include "driver/gpio.h" #include "esp_log.h" +#include "ssd1306.h" +#include "ssd1306-gfx.h" + #include "interface.h" -static int current_interface_state = INTERFACE_STATE_IDLE; +static int old_button_states[GPIO_PIN_COUNT]; +static int button_states[GPIO_PIN_COUNT]; +static interface_button_callback button_callbacks[GPIO_PIN_COUNT]; +static interface_display_function current_display_function; -static int touch_mapping[TOUCH_PAD_COUNT] = {0}; -static bool touch_status[TOUCH_PAD_COUNT] = {0}; -static interface_touch_callback touch_callbacks[TOUCH_PAD_MAX]; - -void interface_register_touch_callback(int touch_pad, interface_touch_callback callback) +void interface_register_button_callback(int button_gpio, interface_button_callback callback) { - touch_callbacks[touch_pad] = callback; + button_callbacks[button_gpio] = callback; } -void interface_run(void *pvParameter) +void interface_set_display_function(interface_display_function display_function) +{ + ssd1306_clear(SSD1306_ADDRESS); + current_display_function = display_function; +} + +void interface_input_check(uint8_t gpio) +{ + button_states[gpio] = gpio_get_level(gpio); + + if (old_button_states[gpio] != 0 && old_button_states[gpio] != 1) + { + old_button_states[gpio] = 1; + } + + if (button_states[gpio] == 0 && button_states[gpio] != old_button_states[gpio] && button_callbacks[gpio] != NULL) + { + ssd1306_clear(SSD1306_ADDRESS); + (*button_callbacks[gpio])(); + } + + old_button_states[gpio] = button_states[gpio]; +} + +void interface_input_task(void *pvParameter) { - static uint16_t touch_value; - static uint16_t touch_thresh; - static bool touch_status_current[TOUCH_PAD_MAX] = {0}; while (1) { - for (int i = 0; i < TOUCH_PAD_COUNT; i++) + interface_input_check(INTERFACE_BUTTON_RST); + interface_input_check(INTERFACE_BUTTON_SET); + interface_input_check(INTERFACE_BUTTON_LFT); + interface_input_check(INTERFACE_BUTTON_RHT); + interface_input_check(INTERFACE_BUTTON_MID); + interface_input_check(INTERFACE_BUTTON_UP); + interface_input_check(INTERFACE_BUTTON_DWN); + vTaskDelay(20 / portTICK_PERIOD_MS); + } +} + +void interface_display_task(void *pvParameter) +{ + while (1) + { + if (current_display_function != NULL) { - ESP_ERROR_CHECK_WITHOUT_ABORT(touch_pad_read_filtered(touch_mapping[i], &touch_value)); - ESP_ERROR_CHECK_WITHOUT_ABORT(touch_pad_get_thresh(touch_mapping[i], &touch_thresh)); - touch_status_current[i] = touch_value < touch_thresh; - - if (!touch_status[i] & touch_status_current[i]) - { - ESP_LOGD(INTERFACE_LOG, "touch %u at %d (thresh %u)", touch_value, touch_mapping[i], touch_thresh); - if (touch_callbacks[touch_mapping[i]] != NULL) - { - (*touch_callbacks[touch_mapping[i]])(); - } - } - touch_status[i] = touch_status_current[i]; + (*current_display_function)(); } - - vTaskDelay(10 / portTICK_PERIOD_MS); + vTaskDelay(500 / portTICK_PERIOD_MS); } } void interface_start(void) { - ESP_ERROR_CHECK(touch_pad_init()); - ESP_ERROR_CHECK(touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V)); - ESP_ERROR_CHECK(touch_pad_set_trigger_mode(TOUCH_TRIGGER_BELOW)); - touch_mapping[0] = TOUCH_PAD_ESC; - touch_mapping[1] = TOUCH_PAD_OK; - touch_mapping[2] = TOUCH_PAD_UP; - touch_mapping[3] = TOUCH_PAD_DOWN; + gpio_config_t io_conf; - for (int i = 0; i < TOUCH_PAD_COUNT; i++) + io_conf.pin_bit_mask = (1ULL << INTERFACE_BUTTON_RST) | (1ULL << INTERFACE_BUTTON_SET) | + (1ULL << INTERFACE_BUTTON_MID) | (1ULL << INTERFACE_BUTTON_RHT) | + (1ULL << INTERFACE_BUTTON_LFT) | (1ULL << INTERFACE_BUTTON_DWN) | + (1ULL << INTERFACE_BUTTON_UP); + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + gpio_config(&io_conf); + + xTaskCreate(&interface_input_task, "interface_input_task", 4096, NULL, 5, NULL); + xTaskCreate(&interface_display_task, "interface_display_task", 4096, NULL, 5, NULL); + + // init label + interface_init_label(); + + ssd1306_start(SSD1306_ADDRESS); + ssd1306_clear(SSD1306_ADDRESS); + + for (int i = 0; i < 8; i++) { - ESP_ERROR_CHECK(touch_pad_config(touch_mapping[i], 0)); + ssd1306_data(SSD1306_ADDRESS, ssd1306_gfx_logo[i], 64, i, 32, false); } - - ESP_ERROR_CHECK(touch_pad_filter_start(TOUCHPAD_FILTER_TOUCH_PERIOD)); - uint16_t touch_value; - for (int i = 0; i < TOUCH_PAD_COUNT; i++) - { - ESP_ERROR_CHECK(touch_pad_read_filtered(touch_mapping[i], &touch_value)); - ESP_ERROR_CHECK(touch_pad_set_thresh(touch_mapping[i], touch_value * 2 / 3)); - ESP_LOGD(INTERFACE_LOG, "calibrate %u at %u (thresh %u)", touch_mapping[i], touch_value, (touch_value * 2 / 3)); - } - - xTaskCreate(&interface_run, "interface_run", 4096, NULL, 5, NULL); -} - -int interface_get_state(void) -{ - return current_interface_state; -} - -void interface_set_state(interface_state_t state) -{ - current_interface_state = state; } diff --git a/components/interface/interface.h b/components/interface/interface.h new file mode 100644 index 0000000..67a0e83 --- /dev/null +++ b/components/interface/interface.h @@ -0,0 +1,193 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/** + * @file + * + * @brief interface functionality via input buttons for control and setup + * + */ +#ifndef _interface_H_ +#define _interface_H_ + +#include "driver/gpio.h" + +#define INTERFACE_LOG "INTERFACE" // TAG for Logging + +#define INTERFACE_BUTTON_RST GPIO_NUM_32 +#define INTERFACE_BUTTON_SET GPIO_NUM_33 +#define INTERFACE_BUTTON_MID GPIO_NUM_25 +#define INTERFACE_BUTTON_RHT GPIO_NUM_26 +#define INTERFACE_BUTTON_LFT GPIO_NUM_27 +#define INTERFACE_BUTTON_DWN GPIO_NUM_14 +#define INTERFACE_BUTTON_UP GPIO_NUM_12 + +#define INTERFACE_FORMAT_TIME "%X" + +#define INTERFACE_NUM_LOCALE 2 + +/** + * @brief available locales + */ +typedef enum +{ + EN = 0, + DE, +} interface_locale_t; + +typedef struct +{ + + char *text[INTERFACE_NUM_LOCALE]; + +} interface_label_t; + +// label variables +interface_label_t interface_text_button_cancel; +interface_label_t interface_text_button_ok; +interface_label_t interface_text_button_menu; +interface_label_t interface_text_button_report; + +interface_label_t interface_text_headline_tan; +interface_label_t interface_text_headline_wifi; +interface_label_t interface_text_headline_time; +interface_label_t interface_text_headline_data; +interface_label_t interface_text_headline_settings; +interface_label_t interface_text_headline_debug; + +interface_label_t interface_text_settings_locale; +interface_label_t interface_text_settings_locales[INTERFACE_NUM_LOCALE]; +interface_label_t interface_text_settings_timezone; + +interface_label_t interface_text_wifi_scanning; + +interface_label_t interface_text_data_del[5]; + +interface_label_t interface_texts_weekday[7]; + +interface_label_t interface_texts_month[12]; + +/** + * @brief callback function for button press + */ +typedef void (*interface_button_callback)(void); + +/** + * @brief current display function + */ +typedef void (*interface_display_function)(void); + +/** + * @brief callback function for text_input + * + * @param[in] text the text from input + * @param[in] cursor current cursor position + */ +typedef void (*interface_input_callback)(char *text, uint8_t cursor); + +/** + * @brief init label + */ +void interface_init_label(void); + +/** + * @brief get text from label for set locale + * + * @param[in] label the label to get the text from + */ +char *interface_get_label_text(interface_label_t *label); + +/** + * @brief set locale for interface + * + * @return + * interface_locale_t current locale + */ +interface_locale_t interface_get_locale(void); + +/** + * @brief set locale for interface + * + * @param[in] locale the locale to set + */ +void interface_set_locale(interface_locale_t locale); + +/** + * @brief register a callback function for button event + * + * @param[in] button_gpio id of the button to listen to + * @param[in] callback callback function + */ +void interface_register_button_callback(int button_gpio, interface_button_callback callback); + +/** + * @brief set the display function + * + * @param[in] display_function display function + */ +void interface_set_display_function(interface_display_function display_function); + +/** + * @brief start interface logic + * + * This will initialize the controls and start a task to listen to button inputs and calling the callbacks. + */ +void interface_start(void); + +/** + * @brief start delete data interface + */ +void interface_data_start(void); + +/** + * @brief start datetime interface + */ +void interface_datetime_start(void); + +/** + * @brief start main interface + */ +void interface_main_start(void); + +/** + * @brief start report interface + */ +void interface_report_start(void); + +/** + * @brief start wifi interface + */ +void interface_wifi_start(void); + +/** + * @brief start settings interface + */ +void interface_settings_start(void); + +/** + * @brief start interface for input + * + * @param[in] callback_rst function to call on RST + * @param[in] callback_set function to call on SET + * @param[in] limit max character allowed (0=255) + */ +void interface_input(interface_input_callback callback_rst, interface_input_callback callback_set, uint8_t limit); + +/** + * @brief set text for input interface + * + * @param[in] text the text to set + */ +void interface_input_set_text(char *text); + +#endif \ No newline at end of file diff --git a/components/ssd1306/CMakeLists.txt b/components/ssd1306/CMakeLists.txt index fed4b96..2201aa0 100644 --- a/components/ssd1306/CMakeLists.txt +++ b/components/ssd1306/CMakeLists.txt @@ -1,7 +1,8 @@ idf_component_register( SRCS "ssd1306.c" - INCLUDE_DIRS "include" + "ssd1306-gfx.c" + INCLUDE_DIRS "." PRIV_REQUIRES i2c-main ) \ No newline at end of file diff --git a/components/ssd1306/include/ssd1306-ascii.h b/components/ssd1306/include/ssd1306-ascii.h deleted file mode 100644 index 9f465d8..0000000 --- a/components/ssd1306/include/ssd1306-ascii.h +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef _ssd1306_FONT_H_ -#define _ssd1306_FONT_H_ - -/** - * @brief constant containing a 5x8 ascii font - */ -const uint8_t ascii_font_5x8[256][5] = { - {0x00, 0x00, 0x00, 0x00, 0x00}, // 0x00 - {0x1E, 0x35, 0x31, 0x35, 0x1E}, // 0x01 - {0x1E, 0x35, 0x37, 0x35, 0x1E}, // 0x02 - {0x0E, 0x1F, 0x3E, 0x1F, 0x0E}, // 0x03 - {0x08, 0x1C, 0x7F, 0x1C, 0x08}, // 0x04 - {0x18, 0x4A, 0x7F, 0x4A, 0x18}, // 0x05 - {0x1C, 0x4E, 0x7F, 0x4E, 0x1C}, // 0x06 - {0x00, 0x00, 0x00, 0x00, 0x00}, // 0x07 - {0x00, 0x00, 0x00, 0x00, 0x00}, // 0x08 - {0x00, 0x00, 0x00, 0x00, 0x00}, // 0x09 - {0x00, 0x00, 0x00, 0x00, 0x00}, // 0x0A - {0x38, 0x44, 0x44, 0x47, 0x3B}, // 0x0B - {0x0E, 0x51, 0xF1, 0x51, 0x0E}, // 0x0C - {0x00, 0x00, 0x00, 0x00, 0x00}, // 0x0D - {0x60, 0x7E, 0x02, 0x33, 0x3F}, // 0x0E - {0x2A, 0x1C, 0x36, 0x1C, 0x2A}, // 0x0F - {0x3E, 0x1C, 0x1C, 0x08, 0x08}, // 0x10 - {0x08, 0x08, 0x1C, 0x1C, 0x3E}, // 0x11 - {0x00, 0x22, 0x7F, 0x22, 0x00}, // 0x12 - {0x00, 0x2E, 0x00, 0x2E, 0x00}, // 0x13 - {0x06, 0x7F, 0x01, 0x7F, 0x00}, // 0x14 - {0x00, 0x4A, 0x55, 0x29, 0x00}, // 0x15 - {0x00, 0x18, 0x18, 0x18, 0x18}, // 0x16 - {0x00, 0x4A, 0x5F, 0x4A, 0x00}, // 0x17 - {0x00, 0x02, 0x7F, 0x02, 0x00}, // 0x18 - {0x00, 0x20, 0x7F, 0x20, 0x00}, // 0x19 - {0x00, 0x08, 0x08, 0x1C, 0x08}, // 0x1A - {0x00, 0x08, 0x1C, 0x08, 0x08}, // 0x1B - {0x00, 0x3C, 0x20, 0x20, 0x20}, // 0x1C - {0x08, 0x1C, 0x08, 0x1C, 0x08}, // 0x1D - {0x20, 0x38, 0x3E, 0x38, 0x20}, // 0x1E - {0x02, 0x0E, 0x3E, 0x0E, 0x02}, // 0x1F - {0x00, 0x00, 0x00, 0x00, 0x00}, // 0x20 (Space) - {0x00, 0x00, 0x9E, 0x00, 0x00}, // 0x21 ! - {0x00, 0x0E, 0x00, 0x0E, 0x00}, // 0x22 " - {0x28, 0xFE, 0x28, 0xFE, 0x28}, // 0x23 # - {0x48, 0x54, 0xFE, 0x54, 0x24}, // 0x24 $ - {0x46, 0x26, 0x10, 0xC8, 0xC4}, // 0x25 % - {0x6C, 0x92, 0xAA, 0x44, 0xA0}, // 0x26 & - {0x00, 0x0A, 0x06, 0x00, 0x00}, // 0x27 ' - {0x00, 0x38, 0x44, 0x82, 0x00}, // 0x28 ( - {0x00, 0x82, 0x44, 0x38, 0x00}, // 0x29 ) - {0x10, 0x54, 0x38, 0x54, 0x10}, // 0x2A * - {0x10, 0x10, 0x7C, 0x10, 0x10}, // 0x2B + - {0x00, 0xA0, 0x60, 0x00, 0x00}, // 0x2C , - {0x10, 0x10, 0x10, 0x10, 0x10}, // 0x2D - - {0x00, 0x60, 0x60, 0x00, 0x00}, // 0x2E . - {0x40, 0x20, 0x10, 0x08, 0x04}, // 0x2F / - {0x7C, 0xA2, 0x92, 0x8A, 0x7C}, // 0x30 0 - {0x00, 0x84, 0xFE, 0x80, 0x00}, // 0x31 1 - {0x84, 0xC2, 0xA2, 0x92, 0x8C}, // 0x32 2 - {0x42, 0x82, 0x8A, 0x96, 0x62}, // 0x33 3 - {0x30, 0x28, 0x24, 0xFE, 0x20}, // 0x34 4 - {0x4E, 0x8A, 0x8A, 0x8A, 0x72}, // 0x35 5 - {0x78, 0x94, 0x92, 0x92, 0x60}, // 0x36 6 - {0x02, 0xE2, 0x12, 0x0A, 0x06}, // 0x37 7 - {0x6C, 0x92, 0x92, 0x92, 0x6C}, // 0x38 8 - {0x0C, 0x92, 0x92, 0x52, 0x3C}, // 0x39 9 - {0x00, 0x6C, 0x6C, 0x00, 0x00}, // 0x3A : - {0x00, 0xAC, 0x6C, 0x00, 0x00}, // 0x3B ; - {0x00, 0x10, 0x28, 0x44, 0x82}, // 0x3C < - {0x28, 0x28, 0x28, 0x28, 0x28}, // 0x3D = - {0x82, 0x44, 0x28, 0x10, 0x00}, // 0x3E > - {0x04, 0x02, 0xA2, 0x12, 0x0C}, // 0x3F ? - {0x64, 0x92, 0xF2, 0x82, 0x7C}, // 0x40 @ - {0xFC, 0x22, 0x22, 0x22, 0xFC}, // 0x41 A - {0xFE, 0x92, 0x92, 0x92, 0x6C}, // 0x42 B - {0x7C, 0x82, 0x82, 0x82, 0x44}, // 0x43 C - {0xFE, 0x82, 0x82, 0x44, 0x38}, // 0x44 D - {0xFE, 0x92, 0x92, 0x92, 0x82}, // 0x45 E - {0xFE, 0x12, 0x12, 0x02, 0x02}, // 0x46 F - {0x7C, 0x82, 0x82, 0xA2, 0x64}, // 0x47 G - {0xFE, 0x10, 0x10, 0x10, 0xFE}, // 0x48 H - {0x00, 0x82, 0xFE, 0x82, 0x00}, // 0x49 I - {0x40, 0x80, 0x82, 0x7E, 0x02}, // 0x4A J - {0xFE, 0x10, 0x28, 0x44, 0x82}, // 0x4B K - {0xFE, 0x80, 0x80, 0x80, 0x80}, // 0x4C L - {0xFE, 0x04, 0x08, 0x04, 0xFE}, // 0x4D M - {0xFE, 0x08, 0x10, 0x20, 0xFE}, // 0x4E N - {0x7C, 0x82, 0x82, 0x82, 0x7C}, // 0x4F O - {0xFE, 0x12, 0x12, 0x12, 0x0C}, // 0x50 P - {0x7C, 0x82, 0xA2, 0x42, 0xBC}, // 0x51 Q - {0xFE, 0x12, 0x32, 0x52, 0x8C}, // 0x52 R - {0x8C, 0x92, 0x92, 0x92, 0x62}, // 0x53 S - {0x02, 0x02, 0xFE, 0x02, 0x02}, // 0x54 T - {0x7E, 0x80, 0x80, 0x80, 0x7E}, // 0x55 U - {0x3E, 0x40, 0x80, 0x40, 0x3E}, // 0x56 V - {0xFE, 0x40, 0x30, 0x40, 0xFE}, // 0x57 W - {0xC6, 0x28, 0x10, 0x28, 0xC6}, // 0x58 X - {0x06, 0x08, 0xF0, 0x08, 0x06}, // 0x59 Y - {0xC2, 0xA2, 0x92, 0x8A, 0x86}, // 0x5A Z - {0x00, 0x00, 0xFE, 0x82, 0x82}, // 0x5B [ - {0x04, 0x08, 0x10, 0x20, 0x40}, // 0x5C "\" - {0x82, 0x82, 0xFE, 0x00, 0x00}, // 0x5D ] - {0x08, 0x04, 0x02, 0x04, 0x08}, // 0x5E ^ - {0x80, 0x80, 0x80, 0x80, 0x80}, // 0x5F _ - {0x00, 0x02, 0x04, 0x08, 0x00}, // 0x60 ` - {0x40, 0xA8, 0xA8, 0xA8, 0xF0}, // 0x61 a - {0xFE, 0x90, 0x88, 0x88, 0x70}, // 0x62 b - {0x70, 0x88, 0x88, 0x88, 0x40}, // 0x63 c - {0x70, 0x88, 0x88, 0x90, 0xFE}, // 0x64 d - {0x70, 0xA8, 0xA8, 0xA8, 0x30}, // 0x65 e - {0x10, 0xFC, 0x12, 0x02, 0x04}, // 0x66 f - {0x10, 0x28, 0xA8, 0xA8, 0x78}, // 0x67 g - {0xFE, 0x10, 0x08, 0x08, 0xF0}, // 0x68 h - {0x00, 0x88, 0xFA, 0x80, 0x00}, // 0x69 i - {0x40, 0x80, 0x88, 0x7A, 0x00}, // 0x6A j - {0x00, 0xFE, 0x20, 0x50, 0x88}, // 0x6B k - {0x00, 0x82, 0xFE, 0x80, 0x00}, // 0x6C l - {0xF8, 0x08, 0x30, 0x08, 0xF0}, // 0x6D m - {0xF8, 0x10, 0x08, 0x08, 0xF0}, // 0x6E n - {0x70, 0x88, 0x88, 0x88, 0x70}, // 0x6F o - {0xF8, 0x28, 0x28, 0x28, 0x10}, // 0x70 p - {0x10, 0x28, 0x28, 0x30, 0xF8}, // 0x71 q - {0xF8, 0x10, 0x08, 0x08, 0x10}, // 0x72 r - {0x90, 0xA8, 0xA8, 0xA8, 0x40}, // 0x73 s - {0x08, 0x7E, 0x88, 0x80, 0x40}, // 0x74 t - {0x78, 0x80, 0x80, 0x40, 0xF8}, // 0x75 u - {0x38, 0x40, 0x80, 0x40, 0x38}, // 0x76 v - {0x78, 0x80, 0x60, 0x80, 0x78}, // 0x77 w - {0x88, 0x50, 0x20, 0x50, 0x88}, // 0x78 x - {0x18, 0xA0, 0xA0, 0xA0, 0x78}, // 0x79 y - {0x88, 0xC8, 0xA8, 0x98, 0x88}, // 0x7A z - {0x00, 0x10, 0x6C, 0x82, 0x00}, // 0x7B { - {0x00, 0x00, 0xFE, 0x00, 0x00}, // 0x7C | - {0x00, 0x82, 0x6C, 0x10, 0x00}, // 0x7D } - {0x20, 0x10, 0x10, 0x20, 0x10}, // 0x7E - {0xF0, 0x88, 0x84, 0x88, 0xF0}, // 0x7F  - {0x28, 0x7C, 0xAA, 0x82, 0x44}, // 0x80 € - {0xF0, 0x29, 0x27, 0x21, 0xFF}, // 0x81  - {0x00, 0xA0, 0x60, 0x00, 0x00}, // 0x82 ‚ - {0x40, 0x90, 0x7C, 0x12, 0x04}, // 0x83 ƒ - {0xC0, 0xA0, 0x00, 0xC0, 0xA0}, // 0x84 „ - {0x80, 0x00, 0x80, 0x00, 0x80}, // 0x85 … - {0x00, 0x04, 0xFE, 0x04, 0x00}, // 0x86 † - {0x00, 0x44, 0xFE, 0x44, 0x00}, // 0x87 ‡ - {0x00, 0x04, 0x02, 0x04, 0x00}, // 0x88 ˆ - {0xC3, 0xD3, 0x08, 0xC4, 0xC2}, // 0x89 ‰ - {0x4C, 0x93, 0x92, 0x93, 0x64}, // 0x8A Š - {0x00, 0x10, 0x28, 0x00, 0x00}, // 0x8B ‹ - {0x7C, 0x82, 0x82, 0x7C, 0x92}, // 0x8C Œ - {0x02, 0xFE, 0x90, 0x90, 0x60}, // 0x8D  - {0xC2, 0xA3, 0x92, 0x8B, 0x86}, // 0x8E Ž - {0x44, 0x92, 0x8A, 0x92, 0x7C}, // 0x8F  - {0x70, 0x88, 0x90, 0x60, 0x98}, // 0x90  - {0x00, 0x02, 0x04, 0x08, 0x00}, // 0x91 ‘ - {0x00, 0x08, 0x04, 0x02, 0x00}, // 0x92 ’ - {0x02, 0x04, 0x0A, 0x04, 0x08}, // 0x93 “ - {0x08, 0x04, 0x0A, 0x04, 0x02}, // 0x94 ” - {0x00, 0x38, 0x38, 0x38, 0x00}, // 0x95 • - {0x00, 0x10, 0x10, 0x10, 0x10}, // 0x96 – - {0x10, 0x10, 0x10, 0x10, 0x10}, // 0x97 — - {0x02, 0x01, 0x02, 0x04, 0x02}, // 0x98 ˜ - {0xF1, 0x5B, 0x55, 0x51, 0x51}, // 0x99 ™ - {0x90, 0xA9, 0xAA, 0xA9, 0x40}, // 0x9A š - {0x00, 0x88, 0x50, 0x20, 0x00}, // 0x9B › - {0x70, 0x88, 0x70, 0xA8, 0xB0}, // 0x9C œ° - {0x38, 0x7C, 0xF8, 0x7C, 0x38}, // 0x9D  - {0x88, 0xC9, 0xAA, 0x99, 0x88}, // 0x9E ž - {0x1C, 0x21, 0xC0, 0x21, 0x1C}, // 0x9F Ÿ - {0x00, 0x00, 0x00, 0x00, 0x00}, // 0xA0 - {0x00, 0x00, 0xF2, 0x00, 0x00}, // 0xA1 ¡ - {0x38, 0x44, 0xFE, 0x44, 0x20}, // 0xA2 ¢ - {0x90, 0x7C, 0x92, 0x82, 0x40}, // 0xA3 £ - {0x44, 0x38, 0x28, 0x38, 0x44}, // 0xA4 ¤ - {0x2A, 0x2C, 0xF8, 0x2C, 0x2A}, // 0xA5 ¥ - {0x00, 0x00, 0xEE, 0x00, 0x00}, // 0xA6 ¦ - {0x40, 0x94, 0xAA, 0x52, 0x04}, // 0xA7 § - {0x00, 0x02, 0x00, 0x02, 0x00}, // 0xA8 ¨ - {0xFE, 0x82, 0xBA, 0x92, 0xFE}, // 0xA9 © - {0x90, 0xAA, 0xAA, 0xAA, 0xBC}, // 0xAA ª - {0x20, 0x50, 0xA8, 0x50, 0x88}, // 0xAB « - {0x20, 0x20, 0x20, 0x20, 0xE0}, // 0xAC ¬ - {0x20, 0x20, 0x20, 0x20, 0x20}, // 0xAD ­ - {0xFE, 0x82, 0xCA, 0xA2, 0xFE}, // 0xAE ® - {0x02, 0x02, 0x02, 0x02, 0x02}, // 0xAF ¯ - {0x0E, 0x11, 0x11, 0x0E, 0x00}, // 0xB0 ° - {0x88, 0x88, 0xBE, 0x88, 0x88}, // 0xB1 ± - {0x12, 0x19, 0x15, 0x12, 0x00}, // 0xB2 ² - {0x11, 0x15, 0x15, 0x0A, 0x00}, // 0xB3 ³ - {0x00, 0x08, 0x04, 0x02, 0x00}, // 0xB4 ´ - {0xFE, 0x20, 0x20, 0x10, 0x3E}, // 0xB5 µ - {0x0C, 0x12, 0x12, 0xFE, 0xFE}, // 0xB6 ¶ - {0x00, 0x30, 0x30, 0x00, 0x00}, // 0xB7 · - {0x00, 0x80, 0xB0, 0x40, 0x00}, // 0xB8 ¸ - {0x00, 0x02, 0x0F, 0x00, 0x00}, // 0xB9 ¹ - {0x00, 0x02, 0x05, 0x02, 0x00}, // 0xBA º - {0x44, 0x28, 0x54, 0x28, 0x10}, // 0xBB » - {0x22, 0x1F, 0x68, 0x54, 0xFA}, // 0xBC ¼ - {0x02, 0x1F, 0x90, 0xC8, 0xB0}, // 0xBD ½ - {0x15, 0x1F, 0x60, 0x50, 0xF8}, // 0xBE ¾ - {0x60, 0x90, 0x8A, 0x80, 0x40}, // 0xBF ¿ - {0xF0, 0x29, 0x26, 0x28, 0xF0}, // 0xC0 À - {0xF0, 0x28, 0x26, 0x29, 0xF0}, // 0xC1 Á - {0xF0, 0x2A, 0x29, 0x2A, 0xF0}, // 0xC2  - {0xF2, 0x29, 0x29, 0x2A, 0xF1}, // 0xC3 à - {0xF0, 0x29, 0x24, 0x29, 0xF0}, // 0xC4 Ä - {0xF0, 0x2A, 0x2D, 0x2A, 0xF0}, // 0xC5 Å - {0xF8, 0x24, 0xFE, 0x92, 0x92}, // 0xC6 Æ - {0x1E, 0x21, 0xA1, 0xE1, 0x12}, // 0xC7 Ç - {0xF8, 0xA9, 0xAA, 0xA8, 0x88}, // 0xC8 È - {0xF8, 0xA8, 0xAA, 0xA9, 0x88}, // 0xC9 É - {0xF8, 0xAA, 0xA9, 0xAA, 0x88}, // 0xCA Ê - {0xF8, 0xAA, 0xA8, 0xAA, 0x88}, // 0xCB Ë - {0x00, 0x89, 0xFA, 0x88, 0x00}, // 0xCC Ì - {0x00, 0x88, 0xFA, 0x89, 0x00}, // 0xCD Í - {0x00, 0x8A, 0xF9, 0x8A, 0x00}, // 0xCE Î - {0x00, 0x8A, 0xF8, 0x8A, 0x00}, // 0xCF Ï - {0x10, 0xFE, 0x92, 0x82, 0x7C}, // 0xD0 Ð - {0xFA, 0x11, 0x21, 0x42, 0xF9}, // 0xD1 Ñ - {0x78, 0x85, 0x86, 0x84, 0x78}, // 0xD2 Ò - {0x78, 0x84, 0x86, 0x85, 0x78}, // 0xD3 Ó - {0x70, 0x8A, 0x89, 0x8A, 0x70}, // 0xD4 Ô - {0x72, 0x89, 0x89, 0x8A, 0x71}, // 0xD5 Õ - {0x78, 0x85, 0x84, 0x85, 0x78}, // 0xD6 Ö - {0x44, 0x28, 0x10, 0x28, 0x44}, // 0xD7 × - {0x10, 0xAA, 0xFE, 0xAA, 0x10}, // 0xD8 Ø - {0x7C, 0x81, 0x82, 0x80, 0x7C}, // 0xD9 Ù - {0x7C, 0x80, 0x82, 0x81, 0x7C}, // 0xDA Ú - {0x78, 0x82, 0x81, 0x82, 0x78}, // 0xDB Û - {0x7C, 0x81, 0x80, 0x81, 0x7C}, // 0xDC Ü - {0x04, 0x08, 0xF2, 0x09, 0x04}, // 0xDD Ý - {0x81, 0xFF, 0x24, 0x24, 0x18}, // 0xDE Þ - {0x80, 0x7C, 0x92, 0x92, 0x6C}, // 0xDF ß - {0x40, 0xA9, 0xAA, 0xA8, 0xF0}, // 0xE0 à - {0x40, 0xA8, 0xAA, 0xA9, 0xF0}, // 0xE1 á - {0x40, 0xAA, 0xA9, 0xAA, 0xF0}, // 0xE2 â - {0x42, 0xA9, 0xA9, 0xAA, 0xF1}, // 0xE3 ã - {0x40, 0xAA, 0xA8, 0xAA, 0xF0}, // 0xE4 ä - {0x40, 0xAA, 0xAD, 0xAA, 0xF0}, // 0xE5 å - {0x64, 0x94, 0x78, 0x94, 0x58}, // 0xE6 æ - {0x18, 0x24, 0xA4, 0xE4, 0x10}, // 0xE7 ç - {0x70, 0xA9, 0xAA, 0xA8, 0x30}, // 0xE8 è - {0x70, 0xA8, 0xAA, 0xA9, 0x30}, // 0xE9 é - {0x70, 0xAA, 0xA9, 0xAA, 0x30}, // 0xEA ê - {0x70, 0xAA, 0xA8, 0xAA, 0x30}, // 0xEB ë - {0x00, 0x91, 0xFA, 0x80, 0x00}, // 0xEC ì - {0x00, 0x90, 0xFA, 0x81, 0x00}, // 0xED í - {0x00, 0x92, 0xF9, 0x82, 0x00}, // 0xEE î - {0x00, 0x92, 0xF8, 0x82, 0x00}, // 0xEF ï - {0x4A, 0xA4, 0xAA, 0xB0, 0x60}, // 0xF0 ð - {0xFA, 0x11, 0x09, 0x0A, 0xF1}, // 0xF1 ñ - {0x70, 0x89, 0x8A, 0x88, 0x70}, // 0xF2 ò - {0x70, 0x88, 0x8A, 0x89, 0x70}, // 0xF3 ó - {0x60, 0x94, 0x92, 0x94, 0x60}, // 0xF4 ô - {0x64, 0x92, 0x92, 0x94, 0x62}, // 0xF5 õ - {0x70, 0x8A, 0x88, 0x8A, 0x70}, // 0xF6 ö - {0x10, 0x10, 0x54, 0x10, 0x10}, // 0xF7 ÷ - {0x10, 0xA8, 0x7C, 0x2A, 0x10}, // 0xF8 ø - {0x78, 0x81, 0x82, 0x40, 0xF8}, // 0xF9 ù - {0x78, 0x80, 0x82, 0x41, 0xF8}, // 0xFA ú - {0x78, 0x82, 0x81, 0x42, 0xF8}, // 0xFB û - {0x78, 0x82, 0x80, 0x42, 0xF8}, // 0xFC ü - {0x18, 0xA0, 0xA4, 0xA2, 0x78}, // 0xFD v - {0x00, 0x82, 0xFE, 0xA8, 0x10}, // 0xFE þ - {0x18, 0xA2, 0xA0, 0xA2, 0x78} // 0xFF ÿ -}; - -const uint8_t ascii_font_8x8[256][8] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x00 - {0x7E, 0x81, 0x95, 0xB1, 0xB1, 0x95, 0x81, 0x7E}, // 0x01 - {0x7E, 0xFF, 0xEB, 0xCF, 0xCF, 0xEB, 0xFF, 0x7E}, // 0x02 - {0x0E, 0x1F, 0x3F, 0x7E, 0x3F, 0x1F, 0x0E, 0x00}, // 0x03 - {0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00}, // 0x04 - {0x38, 0x3A, 0x9F, 0xFF, 0x9F, 0x3A, 0x38, 0x00}, // 0x05 - {0x10, 0x38, 0xBC, 0xFF, 0xBC, 0x38, 0x10, 0x00}, // 0x06 - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x07 - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x08 - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x09 - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x0A - {0x70, 0xF8, 0x88, 0x88, 0xFD, 0x7F, 0x07, 0x0F}, // 0x0B - {0x00, 0x4E, 0x5F, 0xF1, 0xF1, 0x5F, 0x4E, 0x00}, // 0x0C - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x0D - {0xC0, 0xFF, 0x7F, 0x05, 0x05, 0x65, 0x7F, 0x3F}, // 0x0E - {0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99}, // 0x0F - {0x7F, 0x3E, 0x3E, 0x1C, 0x1C, 0x08, 0x08, 0x00}, // 0x10 - {0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x3E, 0x7F, 0x00}, // 0x11 - {0x00, 0x24, 0x66, 0xFF, 0xFF, 0x66, 0x24, 0x00}, // 0x12 - {0x00, 0x5F, 0x5F, 0x00, 0x00, 0x5F, 0x5F, 0x00}, // 0x13 - {0x06, 0x0F, 0x09, 0x7F, 0x7F, 0x01, 0x7F, 0x7F}, // 0x14 - {0xDA, 0xBF, 0xA5, 0xA5, 0xFD, 0x59, 0x03, 0x02}, // 0x15 - {0x00, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x00}, // 0x16 - {0x80, 0x94, 0xB6, 0xFF, 0xFF, 0xB6, 0x94, 0x80}, // 0x17 - {0x00, 0x04, 0x06, 0x7F, 0x7F, 0x06, 0x04, 0x00}, // 0x18 - {0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00}, // 0x19 - {0x08, 0x08, 0x08, 0x2A, 0x3E, 0x1C, 0x08, 0x00}, // 0x1A - {0x08, 0x1C, 0x3E, 0x2A, 0x08, 0x08, 0x08, 0x00}, // 0x1B - {0x3C, 0x3C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00}, // 0x1C - {0x08, 0x1C, 0x3E, 0x08, 0x08, 0x3E, 0x1C, 0x08}, // 0x1D - {0x30, 0x38, 0x3C, 0x3E, 0x3E, 0x3C, 0x38, 0x30}, // 0x1E - {0x06, 0x0E, 0x1E, 0x3E, 0x3E, 0x1E, 0x0E, 0x06}, // 0x1F - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x20 - {0x00, 0x06, 0x5F, 0x5F, 0x06, 0x00, 0x00, 0x00}, // 0x21 - {0x00, 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x00}, // 0x22 - {0x14, 0x7F, 0x7F, 0x14, 0x7F, 0x7F, 0x14, 0x00}, // 0x23 - {0x24, 0x2E, 0x6B, 0x6B, 0x3A, 0x12, 0x00, 0x00}, // 0x24 - {0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, 0x00}, // 0x25 - {0x30, 0x7A, 0x4F, 0x5D, 0x37, 0x7A, 0x48, 0x00}, // 0x26 - {0x04, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x27 - {0x00, 0x1C, 0x3E, 0x63, 0x41, 0x00, 0x00, 0x00}, // 0x28 - {0x00, 0x41, 0x63, 0x3E, 0x1C, 0x00, 0x00, 0x00}, // 0x29 - {0x08, 0x2A, 0x3E, 0x1C, 0x1C, 0x3E, 0x2A, 0x08}, // 0x2A - {0x08, 0x08, 0x3E, 0x3E, 0x08, 0x08, 0x00, 0x00}, // 0x2B - {0x00, 0xA0, 0xE0, 0x60, 0x00, 0x00, 0x00, 0x00}, // 0x2C - {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00}, // 0x2D - {0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00}, // 0x2E - {0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // 0x2F - {0x3E, 0x7F, 0x59, 0x4D, 0x7F, 0x3E, 0x00, 0x00}, // 0x30 - {0x42, 0x42, 0x7F, 0x7F, 0x40, 0x40, 0x00, 0x00}, // 0x31 - {0x62, 0x73, 0x59, 0x49, 0x6F, 0x66, 0x00, 0x00}, // 0x32 - {0x22, 0x63, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00}, // 0x33 - {0x18, 0x1C, 0x16, 0x13, 0x7F, 0x7F, 0x10, 0x00}, // 0x34 - {0x27, 0x67, 0x45, 0x45, 0x7D, 0x39, 0x00, 0x00}, // 0x35 - {0x3C, 0x7E, 0x4B, 0x49, 0x79, 0x30, 0x00, 0x00}, // 0x36 - {0x03, 0x63, 0x71, 0x19, 0x0F, 0x07, 0x00, 0x00}, // 0x37 - {0x36, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00, 0x00}, // 0x38 - {0x06, 0x4F, 0x49, 0x69, 0x3F, 0x1E, 0x00, 0x00}, // 0x39 - {0x00, 0x00, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00}, // 0x3A - {0x00, 0xA0, 0xEC, 0x6C, 0x00, 0x00, 0x00, 0x00}, // 0x3B - {0x08, 0x1C, 0x36, 0x63, 0x41, 0x00, 0x00, 0x00}, // 0x3C - {0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00}, // 0x3D - {0x00, 0x41, 0x63, 0x36, 0x1C, 0x08, 0x00, 0x00}, // 0x3E - {0x02, 0x03, 0x51, 0x59, 0x0F, 0x06, 0x00, 0x00}, // 0x3F - {0x3E, 0x7F, 0x41, 0x5D, 0x5D, 0x1F, 0x1E, 0x00}, // 0x40 - {0x7C, 0x7E, 0x13, 0x13, 0x7E, 0x7C, 0x00, 0x00}, // 0x41 - {0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00}, // 0x42 - {0x1C, 0x3E, 0x63, 0x41, 0x41, 0x63, 0x22, 0x00}, // 0x43 - {0x41, 0x7F, 0x7F, 0x41, 0x63, 0x7F, 0x1C, 0x00}, // 0x44 - {0x41, 0x7F, 0x7F, 0x49, 0x5D, 0x41, 0x63, 0x00}, // 0x45 - {0x41, 0x7F, 0x7F, 0x49, 0x1D, 0x01, 0x03, 0x00}, // 0x46 - {0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00}, // 0x47 - {0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00}, // 0x48 - {0x00, 0x41, 0x7F, 0x7F, 0x41, 0x00, 0x00, 0x00}, // 0x49 - {0x30, 0x70, 0x40, 0x41, 0x7F, 0x3F, 0x01, 0x00}, // 0x4A - {0x41, 0x7F, 0x7F, 0x08, 0x1C, 0x77, 0x63, 0x00}, // 0x4B - {0x41, 0x7F, 0x7F, 0x41, 0x40, 0x60, 0x70, 0x00}, // 0x4C - {0x7F, 0x7F, 0x06, 0x0C, 0x06, 0x7F, 0x7F, 0x00}, // 0x4D - {0x7F, 0x7F, 0x06, 0x0C, 0x18, 0x7F, 0x7F, 0x00}, // 0x4E - {0x1C, 0x3E, 0x63, 0x41, 0x63, 0x3E, 0x1C, 0x00}, // 0x4F - {0x41, 0x7F, 0x7F, 0x49, 0x09, 0x0F, 0x06, 0x00}, // 0x50 - {0x1E, 0x3F, 0x21, 0x71, 0x7F, 0x5E, 0x00, 0x00}, // 0x51 - {0x41, 0x7F, 0x7F, 0x19, 0x39, 0x6F, 0x46, 0x00}, // 0x52 - {0x26, 0x67, 0x4D, 0x59, 0x7B, 0x32, 0x00, 0x00}, // 0x53 - {0x03, 0x41, 0x7F, 0x7F, 0x41, 0x03, 0x00, 0x00}, // 0x54 - {0x7F, 0x7F, 0x40, 0x40, 0x7F, 0x7F, 0x00, 0x00}, // 0x55 - {0x1F, 0x3F, 0x60, 0x60, 0x3F, 0x1F, 0x00, 0x00}, // 0x56 - {0x7F, 0x7F, 0x30, 0x18, 0x30, 0x7F, 0x7F, 0x00}, // 0x57 - {0x63, 0x77, 0x1C, 0x08, 0x1C, 0x77, 0x63, 0x00}, // 0x58 - {0x07, 0x4F, 0x78, 0x78, 0x4F, 0x07, 0x00, 0x00}, // 0x59 - {0x67, 0x73, 0x59, 0x4D, 0x47, 0x63, 0x71, 0x00}, // 0x5A - {0x00, 0x7F, 0x7F, 0x41, 0x41, 0x00, 0x00, 0x00}, // 0x5B - {0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00}, // 0x5C - {0x00, 0x41, 0x41, 0x7F, 0x7F, 0x00, 0x00, 0x00}, // 0x5D - {0x08, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x08, 0x00}, // 0x5E - {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, // 0x5F - {0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x00}, // 0x60 - {0x20, 0x74, 0x54, 0x54, 0x3C, 0x78, 0x40, 0x00}, // 0x61 - {0x41, 0x3F, 0x7F, 0x44, 0x44, 0x7C, 0x38, 0x00}, // 0x62 - {0x38, 0x7C, 0x44, 0x44, 0x6C, 0x28, 0x00, 0x00}, // 0x63 - {0x30, 0x78, 0x48, 0x49, 0x3F, 0x7F, 0x40, 0x00}, // 0x64 - {0x38, 0x7C, 0x54, 0x54, 0x5C, 0x18, 0x00, 0x00}, // 0x65 - {0x48, 0x7E, 0x7F, 0x49, 0x03, 0x02, 0x00, 0x00}, // 0x66 - {0x98, 0xBC, 0xA4, 0xA4, 0xF8, 0x7C, 0x04, 0x00}, // 0x67 - {0x41, 0x7F, 0x7F, 0x08, 0x04, 0x7C, 0x78, 0x00}, // 0x68 - {0x00, 0x44, 0x7D, 0x7D, 0x40, 0x00, 0x00, 0x00}, // 0x69 - {0x40, 0xC4, 0x84, 0xFD, 0x7D, 0x00, 0x00, 0x00}, // 0x6A - {0x41, 0x7F, 0x7F, 0x10, 0x38, 0x6C, 0x44, 0x00}, // 0x6B - {0x00, 0x41, 0x7F, 0x7F, 0x40, 0x00, 0x00, 0x00}, // 0x6C - {0x7C, 0x7C, 0x0C, 0x18, 0x0C, 0x7C, 0x78, 0x00}, // 0x6D - {0x7C, 0x7C, 0x04, 0x04, 0x7C, 0x78, 0x00, 0x00}, // 0x6E - {0x38, 0x7C, 0x44, 0x44, 0x7C, 0x38, 0x00, 0x00}, // 0x6F - {0x84, 0xFC, 0xF8, 0xA4, 0x24, 0x3C, 0x18, 0x00}, // 0x70 - {0x18, 0x3C, 0x24, 0xA4, 0xF8, 0xFC, 0x84, 0x00}, // 0x71 - {0x44, 0x7C, 0x78, 0x44, 0x1C, 0x18, 0x00, 0x00}, // 0x72 - {0x48, 0x5C, 0x54, 0x54, 0x74, 0x24, 0x00, 0x00}, // 0x73 - {0x00, 0x04, 0x3E, 0x7F, 0x44, 0x24, 0x00, 0x00}, // 0x74 - {0x3C, 0x7C, 0x40, 0x40, 0x3C, 0x7C, 0x40, 0x00}, // 0x75 - {0x1C, 0x3C, 0x60, 0x60, 0x3C, 0x1C, 0x00, 0x00}, // 0x76 - {0x3C, 0x7C, 0x60, 0x30, 0x60, 0x7C, 0x3C, 0x00}, // 0x77 - {0x44, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0x44, 0x00}, // 0x78 - {0x9C, 0xBC, 0xA0, 0xA0, 0xFC, 0x7C, 0x00, 0x00}, // 0x79 - {0x4C, 0x64, 0x74, 0x5C, 0x4C, 0x64, 0x00, 0x00}, // 0x7A - {0x08, 0x08, 0x3E, 0x77, 0x41, 0x41, 0x00, 0x00}, // 0x7B - {0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x00}, // 0x7C - {0x41, 0x41, 0x77, 0x3E, 0x08, 0x08, 0x00, 0x00}, // 0x7D - {0x02, 0x03, 0x01, 0x03, 0x02, 0x03, 0x01, 0x00}, // 0x7E - {0x78, 0x7C, 0x46, 0x43, 0x46, 0x7C, 0x78, 0x00}, // 0x7F - {0x1E, 0xBF, 0xE1, 0x61, 0x33, 0x12, 0x00, 0x00}, // 0x80 - {0x3A, 0x7A, 0x40, 0x40, 0x7A, 0x7A, 0x40, 0x00}, // 0x81 - {0x38, 0x7C, 0x56, 0x57, 0x5D, 0x18, 0x00, 0x00}, // 0x82 - {0x02, 0x23, 0x75, 0x55, 0x55, 0x7D, 0x7B, 0x42}, // 0x83 - {0x21, 0x75, 0x54, 0x54, 0x7D, 0x79, 0x40, 0x00}, // 0x84 - {0x20, 0x75, 0x57, 0x56, 0x7C, 0x78, 0x40, 0x00}, // 0x85 - {0x00, 0x22, 0x77, 0x55, 0x55, 0x7F, 0x7A, 0x40}, // 0x86 - {0x1C, 0xBE, 0xE2, 0x62, 0x36, 0x14, 0x00, 0x00}, // 0x87 - {0x02, 0x3B, 0x7D, 0x55, 0x55, 0x5D, 0x1B, 0x02}, // 0x88 - {0x39, 0x7D, 0x54, 0x54, 0x5D, 0x19, 0x00, 0x00}, // 0x89 - {0x38, 0x7D, 0x57, 0x56, 0x5C, 0x18, 0x00, 0x00}, // 0x8A - {0x01, 0x45, 0x7C, 0x7C, 0x41, 0x01, 0x00, 0x00}, // 0x8B - {0x02, 0x03, 0x45, 0x7D, 0x7D, 0x43, 0x02, 0x00}, // 0x8C - {0x00, 0x45, 0x7F, 0x7E, 0x40, 0x00, 0x00, 0x00}, // 0x8D - {0x79, 0x7D, 0x26, 0x26, 0x7D, 0x79, 0x00, 0x00}, // 0x8E - {0x70, 0x7A, 0x2D, 0x2D, 0x7A, 0x70, 0x00, 0x00}, // 0x8F - {0x44, 0x7C, 0x7E, 0x57, 0x55, 0x44, 0x00, 0x00}, // 0x90 - {0x20, 0x74, 0x54, 0x54, 0x7C, 0x7C, 0x54, 0x54}, // 0x91 - {0x7C, 0x7E, 0x0B, 0x09, 0x7F, 0x7F, 0x49, 0x00}, // 0x92 - {0x32, 0x7B, 0x49, 0x49, 0x7B, 0x32, 0x00, 0x00}, // 0x93 - {0x32, 0x7A, 0x48, 0x48, 0x7A, 0x32, 0x00, 0x00}, // 0x94 - {0x30, 0x79, 0x4B, 0x4A, 0x78, 0x30, 0x00, 0x00}, // 0x95 - {0x3A, 0x7B, 0x41, 0x41, 0x7B, 0x7A, 0x40, 0x00}, // 0x96 - {0x38, 0x79, 0x43, 0x42, 0x78, 0x78, 0x40, 0x00}, // 0x97 - {0xBA, 0xBA, 0xA0, 0xA0, 0xFA, 0x7A, 0x00, 0x00}, // 0x98 - {0x39, 0x7D, 0x44, 0x44, 0x44, 0x7D, 0x39, 0x00}, // 0x99 - {0x3D, 0x7D, 0x40, 0x40, 0x7D, 0x3D, 0x00, 0x00}, // 0x9A - {0x38, 0x7C, 0x64, 0x54, 0x4C, 0x7C, 0x38, 0x00}, // 0x9B - {0x68, 0x7E, 0x7F, 0x49, 0x43, 0x66, 0x20, 0x00}, // 0x9C - {0x5C, 0x3E, 0x73, 0x49, 0x67, 0x3E, 0x1D, 0x00}, // 0x9D - {0x44, 0x6C, 0x38, 0x38, 0x6C, 0x44, 0x00, 0x00}, // 0x9E - {0x40, 0xC8, 0x88, 0xFE, 0x7F, 0x09, 0x0B, 0x02}, // 0x9F - {0x20, 0x74, 0x56, 0x57, 0x7D, 0x78, 0x40, 0x00}, // 0xA0 - {0x00, 0x44, 0x7E, 0x7F, 0x41, 0x00, 0x00, 0x00}, // 0xA1 - {0x30, 0x78, 0x48, 0x4A, 0x7B, 0x31, 0x00, 0x00}, // 0xA2 - {0x38, 0x78, 0x40, 0x42, 0x7B, 0x79, 0x40, 0x00}, // 0xA3 - {0x7A, 0x7B, 0x09, 0x0B, 0x7A, 0x73, 0x01, 0x00}, // 0xA4 - {0x7A, 0x7B, 0x19, 0x33, 0x7A, 0x7B, 0x01, 0x00}, // 0xA5 - {0x00, 0x26, 0x2F, 0x29, 0x2F, 0x2F, 0x28, 0x00}, // 0xA6 - {0x00, 0x26, 0x2F, 0x29, 0x29, 0x2F, 0x26, 0x00}, // 0xA7 - {0x30, 0x78, 0x4D, 0x45, 0x60, 0x20, 0x00, 0x00}, // 0xA8 - {0x1C, 0x22, 0x7D, 0x4B, 0x5B, 0x65, 0x22, 0x1C}, // 0xA9 - {0x08, 0x08, 0x08, 0x08, 0x38, 0x38, 0x00, 0x00}, // 0xAA - {0x61, 0x3F, 0x1F, 0xCC, 0xEE, 0xAB, 0xB9, 0x90}, // 0xAB - {0x61, 0x3F, 0x1F, 0x4C, 0x66, 0x73, 0xD9, 0xF8}, // 0xAC - {0x00, 0x00, 0x60, 0xFA, 0xFA, 0x60, 0x00, 0x00}, // 0xAD - {0x08, 0x1C, 0x36, 0x22, 0x08, 0x1C, 0x36, 0x22}, // 0xAE - {0x22, 0x36, 0x1C, 0x08, 0x22, 0x36, 0x1C, 0x08}, // 0xAF - {0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00, 0x55, 0x00}, // 0xB0 - {0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55}, // 0xB1 - {0x55, 0xFF, 0xAA, 0xFF, 0x55, 0xFF, 0xAA, 0xFF}, // 0xB2 - {0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00}, // 0xB3 - {0x10, 0x10, 0x10, 0xFF, 0xFF, 0x00, 0x00, 0x00}, // 0xB4 - {0x70, 0x78, 0x2C, 0x2E, 0x7B, 0x71, 0x00, 0x00}, // 0xB5 - {0x72, 0x79, 0x2D, 0x2D, 0x79, 0x72, 0x00, 0x00}, // 0xB6 - {0x71, 0x7B, 0x2E, 0x2C, 0x78, 0x70, 0x00, 0x00}, // 0xB7 - {0x1C, 0x22, 0x5D, 0x55, 0x55, 0x41, 0x22, 0x1C}, // 0xB8 - {0x14, 0x14, 0xF7, 0xF7, 0x00, 0xFF, 0xFF, 0x00}, // 0xB9 - {0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00}, // 0xBA - {0x14, 0x14, 0xF4, 0xF4, 0x04, 0xFC, 0xFC, 0x00}, // 0xBB - {0x14, 0x14, 0x17, 0x17, 0x10, 0x1F, 0x1F, 0x00}, // 0xBC - {0x18, 0x3C, 0x24, 0xE7, 0xE7, 0x24, 0x24, 0x00}, // 0xBD - {0x2B, 0x2F, 0xFC, 0xFC, 0x2F, 0x2B, 0x00, 0x00}, // 0xBE - {0x10, 0x10, 0x10, 0xF0, 0xF0, 0x00, 0x00, 0x00}, // 0xBF - {0x00, 0x00, 0x00, 0x1F, 0x1F, 0x10, 0x10, 0x10}, // 0xC0 - {0x10, 0x10, 0x10, 0x1F, 0x1F, 0x10, 0x10, 0x10}, // 0xC1 - {0x10, 0x10, 0x10, 0xF0, 0xF0, 0x10, 0x10, 0x10}, // 0xC2 - {0x00, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x10, 0x10}, // 0xC3 - {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, // 0xC4 - {0x10, 0x10, 0x10, 0xFF, 0xFF, 0x10, 0x10, 0x10}, // 0xC5 - {0x22, 0x77, 0x55, 0x57, 0x7E, 0x7B, 0x41, 0x00}, // 0xC6 - {0x72, 0x7B, 0x2D, 0x2F, 0x7A, 0x73, 0x01, 0x00}, // 0xC7 - {0x00, 0x00, 0x1F, 0x1F, 0x10, 0x17, 0x17, 0x14}, // 0xC8 - {0x00, 0x00, 0xFC, 0xFC, 0x04, 0xF4, 0xF4, 0x14}, // 0xC9 - {0x14, 0x14, 0x17, 0x17, 0x10, 0x17, 0x17, 0x14}, // 0xCA - {0x14, 0x14, 0xF4, 0xF4, 0x04, 0xF4, 0xF4, 0x14}, // 0xCB - {0x00, 0x00, 0xFF, 0xFF, 0x00, 0xF7, 0xF7, 0x14}, // 0xCC - {0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, // 0xCD - {0x14, 0x14, 0xF7, 0xF7, 0x00, 0xF7, 0xF7, 0x14}, // 0xCE - {0x66, 0x3C, 0x3C, 0x24, 0x3C, 0x3C, 0x66, 0x00}, // 0xCF - {0x05, 0x27, 0x72, 0x57, 0x7D, 0x38, 0x00, 0x00}, // 0xD0 - {0x49, 0x7F, 0x7F, 0x49, 0x63, 0x7F, 0x1C, 0x00}, // 0xD1 - {0x46, 0x7D, 0x7D, 0x55, 0x55, 0x46, 0x00, 0x00}, // 0xD2 - {0x45, 0x7D, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x00}, // 0xD3 - {0x44, 0x7D, 0x7F, 0x56, 0x54, 0x44, 0x00, 0x00}, // 0xD4 - {0x0A, 0x0E, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0xD5 - {0x00, 0x44, 0x7E, 0x7F, 0x45, 0x00, 0x00, 0x00}, // 0xD6 - {0x02, 0x45, 0x7D, 0x7D, 0x45, 0x02, 0x00, 0x00}, // 0xD7 - {0x01, 0x45, 0x7C, 0x7C, 0x45, 0x01, 0x00, 0x00}, // 0xD8 - {0x10, 0x10, 0x10, 0x1F, 0x1F, 0x00, 0x00, 0x00}, // 0xD9 - {0x00, 0x00, 0x00, 0xF0, 0xF0, 0x10, 0x10, 0x10}, // 0xDA - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // 0xDB - {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, // 0xDC - {0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x00}, // 0xDD - {0x00, 0x45, 0x7F, 0x7E, 0x44, 0x00, 0x00, 0x00}, // 0xDE - {0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, // 0xDF - {0x38, 0x7C, 0x46, 0x47, 0x45, 0x7C, 0x38, 0x00}, // 0xE0 - {0xFC, 0xFE, 0x2A, 0x2A, 0x3E, 0x14, 0x00, 0x00}, // 0xE1 - {0x3A, 0x7D, 0x45, 0x45, 0x45, 0x7D, 0x3A, 0x00}, // 0xE2 - {0x38, 0x7C, 0x45, 0x47, 0x46, 0x7C, 0x38, 0x00}, // 0xE3 - {0x32, 0x7B, 0x49, 0x4B, 0x7A, 0x33, 0x01, 0x00}, // 0xE4 - {0x3A, 0x7F, 0x45, 0x47, 0x46, 0x7F, 0x39, 0x00}, // 0xE5 - {0x80, 0xFE, 0x7E, 0x20, 0x20, 0x3E, 0x1E, 0x00}, // 0xE6 - {0x42, 0x7E, 0x7E, 0x54, 0x1C, 0x08, 0x00, 0x00}, // 0xE7 - {0x41, 0x7F, 0x7F, 0x55, 0x14, 0x1C, 0x08, 0x00}, // 0xE8 - {0x3C, 0x7C, 0x42, 0x43, 0x7D, 0x3C, 0x00, 0x00}, // 0xE9 - {0x3A, 0x79, 0x41, 0x41, 0x79, 0x3A, 0x00, 0x00}, // 0xEA - {0x3C, 0x7D, 0x43, 0x42, 0x7C, 0x3C, 0x00, 0x00}, // 0xEB - {0xB8, 0xB8, 0xA2, 0xA3, 0xF9, 0x78, 0x00, 0x00}, // 0xEC - {0x0C, 0x5C, 0x72, 0x73, 0x5D, 0x0C, 0x00, 0x00}, // 0xED - {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00}, // 0xEE - {0x00, 0x00, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00}, // 0xEF - {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00}, // 0xF0 - {0x44, 0x44, 0x5F, 0x5F, 0x44, 0x44, 0x00, 0x00}, // 0xF1 - {0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00}, // 0xF2 - {0x71, 0x35, 0x1F, 0x4C, 0x66, 0x73, 0xD9, 0xF8}, // 0xF3 - {0x06, 0x0F, 0x09, 0x7F, 0x7F, 0x01, 0x7F, 0x7F}, // 0xF4 - {0xDA, 0xBF, 0xA5, 0xA5, 0xFD, 0x59, 0x03, 0x02}, // 0xF5 - {0x08, 0x08, 0x6B, 0x6B, 0x08, 0x08, 0x00, 0x00}, // 0xF6 - {0x00, 0x80, 0xC0, 0x40, 0x00, 0x00, 0x00, 0x00}, // 0xF7 - {0x00, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00}, // 0xF8 - {0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00}, // 0xF9 - {0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00}, // 0xFA - {0x00, 0x12, 0x13, 0x1F, 0x1F, 0x10, 0x10, 0x00}, // 0xFB - {0x00, 0x11, 0x15, 0x15, 0x1F, 0x1F, 0x0A, 0x00}, // 0xFC - {0x00, 0x19, 0x1D, 0x15, 0x17, 0x12, 0x00, 0x00}, // 0xFD - {0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00}, // 0xFE - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // 0xFF -}; - -#endif \ No newline at end of file diff --git a/components/ssd1306/ssd1306-gfx.c b/components/ssd1306/ssd1306-gfx.c new file mode 100644 index 0000000..36f7cbc --- /dev/null +++ b/components/ssd1306/ssd1306-gfx.c @@ -0,0 +1,307 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "ssd1306-gfx.h" + +uint8_t ssd1306_gfx_clear[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +uint8_t ssd1306_gfx_font[224][8] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x5f, 0x5f, 0x00, 0x00, 0x00}, + {0x00, 0x08, 0x0e, 0x06, 0x08, 0x0e, 0x06, 0x00}, + {0x00, 0x24, 0x7e, 0x7e, 0x24, 0x7e, 0x7e, 0x24}, + {0x00, 0x4c, 0xde, 0x92, 0xfe, 0x92, 0xf6, 0x64}, + {0x06, 0x8d, 0xe6, 0x78, 0x1e, 0x67, 0xd1, 0x60}, + {0x00, 0x36, 0x7f, 0x49, 0x5f, 0x76, 0x70, 0x50}, + {0x00, 0x00, 0x00, 0x08, 0x0e, 0x06, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x3e, 0x7f, 0x41, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x41, 0x7f, 0x3e, 0x00, 0x00}, + {0x00, 0x14, 0x1c, 0x3e, 0x3e, 0x1c, 0x14, 0x00}, + {0x00, 0x08, 0x08, 0x3e, 0x3e, 0x08, 0x08, 0x00}, + {0x00, 0x00, 0x00, 0x80, 0xe0, 0x60, 0x00, 0x00}, + {0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00}, + {0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0xc0, 0xf0, 0x3c, 0x0f, 0x03, 0x00}, + {0x00, 0x3e, 0x7f, 0x4d, 0x59, 0x7f, 0x3e, 0x00}, + {0x00, 0x42, 0x42, 0x7f, 0x7f, 0x40, 0x40, 0x00}, + {0x00, 0x42, 0x63, 0x71, 0x59, 0x4f, 0x46, 0x00}, + {0x00, 0x41, 0x49, 0x49, 0x49, 0x7f, 0x36, 0x00}, + {0x00, 0x18, 0x1c, 0x16, 0x7f, 0x7f, 0x10, 0x00}, + {0x00, 0x27, 0x67, 0x45, 0x45, 0x7d, 0x39, 0x00}, + {0x00, 0x3e, 0x7f, 0x49, 0x49, 0x7b, 0x32, 0x00}, + {0x00, 0x03, 0x63, 0x71, 0x19, 0x0f, 0x07, 0x00}, + {0x00, 0x36, 0x7f, 0x49, 0x49, 0x7f, 0x36, 0x00}, + {0x00, 0x06, 0x4f, 0x49, 0x69, 0x3f, 0x1e, 0x00}, + {0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x80, 0xe8, 0x68, 0x00, 0x00}, + {0x00, 0x00, 0x08, 0x1c, 0x36, 0x22, 0x00, 0x00}, + {0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, + {0x00, 0x00, 0x00, 0x22, 0x36, 0x1c, 0x08, 0x00}, + {0x00, 0x02, 0x13, 0x59, 0x49, 0x0f, 0x06, 0x00}, + {0x3e, 0x7f, 0x55, 0x5d, 0x5d, 0x5f, 0x6e, 0x20}, + {0x00, 0x7e, 0x7f, 0x11, 0x11, 0x7f, 0x7e, 0x00}, + {0x00, 0x7f, 0x7f, 0x49, 0x49, 0x7f, 0x36, 0x00}, + {0x00, 0x3e, 0x7f, 0x41, 0x41, 0x63, 0x22, 0x00}, + {0x01, 0x7f, 0x7f, 0x41, 0x41, 0x7f, 0x3e, 0x00}, + {0x00, 0x7f, 0x7f, 0x49, 0x49, 0x49, 0x41, 0x00}, + {0x00, 0x7f, 0x7f, 0x09, 0x09, 0x09, 0x01, 0x00}, + {0x00, 0x3e, 0x7f, 0x41, 0x49, 0x79, 0x79, 0x00}, + {0x00, 0x7f, 0x7f, 0x08, 0x08, 0x7f, 0x7f, 0x00}, + {0x00, 0x00, 0x41, 0x7f, 0x7f, 0x41, 0x00, 0x00}, + {0x00, 0x41, 0x41, 0x7f, 0x3f, 0x01, 0x00, 0x00}, + {0x00, 0x7f, 0x7f, 0x0c, 0x3e, 0x73, 0x41, 0x00}, + {0x00, 0x00, 0x7f, 0x7f, 0x40, 0x40, 0x40, 0x00}, + {0x00, 0x7f, 0x7f, 0x0c, 0x08, 0x0c, 0x7f, 0x7f}, + {0x00, 0x7f, 0x7f, 0x06, 0x0c, 0x7f, 0x7f, 0x00}, + {0x00, 0x3e, 0x7f, 0x41, 0x41, 0x7f, 0x3e, 0x00}, + {0x00, 0x7f, 0x7f, 0x09, 0x09, 0x0f, 0x06, 0x00}, + {0x00, 0x3e, 0x7f, 0x51, 0x61, 0x7f, 0x5e, 0x00}, + {0x00, 0x7f, 0x7f, 0x09, 0x19, 0x7f, 0x66, 0x00}, + {0x00, 0x26, 0x6f, 0x49, 0x49, 0x7b, 0x32, 0x00}, + {0x00, 0x01, 0x01, 0x7f, 0x7f, 0x01, 0x01, 0x00}, + {0x00, 0x3f, 0x7f, 0x40, 0x40, 0x7f, 0x3f, 0x00}, + {0x00, 0x0f, 0x3f, 0x70, 0x70, 0x3f, 0x0f, 0x00}, + {0x3f, 0x7f, 0x60, 0x3c, 0x3c, 0x60, 0x7f, 0x3f}, + {0x00, 0x63, 0x77, 0x1c, 0x1c, 0x77, 0x63, 0x00}, + {0x00, 0x07, 0x0f, 0x78, 0x78, 0x0f, 0x07, 0x00}, + {0x00, 0x61, 0x71, 0x59, 0x4d, 0x47, 0x43, 0x00}, + {0x00, 0x00, 0x00, 0x7f, 0x7f, 0x41, 0x00, 0x00}, + {0x00, 0x00, 0x03, 0x0f, 0x3c, 0xf0, 0xc0, 0x00}, + {0x00, 0x00, 0x00, 0x41, 0x7f, 0x7f, 0x00, 0x00}, + {0x00, 0x00, 0x04, 0x06, 0x02, 0x06, 0x04, 0x00}, + {0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, + {0x00, 0x00, 0x00, 0x02, 0x0e, 0x0c, 0x00, 0x00}, + {0x00, 0x30, 0x7a, 0x4a, 0x4a, 0x7e, 0x7c, 0x00}, + {0x00, 0x7f, 0x7f, 0x44, 0x44, 0x7c, 0x38, 0x00}, + {0x00, 0x3c, 0x7e, 0x42, 0x42, 0x46, 0x44, 0x00}, + {0x00, 0x38, 0x7c, 0x44, 0x44, 0x7f, 0x7f, 0x00}, + {0x00, 0x3c, 0x7e, 0x4a, 0x4a, 0x4e, 0x4c, 0x00}, + {0x00, 0x08, 0x7e, 0x7f, 0x09, 0x09, 0x01, 0x00}, + {0x00, 0x1c, 0xbe, 0xa2, 0xa2, 0xfe, 0x7e, 0x00}, + {0x00, 0x7f, 0x7f, 0x0c, 0x04, 0x7c, 0x78, 0x00}, + {0x00, 0x00, 0x44, 0x7d, 0x7d, 0x40, 0x00, 0x00}, + {0x00, 0x00, 0x82, 0xfe, 0x7e, 0x02, 0x00, 0x00}, + {0x00, 0x7e, 0x7e, 0x18, 0x3c, 0x66, 0x42, 0x00}, + {0x00, 0x00, 0x3e, 0x7e, 0x40, 0x40, 0x00, 0x00}, + {0x7c, 0x7e, 0x02, 0x7e, 0x7e, 0x02, 0x7e, 0x7c}, + {0x00, 0x7c, 0x7c, 0x06, 0x02, 0x7e, 0x7c, 0x00}, + {0x00, 0x3c, 0x7e, 0x42, 0x42, 0x7e, 0x3c, 0x00}, + {0x00, 0xfe, 0xfe, 0x22, 0x22, 0x3e, 0x1c, 0x00}, + {0x00, 0x1c, 0x3e, 0x22, 0x22, 0xfe, 0xfe, 0x00}, + {0x00, 0x00, 0x7c, 0x7e, 0x06, 0x02, 0x02, 0x00}, + {0x00, 0x24, 0x6e, 0x4a, 0x4a, 0x7a, 0x30, 0x00}, + {0x00, 0x00, 0x3f, 0x7f, 0x44, 0x44, 0x44, 0x00}, + {0x00, 0x3e, 0x7e, 0x40, 0x60, 0x7e, 0x7e, 0x00}, + {0x00, 0x0e, 0x3e, 0x70, 0x70, 0x3e, 0x0e, 0x00}, + {0x3c, 0x7c, 0x40, 0x7c, 0x7c, 0x40, 0x7e, 0x3e}, + {0x00, 0x42, 0x66, 0x3c, 0x3c, 0x66, 0x42, 0x00}, + {0x00, 0x00, 0x4e, 0x5e, 0x70, 0x3e, 0x0e, 0x00}, + {0x00, 0x00, 0x62, 0x72, 0x5a, 0x4e, 0x46, 0x00}, + {0x00, 0x00, 0x18, 0x7e, 0xc3, 0x81, 0x42, 0x00}, + {0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x42, 0x81, 0xc3, 0x7e, 0x18, 0x00}, + {0x00, 0x10, 0x18, 0x08, 0x18, 0x10, 0x18, 0x08}, + {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}, + {0x28, 0x7c, 0xfe, 0xaa, 0xaa, 0x8a, 0xc6, 0x44}, + {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}, + {0x00, 0x00, 0x00, 0x80, 0xe0, 0x60, 0x00, 0x00}, + {0x00, 0x00, 0x48, 0x7c, 0x3e, 0x0a, 0x00, 0x00}, + {0x00, 0x80, 0xe0, 0x60, 0x80, 0xe0, 0x60, 0x00}, + {0x40, 0x40, 0x00, 0x40, 0x40, 0x00, 0x40, 0x40}, + {0x00, 0x08, 0x04, 0x7e, 0x7e, 0x04, 0x08, 0x00}, + {0x00, 0x24, 0x42, 0xff, 0xff, 0x42, 0x24, 0x00}, + {0x00, 0x04, 0x06, 0x03, 0x03, 0x06, 0x04, 0x00}, + {0xc0, 0x66, 0x1d, 0x66, 0xd8, 0x66, 0xd3, 0x60}, + {0x00, 0x25, 0x6f, 0x4a, 0x4a, 0x7b, 0x31, 0x00}, + {0x00, 0x00, 0x00, 0x18, 0x3c, 0x24, 0x00, 0x00}, + {0x3e, 0x7f, 0x41, 0x61, 0x7f, 0x7f, 0x49, 0x49}, + {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}, + {0x00, 0x61, 0x73, 0x5a, 0x4e, 0x47, 0x43, 0x00}, + {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}, + {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}, + {0x00, 0x00, 0x00, 0x06, 0x0e, 0x08, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x02, 0x0e, 0x0c, 0x00, 0x00}, + {0x00, 0x06, 0x0e, 0x08, 0x06, 0x0e, 0x08, 0x00}, + {0x00, 0x02, 0x0e, 0x0c, 0x02, 0x0e, 0x0c, 0x00}, + {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00}, + {0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00}, + {0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00}, + {0x00, 0x04, 0x06, 0x02, 0x04, 0x06, 0x02, 0x00}, + {0x01, 0x0f, 0x0f, 0x01, 0x0e, 0x02, 0x0f, 0x0f}, + {0x00, 0x25, 0x6f, 0x4a, 0x4a, 0x7b, 0x31, 0x00}, + {0x00, 0x00, 0x00, 0x24, 0x3c, 0x18, 0x00, 0x00}, + {0x3c, 0x7e, 0x42, 0x62, 0x7e, 0x7e, 0x52, 0x52}, + {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}, + {0x00, 0x01, 0x63, 0x72, 0x5a, 0x4e, 0x47, 0x01}, + {0x00, 0x05, 0x0d, 0x78, 0x78, 0x0d, 0x05, 0x00}, + {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}, + {0x00, 0x00, 0x00, 0x7d, 0x7d, 0x00, 0x00, 0x00}, + {0x00, 0x38, 0x7c, 0x44, 0xfe, 0x44, 0x6c, 0x28}, + {0x00, 0x90, 0xfc, 0xfe, 0x92, 0x92, 0xc6, 0x44}, + {0x00, 0x42, 0x5a, 0x3c, 0x24, 0x3c, 0x5a, 0x42}, + {0x00, 0x52, 0x56, 0xfc, 0xfc, 0x56, 0x52, 0x00}, + {0x00, 0x00, 0x00, 0xf7, 0xf7, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x1a, 0xbf, 0xa5, 0xfd, 0x58, 0x00}, + {0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00}, + {0x3c, 0x7e, 0xdb, 0xa5, 0xa5, 0xd3, 0x7e, 0x3c}, + {0x00, 0x10, 0x16, 0x13, 0x13, 0x16, 0x10, 0x00}, + {0x00, 0x18, 0x3c, 0x24, 0x18, 0x3c, 0x24, 0x00}, + {0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x38}, + {0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x00, 0x00}, + {0x7e, 0xff, 0x81, 0xbd, 0x95, 0xa9, 0xff, 0x7e}, + {0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, + {0x00, 0x00, 0x0c, 0x1a, 0x1a, 0x0c, 0x00, 0x00}, + {0x00, 0x48, 0x48, 0x7e, 0x7e, 0x48, 0x48, 0x00}, + {0x00, 0x00, 0x19, 0x1d, 0x17, 0x12, 0x00, 0x00}, + {0x00, 0x00, 0x11, 0x15, 0x1f, 0x0a, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x02, 0x03, 0x01, 0x00, 0x00}, + {0x00, 0xfe, 0xfe, 0x20, 0x30, 0x3e, 0x7e, 0x40}, + {0x00, 0x06, 0x0f, 0xff, 0xff, 0x01, 0xff, 0x00}, + {0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x40, 0xc0, 0x80, 0x00, 0x00}, + {0x00, 0x00, 0x12, 0x1f, 0x1f, 0x10, 0x00, 0x00}, + {0x00, 0x10, 0x17, 0x17, 0x15, 0x17, 0x17, 0x10}, + {0x00, 0x24, 0x3c, 0x18, 0x24, 0x3c, 0x18, 0x00}, + {0x09, 0xcf, 0x7f, 0x3c, 0x6e, 0x53, 0xf8, 0xf8}, + {0x89, 0xcf, 0x6f, 0x38, 0xdc, 0xee, 0xbb, 0x90}, + {0x91, 0xd5, 0x7f, 0x3a, 0x6c, 0x56, 0xfb, 0xf8}, + {0x00, 0x60, 0xf0, 0x92, 0x9a, 0xc8, 0x40, 0x00}, + {0x00, 0x01, 0x7b, 0x7e, 0x24, 0x24, 0x7c, 0x78}, + {0x00, 0x78, 0x7c, 0x24, 0x24, 0x7e, 0x7b, 0x01}, + {0x02, 0x7b, 0x7d, 0x25, 0x25, 0x7d, 0x7b, 0x02}, + {0x02, 0x7b, 0x7d, 0x25, 0x27, 0x7e, 0x7b, 0x01}, + {0x00, 0x79, 0x7d, 0x24, 0x24, 0x7d, 0x79, 0x00}, + {0x00, 0x78, 0x7e, 0x25, 0x25, 0x7e, 0x78, 0x00}, + {0x70, 0x7c, 0x0e, 0x0b, 0x7f, 0x7f, 0x49, 0x49}, + {0x00, 0x3e, 0x7f, 0x41, 0x41, 0xe3, 0xa2, 0x00}, + {0x00, 0x01, 0x7f, 0x7e, 0x54, 0x54, 0x54, 0x44}, + {0x7c, 0x7c, 0x54, 0x54, 0x54, 0x46, 0x03, 0x01}, + {0x02, 0x03, 0x7d, 0x7d, 0x55, 0x55, 0x03, 0x02}, + {0x00, 0x7d, 0x7d, 0x54, 0x54, 0x55, 0x45, 0x00}, + {0x00, 0x00, 0x41, 0x7b, 0x7a, 0x40, 0x00, 0x00}, + {0x00, 0x00, 0x40, 0x7a, 0x7b, 0x41, 0x00, 0x00}, + {0x00, 0x02, 0x43, 0x7d, 0x7d, 0x43, 0x02, 0x00}, + {0x00, 0x01, 0x41, 0x7c, 0x7c, 0x41, 0x01, 0x00}, + {0x00, 0x11, 0x7f, 0x7f, 0x51, 0x41, 0x7f, 0x3e}, + {0x02, 0x03, 0x7d, 0x7d, 0x1b, 0x72, 0x7b, 0x01}, + {0x00, 0x01, 0x3b, 0x7e, 0x44, 0x44, 0x7c, 0x38}, + {0x00, 0x38, 0x7c, 0x44, 0x44, 0x7e, 0x3b, 0x01}, + {0x02, 0x3b, 0x7d, 0x45, 0x45, 0x7d, 0x3b, 0x02}, + {0x02, 0x3b, 0x7d, 0x45, 0x47, 0x7e, 0x3b, 0x01}, + {0x00, 0x39, 0x7d, 0x44, 0x44, 0x7d, 0x39, 0x00}, + {0x00, 0x22, 0x36, 0x1c, 0x1c, 0x36, 0x22, 0x00}, + {0x00, 0x5e, 0x7f, 0x39, 0x5d, 0x46, 0x7f, 0x3d}, + {0x00, 0x01, 0x3f, 0x7e, 0x40, 0x40, 0x7c, 0x3c}, + {0x00, 0x3c, 0x7c, 0x40, 0x40, 0x7e, 0x3f, 0x01}, + {0x02, 0x3b, 0x79, 0x41, 0x41, 0x79, 0x3b, 0x02}, + {0x00, 0x3d, 0x7d, 0x40, 0x40, 0x7d, 0x3d, 0x00}, + {0x00, 0x0e, 0x1e, 0x70, 0x72, 0x1b, 0x09, 0x00}, + {0x00, 0x00, 0x7e, 0x7e, 0x24, 0x3c, 0x18, 0x00}, + {0x00, 0xfe, 0xff, 0x49, 0x4f, 0x7e, 0x38, 0x00}, + {0x00, 0x01, 0x7b, 0x7e, 0x24, 0x24, 0x7c, 0x78}, + {0x00, 0x78, 0x7c, 0x24, 0x24, 0x7e, 0x7b, 0x01}, + {0x02, 0x7b, 0x7d, 0x25, 0x25, 0x7d, 0x7b, 0x02}, + {0x02, 0x7b, 0x7d, 0x25, 0x27, 0x7e, 0x7b, 0x01}, + {0x00, 0x79, 0x7d, 0x24, 0x24, 0x7d, 0x79, 0x00}, + {0x00, 0x78, 0x7e, 0x25, 0x25, 0x7e, 0x78, 0x00}, + {0x60, 0x78, 0x1c, 0x16, 0x7e, 0x7e, 0x52, 0x52}, + {0x00, 0x3c, 0x7e, 0x42, 0x42, 0xe6, 0xa4, 0x00}, + {0x00, 0x01, 0x7f, 0x7e, 0x54, 0x54, 0x54, 0x44}, + {0x7c, 0x7c, 0x54, 0x54, 0x54, 0x46, 0x03, 0x01}, + {0x02, 0x03, 0x7d, 0x7d, 0x55, 0x55, 0x03, 0x02}, + {0x00, 0x7d, 0x7d, 0x54, 0x54, 0x55, 0x45, 0x00}, + {0x00, 0x00, 0x41, 0x7b, 0x7a, 0x40, 0x00, 0x00}, + {0x00, 0x00, 0x40, 0x7a, 0x7b, 0x41, 0x00, 0x00}, + {0x00, 0x02, 0x43, 0x7d, 0x7d, 0x43, 0x02, 0x00}, + {0x00, 0x00, 0x41, 0x79, 0x79, 0x41, 0x00, 0x00}, + {0x00, 0x38, 0x7c, 0x44, 0x44, 0x7f, 0x3f, 0x04}, + {0x02, 0x03, 0x7d, 0x7d, 0x1b, 0x72, 0x7b, 0x01}, + {0x00, 0x01, 0x3b, 0x7e, 0x44, 0x44, 0x7c, 0x38}, + {0x00, 0x38, 0x7c, 0x44, 0x44, 0x7e, 0x3b, 0x01}, + {0x02, 0x3b, 0x7d, 0x45, 0x45, 0x7d, 0x3b, 0x02}, + {0x02, 0x3b, 0x7d, 0x45, 0x47, 0x7e, 0x3b, 0x01}, + {0x00, 0x39, 0x7d, 0x44, 0x44, 0x7d, 0x39, 0x00}, + {0x00, 0x08, 0x08, 0x2a, 0x2a, 0x08, 0x08, 0x00}, + {0x00, 0x5c, 0x7e, 0x32, 0x5a, 0x4c, 0x7e, 0x3a}, + {0x00, 0x01, 0x3f, 0x7e, 0x40, 0x40, 0x7c, 0x3c}, + {0x00, 0x3c, 0x7c, 0x40, 0x40, 0x7e, 0x3f, 0x01}, + {0x02, 0x3b, 0x79, 0x41, 0x41, 0x79, 0x3b, 0x02}, + {0x00, 0x3d, 0x7d, 0x40, 0x40, 0x7d, 0x3d, 0x00}, + {0x00, 0x08, 0x18, 0x72, 0x73, 0x19, 0x08, 0x00}, + {0x00, 0x00, 0x7e, 0x7e, 0x24, 0x3c, 0x18, 0x00}, + {0x00, 0x0d, 0x1d, 0x70, 0x70, 0x1d, 0x0d, 0x00}, +}; + +uint8_t ssd1306_gfx_button[3][64] = { + {0x00, 0x00, 0x00, 0xe0, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x30, 0xe0, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 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, 0xff, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x07, 0x0c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0c, 0x07, 0x00, 0x00, 0x00}, +}; + +uint8_t ssd1306_gfx_button_sel[3][64] = { + {0x00, 0x00, 0x00, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0x00, 0x00, 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, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x07, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x07, 0x00, 0x00, 0x00}, +}; + +uint8_t ssd1306_gfx_clock[8] = {0x3c, 0x42, 0x81, 0x9d, 0x91, 0x81, 0x42, 0x3c}; + +uint8_t ssd1306_gfx_menu_head[112] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +uint8_t ssd1306_gfx_sad[4][24] = { + {0x00, 0x00, 0x00, 0x80, 0xe0, 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0xe0, 0x80, 0x00, 0x00, 0x00}, + {0xf8, 0xff, 0x0f, 0x01, 0x01, 0x00, 0x00, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x00, 0x00, 0x01, 0x01, 0x0f, 0xff, 0xf8}, + {0x1f, 0xff, 0xf0, 0x80, 0x00, 0x40, 0x30, 0x38, 0x18, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x18, 0x38, 0x30, 0x40, 0x00, 0x80, 0xf0, 0xff, 0x1f}, + {0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x07, 0x01, 0x00, 0x00, 0x00}, +}; + +uint8_t ssd1306_gfx_smile[4][24] = { + {0x00, 0x00, 0x00, 0x80, 0xe0, 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0xe0, 0x80, 0x00, 0x00, 0x00}, + {0xf8, 0xff, 0x0f, 0x01, 0x01, 0x00, 0x00, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0x00, 0x00, 0x01, 0x01, 0x0f, 0xff, 0xf8}, + {0x1f, 0xff, 0xf0, 0x80, 0x86, 0x06, 0x18, 0x38, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x38, 0x18, 0x06, 0x86, 0x80, 0xf0, 0xff, 0x1f}, + {0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x07, 0x01, 0x00, 0x00, 0x00}, +}; + +uint8_t ssd1306_gfx_question[4][24] = { + {0x00, 0x00, 0x00, 0x80, 0xe0, 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0xe0, 0x80, 0x00, 0x00, 0x00}, + {0xf8, 0xff, 0x0f, 0x01, 0x01, 0x00, 0x00, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0x00, 0x00, 0x01, 0x01, 0x0f, 0xff, 0xf8}, + {0x1f, 0xff, 0xf0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x04, 0x26, 0xb2, 0x92, 0x1e, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xf0, 0xff, 0x1f}, + {0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x07, 0x01, 0x00, 0x00, 0x00}, +}; + +uint8_t ssd1306_gfx_wifi[8] = {0x00, 0x02, 0x0a, 0x2a, 0x2a, 0x0a, 0x02, 0x00}; + +uint8_t ssd1306_gfx_wifi_low[8] = {0x00, 0x00, 0x08, 0x28, 0x28, 0x08, 0x00, 0x00}; + +uint8_t ssd1306_gfx_wifi_lowest[8] = {0x00, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00}; + +uint8_t ssd1306_gfx_arrow_down[8] = {0x08, 0x18, 0x38, 0x78, 0x78, 0x38, 0x18, 0x08}; + +uint8_t ssd1306_gfx_arrow_left[8] = {0x00, 0x18, 0x3c, 0x7e, 0xff, 0x00, 0x00, 0x00}; + +uint8_t ssd1306_gfx_arrow_right[8] = {0x00, 0x00, 0x00, 0xff, 0x7e, 0x3c, 0x18, 0x00}; + +uint8_t ssd1306_gfx_arrow_up[8] = {0x10, 0x18, 0x1c, 0x1e, 0x1e, 0x1c, 0x18, 0x10}; + +uint8_t ssd1306_gfx_cross[8] = {0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00}; + +uint8_t ssd1306_gfx_logo[8][64] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x86, 0x06, 0x06, 0x0e, 0x0e, 0x1e, 0x1e, 0x3e, 0x3c, 0x7c, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x86, 0x06, 0x06, 0x0e, 0x0e, 0x1e, 0x1e, 0x3e, 0x3c, 0x7c, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x86, 0x06, 0x06, 0x0e, 0x0e, 0x1e, 0x1e, 0x3e, 0x3c, 0x7c, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x86, 0x06, 0x06, 0x0e, 0x0e, 0x1e, 0x1e, 0x3e, 0x3c, 0x7c, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x86, 0x06, 0x06, 0x0e, 0x0e, 0x1e, 0x1e, 0x3e, 0x3c, 0x7c, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x86, 0x06, 0x06, 0x0e, 0x0e, 0x1e, 0x1e, 0x3e, 0x3c, 0x7c, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x86, 0x06, 0x06, 0x0e, 0x0e, 0x1e, 0x1e, 0x3e, 0x3c, 0x7c, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x86, 0x06, 0x06, 0x0e, 0x0e, 0x1e, 0x1e, 0x3e, 0x3c, 0x7c, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0, 0xe0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; \ No newline at end of file diff --git a/components/ssd1306/ssd1306-gfx.h b/components/ssd1306/ssd1306-gfx.h new file mode 100644 index 0000000..887543a --- /dev/null +++ b/components/ssd1306/ssd1306-gfx.h @@ -0,0 +1,62 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/** + * @file + * + * @brief gfx elements: font, icons, elements + * + */ +#ifndef _ssd1306_GFX_H_ +#define _ssd1306_GFX_H_ + +#include + +uint8_t ssd1306_gfx_clear[8]; + +// Dogica Font https://www.dafont.com/de/dogica.font starting at space (32) +uint8_t ssd1306_gfx_font[224][8]; + +uint8_t ssd1306_gfx_button[3][64]; + +uint8_t ssd1306_gfx_button_sel[3][64]; + +uint8_t ssd1306_gfx_clock[8]; + +uint8_t ssd1306_gfx_menu_head[112]; + +uint8_t ssd1306_gfx_sad[4][24]; + +uint8_t ssd1306_gfx_smile[4][24]; + +uint8_t ssd1306_gfx_question[4][24]; + +uint8_t ssd1306_gfx_wifi[8]; + +uint8_t ssd1306_gfx_wifi_low[8]; + +uint8_t ssd1306_gfx_wifi_lowest[8]; + +uint8_t ssd1306_gfx_arrow_down[8]; + +uint8_t ssd1306_gfx_arrow_left[8]; + +uint8_t ssd1306_gfx_arrow_right[8]; + +uint8_t ssd1306_gfx_arrow_up[8]; + +uint8_t ssd1306_gfx_cross[8]; + +uint8_t ssd1306_gfx_logo[8][64]; + +#endif \ No newline at end of file diff --git a/components/ssd1306/ssd1306.c b/components/ssd1306/ssd1306.c index 53d07c4..b6a8c07 100644 --- a/components/ssd1306/ssd1306.c +++ b/components/ssd1306/ssd1306.c @@ -17,10 +17,53 @@ #include "esp_log.h" #include "i2c-main.h" -#include "ssd1306-ascii.h" +#include "ssd1306-gfx.h" #include "ssd1306.h" +uint8_t ssd1306_utf8_to_ascii_char(uint8_t ascii) +{ + static uint8_t c1; + if (ascii < 128) // Standard ASCII-set 0..0x7F handling + { + c1 = 0; + return (ascii); + } + + // get previous input + uint8_t last = c1; // get last char + c1 = ascii; // remember actual character + + switch (last) // conversion depending on first UTF8-character + { + case 0xC2: + return (ascii); + break; + case 0xC3: + return (ascii | 0xC0); + break; + case 0x82: + if (ascii == 0xAC) + return (0x80); // special case Euro-symbol + } + + return (0); // otherwise: return zero, if character has to be ignored +} + +void ssd1306_utf8_to_ascii(char *input, char *output) +{ + strcpy(output, input); + int k = 0; + char c; + for (int i = 0; i < strlen(output); i++) + { + c = ssd1306_utf8_to_ascii_char(output[i]); + if (c != 0) + output[k++] = c; + } + output[k] = 0; +} + void ssd1306_start(uint8_t i2address) { if (!i2c_is_initialized()) @@ -163,22 +206,21 @@ void ssd1306_on(uint8_t i2address, bool on) // Turn the Display OFF i2c_master_write_byte(cmd, SSD1306_CMD_OFF, true); } - + i2c_master_stop(cmd); i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); } -void ssd1306_text_line_column(uint8_t i2address, char *text, uint8_t line, uint8_t offset, bool invert) +void ssd1306_data(uint8_t i2address, uint8_t *data, size_t length, uint8_t line, uint8_t offset, bool invert) { - uint8_t font_width = sizeof(ascii_font_5x8[0]); - uint8_t column = offset * font_width; + uint8_t column = offset; if (column > SSD1306_COLUMNS) { column = 0; } - size_t columns = strlen(text) * font_width; + size_t columns = length; if (columns > (SSD1306_COLUMNS - column)) { columns = (SSD1306_COLUMNS - column); @@ -201,17 +243,11 @@ void ssd1306_text_line_column(uint8_t i2address, char *text, uint8_t line, uint8 i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); - uint8_t *linedata = calloc(columns, sizeof(uint8_t)); - for (uint8_t i = 0; i < (columns / font_width); i++) - { - memcpy(&linedata[i * font_width], ascii_font_5x8[(uint8_t)text[i]], font_width); - } - if (invert) { for (uint8_t i = 0; i < columns; i++) { - linedata[i] = ~linedata[i]; + data[i] = ~data[i]; } } @@ -220,16 +256,154 @@ void ssd1306_text_line_column(uint8_t i2address, char *text, uint8_t line, uint8 i2c_master_write_byte(cmd, (i2address << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, SSD1306_CONTROL_DATA_STREAM, true); - i2c_master_write(cmd, linedata, columns, true); + i2c_master_write(cmd, data, columns, true); i2c_master_stop(cmd); i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); +} - free(linedata); +uint8_t *ssd1306_text_to_data(char *text, size_t text_length, size_t *length) +{ + char target_text[strlen(text)]; + ssd1306_utf8_to_ascii(text, target_text); + + uint8_t font_width = sizeof(ssd1306_gfx_font[0]); + + *length = text_length * font_width; + + uint8_t *data = calloc(*length, sizeof(uint8_t)); + for (uint8_t i = 0; i < text_length; i++) + { + memcpy(&data[i * font_width], ssd1306_gfx_font[(uint8_t)target_text[i] - 32], font_width); + } + + return data; +} + +void ssd1306_chars(uint8_t i2address, char *text, size_t length, uint8_t line, uint8_t offset, bool invert) +{ + if (length > 0) + { + uint8_t font_width = sizeof(ssd1306_gfx_font[0]); + size_t res_length = 0; + uint8_t *textdata = ssd1306_text_to_data(text, length, &res_length); + ssd1306_data(i2address, textdata, res_length, line, offset * font_width, invert); + free(textdata); + } +} + +void ssd1306_text_line_column(uint8_t i2address, char *text, uint8_t line, uint8_t offset, bool invert) +{ + ssd1306_chars(i2address, text, strlen(text), line, offset, invert); } void ssd1306_text_line(uint8_t i2address, char *text, uint8_t line, bool invert) { ssd1306_text_line_column(i2address, text, line, 0, invert); +} + +void ssd1306_text_input(uint8_t i2address, char *text, uint8_t position) +{ + size_t start = 0; + if (position > 13) + { + position = 13; + start = position - 13; + } + uint8_t cur_char = (uint8_t)text[start + position] - 32; + + // arrow + ssd1306_data(i2address, ssd1306_gfx_arrow_left, 8, 2, 0, false); + // bounday + ssd1306_text_line_column(i2address, "______________", 2, 1, false); + // arrow + ssd1306_data(i2address, ssd1306_gfx_arrow_right, 8, 2, 15 * 8, false); + // text + size_t text_length = strlen(text); + if (strlen(text) > 14) + { + text_length = 14; + } + size_t length = 0; + uint8_t *textdata = ssd1306_text_to_data(text, text_length, &length); + ssd1306_data(i2address, textdata, length, 2, 8, true); + free(textdata); + // arrow + ssd1306_data(i2address, ssd1306_gfx_arrow_up, 8, 0, (position + 1) * 8, false); + // upper char + ssd1306_data(i2address, ssd1306_gfx_font[cur_char - 1], 8, 1, (position + 1) * 8, false); + // sel char + ssd1306_data(i2address, ssd1306_gfx_font[cur_char], 8, 2, (position + 1) * 8, false); + // lower char + ssd1306_data(i2address, ssd1306_gfx_font[cur_char + 1], 8, 3, (position + 1) * 8, false); + // arrow + ssd1306_data(i2address, ssd1306_gfx_arrow_down, 8, 4, (position + 1) * 8, false); +} + +void ssd1306_set_button(uint8_t i2address, char *text, bool selected, bool primary) +{ + uint8_t start = 0; + if (primary) + { + start = 64; + } + if (selected) + { + ssd1306_data(i2address, ssd1306_gfx_button_sel[0], 64, 5, start, false); + ssd1306_data(i2address, ssd1306_gfx_button_sel[1], 64, 6, start, false); + ssd1306_data(i2address, ssd1306_gfx_button_sel[2], 64, 7, start, false); + } + else + { + ssd1306_data(i2address, ssd1306_gfx_button[0], 64, 5, start, false); + ssd1306_data(i2address, ssd1306_gfx_button[1], 64, 6, start, false); + ssd1306_data(i2address, ssd1306_gfx_button[2], 64, 7, start, false); + } + // text + size_t text_length = strlen(text); + if (strlen(text) > 6) + { + text_length = 6; + } + size_t length = 0; + uint8_t *textdata = ssd1306_text_to_data(text, text_length, &length); + + uint8_t offset = 0; + if (text_length < 6) + { + offset = (6 - text_length) / 2 * 8; + } + + ssd1306_data(i2address, textdata, length, 6, start + 8 + offset, selected); + free(textdata); +} + +void ssd1306_menu_headline(uint8_t i2address, char *text, bool arrows, uint8_t line) +{ + if (arrows) + { + ssd1306_data(i2address, ssd1306_gfx_arrow_left, 8, line, 0, false); + ssd1306_data(i2address, ssd1306_gfx_arrow_right, 8, line, 15 * 8, false); + } + // bounday + ssd1306_data(i2address, ssd1306_gfx_menu_head, 112, line, 8, false); + + // text + size_t text_length = strlen(text); + if (strlen(text) > 10) + { + text_length = 10; + } + size_t length = 0; + uint8_t *textdata = ssd1306_text_to_data(text, text_length, &length); + + uint8_t offset = 0; + if (text_length < 10) + { + offset = (10 - text_length) / 2 * 8; + } + + ssd1306_data(i2address, textdata, length, line, 24 + offset, true); + free(textdata); } \ No newline at end of file diff --git a/components/ssd1306/include/ssd1306.h b/components/ssd1306/ssd1306.h similarity index 78% rename from components/ssd1306/include/ssd1306.h rename to components/ssd1306/ssd1306.h index 37a5241..3da2da2 100644 --- a/components/ssd1306/include/ssd1306.h +++ b/components/ssd1306/ssd1306.h @@ -20,6 +20,8 @@ #ifndef _ssd1306_H_ #define _ssd1306_H_ +#include "esp_system.h" + #define SSD1306_ADDRESS (0x3C) #define SSD1306_COLUMNS (128) #define SSD1306_PAGES (8) @@ -75,6 +77,8 @@ // 1. Charge Pump Command Table #define SSD1306_CMD_CHARGE_PUMP (0x8D) +void ssd1306_utf8_to_ascii(char *input, char *output); + /** * @brief initalize SSD1306 with I2C at given address * @@ -106,6 +110,11 @@ void ssd1306_clear(uint8_t i2address); */ void ssd1306_on(uint8_t i2address, bool on); +/** + * + */ +uint8_t *ssd1306_text_to_data(char *text, size_t text_length, size_t *length); + /** * @brief write raw bytes to display line at starting column * @@ -118,6 +127,18 @@ void ssd1306_on(uint8_t i2address, bool on); */ void ssd1306_data(uint8_t i2address, uint8_t *data, size_t length, uint8_t line, uint8_t offset, bool invert); +/** + * @brief write chars to display + * + * @param[in] i2address I2C address of SSD1306 + * @param[in] text text to display + * @param[in] length length of text + * @param[in] line the line to write to + * @param[in] offset number of offset chars to start + * @param[in] invert if true, image is inverted + */ +void ssd1306_chars(uint8_t i2address, char *text, size_t length, uint8_t line, uint8_t offset, bool invert); + /** * @brief write text to display line at starting column * @@ -139,4 +160,24 @@ void ssd1306_text_line_column(uint8_t i2address, char *text, uint8_t line, uint8 */ void ssd1306_text_line(uint8_t i2address, char *text, uint8_t line, bool invert); +/** + * @brief display a button element + * + * @param[in] i2address I2C address of SSD1306 + * @param[in] text button text + * @param[in] selected is button selected + * @param[in] primary is button primary + */ +void ssd1306_set_button(uint8_t i2address, char *text, bool selected, bool primary); + +/** + * @brief display a menu headline + * + * @param[in] i2address I2C address of SSD1306 + * @param[in] text headline text + * @param[in] arrows if left right arrows should be displays + * @param[in] line line the line to write to + */ +void ssd1306_menu_headline(uint8_t i2address, char *text, bool arrows, uint8_t line); + #endif \ No newline at end of file diff --git a/components/wifi-controller/CMakeLists.txt b/components/wifi-controller/CMakeLists.txt new file mode 100644 index 0000000..f5bd135 --- /dev/null +++ b/components/wifi-controller/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_component_register( + SRCS + "wifi-controller.c" + INCLUDE_DIRS "." + PRIV_REQUIRES + esp_wifi + nvs_flash +) \ No newline at end of file diff --git a/components/wifi-controller/wifi-controller.c b/components/wifi-controller/wifi-controller.c new file mode 100644 index 0000000..7395e63 --- /dev/null +++ b/components/wifi-controller/wifi-controller.c @@ -0,0 +1,185 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#include "lwip/err.h" +#include "lwip/sys.h" + +#include "wifi-controller.h" + +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +static EventGroupHandle_t s_wifi_event_group; + +static bool initialized = false; + +static wifi_ap_record_t current_wifi_ap; + +void wifi_controller_init(void) +{ + // init NVS for WIFI + esp_err_t ret; + ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ESP_ERROR_CHECK(nvs_flash_erase()); + ESP_ERROR_CHECK(nvs_flash_init()); + } + + ESP_ERROR_CHECK(esp_netif_init()); + + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); + assert(sta_netif); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + initialized = true; +} + +void wifi_controller_scan(wifi_ap_record_t *ap_info, uint16_t *ap_count) +{ + if (!initialized) + { + wifi_controller_init(); + } + + uint16_t number = 10; + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, true)); + ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info)); + ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(ap_count)); + ESP_ERROR_CHECK(esp_wifi_stop()); +} + +static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) + { + esp_wifi_connect(); + } + else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) + { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) + { + ESP_LOGD(WIFI_LOG, "got IP"); + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + heap_caps_check_integrity_all(true); + } +} + +esp_err_t wifi_controller_connect(wifi_config_t wifi_config) +{ + if (!initialized) + { + wifi_controller_init(); + } + + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip)); + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + s_wifi_event_group = xEventGroupCreate(); + + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + + ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); + vEventGroupDelete(s_wifi_event_group); + + if (bits & WIFI_CONNECTED_BIT) + { + ESP_LOGV(WIFI_LOG, "connected to ap SSID:%s", wifi_config.sta.ssid); + return ESP_OK; + } + else if (bits & WIFI_FAIL_BIT) + { + ESP_LOGI(WIFI_LOG, "Failed to connect to SSID:%s with PASSWORD:%s", wifi_config.sta.ssid, wifi_config.sta.password); + return ESP_ERR_INVALID_ARG; + } + else + { + ESP_LOGE(WIFI_LOG, "UNEXPECTED EVENT"); + return ESP_FAIL; + } +} + +esp_err_t wifi_controller_reconnect(void) +{ + if (!initialized) + { + wifi_controller_init(); + } + + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip)); + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_start()); + + s_wifi_event_group = xEventGroupCreate(); + + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + + ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); + vEventGroupDelete(s_wifi_event_group); + + if (bits & WIFI_CONNECTED_BIT) + { + ESP_LOGV(WIFI_LOG, "reconnected to last ap"); + return ESP_OK; + } + else if (bits & WIFI_FAIL_BIT) + { + ESP_LOGI(WIFI_LOG, "Failed to reconnect to last ap"); + return ESP_ERR_INVALID_ARG; + } + else + { + ESP_LOGE(WIFI_LOG, "UNEXPECTED EVENT"); + return ESP_FAIL; + } +} + +wifi_ap_record_t *wifi_controller_connection(void) +{ + if (esp_wifi_sta_get_ap_info(¤t_wifi_ap) == ESP_OK) + { + ESP_LOGD(WIFI_LOG, "Current AP: %s", current_wifi_ap.ssid); + return ¤t_wifi_ap; + } + return NULL; +} \ No newline at end of file diff --git a/components/wifi-controller/wifi-controller.h b/components/wifi-controller/wifi-controller.h new file mode 100644 index 0000000..df2c9ae --- /dev/null +++ b/components/wifi-controller/wifi-controller.h @@ -0,0 +1,56 @@ +// Copyright 2020 Lukas Haubaum +// +// Licensed under the GNU Affero General Public License, Version 3; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// https://www.gnu.org/licenses/agpl-3.0.html +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef _wifi_CONTROLLER_H_ +#define _wifi_CONTROLLER_H_ + +#include "esp_err.h" +#include "esp_wifi_types.h" + +#define WIFI_LOG "wifi-controller" // TAG for Logging + +/** + * @brief scan for WiFis + * + * @param[out] ap_info scanned APs + * @param[out] ap_count number of scanned APs + */ +void wifi_controller_scan(wifi_ap_record_t ap_info[], uint16_t *ap_count); + +/** + * @brief connect to wifi ap + * + * @param[in] wifi_config config of wifi to connect + * + * @return + * esp_err_t connection status + */ +esp_err_t wifi_controller_connect(wifi_config_t wifi_config); + +/** + * @brief reconnect to previous wifi + * + * @return + * esp_err_t connection status + */ +esp_err_t wifi_controller_reconnect(void); + +/** + * @brief reconnect to previous wifi + * + * @return + * wifi_ap_record_t pointer to current wifi connection, NULL if not connected + */ +wifi_ap_record_t *wifi_controller_connection(void); + +#endif \ No newline at end of file diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 281f413..af3e167 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,7 +1,5 @@ idf_component_register( SRCS "main.c" - "display-interface.c" - "wifi.c" INCLUDE_DIRS "" ) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild deleted file mode 100644 index 79be2f8..0000000 --- a/main/Kconfig.projbuild +++ /dev/null @@ -1,20 +0,0 @@ -menu "Wifi Setup" - - config WIFI_SSID - string "WiFi SSID" - default "myssid" - help - SSID (network name) for the example to connect to. - - config WIFI_PASSWORD - string "WiFi Password" - default "mypassword" - help - WiFi password (WPA or WPA2) for the example to use. - - config WIFI_MAXIMUM_RETRY - int "Maximum retry" - default 5 - help - Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. -endmenu \ No newline at end of file diff --git a/main/display-interface.c b/main/display-interface.c deleted file mode 100644 index aa31473..0000000 --- a/main/display-interface.c +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_system.h" -#include "esp_log.h" - -#include "ds3231.h" -#include "ena-exposure.h" -#include "interface.h" -#include "interface-menu.h" -#include "interface-datetime.h" -#include "ssd1306.h" - -void interface_display_time(void *pvParameter) -{ - static time_t curtime; - static char *curtime_text; - static struct tm rtc_time; - static bool edit_invert = false; - while (1) - { - curtime = time(NULL); - localtime_r(&curtime, &rtc_time); - curtime_text = asctime(&rtc_time); - ssd1306_text_line(SSD1306_ADDRESS, curtime_text, 0, false); - gmtime_r(&curtime, &rtc_time); - curtime_text = asctime(&rtc_time); - ssd1306_text_line(SSD1306_ADDRESS, curtime_text, 1, false); - if (interface_get_state() == INTERFACE_STATE_SET_DATETIME) - { - edit_invert = !edit_invert; - ds3231_set_time(&rtc_time); - char edit_year[4] = ""; - char edit_month[3] = ""; - char edit_day[2] = ""; - char edit_hour[2] = ""; - char edit_minute[2] = ""; - char edit_second[2] = ""; - switch (interface_datetime_state()) - { - - case INTERFACE_DATETIME_STATE_YEAR: - memcpy(&edit_year, &curtime_text[20], 4); - ssd1306_text_line_column(SSD1306_ADDRESS, edit_year, 0, 20, edit_invert); - break; - case INTERFACE_DATETIME_STATE_MONTH: - memcpy(&edit_month, &curtime_text[4], 3); - ssd1306_text_line_column(SSD1306_ADDRESS, edit_month, 0, 4, edit_invert); - break; - case INTERFACE_DATETIME_STATE_DAY: - memcpy(&edit_day, &curtime_text[8], 2); - ssd1306_text_line_column(SSD1306_ADDRESS, edit_day, 0, 8, edit_invert); - break; - case INTERFACE_DATETIME_STATE_HOUR: - memcpy(&edit_hour, &curtime_text[11], 2); - ssd1306_text_line_column(SSD1306_ADDRESS, edit_hour, 0, 11, edit_invert); - break; - case INTERFACE_DATETIME_STATE_MINUTE: - memcpy(&edit_minute, &curtime_text[14], 2); - ssd1306_text_line_column(SSD1306_ADDRESS, edit_minute, 0, 14, edit_invert); - break; - case INTERFACE_DATETIME_STATE_SECONDS: - memcpy(&edit_second[0], &curtime_text[17], 2); - ssd1306_text_line_column(SSD1306_ADDRESS, edit_second, 0, 17, edit_invert); - break; - } - } - vTaskDelay(500 / portTICK_PERIOD_MS); - } -} - -void interface_display_status(void *pvParameter) -{ - static bool get_status = true; - while (1) - { - if (interface_get_state() == INTERFACE_STATE_STATUS) - { - if (get_status) - { - ena_exposure_summary_t summary; - ena_exposure_summary(ena_exposure_default_config(), &summary); - char buffer[23]; - sprintf(buffer, "Days: %d", summary.days_since_last_exposure); - ssd1306_text_line(SSD1306_ADDRESS, buffer, 3, false); - sprintf(buffer, "Exposures: %d", summary.num_exposures); - ssd1306_text_line(SSD1306_ADDRESS, buffer, 4, false); - sprintf(buffer, "Score: %d, Max: %d", summary.risk_score_sum, summary.max_risk_score); - ssd1306_text_line(SSD1306_ADDRESS, buffer, 5, false); - get_status = false; - } - } - else if (!get_status) - { - ssd1306_clear_line(SSD1306_ADDRESS, 3, false); - ssd1306_clear_line(SSD1306_ADDRESS, 4, false); - ssd1306_clear_line(SSD1306_ADDRESS, 5, false); - get_status = true; - } - vTaskDelay(500 / portTICK_PERIOD_MS); - } -} - -void interface_display_idle(void *pvParameter) -{ - static bool set_status = true; - while (1) - { - if (interface_get_state() == INTERFACE_STATE_IDLE) - { - if (set_status) - { - ssd1306_on(SSD1306_ADDRESS, false); - set_status = false; - } - } - else if (!set_status) - { - ssd1306_on(SSD1306_ADDRESS, true); - set_status = true; - } - vTaskDelay(500 / portTICK_PERIOD_MS); - } -} - -void display_interface_start(void) -{ - ssd1306_start(SSD1306_ADDRESS); - ssd1306_clear(SSD1306_ADDRESS); - - interface_start(); - interface_menu_start(); - - xTaskCreate(&interface_display_time, "interface_display_time", 4096, NULL, 5, NULL); - xTaskCreate(&interface_display_status, "interface_display_status", 4096, NULL, 5, NULL); - xTaskCreate(&interface_display_idle, "interface_display_idle", 4096, NULL, 5, NULL); -} \ No newline at end of file diff --git a/main/display-interface.h b/main/display-interface.h deleted file mode 100644 index 25fedfe..0000000 --- a/main/display-interface.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef _display_interface_H_ -#define _display_interface_H_ - -/** - * @brief start display + interface - */ -void display_interface_start(void); - -#endif \ No newline at end of file diff --git a/main/main.c b/main/main.c index 8d681b5..7abf363 100644 --- a/main/main.c +++ b/main/main.c @@ -25,16 +25,17 @@ #include "ena-exposure.h" #include "ena-bluetooth-advertise.h" #include "ena-bluetooth-scan.h" -#include "interface.h" #include "ena-cwa.h" +#include "interface.h" #include "ds3231.h" -#include "display-interface.h" -#include "wifi.h" +#include "ssd1306.h" +#include "wifi-controller.h" #include "sdkconfig.h" void app_main(void) { + // debug only own LOG TAGs esp_log_level_set("*", ESP_LOG_WARN); esp_log_level_set(ENA_LOG, ESP_LOG_DEBUG); @@ -47,6 +48,9 @@ void app_main(void) esp_log_level_set(INTERFACE_LOG, ESP_LOG_DEBUG); esp_log_level_set(WIFI_LOG, ESP_LOG_DEBUG); + // start interface + interface_start(); + // set system time from DS3231 struct tm rtc_time; ds3231_get_time(&rtc_time); @@ -62,11 +66,13 @@ void app_main(void) ena_start(); - display_interface_start(); - + // start with main interface + interface_main_start(); + while (1) { ena_run(); + ena_cwa_run(); vTaskDelay(1000 / portTICK_PERIOD_MS); } } diff --git a/main/wifi.c b/main/wifi.c deleted file mode 100644 index 3a860e5..0000000 --- a/main/wifi.c +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_event.h" -#include "esp_log.h" -#include "nvs_flash.h" - -#include "lwip/err.h" -#include "lwip/sys.h" - -#include "ena-cwa.h" - -#include "wifi.h" - -#define WIFI_CONNECTED_BIT BIT0 -#define WIFI_FAIL_BIT BIT1 - -static EventGroupHandle_t s_wifi_event_group; - -static int s_retry_num = 0; - -static bool connected = false; - -void wifi_stop(void) -{ - connected = false; - esp_wifi_stop(); - esp_wifi_deinit(); - esp_event_loop_delete_default(); -} - -static void event_handler(void *arg, esp_event_base_t event_base, - int32_t event_id, void *event_data) -{ - if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) - { - esp_wifi_connect(); - } - else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) - { - if (s_retry_num < WIFI_MAXIMUM_RETRY) - { - esp_wifi_connect(); - s_retry_num++; - ESP_LOGD(WIFI_LOG, "retry to connect to the AP"); - } - else - { - xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); - } - ESP_LOGD(WIFI_LOG, "connect to the AP fail"); - connected = false; - } - else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) - { - s_retry_num = 0; - xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); - connected = true; - - heap_caps_check_integrity_all(true); - } - else - { - ESP_LOGD(WIFI_LOG, "eventbase %s, eventid %d", event_base, event_id); - } -} - -void wifi_start(void) -{ - - // init NVS for WIFI - esp_err_t ret; - ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) - { - ESP_ERROR_CHECK(nvs_flash_erase()); - ESP_ERROR_CHECK(nvs_flash_init()); - } - - s_wifi_event_group = xEventGroupCreate(); - - ESP_ERROR_CHECK(esp_event_loop_create_default()); - - ESP_ERROR_CHECK(esp_netif_init()); - esp_netif_create_default_wifi_sta(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - esp_event_handler_instance_t instance_any_id; - esp_event_handler_instance_t instance_got_ip; - ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, - ESP_EVENT_ANY_ID, - &event_handler, - NULL, - &instance_any_id)); - ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, - IP_EVENT_STA_GOT_IP, - &event_handler, - NULL, - &instance_got_ip)); - - wifi_config_t wifi_config = { - .sta = { - .ssid = WIFI_SSID, - .password = WIFI_PASSWORD, - /* Setting a password implies station will connect to all security modes including WEP/WPA. - * However these modes are deprecated and not advisable to be used. Incase your Access point - * doesn't support WPA2, these mode can be enabled by commenting below line */ - .threshold.authmode = WIFI_AUTH_WPA2_PSK, - - .pmf_cfg = { - .capable = true, - .required = false}, - }, - }; - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); - ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); - ESP_ERROR_CHECK(esp_wifi_start()); - - /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum - * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ - EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, - WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, - pdFALSE, - pdFALSE, - portMAX_DELAY); - - /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually - * happened. */ - if (bits & WIFI_CONNECTED_BIT) - { - ESP_LOGV(WIFI_LOG, "connected to ap SSID:%s", - WIFI_SSID); - } - else if (bits & WIFI_FAIL_BIT) - { - ESP_LOGI(WIFI_LOG, "Failed to connect to SSID:%s", - WIFI_SSID); - } - else - { - ESP_LOGE(WIFI_LOG, "UNEXPECTED EVENT"); - } - - /* The event will not be processed after unregister */ - ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); - ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); - vEventGroupDelete(s_wifi_event_group); -} - -bool wifi_is_connected(void) -{ - return connected; -} \ No newline at end of file diff --git a/main/wifi.h b/main/wifi.h deleted file mode 100644 index fa2600b..0000000 --- a/main/wifi.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2020 Lukas Haubaum -// -// Licensed under the GNU Affero General Public License, Version 3; -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// https://www.gnu.org/licenses/agpl-3.0.html -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef _wifi_H_ -#define _wifi_H_ - -#define WIFI_LOG "ESP-ENA-wifi" // TAG for Logging - -#define WIFI_SSID (CONFIG_WIFI_SSID) -#define WIFI_PASSWORD (CONFIG_WIFI_PASSWORD) -#define WIFI_MAXIMUM_RETRY (CONFIG_WIFI_MAXIMUM_RETRY) - -/** - * @brief start wifi connection to configured AP - */ -void wifi_start(void); - -/** - * @brief stop wifi (restart does not work for now!) - */ -void wifi_stop(void); - -/** - * @brief check if a wifi is connected - */ -bool wifi_is_connected(void); - -#endif \ No newline at end of file