Add lightswitch to this firmware

This commit is contained in:
Pim van Pelt
2017-12-20 17:04:51 +01:00
parent 7ba19c8372
commit 1cbd2130db
24 changed files with 749 additions and 13 deletions

23
fs/mqtt.pem Normal file
View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDvzCCAqegAwIBAgIJAOfvQdPViBT2MA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNV
BAYTAkNIMQswCQYDVQQIDAJaSDEVMBMGA1UEBwwMQnJ1dHRpc2VsbGVuMRAwDgYD
VQQKDAdJUG5nLm5sMRUwEwYDVQQDDAxtcXR0LmlwbmcubmwxGjAYBgkqhkiG9w0B
CQEWC3BpbUBpcG5nLm5sMB4XDTE3MTIxNzEwMDMwOVoXDTIyMTIxNzEwMDMwOVow
djELMAkGA1UEBhMCQ0gxCzAJBgNVBAgMAlpIMRUwEwYDVQQHDAxCcnV0dGlzZWxs
ZW4xEDAOBgNVBAoMB0lQbmcubmwxFTATBgNVBAMMDG1xdHQuaXBuZy5ubDEaMBgG
CSqGSIb3DQEJARYLcGltQGlwbmcubmwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDH8aZRJPF/wHHLG7LDPp6zJsAv4MbgmhEMLsl4pv3Z7gp0ftsJEqVy
Wkb5ab5jNDbPgxn2djJG1j1947boCV1AnRQQY2klfwFSEi41o+ncKzu8vjYETvJA
8jz+iqh9Izw+31HNp+TxVabQ5AQnd+9sMq+enTTC/fQoY9TSSh4pMqMuT7kBE5zu
lecHGi78Mw4v2WXXUFm3otfVa4disFBw3EAZMQOeh+C9sTx+QGjDQJ2n+dvn5HUI
23xfRsaX9U8nGbf57/QyoG+r0gP83DK6Ed7Z1AtEY7VUcB7+qOloEh7lweb6h+Z3
RL3N6o5eCLPaEfIhBTbli4kCmZzTarULAgMBAAGjUDBOMB0GA1UdDgQWBBRJgIbw
FOVojHrq/OhjjTpqdWXwHjAfBgNVHSMEGDAWgBRJgIbwFOVojHrq/OhjjTpqdWXw
HjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBFI5a7ZRr5li/yYOmt
VOHcLysxVSF1uDkAwoA/L+NL4/xWp9MYo9XlJ5Ug730PztC5rhpV5fAo57X8v4P8
dWJgnoJ40ivExLrp87pzrO9p1IkDcfFXVfCyatd453iBiD1iRkwheqTld+I9mKNa
Spf8kJxODz5C6LSqr6AuBMNOJVs0rXSzHawiFxGFZKMJ0glMAuH5mnmXD6+KVhOG
MbCKQxstq4e6/0VexzPXIcqEEf1XmB9ga1AwFJm46N6u4XTM99YpvSh1/6DqRHHF
6sdWSTx++N+sQJJhBDTQ5E6Zb/AOkmT+bipU49heRk5uGgTyR0wPDWywP73ZHKas
kMT3
-----END CERTIFICATE-----

26
include/buttons.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef __BUTTONS_H
#define __BUTTONS_H
#include "mgos.h"
#include <string.h>
#define MAX_BUTTONS 8
struct button_t {
uint8_t gpio;
float last_change;
};
int buttons_init(const char *flag);
uint8_t buttons_find_by_gpio(uint8_t gpio);
bool buttons_touch_by_gpio(uint8_t gpio);
float buttons_get_last_change_by_gpio(uint8_t gpio);
bool buttons_touch_by_idx(uint8_t idx);
float buttons_get_last_change_by_idx(uint8_t idx);
uint8_t buttons_get_gpio_by_idx(uint8_t idx);
void buttons_cb(int gpio, void *args);
#endif // __BUTTONS_H

