浏览代码

Add full MQTT/JSON support

rmt-backend
jrhoffa 3 年前
父节点
当前提交
51023de70e
共有 5 个文件被更改,包括 138 次插入36 次删除
  1. +37
    -13
      main/device.cpp
  2. +6
    -2
      main/device.hpp
  3. +11
    -0
      main/leds.cpp
  4. +7
    -4
      main/leds.hpp
  5. +77
    -17
      main/main.cpp

+ 37
- 13
main/device.cpp 查看文件

@@ -11,14 +11,26 @@ Device::Device(std::string _id) :
ready(false), should_publish(false),
mutex(xSemaphoreCreateMutex()),
sem(xSemaphoreCreateBinary()),
power_on(true),
power_on(false), strip_length(0),
client(start_mqtt_client(on_mqtt_connect, on_mqtt_message, this))
{}

void Device::set_mqtt_config(cJSON*) {
void Device::set_json_config(const cJSON *json) {
xSemaphoreTake(mutex, portMAX_DELAY);

// TODO: update power_on and effect from JSON
const cJSON *state = cJSON_GetObjectItem(json, "state");
if (state && cJSON_IsString(state)) {
power_on = strcmp(state->valuestring, "ON") == 0;
}
const cJSON *fx = cJSON_GetObjectItem(json, "effect");
if (fx && cJSON_IsString(fx)) {
effect = fx->valuestring;
}
const cJSON *length = cJSON_GetObjectItem(json, "length");
if (length && cJSON_IsNumber(length)) {
strip_length = length->valuedouble;
}

publish_state_locked();

xSemaphoreGive(mutex);
@@ -55,22 +67,34 @@ void Device::on_mqtt_connect(esp_mqtt_client_handle_t client) {
xSemaphoreGive(mutex);
}

void Device::on_mqtt_message(esp_mqtt_client_handle_t, esp_mqtt_event_handle_t) {
void Device::on_mqtt_message(esp_mqtt_client_handle_t, esp_mqtt_event_handle_t event) {
ESP_LOGI(TAG, "Received command via MQTT");
// TODO: extract JSON, call set_mqtt_config
cJSON *json = cJSON_ParseWithLength(event->data, event->data_len);
if (json) {
set_json_config(json);
cJSON_Delete(json);
} else {
ESP_LOGE(TAG, "Invalid JSON data");
}
xSemaphoreGive(sem);
}

cJSON* Device::make_json_config_locked() const {
cJSON *json = cJSON_CreateObject();
cJSON_AddItemToObject(json, "state", cJSON_CreateString(power_on ? "ON" : "OFF"));
cJSON_AddItemToObject(json, "effect", cJSON_CreateString(effect.c_str()));
cJSON_AddItemToObject(json, "length", cJSON_CreateNumber(strip_length));
return json;
}

void Device::publish_state_locked() {
if (!(should_publish = !ready)) {
cJSON *json = make_json_config_locked();
char *config = cJSON_PrintUnformatted(json);
ESP_ERROR_CHECK(esp_mqtt_client_publish(
client,
state_topic.c_str(),
("{"
"\"state\": \"" + std::string(power_on ? "ON" : "OFF") + "\", "
"\"effect\": \"" + effect + "\""
"}").c_str(), 0,
0, 0
client, state_topic.c_str(), config, 0, 0, 0
));
cJSON_Delete(json);
cJSON_free(config);
}
}
}

+ 6
- 2
main/device.hpp 查看文件

@@ -11,7 +11,7 @@ class Device {
public:
Device(std::string id);

void set_mqtt_config(cJSON*);
void set_json_config(const cJSON*);

void lock() const { xSemaphoreTake(mutex, portMAX_DELAY); }
void unlock() const { xSemaphoreGive(mutex); }
@@ -26,6 +26,9 @@ public:
// Only call while locked
const std::string& get_effect() const { return effect; }
bool is_on() const { return power_on; }
int get_strip_length() const { return strip_length; }

cJSON* make_json_config_locked() const;

private:
std::string id;
@@ -40,6 +43,7 @@ private:

bool power_on;
std::string effect;
int strip_length;

esp_mqtt_client_handle_t client;

@@ -54,4 +58,4 @@ private:
void on_mqtt_message(esp_mqtt_client_handle_t, esp_mqtt_event_handle_t);

void publish_state_locked();
};
};

+ 11
- 0
main/leds.cpp 查看文件

@@ -55,6 +55,13 @@ LEDStrip::LEDStrip(int _length) :
last_us(0), offset(0)
{}

void LEDStrip::setLength(int _length) {
length_changing(_length);
delete[] pixels;
length = _length;
pixels = new Color[_length];
}

void LEDStrip::step() {
if (pattern) pattern->step(pixels, length, last_us, offset);
}
@@ -74,3 +81,7 @@ void TerminalLEDs::show() const {
line += "\x1b[0m";
ESP_LOGI(TAG, "%s", line.c_str());
}

void TerminalLEDs::length_changing(int new_len) {
ESP_LOGI(TAG, "Length changing: %d->%d", length, new_len);
}

+ 7
- 4
main/leds.hpp 查看文件

@@ -32,19 +32,21 @@ struct Pattern {

class LEDStrip {
public:
LEDStrip(int length);
LEDStrip(int length = 0);

void step();

virtual void show() const = 0;
virtual void length_changing(int) {};

void setPattern(const Pattern* _pattern) { pattern = _pattern; }
void setPattern(const Pattern *_pattern) { pattern = _pattern; }
const Pattern* getPattern() const { return pattern; }

void setLength(int length);
int getLength() const { return length; }

protected:
const int length;
int length;
const Pattern *pattern;
Color *pixels;

@@ -55,6 +57,7 @@ private:

class TerminalLEDs : public LEDStrip {
public:
TerminalLEDs(int length) : LEDStrip(length) {}
TerminalLEDs(int length = 0) : LEDStrip(length) {}
void show() const;
void length_changing(int);
};

+ 77
- 17
main/main.cpp 查看文件

@@ -84,6 +84,52 @@ static void log_dir(const std::string &path) {
}


static std::string read_file(const char *path) {
std::string data;
FILE *f = fopen(path, "r");
if (!f) {
ESP_LOGE(TAG, "Failed to read %s", path);
return data;
}
constexpr size_t szbuf = 4096;
char *buf = (char*)malloc(szbuf);
size_t nread;
while ((nread = fread(buf, 1, szbuf - 1, f)) > 0) {
buf[nread] = '\0';
data += buf;
}
fclose(f);
free(buf);
return data;
}

static void write_file(const char *path, const std::string &data) {
FILE *f = fopen(path, "w");
if (!f) {
ESP_LOGE(TAG, "Failed to open %s", path);
return;
}
const char *ptr = data.c_str();
size_t remain = data.length();
size_t nwritten = fwrite(ptr, 1, remain, f);
if (nwritten != remain) {
ESP_LOGE(TAG, "Failed to write to %s: %d/%d", path, nwritten, remain);
}
fclose(f);
}


std::string gen_config(bool is_on, const std::string &effect) {
return std::string() + "{ "
"\"state\": \"" + (is_on ? "ON" : "OFF") + "\", "
"\"effect\": \"" + effect + "\""
" }";
}


constexpr char config_path[] = "/spiffs/blinky.json";


// Entry Point

extern "C" void app_main(void) {
@@ -109,38 +155,52 @@ extern "C" void app_main(void) {

// Dummy Filesystem
start_filesystem();
//log_dir("/spiffs");
// TODO: Scrape this out
log_dir("/spiffs");

Device device("blinky-jr");

// TODO: Load config from SPIFFS
cJSON *json = cJSON_Parse(read_file(config_path).c_str());
if (json) {
ESP_LOGI(TAG, "Setting config");
device.set_json_config(json);
cJSON_Delete(json);
}

auto LEDs = TerminalLEDs(20);
/*** *** ***/
LEDs.setPattern(Presets::find("rainbow"));
/*** *** ***/
LEDStrip *LEDs = new TerminalLEDs();

while (true) {
ESP_LOGI(TAG, "Configuring LEDs");

device.lock();
const std::string &effect = device.get_effect();
bool is_on = device.is_on();
int length = device.get_strip_length();
cJSON *json = device.make_json_config_locked();
device.unlock();

if (length != LEDs->getLength()) {
LEDs->setLength(length);
}

const Pattern *pattern = Presets::find(device.get_effect());
const Pattern *pattern = Presets::find(effect);
if (pattern) {
LEDs.setPattern(pattern);
LEDs->setPattern(pattern);
} else {
ESP_LOGW(TAG, "Could not find pattern '%s'", device.get_effect());
pattern = LEDs.getPattern();
ESP_LOGW(TAG, "Could not find pattern '%s'", effect.c_str());
pattern = LEDs->getPattern();
}

// TODO: Save Config to SPIFFS
char *config = cJSON_PrintUnformatted(json);
write_file(config_path, config);
ESP_LOGI(TAG, "Saved config");

bool is_on = device.is_on();

device.unlock();
cJSON_free(config);
cJSON_Delete(json);

if (!pattern || !is_on) {
if (length <= 0 || !pattern || !is_on) {
if (!is_on) ESP_LOGI(TAG, "Device off");
if (length <= 0) ESP_LOGW(TAG, "No LEDs configured");
if (!pattern) ESP_LOGW(TAG, "No LED pattern set");
ESP_LOGI(TAG, "Waiting for new config");

@@ -159,8 +219,8 @@ extern "C" void app_main(void) {
break;
}

LEDs.step();
LEDs.show();
LEDs->step();
LEDs->show();

target_us += period_us;
}


正在加载...
取消
保存