diff --git a/README.md b/README.md index 94071a0..b24ddfe 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,19 @@ This repository contains the sources required to run the Inkpot frontend on a Lilygo T5, an ESP32 equipped with an e-ink display. It allows you to create Lua sketches that draw on the e-ink screen. +You need to give it a 2.4Ghz Wifi SSID and password by adjusting `main/settings.h`. After that it will start an HTTP server that you can use to interact with the device. The HTTP server has two endpoints: + +- `GET /` -- Hello world page to confirm that things are working +- `POST /draw` -- Accepts a Lua POST body and will execute the script + +You can send scripts like this: + +``` bash +cat foo.lua | curl -X POST --data-binary @- 10.0.0.100/draw +``` + +The IP address of the display is logged to its serial output and can be read via `idf.py monitor` (see below). + ## Prerequisites There is a `flake.nix` at the root of this repository that defines a development environment with all required tools. If you are using `direnv`, `cd` into the repositorie's root directory after cloning it and run `direnv allow`. If you aren't using direnv the enviroment can be activated using `nix develop .`. diff --git a/assets/epaper.lua b/assets/epaper.lua index e7f13ce..ce02d8c 100644 --- a/assets/epaper.lua +++ b/assets/epaper.lua @@ -13,12 +13,43 @@ print( 'height: ' .. height ) -for i = 1, 50 do - x = math.random() * width - y = math.random() * height - r = 24 + math.ceil(math.random() * 12) +-- easing functions taken from https://easings.net/ - paper.fill_circle(x, y, r, math.floor(math.random() * 0xFF)) +function ease_in_sine(x) + return 1 - math.cos(x * math.pi / 2) +end + +function ease_in_out_sine(x) + return -(math.cos(math.pi * x) - 1) / 2 +end + +-- +-- actual sketch follows below +-- + +local x = width / 2 +local max_radius = 128 +local min_radius = max_radius / 6 +local padding = max_radius +local max_color = 0xDD +local min_color = 0x33 +local steps = 72 + +-- background +paper.fill_rect(0, 0, width, height, 0x22) + +-- sketch +for i = 1, steps do + local n = i / steps + local e1 = ease_in_sine(n) + local e2 = 1 - ease_in_out_sine(ease_in_sine(n)) + local color = math.floor(max_color - (max_color - min_color) * e2) + paper.fill_circle( + x + math.sin((math.pi * 2) / n) * 32 * e1, + padding + (height - max_radius - padding * 2) * e1, + min_radius + (max_radius - min_radius) * e1, + color + ) end paper.update() diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index deae97f..dcb39fc 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -2,5 +2,6 @@ idf_component_register( SRCS "inkpot.c" "paper.c" + "server.c" INCLUDE_DIRS "." ) diff --git a/main/inkpot.c b/main/inkpot.c index f7b34cd..c143631 100644 --- a/main/inkpot.c +++ b/main/inkpot.c @@ -15,6 +15,7 @@ #include #include "paper.h" +#include "server.h" #define WIFI_SCAN_LIST_SIZE 10 #define LUA_FILE_PATH "/assets" @@ -96,7 +97,7 @@ void run_lua_file(const char *file_name, const char *test_name) { } // Function to run an embedded Lua script -void run_embedded_lua_test(const char *lua_script, const char *test_name) { +void run_lua_string(const char *lua_script, const char *test_name) { ESP_LOGI(TAG, "Starting Lua test: %s", test_name); log_memory_usage("Start of test"); @@ -176,19 +177,96 @@ void scan_wifi_networks(void) { ESP_LOGI(TAG, "Wi-Fi scan completed."); } -void app_main(void) { - +// HTTP Server +#define WRITE_HEADER(req, buffer, name, format, src) \ + sprintf(buffer, format, src); \ + ESP_ERROR_CHECK(httpd_resp_set_hdr(req, name, buffer)); +static esp_err_t http_index(httpd_req_t* req) { + // TODO: Serve HTML file with form POSTing to `/draw` + const char* response = "Hello world!\n"; + httpd_resp_set_type(req, "text/plain"); + httpd_resp_set_status(req, "200"); + httpd_resp_send(req, response, HTTPD_RESP_USE_STRLEN); + return ESP_OK; +} +esp_err_t http_draw(httpd_req_t* req) { + // READING STREAM + int req_size = req->content_len; + char* content = (char*)heap_caps_malloc(req_size, MALLOC_CAP_SPIRAM); + if (content == NULL) { + char msg[50]; + sprintf(msg, "Failed to allocate %d chars\n", req_size); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, msg); + return ESP_ERR_INVALID_ARG; } + int current_pos = 0; + int amount_recieved; + while ((amount_recieved = httpd_req_recv(req, (content + current_pos), req_size)) > 0) { + ESP_LOGI(__FUNCTION__, "Read %d bytes\n", amount_recieved); + current_pos += amount_recieved; + } + if (amount_recieved < 0) { + char msg[50]; + heap_caps_free(content); + ESP_LOGE(msg, "Failed to read bytes. Error code %d\n", amount_recieved); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, msg); + return ESP_ERR_INVALID_ARG; + } + ESP_LOGI(__FUNCTION__, "Done reading %d bytes out of %d\n", current_pos, req_size); + // TODO: Error handling + run_lua_string(content, "E-Paper Script via HTTP"); + heap_caps_free(content); + // Done reading + char response[100]; + sprintf( + response, "script drawn!" + ); + httpd_resp_set_type(req, "text/plain"); + httpd_resp_set_status(req, "200"); + httpd_resp_send(req, response, HTTPD_RESP_USE_STRLEN); + return ESP_OK; +} + +void register_http_routes(httpd_handle_t server) { + { + httpd_uri_t uri + = { .uri = "/", .method = HTTP_GET, .handler = http_index, .user_ctx = NULL }; + httpd_register_uri_handler(server, &uri); + } + { + httpd_uri_t uri + = { .uri = "/draw", .method = HTTP_POST, .handler = http_draw, .user_ctx = NULL }; + httpd_register_uri_handler(server, &uri); + } +} + +// init + +void app_main(void) { // Initialize and mount the filesystem init_filesystem(); + // Initialize NVS, needed for wifi + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + // Set up HTTP server + httpd_handle_t server = get_server(); + if (server != NULL) { + register_http_routes(server); + } + // Run script in assets/epaper.lua - run_lua_file("epaper.lua", "E-Paper Script"); + run_lua_file("epaper.lua", "E-Paper Startup Script"); ESP_LOGI(TAG, "End of testing application."); diff --git a/main/server.c b/main/server.c new file mode 100644 index 0000000..f84a2e9 --- /dev/null +++ b/main/server.c @@ -0,0 +1,49 @@ +#include "server.h" +#include "settings.h" + +httpd_handle_t get_server(void); + +static void wifi_init_sta(void) { + // Initialize the ESP-NETIF + esp_netif_init(); + esp_event_loop_create_default(); + + // Create default event loop + esp_netif_create_default_wifi_sta(); + + // Initialize the Wi-Fi driver + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + // Set Wi-Fi mode to station + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + + // Configure Wi-Fi connection + wifi_config_t wifi_config = { + // TODO: Allow setting SSID at boot or build time + // For some reason I could not get https://cmake.org/cmake/help/latest/command/add_compile_definitions.html#command:add_compile_definitions to work + .sta = { + .ssid = WIFI_SSID, + .password = WIFI_PASS, + }, + }; + + // Set the Wi-Fi configuration + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_connect()); +} + +httpd_handle_t start_webserver(void) { + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.lru_purge_enable = true; + + httpd_handle_t server = NULL; + ESP_ERROR_CHECK(httpd_start(&server, &config)); + return server; +} + +httpd_handle_t get_server(void) { + wifi_init_sta(); + return start_webserver(); +} diff --git a/main/server.h b/main/server.h new file mode 100644 index 0000000..d6413b2 --- /dev/null +++ b/main/server.h @@ -0,0 +1,19 @@ +#ifndef SERVER_H_ +#define SERVER_H_ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_wifi.h" +#include "esp_netif.h" +#include "esp_http_server.h" + +// #include "settings.h" + +httpd_handle_t get_server(void); + +#endif // SERVER_H_ diff --git a/main/settings.h b/main/settings.h new file mode 100644 index 0000000..a1db51c --- /dev/null +++ b/main/settings.h @@ -0,0 +1,7 @@ +#ifndef SETTINGS_H_ +#define SETTINGS_H_ + +#define WIFI_SSID "" +#define WIFI_PASS "" + +#endif // SETTINGS_H_