diff options
Diffstat (limited to 'code/esp32/components/eos')
24 files changed, 2834 insertions, 0 deletions
diff --git a/code/esp32/components/eos/at_cmd.c b/code/esp32/components/eos/at_cmd.c new file mode 100644 index 0000000..ea31485 --- /dev/null +++ b/code/esp32/components/eos/at_cmd.c @@ -0,0 +1,158 @@ +#include <stdlib.h> +#include <string.h> + +#include <freertos/FreeRTOS.h> +#include <freertos/semphr.h> +#include <esp_log.h> + +#include "eos.h" + +#include "cell.h" +#include "at_cmd.h" + +static const char *TAG = "EOS ATCMD"; + +typedef struct ATURCItem { + regex_t re; + at_urc_cb_t cb; + char pattern[AT_SIZE_PATTERN]; +} ATURCItem; + +typedef struct ATURCList { + ATURCItem item[AT_SIZE_URC_LIST]; + int len; +} ATURCList; + +static ATURCList urc_list; +static ATURCItem *urc_curr; +static SemaphoreHandle_t mutex; + +static char at_buf[128]; + +void at_init(void) { + memset(&urc_list, 0, sizeof(ATURCList)); + + mutex = xSemaphoreCreateBinary(); + xSemaphoreGive(mutex); +} + +int at_urc_process(char *urc) { + regmatch_t match[AT_SIZE_NMATCH]; + at_urc_cb_t cb = NULL; + regmatch_t *m = NULL; + + xSemaphoreTake(mutex, portMAX_DELAY); + + if (urc_curr == NULL) { + int i; + + for (i=0; i<urc_list.len; i++) { + if (regexec(&urc_list.item[i].re, urc, AT_SIZE_NMATCH, match, 0) == 0) { + urc_curr = &urc_list.item[i]; + m = match; + break; + } + } + } + if (urc_curr) cb = urc_curr->cb; + + xSemaphoreGive(mutex); + + if (cb) { + int r = cb(urc, m); + + if (r != AT_URC_MORE) { + xSemaphoreTake(mutex, portMAX_DELAY); + urc_curr = NULL; + xSemaphoreGive(mutex); + } + return 1; + } + return 0; +} + +int at_urc_insert(char *pattern, at_urc_cb_t cb, int flags) { + int r; + int rv = EOS_OK; + + if (strlen(pattern) >= AT_SIZE_PATTERN) return EOS_ERR; + + xSemaphoreTake(mutex, portMAX_DELAY); + + r = regcomp(&urc_list.item[urc_list.len].re, pattern, flags); + if (r) rv = EOS_ERR; + + if (!rv && (urc_list.len == AT_SIZE_URC_LIST)) rv = EOS_ERR_FULL; + if (!rv) { + strcpy(urc_list.item[urc_list.len].pattern, pattern); + urc_list.item[urc_list.len].cb = cb; + urc_list.len++; + } + + xSemaphoreGive(mutex); + + return rv; +} + +int at_urc_delete(char *pattern) { + int i; + int rv = EOS_ERR_NOTFOUND; + + xSemaphoreTake(mutex, portMAX_DELAY); + + for (i=0; i<urc_list.len; i++) { + if ((strcmp(pattern, urc_list.item[i].pattern) == 0)) { + if (i != urc_list.len - 1) memmove(&urc_list.item[i], &urc_list.item[i + 1], (urc_list.len - i - 1) * sizeof(ATURCItem)); + urc_list.len--; + memset(&urc_list.item[urc_list.len], 0, sizeof(ATURCItem)); + if (urc_curr) { + if (urc_curr == &urc_list.item[i]) { + urc_curr = NULL; + } else if (urc_curr > &urc_list.item[i]) { + urc_curr--; + } + } + rv = EOS_OK; + break; + } + } + + xSemaphoreGive(mutex); + + return rv; +} + +int at_expect(char *str_ok, char *str_err, uint32_t timeout) { + int r; + int rv; + regex_t re_ok; + regex_t re_err; + uint32_t e = 0; + uint64_t t_start = esp_timer_get_time(); + + if (str_ok) { + rv = regcomp(&re_ok, str_ok, REG_EXTENDED | REG_NOSUB); + if (rv) return EOS_ERR; + } + + if (str_err) { + rv = regcomp(&re_err, str_err, REG_EXTENDED | REG_NOSUB); + if (rv) return EOS_ERR; + } + + do { + rv = eos_modem_readln(at_buf, sizeof(at_buf), timeout - e); + if (rv) return rv; + + if (at_buf[0] == '\0') continue; + + if (str_ok && (regexec(&re_ok, at_buf, 0, NULL, 0) == 0)) return 1; + if (str_err && (regexec(&re_err, at_buf, 0, NULL, 0) == 0)) return 0; + + r = at_urc_process(at_buf); + if (!r) ESP_LOGD(TAG, "expect unhandled URC: %s", at_buf); + + e = (uint32_t)(esp_timer_get_time() - t_start) / 1000; + if (e >= timeout) return EOS_ERR_TIMEOUT; + } while (1); +} diff --git a/code/esp32/components/eos/bq25895.c b/code/esp32/components/eos/bq25895.c new file mode 100644 index 0000000..0d1bb8d --- /dev/null +++ b/code/esp32/components/eos/bq25895.c @@ -0,0 +1,60 @@ +#include <stdlib.h> + +#include <esp_log.h> + +#include "eos.h" +#include "i2c.h" + +static const char *TAG = "EOS BQ25895"; + +#define BQ25895_ADDR 0x6A + +void eos_bq25895_set_ilim(void) { + uint8_t data = 0; + eos_i2c_write8(BQ25895_ADDR, 0, 0x1c); + eos_i2c_write8(BQ25895_ADDR, 2, 0x28); + eos_i2c_write8(BQ25895_ADDR, 7, 0x8d); + + data = eos_i2c_read8(BQ25895_ADDR, 0x00); + ESP_LOGI(TAG, "REG00: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x01); + ESP_LOGI(TAG, "REG01: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x02); + ESP_LOGI(TAG, "REG02: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x03); + ESP_LOGI(TAG, "REG03: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x04); + ESP_LOGI(TAG, "REG04: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x05); + ESP_LOGI(TAG, "REG05: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x06); + ESP_LOGI(TAG, "REG06: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x07); + ESP_LOGI(TAG, "REG07: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x08); + ESP_LOGI(TAG, "REG08: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x09); + ESP_LOGI(TAG, "REG09: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x0a); + ESP_LOGI(TAG, "REG0A: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x0b); + ESP_LOGI(TAG, "REG0B: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x0c); + ESP_LOGI(TAG, "REG0C: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x0d); + ESP_LOGI(TAG, "REG0D: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x0e); + ESP_LOGI(TAG, "REG0E: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x0f); + ESP_LOGI(TAG, "REG0F: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x10); + ESP_LOGI(TAG, "REG10: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x11); + ESP_LOGI(TAG, "REG11: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x12); + ESP_LOGI(TAG, "REG12: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x13); + ESP_LOGI(TAG, "REG13: %02x", data); + data = eos_i2c_read8(BQ25895_ADDR, 0x14); + ESP_LOGI(TAG, "REG14: %02x", data); +} diff --git a/code/esp32/components/eos/cell.c b/code/esp32/components/eos/cell.c new file mode 100644 index 0000000..2da1450 --- /dev/null +++ b/code/esp32/components/eos/cell.c @@ -0,0 +1,41 @@ +#include <stdlib.h> +#include <stdint.h> + +#include <esp_log.h> + +#include "eos.h" +#include "net.h" +#include "cell.h" + +static uint8_t cell_mode; + +static void cell_handler(unsigned char _mtype, unsigned char *buffer, uint16_t size) { + uint8_t mtype = buffer[0]; + + switch (mtype) { + case EOS_CELL_MTYPE_DATA: + if (eos_modem_get_mode() == EOS_CELL_UART_MODE_RELAY) eos_modem_write(buffer+1, size-1); + break; + case EOS_CELL_MTYPE_DATA_START: + cell_mode = eos_modem_get_mode(); + eos_modem_set_mode(EOS_CELL_UART_MODE_RELAY); + break; + case EOS_CELL_MTYPE_DATA_STOP: + eos_modem_set_mode(cell_mode); + break; + case EOS_CELL_MTYPE_AUDIO: + eos_pcm_push(buffer+1, size-1); + break; + case EOS_CELL_MTYPE_AUDIO_START: + eos_pcm_start(); + break; + case EOS_CELL_MTYPE_AUDIO_STOP: + eos_pcm_stop(); + break; + } +} + +void eos_cell_init(void) { + eos_net_set_handler(EOS_NET_MTYPE_CELL, cell_handler); +} + diff --git a/code/esp32/components/eos/cell_modem.c b/code/esp32/components/eos/cell_modem.c new file mode 100644 index 0000000..5315861 --- /dev/null +++ b/code/esp32/components/eos/cell_modem.c @@ -0,0 +1,768 @@ +#include <stdlib.h> +#include <string.h> + +#include <freertos/FreeRTOS.h> +#include <freertos/semphr.h> +#include <freertos/task.h> +#include <freertos/queue.h> +#include <netif/ppp/pppos.h> +#include <netif/ppp/pppapi.h> +#include <driver/uart.h> +#include <driver/gpio.h> +#include <esp_sleep.h> +#include <esp_log.h> + +#include "eos.h" +#include "net.h" +#include "power.h" + +#include "at_cmd.h" +#include "cell.h" + +// XXX: PPP reconnect on failure + +#define UART_SIZE_BUF 1024 +#define UART_SIZE_URC_BUF 128 + +#define UART_GPIO_TXD 16 +#define UART_GPIO_RXD 17 +#define UART_GPIO_DTR 32 +#define UART_GPIO_RI 35 + +#define MODEM_ETYPE_INIT 1 +#define MODEM_ETYPE_RI 2 + +#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) +#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) + +static const char *TAG = "EOS MODEM"; + +static SemaphoreHandle_t mutex; + +static QueueHandle_t modem_queue; +static QueueHandle_t uart_queue; + +static char uart_buf[UART_SIZE_URC_BUF]; +static unsigned int uart_buf_len; + +static uint8_t uart_mode = EOS_CELL_UART_MODE_ATCMD; +static SemaphoreHandle_t uart_mutex; + +static char ppp_apn[64]; +static char ppp_user[64]; +static char ppp_pass[64]; +static SemaphoreHandle_t ppp_mutex; + +static ppp_pcb *ppp_handle; +static struct netif ppp_netif; + +typedef enum { + UART_EEVT_MODE = UART_EVENT_MAX +} uart_eevt_type_t; + +typedef struct { + uint8_t type; +} modem_event_t; + +static void modem_atcmd_read(size_t bsize); + +static void uart_data_read(uint8_t mode) { + unsigned char *buf; + int rd; + size_t bsize; + + uart_get_buffered_data_len(UART_NUM_2, &bsize); + switch (mode) { + case EOS_CELL_UART_MODE_ATCMD: + modem_atcmd_read(bsize); + break; + + case EOS_CELL_UART_MODE_PPP: + rd = 0; + + do { + int _rd = eos_modem_read(uart_buf, MIN(bsize - rd, sizeof(uart_buf)), 100); + pppos_input_tcpip(ppp_handle, (uint8_t *)uart_buf, _rd); + rd += _rd; + } while (rd != bsize); + break; + + case EOS_CELL_UART_MODE_RELAY: + rd = 0; + + do { + int _rd; + + buf = eos_net_alloc(); + buf[0] = EOS_CELL_MTYPE_DATA; + _rd = eos_modem_read(buf + 1, MIN(bsize - rd, EOS_NET_SIZE_BUF - 1), 100); + eos_net_send(EOS_NET_MTYPE_CELL, buf, _rd + 1, 0); + rd += _rd; + } while (rd != bsize); + break; + + default: + break; + + } +} + +static void uart_event_task(void *pvParameters) { + char mode = EOS_CELL_UART_MODE_ATCMD; + char _mode = EOS_CELL_UART_MODE_ATCMD; + uart_event_t event; + + xSemaphoreTake(uart_mutex, portMAX_DELAY); + while (1) { + /* Waiting for UART event. + */ + if (xQueueReceive(uart_queue, &event, portMAX_DELAY)) { + switch (event.type) { + case UART_DATA: + /* Event of UART receiving data + */ + if (mode != EOS_CELL_UART_MODE_NONE) uart_data_read(mode); + if ((mode != _mode) && (uart_buf_len == 0)) { + if (_mode == EOS_CELL_UART_MODE_NONE) xSemaphoreGive(uart_mutex); + mode = _mode; + } + break; + + case UART_EEVT_MODE: + /* Mode change + */ + _mode = (char)event.size; + if ((_mode != mode) && ((uart_buf_len == 0) || (mode == EOS_CELL_UART_MODE_NONE))) { + if (mode == EOS_CELL_UART_MODE_NONE) { + xSemaphoreTake(uart_mutex, portMAX_DELAY); + uart_data_read(_mode); + } + if (_mode == EOS_CELL_UART_MODE_NONE) xSemaphoreGive(uart_mutex); + mode = _mode; + } + break; + + default: + break; + } + } + } + vTaskDelete(NULL); +} + +static void IRAM_ATTR uart_ri_isr_handler(void *arg) { + modem_event_t evt; + + evt.type = MODEM_ETYPE_RI; + xQueueSendFromISR(modem_queue, &evt, NULL); +} + +static int modem_atcmd_init(void) { + unsigned char *buf; + int echo_on = 0; + int tries = 3; + int r; + int rv = EOS_OK; + + rv = eos_modem_take(1000); + if (rv) return rv; + + do { + eos_modem_write("AT\r", 3); + r = at_expect("^AT", "^OK", 1000); + if (r >= 0) { + echo_on = r; + if (echo_on) { + r = at_expect("^OK", NULL, 1000); + } + break; + } + tries--; + } while (tries); + + if (tries == 0) { + eos_modem_give(); + return EOS_ERR_TIMEOUT; + } + + if (echo_on) { + eos_modem_write("AT&F\r", 5); + r = at_expect("^AT&F", NULL, 1000); + r = at_expect("^OK", NULL, 1000); + } else { + r = eos_modem_write("AT&F\r", 5); + r = at_expect("^OK", NULL, 1000); + + } + eos_modem_write("ATE0\r", 5); + r = at_expect("^ATE0", NULL, 1000); + r = at_expect("^OK", "^ERROR", 1000); + + eos_modem_write("AT+CFGRI=1\r", 11); + r = at_expect("^OK", "^ERROR", 1000); + eos_modem_write("AT+CSCLK=1\r", 11); + r = at_expect("^OK", "^ERROR", 1000); + + buf = eos_net_alloc(); + buf[0] = EOS_CELL_MTYPE_READY; + eos_net_send(EOS_NET_MTYPE_CELL, buf, 1, 0); + + eos_modem_give(); + + return EOS_OK; +} + +static void modem_atcmd_read(size_t bsize) { + char *ln_begin, *ln_end, *ln_next; + int rd = 0; + + do { + int _rd = eos_modem_read(uart_buf + uart_buf_len, MIN(bsize - rd, sizeof(uart_buf) - uart_buf_len - 1), 100); + ln_next = uart_buf + uart_buf_len; + ln_begin = uart_buf; + uart_buf_len += _rd; + rd += _rd; + uart_buf[uart_buf_len] = '\0'; + while ((ln_end = strchr(ln_next, '\n'))) { + ln_end--; + if ((ln_end > ln_begin) && (*ln_end == '\r')) { + int r; + + *ln_end = '\0'; + r = at_urc_process(ln_begin); + if (!r) { + ESP_LOGD(TAG, "unhandled URC: %s", ln_begin); + } + } + ln_next = ln_end + 2; + ln_begin = ln_next; + } + if (ln_begin != uart_buf) { + uart_buf_len -= ln_begin - uart_buf; + if (uart_buf_len) memmove(uart_buf, ln_begin, uart_buf_len); + } else if (uart_buf_len == sizeof(uart_buf) - 1) { + memcpy(uart_buf, uart_buf + sizeof(uart_buf) / 2, sizeof(uart_buf) / 2); + uart_buf_len = sizeof(uart_buf) / 2; + } + } while (rd != bsize); +} + +int modem_urc_init_handler(char *urc, regmatch_t *m) { + modem_event_t evt; + + evt.type = MODEM_ETYPE_INIT; + xQueueSend(modem_queue, &evt, portMAX_DELAY); + + return AT_URC_OK; +} + +static void modem_set_mode(uint8_t mode) { + uart_event_t evt; + + evt.type = UART_EEVT_MODE; + evt.size = mode; + xQueueSend(uart_queue, &evt, portMAX_DELAY); +} + +static void modem_event_task(void *pvParameters) { + modem_event_t evt; + + while (1) { + if (xQueueReceive(modem_queue, &evt, portMAX_DELAY)) { + switch (evt.type) { + case MODEM_ETYPE_INIT: + modem_atcmd_init(); + break; + + case MODEM_ETYPE_RI: + ESP_LOGI(TAG, "URC from RI"); + break; + + default: + break; + } + + /* Obsolete!!! + uint64_t t_start = esp_timer_get_time(); + if (xQueueReceive(modem_queue, &level, 200 / portTICK_RATE_MS) && (level == 1)) { + uint64_t t_end = esp_timer_get_time(); + ESP_LOGI(TAG, "URC:%u", (uint32_t)(t_end - t_start)); + } else { + ESP_LOGI(TAG, "RING"); + } + */ + + } + } + vTaskDelete(NULL); +} + +static char *memstr(char *mem, size_t size, char *str) { + size_t i = 0; + char *max_mem; + + if (str[0] == '\0') return NULL; + + max_mem = mem + size; + + while (mem < max_mem) { + if (*mem != str[i]) { + mem -= i; + i = 0; + } else { + if (str[i+1] == '\0') return mem - i; + i++; + } + mem++; + } + + return NULL; +} + +static uint32_t ppp_output_cb(ppp_pcb *pcb, uint8_t *data, uint32_t len, void *ctx) { + size_t rv; + + xSemaphoreTake(ppp_mutex, portMAX_DELAY); + rv = eos_modem_write(data, len); + xSemaphoreGive(ppp_mutex); + + return rv; +} + +/* PPP status callback */ +static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { + // struct netif *pppif = ppp_netif(pcb); + // LWIP_UNUSED_ARG(ctx); + + switch(err_code) { + case PPPERR_NONE: { + ESP_LOGE(TAG, "status_cb: Connect"); + break; + } + case PPPERR_PARAM: { + ESP_LOGE(TAG, "status_cb: Invalid parameter"); + break; + } + case PPPERR_OPEN: { + ESP_LOGE(TAG, "status_cb: Unable to open PPP session"); + break; + } + case PPPERR_DEVICE: { + ESP_LOGE(TAG, "status_cb: Invalid I/O device for PPP"); + break; + } + case PPPERR_ALLOC: { + ESP_LOGE(TAG, "status_cb: Unable to allocate resources"); + break; + } + case PPPERR_USER: { + ESP_LOGE(TAG, "status_cb: User interrupt"); + break; + } + case PPPERR_CONNECT: { + ESP_LOGE(TAG, "status_cb: Connection lost"); + break; + } + case PPPERR_AUTHFAIL: { + ESP_LOGE(TAG, "status_cb: Failed authentication challenge"); + break; + } + case PPPERR_PROTOCOL: { + ESP_LOGE(TAG, "status_cb: Failed to meet protocol"); + break; + } + case PPPERR_PEERDEAD: { + ESP_LOGE(TAG, "status_cb: Connection timeout"); + break; + } + case PPPERR_IDLETIMEOUT: { + ESP_LOGE(TAG, "status_cb: Idle Timeout"); + break; + } + case PPPERR_CONNECTTIME: { + ESP_LOGE(TAG, "status_cb: Max connect time reached"); + break; + } + case PPPERR_LOOPBACK: { + ESP_LOGE(TAG, "status_cb: Loopback detected"); + break; + } + default: { + ESP_LOGE(TAG, "status_cb: Unknown error code %d", err_code); + break; + } + } +} + +static int ppp_pause(uint32_t timeout, uint8_t retries) { + int done = 0; + int len = 0; + int rv = EOS_OK; + char *ok_str = NULL; + uint64_t t_start; + + timeout += 1000; + xSemaphoreTake(ppp_mutex, portMAX_DELAY); + eos_modem_flush(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + modem_set_mode(EOS_CELL_UART_MODE_NONE); + xSemaphoreTake(uart_mutex, portMAX_DELAY); + eos_modem_write("+++", 3); + t_start = esp_timer_get_time(); + + do { + len = eos_modem_read(uart_buf + uart_buf_len, sizeof(uart_buf) - uart_buf_len, 10); + if (len > 0) { + if (uart_buf_len > 5) { + ok_str = memstr(uart_buf + uart_buf_len - 5, len + 5, "\r\nOK\r\n"); + } else { + ok_str = memstr(uart_buf, len + uart_buf_len, "\r\nOK\r\n"); + } + uart_buf_len += len; + } + if (ok_str) { + pppos_input_tcpip(ppp_handle, (uint8_t *)uart_buf, ok_str - uart_buf); + ok_str += 6; + uart_buf_len -= ok_str - uart_buf; + if (uart_buf_len) memmove(uart_buf, ok_str, uart_buf_len); + done = 1; + } else if (uart_buf_len == sizeof(uart_buf)) { + pppos_input_tcpip(ppp_handle, (uint8_t *)uart_buf, sizeof(uart_buf) / 2); + memcpy(uart_buf, uart_buf + sizeof(uart_buf) / 2, sizeof(uart_buf) / 2); + uart_buf_len = sizeof(uart_buf) / 2; + } + if (timeout && !done && ((uint32_t)((esp_timer_get_time() - t_start) / 1000) > timeout)) { + if (!retries) { + modem_set_mode(EOS_CELL_UART_MODE_PPP); + xSemaphoreGive(uart_mutex); + xSemaphoreGive(ppp_mutex); + rv = EOS_ERR_TIMEOUT; + done = 1; + } else { + retries--; + eos_modem_write("+++", 3); + t_start = esp_timer_get_time(); + } + } + } while (!done); + + return rv; +} + +static int ppp_resume(void) { + int r; + int rv = EOS_OK; + + eos_modem_write("ATO\r", 4); + r = at_expect("^CONNECT", "^(ERROR|NO CARRIER)", 1000); + if (r <= 0) rv = EOS_ERR; + + modem_set_mode(EOS_CELL_UART_MODE_PPP); + xSemaphoreGive(uart_mutex); + xSemaphoreGive(ppp_mutex); + + return rv; +} + +static int ppp_setup(void) { + int r; + char cmd[64]; + int cmd_len = snprintf(cmd, sizeof(cmd), "AT+CGDCONT=1,\"IP\",\"%s\"\r", ppp_apn); + + if ((cmd_len < 0) || (cmd_len >= sizeof(cmd))) return EOS_ERR; + + modem_set_mode(EOS_CELL_UART_MODE_NONE); + r = xSemaphoreTake(uart_mutex, 1000 / portTICK_PERIOD_MS); + if (r == pdFALSE) { + modem_set_mode(uart_mode); + return EOS_ERR_TIMEOUT; + } + + eos_modem_write(cmd, cmd_len); + r = at_expect("^OK", "^ERROR", 1000); + if (r <= 0) { + modem_set_mode(uart_mode); + xSemaphoreGive(uart_mutex); + return EOS_ERR; + } + + eos_modem_write("AT+CGDATA=\"PPP\",1\r", 18); + r = at_expect("^CONNECT", "^(ERROR|\\+CME ERROR|NO CARRIER)", 1000); + if (r <= 0) { + modem_set_mode(uart_mode); + xSemaphoreGive(uart_mutex); + return EOS_ERR; + } + + ppp_handle = pppapi_pppos_create(&ppp_netif, ppp_output_cb, ppp_status_cb, NULL); + ppp_set_usepeerdns(ppp_handle, 1); + pppapi_set_default(ppp_handle); + pppapi_set_auth(ppp_handle, PPPAUTHTYPE_PAP, ppp_user, ppp_pass); + pppapi_connect(ppp_handle, 0); + + modem_set_mode(EOS_CELL_UART_MODE_PPP); + xSemaphoreGive(uart_mutex); + + return EOS_OK; +} + +static int ppp_disconnect(void) { + int rv; + + pppapi_close(ppp_handle, 0); + + rv = ppp_pause(1000, 2); + if (rv) return rv; + + eos_modem_write("ATH\r", 4); + at_expect("^OK", NULL, 1000); + + xSemaphoreGive(uart_mutex); + xSemaphoreGive(ppp_mutex); + ppp_handle = NULL; + + return EOS_OK; +} + +void eos_modem_init(void) { + /* Configure parameters of an UART driver, + * communication pins and install the driver */ + uart_config_t uart_config = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE + }; + uart_param_config(UART_NUM_2, &uart_config); + uart_set_pin(UART_NUM_2, UART_GPIO_TXD, UART_GPIO_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + uart_driver_install(UART_NUM_2, UART_SIZE_BUF, UART_SIZE_BUF, 10, &uart_queue, 0); + + // Configuration for the DTR/RI lines + gpio_config_t io_conf; + + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = ((uint64_t)1 << UART_GPIO_DTR); + io_conf.pull_up_en = 0; + io_conf.pull_down_en = 0; + gpio_config(&io_conf); + gpio_set_level(UART_GPIO_DTR, 0); + + io_conf.intr_type = GPIO_INTR_NEGEDGE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = ((uint64_t)1 << UART_GPIO_RI); + io_conf.pull_up_en = 0; + io_conf.pull_down_en = 0; + gpio_config(&io_conf); + + mutex = xSemaphoreCreateBinary(); + xSemaphoreGive(mutex); + + uart_mutex = xSemaphoreCreateBinary(); + xSemaphoreGive(uart_mutex); + + ppp_mutex = xSemaphoreCreateBinary(); + xSemaphoreGive(ppp_mutex); + + modem_queue = xQueueCreate(4, sizeof(modem_event_t)); + xTaskCreate(uart_event_task, "uart_event", EOS_TASK_SSIZE_UART, NULL, EOS_TASK_PRIORITY_UART, NULL); + xTaskCreate(modem_event_task, "modem_event", EOS_TASK_SSIZE_MODEM, NULL, EOS_TASK_PRIORITY_MODEM, NULL); + + gpio_isr_handler_add(UART_GPIO_RI, uart_ri_isr_handler, NULL); + + at_init(); + at_urc_insert("^PB DONE", modem_urc_init_handler, REG_EXTENDED); + eos_modem_set_mode(EOS_CELL_UART_MODE_ATCMD); + + ESP_LOGI(TAG, "INIT"); +} + +void eos_modem_flush(void){ + uart_wait_tx_done(UART_NUM_2, portMAX_DELAY); +} + +size_t eos_modem_write(void *data, size_t size) { + return uart_write_bytes(UART_NUM_2, (const char *)data, size); +} + +size_t eos_modem_read(void *data, size_t size, uint32_t timeout) { + return uart_read_bytes(UART_NUM_2, (uint8_t *)data, size, timeout / portTICK_RATE_MS); +} + +int eos_modem_readln(char *buf, size_t buf_size, uint32_t timeout) { + int done = 0; + int len = 0; + int rv = EOS_OK; + char *ln_end = NULL; + uint64_t t_start = esp_timer_get_time(); + + uart_buf[uart_buf_len] = '\0'; + ln_end = strchr(uart_buf, '\n'); + + do { + if (ln_end == NULL) { + len = eos_modem_read(uart_buf + uart_buf_len, sizeof(uart_buf) - uart_buf_len - 1, 10); + if (len > 0) { + uart_buf[uart_buf_len + len] = '\0'; + ln_end = strchr(uart_buf + uart_buf_len, '\n'); + uart_buf_len += len; + } + } + if (ln_end) { + ln_end--; + if ((ln_end >= uart_buf) && (*ln_end == '\r')) { + if (buf) { + if (buf_size > ln_end - uart_buf) { + memcpy(buf, uart_buf, ln_end - uart_buf); + buf[ln_end - uart_buf] = '\0'; + } else { + rv = EOS_ERR; + } + } + done = 1; + } + ln_end += 2; + uart_buf_len -= ln_end - uart_buf; + if (uart_buf_len) memmove(uart_buf, ln_end, uart_buf_len); + ln_end = NULL; + } else if (uart_buf_len == sizeof(uart_buf) - 1) { + memcpy(uart_buf, uart_buf + sizeof(uart_buf) / 2, sizeof(uart_buf) / 2); + uart_buf_len = sizeof(uart_buf) / 2; + } + if (timeout && !done && ((uint32_t)((esp_timer_get_time() - t_start) / 1000) > timeout)) { + rv = EOS_ERR_TIMEOUT; + done = 1; + } + } while (!done); + + return rv; +} + +uint8_t eos_modem_get_mode(void) { + uint8_t ret; + + xSemaphoreTake(mutex, portMAX_DELAY); + ret = uart_mode; + xSemaphoreGive(mutex); + + return ret; +} + +int eos_modem_set_mode(uint8_t mode) { + int rv = EOS_OK; + + xSemaphoreTake(mutex, portMAX_DELAY); + if (mode != uart_mode) { + if (uart_mode == EOS_CELL_UART_MODE_PPP) rv = ppp_disconnect(); + if (!rv) { + if (mode == EOS_CELL_UART_MODE_PPP) { + rv = ppp_setup(); + } else { + modem_set_mode(mode); + } + if (!rv) uart_mode = mode; + } + } + xSemaphoreGive(mutex); + + return rv; +} + +int eos_modem_take(uint32_t timeout) { + int rv = EOS_OK; + + xSemaphoreTake(mutex, portMAX_DELAY); + if (uart_mode == EOS_CELL_UART_MODE_PPP) { + rv = ppp_pause(timeout, 0); + } else { + int r; + + modem_set_mode(EOS_CELL_UART_MODE_NONE); + r = xSemaphoreTake(uart_mutex, timeout ? timeout / portTICK_PERIOD_MS : portMAX_DELAY); + if (r == pdFALSE) { + modem_set_mode(uart_mode); + rv = EOS_ERR_TIMEOUT; + } + } + + if (rv) xSemaphoreGive(mutex); + + return rv; +} + +void eos_modem_give(void) { + if (uart_mode == EOS_CELL_UART_MODE_PPP) { + int rv = ppp_resume(); + if (rv) ESP_LOGW(TAG, "PPP resume failed"); + } else { + modem_set_mode(uart_mode); + xSemaphoreGive(uart_mutex); + } + xSemaphoreGive(mutex); +} + +void eos_modem_sleep(uint8_t mode) { + int r; + + xSemaphoreTake(mutex, portMAX_DELAY); + modem_set_mode(EOS_CELL_UART_MODE_NONE); + r = xSemaphoreTake(uart_mutex, 1000 / portTICK_PERIOD_MS); + if (r == pdFALSE) { + ESP_LOGE(TAG, "Obtaining mutex before sleep failed"); + } + gpio_set_level(UART_GPIO_DTR, 1); + if (mode == EOS_PWR_SMODE_DEEP) { + gpio_hold_en(UART_GPIO_DTR); + } +} + +void eos_modem_wake(uint8_t source, uint8_t mode) { + if (source == EOS_PWR_WAKE_UART) { + modem_event_t evt; + + evt.type = MODEM_ETYPE_RI; + xQueueSend(modem_queue, &evt, portMAX_DELAY); + } + + if (mode != EOS_PWR_SMODE_DEEP) { + gpio_set_intr_type(UART_GPIO_RI, GPIO_INTR_NEGEDGE); + gpio_isr_handler_add(UART_GPIO_RI, uart_ri_isr_handler, NULL); + gpio_set_level(UART_GPIO_DTR, 0); + + modem_set_mode(uart_mode); + xSemaphoreGive(uart_mutex); + xSemaphoreGive(mutex); + } else { + gpio_hold_dis(UART_GPIO_DTR); + } +} + +void eos_ppp_set_apn(char *apn) { + xSemaphoreTake(mutex, portMAX_DELAY); + strncpy(ppp_apn, apn, sizeof(ppp_apn) - 1); + xSemaphoreGive(mutex); +} + +void eos_ppp_set_auth(char *user, char *pass) { + xSemaphoreTake(mutex, portMAX_DELAY); + strncpy(ppp_user, user, sizeof(ppp_user) - 1); + strncpy(ppp_pass, pass, sizeof(ppp_pass) - 1); + xSemaphoreGive(mutex); +} + +int eos_ppp_connect(void) { + return eos_modem_set_mode(EOS_CELL_UART_MODE_PPP); +} + +int eos_ppp_disconnect(void) { + int rv = eos_modem_set_mode(EOS_CELL_UART_MODE_ATCMD); + + xSemaphoreTake(mutex, portMAX_DELAY); + memset(ppp_apn, 0, sizeof(ppp_apn)); + memset(ppp_user, 0, sizeof(ppp_user)); + memset(ppp_pass, 0, sizeof(ppp_pass)); + xSemaphoreGive(mutex); + + return rv; +}
\ No newline at end of file diff --git a/code/esp32/components/eos/cell_pcm.c b/code/esp32/components/eos/cell_pcm.c new file mode 100644 index 0000000..c1419b8 --- /dev/null +++ b/code/esp32/components/eos/cell_pcm.c @@ -0,0 +1,258 @@ +#include <stdlib.h> +#include <string.h> + +#include <freertos/FreeRTOS.h> +#include <freertos/semphr.h> +#include <freertos/task.h> +#include <freertos/queue.h> +#include <driver/i2s.h> +#include <driver/gpio.h> +#include <esp_log.h> + +#include "eos.h" +#include "net.h" +#include "msgq.h" +#include "cell.h" + +#define PCM_MIC_WM 128 +#define PCM_HOLD_CNT_TX 3 +#define PCM_HOLD_CNT_RX 3 +#define PCM_SIZE_BUFQ 4 +#define PCM_SIZE_BUF (PCM_MIC_WM * 4) + +#define PCM_GPIO_BCK 33 +#define PCM_GPIO_WS 4 +#define PCM_GPIO_DIN 34 +#define PCM_GPIO_DOUT 2 + +#define PCM_ETYPE_WRITE 1 + +static EOSBufQ pcm_buf_q; +static unsigned char *pcm_bufq_array[PCM_SIZE_BUFQ]; + +static EOSMsgQ pcm_evt_q; +static EOSMsgItem pcm_evtq_array[PCM_SIZE_BUFQ]; +static char pcm_hold_tx; + +static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1}; + +static QueueHandle_t i2s_queue; +static SemaphoreHandle_t mutex; + +static const char *TAG = "EOS PCM"; + +static void i2s_event_task(void *pvParameters) { + i2s_event_t event; + unsigned char *buf; + unsigned char _type; + size_t bytes_w; + ssize_t bytes_r; + uint16_t bytes_e; + ssize_t hold_bytes_r = 0; + unsigned char *hold_buf = NULL; + char hold_cnt = 0; + + while (1) { + // Waiting for I2S event. + if (xQueueReceive(i2s_queue, &event, portMAX_DELAY)) { + switch (event.type) { + case I2S_EVENT_RX_DONE: + // Event of I2S receiving data + if (!hold_cnt) { + buf = eos_net_alloc(); + buf[0] = EOS_CELL_MTYPE_AUDIO; + bytes_r = eos_pcm_read(buf + 1, PCM_MIC_WM); + eos_net_send(EOS_NET_MTYPE_CELL, buf, bytes_r + 1, 0); + } else { + hold_cnt--; + if (hold_buf == NULL) { + hold_buf = eos_net_alloc(); + hold_buf[0] = EOS_CELL_MTYPE_AUDIO; + } + if (1 + hold_bytes_r + PCM_MIC_WM <= EOS_NET_SIZE_BUF) hold_bytes_r += eos_pcm_read(hold_buf + 1 + hold_bytes_r, PCM_MIC_WM); + if (hold_cnt == 0) { + eos_net_send(EOS_NET_MTYPE_CELL, hold_buf, hold_bytes_r + 1, 0); + hold_bytes_r = 0; + hold_buf = NULL; + } + } + + buf = NULL; + xSemaphoreTake(mutex, portMAX_DELAY); + if (pcm_hold_tx && (eos_msgq_len(&pcm_evt_q) == PCM_HOLD_CNT_TX)) pcm_hold_tx = 0; + if (!pcm_hold_tx) eos_msgq_pop(&pcm_evt_q, &_type, &buf, &bytes_e, NULL); + xSemaphoreGive(mutex); + + if (buf) { + i2s_write(I2S_NUM_0, (const void *)buf, bytes_e, &bytes_w, portMAX_DELAY); + xSemaphoreTake(mutex, portMAX_DELAY); + eos_bufq_push(&pcm_buf_q, buf); + xSemaphoreGive(mutex); + } + break; + case I2S_EVENT_DMA_ERROR: + ESP_LOGE(TAG, "*** I2S DMA ERROR ***"); + break; + case I2S_EVENT_MAX: + hold_cnt = PCM_HOLD_CNT_RX; + break; + default: + break; + } + } + } + vTaskDelete(NULL); +} + +void eos_pcm_init(void) { + int i; + + i2s_config_t i2s_config = { + .mode = I2S_MODE_SLAVE | I2S_MODE_TX | I2S_MODE_RX, + .sample_rate = 32000, + .bits_per_sample = 32, + .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, + .communication_format = I2S_COMM_FORMAT_I2S, + .dma_buf_count = 4, + .dma_buf_len = PCM_MIC_WM, + .use_apll = true, + .fixed_mclk = 2048000 * 8, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 + }; + + i2s_pin_config_t pin_config = { + .bck_io_num = PCM_GPIO_BCK, + .ws_io_num = PCM_GPIO_WS, + .data_in_num = PCM_GPIO_DIN, + .data_out_num = PCM_GPIO_DOUT + }; + i2s_driver_install(I2S_NUM_0, &i2s_config, 10, &i2s_queue); //install and start i2s driver + i2s_stop(I2S_NUM_0); + i2s_set_pin(I2S_NUM_0, &pin_config); + gpio_matrix_in(pin_config.ws_io_num, I2S0I_WS_IN_IDX, 1); + gpio_matrix_in(pin_config.bck_io_num, I2S0I_BCK_IN_IDX, 1); + ESP_LOGI(TAG, "TX FIFO:%d TX CHAN:%d RX FIFO:%d RX CHAN:%d", I2S[I2S_NUM_0]->fifo_conf.tx_fifo_mod, I2S[I2S_NUM_0]->conf_chan.tx_chan_mod, I2S[I2S_NUM_0]->fifo_conf.rx_fifo_mod, I2S[I2S_NUM_0]->conf_chan.rx_chan_mod); + + I2S[I2S_NUM_0]->fifo_conf.tx_fifo_mod = 2; + I2S[I2S_NUM_0]->conf_chan.tx_chan_mod = 0; + + I2S[I2S_NUM_0]->fifo_conf.rx_fifo_mod = 3; + I2S[I2S_NUM_0]->conf_chan.rx_chan_mod = 1; + // I2S[I2S_NUM_0]->conf.tx_mono = 1; + I2S[I2S_NUM_0]->conf.rx_mono = 1; + // I2S[I2S_NUM_0]->timing.tx_dsync_sw = 1 + // I2S[I2S_NUM_0]->timing.rx_dsync_sw = 1 + // I2S[I2S_NUM_0]->conf.sig_loopback = 0; + + // I2S[I2S_NUM_0]->timing.tx_bck_in_inv = 1; + + eos_msgq_init(&pcm_evt_q, pcm_evtq_array, PCM_SIZE_BUFQ); + eos_bufq_init(&pcm_buf_q, pcm_bufq_array, PCM_SIZE_BUFQ); + for (i=0; i<PCM_SIZE_BUFQ; i++) { + eos_bufq_push(&pcm_buf_q, malloc(PCM_SIZE_BUF)); + } + + mutex = xSemaphoreCreateBinary(); + xSemaphoreGive(mutex); + + // Create a task to handle i2s event from ISR + xTaskCreate(i2s_event_task, "i2s_event", EOS_TASK_SSIZE_I2S, NULL, EOS_TASK_PRIORITY_I2S, NULL); + ESP_LOGI(TAG, "INIT"); +} + +ssize_t eos_pcm_read(unsigned char *data, size_t size) { + static unsigned char buf[PCM_SIZE_BUF]; + size_t bytes_r; + int i; + + if (size > PCM_MIC_WM) return EOS_ERR; + + esp_err_t ret = i2s_read(I2S_NUM_0, (void *)buf, size * 4, &bytes_r, portMAX_DELAY); + if (ret != ESP_OK) return EOS_ERR; + + for (i=0; i<size/2; i++) { + data[i * 2] = buf[i * 8 + 3]; + data[i * 2 + 1] = buf[i * 8 + 2]; + } + + return bytes_r / 4; +} + +ssize_t eos_pcm_expand(unsigned char *buf, unsigned char *data, size_t size) { + int i; + + if (size > PCM_MIC_WM) return EOS_ERR; + + memset(buf, 0, PCM_SIZE_BUF); + for (i=0; i<size/2; i++) { + buf[i * 8 + 3] = data[i * 2]; + buf[i * 8 + 2] = data[i * 2 + 1]; + } + + return size * 4; +} + +int eos_pcm_push(unsigned char *data, size_t size) { + unsigned char *buf = NULL; + ssize_t esize; + int rv; + + if (size > PCM_MIC_WM) return EOS_ERR; + + xSemaphoreTake(mutex, portMAX_DELAY); + if (pcm_hold_tx && (eos_msgq_len(&pcm_evt_q) == PCM_HOLD_CNT_TX)) { + unsigned char _type; + uint16_t _len; + + eos_msgq_pop(&pcm_evt_q, &_type, &buf, &_len, NULL); + } else { + buf = eos_bufq_pop(&pcm_buf_q); + } + xSemaphoreGive(mutex); + + if (buf == NULL) return EOS_ERR_EMPTY; + + esize = eos_pcm_expand(buf, data, size); + if (esize < 0) { + xSemaphoreTake(mutex, portMAX_DELAY); + eos_bufq_push(&pcm_buf_q, buf); + xSemaphoreGive(mutex); + return esize; + } + + xSemaphoreTake(mutex, portMAX_DELAY); + rv = eos_msgq_push(&pcm_evt_q, PCM_ETYPE_WRITE, buf, esize, 0); + if (rv) eos_bufq_push(&pcm_buf_q, buf); + xSemaphoreGive(mutex); + + return rv; +} + +void eos_pcm_start(void) { + i2s_event_t evt; + + xSemaphoreTake(mutex, portMAX_DELAY); + while (1) { + unsigned char _type; + unsigned char *buf; + uint16_t len; + + eos_msgq_pop(&pcm_evt_q, &_type, &buf, &len, NULL); + if (buf) { + eos_bufq_push(&pcm_buf_q, buf); + } else { + break; + } + } + pcm_hold_tx = 1; + xSemaphoreGive(mutex); + + evt.type = I2S_EVENT_MAX; /* my type */ + xQueueSend(i2s_queue, &evt, portMAX_DELAY); + i2s_zero_dma_buffer(I2S_NUM_0); + i2s_start(I2S_NUM_0); +} + +void eos_pcm_stop(void) { + i2s_stop(I2S_NUM_0); +} diff --git a/code/esp32/components/eos/component.mk b/code/esp32/components/eos/component.mk new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/code/esp32/components/eos/component.mk diff --git a/code/esp32/components/eos/drv2605l.c b/code/esp32/components/eos/drv2605l.c new file mode 100644 index 0000000..3944289 --- /dev/null +++ b/code/esp32/components/eos/drv2605l.c @@ -0,0 +1,82 @@ +#include <stdlib.h> + +#include <esp_log.h> + +#include "eos.h" +#include "i2c.h" + +static const char *TAG = "EOS DRV2605L"; + +#define DRV2605L_ADDR 0x5A + +#define DRV2605_REG_STATUS 0x00 ///< Status register +#define DRV2605_REG_MODE 0x01 ///< Mode register +#define DRV2605_MODE_INTTRIG 0x00 ///< Internal trigger mode +#define DRV2605_MODE_EXTTRIGEDGE 0x01 ///< External edge trigger mode +#define DRV2605_MODE_EXTTRIGLVL 0x02 ///< External level trigger mode +#define DRV2605_MODE_PWMANALOG 0x03 ///< PWM/Analog input mode +#define DRV2605_MODE_AUDIOVIBE 0x04 ///< Audio-to-vibe mode +#define DRV2605_MODE_REALTIME 0x05 ///< Real-time playback (RTP) mode +#define DRV2605_MODE_DIAGNOS 0x06 ///< Diagnostics mode +#define DRV2605_MODE_AUTOCAL 0x07 ///< Auto calibration mode + +#define DRV2605_REG_RTPIN 0x02 ///< Real-time playback input register +#define DRV2605_REG_LIBRARY 0x03 ///< Waveform library selection register +#define DRV2605_REG_WAVESEQ1 0x04 ///< Waveform sequence register 1 +#define DRV2605_REG_WAVESEQ2 0x05 ///< Waveform sequence register 2 +#define DRV2605_REG_WAVESEQ3 0x06 ///< Waveform sequence register 3 +#define DRV2605_REG_WAVESEQ4 0x07 ///< Waveform sequence register 4 +#define DRV2605_REG_WAVESEQ5 0x08 ///< Waveform sequence register 5 +#define DRV2605_REG_WAVESEQ6 0x09 ///< Waveform sequence register 6 +#define DRV2605_REG_WAVESEQ7 0x0A ///< Waveform sequence register 7 +#define DRV2605_REG_WAVESEQ8 0x0B ///< Waveform sequence register 8 + +#define DRV2605_REG_GO 0x0C ///< Go register +#define DRV2605_REG_OVERDRIVE 0x0D ///< Overdrive time offset register +#define DRV2605_REG_SUSTAINPOS 0x0E ///< Sustain time offset, positive register +#define DRV2605_REG_SUSTAINNEG 0x0F ///< Sustain time offset, negative register +#define DRV2605_REG_BREAK 0x10 ///< Brake time offset register +#define DRV2605_REG_AUDIOCTRL 0x11 ///< Audio-to-vibe control register +#define DRV2605_REG_AUDIOLVL 0x12 ///< Audio-to-vibe minimum input level register +#define DRV2605_REG_AUDIOMAX 0x13 ///< Audio-to-vibe maximum input level register +#define DRV2605_REG_AUDIOOUTMIN 0x14 ///< Audio-to-vibe minimum output drive register +#define DRV2605_REG_AUDIOOUTMAX 0x15 ///< Audio-to-vibe maximum output drive register +#define DRV2605_REG_RATEDV 0x16 ///< Rated voltage register +#define DRV2605_REG_CLAMPV 0x17 ///< Overdrive clamp voltage register +#define DRV2605_REG_AUTOCALCOMP 0x18 ///< Auto-calibration compensation result register +#define DRV2605_REG_AUTOCALEMP 0x19 ///< Auto-calibration back-EMF result register +#define DRV2605_REG_FEEDBACK 0x1A ///< Feedback control register +#define DRV2605_REG_CONTROL1 0x1B ///< Control1 Register +#define DRV2605_REG_CONTROL2 0x1C ///< Control2 Register +#define DRV2605_REG_CONTROL3 0x1D ///< Control3 Register +#define DRV2605_REG_CONTROL4 0x1E ///< Control4 Register +#define DRV2605_REG_VBAT 0x21 ///< Vbat voltage-monitor register +#define DRV2605_REG_LRARESON 0x22 ///< LRA resonance-period register + +void eos_drv2605l_test(void) { + uint8_t data = 0; + + int ret = eos_i2c_read(DRV2605L_ADDR, DRV2605_REG_STATUS, &data, 1); + if (ret) ESP_LOGE(TAG, "I2C ERROR!"); + + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_MODE, 0x00); // out of standby + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_RTPIN, 0x00); // no real-time-playback + + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_WAVESEQ1, 1); // strong click + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_WAVESEQ2, 0); // end sequence + + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_OVERDRIVE, 0); // no overdrive + + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_SUSTAINPOS, 0); + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_SUSTAINNEG, 0); + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_BREAK, 0); + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_AUDIOMAX, 0x64); + + // LRA open loop + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_FEEDBACK, eos_i2c_read8(DRV2605L_ADDR, DRV2605_REG_FEEDBACK) | 0x80); // turn on N_ERM_LRA + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_CONTROL3, eos_i2c_read8(DRV2605L_ADDR, DRV2605_REG_CONTROL3) | 0x01); // turn on LRA_OPEN_LOOP + + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_LIBRARY, 6); // set LRA library + eos_i2c_write8(DRV2605L_ADDR, DRV2605_REG_GO, 1); // go + +} diff --git a/code/esp32/components/eos/i2c.c b/code/esp32/components/eos/i2c.c new file mode 100644 index 0000000..5b8fcc7 --- /dev/null +++ b/code/esp32/components/eos/i2c.c @@ -0,0 +1,103 @@ +#include <stdlib.h> + +#include <esp_log.h> +#include <driver/i2c.h> + +#include "eos.h" + +static const char *TAG = "EOS I2C"; + +#define I2C_MASTER_NUM I2C_NUM_0 +#define I2C_MASTER_FREQ_HZ 100000 +#define I2C_MASTER_GPIO_SCL 25 +#define I2C_MASTER_GPIO_SDA 26 + +#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ +#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ +#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ +#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ +#define ACK_VAL 0x0 /*!< I2C ack value */ +#define NCK_VAL 0x1 /*!< I2C nack value */ + +/** + * @brief i2c initialization + */ + +void eos_i2c_init(void) { + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = I2C_MASTER_GPIO_SDA; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_io_num = I2C_MASTER_GPIO_SCL; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = I2C_MASTER_FREQ_HZ; + i2c_param_config(I2C_MASTER_NUM, &conf); + i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); + ESP_LOGI(TAG, "INIT"); +} + +/** + * @brief i2c read + */ + +int eos_i2c_read(uint8_t addr, uint8_t reg, uint8_t *data, size_t len) { + int i, ret; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, addr << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg, ACK_CHECK_EN); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, addr << 1 | I2C_MASTER_READ, ACK_CHECK_EN); + for (i=0; i < len - 1; i++) { + i2c_master_read_byte(cmd, data+i, ACK_VAL); + } + i2c_master_read_byte(cmd, data+i, NCK_VAL); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if (ret != ESP_OK) { + return EOS_ERR; + } + return EOS_OK; +} + +/** + * @brief i2c read8 + */ + +uint8_t eos_i2c_read8(uint8_t addr, uint8_t reg) { + uint8_t data; + eos_i2c_read(addr, reg, &data, 1); + return data; +} + +/** + * @brief i2c write + */ + +int eos_i2c_write(uint8_t addr, uint8_t reg, uint8_t *data, size_t len) { + int i, ret; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, addr << 1 | I2C_MASTER_WRITE, ACK_CHECK_EN); + i2c_master_write_byte(cmd, reg, ACK_CHECK_EN); + for (i=0; i < len; i++) { + i2c_master_write_byte(cmd, *(data+i), ACK_CHECK_EN); + } + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if (ret != ESP_OK) { + return EOS_ERR; + } + return EOS_OK; +} + +/** + * @brief i2c write8 + */ + +void eos_i2c_write8(uint8_t addr, uint8_t reg, uint8_t data) { + eos_i2c_write(addr, reg, &data, 1); +} + diff --git a/code/esp32/components/eos/include/at_cmd.h b/code/esp32/components/eos/include/at_cmd.h new file mode 100644 index 0000000..ca46e23 --- /dev/null +++ b/code/esp32/components/eos/include/at_cmd.h @@ -0,0 +1,19 @@ +#include <sys/types.h> +#include <stdint.h> +#include <regex.h> + +#define AT_SIZE_NMATCH 4 +#define AT_SIZE_PATTERN 64 + +#define AT_SIZE_URC_LIST 16 + +#define AT_URC_OK 0 +#define AT_URC_MORE 1 + +typedef int (*at_urc_cb_t) (char *, regmatch_t[]); + +void at_init(void); +int at_urc_process(char *urc); +int at_urc_insert(char *pattern, at_urc_cb_t cb, int flags); +int at_urc_delete(char *pattern); +int at_expect(char *str_ok, char *str_err, uint32_t timeout);
\ No newline at end of file diff --git a/code/esp32/components/eos/include/bq25895.h b/code/esp32/components/eos/include/bq25895.h new file mode 100644 index 0000000..b5a7f92 --- /dev/null +++ b/code/esp32/components/eos/include/bq25895.h @@ -0,0 +1,3 @@ +#include <stdint.h> + +void eos_bq25895_set_ilim(void);
\ No newline at end of file diff --git a/code/esp32/components/eos/include/cell.h b/code/esp32/components/eos/include/cell.h new file mode 100644 index 0000000..23adecf --- /dev/null +++ b/code/esp32/components/eos/include/cell.h @@ -0,0 +1,47 @@ +#include <sys/types.h> + +#define EOS_CELL_MTYPE_READY 0 +#define EOS_CELL_MTYPE_DATA 1 +#define EOS_CELL_MTYPE_AUDIO 2 + +#define EOS_CELL_MTYPE_DATA_START 4 +#define EOS_CELL_MTYPE_DATA_STOP 5 + +#define EOS_CELL_MTYPE_AUDIO_START 6 +#define EOS_CELL_MTYPE_AUDIO_STOP 7 + +#define EOS_CELL_UART_MODE_NONE 0 +#define EOS_CELL_UART_MODE_ATCMD 1 +#define EOS_CELL_UART_MODE_PPP 2 +#define EOS_CELL_UART_MODE_RELAY 3 + +void eos_pcm_init(void); + +ssize_t eos_pcm_read(unsigned char *data, size_t size); +int eos_pcm_push(unsigned char *data, size_t size); +void eos_pcm_start(void); +void eos_pcm_stop(void); + +void eos_modem_init(void); + +void eos_modem_flush(void); +size_t eos_modem_write(void *data, size_t size); +size_t eos_modem_read(void *data, size_t size, uint32_t timeout); +int eos_modem_readln(char *buf, size_t buf_size, uint32_t timeout); +int eos_modem_resp(char *ok_str, char *err_str, uint32_t timeout); + +uint8_t eos_modem_get_mode(void); +int eos_modem_set_mode(uint8_t mode); +int eos_modem_take(uint32_t timeout); +void eos_modem_give(void); + +void eos_modem_sleep(uint8_t mode); +void eos_modem_wake(uint8_t source, uint8_t mode); + +void eos_ppp_set_apn(char *apn); +void eos_ppp_set_auth(char *user, char *pass); + +int eos_ppp_connect(void); +int eos_ppp_disconnect(void); + +void eos_cell_init(void); diff --git a/code/esp32/components/eos/include/drv2605l.h b/code/esp32/components/eos/include/drv2605l.h new file mode 100644 index 0000000..de222e4 --- /dev/null +++ b/code/esp32/components/eos/include/drv2605l.h @@ -0,0 +1,3 @@ +#include <stdint.h> + +void eos_drv2605l_test(void);
\ No newline at end of file diff --git a/code/esp32/components/eos/include/eos.h b/code/esp32/components/eos/include/eos.h new file mode 100644 index 0000000..0e660fb --- /dev/null +++ b/code/esp32/components/eos/include/eos.h @@ -0,0 +1,23 @@ +#define EOS_OK 0 +#define EOS_ERR -1 +#define EOS_ERR_TIMEOUT -2 +#define EOS_ERR_BUSY -3 + +#define EOS_ERR_FULL -10 +#define EOS_ERR_EMPTY -11 +#define EOS_ERR_NOTFOUND -12 + +#define EOS_TASK_PRIORITY_UART 1 +#define EOS_TASK_PRIORITY_MODEM 1 +#define EOS_TASK_PRIORITY_I2S 1 +#define EOS_TASK_PRIORITY_NET_XCHG 1 +#define EOS_TASK_PRIORITY_UDP_RCVR 1 +#define EOS_TASK_PRIORITY_PWR 1 + +#define EOS_TASK_SSIZE_UART 4096 +#define EOS_TASK_SSIZE_MODEM 4096 +#define EOS_TASK_SSIZE_I2S 4096 +#define EOS_TASK_SSIZE_NET_XCHG 8192 +#define EOS_TASK_SSIZE_UDP_RCVR 4096 +#define EOS_TASK_SSIZE_PWR 4096 + diff --git a/code/esp32/components/eos/include/i2c.h b/code/esp32/components/eos/include/i2c.h new file mode 100644 index 0000000..144f5e1 --- /dev/null +++ b/code/esp32/components/eos/include/i2c.h @@ -0,0 +1,9 @@ +#include <sys/types.h> +#include <stdint.h> + +void eos_i2c_init(void); + +int eos_i2c_read(uint8_t addr, uint8_t reg, uint8_t *data, size_t len); +uint8_t eos_i2c_read8(uint8_t addr, uint8_t reg); +int eos_i2c_write(uint8_t addr, uint8_t reg, uint8_t *data, size_t len); +void eos_i2c_write8(uint8_t addr, uint8_t reg, uint8_t data); diff --git a/code/esp32/components/eos/include/msgq.h b/code/esp32/components/eos/include/msgq.h new file mode 100644 index 0000000..86bb067 --- /dev/null +++ b/code/esp32/components/eos/include/msgq.h @@ -0,0 +1,32 @@ +#include <stdint.h> + +typedef struct EOSMsgItem { + unsigned char type; + unsigned char *buffer; + uint16_t len; + uint8_t flags; +} EOSMsgItem; + +typedef struct EOSMsgQ { + uint8_t idx_r; + uint8_t idx_w; + uint8_t size; + EOSMsgItem *array; +} EOSMsgQ; + +void eos_msgq_init(EOSMsgQ *msgq, EOSMsgItem *array, uint8_t size); +int eos_msgq_push(EOSMsgQ *msgq, unsigned char type, unsigned char *buffer, uint16_t len, uint8_t flags); +void eos_msgq_pop(EOSMsgQ *msgq, unsigned char *type, unsigned char **buffer, uint16_t *len, uint8_t *flags); +uint8_t eos_msgq_len(EOSMsgQ *msgq); + +typedef struct EOSBufQ { + uint8_t idx_r; + uint8_t idx_w; + uint8_t size; + unsigned char **array; +} EOSBufQ; + +void eos_bufq_init(EOSBufQ *bufq, unsigned char **array, uint8_t size); +int eos_bufq_push(EOSBufQ *bufq, unsigned char *buffer); +unsigned char *eos_bufq_pop(EOSBufQ *bufq); +uint8_t eos_bufq_len(EOSBufQ *bufq); diff --git a/code/esp32/components/eos/include/net.h b/code/esp32/components/eos/include/net.h new file mode 100644 index 0000000..54bad6d --- /dev/null +++ b/code/esp32/components/eos/include/net.h @@ -0,0 +1,34 @@ +#include <stdint.h> + +/* common */ +#define EOS_NET_SIZE_BUF 1500 + +#define EOS_NET_MTYPE_SOCK 1 +#define EOS_NET_MTYPE_POWER 4 + +#define EOS_NET_MTYPE_WIFI 5 +#define EOS_NET_MTYPE_CELL 6 +#define EOS_NET_MTYPE_SIP 7 +#define EOS_NET_MTYPE_APP 8 + +#define EOS_NET_MAX_MTYPE 8 + +#define EOS_NET_MTYPE_FLAG_ONEW 0x80 + +/* esp32 specific */ +#define EOS_NET_SIZE_BUFQ 4 +#define EOS_NET_SIZE_SNDQ 4 + +#define EOS_NET_FLAG_BFREE 0x1 +#define EOS_NET_FLAG_BCOPY 0x2 + +typedef void (*eos_net_fptr_t) (unsigned char, unsigned char *, uint16_t); + +void eos_net_init(void); + +unsigned char *eos_net_alloc(void); +void eos_net_free(unsigned char *buf); +int eos_net_send(unsigned char mtype, unsigned char *buffer, uint16_t len, uint8_t flags); +void eos_net_set_handler(unsigned char mtype, eos_net_fptr_t handler); +void eos_net_sleep_done(uint8_t mode); +void eos_net_wake(uint8_t source, uint8_t mode); diff --git a/code/esp32/components/eos/include/power.h b/code/esp32/components/eos/include/power.h new file mode 100644 index 0000000..0a57b19 --- /dev/null +++ b/code/esp32/components/eos/include/power.h @@ -0,0 +1,20 @@ +#include <stdint.h> + +#define EOS_PWR_MTYPE_BUTTON 0 + +#define EOS_PWR_WAKE_RST 0 +#define EOS_PWR_WAKE_BTN 1 +#define EOS_PWR_WAKE_NET 2 +#define EOS_PWR_WAKE_MSG 3 +#define EOS_PWR_WAKE_UART 4 + +#define EOS_PWR_SMODE_LIGHT 1 +#define EOS_PWR_SMODE_DEEP 2 + +void eos_power_init(void); + +void eos_power_wait4init(void); +uint8_t eos_power_wakeup_cause(void); +void eos_power_sleep(void); +void eos_power_wake(uint8_t source); +void eos_power_net_ready(void);
\ No newline at end of file diff --git a/code/esp32/components/eos/include/sock.h b/code/esp32/components/eos/include/sock.h new file mode 100644 index 0000000..7e937cb --- /dev/null +++ b/code/esp32/components/eos/include/sock.h @@ -0,0 +1,18 @@ +#include <stdint.h> + +#define EOS_SOCK_MTYPE_PKT 0 +#define EOS_SOCK_MTYPE_OPEN_DGRAM 1 +#define EOS_SOCK_MTYPE_CLOSE 127 + +#define EOS_SOCK_MAX_SOCK 2 + +#define EOS_SOCK_SIZE_UDP_HDR 8 + +#define EOS_IPv4_ADDR_SIZE 4 + +typedef struct EOSNetAddr { + unsigned char host[EOS_IPv4_ADDR_SIZE]; + uint16_t port; +} EOSNetAddr; + +void eos_sock_init(void);
\ No newline at end of file diff --git a/code/esp32/components/eos/include/wifi.h b/code/esp32/components/eos/include/wifi.h new file mode 100644 index 0000000..6009f7c --- /dev/null +++ b/code/esp32/components/eos/include/wifi.h @@ -0,0 +1,12 @@ +#define EOS_WIFI_MTYPE_SCAN 0 +#define EOS_WIFI_MTYPE_CONNECT 1 +#define EOS_WIFI_MTYPE_DISCONNECT 2 + +#define EOS_WIFI_MAX_MTYPE 3 + +void eos_wifi_init(void); + +int eos_wifi_scan(void); +int eos_wifi_set_auth(char *ssid, char *pass); +int eos_wifi_connect(void); +int eos_wifi_disconnect(void); diff --git a/code/esp32/components/eos/msgq.c b/code/esp32/components/eos/msgq.c new file mode 100644 index 0000000..c704399 --- /dev/null +++ b/code/esp32/components/eos/msgq.c @@ -0,0 +1,69 @@ +#include <stdlib.h> + +#include "eos.h" +#include "msgq.h" + +#define IDX_MASK(IDX, SIZE) ((IDX) & ((SIZE) - 1)) + +void eos_msgq_init(EOSMsgQ *msgq, EOSMsgItem *array, uint8_t size) { + msgq->idx_r = 0; + msgq->idx_w = 0; + msgq->size = size; + msgq->array = array; +} + +int eos_msgq_push(EOSMsgQ *msgq, unsigned char type, unsigned char *buffer, uint16_t len, uint8_t flags) { + if ((uint8_t)(msgq->idx_w - msgq->idx_r) == msgq->size) return EOS_ERR_FULL; + + uint8_t idx = IDX_MASK(msgq->idx_w, msgq->size); + msgq->array[idx].type = type; + msgq->array[idx].buffer = buffer; + msgq->array[idx].len = len; + msgq->array[idx].flags = flags; + msgq->idx_w++; + return EOS_OK; +} + +void eos_msgq_pop(EOSMsgQ *msgq, unsigned char *type, unsigned char **buffer, uint16_t *len, uint8_t *flags) { + if (msgq->idx_r == msgq->idx_w) { + *type = 0; + *buffer = NULL; + *len = 0; + if (flags) *flags = 0; + } else { + uint8_t idx = IDX_MASK(msgq->idx_r, msgq->size); + *type = msgq->array[idx].type; + *buffer = msgq->array[idx].buffer; + *len = msgq->array[idx].len; + if (flags) *flags = msgq->array[idx].flags; + msgq->idx_r++; + } +} + +uint8_t eos_msgq_len(EOSMsgQ *msgq) { + return (uint8_t)(msgq->idx_w - msgq->idx_r); +} + +void eos_bufq_init(EOSBufQ *bufq, unsigned char **array, uint8_t size) { + bufq->idx_r = 0; + bufq->idx_w = 0; + bufq->size = size; + bufq->array = array; +} + +int eos_bufq_push(EOSBufQ *bufq, unsigned char *buffer) { + if ((uint8_t)(bufq->idx_w - bufq->idx_r) == bufq->size) return EOS_ERR_FULL; + + bufq->array[IDX_MASK(bufq->idx_w++, bufq->size)] = buffer; + return EOS_OK; +} + +unsigned char *eos_bufq_pop(EOSBufQ *bufq) { + if (bufq->idx_r == bufq->idx_w) return NULL; + + return bufq->array[IDX_MASK(bufq->idx_r++, bufq->size)]; +} + +uint8_t eos_bufq_len(EOSBufQ *bufq) { + return (uint8_t)(bufq->idx_w - bufq->idx_r); +} diff --git a/code/esp32/components/eos/net.c b/code/esp32/components/eos/net.c new file mode 100644 index 0000000..9a4a024 --- /dev/null +++ b/code/esp32/components/eos/net.c @@ -0,0 +1,285 @@ +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include <freertos/FreeRTOS.h> +#include <freertos/semphr.h> +#include <freertos/task.h> + +#include <esp_system.h> +#include <esp_log.h> +#include <esp_err.h> +#include <esp_heap_caps.h> +#include <driver/gpio.h> +#include <driver/spi_slave.h> + +#include "eos.h" +#include "msgq.h" +#include "power.h" +#include "net.h" + +#define SPI_GPIO_RTS 22 +#define SPI_GPIO_CTS 21 +#define SPI_GPIO_MOSI 23 +#define SPI_GPIO_MISO 19 +#define SPI_GPIO_SCLK 18 +#define SPI_GPIO_CS 5 + +#define SPI_SIZE_BUF (EOS_NET_SIZE_BUF + 8) + +static volatile char net_sleep = 0; + +static EOSBufQ net_buf_q; +static unsigned char *net_bufq_array[EOS_NET_SIZE_BUFQ]; + +static EOSMsgQ net_send_q; +static EOSMsgItem net_sndq_array[EOS_NET_SIZE_SNDQ]; + +static SemaphoreHandle_t mutex; +static SemaphoreHandle_t semaph; +static TaskHandle_t net_xchg_task_handle; +static const char *TAG = "EOS NET"; + +static eos_net_fptr_t mtype_handler[EOS_NET_MAX_MTYPE]; + +static void bad_handler(unsigned char mtype, unsigned char *buffer, uint16_t len) { + ESP_LOGE(TAG, "bad handler: %d len: %d", mtype, len); +} + +// Called after a transaction is queued and ready for pickup by master. We use this to set the handshake line high. +static void _post_setup_cb(spi_slave_transaction_t *trans) { + gpio_set_level(SPI_GPIO_CTS, 1); +} + +// Called after transaction is sent/received. We use this to set the handshake line low. +static void _post_trans_cb(spi_slave_transaction_t *trans) { + gpio_set_level(SPI_GPIO_CTS, 0); +} + +static void net_xchg_task(void *pvParameters) { + int repeat = 0; + int wake = 0; + unsigned char mtype = 0; + unsigned char *buffer; + uint16_t len; + uint8_t flags; + unsigned char *buf_send = heap_caps_malloc(SPI_SIZE_BUF, MALLOC_CAP_DMA); + unsigned char *buf_recv = heap_caps_malloc(SPI_SIZE_BUF, MALLOC_CAP_DMA); + esp_err_t ret; + spi_slave_transaction_t spi_tr; + + //Configuration for the SPI bus + spi_bus_config_t spi_bus_cfg = { + .mosi_io_num = SPI_GPIO_MOSI, + .miso_io_num = SPI_GPIO_MISO, + .sclk_io_num = SPI_GPIO_SCLK + }; + + //Configuration for the SPI slave interface + spi_slave_interface_config_t spi_slave_cfg = { + .mode = 0, + .spics_io_num = SPI_GPIO_CS, + .queue_size = 2, + .flags = 0, + .post_setup_cb = _post_setup_cb, + .post_trans_cb = _post_trans_cb + }; + + //Initialize SPI slave interface + ret = spi_slave_initialize(VSPI_HOST, &spi_bus_cfg, &spi_slave_cfg, 1); + assert(ret == ESP_OK); + + memset(&spi_tr, 0, sizeof(spi_tr)); + spi_tr.length = SPI_SIZE_BUF * 8; + spi_tr.tx_buffer = buf_send; + spi_tr.rx_buffer = buf_recv; + + if (eos_power_wakeup_cause()) { + wake = 1; + repeat = 1; + } + + eos_power_wait4init(); + while (1) { + if (!repeat) { + xSemaphoreTake(mutex, portMAX_DELAY); + + eos_msgq_pop(&net_send_q, &mtype, &buffer, &len, &flags); + if (mtype) { + buf_send[0] = mtype; + buf_send[1] = len >> 8; + buf_send[2] = len & 0xFF; + if (buffer) { + memcpy(buf_send + 3, buffer, len); + if (flags & EOS_NET_FLAG_BFREE) { + free(buffer); + } else { + eos_bufq_push(&net_buf_q, buffer); + xSemaphoreGive(semaph); + } + } + } else { + gpio_set_level(SPI_GPIO_RTS, 0); + buf_send[0] = 0; + buf_send[1] = 0; + buf_send[2] = 0; + } + + xSemaphoreGive(mutex); + } + repeat = 0; + + buf_recv[0] = 0; + buf_recv[1] = 0; + buf_recv[2] = 0; + spi_slave_transmit(VSPI_HOST, &spi_tr, portMAX_DELAY); + ESP_LOGD(TAG, "RECV:%d", buf_recv[0]); + + if (wake) { + eos_power_net_ready(); + wake = 0; + } + if (buf_recv[0] == 0x00) continue; + if (buf_recv[0] == 0xFF) { // Sleep req + if (buf_send[0] == 0) { + int abort = 0; + + xSemaphoreTake(mutex, portMAX_DELAY); + net_sleep = 1; + if (eos_msgq_len(&net_send_q)) abort = 1; + xSemaphoreGive(mutex); + + spi_slave_free(VSPI_HOST); + + eos_power_sleep(); + if (abort) eos_power_wake(EOS_PWR_WAKE_MSG); + + vTaskSuspend(NULL); + + xSemaphoreTake(mutex, portMAX_DELAY); + net_sleep = 0; + xSemaphoreGive(mutex); + + spi_slave_initialize(VSPI_HOST, &spi_bus_cfg, &spi_slave_cfg, 1); + wake = 1; + repeat = 1; + } + continue; + } + mtype = buf_recv[0]; + len = (uint16_t)buf_recv[1] << 8; + len |= (uint16_t)buf_recv[2] & 0xFF; + buffer = buf_recv + 3; + if (mtype & EOS_NET_MTYPE_FLAG_ONEW) { + mtype &= ~EOS_NET_MTYPE_FLAG_ONEW; + if (buf_send[0]) repeat = 1; + } + if (mtype <= EOS_NET_MAX_MTYPE) { + mtype_handler[mtype-1](mtype, buffer, len); + } else { + bad_handler(mtype, buffer, len); + } + } + vTaskDelete(NULL); +} + +void eos_net_init(void) { + int i; + + // Configuration for the handshake lines + gpio_config_t io_conf; + + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pull_up_en = 0; + io_conf.pull_down_en = 0; + io_conf.pin_bit_mask = ((uint64_t)1 << SPI_GPIO_CTS); + gpio_config(&io_conf); + gpio_set_level(SPI_GPIO_CTS, 0); + + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pull_up_en = 0; + io_conf.pull_down_en = 0; + io_conf.pin_bit_mask = ((uint64_t)1 << SPI_GPIO_RTS); + gpio_config(&io_conf); + gpio_set_level(SPI_GPIO_RTS, 0); + + eos_msgq_init(&net_send_q, net_sndq_array, EOS_NET_SIZE_SNDQ); + eos_bufq_init(&net_buf_q, net_bufq_array, EOS_NET_SIZE_BUFQ); + for (i=0; i<EOS_NET_SIZE_BUFQ; i++) { + eos_bufq_push(&net_buf_q, malloc(EOS_NET_SIZE_BUF)); + } + + for (i=0; i<EOS_NET_MAX_MTYPE; i++) { + mtype_handler[i] = bad_handler; + } + + semaph = xSemaphoreCreateCounting(EOS_NET_SIZE_BUFQ, EOS_NET_SIZE_BUFQ); + mutex = xSemaphoreCreateBinary(); + xSemaphoreGive(mutex); + xTaskCreate(&net_xchg_task, "net_xchg", EOS_TASK_SSIZE_NET_XCHG, NULL, EOS_TASK_PRIORITY_NET_XCHG, &net_xchg_task_handle); + ESP_LOGI(TAG, "INIT"); +} + +unsigned char *eos_net_alloc(void) { + unsigned char *ret; + + xSemaphoreTake(semaph, portMAX_DELAY); + xSemaphoreTake(mutex, portMAX_DELAY); + ret = eos_bufq_pop(&net_buf_q); + xSemaphoreGive(mutex); + + return ret; +} + +void eos_net_free(unsigned char *buf) { + xSemaphoreTake(mutex, portMAX_DELAY); + eos_bufq_push(&net_buf_q, buf); + xSemaphoreGive(semaph); + xSemaphoreGive(mutex); +} + +int eos_net_send(unsigned char mtype, unsigned char *buffer, uint16_t len, uint8_t flags) { + int rv, sleep; + + if (flags & EOS_NET_FLAG_BCOPY) xSemaphoreTake(semaph, portMAX_DELAY); + xSemaphoreTake(mutex, portMAX_DELAY); + sleep = net_sleep; + gpio_set_level(SPI_GPIO_RTS, 1); + if (flags & EOS_NET_FLAG_BCOPY) { + unsigned char *b = eos_bufq_pop(&net_buf_q); + memcpy(b, buffer, len); + buffer = b; + } + rv = eos_msgq_push(&net_send_q, mtype, buffer, len, flags); + xSemaphoreGive(mutex); + + if (sleep) eos_power_wake(EOS_PWR_WAKE_MSG); + + return rv; +} + +void eos_net_set_handler(unsigned char mtype, eos_net_fptr_t handler) { + mtype_handler[mtype-1] = handler; +} + +void eos_net_sleep_done(uint8_t mode) { + gpio_set_level(SPI_GPIO_CTS, 1); + vTaskDelay(200 / portTICK_PERIOD_MS); + gpio_set_level(SPI_GPIO_CTS, 0); +} + +void eos_net_wake(uint8_t source, uint8_t mode) { + int sleep; + + if (mode == EOS_PWR_SMODE_DEEP) return; + do { + vTaskResume(net_xchg_task_handle); + vTaskDelay(10 / portTICK_PERIOD_MS); + + xSemaphoreTake(mutex, portMAX_DELAY); + sleep = net_sleep; + xSemaphoreGive(mutex); + } while (sleep); +} diff --git a/code/esp32/components/eos/power.c b/code/esp32/components/eos/power.c new file mode 100644 index 0000000..f07e67b --- /dev/null +++ b/code/esp32/components/eos/power.c @@ -0,0 +1,325 @@ +#include <stdlib.h> + +#include <freertos/FreeRTOS.h> +#include <freertos/task.h> +#include <freertos/queue.h> +#include <driver/gpio.h> +#include <esp_sleep.h> +#include <esp_pm.h> +#include <esp_log.h> + +#include "eos.h" +#include "net.h" +#include "cell.h" +#include "power.h" + +#define POWER_GPIO_BTN 0 +#define POWER_GPIO_NET 5 +#define POWER_GPIO_UART 35 + +#define POWER_ETYPE_BTN 1 +#define POWER_ETYPE_SLEEP 2 +#define POWER_ETYPE_WAKE 3 +#define POWER_ETYPE_NETRDY 4 + +typedef struct { + uint8_t type; + union { + uint8_t source; + uint8_t level; + }; +} power_event_t; + +static esp_pm_lock_handle_t power_lock_cpu_freq; +static esp_pm_lock_handle_t power_lock_apb_freq; +static esp_pm_lock_handle_t power_lock_no_sleep; + +static const char *TAG = "EOS POWER"; + +static QueueHandle_t power_queue; + +static volatile int init_done = 0; + +static void IRAM_ATTR btn_handler(void *arg) { + power_event_t evt; + + evt.type = POWER_ETYPE_BTN; + evt.level = gpio_get_level(POWER_GPIO_BTN); + xQueueSendFromISR(power_queue, &evt, NULL); +} + +static void IRAM_ATTR btn_wake_handler(void *arg) { + power_event_t evt; + + gpio_intr_disable(POWER_GPIO_BTN); + + evt.type = POWER_ETYPE_WAKE; + evt.source = EOS_PWR_WAKE_BTN; + xQueueSendFromISR(power_queue, &evt, NULL); + +} + +static void IRAM_ATTR net_wake_handler(void *arg) { + power_event_t evt; + + gpio_intr_disable(POWER_GPIO_NET); + + evt.type = POWER_ETYPE_WAKE; + evt.source = EOS_PWR_WAKE_NET; + xQueueSendFromISR(power_queue, &evt, NULL); +} + +static void IRAM_ATTR uart_wake_handler(void *arg) { + power_event_t evt; + + gpio_intr_disable(POWER_GPIO_UART); + + evt.type = POWER_ETYPE_WAKE; + evt.source = EOS_PWR_WAKE_UART; + xQueueSendFromISR(power_queue, &evt, NULL); +} + +void power_sleep(uint8_t mode) { + gpio_config_t io_conf; + + eos_modem_sleep(mode); + eos_net_sleep_done(mode); + + switch (mode) { + case EOS_PWR_SMODE_LIGHT: + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_NET); + io_conf.pull_up_en = 0; + io_conf.pull_down_en = 0; + gpio_config(&io_conf); + + gpio_isr_handler_add(POWER_GPIO_BTN, btn_wake_handler, NULL); + gpio_isr_handler_add(POWER_GPIO_NET, net_wake_handler, NULL); + gpio_isr_handler_add(POWER_GPIO_UART, uart_wake_handler, NULL); + + esp_sleep_enable_gpio_wakeup(); + gpio_wakeup_enable(POWER_GPIO_BTN, GPIO_INTR_LOW_LEVEL); + gpio_wakeup_enable(POWER_GPIO_NET, GPIO_INTR_LOW_LEVEL); + gpio_wakeup_enable(POWER_GPIO_UART, GPIO_INTR_LOW_LEVEL); + + ESP_LOGD(TAG, "SLEEP"); + + esp_pm_lock_release(power_lock_apb_freq); + esp_pm_lock_release(power_lock_no_sleep); + + break; + + case EOS_PWR_SMODE_DEEP: + gpio_deep_sleep_hold_en(); + esp_sleep_enable_ext0_wakeup(POWER_GPIO_BTN, 0); + esp_sleep_enable_ext1_wakeup((uint64_t)1 << POWER_GPIO_UART, ESP_EXT1_WAKEUP_ALL_LOW); + + ESP_LOGD(TAG, "SLEEP"); + + esp_deep_sleep_start(); + break; + + default: + break; + } +} + +void power_wake_stage1(uint8_t source, uint8_t mode) { + gpio_config_t io_conf; + + if (mode == EOS_PWR_SMODE_LIGHT) { + esp_pm_lock_acquire(power_lock_apb_freq); + esp_pm_lock_acquire(power_lock_no_sleep); + + gpio_wakeup_disable(POWER_GPIO_BTN); + gpio_wakeup_disable(POWER_GPIO_NET); + gpio_wakeup_disable(POWER_GPIO_UART); + + gpio_isr_handler_remove(POWER_GPIO_NET); + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_DISABLE; + io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_NET); + io_conf.pull_up_en = 0; + io_conf.pull_down_en = 0; + gpio_config(&io_conf); + } + + gpio_intr_disable(POWER_GPIO_BTN); + if ((source != EOS_PWR_WAKE_BTN) && (source != EOS_PWR_WAKE_NET)) { + gpio_set_direction(POWER_GPIO_BTN, GPIO_MODE_OUTPUT); + gpio_set_level(POWER_GPIO_BTN, 0); + vTaskDelay(200 / portTICK_PERIOD_MS); + gpio_set_direction(POWER_GPIO_BTN, GPIO_MODE_INPUT); + } + + eos_net_wake(source, mode); +} + +void power_wake_stage2(uint8_t source, uint8_t mode) { + eos_modem_wake(source, mode); + + gpio_set_intr_type(POWER_GPIO_BTN, GPIO_INTR_ANYEDGE); + gpio_isr_handler_add(POWER_GPIO_BTN, btn_handler, NULL); + + ESP_LOGD(TAG, "WAKE"); +} + +static void power_event_task(void *pvParameters) { + unsigned char *buf; + power_event_t evt; + uint8_t source; + uint8_t wakeup_cause; + uint8_t mode; + int sleep; + + source = 0; + wakeup_cause = eos_power_wakeup_cause(); + if (wakeup_cause) { + mode = EOS_PWR_SMODE_DEEP; + sleep = 1; + } else { + mode = EOS_PWR_SMODE_LIGHT; + sleep = 0; + } + + while (1) { + if (xQueueReceive(power_queue, &evt, portMAX_DELAY)) { + switch (evt.type) { + case POWER_ETYPE_SLEEP: + if (!sleep) { + mode = EOS_PWR_SMODE_DEEP; + power_sleep(mode); + sleep = 1; + } + break; + + case POWER_ETYPE_WAKE: + if (sleep) { + source = evt.source; + power_wake_stage1(source, mode); + } + break; + + case POWER_ETYPE_NETRDY: + if (sleep && source) { + power_wake_stage2(source, mode); + sleep = 0; + source = 0; + } + break; + + case POWER_ETYPE_BTN: + buf = eos_net_alloc(); + buf[0] = EOS_PWR_MTYPE_BUTTON; + buf[1] = evt.level; + eos_net_send(EOS_NET_MTYPE_POWER, buf, 2, 0); + break; + + default: + break; + } + } + } + vTaskDelete(NULL); +} + +void eos_power_init(void) { + esp_err_t ret; + gpio_config_t io_conf; + esp_pm_config_esp32_t pwr_conf; + uint8_t wakeup_cause; + + io_conf.intr_type = GPIO_INTR_ANYEDGE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = ((uint64_t)1 << POWER_GPIO_BTN); + io_conf.pull_up_en = 1; + io_conf.pull_down_en = 0; + gpio_config(&io_conf); + gpio_isr_handler_add(POWER_GPIO_BTN, btn_handler, NULL); + + /* + ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); + assert(ret == ESP_OK); + ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON); + assert(ret == ESP_OK); + ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON); + assert(ret == ESP_OK); + */ + + ret = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, NULL, &power_lock_cpu_freq); + assert(ret == ESP_OK); + ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, NULL, &power_lock_apb_freq); + assert(ret == ESP_OK); + ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, NULL, &power_lock_no_sleep); + assert(ret == ESP_OK); + + ret = esp_pm_lock_acquire(power_lock_cpu_freq); + assert(ret == ESP_OK); + ret = esp_pm_lock_acquire(power_lock_apb_freq); + assert(ret == ESP_OK); + ret = esp_pm_lock_acquire(power_lock_no_sleep); + assert(ret == ESP_OK); + + pwr_conf.max_freq_mhz = 160; + pwr_conf.min_freq_mhz = 80; + pwr_conf.light_sleep_enable = 1; + + ret = esp_pm_configure(&pwr_conf); + assert(ret == ESP_OK); + + power_queue = xQueueCreate(4, sizeof(power_event_t)); + xTaskCreate(power_event_task, "power_event", EOS_TASK_SSIZE_PWR, NULL, EOS_TASK_PRIORITY_PWR, NULL); + + wakeup_cause = eos_power_wakeup_cause(); + if (wakeup_cause) eos_power_wake(wakeup_cause); + + init_done = 1; + ESP_LOGI(TAG, "INIT"); +} + +void eos_power_wait4init(void) { + while (!init_done); +} + +uint8_t eos_power_wakeup_cause(void) { + esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); + + switch (cause) { + case ESP_SLEEP_WAKEUP_EXT0: + return EOS_PWR_WAKE_BTN; + + case ESP_SLEEP_WAKEUP_EXT1: + return EOS_PWR_WAKE_UART; + + default: + case ESP_SLEEP_WAKEUP_UNDEFINED: + return EOS_PWR_WAKE_RST; + } +} + +void eos_power_sleep(void) { + power_event_t evt; + + evt.type = POWER_ETYPE_SLEEP; + evt.source = 0; + xQueueSend(power_queue, &evt, portMAX_DELAY); +} + +void eos_power_wake(uint8_t source) { + power_event_t evt; + + evt.type = POWER_ETYPE_WAKE; + evt.source = source; + + xQueueSend(power_queue, &evt, portMAX_DELAY); +} + +void eos_power_net_ready(void) { + power_event_t evt; + + evt.type = POWER_ETYPE_NETRDY; + evt.source = 0; + + xQueueSend(power_queue, &evt, portMAX_DELAY); +} diff --git a/code/esp32/components/eos/sock.c b/code/esp32/components/eos/sock.c new file mode 100644 index 0000000..17357e4 --- /dev/null +++ b/code/esp32/components/eos/sock.c @@ -0,0 +1,163 @@ +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include <freertos/FreeRTOS.h> +#include <freertos/semphr.h> +#include <freertos/task.h> + +#include <esp_system.h> +#include <esp_event.h> +#include <esp_event_loop.h> +#include <esp_log.h> +#include <esp_err.h> +#include <esp_wifi.h> +#include <nvs_flash.h> + +#include <lwip/sockets.h> +#include <lwip/err.h> +#include <lwip/sockets.h> +#include <lwip/sys.h> +#include <lwip/netdb.h> +#include <lwip/dns.h> + +#include "eos.h" +#include "net.h" +#include "sock.h" + +static const char *TAG = "EOS SOCK"; +static SemaphoreHandle_t mutex; +static int _socks[EOS_SOCK_MAX_SOCK]; + +static int t_open_dgram(void) { + struct sockaddr_in _myaddr; + int sock; + + sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock < 0) return sock; + + memset((char *)&_myaddr, 0, sizeof(_myaddr)); + _myaddr.sin_family = AF_INET; + _myaddr.sin_addr.s_addr = htonl(INADDR_ANY); + _myaddr.sin_port = htons(3000); + + int rv = bind(sock, (struct sockaddr *)&_myaddr, sizeof(_myaddr)); + if (rv < 0) { + close(sock); + return rv; + } + return sock; +} + +static void t_close(int sock) { + close(sock); +} + +static ssize_t t_sendto(int sock, void *msg, size_t msg_size, EOSNetAddr *addr) { + struct sockaddr_in servaddr; + + memset((void *)&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = addr->port; + memcpy((void *)&servaddr.sin_addr, addr->host, sizeof(addr->host)); + return sendto(sock, msg, msg_size, 0, (struct sockaddr *)&servaddr, sizeof(servaddr)); +} + +static ssize_t t_recvfrom(int sock, void *msg, size_t msg_size, EOSNetAddr *addr) { + struct sockaddr_in servaddr; + socklen_t addrlen = sizeof(servaddr); + memset((void *)&servaddr, 0, sizeof(servaddr)); + + ssize_t recvlen = recvfrom(sock, msg, msg_size, 0, (struct sockaddr *)&servaddr, &addrlen); + if (recvlen < 0) return recvlen; + + if (addr) { + addr->port = servaddr.sin_port; + memcpy(addr->host, (void *)&servaddr.sin_addr, sizeof(addr->host)); + } + return recvlen; +} + +static void udp_rcvr_task(void *pvParameters) { + EOSNetAddr addr; + uint8_t esock = (uint8_t)pvParameters; + int sock = _socks[esock-1]; + unsigned char *buf; + + do { + ssize_t rv; + + buf = eos_net_alloc(); + rv = t_recvfrom(sock, buf+EOS_SOCK_SIZE_UDP_HDR, EOS_NET_SIZE_BUF-EOS_SOCK_SIZE_UDP_HDR, &addr); + if (rv < 0) { + sock = 0; + ESP_LOGE(TAG, "UDP RECV ERR:%d", rv); + continue; + } + buf[0] = EOS_SOCK_MTYPE_PKT; + buf[1] = esock; + memcpy(buf+2, addr.host, sizeof(addr.host)); + memcpy(buf+2+sizeof(addr.host), &addr.port, sizeof(addr.port)); + eos_net_send(EOS_NET_MTYPE_SOCK, buf, rv+EOS_SOCK_SIZE_UDP_HDR, 0); + } while(sock); + xSemaphoreTake(mutex, portMAX_DELAY); + _socks[esock-1] = 0; + xSemaphoreGive(mutex); + vTaskDelete(NULL); +} + +static void sock_handler(unsigned char type, unsigned char *buffer, uint16_t size) { + EOSNetAddr addr; + uint8_t esock; + int sock, i; + unsigned char *rbuf; + + if (size < 1) return; + + switch(buffer[0]) { + case EOS_SOCK_MTYPE_PKT: + if (size < EOS_SOCK_SIZE_UDP_HDR) return; + sock = _socks[buffer[1]-1]; + memcpy(addr.host, buffer+2, sizeof(addr.host)); + memcpy(&addr.port, buffer+2+sizeof(addr.host), sizeof(addr.port)); + t_sendto(sock, buffer+EOS_SOCK_SIZE_UDP_HDR, size-EOS_SOCK_SIZE_UDP_HDR, &addr); + break; + + case EOS_SOCK_MTYPE_OPEN_DGRAM: + sock = t_open_dgram(); + esock = 0; + if (sock > 0) { + xSemaphoreTake(mutex, portMAX_DELAY); + for (i=0; i<EOS_SOCK_MAX_SOCK; i++) { + if (_socks[i] == 0) { + esock = i+1; + _socks[i] = sock; + } + } + xSemaphoreGive(mutex); + } + // xTaskCreatePinnedToCore(&sock_receiver, "sock_receiver", EOS_TASK_SSIZE_UDP_RCVR, (void *)esock, EOS_TASK_PRIORITY_UDP_RCVR, NULL, 1); + xTaskCreate(&udp_rcvr_task, "udp_rcvr", EOS_TASK_SSIZE_UDP_RCVR, (void *)esock, EOS_TASK_PRIORITY_UDP_RCVR, NULL); + rbuf = eos_net_alloc(); + rbuf[0] = EOS_SOCK_MTYPE_OPEN_DGRAM; + rbuf[1] = esock; + eos_net_send(EOS_NET_MTYPE_SOCK, rbuf, 2, 0); + break; + + case EOS_SOCK_MTYPE_CLOSE: + if (size < 2) return; + sock = _socks[buffer[1]-1]; + t_close(sock); + break; + + default: + break; + } +} + +void eos_sock_init(void) { + mutex = xSemaphoreCreateBinary(); + xSemaphoreGive(mutex); + eos_net_set_handler(EOS_NET_MTYPE_SOCK, sock_handler); + ESP_LOGI(TAG, "INIT"); +}
\ No newline at end of file diff --git a/code/esp32/components/eos/wifi.c b/code/esp32/components/eos/wifi.c new file mode 100755 index 0000000..3dd90ba --- /dev/null +++ b/code/esp32/components/eos/wifi.c @@ -0,0 +1,302 @@ +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include <freertos/FreeRTOS.h> +#include <freertos/task.h> + +#include <esp_system.h> +#include <esp_event.h> +#include <esp_event_loop.h> +#include <esp_log.h> +#include <esp_err.h> +#include <esp_wifi.h> + +#include "eos.h" +#include "net.h" +#include "wifi.h" + +// XXX: WiFi fail due to no DHCP server + +#define WIFI_MAX_SCAN_RECORDS 20 +#define WIFI_MAX_CONNECT_ATTEMPTS 3 + +#define WIFI_STATE_STOPPED 0 +#define WIFI_STATE_SCANNING 1 +#define WIFI_STATE_CONNECTING 2 +#define WIFI_STATE_CONNECTED 3 +#define WIFI_STATE_DISCONNECTING 4 +#define WIFI_STATE_DISCONNECTED 5 + +#define WIFI_ACTION_NONE 0 +#define WIFI_ACTION_SCAN 1 +#define WIFI_ACTION_CONNECT 2 +#define WIFI_ACTION_DISCONNECT 3 + +static const char *TAG = "EOS WIFI"; + +static SemaphoreHandle_t mutex; + +static wifi_config_t wifi_sta_config; +static wifi_scan_config_t wifi_scan_config; + +static int wifi_connect_cnt = 0; +static uint8_t wifi_action; +static uint8_t wifi_state; + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) { + esp_err_t ret = ESP_OK; + char _disconnect; + uint8_t _action, _state; + unsigned char *rbuf; + wifi_ap_record_t scan_r[WIFI_MAX_SCAN_RECORDS]; + uint16_t scan_n = WIFI_MAX_SCAN_RECORDS; + + switch(event->event_id) { + case WIFI_EVENT_SCAN_DONE: + memset(scan_r, 0, sizeof(scan_r)); + esp_wifi_scan_get_ap_records(&scan_n, scan_r); + + xSemaphoreTake(mutex, portMAX_DELAY); + _state = wifi_state; + if (wifi_state == WIFI_STATE_CONNECTED) wifi_action = WIFI_ACTION_NONE; + xSemaphoreGive(mutex); + + if (_state != WIFI_STATE_CONNECTED) ret = esp_wifi_stop(); + break; + + case WIFI_EVENT_STA_START: + xSemaphoreTake(mutex, portMAX_DELAY); + _action = wifi_action; + xSemaphoreGive(mutex); + + switch (_action) { + case WIFI_ACTION_SCAN: + ret = esp_wifi_scan_start(&wifi_scan_config, 0); + break; + + case WIFI_ACTION_CONNECT: + ret = esp_wifi_connect(); + break; + + default: + break; + } + break; + + case WIFI_EVENT_STA_STOP: + xSemaphoreTake(mutex, portMAX_DELAY); + wifi_state = WIFI_STATE_STOPPED; + wifi_action = WIFI_ACTION_NONE; + xSemaphoreGive(mutex); + break; + + case WIFI_EVENT_STA_CONNECTED: + xSemaphoreTake(mutex, portMAX_DELAY); + wifi_state = WIFI_STATE_CONNECTED; + wifi_action = WIFI_ACTION_NONE; + wifi_connect_cnt = WIFI_MAX_CONNECT_ATTEMPTS; + xSemaphoreGive(mutex); + break; + + case WIFI_EVENT_STA_DISCONNECTED: + xSemaphoreTake(mutex, portMAX_DELAY); + if (wifi_connect_cnt) wifi_connect_cnt--; + _action = wifi_action; + _disconnect = (wifi_connect_cnt == 0); + if (_disconnect) wifi_state = WIFI_STATE_DISCONNECTED; + xSemaphoreGive(mutex); + + if (_disconnect) { + rbuf = eos_net_alloc(); + if (_action == WIFI_ACTION_CONNECT) { + rbuf[0] = EOS_WIFI_MTYPE_CONNECT; + rbuf[1] = EOS_ERR; + eos_net_send(EOS_NET_MTYPE_WIFI, rbuf, 2, 0); + } else { + rbuf[0] = EOS_WIFI_MTYPE_DISCONNECT; + eos_net_send(EOS_NET_MTYPE_WIFI, rbuf, 1, 0); + } + if (!_action) ret = esp_wifi_stop(); + } else { + ret = esp_wifi_connect(); + } + break; + + case IP_EVENT_STA_GOT_IP: + ESP_LOGD(TAG, "IP address: " IPSTR, IP2STR(&event->event_info.got_ip.ip_info.ip)); + if (event->event_info.got_ip.ip_changed) { + // send wifi reconnect + } else { + rbuf = eos_net_alloc(); + rbuf[0] = EOS_WIFI_MTYPE_CONNECT; + rbuf[1] = EOS_OK; + eos_net_send(EOS_NET_MTYPE_WIFI, rbuf, 2, 0); + } + break; + + default: // Ignore the other event types + break; + } + if (ret != ESP_OK) ESP_LOGD(TAG, "ESP WIFI ERR: %d", ret); + + return ESP_OK; +} + +static void wifi_handler(unsigned char _mtype, unsigned char *buffer, uint16_t size) { + int rv = EOS_OK; + uint8_t mtype = buffer[0]; + char *ssid, *pass; + + switch (mtype) { + case EOS_WIFI_MTYPE_SCAN: + rv = eos_wifi_scan(); + break; + + case EOS_WIFI_MTYPE_CONNECT: + ssid = (char *)buffer+1; + pass = ssid+strlen(ssid)+1; + rv = eos_wifi_set_auth(ssid, pass); + if (!rv) rv = eos_wifi_connect(); + break; + + case EOS_WIFI_MTYPE_DISCONNECT: + rv = eos_wifi_disconnect(); + break; + } + if (rv) ESP_LOGD(TAG, "WIFI HANDLER ERR: %d", rv); +} + +void eos_wifi_init(void) { + esp_err_t ret; + wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT(); + + memset(&wifi_sta_config, 0, sizeof(wifi_sta_config)); + + ret = esp_event_loop_init(wifi_event_handler, NULL); + assert(ret == ESP_OK); + + ret = esp_wifi_init(&wifi_config); + assert(ret == ESP_OK); + + ret = esp_wifi_set_storage(WIFI_STORAGE_RAM); + assert(ret == ESP_OK); + + ret = esp_wifi_set_mode(WIFI_MODE_STA); + assert(ret == ESP_OK); + + ret = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config); + assert(ret == ESP_OK); + + ret = esp_wifi_stop(); + assert(ret == ESP_OK); + + mutex = xSemaphoreCreateBinary(); + xSemaphoreGive(mutex); + + eos_net_set_handler(EOS_NET_MTYPE_WIFI, wifi_handler); + ESP_LOGI(TAG, "INIT"); +} + +int eos_wifi_scan(void) { + int rv = EOS_OK; + esp_err_t ret = ESP_OK; + uint8_t _wifi_state = 0; + + xSemaphoreTake(mutex, portMAX_DELAY); + if (!wifi_action) { + _wifi_state = wifi_state; + + wifi_action = WIFI_ACTION_SCAN; + if (wifi_state == WIFI_STATE_STOPPED) wifi_state = WIFI_STATE_SCANNING; + + memset(&wifi_scan_config, 0, sizeof(wifi_scan_config)); + } else { + rv = EOS_ERR_BUSY; + } + xSemaphoreGive(mutex); + + if (rv) return rv; + + if (_wifi_state == WIFI_STATE_STOPPED) { + ret = esp_wifi_start(); + } else { + ret = esp_wifi_scan_start(&wifi_scan_config, 0); + } + if (ret != ESP_OK) rv = EOS_ERR; + + return rv; +} + +int eos_wifi_set_auth(char *ssid, char *pass) { + int rv = EOS_OK; + + xSemaphoreTake(mutex, portMAX_DELAY); + if (!wifi_action) { + if (ssid) strncpy((char *)wifi_sta_config.sta.ssid, ssid, sizeof(wifi_sta_config.sta.ssid) - 1); + if (pass) strncpy((char *)wifi_sta_config.sta.password, pass, sizeof(wifi_sta_config.sta.password) - 1); + } else { + rv = EOS_ERR_BUSY; + + } + xSemaphoreGive(mutex); + + return rv; +} + +int eos_wifi_connect(void) { + int rv = EOS_OK; + esp_err_t ret = ESP_OK; + uint8_t _wifi_state = 0; + + xSemaphoreTake(mutex, portMAX_DELAY); + if (!wifi_action) { + _wifi_state = wifi_state; + + wifi_action = WIFI_ACTION_CONNECT; + wifi_state = WIFI_STATE_CONNECTING; + wifi_connect_cnt = WIFI_MAX_CONNECT_ATTEMPTS; + } else { + rv = EOS_ERR_BUSY; + } + xSemaphoreGive(mutex); + + if (rv) return rv; + + esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config); + + if (_wifi_state == WIFI_STATE_STOPPED) { + ret = esp_wifi_start(); + } else { + ret = esp_wifi_connect(); + } + if (ret != ESP_OK) rv = EOS_ERR; + + return rv; +} + +int eos_wifi_disconnect(void) { + int rv = EOS_OK; + esp_err_t ret = ESP_OK; + + xSemaphoreTake(mutex, portMAX_DELAY); + if (!wifi_action) { + wifi_action = WIFI_ACTION_DISCONNECT; + wifi_state = WIFI_STATE_DISCONNECTING; + wifi_connect_cnt = 0; + + memset(wifi_sta_config.sta.ssid, 0, sizeof(wifi_sta_config.sta.ssid)); + memset(wifi_sta_config.sta.password, 0, sizeof(wifi_sta_config.sta.password)); + } else { + rv = EOS_ERR_BUSY; + } + xSemaphoreGive(mutex); + + if (rv) return rv; + + ret = esp_wifi_stop(); + if (ret != ESP_OK) rv = EOS_ERR; + + return rv; +} + |