#include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/uart.h" #include "driver/gpio.h" #include "netif/ppp/pppapi.h" #include "netif/ppp/pppos.h" #include "netif/ppp/ppp.h" #include "lwip/netif.h" #include "lwip/sockets.h" #include "lwip/inet.h" #include "esp_log.h" #include "esp_event.h" #include "esp_netif.h" #define MODEM_UART UART_NUM_1 #define UART_TX_PIN 37 #define UART_RX_PIN 36 #define UART_BAUDRATE 115200 #define MODEM_PWRKEY 1 #define APN "jionet" #define MODEM_PDP_TYPE "IPV6" // IPv6-only as suggested static const char *TAG = "PPPOS_ONLY"; static ppp_pcb *ppp; static struct netif ppp_netif; static esp_netif_t *esp_netif_ppp; /* UART init */ static void uart_init(void) { uart_config_t cfg = { .baud_rate = UART_BAUDRATE, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, }; uart_driver_install(MODEM_UART, 4096, 4096, 0, NULL, 0); uart_param_config(MODEM_UART, &cfg); uart_set_pin(MODEM_UART, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); } /* Modem power pulse */ static void modem_power_on(void) { gpio_set_direction(MODEM_PWRKEY, GPIO_MODE_OUTPUT); gpio_set_level(MODEM_PWRKEY, 1); vTaskDelay(pdMS_TO_TICKS(100)); gpio_set_level(MODEM_PWRKEY, 0); vTaskDelay(pdMS_TO_TICKS(1200)); gpio_set_level(MODEM_PWRKEY, 1); vTaskDelay(pdMS_TO_TICKS(6000)); } /* AT helpers */ static void at_cmd(const char *cmd, int delay_ms) { uart_write_bytes(MODEM_UART, cmd, strlen(cmd)); uart_write_bytes(MODEM_UART, "\r", 1); vTaskDelay(pdMS_TO_TICKS(delay_ms)); } static void at_cmd_log(const char *cmd, int collect_ms) { uint8_t buf[256]; uart_flush_input(MODEM_UART); uart_write_bytes(MODEM_UART, cmd, strlen(cmd)); uart_write_bytes(MODEM_UART, "\r", 1); TickType_t start = xTaskGetTickCount(); while ((xTaskGetTickCount() - start) < pdMS_TO_TICKS(collect_ms)) { int len = uart_read_bytes(MODEM_UART, buf, sizeof(buf) - 1, 200 / portTICK_PERIOD_MS); if (len > 0) { buf[len] = 0; ESP_LOGI(TAG, "%s rsp: %s", cmd, buf); } } } /* Wait for CONNECT */ static bool wait_for_connect(int timeout_ms) { uint8_t buf[256]; TickType_t start = xTaskGetTickCount(); while ((xTaskGetTickCount() - start) < pdMS_TO_TICKS(timeout_ms)) { int len = uart_read_bytes(MODEM_UART, buf, sizeof(buf) - 1, 200 / portTICK_PERIOD_MS); if (len > 0) { buf[len] = 0; ESP_LOGI(TAG, "MODEM: %s", buf); if (strstr((char*)buf, "CONNECT")) return true; if (strstr((char*)buf, "ERROR") || strstr((char*)buf, "NO CARRIER")) return false; } } ESP_LOGE(TAG, "Timed out waiting for CONNECT"); return false; } /* PPP callbacks */ static u32_t ppp_output_cb(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) { uart_write_bytes(MODEM_UART, (const char *)data, len); return len; } static void ppp_status_cb(ppp_pcb *pcb, int err, void *ctx) { if (err == PPPERR_NONE) ESP_LOGI(TAG, "PPP CONNECTED"); else ESP_LOGE(TAG, "PPP ERROR: %d", err); } /* PPP RX task */ static void ppp_rx_task(void *arg) { uint8_t buf[512]; while (1) { int len = uart_read_bytes(MODEM_UART, buf, sizeof(buf), portMAX_DELAY); if (len > 0 && ppp) { pppos_input_tcpip(ppp, buf, len); } } } /* IP events */ static void ip_event_handler(void* arg, esp_event_base_t base, int32_t id, void* data) { if (id == IP_EVENT_PPP_GOT_IP) { ip_event_got_ip_t *ev = (ip_event_got_ip_t *)data; ESP_LOGI(TAG, "PPP GOT IPv4: " IPSTR, IP2STR(&ev->ip_info.ip)); } else if (id == IP_EVENT_GOT_IP6) { ip_event_got_ip6_t *ev6 = (ip_event_got_ip6_t *)data; ESP_LOGI(TAG, "PPP GOT IPv6: " IPV6STR, IPV62STR(ev6->ip6_info.ip)); } } /* PDP bring-up on CID1, IPV6, activate before dial */ static bool setup_pdp_and_connect(void) { ESP_LOGI(TAG, "Setting PDP CID1 as IPV6 and activating"); at_cmd("AT+QIDEACT=1", 500); // deactivate lingering session char cmd[96]; snprintf(cmd, sizeof(cmd), "AT+CGDCONT=1,\"%s\",\"%s\"", MODEM_PDP_TYPE, APN); // IPV6, jionet at_cmd(cmd, 500); at_cmd("AT+CGACT=1,1", 800); // activate CID1 at_cmd_log("AT+CGACT?", 500); // expect: +CGACT: 1,1 at_cmd_log("AT+CGPADDR=1", 500); // should show a global IPv6 uart_flush_input(MODEM_UART); ESP_LOGI(TAG, "Dialing ATD*99***1# ..."); uart_write_bytes(MODEM_UART, "ATD*99***1#\r", 12); bool got_connect = wait_for_connect(10000); if (!got_connect) { ESP_LOGW(TAG, "Dial failed; trying AT+CGDATA on CID1"); uart_flush_input(MODEM_UART); uart_write_bytes(MODEM_UART, "AT+CGDATA=\"PPP\",1\r", 18); got_connect = wait_for_connect(10000); } return got_connect; } /* Start PPP */ static void start_pppos(void) { at_cmd_log("AT+CPIN?", 400); at_cmd_log("AT+CSQ", 400); at_cmd_log("AT+CGATT?", 400); bool connected = setup_pdp_and_connect(); if (!connected) { ESP_LOGE(TAG, "No CONNECT; aborting PPP start"); return; } esp_netif_config_t cfg = ESP_NETIF_DEFAULT_PPP(); esp_netif_ppp = esp_netif_new(&cfg); assert(esp_netif_ppp); esp_netif_attach(esp_netif_ppp, &ppp_netif); ppp = pppapi_pppos_create(&ppp_netif, ppp_output_cb, ppp_status_cb, NULL); if (!ppp) { ESP_LOGE(TAG, "pppos_create failed"); return; } pppapi_set_default(ppp); ppp_set_usepeerdns(ppp, 1); pppapi_set_auth(ppp, PPPAUTHTYPE_NONE, NULL, NULL); xTaskCreatePinnedToCore(ppp_rx_task, "ppp_rx", 6144, NULL, 6, NULL, 1); ESP_LOGI(TAG, "Calling pppapi_connect..."); pppapi_connect(ppp, 0); // If the peer does not provide DNS over IPv6, set manually: // esp_netif_dns_info_t dns = { // .ip = { // .type = IPADDR_TYPE_V6, // .u_addr.ip6 = { PP_HTONL(0x20014860), PP_HTONL(0x48600000), 0, PP_HTONL(0x00008888) } // 2001:4860:4860::8888 // } // }; // esp_netif_set_dns_info(esp_netif_ppp, ESP_NETIF_DNS_MAIN, &dns); } /* MAIN */ void app_main(void) { ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &ip_event_handler, NULL)); uart_init(); modem_power_on(); start_pppos(); for (;;) { vTaskDelay(pdMS_TO_TICKS(5000)); } }