This commit is contained in:
2026-05-18 22:59:45 +02:00
parent aed00ceda2
commit 77c9d4a4d5
10 changed files with 1185 additions and 119 deletions
+855
View File
@@ -0,0 +1,855 @@
#include "sim800.h"
#include "drivers/pins.h"
#include "esp_log.h"
#include "freertos/idf_additions.h"
#include "projdefs.h"
#include <stdio.h>
#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;
}