Compare commits
No commits in common. "master" and "77eaea81d10b86f81ccd7d257c1cc797fef3cbec" have entirely different histories.
master
...
77eaea81d1
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,4 +3,3 @@
|
|||||||
.vscode/c_cpp_properties.json
|
.vscode/c_cpp_properties.json
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
.vscode/ipch
|
.vscode/ipch
|
||||||
include/config.h
|
|
||||||
|
43
README.md
43
README.md
@ -1,43 +0,0 @@
|
|||||||
# ESPleaf
|
|
||||||
|
|
||||||
## Layout description
|
|
||||||
|
|
||||||
Layout is defined in `LAYOUT` in `config.h`.
|
|
||||||
|
|
||||||
LEDs within a node are supposed to be installed counter-clockwise. The edge where the cables enter the node is node 0. To its right (counter-clockwise, remember) is corner 0. Then edge 1, corner 1, edge 2, corner 2 before coming back to edge 0.
|
|
||||||
|
|
||||||
Thereby, the next panel can either be installed along edge 1 or edge 2. (But the cable has to go corner 0, corner 1, corner 2 first.)
|
|
||||||
|
|
||||||
First panel is ignored in `LAYOUT`. First entry in `LAYOUT` is the edge through which the cable leaves Node 0 to Node 1 - which can be either Edge 1 or Edge 2.
|
|
||||||
|
|
||||||
On the next panel, the edge the cable is coming through is called Edge 0 again. And so on.
|
|
||||||
|
|
||||||
So, `LAYOUT` countains a list of edges the cable takes to leave to the next node.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
Command can be sent via MQTT to `MQTT_TOPIC_COMMANDS`. Syntax is `key=value` or `key=value&key2=value2...`.
|
|
||||||
|
|
||||||
Valid keys are:
|
|
||||||
* `mode` sets a new mode. Valid modes are:
|
|
||||||
* `corners`
|
|
||||||
* `nodes`
|
|
||||||
* `first_corner`
|
|
||||||
* `first_node`
|
|
||||||
* `flash`
|
|
||||||
* `static`
|
|
||||||
* `off`
|
|
||||||
* `brightness` sets the overall brightness of the lamp. Values are from 0 to 255 inclusive.
|
|
||||||
* `duration` if sets, `mode` and `brightness` are set for this amount of seconds only.
|
|
||||||
* `color` sets the color for `static` and `flash` modes. A predefined color name is expected.
|
|
||||||
* `speedup` sets a speedup for faster animations. Default value 1, possible values are 1 to 255. 0 will lead to an exception.
|
|
||||||
|
|
||||||
## Startup sequence
|
|
||||||
|
|
||||||
During startup:
|
|
||||||
* 1 green corner: FastLED is initialized.
|
|
||||||
* 2 green corners: WiFi is connecting...
|
|
||||||
* 3 green corners: Layout is analyzed...
|
|
||||||
* 4 green corners: Connecting to MQTT server...
|
|
||||||
* 5 green corners (only if OTA_DELAY is set): Waiting for an OTA connection...
|
|
||||||
* Everything green (quarter of a second): Initialization done.
|
|
23
include/config.h
Normal file
23
include/config.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define CORNERS_PER_PART 3
|
||||||
|
#define LEDS_PER_CORNER 2
|
||||||
|
|
||||||
|
// Layout definition
|
||||||
|
// Every node has EDGES_PER_PART corners and edges.
|
||||||
|
// The edge the signal comes in is edge number 0.
|
||||||
|
// All other edges are numbered counter-clockwise.
|
||||||
|
// LAYOUT contains a list of edges the signal takes.
|
||||||
|
// First node is implied.
|
||||||
|
|
||||||
|
// Examples, assuming EDGES_PER_PART == 3:
|
||||||
|
// Example: Nodes arranged in a circle: {1, 2, 2, 2, 2}
|
||||||
|
// Example: Nodes arranged in a line: {1, 2, 1, 2, 1, 2}
|
||||||
|
#define NODE_COUNT 5
|
||||||
|
#define LAYOUT {2, 1, 1, 2}
|
||||||
|
|
||||||
|
#define LED_COUNT NODE_COUNT * CORNERS_PER_PART * LEDS_PER_CORNER
|
||||||
|
|
||||||
|
#define SPEEDUP 1
|
||||||
|
|
||||||
|
//#define TEST_MODE
|
@ -1,49 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#define CORNERS_PER_PART 3
|
|
||||||
#define LEDS_PER_CORNER 2
|
|
||||||
|
|
||||||
// Layout definition
|
|
||||||
// Every node has EDGES_PER_PART corners and edges.
|
|
||||||
// The edge the signal comes in is edge number 0.
|
|
||||||
// All other edges are numbered counter-clockwise.
|
|
||||||
// LAYOUT contains a list of edges the signal takes.
|
|
||||||
// First node is implied.
|
|
||||||
|
|
||||||
// Examples, assuming EDGES_PER_PART == 3:
|
|
||||||
// Example: Nodes arranged in a circle: {1, 2, 2, 2, 2}
|
|
||||||
// Example: Nodes arranged in a line: {1, 2, 1, 2, 1, 2}
|
|
||||||
#define NODE_COUNT 5
|
|
||||||
#define LAYOUT {2, 1, 1, 2}
|
|
||||||
|
|
||||||
#define LED_COUNT NODE_COUNT * CORNERS_PER_PART * LEDS_PER_CORNER
|
|
||||||
|
|
||||||
#define SPEEDUP 1
|
|
||||||
|
|
||||||
#define MAX_MILLIAMPS 1000
|
|
||||||
// Maximum color difference for the random effects.
|
|
||||||
// This changes the hue value +/- this value. Use a maximum value of 127, otherwise strange things might happen.
|
|
||||||
#define COLOR_DIFFERENCE 25
|
|
||||||
// If this is enabled, the node layout will be visualized at startup.
|
|
||||||
#define SHOW_LAYOUT_AT_BOOT
|
|
||||||
// Wait this many seconds for OTA requests during startup.
|
|
||||||
#define WAIT_FOR_OTA 5
|
|
||||||
|
|
||||||
#define WIFI_SSID "..."
|
|
||||||
#define WIFI_PASS "..."
|
|
||||||
|
|
||||||
#define OTA_HOSTNAME "..."
|
|
||||||
|
|
||||||
#define MQTT_CLIENT_ID "espleaf"
|
|
||||||
#define MQTT_USER "..."
|
|
||||||
#define MQTT_PASS "..."
|
|
||||||
#define MQTT_SERVER "..."
|
|
||||||
#define MQTT_SERVER_PORT 1883
|
|
||||||
#define MQTT_TOPIC_STATE "espleaf/state"
|
|
||||||
#define MQTT_TOPIC_STATE_LONG "espleaf/state_long"
|
|
||||||
#define MQTT_TOPIC_COMMANDS "esplead/cmnd"
|
|
||||||
|
|
||||||
#define SYSLOG_HOST "..."
|
|
||||||
#define SYSLOG_PORT 514
|
|
||||||
|
|
||||||
//#define TEST_MODE
|
|
@ -1,17 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
class Corner;
|
|
||||||
|
|
||||||
#include "my_fastled.h"
|
#include "my_fastled.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include "node.h"
|
#include "edge.h"
|
||||||
|
|
||||||
class Corner {
|
class Corner {
|
||||||
public:
|
public:
|
||||||
Node* node;
|
Edge* e1;
|
||||||
uint8_t number;
|
Edge* e2;
|
||||||
Corner(Node* node, uint8_t number);
|
Corner(Edge* e1, Edge* e2);
|
||||||
std::list<uint16_t> _leds;
|
std::list<uint16_t> _leds;
|
||||||
std::vector<Corner*> _long_neighbours {};
|
std::vector<Corner*> _long_neighbours {};
|
||||||
std::vector<Corner*> _short_neighbours {};
|
std::vector<Corner*> _short_neighbours {};
|
||||||
@ -19,19 +16,19 @@ class Corner {
|
|||||||
CRGB source_color;
|
CRGB source_color;
|
||||||
CRGB target_color;
|
CRGB target_color;
|
||||||
bool effect_running = false;
|
bool effect_running = false;
|
||||||
uint16_t color_blend_state = 1023;
|
uint8_t color_blend_state = 255;
|
||||||
uint16_t effect_id;
|
uint16_t effect_id;
|
||||||
uint16_t effect_speed;
|
uint8_t effect_speed;
|
||||||
//bool loop();
|
//bool loop();
|
||||||
void draw();
|
void draw();
|
||||||
void blend_to(CRGB color, uint16_t effect_id=0, uint8_t effect_speed=0);
|
void blend_to(CRGB color, uint16_t effect_id=0, uint8_t effect_speed=0);
|
||||||
void add_led(uint16_t led_id);
|
void add_led(uint16_t led_id);
|
||||||
void merge_leds(Corner* c);
|
void merge_leds(Corner* c);
|
||||||
void step();
|
void step();
|
||||||
void infect(uint16_t short_level, uint16_t long_level);
|
void infect(uint8_t short_level, uint8_t long_level);
|
||||||
bool is_finished();
|
bool is_finished();
|
||||||
void set_color(CRGB color);
|
void set_color(CRGB color);
|
||||||
bool reached_level(uint16_t level);
|
bool reached_level(uint8_t level);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _infect(std::vector<Corner*>* neighbours);
|
void _infect(std::vector<Corner*>* neighbours);
|
||||||
|
6
include/edge.h
Normal file
6
include/edge.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class Edge {
|
||||||
|
public:
|
||||||
|
Edge* neighbour = nullptr;
|
||||||
|
};
|
@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "wifi.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
|
|
||||||
static PubSubClient mqtt(wifi);
|
|
||||||
|
|
||||||
void mqtt_setup();
|
|
||||||
void mqtt_loop();
|
|
||||||
void mqtt_publish_current_state(String state);
|
|
@ -1,36 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
class Node;
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "edge.h"
|
||||||
#include "corner.h"
|
#include "corner.h"
|
||||||
#include "prototypes.h"
|
|
||||||
|
|
||||||
// Delta-X data. Per direction of a triangle is a triple with Delta-X values for each direction.
|
|
||||||
static const int8_t dx[][3] = {{0, 1, -1}, {1, 0, -1}, {1, -1, 0}, {0, -1, 1}, {-1, 0, 1}, {-1, 1, 0}};
|
|
||||||
static const int8_t dy[][3] = {{-1, 0, 0}, {0, 1, 0}, {0, 0, -1}, {1, 0, 0}, {0, -1, 0}, {0, 0, 1}};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int8_t x;
|
|
||||||
int8_t y;
|
|
||||||
} Coords;
|
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
public:
|
private:
|
||||||
uint16_t _number;
|
uint16_t _number;
|
||||||
Coords coords;
|
public:
|
||||||
uint8_t direction;
|
|
||||||
Node* neighbours[CORNERS_PER_PART];
|
Node* neighbours[CORNERS_PER_PART];
|
||||||
|
Edge* edges[CORNERS_PER_PART];
|
||||||
Corner* _corners[CORNERS_PER_PART];
|
Corner* _corners[CORNERS_PER_PART];
|
||||||
Node(uint16_t number, Coords c, uint8_t direction);
|
Node(uint16_t number);
|
||||||
Node* create_neighbour(uint8_t edge);
|
Node* create_neighbour(uint8_t edge);
|
||||||
Coords coords_at_direction(uint8_t edge);
|
|
||||||
uint16_t distance_from_start = 0;
|
|
||||||
|
|
||||||
void blend_to(CRGB color, uint16_t effect_id=0, uint8_t effect_speed=0);
|
void blend_to(CRGB color, uint16_t effect_id=0, uint8_t effect_speed=0);
|
||||||
void set_color(CRGB color);
|
void set_color(CRGB color);
|
||||||
void infect(uint16_t level);
|
void infect(uint8_t level);
|
||||||
void step();
|
void step();
|
||||||
void draw();
|
void draw();
|
||||||
};
|
};
|
@ -2,36 +2,19 @@
|
|||||||
#include "my_fastled.h"
|
#include "my_fastled.h"
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
#include "edge.h"
|
||||||
#include "corner.h"
|
#include "corner.h"
|
||||||
|
|
||||||
class Node;
|
|
||||||
|
|
||||||
extern std::vector<Node*> nodes;
|
extern std::vector<Node*> nodes;
|
||||||
|
extern std::list<Edge*> edges;
|
||||||
extern std::vector<Corner*> corners;
|
extern std::vector<Corner*> corners;
|
||||||
|
|
||||||
extern CRGB leds[LED_COUNT];
|
extern CRGB leds[LED_COUNT];
|
||||||
extern CRGB color;
|
|
||||||
|
|
||||||
enum AnimationMode {
|
enum AnimationMode {
|
||||||
AM_CORNERS,
|
AM_CORNERS,
|
||||||
AM_FIRST_CORNER,
|
AM_NODES
|
||||||
AM_NODES,
|
|
||||||
AM_FIRST_NODE,
|
|
||||||
AM_FLASH,
|
|
||||||
AM_OFF,
|
|
||||||
AM_ERROR,
|
|
||||||
AM_NONE,
|
|
||||||
AM_STATIC,
|
|
||||||
AM_RAINBOW
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::map<AnimationMode, const char*> animation_mode_names;
|
|
||||||
|
|
||||||
extern AnimationMode mode;
|
|
||||||
extern AnimationMode temp_mode;
|
|
||||||
extern unsigned long temp_mode_until;
|
|
||||||
extern int return_to_brightness;
|
|
||||||
extern uint8_t speedup;
|
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include "prototypes.h"
|
|
||||||
|
|
||||||
class State {
|
|
||||||
AnimationMode _mode = AM_NONE;
|
|
||||||
uint16_t _duration = 0;
|
|
||||||
int16_t _brightness = -1;
|
|
||||||
uint8_t _speedup = 0;
|
|
||||||
CRGB _color = CRGB::Black;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void parse_json(char* str);
|
|
||||||
void set(String key, String value);
|
|
||||||
void commit();
|
|
||||||
static void publish_current_state();
|
|
||||||
static AnimationMode get_active_mode();
|
|
||||||
|
|
||||||
void parse_state(String state);
|
|
||||||
void parse_mode(String mode);
|
|
||||||
void set_mode(AnimationMode mode);
|
|
||||||
void set_duration(uint16_t duration);
|
|
||||||
void set_brightness(uint8_t brightness);
|
|
||||||
void set_color(JsonObject rgb);
|
|
||||||
void set_speedup(uint8_t speedup);
|
|
||||||
};
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <WiFiUdp.h>
|
|
||||||
|
|
||||||
#ifdef SYSLOG_HOST
|
|
||||||
void syslog(String msg, uint8_t severity=7);
|
|
||||||
#endif
|
|
@ -1,15 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "my_fastled.h"
|
#include "my_fastled.h"
|
||||||
#include "syslog.h"
|
|
||||||
|
|
||||||
//#define LOG(...) Serial.printf(__VA_ARGS__)
|
#define LOG(...) Serial.printf(__VA_ARGS__)
|
||||||
//#define LOGln(...) Serial.printf(__VA_ARGS__); Serial.println()
|
|
||||||
|
|
||||||
#ifdef SYSLOG_HOST
|
|
||||||
#define LOGln(...) { char buffer[512]; snprintf(buffer, 512, __VA_ARGS__); syslog(buffer); Serial.println(buffer);}
|
|
||||||
#else
|
|
||||||
#define LOGln(...) Serial.printf(__VA_ARGS__); Serial.println()
|
#define LOGln(...) Serial.printf(__VA_ARGS__); Serial.println()
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void clear_leds();
|
void clear_leds();
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
static WiFiClient wifi;
|
|
||||||
|
|
||||||
void wifi_setup();
|
|
@ -12,13 +12,9 @@
|
|||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
board = d1_mini
|
board = d1_mini
|
||||||
framework = arduino
|
framework = arduino
|
||||||
;upload_port = /dev/cu.wchusbserial*
|
upload_port = /dev/cu.wchusbserial*
|
||||||
upload_protocol = espota
|
|
||||||
upload_port = espleaf-prod.local
|
|
||||||
upload_speed = 921600
|
upload_speed = 921600
|
||||||
monitor_port = /dev/cu.wchusbserial*
|
monitor_port = /dev/cu.wchusbserial*
|
||||||
monitor_speed = 74880
|
monitor_speed = 74880
|
||||||
monitor_filters = default, time, send_on_enter, esp8266_exception_decoder
|
monitor_filters = default, time, send_on_enter, esp8266_exception_decoder
|
||||||
lib_deps = fastled/FastLED @ 3.4.0
|
lib_deps = FastLED
|
||||||
PubSubClient @ 2.8
|
|
||||||
ArduinoJson
|
|
@ -2,7 +2,7 @@
|
|||||||
#include "prototypes.h"
|
#include "prototypes.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
Corner::Corner(Node* no, uint8_t nu): node(no), number(nu) {
|
Corner::Corner(Edge* new_e1, Edge* new_e2): e1(new_e1), e2(new_e2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,16 +20,16 @@ void Corner::_infect(std::vector<Corner*>* neighbours) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Corner::step() {
|
void Corner::step() {
|
||||||
if (this->color_blend_state < 1024) {
|
if (this->color_blend_state < 255) {
|
||||||
this->color = blend(this->source_color, this->target_color, color_blend_state>>2);
|
this->color = blend(this->source_color, this->target_color, color_blend_state);
|
||||||
this->color_blend_state = (this->color_blend_state + effect_speed > 1024) ? 1024 : this->color_blend_state + effect_speed;
|
this->color_blend_state = (this->color_blend_state + effect_speed > 255) ? 255 : this->color_blend_state + effect_speed;
|
||||||
if (this->color_blend_state==1024) {
|
if (this->color_blend_state==255) {
|
||||||
this->color = this->target_color;
|
this->color = this->target_color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Corner::infect(uint16_t infect_short_level, uint16_t infect_long_level) {
|
void Corner::infect(uint8_t infect_short_level, uint8_t infect_long_level) {
|
||||||
if (reached_level(infect_short_level)) {
|
if (reached_level(infect_short_level)) {
|
||||||
_infect(&_short_neighbours);
|
_infect(&_short_neighbours);
|
||||||
}
|
}
|
||||||
@ -38,29 +38,53 @@ void Corner::infect(uint16_t infect_short_level, uint16_t infect_long_level) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Corner::reached_level(uint16_t level) {
|
bool Corner::reached_level(uint8_t level) {
|
||||||
if (color_blend_state >= 1024) return false;
|
uint8_t old_cbs = color_blend_state >= effect_speed ? color_blend_state - effect_speed : 0;
|
||||||
uint16_t old_cbs = color_blend_state >= effect_speed ? color_blend_state - effect_speed : 0;
|
|
||||||
return (old_cbs < level && color_blend_state >= level);
|
return (old_cbs < level && color_blend_state >= level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*bool Corner::loop() {
|
||||||
|
if (this->color_blend_state < 255) {
|
||||||
|
this->color = blend(this->source_color, this->target_color, color_blend_state);
|
||||||
|
if (255 - color_blend_state >= effect_speed) {
|
||||||
|
color_blend_state += effect_speed;
|
||||||
|
} else {
|
||||||
|
color_blend_state = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color_blend_state >= 200 && color_blend_state-effect_speed<200) {
|
||||||
|
infect(&_long_neighbours);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color_blend_state>=75 && color_blend_state-effect_speed<75) {
|
||||||
|
infect(&_short_neighbours);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color_blend_state==255) {
|
||||||
|
this->color = this->target_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->draw();
|
||||||
|
|
||||||
|
return color_blend_state < 255;
|
||||||
|
}*/
|
||||||
|
|
||||||
void Corner::blend_to(CRGB target, uint16_t eid, uint8_t effect_speed) {
|
void Corner::blend_to(CRGB target, uint16_t eid, uint8_t effect_speed) {
|
||||||
//LOGln("blendTo called. Corner: %p, target: %d,%d,%d, eid: %d, 'old' effect_id: %d, speed: %d", this, target.r, target.g, target.b, eid, effect_id, effect_speed);
|
LOGln("blendTo called. Corner: %p, target: %d,%d,%d, eid: %d, 'old' effect_id: %d, speed: %d", this, target.r, target.g, target.b, eid, effect_id, effect_speed);
|
||||||
if (eid==0) {
|
if (eid==0) {
|
||||||
this->effect_id = random16();
|
this->effect_id = random16();
|
||||||
//LOGln("Set effect_id to %d", this->effect_id);
|
LOGln("Set effect_id to %d", this->effect_id);
|
||||||
} else {
|
} else {
|
||||||
if (this->effect_id == eid) {
|
if (this->effect_id == eid) {
|
||||||
//LOGln("'Old' effect. Doing nothing.");
|
LOGln("'Old' effect. Doing nothing.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->effect_id = eid;
|
this->effect_id = eid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effect_speed==0) {
|
if (effect_speed==0) {
|
||||||
// Effect speed 2 - 7.
|
effect_speed = random8(3) + 1;
|
||||||
// Speed 7 @ 50fps => 3 seconds
|
|
||||||
effect_speed = random8(6) + 2;
|
|
||||||
}
|
}
|
||||||
this->effect_speed = effect_speed;
|
this->effect_speed = effect_speed;
|
||||||
|
|
||||||
@ -80,10 +104,10 @@ void Corner::merge_leds(Corner* c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Corner::is_finished() {
|
bool Corner::is_finished() {
|
||||||
return color_blend_state < 1024;
|
return color_blend_state < 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Corner::set_color(CRGB color) {
|
void Corner::set_color(CRGB color) {
|
||||||
this->color = color;
|
this->color = color;
|
||||||
this->color_blend_state = 1024;
|
this->color_blend_state = 255;
|
||||||
}
|
}
|
258
src/main.cpp
258
src/main.cpp
@ -1,29 +1,20 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ArduinoOTA.h>
|
|
||||||
#include "my_fastled.h"
|
#include "my_fastled.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
#include "edge.h"
|
||||||
#include "corner.h"
|
#include "corner.h"
|
||||||
#include "prototypes.h"
|
#include "prototypes.h"
|
||||||
#include "mqtt.h"
|
|
||||||
#include "state.h"
|
|
||||||
#include "syslog.h"
|
|
||||||
|
|
||||||
std::vector<Node*> nodes;
|
std::vector<Node*> nodes;
|
||||||
|
std::list<Edge*> edges;
|
||||||
std::vector<Corner*> corners;
|
std::vector<Corner*> corners;
|
||||||
|
|
||||||
CRGB leds[LED_COUNT];
|
CRGB leds[LED_COUNT];
|
||||||
CRGB color = CRGB::Pink;
|
|
||||||
|
|
||||||
AnimationMode mode = AM_CORNERS;
|
AnimationMode mode = AM_CORNERS;
|
||||||
AnimationMode temp_mode;
|
|
||||||
unsigned long temp_mode_until;
|
|
||||||
int return_to_brightness = -1;
|
|
||||||
uint8_t speedup = SPEEDUP;
|
|
||||||
|
|
||||||
unsigned long last_loop = 0;
|
|
||||||
|
|
||||||
#ifdef TEST_MODE
|
#ifdef TEST_MODE
|
||||||
uint8_t base_hue = 0;
|
uint8_t base_hue = 0;
|
||||||
@ -34,8 +25,7 @@ bool looping;
|
|||||||
void setup_layout() {
|
void setup_layout() {
|
||||||
LOGln("Setting up layout...");
|
LOGln("Setting up layout...");
|
||||||
uint8_t layout[] = LAYOUT;
|
uint8_t layout[] = LAYOUT;
|
||||||
Node* current_node = new Node(0, {0, 0}, 0);
|
Node* current_node = new Node(0);
|
||||||
current_node->distance_from_start = 1;
|
|
||||||
nodes.push_back(current_node);
|
nodes.push_back(current_node);
|
||||||
|
|
||||||
for(uint16_t i=0; i<NODE_COUNT-1; i++) {
|
for(uint16_t i=0; i<NODE_COUNT-1; i++) {
|
||||||
@ -43,85 +33,32 @@ void setup_layout() {
|
|||||||
nodes.push_back(current_node);
|
nodes.push_back(current_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool verbose = false;
|
|
||||||
for (Node* n1 : nodes) {
|
|
||||||
if (verbose) LOGln("Looking for neighbours of node #%d @ %d,%d", n1->_number, n1->coords.x, n1->coords.y);
|
|
||||||
for(int edge=0; edge<CORNERS_PER_PART; edge++) {
|
|
||||||
Coords c = n1->coords_at_direction(edge);
|
|
||||||
if (verbose) LOGln(" Checking edge %d @ %d,%d...", edge, c.x, c.y);
|
|
||||||
|
|
||||||
Node* found = nullptr;
|
|
||||||
for(Node* n2 : nodes) {
|
|
||||||
if (n2 == n1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (n2->coords.x == c.x && n2->coords.y == c.y) {
|
|
||||||
found = n2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found != nullptr) {
|
|
||||||
if (verbose) LOGln(" Found node #%d", found->_number);
|
|
||||||
uint8_t inverse_dir = (n1->direction + 2*edge + 3) % 6;
|
|
||||||
int8_t e = (inverse_dir - found->direction) % 6;
|
|
||||||
if (e < 0) e+=6;
|
|
||||||
e = e / 2;
|
|
||||||
//LOGln(" inverse_dir: %d, edge: %d", inverse_dir, edge);
|
|
||||||
int8_t e1 = (edge - 1) % CORNERS_PER_PART;
|
|
||||||
if (e1<0) e1+=CORNERS_PER_PART;
|
|
||||||
int8_t e2 = (e - 1) % CORNERS_PER_PART;
|
|
||||||
if (e2<0) e2+=CORNERS_PER_PART;
|
|
||||||
|
|
||||||
if (verbose) LOGln(" Mapping Corner #%d,%d with #%d,%d", n1->_number, edge, found->_number, e2);
|
|
||||||
n1->_corners[edge]->_short_neighbours.push_back(found->_corners[e2]);
|
|
||||||
|
|
||||||
if (verbose) LOGln(" Mapping Corner #%d,%d with #%d,%d", n1->_number, e1, found->_number, e);
|
|
||||||
n1->_corners[e1]->_short_neighbours.push_back(found->_corners[e]);
|
|
||||||
} else {
|
|
||||||
if (verbose) LOGln(" No match.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checked_all = false;
|
|
||||||
while (!checked_all) {
|
|
||||||
checked_all = true;
|
|
||||||
for(Node* n : nodes) {
|
|
||||||
if (n->distance_from_start == 0) {
|
|
||||||
checked_all = false;
|
|
||||||
} else {
|
|
||||||
for(int i=0; i<CORNERS_PER_PART; i++) {
|
|
||||||
if (n->neighbours[i] != nullptr && (n->neighbours[i]->distance_from_start==0 || n->neighbours[i]->distance_from_start > n->distance_from_start)) {
|
|
||||||
n->neighbours[i]->distance_from_start = n->distance_from_start+1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(Node* node: nodes) {
|
for(Node* node: nodes) {
|
||||||
LOGln("Node #%d:", node->_number);
|
for(Edge* edge: node->edges) {
|
||||||
LOGln(" Distance from start: %d", node->distance_from_start);
|
auto e = std::find(edges.begin(), edges.end(), edge);
|
||||||
for(Corner* corner : node->_corners) {
|
if (e == edges.end()) {
|
||||||
LOGln(" Corner #%d,%d:", node->_number, corner->number);
|
edges.push_back(edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGln("Node %p:", node);
|
||||||
|
for(Node* n : node->neighbours) {
|
||||||
|
LOGln(" %p", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Corner* corner: corners) {
|
||||||
|
LOGln("Corner %p:", corner);
|
||||||
for(auto c: corner->_long_neighbours) {
|
for(auto c: corner->_long_neighbours) {
|
||||||
LOGln(" Long neighbour: #%d,%d", c->node->_number, c->number);
|
LOGln(" Long: %p", c);
|
||||||
}
|
}
|
||||||
for(auto c: corner->_short_neighbours) {
|
for(auto c: corner->_short_neighbours) {
|
||||||
LOGln(" Short neighbour: #%d,%d", c->node->_number, c->number);
|
LOGln(" Short: %p", c);
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i=0; i<CORNERS_PER_PART; i++) {
|
|
||||||
if (node->neighbours[i]==nullptr) {
|
|
||||||
LOGln(" Neighbour %d: -", i);
|
|
||||||
} else {
|
|
||||||
LOGln(" Neighbour %d: #%d", i, node->neighbours[i]->_number);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGln("Counts:");
|
LOGln("Counts:");
|
||||||
LOGln("Nodes: %3d", nodes.size());
|
LOGln("Nodes: %3d", nodes.size());
|
||||||
|
LOGln("Edges: %3d", edges.size());
|
||||||
LOGln("Corners: %3d", corners.size());
|
LOGln("Corners: %3d", corners.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,102 +69,36 @@ void setup_fastled() {
|
|||||||
FastLED.addLeds<WS2812B, 5, GRB>(leds, LEDS_PER_CORNER * CORNERS_PER_PART * NODE_COUNT).setCorrection(TypicalLEDStrip);
|
FastLED.addLeds<WS2812B, 5, GRB>(leds, LEDS_PER_CORNER * CORNERS_PER_PART * NODE_COUNT).setCorrection(TypicalLEDStrip);
|
||||||
LOGln("LEDs: %3d", LED_COUNT);
|
LOGln("LEDs: %3d", LED_COUNT);
|
||||||
FastLED.setBrightness(255);
|
FastLED.setBrightness(255);
|
||||||
//FastLED.setDither(DISABLE_DITHER);
|
FastLED.setDither(DISABLE_DITHER);
|
||||||
FastLED.setMaxPowerInVoltsAndMilliamps(5, MAX_MILLIAMPS);
|
|
||||||
set_all_leds(CRGB::Black);
|
set_all_leds(CRGB::Black);
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_all() {
|
void setup_wifi() {
|
||||||
for(Node* node : nodes) {
|
LOGln("Starting WiFi scan for RNG initialization...");
|
||||||
node->draw();
|
WiFi.mode(WIFI_STA);
|
||||||
}
|
WiFi.disconnect();
|
||||||
FastLED.show();
|
uint16_t seed = 0;
|
||||||
}
|
int n = WiFi.scanNetworks();
|
||||||
|
LOGln("%d WiFi networks found", n);
|
||||||
void show_status(uint8_t status, CRGB color=CRGB::Green) {
|
for(int i=0; i<n; i++) {
|
||||||
for (int i=0; i<status*LEDS_PER_CORNER && i<LED_COUNT; i++) {
|
LOGln(" %s, %d dB", WiFi.SSID(i).c_str(), WiFi.RSSI(i));
|
||||||
leds[i] = color;
|
seed = (seed << 2) | (WiFi.RSSI(i) & 0x03);
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=status*LEDS_PER_CORNER; i<LED_COUNT; i++) {
|
|
||||||
leds[i] = CRGB::Black;
|
|
||||||
}
|
|
||||||
FastLED.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void display_layout() {
|
|
||||||
for(Corner* corner : corners) {
|
|
||||||
corner->set_color(CRGB::Blue);
|
|
||||||
for(Corner* c : corner->_short_neighbours) {
|
|
||||||
c->set_color(CRGB::Red);
|
|
||||||
}
|
|
||||||
for(Corner* c : corner->_long_neighbours) {
|
|
||||||
c->set_color(CRGB::Green);
|
|
||||||
}
|
|
||||||
show_all();
|
|
||||||
delay(1500);
|
|
||||||
for(Corner* c : corners) {
|
|
||||||
c->set_color(CRGB::Black);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
LOGln("WiFi scan done. Generated seed is 0x%04x", seed);
|
||||||
|
random16_set_seed(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(74880);
|
Serial.begin(74880);
|
||||||
LOGln("ESPleaf starting.");
|
LOGln("ESPleaf starting.");
|
||||||
|
|
||||||
|
setup_layout();
|
||||||
setup_fastled();
|
setup_fastled();
|
||||||
show_status(1);
|
setup_wifi();
|
||||||
|
|
||||||
#ifdef TEST_MODE
|
#ifdef TEST_MODE
|
||||||
LOGln("TEST_MODE is active!");
|
LOGln("TEST_MODE is active!");
|
||||||
#else
|
|
||||||
show_status(2);
|
|
||||||
wifi_setup();
|
|
||||||
|
|
||||||
show_status(3);
|
|
||||||
setup_layout();
|
|
||||||
|
|
||||||
show_status(4);
|
|
||||||
mqtt_setup();
|
|
||||||
ArduinoOTA.setHostname(OTA_HOSTNAME);
|
|
||||||
ArduinoOTA.onProgress([&](unsigned int progress, unsigned int total){
|
|
||||||
uint8_t count = progress * corners.size() / total;
|
|
||||||
show_status(count, CRGB::Blue);
|
|
||||||
});
|
|
||||||
ArduinoOTA.onEnd([](){ show_status(0); });
|
|
||||||
ArduinoOTA.begin();
|
|
||||||
|
|
||||||
#ifdef WAIT_FOR_OTA
|
|
||||||
show_status(5);
|
|
||||||
LOGln("Waiting %d seconds for OTA requests...", WAIT_FOR_OTA);
|
|
||||||
unsigned long ota_target_time = millis() + WAIT_FOR_OTA*1000;
|
|
||||||
while (millis() < ota_target_time) {
|
|
||||||
ArduinoOTA.handle();
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
LOGln("Done waiting for OTA requests.");
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
show_status(255);
|
|
||||||
delay(250);
|
|
||||||
show_status(0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LOGln("Setup done.");
|
|
||||||
|
|
||||||
#ifdef SHOW_LAYOUT_AT_BOOT
|
|
||||||
LOGln("SHOW_LAYOUT_AT_BOOT is active - showing layout...");
|
|
||||||
display_layout();
|
|
||||||
LOGln("Showing layout is done.");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for(Corner* corner : corners) {
|
|
||||||
corner->set_color(CRGB::Black);
|
|
||||||
}
|
|
||||||
|
|
||||||
State::publish_current_state();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
@ -245,72 +116,33 @@ void loop() {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// Normal mode
|
// Normal mode
|
||||||
mqtt_loop();
|
EVERY_N_MILLISECONDS(40 / SPEEDUP) {
|
||||||
ArduinoOTA.handle();
|
|
||||||
|
|
||||||
if (speedup > 0 && (millis() - last_loop > (20 / speedup) || last_loop > millis())) {
|
|
||||||
looping = false;
|
looping = false;
|
||||||
|
|
||||||
AnimationMode active_mode = State::get_active_mode();
|
if (mode == AM_CORNERS) {
|
||||||
|
|
||||||
if (active_mode == AM_CORNERS || active_mode == AM_FIRST_CORNER) {
|
|
||||||
for(Corner* corner: corners) {
|
for(Corner* corner: corners) {
|
||||||
corner->step();
|
corner->step();
|
||||||
if (active_mode == AM_FIRST_CORNER) {
|
corner->infect(75, 200);
|
||||||
corner->infect(512, 512);
|
|
||||||
} else {
|
|
||||||
corner->infect(300, 600);
|
|
||||||
}
|
|
||||||
looping |= !corner->is_finished();
|
looping |= !corner->is_finished();
|
||||||
|
corner->draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (random8(128)==0) {
|
if (random8(128)==0) {
|
||||||
uint16_t corner = (active_mode == AM_FIRST_CORNER) ? 0 : random16(corners.size());
|
corners[random16(corners.size())]->blend_to(CHSV(random8(), 255, 255));
|
||||||
CHSV color = rgb2hsv_approximate(corners[corner]->color);
|
|
||||||
corners[corner]->blend_to(CHSV(color.h - COLOR_DIFFERENCE + random8(2*COLOR_DIFFERENCE), 255, 255));
|
|
||||||
}
|
}
|
||||||
} else if (active_mode == AM_NODES || active_mode == AM_FIRST_NODE) {
|
} else if (mode == AM_NODES) {
|
||||||
for(Node* node : nodes) {
|
for(Node* node : nodes) {
|
||||||
node->step();
|
node->step();
|
||||||
node->infect(512);
|
node->infect(128);
|
||||||
|
node->draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (random8(128)==0) {
|
if (random8(128)==0) {
|
||||||
uint16_t corner = (active_mode == AM_FIRST_NODE) ? 0 : random8(nodes.size());
|
nodes[random8(nodes.size())]->blend_to(CHSV(random8(), 255, 255));
|
||||||
CHSV color = rgb2hsv_approximate(corners[corner]->color);
|
|
||||||
nodes[corner]->blend_to(CHSV(color.h - COLOR_DIFFERENCE + random8(2*COLOR_DIFFERENCE), 255, 255));
|
|
||||||
}
|
}
|
||||||
} else if (active_mode == AM_FLASH) {
|
|
||||||
for (Node* node : nodes) {
|
|
||||||
node->step();
|
|
||||||
node->infect(512);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (millis() / 1000 > last_loop / 1000) {
|
|
||||||
nodes[0]->blend_to(((millis() / 1000) % 2 == 0) ? CRGB::Black : color, 0, 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (active_mode == AM_OFF || active_mode == AM_STATIC) {
|
|
||||||
for(Node* node : nodes) {
|
|
||||||
node->set_color(active_mode == AM_OFF ? CRGB::Black : color);
|
|
||||||
}
|
|
||||||
} else if (active_mode == AM_RAINBOW) {
|
|
||||||
const uint8_t speed = 5; // seconds (approx.) for full color sweep
|
|
||||||
const uint8_t diff_per_node = 5;
|
|
||||||
uint8_t base_hue = (millis() * 256 / speed) / 1000;
|
|
||||||
for(Node* node : nodes) {
|
|
||||||
node->set_color(CHSV(base_hue + diff_per_node * node->distance_from_start, 255, 255));
|
|
||||||
}
|
|
||||||
} else { // This includes AM_ERROR
|
|
||||||
for(Node* node : nodes) {
|
|
||||||
node->set_color(CRGB::Black);
|
|
||||||
}
|
|
||||||
nodes[0]->set_color(CRGB::Red);
|
|
||||||
}
|
|
||||||
|
|
||||||
last_loop = millis();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
show_all();
|
FastLED.show();
|
||||||
}
|
}
|
79
src/mqtt.cpp
79
src/mqtt.cpp
@ -1,79 +0,0 @@
|
|||||||
#include "mqtt.h"
|
|
||||||
#include "tools.h"
|
|
||||||
#include "prototypes.h"
|
|
||||||
#include "state.h"
|
|
||||||
|
|
||||||
void connect() {
|
|
||||||
LOGln("Connecting to MQTT broker...");
|
|
||||||
if (mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_TOPIC "available", 0, true, "offline")) {
|
|
||||||
LOGln("Connected.");
|
|
||||||
mqtt.publish(MQTT_TOPIC "available", "online", true);
|
|
||||||
char buffer[40];
|
|
||||||
snprintf(buffer, 40, "online %s", wifi.localIP().toString().c_str());
|
|
||||||
mqtt.publish(MQTT_TOPIC "available_long", buffer, true);
|
|
||||||
mqtt.subscribe(MQTT_TOPIC "cmnd");
|
|
||||||
String discovery_msg = "{"
|
|
||||||
"\"~\":\"" MQTT_TOPIC "\",\"avty_t\":\"~available\",\"cmd_t\":\"~cmnd\",\"stat_t\":\"~state\","
|
|
||||||
"\"name\":\"ESPleaf\",\"schema\":\"json\","
|
|
||||||
"\"brightness\":true,\"bri_scl\":255,"
|
|
||||||
"\"effect\":true,\"effect_list\":[\"off\",\"corners\",\"nodes\",\"flash\",\"static\"],"
|
|
||||||
"\"rgb\":true}";
|
|
||||||
|
|
||||||
mqtt.publish("homeassistant/light/" HOMEASSISTANT_OBJECT_ID "/config", discovery_msg.c_str(), true);
|
|
||||||
} else {
|
|
||||||
LOGln("Connection failed. Reason: %d", mqtt.state());
|
|
||||||
delay(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void callback(char* topic, byte* pl, unsigned int length) {
|
|
||||||
pl[length] = 0;
|
|
||||||
State s;
|
|
||||||
if (pl[0]=='{') {
|
|
||||||
s.parse_json((char*)pl);
|
|
||||||
} else {
|
|
||||||
String payload((char*)pl);
|
|
||||||
// Syntax: key=value&key2=value2...
|
|
||||||
String current_part;
|
|
||||||
LOGln("Received command %s", payload.c_str());
|
|
||||||
while (payload.length() > 0) {
|
|
||||||
int offset = payload.indexOf("&");
|
|
||||||
if (offset != -1) {
|
|
||||||
current_part = payload.substring(0, offset);
|
|
||||||
payload = payload.substring(offset + 1);
|
|
||||||
} else {
|
|
||||||
current_part = payload;
|
|
||||||
payload = "";
|
|
||||||
}
|
|
||||||
offset = current_part.indexOf("=");
|
|
||||||
if (offset==-1) {
|
|
||||||
LOGln("Parameter without '=' detected: %s", current_part.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String key = current_part.substring(0, offset);
|
|
||||||
String value = current_part.substring(offset+1);
|
|
||||||
s.set(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void mqtt_setup() {
|
|
||||||
mqtt.setServer(MQTT_SERVER, MQTT_SERVER_PORT);
|
|
||||||
mqtt.setCallback(callback);
|
|
||||||
mqtt.setSocketTimeout(1);
|
|
||||||
mqtt.setBufferSize(400);
|
|
||||||
connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
void mqtt_loop() {
|
|
||||||
if (!mqtt.connected()) {
|
|
||||||
LOGln("MQTT disconnected. Reason: %d", mqtt.state());
|
|
||||||
connect();
|
|
||||||
}
|
|
||||||
mqtt.loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void mqtt_publish_current_state(String state) {
|
|
||||||
mqtt.publish(MQTT_TOPIC "state", state.c_str(), true);
|
|
||||||
};
|
|
44
src/node.cpp
44
src/node.cpp
@ -2,19 +2,15 @@
|
|||||||
#include "prototypes.h"
|
#include "prototypes.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
Node::Node(uint16_t number, Coords c, uint8_t _dir) {
|
Node::Node(uint16_t number) {
|
||||||
_number = number;
|
_number = number;
|
||||||
coords = c;
|
|
||||||
direction = _dir;
|
|
||||||
|
|
||||||
LOGln("Created Node #%d at coordinates %d,%d with direction %d.", _number, coords.x, coords.y, direction);
|
|
||||||
for(int i=0; i<CORNERS_PER_PART; i++) {
|
for(int i=0; i<CORNERS_PER_PART; i++) {
|
||||||
neighbours[i] = nullptr;
|
edges[i] = new Edge();
|
||||||
}
|
}
|
||||||
|
|
||||||
Corner* last_corner = nullptr;
|
Corner* last_corner = nullptr;
|
||||||
for(int i=0; i<CORNERS_PER_PART; i++) {
|
for(int i=0; i<CORNERS_PER_PART; i++) {
|
||||||
Corner* c = new Corner(this, i);
|
Corner* c = new Corner(edges[i], edges[(i+1) % CORNERS_PER_PART]);
|
||||||
for(int j=0; j<LEDS_PER_CORNER; j++) {
|
for(int j=0; j<LEDS_PER_CORNER; j++) {
|
||||||
c->add_led(number * CORNERS_PER_PART * LEDS_PER_CORNER + i * LEDS_PER_CORNER + j);
|
c->add_led(number * CORNERS_PER_PART * LEDS_PER_CORNER + i * LEDS_PER_CORNER + j);
|
||||||
}
|
}
|
||||||
@ -30,22 +26,38 @@ Node::Node(uint16_t number, Coords c, uint8_t _dir) {
|
|||||||
last_corner->_long_neighbours.push_back(_corners[0]);
|
last_corner->_long_neighbours.push_back(_corners[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Coords Node::coords_at_direction(uint8_t edge) {
|
|
||||||
return {(int8_t)(coords.x + dx[direction][edge]), (int8_t)(coords.y + dy[direction][edge])};
|
|
||||||
}
|
|
||||||
|
|
||||||
Node* Node::create_neighbour(uint8_t edge) {
|
Node* Node::create_neighbour(uint8_t edge) {
|
||||||
Coords new_c = coords_at_direction(edge);
|
Node* node = new Node(_number + 1);
|
||||||
int8_t new_dir = (edge==1) ? ((direction - 1) % 6) : ((direction + 1) % 6);
|
|
||||||
if (new_dir < 0) new_dir+=6;
|
|
||||||
Node* node = new Node(_number + 1, new_c, new_dir);
|
|
||||||
node->neighbours[0] = this;
|
node->neighbours[0] = this;
|
||||||
neighbours[edge] = node;
|
neighbours[edge] = node;
|
||||||
|
|
||||||
|
node->edges[0]->neighbour = this->edges[edge];
|
||||||
|
this->edges[edge]->neighbour = node->edges[0];
|
||||||
|
|
||||||
|
node->_corners[0]->_short_neighbours.push_back(_corners[(edge-1) % CORNERS_PER_PART]);
|
||||||
|
node->_corners[CORNERS_PER_PART - 1]->_short_neighbours.push_back(_corners[edge]);
|
||||||
|
_corners[(edge-1) % CORNERS_PER_PART]->_short_neighbours.push_back(node->_corners[0]);
|
||||||
|
_corners[edge]->_short_neighbours.push_back(node->_corners[CORNERS_PER_PART - 1]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
delete node->edges[0];
|
||||||
|
node->edges[0] = this->edges[edge];
|
||||||
|
|
||||||
|
Corner* c = this->corners[(edge-1) % CORNERS_PER_PART];
|
||||||
|
c->merge_leds(node->corners[0]);
|
||||||
|
delete node->corners[0];
|
||||||
|
node->corners[0] = c;
|
||||||
|
|
||||||
|
c = this->corners[edge];
|
||||||
|
c->merge_leds(node->corners[CORNERS_PER_PART-1]);
|
||||||
|
delete node->corners[CORNERS_PER_PART-1];
|
||||||
|
node->corners[CORNERS_PER_PART-1] = c;
|
||||||
|
*/
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::blend_to(CRGB color, uint16_t effect_id, uint8_t effect_speed) {
|
void Node::blend_to(CRGB color, uint16_t effect_id, uint8_t effect_speed) {
|
||||||
|
LOGln("Node::blend_to called. this:%p", this);
|
||||||
if (effect_speed == 0) effect_speed = random8(2)+1;
|
if (effect_speed == 0) effect_speed = random8(2)+1;
|
||||||
if (effect_id == 0) effect_id = random16();
|
if (effect_id == 0) effect_id = random16();
|
||||||
for(Corner* corner : this->_corners) {
|
for(Corner* corner : this->_corners) {
|
||||||
@ -71,7 +83,7 @@ void Node::set_color(CRGB color) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::infect(uint16_t level) {
|
void Node::infect(uint8_t level) {
|
||||||
if (this->_corners[0]->reached_level(level)) {
|
if (this->_corners[0]->reached_level(level)) {
|
||||||
for (Node* neighbour : neighbours) {
|
for (Node* neighbour : neighbours) {
|
||||||
if (neighbour == nullptr) continue;
|
if (neighbour == nullptr) continue;
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
#include "prototypes.h"
|
|
||||||
|
|
||||||
std::map<AnimationMode, const char*> animation_mode_names {
|
|
||||||
{AM_CORNERS, "corners"},
|
|
||||||
{AM_FLASH, "flash"},
|
|
||||||
{AM_FIRST_CORNER, "first_corner"},
|
|
||||||
{AM_NODES, "nodes"},
|
|
||||||
{AM_FIRST_NODE, "first_node"},
|
|
||||||
{AM_OFF, "off"},
|
|
||||||
{AM_ERROR, "error"},
|
|
||||||
{AM_NONE, "NONE"},
|
|
||||||
{AM_STATIC, "static"},
|
|
||||||
{AM_RAINBOW, "rainbow"}
|
|
||||||
};
|
|
139
src/state.cpp
139
src/state.cpp
@ -1,139 +0,0 @@
|
|||||||
#include "state.h"
|
|
||||||
#include "tools.h"
|
|
||||||
#include "mqtt.h"
|
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
|
|
||||||
void State::parse_json(char* str) {
|
|
||||||
StaticJsonDocument<512> json;
|
|
||||||
deserializeJson(json, str);
|
|
||||||
|
|
||||||
if(json.containsKey("state")) parse_state(json["state"]);
|
|
||||||
if(json.containsKey("effect")) parse_mode(json["effect"]);
|
|
||||||
if(json.containsKey("duration")) set_duration(json["duration"]);
|
|
||||||
if(json.containsKey("brightness")) set_brightness(json["brightness"]);
|
|
||||||
if(json.containsKey("rgb")) set_color(json["rgb"]);
|
|
||||||
if(json.containsKey("speedup")) set_speedup(json["speedup"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::parse_state(String state) {
|
|
||||||
if (state.equals("ON")) {
|
|
||||||
set_mode(AM_CORNERS);
|
|
||||||
} else if (state.equals("OFF")) {
|
|
||||||
set_mode(AM_OFF);
|
|
||||||
} else {
|
|
||||||
LOGln("parse_state: Unknown state %s", state.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::parse_mode(String mode) {
|
|
||||||
AnimationMode m = AM_NONE;
|
|
||||||
for (std::pair<AnimationMode, const char*> pair : animation_mode_names) {
|
|
||||||
if (mode.equals(pair.second)) {
|
|
||||||
m = pair.first;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m == AM_NONE) {
|
|
||||||
LOGln("parse_mode: Unknown mode '%s'.", mode.c_str());
|
|
||||||
} else {
|
|
||||||
set_mode(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::set_mode(AnimationMode m) {
|
|
||||||
_mode = m;
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::set_duration(uint16_t d) {
|
|
||||||
_duration = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::set_brightness(uint8_t b) {
|
|
||||||
_brightness = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::set_color(JsonObject rgb) {
|
|
||||||
if (!rgb.containsKey("r") || !rgb.containsKey("g") || !rgb.containsKey("b")) {
|
|
||||||
LOGln("set_color: Invalid rgb data.");
|
|
||||||
} else {
|
|
||||||
_color = CRGB(rgb["r"], rgb["g"], rgb["b"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::set_speedup(uint8_t s) {
|
|
||||||
_speedup = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::set(String key, String value) {
|
|
||||||
if (key.equals("state")) { parse_state(value); }
|
|
||||||
else if (key.equals("effect") || key.equals("mode")) { parse_mode(value); }
|
|
||||||
else if (key.equals("duration")) { set_duration(value.toInt()); }
|
|
||||||
else if (key.equals("brightness")) { set_brightness(value.toInt()); }
|
|
||||||
else if (key.equals("color")) {
|
|
||||||
if (value.equals("red")) { _color = CRGB::Red; }
|
|
||||||
else if (value.equals("green")) { _color = CRGB::Green; }
|
|
||||||
else if (value.equals("blue")) { _color = CRGB::Blue; }
|
|
||||||
else if (value.equals("pink")) { _color = CRGB::Pink; }
|
|
||||||
else if (value.equals("yellow")) { _color = CRGB::Yellow; }
|
|
||||||
else if (value.equals("orange")) { _color = CRGB::Orange; }
|
|
||||||
else { LOGln("Unknown color name %s.", value.c_str());}
|
|
||||||
} else if (key.equals("speedup")) { set_speedup(value.toInt()); }
|
|
||||||
else {
|
|
||||||
LOGln("Unknown key '%s'. (For reference: Value is '%s'.)", key.c_str(), value.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::commit() {
|
|
||||||
if (_brightness >= 0) {
|
|
||||||
if (temp_mode_until == 0) {
|
|
||||||
return_to_brightness = FastLED.getBrightness();
|
|
||||||
}
|
|
||||||
FastLED.setBrightness(_brightness);
|
|
||||||
}
|
|
||||||
if (_mode != AM_NONE) {
|
|
||||||
if (_duration > 0) {
|
|
||||||
temp_mode = _mode;
|
|
||||||
temp_mode_until = millis() + _duration*1000;
|
|
||||||
} else {
|
|
||||||
mode = _mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_color != CRGB(0, 0, 0)) {
|
|
||||||
color = _color;
|
|
||||||
}
|
|
||||||
|
|
||||||
publish_current_state();
|
|
||||||
}
|
|
||||||
|
|
||||||
void State::publish_current_state() {
|
|
||||||
StaticJsonDocument<512> json;
|
|
||||||
json["state"] = (mode==AM_OFF) ? "OFF" : "ON";
|
|
||||||
json["brightness"] = FastLED.getBrightness();
|
|
||||||
json["effect"] = animation_mode_names[get_active_mode()];
|
|
||||||
JsonObject rgb = json.createNestedObject("rgb");
|
|
||||||
rgb["r"] = color.r;
|
|
||||||
rgb["g"] = color.g;
|
|
||||||
rgb["b"] = color.b;
|
|
||||||
String result = "";
|
|
||||||
serializeJson(json, result);
|
|
||||||
LOGln("Reporting current state: %s", result.c_str());
|
|
||||||
mqtt_publish_current_state(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimationMode State::get_active_mode() {
|
|
||||||
AnimationMode active_mode = mode;
|
|
||||||
if (temp_mode_until > 0) {
|
|
||||||
if (temp_mode_until>millis()) {
|
|
||||||
active_mode = temp_mode;
|
|
||||||
} else {
|
|
||||||
temp_mode_until = 0;
|
|
||||||
if (return_to_brightness != -1) {
|
|
||||||
FastLED.setBrightness(return_to_brightness);
|
|
||||||
return_to_brightness = -1;
|
|
||||||
}
|
|
||||||
State::publish_current_state();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return active_mode;
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
#include "syslog.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifdef SYSLOG_HOST
|
|
||||||
static uint16_t syslog_msg_id = 1;
|
|
||||||
|
|
||||||
void syslog(String msg, uint8_t severity) {
|
|
||||||
uint8_t facility = 16; // local0
|
|
||||||
if (severity > 7) severity = 7;
|
|
||||||
WiFiUDP udp;
|
|
||||||
|
|
||||||
udp.beginPacket(SYSLOG_HOST, SYSLOG_PORT);
|
|
||||||
udp.write("<");
|
|
||||||
udp.write(String(facility * 8 + severity).c_str());
|
|
||||||
udp.write(">1 - " OTA_HOSTNAME " core 1 ");
|
|
||||||
udp.write(String(syslog_msg_id++).c_str());
|
|
||||||
udp.write(" - ");
|
|
||||||
udp.write(msg.c_str());
|
|
||||||
udp.endPacket();
|
|
||||||
}
|
|
||||||
#endif
|
|
15
src/wifi.cpp
15
src/wifi.cpp
@ -1,15 +0,0 @@
|
|||||||
#include "wifi.h"
|
|
||||||
#include "tools.h"
|
|
||||||
|
|
||||||
void wifi_setup() {
|
|
||||||
Serial.printf("Connecting to WiFi %s...", WIFI_SSID);
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
|
||||||
while (WiFi.status() != WL_CONNECTED) {
|
|
||||||
Serial.print(".");
|
|
||||||
delay(300);
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
LOGln("Connected as %s", WiFi.localIP().toString().c_str());
|
|
||||||
random16_add_entropy(micros());
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user