19
include/helper.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __HELPER_H
#define __HELPER_H
#include "mgos.h"
#include <ctype.h>
#include <fnmatch.h>
#define MIN_GPIO 0
#define MAX_GPIO 16
#define MQTT_TOPIC_PREFIX ""
#define MQTT_TOPIC_BROADCAST_CMD "/mongoose/broadcast"
#define MQTT_TOPIC_BROADCAST_STAT "/mongoose/broadcast/stat"
#define ERR_GPIO_INVALID 255
bool is_button_flag_valid(const char *flag);
bool is_relay_flag_valid(const char *flag);
#endif // __HELPER_H

10
include/lightswitch.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef __LIGHTSWITCH_H
#define __LIGHTSWITCH_h
#include <string.h>
#include "mgos.h"
#include "helper.h"
#include "buttons.h"
#include "relays.h"
#endif // __LIGHTSWITCH_h

10
include/mqtt.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef __MQTT_H
#define __MQTT_H
#include "mgos.h"
#include "mgos_mqtt.h"
void mqtt_init();
void mqtt_publish_stat(const char *stat, const char *msg);
#endif // __MQTT_H

26
include/relays.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef __RELAYS_H
#define __RELAYS_H
#include "mgos.h"
#include <string.h>
#define MAX_RELAYS 8
struct relay_t {
uint8_t gpio;
float last_change;
bool state;
};
int relays_init(const char *flag);
uint8_t relays_find_by_gpio(uint8_t gpio);
bool relays_set_by_gpio(uint8_t gpio, bool state);
bool relays_get_by_gpio(uint8_t gpio);
float relays_get_last_change_by_gpio(uint8_t gpio);
bool relays_set_by_idx(uint8_t idx, bool state);
bool relays_get_by_idx(uint8_t idx);
float relays_get_last_change_by_idx(uint8_t idx);
uint8_t relays_get_gpio_by_idx(uint8_t idx);
#endif // __RELAYS_H

10
include/rpc.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef __RPC_H
#define __RPC_H
#include "mgos.h"
#include "mgos_rpc.h"
#include "common/mg_str.h"
void rpc_init();
#endif // __RPC_H

15
mos.yml
View File

@ -46,6 +46,10 @@ config_schema:
- ["app.inactivity_timeout", 10] - ["app.inactivity_timeout", 10]
- ["app.battery_calibration", i, {title: "Battery ADC value at 4000mV"}] - ["app.battery_calibration", i, {title: "Battery ADC value at 4000mV"}]
- ["app.battery_calibration", 2360] - ["app.battery_calibration", 2360]
- ["app.buttons", "s", {title: "comma-separated list of button pins"}]
- ["app.buttons", "0,2"]
- ["app.relays", "s", {title: "comma-separated list of relay pins"}]
- ["app.relays", "16,12,13,14"]
- ["spi.enable", true] - ["spi.enable", true]
- ["spi.cs1_gpio", -1] - ["spi.cs1_gpio", -1]
- ["spi.cs2_gpio", -1] - ["spi.cs2_gpio", -1]
@ -53,7 +57,11 @@ config_schema:
- ["stmpe610.cs_index", 1] - ["stmpe610.cs_index", 1]
- ["stmpe610.orientation", 5] # Set to 4 for 2.4" Featherwing, set to 5 for 3.5" Featherwing - ["stmpe610.orientation", 5] # Set to 4 for 2.4" Featherwing, set to 5 for 3.5" Featherwing
- ["mqtt.enable", true] - ["mqtt.enable", true]
- ["mqtt.server", "chbtl01.paphosting.net:1883"] - ["http.enable", true]
- ["mqtt.server", "mqtt.ipng.nl:8883"]
- ["mqtt.ssl_ca_cert", "mqtt.pem"]
- ["rpc.mqtt.enable", true]
conds: conds:
- when: mos.platform == "esp32" - when: mos.platform == "esp32"
@ -83,14 +91,15 @@ libs:
- origin: https://github.com/mongoose-os-libs/rpc-service-config - origin: https://github.com/mongoose-os-libs/rpc-service-config
- origin: https://github.com/mongoose-os-libs/rpc-service-fs - origin: https://github.com/mongoose-os-libs/rpc-service-fs
- origin: https://github.com/mongoose-os-libs/prometheus-metrics - origin: https://github.com/mongoose-os-libs/prometheus-metrics
- origin: https://github.com/mongoose-os-libs/rpc-common
- origin: https://github.com/mongoose-os-libs/rpc-service-config
- origin: https://github.com/mongoose-os-libs/rpc-mqtt
- origin: https://github.com/mongoose-os-libs/mqtt - origin: https://github.com/mongoose-os-libs/mqtt
- origin: https://github.com/mongoose-os-libs/pwm - origin: https://github.com/mongoose-os-libs/pwm
- origin: https://github.com/mongoose-os-libs/adc - origin: https://github.com/mongoose-os-libs/adc
- origin: https://github.com/mongoose-os-libs/spi - origin: https://github.com/mongoose-os-libs/spi
- origin: https://github.com/mongoose-os-libs/ili9341-spi - origin: https://github.com/mongoose-os-libs/ili9341-spi
- origin: https://github.com/mongoose-os-libs/stmpe610-spi - origin: https://github.com/mongoose-os-libs/stmpe610-spi
- origin: libs/ota-http-client
- origin: libs/ota-http-server
- origin: libs/rpc-service-ota - origin: libs/rpc-service-ota

