Add lightswitch to this firmware
This commit is contained in:
119
src/buttons.c
Normal file
119
src/buttons.c
Normal file
@ -0,0 +1,119 @@
|
||||
#include "mqtt.h"
|
||||
#include "mgos_gpio.h"
|
||||
#include "buttons.h"
|
||||
#include "relays.h"
|
||||
#include "helper.h"
|
||||
|
||||
struct button_t s_buttons[MAX_BUTTONS];
|
||||
|
||||
void buttons_cb(int gpio, void *arg) {
|
||||
int idx = buttons_find_by_gpio(gpio);
|
||||
|
||||
if (ERR_GPIO_INVALID == idx) {
|
||||
LOG(LL_ERROR, ("No button on gpio=%d", gpio));
|
||||
return;
|
||||
}
|
||||
LOG(LL_INFO, ("Button on gpio=%d idx=%d", gpio, idx));
|
||||
|
||||
buttons_touch_by_idx(idx);
|
||||
return;
|
||||
(void) arg;
|
||||
}
|
||||
|
||||
int buttons_init(const char *flag) {
|
||||
int next_button=0;
|
||||
int i;
|
||||
|
||||
memset(s_buttons, -1, sizeof(s_buttons));
|
||||
|
||||
LOG(LL_DEBUG, ("Flag='%s'", flag));
|
||||
if (!is_button_flag_valid(flag)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
i=0;
|
||||
while(i<(int) strlen(flag)) {
|
||||
int gpio = -1;
|
||||
|
||||
if (!isdigit((int) flag[i])) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
gpio = atoi((char *)flag+i);
|
||||
// LOG(LL_DEBUG, ("Token: %d at pos %u", gpio, i));
|
||||
if (gpio<MIN_GPIO || gpio>MAX_GPIO) {
|
||||
LOG(LL_ERROR, ("GPIO values must be within [%d,%d], gpio=%d is invalid", MIN_GPIO, MAX_GPIO, gpio));
|
||||
return -2;
|
||||
}
|
||||
s_buttons[next_button].gpio=gpio;
|
||||
s_buttons[next_button].last_change=-1;
|
||||
mgos_gpio_set_mode(gpio, MGOS_GPIO_MODE_INPUT);
|
||||
mgos_gpio_set_button_handler(gpio, MGOS_GPIO_PULL_UP, MGOS_GPIO_INT_EDGE_NEG, 100, buttons_cb, NULL);
|
||||
|
||||
next_button++;
|
||||
if (next_button>MAX_BUTTONS) {
|
||||
LOG(LL_ERROR, ("Too many buttons defined, max is %d", MAX_BUTTONS));
|
||||
return -3;
|
||||
}
|
||||
|
||||
while (i<(int)strlen(flag) && isdigit((int) flag[i])) i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t buttons_get_gpio_by_idx(uint8_t idx) {
|
||||
if (idx>=MAX_BUTTONS)
|
||||
return ERR_GPIO_INVALID;
|
||||
return s_buttons[idx].gpio;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t buttons_find_by_gpio(uint8_t gpio) {
|
||||
int i;
|
||||
for (i=0; i<MAX_BUTTONS; i++) {
|
||||
if (gpio==s_buttons[i].gpio)
|
||||
return i;
|
||||
}
|
||||
return ERR_GPIO_INVALID;
|
||||
}
|
||||
|
||||
bool buttons_touch_by_idx(uint8_t idx) {
|
||||
char msg[80];
|
||||
int relay_gpio;
|
||||
|
||||
if (idx>=MAX_BUTTONS || s_buttons[idx].gpio == ERR_GPIO_INVALID) {
|
||||
LOG(LL_ERROR, ("No button at idx=%d", idx));
|
||||
return false;
|
||||
}
|
||||
s_buttons[idx].last_change=mg_time();
|
||||
|
||||
snprintf(msg, sizeof(msg)-1, "{\"idx\": %d, \"gpio\": %d}", idx, s_buttons[idx].gpio);
|
||||
mqtt_publish_stat("button", msg);
|
||||
|
||||
// Lookup corresponding relay at the same index. If it exists, trigger it.
|
||||
relay_gpio = relays_get_gpio_by_idx(idx);
|
||||
if (ERR_GPIO_INVALID == relay_gpio)
|
||||
return true;
|
||||
|
||||
relays_set_by_gpio(relay_gpio, !relays_get_by_gpio(relay_gpio));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool buttons_touch_by_gpio(uint8_t gpio) {
|
||||
return buttons_touch_by_idx(buttons_find_by_gpio(gpio));
|
||||
}
|
||||
|
||||
float buttons_get_last_change_by_gpio(uint8_t gpio) {
|
||||
return buttons_get_last_change_by_idx(buttons_find_by_gpio(gpio));
|
||||
}
|
||||
|
||||
float buttons_get_last_change_by_idx(uint8_t idx) {
|
||||
if (idx>=MAX_BUTTONS || s_buttons[idx].gpio == ERR_GPIO_INVALID) {
|
||||
LOG(LL_ERROR, ("No button at idx=%d", idx));
|
||||
return -1;
|
||||
}
|
||||
return s_buttons[idx].last_change;
|
||||
}
|
22
src/helper.c
Normal file
22
src/helper.c
Normal file
@ -0,0 +1,22 @@
|
||||
#include "helper.h"
|
||||
|
||||
static bool is_csv_of_digits(const char *flag) {
|
||||
unsigned char *p = (unsigned char*)flag;
|
||||
|
||||
while (*p) {
|
||||
if (*p!=' ' && !isdigit(*p) && *p!=',') {
|
||||
LOG(LL_ERROR, ("Flag value '%s' invalid CSV of digits at pos %d: use only digits, ' ' and ','", flag, (int) ((char*)p-(char*)flag)));
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_button_flag_valid(const char *flag) {
|
||||
return is_csv_of_digits(flag);
|
||||
}
|
||||
|
||||
bool is_relay_flag_valid(const char *flag) {
|
||||
return is_csv_of_digits(flag);
|
||||
}
|
18
src/main.c
18
src/main.c
@ -6,6 +6,11 @@
|
||||
#include "mongoose-touch.h"
|
||||
#include "fonts/FreeSerifBold9pt7b.h"
|
||||
#include "fonts/FreeMonoBold9pt7b.h"
|
||||
#include "mgos_config.h"
|
||||
#include "buttons.h"
|
||||
#include "relays.h"
|
||||
#include "mqtt.h"
|
||||
#include "rpc.h"
|
||||
|
||||
struct screen_t *s_screen = NULL;
|
||||
|
||||
@ -62,18 +67,13 @@ void tft_demo(void)
|
||||
LOG(LL_INFO, ("Screen '%s' has %d widgets", s_screen->name, screen_get_num_widgets(s_screen)));
|
||||
}
|
||||
|
||||
static void mqtt_recv_cb(struct mg_connection *nc, const char *topic, int topic_len, const char *msg, int msg_len, void *user_data) {
|
||||
widget_network_recv();
|
||||
LOG(LL_INFO, ("topic='%.*s' msg='%.*s'", topic_len, topic, msg_len, msg));
|
||||
(void) nc;
|
||||
(void) user_data;
|
||||
}
|
||||
|
||||
|
||||
enum mgos_app_init_result mgos_app_init(void)
|
||||
{
|
||||
buttons_init(mgos_sys_config_get_app_buttons());
|
||||
relays_init(mgos_sys_config_get_app_relays());
|
||||
mqtt_init();
|
||||
rpc_init();
|
||||
backlight_init();
|
||||
mgos_mqtt_sub("/s/#", mqtt_recv_cb, NULL);
|
||||
|
||||
tft_demo();
|
||||
|
||||
|
84
src/mqtt.c
Normal file
84
src/mqtt.c
Normal file
@ -0,0 +1,84 @@
|
||||
#include "mgos.h"
|
||||
#include "mgos_mqtt.h"
|
||||
#include "mgos_config.h"
|
||||
#include "mqtt.h"
|
||||
#include "buttons.h"
|
||||
#include "relays.h"
|
||||
#include "helper.h"
|
||||
|
||||
static void mqtt_publish_broadcast_stat(const char *stat, const char *msg) {
|
||||
char topic[80];
|
||||
snprintf(topic, sizeof(topic)-1, "%s/%s", MQTT_TOPIC_BROADCAST_STAT, stat);
|
||||
mgos_mqtt_pub((char*)topic, (char*)msg, strlen(msg), 0, false);
|
||||
LOG(LL_INFO, ("Sent topic='%s' msg='%s'", topic, msg));
|
||||
}
|
||||
|
||||
static void mqtt_broadcast_cmd_id() {
|
||||
char resp[300];
|
||||
struct mgos_net_ip_info ip_info;
|
||||
char sta_ip[16], ap_ip[16];
|
||||
|
||||
memset(sta_ip, 0, sizeof(sta_ip));
|
||||
memset(ap_ip, 0, sizeof(ap_ip));
|
||||
|
||||
if (mgos_net_get_ip_info(MGOS_NET_IF_TYPE_WIFI, MGOS_NET_IF_WIFI_STA, &ip_info))
|
||||
mgos_net_ip_to_str(&ip_info.ip, sta_ip);
|
||||
if (mgos_net_get_ip_info(MGOS_NET_IF_TYPE_WIFI, MGOS_NET_IF_WIFI_AP, &ip_info))
|
||||
mgos_net_ip_to_str(&ip_info.ip, ap_ip);
|
||||
|
||||
snprintf(resp, sizeof(resp)-1, "{\"deviceid\": \"%s\", \"macaddress\": \"%s\", \"sta_ip\": \"%s\", \"ap_ip\": \"%s\", \"app\": \"%s\", \"arch\": \"%s\", \"uptime\": %lu}",
|
||||
mgos_sys_config_get_device_id(),
|
||||
mgos_sys_ro_vars_get_mac_address(),
|
||||
sta_ip,
|
||||
ap_ip,
|
||||
MGOS_APP,
|
||||
mgos_sys_ro_vars_get_arch(), (unsigned long) mgos_uptime());
|
||||
|
||||
mqtt_publish_broadcast_stat("id", resp);
|
||||
}
|
||||
|
||||
static void mqtt_cb(struct mg_connection *nc, const char *topic, int topic_len, const char *msg, int msg_len, void *ud) {
|
||||
LOG(LL_INFO, ("Received topic='%.*s' msg='%.*s'", topic_len, topic, msg_len, msg));
|
||||
|
||||
if (topic_len >= strlen(MQTT_TOPIC_BROADCAST_CMD) && 0 == strncmp(MQTT_TOPIC_BROADCAST_CMD, topic, strlen(MQTT_TOPIC_BROADCAST_CMD)))
|
||||
mqtt_broadcast_cmd_id();
|
||||
(void) nc;
|
||||
(void) ud;
|
||||
}
|
||||
|
||||
static void mqtt_ev(struct mg_connection *nc, int ev, void *ev_data, void *user_data) {
|
||||
switch (ev) {
|
||||
case MG_EV_MQTT_CONNACK:
|
||||
mqtt_broadcast_cmd_id();
|
||||
break;
|
||||
}
|
||||
(void) nc;
|
||||
(void) ev_data;
|
||||
(void) user_data;
|
||||
}
|
||||
|
||||
|
||||
void mqtt_publish_stat(const char *stat, const char *msg) {
|
||||
char topic[80];
|
||||
snprintf(topic, sizeof(topic)-1, "%s%s/stat/%s", MQTT_TOPIC_PREFIX, mgos_sys_config_get_device_id(), stat);
|
||||
mgos_mqtt_pub((char*)topic, (char*)msg, strlen(msg), 0, false);
|
||||
LOG(LL_INFO, ("Sent topic='%s' msg='%s'", topic, msg));
|
||||
}
|
||||
|
||||
void mqtt_init() {
|
||||
char topic[100];
|
||||
|
||||
mgos_mqtt_add_global_handler(mqtt_ev, NULL);
|
||||
|
||||
// Subscribe to broadcast for identification purposes
|
||||
mgos_mqtt_sub(MQTT_TOPIC_BROADCAST_CMD, mqtt_cb, NULL);
|
||||
|
||||
// Subscribe to broadcast/appname
|
||||
snprintf(topic, sizeof(topic)-1, "%s/%s", MQTT_TOPIC_BROADCAST_CMD, MGOS_APP);
|
||||
mgos_mqtt_sub(topic, mqtt_cb, NULL);
|
||||
|
||||
// Subscribe to Sonoff updates
|
||||
mgos_mqtt_sub("/s/#", mqtt_cb, NULL);
|
||||
|
||||
return;
|
||||
}
|
112
src/relays.c
Normal file
112
src/relays.c
Normal file
@ -0,0 +1,112 @@
|
||||
#include "mgos_gpio.h"
|
||||
#include "mqtt.h"
|
||||
#include "relays.h"
|
||||
#include "helper.h"
|
||||
|
||||
struct relay_t s_relays[MAX_RELAYS];
|
||||
|
||||
int relays_init(const char *flag) {
|
||||
int next_relay=0;
|
||||
int i;
|
||||
|
||||
memset(s_relays, -1, sizeof(s_relays));
|
||||
|
||||
LOG(LL_DEBUG, ("Flag='%s'", flag));
|
||||
if (!is_relay_flag_valid(flag)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
i=0;
|
||||
while(i<(int) strlen(flag)) {
|
||||
int gpio = -1;
|
||||
|
||||
if (!isdigit((int) flag[i])) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
gpio = atoi((char *)flag+i);
|
||||
// LOG(LL_DEBUG, ("Token: %d at pos %u", gpio, i));
|
||||
if (gpio<MIN_GPIO || gpio>MAX_GPIO) {
|
||||
LOG(LL_ERROR, ("GPIO values must be within [%d,%d], gpio=%d is invalid", MIN_GPIO, MAX_GPIO, gpio));
|
||||
return -2;
|
||||
}
|
||||
s_relays[next_relay].gpio=gpio;
|
||||
s_relays[next_relay].last_change=-1;
|
||||
s_relays[next_relay].state=0;
|
||||
mgos_gpio_set_mode(gpio, MGOS_GPIO_MODE_OUTPUT);
|
||||
mgos_gpio_write(gpio, !s_relays[next_relay].state);
|
||||
next_relay++;
|
||||
if (next_relay>MAX_RELAYS) {
|
||||
LOG(LL_ERROR, ("Too many relays defined, max is %d", MAX_RELAYS));
|
||||
return -3;
|
||||
}
|
||||
|
||||
while (i<(int)strlen(flag) && isdigit((int) flag[i])) i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t relays_get_gpio_by_idx(uint8_t idx) {
|
||||
if (idx>=MAX_RELAYS)
|
||||
return ERR_GPIO_INVALID;
|
||||
return s_relays[idx].gpio;
|
||||
}
|
||||
|
||||
uint8_t relays_find_by_gpio(uint8_t gpio) {
|
||||
int i;
|
||||
for (i=0; i<MAX_RELAYS; i++) {
|
||||
if (gpio==s_relays[i].gpio)
|
||||
return i;
|
||||
}
|
||||
return ERR_GPIO_INVALID;
|
||||
}
|
||||
|
||||
bool relays_set_by_gpio(uint8_t gpio, bool state) {
|
||||
return relays_set_by_idx(relays_find_by_gpio(gpio), state);
|
||||
}
|
||||
|
||||
bool relays_set_by_idx(uint8_t idx, bool state) {
|
||||
char msg[60];
|
||||
|
||||
if (idx>=MAX_RELAYS || s_relays[idx].gpio == ERR_GPIO_INVALID) {
|
||||
LOG(LL_ERROR, ("No relay at idx=%d", idx));
|
||||
return false;
|
||||
}
|
||||
s_relays[idx].last_change=mg_time();
|
||||
if (state != s_relays[idx].state) {
|
||||
|
||||
LOG(LL_INFO, ("Setting relay idx=%d gpio=%d to %s", idx, s_relays[idx].gpio, state==true?"ON":"OFF"));
|
||||
s_relays[idx].state=state;
|
||||
mgos_gpio_write(s_relays[idx].gpio, !s_relays[idx].state);
|
||||
}
|
||||
|
||||
snprintf(msg, sizeof(msg)-1, "{\"idx\": %d, \"gpio\": %d, \"state\": %d}", idx, s_relays[idx].gpio, state);
|
||||
mqtt_publish_stat("relay", msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool relays_get_by_gpio(uint8_t gpio) {
|
||||
return relays_get_by_idx(relays_find_by_gpio(gpio));
|
||||
}
|
||||
|
||||
bool relays_get_by_idx(uint8_t idx) {
|
||||
if (idx>=MAX_RELAYS || s_relays[idx].gpio == ERR_GPIO_INVALID) {
|
||||
LOG(LL_ERROR, ("No relay at idx=%d", idx));
|
||||
return false;
|
||||
}
|
||||
return s_relays[idx].state;
|
||||
}
|
||||
|
||||
float relays_get_last_change_by_gpio(uint8_t gpio) {
|
||||
return relays_get_last_change_by_idx(relays_find_by_gpio(gpio));
|
||||
}
|
||||
|
||||
float relays_get_last_change_by_idx(uint8_t idx) {
|
||||
if (idx>=MAX_RELAYS || s_relays[idx].gpio == ERR_GPIO_INVALID) {
|
||||
LOG(LL_ERROR, ("No relay at idx=%d", idx));
|
||||
return -1;
|
||||
}
|
||||
return s_relays[idx].last_change;
|
||||
}
|
133
src/rpc.c
Normal file
133
src/rpc.c
Normal file
@ -0,0 +1,133 @@
|
||||
#include "rpc.h"
|
||||
#include "relays.h"
|
||||
#include "buttons.h"
|
||||
#include "helper.h"
|
||||
|
||||
static void rpc_log(struct mg_rpc_request_info *ri, struct mg_str args) {
|
||||
LOG(LL_INFO, ("tag=%.*s src=%.*s method=%.*s args='%.*s'",
|
||||
ri->tag.len, ri->tag.p, ri->src.len, ri->src.p,
|
||||
ri->method.len, ri->method.p, args.len, args.p));
|
||||
// TODO(pim): log to MQTT
|
||||
}
|
||||
|
||||
|
||||
static void rpc_button_touch_handler(struct mg_rpc_request_info *ri, void *cb_arg, struct mg_rpc_frame_info *fi, struct mg_str args) {
|
||||
int idx;
|
||||
uint8_t gpio;
|
||||
|
||||
rpc_log(ri, args);
|
||||
|
||||
if (json_scanf(args.p, args.len, ri->args_fmt, &idx) != 1) {
|
||||
mg_rpc_send_errorf(ri, 400, "idx is required");
|
||||
ri = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ERR_GPIO_INVALID==(gpio=buttons_get_gpio_by_idx(idx))) {
|
||||
mg_rpc_send_errorf(ri, 400, "No button at idx=%d", idx);
|
||||
ri = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
buttons_touch_by_idx(idx);
|
||||
mg_rpc_send_responsef(ri, "{idx: %d, gpio: %d}", idx, gpio);
|
||||
ri = NULL;
|
||||
|
||||
(void) ri;
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
(void) args;
|
||||
}
|
||||
|
||||
static void rpc_relay_get_handler(struct mg_rpc_request_info *ri, void *cb_arg, struct mg_rpc_frame_info *fi, struct mg_str args) {
|
||||
int idx;
|
||||
uint8_t gpio;
|
||||
bool state;
|
||||
|
||||
rpc_log(ri, args);
|
||||
|
||||
if (json_scanf(args.p, args.len, ri->args_fmt, &idx) != 1) {
|
||||
mg_rpc_send_errorf(ri, 400, "idx is required");
|
||||
ri = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ERR_GPIO_INVALID==(gpio=relays_get_gpio_by_idx(idx))) {
|
||||
mg_rpc_send_errorf(ri, 400, "No relay at idx=%d", idx);
|
||||
ri = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
state=relays_get_by_idx(idx);
|
||||
mg_rpc_send_responsef(ri, "{idx: %d, gpio: %d, state: %d}", idx, gpio, state);
|
||||
ri = NULL;
|
||||
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
}
|
||||
|
||||
static void rpc_relay_set_handler(struct mg_rpc_request_info *ri, void *cb_arg, struct mg_rpc_frame_info *fi, struct mg_str args) {
|
||||
int idx;
|
||||
uint8_t gpio;
|
||||
int state;
|
||||
|
||||
rpc_log(ri, args);
|
||||
|
||||
if (json_scanf(args.p, args.len, ri->args_fmt, &idx, &state) != 2) {
|
||||
mg_rpc_send_errorf(ri, 400, "idx and state are required");
|
||||
ri = NULL;
|
||||
return;
|
||||
}
|
||||
if (state!=0 && state!=1) {
|
||||
mg_rpc_send_errorf(ri, 400, "state must be 0 or 1");
|
||||
ri = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ERR_GPIO_INVALID==(gpio=relays_get_gpio_by_idx(idx))) {
|
||||
mg_rpc_send_errorf(ri, 400, "No relay at idx=%d", idx);
|
||||
ri = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
relays_set_by_idx(idx, state);
|
||||
mg_rpc_send_responsef(ri, "{idx: %d, gpio: %d, state: %d}", idx, gpio, state);
|
||||
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
}
|
||||
|
||||
static void rpc_relay_toggle_handler(struct mg_rpc_request_info *ri, void *cb_arg, struct mg_rpc_frame_info *fi, struct mg_str args) {
|
||||
int idx;
|
||||
uint8_t gpio;
|
||||
bool state;
|
||||
|
||||
rpc_log(ri, args);
|
||||
|
||||
if (json_scanf(args.p, args.len, ri->args_fmt, &idx) != 1) {
|
||||
mg_rpc_send_errorf(ri, 400, "idx is required");
|
||||
ri = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ERR_GPIO_INVALID==(gpio=relays_get_gpio_by_idx(idx))) {
|
||||
mg_rpc_send_errorf(ri, 400, "No relay at idx=%d", idx);
|
||||
ri = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
state=!relays_get_by_idx(idx);
|
||||
relays_set_by_idx(idx, state);
|
||||
mg_rpc_send_responsef(ri, "{idx: %d, gpio: %d, state: %d}", idx, gpio, state);
|
||||
|
||||
(void) cb_arg;
|
||||
(void) fi;
|
||||
}
|
||||
|
||||
void rpc_init() {
|
||||
struct mg_rpc *c = mgos_rpc_get_global();
|
||||
mg_rpc_add_handler(c, "Button.Touch", "{idx: %d}", rpc_button_touch_handler, NULL);
|
||||
mg_rpc_add_handler(c, "Relay.Get", "{idx: %d}", rpc_relay_get_handler, NULL);
|
||||
mg_rpc_add_handler(c, "Relay.Set", "{idx: %d, state:%d}", rpc_relay_set_handler, NULL);
|
||||
mg_rpc_add_handler(c, "Relay.Toggle", "{idx: %d}", rpc_relay_toggle_handler, NULL);
|
||||
}
|
Reference in New Issue
Block a user