mirror of
https://github.com/eddyem/eddys_snippets.git
synced 2025-12-06 02:35:12 +03:00
179 lines
5.1 KiB
C
179 lines
5.1 KiB
C
/*
|
|
* 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);
|
|
}
|