119
src/buttons.c Normal file
View 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
View 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);
}

View File

@ -6,6 +6,11 @@
#include "mongoose-touch.h" #include "mongoose-touch.h"
#include "fonts/FreeSerifBold9pt7b.h" #include "fonts/FreeSerifBold9pt7b.h"
#include "fonts/FreeMonoBold9pt7b.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; 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))); 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) 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(); backlight_init();
mgos_mqtt_sub("/s/#", mqtt_recv_cb, NULL);
tft_demo(); tft_demo();

84
src/mqtt.c Normal file
View 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
View 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
View 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);
}

View File

@ -9,7 +9,7 @@ default: $(TARGET)
all: default all: default
OBJECTS = $(patsubst %.c, %.o, $(wildcard *.c)) OBJECTS = $(patsubst %.c, %.o, $(wildcard *.c))
SRCS = frozen/frozen.c ../src/widget.c ../src/screen.c SRCS = frozen/frozen.c ../src/widget.c ../src/screen.c ../src/helper.c ../src/buttons.c ../src/relays.c ../src/mqtt.c
HEADERS = $(wildcard *.h) HEADERS = $(wildcard *.h)
%.o: %.c $(HEADERS) %.o: %.c $(HEADERS)

View File

@ -2,6 +2,7 @@
#define __MGOS_H #define __MGOS_H
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>

5
unittest/mgos_config.c Normal file
View File

@ -0,0 +1,5 @@
#include "mgos_config.h"
const char *mgos_sys_config_get_device_id() {
return "esp8266_ABCDEF";
}

6
unittest/mgos_config.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __MGOS_CONFIG_H
#define __MGOS_CONFIG_H
const char *mgos_sys_config_get_device_id();
#endif // __MGOS_CONFIG_H

31
unittest/mgos_gpio.c Normal file
View File

@ -0,0 +1,31 @@
#include "mgos.h"
#include "mgos_gpio.h"
static mgos_gpio_int_handler_f s_handler_cb;
static void *s_handler_cb_arg;
bool mgos_gpio_set_mode(int pin, enum mgos_gpio_mode mode) {
LOG(LL_INFO, ("Setting pin=%d to mode=%d", pin, mode));
return true;
}
void mgos_gpio_write(int pin, bool level) {
LOG(LL_INFO, ("Setting pin=%d to %s", pin, level?"HIGH":"LOW"));
}
bool mgos_gpio_set_button_handler(int pin, enum mgos_gpio_pull_type pull_type, enum mgos_gpio_int_mode int_mode, int debounce_ms, mgos_gpio_int_handler_f cb, void *arg) {
s_handler_cb = cb;
s_handler_cb_arg = arg;
return true;
(void) debounce_ms;
(void) int_mode;
(void) pull_type;
(void) pin;
}
void mgos_gpio_inject(int pin) {
if (s_handler_cb)
s_handler_cb(pin, s_handler_cb_arg);
}

