├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── components ├── display │ ├── CMakeLists.txt │ ├── custom-ssd1306 │ │ ├── ssd1306.c │ │ └── ssd1306.h │ ├── display-gfx.c │ ├── display-gfx.h │ ├── display.c │ ├── display.h │ ├── dummy.c │ ├── m5-axp192 │ │ ├── axp192.c │ │ └── axp192.h │ ├── m5-st7735s │ │ ├── st7735s.c │ │ └── st7735s.h │ ├── m5-st7789 │ │ ├── st7789.c │ │ └── st7789.h │ └── ttgo-st7735 │ │ ├── st7735.c │ │ └── st7735.h ├── ena-eke-proxy │ ├── CMakeLists.txt │ ├── Kconfig.projbuild │ ├── certs │ │ └── cwa-proxy.champonthis.de.pem │ ├── ena-eke-proxy.c │ └── ena-eke-proxy.h ├── ena │ ├── CMakeLists.txt │ ├── Kconfig.projbuild │ ├── ena-beacons.c │ ├── ena-bluetooth-advertise.c │ ├── ena-bluetooth-scan.c │ ├── ena-crypto.c │ ├── ena-exposure.c │ ├── ena-storage.c │ ├── ena.c │ └── include │ │ ├── ena-beacons.h │ │ ├── ena-bluetooth-advertise.h │ │ ├── ena-bluetooth-scan.h │ │ ├── ena-crypto.h │ │ ├── ena-exposure.h │ │ ├── ena-storage.h │ │ └── ena.h ├── i2c-main │ ├── CMakeLists.txt │ ├── i2c-main.c │ └── i2c-main.h ├── interface │ ├── CMakeLists.txt │ ├── Kconfig.projbuild │ ├── custom-input │ │ ├── custom-input.c │ │ └── custom-input.h │ ├── dummy.c │ ├── interface-data.c │ ├── interface-datetime.c │ ├── interface-info.c │ ├── interface-input.c │ ├── interface-label.c │ ├── interface-main.c │ ├── interface-report.c │ ├── interface-settings.c │ ├── interface-wifi.c │ ├── interface.c │ ├── interface.h │ ├── m5-input │ │ ├── m5-input.c │ │ └── m5-input.h │ ├── m5-mpu6886 │ │ ├── mpu6886.c │ │ └── mpu6886.h │ ├── ttgo-input │ │ ├── ttgo-input.c │ │ └── ttgo-input.h │ └── ttgo-lsm9ds1 │ │ ├── lsm9ds1.c │ │ └── lsm9ds1.h ├── rtc │ ├── CMakeLists.txt │ ├── custom-ds3231 │ │ ├── ds3231.c │ │ └── ds3231.h │ ├── dummy.c │ ├── m5-bm8563 │ │ ├── bm8563.c │ │ └── bm8563.h │ └── rtc.h └── wifi-controller │ ├── CMakeLists.txt │ ├── wifi-controller.c │ └── wifi-controller.h ├── main ├── CMakeLists.txt └── main.c └── partitions.csv /.gitignore: -------------------------------------------------------------------------------- 1 | .config 2 | *.o 3 | *.pyc 4 | 5 | # emacs 6 | .dir-locals.el 7 | 8 | # emacs temp file suffixes 9 | *~ 10 | .#* 11 | \#*# 12 | 13 | # eclipse setting 14 | .settings 15 | 16 | # MacOS directory files 17 | .DS_Store 18 | 19 | # Doc build artifacts 20 | docs 21 | 22 | # VS Code Settings 23 | .vscode/ 24 | 25 | # VIM files 26 | *.swp 27 | *.swo 28 | 29 | # Clion IDE CMake build & config 30 | .idea/ 31 | cmake-build-*/ 32 | 33 | # Results for the checking of the Python coding style and static analysis 34 | .mypy_cache 35 | flake8_output.txt 36 | 37 | # ESP-IDF default build directory name 38 | build 39 | 40 | # lock files for examples and components 41 | dependencies.lock 42 | 43 | sdkconfig 44 | sdkconfig.old 45 | 46 | .local 47 | 48 | cert.pem -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 5 | project(esp-ena-device) -------------------------------------------------------------------------------- /components/display/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(src_list "display.c" "display-gfx.c") 2 | set(include_list ".") 3 | 4 | if(CONFIG_ENA_INTERFACE_DISPLAY_SSD1306) 5 | list(APPEND src_list "custom-ssd1306/ssd1306.c") 6 | list(APPEND include_list "custom-ssd1306") 7 | elseif(CONFIG_ENA_INTERFACE_M5STICKC) 8 | list(APPEND src_list "m5-st7735s/st7735s.c" "m5-axp192/axp192.c") 9 | list(APPEND include_list "m5-st7735s" "m5-axp192") 10 | elseif(CONFIG_ENA_INTERFACE_M5STICKC_PLUS) 11 | list(APPEND src_list "m5-st7789/st7789.c" "m5-axp192/axp192.c") 12 | list(APPEND include_list "m5-st7789" "m5-axp192") 13 | elseif(CONFIG_ENA_INTERFACE_TTGO_T_WRISTBAND) 14 | list(APPEND src_list "ttgo-st7735/st7735.c") 15 | list(APPEND include_list "ttgo-st7735") 16 | else() 17 | list(APPEND src_list "dummy.c") 18 | endif() 19 | 20 | idf_component_register( 21 | SRCS 22 | ${src_list} 23 | INCLUDE_DIRS 24 | ${include_list} 25 | PRIV_REQUIRES 26 | "i2c-main" 27 | "spi_flash" 28 | ) -------------------------------------------------------------------------------- /components/display/custom-ssd1306/ssd1306.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief I2C driver for SSD1306 display 18 | * 19 | */ 20 | #ifndef _ssd1306_H_ 21 | #define _ssd1306_H_ 22 | 23 | #include "esp_system.h" 24 | 25 | #define SSD1306_ADDRESS (0x3C) 26 | #define SSD1306_COLUMNS (128) 27 | #define SSD1306_PAGES (8) 28 | 29 | // Write mode for I2C https://robotcantalk.blogspot.com/2015/03/interfacing-arduino-with-ssd1306-driven.html 30 | #define SSD1306_CONTROL_CMD_BYTE (0x80) 31 | #define SSD1306_CONTROL_CMD_STREAM (0x00) 32 | #define SSD1306_CONTROL_DATA_BYTE (0xC0) 33 | #define SSD1306_CONTROL_DATA_STREAM (0x40) 34 | 35 | // 1. Fundamental Command Table 36 | #define SSD1306_CMD_CONTRAST (0x81) 37 | #define SSD1306_CMD_RAM (0xA4) 38 | #define SSD1306_CMD_ALL_ON (0xA5) 39 | #define SSD1306_CMD_NORMAL (0xA6) 40 | #define SSD1306_CMD_INVERSE (0xA7) 41 | #define SSD1306_CMD_OFF (0xAE) 42 | #define SSD1306_CMD_ON (0xAF) 43 | 44 | // 2. Scrolling Command Table 45 | #define SSD1306_CMD_SCROLL_HORI_RIGHT (0x26) 46 | #define SSD1306_CMD_SCROLL_HORI_LEFT (0x27) 47 | #define SSD1306_CMD_SCROLL_VERT_RIGHT (0x29) 48 | #define SSD1306_CMD_SCROLL_VERT_LEFT (0x2A) 49 | #define SSD1306_CMD_SCROLL_STOP (0x2E) 50 | #define SSD1306_CMD_SCROLL_START (0x2F) 51 | #define SSD1306_CMD_SCROLL_VERT_AREA (0xA3) 52 | 53 | // 3. Addressing Setting Command Table 54 | #define SSD1306_CMD_COLUMN_LOW (0x00) 55 | #define SSD1306_CMD_COLUMN_HIGH (0x10) 56 | #define SSD1306_CMD_MEMORY_MODE (0x20) 57 | #define SSD1306_CMD_COLUMN_ADDRESS (0x21) 58 | #define SSD1306_CMD_PAGE_ADDRESS (0x22) 59 | #define SSD1306_CMD_PAGE (0xB0) 60 | 61 | // 4. Hardware Configuration (Panel resolution & layout related) Command Table 62 | #define SSD1306_CMD_START_LINE (0x40) 63 | #define SSD1306_CMD_SEGMENT_LOW (0xA0) 64 | #define SSD1306_CMD_SEGMENT_HIGH (0xA1) 65 | #define SSD1306_CMD_MULTIPLEX_RATIO (0xA8) 66 | #define SSD1306_CMD_SCAN_DIRECTION_NORMAL (0xC0) 67 | #define SSD1306_CMD_SCAN_DIRECTION_REMAPPED (0xC8) 68 | #define SSD1306_CMD_OFFSET (0xD3) 69 | #define SSD1306_CMD_COM_PINS (0xDA) 70 | 71 | // 5. Timing & Driving Scheme Setting Command Table 72 | #define SSD1306_CMD_CLOCK (0xD5) 73 | #define SSD1306_CMD_PRE_CHARGE_PERIOD (0xD9) 74 | #define SSD1306_CMD_VCOMH (0xDB) 75 | #define SSD1306_CMD_NOP (0xE3) 76 | 77 | // 1. Charge Pump Command Table 78 | #define SSD1306_CMD_CHARGE_PUMP (0x8D) 79 | 80 | #endif -------------------------------------------------------------------------------- /components/display/display-gfx.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief gfx elements: font, icons, elements 18 | * 19 | */ 20 | #ifndef _display_GFX_H_ 21 | #define _display_GFX_H_ 22 | 23 | #include 24 | 25 | uint8_t display_gfx_clear[8]; 26 | 27 | // Dogica Font https://www.dafont.com/de/dogica.font starting at space (32) 28 | uint8_t display_gfx_font[224][8]; 29 | 30 | uint8_t display_gfx_button[3][64]; 31 | 32 | uint8_t display_gfx_button_sel[3][64]; 33 | 34 | uint8_t display_gfx_clock[8]; 35 | 36 | uint8_t display_gfx_menu_head[112]; 37 | 38 | uint8_t display_gfx_sad[4][24]; 39 | 40 | uint8_t display_gfx_smile[4][24]; 41 | 42 | uint8_t display_gfx_question[4][24]; 43 | 44 | uint8_t display_gfx_wifi[8]; 45 | 46 | uint8_t display_gfx_wifi_low[8]; 47 | 48 | uint8_t display_gfx_wifi_lowest[8]; 49 | 50 | uint8_t display_gfx_arrow_down[8]; 51 | 52 | uint8_t display_gfx_arrow_left[8]; 53 | 54 | uint8_t display_gfx_arrow_right[8]; 55 | 56 | uint8_t display_gfx_arrow_up[8]; 57 | 58 | uint8_t display_gfx_cross[8]; 59 | 60 | uint8_t display_gfx_logo[8][64]; 61 | 62 | #endif -------------------------------------------------------------------------------- /components/display/display.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | 16 | #include "display.h" 17 | #include "display-gfx.h" 18 | 19 | static uint16_t current_color = WHITE; 20 | 21 | uint8_t display_utf8_to_ascii_char(uint8_t ascii) 22 | { 23 | static uint8_t c1; 24 | if (ascii < 128) // Standard ASCII-set 0..0x7F handling 25 | { 26 | c1 = 0; 27 | return (ascii); 28 | } 29 | 30 | // get previous input 31 | uint8_t last = c1; // get last char 32 | c1 = ascii; // remember actual character 33 | 34 | switch (last) // conversion depending on first UTF8-character 35 | { 36 | case 0xC2: 37 | return (ascii); 38 | break; 39 | case 0xC3: 40 | return (ascii | 0xC0); 41 | break; 42 | case 0x82: 43 | if (ascii == 0xAC) 44 | return (0x80); // special case Euro-symbol 45 | } 46 | 47 | return (0); // otherwise: return zero, if character has to be ignored 48 | } 49 | 50 | void display_utf8_to_ascii(char *input, char *output) 51 | { 52 | strcpy(output, input); 53 | int k = 0; 54 | char c; 55 | for (int i = 0; i < strlen(output); i++) 56 | { 57 | c = display_utf8_to_ascii_char(output[i]); 58 | if (c != 0) 59 | output[k++] = c; 60 | } 61 | output[k] = 0; 62 | } 63 | 64 | uint8_t *display_text_to_data(char *text, size_t text_length, size_t *length) 65 | { 66 | char target_text[strlen(text)]; 67 | display_utf8_to_ascii(text, target_text); 68 | 69 | uint8_t font_width = sizeof(display_gfx_font[0]); 70 | 71 | *length = text_length * font_width; 72 | 73 | uint8_t *data = calloc(*length, sizeof(uint8_t)); 74 | for (uint8_t i = 0; i < text_length; i++) 75 | { 76 | memcpy(&data[i * font_width], display_gfx_font[(uint8_t)target_text[i] - 32], font_width); 77 | } 78 | 79 | return data; 80 | } 81 | 82 | void display_chars(char *text, size_t length, uint8_t line, uint8_t offset, bool invert) 83 | { 84 | if (length > 0) 85 | { 86 | uint8_t font_width = sizeof(display_gfx_font[0]); 87 | size_t res_length = 0; 88 | uint8_t *textdata = display_text_to_data(text, length, &res_length); 89 | display_data(textdata, res_length, line, offset * font_width, invert); 90 | free(textdata); 91 | } 92 | } 93 | 94 | void display_text_line_column(char *text, uint8_t line, uint8_t offset, bool invert) 95 | { 96 | display_chars(text, strlen(text), line, offset, invert); 97 | } 98 | 99 | void display_text_line(char *text, uint8_t line, bool invert) 100 | { 101 | display_text_line_column(text, line, 0, invert); 102 | } 103 | 104 | void display_text_input(char *text, uint8_t position) 105 | { 106 | size_t start = 0; 107 | if (position > 13) 108 | { 109 | position = 13; 110 | start = position - 13; 111 | } 112 | uint8_t cur_char = (uint8_t)text[start + position] - 32; 113 | 114 | // arrow 115 | display_data(display_gfx_arrow_left, 8, 2, 0, false); 116 | // bounday 117 | display_text_line_column("______________", 2, 1, false); 118 | // arrow 119 | display_data(display_gfx_arrow_right, 8, 2, 15 * 8, false); 120 | // text 121 | size_t text_length = strlen(text); 122 | if (strlen(text) > 14) 123 | { 124 | text_length = 14; 125 | } 126 | size_t length = 0; 127 | uint8_t *textdata = display_text_to_data(text, text_length, &length); 128 | display_data(textdata, length, 2, 8, true); 129 | free(textdata); 130 | // arrow 131 | display_data(display_gfx_arrow_up, 8, 0, (position + 1) * 8, false); 132 | // upper char 133 | display_data(display_gfx_font[cur_char - 1], 8, 1, (position + 1) * 8, false); 134 | // sel char 135 | display_data(display_gfx_font[cur_char], 8, 2, (position + 1) * 8, false); 136 | // lower char 137 | display_data(display_gfx_font[cur_char + 1], 8, 3, (position + 1) * 8, false); 138 | // arrow 139 | display_data(display_gfx_arrow_down, 8, 4, (position + 1) * 8, false); 140 | } 141 | 142 | void display_set_button(char *text, bool selected, bool primary) 143 | { 144 | uint8_t start = 0; 145 | if (primary) 146 | { 147 | start = 64; 148 | } 149 | if (selected) 150 | { 151 | display_data(display_gfx_button_sel[0], 64, 5, start, false); 152 | display_data(display_gfx_button_sel[1], 64, 6, start, false); 153 | display_data(display_gfx_button_sel[2], 64, 7, start, false); 154 | } 155 | else 156 | { 157 | display_data(display_gfx_button[0], 64, 5, start, false); 158 | display_data(display_gfx_button[1], 64, 6, start, false); 159 | display_data(display_gfx_button[2], 64, 7, start, false); 160 | } 161 | // text 162 | size_t text_length = strlen(text); 163 | if (strlen(text) > 6) 164 | { 165 | text_length = 6; 166 | } 167 | size_t length = 0; 168 | uint8_t *textdata = display_text_to_data(text, text_length, &length); 169 | 170 | uint8_t offset = 0; 171 | if (text_length < 6) 172 | { 173 | offset = (6 - text_length) / 2 * 8; 174 | } 175 | 176 | display_data(textdata, length, 6, start + 8 + offset, selected); 177 | free(textdata); 178 | } 179 | 180 | void display_menu_headline(char *text, bool arrows, uint8_t line) 181 | { 182 | if (arrows) 183 | { 184 | display_data(display_gfx_arrow_left, 8, line, 0, false); 185 | display_data(display_gfx_arrow_right, 8, line, 15 * 8, false); 186 | } 187 | // bounday 188 | display_data(display_gfx_menu_head, 112, line, 8, false); 189 | 190 | // text 191 | size_t text_length = strlen(text); 192 | if (strlen(text) > 10) 193 | { 194 | text_length = 10; 195 | } 196 | size_t length = 0; 197 | uint8_t *textdata = display_text_to_data(text, text_length, &length); 198 | 199 | uint8_t offset = 0; 200 | if (text_length < 10) 201 | { 202 | offset = (10 - text_length) / 2 * 8; 203 | } 204 | 205 | display_data(textdata, length, line, 24 + offset, true); 206 | free(textdata); 207 | } 208 | 209 | void display_set_color(uint16_t color) 210 | { 211 | current_color = color; 212 | } 213 | 214 | uint16_t display_get_color(void) 215 | { 216 | return current_color; 217 | } -------------------------------------------------------------------------------- /components/display/display.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief interface for display 18 | * 19 | */ 20 | #ifndef _display_H_ 21 | #define _display_H_ 22 | 23 | #include "esp_system.h" 24 | 25 | #define DISPLAY_COLUMNS 8 26 | 27 | #define BLACK 0x0000 28 | #define WHITE 0xffff 29 | #define RED 0xf800 30 | #define GREEN 0x07e0 31 | #define BLUE 0x001f 32 | #define GRAY 0x8c51 33 | #define YELLOW 0xFFE0 34 | #define CYAN 0x07FF 35 | #define PURPLE 0xF81F 36 | 37 | /** 38 | * @brief initalize display 39 | * 40 | * MUST BE DEFINED DEVICE SPECIFIC 41 | * 42 | */ 43 | void display_start(void); 44 | 45 | /** 46 | * @brief clear the display 47 | * 48 | * MUST BE DEFINED DEVICE SPECIFIC 49 | * 50 | * @param[in] line the line to clear 51 | * @param[in] invert if true, image is inverted 52 | */ 53 | void display_clear_line(uint8_t line, bool invert); 54 | 55 | /** 56 | * @brief clear the display 57 | * 58 | * MUST BE DEFINED DEVICE SPECIFIC 59 | * 60 | */ 61 | void display_clear(void); 62 | 63 | /** 64 | * @brief set display on or off 65 | * 66 | * MUST BE DEFINED DEVICE SPECIFIC 67 | * 68 | * @param[in] on true if display on, false if display off 69 | */ 70 | void display_on(bool on); 71 | 72 | /** 73 | * @brief set display flipped or not 74 | * 75 | * MUST BE DEFINED DEVICE SPECIFIC 76 | * 77 | * @param[in] on true display is flipped 78 | */ 79 | void display_flipped(bool flipped); 80 | 81 | /** 82 | * @brief write raw bytes to display line at starting column 83 | * 84 | * MUST BE DEFINED DEVICE SPECIFIC 85 | * 86 | * @param[in] data bytes to display 87 | * @param[in] length length of data 88 | * @param[in] line the line to write to 89 | * @param[in] offset number of offset chars to start 90 | * @param[in] invert if true, image is inverted 91 | */ 92 | void display_data(uint8_t *data, size_t length, uint8_t line, uint8_t offset, bool invert); 93 | 94 | /** 95 | * 96 | */ 97 | void display_utf8_to_ascii(char *input, char *output); 98 | 99 | /** 100 | * 101 | */ 102 | uint8_t *display_text_to_data(char *text, size_t text_length, size_t *length); 103 | 104 | /** 105 | * 106 | */ 107 | uint8_t *display_text_to_data(char *text, size_t text_length, size_t *length); 108 | 109 | /** 110 | * @brief write chars to display 111 | * 112 | * @param[in] text text to display 113 | * @param[in] length length of text 114 | * @param[in] line the line to write to 115 | * @param[in] offset number of offset chars to start 116 | * @param[in] invert if true, image is inverted 117 | */ 118 | void display_chars(char *text, size_t length, uint8_t line, uint8_t offset, bool invert); 119 | 120 | /** 121 | * @brief write text to display line at starting column 122 | * 123 | * @param[in] text text to display 124 | * @param[in] line the line to write to 125 | * @param[in] offset number of offset chars to start 126 | * @param[in] invert if true, image is inverted 127 | */ 128 | void display_text_line_column(char *text, uint8_t line, uint8_t offset, bool invert); 129 | 130 | /** 131 | * @brief write text to display line 132 | * 133 | * @param[in] text text to display 134 | * @param[in] line the line to write to 135 | * @param[in] invert if true, image is inverted 136 | */ 137 | void display_text_line(char *text, uint8_t line, bool invert); 138 | 139 | /** 140 | * @brief display a button element 141 | * 142 | * @param[in] text button text 143 | * @param[in] selected is button selected 144 | * @param[in] primary is button primary 145 | */ 146 | void display_set_button(char *text, bool selected, bool primary); 147 | 148 | /** 149 | * @brief display a menu headline 150 | * 151 | * @param[in] text headline text 152 | * @param[in] arrows if left right arrows should be displays 153 | * @param[in] line line the line to write to 154 | */ 155 | void display_menu_headline(char *text, bool arrows, uint8_t line); 156 | 157 | /** 158 | * @brief set current color for colored displays 159 | * 160 | * @param[in] color the color to set 161 | */ 162 | void display_set_color(uint16_t color); 163 | 164 | /** 165 | * @brief set current color for colored displays 166 | * 167 | * @return the current color 168 | */ 169 | uint16_t display_get_color(void); 170 | 171 | #endif -------------------------------------------------------------------------------- /components/display/dummy.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include "display.h" 16 | 17 | void display_start(void) 18 | { 19 | } 20 | 21 | void display_clear_line(uint8_t line, bool invert) 22 | { 23 | } 24 | 25 | void display_clear(void) 26 | { 27 | } 28 | 29 | void display_on(bool on) 30 | { 31 | } 32 | 33 | void display_data(uint8_t *data, size_t length, uint8_t line, uint8_t offset, bool invert) 34 | { 35 | } 36 | 37 | void display_flipped(bool flipped) 38 | { 39 | } -------------------------------------------------------------------------------- /components/display/m5-axp192/axp192.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #ifndef _PMW_AXP192_H_ 15 | #define _PWM_AXP192_H_ 16 | 17 | #include "stdio.h" 18 | 19 | #define AXP192_ADDRESS 0x34 20 | 21 | void axp192_start(void); 22 | void axp192_screen_breath(uint8_t brightness); 23 | bool axp192_get_bat_state(); 24 | 25 | void axp192_enable_coulombcounter(void); 26 | void axp192_disable_coulombcounter(void); 27 | void axp192_stop_coulombcounter(void); 28 | void axp192_clear_coulombcounter(void); 29 | uint32_t axp192_get_coulombcharge_data(void); 30 | uint32_t axp192_get_coulombdischarge_data(void); 31 | float axp192_get_coulomb_data(void); 32 | 33 | uint8_t axp192_get_btn_press(void); 34 | 35 | // -- sleep 36 | void axp192_set_sleep(void); 37 | void axp192_deep_sleep(uint64_t time_in_us); 38 | void axp192_light_sleep(uint64_t time_in_us); 39 | uint8_t axp192_get_warning_leve(void); 40 | 41 | float axp192_get_bat_voltage(); 42 | float axp192_get_bat_current(); 43 | float axp192_get_vin_voltage(); 44 | float axp192_get_vin_current(); 45 | float axp192_get_vbus_voltage(); 46 | float axp192_get_vbus_current(); 47 | float axp192_get_temp(); 48 | float axp192_get_bat_power(); 49 | float axp192_get_bat_charge_current(); 50 | float axp192_get_aps_voltage(); 51 | float axp192_get_bat_coulomb_input(); 52 | float axp192_get_bat_coulomb_out(); 53 | uint8_t axp192_get_warning_level(void); 54 | void axp192_set_coulomb_clear(); 55 | void axp192_set_ldo2(bool state); 56 | 57 | void axp192_write_byte(uint8_t addr, uint8_t data); 58 | uint8_t axp192_read_8bit(uint8_t addr); 59 | uint16_t axp192_read_12bit(uint8_t addr); 60 | uint16_t axp192_read_13bit(uint8_t addr); 61 | uint16_t axp192_read_16bit(uint8_t addr); 62 | uint32_t axp192_read_24bit(uint8_t addr); 63 | uint32_t axp192_read_32bit(uint8_t addr); 64 | void axp192_read_buff(uint8_t addr, uint8_t size, uint8_t *buff); 65 | 66 | #endif -------------------------------------------------------------------------------- /components/display/m5-st7735s/st7735s.h: -------------------------------------------------------------------------------- 1 | #ifndef DISPLAY_ST7735S_H_ 2 | #define DISPLAY_ST7735S_H_ 3 | 4 | #include "driver/spi_master.h" 5 | 6 | // M5stickC 7 | #define M5_ST7735S_WIDTH 160 8 | #define M5_ST7735S_HEIGHT 80 9 | #define M5_ST7735S_MOSI_GPIO 15 10 | #define M5_ST7735S_SCLK_GPIO 13 11 | #define M5_ST7735S_CS_GPIO 5 12 | #define M5_ST7735S_DC_GPIO 23 13 | #define M5_ST7735S_RESET_GPIO 18 14 | 15 | #define M5_ST7735S_OFFSETX 1 16 | #define M5_ST7735S_OFFSETY 26 17 | 18 | #define M5_ST7735S_INTERFACE_OFFSETX 16 19 | #define M5_ST7735S_INTERFACE_OFFSETY 8 20 | 21 | #define M5_ST7735S_LANDSCAPE_FLIPPED 0x68 22 | #define M5_ST7735S_LANDSCAPE 0xA8 23 | 24 | #define SPI_COMMAND_MODE 0 25 | #define SPI_DATA_MODE 1 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /components/display/m5-st7789/st7789.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | 17 | #include "freertos/FreeRTOS.h" 18 | #include "freertos/task.h" 19 | 20 | #include 21 | #include 22 | #include "esp_log.h" 23 | 24 | #include "display.h" 25 | #include "display-gfx.h" 26 | 27 | #include "axp192.h" 28 | 29 | #include "st7789.h" 30 | 31 | static spi_device_handle_t st7789_handle; 32 | 33 | bool spi_master_write(uint8_t *data, size_t len, uint8_t dc) 34 | { 35 | spi_transaction_t spi_trans; 36 | 37 | gpio_set_level(M5_ST7789_DC_GPIO, dc); 38 | 39 | memset(&spi_trans, 0, sizeof(spi_transaction_t)); 40 | spi_trans.length = len * 8; 41 | spi_trans.tx_buffer = data; 42 | spi_device_transmit(st7789_handle, &spi_trans); 43 | 44 | return true; 45 | } 46 | 47 | bool spi_master_write_command(uint8_t cmd) 48 | { 49 | return spi_master_write(&cmd, 1, SPI_COMMAND_MODE); 50 | } 51 | 52 | bool spi_master_write_data_byte(uint8_t data) 53 | { 54 | return spi_master_write(&data, 1, SPI_DATA_MODE); 55 | } 56 | 57 | bool spi_master_write_data(uint8_t *data, size_t len) 58 | { 59 | return spi_master_write(data, len, SPI_DATA_MODE); 60 | } 61 | 62 | bool spi_master_write_addr(uint16_t addr1, uint16_t addr2) 63 | { 64 | uint8_t data[4]; 65 | data[0] = (addr1 >> 8) & 0xFF; 66 | data[1] = addr1 & 0xFF; 67 | data[2] = (addr2 >> 8) & 0xFF; 68 | data[3] = addr2 & 0xFF; 69 | 70 | return spi_master_write_data(data, 4); 71 | } 72 | 73 | bool spi_master_write_color(uint16_t color, size_t size) 74 | { 75 | uint8_t data[size * 2]; 76 | int index = 0; 77 | 78 | uint8_t msbColor = (color >> 8) & 0xFF; 79 | uint8_t lsbColor = color & 0xFF; 80 | 81 | for (int i = 0; i < size; i++) 82 | { 83 | data[index++] = msbColor; 84 | data[index++] = lsbColor; 85 | } 86 | return spi_master_write_data(data, size * 2); 87 | } 88 | 89 | bool spi_master_write_colors(uint16_t *colors, size_t size) 90 | { 91 | uint8_t data[size * 2]; 92 | int index = 0; 93 | for (int i = 0; i < size; i++) 94 | { 95 | data[index++] = (colors[i] >> 8) & 0xFF; 96 | data[index++] = colors[i] & 0xFF; 97 | } 98 | return spi_master_write_data(data, size * 2); 99 | } 100 | 101 | void display_start(void) 102 | { 103 | 104 | axp192_start(); 105 | axp192_screen_breath(0); 106 | 107 | esp_err_t ret; 108 | 109 | gpio_pad_select_gpio(M5_ST7789_CS_GPIO); 110 | gpio_set_direction(M5_ST7789_CS_GPIO, GPIO_MODE_OUTPUT); 111 | gpio_set_level(M5_ST7789_CS_GPIO, 0); 112 | 113 | gpio_pad_select_gpio(M5_ST7789_DC_GPIO); 114 | gpio_set_direction(M5_ST7789_DC_GPIO, GPIO_MODE_OUTPUT); 115 | gpio_set_level(M5_ST7789_DC_GPIO, 0); 116 | 117 | gpio_pad_select_gpio(M5_ST7789_RESET_GPIO); 118 | gpio_set_direction(M5_ST7789_RESET_GPIO, GPIO_MODE_OUTPUT); 119 | gpio_set_level(M5_ST7789_RESET_GPIO, 1); 120 | vTaskDelay(50 / portTICK_PERIOD_MS); 121 | gpio_set_level(M5_ST7789_RESET_GPIO, 0); 122 | vTaskDelay(50 / portTICK_PERIOD_MS); 123 | gpio_set_level(M5_ST7789_RESET_GPIO, 1); 124 | vTaskDelay(50 / portTICK_PERIOD_MS); 125 | 126 | gpio_pad_select_gpio(M5_ST7789_BL_GPIO); 127 | gpio_set_direction(M5_ST7789_BL_GPIO, GPIO_MODE_OUTPUT); 128 | gpio_set_level(M5_ST7789_BL_GPIO, 0); 129 | 130 | spi_bus_config_t buscfg = { 131 | .sclk_io_num = M5_ST7789_SCLK_GPIO, 132 | .mosi_io_num = M5_ST7789_MOSI_GPIO, 133 | .miso_io_num = -1, 134 | .quadwp_io_num = -1, 135 | .quadhd_io_num = -1}; 136 | 137 | ret = spi_bus_initialize(HSPI_HOST, &buscfg, 1); 138 | assert(ret == ESP_OK); 139 | 140 | spi_device_interface_config_t devcfg = { 141 | .clock_speed_hz = SPI_MASTER_FREQ_20M, 142 | .queue_size = 7, 143 | .mode = 2, 144 | .flags = SPI_DEVICE_NO_DUMMY, 145 | }; 146 | 147 | devcfg.spics_io_num = M5_ST7789_CS_GPIO; 148 | 149 | ret = spi_bus_add_device(HSPI_HOST, &devcfg, &st7789_handle); 150 | assert(ret == ESP_OK); 151 | 152 | spi_master_write_command(0x01); //Software Reset 153 | vTaskDelay(150 / portTICK_PERIOD_MS); 154 | 155 | spi_master_write_command(0x11); //Sleep Out 156 | vTaskDelay(255 / portTICK_PERIOD_MS); 157 | 158 | spi_master_write_command(0x3A); //Interface Pixel Format 159 | spi_master_write_data_byte(0x55); 160 | vTaskDelay(10 / portTICK_PERIOD_MS); 161 | 162 | // landscape RGB 163 | spi_master_write_command(0x36); //Memory Data Access Control 164 | spi_master_write_data_byte(M5_ST7789_LANDSCAPE); 165 | 166 | spi_master_write_command(0x2A); //Column Address Set 167 | spi_master_write_data_byte(0x00); 168 | spi_master_write_data_byte(0x00); 169 | spi_master_write_data_byte(0x00); 170 | spi_master_write_data_byte(0xF0); 171 | 172 | spi_master_write_command(0x2B); //Row Address Set 173 | spi_master_write_data_byte(0x00); 174 | spi_master_write_data_byte(0x00); 175 | spi_master_write_data_byte(0x00); 176 | spi_master_write_data_byte(0xF0); 177 | 178 | spi_master_write_command(0x21); //Display Inversion On 179 | vTaskDelay(10 / portTICK_PERIOD_MS); 180 | 181 | spi_master_write_command(0x13); //Normal Display Mode On 182 | vTaskDelay(10 / portTICK_PERIOD_MS); 183 | 184 | spi_master_write_command(0x29); //Display ON 185 | vTaskDelay(255 / portTICK_PERIOD_MS); 186 | 187 | gpio_set_level(M5_ST7789_BL_GPIO, 1); 188 | 189 | axp192_screen_breath(10); 190 | } 191 | 192 | void display_flipped(bool flipped) 193 | { 194 | spi_master_write_command(0x36); //Memory Data Access Control 195 | if (flipped) 196 | { 197 | spi_master_write_data_byte(M5_ST7789_LANDSCAPE_FLIPPED); 198 | } 199 | else 200 | { 201 | spi_master_write_data_byte(M5_ST7789_LANDSCAPE); 202 | } 203 | } 204 | 205 | void display_clear_line(uint8_t line, bool invert) 206 | { 207 | uint16_t _x1 = 0 + M5_ST7789_OFFSETX; 208 | uint16_t _x2 = M5_ST7789_WIDTH + M5_ST7789_OFFSETX - 1; 209 | uint16_t _y1 = line * 8 + M5_ST7789_OFFSETY + M5_ST7789_INTERFACE_OFFSETY; 210 | uint16_t _y2 = line * 8 + 8 + M5_ST7789_OFFSETY - 1 + M5_ST7789_INTERFACE_OFFSETY; 211 | 212 | spi_master_write_command(0x2A); // set column(x) address 213 | spi_master_write_addr(_x1, _x2); 214 | spi_master_write_command(0x2B); // set Page(y) address 215 | spi_master_write_addr(_y1, _y2); 216 | spi_master_write_command(0x2C); // Memory Write 217 | for (int i = _x1; i <= _x2; i++) 218 | { 219 | uint16_t size = _y2 - _y1 + 1; 220 | spi_master_write_color(invert ? display_get_color() : BLACK, size); 221 | } 222 | } 223 | 224 | void display_clear(void) 225 | { 226 | uint16_t _x1 = 0 + M5_ST7789_OFFSETX; 227 | uint16_t _x2 = M5_ST7789_WIDTH + M5_ST7789_OFFSETX - 1; 228 | uint16_t _y1 = 0 + M5_ST7789_OFFSETY; 229 | uint16_t _y2 = M5_ST7789_HEIGHT + M5_ST7789_OFFSETY - 1; 230 | 231 | spi_master_write_command(0x2A); // set column(x) address 232 | spi_master_write_addr(_x1, _x2); 233 | spi_master_write_command(0x2B); // set Page(y) address 234 | spi_master_write_addr(_y1, _y2); 235 | spi_master_write_command(0x2C); // Memory Write 236 | for (int i = _x1; i <= _x2; i++) 237 | { 238 | uint16_t size = _y2 - _y1 + 1; 239 | spi_master_write_color(BLACK, size); 240 | } 241 | } 242 | 243 | void display_on(bool on) 244 | { 245 | axp192_screen_breath(on ? 10 : 0); 246 | } 247 | 248 | void display_data(uint8_t *data, size_t length, uint8_t line, uint8_t offset, bool invert) 249 | { 250 | uint16_t _x1 = offset + M5_ST7789_OFFSETX + M5_ST7789_INTERFACE_OFFSETX; 251 | uint16_t _x2 = offset + length + M5_ST7789_OFFSETX - 1 + M5_ST7789_INTERFACE_OFFSETX; 252 | uint16_t _y1 = line * 8 + M5_ST7789_OFFSETY + M5_ST7789_INTERFACE_OFFSETY; 253 | uint16_t _y2 = line * 8 + 8 + M5_ST7789_OFFSETY - 1 + M5_ST7789_INTERFACE_OFFSETY; 254 | 255 | spi_master_write_command(0x2A); // set column(x) address 256 | spi_master_write_addr(_x1, _x2); 257 | spi_master_write_command(0x2B); // set Page(y) address 258 | spi_master_write_addr(_y1, _y2); 259 | spi_master_write_command(0x2C); //Memory Write 260 | 261 | uint8_t msbColor = (display_get_color() >> 8) & 0xFF; 262 | uint8_t lsbColor = display_get_color() & 0xFF; 263 | 264 | for (int j = 0; j < 8; j++) 265 | { 266 | uint8_t color[length * 2]; 267 | int index = 0; 268 | 269 | for (int i = 0; i < length; i++) 270 | { 271 | bool bit = (data[i] & (1 << j)); 272 | if (invert) 273 | { 274 | bit = !bit; 275 | } 276 | color[index++] = bit ? msbColor : 0x00; 277 | color[index++] = bit ? lsbColor : 0x00; 278 | } 279 | spi_master_write_data(color, length * 2); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /components/display/m5-st7789/st7789.h: -------------------------------------------------------------------------------- 1 | #ifndef DISPLAY_ST7789_H_ 2 | #define DISPLAY_ST7789_H_ 3 | 4 | #include "driver/spi_master.h" 5 | 6 | // M5stickC PLUS 7 | #define M5_ST7789_WIDTH 240 8 | #define M5_ST7789_HEIGHT 135 9 | #define M5_ST7789_MOSI_GPIO 15 10 | #define M5_ST7789_SCLK_GPIO 13 11 | #define M5_ST7789_CS_GPIO 5 12 | #define M5_ST7789_DC_GPIO 23 13 | #define M5_ST7789_RESET_GPIO 18 14 | #define M5_ST7789_BL_GPIO 32 15 | #define M5_ST7789_OFFSETX 40 16 | #define M5_ST7789_OFFSETY 52 17 | 18 | #define M5_ST7789_LANDSCAPE 0x60 19 | #define M5_ST7789_LANDSCAPE_FLIPPED 0xA0 20 | 21 | #define M5_ST7789_INTERFACE_OFFSETX 56 22 | #define M5_ST7789_INTERFACE_OFFSETY 35 23 | 24 | #define SPI_COMMAND_MODE 0 25 | #define SPI_DATA_MODE 1 26 | 27 | 28 | 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /components/display/ttgo-st7735/st7735.h: -------------------------------------------------------------------------------- 1 | #ifndef DISPLAY_ST7735_H_ 2 | #define DISPLAY_ST7735_H_ 3 | 4 | #include "driver/spi_master.h" 5 | 6 | // M5stickC 7 | #define TTGO_T_WRISTBAND_WIDTH 160 8 | #define TTGO_T_WRISTBAND_HEIGHT 80 9 | #define TTGO_T_WRISTBAND_MOSI_GPIO 19 10 | #define TTGO_T_WRISTBAND_SCLK_GPIO 18 11 | #define TTGO_T_WRISTBAND_CS_GPIO 5 12 | #define TTGO_T_WRISTBAND_DC_GPIO 23 13 | #define TTGO_T_WRISTBAND_RESET_GPIO 26 14 | #define TTGO_T_WRISTBAND__BL_GPIO 27 15 | 16 | #define TTGO_T_WRISTBAND_OFFSETX 1 17 | #define TTGO_T_WRISTBAND_OFFSETY 26 18 | 19 | #define TTGO_T_WRISTBAND_INTERFACE_OFFSETX 16 20 | #define TTGO_T_WRISTBAND_INTERFACE_OFFSETY 8 21 | 22 | #define TTGO_T_WRISTBAND_LANDSCAPE_FLIPPED 0x68 23 | #define TTGO_T_WRISTBAND_LANDSCAPE 0xA8 24 | 25 | #define SPI_COMMAND_MODE 0 26 | #define SPI_DATA_MODE 1 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /components/ena-eke-proxy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS 3 | "ena-eke-proxy.c" 4 | INCLUDE_DIRS "." 5 | PRIV_REQUIRES 6 | esp_http_client 7 | ena 8 | wifi-controller 9 | EMBED_FILES 10 | "certs/cert.pem" 11 | ) -------------------------------------------------------------------------------- /components/ena-eke-proxy/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "ENA Exposue Key Export Proxy" 2 | 3 | config ENA_EKE_PROXY_KEYFILES_DAILY_URL 4 | string "Url to fetch daily keys" 5 | default "https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys/country/DE/date/%s?page=%u&size=%u" 6 | help 7 | Defines the url to fetch keys. Datestring of ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT (%s), page (%u) and size (%u) are passed as paramter. (Default https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys/country/DE/date/%s?page=%u&size=%u) 8 | 9 | config ENA_EKE_PROXY_KEYFILES_HOURLY 10 | bool "Also fetch keys hourly" 11 | default true 12 | help 13 | If enabled, the hourly keys will be fetched for current day. 14 | 15 | config ENA_EKE_PROXY_KEYFILES_HOURLY_URL 16 | string "Url to fetch daily keys" 17 | depends on ENA_EKE_PROXY_KEYFILES_HOURLY 18 | default "https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys/country/DE/date/%s/hour/%u?page=%u&size=%u" 19 | help 20 | Defines the url to fetch keys. Datestring of ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT (%s), hour (%u), page (%u) and size (%u) are passed as paramter. (Default https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys/country/DE/date/%s/hour/%u?page=%u&size=%u) 21 | 22 | config ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT 23 | string "Format of date to fetch" 24 | default "%Y-%m-%d" 25 | help 26 | Defines the Datestring (%s) for setting the date in fetch url. (Default %Y-%m-%d) 27 | 28 | config ENA_EKE_PROXY_KEYFILES_UPLOAD_URL 29 | string "Url to upload keys" 30 | default "https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys" 31 | help 32 | Defines the url to upload keys to. (Default https://cwa-proxy.champonthis.de/version/v1/diagnosis-keys) 33 | 34 | config ENA_EKE_PROXY_KEY_LIMIT 35 | int "Limit of keys to receive" 36 | default 500 37 | help 38 | Defines the limit of keys to receive in one request from server. (Default 500) 39 | 40 | config ENA_EKE_PROXY_MAX_PAST_DAYS 41 | int "Max. days to retrieve keys" 42 | default 14 43 | help 44 | Defines the maximum number of days to receive missed keys from server. (Default 14) 45 | 46 | config ENA_EKE_PROXY_AUTHORIZATION 47 | string "Authorization Header value (PanTra)" 48 | help 49 | Defines the value to put in Authorization header during key upload. (For PanTra key server) 50 | 51 | endmenu -------------------------------------------------------------------------------- /components/ena-eke-proxy/certs/cwa-proxy.champonthis.de.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEBTCCAu2gAwIBAgIUKHGL0rVkxhBZuJx19Syv3l2lOnowDQYJKoZIhvcNAQELBQAwgZExCzAJBgNVBAYTAkRFMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxITAfBgNVBAMMGGN3YS1wcm94eS5jaGFtcG9udGhpcy5kZTEnMCUGCSqGSIb3DQEJARYYY3dhLXByb3h5QGNoYW1wb250aGlzLmRlMB4XDTIwMTIxNDA3NTk1MFoXDTMwMTIyMjA3NTk1MFowgZExCzAJBgNVBAYTAkRFMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxITAfBgNVBAMMGGN3YS1wcm94eS5jaGFtcG9udGhpcy5kZTEnMCUGCSqGSIb3DQEJARYYY3dhLXByb3h5QGNoYW1wb250aGlzLmRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAopV/NJl6cAScH4S77QwIIgD57qTVozlL12UprUbWYpM4kxnbNHVEwQFX6O37AGXmzXbmD0JlGpPtFfPiPLyUzE9Lvt/qSoZjA9aDBrLIkOTY9SGMk18ugw5lDEK06ovxbSEqmG4UQ/VsgRf+v8kI/u7PNgY5spqR3PX2yocypNzrisF3dWdUuQozjTFtv9eLFkbacihpT4arWRrVdO9QWY28xFcp/+mGkK8LtLhnyxhD11cNvL0aifiEO4PXHGhqBrROf5u+AIiY9zqQ8INzuPHfyvyeXmcZ/yeQ5n5+uHaNbfCaF/vELDt5N4uqmoXoyL/qpvq7iU1uvg8kIA2n/wIDAQABo1MwUTAdBgNVHQ4EFgQULg7DzESpFiVYtLhooK7b0yStIrUwHwYDVR0jBBgwFoAULg7DzESpFiVYtLhooK7b0yStIrUwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEALg9qUsXYwhohq1Kak8iyKMM0NRkcFuo05E9X+tHKxtMCxR/YCc4T2ZeRN8w3LIB5AL66jTpig5hr2prUSUWF/XVAlZSU/tYYGseIbW+gngyShECuEeDQRxulsHZl3NWgoFZGV9yaqmyvJXhbSUnqRlx3jUxq68DggjS+lOsEZe/BZrFdhEA5N7EqwpcFMr2tPwr4+SPn3i68WvAIMXqAvc6EUUTZWGcRlSnjdwf/YklWuCUgagd/KV5E7xgIzQuT9OAXoYS52ADBDexqKQasSPjfZoMs1OUgKKFRTSeapXtC+wGarfBbH9qm3SflLGVUZDSNtiL0aPPDIrA3mYip+w== 3 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /components/ena-eke-proxy/ena-eke-proxy.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief connection to an Exposure Key export proxy 18 | * 19 | * This is for receiving the Exposure Key export from a compatible proxy server and upload own infected keys to it. 20 | * 21 | */ 22 | #ifndef _ena_EKE_PROXY_H_ 23 | #define _ena_EKE_PROXY_H_ 24 | 25 | #include "esp_err.h" 26 | #include "ena-crypto.h" 27 | 28 | #define ENA_EKE_PROXY_LOG "ESP-ENA-eke-proxy" // TAG for Logging 29 | 30 | #define ENA_EKE_PROXY_KEYFILES_DAILY_URL CONFIG_ENA_EKE_PROXY_KEYFILES_DAILY_URL 31 | 32 | #ifdef CONFIG_ENA_EKE_PROXY_KEYFILES_HOURLY 33 | #define ENA_EKE_PROXY_KEYFILES_HOURLY true 34 | #else 35 | #define ENA_EKE_PROXY_KEYFILES_HOURLY false 36 | #endif 37 | 38 | #if ENA_EKE_PROXY_KEYFILES_HOURLY 39 | #define ENA_EKE_PROXY_KEYFILES_HOURLY_URL CONFIG_ENA_EKE_PROXY_KEYFILES_HOURLY_URL 40 | #else 41 | #define ENA_EKE_PROXY_KEYFILES_HOURLY_URL "/%s/%u/%u/%u" 42 | #endif 43 | 44 | #define ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT CONFIG_ENA_EKE_PROXY_KEYFILES_DAILY_FORMAT 45 | #define ENA_EKE_PROXY_KEYFILES_UPLOAD_URL CONFIG_ENA_EKE_PROXY_KEYFILES_UPLOAD_URL 46 | #define ENA_EKE_PROXY_DEFAULT_LIMIT CONFIG_ENA_EKE_PROXY_KEY_LIMIT 47 | #define ENA_EKE_PROXY_MAX_PAST_DAYS CONFIG_ENA_EKE_PROXY_MAX_PAST_DAYS // ENA_STORAGE_TEK_MAX 48 | 49 | /** 50 | * @brief fetch key export from given url 51 | * 52 | * @param[in] url the url to fetch the data from 53 | */ 54 | esp_err_t ena_eke_proxy_receive_keys(char *url); 55 | 56 | /** 57 | * @brief fetch key export for given date with limit and offset 58 | * 59 | * @param[in] date_string the date to fetch the data for 60 | * @param[in] page the page to request 61 | * @param[in] size the size of a page 62 | */ 63 | esp_err_t ena_eke_proxy_receive_daily_keys(char *date_string, size_t page, size_t size); 64 | 65 | /** 66 | * @brief fetch key export for given date with limit and offset 67 | * 68 | * @param[in] date_string the date to fetch the data for 69 | * @param[in] hour the hour to fetch the data for 70 | * @param[in] page the page to request 71 | * @param[in] size the size of a page 72 | */ 73 | esp_err_t ena_eke_proxy_receive_hourly_keys(char *date_string, uint8_t hour, size_t page, size_t size); 74 | 75 | /** 76 | * @brief run ena eke proxy 77 | */ 78 | void ena_eke_proxy_run(void); 79 | 80 | /** 81 | * @brief Upload own keys to server 82 | * 83 | * @param[in] token token for authentication 84 | * @param[in] days_since_onset_of_symptomstoken days after test/symptoms? 85 | */ 86 | esp_err_t ena_eke_proxy_upload(char *token, uint32_t days_since_onset_of_symptoms); 87 | 88 | /** 89 | * @brief pause requests 90 | * 91 | */ 92 | void ena_eke_proxy_pause(void); 93 | 94 | /** 95 | * @brief resume requests 96 | * 97 | */ 98 | void ena_eke_proxy_resume(void); 99 | 100 | #endif -------------------------------------------------------------------------------- /components/ena/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS 3 | "ena.c" 4 | "ena-beacons.c" 5 | "ena-bluetooth-advertise.c" 6 | "ena-bluetooth-scan.c" 7 | "ena-crypto.c" 8 | "ena-exposure.c" 9 | "ena-storage.c" 10 | INCLUDE_DIRS "include" 11 | PRIV_REQUIRES 12 | spi_flash 13 | mbedtls 14 | bt) -------------------------------------------------------------------------------- /components/ena/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Exposure Notification API" 2 | 3 | menu "Storage" 4 | config ENA_STORAGE_DUMP 5 | bool "Dump storage" 6 | default false 7 | help 8 | Dump storage (stored TEKs, temp. beacons and perm. beacons) to serial output after scan. 9 | 10 | config ENA_RESET_LAST_CHECK 11 | bool "Reset last exposure check" 12 | default false 13 | help 14 | Resets the last exposure check date on start. 15 | 16 | config ENA_STORAGE_TEK_MAX 17 | int "Max. TEKs" 18 | default 14 19 | help 20 | Defines the maximum number of TEKs to be stored. (Default 14 [14 * 144 => 14 days]) 21 | 22 | config ENA_STORAGE_EXPOSURE_INFORMATION_MAX 23 | int "Max. exporure information" 24 | default 500 25 | help 26 | Defines the maximum number of exposure information to be stored. (Default 500) 27 | 28 | config ENA_STORAGE_TEMP_BEACONS_MAX 29 | int "Max. temporary beacons" 30 | default 1000 31 | help 32 | Defines the maximum number of temporary beacons to be stored. (Default 1000) 33 | 34 | config ENA_STORAGE_START_ADDRESS 35 | int "Storage start address" 36 | default 0 37 | help 38 | Defines the start address on partition. (Default 0) 39 | 40 | config ENA_STORAGE_PARTITION_NAME 41 | string "Partition name" 42 | default "ena" 43 | help 44 | Name of the partition used for storage. (Default "ena", see partitions.csv) 45 | 46 | config ENA_STORAGE_ERASE 47 | bool "Erase storage (!)" 48 | default false 49 | help 50 | Erases the complete(!) partition on startup and reset counters. 51 | 52 | endmenu 53 | 54 | menu "Scanning" 55 | config ENA_BEACON_TRESHOLD 56 | int "Contact threshold" 57 | default 300 58 | help 59 | Threshold in seconds after a received beacon is stored permanently. (Default 5 minutes) 60 | 61 | config ENA_BEACON_CLEANUP_TRESHOLD 62 | int "Clean-Up threshold" 63 | default 14 64 | help 65 | Threshold in days after stored beacons to be removed. 66 | 67 | config ENA_SCANNING_TIME 68 | int "Scanning time" 69 | default 30 70 | help 71 | Time in seconds how long a scan should run. (Default 30 seconds) 72 | 73 | config ENA_SCANNING_INTERVAL 74 | int "Scanning interval" 75 | default 300 76 | help 77 | Interval in seconds for the next scan to happen. (Default 5 minutes) 78 | endmenu 79 | 80 | menu "Advertising" 81 | config ENA_BT_ROTATION_TIMEOUT_INTERVAL 82 | int "Rotation timeout interval" 83 | default 900 84 | help 85 | Base rotation timeout interval in seconds for BT address change & therefore the advertised beacon.(Default 5 minutes) 86 | 87 | config ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL 88 | int "Randomize rotation timeout interval" 89 | default 150 90 | help 91 | Range in seconds for randomize the rotation timeout interval. (Default +/- ~2.5 minutes) 92 | 93 | config ENA_TEK_ROLLING_PERIOD 94 | int "TEK rolling period" 95 | default 144 96 | help 97 | Defines the TEK rolling period in 10 minute steps. (Default 144 => 24 hours) 98 | endmenu 99 | 100 | 101 | endmenu -------------------------------------------------------------------------------- /components/ena/ena-beacons.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | 17 | #include "esp_log.h" 18 | 19 | #include "ena-crypto.h" 20 | #include "ena-storage.h" 21 | 22 | #include "ena-beacons.h" 23 | 24 | static uint32_t temp_beacons_count = 0; 25 | static ena_beacon_t temp_beacons[ENA_STORAGE_TEMP_BEACONS_MAX]; 26 | 27 | int ena_get_temp_beacon_index(uint8_t *rpi, uint8_t *aem) 28 | { 29 | for (int i = 0; i < temp_beacons_count; i++) 30 | { 31 | if (memcmp(temp_beacons[i].rpi, rpi, sizeof(ENA_KEY_LENGTH)) == 0 && 32 | memcmp(temp_beacons[i].aem, aem, sizeof(ENA_AEM_METADATA_LENGTH)) == 0) 33 | { 34 | return i; 35 | } 36 | } 37 | return -1; 38 | } 39 | 40 | void ena_beacons_temp_refresh(uint32_t unix_timestamp) 41 | { 42 | for (int i = temp_beacons_count - 1; i >= 0; i--) 43 | { 44 | // check for treshold and add permanent beacon 45 | if (temp_beacons[i].timestamp_last - temp_beacons[i].timestamp_first >= ENA_BEACON_TRESHOLD) 46 | { 47 | ESP_LOGD(ENA_BEACON_LOG, "create beacon after treshold"); 48 | ESP_LOG_BUFFER_HEXDUMP(ENA_BEACON_LOG, temp_beacons[i].rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG); 49 | ena_storage_add_beacon(&temp_beacons[i]); 50 | ena_storage_remove_temp_beacon(i); 51 | } 52 | else 53 | // delete temp beacons older than two times time window (two times to be safe, one times time window enough?!) 54 | if (unix_timestamp - temp_beacons[i].timestamp_last > (ENA_TIME_WINDOW * 2)) 55 | { 56 | ESP_LOGD(ENA_BEACON_LOG, "remove old temporary beacon %u", i); 57 | ena_storage_remove_temp_beacon(i); 58 | } 59 | } 60 | 61 | // update beacons 62 | temp_beacons_count = ena_storage_temp_beacons_count(); 63 | for (int i = 0; i < temp_beacons_count; i++) 64 | { 65 | ena_storage_get_temp_beacon(i, &temp_beacons[i]); 66 | } 67 | 68 | #if (CONFIG_ENA_STORAGE_DUMP) 69 | // DEBUG dump 70 | ena_storage_dump_teks(); 71 | ena_storage_dump_exposure_information(); 72 | ena_storage_dump_temp_beacons(); 73 | ena_storage_dump_beacons(); 74 | #endif 75 | } 76 | 77 | void ena_beacons_cleanup(uint32_t unix_timestamp) 78 | { 79 | uint32_t count = ena_storage_beacons_count(); 80 | ena_beacon_t beacon; 81 | for (int i = count - 1; i >= 0; i--) 82 | { 83 | ena_storage_get_beacon(i, &beacon); 84 | if (((unix_timestamp - beacon.timestamp_last) / (60 * 60 * 24)) > ENA_BEACON_CLEANUP_TRESHOLD) 85 | { 86 | ena_storage_remove_beacon(i); 87 | } 88 | } 89 | } 90 | 91 | void ena_beacon(uint32_t unix_timestamp, uint8_t *rpi, uint8_t *aem, int rssi) 92 | { 93 | uint32_t beacon_index = ena_get_temp_beacon_index(rpi, aem); 94 | if (beacon_index == -1) 95 | { 96 | temp_beacons[temp_beacons_count].timestamp_first = unix_timestamp; 97 | memcpy(temp_beacons[temp_beacons_count].rpi, rpi, ENA_KEY_LENGTH); 98 | memcpy(temp_beacons[temp_beacons_count].aem, aem, ENA_AEM_METADATA_LENGTH); 99 | temp_beacons[temp_beacons_count].rssi = rssi; 100 | temp_beacons[temp_beacons_count].timestamp_last = unix_timestamp; 101 | beacon_index = ena_storage_add_temp_beacon(&temp_beacons[temp_beacons_count]); 102 | ESP_LOGD(ENA_BEACON_LOG, "new temporary beacon %d at %u", temp_beacons_count, unix_timestamp); 103 | ESP_LOG_BUFFER_HEX_LEVEL(ENA_BEACON_LOG, rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG); 104 | ESP_LOG_BUFFER_HEX_LEVEL(ENA_BEACON_LOG, aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG); 105 | ESP_LOGD(ENA_BEACON_LOG, "RSSI %d", rssi); 106 | 107 | if (beacon_index != temp_beacons_count) 108 | { 109 | ESP_LOGW(ENA_BEACON_LOG, "last temporary beacon index does not match array index!"); 110 | } 111 | temp_beacons_count++; 112 | } 113 | else 114 | { 115 | temp_beacons[beacon_index].rssi = (temp_beacons[beacon_index].rssi + rssi) / 2; 116 | temp_beacons[beacon_index].timestamp_last = unix_timestamp; 117 | ESP_LOGD(ENA_BEACON_LOG, "update temporary beacon %d at %u", beacon_index, unix_timestamp); 118 | ESP_LOG_BUFFER_HEX_LEVEL(ENA_BEACON_LOG, temp_beacons[beacon_index].rpi, ENA_KEY_LENGTH, ESP_LOG_DEBUG); 119 | ESP_LOG_BUFFER_HEX_LEVEL(ENA_BEACON_LOG, temp_beacons[beacon_index].aem, ENA_AEM_METADATA_LENGTH, ESP_LOG_DEBUG); 120 | ESP_LOGD(ENA_BEACON_LOG, "RSSI %d", temp_beacons[beacon_index].rssi); 121 | ena_storage_set_temp_beacon(beacon_index, &temp_beacons[beacon_index]); 122 | } 123 | } -------------------------------------------------------------------------------- /components/ena/ena-bluetooth-advertise.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include "esp_log.h" 15 | #include "esp_bt.h" 16 | #include "esp_gap_ble_api.h" 17 | 18 | #include "ena-crypto.h" 19 | 20 | #include "ena-bluetooth-advertise.h" 21 | 22 | static esp_ble_adv_params_t ena_adv_params = { 23 | .adv_int_min = 0x140, // 200 ms 24 | .adv_int_max = 0x190, // 250 ms 25 | .adv_type = ADV_TYPE_NONCONN_IND, 26 | .own_addr_type = BLE_ADDR_TYPE_RANDOM, 27 | .channel_map = ADV_CHNL_ALL, 28 | .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, 29 | }; 30 | 31 | void ena_bluetooth_advertise_start(void) 32 | { 33 | ESP_ERROR_CHECK(esp_ble_gap_start_advertising(&ena_adv_params)); 34 | } 35 | 36 | void ena_bluetooth_advertise_set_payload(uint32_t enin, uint8_t *tek) 37 | { 38 | uint8_t rpik[ENA_KEY_LENGTH] = {0}; 39 | uint8_t rpi[ENA_KEY_LENGTH] = {0}; 40 | uint8_t aemk[ENA_KEY_LENGTH] = {0}; 41 | uint8_t aem[ENA_AEM_METADATA_LENGTH] = {0}; 42 | 43 | ena_crypto_rpik(rpik, tek); 44 | 45 | ena_crypto_rpi(rpi, rpik, enin); 46 | 47 | ena_crypto_aemk(aemk, tek); 48 | 49 | ena_crypto_aem(aem, aemk, rpi, esp_ble_tx_power_get(ESP_BLE_PWR_TYPE_ADV)); 50 | 51 | uint8_t adv_raw_data[31]; 52 | // FLAG??? skipped on sniffed android packages!? 53 | adv_raw_data[0] = 0x02; 54 | adv_raw_data[1] = 0x01; 55 | adv_raw_data[2] = ENA_BLUETOOTH_TAG_DATA; 56 | 57 | // SERVICE UUID 58 | adv_raw_data[3] = 0x03; 59 | adv_raw_data[4] = 0x03; 60 | adv_raw_data[5] = 0x6F; 61 | adv_raw_data[6] = 0xFD; 62 | 63 | // SERVICE DATA 64 | adv_raw_data[7] = 0x17; 65 | adv_raw_data[8] = 0x16; 66 | 67 | adv_raw_data[9] = 0x6F; 68 | adv_raw_data[10] = 0xFD; 69 | 70 | for (int i = 0; i < ENA_KEY_LENGTH; i++) 71 | { 72 | adv_raw_data[i + 11] = rpi[i]; 73 | } 74 | for (int i = 0; i < ENA_AEM_METADATA_LENGTH; i++) 75 | { 76 | adv_raw_data[i + ENA_KEY_LENGTH + 11] = aem[i]; 77 | } 78 | 79 | esp_ble_gap_config_adv_data_raw(adv_raw_data, sizeof(adv_raw_data)); 80 | 81 | ESP_LOGD(ENA_ADVERTISE_LOG, "payload for ENIN %u", enin); 82 | ESP_LOG_BUFFER_HEXDUMP(ENA_ADVERTISE_LOG, adv_raw_data, sizeof(adv_raw_data), ESP_LOG_DEBUG); 83 | } 84 | 85 | void ena_bluetooth_advertise_stop(void) 86 | { 87 | ESP_ERROR_CHECK(esp_ble_gap_stop_advertising()); 88 | } -------------------------------------------------------------------------------- /components/ena/ena-bluetooth-scan.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | 17 | #include "esp_log.h" 18 | #include "esp_gap_ble_api.h" 19 | 20 | #include "ena-crypto.h" 21 | #include "ena-beacons.h" 22 | 23 | #include "ena-bluetooth-scan.h" 24 | 25 | static int scan_status = ENA_SCAN_STATUS_NOT_SCANNING; 26 | 27 | static const uint16_t ENA_SERVICE_UUID = 0xFD6F; 28 | 29 | static int last_scan_num; 30 | 31 | static esp_ble_scan_params_t ena_scan_params = { 32 | .scan_type = BLE_SCAN_TYPE_ACTIVE, 33 | .own_addr_type = BLE_ADDR_TYPE_RANDOM, 34 | .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, 35 | .scan_interval = 0x50, // don't know good parameters, just copied 36 | .scan_window = 0x30, // don't know good parameters, just copied 37 | .scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE, 38 | }; 39 | 40 | void ena_bluetooth_scan_event_callback(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) 41 | { 42 | uint32_t unix_timestamp = (uint32_t)time(NULL); 43 | esp_ble_gap_cb_param_t *p = (esp_ble_gap_cb_param_t *)param; 44 | switch (event) 45 | { 46 | case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: 47 | ESP_LOGD(ENA_SCAN_LOG, "start scanning..."); 48 | break; 49 | case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: 50 | ESP_LOGD(ENA_SCAN_LOG, "stopped scanning..."); 51 | ena_beacons_temp_refresh(unix_timestamp); 52 | break; 53 | case ESP_GAP_BLE_SCAN_RESULT_EVT: 54 | if (p->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) 55 | { 56 | uint8_t service_uuid_length = 0; 57 | uint8_t *service_uuid_data = esp_ble_resolve_adv_data(p->scan_rst.ble_adv, 0x03, &service_uuid_length); 58 | // check for ENA Service UUID 59 | if (service_uuid_length == sizeof(ENA_SERVICE_UUID) && memcmp(service_uuid_data, &ENA_SERVICE_UUID, service_uuid_length) == 0) 60 | { 61 | uint8_t service_data_length = 0; 62 | uint8_t *service_data = esp_ble_resolve_adv_data(p->scan_rst.ble_adv, 0x16, &service_data_length); 63 | if (service_data_length != (sizeof(ENA_SERVICE_UUID) + ENA_KEY_LENGTH + ENA_AEM_METADATA_LENGTH)) 64 | { 65 | ESP_LOGW(ENA_SCAN_LOG, "received ENA Service with invalid payload"); 66 | break; 67 | } 68 | 69 | uint8_t *rpi = malloc(ENA_KEY_LENGTH); 70 | memcpy(rpi, &service_data[sizeof(ENA_SERVICE_UUID)], ENA_KEY_LENGTH); 71 | uint8_t *aem = malloc(ENA_AEM_METADATA_LENGTH); 72 | memcpy(aem, &service_data[sizeof(ENA_SERVICE_UUID) + ENA_KEY_LENGTH], ENA_AEM_METADATA_LENGTH); 73 | ena_beacon(unix_timestamp, rpi, aem, p->scan_rst.rssi); 74 | last_scan_num++; 75 | free(rpi); 76 | free(aem); 77 | } 78 | } 79 | else if (p->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) 80 | { 81 | scan_status = ENA_SCAN_STATUS_NOT_SCANNING; 82 | ena_beacons_temp_refresh(unix_timestamp); 83 | ESP_LOGD(ENA_SCAN_LOG, "finished scanning..."); 84 | } 85 | break; 86 | default: 87 | // nothing 88 | break; 89 | } 90 | } 91 | 92 | void ena_bluetooth_scan_init(void) 93 | { 94 | ESP_ERROR_CHECK(esp_ble_gap_set_scan_params(&ena_scan_params)); 95 | ESP_ERROR_CHECK(esp_ble_gap_register_callback(ena_bluetooth_scan_event_callback)); 96 | // init temporary beacons 97 | ena_beacons_temp_refresh((uint32_t)time(NULL)); 98 | } 99 | 100 | void ena_bluetooth_scan_start(uint32_t duration) 101 | { 102 | scan_status = ENA_SCAN_STATUS_SCANNING; 103 | last_scan_num = 0; 104 | ESP_ERROR_CHECK(esp_ble_gap_start_scanning(duration)); 105 | } 106 | 107 | void ena_bluetooth_scan_stop(void) 108 | { 109 | scan_status = ENA_SCAN_STATUS_WAITING; 110 | ESP_ERROR_CHECK(esp_ble_gap_stop_scanning()); 111 | } 112 | 113 | int ena_bluetooth_scan_get_status(void) 114 | { 115 | return scan_status; 116 | } 117 | 118 | int ena_bluetooth_scan_get_last_num(void) 119 | { 120 | return last_scan_num; 121 | } -------------------------------------------------------------------------------- /components/ena/ena-crypto.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include "mbedtls/md.h" 15 | #include "mbedtls/aes.h" 16 | #include "mbedtls/hkdf.h" 17 | #include "mbedtls/entropy.h" 18 | #include "mbedtls/ctr_drbg.h" 19 | 20 | #include "esp_log.h" 21 | 22 | #include "ena-crypto.h" 23 | 24 | #define ESP_CRYPTO_LOG "ESP-CRYPTO" 25 | 26 | static mbedtls_ctr_drbg_context ctr_drbg; 27 | 28 | void ena_crypto_init(void) 29 | { 30 | mbedtls_entropy_context entropy; 31 | uint8_t pers[] = "Exposure Notifcation API esp32"; 32 | int ret; 33 | 34 | mbedtls_entropy_init(&entropy); 35 | mbedtls_ctr_drbg_init(&ctr_drbg); 36 | 37 | if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, pers, sizeof(pers))) != 0) 38 | { 39 | ESP_LOGE(ESP_CRYPTO_LOG, " failed\n ! mbedtls_ctr_drbg_init returned -0x%04x\n", -ret); 40 | } 41 | } 42 | 43 | uint32_t ena_crypto_enin(uint32_t unix_epoch_time) 44 | { 45 | return unix_epoch_time / ENA_TIME_WINDOW; 46 | } 47 | 48 | void ena_crypto_tek(uint8_t *tek) 49 | { 50 | int ret; 51 | if ((ret = mbedtls_ctr_drbg_random(&ctr_drbg, tek, ENA_KEY_LENGTH)) != 0) 52 | { 53 | ESP_LOGE(ESP_CRYPTO_LOG, " failed\n ! mbedtls_ctr_drbg_random returned -0x%04x\n", -ret); 54 | } 55 | } 56 | 57 | void ena_crypto_rpik(uint8_t *rpik, uint8_t *tek) 58 | { 59 | const uint8_t rpik_info[] = "EN-RPIK"; 60 | mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, tek, ENA_KEY_LENGTH, rpik_info, sizeof(rpik_info), rpik, ENA_KEY_LENGTH); 61 | } 62 | 63 | void ena_crypto_rpi(uint8_t *rpi, uint8_t *rpik, uint32_t enin) 64 | { 65 | uint8_t padded_data[] = "EN-RPI"; 66 | padded_data[12] = (enin & 0x000000ff); 67 | padded_data[13] = (enin & 0x0000ff00) >> 8; 68 | padded_data[14] = (enin & 0x00ff0000) >> 16; 69 | padded_data[15] = (enin & 0xff000000) >> 24; 70 | 71 | mbedtls_aes_context aes; 72 | mbedtls_aes_init(&aes); 73 | mbedtls_aes_setkey_enc(&aes, rpik, ENA_KEY_LENGTH * 8); 74 | mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, padded_data, rpi); 75 | mbedtls_aes_free(&aes); 76 | } 77 | 78 | void ena_crypto_aemk(uint8_t *aemk, uint8_t *tek) 79 | { 80 | uint8_t aemkInfo[] = "EN-AEMK"; 81 | mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, tek, ENA_KEY_LENGTH, aemkInfo, sizeof(aemkInfo), aemk, ENA_KEY_LENGTH); 82 | } 83 | 84 | void ena_crypto_aem(uint8_t *aem, uint8_t *aemk, uint8_t *rpi, uint8_t power_level) 85 | { 86 | uint8_t metadata[ENA_AEM_METADATA_LENGTH]; 87 | metadata[0] = 0b01000000; 88 | metadata[1] = power_level; 89 | size_t count = 0; 90 | uint8_t sb[16] = {0}; 91 | mbedtls_aes_context aes; 92 | mbedtls_aes_init(&aes); 93 | mbedtls_aes_setkey_enc(&aes, aemk, ENA_KEY_LENGTH * 8); 94 | mbedtls_aes_crypt_ctr(&aes, ENA_AEM_METADATA_LENGTH, &count, rpi, sb, metadata, aem); 95 | mbedtls_aes_free(&aes); 96 | } -------------------------------------------------------------------------------- /components/ena/ena.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | 17 | #include "esp_system.h" 18 | #include "esp_log.h" 19 | #include "esp_bt.h" 20 | #include "esp_bt_main.h" 21 | #include "esp_gap_ble_api.h" 22 | 23 | #include "nvs_flash.h" 24 | 25 | #include "ena-crypto.h" 26 | #include "ena-storage.h" 27 | #include "ena-bluetooth-scan.h" 28 | #include "ena-bluetooth-advertise.h" 29 | #include "ena-beacons.h" 30 | 31 | #include "ena.h" 32 | 33 | static ena_tek_t last_tek; // last ENIN 34 | static uint32_t next_rpi_timestamp; // next rpi 35 | 36 | void ena_next_rpi_timestamp(uint32_t timestamp) 37 | { 38 | int random_interval = esp_random() % (2 * ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL); 39 | if (random_interval > ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL) 40 | { 41 | random_interval = ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL - random_interval; 42 | } 43 | next_rpi_timestamp = timestamp + ENA_BT_ROTATION_TIMEOUT_INTERVAL + random_interval; 44 | ESP_LOGD(ENA_LOG, "next rpi at %u (%u from %u)", next_rpi_timestamp, (ENA_BT_ROTATION_TIMEOUT_INTERVAL + random_interval), timestamp); 45 | } 46 | 47 | void ena_run(void) 48 | { 49 | static uint32_t unix_timestamp = 0; 50 | static uint32_t current_enin = 0; 51 | unix_timestamp = (uint32_t)time(NULL); 52 | current_enin = ena_crypto_enin(unix_timestamp); 53 | if (current_enin - last_tek.enin >= last_tek.rolling_period) 54 | { 55 | ena_crypto_tek(last_tek.key_data); 56 | last_tek.enin = current_enin; 57 | // validity only to next day 00:00 58 | last_tek.rolling_period = ENA_TEK_ROLLING_PERIOD - (last_tek.enin % ENA_TEK_ROLLING_PERIOD); 59 | ena_storage_write_tek(&last_tek); 60 | // clean up old beacons 61 | ena_beacons_cleanup(unix_timestamp); 62 | } 63 | 64 | // change RPI 65 | if (unix_timestamp >= next_rpi_timestamp) 66 | { 67 | if (ena_bluetooth_scan_get_status() == ENA_SCAN_STATUS_SCANNING) 68 | { 69 | ena_bluetooth_scan_stop(); 70 | } 71 | ena_bluetooth_advertise_stop(); 72 | ena_bluetooth_advertise_set_payload(current_enin, last_tek.key_data); 73 | ena_bluetooth_advertise_start(); 74 | if (ena_bluetooth_scan_get_status() == ENA_SCAN_STATUS_WAITING) 75 | { 76 | ena_bluetooth_scan_start(ENA_SCANNING_TIME); 77 | } 78 | ena_next_rpi_timestamp(unix_timestamp); 79 | } 80 | 81 | // scan 82 | if (unix_timestamp % ENA_SCANNING_INTERVAL == 0 && ena_bluetooth_scan_get_status() == ENA_SCAN_STATUS_NOT_SCANNING) 83 | { 84 | ena_bluetooth_scan_start(ENA_SCANNING_TIME); 85 | } 86 | } 87 | 88 | void ena_start(void) 89 | { 90 | #if (CONFIG_ENA_STORAGE_ERASE) 91 | ena_storage_erase_all(); 92 | #endif 93 | 94 | #ifdef CONFIG_ENA_RESET_LAST_CHECK 95 | ena_storage_write_last_exposure_date(0); 96 | #endif 97 | 98 | if (ena_storage_read_last_exposure_date() == 0xFFFFFFFF) 99 | { 100 | ena_storage_erase_all(); 101 | } 102 | 103 | // init NVS for BLE 104 | esp_err_t ret; 105 | ret = nvs_flash_init(); 106 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) 107 | { 108 | ESP_ERROR_CHECK(nvs_flash_erase()); 109 | ESP_ERROR_CHECK(nvs_flash_init()); 110 | } 111 | 112 | // init BLE 113 | if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) 114 | { 115 | esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); 116 | ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg)); 117 | while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) 118 | { 119 | } 120 | } 121 | 122 | if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) 123 | { 124 | ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE)); 125 | } 126 | 127 | if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) 128 | { 129 | ESP_ERROR_CHECK(esp_bluedroid_init()); 130 | } 131 | if (esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_INITIALIZED) 132 | { 133 | ESP_ERROR_CHECK(esp_bluedroid_enable()); 134 | } 135 | 136 | // new bluetooth address nesseccary? 137 | uint8_t bt_address[ESP_BD_ADDR_LEN]; 138 | esp_fill_random(bt_address, ESP_BD_ADDR_LEN); 139 | bt_address[0] |= 0xC0; 140 | 141 | ESP_ERROR_CHECK(esp_ble_gap_set_rand_addr(bt_address)); 142 | 143 | ESP_ERROR_CHECK(esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9)); 144 | ESP_ERROR_CHECK(esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9)); 145 | ESP_ERROR_CHECK(esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN, ESP_PWR_LVL_P9)); 146 | ESP_ERROR_CHECK(esp_ble_gap_config_local_privacy(true)); 147 | 148 | // init ENA 149 | ena_crypto_init(); 150 | 151 | uint32_t unix_timestamp = (uint32_t)time(NULL); 152 | 153 | uint32_t current_enin = ena_crypto_enin(unix_timestamp); 154 | uint32_t tek_count = ena_storage_read_last_tek(&last_tek); 155 | 156 | ena_next_rpi_timestamp(unix_timestamp); 157 | 158 | // read last TEK or create new 159 | if (tek_count == 0 || (current_enin - last_tek.enin) >= last_tek.rolling_period) 160 | { 161 | ena_crypto_tek(last_tek.key_data); 162 | last_tek.enin = ena_crypto_enin(unix_timestamp); 163 | // validity only to next day 00:00 164 | last_tek.rolling_period = ENA_TEK_ROLLING_PERIOD - (last_tek.enin % ENA_TEK_ROLLING_PERIOD); 165 | ena_storage_write_tek(&last_tek); 166 | } 167 | 168 | // init scan 169 | ena_bluetooth_scan_init(); 170 | 171 | // init and start advertising 172 | ena_bluetooth_advertise_set_payload(current_enin, last_tek.key_data); 173 | ena_bluetooth_advertise_start(); 174 | // initial scan on every start 175 | ena_bluetooth_scan_start(ENA_SCANNING_TIME); 176 | 177 | // what is a good stack size here? 178 | // xTaskCreate(&ena_run, "ena_run", ENA_RAM, NULL, 5, NULL); 179 | } 180 | 181 | void ena_stop(void) 182 | { 183 | ena_bluetooth_advertise_stop(); 184 | ena_bluetooth_scan_stop(); 185 | esp_bluedroid_disable(); 186 | esp_bluedroid_deinit(); 187 | esp_bt_controller_disable(); 188 | esp_bt_controller_deinit(); 189 | } -------------------------------------------------------------------------------- /components/ena/include/ena-beacons.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief handles scanned data by storing temporary beacons, check for threshold and store beacons permanently 18 | * 19 | */ 20 | #ifndef _ena_BEACON_H_ 21 | #define _ena_BEACON_H_ 22 | 23 | #define ENA_BEACON_LOG "ESP-ENA-beacon" // TAG for Logging 24 | #define ENA_BEACON_TRESHOLD (CONFIG_ENA_BEACON_TRESHOLD) // meet for longer than 5 minutes 25 | #define ENA_BEACON_CLEANUP_TRESHOLD (CONFIG_ENA_BEACON_CLEANUP_TRESHOLD) // threshold (in days) for stored beacons to be removed 26 | 27 | /** 28 | * @brief check temporary beacon for threshold or expiring 29 | * 30 | * This function checks all current temporary beacons if the contact threshold is 31 | * reached or if the temporary contact can be discarded. 32 | * 33 | * @param[in] unix_timestamp current time as UNIX timestamp to compare 34 | * 35 | */ 36 | void ena_beacons_temp_refresh(uint32_t unix_timestamp); 37 | 38 | /** 39 | * @brief check stored beacons to expire 40 | * 41 | * This function checks for all stored beacons if the last timestamp is over a threshold to remove the beacon. 42 | * 43 | * @param[in] unix_timestamp current time as UNIX timestamp to compate 44 | * 45 | */ 46 | void ena_beacons_cleanup(uint32_t unix_timestamp); 47 | 48 | /** 49 | * @brief handle new beacon received from a BLE scan 50 | * 51 | * This function gets called when a running BLE scan received a new ENA payload. 52 | * On already detected RPI this will update just the timestamp and RSSI. 53 | * 54 | * @param[in] unix_timestamp UNIX timestamp when beacon was made 55 | * @param[in] rpi received RPI from scanned payload 56 | * @param[in] aem received AEM from scanned payload 57 | * @param[in] rssi measured RSSI on scan 58 | * 59 | */ 60 | void ena_beacon(uint32_t unix_timestamp, uint8_t *rpi, uint8_t *aem, int rssi); 61 | 62 | #endif -------------------------------------------------------------------------------- /components/ena/include/ena-bluetooth-advertise.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief BLE advertising to send own beacons 18 | * 19 | */ 20 | #ifndef _ena_BLUETOOTH_ADVERTISE_H_ 21 | #define _ena_BLUETOOTH_ADVERTISE_H_ 22 | 23 | #define ENA_ADVERTISE_LOG "ESP-ENA-advertise" // TAG for Logging 24 | #define ENA_BLUETOOTH_TAG_DATA (0x1A) // Data for BLE payload TAG 25 | 26 | /** 27 | * @brief Start BLE advertising 28 | */ 29 | void ena_bluetooth_advertise_start(void); 30 | 31 | /** 32 | * @brief Set payload for BLE advertising 33 | * 34 | * This will set the payload for based on given ENIN and TEK. 35 | * 36 | * Source documents (Section: Advertising Payload) 37 | * 38 | * https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf 39 | * 40 | * https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-BluetoothSpecificationv1.2.pdf 41 | * 42 | * @param[in] enin ENIN defining the start of the tek vadility. This should be the ENIN for the current timestamp 43 | * @param[in] tek pointer to the TEK used to encrypt the payload. 44 | */ 45 | void ena_bluetooth_advertise_set_payload(uint32_t enin, uint8_t *tek); 46 | 47 | /** 48 | * @brief Stop BLE advertising 49 | */ 50 | void ena_bluetooth_advertise_stop(void); 51 | 52 | #endif -------------------------------------------------------------------------------- /components/ena/include/ena-bluetooth-scan.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief BLE scans for detecting other beacons 18 | * 19 | */ 20 | #ifndef _ena_BLUETOOTH_SCAN_H_ 21 | #define _ena_BLUETOOTH_SCAN_H_ 22 | 23 | #define ENA_SCAN_LOG "ESP-ENA-scan" // TAG for Logging 24 | #define ENA_SCANNING_TIME (CONFIG_ENA_SCANNING_TIME) // time how long a scan should run 25 | #define ENA_SCANNING_INTERVAL (CONFIG_ENA_SCANNING_INTERVAL) // interval for next scan to happen 26 | 27 | /** 28 | * @brief status of BLE scan 29 | */ 30 | typedef enum 31 | { 32 | ENA_SCAN_STATUS_SCANNING = 0, // scan is running 33 | ENA_SCAN_STATUS_NOT_SCANNING, // scan is not running 34 | ENA_SCAN_STATUS_WAITING, // scan is not running but stopped manually 35 | } ena_bluetooth_scan_status; 36 | 37 | /** 38 | * @brief initialize the BLE scanning 39 | * 40 | */ 41 | void ena_bluetooth_scan_init(void); 42 | 43 | /** 44 | * @brief start BLE scanning for a given duration 45 | * 46 | * Source documents (Section: Scanning Behavior) 47 | * 48 | * https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf 49 | * 50 | * https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-BluetoothSpecificationv1.2.pdf 51 | * 52 | * @param[in] duration duration of the scan in seconds 53 | */ 54 | void ena_bluetooth_scan_start(uint32_t duration); 55 | 56 | /** 57 | * @brief stop a running BLE scanning 58 | */ 59 | void ena_bluetooth_scan_stop(void); 60 | 61 | /** 62 | * @brief return the current scanning status 63 | * 64 | * @return 65 | * current scan status 66 | */ 67 | int ena_bluetooth_scan_get_status(void); 68 | 69 | /** 70 | * @brief return the num of found devices in last scan 71 | * 72 | * @return 73 | * num of last beacons 74 | */ 75 | int ena_bluetooth_scan_get_last_num(void); 76 | 77 | #endif -------------------------------------------------------------------------------- /components/ena/include/ena-crypto.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief covers cryptography part (key creation, encryption etc.) 18 | * 19 | */ 20 | #ifndef _ena_CRYPTO_H_ 21 | #define _ena_CRYPTO_H_ 22 | 23 | #define ENA_TIME_WINDOW (600) // time window every 10 minutes 24 | #define ENA_KEY_LENGTH (16) // key length 25 | #define ENA_AEM_METADATA_LENGTH (4) // size of metadata 26 | #define ENA_TEK_ROLLING_PERIOD (CONFIG_ENA_TEK_ROLLING_PERIOD) // TEKRollingPeriod 27 | 28 | #include 29 | 30 | /** 31 | * @brief initialize cryptography 32 | * 33 | * This initialize the cryptography by setting up entropy. 34 | */ 35 | void ena_crypto_init(void); 36 | 37 | /** 38 | * @brief calculate ENIntervalNumber (ENIN) for given UNIX timestamp 39 | * 40 | * Source documents (Section: ENIntervalNumber) 41 | * 42 | * https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf 43 | * 44 | * https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf 45 | * 46 | * 47 | * @param[in] unix_timestamp UNIX Timestamp to calculate ENIN for 48 | * 49 | * @return 50 | * ENIN for given timestamp 51 | */ 52 | uint32_t ena_crypto_enin(uint32_t unix_timestamp); 53 | 54 | /** 55 | * @brief calculate a new random Temporary Exposure Key (TEK) 56 | * 57 | * Source documents (Section: Temporary Exposure Key) 58 | * 59 | * https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf 60 | * 61 | * https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf 62 | * 63 | * @param[out] tek pointer to the new TEK 64 | */ 65 | void ena_crypto_tek(uint8_t *tek); 66 | 67 | /** 68 | * @brief calculate a new Rolling Proximity Identifier Key (RPIK) with given TEK 69 | * 70 | * Source documents (Section: Rolling Proximity Identifier Key) 71 | * 72 | * https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf 73 | * 74 | * https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf 75 | * 76 | * @param[out] rpik pointer to the new RPIK 77 | * @param[in] tek TEK for calculating RPIK 78 | */ 79 | void ena_crypto_rpik(uint8_t *rpik, uint8_t *tek); 80 | 81 | /** 82 | * @brief calculate a new Rolling Proximity Identifier with given RPIK and ENIN 83 | * 84 | * Source documents (Section: Rolling Proximity Identifier) 85 | * 86 | * https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf 87 | * 88 | * https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf 89 | * 90 | * @param[out] rpi pointer to the new RPI 91 | * @param[in] rpik RPIK for encrypting RPI 92 | * @param[in] enin ENIN to encrypt in RPI 93 | */ 94 | void ena_crypto_rpi(uint8_t *rpi, uint8_t *rpik, uint32_t enin); 95 | 96 | /** 97 | * @brief calculate a new Associated Encrypted Metadata Key (AEMK) with given TEK 98 | * 99 | * Source documents (Section: Associated Encrypted Metadata Key) 100 | * 101 | * https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf 102 | * 103 | * https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf 104 | * 105 | * @param[out] aemk pointer to the new AEMK 106 | * @param[in] tek TEK for calculating AEMK 107 | */ 108 | void ena_crypto_aemk(uint8_t *aemk, uint8_t *tek); 109 | 110 | /** 111 | * @brief create Associated Encrypted Metadata (AEM) with given AEMK along the RPI 112 | * 113 | * Source documents (Section: Associated Encrypted Metadata) 114 | * 115 | * https://blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf 116 | * 117 | * https://covid19-static.cdn-apple.com/applications/covid19/current/static/detection-tracing/pdf/ExposureNotification-CryptographySpecificationv1.2.pdf 118 | * 119 | * @param[out] aem pointer to the new AEM 120 | * @param[in] aemk AEMK for encrypting AEM 121 | * @param[in] rpi RPI for encrypting AEM 122 | * @param[in] power_level BLE power level to encrypt in AEM 123 | */ 124 | void ena_crypto_aem(uint8_t *aem, uint8_t *aemk, uint8_t *rpi, uint8_t power_level); 125 | 126 | #endif -------------------------------------------------------------------------------- /components/ena/include/ena-exposure.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief compare temporary exposure keys with stored beacons, calculate score and risk 18 | * 19 | */ 20 | #ifndef _ena_EXPOSURE_H_ 21 | #define _ena_EXPOSURE_H_ 22 | 23 | #include 24 | #include "esp_err.h" 25 | #include "ena-storage.h" 26 | #include "ena-crypto.h" 27 | 28 | #define ENA_EXPOSURE_LOG "ESP-ENA-exposure" // TAG for Logging 29 | 30 | /** 31 | * @brief report type 32 | */ 33 | typedef enum 34 | { 35 | UNKNOWN = 0, 36 | CONFIRMED_TEST_LOW = 1, 37 | CONFIRMED_TEST_STANDARD = 2, 38 | CONFIRMED_TEST_HIGH = 3, 39 | CONFIRMED_CLINICAL_DIAGNOSIS = 4, 40 | SELF_REPORT = 5, 41 | NEGATIVE = 6, 42 | RECURSIVE = 7, 43 | } ena_report_type_t; 44 | 45 | /** 46 | * @brief duration risk 47 | */ 48 | typedef enum 49 | { 50 | MINUTES_0 = 0, // D = 0 min 51 | MINUTES_5 = 1, // D <= 5 min 52 | MINUTES_10 = 2, // D <= 10 min 53 | MINUTES_15 = 3, // D <= 15 min 54 | MINUTES_20 = 4, // D <= 20 min 55 | MINUTES_25 = 5, // D <= 25 min 56 | MINUTES_30 = 6, // D <= 30 min 57 | MINUTES_LONGER = 7, // D > 30 min 58 | } ena_duration_risk_t; 59 | 60 | /** 61 | * @brief day risk 62 | */ 63 | typedef enum 64 | { 65 | DAYS_14 = 0, // >= 14 days 66 | DAYS_13 = 1, // 12-13 days 67 | DAYS_11 = 2, // 10-11 days 68 | DAYS_9 = 3, // 8-9 days 69 | DAYS_7 = 4, // 6-7 days 70 | DAYS_5 = 5, // 4-5 days 71 | DAYS_3 = 6, // 2-3 days 72 | DAYS_0 = 7, // 0-1 days 73 | } ena_day_risk_t; 74 | 75 | /** 76 | * @brief attenuation risk 77 | */ 78 | typedef enum 79 | { 80 | ATTENUATION_73 = 0, // A > 73 dB 81 | ATTENUATION_63 = 1, // 73 >= A > 63 82 | ATTENUATION_51 = 2, // 63 >= A > 61 83 | ATTENUATION_33 = 3, // 51 >= A > 33 84 | ATTENUATION_27 = 4, // 33 >= A > 27 85 | ATTENUATION_15 = 5, // 27 >= A > 15 86 | ATTENUATION_10 = 6, // 15 >= A > 10 87 | ATTENUATION_LOWER = 7, // A <= 10 88 | } ena_attenuation_risk_t; 89 | 90 | /** 91 | * @brief risk level from 0-8 92 | */ 93 | typedef enum 94 | { 95 | ZERO = 0, 96 | MINIMAL = 1, 97 | VERY_LOW = 2, 98 | LOW = 3, 99 | MEDIUM = 4, 100 | INCREASED = 5, 101 | HIGH = 6, 102 | VERY_HIGH = 7, 103 | MAXIMUM = 8, 104 | } ena_risk_level_t; 105 | 106 | /** 107 | * @brief structure for exposure configuration 108 | * 109 | * The exposure configuration is used to calculate the risk score. 110 | */ 111 | typedef struct __attribute__((__packed__)) 112 | { 113 | uint8_t transmission_risk_values[8]; 114 | uint8_t duration_risk_values[8]; 115 | uint8_t days_risk_values[8]; 116 | uint8_t attenuation_risk_values[8]; 117 | } ena_exposure_config_t; 118 | 119 | /** 120 | * @brief structure for exposure parameter 121 | * 122 | * These parameter are obtained from an exposure information to calculate the risk score. 123 | */ 124 | typedef struct __attribute__((__packed__)) 125 | { 126 | ena_report_type_t report_type; 127 | int days; 128 | int duration; 129 | int attenuation; 130 | } ena_exposure_parameter_t; 131 | 132 | /** 133 | * @brief structure for exposure summary 134 | * 135 | * This represents the current state of all exposures. 136 | */ 137 | typedef struct __attribute__((__packed__)) 138 | { 139 | uint32_t last_update; // timestamp of last update of exposure data 140 | int days_since_last_exposure; // Number of days since the most recent exposure. 141 | int num_exposures; // Number of all exposure information 142 | int max_risk_score; // max. risk score of all exposure information 143 | int risk_score_sum; // sum of all risk_scores 144 | } ena_exposure_summary_t; 145 | 146 | /** 147 | * @brief structure for temporary exposure key 148 | * 149 | * The temporary exposure key is used to check for exposure. 150 | */ 151 | typedef struct __attribute__((__packed__)) 152 | { 153 | uint8_t key_data[ENA_KEY_LENGTH]; 154 | uint8_t transmission_risk_level; 155 | uint32_t rolling_start_interval_number; 156 | uint32_t rolling_period; 157 | ena_report_type_t report_type; 158 | uint32_t days_since_onset_of_symptoms; 159 | } ena_temporary_exposure_key_t; 160 | 161 | /** 162 | * @brief calculate transmission risk score 163 | * 164 | * @param[in] config the exposure configuration used for calculating score 165 | * @param[in] params the exposure parameter to calculate with 166 | * 167 | * @return 168 | */ 169 | int ena_exposure_transmission_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params); 170 | 171 | /** 172 | * @brief calculate duration risk score 173 | * 174 | * @param[in] config the exposure configuration used for calculating score 175 | * @param[in] params the exposure parameter to calculate with 176 | * 177 | * @return 178 | */ 179 | int ena_exposure_duration_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params); 180 | 181 | /** 182 | * @brief calculate days risk score 183 | * 184 | * @param[in] config the exposure configuration used for calculating score 185 | * @param[in] params the exposure parameter to calculate with 186 | * 187 | * @return 188 | */ 189 | int ena_exposure_days_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params); 190 | 191 | /** 192 | * @brief calculate attenuation risk score 193 | * 194 | * @param[in] config the exposure configuration used for calculating score 195 | * @param[in] params the exposure parameter to calculate with 196 | * 197 | * @return 198 | */ 199 | int ena_exposure_attenuation_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params); 200 | 201 | /** 202 | * @brief calculate overall risk score 203 | * 204 | * @param[in] config the exposure configuration used for calculating score 205 | * @param[in] params the exposure parameter to calculate with 206 | * 207 | * @return 208 | */ 209 | int ena_exposure_risk_score(ena_exposure_config_t *config, ena_exposure_parameter_t params); 210 | 211 | /** 212 | * @brief returns the current exposure summary 213 | * 214 | * @param[in] config the exposure configuration used for calculating scores 215 | */ 216 | void ena_exposure_summary(ena_exposure_config_t *config); 217 | 218 | /** 219 | * @brief return the current exposure summary 220 | * 221 | * @return 222 | * ena_exposure_summary_t pointer to the current exposure summary 223 | */ 224 | ena_exposure_summary_t *ena_exposure_current_summary(void); 225 | 226 | /** 227 | * @brief return a default exposure configuration 228 | * 229 | * @return 230 | * ena_exposure_config_t default exposure configuration 231 | */ 232 | ena_exposure_config_t *ena_exposure_default_config(void); 233 | 234 | /** 235 | * @brief reads Temporary Exposue Key check for exposures with certain beacon 236 | * 237 | * @param[in] ena_beacon_t the beacon to check against 238 | * @param[in] temporary_exposure_key the temporary exposure keys to check 239 | */ 240 | void ena_exposure_check(ena_beacon_t beacon, ena_temporary_exposure_key_t temporary_exposure_key); 241 | 242 | /** 243 | * @brief find minimal key index of beacons for a certain timestamp 244 | * 245 | * @param[in] timestamp the timestamp to check against 246 | */ 247 | int ena_expore_check_find_min(uint32_t timestamp); 248 | 249 | /** 250 | * @brief find maximum key index of beacons for a certain timestamp 251 | * 252 | * @param[in] timestamp the timestamp to check against 253 | */ 254 | int ena_expore_check_find_max(uint32_t timestamp); 255 | 256 | /** 257 | * @brief reads Temporary Exposue Key and check for exposures with all beacons 258 | * 259 | * @param[in] temporary_exposure_key the temporary exposure keys to check 260 | */ 261 | void ena_exposure_check_temporary_exposure_key(ena_temporary_exposure_key_t temporary_exposure_key); 262 | 263 | #endif -------------------------------------------------------------------------------- /components/ena/include/ena.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief run all other ena parts together to time scanning, advertising and exposure checks 18 | * 19 | */ 20 | #ifndef _ena_H_ 21 | #define _ena_H_ 22 | 23 | #define ENA_LOG "ESP-ENA" // TAG for Logging 24 | #define ENA_BT_ROTATION_TIMEOUT_INTERVAL (CONFIG_ENA_BT_ROTATION_TIMEOUT_INTERVAL) // change advertising payload and therefore the BT address 25 | #define ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL (CONFIG_ENA_BT_RANDOMIZE_ROTATION_TIMEOUT_INTERVAL) // random intervall change for BT address change 26 | 27 | /** 28 | * @brief Run Exposure Notification API 29 | * 30 | * This runs the complete BLE logic 31 | * 32 | */ 33 | void ena_run(void); 34 | 35 | /** 36 | * @brief Start Exposure Notification API 37 | * 38 | * This initializes the complete stack of ESP_ENA. It will initialize BLE module and 39 | * starting a task for managing advertising and scanning processes. 40 | * 41 | */ 42 | void ena_start(void); 43 | 44 | /** 45 | * @brief stop ena 46 | */ 47 | void ena_stop(void); 48 | 49 | #endif -------------------------------------------------------------------------------- /components/i2c-main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS 3 | "i2c-main.c" 4 | INCLUDE_DIRS "." 5 | PRIV_REQUIRES 6 | driver 7 | ) -------------------------------------------------------------------------------- /components/i2c-main/i2c-main.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | 17 | #include "driver/i2c.h" 18 | 19 | #include "i2c-main.h" 20 | 21 | static bool i2c_initialized = false; 22 | 23 | void i2c_main_init() 24 | { 25 | i2c_config_t i2c_config = { 26 | .mode = I2C_MODE_MASTER, 27 | .sda_io_num = I2C_SDA_PIN, 28 | .scl_io_num = I2C_SCL_PIN, 29 | .sda_pullup_en = GPIO_PULLUP_ENABLE, 30 | .scl_pullup_en = GPIO_PULLUP_ENABLE, 31 | .master.clk_speed = I2C_CLK_SPEED}; 32 | ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &i2c_config)); 33 | ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0)); 34 | i2c_initialized = true; 35 | } 36 | 37 | bool i2c_is_initialized() 38 | { 39 | return i2c_initialized; 40 | } -------------------------------------------------------------------------------- /components/i2c-main/i2c-main.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief start I2C driver for display and RTC. 18 | * 19 | */ 20 | #ifndef _i2c_main_H_ 21 | #define _i2c_main_H_ 22 | 23 | #if defined(CONFIG_ENA_INTERFACE_CUSTOM) 24 | #define I2C_SDA_PIN (CONFIG_I2C_SDA_PIN) 25 | #define I2C_SCL_PIN (CONFIG_I2C_SCL_PIN) 26 | #else 27 | #define I2C_SDA_PIN 21 28 | #define I2C_SCL_PIN 22 29 | #endif 30 | #define I2C_CLK_SPEED (1000000) 31 | 32 | /** 33 | * @brief initialize main I2C interface 34 | */ 35 | void i2c_main_init(); 36 | 37 | /** 38 | * @brief check if I2C interface already initialized 39 | * 40 | * @return 41 | * - false I2C not initialized 42 | * - true I2C initialized 43 | */ 44 | bool i2c_is_initialized(); 45 | 46 | #endif -------------------------------------------------------------------------------- /components/interface/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(src_list 2 | "interface.c" 3 | "interface-main.c" 4 | "interface-data.c" 5 | "interface-datetime.c" 6 | "interface-info.c" 7 | "interface-input.c" 8 | "interface-label.c" 9 | "interface-report.c" 10 | "interface-settings.c" 11 | "interface-wifi.c") 12 | 13 | set(include_list ".") 14 | 15 | if(CONFIG_ENA_INTERFACE_INPUT_CUSTOM) 16 | list(APPEND src_list "custom-input/custom-input.c") 17 | list(APPEND include_list "custom-input") 18 | elseif(CONFIG_ENA_INTERFACE_M5STICKC OR CONFIG_ENA_INTERFACE_M5STICKC_PLUS) 19 | list(APPEND src_list "m5-input/m5-input.c" "m5-mpu6886/mpu6886.c") 20 | list(APPEND include_list "m5-input" "m5-mpu6886") 21 | elseif(CONFIG_ENA_INTERFACE_TTGO_T_WRISTBAND) 22 | list(APPEND src_list "ttgo-input/ttgo-input.c" "ttgo-lsm9ds1/lsm9ds1.c") 23 | list(APPEND include_list "ttgo-input" "ttgo-lsm9ds1") 24 | else() 25 | list(APPEND src_list "dummy.c") 26 | endif() 27 | 28 | idf_component_register( 29 | SRCS 30 | ${src_list} 31 | INCLUDE_DIRS 32 | ${include_list} 33 | PRIV_REQUIRES 34 | "ena" 35 | "ena-eke-proxy" 36 | "display" 37 | "rtc" 38 | "wifi-controller" 39 | "i2c-main" 40 | ) 41 | -------------------------------------------------------------------------------- /components/interface/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | 2 | menu "ENA Interface" 3 | 4 | config ENA_INTERFACE_IDLE_TIME 5 | int "Seconds after display turns off on inactivity" 6 | default 15 7 | 8 | choice ENA_INTERFACE_DEVICE 9 | prompt "Choose device" 10 | default ENA_INTERFACE_CUSTOM 11 | 12 | config ENA_INTERFACE_CUSTOM 13 | bool "Custom" 14 | 15 | choice ENA_INTERFACE_CUSTOM_DISPLAY 16 | depends on ENA_INTERFACE_CUSTOM 17 | prompt "Choose custom display" 18 | default ENA_INTERFACE_DISPLAY_SSD1306 19 | config ENA_INTERFACE_DISPLAY_SSD1306 20 | bool "SSD1306" 21 | config ENA_INTERFACE_DISPLAY_NONE 22 | bool "none" 23 | endchoice 24 | 25 | choice ENA_INTERFACE_CUSTOM_RTC 26 | depends on ENA_INTERFACE_CUSTOM 27 | prompt "Choose custom rtc" 28 | default ENA_INTERFACE_RTC_DS3231 29 | config ENA_INTERFACE_RTC_DS3231 30 | bool "DS3231" 31 | config ENA_INTERFACE_RTC_NONE 32 | bool "none" 33 | endchoice 34 | 35 | choice ENA_INTERFACE_CUSTOM_INPUT 36 | depends on ENA_INTERFACE_CUSTOM 37 | prompt "Choose custom input" 38 | default ENA_INTERFACE_INPUT_CUSTOM 39 | config ENA_INTERFACE_INPUT_CUSTOM 40 | bool "7-Button input" 41 | config ENA_INTERFACE_INPUT_NONE 42 | bool "none" 43 | endchoice 44 | 45 | menu "Custom I²C" 46 | depends on ENA_INTERFACE_CUSTOM 47 | 48 | config I2C_SDA_PIN 49 | int "I²C sda pin" 50 | default 21 51 | 52 | config I2C_SCL_PIN 53 | int "I²C scl pin" 54 | default 22 55 | 56 | endmenu 57 | 58 | config ENA_INTERFACE_M5STICKC 59 | bool "M5StickC" 60 | 61 | config ENA_INTERFACE_M5STICKC_PLUS 62 | bool "M5StickC PLUS" 63 | 64 | config ENA_INTERFACE_TTGO_T_WRISTBAND 65 | bool "TTGO T-Wristband" 66 | endchoice 67 | 68 | endmenu -------------------------------------------------------------------------------- /components/interface/custom-input/custom-input.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include "freertos/FreeRTOS.h" 16 | #include "freertos/task.h" 17 | #include "driver/gpio.h" 18 | #include "esp_log.h" 19 | 20 | #include "interface.h" 21 | 22 | #include "custom-input.h" 23 | 24 | static float input_states[INTERFACE_COMMANDS_SIZE]; 25 | static float input_trigger_state[INTERFACE_COMMANDS_SIZE]; 26 | static int input_command_mapping[INTERFACE_COMMANDS_SIZE]; 27 | 28 | void custom_input_check(interface_command_t command) 29 | { 30 | int button_level = gpio_get_level(input_command_mapping[command]); 31 | 32 | if (button_level == 0) 33 | { 34 | input_states[command] = input_states[command] + ((float)INTERFACE_INPUT_TICKS_MS / 1000); 35 | 36 | if (command == INTERFACE_COMMAND_SET && input_states[command] > INTERFACE_LONG_STATE_SECONDS) 37 | { 38 | input_states[command] = 0; 39 | input_states[INTERFACE_COMMAND_SET_LONG] = input_states[INTERFACE_COMMAND_SET_LONG] + 1; 40 | interface_execute_command(INTERFACE_COMMAND_SET_LONG); 41 | } 42 | else if (command == INTERFACE_COMMAND_RST && input_states[command] > INTERFACE_LONG_STATE_SECONDS) 43 | { 44 | input_states[command] = 0; 45 | input_states[INTERFACE_COMMAND_RST_LONG] = input_states[INTERFACE_COMMAND_RST_LONG] + 1; 46 | interface_execute_command(INTERFACE_COMMAND_RST_LONG); 47 | } 48 | else if (input_states[command] > input_trigger_state[command]) 49 | { 50 | input_trigger_state[command] = input_trigger_state[command] - (input_trigger_state[command] / 8); 51 | if (input_trigger_state[command] <= ((float)INTERFACE_INPUT_TICKS_MS / 200)) 52 | { 53 | input_trigger_state[command] = ((float)INTERFACE_INPUT_TICKS_MS / 200); 54 | } 55 | input_states[command] = 0; 56 | interface_execute_command_trigger(command); 57 | } 58 | } 59 | else if (button_level == 1 && input_states[command] > 0) 60 | { 61 | input_states[command] = 0; 62 | input_trigger_state[command] = INTERFACE_LONG_STATE_SECONDS; 63 | if (command == INTERFACE_COMMAND_SET && input_states[INTERFACE_COMMAND_SET_LONG] > 0) 64 | { 65 | input_states[INTERFACE_COMMAND_SET_LONG] = 0; 66 | } 67 | else if (command == INTERFACE_COMMAND_RST && input_states[INTERFACE_COMMAND_RST_LONG] > 0) 68 | { 69 | input_states[INTERFACE_COMMAND_RST_LONG] = 0; 70 | } 71 | else 72 | { 73 | interface_execute_command(command); 74 | } 75 | } 76 | else if (button_level == 1) 77 | { 78 | input_trigger_state[command] = INTERFACE_LONG_STATE_SECONDS; 79 | } 80 | } 81 | 82 | void custom_input_task(void *pvParameter) 83 | { 84 | while (1) 85 | { 86 | custom_input_check(INTERFACE_COMMAND_SET); 87 | if (!interface_is_idle()) 88 | { 89 | custom_input_check(INTERFACE_COMMAND_RST); 90 | custom_input_check(INTERFACE_COMMAND_MID); 91 | custom_input_check(INTERFACE_COMMAND_RHT); 92 | custom_input_check(INTERFACE_COMMAND_LFT); 93 | custom_input_check(INTERFACE_COMMAND_DWN); 94 | custom_input_check(INTERFACE_COMMAND_UP); 95 | } 96 | vTaskDelay(INTERFACE_INPUT_TICKS_MS / portTICK_PERIOD_MS); 97 | } 98 | } 99 | 100 | void interface_input_start(void) 101 | { 102 | gpio_config_t io_conf; 103 | 104 | io_conf.pin_bit_mask = (1ULL << BUTTON_RST) | (1ULL << BUTTON_SET) | 105 | (1ULL << BUTTON_MID) | (1ULL << BUTTON_RHT) | 106 | (1ULL << BUTTON_LFT) | (1ULL << BUTTON_DWN) | 107 | (1ULL << BUTTON_UP); 108 | io_conf.intr_type = GPIO_INTR_DISABLE; 109 | io_conf.mode = GPIO_MODE_INPUT; 110 | io_conf.pull_up_en = GPIO_PULLUP_ENABLE; 111 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; 112 | gpio_config(&io_conf); 113 | 114 | input_command_mapping[INTERFACE_COMMAND_RST] = BUTTON_RST; 115 | input_command_mapping[INTERFACE_COMMAND_SET] = BUTTON_SET; 116 | input_command_mapping[INTERFACE_COMMAND_MID] = BUTTON_MID; 117 | input_command_mapping[INTERFACE_COMMAND_RHT] = BUTTON_RHT; 118 | input_command_mapping[INTERFACE_COMMAND_LFT] = BUTTON_LFT; 119 | input_command_mapping[INTERFACE_COMMAND_DWN] = BUTTON_DWN; 120 | input_command_mapping[INTERFACE_COMMAND_UP] = BUTTON_UP; 121 | 122 | for (int i = 0; i < INTERFACE_COMMANDS_SIZE; i++) 123 | { 124 | input_trigger_state[i] = INTERFACE_LONG_STATE_SECONDS; 125 | } 126 | 127 | xTaskCreate(&custom_input_task, "custom_input_task", 4096, NULL, 5, NULL); 128 | } -------------------------------------------------------------------------------- /components/interface/custom-input/custom-input.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief execute interface commands via simple push buttons 18 | * 19 | */ 20 | #ifndef _button_input_H_ 21 | #define _button_input_H_ 22 | 23 | #define BUTTON_RST GPIO_NUM_32 24 | #define BUTTON_SET GPIO_NUM_33 25 | #define BUTTON_MID GPIO_NUM_25 26 | #define BUTTON_RHT GPIO_NUM_26 27 | #define BUTTON_LFT GPIO_NUM_27 28 | #define BUTTON_DWN GPIO_NUM_14 29 | #define BUTTON_UP GPIO_NUM_12 30 | 31 | #endif -------------------------------------------------------------------------------- /components/interface/dummy.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include "interface.h" 16 | 17 | void interface_input_start(void) 18 | { 19 | } -------------------------------------------------------------------------------- /components/interface/interface-data.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | #include 17 | #include "freertos/FreeRTOS.h" 18 | #include "freertos/task.h" 19 | #include "esp_log.h" 20 | #include "driver/gpio.h" 21 | 22 | #include "display.h" 23 | #include "display-gfx.h" 24 | 25 | #include "ena-storage.h" 26 | 27 | #include "interface.h" 28 | 29 | typedef enum 30 | { 31 | INTERFACE_DATA_DEL_TEK = 0, 32 | INTERFACE_DATA_DEL_EXPOSURE_INFO, 33 | INTERFACE_DATA_DEL_TEMP_RPI, 34 | INTERFACE_DATA_DEL_RPI, 35 | INTERFACE_DATA_DEL_LAST_EXPOSURE, 36 | INTERFACE_DATA_DEL_ALL, 37 | } interface_data_state_t; 38 | 39 | static int current_interface_data_state; 40 | static int current_data_index; 41 | static bool confirm_current; 42 | 43 | void interface_data_set(void) 44 | { 45 | if (!confirm_current) 46 | { 47 | interface_main_start(); 48 | } 49 | else 50 | { 51 | confirm_current = false; 52 | display_clear_line(2, false); 53 | display_clear_line(3, false); 54 | display_clear_line(4, false); 55 | display_clear_line(5, false); 56 | display_clear_line(6, false); 57 | display_clear_line(7, false); 58 | } 59 | } 60 | 61 | void interface_data_rst(void) 62 | { 63 | 64 | if (confirm_current) 65 | { 66 | switch (current_interface_data_state) 67 | { 68 | case INTERFACE_DATA_DEL_TEK: 69 | ena_storage_erase_tek(); 70 | break; 71 | case INTERFACE_DATA_DEL_EXPOSURE_INFO: 72 | ena_storage_erase_exposure_information(); 73 | break; 74 | case INTERFACE_DATA_DEL_TEMP_RPI: 75 | ena_storage_erase_temporary_beacon(); 76 | break; 77 | case INTERFACE_DATA_DEL_RPI: 78 | ena_storage_erase_beacon(); 79 | break; 80 | case INTERFACE_DATA_DEL_LAST_EXPOSURE: 81 | ena_storage_write_last_exposure_date(0); 82 | break; 83 | case INTERFACE_DATA_DEL_ALL: 84 | ena_storage_erase_all(); 85 | break; 86 | } 87 | 88 | confirm_current = false; 89 | display_clear_line(2, false); 90 | display_clear_line(3, false); 91 | display_clear_line(4, false); 92 | display_clear_line(5, false); 93 | display_clear_line(6, false); 94 | display_clear_line(7, false); 95 | } 96 | else 97 | { 98 | display_clear_line(2, false); 99 | display_clear_line(4, false); 100 | display_clear_line(6, false); 101 | confirm_current = true; 102 | } 103 | } 104 | 105 | void interface_data_lft(void) 106 | { 107 | interface_datetime_start(); 108 | } 109 | 110 | void interface_data_rht(void) 111 | { 112 | interface_info_start(); 113 | } 114 | 115 | void interface_data_mid(void) 116 | { 117 | if (!confirm_current) 118 | { 119 | display_clear_line(2, false); 120 | display_clear_line(4, false); 121 | display_clear_line(6, false); 122 | confirm_current = true; 123 | } 124 | } 125 | 126 | void interface_data_up(void) 127 | { 128 | current_interface_data_state--; 129 | if (current_interface_data_state < INTERFACE_DATA_DEL_TEK) 130 | { 131 | current_interface_data_state = INTERFACE_DATA_DEL_ALL; 132 | 133 | current_data_index = 3; 134 | } 135 | else if (current_interface_data_state < current_data_index) 136 | { 137 | current_data_index--; 138 | } 139 | 140 | display_clear_line(2, false); 141 | display_clear_line(4, false); 142 | display_clear_line(6, false); 143 | } 144 | 145 | void interface_data_dwn(void) 146 | { 147 | current_interface_data_state++; 148 | if (current_interface_data_state > INTERFACE_DATA_DEL_ALL) 149 | { 150 | current_interface_data_state = INTERFACE_DATA_DEL_TEK; 151 | current_data_index = 0; 152 | } 153 | else if (current_interface_data_state >= (current_data_index + 3)) 154 | { 155 | current_data_index++; 156 | } 157 | 158 | display_clear_line(2, false); 159 | display_clear_line(4, false); 160 | display_clear_line(6, false); 161 | } 162 | 163 | void interface_data_display(void) 164 | { 165 | display_menu_headline(interface_get_label_text(&interface_text_headline_data), true, 0); 166 | 167 | if (confirm_current) 168 | { 169 | display_text_line_column(interface_get_label_text(&interface_text_data_del[current_interface_data_state]), 2, 2, false); 170 | display_text_line_column("?", 2, strlen(interface_get_label_text(&interface_text_data_del[current_interface_data_state])) + 2, false); 171 | 172 | display_set_button(interface_get_label_text(&interface_text_button_cancel), true, false); 173 | display_set_button(interface_get_label_text(&interface_text_button_ok), false, true); 174 | } 175 | else 176 | { 177 | display_clear_line(2, false); 178 | display_clear_line(4, false); 179 | display_clear_line(6, false); 180 | for (int i = 0; i < 3; i++) 181 | { 182 | int index = i + current_data_index; 183 | if (index <= INTERFACE_DATA_DEL_ALL) 184 | { 185 | if (index == current_interface_data_state) 186 | { 187 | display_data(display_gfx_arrow_right, 8, i * 2 + 2, 8, false); 188 | } 189 | else 190 | { 191 | display_data(display_gfx_clear, 8, i * 2 + 2, 8, false); 192 | } 193 | 194 | display_text_line_column(interface_get_label_text(&interface_text_data_del[index]), i * 2 + 2, 2, false); 195 | } 196 | } 197 | } 198 | } 199 | 200 | void interface_data_start(void) 201 | { 202 | current_interface_data_state = INTERFACE_DATA_DEL_TEK; 203 | 204 | interface_register_command_callback(INTERFACE_COMMAND_RST, &interface_data_rst); 205 | interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_data_set); 206 | interface_register_command_callback(INTERFACE_COMMAND_LFT, &interface_data_lft); 207 | interface_register_command_callback(INTERFACE_COMMAND_RHT, &interface_data_rht); 208 | interface_register_command_callback(INTERFACE_COMMAND_MID, &interface_data_mid); 209 | interface_register_command_callback(INTERFACE_COMMAND_UP, &interface_data_up); 210 | interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_data_dwn); 211 | interface_register_command_callback(INTERFACE_COMMAND_RST_LONG, NULL); 212 | interface_register_command_callback(INTERFACE_COMMAND_SET_LONG, NULL); 213 | 214 | interface_set_display_function(&interface_data_display); 215 | } 216 | -------------------------------------------------------------------------------- /components/interface/interface-datetime.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | #include 17 | #include "esp_log.h" 18 | #include "driver/gpio.h" 19 | 20 | #include "rtc.h" 21 | #include "display.h" 22 | #include "display-gfx.h" 23 | 24 | #include "interface.h" 25 | 26 | typedef enum 27 | { 28 | INTERFACE_DATETIME_STATE_HOUR = 0, 29 | INTERFACE_DATETIME_STATE_MINUTE, 30 | INTERFACE_DATETIME_STATE_SECONDS, 31 | INTERFACE_DATETIME_STATE_DAY, 32 | INTERFACE_DATETIME_STATE_MONTH, 33 | INTERFACE_DATETIME_STATE_YEAR, 34 | } interface_datetime_state_t; 35 | 36 | static int current_interface_datetime_state; 37 | 38 | const uint32_t interface_datetime_steps[6] = {3600, 60, 1, 86400, 2629800, 31557600}; 39 | 40 | int interface_datetime_state(void) 41 | { 42 | return current_interface_datetime_state; 43 | } 44 | 45 | void interface_datetime_set(void) 46 | { 47 | interface_main_start(); 48 | } 49 | 50 | void interface_datetime_lft(void) 51 | { 52 | interface_wifi_start(); 53 | } 54 | 55 | void interface_datetime_rht(void) 56 | { 57 | interface_data_start(); 58 | } 59 | 60 | void interface_datetime_mid(void) 61 | { 62 | current_interface_datetime_state++; 63 | if (current_interface_datetime_state > INTERFACE_DATETIME_STATE_YEAR) 64 | { 65 | current_interface_datetime_state = INTERFACE_DATETIME_STATE_HOUR; 66 | } 67 | } 68 | 69 | void interface_datetime_up(void) 70 | { 71 | time_t curtime = time(NULL); 72 | curtime += interface_datetime_steps[current_interface_datetime_state]; 73 | struct timeval tv = {0}; 74 | tv.tv_sec = curtime; 75 | settimeofday(&tv, NULL); 76 | rtc_set_time(gmtime(&curtime)); 77 | } 78 | 79 | void interface_datetime_dwn(void) 80 | { 81 | time_t curtime = time(NULL); 82 | curtime -= interface_datetime_steps[current_interface_datetime_state]; 83 | struct timeval tv = {0}; 84 | tv.tv_sec = curtime; 85 | settimeofday(&tv, NULL); 86 | rtc_set_time(gmtime(&curtime)); 87 | } 88 | 89 | void interface_datetime_display(void) 90 | { 91 | display_menu_headline(interface_get_label_text(&interface_text_headline_time), true, 0); 92 | } 93 | 94 | void interface_datetime_display_refresh(void) 95 | { 96 | static time_t current_timstamp; 97 | static struct tm *current_tm; 98 | static char time_buffer[9]; 99 | static char date_buffer[32]; 100 | 101 | static char edit_char[3]; 102 | static int edit_line = 3; 103 | int edit_length = 2; 104 | int edit_offset = 0; 105 | 106 | display_clear_line(edit_line - 1, false); 107 | display_clear_line(edit_line + 1, false); 108 | 109 | time(¤t_timstamp); 110 | current_tm = gmtime(¤t_timstamp); 111 | current_tm->tm_hour = current_tm->tm_hour + (interface_get_timezone_offset()) % 24; 112 | 113 | strftime(time_buffer, 16, INTERFACE_FORMAT_TIME, current_tm); 114 | display_text_line_column(time_buffer, 3, 4, false); 115 | 116 | sprintf(date_buffer, "%02d %s %02d", 117 | current_tm->tm_mday, 118 | interface_get_label_text(&interface_texts_month[current_tm->tm_mon]), 119 | current_tm->tm_year - 100); 120 | display_text_line_column(date_buffer, 6, 4, false); 121 | 122 | switch (interface_datetime_state()) 123 | { 124 | 125 | case INTERFACE_DATETIME_STATE_YEAR: 126 | memcpy(&edit_char, &date_buffer[7], edit_length); 127 | edit_line = 6; 128 | edit_offset = 11; 129 | break; 130 | case INTERFACE_DATETIME_STATE_DAY: 131 | memcpy(&edit_char, &date_buffer[0], edit_length); 132 | edit_line = 6; 133 | edit_offset = 4; 134 | break; 135 | case INTERFACE_DATETIME_STATE_MONTH: 136 | edit_length = 3; 137 | memcpy(&edit_char, &date_buffer[3], edit_length); 138 | edit_line = 6; 139 | edit_offset = 7; 140 | break; 141 | case INTERFACE_DATETIME_STATE_HOUR: 142 | memcpy(&edit_char, &time_buffer[0], edit_length); 143 | edit_line = 3; 144 | edit_offset = 4; 145 | break; 146 | case INTERFACE_DATETIME_STATE_MINUTE: 147 | memcpy(&edit_char, &time_buffer[3], edit_length); 148 | edit_line = 3; 149 | edit_offset = 7; 150 | break; 151 | case INTERFACE_DATETIME_STATE_SECONDS: 152 | memcpy(&edit_char, &time_buffer[6], edit_length); 153 | edit_line = 3; 154 | edit_offset = 10; 155 | break; 156 | } 157 | 158 | display_data(display_gfx_arrow_up, 8, edit_line - 1, edit_offset * 8 + 4, false); 159 | display_chars(edit_char, edit_length, edit_line, edit_offset, true); 160 | display_data(display_gfx_arrow_down, 8, edit_line + 1, edit_offset * 8 + 4, false); 161 | 162 | display_menu_headline(interface_get_label_text(&interface_text_headline_time), true, 0); 163 | } 164 | 165 | void interface_datetime_start(void) 166 | { 167 | current_interface_datetime_state = INTERFACE_DATETIME_STATE_HOUR; 168 | interface_register_command_callback(INTERFACE_COMMAND_LFT, &interface_datetime_lft); 169 | interface_register_command_callback(INTERFACE_COMMAND_RHT, &interface_datetime_rht); 170 | interface_register_command_callback(INTERFACE_COMMAND_RST, &interface_datetime_mid); 171 | interface_register_command_callback(INTERFACE_COMMAND_MID, &interface_datetime_mid); 172 | interface_register_command_callback(INTERFACE_COMMAND_UP, &interface_datetime_up); 173 | interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_datetime_dwn); 174 | interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_datetime_set); 175 | interface_register_command_callback(INTERFACE_COMMAND_RST_LONG, NULL); 176 | interface_register_command_callback(INTERFACE_COMMAND_SET_LONG, NULL); 177 | 178 | interface_set_display_function(&interface_datetime_display); 179 | interface_set_display_refresh_function(&interface_datetime_display_refresh); 180 | } -------------------------------------------------------------------------------- /components/interface/interface-info.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | #include 17 | #include "freertos/FreeRTOS.h" 18 | #include "freertos/task.h" 19 | #include "esp_log.h" 20 | #include "driver/gpio.h" 21 | 22 | #include "display.h" 23 | #include "display-gfx.h" 24 | 25 | #include "ena-storage.h" 26 | #include "ena-exposure.h" 27 | #include "ena-bluetooth-scan.h" 28 | 29 | #include "interface.h" 30 | 31 | typedef enum 32 | { 33 | INTERFACE_INFO_STATUS_EXPOSURE = 0, 34 | INTERFACE_INFO_STATUS_BEACONS, 35 | INTERFACE_INFO_STATUS_SCAN, 36 | INTERFACE_INFO_STATUS_SYSTEM 37 | } info_status_e; 38 | 39 | static int current_info_status = INTERFACE_INFO_STATUS_EXPOSURE; 40 | 41 | void interface_info_set(void) 42 | { 43 | interface_main_start(); 44 | } 45 | 46 | void interface_info_rst(void) 47 | { 48 | } 49 | 50 | void interface_info_lft(void) 51 | { 52 | interface_data_start(); 53 | } 54 | 55 | void interface_info_rht(void) 56 | { 57 | interface_settings_start(); 58 | } 59 | 60 | void interface_info_mid(void) 61 | { 62 | } 63 | 64 | void interface_info_up(void) 65 | { 66 | current_info_status--; 67 | if (current_info_status < INTERFACE_INFO_STATUS_EXPOSURE) 68 | { 69 | current_info_status = INTERFACE_INFO_STATUS_SYSTEM; 70 | } 71 | } 72 | 73 | void interface_info_dwn(void) 74 | { 75 | current_info_status++; 76 | if (current_info_status > INTERFACE_INFO_STATUS_SYSTEM) 77 | { 78 | current_info_status = INTERFACE_INFO_STATUS_EXPOSURE; 79 | } 80 | } 81 | 82 | int interface_info_num_offset(int num) 83 | { 84 | if (num < 10) 85 | { 86 | return 13; 87 | } 88 | else if (num < 100) 89 | { 90 | return 12; 91 | } 92 | else 93 | { 94 | return 11; 95 | } 96 | } 97 | 98 | void interface_info_display(void) 99 | { 100 | display_clear(); 101 | char char_buffer[64]; 102 | display_menu_headline(interface_get_label_text(&interface_text_headline_info), true, 0); 103 | 104 | display_data(display_gfx_arrow_up, 8, 1, 60, false); 105 | 106 | if (current_info_status == INTERFACE_INFO_STATUS_EXPOSURE) 107 | { 108 | ena_exposure_summary_t *current_exposure_summary = ena_exposure_current_summary(); 109 | 110 | display_text_line_column(interface_get_label_text(&interface_text_info_exp_days), 3, 1, false); 111 | int last = current_exposure_summary->days_since_last_exposure; 112 | if (last >= 0) 113 | { 114 | sprintf(char_buffer, "%d", last); 115 | display_text_line_column(char_buffer, 3, interface_info_num_offset(last), false); 116 | } 117 | 118 | display_text_line_column(interface_get_label_text(&interface_text_info_exp_num), 4, 1, false); 119 | sprintf(char_buffer, "%d", current_exposure_summary->num_exposures); 120 | display_text_line_column(char_buffer, 4, interface_info_num_offset(current_exposure_summary->num_exposures), false); 121 | 122 | display_text_line_column(interface_get_label_text(&interface_text_info_exp_max), 5, 1, false); 123 | sprintf(char_buffer, "%d", current_exposure_summary->max_risk_score); 124 | display_text_line_column(char_buffer, 5, interface_info_num_offset(current_exposure_summary->max_risk_score), false); 125 | 126 | display_text_line_column(interface_get_label_text(&interface_text_info_exp_sum), 6, 1, false); 127 | sprintf(char_buffer, "%d", current_exposure_summary->risk_score_sum); 128 | display_text_line_column(char_buffer, 6, interface_info_num_offset(current_exposure_summary->risk_score_sum), false); 129 | } 130 | else if (current_info_status == INTERFACE_INFO_STATUS_BEACONS) 131 | { 132 | display_text_line_column(interface_get_label_text(&interface_text_info_num_keys), 3, 1, false); 133 | 134 | int num = ena_storage_beacons_count(); 135 | sprintf(char_buffer, "%u", num); 136 | display_text_line_column(char_buffer, 3, interface_info_num_offset(num), false); 137 | 138 | display_text_line_column(interface_get_label_text(&interface_text_info_last_keys), 4, 1, false); 139 | 140 | time_t current_timstamp; 141 | time(¤t_timstamp); 142 | 143 | int min = ena_expore_check_find_min((uint32_t)current_timstamp - 60 * 30); 144 | int last30 = num - min; 145 | 146 | if (last30 >= 0) 147 | { 148 | sprintf(char_buffer, "%d", last30); 149 | display_text_line_column(char_buffer, 4, interface_info_num_offset(last30), false); 150 | } 151 | } 152 | else if (current_info_status == INTERFACE_INFO_STATUS_SCAN) 153 | { 154 | int current_scan = ena_bluetooth_scan_get_last_num(); 155 | 156 | switch (ena_bluetooth_scan_get_status()) 157 | { 158 | case ENA_SCAN_STATUS_SCANNING: 159 | display_text_line_column(interface_get_label_text(&interface_text_info_scan_status_scanning), 3, 1, false); 160 | break; 161 | case ENA_SCAN_STATUS_NOT_SCANNING: 162 | display_text_line_column(interface_get_label_text(&interface_text_info_scan_status_notscanning), 3, 1, false); 163 | break; 164 | case ENA_SCAN_STATUS_WAITING: 165 | display_text_line_column(interface_get_label_text(&interface_text_info_scan_status_waiting), 3, 1, false); 166 | break; 167 | } 168 | 169 | display_text_line_column(interface_get_label_text(&interface_text_info_scan_last), 5, 1, false); 170 | sprintf(char_buffer, "%d", current_scan); 171 | display_text_line_column(char_buffer, 5, interface_info_num_offset(current_scan), false); 172 | } 173 | else if (current_info_status == INTERFACE_INFO_STATUS_SYSTEM) 174 | { 175 | } 176 | 177 | display_data(display_gfx_arrow_down, 8, 7, 60, false); 178 | } 179 | 180 | void interface_info_start(void) 181 | { 182 | 183 | interface_register_command_callback(INTERFACE_COMMAND_RST, &interface_info_rst); 184 | interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_info_set); 185 | interface_register_command_callback(INTERFACE_COMMAND_LFT, &interface_info_lft); 186 | interface_register_command_callback(INTERFACE_COMMAND_RHT, &interface_info_rht); 187 | interface_register_command_callback(INTERFACE_COMMAND_MID, &interface_info_mid); 188 | interface_register_command_callback(INTERFACE_COMMAND_UP, &interface_info_up); 189 | interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_info_dwn); 190 | interface_register_command_callback(INTERFACE_COMMAND_RST_LONG, NULL); 191 | interface_register_command_callback(INTERFACE_COMMAND_SET_LONG, NULL); 192 | 193 | interface_set_display_function(&interface_info_display); 194 | } 195 | -------------------------------------------------------------------------------- /components/interface/interface-label.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief texts for intefaces 18 | * 19 | */ 20 | #include 21 | #include "interface.h" 22 | 23 | static interface_locale_t current_locale = EN; 24 | 25 | void interface_init_label(void) 26 | { 27 | // EN 28 | interface_text_button_cancel.text[EN] = "CANCEL"; 29 | interface_text_button_ok.text[EN] = "OK"; 30 | interface_text_button_back.text[EN] = "BACK"; 31 | interface_text_button_menu.text[EN] = "MENU"; 32 | interface_text_button_report.text[EN] = "REPORT"; 33 | 34 | interface_text_headline_tan.text[EN] = "ENTER TAN"; 35 | interface_text_headline_report.text[EN] = "REPORT"; 36 | interface_text_headline_wifi.text[EN] = "WIFI"; 37 | interface_text_headline_time.text[EN] = "TIME/DATE"; 38 | interface_text_headline_data.text[EN] = "DEL DATA"; 39 | interface_text_headline_settings.text[EN] = "SETTING"; 40 | interface_text_headline_info.text[EN] = "INFO"; 41 | interface_text_headline_debug.text[EN] = "DEBUG"; 42 | 43 | interface_text_wifi_waiting.text[EN] = "Waiting..."; 44 | interface_text_wifi_scanning.text[EN] = "Scanning..."; 45 | interface_text_wifi_connecting.text[EN] = "Connecting..."; 46 | interface_text_wifi_nothing.text[EN] = "None..."; 47 | 48 | interface_text_settings_locale.text[EN] = "Language:"; 49 | interface_text_settings_locales[EN].text[EN] = "EN"; 50 | interface_text_settings_locales[DE].text[EN] = "DE"; 51 | 52 | interface_text_settings_timezone.text[EN] = "UTC:"; 53 | 54 | interface_text_info_num_keys.text[EN] = "Seen:"; 55 | interface_text_info_last_keys.text[EN] = "<=30m:"; 56 | interface_text_info_exp_days.text[EN] = "Last Exp:"; 57 | interface_text_info_exp_num.text[EN] = "Num Exp:"; 58 | interface_text_info_exp_max.text[EN] = "Score:"; 59 | interface_text_info_exp_sum.text[EN] = "Scores:"; 60 | 61 | interface_text_info_scan_status_scanning.text[EN] = "Scanning..."; 62 | interface_text_info_scan_status_notscanning.text[EN] = "Not scanning."; 63 | interface_text_info_scan_status_waiting.text[EN] = "Waiting for scan"; 64 | interface_text_info_scan_last.text[EN] = "Last scan:"; 65 | 66 | interface_text_report_pending.text[EN] = "Uploading..."; 67 | interface_text_report_success.text[EN] = "Upload succeed!"; 68 | interface_text_report_fail.text[EN] = "Upload failed!"; 69 | 70 | interface_text_data_del[0].text[EN] = "DEL TEK"; 71 | interface_text_data_del[1].text[EN] = "DEL Exp Info"; 72 | interface_text_data_del[2].text[EN] = "DEL Tmp RPI"; 73 | interface_text_data_del[3].text[EN] = "DEL RPI"; 74 | interface_text_data_del[4].text[EN] = "DEL Lst Upd."; 75 | interface_text_data_del[5].text[EN] = "DEL All Data"; 76 | 77 | interface_texts_weekday[0].text[EN] = "Sun"; 78 | interface_texts_weekday[1].text[EN] = "Mon"; 79 | interface_texts_weekday[2].text[EN] = "Tue"; 80 | interface_texts_weekday[3].text[EN] = "Wed"; 81 | interface_texts_weekday[4].text[EN] = "Thu"; 82 | interface_texts_weekday[5].text[EN] = "Fri"; 83 | interface_texts_weekday[6].text[EN] = "Sat"; 84 | 85 | interface_texts_month[0].text[EN] = "Jan"; 86 | interface_texts_month[1].text[EN] = "Feb"; 87 | interface_texts_month[2].text[EN] = "Mar"; 88 | interface_texts_month[3].text[EN] = "Apr"; 89 | interface_texts_month[4].text[EN] = "May"; 90 | interface_texts_month[5].text[EN] = "Jun"; 91 | interface_texts_month[6].text[EN] = "Jul"; 92 | interface_texts_month[7].text[EN] = "Aug"; 93 | interface_texts_month[8].text[EN] = "Sep"; 94 | interface_texts_month[9].text[EN] = "Oct"; 95 | interface_texts_month[10].text[EN] = "Nov"; 96 | interface_texts_month[11].text[EN] = "Dec"; 97 | 98 | // DE 99 | interface_text_button_cancel.text[DE] = "ZURÜCK"; 100 | interface_text_button_back.text[DE] = "ZURÜCK"; 101 | interface_text_button_ok.text[DE] = "OK"; 102 | interface_text_button_menu.text[DE] = "MENU"; 103 | interface_text_button_report.text[DE] = "MELDEN"; 104 | 105 | interface_text_headline_tan.text[DE] = "TAN EING."; 106 | interface_text_headline_report.text[DE] = "MELDEN"; 107 | interface_text_headline_wifi.text[DE] = "WLAN"; 108 | interface_text_headline_time.text[DE] = "ZEIT/DATUM"; 109 | interface_text_headline_data.text[DE] = "DATEN ENTF"; 110 | interface_text_headline_settings.text[DE] = "EINSTEL."; 111 | interface_text_headline_info.text[DE] = "INFOS"; 112 | interface_text_headline_debug.text[DE] = "DEBUG"; 113 | 114 | interface_text_wifi_waiting.text[DE] = "Warten..."; 115 | interface_text_wifi_scanning.text[DE] = "Scannen..."; 116 | interface_text_wifi_connecting.text[DE] = "Verbinden..."; 117 | interface_text_wifi_nothing.text[DE] = "Keine..."; 118 | 119 | interface_text_settings_locale.text[DE] = "Sprache:"; 120 | interface_text_settings_locales[EN].text[DE] = "EN"; 121 | interface_text_settings_locales[DE].text[DE] = "DE"; 122 | 123 | interface_text_settings_timezone.text[DE] = "GMT:"; 124 | 125 | interface_text_info_num_keys.text[DE] = "Gesehen:"; 126 | interface_text_info_last_keys.text[DE] = "<=30m:"; 127 | interface_text_info_exp_days.text[DE] = "letz. Exp:"; 128 | interface_text_info_exp_num.text[DE] = "Anz. Exp:"; 129 | interface_text_info_exp_max.text[DE] = "Score:"; 130 | interface_text_info_exp_sum.text[DE] = "Scores:"; 131 | 132 | interface_text_info_scan_status_scanning.text[DE] = "Scannen..."; 133 | interface_text_info_scan_status_notscanning.text[DE] = "Kein Scan."; 134 | interface_text_info_scan_status_waiting.text[DE] = "Warten auf Scan."; 135 | interface_text_info_scan_last.text[DE] = "letz. Scan:"; 136 | 137 | interface_text_report_pending.text[DE] = "Hochladen..."; 138 | interface_text_report_success.text[DE] = "Erfolgreich!"; 139 | interface_text_report_fail.text[DE] = "Fehlgeschlagen!"; 140 | 141 | interface_text_data_del[0] 142 | .text[DE] = "ENTF TEK"; 143 | interface_text_data_del[1].text[DE] = "ENTF Exp Info"; 144 | interface_text_data_del[2].text[DE] = "ENTF Tmp RPI"; 145 | interface_text_data_del[3].text[DE] = "ENTF RPI"; 146 | interface_text_data_del[4].text[DE] = "ENTF letz. Up"; 147 | interface_text_data_del[5].text[DE] = "ENTF Daten"; 148 | 149 | interface_texts_weekday[0].text[DE] = "So."; 150 | interface_texts_weekday[1].text[DE] = "Mo."; 151 | interface_texts_weekday[2].text[DE] = "Di."; 152 | interface_texts_weekday[3].text[DE] = "Mi."; 153 | interface_texts_weekday[4].text[DE] = "Do."; 154 | interface_texts_weekday[5].text[DE] = "Fr."; 155 | interface_texts_weekday[6].text[DE] = "Sa."; 156 | 157 | interface_texts_month[0].text[DE] = "Jan"; 158 | interface_texts_month[1].text[DE] = "Feb"; 159 | interface_texts_month[2].text[DE] = "Mär"; 160 | interface_texts_month[3].text[DE] = "Apr"; 161 | interface_texts_month[4].text[DE] = "Mai"; 162 | interface_texts_month[5].text[DE] = "Jun"; 163 | interface_texts_month[6].text[DE] = "Jul"; 164 | interface_texts_month[7].text[DE] = "Aug"; 165 | interface_texts_month[8].text[DE] = "Sep"; 166 | interface_texts_month[9].text[DE] = "Okt"; 167 | interface_texts_month[10].text[DE] = "Nov"; 168 | interface_texts_month[11].text[DE] = "Dez"; 169 | } 170 | 171 | interface_locale_t interface_get_locale(void) 172 | { 173 | return current_locale; 174 | } 175 | 176 | void interface_set_locale(interface_locale_t locale) 177 | { 178 | current_locale = locale; 179 | } 180 | 181 | char *interface_get_label_text(interface_label_t *label) 182 | { 183 | return label->text[current_locale]; 184 | } -------------------------------------------------------------------------------- /components/interface/interface-main.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | #include 17 | #include "esp_log.h" 18 | #include "driver/gpio.h" 19 | 20 | #include "display.h" 21 | #include "display-gfx.h" 22 | 23 | #include "wifi-controller.h" 24 | #include "ena-storage.h" 25 | #include "ena-exposure.h" 26 | 27 | #include "interface.h" 28 | 29 | static time_t current_timstamp; 30 | static struct tm *current_tm; 31 | static char time_buffer[32]; 32 | static char text_buffer[32]; 33 | 34 | void interface_main_set(void) 35 | { 36 | interface_info_start(); 37 | } 38 | 39 | void interface_main_rst(void) 40 | { 41 | interface_report_start(); 42 | } 43 | 44 | void interface_main_display(void) 45 | { 46 | ena_exposure_summary_t *current_exposure_summary = ena_exposure_current_summary(); 47 | 48 | time(¤t_timstamp); 49 | uint32_t last_update = current_exposure_summary->last_update; 50 | 51 | // status unknown if no update or last update older than two days 52 | if (last_update == 0 || ((current_timstamp - last_update) / (60 * 60 * 24)) > 2) 53 | { 54 | display_set_color(YELLOW); 55 | display_data(display_gfx_question[0], 24, 0, 12, false); 56 | display_data(display_gfx_question[1], 24, 1, 12, false); 57 | display_data(display_gfx_question[2], 24, 2, 12, false); 58 | display_data(display_gfx_question[3], 24, 3, 12, false); 59 | } 60 | else if (current_exposure_summary->max_risk_score < 100) 61 | { 62 | display_set_color(GREEN); 63 | display_data(display_gfx_smile[0], 24, 0, 12, false); 64 | display_data(display_gfx_smile[1], 24, 1, 12, false); 65 | display_data(display_gfx_smile[2], 24, 2, 12, false); 66 | display_data(display_gfx_smile[3], 24, 3, 12, false); 67 | display_set_color(WHITE); 68 | } 69 | else 70 | { 71 | display_set_color(RED); 72 | display_data(display_gfx_sad[0], 24, 0, 12, false); 73 | display_data(display_gfx_sad[1], 24, 1, 12, false); 74 | display_data(display_gfx_sad[2], 24, 2, 12, false); 75 | display_data(display_gfx_sad[3], 24, 3, 12, false); 76 | display_set_color(WHITE); 77 | } 78 | 79 | // clock icon 80 | display_data(display_gfx_clock, 8, 4, 8, false); 81 | 82 | // last update 83 | struct tm *last_update_tm = gmtime((time_t *)&last_update); 84 | 85 | last_update_tm->tm_hour = last_update_tm->tm_hour + (interface_get_timezone_offset()) % 24; 86 | 87 | sprintf(time_buffer, "%s %02d %02d:%02d", 88 | interface_get_label_text(&interface_texts_month[last_update_tm->tm_mon]), 89 | last_update_tm->tm_mday, 90 | last_update_tm->tm_hour, 91 | last_update_tm->tm_min); 92 | 93 | if (last_update != 0) 94 | { 95 | display_text_line_column(time_buffer, 4, 3, false); 96 | } 97 | 98 | display_set_color(WHITE); 99 | 100 | // buttons 101 | display_set_button(interface_get_label_text(&interface_text_button_menu), true, false); 102 | display_set_button(interface_get_label_text(&interface_text_button_report), false, true); 103 | } 104 | 105 | void interface_main_display_refresh(void) 106 | { 107 | if (wifi_controller_connection() != NULL) 108 | { 109 | display_data(display_gfx_wifi, 8, 0, 0, false); 110 | } 111 | else 112 | { 113 | display_data(display_gfx_cross, 8, 0, 0, false); 114 | } 115 | 116 | time(¤t_timstamp); 117 | current_tm = gmtime(¤t_timstamp); 118 | 119 | current_tm->tm_hour = current_tm->tm_hour + (interface_get_timezone_offset()) % 24; 120 | 121 | // curent date 122 | sprintf(text_buffer, "%s %s %d", 123 | interface_get_label_text(&interface_texts_weekday[current_tm->tm_wday]), 124 | interface_get_label_text(&interface_texts_month[current_tm->tm_mon]), 125 | current_tm->tm_mday); 126 | display_text_line_column(text_buffer, 0, 16 - strlen(text_buffer), false); 127 | 128 | // current time 129 | strftime(time_buffer, 16, INTERFACE_FORMAT_TIME, current_tm); 130 | display_text_line_column(time_buffer, 1, 16 - strlen(time_buffer), false); 131 | } 132 | 133 | void interface_main_start(void) 134 | { 135 | interface_register_command_callback(INTERFACE_COMMAND_RST, &interface_main_rst); 136 | interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_main_set); 137 | interface_register_command_callback(INTERFACE_COMMAND_LFT, NULL); 138 | interface_register_command_callback(INTERFACE_COMMAND_RHT, NULL); 139 | interface_register_command_callback(INTERFACE_COMMAND_MID, NULL); 140 | interface_register_command_callback(INTERFACE_COMMAND_UP, NULL); 141 | interface_register_command_callback(INTERFACE_COMMAND_DWN, NULL); 142 | interface_register_command_callback(INTERFACE_COMMAND_RST_LONG, NULL); 143 | interface_register_command_callback(INTERFACE_COMMAND_SET_LONG, NULL); 144 | 145 | interface_set_display_function(&interface_main_display); 146 | interface_set_display_refresh_function(&interface_main_display_refresh); 147 | } 148 | -------------------------------------------------------------------------------- /components/interface/interface-settings.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | #include 17 | #include "freertos/FreeRTOS.h" 18 | #include "freertos/task.h" 19 | #include "esp_log.h" 20 | #include "driver/gpio.h" 21 | 22 | #include "display.h" 23 | #include "display-gfx.h" 24 | 25 | #include "ena-storage.h" 26 | 27 | #include "interface.h" 28 | 29 | typedef enum 30 | { 31 | INTERFACE_SETTINGS_LOCALE = 0, 32 | INTERFACE_SETTINGS_TIMEZONE, 33 | } interface_settings_state_t; 34 | 35 | static int current_interface_settings_state; 36 | static int current_timezone_offset = 0; 37 | 38 | int interface_get_timezone_offset(void) 39 | { 40 | return current_timezone_offset; 41 | } 42 | 43 | void interface_set_timezone_offset(int timezone_offset) 44 | { 45 | current_timezone_offset = timezone_offset; 46 | } 47 | 48 | void interface_settings_set(void) 49 | { 50 | interface_main_start(); 51 | } 52 | 53 | void interface_settings_lft(void) 54 | { 55 | interface_info_start(); 56 | } 57 | 58 | void interface_settings_rht(void) 59 | { 60 | interface_wifi_start(); 61 | } 62 | 63 | void interface_settings_mid(void) 64 | { 65 | 66 | current_interface_settings_state++; 67 | if (current_interface_settings_state > INTERFACE_SETTINGS_TIMEZONE) 68 | { 69 | current_interface_settings_state = INTERFACE_SETTINGS_LOCALE; 70 | } 71 | 72 | display_clear_line(2, false); 73 | display_clear_line(3, false); 74 | display_clear_line(4, false); 75 | display_clear_line(5, false); 76 | display_clear_line(6, false); 77 | display_clear_line(7, false); 78 | } 79 | 80 | void interface_settings_up(void) 81 | { 82 | if (current_interface_settings_state == INTERFACE_SETTINGS_LOCALE) 83 | { 84 | if (interface_get_locale() == 0) 85 | { 86 | interface_set_locale(INTERFACE_NUM_LOCALE - 1); 87 | } 88 | else 89 | { 90 | interface_set_locale(interface_get_locale() - 1); 91 | } 92 | } 93 | else if (current_interface_settings_state == INTERFACE_SETTINGS_TIMEZONE) 94 | { 95 | current_timezone_offset++; 96 | if (current_timezone_offset > 12) 97 | { 98 | current_timezone_offset = -11; 99 | } 100 | } 101 | } 102 | 103 | void interface_settings_dwn(void) 104 | { 105 | 106 | if (current_interface_settings_state == INTERFACE_SETTINGS_LOCALE) 107 | { 108 | if (interface_get_locale() + 1 == INTERFACE_NUM_LOCALE) 109 | { 110 | interface_set_locale(0); 111 | } 112 | else 113 | { 114 | interface_set_locale(interface_get_locale() + 1); 115 | } 116 | } 117 | else if (current_interface_settings_state == INTERFACE_SETTINGS_TIMEZONE) 118 | { 119 | current_timezone_offset--; 120 | if (current_timezone_offset < -11) 121 | { 122 | current_timezone_offset = 12; 123 | } 124 | } 125 | } 126 | 127 | void interface_settings_display(void) 128 | { 129 | display_menu_headline(interface_get_label_text(&interface_text_headline_settings), true, 0); 130 | 131 | display_text_line_column(interface_get_label_text(&interface_text_settings_locale), 3, 1, false); 132 | 133 | display_text_line_column(interface_get_label_text(&interface_text_settings_timezone), 6, 1, false); 134 | 135 | if (current_interface_settings_state == INTERFACE_SETTINGS_LOCALE) 136 | { 137 | display_data(display_gfx_arrow_up, 8, 2, 11 * 8 + 4, false); 138 | display_text_line_column( 139 | interface_get_label_text(&interface_text_settings_locales[interface_get_locale()]), 3, 11, true); 140 | display_data(display_gfx_arrow_down, 8, 4, 11 * 8 + 4, false); 141 | } 142 | else 143 | { 144 | display_text_line_column( 145 | interface_get_label_text(&interface_text_settings_locales[interface_get_locale()]), 3, 11, false); 146 | } 147 | 148 | char timezone_char[32]; 149 | timezone_char[0] = ' '; 150 | if (current_timezone_offset == 0) 151 | { 152 | timezone_char[0] = ' '; 153 | sprintf(&timezone_char[1], "%d", current_timezone_offset); 154 | timezone_char[2] = ' '; 155 | } 156 | else if (current_timezone_offset > 0) 157 | { 158 | timezone_char[0] = '+'; 159 | sprintf(&timezone_char[1], "%d", current_timezone_offset); 160 | if (current_timezone_offset < 10) 161 | { 162 | timezone_char[2] = ' '; 163 | } 164 | } 165 | else if (current_timezone_offset < 0) 166 | { 167 | sprintf(&timezone_char[0], "%d", current_timezone_offset); 168 | if (current_timezone_offset > -10) 169 | { 170 | timezone_char[2] = ' '; 171 | } 172 | } 173 | 174 | if (current_interface_settings_state == INTERFACE_SETTINGS_TIMEZONE) 175 | { 176 | display_data(display_gfx_arrow_up, 8, 5, 7 * 8, false); 177 | display_chars(timezone_char, 3, 6, 6, true); 178 | display_data(display_gfx_arrow_down, 8, 7, 7 * 8, false); 179 | } 180 | else 181 | { 182 | display_chars(timezone_char, 3, 6, 6, false); 183 | } 184 | } 185 | 186 | void interface_settings_start(void) 187 | { 188 | current_interface_settings_state = INTERFACE_SETTINGS_LOCALE; 189 | 190 | interface_register_command_callback(INTERFACE_COMMAND_RST, &interface_settings_mid); 191 | interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_settings_set); 192 | interface_register_command_callback(INTERFACE_COMMAND_LFT, &interface_settings_lft); 193 | interface_register_command_callback(INTERFACE_COMMAND_RHT, &interface_settings_rht); 194 | interface_register_command_callback(INTERFACE_COMMAND_MID, &interface_settings_mid); 195 | interface_register_command_callback(INTERFACE_COMMAND_UP, &interface_settings_up); 196 | interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_settings_dwn); 197 | interface_register_command_callback(INTERFACE_COMMAND_RST_LONG, NULL); 198 | interface_register_command_callback(INTERFACE_COMMAND_SET_LONG, NULL); 199 | 200 | interface_set_display_function(&interface_settings_display); 201 | } 202 | -------------------------------------------------------------------------------- /components/interface/interface-wifi.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | #include 17 | #include "freertos/FreeRTOS.h" 18 | #include "freertos/task.h" 19 | #include "esp_log.h" 20 | #include "driver/gpio.h" 21 | 22 | #include "display.h" 23 | #include "display-gfx.h" 24 | #include "wifi-controller.h" 25 | #include "ena-eke-proxy.h" 26 | 27 | #include "interface.h" 28 | 29 | #define APS_TO_DISPLAY 3 30 | 31 | static wifi_ap_record_t ap_info[10]; 32 | static uint16_t ap_count = 0; 33 | static int ap_index = 0; 34 | static int ap_selected = 0; 35 | static bool interface_wifi_working = false; 36 | 37 | static wifi_config_t current_wifi_config; 38 | 39 | void interface_wifi_input_rst(char *text, uint8_t cursor) 40 | { 41 | interface_wifi_start(); 42 | } 43 | 44 | void interface_wifi_input_set(char *text, uint8_t cursor) 45 | { 46 | 47 | display_clear(); 48 | display_menu_headline(interface_get_label_text(&interface_text_headline_wifi), true, 0); 49 | 50 | display_text_line_column(interface_get_label_text(&interface_text_wifi_connecting), 4, 1, false); 51 | 52 | memcpy(current_wifi_config.sta.password, text, cursor + 1); 53 | 54 | ESP_LOGD(INTERFACE_LOG, "ssid: '%s' password '%s'", current_wifi_config.sta.ssid, current_wifi_config.sta.password); 55 | 56 | if (wifi_controller_connect(current_wifi_config, NULL) == ESP_OK) 57 | { 58 | interface_main_start(); 59 | } 60 | else 61 | { 62 | // what happens here? 63 | interface_wifi_start(); 64 | } 65 | } 66 | 67 | void interface_wifi_set(void) 68 | { 69 | interface_main_start(); 70 | } 71 | 72 | void interface_wifi_lft(void) 73 | { 74 | interface_settings_start(); 75 | } 76 | 77 | void interface_wifi_rht(void) 78 | { 79 | interface_datetime_start(); 80 | } 81 | 82 | void interface_wifi_mid(void) 83 | { 84 | memset(¤t_wifi_config, 0, sizeof(wifi_config_t)); 85 | memcpy(current_wifi_config.sta.ssid, ap_info[ap_selected].ssid, strlen((char *)ap_info[ap_selected].ssid)); 86 | interface_input(&interface_wifi_input_rst, &interface_wifi_input_set, 64); 87 | } 88 | 89 | void interface_wifi_up(void) 90 | { 91 | ap_selected--; 92 | if (ap_selected < 0) 93 | { 94 | ap_selected = ap_count - 1; 95 | if (ap_count - APS_TO_DISPLAY < 0) 96 | { 97 | ap_index = 0; 98 | } 99 | else 100 | { 101 | ap_index = ap_count - APS_TO_DISPLAY; 102 | } 103 | } 104 | else if (ap_selected < ap_index) 105 | { 106 | ap_index--; 107 | } 108 | } 109 | 110 | void interface_wifi_dwn(void) 111 | { 112 | ap_selected++; 113 | if (ap_selected >= ap_count) 114 | { 115 | ap_selected = 0; 116 | ap_index = 0; 117 | } 118 | else if (ap_selected >= (ap_index + APS_TO_DISPLAY)) 119 | { 120 | ap_index++; 121 | } 122 | } 123 | 124 | void interface_wifi_display(void) 125 | { 126 | 127 | display_menu_headline(interface_get_label_text(&interface_text_headline_wifi), true, 0); 128 | if (ap_count > 0) 129 | { 130 | display_clear_line(2, false); 131 | display_clear_line(4, false); 132 | display_clear_line(6, false); 133 | for (int i = 0; i < 3; i++) 134 | { 135 | int index = i + ap_index; 136 | if (index < ap_count) 137 | { 138 | if (index == ap_selected) 139 | { 140 | display_data(display_gfx_arrow_right, 8, i * 2 + 2, 8, false); 141 | } 142 | else 143 | { 144 | display_data(display_gfx_clear, 8, i * 2 + 2, 8, false); 145 | } 146 | 147 | if (sizeof(ap_info[i].ssid) > 0) 148 | { 149 | display_text_line_column((char *)ap_info[index].ssid, i * 2 + 2, 2, false); 150 | } 151 | else 152 | { 153 | display_text_line_column(" / ", i * 2 + 2, 2, false); 154 | } 155 | 156 | if (ap_info[index].rssi >= -67) 157 | { 158 | display_data(display_gfx_wifi, 8, i * 2 + 2, 112, false); 159 | } 160 | else if (ap_info[index].rssi >= -80) 161 | { 162 | display_data(display_gfx_wifi_low, 8, i * 2 + 2, 112, false); 163 | } 164 | else if (ap_info[index].rssi >= -90) 165 | { 166 | display_data(display_gfx_wifi_lowest, 8, i * 2 + 2, 112, false); 167 | } 168 | } 169 | } 170 | } 171 | else 172 | { 173 | display_text_line_column(interface_get_label_text(&interface_text_wifi_scanning), 4, 1, false); 174 | } 175 | } 176 | 177 | void interface_wifi_scan(void) 178 | { 179 | if (!interface_wifi_working) 180 | { 181 | interface_wifi_working = true; 182 | display_clear(); 183 | display_menu_headline(interface_get_label_text(&interface_text_headline_wifi), true, 0); 184 | 185 | display_text_line_column(interface_get_label_text(&interface_text_wifi_waiting), 4, 1, false); 186 | ena_eke_proxy_pause(); 187 | 188 | memset(ap_info, 0, sizeof(ap_info)); 189 | ap_count = 0; 190 | ap_index = 0; 191 | ap_selected = 0; 192 | display_text_line_column(interface_get_label_text(&interface_text_wifi_scanning), 4, 1, false); 193 | wifi_controller_scan(ap_info, &ap_count, interface_wifi_display); 194 | 195 | ena_eke_proxy_resume(); 196 | interface_wifi_working = false; 197 | } 198 | } 199 | 200 | void interface_wifi_reconnect(void) 201 | { 202 | if (!interface_wifi_working) 203 | { 204 | display_clear(); 205 | display_menu_headline(interface_get_label_text(&interface_text_headline_wifi), true, 0); 206 | display_text_line_column(interface_get_label_text(&interface_text_wifi_connecting), 4, 1, false); 207 | interface_wifi_working = true; 208 | wifi_controller_reconnect(&interface_wifi_set); 209 | interface_wifi_working = false; 210 | } 211 | } 212 | 213 | void interface_wifi_start(void) 214 | { 215 | interface_register_command_callback(INTERFACE_COMMAND_SET, &interface_wifi_set); 216 | interface_register_command_callback(INTERFACE_COMMAND_LFT, &interface_wifi_lft); 217 | interface_register_command_callback(INTERFACE_COMMAND_RHT, &interface_wifi_rht); 218 | interface_register_command_callback(INTERFACE_COMMAND_MID, &interface_wifi_mid); 219 | interface_register_command_callback(INTERFACE_COMMAND_UP, &interface_wifi_up); 220 | interface_register_command_callback(INTERFACE_COMMAND_DWN, &interface_wifi_dwn); 221 | interface_register_command_callback(INTERFACE_COMMAND_RST, &interface_wifi_mid); 222 | interface_register_command_callback(INTERFACE_COMMAND_SET_LONG, &interface_wifi_scan); 223 | interface_register_command_callback(INTERFACE_COMMAND_RST_LONG, &interface_wifi_reconnect); 224 | 225 | interface_set_display_function(&interface_wifi_display); 226 | interface_set_display_refresh_function(NULL); 227 | 228 | interface_wifi_scan(); 229 | } 230 | -------------------------------------------------------------------------------- /components/interface/interface.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include "freertos/FreeRTOS.h" 16 | #include "freertos/task.h" 17 | #include 18 | #include "driver/gpio.h" 19 | #include "esp_log.h" 20 | 21 | #include "display.h" 22 | #include "display-gfx.h" 23 | 24 | #include "interface.h" 25 | 26 | static interface_command_callback command_callbacks[INTERFACE_COMMANDS_SIZE]; 27 | static bool command_callback_trigger[INTERFACE_COMMANDS_SIZE]; 28 | static interface_display_function current_display_function; 29 | static interface_display_function current_display_refresh_function; 30 | 31 | static TimerHandle_t interface_idle_timer; 32 | static bool interface_idle = false; 33 | static bool busy = false; 34 | 35 | bool interface_is_idle(void) 36 | { 37 | return interface_idle; 38 | } 39 | 40 | void interface_register_command_callback(interface_command_t command, interface_command_callback callback) 41 | { 42 | command_callbacks[command] = callback; 43 | command_callback_trigger[command] = false; 44 | } 45 | 46 | void interface_command_callback_set_trigger(interface_command_t command) 47 | { 48 | command_callback_trigger[command] = true; 49 | } 50 | 51 | void interface_set_display_function(interface_display_function display_function) 52 | { 53 | busy = true; 54 | display_clear(); 55 | current_display_refresh_function = NULL; 56 | current_display_function = display_function; 57 | if (current_display_function != NULL) 58 | { 59 | (*current_display_function)(); 60 | } 61 | busy = false; 62 | } 63 | 64 | void interface_set_display_refresh_function(interface_display_function display_function) 65 | { 66 | busy = true; 67 | current_display_refresh_function = display_function; 68 | if (current_display_function != NULL) 69 | { 70 | (*current_display_function)(); 71 | } 72 | busy = false; 73 | } 74 | 75 | void interface_execute_command(interface_command_t command) 76 | { 77 | if (!interface_idle && command_callbacks[command] != NULL) 78 | { 79 | xTimerReset(interface_idle_timer, 0); 80 | (*command_callbacks[command])(); 81 | if (!busy) 82 | { 83 | busy = true; 84 | if (current_display_function != NULL) 85 | { 86 | (*current_display_function)(); 87 | } 88 | busy = false; 89 | } 90 | } 91 | else if (interface_idle && command == INTERFACE_COMMAND_SET) 92 | { 93 | xTimerReset(interface_idle_timer, 0); 94 | interface_idle = false; 95 | display_on(true); 96 | } 97 | } 98 | 99 | void interface_execute_command_trigger(interface_command_t command) 100 | { 101 | if (command_callback_trigger[command]) 102 | { 103 | interface_execute_command(command); 104 | } 105 | } 106 | 107 | void interface_display_task(void *pvParameter) 108 | { 109 | xTimerStart(interface_idle_timer, 0); 110 | 111 | while (1) 112 | { 113 | if (!interface_idle && !busy && current_display_refresh_function != NULL) 114 | { 115 | (*current_display_refresh_function)(); 116 | vTaskDelay(500 / portTICK_PERIOD_MS); 117 | } 118 | else 119 | { 120 | vTaskDelay(100 / portTICK_PERIOD_MS); 121 | } 122 | } 123 | } 124 | 125 | void interface_idle_callback(TimerHandle_t timer) 126 | { 127 | display_on(false); 128 | interface_idle = true; 129 | } 130 | 131 | void interface_start(void) 132 | { 133 | 134 | interface_idle_timer = xTimerCreate( 135 | "interface_idle", 136 | (INTERFACE_IDLE_SECONDS * 1000) / portTICK_PERIOD_MS, 137 | false, 138 | NULL, 139 | interface_idle_callback); 140 | 141 | // init label 142 | interface_init_label(); 143 | 144 | display_start(); 145 | display_clear(); 146 | 147 | xTaskCreate(&interface_display_task, "interface_display_task", 4096, NULL, 5, NULL); 148 | 149 | interface_input_start(); 150 | } 151 | 152 | void interface_flipped(bool flipped) 153 | { 154 | busy = true; 155 | vTaskDelay(500 / portTICK_PERIOD_MS); 156 | display_clear(); 157 | display_flipped(flipped); 158 | if (current_display_function != NULL) 159 | { 160 | (*current_display_function)(); 161 | } 162 | busy = false; 163 | } -------------------------------------------------------------------------------- /components/interface/interface.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief interface functionality via input buttons for control and setup 18 | * 19 | */ 20 | #ifndef _interface_H_ 21 | #define _interface_H_ 22 | 23 | #include "driver/gpio.h" 24 | 25 | #define INTERFACE_LOG "INTERFACE" // TAG for Logging 26 | 27 | #define INTERFACE_FORMAT_TIME "%X" 28 | 29 | #define INTERFACE_NUM_LOCALE 2 30 | 31 | #define INTERFACE_IDLE_SECONDS CONFIG_ENA_INTERFACE_IDLE_TIME 32 | 33 | #define INTERFACE_INPUT_TICKS_MS 20 34 | #define INTERFACE_LONG_STATE_SECONDS 0.6 35 | 36 | /** 37 | * @brief available commands 38 | */ 39 | typedef enum 40 | { 41 | INTERFACE_COMMAND_RST = 0, 42 | INTERFACE_COMMAND_SET, 43 | INTERFACE_COMMAND_MID, 44 | INTERFACE_COMMAND_RHT, 45 | INTERFACE_COMMAND_LFT, 46 | INTERFACE_COMMAND_DWN, 47 | INTERFACE_COMMAND_UP, 48 | INTERFACE_COMMAND_RST_LONG, 49 | INTERFACE_COMMAND_SET_LONG, 50 | INTERFACE_COMMANDS_SIZE, 51 | } interface_command_t; 52 | 53 | /** 54 | * @brief available locales 55 | */ 56 | typedef enum 57 | { 58 | EN = 0, 59 | DE, 60 | } interface_locale_t; 61 | 62 | typedef struct 63 | { 64 | 65 | char *text[INTERFACE_NUM_LOCALE]; 66 | 67 | } interface_label_t; 68 | 69 | // label variables 70 | interface_label_t interface_text_button_cancel; 71 | interface_label_t interface_text_button_ok; 72 | interface_label_t interface_text_button_back; 73 | interface_label_t interface_text_button_menu; 74 | interface_label_t interface_text_button_report; 75 | 76 | interface_label_t interface_text_headline_tan; 77 | interface_label_t interface_text_headline_report; 78 | interface_label_t interface_text_headline_wifi; 79 | interface_label_t interface_text_headline_time; 80 | interface_label_t interface_text_headline_data; 81 | interface_label_t interface_text_headline_settings; 82 | interface_label_t interface_text_headline_info; 83 | interface_label_t interface_text_headline_debug; 84 | 85 | interface_label_t interface_text_settings_locale; 86 | interface_label_t interface_text_settings_locales[INTERFACE_NUM_LOCALE]; 87 | interface_label_t interface_text_settings_timezone; 88 | 89 | interface_label_t interface_text_info_num_keys; 90 | interface_label_t interface_text_info_last_keys; 91 | interface_label_t interface_text_info_exp_update; 92 | interface_label_t interface_text_info_exp_days; 93 | interface_label_t interface_text_info_exp_num; 94 | interface_label_t interface_text_info_exp_max; 95 | interface_label_t interface_text_info_exp_sum; 96 | interface_label_t interface_text_info_scan_status_scanning; 97 | interface_label_t interface_text_info_scan_status_notscanning; 98 | interface_label_t interface_text_info_scan_status_waiting; 99 | interface_label_t interface_text_info_scan_last; 100 | 101 | interface_label_t interface_text_report_pending; 102 | interface_label_t interface_text_report_success; 103 | interface_label_t interface_text_report_fail; 104 | 105 | interface_label_t interface_text_wifi_waiting; 106 | interface_label_t interface_text_wifi_scanning; 107 | interface_label_t interface_text_wifi_connecting; 108 | interface_label_t interface_text_wifi_nothing; 109 | 110 | interface_label_t interface_text_data_del[6]; 111 | 112 | interface_label_t interface_texts_weekday[7]; 113 | 114 | interface_label_t interface_texts_month[12]; 115 | 116 | /** 117 | * @brief callback function for command input (button press) 118 | */ 119 | typedef void (*interface_command_callback)(void); 120 | 121 | /** 122 | * @brief current display function 123 | */ 124 | typedef void (*interface_display_function)(void); 125 | 126 | /** 127 | * @brief callback function for text_input 128 | * 129 | * @param[in] text the text from input 130 | * @param[in] cursor current cursor position 131 | */ 132 | typedef void (*interface_text_callback)(char *text, uint8_t cursor); 133 | 134 | 135 | /** 136 | * @brief start input routine 137 | * 138 | * MUST BE DEFINED DEVICE SPECIFIC 139 | * 140 | */ 141 | void interface_input_start(void); 142 | 143 | 144 | /** 145 | * @brief is interface in idle mode 146 | * 147 | * @return if interface is idle 148 | */ 149 | bool interface_is_idle(void); 150 | 151 | /** 152 | * @brief init label 153 | */ 154 | void interface_init_label(void); 155 | 156 | /** 157 | * @brief get text from label for set locale 158 | * 159 | * @param[in] label the label to get the text from 160 | */ 161 | char *interface_get_label_text(interface_label_t *label); 162 | 163 | /** 164 | * @brief get locale for interface 165 | * 166 | * @return 167 | * interface_locale_t current locale 168 | */ 169 | interface_locale_t interface_get_locale(void); 170 | 171 | /** 172 | * @brief set locale for interface 173 | * 174 | * @param[in] locale the locale to set 175 | */ 176 | void interface_set_locale(interface_locale_t locale); 177 | 178 | /** 179 | * @brief get timezone offset for interface 180 | * 181 | * @return 182 | * int current timezone offset 183 | */ 184 | int interface_get_timezone_offset(void); 185 | 186 | /** 187 | * @brief set timezone offset for interface 188 | * 189 | * @param[in] timezone_offset the timezone offset to set 190 | */ 191 | void interface_set_timezone_offset(int timezone_offset); 192 | 193 | /** 194 | * @brief register a callback function for command event 195 | * 196 | * @param[in] command id of the command to listen to 197 | * @param[in] callback callback function 198 | */ 199 | void interface_register_command_callback(interface_command_t command, interface_command_callback callback); 200 | 201 | /** 202 | * @brief set if command is trigger 203 | * 204 | * @param[in] command id of the command to set as trigger 205 | */ 206 | void interface_command_callback_set_trigger(interface_command_t command); 207 | 208 | /** 209 | * @brief execute a command 210 | * 211 | * @param[in] command id of the command to trigger 212 | */ 213 | void interface_execute_command(interface_command_t command); 214 | 215 | /** 216 | * @brief execute a command as trigger 217 | * 218 | * @param[in] command id of the command to trigger 219 | */ 220 | void interface_execute_command_trigger(interface_command_t command); 221 | 222 | /** 223 | * @brief set the display function 224 | * 225 | * @param[in] display_function display function 226 | */ 227 | void interface_set_display_function(interface_display_function display_function); 228 | 229 | /** 230 | * @brief set the display refresh function 231 | * 232 | * @param[in] display_function display function 233 | */ 234 | void interface_set_display_refresh_function(interface_display_function display_function); 235 | 236 | /** 237 | * @brief start interface logic 238 | * 239 | * This will initialize the controls and start a task to listen to button inputs and calling the callbacks. 240 | */ 241 | void interface_start(void); 242 | 243 | /** 244 | * @brief set interface flipped or not 245 | * 246 | * @param[in] on true interface is flipped 247 | */ 248 | void interface_flipped(bool flipped); 249 | 250 | /** 251 | * @brief start delete data interface 252 | */ 253 | void interface_data_start(void); 254 | 255 | /** 256 | * @brief start datetime interface 257 | */ 258 | void interface_datetime_start(void); 259 | 260 | /** 261 | * @brief start main interface 262 | */ 263 | void interface_main_start(void); 264 | 265 | /** 266 | * @brief start report interface 267 | */ 268 | void interface_report_start(void); 269 | 270 | /** 271 | * @brief start wifi interface 272 | */ 273 | void interface_wifi_start(void); 274 | 275 | /** 276 | * @brief start settings interface 277 | */ 278 | void interface_settings_start(void); 279 | 280 | /** 281 | * @brief start info interface 282 | */ 283 | void interface_info_start(void); 284 | 285 | /** 286 | * @brief start interface for input 287 | * 288 | * @param[in] callback_rst function to call on RST 289 | * @param[in] callback_set function to call on SET 290 | * @param[in] limit max character allowed (0=255) 291 | */ 292 | void interface_input(interface_text_callback callback_rst, interface_text_callback callback_set, uint8_t limit); 293 | 294 | /** 295 | * @brief set text for input interface 296 | * 297 | * @param[in] text the text to set 298 | */ 299 | void interface_input_set_text(char *text); 300 | 301 | #endif -------------------------------------------------------------------------------- /components/interface/m5-input/m5-input.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include "freertos/FreeRTOS.h" 16 | #include "freertos/task.h" 17 | #include "driver/gpio.h" 18 | #include "esp_log.h" 19 | 20 | #include "mpu6886.h" 21 | 22 | #include "interface.h" 23 | 24 | #include "m5-input.h" 25 | 26 | static float input_states[INTERFACE_COMMANDS_SIZE]; 27 | static float input_trigger_state[INTERFACE_COMMANDS_SIZE]; 28 | static int input_command_mapping[INTERFACE_COMMANDS_SIZE]; 29 | static bool flipped = false; 30 | 31 | void button_input_check(interface_command_t command) 32 | { 33 | int button_level = gpio_get_level(input_command_mapping[command]); 34 | 35 | if (button_level == 0) 36 | { 37 | input_states[command] = input_states[command] + ((float)INTERFACE_INPUT_TICKS_MS / 1000); 38 | if (command == INTERFACE_COMMAND_SET && input_states[command] > INTERFACE_LONG_STATE_SECONDS) 39 | { 40 | input_states[command] = 0; 41 | input_states[INTERFACE_COMMAND_SET_LONG] = input_states[INTERFACE_COMMAND_SET_LONG] + 1; 42 | interface_execute_command(INTERFACE_COMMAND_SET_LONG); 43 | } 44 | else if (command == INTERFACE_COMMAND_RST && input_states[command] > INTERFACE_LONG_STATE_SECONDS) 45 | { 46 | input_states[command] = 0; 47 | input_states[INTERFACE_COMMAND_RST_LONG] = input_states[INTERFACE_COMMAND_RST_LONG] + 1; 48 | interface_execute_command(INTERFACE_COMMAND_RST_LONG); 49 | } 50 | } 51 | else if (button_level == 1 && input_states[command] > 0) 52 | { 53 | input_states[command] = 0; 54 | if (command == INTERFACE_COMMAND_SET && input_states[INTERFACE_COMMAND_SET_LONG] > 0) 55 | { 56 | input_states[INTERFACE_COMMAND_SET_LONG] = 0; 57 | } 58 | else if (command == INTERFACE_COMMAND_RST && input_states[INTERFACE_COMMAND_RST_LONG] > 0) 59 | { 60 | input_states[INTERFACE_COMMAND_RST_LONG] = 0; 61 | } 62 | else 63 | { 64 | if (!interface_is_idle() && flipped) 65 | { 66 | if (command == INTERFACE_COMMAND_SET) 67 | { 68 | command = INTERFACE_COMMAND_RST; 69 | } 70 | else if (command == INTERFACE_COMMAND_RST) 71 | { 72 | command = INTERFACE_COMMAND_SET; 73 | } 74 | } 75 | interface_execute_command(command); 76 | } 77 | } 78 | } 79 | 80 | void accel_input(float axis, interface_command_t command, float tresh) 81 | { 82 | if (axis > tresh) 83 | { 84 | input_states[command] = input_states[command] + ((float)INTERFACE_INPUT_TICKS_MS / 1000); 85 | 86 | if (input_states[command] > input_trigger_state[command]) 87 | { 88 | input_trigger_state[command] = input_trigger_state[command] - (input_trigger_state[command] / 8); 89 | if (input_trigger_state[command] <= ((float)INTERFACE_INPUT_TICKS_MS / 200)) 90 | { 91 | input_trigger_state[command] = ((float)INTERFACE_INPUT_TICKS_MS / 200); 92 | } 93 | input_states[command] = 0; 94 | interface_execute_command_trigger(command); 95 | } 96 | } 97 | else if (input_states[command] > 0) 98 | { 99 | input_states[command] = 0; 100 | input_trigger_state[command] = INTERFACE_LONG_STATE_SECONDS; 101 | interface_execute_command(command); 102 | } 103 | } 104 | 105 | void m5_input_task(void *pvParameter) 106 | { 107 | float ax = 0; 108 | float ay = 0; 109 | float az = 0; 110 | 111 | while (1) 112 | { 113 | button_input_check(INTERFACE_COMMAND_SET); 114 | 115 | if (!interface_is_idle()) 116 | { 117 | button_input_check(INTERFACE_COMMAND_RST); 118 | mpu6886_get_accel_data(&ax, &ay, &az); 119 | 120 | accel_input(flipped ? ax : -ax, INTERFACE_COMMAND_UP, 0.3); 121 | accel_input(flipped ? ay : -ay, INTERFACE_COMMAND_LFT, 0.5); 122 | accel_input(flipped ? -ax : ax, INTERFACE_COMMAND_DWN, 0.5); 123 | accel_input(flipped ? -ay : ay, INTERFACE_COMMAND_RHT, 0.3); 124 | 125 | if (ax >= 0.95) 126 | { 127 | flipped = false; 128 | interface_flipped(flipped); 129 | } 130 | else if (ax <= -0.95) 131 | { 132 | flipped = true; 133 | interface_flipped(flipped); 134 | } 135 | } 136 | 137 | vTaskDelay(INTERFACE_INPUT_TICKS_MS / portTICK_PERIOD_MS); 138 | } 139 | } 140 | 141 | void interface_input_start(void) 142 | { 143 | gpio_config_t io_conf; 144 | 145 | io_conf.pin_bit_mask = (1ULL << BUTTON_RST) | (1ULL << BUTTON_SET); 146 | io_conf.intr_type = GPIO_INTR_DISABLE; 147 | io_conf.mode = GPIO_MODE_INPUT; 148 | io_conf.pull_up_en = GPIO_PULLUP_ENABLE; 149 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; 150 | gpio_config(&io_conf); 151 | 152 | input_command_mapping[INTERFACE_COMMAND_RST] = BUTTON_RST; 153 | input_command_mapping[INTERFACE_COMMAND_SET] = BUTTON_SET; 154 | 155 | mpu6886_start(); 156 | 157 | for (int i = 0; i < INTERFACE_COMMANDS_SIZE; i++) 158 | { 159 | input_trigger_state[i] = INTERFACE_LONG_STATE_SECONDS; 160 | } 161 | 162 | xTaskCreate(&m5_input_task, "m5_input_task", 4096, NULL, 5, NULL); 163 | } -------------------------------------------------------------------------------- /components/interface/m5-input/m5-input.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief execute interface commands via simple push buttons 18 | * 19 | */ 20 | #ifndef _m5_input_H_ 21 | #define _m5_input_H_ 22 | 23 | #define BUTTON_RST GPIO_NUM_37 24 | #define BUTTON_SET GPIO_NUM_39 25 | 26 | #endif -------------------------------------------------------------------------------- /components/interface/m5-mpu6886/mpu6886.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | 17 | #include "driver/i2c.h" 18 | #include "esp_log.h" 19 | 20 | #include "i2c-main.h" 21 | 22 | #include "mpu6886.h" 23 | 24 | int Gyscale = GFS_2000DPS; 25 | int Acscale = AFS_8G; 26 | float aRes, gRes; 27 | 28 | void mpu6886_i2c_read_bytes(uint8_t driver_addr, uint8_t start_addr, uint8_t number_Bytes, uint8_t *read_buffer) 29 | { 30 | i2c_cmd_handle_t cmd; 31 | cmd = i2c_cmd_link_create(); 32 | i2c_master_start(cmd); 33 | i2c_master_write_byte(cmd, (driver_addr << 1) | I2C_MASTER_WRITE, true); 34 | i2c_master_write_byte(cmd, start_addr, true); 35 | i2c_master_start(cmd); 36 | i2c_master_write_byte(cmd, (driver_addr << 1) | I2C_MASTER_READ, true); 37 | i2c_master_read(cmd, read_buffer, number_Bytes, I2C_MASTER_LAST_NACK); 38 | i2c_master_stop(cmd); 39 | ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS)); 40 | i2c_cmd_link_delete(cmd); 41 | } 42 | 43 | void mpu6886_i2c_write_bytes(uint8_t driver_addr, uint8_t start_addr, uint8_t number_Bytes, uint8_t *write_buffer) 44 | { 45 | i2c_cmd_handle_t cmd; 46 | cmd = i2c_cmd_link_create(); 47 | i2c_master_start(cmd); 48 | i2c_master_write_byte(cmd, (driver_addr << 1) | I2C_MASTER_WRITE, true); 49 | 50 | i2c_master_write_byte(cmd, start_addr, true); 51 | i2c_master_write(cmd, write_buffer, number_Bytes, true); 52 | 53 | i2c_master_stop(cmd); 54 | ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS)); 55 | i2c_cmd_link_delete(cmd); 56 | } 57 | 58 | 59 | 60 | void mpu6886_getGres() 61 | { 62 | 63 | switch (Gyscale) 64 | { 65 | // Possible gyro scales (and their register bit settings) are: 66 | case GFS_250DPS: 67 | gRes = 250.0 / 32768.0; 68 | break; 69 | case GFS_500DPS: 70 | gRes = 500.0 / 32768.0; 71 | break; 72 | case GFS_1000DPS: 73 | gRes = 1000.0 / 32768.0; 74 | break; 75 | case GFS_2000DPS: 76 | gRes = 2000.0 / 32768.0; 77 | break; 78 | } 79 | } 80 | 81 | void mpu6886_getAres() 82 | { 83 | switch (Acscale) 84 | { 85 | // Possible accelerometer scales (and their register bit settings) are: 86 | // 2 Gs (00), 4 Gs (01), 8 Gs (10), and 16 Gs (11). 87 | // Here's a bit of an algorith to calculate DPS/(ADC tick) based on that 2-bit value: 88 | case AFS_2G: 89 | aRes = 2.0 / 32768.0; 90 | break; 91 | case AFS_4G: 92 | aRes = 4.0 / 32768.0; 93 | break; 94 | case AFS_8G: 95 | aRes = 8.0 / 32768.0; 96 | break; 97 | case AFS_16G: 98 | aRes = 16.0 / 32768.0; 99 | break; 100 | } 101 | } 102 | 103 | int mpu6886_start(void) 104 | { 105 | unsigned char tempdata[1]; 106 | unsigned char regdata; 107 | 108 | if (!i2c_is_initialized()) 109 | { 110 | i2c_main_init(); 111 | } 112 | 113 | mpu6886_i2c_read_bytes(MPU6886_ADDRESS, MPU6886_WHOAMI, 1, tempdata); 114 | if (tempdata[0] != 0x19) 115 | return -1; 116 | vTaskDelay(1 / portTICK_PERIOD_MS);; 117 | 118 | regdata = 0x00; 119 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_PWR_MGMT_1, 1, ®data); 120 | vTaskDelay(10 / portTICK_PERIOD_MS);; 121 | 122 | regdata = (0x01 << 7); 123 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_PWR_MGMT_1, 1, ®data); 124 | vTaskDelay(10 / portTICK_PERIOD_MS);; 125 | 126 | regdata = (0x01 << 0); 127 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_PWR_MGMT_1, 1, ®data); 128 | vTaskDelay(10 / portTICK_PERIOD_MS);; 129 | 130 | regdata = 0x10; 131 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_ACCEL_CONFIG, 1, ®data); 132 | vTaskDelay(1 / portTICK_PERIOD_MS);; 133 | 134 | regdata = 0x18; 135 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_GYRO_CONFIG, 1, ®data); 136 | vTaskDelay(1 / portTICK_PERIOD_MS);; 137 | 138 | regdata = 0x01; 139 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_CONFIG, 1, ®data); 140 | vTaskDelay(1 / portTICK_PERIOD_MS);; 141 | 142 | regdata = 0x05; 143 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_SMPLRT_DIV, 1, ®data); 144 | vTaskDelay(1 / portTICK_PERIOD_MS);; 145 | 146 | regdata = 0x00; 147 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_INT_ENABLE, 1, ®data); 148 | vTaskDelay(1 / portTICK_PERIOD_MS);; 149 | 150 | regdata = 0x00; 151 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_ACCEL_CONFIG2, 1, ®data); 152 | vTaskDelay(1 / portTICK_PERIOD_MS);; 153 | 154 | regdata = 0x00; 155 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_USER_CTRL, 1, ®data); 156 | vTaskDelay(1 / portTICK_PERIOD_MS);; 157 | 158 | regdata = 0x00; 159 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_FIFO_EN, 1, ®data); 160 | vTaskDelay(1 / portTICK_PERIOD_MS);; 161 | 162 | regdata = 0x22; 163 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_INT_PIN_CFG, 1, ®data); 164 | vTaskDelay(1 / portTICK_PERIOD_MS);; 165 | 166 | regdata = 0x01; 167 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_INT_ENABLE, 1, ®data); 168 | 169 | vTaskDelay(100 / portTICK_PERIOD_MS);; 170 | mpu6886_getGres(); 171 | mpu6886_getAres(); 172 | return 0; 173 | } 174 | 175 | void mpu6886_get_accel_adc(int16_t *ax, int16_t *ay, int16_t *az) 176 | { 177 | 178 | uint8_t buf[6]; 179 | mpu6886_i2c_read_bytes(MPU6886_ADDRESS, MPU6886_ACCEL_XOUT_H, 6, buf); 180 | 181 | *ax = ((int16_t)buf[0] << 8) | buf[1]; 182 | *ay = ((int16_t)buf[2] << 8) | buf[3]; 183 | *az = ((int16_t)buf[4] << 8) | buf[5]; 184 | } 185 | void mpu6886_get_gyro_adc(int16_t *gx, int16_t *gy, int16_t *gz) 186 | { 187 | 188 | uint8_t buf[6]; 189 | mpu6886_i2c_read_bytes(MPU6886_ADDRESS, MPU6886_GYRO_XOUT_H, 6, buf); 190 | 191 | *gx = ((uint16_t)buf[0] << 8) | buf[1]; 192 | *gy = ((uint16_t)buf[2] << 8) | buf[3]; 193 | *gz = ((uint16_t)buf[4] << 8) | buf[5]; 194 | } 195 | 196 | void mpu6886_get_temp_adc(int16_t *t) 197 | { 198 | 199 | uint8_t buf[2]; 200 | mpu6886_i2c_read_bytes(MPU6886_ADDRESS, MPU6886_TEMP_OUT_H, 2, buf); 201 | 202 | *t = ((uint16_t)buf[0] << 8) | buf[1]; 203 | } 204 | 205 | void mpu6886_set_gyro_fsr(int scale) 206 | { 207 | //return IIC_Write_Byte(MPU_GYRO_CFG_REG,scale<<3);//设置陀螺仪满量程范围 208 | unsigned char regdata; 209 | regdata = (scale << 3); 210 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_GYRO_CONFIG, 1, ®data); 211 | vTaskDelay(10 / portTICK_PERIOD_MS);; 212 | 213 | Gyscale = scale; 214 | mpu6886_getGres(); 215 | } 216 | 217 | void mpu6886_set_accel_fsr(int scale) 218 | { 219 | unsigned char regdata; 220 | regdata = (scale << 3); 221 | mpu6886_i2c_write_bytes(MPU6886_ADDRESS, MPU6886_ACCEL_CONFIG, 1, ®data); 222 | vTaskDelay(10 / portTICK_PERIOD_MS);; 223 | 224 | Acscale = scale; 225 | mpu6886_getAres(); 226 | } 227 | 228 | void mpu6886_get_accel_data(float *ax, float *ay, float *az) 229 | { 230 | 231 | int16_t accX = 0; 232 | int16_t accY = 0; 233 | int16_t accZ = 0; 234 | mpu6886_get_accel_adc(&accX, &accY, &accZ); 235 | 236 | *ax = (float)accX * aRes; 237 | *ay = (float)accY * aRes; 238 | *az = (float)accZ * aRes; 239 | } 240 | 241 | void mpu6886_get_gyro_data(float *gx, float *gy, float *gz) 242 | { 243 | int16_t gyroX = 0; 244 | int16_t gyroY = 0; 245 | int16_t gyroZ = 0; 246 | mpu6886_get_gyro_adc(&gyroX, &gyroY, &gyroZ); 247 | 248 | *gx = (float)gyroX * gRes; 249 | *gy = (float)gyroY * gRes; 250 | *gz = (float)gyroZ * gRes; 251 | } 252 | 253 | void mpu6886_get_temp_data(float *t) 254 | { 255 | 256 | int16_t temp = 0; 257 | mpu6886_get_temp_adc(&temp); 258 | 259 | *t = (float)temp / 326.8 + 25.0; 260 | } 261 | -------------------------------------------------------------------------------- /components/interface/m5-mpu6886/mpu6886.h: -------------------------------------------------------------------------------- 1 | /* 2 | Note: The MPU6886 is an I2C sensor and uses the Arduino Wire library. 3 | Because the sensor is not 5V tolerant, we are using a 3.3 V 8 MHz Pro Mini or 4 | a 3.3 V Teensy 3.1. We have disabled the internal pull-ups used by the Wire 5 | library in the Wire.h/twi.c utility file. We are also using the 400 kHz fast 6 | I2C mode by setting the TWI_FREQ to 400000L /twi.h utility file. 7 | */ 8 | #ifndef _IMU_MPU6886_H_ 9 | #define _IMU_MPU6886_H_ 10 | 11 | #include "stdio.h" 12 | 13 | #define MPU6886_ADDRESS 0x68 14 | #define MPU6886_WHOAMI 0x75 15 | #define MPU6886_ACCEL_INTEL_CTRL 0x69 16 | #define MPU6886_SMPLRT_DIV 0x19 17 | #define MPU6886_INT_PIN_CFG 0x37 18 | #define MPU6886_INT_ENABLE 0x38 19 | #define MPU6886_ACCEL_XOUT_H 0x3B 20 | #define MPU6886_ACCEL_XOUT_L 0x3C 21 | #define MPU6886_ACCEL_YOUT_H 0x3D 22 | #define MPU6886_ACCEL_YOUT_L 0x3E 23 | #define MPU6886_ACCEL_ZOUT_H 0x3F 24 | #define MPU6886_ACCEL_ZOUT_L 0x40 25 | 26 | #define MPU6886_TEMP_OUT_H 0x41 27 | #define MPU6886_TEMP_OUT_L 0x42 28 | 29 | #define MPU6886_GYRO_XOUT_H 0x43 30 | #define MPU6886_GYRO_XOUT_L 0x44 31 | #define MPU6886_GYRO_YOUT_H 0x45 32 | #define MPU6886_GYRO_YOUT_L 0x46 33 | #define MPU6886_GYRO_ZOUT_H 0x47 34 | #define MPU6886_GYRO_ZOUT_L 0x48 35 | 36 | #define MPU6886_USER_CTRL 0x6A 37 | #define MPU6886_PWR_MGMT_1 0x6B 38 | #define MPU6886_PWR_MGMT_2 0x6C 39 | #define MPU6886_CONFIG 0x1A 40 | #define MPU6886_GYRO_CONFIG 0x1B 41 | #define MPU6886_ACCEL_CONFIG 0x1C 42 | #define MPU6886_ACCEL_CONFIG2 0x1D 43 | #define MPU6886_FIFO_EN 0x23 44 | 45 | //#define G (9.8) 46 | #define RtA 57.324841 47 | #define AtR 0.0174533 48 | #define Gyro_Gr 0.0010653 49 | 50 | enum Ascale 51 | { 52 | AFS_2G = 0, 53 | AFS_4G, 54 | AFS_8G, 55 | AFS_16G 56 | } ; 57 | 58 | enum Gscale 59 | { 60 | GFS_250DPS = 0, 61 | GFS_500DPS, 62 | GFS_1000DPS, 63 | GFS_2000DPS 64 | }; 65 | 66 | int mpu6886_start(void); 67 | void mpu6886_get_accel_adc(int16_t *ax, int16_t *ay, int16_t *az); 68 | void mpu6886_get_gyro_adc(int16_t *gx, int16_t *gy, int16_t *gz); 69 | void mpu6886_get_temp_adc(int16_t *t); 70 | 71 | void mpu6886_get_accel_data(float *ax, float *ay, float *az); 72 | void mpu6886_get_gyro_data(float *gx, float *gy, float *gz); 73 | void mpu6886_get_temp_data(float *t); 74 | 75 | void mpu6886_set_gyro_fsr(int scale); 76 | void mpu6886_set_accel_fsr(int scale); 77 | 78 | #endif -------------------------------------------------------------------------------- /components/interface/ttgo-input/ttgo-input.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include "freertos/FreeRTOS.h" 16 | #include "freertos/task.h" 17 | #include "driver/gpio.h" 18 | #include "esp_log.h" 19 | 20 | #include "lsm9ds1.h" 21 | 22 | #include "interface.h" 23 | 24 | #include "ttgo-input.h" 25 | 26 | void ttgo_input_task(void *pvParameter) 27 | { 28 | float ax = 0; 29 | float ay = 0; 30 | float az = 0; 31 | 32 | while (1) 33 | { 34 | lsm9ds1_get_accel_data(&ax, &ay, &az); 35 | // ESP_LOGI(INTERFACE_LOG, "ax: %f ay:%f az:%f", ax, ay, az); 36 | vTaskDelay(INTERFACE_INPUT_TICKS_MS / portTICK_PERIOD_MS); 37 | } 38 | } 39 | 40 | void interface_input_start(void) 41 | { 42 | lsm9ds1_start(); 43 | xTaskCreate(&ttgo_input_task, "ttgo_input_task", 4096, NULL, 5, NULL); 44 | } -------------------------------------------------------------------------------- /components/interface/ttgo-input/ttgo-input.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief execute interface commands via simple push buttons 18 | * 19 | */ 20 | #ifndef _ttgo_input_H_ 21 | #define _ttgo_input_H_ 22 | 23 | #endif -------------------------------------------------------------------------------- /components/interface/ttgo-lsm9ds1/lsm9ds1.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | 17 | #include "driver/i2c.h" 18 | #include "esp_log.h" 19 | 20 | #include "i2c-main.h" 21 | 22 | #include "lsm9ds1.h" 23 | 24 | float aRes, gRes; 25 | 26 | void lsm9ds1_i2c_read_bytes(uint8_t driver_addr, uint8_t start_addr, uint8_t number_Bytes, uint8_t *read_buffer) 27 | { 28 | i2c_cmd_handle_t cmd; 29 | cmd = i2c_cmd_link_create(); 30 | i2c_master_start(cmd); 31 | i2c_master_write_byte(cmd, (driver_addr << 1) | I2C_MASTER_WRITE, true); 32 | i2c_master_write_byte(cmd, start_addr, true); 33 | i2c_master_start(cmd); 34 | i2c_master_write_byte(cmd, (driver_addr << 1) | I2C_MASTER_READ, true); 35 | i2c_master_read(cmd, read_buffer, number_Bytes, I2C_MASTER_LAST_NACK); 36 | i2c_master_stop(cmd); 37 | ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS)); 38 | i2c_cmd_link_delete(cmd); 39 | } 40 | 41 | void lsm9ds1_i2c_write_bytes(uint8_t driver_addr, uint8_t start_addr, uint8_t number_Bytes, uint8_t *write_buffer) 42 | { 43 | i2c_cmd_handle_t cmd; 44 | cmd = i2c_cmd_link_create(); 45 | i2c_master_start(cmd); 46 | i2c_master_write_byte(cmd, (driver_addr << 1) | I2C_MASTER_WRITE, true); 47 | 48 | i2c_master_write_byte(cmd, start_addr, true); 49 | i2c_master_write(cmd, write_buffer, number_Bytes, true); 50 | 51 | i2c_master_stop(cmd); 52 | ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS)); 53 | i2c_cmd_link_delete(cmd); 54 | } 55 | 56 | int lsm9ds1_start(void) 57 | { 58 | if (!i2c_is_initialized()) 59 | { 60 | i2c_main_init(); 61 | } 62 | 63 | unsigned char regdata; 64 | // init ACC 65 | regdata = 0x38; 66 | lsm9ds1_i2c_write_bytes(ACC_ADDR, CTRL_REG5_A, 1, ®data); 67 | regdata = 0xC0; 68 | lsm9ds1_i2c_write_bytes(ACC_ADDR, CTRL_REG6_A, 1, ®data); 69 | 70 | lsm9ds1_i2c_read_bytes(ACC_ADDR, CTRL_REG6_A, 1, ®data); 71 | regdata &= ~(0b00011000); 72 | regdata |= ACCELRANGE_16G; 73 | lsm9ds1_i2c_write_bytes(ACC_ADDR, CTRL_REG6_A, 1, ®data); 74 | 75 | // init gyr 76 | regdata = 0xC0; 77 | lsm9ds1_i2c_write_bytes(GYR_ADDR, CTRL_REG1_G, 1, ®data); 78 | lsm9ds1_i2c_read_bytes(GYR_ADDR, CTRL_REG1_G, 1, ®data); 79 | regdata &= ~(0b00011000); 80 | regdata |= GYROSCALE_500DPS; 81 | lsm9ds1_i2c_write_bytes(GYR_ADDR, CTRL_REG1_G, 1, ®data); 82 | 83 | aRes = 16.0 / 32768.0; 84 | gRes = 500.0 / 32768.0; 85 | return 0; 86 | } 87 | 88 | void lsm9ds1_get_accel_adc(int16_t *ax, int16_t *ay, int16_t *az) 89 | { 90 | 91 | uint8_t buf[6]; 92 | lsm9ds1_i2c_read_bytes(ACC_ADDR, OUT_X_L_A, 6, buf); 93 | 94 | *ax = ((int16_t)buf[0] << 8) | buf[1]; 95 | *ay = ((int16_t)buf[2] << 8) | buf[3]; 96 | *az = ((int16_t)buf[4] << 8) | buf[5]; 97 | } 98 | void lsm9ds1_get_gyro_adc(int16_t *gx, int16_t *gy, int16_t *gz) 99 | { 100 | 101 | uint8_t buf[6]; 102 | lsm9ds1_i2c_read_bytes(GYR_ADDR, OUT_X_L_G, 6, buf); 103 | 104 | *gx = ((uint16_t)buf[0] << 8) | buf[1]; 105 | *gy = ((uint16_t)buf[2] << 8) | buf[3]; 106 | *gz = ((uint16_t)buf[4] << 8) | buf[5]; 107 | } 108 | 109 | void lsm9ds1_get_accel_data(float *ax, float *ay, float *az) 110 | { 111 | 112 | int16_t accX = 0; 113 | int16_t accY = 0; 114 | int16_t accZ = 0; 115 | lsm9ds1_get_accel_adc(&accX, &accY, &accZ); 116 | 117 | *ax = (float)accX * aRes; 118 | *ay = (float)accY * aRes; 119 | *az = (float)accZ * aRes; 120 | } 121 | 122 | void lsm9ds1_get_gyro_data(float *gx, float *gy, float *gz) 123 | { 124 | int16_t gyroX = 0; 125 | int16_t gyroY = 0; 126 | int16_t gyroZ = 0; 127 | lsm9ds1_get_gyro_adc(&gyroX, &gyroY, &gyroZ); 128 | 129 | *gx = (float)gyroX * gRes; 130 | *gy = (float)gyroY * gRes; 131 | *gz = (float)gyroZ * gRes; 132 | } 133 | -------------------------------------------------------------------------------- /components/interface/ttgo-lsm9ds1/lsm9ds1.h: -------------------------------------------------------------------------------- 1 | /* 2 | Note: The MPU6886 is an I2C sensor and uses the Arduino Wire library. 3 | Because the sensor is not 5V tolerant, we are using a 3.3 V 8 MHz Pro Mini or 4 | a 3.3 V Teensy 3.1. We have disabled the internal pull-ups used by the Wire 5 | library in the Wire.h/twi.c utility file. We are also using the 400 kHz fast 6 | I2C mode by setting the TWI_FREQ to 400000L /twi.h utility file. 7 | */ 8 | #ifndef _IMU_LSM9DS1_H_ 9 | #define _IMU_LSM9DS1_H_ 10 | 11 | #include "stdio.h" 12 | 13 | #define ID_AG 0x6F 14 | #define ID_A 0x6F 15 | #define ID_G 0x6F 16 | #define ID_M 0x3D 17 | 18 | #define FILENAME "/dev/i2c-1" 19 | #define MAG_ADDR 0x1E 20 | #define ACC_ADDR 0x6B 21 | #define GYR_ADDR 0x6B 22 | 23 | // Shared Accelerometer/Gyroscope Addresses 24 | #define WHO_AM_I_AG 0x0F 25 | #define CTRL_REG1_AG 0x10 26 | #define CTRL_REG2_AG 0x11 27 | #define CTRL_REG3_AG 0x12 28 | #define OUT_TEMP_L_AG 0x15 29 | #define OUT_TEMP_H_AG 0x16 30 | #define REG_STATUS_REG_AG 0x17 31 | #define CTRL_REG4_AG 0x1E 32 | #define CTRL_REG5_AG 0x1F 33 | #define CTRL_REG6_AG 0x20 34 | #define CTRL_REG7_AG 0x21 35 | #define CTRL_REG8_AG 0x22 36 | #define CTRL_REG9_AG 0x23 37 | #define CTRL_REG10_AG 0x24 38 | 39 | // Gyroscope addresses 40 | #define WHO_AM_I_G 0x0F 41 | #define CTRL_REG1_G 0x10 42 | #define CTRL_REG2_G 0x11 43 | #define CTRL_REG3_G 0x12 44 | #define OUT_X_L_G 0x18 45 | #define OUT_X_H_G 0x19 46 | #define OUT_Y_L_G 0x1A 47 | #define OUT_Y_H_G 0x1B 48 | #define OUT_Z_L_G 0x1C 49 | #define OUT_Z_H_G 0x1D 50 | 51 | // Accelerometer addresses 52 | #define WHO_AM_I_A 0x0F 53 | #define CTRL_REG5_A 0x1F 54 | #define CTRL_REG6_A 0x20 55 | #define CTRL_REG7_A 0x21 56 | #define OUT_X_L_A 0x28 57 | #define OUT_X_H_A 0x29 58 | #define OUT_Y_L_A 0x2A 59 | #define OUT_Y_H_A 0x2B 60 | #define OUT_Z_L_A 0x2C 61 | #define OUT_Z_H_A 0x2D 62 | 63 | // Magnetometer addresses 64 | #define WHO_AM_I_M 0x0F 65 | #define CTRL_REG1_M 0x20 66 | #define CTRL_REG2_M 0x21 67 | #define CTRL_REG3_M 0x22 68 | #define CTRL_REG4_M 0x23 69 | #define CTRL_REG5_M 0x24 70 | #define REG_STATUS_REG_M 0x27 71 | #define OUT_X_L_M 0x28 72 | #define OUT_X_H_M 0x29 73 | #define OUT_Y_L_M 0x2A 74 | #define OUT_Y_H_M 0x2B 75 | #define OUT_Z_L_M 0x2C 76 | #define OUT_Z_H_M 0x2D 77 | #define REG_CFG_M 0x30 78 | #define INT_SRC_M 0x31 79 | 80 | // Settings 81 | #define ACCELRANGE_2G 0x0 << 3 82 | #define ACCELRANGE_16G 0x1 << 3 83 | #define ACCELRANGE_4G 0x2 << 3 84 | #define ACCELRANGE_8G 0x3 << 3 85 | 86 | #define ACCELDATARATE_POWERDOWN 0x0 << 4 87 | #define ACCELDATARATE_3_125HZ 0x1 << 4 88 | #define ACCELDATARATE_6_25HZ 0x2 << 4 89 | #define ACCELDATARATE_12_5HZ 0x3 << 4 90 | #define ACCELDATARATE_25HZ 0x4 << 4 91 | #define ACCELDATARATE_50HZ 0x5 << 4 92 | #define ACCELDATARATE_100HZ 0x6 << 4 93 | #define ACCELDATARATE_200HZ 0x7 << 4 94 | #define ACCELDATARATE_400HZ 0x8 << 4 95 | #define ACCELDATARATE_800HZ 0x9 << 4 96 | #define ACCELDATARATE_1600HZ 0xa << 4 97 | 98 | #define MAGGAIN_4GAUSS 0x0 << 5 99 | #define MAGGAIN_8GAUSS 0x1 << 5 100 | #define MAGGAIN_12GAUSS 0x2 << 5 101 | #define MAGGAIN_16GAUSS 0x3 << 5 102 | 103 | #define MAGDATARATE_3_125HZ 0x0 << 2 104 | #define MAGDATARATE_6_25HZ 0x1 << 2 105 | #define MAGDATARATE_12_5HZ 0x2 << 2 106 | #define MAGDATARATE_25HZ 0x3 << 2 107 | #define MAGDATARATE_50HZ 0x4 << 2 108 | #define MAGDATARATE_100HZ 0x5 << 2 109 | 110 | #define GYROSCALE_245DPS 0x0 << 4 111 | #define GYROSCALE_500DPS 0x1 << 4 112 | #define GYROSCALE_2000DPS 0x2 << 4 113 | 114 | /* Conversions */ 115 | #define GRAVITY (9.80665F) 116 | 117 | #define ACCEL_MG_LSB_2G (0.061F) 118 | #define ACCEL_MG_LSB_4G (0.122F) 119 | #define ACCEL_MG_LSB_8G (0.244F) 120 | #define ACCEL_MG_LSB_16G (0.732F) // Is this right? Was expecting 0.488F 121 | 122 | #define MAG_MGAUSS_4GAUSS (0.16F) 123 | #define MAG_MGAUSS_8GAUSS (0.32F) 124 | #define MAG_MGAUSS_12GAUSS (0.48F) 125 | #define MAG_MGAUSS_16GAUSS (0.58F) 126 | 127 | #define GYRO_DPS_DIGIT_245DPS (0.00875F) 128 | #define GYRO_DPS_DIGIT_500DPS (0.01750F) 129 | #define GYRO_DPS_DIGIT_2000DPS (0.07000F) 130 | 131 | int lsm9ds1_start(void); 132 | void lsm9ds1_get_accel_adc(int16_t *ax, int16_t *ay, int16_t *az); 133 | void lsm9ds1_get_gyro_adc(int16_t *gx, int16_t *gy, int16_t *gz); 134 | 135 | void lsm9ds1_get_accel_data(float *ax, float *ay, float *az); 136 | void lsm9ds1_get_gyro_data(float *gx, float *gy, float *gz); 137 | 138 | #endif -------------------------------------------------------------------------------- /components/rtc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(include_list ".") 2 | 3 | if(CONFIG_ENA_INTERFACE_RTC_DS3231) 4 | set(src_list "custom-ds3231/ds3231.c") 5 | list(APPEND include_list "custom-ds3231") 6 | elseif(CONFIG_ENA_INTERFACE_M5STICKC OR CONFIG_ENA_INTERFACE_M5STICKC_PLUS) 7 | set(src_list "m5-bm8563/bm8563.c") 8 | list(APPEND include_list "m5-bm8563") 9 | else() 10 | set(src_list "dummy.c") 11 | endif() 12 | 13 | idf_component_register( 14 | SRCS 15 | ${src_list} 16 | INCLUDE_DIRS 17 | ${include_list} 18 | PRIV_REQUIRES 19 | "i2c-main" 20 | ) -------------------------------------------------------------------------------- /components/rtc/custom-ds3231/ds3231.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | 17 | #include "driver/i2c.h" 18 | #include "esp_log.h" 19 | 20 | #include "rtc.h" 21 | #include "i2c-main.h" 22 | 23 | #include "ds3231.h" 24 | 25 | uint8_t ds3231_dec2bcd(uint8_t value) 26 | { 27 | return ((value / 10 * 16) + (value % 10)); 28 | } 29 | 30 | uint8_t ds3231_bcd2dec(uint8_t value) 31 | { 32 | return ((value / 16 * 10) + (value % 16)); 33 | } 34 | 35 | void rtc_get_time(struct tm *time) 36 | { 37 | if (!i2c_is_initialized()) 38 | { 39 | i2c_main_init(); 40 | } 41 | uint8_t data[7]; 42 | 43 | i2c_cmd_handle_t cmd; 44 | cmd = i2c_cmd_link_create(); 45 | i2c_master_start(cmd); 46 | i2c_master_write_byte(cmd, (DS3231_ADDRESS << 1) | I2C_MASTER_WRITE, true); 47 | i2c_master_write_byte(cmd, DS3231_TIME, true); 48 | i2c_master_start(cmd); 49 | i2c_master_write_byte(cmd, (DS3231_ADDRESS << 1) | I2C_MASTER_READ, true); 50 | i2c_master_read(cmd, data, 7, I2C_MASTER_LAST_NACK); 51 | i2c_master_stop(cmd); 52 | ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS)); 53 | i2c_cmd_link_delete(cmd); 54 | 55 | time->tm_sec = ds3231_bcd2dec(data[0]); 56 | time->tm_min = ds3231_bcd2dec(data[1]); 57 | if (data[2] & DS3231_12_HOUR_FLAG) 58 | { 59 | time->tm_hour = ds3231_bcd2dec(data[2] & DS3231_12_HOUR_MASK) - 1; 60 | if (data[2] & DS3231_PM_HOUR_FLAG) 61 | { 62 | time->tm_hour += 12; 63 | } 64 | } 65 | else 66 | { 67 | time->tm_hour = ds3231_bcd2dec(data[2]); 68 | } 69 | time->tm_wday = ds3231_bcd2dec(data[3]) - 1; 70 | time->tm_mday = ds3231_bcd2dec(data[4]); 71 | time->tm_mon = ds3231_bcd2dec(data[5] & DS3231_MONTH_MASK) - 1; 72 | uint8_t century = (data[5] & DS3231_CENTURY_FLAG) >> 7; 73 | if (century) 74 | { 75 | time->tm_year = ds3231_bcd2dec(data[6]) + 100; 76 | } 77 | else 78 | { 79 | time->tm_year = ds3231_bcd2dec(data[6]); 80 | } 81 | time->tm_isdst = 0; 82 | } 83 | 84 | void rtc_set_time(struct tm *time) 85 | { 86 | if (!i2c_is_initialized()) 87 | { 88 | i2c_main_init(); 89 | } 90 | uint8_t data[7] = {0}; 91 | data[0] = ds3231_dec2bcd(time->tm_sec); 92 | data[1] = ds3231_dec2bcd(time->tm_min); 93 | data[2] = ds3231_dec2bcd(time->tm_hour); // write 24h format 94 | data[3] = ds3231_dec2bcd(time->tm_wday + 1); 95 | data[4] = ds3231_dec2bcd(time->tm_mday); 96 | uint8_t century = 0; 97 | if (time->tm_year > 100) 98 | { 99 | century = DS3231_CENTURY_FLAG; 100 | data[6] = ds3231_dec2bcd(time->tm_year - 100); 101 | } 102 | else 103 | { 104 | data[6] = ds3231_dec2bcd(time->tm_year); 105 | } 106 | 107 | data[5] = ds3231_dec2bcd(time->tm_mon + 1) + century; 108 | 109 | i2c_cmd_handle_t cmd; 110 | cmd = i2c_cmd_link_create(); 111 | i2c_master_start(cmd); 112 | i2c_master_write_byte(cmd, (DS3231_ADDRESS << 1) | I2C_MASTER_WRITE, true); 113 | 114 | i2c_master_write_byte(cmd, DS3231_TIME, true); 115 | i2c_master_write(cmd, data, 7, true); 116 | 117 | i2c_master_stop(cmd); 118 | ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS)); 119 | i2c_cmd_link_delete(cmd); 120 | } 121 | -------------------------------------------------------------------------------- /components/rtc/custom-ds3231/ds3231.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief I2C driver for DS3231 Real Time Clock 18 | * 19 | */ 20 | #ifndef _ds3231_H_ 21 | #define _ds3231_H_ 22 | 23 | #include 24 | 25 | // I2C address 26 | #define DS3231_ADDRESS 0x68 27 | 28 | // I2C registers 29 | #define DS3231_TIME 0x00 30 | #define DS3231_SECONDS 0x00 31 | #define DS3231_MINUTES 0x01 32 | #define DS3231_HOURS 0x02 33 | #define DS3231_DAY 0x03 34 | #define DS3231_DATE 0x04 35 | #define DS3231_MONTH 0x05 36 | #define DS3231_YEAR 0x06 37 | #define DS3231_ALARM1_SECONDS 0x07 38 | #define DS3231_ALARM1_MINUTES 0x08 39 | #define DS3231_ALARM1_HOURS 0x09 40 | #define DS3231_ALARM1_DATE 0x0A 41 | #define DS3231_ALARM2_MINUTES 0x0B 42 | #define DS3231_ALARM2_HOURS 0x0C 43 | #define DS3231_ALARM2_DATE 0x0D 44 | #define DS3231_CONTROL 0x0E 45 | #define DS3231_STATUS 0x0F 46 | #define DS3231_AGING_OFFSET 0x10 47 | #define DS3231_MSB_TEMP 0x11 48 | #define DS3231_LSB_TEMP 0x12 49 | 50 | // control registers 51 | #define DS3231_CONTROL_A1IE 0x01 52 | #define DS3231_CONTROL_A2IE 0x02 53 | #define DS3231_CONTROL_INTCN 0x04 54 | #define DS3231_CONTROL_RS1 0x08 55 | #define DS3231_CONTROL_RS2 0x10 56 | #define DS3231_CONTROL_CONV 0x20 57 | #define DS3231_CONTROL_BBSQW 0x40 58 | #define DS3231_CONTROL_EOSC 0x80 59 | 60 | // status registers 61 | #define DS3231_STATUSL_A1F 0x01 62 | #define DS3231_STATUSL_A2F 0x02 63 | #define DS3231_STATUSL_BSY 0x04 64 | #define DS3231_STATUSL_EN32KHZ 0x08 65 | #define DS3231_STATUSL_OSF 0x80 66 | 67 | // flags 68 | #define DS3231_CENTURY_FLAG 0x80 69 | #define DS3231_12_HOUR_FLAG 0x40 70 | #define DS3231_PM_HOUR_FLAG 0x20 71 | #define DS3231_12_HOUR_MASK 0x1F 72 | #define DS3231_MONTH_MASK 0x1F 73 | 74 | #endif -------------------------------------------------------------------------------- /components/rtc/dummy.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include "rtc.h" 16 | 17 | void rtc_get_time(struct tm *time) 18 | { 19 | } 20 | 21 | void rtc_set_time(struct tm *time) 22 | { 23 | } -------------------------------------------------------------------------------- /components/rtc/m5-bm8563/bm8563.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | 17 | #include "driver/i2c.h" 18 | #include "esp_log.h" 19 | 20 | #include "rtc.h" 21 | #include "i2c-main.h" 22 | 23 | #include "bm8563.h" 24 | 25 | uint8_t bm8563_dec2bcd(uint8_t value) 26 | { 27 | return (((value / 10) << 4) | (value % 10)); 28 | } 29 | 30 | uint8_t bm8563_bcd2dec(uint8_t value) 31 | { 32 | return (((value >> 4) * 10) + (value & 0x0f)); 33 | } 34 | 35 | /** 36 | * @brief Read time 37 | */ 38 | void rtc_get_time(struct tm *time) 39 | { 40 | if (!i2c_is_initialized()) 41 | { 42 | i2c_main_init(); 43 | } 44 | uint8_t data[7]; 45 | 46 | i2c_cmd_handle_t cmd; 47 | cmd = i2c_cmd_link_create(); 48 | i2c_master_start(cmd); 49 | i2c_master_write_byte(cmd, (BM8563_ADDRESS << 1) | I2C_MASTER_WRITE, true); 50 | i2c_master_write_byte(cmd, BM8563_SECONDS, true); 51 | i2c_master_start(cmd); 52 | i2c_master_write_byte(cmd, (BM8563_ADDRESS << 1) | I2C_MASTER_READ, true); 53 | i2c_master_read(cmd, data, 7, I2C_MASTER_LAST_NACK); 54 | i2c_master_stop(cmd); 55 | ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS)); 56 | i2c_cmd_link_delete(cmd); 57 | 58 | time->tm_sec = bm8563_bcd2dec(data[0] & 0b01111111); 59 | time->tm_min = bm8563_bcd2dec(data[1] & 0b01111111); 60 | time->tm_hour = bm8563_bcd2dec(data[2] & 0b00111111); 61 | time->tm_mday = bm8563_bcd2dec(data[3] & 0b00111111); 62 | time->tm_wday = bm8563_bcd2dec(data[4] & 0b00000111); 63 | time->tm_mon = bm8563_bcd2dec(data[5] & 0b00011111) - 1; 64 | if (data[5] & BM8563_CENTURY_BIT) 65 | { 66 | time->tm_year = bm8563_bcd2dec(data[6] & 0b11111111) + 100; 67 | } 68 | else 69 | { 70 | time->tm_year = bm8563_bcd2dec(data[6] & 0b01111111); 71 | } 72 | time->tm_isdst = 0; 73 | 74 | mktime(time); 75 | } 76 | 77 | /** 78 | * @brief Write time 79 | */ 80 | void rtc_set_time(struct tm *time) 81 | { 82 | if (!i2c_is_initialized()) 83 | { 84 | i2c_main_init(); 85 | } 86 | 87 | uint8_t data[7] = {0}; 88 | data[0] = bm8563_dec2bcd(time->tm_sec) & 0b01111111; 89 | data[1] = bm8563_dec2bcd(time->tm_min) & 0b01111111; 90 | data[2] = bm8563_dec2bcd(time->tm_hour) & 0b00111111; 91 | data[3] = bm8563_dec2bcd(time->tm_mday) & 0b00111111; 92 | data[4] = bm8563_dec2bcd(time->tm_wday) & 0b00000111; 93 | data[5] = bm8563_dec2bcd(time->tm_mon + 1) & 0b00011111; 94 | if (time->tm_year > 100) 95 | { 96 | data[5] |= BM8563_CENTURY_BIT; 97 | } 98 | 99 | data[6] = bm8563_dec2bcd(time->tm_year % 100) & 0b11111111; 100 | 101 | i2c_cmd_handle_t cmd; 102 | cmd = i2c_cmd_link_create(); 103 | i2c_master_start(cmd); 104 | i2c_master_write_byte(cmd, (BM8563_ADDRESS << 1) | I2C_MASTER_WRITE, true); 105 | 106 | i2c_master_write_byte(cmd, BM8563_SECONDS, true); 107 | i2c_master_write(cmd, data, 7, true); 108 | 109 | i2c_master_stop(cmd); 110 | ESP_ERROR_CHECK_WITHOUT_ABORT(i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS)); 111 | i2c_cmd_link_delete(cmd); 112 | } -------------------------------------------------------------------------------- /components/rtc/m5-bm8563/bm8563.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief I2C driver for BM8563 RTC 18 | * 19 | */ 20 | #ifndef _RTC_BM8562_H_ 21 | #define _RTC_BM8562_H_ 22 | 23 | #include "stdio.h" 24 | 25 | #include 26 | 27 | #define BM8563_ADDRESS 0x51 28 | #define BM8563_CONTROL_STATUS1 0x00 29 | #define BM8563_TESTC 0b00001000 30 | #define BM8563_STOP 0b00100000 31 | #define BM8563_TEST1 0b10000000 32 | #define BM8563_CONTROL_STATUS2 0x01 33 | #define BM8563_TIE 0b00000001 34 | #define BM8563_AIE 0b00000010 35 | #define BM8563_TF 0b00000100 36 | #define BM8563_AF 0b00001000 37 | #define BM8563_TI_TP 0b00010000 38 | #define BM8563_SECONDS 0x02 39 | #define BM8563_MINUTES 0x03 40 | #define BM8563_HOURS 0x04 41 | #define BM8563_DAY 0x05 42 | #define BM8563_WEEKDAY 0x06 43 | #define BM8563_MONTH 0x07 44 | #define BM8563_YEAR 0x08 45 | #define BM8563_TIME_SIZE 0x07 46 | #define BM8563_CENTURY_BIT 0b10000000 47 | 48 | #define BM8563_MINUTE_ALARM 0x09 49 | #define BM8563_HOUR_ALARM 0x0a 50 | #define BM8563_DAY_ALARM 0x0b 51 | #define BM8563_WEEKDAY_ALARM 0x0c 52 | #define BM8563_ALARM_DISABLE 0b10000000 53 | #define BM8563_ALARM_NONE 0xff 54 | #define BM8563_ALARM_SIZE 0x04 55 | 56 | #define BM8563_TIMER_CONTROL 0x0e 57 | #define BM8563_TIMER_ENABLE 0b10000000 58 | #define BM8563_TIMER_4_096KHZ 0b00000000 59 | #define BM8563_TIMER_64HZ 0b00000001 60 | #define BM8563_TIMER_1HZ 0b00000010 61 | #define BM8563_TIMER_1_60HZ 0b00000011 62 | #define BM8563_TIMER 0x0f 63 | 64 | #define BM8563_ALARM_SET 0x0900 65 | #define BM8563_ALARM_READ 0x0901 66 | #define BM8563_CONTROL_STATUS1_READ 0x0000 67 | #define BM8563_CONTROL_STATUS1_WRITE 0x0001 68 | #define BM8563_CONTROL_STATUS2_READ 0x0100 69 | #define BM8563_CONTROL_STATUS2_WRITE 0x0101 70 | #define BM8563_TIMER_CONTROL_READ 0x0e00 71 | #define BM8563_TIMER_CONTROL_WRITE 0x0e01 72 | #define BM8563_TIMER_READ 0x0f00 73 | #define BM8563_TIMER_WRITE 0x0f01 74 | 75 | /* Status codes. */ 76 | #define BM8563_ERROR_NOTTY (-1) 77 | #define BM8563_OK (0x00) 78 | #define BM8563_ERR_LOW_VOLTAGE (0x80) 79 | 80 | #endif -------------------------------------------------------------------------------- /components/rtc/rtc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | /** 15 | * @file 16 | * 17 | * @brief interface for RTC 18 | * 19 | */ 20 | #ifndef _rtc_H_ 21 | #define _rtc_H_ 22 | 23 | #include 24 | 25 | /** 26 | * @brief Read time 27 | */ 28 | void rtc_get_time(struct tm *time); 29 | 30 | /** 31 | * @brief Write time 32 | */ 33 | void rtc_set_time(struct tm *time); 34 | 35 | #endif -------------------------------------------------------------------------------- /components/wifi-controller/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS 3 | "wifi-controller.c" 4 | INCLUDE_DIRS "." 5 | PRIV_REQUIRES 6 | esp_wifi 7 | nvs_flash 8 | ) -------------------------------------------------------------------------------- /components/wifi-controller/wifi-controller.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | 16 | #include "freertos/FreeRTOS.h" 17 | #include "freertos/task.h" 18 | #include "freertos/event_groups.h" 19 | #include "esp_system.h" 20 | #include "esp_wifi.h" 21 | #include "esp_event.h" 22 | #include "esp_log.h" 23 | #include "nvs_flash.h" 24 | 25 | #include "lwip/err.h" 26 | #include "lwip/sys.h" 27 | 28 | #include "wifi-controller.h" 29 | 30 | #define WIFI_CONNECTED_BIT BIT0 31 | #define WIFI_FAIL_BIT BIT1 32 | 33 | static EventGroupHandle_t s_wifi_event_group; 34 | 35 | static bool initialized = false; 36 | static bool connecting = false; 37 | 38 | static wifi_ap_record_t current_wifi_ap; 39 | 40 | static wifi_callback wifi_connected_callback; 41 | static wifi_callback wifi_scan_callback; 42 | 43 | static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) 44 | { 45 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) 46 | { 47 | esp_wifi_connect(); 48 | } 49 | else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) 50 | { 51 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 52 | connecting = false; 53 | } 54 | else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) 55 | { 56 | if (wifi_connected_callback != NULL) 57 | { 58 | (*wifi_connected_callback)(); 59 | } 60 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 61 | connecting = false; 62 | heap_caps_check_integrity_all(true); 63 | } 64 | else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) 65 | { 66 | if (wifi_scan_callback != NULL) 67 | { 68 | (*wifi_scan_callback)(); 69 | } 70 | } 71 | else 72 | { 73 | ESP_LOGD(WIFI_LOG, "other wifi event: event base %s, event id %d", event_base, event_id); 74 | } 75 | } 76 | 77 | void wifi_controller_init(void) 78 | { 79 | // init NVS for WIFI 80 | esp_err_t ret; 81 | ret = nvs_flash_init(); 82 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) 83 | { 84 | ESP_ERROR_CHECK(nvs_flash_erase()); 85 | ESP_ERROR_CHECK(nvs_flash_init()); 86 | } 87 | 88 | ESP_ERROR_CHECK(esp_netif_init()); 89 | 90 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 91 | 92 | esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); 93 | assert(sta_netif); 94 | 95 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 96 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 97 | 98 | initialized = true; 99 | } 100 | 101 | void wifi_controller_scan(wifi_ap_record_t *ap_info, uint16_t *ap_count, wifi_callback callback) 102 | { 103 | if (!initialized) 104 | { 105 | wifi_controller_init(); 106 | } 107 | 108 | wifi_scan_callback = callback; 109 | 110 | uint16_t number = 10; 111 | 112 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop()); 113 | vTaskDelay(1000 / portTICK_PERIOD_MS); 114 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); 115 | ESP_ERROR_CHECK(esp_wifi_start()); 116 | ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, true)); 117 | ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info)); 118 | ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(ap_count)); 119 | ESP_ERROR_CHECK(esp_wifi_stop()); 120 | } 121 | 122 | esp_err_t wifi_controller_connect(wifi_config_t wifi_config, wifi_callback callback) 123 | { 124 | if (connecting) 125 | { 126 | return ESP_ERR_NOT_SUPPORTED; 127 | } 128 | connecting = true; 129 | wifi_connected_callback = callback; 130 | if (!initialized) 131 | { 132 | wifi_controller_init(); 133 | } 134 | 135 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop()); 136 | vTaskDelay(1000 / portTICK_PERIOD_MS); 137 | esp_event_handler_instance_t instance_any_id; 138 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id)); 139 | esp_event_handler_instance_t instance_got_ip; 140 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip)); 141 | 142 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); 143 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); 144 | ESP_ERROR_CHECK(esp_wifi_start()); 145 | 146 | s_wifi_event_group = xEventGroupCreate(); 147 | 148 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); 149 | 150 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 151 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 152 | vEventGroupDelete(s_wifi_event_group); 153 | 154 | if (bits & WIFI_CONNECTED_BIT) 155 | { 156 | ESP_LOGV(WIFI_LOG, "connected to ap SSID:%s", wifi_config.sta.ssid); 157 | return ESP_OK; 158 | } 159 | else if (bits & WIFI_FAIL_BIT) 160 | { 161 | ESP_LOGI(WIFI_LOG, "Failed to connect to SSID:%s with PASSWORD:%s", wifi_config.sta.ssid, wifi_config.sta.password); 162 | return ESP_ERR_INVALID_ARG; 163 | } 164 | else 165 | { 166 | ESP_LOGE(WIFI_LOG, "UNEXPECTED EVENT"); 167 | return ESP_FAIL; 168 | } 169 | } 170 | 171 | esp_err_t wifi_controller_reconnect(wifi_callback callback) 172 | { 173 | if (connecting) 174 | { 175 | return ESP_ERR_NOT_SUPPORTED; 176 | } 177 | connecting = true; 178 | 179 | wifi_connected_callback = callback; 180 | if (!initialized) 181 | { 182 | wifi_controller_init(); 183 | } 184 | 185 | ESP_ERROR_CHECK_WITHOUT_ABORT(esp_wifi_stop()); 186 | vTaskDelay(1000 / portTICK_PERIOD_MS); 187 | 188 | esp_event_handler_instance_t instance_any_id; 189 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id)); 190 | esp_event_handler_instance_t instance_got_ip; 191 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip)); 192 | 193 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); 194 | ESP_ERROR_CHECK(esp_wifi_start()); 195 | 196 | s_wifi_event_group = xEventGroupCreate(); 197 | 198 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); 199 | 200 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 201 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 202 | vEventGroupDelete(s_wifi_event_group); 203 | 204 | if (bits & WIFI_CONNECTED_BIT) 205 | { 206 | ESP_LOGV(WIFI_LOG, "reconnected to last ap"); 207 | return ESP_OK; 208 | } 209 | else if (bits & WIFI_FAIL_BIT) 210 | { 211 | ESP_LOGD(WIFI_LOG, "Failed to reconnect to last ap"); 212 | return ESP_ERR_INVALID_ARG; 213 | } 214 | else 215 | { 216 | ESP_LOGE(WIFI_LOG, "UNEXPECTED EVENT"); 217 | return ESP_FAIL; 218 | } 219 | } 220 | 221 | esp_err_t wifi_controller_disconnect(void) 222 | { 223 | if (esp_wifi_sta_get_ap_info(¤t_wifi_ap) == ESP_OK) 224 | { 225 | ESP_ERROR_CHECK(esp_wifi_disconnect()); 226 | } 227 | return ESP_OK; 228 | } 229 | 230 | wifi_ap_record_t *wifi_controller_connection(void) 231 | { 232 | if (esp_wifi_sta_get_ap_info(¤t_wifi_ap) == ESP_OK) 233 | { 234 | ESP_LOGD(WIFI_LOG, "Current AP: %s", current_wifi_ap.ssid); 235 | return ¤t_wifi_ap; 236 | } 237 | return NULL; 238 | } -------------------------------------------------------------------------------- /components/wifi-controller/wifi-controller.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #ifndef _wifi_CONTROLLER_H_ 15 | #define _wifi_CONTROLLER_H_ 16 | 17 | #include "esp_err.h" 18 | #include "esp_wifi_types.h" 19 | 20 | #define WIFI_LOG "wifi-controller" // TAG for Logging 21 | 22 | /** 23 | * @brief callback function after successfull wifi connect 24 | */ 25 | typedef void (*wifi_callback)(void); 26 | 27 | /** 28 | * @brief scan for WiFis 29 | * 30 | * @param[out] ap_info scanned APs 31 | * @param[out] ap_count number of scanned APs 32 | */ 33 | void wifi_controller_scan(wifi_ap_record_t ap_info[], uint16_t *ap_count, wifi_callback callback); 34 | 35 | /** 36 | * @brief connect to wifi ap 37 | * 38 | * @param[in] wifi_config config of wifi to connect 39 | * @param[in] callback callback function after connection 40 | * 41 | * @return 42 | * esp_err_t connection status 43 | */ 44 | esp_err_t wifi_controller_connect(wifi_config_t wifi_config, wifi_callback callback); 45 | 46 | /** 47 | * @brief reconnect to previous wifi 48 | * 49 | * @param[in] callback callback function after connection 50 | * 51 | * @return 52 | * esp_err_t connection status 53 | */ 54 | esp_err_t wifi_controller_reconnect(wifi_callback callback); 55 | 56 | /** 57 | * @brief disconnect wifi 58 | * 59 | */ 60 | esp_err_t wifi_controller_disconnect(void); 61 | 62 | /** 63 | * @brief get current wifi connection 64 | * 65 | * @return 66 | * wifi_ap_record_t pointer to current wifi connection, NULL if not connected 67 | */ 68 | wifi_ap_record_t *wifi_controller_connection(void); 69 | 70 | #endif -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS 3 | "main.c" 4 | INCLUDE_DIRS "" 5 | ) 6 | -------------------------------------------------------------------------------- /main/main.c: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Lukas Haubaum 2 | // 3 | // Licensed under the GNU Affero General Public License, Version 3; 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // https://www.gnu.org/licenses/agpl-3.0.html 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include "freertos/FreeRTOS.h" 16 | #include "freertos/task.h" 17 | #include 18 | #include 19 | #include "esp_system.h" 20 | #include "esp_sntp.h" 21 | #include "esp_log.h" 22 | 23 | #include "ena.h" 24 | #include "ena-storage.h" 25 | #include "ena-beacons.h" 26 | #include "ena-exposure.h" 27 | #include "ena-bluetooth-advertise.h" 28 | #include "ena-bluetooth-scan.h" 29 | #include "ena-eke-proxy.h" 30 | #include "interface.h" 31 | #include "rtc.h" 32 | #include "wifi-controller.h" 33 | 34 | #include "sdkconfig.h" 35 | 36 | void time_sync_notification_cb(struct timeval *tv) 37 | { 38 | time_t time = (time_t)tv->tv_sec; 39 | struct tm *rtc_time = gmtime(&time); 40 | rtc_set_time(rtc_time); 41 | settimeofday(tv, NULL); 42 | ESP_LOGD(ENA_LOG, "NTP time:%lu %s", tv->tv_sec, asctime(rtc_time)); 43 | } 44 | 45 | void app_main(void) 46 | { 47 | // debug only own LOG TAGs 48 | esp_log_level_set("*", ESP_LOG_WARN); 49 | esp_log_level_set("wifi", ESP_LOG_ERROR); 50 | esp_log_level_set(ENA_LOG, ESP_LOG_DEBUG); 51 | esp_log_level_set(ENA_BEACON_LOG, ESP_LOG_INFO); 52 | esp_log_level_set(ENA_ADVERTISE_LOG, ESP_LOG_INFO); 53 | esp_log_level_set(ENA_SCAN_LOG, ESP_LOG_INFO); 54 | esp_log_level_set(ENA_EXPOSURE_LOG, ESP_LOG_DEBUG); 55 | esp_log_level_set(ENA_STORAGE_LOG, ESP_LOG_INFO); 56 | esp_log_level_set(ENA_EKE_PROXY_LOG, ESP_LOG_DEBUG); 57 | esp_log_level_set(INTERFACE_LOG, ESP_LOG_INFO); 58 | esp_log_level_set(WIFI_LOG, ESP_LOG_INFO); 59 | 60 | sntp_setoperatingmode(SNTP_OPMODE_POLL); 61 | sntp_setservername(0, "pool.ntp.org"); 62 | sntp_set_time_sync_notification_cb(time_sync_notification_cb); 63 | sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH); 64 | sntp_init(); 65 | 66 | // set system time from RTC 67 | struct tm rtc_time; 68 | rtc_get_time(&rtc_time); 69 | 70 | time_t curtime = mktime(&rtc_time); 71 | struct timeval tv = {0}; 72 | tv.tv_sec = curtime; 73 | settimeofday(&tv, NULL); 74 | 75 | ena_start(); 76 | 77 | // start interface 78 | interface_start(); 79 | 80 | // start with main interface 81 | interface_main_start(); 82 | 83 | wifi_controller_reconnect(NULL); 84 | 85 | while (1) 86 | { 87 | ena_run(); 88 | ena_eke_proxy_run(); 89 | vTaskDelay(1000 / portTICK_PERIOD_MS); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /partitions.csv: -------------------------------------------------------------------------------- 1 | # ESP-IDF Partition Table 2 | # Name, Type, SubType, Offset, Size, Flags 3 | nvs, data, nvs, 0x9000, 0x6000, 4 | phy_init, data, phy, 0xf000, 0x1000, 5 | factory, app, factory, 0x10000, 0x177000, 6 | ena, data, 0xFF, 0x187000,0x261000, --------------------------------------------------------------------------------