mirror of
https://github.com/eddyem/eddys_snippets.git
synced 2025-12-06 02:35:12 +03:00
ESP simplest server
This commit is contained in:
parent
19fbf250a9
commit
ca7a53ee1c
34
ESP8266/CMakeLists.txt
Normal file
34
ESP8266/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
set(PROJ esp8266)
|
||||
|
||||
project(${PROJ} LANGUAGES C)
|
||||
|
||||
set(CMAKE_COLOR_MAKEFILE ON)
|
||||
|
||||
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
||||
|
||||
option(DEBUG "Compile in debug mode" OFF)
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -Og -g3 -ggdb -Werror")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3 -march=native -fdata-sections -ffunction-sections -flto=auto")
|
||||
|
||||
if(DEBUG)
|
||||
add_definitions(-DEBUG)
|
||||
set(CMAKE_BUILD_TYPE DEBUG)
|
||||
set(CMAKE_VERBOSE_MAKEFILE "ON")
|
||||
else()
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -flto=auto")
|
||||
set(CMAKE_BUILD_TYPE RELEASE)
|
||||
endif()
|
||||
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(${PROJ} REQUIRED usefull_macros)
|
||||
add_executable(esp8266 ${SOURCES})
|
||||
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS ${PROJ}
|
||||
# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
2
ESP8266/Readme
Normal file
2
ESP8266/Readme
Normal file
@ -0,0 +1,2 @@
|
||||
Simplest server based on AT-commands using ESP8266.
|
||||
I tried to make code as nearest to MCU as possible.
|
||||
170
ESP8266/esp8266.c
Normal file
170
ESP8266/esp8266.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* This file is part of the esp8266 project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <usefull_macros.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "esp8266.h"
|
||||
#include "serial.h"
|
||||
|
||||
#define ESPMSG(x) do{if(!serial_send_msg(x)) return FALSE;}while(0)
|
||||
|
||||
// sometimes ESP hangs with message "WIFI GOT IP" and I can do nothing except waiting
|
||||
|
||||
const char *msgptr = NULL; // pointer to received message
|
||||
static int receivedlen = 0;
|
||||
|
||||
// check module working
|
||||
int esp_check(){
|
||||
// try simplest check for three times
|
||||
for(int i = 0; i < 3; ++i) if( ANS_OK == serial_sendwans("AT")) break;
|
||||
//esp_close();
|
||||
// now try next even if no answer for "AT"
|
||||
if( ANS_OK == serial_sendwans("ATE0") // echo off
|
||||
&& ANS_OK == serial_sendwans("AT+CWMODE_CUR=1") // station mode
|
||||
) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// check established wifi connection
|
||||
esp_cipstatus_t esp_cipstatus(){
|
||||
if(ANS_OK != serial_sendwans("AT+CIPSTATUS")) return ESP_CIP_FAILED;
|
||||
char *l = serial_getline(NULL, NULL);
|
||||
const char *n = NULL;
|
||||
if(!l || !(n = serial_tidx(l, "STATUS:"))) return ESP_CIP_FAILED;
|
||||
return (esp_cipstatus_t)serial_s2i(n);
|
||||
}
|
||||
|
||||
// connect to AP
|
||||
int esp_connect(const char *SSID, const char *pass){
|
||||
ESPMSG("AT+CWJAP_CUR=\"");
|
||||
ESPMSG(SSID);
|
||||
ESPMSG("\",\"");
|
||||
ESPMSG(pass);
|
||||
if(ANS_OK != serial_sendwans("\"")) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// just send AT+CIFSR
|
||||
int esp_myip(){
|
||||
return (ANS_OK == serial_sendwans("AT+CIFSR"));
|
||||
}
|
||||
|
||||
// start server on given port
|
||||
int esp_start_server(const char *port){
|
||||
if(ANS_OK != serial_sendwans("AT+CIPMUX=1")){ // can't start server without mux
|
||||
// what if already ready?
|
||||
char *x = serial_getline(NULL, NULL);
|
||||
if(!x || !serial_tidx(x, "link is builded")) return FALSE;
|
||||
}
|
||||
ESPMSG("AT+CIPSERVER=1,");
|
||||
if(ANS_OK != serial_sendwans(port)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// stop server
|
||||
int ep_stop_server(){
|
||||
return (ANS_OK == serial_sendwans("AT+CIPSERVER=0"));
|
||||
}
|
||||
|
||||
// next (or only) line of received data
|
||||
const char *esp_msgline(){
|
||||
DBG("receivedlen=%d", receivedlen);
|
||||
if(msgptr){
|
||||
const char *p = msgptr;
|
||||
msgptr = NULL;
|
||||
return p;
|
||||
}
|
||||
if(receivedlen < 1) return NULL;
|
||||
int l, d;
|
||||
const char *got = serial_getline(&l, &d);
|
||||
receivedlen -= l + d;
|
||||
return got;
|
||||
}
|
||||
|
||||
// process connection/disconnection/messages
|
||||
// fd - file descriptor of opened/closed connections
|
||||
esp_clientstat_t esp_process(int *fd){
|
||||
msgptr = NULL;
|
||||
receivedlen = 0;
|
||||
int l, d;
|
||||
char *got = serial_getline(&l, &d);
|
||||
if(!got) return ESP_CLT_IDLE;
|
||||
const char *x = serial_tidx(got, "+IPD,");
|
||||
if(x){
|
||||
if(fd) *fd = serial_s2i(x);
|
||||
x = serial_tidx(x, ",");
|
||||
if(!x) return ESP_CLT_ERROR;
|
||||
int r = serial_s2i(x);
|
||||
x = serial_tidx(x, ":");
|
||||
if(!x) return ESP_CLT_ERROR;
|
||||
receivedlen = r - d - (l - (x-got)); // this is a rest of data (if any)
|
||||
msgptr = x;
|
||||
return ESP_CLT_GETMESSAGE;
|
||||
}
|
||||
// check for CONNECT/CLOSE
|
||||
if((x = serial_tidx(got, ",CONNECT"))){
|
||||
if(fd) *fd = serial_s2i(got);
|
||||
return ESP_CLT_CONNECTED;
|
||||
}
|
||||
if((x = serial_tidx(got, ",CLOSED"))){
|
||||
if(fd) *fd = serial_s2i(got);
|
||||
return ESP_CLT_DISCONNECTED;
|
||||
}
|
||||
DBG("Unknown message: '%s'", got);
|
||||
return ESP_CLT_IDLE;
|
||||
}
|
||||
|
||||
int esp_send(int fd, const char *msg){
|
||||
DBG("send '%s' to %d", msg, fd);
|
||||
ESPMSG("AT+CIPSENDEX=");
|
||||
if(!serial_putchar('0' + fd)) return FALSE;
|
||||
if(ANS_OK != serial_sendwans(",2048")) return FALSE;
|
||||
int got = 0;
|
||||
// try several times
|
||||
for(int i = 0; i < 10; ++i){
|
||||
got = serial_getch();
|
||||
if(got == '>') break;
|
||||
}
|
||||
if(got != '>'){
|
||||
DBG("Didn't found '>'");
|
||||
serial_send_msg("\\0"); // terminate message
|
||||
serial_clr();
|
||||
return FALSE; // go into terminal mode
|
||||
}
|
||||
serial_clr(); // remove space after '>'
|
||||
ESPMSG(msg);
|
||||
if(ANS_OK == serial_sendwans("\\0")) return TRUE;
|
||||
DBG("Didn't sent");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void esp_reset(){
|
||||
serial_sendwans("AT+RST");
|
||||
}
|
||||
|
||||
void esp_close(){
|
||||
serial_sendwans("AT+CIPMUX=0");
|
||||
serial_sendwans("AT+CIPCLOSE");
|
||||
}
|
||||
|
||||
|
||||
int esp_listAP(){
|
||||
if(ANS_OK == serial_sendwans("AT+CWLAP")) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
53
ESP8266/esp8266.h
Normal file
53
ESP8266/esp8266.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of the esp8266 project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// really only 5 clients allowed
|
||||
#define ESP_MAX_CLT_NUMBER 8
|
||||
|
||||
typedef enum{
|
||||
ESP_CIP_0,
|
||||
ESP_CIP_1,
|
||||
ESP_CIP_GOTIP,
|
||||
ESP_CIP_CONNECTED,
|
||||
ESP_CIP_DISCONNECTED,
|
||||
ESP_CIP_FAILED
|
||||
} esp_cipstatus_t;
|
||||
|
||||
typedef enum{
|
||||
ESP_CLT_IDLE, // nothing happened
|
||||
ESP_CLT_CONNECTED, // new client connected
|
||||
ESP_CLT_DISCONNECTED, // disconnected
|
||||
ESP_CLT_ERROR, // error writing or other
|
||||
ESP_CLT_GETMESSAGE, // receive message from client
|
||||
ESP_CLT_OK, // sent OK
|
||||
} esp_clientstat_t;
|
||||
|
||||
int esp_check();
|
||||
void esp_close();
|
||||
void esp_reset();
|
||||
esp_cipstatus_t esp_cipstatus();
|
||||
int esp_connect(const char *SSID, const char *pass);
|
||||
int esp_myip();
|
||||
int esp_start_server(const char *port);
|
||||
int ep_stop_server();
|
||||
esp_clientstat_t esp_process(int *fd);
|
||||
int esp_send(int fd, const char *msg);
|
||||
const char *esp_msgline();
|
||||
int esp_listAP();
|
||||
182
ESP8266/main.c
Normal file
182
ESP8266/main.c
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* This file is part of the esp8266 project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "esp8266.h"
|
||||
#include "serial.h"
|
||||
|
||||
static struct{
|
||||
int help;
|
||||
char *serialdev;
|
||||
char *SSID;
|
||||
char *SSpass;
|
||||
int speed;
|
||||
char *port;
|
||||
int reset;
|
||||
int list;
|
||||
} G = {
|
||||
.speed = 115200,
|
||||
.serialdev = "/dev/ttyUSB0",
|
||||
.port = "1111",
|
||||
};
|
||||
|
||||
static sl_option_t options[] = {
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
|
||||
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.serialdev), "serial device path (default: /dev/ttyUSB0)"},
|
||||
{"baudrate",NEED_ARG, NULL, 'b', arg_int, APTR(&G.speed), "serial speed"},
|
||||
{"ssid", NEED_ARG, NULL, 0, arg_string, APTR(&G.SSID), "SSID to connect"},
|
||||
{"pass", NEED_ARG, NULL, 0, arg_string, APTR(&G.SSpass), "SSID password"},
|
||||
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), "servr port (default: 1111)"},
|
||||
{"reset", NO_ARGS, NULL, 0, arg_int, APTR(&G.reset), "reset ESP"},
|
||||
{"list", NO_ARGS, NULL, 'l', arg_int, APTR(&G.list), "list available APs"},
|
||||
end_option
|
||||
};
|
||||
|
||||
void signals(int signo){
|
||||
esp_close();
|
||||
serial_close();
|
||||
exit(signo);
|
||||
}
|
||||
|
||||
static esp_clientstat_t parse_message(int fd){
|
||||
// this isn't a good idea to send data while not received all; so we use buffer
|
||||
static sl_ringbuffer_t *rb = NULL;
|
||||
if(!rb) rb = sl_RB_new(4096);
|
||||
const char *msg = NULL;
|
||||
char buf[4096];
|
||||
while((msg = esp_msgline())){
|
||||
printf("Received line from fd %d: %s\n", fd, msg);
|
||||
// do something with this data
|
||||
if(0 == strcmp(msg, "help")){
|
||||
sl_RB_writestr(rb, "Hey, we don't have any help yet, try `time`\n");
|
||||
}else if(0 == strcmp(msg, "time")){
|
||||
snprintf(buf, 64, "TIME=%.3f\n", sl_dtime());
|
||||
sl_RB_writestr(rb, buf);
|
||||
}else{
|
||||
if(*msg){
|
||||
snprintf(buf, 127, "Part of your message: _%s_\n", msg);
|
||||
sl_RB_writestr(rb, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t L = 4094;
|
||||
char *b = buf;
|
||||
while(sl_RB_readline(rb, b, L) > 0){ // there's a bug in my library! Need to fix (sl_RB_readline returns NOT an amount of bytes)
|
||||
size_t got = strlen(b);
|
||||
L -= ++got;
|
||||
b += got;
|
||||
b[-1] = '\n';
|
||||
}
|
||||
if(L == 4094) return ESP_CLT_OK; // nothing to send
|
||||
return esp_send(fd, buf) ? ESP_CLT_OK : ESP_CLT_ERROR;
|
||||
}
|
||||
|
||||
static void processing(){
|
||||
uint8_t connected[ESP_MAX_CLT_NUMBER] = {0};
|
||||
esp_clientstat_t oldstat[ESP_MAX_CLT_NUMBER] = {0};
|
||||
double T0 = sl_dtime();
|
||||
int N = -1;
|
||||
void chkerr(){
|
||||
if(oldstat[N] == ESP_CLT_ERROR){
|
||||
DBG("error again -> turn off");
|
||||
connected[N] = 0;
|
||||
}
|
||||
}
|
||||
while(1){
|
||||
esp_clientstat_t s = esp_process(&N);
|
||||
if(N > -1 && N < ESP_MAX_CLT_NUMBER){ // parsing
|
||||
//if(s == ESP_CLT_IDLE){ usleep(1000); continue; }
|
||||
switch(s){
|
||||
case ESP_CLT_CONNECTED:
|
||||
connected[N] = 1;
|
||||
green("Connection on fd=%d\n", N);
|
||||
break;
|
||||
case ESP_CLT_DISCONNECTED:
|
||||
connected[N] = 0;
|
||||
green("fd=%d disconnected\n", N);
|
||||
break;
|
||||
case ESP_CLT_ERROR:
|
||||
DBG("Error from %d", N);
|
||||
chkerr();
|
||||
break;
|
||||
case ESP_CLT_GETMESSAGE:
|
||||
DBG("%d have message", N);
|
||||
s = parse_message(N);
|
||||
if(s == ESP_CLT_ERROR) chkerr();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
oldstat[N] = s;
|
||||
}
|
||||
// and here we can do something for all
|
||||
int R = rand() % 5000;
|
||||
// example of `broadcast` message
|
||||
if(R < 2){
|
||||
DBG("Send 'broadcasting' message");
|
||||
char buf[64];
|
||||
snprintf(buf, 63, "Hello, there's %.2f seconds from start\n", sl_dtime() - T0);
|
||||
for(int i = 0; i < ESP_MAX_CLT_NUMBER; ++i){
|
||||
if(!connected[i]) continue;
|
||||
if(ESP_CLT_ERROR == esp_send(i, buf)) chkerr();
|
||||
}
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
sl_init();
|
||||
sl_parseargs(&argc, &argv, options);
|
||||
if(G.help) sl_showhelp(-1, options);
|
||||
if(!serial_init(G.serialdev, G.speed)) ERRX("Can't open %s at speed %d", G.serialdev, G.speed);
|
||||
if(G.reset){
|
||||
red("Resetting, please wait!\n");
|
||||
esp_reset();
|
||||
usleep(500000);
|
||||
DBG("Get buff");
|
||||
char *str;
|
||||
while((str = serial_getline(NULL, NULL))) if(*str) printf("\t%s\n", str);
|
||||
signals(0);
|
||||
}
|
||||
if(!esp_check()) ERRX("No answer from ESP");
|
||||
if(G.list){
|
||||
if(esp_listAP()){
|
||||
green("Available wifi:\n");
|
||||
char *str;
|
||||
while(str = serial_getline(NULL, NULL)) printf("\t%s\n", str);
|
||||
}else WARNX("Error listing");
|
||||
}
|
||||
esp_cipstatus_t st = esp_cipstatus();
|
||||
if(st != ESP_CIP_GOTIP && st != ESP_CIP_CONNECTED){
|
||||
DBG("Need to connect");
|
||||
if(!esp_connect(G.SSID, G.SSpass)) ERRX("Can't connect");
|
||||
}
|
||||
if(esp_myip()){
|
||||
green("Connected\n");
|
||||
char *str;
|
||||
while(str = serial_getline(NULL, NULL)) if(*str) printf("\t%s\n", str);
|
||||
}
|
||||
ep_stop_server(); // stop just in case
|
||||
if(!esp_start_server(G.port)) ERRX("Can't start server");
|
||||
processing();
|
||||
serial_close();
|
||||
return 0;
|
||||
}
|
||||
178
ESP8266/serial.c
Normal file
178
ESP8266/serial.c
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* This file is part of the esp8266 project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "serial.h"
|
||||
|
||||
// ALL functions here aren't thread-independent, as you can't use the same line simultaneously
|
||||
|
||||
static sl_tty_t *device = NULL;
|
||||
static double timeout = 30.; // timeout, s
|
||||
static sl_ringbuffer_t *rbin = NULL; // input ring buffer
|
||||
|
||||
// read all incoming
|
||||
void serial_clr(){
|
||||
if(!device) return;
|
||||
while(sl_tty_read(device) > 0);
|
||||
}
|
||||
|
||||
int serial_init(char *path, int speed){
|
||||
device = sl_tty_new(path, speed, 256);
|
||||
if(!device) return FALSE;
|
||||
device = sl_tty_open(device, 1);
|
||||
if(!device) return FALSE;
|
||||
rbin = sl_RB_new(4096);
|
||||
if(!rbin){
|
||||
sl_tty_close(&device);
|
||||
return FALSE;
|
||||
}
|
||||
sl_tty_tmout(1000); // set select() timeout to 1ms
|
||||
// clear buffer
|
||||
serial_clr();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void serial_close(){
|
||||
if(device) sl_tty_close(&device);
|
||||
if(rbin) sl_RB_delete(&rbin);
|
||||
}
|
||||
|
||||
int serial_set_timeout(double tms){
|
||||
if(tms < 0.1) return FALSE;
|
||||
timeout = tms;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// send messages over serial,
|
||||
// without EOL:
|
||||
int serial_send_msg(const char *msg){
|
||||
if(!msg || !device) return FALSE;
|
||||
int l = strlen(msg);
|
||||
DBG("Write message `%s` (%d bytes)", msg, l);
|
||||
if(sl_tty_write(device->comfd, msg, l)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
// and with:
|
||||
int serial_send_cmd(const char *msg){
|
||||
if(!msg || !device) return FALSE;
|
||||
if(!serial_send_msg(msg)) return FALSE;
|
||||
DBG("Write EOL");
|
||||
if(sl_tty_write(device->comfd, "\r\n", 2)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
int serial_putchar(char ch){
|
||||
if(!device) return FALSE;
|
||||
if(sl_tty_write(device->comfd, &ch, 1)) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void fillinbuff(){
|
||||
ssize_t got;
|
||||
while((got = sl_tty_read(device))){
|
||||
if(got < 0){
|
||||
WARNX("Serial device disconnected!");
|
||||
serial_close();
|
||||
}else if(got){
|
||||
if((size_t)got != sl_RB_write(rbin, (const uint8_t*)device->buf, got)){
|
||||
WARNX("Rinbguffer overflow?");
|
||||
sl_RB_clearbuf(rbin);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read one string line from serial
|
||||
// @arg deleted - N symbols deleted from rest of string (1 in case of '\n' and 2 in case of "\r\n")
|
||||
// @arg len - strlen of data
|
||||
char *serial_getline(int *len, int *deleted){
|
||||
if(!device) return NULL;
|
||||
static char buf[BUFSIZ];
|
||||
fillinbuff();
|
||||
// read old records
|
||||
if(!sl_RB_readline(rbin, buf, BUFSIZ-1)) return NULL;
|
||||
// remove trailing '\r'
|
||||
int l = strlen(buf), d = 1;
|
||||
if(l > -1 && buf[l - 1] == '\r'){
|
||||
++d;
|
||||
buf[--l] = 0;
|
||||
}
|
||||
if(deleted) *deleted = d;
|
||||
if(len) *len = l;
|
||||
DBG("read: '%s'", buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// get symbol
|
||||
int serial_getch(){
|
||||
if(!device) return -1;
|
||||
fillinbuff();
|
||||
char C;
|
||||
DBG("rb size: %zd", sl_RB_datalen(rbin));
|
||||
size_t rd = sl_RB_read(rbin, (uint8_t*)&C, 1);
|
||||
DBG("got %zd : '%c'", rd, C);
|
||||
if(rd != 1) return -1;
|
||||
//if(1 != sl_RB_read(rbin, (uint8_t*)&C, 1)) return -1;
|
||||
return (int) C;
|
||||
}
|
||||
|
||||
serial_ans_t serial_sendwans(const char *msg){
|
||||
if(!msg || !device) return ANS_FAILED;
|
||||
if(!serial_send_cmd(msg)) return ANS_FAILED;
|
||||
double t0 = sl_dtime();
|
||||
int ret = ANS_FAILED;
|
||||
while(sl_dtime() - t0 < timeout && device){
|
||||
char *ans = NULL;
|
||||
if(!(ans = serial_getline(NULL, NULL))){ usleep(500); continue; }
|
||||
DBG("Get line: '%s' (%zd bytes)", ans, strlen(ans));
|
||||
if(!*ans) continue; // empty string
|
||||
if(strcmp(ans, "OK") == 0 || strcmp(ans, "SEND OK") == 0){ ret = ANS_OK; goto rtn; }
|
||||
if(strcmp(ans, "ERROR") == 0){ ret = ANS_ERR; goto rtn; }
|
||||
if(strcmp(ans, "FAIL") == 0){ ret = ANS_FAILED; goto rtn; }
|
||||
DBG("Return '%s' into buff", ans);
|
||||
sl_RB_writestr(rbin, ans); // put other data into ringbuffer for further processing
|
||||
sl_RB_putbyte(rbin, '\n');
|
||||
}
|
||||
rtn:
|
||||
DBG("proc time: %g", sl_dtime() - t0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// return NULL if `s` don't contain `t`, else return next symbol in `s`
|
||||
const char *serial_tidx(const char *s, const char *t){
|
||||
if(!s) return NULL;
|
||||
DBG("check '%s' for '%s'", s, t);
|
||||
int pos = 0;
|
||||
if(t){
|
||||
const char *sub = strstr(s, t);
|
||||
if(!sub) return NULL;
|
||||
int l = strlen(t);
|
||||
pos = (sub - s) + l;
|
||||
}
|
||||
DBG("pos = %d", pos);
|
||||
return s + pos;
|
||||
}
|
||||
|
||||
int serial_s2i(const char *s){
|
||||
if(!s) return -1;
|
||||
DBG("conv '%s' to %d", s, atoi(s));
|
||||
return atoi(s);
|
||||
}
|
||||
40
ESP8266/serial.h
Normal file
40
ESP8266/serial.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of the esp8266 project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef enum{
|
||||
ANS_FAILED,
|
||||
ANS_OK,
|
||||
ANS_ERR
|
||||
} serial_ans_t;
|
||||
|
||||
void serial_clr();
|
||||
int serial_set_timeout(double tms);
|
||||
int serial_init(char *path, int speed);
|
||||
void serial_close();
|
||||
int serial_send_msg(const char *msg);
|
||||
int serial_send_cmd(const char *msg);
|
||||
int serial_putchar(char ch);
|
||||
serial_ans_t serial_sendwans(const char *msg);
|
||||
char *serial_getline(int *len, int *deleted);
|
||||
int serial_getch();
|
||||
|
||||
// conversion functions for ESP
|
||||
const char *serial_tidx(const char *s, const char *t);
|
||||
int serial_s2i(const char *s);
|
||||
Loading…
x
Reference in New Issue
Block a user