Skip to content

Commit

Permalink
Example: HTTP Server
Browse files Browse the repository at this point in the history
Runs an HTTP server that will draw images on the screen.
Useful for setting up a small digital frame that can be remotely controlled.
  • Loading branch information
BackSlasher committed Nov 2, 2024
1 parent 2591847 commit 424de70
Show file tree
Hide file tree
Showing 13 changed files with 3,381 additions and 0 deletions.
6 changes: 6 additions & 0 deletions examples/http-server/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.5)

set(EXTRA_COMPONENT_DIRS "../../")

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(http-server)
34 changes: 34 additions & 0 deletions examples/http-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# HTTP server

Runs an HTTP server that will draw images on the screen.
Useful for setting up a small digital frame that can be remotely controlled.

## Config
1. In `main/server.h`, edit `WIFI_SSID` and `WIFI_PASSWORD` to match your wifi config
2. In `main/main.c`, edit `n_epd_setup` to refer to the right EPD screen

## Running
flash (`idf.py flash`), then connect the EPDiy to a power source (computer is fine).
The endpoints are:
1. `GET /`, prints the screen temp / height / width as headers
2. `POST /clear`, clears the screen
3. `POST /draw`, expects:
1. a body that is a binary stream already encoded to EPDiy's standards (like the one in `dragon.h`).
2. Headers `width`, `height`
3. Optional headers `x`,`y` (default to 0)
4. Optional header `clear`, if set to nonzero integer will force-clear the screen before drawing

## Helper script
`send_image.py` is a friendlier client.
```bash
$ ./send_image.py ESP_IP info
EpdInfo(width=1024, height=768, temperature=20)

$ ./send_image.py ESP_IP clear
# Clears the screen

$ ./send_image.y ESP_IP draw /tmp/spooder-man.png
# Draws on screen
```
Thanks to argparse, all arguments are visible with `--help`.
Requires `requests` and `PIL` (or Pillow)
4 changes: 4 additions & 0 deletions examples/http-server/component.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
8 changes: 8 additions & 0 deletions examples/http-server/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
set(
app_sources "epd.c" "server.c" "main.c"
)

idf_component_register(
SRCS ${app_sources}
REQUIRES epdiy esp_wifi nvs_flash esp_http_server esp_netif
)
44 changes: 44 additions & 0 deletions examples/http-server/main/epd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "epd.h"

static EpdiyHighlevelState hl;
static EpdData data;

static inline void checkError(enum EpdDrawError err) {
if (err != EPD_DRAW_SUCCESS) {
ESP_LOGE("demo", "draw error: %X", err);
}
}

EpdData n_epd_data() {
return data;
}

void n_epd_setup(const EpdDisplay_t* display) {
epd_init(&epd_board_v7, display, EPD_LUT_64K);
epd_set_vcom(1560);
hl = epd_hl_init(EPD_BUILTIN_WAVEFORM);
epd_set_rotation(EPD_ROT_LANDSCAPE);
data.width = epd_rotated_display_width();
data.height = epd_rotated_display_height();
data.temperature = epd_ambient_temperature();
}

void n_epd_clear() {
epd_poweron();
epd_fullclear(&hl, data.temperature);
epd_poweroff();
}

void n_epd_draw(uint8_t* content, int x, int y, int width, int height) {
uint8_t* fb = epd_hl_get_framebuffer(&hl);
EpdRect area = {
.x = x,
.y = y,
.width = width,
.height = height,
};
epd_draw_rotated_image(area, content, fb);
epd_poweron();
checkError(epd_hl_update_screen(&hl, MODE_GC16, data.temperature));
epd_poweroff();
}
17 changes: 17 additions & 0 deletions examples/http-server/main/epd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef EPD_H
#define EPD_H

#include "epdiy.h"
#include <esp_log.h>

typedef struct {
int width;
int height;
int temperature;
} EpdData;

EpdData n_epd_data();
void n_epd_setup();
void n_epd_clear();
void n_epd_draw(uint8_t* content, int x, int y, int width, int height);
#endif /* EPD_H */
147 changes: 147 additions & 0 deletions examples/http-server/main/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#include "server.h"
#include "esp_http_server.h"
#include "epd.h"
#include "esp_heap_caps.h"

#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) {
EpdData data = n_epd_data();
char width[20], height[20], temperature[20];
WRITE_HEADER(req, width, "width", "%d", data.width);
WRITE_HEADER(req, height, "height", "%d", data.height);
WRITE_HEADER(req, temperature, "temperature", "%d", data.temperature);
const char* response = "Hello! Check headers\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_clear(httpd_req_t* req) {
ESP_LOGI(__FUNCTION__, "Clear\n");
n_epd_clear();
const char* response = "Cleared\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) {
// optional headers: x,y. Default to 0
// required headers: height, width
// Content should be a stream of special bytes - we're reading 4 bits at a time.
int x, y, width, height, clear;
char header[20];
memset(header, 0, 20);
if (httpd_req_get_hdr_value_str(req, "clear", header, 20) == ESP_OK) {
sscanf(header, "%d", &clear);
} else {
clear = 0;
}
if (httpd_req_get_hdr_value_str(req, "x", header, 20) == ESP_OK) {
sscanf(header, "%d", &x);
} else {
x = 0;
}
if (httpd_req_get_hdr_value_str(req, "y", header, 20) == ESP_OK) {
sscanf(header, "%d", &y);
} else {
y = 0;
}
if (httpd_req_get_hdr_value_str(req, "width", header, 20) == ESP_OK) {
sscanf(header, "%d", &width);
} else {
char response[60];
sprintf(response, "Missing header width");
httpd_resp_set_type(req, "text/plain");
httpd_resp_set_status(req, "400");
httpd_resp_send(req, response, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
if (httpd_req_get_hdr_value_str(req, "height", header, 20) == ESP_OK) {
sscanf(header, "%d", &height);
} else {
char response[60];
sprintf(response, "Missing header height");
httpd_resp_set_type(req, "text/plain");
httpd_resp_set_status(req, "400");
httpd_resp_send(req, response, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}

// 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];
free(content);
ESP_LOGE(msg, "Failed to read byets. 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);

if (clear) {
n_epd_clear();
}
n_epd_draw(((uint8_t*)content), x, y, width, height);

// Done reading
char response[100];
sprintf(
response, "x %d, y %d, width %d, height %d, byte count %d\n", x, y, width, height, req_size
);
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_paths(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 = "/clear", .method = HTTP_POST, .handler = http_clear, .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);
}
}

void app_main(void) {
// 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);

httpd_handle_t server = get_server();
if (server != NULL) {
register_paths(server);
}
n_epd_setup(&ED060XC3);
}
46 changes: 46 additions & 0 deletions examples/http-server/main/server.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "server.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 = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASSWORD,
},
};

// 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();
}
20 changes: 20 additions & 0 deletions examples/http-server/main/server.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef SERVER_H
#define SERVER_H

#include <stdio.h>
#include <string.h>
#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"

#define WIFI_SSID "best ssid" // Replace with your Wi-Fi SSID
#define WIFI_PASSWORD "great password much wow" // Replace with your Wi-Fi password

httpd_handle_t get_server(void);

#endif /* SERVER_H */
Empty file.
Loading

0 comments on commit 424de70

Please sign in to comment.