417 lines
11 KiB
C
417 lines
11 KiB
C
/*
|
|
* Copyright 2019 by Morgan Allen
|
|
*
|
|
* This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International
|
|
* https://creativecommons.org/licenses/by-nc/4.0/
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "freertos/timers.h"
|
|
#include "freertos/event_groups.h"
|
|
#include "esp_system.h"
|
|
#include "esp_log.h"
|
|
#include "esp_console.h"
|
|
#include "esp_sleep.h"
|
|
#include "esp_vfs_dev.h"
|
|
#include "nvs_flash.h"
|
|
#include "esp_http_client.h"
|
|
#include "driver/uart.h"
|
|
#include "driver/mcpwm.h"
|
|
#include "soc/mcpwm_reg.h"
|
|
#include "soc/mcpwm_struct.h"
|
|
#include "linenoise/linenoise.h"
|
|
#include "argtable3/argtable3.h"
|
|
#include "driver/gpio.h"
|
|
#include "driver/spi_master.h"
|
|
#include "lwip/sockets.h"
|
|
#include "lwip/dns.h"
|
|
#include "lwip/netdb.h"
|
|
|
|
#include "mqtt_client.h"
|
|
|
|
#include "esp32-wifi-manager.h"
|
|
|
|
#define TAG "BTN"
|
|
|
|
#define GPIO_INPUT_IO_0 33
|
|
#define GPIO_INPUT_PIN_SEL ((1ULL<<GPIO_INPUT_IO_0))
|
|
#define ESP_INTR_FLAG_DEFAULT 0
|
|
|
|
#define CONFIG_SLEEP_TIMEOUT (60000 / portTICK_PERIOD_MS)
|
|
|
|
#define RTC_WAKE_IO RTC_GPIO8
|
|
#define RTC_WAKE_PIN GPIO_INPUT_IO_0
|
|
|
|
#define PIN_NUM_MOSI 22
|
|
#define PIN_NUM_CLK 23
|
|
|
|
#define LED_COUNT (1)
|
|
#define SPI_BUF (LED_COUNT + 2)
|
|
|
|
#define TOPIC_POWER_STATUS "stat/tasmota_E74A79/POWER"
|
|
|
|
static uint8_t id;
|
|
|
|
static EventGroupHandle_t wm_event_group;
|
|
static xTimerHandle button_timer;
|
|
static xTimerHandle sleep_timer;
|
|
static spi_device_handle_t spi;
|
|
xQueueHandle gpio_evt_queue = NULL;
|
|
|
|
enum led_states {
|
|
POR,
|
|
CONNECTING,
|
|
CONNECTED,
|
|
SHOOTING
|
|
};
|
|
|
|
enum led_states led_state;
|
|
|
|
esp_mqtt_client_handle_t client;
|
|
|
|
void enter_sleep() {
|
|
ESP_LOGI(TAG, "Enter sleep");
|
|
|
|
static spi_transaction_t trans[SPI_BUF];
|
|
|
|
int i;
|
|
for (i = 0; i < SPI_BUF; i++) {
|
|
memset(&trans[i], 0, sizeof(spi_transaction_t));
|
|
|
|
trans[i].length=8*4;
|
|
trans[i].flags = SPI_TRANS_USE_TXDATA;
|
|
}
|
|
|
|
trans[1].tx_data[0] = (0b11100000 | 0);
|
|
trans[1].tx_data[1] = 0;
|
|
trans[1].tx_data[2] = 0;
|
|
trans[1].tx_data[3] = 0;
|
|
|
|
memset(&trans[SPI_BUF - 1].tx_data, 0xFF, 4);
|
|
|
|
for(i = 0; i < SPI_BUF; i++) {
|
|
spi_device_queue_trans(spi, &trans[i], portMAX_DELAY);
|
|
}
|
|
|
|
esp_sleep_enable_ext0_wakeup(RTC_WAKE_PIN, 0);
|
|
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
|
|
|
//rtc_gpio_isolate(GPIO_INPUT_IO_0);
|
|
esp_deep_sleep_start();
|
|
}
|
|
|
|
void reset_sleep_timer() {
|
|
xTimerReset(sleep_timer, portMAX_DELAY);
|
|
xTimerStart(sleep_timer, portMAX_DELAY);
|
|
}
|
|
|
|
uint8_t led_state_new[4];
|
|
uint8_t led_state_cur[4];
|
|
|
|
void led_display_loop() {
|
|
static spi_transaction_t trans[SPI_BUF];
|
|
|
|
int i;
|
|
for (i = 0; i < SPI_BUF; i++) {
|
|
memset(&trans[i], 0, sizeof(spi_transaction_t));
|
|
|
|
trans[i].length=8*4;
|
|
trans[i].flags = SPI_TRANS_USE_TXDATA;
|
|
}
|
|
|
|
trans[1].tx_data[0] = (0b11100000 | 1);
|
|
memset(&trans[SPI_BUF - 1].tx_data, 0xFF, 4);
|
|
|
|
bool update = false;
|
|
|
|
while(true) {
|
|
/// TODO check _new vs _cur, ramping the channel value per step until they match
|
|
/// only transmit spi data if a change was made
|
|
if(led_state_new[1] != led_state_cur[1]) {
|
|
trans[1].tx_data[1] = 0;
|
|
}
|
|
trans[1].tx_data[2] = 127;
|
|
trans[1].tx_data[3] = 0;
|
|
|
|
for(i = 0; i < SPI_BUF; i++) {
|
|
spi_device_queue_trans(spi, &trans[i], portMAX_DELAY);
|
|
}
|
|
|
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
}
|
|
}
|
|
|
|
void led_control_loop() {
|
|
uint8_t v = 0;
|
|
|
|
while(true) {
|
|
if(xQueueReceive(wm_event_queue, &v, portMAX_DELAY)) {
|
|
EventBits_t ev_bits = xEventGroupGetBits(wm_event_group);
|
|
ESP_LOGI(TAG, "got event: %d", ev_bits);
|
|
|
|
// TODO XXX rewrite to only update led_state_new and let led_display_loop
|
|
// handle rendering
|
|
|
|
static spi_transaction_t trans[SPI_BUF];
|
|
|
|
int i;
|
|
for (i = 0; i < SPI_BUF; i++) {
|
|
memset(&trans[i], 0, sizeof(spi_transaction_t));
|
|
|
|
trans[i].length=8*4;
|
|
trans[i].flags = SPI_TRANS_USE_TXDATA;
|
|
}
|
|
|
|
trans[1].tx_data[0] = (0b11100000 | 1);
|
|
|
|
if(ev_bits & WIFI_CONNECTED) {
|
|
esp_mqtt_client_start(client);
|
|
|
|
trans[1].tx_data[1] = 0;
|
|
trans[1].tx_data[2] = 127;
|
|
trans[1].tx_data[3] = 0;
|
|
} else if(ev_bits & WIFI_SCANNING) {
|
|
trans[1].tx_data[1] = 0;
|
|
trans[1].tx_data[2] = 55;
|
|
trans[1].tx_data[3] = 127;
|
|
} else if(ev_bits & WIFI_CONNECTING) {
|
|
trans[1].tx_data[1] = 127;
|
|
trans[1].tx_data[2] = 127;
|
|
trans[1].tx_data[3] = 0;
|
|
} else if(ev_bits & WIFI_IDLE) {
|
|
trans[1].tx_data[1] = 0;
|
|
trans[1].tx_data[2] = 0;
|
|
trans[1].tx_data[3] = 127;
|
|
}
|
|
|
|
memset(&trans[SPI_BUF - 1].tx_data, 0xFF, 4);
|
|
|
|
for(i = 0; i < SPI_BUF; i++) {
|
|
spi_device_queue_trans(spi, &trans[i], portMAX_DELAY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void IRAM_ATTR gpio_isr_handler(void* arg)
|
|
{
|
|
uint32_t gpio_num = (uint32_t) arg;
|
|
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
|
|
}
|
|
|
|
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
|
{
|
|
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
|
|
esp_mqtt_event_handle_t event = event_data;
|
|
esp_mqtt_client_handle_t client = event->client;
|
|
int msg_id;
|
|
switch ((esp_mqtt_event_id_t)event_id) {
|
|
case MQTT_EVENT_CONNECTED:
|
|
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
|
|
esp_mqtt_client_subscribe(client, "espbutton/update", 0);
|
|
esp_mqtt_client_subscribe(client, TOPIC_POWER_STATUS, 0);
|
|
break;
|
|
case MQTT_EVENT_DISCONNECTED:
|
|
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
|
|
break;
|
|
|
|
case MQTT_EVENT_SUBSCRIBED:
|
|
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
|
break;
|
|
case MQTT_EVENT_UNSUBSCRIBED:
|
|
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
|
break;
|
|
case MQTT_EVENT_PUBLISHED:
|
|
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
|
break;
|
|
case MQTT_EVENT_DATA:
|
|
ESP_LOGI(TAG, "MQTT_EVENT_DATA [topic: %.*s]", event->topic_len, event->topic);
|
|
|
|
if(strncmp(event->topic, (const char*)TOPIC_POWER_STATUS, event->data_len) == 0) {
|
|
ESP_LOGI(TAG, "data_len: %d", event->data_len);
|
|
if(strncmp(event->data, "ON", 2) == 0) {
|
|
xTimerStop(sleep_timer, portMAX_DELAY);
|
|
} else if(strncmp(event->data, "OFF", 3) == 0) {
|
|
reset_sleep_timer();
|
|
}
|
|
}
|
|
break;
|
|
case MQTT_EVENT_ERROR:
|
|
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
|
|
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
|
|
ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
|
|
|
|
}
|
|
break;
|
|
default:
|
|
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void gpio_loop() {
|
|
int8_t state = -1;
|
|
uint32_t io_num;
|
|
|
|
esp_mqtt_client_config_t mqtt_cfg = {
|
|
.uri = "mqtt://192.168.1.1",
|
|
};
|
|
|
|
client = esp_mqtt_client_init(&mqtt_cfg);
|
|
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
|
|
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
|
|
|
|
for(;;) {
|
|
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
|
|
uint8_t level = gpio_get_level(io_num);
|
|
|
|
// don't act on state level twice
|
|
if(level == state) continue;
|
|
// track level for duplicate
|
|
state = level;
|
|
|
|
EventBits_t ev_bits = xEventGroupGetBits(wm_event_group);
|
|
ESP_LOGI(TAG, "gpio_loop: %d", ev_bits);
|
|
|
|
if(level == 0 && (ev_bits & WIFI_CONNECTED) == 0) {
|
|
ESP_LOGI(TAG, "No connectiong, connecting");
|
|
|
|
wifi_manager_scan();
|
|
|
|
continue;
|
|
}
|
|
|
|
printf("GPIO[%d] intr, val: %d\n", io_num, level);
|
|
|
|
reset_sleep_timer();
|
|
|
|
if(level == 0) {
|
|
xTimerChangePeriod(button_timer, 100 / portTICK_PERIOD_MS, portMAX_DELAY);
|
|
xTimerStart(button_timer, portMAX_DELAY);
|
|
} else {
|
|
xTimerStop(button_timer, portMAX_DELAY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void handle_button_timer() {
|
|
esp_mqtt_client_publish(client, "cmnd/tasmota_E74A79/POWER", "TOGGLE", 0, 1, 0);
|
|
}
|
|
|
|
void gpio_init() {
|
|
gpio_config_t io_conf;
|
|
|
|
io_conf.intr_type = GPIO_PIN_INTR_ANYEDGE;
|
|
io_conf.mode = GPIO_MODE_INPUT;
|
|
io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
|
|
io_conf.pull_down_en = 0;
|
|
io_conf.pull_up_en = 1;
|
|
gpio_config(&io_conf);
|
|
|
|
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
|
|
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
|
|
|
|
//create a queue to handle gpio event from isr
|
|
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
|
|
//start gpio task
|
|
|
|
button_timer = xTimerCreate("gpio_timer", 1000, pdFALSE, (void*)NULL, handle_button_timer);
|
|
sleep_timer = xTimerCreate("sleep_timer", CONFIG_SLEEP_TIMEOUT, pdFALSE, (void*)NULL, enter_sleep);
|
|
reset_sleep_timer();
|
|
|
|
xTaskCreate(gpio_loop, "gpio_loop", 2048, NULL, 10, NULL);
|
|
}
|
|
|
|
void spi_init() {
|
|
esp_err_t ret;
|
|
spi_bus_config_t buscfg={
|
|
.mosi_io_num=PIN_NUM_MOSI,
|
|
.sclk_io_num=PIN_NUM_CLK,
|
|
.quadwp_io_num=-1,
|
|
.quadhd_io_num=-1,
|
|
.max_transfer_sz=120*320*2+8
|
|
};
|
|
spi_device_interface_config_t devcfg={
|
|
.clock_speed_hz=10*1000*1000, //Clock out at 10 MHz
|
|
.mode=0, //SPI mode 0
|
|
.queue_size=7, //We want to be able to queue 7 transactions at a time
|
|
//.pre_cb=lcd_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line
|
|
};
|
|
//Initialize the SPI bus
|
|
ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
|
|
ESP_ERROR_CHECK(ret);
|
|
//Attach the LCD to the SPI bus
|
|
ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
|
|
ESP_ERROR_CHECK(ret);
|
|
|
|
static spi_transaction_t trans[SPI_BUF];
|
|
|
|
int i;
|
|
for (i = 0; i < SPI_BUF; i++) {
|
|
memset(&trans[i], 0, sizeof(spi_transaction_t));
|
|
|
|
trans[i].length=8*4;
|
|
trans[i].flags = SPI_TRANS_USE_TXDATA;
|
|
}
|
|
|
|
trans[1].tx_data[0] = (0b11100000 | 1);
|
|
trans[1].tx_data[1] = 127;
|
|
trans[1].tx_data[2] = 127;
|
|
trans[1].tx_data[3] = 127;
|
|
|
|
memset(&trans[SPI_BUF - 1].tx_data, 0xFF, 4);
|
|
|
|
for(i = 0; i < SPI_BUF; i++) {
|
|
ret = spi_device_queue_trans(spi, &trans[i], portMAX_DELAY);
|
|
assert(ret==ESP_OK);
|
|
}
|
|
}
|
|
|
|
void app_main() {
|
|
esp_err_t ret;
|
|
|
|
// Initialize NVS.
|
|
ret = nvs_flash_init();
|
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
|
|
ESP_LOGI(TAG, "Erasing flash memory");
|
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
|
ret = nvs_flash_init();
|
|
}
|
|
ESP_ERROR_CHECK( ret );
|
|
|
|
uint8_t *mac;
|
|
mac = (uint8_t *)malloc(6);
|
|
esp_efuse_mac_get_default(mac);
|
|
id = mac[5];
|
|
|
|
ESP_LOGI(TAG, "MAC: %X:%X:%X:%X:%X:%X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
|
|
wm_event_group = wifi_manager_start();
|
|
wifi_manager_reset_store();
|
|
|
|
if(wifi_manager_ap_count() == 0) {
|
|
ESP_LOGI(TAG, "Adding new AP");
|
|
|
|
if(strlen(CONFIG_WIFI_MANAGER_TEST_AP) > 0) {
|
|
wifi_manager_add_ap(CONFIG_WIFI_MANAGER_TEST_AP, CONFIG_WIFI_MANAGER_TEST_PWD);
|
|
}
|
|
}
|
|
|
|
gpio_init();
|
|
spi_init();
|
|
|
|
xTaskCreate(&led_display_loop, "led_display_loop", 4096, NULL, 6, NULL);
|
|
xTaskCreate(&led_control_loop, "led_control_loop", 4096, NULL, 6, NULL);
|
|
|
|
wifi_manager_scan();
|
|
|
|
while(true) {
|
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
}
|
|
};
|