diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c16face --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +Kconfig.projbuild +build +sdkconfig +sdkconfig.old +wifi_cfg.h +config.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3a68925 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "components/u8g2"] + path = components/u8g2 + url = git@github.com:olikraus/u8g2.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c15c7d7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(esp32-wallet-viewer) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9d6b7fb --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := esp32-wallet-viewer + +include $(IDF_PATH)/make/project.mk + diff --git a/README.md b/README.md new file mode 100644 index 0000000..d31034d --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# esp32-wallet-viewer +esp32 code to monitor wallets + +![img01](https://raw.githubusercontent.com/arnaucube/esp32-wallet-viewer/master/img01.png 'img01') + +- `main/wifi_cfg.h` example: +```c +#define WIFI_SSID "wifi name" +#define WIFI_PASS "password" +``` + +- get `u8g2` submodule: +``` +git submodule init +``` + +- run: +``` +> make + +> make flash +``` + +--- + +- Helpful esp32 documentation: https://docs.espressif.com/projects/esp-idf/en/latest/get-started/ +- Helpful esp32 examples: https://github.com/lucadentella/esp32-tutorial diff --git a/components/u8g2 b/components/u8g2 new file mode 160000 index 0000000..45ac4e0 --- /dev/null +++ b/components/u8g2 @@ -0,0 +1 @@ +Subproject commit 45ac4e0edc76c6bc3d808304fccb73c543535ded diff --git a/img01.png b/img01.png new file mode 100644 index 0000000..fcbfad0 Binary files /dev/null and b/img01.png differ diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..647a294 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_SRCS "main.c") +set(COMPONENT_ADD_INCLUDEDIRS "") + +register_component() diff --git a/main/component.mk b/main/component.mk new file mode 100644 index 0000000..0b9d758 --- /dev/null +++ b/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..92b4401 --- /dev/null +++ b/main/main.c @@ -0,0 +1,221 @@ +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_spi_flash.h" + +#include "u8g2_esp32_hal.h" + +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#include "lwip/netdb.h" +#include "lwip/sockets.h" + +#include "config.h" + + +#define PIN_SDA 5 +#define PIN_SCL 4 + +static u8g2_t u8g2; + +// Event group +static EventGroupHandle_t wifi_event_group; +const int CONNECTED_BIT = BIT0; + +static void print_screen(char *s1, char *s2, char *s3); + +// HTTP request +static const char *REQUEST = "GET "CONFIG_PATH" HTTP/1.1\n" + "Host: "CONFIG_URL"\n" + "User-Agent: ESP32\n" + "\n"; + +static void print_screen(char *s1, char *s2, char *s3) +{ + u8g2_ClearBuffer(&u8g2); + u8g2_DrawStr(&u8g2, 2,17,s1); + u8g2_DrawStr(&u8g2, 2,37,s2); + u8g2_DrawStr(&u8g2, 2,57,s3); + u8g2_SendBuffer(&u8g2); + vTaskDelay(300 / portTICK_RATE_MS); +} + +// Wifi event handler +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +// Main task +void main_task(void *pvParameter) +{ + // wait for connection + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); + printf("connected!\n"); + print_screen("wifi connected", "", ""); + printf("\n"); + + // print the local IP address + tcpip_adapter_ip_info_t ip_info; + ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info)); + printf("IP Address: %s\n", ip4addr_ntoa(&ip_info.ip)); + printf("Subnet mask: %s\n", ip4addr_ntoa(&ip_info.netmask)); + printf("Gateway: %s\n", ip4addr_ntoa(&ip_info.gw)); + printf("\n"); + print_screen("IP Address:", ip4addr_ntoa(&ip_info.ip), ""); + + // define connection parameters + const struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + }; + + // address info struct and receive buffer + struct addrinfo *res; + char recv_buf[100]; + + // resolve the IP of the target website + int result = getaddrinfo(CONFIG_URL, CONFIG_PORT, &hints, &res); + if((result != 0) || (res == NULL)) { + printf("Unable to resolve IP for target website %s\n", CONFIG_URL); + while(1) vTaskDelay(1000 / portTICK_RATE_MS); + } + printf("Target website's IP resolved\n"); + + // create a new socket + int s = socket(res->ai_family, res->ai_socktype, 0); + if(s < 0) { + printf("Unable to allocate a new socket\n"); + while(1) vTaskDelay(1000 / portTICK_RATE_MS); + } + printf("Socket allocated, id=%d\n", s); + + // connect to the specified server + result = connect(s, res->ai_addr, res->ai_addrlen); + if(result != 0) { + printf("Unable to connect to the target website\n"); + close(s); + while(1) vTaskDelay(1000 / portTICK_RATE_MS); + } + printf("Connected to the target website\n"); + print_screen("Connected to the", "target website",""); + + // send the request + result = write(s, REQUEST, strlen(REQUEST)); + if(result < 0) { + printf("Unable to send the HTTP request\n"); + close(s); + while(1) vTaskDelay(1000 / portTICK_RATE_MS); + } + printf("HTTP request sent\n"); + print_screen("http request", "", ""); + + // print the response + printf("HTTP response:\n"); + printf("--------------------------------------------------------------------------------\n"); + int r; + do { + bzero(recv_buf, sizeof(recv_buf)); + r = read(s, recv_buf, sizeof(recv_buf) - 1); + for(int i = 0; i < r; i++) { + putchar(recv_buf[i]); + } + } while(r > 0); + printf("--------------------------------------------------------------------------------\n"); + + close(s); + printf("Socket closed\n"); + + while(1) { + vTaskDelay(5000 / portTICK_RATE_MS); + } +} + +void app_main() { + + // initialize the u8g2 hal + u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT; + u8g2_esp32_hal.sda = PIN_SDA; + u8g2_esp32_hal.scl = PIN_SCL; + u8g2_esp32_hal_init(u8g2_esp32_hal); + + // initialize the u8g2 library + u8g2_Setup_ssd1306_i2c_128x64_noname_f( + &u8g2, + U8G2_R0, + u8g2_esp32_i2c_byte_cb, + u8g2_esp32_gpio_and_delay_cb); + + // set the display address + u8x8_SetI2CAddress(&u8g2.u8x8, 0x78); + + // initialize the display + u8g2_InitDisplay(&u8g2); + + // wake up the display + u8g2_SetPowerSave(&u8g2, 0); + + // set font + u8g2_SetFont(&u8g2, u8g2_font_timR14_tf); + + print_screen("screen ready", "", ""); + + // initialize NVS + ESP_ERROR_CHECK(nvs_flash_init()); + + // -- WIFI -- + // disable the default wifi logging + esp_log_level_set("wifi", ESP_LOG_NONE); + + // create the event group to handle wifi events + wifi_event_group = xEventGroupCreate(); + + // initialize the tcp stack + tcpip_adapter_init(); + + // initialize the wifi event handler + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + + // initialize the wifi stack in STAtion mode with config in RAM + wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + + // configure the wifi connection and start the interface + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_WIFI_SSID, + .password = CONFIG_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + /* char buffer[50]; */ + /* snprintf(buffer, sizeof(buffer), "Connecting to\n%s... ", CONFIG_WIFI_SSID); */ + /* print_screen(buffer, "", ""); */ + print_screen("Connectiong to:", CONFIG_WIFI_SSID, ""); + + // start the main task + xTaskCreate(&main_task, "main_task", 2048, NULL, 5, NULL); +} diff --git a/main/u8g2_esp32_hal.c b/main/u8g2_esp32_hal.c new file mode 100644 index 0000000..030f6a5 --- /dev/null +++ b/main/u8g2_esp32_hal.c @@ -0,0 +1,236 @@ +#include +#include + +#include "sdkconfig.h" +#include "esp_log.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "u8g2_esp32_hal.h" + +static const char *TAG = "u8g2_hal"; +static const unsigned int I2C_TIMEOUT_MS = 1000; + +static spi_device_handle_t handle_spi; // SPI handle. +static i2c_cmd_handle_t handle_i2c; // I2C handle. +static u8g2_esp32_hal_t u8g2_esp32_hal; // HAL state data. + +#undef ESP_ERROR_CHECK +#define ESP_ERROR_CHECK(x) do { esp_err_t rc = (x); if (rc != ESP_OK) { ESP_LOGE("err", "esp_err_t = %d", rc); assert(0 && #x);} } while(0); + +/* + * Initialze the ESP32 HAL. + */ +void u8g2_esp32_hal_init(u8g2_esp32_hal_t u8g2_esp32_hal_param) { + u8g2_esp32_hal = u8g2_esp32_hal_param; +} // u8g2_esp32_hal_init + +/* + * HAL callback function as prescribed by the U8G2 library. This callback is invoked + * to handle SPI communications. + */ +uint8_t u8g2_esp32_spi_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { + ESP_LOGD(TAG, "spi_byte_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr); + switch(msg) { + case U8X8_MSG_BYTE_SET_DC: + if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) { + gpio_set_level(u8g2_esp32_hal.dc, arg_int); + } + break; + + case U8X8_MSG_BYTE_INIT: { + if (u8g2_esp32_hal.clk == U8G2_ESP32_HAL_UNDEFINED || + u8g2_esp32_hal.mosi == U8G2_ESP32_HAL_UNDEFINED || + u8g2_esp32_hal.cs == U8G2_ESP32_HAL_UNDEFINED) { + break; + } + + spi_bus_config_t bus_config; + memset(&bus_config, 0, sizeof(spi_bus_config_t)); + bus_config.sclk_io_num = u8g2_esp32_hal.clk; // CLK + bus_config.mosi_io_num = u8g2_esp32_hal.mosi; // MOSI + bus_config.miso_io_num = -1; // MISO + bus_config.quadwp_io_num = -1; // Not used + bus_config.quadhd_io_num = -1; // Not used + //ESP_LOGI(TAG, "... Initializing bus."); + ESP_ERROR_CHECK(spi_bus_initialize(HSPI_HOST, &bus_config, 1)); + + spi_device_interface_config_t dev_config; + dev_config.address_bits = 0; + dev_config.command_bits = 0; + dev_config.dummy_bits = 0; + dev_config.mode = 0; + dev_config.duty_cycle_pos = 0; + dev_config.cs_ena_posttrans = 0; + dev_config.cs_ena_pretrans = 0; + dev_config.clock_speed_hz = 10000; + dev_config.spics_io_num = u8g2_esp32_hal.cs; + dev_config.flags = 0; + dev_config.queue_size = 200; + dev_config.pre_cb = NULL; + dev_config.post_cb = NULL; + //ESP_LOGI(TAG, "... Adding device bus."); + ESP_ERROR_CHECK(spi_bus_add_device(HSPI_HOST, &dev_config, &handle_spi)); + + break; + } + + case U8X8_MSG_BYTE_SEND: { + spi_transaction_t trans_desc; + trans_desc.addr = 0; + trans_desc.cmd = 0; + trans_desc.flags = 0; + trans_desc.length = 8 * arg_int; // Number of bits NOT number of bytes. + trans_desc.rxlength = 0; + trans_desc.tx_buffer = arg_ptr; + trans_desc.rx_buffer = NULL; + + //ESP_LOGI(TAG, "... Transmitting %d bytes.", arg_int); + ESP_ERROR_CHECK(spi_device_transmit(handle_spi, &trans_desc)); + break; + } + } + return 0; +} // u8g2_esp32_spi_byte_cb + +/* + * HAL callback function as prescribed by the U8G2 library. This callback is invoked + * to handle I2C communications. + */ +uint8_t u8g2_esp32_i2c_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { + ESP_LOGD(TAG, "i2c_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr); + + switch(msg) { + case U8X8_MSG_BYTE_SET_DC: { + if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) { + gpio_set_level(u8g2_esp32_hal.dc, arg_int); + } + break; + } + + case U8X8_MSG_BYTE_INIT: { + if (u8g2_esp32_hal.sda == U8G2_ESP32_HAL_UNDEFINED || + u8g2_esp32_hal.scl == U8G2_ESP32_HAL_UNDEFINED) { + break; + } + + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + ESP_LOGI(TAG, "sda_io_num %d", u8g2_esp32_hal.sda); + conf.sda_io_num = u8g2_esp32_hal.sda; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + ESP_LOGI(TAG, "scl_io_num %d", u8g2_esp32_hal.scl); + conf.scl_io_num = u8g2_esp32_hal.scl; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + ESP_LOGI(TAG, "clk_speed %d", I2C_MASTER_FREQ_HZ); + conf.master.clk_speed = I2C_MASTER_FREQ_HZ; + ESP_LOGI(TAG, "i2c_param_config %d", conf.mode); + ESP_ERROR_CHECK(i2c_param_config(I2C_MASTER_NUM, &conf)); + ESP_LOGI(TAG, "i2c_driver_install %d", I2C_MASTER_NUM); + ESP_ERROR_CHECK(i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0)); + break; + } + + case U8X8_MSG_BYTE_SEND: { + uint8_t* data_ptr = (uint8_t*)arg_ptr; + ESP_LOG_BUFFER_HEXDUMP(TAG, data_ptr, arg_int, ESP_LOG_VERBOSE); + + while( arg_int > 0 ) { + ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, *data_ptr, ACK_CHECK_EN)); + data_ptr++; + arg_int--; + } + break; + } + + case U8X8_MSG_BYTE_START_TRANSFER: { + uint8_t i2c_address = u8x8_GetI2CAddress(u8x8); + handle_i2c = i2c_cmd_link_create(); + ESP_LOGD(TAG, "Start I2C transfer to %02X.", i2c_address>>1); + ESP_ERROR_CHECK(i2c_master_start(handle_i2c)); + ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, i2c_address | I2C_MASTER_WRITE, ACK_CHECK_EN)); + break; + } + + case U8X8_MSG_BYTE_END_TRANSFER: { + ESP_LOGD(TAG, "End I2C transfer."); + ESP_ERROR_CHECK(i2c_master_stop(handle_i2c)); + ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_MASTER_NUM, handle_i2c, I2C_TIMEOUT_MS / portTICK_RATE_MS)); + i2c_cmd_link_delete(handle_i2c); + break; + } + } + return 0; +} // u8g2_esp32_i2c_byte_cb + +/* + * HAL callback function as prescribed by the U8G2 library. This callback is invoked + * to handle callbacks for GPIO and delay functions. + */ +uint8_t u8g2_esp32_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { + ESP_LOGD(TAG, "gpio_and_delay_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr); + + switch(msg) { + // Initialize the GPIO and DELAY HAL functions. If the pins for DC and RESET have been + // specified then we define those pins as GPIO outputs. + case U8X8_MSG_GPIO_AND_DELAY_INIT: { + uint64_t bitmask = 0; + if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) { + bitmask = bitmask | (1ull<