Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example: HTTP Server #368

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 */
149 changes: 149 additions & 0 deletions examples/http-server/main/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#include "server.h"
#include "esp_http_server.h"
#include "epd.h"
#include "esp_heap_caps.h"
#include "settings.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);
BackSlasher marked this conversation as resolved.
Show resolved Hide resolved
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 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);
heap_caps_free(content);

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

#include "settings.h"

httpd_handle_t get_server(void);

#endif /* SERVER_H */
9 changes: 9 additions & 0 deletions examples/http-server/main/settings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef SETTINGS_H
#define SETTINGS_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

#define SCREEN_MODEL ED060XC3

#endif /* SETTINGS_H */
Loading