Compare commits
3 Commits
Author | SHA1 | Date |
---|---|---|
Sara | 46011ea8d9 | |
Sara | afb521f7a8 | |
Sara | f6b5d577e1 |
17
api-doc.txt
17
api-doc.txt
|
@ -1,8 +1,21 @@
|
||||||
The server listens on '/' for GET queries with a url query format like
|
The server listens on '/' for GET queries with a url query format like
|
||||||
?l=*&r0=*&g0=*&b0=*&a0=*&t0=* ... &rl=*&gl=*&bl=*&al=*&tl=*
|
?l=*&r0=*&g0=*&b0=*&a0=*&t0=* ... &rl=*&gl=*&bl=*&al=*&tl=*
|
||||||
|
|
||||||
Where l is the number of points on a gradient. And each point of the gradient has a r* g* b* a* and t* where * is the index.
|
Where l is the number of points on a gradient. And each point of the gradient has a r* g* b* a* and t* where * is the index.
|
||||||
|
|
||||||
r g and b are the red green and blue 8-bit colour components of a point on the gradient. A is the 5-bit global component of the led at that point.
|
r g and b are the red green and blue 8-bit colour components of a point on the gradient. A is the 5-bit global component of the led at that point.
|
||||||
t is the offset from the start measured in leds.
|
t is the offset from the start measured in leds.
|
||||||
|
|
||||||
Each point also has an optional &m 'movement' argument. &m should be either -1, +1 or 0 and represents the movement per frame or the point.
|
Each point also has an optional &m 'movement' argument. &m should be either -1, +1 or 0 and represents the movement per frame or the point.
|
||||||
A point on the gradient can have an &m parameter, and has to have a &r, &g, &b, &a, and &t.
|
When no movement is set, 0 (no movement) is implied.
|
||||||
The whole gradient can have a duration &d parameter and must have a length &l
|
|
||||||
|
When movement is set, a &e 'edge' component can also be set for that point. &e defines what the point will do once it reaches an end of the strip.
|
||||||
|
Edge should be one of
|
||||||
|
w to wrap to the opposite end of the strip.
|
||||||
|
r to reverse direction
|
||||||
|
s to stop
|
||||||
|
|
||||||
|
If no &e is specified, s is implied.
|
||||||
|
|
||||||
|
A point on the gradient may have a movement &m and edge &e parameter, and has to have a &r, &g, &b, &a, and &t.
|
||||||
|
The whole gradient may have a duration &d parameter and must have a length &l
|
||||||
|
|
|
@ -0,0 +1,255 @@
|
||||||
|
#include "leds.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "shared.h"
|
||||||
|
#include "esp8266/gpio_register.h"
|
||||||
|
#include "rom/ets_sys.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
|
uint32_t g_serial_out_buffer[122];
|
||||||
|
Led* g_leds = ((Led*)g_serial_out_buffer + 1);
|
||||||
|
|
||||||
|
Gradient g_default_gradient;
|
||||||
|
Gradient g_current_gradient;
|
||||||
|
int g_leds_are_default = 1;
|
||||||
|
LedsSendStatus g_leds_send_state = LEDS_SEND_WAITING;
|
||||||
|
|
||||||
|
SemaphoreHandle_t g_led_mutex; // mutex governing access to data for leds
|
||||||
|
// use for all global data defined in this header
|
||||||
|
|
||||||
|
LedThreadData g_led_thread_data = {
|
||||||
|
.task = 0,
|
||||||
|
.func = &leds_thread
|
||||||
|
};
|
||||||
|
|
||||||
|
/// =======================================
|
||||||
|
// leds.h internal helper functions
|
||||||
|
/// =======================================
|
||||||
|
|
||||||
|
static inline
|
||||||
|
uint8_t lerp_uint8(uint8_t a, uint8_t b, float t) {
|
||||||
|
if(t <= 0) return a;
|
||||||
|
else if(t >= 1.0) return b;
|
||||||
|
else {
|
||||||
|
int dir = b - a;
|
||||||
|
return a + dir * t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void lerp_led(Led* out, const Led* from, const Led* to, float t) {
|
||||||
|
out->components.red = lerp_uint8(from->components.red, to->components.red, t);
|
||||||
|
out->components.green = lerp_uint8(from->components.green, to->components.green, t);
|
||||||
|
out->components.blue = lerp_uint8(from->components.blue, to->components.blue, t);
|
||||||
|
uint8_t glob_from = from->components.global & ~0xE0;
|
||||||
|
uint8_t glob_to = to->components.global & ~0xE0;
|
||||||
|
out->components.global = GLOBAL(lerp_uint8(glob_from, glob_to, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void lerp_points_between(const GradientPoint from, const GradientPoint to) {
|
||||||
|
const int dif = to.offset - from.offset;
|
||||||
|
float t = 0.f;
|
||||||
|
for(int led = from.offset; led <= to.offset; ++led) {
|
||||||
|
t = (float)(led - from.offset) / (float)dif;
|
||||||
|
lerp_led(g_leds + led, &from.led, &to.led, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void leds_config_gpio() {
|
||||||
|
gpio_config_t config = {
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = 0x18030,
|
||||||
|
.pull_up_en = 0,
|
||||||
|
.pull_down_en = 0,
|
||||||
|
};
|
||||||
|
gpio_config(&config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void serial_write(int high) {
|
||||||
|
// set clock out to high, triggering a rising edge
|
||||||
|
gpio_set_level(CLOCK, 1);
|
||||||
|
// write bit to data out
|
||||||
|
gpio_set_level(DATA, high);
|
||||||
|
|
||||||
|
ets_delay_us(1);
|
||||||
|
|
||||||
|
// set clock to low, triggering a falling edge, shifting the LEDs shift register
|
||||||
|
gpio_set_level(CLOCK, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// =======================================
|
||||||
|
// leds.h external interface functions
|
||||||
|
/// =======================================
|
||||||
|
|
||||||
|
|
||||||
|
void send_leds() {
|
||||||
|
// index of the bit being written
|
||||||
|
// fixed point number where the first 5 bits are the bit of a 32bit integger, and the rest is the integer
|
||||||
|
int write_bit = 0;
|
||||||
|
int write_next = 0;
|
||||||
|
|
||||||
|
gpio_set_level(CLOCK, 0);
|
||||||
|
|
||||||
|
while(write_bit < sizeof(g_serial_out_buffer) * 8) {
|
||||||
|
// fetch the bit being addressed
|
||||||
|
write_next = 0x1 & (g_serial_out_buffer[write_bit >> 5] >> (32 - (write_bit & 0x1F)));
|
||||||
|
|
||||||
|
serial_write(write_next);
|
||||||
|
|
||||||
|
write_bit++;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_set_level(CLOCK, 0);
|
||||||
|
gpio_set_level(DATA, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_led_range(int start, int end, Led value) {
|
||||||
|
for(int i = start; i < end; ++i) {
|
||||||
|
g_leds[i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void leds_set_default_gradient(const Gradient* gradient) {
|
||||||
|
g_default_gradient = *gradient;
|
||||||
|
}
|
||||||
|
|
||||||
|
void leds_set_current_gradient(const Gradient* gradient, int defer_send) {
|
||||||
|
GradientPoint from = gradient->points[0];
|
||||||
|
GradientPoint to;
|
||||||
|
|
||||||
|
set_led_range(0, gradient->points[0].offset, gradient->points[0].led);
|
||||||
|
set_led_range(gradient->points[gradient->points_len-1].offset, 120, gradient->points[gradient->points_len-1].led);
|
||||||
|
|
||||||
|
g_current_gradient = *gradient;
|
||||||
|
|
||||||
|
for(int i = 1; i < gradient->points_len; ++i) {
|
||||||
|
to = gradient->points[i];
|
||||||
|
lerp_points_between(from, to);
|
||||||
|
from = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!defer_send) {
|
||||||
|
send_leds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void leds_reset_gradient(int defer_send) {
|
||||||
|
leds_set_current_gradient(&g_default_gradient, defer_send);
|
||||||
|
}
|
||||||
|
|
||||||
|
// swap to ranges of memory using a temporary block
|
||||||
|
static
|
||||||
|
void memswap(void* d, void* s, size_t n) {
|
||||||
|
void* tmp = malloc(n);
|
||||||
|
memcpy(tmp, s, n);
|
||||||
|
memcpy(s, d, n);
|
||||||
|
memcpy(d, tmp, n);
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void led_check_edge(GradientPoint* point) {
|
||||||
|
int offset = clamp(point->offset, 0, 120);
|
||||||
|
switch(point->edge_action) {
|
||||||
|
default:
|
||||||
|
case LEDS_EDGE_WRAP:
|
||||||
|
if(point->offset < 0) {
|
||||||
|
point->offset += 120;
|
||||||
|
} else if(point->offset >= 120) {
|
||||||
|
point->offset %= 120;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LEDS_EDGE_REVERSE: {
|
||||||
|
if(offset != point->offset) {
|
||||||
|
point->offset = offset;
|
||||||
|
point->movement *= -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LEDS_EDGE_STOP:
|
||||||
|
point->offset = clamp(point->offset, 0, 120);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void leds_animate() {
|
||||||
|
for(size_t i = 0; i < g_current_gradient.points_len; ++i) {
|
||||||
|
// The gradient point at i
|
||||||
|
GradientPoint* point = g_current_gradient.points + i;
|
||||||
|
// move towards end
|
||||||
|
if(point->movement > 0) {
|
||||||
|
++point->offset;
|
||||||
|
// without moving past it
|
||||||
|
led_check_edge(point);
|
||||||
|
// swap with next point if we pass it
|
||||||
|
if(point->offset > (point+1)->offset) {
|
||||||
|
memswap(point, point+1, sizeof(GradientPoint));
|
||||||
|
}
|
||||||
|
// move towards start
|
||||||
|
} else if(point->movement < 0) {
|
||||||
|
++point->offset;
|
||||||
|
// without passing it
|
||||||
|
led_check_edge(point);
|
||||||
|
|
||||||
|
// swap with previous point if we fall below it
|
||||||
|
if(point->offset < (point-1)->offset) {
|
||||||
|
memswap(point, point-1, sizeof(GradientPoint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leds_set_current_gradient(&g_current_gradient, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void leds_thread() {
|
||||||
|
send_leds();
|
||||||
|
float timer = 0;
|
||||||
|
for(;;) {
|
||||||
|
// wait for 10 milliseconds,
|
||||||
|
// giving FreeRTOS time to run other tasks
|
||||||
|
vTaskDelay(10 / portTICK_RATE_MS);
|
||||||
|
// tick timer by 10ms
|
||||||
|
timer += 0.01;
|
||||||
|
xSemaphoreTake(g_led_mutex, portMAX_DELAY);
|
||||||
|
{
|
||||||
|
send_leds();
|
||||||
|
leds_animate();
|
||||||
|
|
||||||
|
// reset gradient, defer send until next frame
|
||||||
|
if(timer > g_current_gradient.duration) {
|
||||||
|
timer = 0.f;
|
||||||
|
leds_reset_gradient(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xSemaphoreGive(g_led_mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void leds_init() {
|
||||||
|
g_serial_out_buffer[0] = 0u;
|
||||||
|
g_serial_out_buffer[61] = ~0u;
|
||||||
|
set_led_range(0, 120,
|
||||||
|
(Led){.components =
|
||||||
|
(LedComponents) {
|
||||||
|
.red = 0,
|
||||||
|
.green = 0,
|
||||||
|
.blue = 0,
|
||||||
|
.global = GLOBAL(5)
|
||||||
|
}}
|
||||||
|
);
|
||||||
|
|
||||||
|
g_led_mutex = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
|
xTaskCreate(g_led_thread_data.func, "Leds", 1024, NULL, 1, &g_led_thread_data.task);
|
||||||
|
|
||||||
|
// initialize mutex for leds data
|
||||||
|
leds_config_gpio();
|
||||||
|
}
|
265
main/leds.h
265
main/leds.h
|
@ -8,259 +8,80 @@
|
||||||
#define _potion_leds_h
|
#define _potion_leds_h
|
||||||
|
|
||||||
#include <FreeRTOS.h>
|
#include <FreeRTOS.h>
|
||||||
#include <stdint.h>
|
#include <freertos/task.h>
|
||||||
#include <stddef.h>
|
#include <freertos/semphr.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include "shared.h"
|
|
||||||
#include "esp8266/gpio_register.h"
|
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "rom/ets_sys.h"
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
|
|
||||||
enum leds_send_state_t {
|
typedef enum LedsSendStatus {
|
||||||
LEDS_SEND_WAITING,
|
LEDS_SEND_WAITING,
|
||||||
LEDS_SEND_REQUESTED,
|
LEDS_SEND_REQUESTED,
|
||||||
LEDS_SENDING,
|
LEDS_SENDING,
|
||||||
};
|
} LedsSendStatus;
|
||||||
|
|
||||||
|
typedef enum MoveEdgeAction {
|
||||||
|
LEDS_EDGE_WRAP,
|
||||||
|
LEDS_EDGE_REVERSE,
|
||||||
|
LEDS_EDGE_STOP
|
||||||
|
} MoveEdgeAction;
|
||||||
|
|
||||||
// pack the struct to match exactly 8 * 4 = 32bits
|
// pack the struct to match exactly 8 * 4 = 32bits
|
||||||
struct __attribute__((__packed__)) led_components_t {
|
typedef struct __attribute__((__packed__)) LedComponents {
|
||||||
// RGB component values
|
// RGB component values
|
||||||
uint8_t red;
|
uint8_t red;
|
||||||
uint8_t green;
|
uint8_t green;
|
||||||
uint8_t blue;
|
uint8_t blue;
|
||||||
uint8_t global; // global baseline brightness, highest 3 bits should always be ones
|
uint8_t global; // global baseline brightness, highest 3 bits should always be ones
|
||||||
};
|
} LedComponents;
|
||||||
|
|
||||||
// union of components and their representation as a u32
|
// union of components and their representation as a u32
|
||||||
// allows for easier sending of data over serial
|
// allows for easier sending of data over serial
|
||||||
union led_t {
|
typedef union Led {
|
||||||
struct led_components_t components;
|
struct LedComponents components;
|
||||||
uint32_t bits;
|
uint32_t bits;
|
||||||
};
|
} Led;
|
||||||
|
|
||||||
// point on a gradient
|
// point on a gradient
|
||||||
struct gradient_point_t {
|
typedef struct GradientPoint {
|
||||||
union led_t led; // value of the led at this point
|
union Led led; // value of the led at this point
|
||||||
size_t offset; // offset (measured in leds) from the beginning
|
int offset; // offset (measured in leds) from the beginning
|
||||||
short movement; // direction of movement over time
|
short movement; // direction of movement over time
|
||||||
};
|
MoveEdgeAction edge_action; // what to do when movement hits an end of the strip
|
||||||
|
} GradientPoint;
|
||||||
|
|
||||||
struct gradient_t {
|
typedef struct Gradient {
|
||||||
struct gradient_point_t points[16]; // array of gradient points, support at most 16 points in a gradient
|
struct GradientPoint points[16]; // array of gradient points, support at most 16 points in a gradient
|
||||||
size_t points_len; // number of used gradient points
|
size_t points_len; // number of used gradient points
|
||||||
float duration; // amount of time to allow this gradient to last
|
float duration; // amount of time to allow this gradient to last
|
||||||
// positive means an amount in second 0 or negative means indefinitely until further notice
|
// positive means an amount in second 0 or negative means indefinitely until further notice
|
||||||
};
|
} Gradient;
|
||||||
|
|
||||||
|
typedef struct LedThreadData {
|
||||||
|
TaskHandle_t task;
|
||||||
|
TaskFunction_t func;
|
||||||
|
} LedThreadData;
|
||||||
|
|
||||||
// buffer that will be written out to the led strip over serial
|
// buffer that will be written out to the led strip over serial
|
||||||
uint32_t g_serial_out_buffer[122];
|
extern uint32_t g_serial_out_buffer[122];
|
||||||
// 120-long slice of the out buffer that represents the first few leds
|
// 120-long slice of the out buffer that represents the first few leds
|
||||||
union led_t* g_leds = ((union led_t*)g_serial_out_buffer + 1);
|
extern Led* g_leds;
|
||||||
|
extern Gradient g_default_gradient;
|
||||||
struct gradient_t g_default_gradient;
|
extern Gradient g_current_gradient;
|
||||||
struct gradient_t g_current_gradient;
|
extern int g_leds_are_default;
|
||||||
int g_leds_are_default = 1;
|
extern enum LedsSendStatus g_leds_send_state;
|
||||||
enum leds_send_state_t g_leds_send_state = LEDS_SEND_WAITING;
|
extern SemaphoreHandle_t g_led_mutex;
|
||||||
|
extern LedThreadData g_led_thread_data;
|
||||||
SemaphoreHandle_t g_led_mutex; // mutex governing access to data for leds
|
|
||||||
// use for all global data defined in this header
|
|
||||||
|
|
||||||
#define CLOCK 4
|
#define CLOCK 4
|
||||||
#define DATA 5
|
#define DATA 5
|
||||||
|
|
||||||
static
|
extern void send_leds();
|
||||||
void leds_config_gpio() {
|
|
||||||
gpio_config_t config = {
|
|
||||||
.intr_type = GPIO_INTR_DISABLE,
|
|
||||||
.mode = GPIO_MODE_OUTPUT,
|
|
||||||
.pin_bit_mask = 0x18030,
|
|
||||||
.pull_up_en = 0,
|
|
||||||
.pull_down_en = 0,
|
|
||||||
};
|
|
||||||
gpio_config(&config);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
extern void set_led_range(int start, int end, Led value);
|
||||||
void serial_write(int high) {
|
extern void leds_set_default_gradient(const Gradient* gradient);
|
||||||
// set clock out to high, triggering a rising edge
|
extern void leds_set_current_gradient(const Gradient* gradient, int defer_send);
|
||||||
gpio_set_level(CLOCK, 1);
|
extern void leds_reset_gradient(int defer_send);
|
||||||
// write bit to data out
|
|
||||||
gpio_set_level(DATA, high);
|
|
||||||
|
|
||||||
ets_delay_us(1);
|
extern void leds_thread();
|
||||||
|
extern void leds_init();
|
||||||
// set clock to low, triggering a falling edge, shifting the LEDs shift register
|
|
||||||
gpio_set_level(CLOCK, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
void send_leds() {
|
|
||||||
// index of the bit being written
|
|
||||||
// fixed point number where the first 5 bits are the bit of a 32bit integger, and the rest is the integer
|
|
||||||
int write_bit = 0;
|
|
||||||
int write_next = 0;
|
|
||||||
|
|
||||||
gpio_set_level(CLOCK, 0);
|
|
||||||
|
|
||||||
while(write_bit < sizeof(g_serial_out_buffer) * 8) {
|
|
||||||
// fetch the bit being addressed
|
|
||||||
write_next = 0x1 & (g_serial_out_buffer[write_bit >> 5] >> (32 - (write_bit & 0x1F)));
|
|
||||||
|
|
||||||
serial_write(write_next);
|
|
||||||
|
|
||||||
write_bit++;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpio_set_level(CLOCK, 0);
|
|
||||||
gpio_set_level(DATA, 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline
|
|
||||||
uint8_t lerp_uint8(uint8_t a, uint8_t b, float t) {
|
|
||||||
if(t <= 0) return a;
|
|
||||||
else if(t >= 1.0) return b;
|
|
||||||
else {
|
|
||||||
int dir = b - a;
|
|
||||||
return a + dir * t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline
|
|
||||||
void lerp_led(union led_t* out, const union led_t* from, const union led_t* to, float t) {
|
|
||||||
out->components.red = lerp_uint8(from->components.red, to->components.red, t);
|
|
||||||
out->components.green = lerp_uint8(from->components.green, to->components.green, t);
|
|
||||||
out->components.blue = lerp_uint8(from->components.blue, to->components.blue, t);
|
|
||||||
uint8_t glob_from = from->components.global & ~0xE0;
|
|
||||||
uint8_t glob_to = to->components.global & ~0xE0;
|
|
||||||
out->components.global = GLOBAL(lerp_uint8(glob_from, glob_to, t));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline
|
|
||||||
void lerp_points_between(const struct gradient_point_t from, const struct gradient_point_t to) {
|
|
||||||
const int dif = to.offset - from.offset;
|
|
||||||
float t = 0.f;
|
|
||||||
for(int led = from.offset; led <= to.offset; ++led) {
|
|
||||||
t = (float)(led - from.offset) / (float)dif;
|
|
||||||
lerp_led(g_leds + led, &from.led, &to.led, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
void set_led_range(int start, int end, union led_t value) {
|
|
||||||
for(int i = start; i < end; ++i) {
|
|
||||||
g_leds[i] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
void leds_set_default_gradient(const struct gradient_t* gradient) {
|
|
||||||
g_default_gradient = *gradient;
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
void leds_set_current_gradient(const struct gradient_t* gradient, int defer_send) {
|
|
||||||
struct gradient_point_t from = gradient->points[0];
|
|
||||||
struct gradient_point_t to;
|
|
||||||
|
|
||||||
set_led_range(0, gradient->points[0].offset, gradient->points[0].led);
|
|
||||||
set_led_range(gradient->points[gradient->points_len-1].offset, 120, gradient->points[gradient->points_len-1].led);
|
|
||||||
|
|
||||||
g_current_gradient = *gradient;
|
|
||||||
|
|
||||||
for(int i = 1; i < gradient->points_len; ++i) {
|
|
||||||
to = gradient->points[i];
|
|
||||||
lerp_points_between(from, to);
|
|
||||||
from = to;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!defer_send) {
|
|
||||||
send_leds();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void memswap(void* d, void* s, size_t n) {
|
|
||||||
void* tmp = malloc(n);
|
|
||||||
memcpy(tmp, s, n);
|
|
||||||
memcpy(s, d, n);
|
|
||||||
memcpy(d, tmp, n);
|
|
||||||
free(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void leds_animate() {
|
|
||||||
for(size_t i = 0; i < g_current_gradient.points_len; ++i) {
|
|
||||||
struct gradient_point_t* point = g_current_gradient.points + i;
|
|
||||||
if(point->movement > 0) {
|
|
||||||
point->offset = min(point->offset + 1, 120);
|
|
||||||
LOGLN("move result %i", point->offset);
|
|
||||||
LOGLN("next %d", (point+1)->offset);
|
|
||||||
|
|
||||||
if(point->offset > (point+1)->offset) {
|
|
||||||
memswap(point, point+1, sizeof(struct gradient_point_t));
|
|
||||||
LOGLN("swap down");
|
|
||||||
}
|
|
||||||
} else if(point->movement < 0) {
|
|
||||||
point->offset = max(0, point->offset - 1);
|
|
||||||
LOGLN("move result %i", point->offset);
|
|
||||||
LOGLN("next %d", (point-1)->offset);
|
|
||||||
|
|
||||||
if(point->offset < (point-1)->offset) {
|
|
||||||
memswap(point, point-1, sizeof(struct gradient_point_t));
|
|
||||||
LOGLN("swap up");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
leds_set_current_gradient(&g_current_gradient, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct led_thread_data_t {
|
|
||||||
TaskHandle_t task;
|
|
||||||
TaskFunction_t func;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void leds_thread();
|
|
||||||
|
|
||||||
static struct led_thread_data_t _thread_data = {
|
|
||||||
.task = 0,
|
|
||||||
.func = &leds_thread
|
|
||||||
};
|
|
||||||
|
|
||||||
static
|
|
||||||
void leds_thread() {
|
|
||||||
send_leds();
|
|
||||||
for(;;) {
|
|
||||||
vTaskDelay(10);
|
|
||||||
xSemaphoreTake(g_led_mutex, portMAX_DELAY);
|
|
||||||
send_leds();
|
|
||||||
leds_animate();
|
|
||||||
xSemaphoreGive(g_led_mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static
|
|
||||||
void leds_init() {
|
|
||||||
g_serial_out_buffer[0] = 0u;
|
|
||||||
g_serial_out_buffer[61] = ~0u;
|
|
||||||
set_led_range(0, 120,
|
|
||||||
(union led_t){.components =
|
|
||||||
(struct led_components_t) {
|
|
||||||
.red = 0,
|
|
||||||
.green = 0,
|
|
||||||
.blue = 0,
|
|
||||||
.global = GLOBAL(5)
|
|
||||||
}}
|
|
||||||
);
|
|
||||||
|
|
||||||
g_led_mutex = xSemaphoreCreateMutex();
|
|
||||||
|
|
||||||
xTaskCreate(_thread_data.func, "Leds", 1024, NULL, 1, &_thread_data.task);
|
|
||||||
|
|
||||||
// initialize mutex for leds data
|
|
||||||
leds_config_gpio();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !_leds_h
|
#endif // !_leds_h
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
|
#include "shared.h"
|
||||||
|
#include "parse.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_wifi.h>
|
||||||
|
#include <esp_wifi_types.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
|
||||||
|
static
|
||||||
|
void on_station_connects(wifi_event_ap_staconnected_t* event) {
|
||||||
|
LOGLN("Station %d connected", event->aid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void on_station_disconnects(wifi_event_ap_stadisconnected_t* event) {
|
||||||
|
LOGLN("Station %d disconnected", event->aid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void on_wifi_event(void* arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void* event_data) {
|
||||||
|
switch(event_id) {
|
||||||
|
case WIFI_EVENT_AP_STACONNECTED:
|
||||||
|
on_station_connects(event_data);
|
||||||
|
break;
|
||||||
|
case WIFI_EVENT_AP_STADISCONNECTED:
|
||||||
|
on_station_disconnects(event_data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wifi_init() {
|
||||||
|
LOGLN("Configuring WIFI");
|
||||||
|
// init tcp/ip stack
|
||||||
|
tcpip_adapter_init();
|
||||||
|
// initialize wifi
|
||||||
|
wifi_init_config_t wifi_startup = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_init(&wifi_startup));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &on_wifi_event, NULL));
|
||||||
|
LOGLN("WiFi initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
void softap_init() {
|
||||||
|
LOGLN("Starting wireless Access Point");
|
||||||
|
// set mode to wifi
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
||||||
|
|
||||||
|
// configure wifi hardware to serve as a wifi access point
|
||||||
|
wifi_config_t accesspoint_startup_config = {
|
||||||
|
.ap = {
|
||||||
|
.ssid = SSID,
|
||||||
|
.ssid_len = strlen(SSID),
|
||||||
|
.password = PASSW,
|
||||||
|
.max_connection=32,
|
||||||
|
.authmode = WIFI_AUTH_WPA_WPA2_PSK
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// configure with created startup config
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &accesspoint_startup_config));
|
||||||
|
// start wifi access point
|
||||||
|
ESP_ERROR_CHECK(esp_wifi_start());
|
||||||
|
|
||||||
|
LOGLN("Opened AP SSID: \"%s\" PW: \"%s\"", SSID, PASSW);
|
||||||
|
}
|
|
@ -5,71 +5,10 @@
|
||||||
//
|
//
|
||||||
///
|
///
|
||||||
|
|
||||||
#ifndef _server_h
|
#ifndef _potion_party_network_h
|
||||||
#define _server_h
|
#define _potion_party_network_h
|
||||||
|
|
||||||
#include "esp_wifi_types.h"
|
void wifi_init();
|
||||||
#include "shared.h"
|
void softap_init();
|
||||||
#include "esp_event.h"
|
|
||||||
|
|
||||||
static
|
#endif
|
||||||
void on_station_connects(wifi_event_ap_staconnected_t* event) {
|
|
||||||
LOGLN("Station %d connected", event->aid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
void on_station_disconnects(wifi_event_ap_stadisconnected_t* event) {
|
|
||||||
LOGLN("Station %d disconnected", event->aid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
void on_wifi_event(void* arg, esp_event_base_t event_base,
|
|
||||||
int32_t event_id, void* event_data) {
|
|
||||||
switch(event_id) {
|
|
||||||
case WIFI_EVENT_AP_STACONNECTED:
|
|
||||||
on_station_connects(event_data);
|
|
||||||
break;
|
|
||||||
case WIFI_EVENT_AP_STADISCONNECTED:
|
|
||||||
on_station_disconnects(event_data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline
|
|
||||||
void wifi_init() {
|
|
||||||
LOGLN("Configuring WIFI");
|
|
||||||
// init tcp/ip stack
|
|
||||||
tcpip_adapter_init();
|
|
||||||
// initialize wifi
|
|
||||||
wifi_init_config_t wifi_startup = WIFI_INIT_CONFIG_DEFAULT();
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_init(&wifi_startup));
|
|
||||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &on_wifi_event, NULL));
|
|
||||||
LOGLN("WiFi initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline
|
|
||||||
void softap_init() {
|
|
||||||
LOGLN("Starting wireless Access Point");
|
|
||||||
// set mode to wifi
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
|
||||||
|
|
||||||
// configure wifi hardware to serve as a wifi access point
|
|
||||||
wifi_config_t accesspoint_startup_config = {
|
|
||||||
.ap = {
|
|
||||||
.ssid = SSID,
|
|
||||||
.ssid_len = strlen(SSID),
|
|
||||||
.password = PASSW,
|
|
||||||
.max_connection=32,
|
|
||||||
.authmode = WIFI_AUTH_WPA_WPA2_PSK
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// configure with created startup config
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &accesspoint_startup_config));
|
|
||||||
// start wifi access point
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_start());
|
|
||||||
|
|
||||||
LOGLN("Opened AP SSID: \"%s\" PW: \"%s\"", SSID, PASSW);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !_server_h
|
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
#include "shared.h"
|
||||||
|
#include "leds.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <esp_http_server.h>
|
||||||
|
|
||||||
|
MoveEdgeAction strtoea(const char* str) {
|
||||||
|
if(strcmp(str, "w")) {
|
||||||
|
return LEDS_EDGE_WRAP;
|
||||||
|
} else if(strcmp(str, "r")) {
|
||||||
|
return LEDS_EDGE_REVERSE;
|
||||||
|
} else {
|
||||||
|
return LEDS_EDGE_STOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse a URL query as described in api-doc.txt into a valid Gradient struct.
|
||||||
|
// If the gradient is invalid, the is_ok flag on the return value will be set, and error will be set to a message describing the problem.
|
||||||
|
Result parse_leds_query(char* query_string, size_t query_size) {
|
||||||
|
char query_value[16];
|
||||||
|
char query_key[3];
|
||||||
|
Gradient* gradient = malloc(sizeof(Gradient));
|
||||||
|
|
||||||
|
// Fetch the &l length parameter.
|
||||||
|
// Interpret as a positive integer number of points on the gradient.
|
||||||
|
if(httpd_query_key_value(query_string, "l", query_value, sizeof(query_value)) == ESP_OK) {
|
||||||
|
gradient->points_len = max(0, atoi(query_value));
|
||||||
|
} else {
|
||||||
|
free(gradient);
|
||||||
|
return PARSE_ERR("ERROR: Failed to find length parameter &l");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the &d 'duration' parameter from the query.
|
||||||
|
// Interpreted as a floating point number of seconds before returning to default state.
|
||||||
|
if(httpd_query_key_value(query_string, "d", query_value, sizeof(query_value)) == ESP_OK) {
|
||||||
|
gradient->duration = atof(query_string);
|
||||||
|
} else {
|
||||||
|
gradient->duration = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGLN("Reading %zu points of gradient query:", gradient->points_len);
|
||||||
|
LOGLN("duration: %f", gradient->duration);
|
||||||
|
|
||||||
|
// Get the gradient point components for every point that was promised by the &l parameter
|
||||||
|
for(int point = 0; point < gradient->points_len; ++point) {
|
||||||
|
// Get the r, g, and b components as 8 bit integers
|
||||||
|
sprintf(query_key, "r%d", point);
|
||||||
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) {
|
||||||
|
gradient->points[point].led.components.red = atoi(query_value);
|
||||||
|
} else {
|
||||||
|
free(gradient);
|
||||||
|
return PARSE_ERR("ERROR: Point missing red component &r.");
|
||||||
|
}
|
||||||
|
sprintf(query_key, "g%d", point);
|
||||||
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) {
|
||||||
|
gradient->points[point].led.components.green = atoi(query_value);
|
||||||
|
} else {
|
||||||
|
free(gradient);
|
||||||
|
return PARSE_ERR("ERROR: Point missing green component &g.");
|
||||||
|
}
|
||||||
|
sprintf(query_key, "b%d", point);
|
||||||
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
||||||
|
gradient->points[point].led.components.blue = atoi(query_value);
|
||||||
|
} else {
|
||||||
|
free(gradient);
|
||||||
|
return PARSE_ERR("ERROR: Point missing blue component &b.");
|
||||||
|
}
|
||||||
|
// Get the global variable, passed as alpha. Limited to 0-32 (a 5bit unsigned int)
|
||||||
|
// Make sure the most significant 3 bits are all ones
|
||||||
|
sprintf(query_key, "a%d", point);
|
||||||
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
||||||
|
gradient->points[point].led.components.global = GLOBAL((uint8_t)atoi(query_value));
|
||||||
|
} else {
|
||||||
|
free(gradient);
|
||||||
|
return PARSE_ERR("ERROR: Point missing alpha component &a.");
|
||||||
|
}
|
||||||
|
// Get the time of the gradient as a number ranging from 0 - 120.
|
||||||
|
// Interpreted as an integer offset from the first led to the last
|
||||||
|
sprintf(query_key, "t%d", point);
|
||||||
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
||||||
|
gradient->points[point].offset = clamp(0, 120, atoi(query_value));
|
||||||
|
} else {
|
||||||
|
free(gradient);
|
||||||
|
return PARSE_ERR("ERROR: Point missing time component &t.");
|
||||||
|
}
|
||||||
|
// Get the movement variable &m.
|
||||||
|
// Interpreted as an integer number from -1 to +1
|
||||||
|
sprintf(query_key, "m%d", point);
|
||||||
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
||||||
|
gradient->points[point].movement = clamp(-1, +1, atoi(query_value));
|
||||||
|
} else {
|
||||||
|
gradient->points[point].movement = 0;
|
||||||
|
}
|
||||||
|
sprintf(query_key, "e%d", point);
|
||||||
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
||||||
|
gradient->points[point].edge_action = strtoea(query_value);
|
||||||
|
} else {
|
||||||
|
gradient->points[point].edge_action = LEDS_EDGE_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log fetched fields
|
||||||
|
LOGLN("led[%d]:", point);
|
||||||
|
LOGLN(" r %d", gradient->points[point].led.components.red);
|
||||||
|
LOGLN(" g %d", gradient->points[point].led.components.green);
|
||||||
|
LOGLN(" b %d", gradient->points[point].led.components.blue);
|
||||||
|
LOGLN(" global %d", gradient->points[point].led.components.global >> 3);
|
||||||
|
// may show up as a compile error on modern computers,
|
||||||
|
// x86_64 size_t is usually an unsigned long int, on the ESP8266 it is an unsigned int
|
||||||
|
LOGLN(" t %u", gradient->points[point].offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PARSE_OK(gradient);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _potion_party_parse_h
|
||||||
|
#define _potion_party_parse_h
|
||||||
|
|
||||||
|
#include "shared.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
extern Result parse_leds_query(char* query_string, size_t query_size);
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,8 +3,13 @@
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "rom/ets_sys.h"
|
#include "rom/ets_sys.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_netif.h"
|
||||||
|
#include "esp_event.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// starts the basic subsystems we will be using
|
// starts the basic subsystems we will be using
|
||||||
|
@ -21,7 +26,7 @@ void TEST_leds() {
|
||||||
// TEST: after a delay, set the leds to a gradient of red - black
|
// TEST: after a delay, set the leds to a gradient of red - black
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
union led_t led = {
|
Led led = {
|
||||||
.components = {
|
.components = {
|
||||||
.red = 0,
|
.red = 0,
|
||||||
.green = 255,
|
.green = 255,
|
||||||
|
@ -37,12 +42,12 @@ void TEST_leds() {
|
||||||
|
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
struct gradient_t gradient;
|
Gradient gradient;
|
||||||
gradient.points_len = 4;
|
gradient.points_len = 4;
|
||||||
|
|
||||||
gradient.points[0].offset = 0;
|
gradient.points[0].offset = 0;
|
||||||
gradient.points[0].movement = 0;
|
gradient.points[0].movement = 0;
|
||||||
gradient.points[0].led.components = (struct led_components_t) {
|
gradient.points[0].led.components = (LedComponents) {
|
||||||
.global = GLOBAL(0),
|
.global = GLOBAL(0),
|
||||||
.red = 0,
|
.red = 0,
|
||||||
.green = 0,
|
.green = 0,
|
||||||
|
@ -51,7 +56,7 @@ void TEST_leds() {
|
||||||
|
|
||||||
gradient.points[1].offset = 59;
|
gradient.points[1].offset = 59;
|
||||||
gradient.points[1].movement = -1;
|
gradient.points[1].movement = -1;
|
||||||
gradient.points[1].led.components = (struct led_components_t){
|
gradient.points[1].led.components = (LedComponents){
|
||||||
.global = GLOBAL(10),
|
.global = GLOBAL(10),
|
||||||
.red = 40,
|
.red = 40,
|
||||||
.green = 200,
|
.green = 200,
|
||||||
|
@ -59,7 +64,7 @@ void TEST_leds() {
|
||||||
};
|
};
|
||||||
gradient.points[2].offset = 61;
|
gradient.points[2].offset = 61;
|
||||||
gradient.points[2].movement = 1;
|
gradient.points[2].movement = 1;
|
||||||
gradient.points[2].led.components = (struct led_components_t){
|
gradient.points[2].led.components = (LedComponents){
|
||||||
.global = GLOBAL(10),
|
.global = GLOBAL(10),
|
||||||
.red = 40,
|
.red = 40,
|
||||||
.green = 200,
|
.green = 200,
|
||||||
|
@ -67,7 +72,7 @@ void TEST_leds() {
|
||||||
};
|
};
|
||||||
gradient.points[3].offset = 120;
|
gradient.points[3].offset = 120;
|
||||||
gradient.points[3].movement = 0;
|
gradient.points[3].movement = 0;
|
||||||
gradient.points[3].led.components = (struct led_components_t){
|
gradient.points[3].led.components = (LedComponents){
|
||||||
.global = GLOBAL(0),
|
.global = GLOBAL(0),
|
||||||
.red = 0,
|
.red = 0,
|
||||||
.green = 0,
|
.green = 0,
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
#include "server.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "shared.h"
|
||||||
|
#include "leds.h"
|
||||||
|
#include "parse.h"
|
||||||
|
#include <esp_http_server.h>
|
||||||
|
#include <esp_system.h>
|
||||||
|
#include <esp_netif.h>
|
||||||
|
#include <sys_arch.h>
|
||||||
|
|
||||||
|
const char* http_response_ok = "OK!";
|
||||||
|
static httpd_handle_t g_http_server = NULL;
|
||||||
|
|
||||||
|
// convert a (valid) request as described in api-doc.txt to a gradient.
|
||||||
|
static
|
||||||
|
Result request_to_gradient(httpd_req_t* request) {
|
||||||
|
// buffer for query string
|
||||||
|
char* query_buffer;
|
||||||
|
size_t query_length = httpd_req_get_url_query_len(request) + 1;
|
||||||
|
|
||||||
|
Result result = { .error = NULL };
|
||||||
|
|
||||||
|
if(query_length > 1) {
|
||||||
|
query_buffer = malloc(query_length * sizeof(char));
|
||||||
|
if(httpd_req_get_url_query_str(request, query_buffer, query_length) == ESP_OK) {
|
||||||
|
LOGLN("Received query.");
|
||||||
|
result = parse_leds_query(query_buffer, query_length);
|
||||||
|
}
|
||||||
|
free(query_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// receives HTTP GET requests on root
|
||||||
|
static
|
||||||
|
esp_err_t on_http_get_root(httpd_req_t* request) {
|
||||||
|
const char* response_msg = http_response_ok;
|
||||||
|
LOGLN("GET received on '/next'.");
|
||||||
|
// convert the request url to a gradient
|
||||||
|
Result result = request_to_gradient(request);
|
||||||
|
// an error was returned, pass it on to the API caller
|
||||||
|
if(!result.is_ok) {
|
||||||
|
httpd_resp_set_status(request, "400 Bad Request");
|
||||||
|
response_msg = result.error;
|
||||||
|
} else {
|
||||||
|
// grab a lock on the leds data
|
||||||
|
xSemaphoreTake(g_led_mutex, portMAX_DELAY);
|
||||||
|
// modify leds data, defer sending for the leds thread to take care of
|
||||||
|
leds_set_current_gradient(result.ok, 1);
|
||||||
|
// release lock
|
||||||
|
xSemaphoreGive(g_led_mutex);
|
||||||
|
// request to gradient allocates the gradient on the heap,
|
||||||
|
// we can free that after use
|
||||||
|
free(result.ok);
|
||||||
|
}
|
||||||
|
// respond to http caller
|
||||||
|
httpd_resp_send(request, response_msg, strlen(response_msg));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
httpd_uri_t get_root_uri = {
|
||||||
|
.uri="/",
|
||||||
|
.method=HTTP_GET,
|
||||||
|
.handler=&on_http_get_root,
|
||||||
|
.user_ctx = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
esp_err_t on_http_get_default(httpd_req_t* request) {
|
||||||
|
const char* response_msg = http_response_ok;
|
||||||
|
LOGLN("GET received on '/default'.");
|
||||||
|
// convert the request to a gradient object
|
||||||
|
Result result = request_to_gradient(request);
|
||||||
|
// handle invalid query
|
||||||
|
if(!result.is_ok) {
|
||||||
|
httpd_resp_set_status(request, "400 Bad Request");
|
||||||
|
response_msg = result.error;
|
||||||
|
} else {
|
||||||
|
// set default gradient
|
||||||
|
// take lock on leds data
|
||||||
|
xSemaphoreTake(g_led_mutex, portMAX_DELAY);
|
||||||
|
leds_set_default_gradient(result.ok);
|
||||||
|
// release lock on leds data
|
||||||
|
xSemaphoreGive(g_led_mutex);
|
||||||
|
// we allocated a gradient_t struct with request_to_gradient,
|
||||||
|
// we no longer need it so we'll free it asap
|
||||||
|
free(result.ok);
|
||||||
|
}
|
||||||
|
// send http response
|
||||||
|
httpd_resp_send(request, response_msg, strlen(response_msg));
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
httpd_uri_t get_default_uri = {
|
||||||
|
.uri="/default",
|
||||||
|
.method=HTTP_GET,
|
||||||
|
.handler=&on_http_get_default,
|
||||||
|
.user_ctx=NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configure server and enable the http handler.
|
||||||
|
static
|
||||||
|
httpd_handle_t start_webserver(void) {
|
||||||
|
httpd_handle_t server = NULL;
|
||||||
|
httpd_config_t server_config = HTTPD_DEFAULT_CONFIG();
|
||||||
|
|
||||||
|
LOGLN("Starting HTTPd server ':%d'.", server_config.server_port);
|
||||||
|
|
||||||
|
if(httpd_start(&server, &server_config) == ESP_OK) {
|
||||||
|
httpd_register_uri_handler(server, &get_root_uri);
|
||||||
|
httpd_register_uri_handler(server, &get_default_uri);
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGLN("Failed to start HTTPd server.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start an http server and store it's handle in g_http_server.
|
||||||
|
void server_init(void) {
|
||||||
|
g_http_server = start_webserver();
|
||||||
|
}
|
185
main/server.h
185
main/server.h
|
@ -14,189 +14,6 @@
|
||||||
#ifndef _potion_party_server_h
|
#ifndef _potion_party_server_h
|
||||||
#define _potion_party_server_h
|
#define _potion_party_server_h
|
||||||
|
|
||||||
#include <stddef.h>
|
void server_init();
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "shared.h"
|
|
||||||
#include "leds.h"
|
|
||||||
#include "esp_http_server.h"
|
|
||||||
#include "esp_system.h"
|
|
||||||
#include "esp_netif.h"
|
|
||||||
#include "sys_arch.h"
|
|
||||||
|
|
||||||
static
|
|
||||||
httpd_handle_t g_http_server = NULL;
|
|
||||||
|
|
||||||
struct parse_error_t {
|
|
||||||
const char* error;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Parse a gradient query
|
|
||||||
static
|
|
||||||
struct parse_error_t parse_leds_query(char* query_string, size_t query_size) {
|
|
||||||
char query_value[16];
|
|
||||||
char query_key[3];
|
|
||||||
struct gradient_t gradient;
|
|
||||||
|
|
||||||
// Fetch the &l length parameter.
|
|
||||||
// Interpret as a positive integer number of points on the gradient.
|
|
||||||
if(httpd_query_key_value(query_string, "l", query_value, sizeof(query_value)) == ESP_OK) {
|
|
||||||
gradient.points_len = max(0, atoi(query_value));
|
|
||||||
} else {
|
|
||||||
leds_set_current_gradient(&g_default_gradient, 0);
|
|
||||||
return (struct parse_error_t) {
|
|
||||||
.error = "ERROR: Failed to find length parameter &l"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the &d 'duration' parameter from the query.
|
|
||||||
// Interpreted as a floating point number of seconds before returning to default state.
|
|
||||||
if(httpd_query_key_value(query_string, "d", query_value, sizeof(query_value)) == ESP_OK) {
|
|
||||||
gradient.duration = atof(query_string);
|
|
||||||
} else {
|
|
||||||
gradient.duration = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGLN("Reading %zu points of gradient query:", gradient.points_len);
|
|
||||||
LOGLN("duration: %f", gradient.duration);
|
|
||||||
|
|
||||||
// Get the gradient point components for every point that was promised by the &l parameter
|
|
||||||
for(int point = 0; point < gradient.points_len; ++point) {
|
|
||||||
// Get the r, g, and b components as 8 bit integers
|
|
||||||
sprintf(query_key, "r%d", point);
|
|
||||||
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) {
|
|
||||||
gradient.points[point].led.components.red = atoi(query_value);
|
|
||||||
} else {
|
|
||||||
return (struct parse_error_t) {
|
|
||||||
.error = "ERROR: Point missing red component &r."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
sprintf(query_key, "g%d", point);
|
|
||||||
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) {
|
|
||||||
gradient.points[point].led.components.green = atoi(query_value);
|
|
||||||
} else {
|
|
||||||
return (struct parse_error_t) {
|
|
||||||
.error = "ERROR: Point missing green component &g."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
sprintf(query_key, "b%d", point);
|
|
||||||
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
|
||||||
gradient.points[point].led.components.blue = atoi(query_value);
|
|
||||||
} else {
|
|
||||||
return (struct parse_error_t) {
|
|
||||||
.error = "ERROR: Point missing blue component &b."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Get the global variable, passed as alpha. Limited to 0-32 (a 5bit unsigned int)
|
|
||||||
// Make sure the most significant 3 bits are all ones
|
|
||||||
sprintf(query_key, "a%d", point);
|
|
||||||
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
|
||||||
gradient.points[point].led.components.global = GLOBAL((uint8_t)atoi(query_value));
|
|
||||||
} else {
|
|
||||||
return (struct parse_error_t) {
|
|
||||||
.error = "ERROR: Point missing alpha component &a."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Get the time of the gradient as a number ranging from 0 - 120.
|
|
||||||
// Interpreted as an integer offset from the first led to the last
|
|
||||||
sprintf(query_key, "t%d", point);
|
|
||||||
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
|
||||||
gradient.points[point].offset = clamp(0, 120, atoi(query_value));
|
|
||||||
} else {
|
|
||||||
return (struct parse_error_t) {
|
|
||||||
.error = "ERROR: Point missing time component &t."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Get the movement variable &m.
|
|
||||||
// Interpreted as an integer number from -1 to +1
|
|
||||||
sprintf(query_key, "m%d", point);
|
|
||||||
if(gradient.duration > 0 && httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
|
||||||
gradient.points[point].movement = clamp(-1, +1, atoi(query_value));
|
|
||||||
} else {
|
|
||||||
gradient.points[point].movement = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log fetched fields
|
|
||||||
LOGLN("led[%d]:", point);
|
|
||||||
LOGLN(" r %d", gradient.points[point].led.components.red);
|
|
||||||
LOGLN(" g %d", gradient.points[point].led.components.green);
|
|
||||||
LOGLN(" b %d", gradient.points[point].led.components.blue);
|
|
||||||
LOGLN(" global %d", gradient.points[point].led.components.global >> 3);
|
|
||||||
// may show up as a compile error on modern computers,
|
|
||||||
// x86_64 size_t is usually an unsigned long int, on the ESP8266 it is an unsigned int
|
|
||||||
LOGLN(" t %u", gradient.points[point].offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
xSemaphoreTake(g_led_mutex, portMAX_DELAY);
|
|
||||||
|
|
||||||
leds_set_current_gradient(&gradient, 0);
|
|
||||||
|
|
||||||
xSemaphoreGive(g_led_mutex);
|
|
||||||
|
|
||||||
return (struct parse_error_t) { .error = NULL };
|
|
||||||
}
|
|
||||||
|
|
||||||
// receives HTTP GET requests on root
|
|
||||||
static
|
|
||||||
esp_err_t on_http_get_root(httpd_req_t* request) {
|
|
||||||
// Error mewsages for cases of good and bad
|
|
||||||
static const char* response_ok = "OK!";
|
|
||||||
|
|
||||||
const char* response_msg = response_ok;
|
|
||||||
|
|
||||||
LOGLN("POST received on '/'.");
|
|
||||||
|
|
||||||
// buffer for query string
|
|
||||||
char* query_buffer;
|
|
||||||
size_t query_length = httpd_req_get_url_query_len(request) + 1;
|
|
||||||
struct parse_error_t result = { .error = NULL };
|
|
||||||
|
|
||||||
if(query_length > 1) {
|
|
||||||
query_buffer = malloc(query_length * sizeof(char));
|
|
||||||
if(httpd_req_get_url_query_str(request, query_buffer, query_length) == ESP_OK) {
|
|
||||||
LOGLN("Received query.");
|
|
||||||
result = parse_leds_query(query_buffer, query_length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// an error was returned, pass it on to the API caller
|
|
||||||
if(result.error != NULL) {
|
|
||||||
httpd_resp_set_status(request, "400 Bad Request");
|
|
||||||
response_msg = result.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
httpd_resp_send(request, response_msg, strlen(response_msg));
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
httpd_uri_t get_root_uri = {
|
|
||||||
.uri="/",
|
|
||||||
.method=HTTP_GET,
|
|
||||||
.handler=&on_http_get_root,
|
|
||||||
.user_ctx = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
// Configure server and enable the http handler.
|
|
||||||
static
|
|
||||||
httpd_handle_t start_webserver(void) {
|
|
||||||
httpd_handle_t server = NULL;
|
|
||||||
httpd_config_t server_config = HTTPD_DEFAULT_CONFIG();
|
|
||||||
|
|
||||||
LOGLN("Starting HTTPd server ':%d'.", server_config.server_port);
|
|
||||||
|
|
||||||
if(httpd_start(&server, &server_config) == ESP_OK) {
|
|
||||||
httpd_register_uri_handler(server, &get_root_uri);
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGLN("Failed to start HTTPd server.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start an http server and store it's handle in g_http_server.
|
|
||||||
static
|
|
||||||
void server_init(void) {
|
|
||||||
g_http_server = start_webserver();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !_potion_party_server_h
|
#endif // !_potion_party_server_h
|
||||||
|
|
|
@ -7,10 +7,7 @@
|
||||||
#ifndef _shared_h
|
#ifndef _shared_h
|
||||||
#define _shared_h
|
#define _shared_h
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
|
||||||
#include <esp_wifi.h>
|
|
||||||
#include "esp_system.h"
|
|
||||||
|
|
||||||
// wifi configuration
|
// wifi configuration
|
||||||
#define SSID "ESP8266"
|
#define SSID "ESP8266"
|
||||||
|
@ -36,4 +33,15 @@ int clamp(int x, int mi, int ma) {
|
||||||
|
|
||||||
#define GLOBAL(__a) (uint8_t)(__a|0xE0)
|
#define GLOBAL(__a) (uint8_t)(__a|0xE0)
|
||||||
|
|
||||||
|
typedef struct Result {
|
||||||
|
uint8_t is_ok;
|
||||||
|
union {
|
||||||
|
void* ok;
|
||||||
|
const char* error;
|
||||||
|
};
|
||||||
|
} Result;
|
||||||
|
|
||||||
|
#define PARSE_ERR(__err) (Result){.is_ok=0,.error=__err}
|
||||||
|
#define PARSE_OK(__result) (Result){.is_ok=1,.ok=__result}
|
||||||
|
|
||||||
#endif // !_shared_h
|
#endif // !_shared_h
|
||||||
|
|
Loading…
Reference in New Issue