From 77c9d4a4d528b37a4ef448f830b8e1c531b71140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Ryb=C3=A1rsky?= Date: Mon, 18 May 2026 22:59:45 +0200 Subject: [PATCH] update --- dependencies.lock | 13 +- main/CMakeLists.txt | 4 + main/drivers/es8311.c | 128 +----- main/drivers/pins.h | 4 +- main/drivers/servergprs.c | 130 ++++++ main/drivers/servergprs.h | 12 + main/drivers/sim800.c | 855 ++++++++++++++++++++++++++++++++++++++ main/drivers/sim800.h | 115 +++++ main/idf_component.yml | 1 + main/main.c | 42 +- 10 files changed, 1185 insertions(+), 119 deletions(-) create mode 100644 main/drivers/servergprs.c create mode 100644 main/drivers/servergprs.h create mode 100644 main/drivers/sim800.c create mode 100644 main/drivers/sim800.h diff --git a/dependencies.lock b/dependencies.lock index da9cd27..0b89f4d 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -1,4 +1,14 @@ dependencies: + espressif/esp_codec_dev: + component_hash: 896c67360ae1f8dedaf8a53dfe48c015317fa455f061a5165307892296990028 + dependencies: + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.5.10 espressif/esp_tinyusb: component_hash: 8e57a14ac14d8e4d4588b5b9222977b96475323bcebae3c838919bb4e8587887 dependencies: @@ -49,9 +59,10 @@ dependencies: type: idf version: 6.0.0 direct_dependencies: +- espressif/esp_codec_dev - espressif/esp_tinyusb - espressif/led_strip - idf -manifest_hash: 4367414e2d87717eea085884b361169910107724286ca676c237567381b84306 +manifest_hash: ef62d5f630d2fef4437689964fe6bd9bff7fe496aa1d7a7d95e6cd6db62ff7c7 target: esp32s3 version: 3.0.0 diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index ef8977b..03f72b5 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -23,6 +23,10 @@ idf_component_register(SRCS "main.c" "drivers/fonts.c" "drivers/st7789.c" "drivers/st7789.h" + "drivers/servergprs.c" + "drivers/servergprs.h" + "drivers/sim800.c" + "drivers/sim800.h" "drivers/tca8418.c" "drivers/tca8418.h" "drivers/ws2812.c" diff --git a/main/drivers/es8311.c b/main/drivers/es8311.c index 5317d5f..1ca0eda 100644 --- a/main/drivers/es8311.c +++ b/main/drivers/es8311.c @@ -11,6 +11,8 @@ #include #include +#define TAG "ES8311" + i2c_master_dev_handle_t es8311_i2c_dev; int es8311_write(uint8_t reg, uint8_t val) { @@ -100,122 +102,20 @@ void es8311_init(void) { } // Reset sequence - es8311_write(ES8311_RESET_REG00, 0x1F); - vTaskDelay(pdMS_TO_TICKS(20)); - es8311_write(ES8311_RESET_REG00, 0x00); - es8311_write(ES8311_RESET_REG00, 0x80); // Power-on - vTaskDelay(pdMS_TO_TICKS(20)); + es8311_write(ES8311_RESET_REG00, 0x1F); + vTaskDelay(pdMS_TO_TICKS(20)); + es8311_write(ES8311_RESET_REG00, 0x00); + es8311_write(ES8311_RESET_REG00, 0x80); // Power-on command - // Start analog circuitry - es8311_write(ES8311_SYSTEM_REG0D, 0x01); - vTaskDelay(pdMS_TO_TICKS(20)); + es8311_write(ES8311_CLK_MANAGER_REG01, + ES8311_CLK_MANAGER_REG01_MCLK_SEL_FROM_BCLK | + 0x3F + ); - // Configure I2S format - Slave mode - es8311_write(ES8311_RESET_REG00, 0x80); // Ensure slave mode - - es8311_write(ES8311_SDPIN_REG, - ES8311_SDPIN_FORMAT_I2S | ES8311_SDPIN_WORD_16BIT | - ES8311_SDPIN_LR_NORMAL_POLARITY | ES8311_SDPIN_UNMUTE | - ES8311_SDPIN_SEL_LEFT_TO_DAC); - - es8311_write(ES8311_SDPOUT_REG, - ES8311_SDPOUT_FORMAT_I2S | ES8311_SDPOUT_WORD_16BIT | - ES8311_SDPOUT_LR_NORMAL_POLARITY | ES8311_SDPOUT_UNMUTE); - - /* - // Clock configuration for 48kHz from BCLK - es8311_write(ES8311_CLK_MANAGER_REG01, - ES8311_CLK_MANAGER_REG01_MCLK_INV_OFF | - ES8311_CLK_MANAGER_REG01_MCLK_ON | - ES8311_CLK_MANAGER_REG01_MCLK_SEL_FROM_BCLK | - ES8311_CLK_MANAGER_REG01_BCLK_ON); - */ - - es8311_write(ES8311_CLK_MANAGER_REG01, - ES8311_CLK_MANAGER_REG01_BCLK_ON); - - /* - es8311_write(ES8311_CLK_MANAGER_REG02, 0x00); - es8311_write(ES8311_CLK_MANAGER_REG03, 0x10); - es8311_write(ES8311_CLK_MANAGER_REG04, 0x10); - es8311_write(ES8311_CLK_MANAGER_REG05, 0x00); - */ - - // CRITICAL: Set LRCK divider - es8311_write(ES8311_CLK_MANAGER_REG06, - ES8311_CLK_MANAGER_REG06_CONTINUAL_BCLK | - ES8311_CLK_MANAGER_REG06_NORMAL_BCLK | - ES8311_CLK_MANAGER_REG06_DIV_BCLK_4); - - es8311_write(ES8311_CLK_MANAGER_REG07, - ES8311_CLK_MANAGER_REG07_ADCDAT_NORMAL_MODE | - ES8311_CLK_MANAGER_REG07_BCLK_LRCLK_NORMAL_MODE); - - // MISSING REGISTER - LRCK divider low byte - es8311_write(ES8311_CLK_MANAGER_REG08, 0xFF); - - vTaskDelay(pdMS_TO_TICKS(20)); - - // Power up and enable everything - es8311_write(ES8311_SYSTEM_REG0E, ES8311_SYSTEM_REG0E_PGA_ENABLE | - ES8311_SYSTEM_REG0E_PDN_MOD_ENABLE | - ES8311_SYSTEM_REG0E_MOD_NORMAL); - - es8311_write(ES8311_SYSTEM_REG12, 0x00); // DAC on - es8311_write(ES8311_SYSTEM_REG13, 0x10); // Output enable - - es8311_write(ES8311_SYSTEM_REG0F, - ES8311_SYSTEM_REG0F_DAC_NORMAL_MODE | - ES8311_SYSTEM_REG0F_PGA_NORMAL_MODE | - ES8311_SYSTEM_REG0F_PGA_OUTPUT_NORMAL_MODE | - ES8311_SYSTEM_REG0F_VCMMOD_NORMAL_MODE | - ES8311_SYSTEM_REG0F_ADC_REFERENCE_NORMAL_MODE | - ES8311_SYSTEM_REG0F_DAC_REFERENCE_NORMAL_MODE | - ES8311_SYSTEM_REG0F_FLASH_NORMAL_MODE | - ES8311_SYSTEM_REG0F_INT1_NORMAL_MODE); - - // Microphone configuration - es8311_write(ES8311_SYSTEM_REG14, ES8311_SYSTEM_REG14_DMIC_OFF | - ES8311_SYSTEM_REG14_MIC1 | - ES8311_SYSTEM_REG14_GAIN_30DB); - - es8311_write(ES8311_ADC_REG16, ES8311_ADC_REG16_ADC_GAIN_24DB); - - es8311_write(ES8311_ADC_REG1C, ES8311_ADC_REG1C_HPF_DYNAMIC_HPF | - ES8311_ADC_REG1C_ADCEQ_BYPASS); - - es8311_write(ES8311_ADC_REG18, ES8311_ADC_REG18_AUTO_LEVEL_CONTROL_ENABLE | - ES8311_ADC_REG18_AUTO_MUTE_DISABLE | - ES8311_ADC_REG18_0_25DB_PER_65536LRCK); - - es8311_write(ES8311_ADC_REG19, ES8311_ADC_REG19_ALC_MIN_LEVEL_NEG_30_1DB | - ES8311_ADC_REG19_ALC_MAX_LEVEL_NEG_6_0DB); - - es8311_write(ES8311_ADC_REG1A, ES8311_ADC_REG1A_AUTOMUTE_NEG_96DB | - ES8311_ADC_REG1A_AUTOMUTE_32768_SAMPLES); - - es8311_write(ES8311_GPIO_REG44, ES8311_GPIO_REG44_ADCDAT_SEL_ADC_ADC | - ES8311_GPIO_REG44_ADCDAT_SEL_NO_LOOPBACK); - //ES8311_GPIO_REG44_ADCDAT_ADC_TO_DAC); - - es8311_write(ES8311_ADC_REG15, ES8311_ADC_REG15_RAMPRATE_0_25_4096LRCK); - es8311_write(ES8311_DAC_REG37, ES8311_DAC_REG37_RAMPRATE_0_25_4096LRCK); - - es8311_set_adc_volume(5); - es8311_set_dac_volume(5); - - es8311_write(ES8311_DAC_REG31, ES8311_DAC_REG31_MUTE_TO8 | - ES8311_DAC_REG31_DSM_UNMUTE | - ES8311_DAC_REG31_DEM_UNMUTE); - - es8311_write(ES8311_DAC_REG34, - ES8311_DAC_REG34_DRC_DISABLE | - ES8311_DAC_REG34_DRC_WINSIZE_0_25_65536LRCK); - - es8311_write(ES8311_DAC_REG35, ES8311_DAC_REG35_DRC_MAX_LEVEL_NEG_6_0DB | - ES8311_DAC_REG35_DRC_MIN_LEVEL_NEG_30_1DB); - - printf("ES8311: Initialization complete\n"); + es8311_write(ES8311_CLK_MANAGER_REG06, + ES8311_CLK_MANAGER_REG06_NORMAL_BCLK | + ES8311_CLK_MANAGER_REG06_CONTINUAL_BCLK + ); } int audio_write(const int16_t *samples, size_t count) { diff --git a/main/drivers/pins.h b/main/drivers/pins.h index a88c466..9e0a7e7 100644 --- a/main/drivers/pins.h +++ b/main/drivers/pins.h @@ -51,7 +51,7 @@ */ #define HEADER_CS GPIO_NUM_5 // also has i2c bus -#define HEADER_UART_RX GPIO_NUM_13 -#define HEADER_UART_TX GPIO_NUM_15 +#define HEADER_UART_RX GPIO_NUM_15 +#define HEADER_UART_TX GPIO_NUM_13 void set_pin_dirs(); diff --git a/main/drivers/servergprs.c b/main/drivers/servergprs.c new file mode 100644 index 0000000..72170a6 --- /dev/null +++ b/main/drivers/servergprs.c @@ -0,0 +1,130 @@ +#include "servergprs.h" + +#include "psa_crypto_driver_esp_aes_gcm.h" + +char linebufIn[1024]; +char *linebufInPtr = linebufIn; + +#define MAX_FRAME 512 + +static uint8_t rx_buf[MAX_FRAME]; +static size_t rx_len = 0; +static uint16_t expected_len = 0; +static int state = 0; + +void process_encrypted_frame(uint8_t *data, size_t len) +{ + // layout: + // [4B device_id][4B counter][12B nonce][ciphertext...][16B tag] + + if (len < 36) return; + + uint8_t *device_id = data; + uint8_t *counter = data + 4; + uint8_t *nonce = data + 8; + uint8_t *cipher = data + 20; + size_t cipher_len = len - 36; + uint8_t *tag = data + len - 16; + + uint8_t plaintext[512]; + size_t plaintext_len = 0; + + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + + psa_status_t st = esp_crypto_aes_gcm_decrypt( + &attr, + shared_key, + sizeof(shared_key), + PSA_ALG_GCM, + nonce, 12, + NULL, 0, + cipher, cipher_len, + plaintext, sizeof(plaintext), + &plaintext_len + ); + + if (st != PSA_SUCCESS) { + return; + } + + handle_message(plaintext, plaintext_len); +} + +void send_secure_packet(uint8_t *msg, size_t msg_len) +{ + uint8_t nonce[12]; + generate_random(nonce, 12); + + uint8_t cipher[512]; + size_t cipher_len = 0; + + uint8_t tag[16]; + size_t tag_len = 0; + + esp_crypto_aes_gcm_encrypt( + &attr, + shared_key, + sizeof(shared_key), + PSA_ALG_GCM, + nonce, 12, + NULL, 0, + msg, msg_len, + cipher, sizeof(cipher), + &cipher_len + ); + + // build frame: + // [LEN][NONCE][CIPHER][TAG] + + uint16_t total = + 12 + cipher_len + 16; + + uint8_t out[600]; + + out[0] = (total >> 8) & 0xFF; + out[1] = total & 0xFF; + + memcpy(out + 2, nonce, 12); + memcpy(out + 14, cipher, cipher_len); + memcpy(out + 14 + cipher_len, tag, 16); + + sim800_tcp_send(out, total + 2); +} + + +void tcp_on_byte(char c) +{ + switch (state) + { + // WAIT FOR LENGTH (2 bytes) + case 0: + rx_buf[rx_len++] = c; + if (rx_len == 2) + { + expected_len = (rx_buf[0] << 8) | rx_buf[1]; + rx_len = 0; + + if (expected_len > MAX_FRAME) { + rx_len = 0; + state = 0; + return; + } + + state = 1; + } + break; + + // READ FRAME + case 1: + rx_buf[rx_len++] = c; + + if (rx_len >= expected_len) + { + process_encrypted_frame(rx_buf, rx_len); + + rx_len = 0; + state = 0; + } + break; + } +} \ No newline at end of file diff --git a/main/drivers/servergprs.h b/main/drivers/servergprs.h new file mode 100644 index 0000000..d9605d8 --- /dev/null +++ b/main/drivers/servergprs.h @@ -0,0 +1,12 @@ +#pragma once +#include "string.h" +#include "stddef.h" +#include "ctype.h" +#include "sim800.h" + +//#include + +extern char linebufIn[1024]; +extern char *linebufInPtr; + +void tcp_on_byte(char c); \ No newline at end of file diff --git a/main/drivers/sim800.c b/main/drivers/sim800.c new file mode 100644 index 0000000..f6a74e5 --- /dev/null +++ b/main/drivers/sim800.c @@ -0,0 +1,855 @@ +#include "sim800.h" +#include "drivers/pins.h" +#include "esp_log.h" +#include "freertos/idf_additions.h" +#include "projdefs.h" +#include +#define SIM800_UART UART_NUM_2 + +static QueueHandle_t uart_queue; + +static char smsBufSend[512]; +static uint8_t tcp_internal_buf[512]; +static uint8_t *tcp_send_buf = NULL; +static volatile int tcp_send_len = 0; +static volatile bool tcp_waiting_prompt = false; + +static volatile bool tcp_send_in_progress = false; + +static char line_buf[512]; +static volatile size_t line_pos = 0; + +static const char *TAG = "SIM800"; + +static volatile bool sim_ready = false; +static volatile bool sms_ready = false; +static volatile bool sim_needs_pin = false; + +static SemaphoreHandle_t sim800_done; +static bool sim800_waiting = false; +static bool sim800_result = false; +static modem_t modem; + +static bool sms_waiting_prompt = false; + +static SemaphoreHandle_t sim800_mutex; + +static SemaphoreHandle_t sms_mutex; + +void print_state() { + switch (modem.state) { + + case MODEM_STATE_OFF: + ESP_LOGI(TAG, "State is OFF"); + break; + case MODEM_STATE_INIT: + ESP_LOGI(TAG, "State is INIT"); + break; + case MODEM_STATE_READY: + ESP_LOGI(TAG, "State is Ready"); + break; + case MODEM_STATE_REGISTERING: + ESP_LOGI(TAG, "State is Registering"); + break; + case MODEM_STATE_REGISTERED: + ESP_LOGI(TAG, "State is Registered"); + break; + case MODEM_STATE_SMS_READY: + ESP_LOGI(TAG, "State is SMS Ready"); + break; + case MODEM_STATE_CALL_ACTIVE: + ESP_LOGI(TAG, "State is Call active"); + break; + case MODEM_STATE_GPRS_CONNECTING: + ESP_LOGI(TAG, "State is GPRS Connecting"); + break; + case MODEM_STATE_GPRS_UP: + ESP_LOGI(TAG, "State is GPRS UP"); + break; + case MODEM_STATE_TCP_CONNECTING: + ESP_LOGI(TAG, "State is TCP Connecting"); + break; + case MODEM_STATE_TCP_CONNECTED: + ESP_LOGI(TAG, "State is TCP Connected"); + break; + case MODEM_STATE_ERROR: + ESP_LOGI(TAG, "State is Error"); + break; + } +} + +void sim800_uart_init(void) { + uart_config_t uart_config = { + .baud_rate = 9600, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_APB, + }; + + uart_driver_install(SIM800_UART, 2048, 2048, 20, &uart_queue, 0); + + ESP_LOGI(TAG, "UART queue: %p", uart_queue); + + uart_param_config(SIM800_UART, &uart_config); + + uart_set_pin(SIM800_UART, HEADER_UART_TX, HEADER_UART_RX, UART_PIN_NO_CHANGE, + UART_PIN_NO_CHANGE); +} + +void modem_update_flags(void) { + modem.in_call = (modem.state == MODEM_STATE_CALL_ACTIVE); + + modem.gprs_active = (modem.state == MODEM_STATE_GPRS_UP); +} + +void modem_set_state(modem_state_t new_state) { + xSemaphoreTake(modem.state_mutex, portMAX_DELAY); + + if (modem.state == new_state) { + xSemaphoreGive(modem.state_mutex); + return; + } + + ESP_LOGI(TAG, "STATE: %d -> %d", modem.state, new_state); + + modem.state = new_state; + + print_state(); + + xSemaphoreGive(modem.state_mutex); + + modem_update_flags(); +} + +void modem_handle_event(modem_event_t ev) { + switch (modem.state) { + + // ----------------------- + case MODEM_STATE_INIT: + if (ev == EV_MODEM_OK) { + modem_set_state(MODEM_STATE_READY); + } + break; + + // ----------------------- + case MODEM_STATE_READY: + if (ev == EV_NETWORK_REG_OK) { + modem_set_state(MODEM_STATE_REGISTERED); + } + + if (ev == EV_INCOMING_CALL) { + bool was_gprs = modem.gprs_active; + + modem_set_state(MODEM_STATE_CALL_ACTIVE); + + if (was_gprs) { + sim800_command("AT+CIPSHUT", 5000); + modem_handle_event(EV_GPRS_DOWN); + } + } + + if (ev == EV_SMS_RECEIVED) { + // stay in same state (SMS is async service) + } + break; + + // ----------------------- + case MODEM_STATE_REGISTERED: + if (ev == EV_GPRS_UP) { + modem_set_state(MODEM_STATE_GPRS_UP); + } + + if (ev == EV_INCOMING_CALL) { + modem_set_state(MODEM_STATE_CALL_ACTIVE); + } + + if (ev == EV_NETWORK_LOST) { + modem_set_state(MODEM_STATE_REGISTERING); + } + break; + + // ----------------------- + case MODEM_STATE_GPRS_UP: + if (ev == EV_GPRS_DOWN) { + modem.tcp_connected = false; + modem_set_state(MODEM_STATE_SMS_READY); + } + + if (ev == EV_INCOMING_CALL) { + // SIM800 limitation: calls break data + modem_set_state(MODEM_STATE_CALL_ACTIVE); + } + break; + + // ----------------------- + case MODEM_STATE_CALL_ACTIVE: + if (ev == EV_CALL_ENDED) { + modem_set_state(MODEM_STATE_SMS_READY); + } + break; + + default: + break; + } +} + +bool modem_start_gprs(void) { + if (modem.state != MODEM_STATE_REGISTERED && + modem.state != MODEM_STATE_SMS_READY) + return false; + + modem_set_state(MODEM_STATE_GPRS_CONNECTING); + + // Always reset stack first + sim800_command("AT+CIPSHUT", 10000); + + sim800_command("AT+CIPMUX=0", 10000); + + // Ensure network attach (retry is IMPORTANT) + for (int i = 0; i < 5; i++) { + if (sim800_command("AT+CGATT=1", 3000)) + break; + vTaskDelay(pdMS_TO_TICKS(100)); + } + + if (!sim800_command("AT+CGATT?", 2000)) { + ESP_LOGW(TAG, "CGATT check failed"); + } + + if (!sim800_command("AT+CSTT=\"internet\"", 5000)) + return false; + + if (!sim800_command("AT+CIICR", 15000)) + return false; + + vTaskDelay(pdMS_TO_TICKS(100)); + + sim800_command("AT+CIFSR", 100); + + if (!sim800_command("AT+CIPHEAD=1", 15000)) + return false; + + modem_handle_event(EV_GPRS_UP); + modem_set_state(MODEM_STATE_GPRS_UP); + ESP_LOGI(TAG, "STARTED GPRS"); + return true; +} + +void sim800_csq_task(void *arg) { + while (1) { + /* + if (modem.state == MODEM_STATE_TCP_CONNECTED || + modem.state == MODEM_STATE_GPRS_UP) { + sim800_command("AT+CSQ", 2000); + } + */ + if (modem.need_gprs_restart && modem.network_registered) { + modem.need_gprs_restart = false; + ESP_LOGI(TAG, "STARTING GPRS"); + print_state(); + modem_start_gprs(); + print_state(); + vTaskDelay(pdMS_TO_TICKS(100)); + } + + print_state(); + if (modem.state == MODEM_STATE_GPRS_UP) { + sim800_tcp_connect("brn.systems", 6969); + if (modem.state != MODEM_STATE_TCP_CONNECTED) { + vTaskDelay(pdMS_TO_TICKS(1000)); + } + } + + vTaskDelay(pdMS_TO_TICKS(5000)); + } +} + +void sim800_task(void *arg) +{ + uart_event_t event; + uint8_t *data = malloc(1024); + + char lineBuf[128]; + int linePos = 0; + + int ipdRemaining = 0; + int ipdActive = 0; + + while (1) + { + if (xQueueReceive(uart_queue, &event, portMAX_DELAY)) + { + if (event.type == UART_DATA) + { + int len = uart_read_bytes(SIM800_UART, data, event.size, pdMS_TO_TICKS(100)); + + for (int i = 0; i < len; i++) + { + char c = data[i]; + + /* ========================= + RAW TCP MODE (NO PARSING) + ========================= */ + if (ipdActive) + { + tcp_on_byte(c); + + if (--ipdRemaining <= 0) + { + ipdActive = 0; + ESP_LOGI(TAG, "TCP RX END"); + } + + continue; + } + + /* ========================= + CONTROL LINE BUILDING + ========================= */ + if (c == '\r') + continue; + + if (c == '\n') + { + if (linePos > 0) + { + lineBuf[linePos] = 0; + + /* ========================= + DETECT +IPD HEADER + ========================= */ + if (strncmp(lineBuf, "+IPD,", 5) == 0) + { + int len = 0; + char *p = strchr(lineBuf, ':'); + + if (p) + { + *p = 0; + sscanf(lineBuf, "+IPD,%d", &len); + + ipdRemaining = len; + ipdActive = 1; + + ESP_LOGI(TAG, "TCP RX START len=%d", len); + + // if data after ':' already exists in same packet + p++; + while (*p && ipdRemaining > 0) + { + tcp_on_byte(*p++); + ipdRemaining--; + } + + if (ipdRemaining <= 0) + { + ipdActive = 0; + ESP_LOGI(TAG, "TCP RX END (inline)"); + } + } + } + else + { + sim800_handle_line(lineBuf); + } + + linePos = 0; + } + continue; + } + + /* ========================= + BUILD LINE BUFFER (CTRL) + ========================= */ + if (linePos < sizeof(lineBuf) - 1) + { + lineBuf[linePos++] = c; + } + } + } + + if (event.type == UART_FIFO_OVF) + { + ESP_LOGW(TAG, "FIFO overflow"); + uart_flush_input(SIM800_UART); + xQueueReset(uart_queue); + } + } + } + + free(data); +} + +void sim800_handle_line(char *line) { + ESP_LOGI(TAG, "LINE: [%s]", line); + + // ------------------------- + // COMMAND RESPONSE HANDLER + // ------------------------- + if (sim800_waiting) { + + if (strcmp(line, "OK") == 0) { + ESP_LOGI(TAG, "Command successful"); + sim800_result = true; + sim800_waiting = false; + xSemaphoreGive(sim800_done); + return; + } + + if (strcmp(line, "ERROR") == 0) { + sim800_result = false; + sim800_waiting = false; + tcp_send_in_progress = false; + xSemaphoreGive(sim800_done); + return; + } + if (strstr(line, "ERROR") != 0) { + sim800_result = false; + sim800_waiting = false; + tcp_send_in_progress = false; + xSemaphoreGive(sim800_done); + return; + } + } + + // ------------------------- + // CALL EVENTS + // ------------------------- + if (strcmp(line, "RING") == 0) { + ESP_LOGI(TAG, "Incoming call"); + modem.in_call = true; + if (modem.state == MODEM_STATE_GPRS_UP) { + sim800_command("AT+CIPSHUT", 10000); + modem_handle_event(EV_GPRS_DOWN); + } + modem_handle_event(EV_INCOMING_CALL); + return; + } + + if (strncmp(line, "NO CARRIER", 11) == 0) { + ESP_LOGI(TAG, "Call ended"); + modem.in_call = false; + modem_handle_event(EV_CALL_ENDED); + + if (modem.network_registered) { + modem.need_gprs_restart = true; + } + return; + } + + if (strncmp(line, "BUSY", 4) == 0) { + modem.in_call = false; + modem_handle_event(EV_CALL_ENDED); + if (modem.network_registered) { + modem.need_gprs_restart = true; + } + return; + } + + // ------------------------- + // SMS EVENTS + // ------------------------- + if (strncmp(line, "+CMTI:", 6) == 0) { + int index = -1; + sscanf(line, "+CMTI: \"SM\",%d", &index); + + ESP_LOGI(TAG, "New SMS at index %d", index); + + char cmd[32]; + snprintf(cmd, sizeof(cmd), "AT+CMGR=%d", index); + sim800_command(cmd, 5000); + + modem_handle_event(EV_SMS_RECEIVED); + + return; + } + + if (strncmp(line, "+CMGR:", 6) == 0) { + ESP_LOGI(TAG, "SMS header received"); + return; + } + + // ------------------------- + // SIGNAL QUALITY + // ------------------------- + if (strncmp(line, "+CSQ:", 5) == 0) { + int rssi, ber; + + if (sscanf(line, "+CSQ: %d,%d", &rssi, &ber) == 2) { + + modem.signal = rssi; + modem.last_rssi = rssi; + modem.last_ber = ber; + + ESP_LOGI(TAG, "CSQ RSSI=%d BER=%d", rssi, ber); + + // Optional: trigger behavior + if (rssi == 99) { + ESP_LOGW(TAG, "No signal"); + } else if (rssi < 10) { + ESP_LOGW(TAG, "Weak signal"); + } + } + return; + } + + // ------------------------- + // NETWORK REGISTRATION + // ------------------------- + if (strncmp(line, "+CREG:", 6) == 0) { + ESP_LOGI(TAG, "Network registration: %s", line); + int n, stat; + if (strlen(line) > 10) { + sscanf(line, "+CREG: %d,%d", &n, &stat); + ESP_LOGI(TAG, "Network registration data: %d,%d", n, stat); + + if (stat == 1 || stat == 5) { + modem.network_registered = 1; + ESP_LOGI(TAG, "REGISTERED"); + modem_handle_event(EV_NETWORK_REG_OK); + } else { + modem.network_registered = 0; + modem_handle_event(EV_NETWORK_LOST); + } + } else { + sscanf(line, "+CREG: %d", &stat); + ESP_LOGI(TAG, "Network registration data: %d,%d", stat); + + if (stat == 1 || stat == 5) { + modem.network_registered = 1; + ESP_LOGI(TAG, "REGISTERED"); + modem_handle_event(EV_NETWORK_REG_OK); + } else { + modem.network_registered = 0; + modem_handle_event(EV_NETWORK_LOST); + } + } + return; + } + + // ------------------------- + // GPRS / DATA EVENTS + // ------------------------- + if (strncmp(line, "CONNECT OK", 10) == 0) { + ESP_LOGI(TAG, "TCP connected"); + modem_set_state(MODEM_STATE_TCP_CONNECTED); + + modem.tcp_connected = true; + + modem_set_state(MODEM_STATE_TCP_CONNECTED); + + return; + } + + if (strstr(line, "CONNECT FAIL")) { + modem.tcp_connected = false; + modem_set_state(MODEM_STATE_GPRS_UP); + } + + if (strstr(line, "+CPIN: READY")) { + sim_ready = true; + modem_handle_event(EV_MODEM_OK); + ESP_LOGI(TAG, "SIM ready"); + return; + } + + if (strstr(line, "+CPIN: SIM PIN")) { + sim_needs_pin = true; + ESP_LOGI(TAG, "SIM needs pin"); + return; + } + + if (strstr(line, "CALL Ready")) { + modem.network_registered = 1; + ESP_LOGI(TAG, "SMS subsystem ready"); + return; + } + + if (strstr(line, "SMS Ready")) { + sms_ready = true; + modem.network_registered = 1; + ESP_LOGI(TAG, "SMS subsystem ready"); + return; + } + + if (strncmp(line, "SEND OK", 7) == 0) { + ESP_LOGI(TAG, "Data sent"); + tcp_send_in_progress = false; + return; + } + + if (strncmp(line, "CLOSED", 6) == 0) { + tcp_send_in_progress = false; + modem.tcp_connected = false; + modem_set_state(MODEM_STATE_GPRS_UP); + return; + } + + /* + if (strncmp(line, "+IPD,", 5) == 0) { + + ESP_LOGI(TAG, "TCP DATA: %s", line); + + int len = 0; + sscanf(line, "+IPD,%d", &len); + // format: +IPD,len:data + char *p = strchr(line, ':'); + if (p) { + p++; + + ESP_LOGI(TAG, "RX TCP payload: %s", p); + + // TODO: forward to your app layer + tcp_on_data(p, len); + } + + return; + } + + + if (strncmp(line, "+RECEIVE", 8) == 0) { + + char *payload = strchr(line, ':'); + + int pipe = 0; + if (payload) { + payload++; // skip ':' + int len = 0; + sscanf(line, "+RECEIVE,%d,%d", &pipe, &len); + + ESP_LOGI(TAG, "TCP RX (%d): %.*s", pipe, len, payload); + + tcp_on_data(payload, len); + } + + return; + } + */ +} + +void sim800_parse(char *data) { + while (*data) { + + char c = *data++; + + putchar(c); + + if (c == '\r') + continue; + + if (line_buf[0] == '>') { + + if (sms_waiting_prompt) { + sms_waiting_prompt = false; + + uart_write_bytes(SIM800_UART, smsBufSend, strlen(smsBufSend)); + uint8_t ctrlz = 0x1A; + uart_write_bytes(SIM800_UART, &ctrlz, 1); + } + + if (tcp_waiting_prompt) { + ESP_LOGI(TAG, "Got TCP >"); + tcp_waiting_prompt = false; + + uart_write_bytes(SIM800_UART, tcp_send_buf, tcp_send_len); + + uint8_t ctrlz = 0x1A; + uart_write_bytes(SIM800_UART, &ctrlz, 1); + tcp_send_in_progress = false; + } + + continue; + } + + if (c == '\n') { + + if (line_pos > 0) { + + line_buf[line_pos] = 0; + + sim800_handle_line(line_buf); + + line_pos = 0; + } + + continue; + } + + if (line_pos < sizeof(line_buf) - 1) { + line_buf[line_pos++] = c; + } + } +} + +void sim800_send(const char *cmd) { + ESP_LOGI(TAG, "TX: %s", cmd); + + uart_write_bytes(SIM800_UART, cmd, strlen(cmd)); + + uart_write_bytes(SIM800_UART, "\r\n", 2); +} + +bool sim800_command(const char *cmd, uint32_t timeout_ms) { + xSemaphoreTake(sim800_mutex, portMAX_DELAY); + + sim800_waiting = true; + sim800_result = false; + + sim800_send(cmd); + + bool ok = xSemaphoreTake(sim800_done, pdMS_TO_TICKS(timeout_ms)); + + if (!ok) { + sim800_waiting = false; // important cleanup + ESP_LOGW(TAG, "Timeout"); + + xSemaphoreGive(sim800_mutex); + return false; + } + + bool result = sim800_result; + + sim800_waiting = false; + xSemaphoreGive(sim800_mutex); + + return result; +} + +void init_modem(void *arg) { + modem.state_mutex = xSemaphoreCreateMutex(); + modem.state = MODEM_STATE_INIT; + modem.network_registered = 0; + modem.in_call = false; + modem.gprs_active = false; + modem.need_gprs_restart = true; + sim800_done = xSemaphoreCreateBinary(); + sim800_mutex = xSemaphoreCreateMutex(); + sms_mutex = xSemaphoreCreateMutex(); + xTaskCreate(sim800_task, "sim800_task", 4096, NULL, 10, NULL); + sim800_command("AT+CFUN=1,1", 1); + while (1) { + if (sim800_command("AT", 1000)) { + ESP_LOGI(TAG, "Modem alive"); + sim800_command("ATE0", 100); + sim800_command("AT+IPR=115200", 100); + uart_set_baudrate(SIM800_UART, 115200); + vTaskDelay(pdMS_TO_TICKS(50)); + while (!sim_needs_pin) { + vTaskDelay(pdMS_TO_TICKS(100)); + } + sim800_command("AT+CPIN=\"1577\"", 5000); // change to used sim, this is a test pin + while (!sim_ready) { + vTaskDelay(pdMS_TO_TICKS(100)); + } + /* + while (!sms_ready) { + vTaskDelay(pdMS_TO_TICKS(100)); + } + */ + sim800_command("AT+CREG=1", 3000); + while (!modem.network_registered) { + sim800_command("AT+CREG?", 3000); + vTaskDelay(pdMS_TO_TICKS(100)); + } + sim800_command("AT+CMEE=2", 100); + sim800_command("AT+CMGF=1", 3000); + sim800_command("AT+CNMI=2,1,0,0,0", 3000); + xTaskCreate(sim800_csq_task, "csq_task", 3072, NULL, 5, NULL); + break; + } else { + ESP_LOGE(TAG, "No response"); + } + } + while (1) { + vTaskDelay(pdTICKS_TO_MS(1000)); + } +} + +bool sim800_send_sms(const char *number, const char *msg) { + xSemaphoreTake(sms_mutex, portMAX_DELAY); + + strcpy(smsBufSend, msg); + + char cmd[64]; + snprintf(cmd, sizeof(cmd), "AT+CMGS=\"%s\"", number); + + sms_waiting_prompt = true; + sim800_send(cmd); + + return true; +} + +bool sim800_call(const char *number) { + if (modem.gprs_active) { + sim800_command("AT+CIPSHUT", 5000); + modem_handle_event(EV_GPRS_DOWN); + } + + char cmd[64]; + snprintf(cmd, sizeof(cmd), "ATD%s;", number); + + return sim800_command(cmd, 15000); +} +bool sim800_hangup(void) { return sim800_command("ATH", 3000); } + +bool sim800_answer(void) { return sim800_command("ATA", 5000); } + +bool sim800_tcp_connect(const char *host, int port) { + if (modem.state != MODEM_STATE_GPRS_UP) + return false; + + snprintf(modem.tcp_host, sizeof(modem.tcp_host), "%s", host); + modem.tcp_port = port; + + modem_set_state(MODEM_STATE_TCP_CONNECTING); + + char cmd[128]; + snprintf(cmd, sizeof(cmd), "AT+CIPSTART=\"TCP\",\"%s\",\"%d\"", host, port); + + if (!sim800_command(cmd, 15000)) { + modem_set_state(MODEM_STATE_GPRS_UP); + return false; + } + + return true; +} + +bool sim800_tcp_send(const void *data, int len) { + if (modem.state != MODEM_STATE_TCP_CONNECTED) + return false; + + if (tcp_send_in_progress) + return false; + tcp_send_in_progress = true; + + xSemaphoreTake(sim800_mutex, portMAX_DELAY); + + memcpy(tcp_internal_buf, data, len); + tcp_send_buf = tcp_internal_buf; + tcp_send_len = len; + tcp_waiting_prompt = true; + + char cmd[32]; + snprintf(cmd, sizeof(cmd), "AT+CIPSEND=%d", len); + sim800_send(cmd); + + // actual sending happens when '>' arrives in parser + + xSemaphoreGive(sim800_mutex); + return true; +} + +bool sim800_tcp_close(void) { + if (!modem.tcp_connected) + return true; + + bool ok = sim800_command("AT+CIPCLOSE", 5000); + + modem.tcp_connected = false; + + modem_set_state(MODEM_STATE_GPRS_UP); + + return ok; +} \ No newline at end of file diff --git a/main/drivers/sim800.h b/main/drivers/sim800.h new file mode 100644 index 0000000..fb8641e --- /dev/null +++ b/main/drivers/sim800.h @@ -0,0 +1,115 @@ +#pragma once + +#include "driver/uart.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "pins.h" +#include "servergprs.h" + +typedef struct { + SemaphoreHandle_t done; + char response[256]; + bool success; +} sim800_cmd_t; + +typedef enum { + MODEM_STATE_OFF = 0, + MODEM_STATE_INIT, + MODEM_STATE_READY, + + MODEM_STATE_REGISTERING, + MODEM_STATE_REGISTERED, + + MODEM_STATE_SMS_READY, + + MODEM_STATE_CALL_ACTIVE, + + MODEM_STATE_GPRS_CONNECTING, + MODEM_STATE_GPRS_UP, + + MODEM_STATE_TCP_CONNECTING, + MODEM_STATE_TCP_CONNECTED, + + MODEM_STATE_ERROR +} modem_state_t; + +typedef struct { + modem_state_t state; + + bool in_call; + bool gprs_active; + bool need_gprs_restart; + + int signal; + int network_registered; + + int last_rssi; + int last_ber; + + bool tcp_connected; + char tcp_host[64]; + int tcp_port; + + SemaphoreHandle_t state_mutex; +} modem_t; + +typedef enum { + EV_NONE = 0, + + EV_MODEM_OK, + EV_MODEM_ERROR, + + EV_NETWORK_REG_OK, + EV_NETWORK_LOST, + + EV_INCOMING_CALL, + EV_CALL_ENDED, + + EV_SMS_RECEIVED, + + EV_GPRS_UP, + EV_GPRS_DOWN +} modem_event_t; + + +void print_state(); + +void sim800_uart_init(void); + +void modem_update_flags(void); + +void modem_set_state(modem_state_t new_state); + +void modem_handle_event(modem_event_t ev); + +bool modem_start_gprs(void); + +void sim800_csq_task(void *arg); + +void sim800_task(void *arg); + +void sim800_handle_line(char *line); + +void sim800_parse(char *data); + +void sim800_send(const char *cmd); + +bool sim800_command(const char *cmd, uint32_t timeout_ms); + +void init_modem(void *arg); + +bool sim800_send_sms(const char *number, const char *msg); + +bool sim800_call(const char *number); + +bool sim800_hangup(void); + +bool sim800_answer(void); + +bool sim800_tcp_connect(const char *host, int port); + +bool sim800_tcp_send(const void *data, int len); + +bool sim800_tcp_close(void); \ No newline at end of file diff --git a/main/idf_component.yml b/main/idf_component.yml index 512ed94..30f4e82 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -16,3 +16,4 @@ dependencies: # public: true espressif/esp_tinyusb: ~2.2.0 espressif/led_strip: ^3.0.0 + espressif/esp_codec_dev: ^1.5.9 diff --git a/main/main.c b/main/main.c index 4cdc78b..7076e9d 100644 --- a/main/main.c +++ b/main/main.c @@ -1,9 +1,12 @@ +#include #include #include +#include #include "driver/gpio.h" #include "drivers/fonts.h" #include "drivers/i2c.h" +#include "drivers/sim800.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -21,13 +24,19 @@ #include "hal/gpio_types.h" #include "projdefs.h" +char linebuf[1024]; +char *linebufPtr = linebuf; + void app_main(void) { + memset(linebuf, 0, sizeof(linebuf)); printf("boot\n"); set_pin_dirs(); battery_start_task(); i2c_init(BUS_I2C_SDA, BUS_I2C_SCL); ws2812_init(); + sim800_uart_init(); + xTaskCreate(init_modem, "init_modem", 4096, NULL, 10, NULL); audio_i2s_init(); vTaskDelay(pdMS_TO_TICKS(10)); es8311_init(); @@ -66,13 +75,40 @@ void app_main(void) { font5x7); while (tca8418_read(&key, &pressed)) { + char c = getKeyboardChar(key); + + if (!pressed) { + if (isprint(c)) { + *(linebufPtr++) = c; + } else { + switch (c) { + case '\n': + sim800_tcp_send(linebuf, linebufPtr - linebuf); + linebufPtr = linebuf; + memset(linebuf, 0, sizeof(linebuf)); + break; + + case '\b': + if (linebufPtr > linebuf) { + *(linebufPtr--) = 0; + } + } + } + if (linebufPtr > linebuf + 1023) { + linebufPtr = linebuf + 1023; + } + } + + /* snprintf(buf, sizeof(buf), "key=%c %s %d %s", getKeyboardChar(key), getKeyboardKeyName(key), key, pressed ? "pressed" : "released"); printf("%s\n", buf); - st7789_draw_string(30, 30, buf, 0x07E0, 0x0000, true, fontHitachi); - audio_beep(); + */ + + // audio_beep(); } + st7789_draw_string(30, 30, linebuf, 0x07E0, 0x0000, true, fontHitachi); bmi270_data_t bmiData; @@ -88,6 +124,8 @@ void app_main(void) { snprintf(buf, sizeof(buf), "BAT: %d%%(%.3fV)", getBatteryPercentage(), getBatteryVoltage()); st7789_draw_string(140, 10, buf, 0x07E0, 0x0000, true, fontHitachi); + + st7789_draw_string(30, 90, linebufIn, 0x07E0, 0x0000, true, fontHitachi); st7789_flush(); vTaskDelay(pdMS_TO_TICKS(20)); // 50 fps max