39
unittest/mgos_gpio.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __MGOS_GPIO_H
#define __MGOS_GPIO_H
#include "mgos.h"
enum mgos_gpio_mode {
MGOS_GPIO_MODE_INPUT = 0, /* input mode */
MGOS_GPIO_MODE_OUTPUT = 1 /* output mode */
};
enum mgos_gpio_pull_type {
MGOS_GPIO_PULL_NONE = 0,
MGOS_GPIO_PULL_UP = 1, /* pin is pilled to the high voltage */
MGOS_GPIO_PULL_DOWN = 2 /* pin is pulled to the low voltage */
};
enum mgos_gpio_int_mode {
MGOS_GPIO_INT_NONE = 0,
MGOS_GPIO_INT_EDGE_POS = 1, /* positive edge */
MGOS_GPIO_INT_EDGE_NEG = 2, /* negative edge */
MGOS_GPIO_INT_EDGE_ANY = 3, /* any edge - positive or negative */
MGOS_GPIO_INT_LEVEL_HI = 4, /* high voltage level */
MGOS_GPIO_INT_LEVEL_LO = 5 /* low voltage level */
};
typedef void (*mgos_gpio_int_handler_f)(int pin, void *arg);
bool mgos_gpio_set_mode(int pin, enum mgos_gpio_mode mode);
void mgos_gpio_write(int pin, bool level);
bool mgos_gpio_set_button_handler(int pin, enum mgos_gpio_pull_type pull_type,
enum mgos_gpio_int_mode int_mode,
int debounce_ms, mgos_gpio_int_handler_f cb,
void *arg);
void mgos_gpio_inject(int pin);
#endif // __MGOS_GPIO_H

View File

@ -49,3 +49,7 @@ void mgos_clear_timer(mgos_timer_id id) {
return; return;
} }
float mg_time() {
return (float) time(NULL);
}

View File

@ -6,6 +6,8 @@
#include "mgos.h" #include "mgos.h"
#define MGOS_APP "unittest"
// mgos_log // mgos_log
enum cs_log_level { enum cs_log_level {
LL_NONE = -1, LL_NONE = -1,
@ -36,4 +38,6 @@ typedef void (*timer_callback)(void *param);
mgos_timer_id mgos_set_timer(int msecs, int flags, timer_callback cb, void *cb_arg); mgos_timer_id mgos_set_timer(int msecs, int flags, timer_callback cb, void *cb_arg);
void mgos_clear_timer(mgos_timer_id id); void mgos_clear_timer(mgos_timer_id id);
float mg_time();
#endif // __MGOS_MOCK_H #endif // __MGOS_MOCK_H

26
unittest/mgos_mqtt.c Normal file
View File

@ -0,0 +1,26 @@
#include "mgos.h"
#include "mgos_mqtt.h"
uint32_t mqtt_pub_count=0;
uint32_t mqtt_sub_count=0;
static sub_handler_t s_handler;
static void *s_handler_ud;
void mgos_mqtt_pub(char *t, char *m, int m_len, int flags, bool persist) {
LOG(LL_INFO, ("Sending topic='%s' msg='%s' persist=%s", t, m, persist?"ON":"OFF"));
mqtt_pub_count++;
}
void mgos_mqtt_sub(char *t, sub_handler_t cb, void *ud) {
LOG(LL_INFO, ("Subscribing to topic='%s'", t));
s_handler = cb;
s_handler_ud = ud;
}
void mgos_mqtt_inject(char *topic, char *msg) {
LOG(LL_INFO, ("Injecting topic='%s' msg='%s'", topic, msg));
mqtt_sub_count++;
if (s_handler)
s_handler(NULL, topic, strlen(topic), msg, strlen(msg), s_handler_ud);
}

17
unittest/mgos_mqtt.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef __MGOS_MQTT_H
#define __MGOS_MQTT_H
#include "mgos.h"
struct mg_connection;
typedef void (*sub_handler_t)(struct mg_connection *nc, const char *topic,
int topic_len, const char *msg, int msg_len,
void *ud);
void mgos_mqtt_pub(char *t, char *m, int m_len, int flags, bool persist);
void mgos_mqtt_sub(char *t, sub_handler_t cb, void *ud);
void mgos_mqtt_inject(char *topic, char *msg);
#endif // __MGOS_MQTT_H