From 8c36ac5b92941ae0d024eea00d110b200446ef63 Mon Sep 17 00:00:00 2001 From: levkovigor Date: Wed, 18 Aug 2021 14:21:08 +0300 Subject: [PATCH] Add files via upload --- LICENSE.txt | 20 ++ .../simple_pppos_with_mqtt.ino | 182 ++++++++++++ keywords.txt | 34 +++ library.json | 15 + library.properties | 9 + src/PPPOS.c | 265 +++++++++++++++++ src/PPPOS.h | 28 ++ src/PPPOSClient.cpp | 266 ++++++++++++++++++ src/PPPOSClient.h | 52 ++++ 9 files changed, 871 insertions(+) create mode 100644 LICENSE.txt create mode 100644 examples/simple_pppos_with_mqtt/simple_pppos_with_mqtt.ino create mode 100644 keywords.txt create mode 100644 library.json create mode 100644 library.properties create mode 100644 src/PPPOS.c create mode 100644 src/PPPOS.h create mode 100644 src/PPPOSClient.cpp create mode 100644 src/PPPOSClient.h diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c4c42ab --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2021 Igor Levkov + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/examples/simple_pppos_with_mqtt/simple_pppos_with_mqtt.ino b/examples/simple_pppos_with_mqtt/simple_pppos_with_mqtt.ino new file mode 100644 index 0000000..9cecb83 --- /dev/null +++ b/examples/simple_pppos_with_mqtt/simple_pppos_with_mqtt.ino @@ -0,0 +1,182 @@ +#include +#include +#include + +#define SERIAL_BR 115200 +#define GSM_SERIAL 1 +#define GSM_RX 16 +#define GSM_TX 17 +#define GSM_BR 115200 + +char* server = "example.com"; +char* ppp_user = ""; +char* ppp_pass = ""; +String APN = "internet"; + +#define WEB_SERVER "www.w3.org" +#define WEB_URL "https://www.w3.org/TR/PNG/iso_8859-1.txt" +static const char *REQUEST = "GET " WEB_URL "\ HTTP/1.1\r\n" + "Host: "WEB_SERVER"\r\n" + "Connection: keep-alive\r\n" + "User-Agent: esp/1.0 esp32\r\n" + "\r\n"; + +String buffer = ""; +char *data = (char *) malloc(1024); +bool atMode = true; + +void callback(char* topic, byte* payload, unsigned int length) { + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + for (int i=0;i (_tg + 5000)) { buffer = ""; return false; } + } + buffer = ""; + return false; +} + +bool startPPPOS(){ + String apnSet = "AT+CGDCONT=1,\"IP\",\"" + APN + "\"\n"; + if (!sendCommandWithAnswer(apnSet, "OK")) { return false; } + if (!sendCommandWithAnswer("AT+CGDATA=\"PPP\",1\n", "CONNECT")) { return false; } + atMode = false; + PPPOS_start(); + unsigned long _tg = millis(); + while(!PPPOS_isConnected()) { + if (millis() > (_tg + 10000)) { PPPOS_stop(); atMode = true; return false; } + } + Serial.println("PPPOS Started"); + return true; +} + +bool enterATModePPPOS(){ + if (PPPOS_isConnected() && !atMode){ + if (sendCommandWithAnswer("+++", "OK")) { atMode = true; return true;} + } + return false; +} + +bool cancelATModePPPOS(){ + if (PPPOS_isConnected() && atMode){ + if (sendCommandWithAnswer("ATO\n", "CONNECT")) { atMode = false; return true;} + } + return false; +} + +void simpleGetRequest(){ + if (!ppposClient.connected() ) { + Serial.println("Connecting..."); + ppposClient.connect(WEB_SERVER, 80); + } + if (ppposClient.connected() ) { + Serial.println("Connected"); + Serial.println(ppposClient.write(REQUEST, strlen(REQUEST))); + while(ppposClient.available()){ + Serial.print((char)ppposClient.read()); + } + } +} + +void reconnect() { + // Loop until we're reconnected + while (!client.connected()) { + Serial.print("Attempting MQTT connection..."); + // Attempt to connect + if (client.connect("arduinoClient")) { + Serial.println("connected"); + // Once connected, publish an announcement... + client.publish("outTopic","hello world"); + // ... and resubscribe + client.subscribe("inTopic"); + } else { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void setup() +{ + Serial.begin(SERIAL_BR); + PPPOS_init(GSM_TX, GSM_RX, GSM_BR, GSM_SERIAL, ppp_user, ppp_pass); + client.setServer(server, 1883); + client.setCallback(callback); + + Serial.println("1) Start GSM Communication: AT\\n"); + Serial.println("2) Start PPP Protocol: ppp\\n"); + Serial.println("3) Test GET Request: get\\n"); + Serial.println("4) Switch to AT Mode: +++\\n"); + Serial.println("5) Switch to Data Mode: ATO\\n"); + Serial.println("6) Stop PPP Protocol: stop\\n"); +} + +void loop() +{ + if (!PPPOS_isConnected() || atMode){ + data = PPPOS_read(); + if (data != NULL){ + Serial.println(data); + } + } + + if (Serial.available()){ + char c = Serial.read(); + if (c == '\n'){ + if (buffer == "ppp") { + Serial.println("Starting PPPOS..."); + if (startPPPOS()) { Serial.println("Starting PPPOS... OK"); } else { Serial.println("Starting PPPOS... Failed"); } + } else if (buffer == "stop") { + PPPOS_stop(); + } else if (buffer == "+++") { + if (enterATModePPPOS()) { Serial.println("Entering ATMode... OK"); } else { Serial.println("Entering ATMode... Failed"); } + } else if (buffer == "ATO") { + if (cancelATModePPPOS()) { Serial.println("Canceling ATMode... OK"); } else { Serial.println("Canceling ATMode... Failed"); } + } else if (buffer == "get") { + simpleGetRequest(); + } else { + buffer += "\n"; + PPPOS_write((char *)buffer.c_str()); + Serial.println(buffer); + } + buffer = ""; + } else { + buffer += c; + } + } + + if (PPPOS_isConnected() && !atMode) { + if (!client.connected()) { + reconnect(); + } + client.loop(); + } +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..fd00fbb --- /dev/null +++ b/keywords.txt @@ -0,0 +1,34 @@ +####################################### +# Syntax Coloring Map For PPPOSClient +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +PPPOSClient KEYWORD1 +PPPOS KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +connect KEYWORD2 +PPPOS_write KEYWORD2 +available KEYWORD2 +read KEYWORD2 +peek KEYWORD2 +flush KEYWORD2 +stop KEYWORD2 +connected KEYWORD2 +PPPOS_init KEYWORD2 +PPPOS_isConnected KEYWORD2 +PPPOS_start KEYWORD2 +PPPOS_status KEYWORD2 +PPPOS_stop KEYWORD2 +PPPOS_write KEYWORD2 +PPPOS_read KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/library.json b/library.json new file mode 100644 index 0000000..64474ec --- /dev/null +++ b/library.json @@ -0,0 +1,15 @@ +{ + "name": "PPPOSClient", + "keywords": "gsm, sim800, internet, ppp, iot, mqtt", + "description": "A client library for gsm ppp protocol. This library can be used to make GET and POST requests and to connect mqtt with PubSubClient. It supports ESP32.", + "repository": { + "type": "git", + "url": "https://github.com/levkovigor/ppposclient.git" + }, + "version": "1.0", + "examples": "examples/*/*.ino", + "frameworks": "arduino", + "platforms": [ + "espressif32" + ] +} diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..1a6496c --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=PPPOSClient +version=1.0 +author=Igor Levkov +maintainer=Igor Levkov +sentence=A client library for gsm ppp protocol. +paragraph=This library can be used to make GET and POST requests and to connect mqtt with PubSubClient. It supports ESP32. +category=Communication +url=https://github.com/levkovigor/ppposclient.git +architectures=* diff --git a/src/PPPOS.c b/src/PPPOS.c new file mode 100644 index 0000000..84bdf89 --- /dev/null +++ b/src/PPPOS.c @@ -0,0 +1,265 @@ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#include "driver/uart.h" +#include "driver/gpio.h" +#include "tcpip_adapter.h" +#include "netif/ppp/pppos.h" +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/netdb.h" +#include "lwip/dns.h" +#include "netif/ppp/pppapi.h" +#include "PPPOS.h" + + + #ifdef __cplusplus +extern "C" { +#endif + + +bool PPPOS_firststart = false; +bool PPPOS_connected = false; +bool PPPOS_started = false; +char *PPP_User = ""; +char *PPP_Pass = ""; +char PPPOS_out[BUF_SIZE]; + +/* UART */ +int PPPOS_uart_num; + +static const char *TAG = "status"; + +/* The PPP control block */ +ppp_pcb *ppp; + +/* The PPP IP interface */ +struct netif ppp_netif; + +/* PPP status callback example */ +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: Connected\n"); + #if PPP_IPV4_SUPPORT + ESP_LOGE(TAG, " ipaddr_v4 = %s\n", ipaddr_ntoa(&pppif->ip_addr)); + ESP_LOGE(TAG, " gateway = %s\n", ipaddr_ntoa(&pppif->gw)); + ESP_LOGE(TAG, " netmask = %s\n", ipaddr_ntoa(&pppif->netmask)); + #endif /* PPP_IPV4_SUPPORT */ + #if PPP_IPV6_SUPPORT + ESP_LOGE(TAG, " ipaddr_v6 = %s\n", ip6addr_ntoa(netif_ip6_addr(pppif, 0))); + #endif /* PPP_IPV6_SUPPORT */ + PPPOS_connected = true; + + break; + } + case PPPERR_PARAM: { + ESP_LOGE(TAG, "status_cb: Invalid parameter\n"); + break; + } + case PPPERR_OPEN: { + ESP_LOGE(TAG, "status_cb: Unable to open PPP session\n"); + break; + } + case PPPERR_DEVICE: { + ESP_LOGE(TAG, "status_cb: Invalid I/O device for PPP\n"); + break; + } + case PPPERR_ALLOC: { + ESP_LOGE(TAG, "status_cb: Unable to allocate resources\n"); + break; + } + case PPPERR_USER: { + ESP_LOGE(TAG, "status_cb: User interrupt\n"); + PPPOS_started = false; + PPPOS_connected = false; + break; + } + case PPPERR_CONNECT: { + ESP_LOGE(TAG, "status_cb: Connection lost\n"); + PPPOS_started = false; + PPPOS_connected = false; + break; + } + case PPPERR_AUTHFAIL: { + ESP_LOGE(TAG, "status_cb: Failed authentication challenge\n"); + PPPOS_started = false; + PPPOS_connected = false; + break; + } + case PPPERR_PROTOCOL: { + ESP_LOGE(TAG, "status_cb: Failed to meet protocol\n"); + PPPOS_started = false; + PPPOS_connected = false; + break; + } + case PPPERR_PEERDEAD: { + ESP_LOGE(TAG, "status_cb: Connection timeout\n"); + PPPOS_started = false; + PPPOS_connected = false; + break; + } + case PPPERR_IDLETIMEOUT: { + ESP_LOGE(TAG, "status_cb: Idle Timeout\n"); + PPPOS_started = false; + PPPOS_connected = false; + break; + } + case PPPERR_CONNECTTIME: { + ESP_LOGE(TAG, "status_cb: Max connect time reached\n"); + PPPOS_started = false; + PPPOS_connected = false; + break; + } + case PPPERR_LOOPBACK: { + ESP_LOGE(TAG, "status_cb: Loopback detected\n"); + PPPOS_started = false; + PPPOS_connected = false; + break; + } + default: { + ESP_LOGE(TAG, "status_cb: Unknown error code %d\n", err_code); + PPPOS_started = false; + PPPOS_connected = false; + break; + } + } + + if (err_code == PPPERR_NONE) { + return; + } + if (err_code == PPPERR_USER) { + return; + } +} + +static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) +{ + return uart_write_bytes(PPPOS_uart_num, (const char *)data, len); +} + + + +static void pppos_client_task(void *pvParameters) +{ + +char* data = (char*)malloc(BUF_SIZE); + while (1) { + while (PPPOS_started) { + memset(data, 0, BUF_SIZE); + int len = uart_read_bytes(PPPOS_uart_num, (uint8_t *)data, BUF_SIZE, 10 / portTICK_RATE_MS); + if (len > 0) { + pppos_input_tcpip(ppp, (u8_t *)data, len); + } + vTaskDelay(100 / portTICK_PERIOD_MS); + } + vTaskDelay(100 / portTICK_PERIOD_MS); + } + + +} +void PPPOS_init(int txPin, int rxPin, int baudrate, int uart_number, char* user, char* pass){ + PPPOS_uart_num = uart_number; + gpio_set_direction(txPin, GPIO_MODE_OUTPUT); + gpio_set_direction(rxPin, GPIO_MODE_INPUT); + gpio_set_pull_mode(rxPin, GPIO_PULLUP_ONLY); + + uart_config_t uart_config = { + .baud_rate = baudrate, + .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(PPPOS_uart_num, &uart_config) ; + uart_set_pin(PPPOS_uart_num, txPin, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + uart_driver_install(PPPOS_uart_num, BUF_SIZE * 2, BUF_SIZE * 2, 0, NULL, 0); + tcpip_adapter_init(); + PPP_User = user; + PPP_Pass = pass; + xTaskCreate(&pppos_client_task, "pppos_client_task", 10048, NULL, 5, NULL); +} + +bool PPPOS_isConnected(){ + return PPPOS_connected; +} + +void PPPOS_start(){ + if (!PPPOS_firststart){ + ppp = pppapi_pppos_create(&ppp_netif, ppp_output_callback, ppp_status_cb, NULL); + + if (ppp == NULL) { + return; + } + + pppapi_set_default(ppp); + pppapi_set_auth(ppp, PPPAUTHTYPE_PAP, PPP_User, PPP_Pass); + ppp_set_usepeerdns(ppp, 1);} + pppapi_connect(ppp, 0); + + PPPOS_started = true; + PPPOS_firststart = true; +} + +bool PPPOS_status(){ + return PPPOS_started; +} + +void PPPOS_stop(){ + pppapi_close(ppp, 0); +} + +/*void gsmInit(int txPin, int rxPin, int baudrate, int uart_number){ + PPPOS_uart_num = uart_number; + gpio_set_direction(txPin, GPIO_MODE_OUTPUT); + gpio_set_direction(rxPin, GPIO_MODE_INPUT); + gpio_set_pull_mode(rxPin, GPIO_PULLUP_ONLY); + + uart_config_t uart_config = { + .baud_rate = baudrate, + .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(PPPOS_uart_num, &uart_config) ; + uart_set_pin(PPPOS_uart_num, txPin, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + uart_driver_install(PPPOS_uart_num, BUF_SIZE * 2, BUF_SIZE * 2, 0, NULL, 0); +}*/ + +char* PPPOS_read(){ + memset(PPPOS_out, 0, BUF_SIZE); + int len = uart_read_bytes(PPPOS_uart_num, (uint8_t *)PPPOS_out, BUF_SIZE, 10 / portTICK_RATE_MS); + if (len > 0) { + return PPPOS_out; + } else { + return NULL; + } +} + +void PPPOS_write(char* cmd){ + uart_flush(PPPOS_uart_num); + if (cmd != NULL) { + int cmdSize = strlen(cmd); + uart_write_bytes(PPPOS_uart_num, (const char*)cmd, cmdSize); + uart_wait_tx_done(PPPOS_uart_num, 100 / portTICK_RATE_MS); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/src/PPPOS.h b/src/PPPOS.h new file mode 100644 index 0000000..8112c09 --- /dev/null +++ b/src/PPPOS.h @@ -0,0 +1,28 @@ +#ifndef _GSM_H_ +#define _GSM_H_ +#ifdef __cplusplus +extern "C" { +#endif + + +#define BUF_SIZE (1024) + + +void PPPOS_init(int txPin, int rxPin, int baudrate, int uart_number, char* user, char* pass); + +bool PPPOS_isConnected(); + +void PPPOS_start(); + +bool PPPOS_status(); + +void PPPOS_stop(); + +void PPPOS_write(char* cmd); + +char* PPPOS_read(); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/PPPOSClient.cpp b/src/PPPOSClient.cpp new file mode 100644 index 0000000..65608fb --- /dev/null +++ b/src/PPPOSClient.cpp @@ -0,0 +1,266 @@ +#include "PPPOSClient.h" + + +void PPPOSClient::stop() +{ + lwip_close(_socket); + _startPos = 0; + _endPos = 0; + bzero(RxBuffer, sizeof(RxBuffer)); + _connected = false; +} + +int PPPOSClient::connect(IPAddress ip, uint16_t port) +{ + int32_t timeout = PPPOS_CLIENT_DEF_CONN_TIMEOUT_MS; + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + log_e("socket: %d", errno); + return 0; + } + fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) | O_NONBLOCK ); + + uint32_t ip_addr = ip; + struct sockaddr_in serveraddr; + memset((char *) &serveraddr, 0, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + memcpy((void *)&serveraddr.sin_addr.s_addr, (const void *)(&ip_addr), 4); + serveraddr.sin_port = htons(port); + fd_set fdset; + struct timeval tv; + FD_ZERO(&fdset); + FD_SET(sockfd, &fdset); + tv.tv_sec = 0; + tv.tv_usec = timeout * 1000; + + #ifdef ESP_IDF_VERSION_MAJOR + int res = lwip_connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)); + #else + int res = lwip_connect_r(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)); + #endif + if (res < 0 && errno != EINPROGRESS) { + log_e("connect on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno)); + close(sockfd); + return 0; + } + + res = select(sockfd + 1, nullptr, &fdset, nullptr, timeout<0 ? nullptr : &tv); + if (res < 0) { + log_e("select on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno)); + close(sockfd); + return 0; + } else if (res == 0) { + log_i("select returned due to timeout %d ms for fd %d", timeout, sockfd); + close(sockfd); + return 0; + } else { + int sockerr; + socklen_t len = (socklen_t)sizeof(int); + res = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &sockerr, &len); + + if (res < 0) { + log_e("getsockopt on fd %d, errno: %d, \"%s\"", sockfd, errno, strerror(errno)); + close(sockfd); + return 0; + } + + if (sockerr != 0) { + log_e("socket error on fd %d, errno: %d, \"%s\"", sockfd, sockerr, strerror(sockerr)); + close(sockfd); + return 0; + } + } + + fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) & (~O_NONBLOCK) ); + + _connected = true; + _socket = sockfd; + return 1; +} + +int PPPOSClient::connect(const char *host, uint16_t port) +{ + ip_addr_t ip_addr; + IPAddress aResult = static_cast(0); + struct in_addr retAddr; + struct hostent* he = gethostbyname(host); + if (he == nullptr) { + retAddr.s_addr = 0; + return 0; + } else { + retAddr = *(struct in_addr*) (he->h_addr_list[0]); + } + inet_aton(inet_ntoa(retAddr), &ip_addr); + aResult = ip_addr.u_addr.ip4.addr; + return connect(aResult, port); +} + +size_t PPPOSClient::write(uint8_t data) +{ + return write(&data, 1); +} + +int PPPOSClient::read() +{ + if (!_connected) return -1; + if (_startPos >= (_endPos - 1)) { + _startPos = 0; + _endPos = 0; + bzero(RxBuffer, sizeof(RxBuffer)); + int _r = lwip_recv(_socket, RxBuffer, sizeof(RxBuffer)-1, MSG_DONTWAIT); + if (_r > 0) { + _endPos = _r + 1; + } else { + return -1; + } + } + _startPos++; + return RxBuffer[_startPos-1]; +} + +size_t PPPOSClient::write(const uint8_t *buf, size_t size) +{ + int res =0; + int retry = PPPOS_CLIENT_MAX_WRITE_RETRY; + int socketFileDescriptor = _socket; + size_t totalBytesSent = 0; + size_t bytesRemaining = size; + + if(!_connected || (socketFileDescriptor < 0)) { + return 0; + } + + while(retry) { + fd_set set; + struct timeval tv; + FD_ZERO(&set); + FD_SET(socketFileDescriptor, &set); + tv.tv_sec = 0; + tv.tv_usec = PPPOS_CLIENT_SELECT_TIMEOUT_US; + retry--; + + if(select(socketFileDescriptor + 1, NULL, &set, NULL, &tv) < 0) { + return 0; + } + + if(FD_ISSET(socketFileDescriptor, &set)) { + res = lwip_send(socketFileDescriptor, (void*) buf, bytesRemaining, MSG_DONTWAIT); + if(res > 0) { + totalBytesSent += res; + if (totalBytesSent >= size) { + retry = 0; + } else { + buf += res; + bytesRemaining -= res; + retry = PPPOS_CLIENT_MAX_WRITE_RETRY; + } + } + else if(res < 0) { + log_e("fail on fd %d, errno: %d, \"%s\"", fd(), errno, strerror(errno)); + if(errno != EAGAIN) { + stop(); + res = 0; + retry = 0; + } + } + else { + } + } + } + return totalBytesSent; +} + +int PPPOSClient::available() +{ + if (!_connected) return false; + if (_startPos >= (_endPos - 1)) { + _startPos = 0; + _endPos = 0; + bzero(RxBuffer, sizeof(RxBuffer)); + int _r = lwip_recv(_socket, RxBuffer, sizeof(RxBuffer)-1, MSG_DONTWAIT); + if (_r > 0) { + _endPos = _r + 1; + } else { + return false; + } + } + return true; +} + +void PPPOSClient::flush() { + char recv_buf[5000]; + int r = 0; + do { + bzero(recv_buf, sizeof(recv_buf)); + r = lwip_recv(_socket, recv_buf, sizeof(recv_buf)-1, MSG_DONTWAIT); + }while(r > 0); + _startPos = 0; + _endPos = 0; + bzero(RxBuffer, sizeof(RxBuffer)); +} + +uint8_t PPPOSClient::connected() { + if (_connected) { + uint8_t dummy; + int res = lwip_recv(_socket, &dummy, 0, MSG_DONTWAIT); + (void)res; + if (res <= 0){ + switch (errno) { + case EWOULDBLOCK: + case ENOENT: + _connected = true; + break; + case ENOTCONN: + case EPIPE: + case ECONNRESET: + case ECONNREFUSED: + case ECONNABORTED: + _connected = false; + log_e("Disconnected: RES: %d, ERR: %d", res, errno); + break; + default: + log_e("Unexpected: RES: %d, ERR: %d", res, errno); + _connected = true; + break; + } + } else { + _connected = true; + } + } + return _connected; +} + +int PPPOSClient::read(uint8_t *buf, size_t size) { + if (!_connected) return -1; + int res = -1; + if (available()){ + int j = 0; + for (int i = _startPos; i < _endPos; i++) { + if (j < size) { + buf[j] = RxBuffer[i]; + } else { + res = j; + _startPos += j; + } + j++; + } + } + return res; +} + +int PPPOSClient::peek() { + if (!_connected) return -1; + if (_startPos >= (_endPos - 1)) { + _startPos = 0; + _endPos = 0; + bzero(RxBuffer, sizeof(RxBuffer)); + int _r = lwip_recv(_socket, RxBuffer, sizeof(RxBuffer)-1, MSG_DONTWAIT); + if (_r > 0) { + _endPos = _r + 1; + } else { + _connected = false; + return -1; + } + } + return RxBuffer[_startPos]; +} \ No newline at end of file diff --git a/src/PPPOSClient.h b/src/PPPOSClient.h new file mode 100644 index 0000000..a3cd128 --- /dev/null +++ b/src/PPPOSClient.h @@ -0,0 +1,52 @@ +#ifndef _PPPOSCLIENT_H_ +#define _PPPOSCLIENT_H_ + +#define PPPOS_RXBUFFER_LENGTH 1024 +#define PPPOS_CLIENT_DEF_CONN_TIMEOUT_MS (3000) +#define PPPOS_CLIENT_MAX_WRITE_RETRY (10) +#define PPPOS_CLIENT_SELECT_TIMEOUT_US (1000000) +#define PPPOS_CLIENT_FLUSH_BUFFER_SIZE (1024) + +#include +#include "Client.h" +#include "lwip/dns.h" +#include "lwip/inet.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/netdb.h" +#include + + +class PPPOSClient : public Client +{ + + public: + + PPPOSClient() : _connected(false), _socket(0) {} + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, uint16_t port); + virtual size_t write(uint8_t data); + virtual size_t write(const uint8_t *buf, size_t size); + virtual int available(); + virtual int read(); + virtual int read(uint8_t *buf, size_t size); + virtual operator bool() { return 0; } + virtual int peek(); + virtual void flush(); + virtual void stop(); + virtual uint8_t connected(); + + using Print::write; + + protected: + bool _connected; + int _socket; + uint8_t RxBuffer[PPPOS_RXBUFFER_LENGTH]; + int _startPos = 0; + int _endPos = 0; +}; + +#endif /* _PPPOSCLIENT_H_ */