2017-12-30 20:12:01 +00:00
/*
* Copyright ( c ) 2017 Shaun Feakes - All rights reserved
*
* You may use redistribute and / or modify this code under the terms of
* the GNU General Public License version 2 as published by the
* Free Software Foundation . For the terms of this license ,
* see < http : //www.gnu.org/licenses/>.
*
* You are free to use this software under the terms of the GNU General
* Public License , 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 .
*
* https : //github.com/sfeakes/aqualinkd
*/
# include <stdio.h>
# include <stdarg.h>
# include <termios.h>
# include <fcntl.h>
# include <unistd.h>
# include <string.h>
# include <errno.h>
2018-03-14 17:29:52 +00:00
# include <string.h>
2018-08-02 00:47:55 +00:00
# include <sys/ioctl.h>
2018-09-02 21:45:24 +00:00
# include <stdbool.h>
2023-06-04 21:17:48 +00:00
// Below is needed to set low latency.
# include <linux/serial.h>
2017-12-30 20:12:01 +00:00
# include "aq_serial.h"
# include "utils.h"
2018-09-02 21:45:24 +00:00
# include "config.h"
2019-08-18 20:54:10 +00:00
# include "packetLogger.h"
2024-03-30 16:51:31 +00:00
# include "timespec_subtract.h"
2017-12-30 20:12:01 +00:00
2023-06-04 21:17:48 +00:00
/*
Notes for serial usb speed
File should exist if using ftdi chip , ie ftdi_sio driver .
/ sys / bus / usb - serial / devices / ttyUSB0 / latency_timer
Set to 1 for fastest latency .
Can also be set in code
ioctl ( fd , TIOCGSERIAL , & serial ) ;
serial . flags | = ASYNC_LOW_LATENCY ;
ioctl ( fd , TIOCSSERIAL , & serial ) ;
*/
2019-10-16 22:53:09 +00:00
2020-07-26 19:28:58 +00:00
//#define BLOCKING_MODE
2020-07-18 16:37:19 +00:00
2020-07-26 19:28:58 +00:00
static bool _blocking_mode = false ;
int _blocking_fds = - 1 ;
2020-07-18 16:37:19 +00:00
2019-10-16 22:53:09 +00:00
static struct termios _oldtio ;
2024-03-30 16:51:31 +00:00
static struct timespec last_serial_read_time ;
2019-10-16 22:53:09 +00:00
void send_packet ( int fd , unsigned char * packet , int length ) ;
//unsigned char getProtocolType(unsigned char* packet);
2020-06-01 00:35:17 +00:00
2019-10-16 22:53:09 +00:00
const char * get_packet_type ( unsigned char * packet , int length )
{
static char buf [ 15 ] ;
if ( length < = 0 )
return " " ;
switch ( packet [ PKT_CMD ] ) {
case CMD_ACK :
2023-05-30 23:14:04 +00:00
if ( packet [ 5 ] = = NUL )
return " Ack " ;
else
return " Ack w/ Command " ;
2019-10-16 22:53:09 +00:00
break ;
case CMD_STATUS :
return " Status " ;
break ;
case CMD_MSG :
return " Message " ;
break ;
case CMD_MSG_LONG :
return " Lng Message " ;
break ;
case CMD_PROBE :
return " Probe " ;
break ;
case CMD_GETID :
return " GetID " ;
break ;
case CMD_PERCENT :
return " AR %% " ;
break ;
case CMD_PPM :
return " AR PPM " ;
break ;
case CMD_PDA_0x05 :
return " PDA Unknown " ;
break ;
case CMD_PDA_0x1B :
return " PDA Init (*guess*) " ;
break ;
case CMD_PDA_HIGHLIGHT :
return " PDA Hlight " ;
break ;
case CMD_PDA_CLEAR :
return " PDA Clear " ;
break ;
case CMD_PDA_SHIFTLINES :
return " PDA Shiftlines " ;
break ;
case CMD_PDA_HIGHLIGHTCHARS :
2020-06-01 00:35:17 +00:00
return " PDA HlightChars " ;
2019-10-16 22:53:09 +00:00
break ;
2020-07-18 16:37:19 +00:00
case CMD_IAQ_PAGE_MSG :
if ( packet [ 4 ] = = 0x31 )
return " iAq setDevice " ;
else
return " iAq pMessage " ;
2019-10-16 22:53:09 +00:00
break ;
2020-07-18 16:37:19 +00:00
case CMD_IAQ_PAGE_BUTTON :
return " iAq pButton " ;
break ;
case CMD_IAQ_POLL :
return " iAq Poll " ;
break ;
case CMD_IAQ_PAGE_END :
return " iAq PageEnd " ;
break ;
case CMD_IAQ_PAGE_START :
return " iAq PageStart " ;
2019-10-16 22:53:09 +00:00
break ;
2020-08-28 19:12:38 +00:00
case CMD_IAQ_TABLE_MSG :
return " iAq TableMsg " ;
break ;
case CMD_IAQ_PAGE_CONTINUE :
return " iAq PageContinue " ;
break ;
case CMD_IAQ_STARTUP :
return " iAq init " ;
break ;
case RSSA_DEV_STATUS :
// This is a fail reply 0x10|0x02|0x48|0x13|0x02|0x00|0x10|0x00|0x7f|0x10|0x03|
// Rather than check all, just check 0x02 and checksum sin't I'm not sure 0x10 means faiure without 0x00 around it.
if ( packet [ 4 ] = = 0x02 & & packet [ 8 ] = = 0x7f )
return " RSSA Cmd Error " ;
else
return " RSSA DevStatus " ;
break ;
case RSSA_DEV_READY :
return " RSSA SendCommand " ;
break ;
2023-07-08 16:14:44 +00:00
case CMD_EPUMP_STATUS :
if ( packet [ 4 ] = = CMD_EPUMP_RPM )
return " ePump RPM " ;
else if ( packet [ 4 ] = = CMD_EPUMP_WATTS )
return " ePump Watts " ;
else
return " ePump (unknown) " ;
break ;
case CMD_EPUMP_RPM :
return " ePump set RPM " ;
break ;
case CMD_EPUMP_WATTS :
return " ePump get Watts " ;
break ;
2019-10-16 22:53:09 +00:00
default :
sprintf ( buf , " Unknown '0x%02hhx' " , packet [ PKT_CMD ] ) ;
return buf ;
break ;
}
}
// Generate and return checksum of packet.
int generate_checksum ( unsigned char * packet , int length )
{
int i , sum , n ;
n = length - 3 ;
sum = 0 ;
for ( i = 0 ; i < n ; i + + )
sum + = ( int ) packet [ i ] ;
return ( sum & 0x0ff ) ;
}
bool check_jandy_checksum ( unsigned char * packet , int length )
{
2020-08-28 19:12:38 +00:00
//printf("Checking 0x%02hhx against 0x%02hhx\n",generate_checksum(packet, length), packet[length-3]);
2019-10-16 22:53:09 +00:00
if ( generate_checksum ( packet , length ) = = packet [ length - 3 ] )
return true ;
2020-08-28 19:12:38 +00:00
// There seems to be a bug with jandy one touch protocol where on a long msg to one touch you get
// a bad checksum but everything is actually accurate, so forcing a good return on this.
// Example message (always one touch / status message / line 3 and always 0x0a checksum)
// 0x10|0x02|0x43|0x04|0x03|0x20|0x20|0x20|0x20|0x35|0x3a|0x30|0x35|0x20|0x50|0x4d|0x20|0x20|0x20|0x20|0x20|0x0a|0x10|0x03|
if ( packet [ 3 ] = = 0x04 & & packet [ 4 ] = = 0x03 & & packet [ length - 3 ] = = 0x0a ) {
LOG ( RSSD_LOG , LOG_INFO , " Ignoring bad checksum, seems to be bug in Jandy protocol \n " ) ;
if ( getLogLevel ( RSSD_LOG ) > = LOG_DEBUG ) {
static char buf [ 1000 ] ;
2023-05-14 21:35:13 +00:00
beautifyPacket ( buf , packet , length , true ) ;
2020-08-28 19:12:38 +00:00
LOG ( RSSD_LOG , LOG_DEBUG , " Packetin question %s \n " , buf ) ;
}
return true ;
}
2019-10-16 22:53:09 +00:00
return false ;
}
bool check_pentair_checksum ( unsigned char * packet , int length )
{
//printf("check_pentair_checksum \n");
int i , sum , n ;
n = packet [ 8 ] + 9 ;
//n = packet[8] + 8;
sum = 0 ;
for ( i = 3 ; i < n ; i + + ) {
//printf("Sum 0x%02hhx\n",packet[i]);
sum + = ( int ) packet [ i ] ;
}
//printf("Check High 0x%02hhx = 0x%02hhx = 0x%02hhx\n",packet[n], packet[length-2],((sum >> 8) & 0xFF) );
//printf("Check Low 0x%02hhx = 0x%02hhx = 0x%02hhx\n",packet[n + 1], packet[length-1], (sum & 0xFF) );
// Check against caculated length
if ( sum = = ( packet [ length - 2 ] * 256 + packet [ length - 1 ] ) )
return true ;
// Check against actual # length
if ( sum = = ( packet [ n ] * 256 + packet [ n + 1 ] ) ) {
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_ERR , " Pentair checksum is accurate but length is not \n " ) ;
2019-10-16 22:53:09 +00:00
return true ;
}
return false ;
}
void generate_pentair_checksum ( unsigned char * packet , int length )
{
int i , sum , n ;
n = packet [ 8 ] + 9 ;
//n = packet[8] + 6;
sum = 0 ;
for ( i = 3 ; i < n ; i + + ) {
//printf("Sum 0x%02hhx\n",packet[i]);
sum + = ( int ) packet [ i ] ;
}
packet [ n + 1 ] = ( unsigned char ) ( sum & 0xFF ) ; // Low Byte
packet [ n ] = ( unsigned char ) ( ( sum > > 8 ) & 0xFF ) ; // High Byte
}
protocolType getProtocolType ( unsigned char * packet ) {
if ( packet [ 0 ] = = DLE )
return JANDY ;
else if ( packet [ 0 ] = = PP1 )
return PENTAIR ;
return P_UNKNOWN ;
}
/*
unsigned char getProtocolType ( unsigned char * packet ) {
if ( packet [ 0 ] = = DLE )
return PCOL_JANDY ;
else if ( packet [ 0 ] = = PP1 )
return PCOL_PENTAIR ;
return PCOL_UNKNOWN ;
}
*/
# ifndef PLAYBACK_MODE
/*
Open and Initialize the serial communications port to the Aqualink RS8 device .
Arg is tty or port designation string
returns the file descriptor
*/
2020-07-26 19:28:58 +00:00
//#define TXDEN_DUMMY_RS485_MODE
# ifdef TXDEN_DUMMY_RS485_MODE
# include <linux/serial.h>
/* RS485 ioctls: */
# define TIOCGRS485 0x542E
# define TIOCSRS485 0x542F
int init_serial_port_Pi ( const char * tty )
{
struct serial_rs485 rs485conf = { 0 } ;
//int fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
int fd = open ( tty , O_RDWR ) ;
if ( fd < 0 ) {
LOG ( RSSD_LOG , LOG_ERR , " Unable to open port: %s \n " , tty ) ;
return - 1 ;
}
LOG ( RSSD_LOG , LOG_DEBUG_SERIAL , " Openeded serial port %s \n " , tty ) ;
if ( ioctl ( fd , TIOCGRS485 , & rs485conf ) < 0 ) {
LOG ( RSSD_LOG , LOG_ERR , " Error reading ioctl port (%d): %s \n " , errno , strerror ( errno ) ) ;
return - 1 ;
}
LOG ( RSSD_LOG , LOG_DEBUG , " Port currently RS485 mode is %s \n " , ( rs485conf . flags & SER_RS485_ENABLED ) ? " set " : " NOT set " ) ;
/* Enable RS485 mode: */
rs485conf . flags | = SER_RS485_ENABLED ;
/* Set logical level for RTS pin equal to 1 when sending: */
rs485conf . flags | = SER_RS485_RTS_ON_SEND ;
/* or, set logical level for RTS pin equal to 0 when sending: */
//rs485conf.flags &= ~(SER_RS485_RTS_ON_SEND);
/* Set logical level for RTS pin equal to 1 after sending: */
rs485conf . flags | = SER_RS485_RTS_AFTER_SEND ;
/* or, set logical level for RTS pin equal to 0 after sending: */
//rs485conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);
/* Set this flag if you want to receive data even whilst sending data */
//rs485conf.flags |= SER_RS485_RX_DURING_TX;
if ( ioctl ( fd , TIOCSRS485 , & rs485conf ) < 0 ) {
LOG ( RSSD_LOG , LOG_ERR , " Unable to set port to RS485 %s (%d): %s \n " , tty , errno , strerror ( errno ) ) ;
return - 1 ;
}
return fd ;
}
2020-08-28 19:12:38 +00:00
# endif // TXDEN_DUMMY_RS485_MODE
2020-07-26 19:28:58 +00:00
2020-08-28 19:12:38 +00:00
int _init_serial_port ( const char * tty , bool blocking , bool readahead ) ;
2020-07-26 19:28:58 +00:00
2019-10-16 22:53:09 +00:00
int init_serial_port ( const char * tty )
{
2023-06-04 21:17:48 +00:00
# ifdef AQ_NO_THREAD_NETSERVICE
if ( _aqconfig_ . rs_poll_speed < 0 )
return init_blocking_serial_port ( _aqconfig_ . serial_port ) ;
else if ( _aqconfig_ . readahead_b4_write )
return init_readahead_serial_port ( _aqconfig_ . serial_port ) ;
else
return init_serial_port ( _aqconfig_ . serial_port ) ;
2024-03-30 16:51:31 +00:00
# elif AQ_RS_EXTRA_OPTS
2023-06-04 21:17:48 +00:00
if ( _aqconfig_ . readahead_b4_write )
return init_readahead_serial_port ( _aqconfig_ . serial_port ) ;
else
return init_blocking_serial_port ( _aqconfig_ . serial_port ) ;
2024-03-30 16:51:31 +00:00
# else
return init_blocking_serial_port ( _aqconfig_ . serial_port ) ;
2023-06-04 21:17:48 +00:00
# endif
2020-08-28 19:12:38 +00:00
}
2024-03-30 16:51:31 +00:00
# ifdef AQ_RS_EXTRA_OPTS
2020-08-28 19:12:38 +00:00
int init_readahead_serial_port ( const char * tty )
{
return _init_serial_port ( tty , false , true ) ;
2020-07-26 19:28:58 +00:00
}
2024-03-30 16:51:31 +00:00
# endif
2020-07-26 19:28:58 +00:00
int init_blocking_serial_port ( const char * tty )
{
2020-08-28 19:12:38 +00:00
_blocking_fds = _init_serial_port ( tty , true , false ) ;
2020-07-26 19:28:58 +00:00
return _blocking_fds ;
}
2023-06-04 21:17:48 +00:00
int set_port_low_latency ( int fd , const char * tty )
{
struct serial_struct serial ;
if ( ioctl ( fd , TIOCGSERIAL , & serial ) < 0 ) {
LOG ( RSSD_LOG , LOG_WARNING , " Doesn't look like your USB2RS485 device (%s) supports low latency, this might cause problems on a busy RS485 bus (%d): %s \n " , tty , errno , strerror ( errno ) ) ;
//LOG(RSSD_LOG,LOG_WARNING, "Error reading low latency mode for port %s (%d): %s\n", tty, errno, strerror( errno ));
return - 1 ;
}
LOG ( RSSD_LOG , LOG_NOTICE , " Port %s low latency mode is %s \n " , tty , ( serial . flags & ASYNC_LOW_LATENCY ) ? " set " : " NOT set, resetting to low latency! " ) ;
serial . flags | = ASYNC_LOW_LATENCY ;
if ( ioctl ( fd , TIOCSSERIAL , & serial ) < 0 ) {
LOG ( RSSD_LOG , LOG_ERR , " Unable to set port %s to low latency mode (%d): %s \n " , tty , errno , strerror ( errno ) ) ;
return - 1 ;
}
return 0 ;
}
2023-06-20 00:15:45 +00:00
# include <sys/file.h>
int lock_port ( int fd , const char * tty )
{
if ( ioctl ( fd , TIOCEXCL ) < 0 ) {
LOG ( RSSD_LOG , LOG_ERR , " Can't put (%s) into exclusive mode (%d): %s \n " , tty , errno , strerror ( errno ) ) ;
return - 1 ;
}
if ( flock ( fd , LOCK_EX | LOCK_NB ) < 0 ) {
LOG ( RSSD_LOG , LOG_ERR , " Can't lock (%s) (%d): %s \n " , tty , errno , strerror ( errno ) ) ;
return - 1 ;
}
return 0 ;
}
int unlock_port ( int fd )
{
if ( flock ( fd , LOCK_UN ) < 0 ) {
LOG ( RSSD_LOG , LOG_ERR , " Can't unlock serial port (%d): %s \n " , errno , strerror ( errno ) ) ;
return - 1 ;
}
return 0 ;
}
2020-08-28 19:12:38 +00:00
// https://www.cmrr.umn.edu/~strupp/serial.html#2_5_2
// http://unixwiz.net/techtips/termios-vmin-vtime.html
//#define OLD_SERIAL_INIT
# ifndef OLD_SERIAL_INIT
2024-03-30 16:51:31 +00:00
// Unless AQ_RS_EXTRA_OPTS is defined, blocking will always be true
2020-08-28 19:12:38 +00:00
int _init_serial_port ( const char * tty , bool blocking , bool readahead )
{
//B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400
const int BAUD = B9600 ;
const int PARITY = 0 ;
struct termios newtio ;
_blocking_mode = blocking ;
int fd = open ( tty , O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY ) ;
//int fd = open(tty, O_RDWR | O_NOCTTY | O_SYNC); // This is way to slow at reading
if ( fd < 0 ) {
LOG ( RSSD_LOG , LOG_ERR , " Unable to open port: %s, error %d \n " , tty , errno ) ;
return - 1 ;
}
LOG ( RSSD_LOG , LOG_DEBUG , " Openeded serial port %s \n " , tty ) ;
if ( tcgetattr ( fd , & newtio ) ! = 0 ) {
LOG ( RSSD_LOG , LOG_ERR , " Unable to get port attributes: %s, error %d \n " , tty , errno ) ;
return - 1 ;
}
2023-06-20 00:15:45 +00:00
if ( lock_port ( fd , tty ) < 0 ) {
//LOG(RSSD_LOG,LOG_ERR, "Unable to lock port: %s, error %d\n", tty, errno);
return - 1 ;
}
2023-06-04 21:17:48 +00:00
if ( _aqconfig_ . ftdi_low_latency )
set_port_low_latency ( fd , tty ) ;
2020-08-28 19:12:38 +00:00
memcpy ( & _oldtio , & newtio , sizeof ( struct termios ) ) ;
cfsetospeed ( & newtio , BAUD ) ;
cfsetispeed ( & newtio , BAUD ) ;
newtio . c_cflag = ( newtio . c_cflag & ~ CSIZE ) | CS8 ; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
//newtio.c_iflag &= ~IGNBRK; // disable break processing
newtio . c_iflag = 0 ; // raw input
newtio . c_lflag = 0 ; // no signaling chars, no echo,
// no canonical processing
newtio . c_oflag = 0 ; // no remapping, no delays, raw output
if ( _blocking_mode ) {
fcntl ( fd , F_SETFL , 0 ) ; //efficient blocking for the read
//newtio.c_cc[VMIN] = 1; // read blocks for 1 character or timeout below
2023-06-04 21:17:48 +00:00
//newtio.c_cc[VTIME] = 0; // 0.5 seconds read timeout
//newtio.c_cc[VTIME] = 255; // 25 seconds read timeout
//newtio.c_cc[VTIME] = 10; // (1 to 255) 1 = 0.1 sec, 255 = 25.5 sec
newtio . c_cc [ VTIME ] = SERIAL_BLOCKING_TIME ;
2020-08-28 19:12:38 +00:00
newtio . c_cc [ VMIN ] = 0 ;
} else {
newtio . c_cc [ VMIN ] = 0 ; // read doesn't block
//newtio.c_cc[VTIME]= 1;
newtio . c_cc [ VTIME ] = ( readahead ? 0 : 1 ) ;
}
/*
Raw output is selected by resetting the OPOST option in the c_oflag member :
newtio . c_oflag & = ~ OPOST ;
When the OPOST option is disabled , all other option bits in c_oflag are ignored .
*/
//newtio.c_oflag &= ~OPOST; // Raw output
newtio . c_iflag & = ~ ( IXON | IXOFF | IXANY ) ; // shut off xon/xoff ctrl
newtio . c_cflag | = ( CLOCAL | CREAD ) ; // ignore modem controls,
// enable reading
newtio . c_cflag & = ~ ( PARENB | PARODD ) ; // shut off parity
newtio . c_cflag | = PARITY ;
newtio . c_cflag & = ~ CSTOPB ;
newtio . c_cflag & = ~ CRTSCTS ;
tcflush ( fd , TCIFLUSH ) ;
if ( tcsetattr ( fd , TCSANOW , & newtio ) ! = 0 ) {
LOG ( RSSD_LOG , LOG_ERR , " Unable to set port attributes: %s, error %d \n " , tty , errno ) ;
return - 1 ;
}
2023-06-20 00:15:45 +00:00
LOG ( RSSD_LOG , LOG_INFO , " Port %s set I/O %s attributes \n " , tty , _blocking_mode ? " blocking " : " non blocking " ) ;
2020-08-28 19:12:38 +00:00
return fd ;
}
# else //OLD_SERIAL_INIT
int _init_serial_port ( const char * tty , bool blocking , bool readahead ) // readahead ignored in this implimentation
2020-07-26 19:28:58 +00:00
{
2019-10-16 22:53:09 +00:00
long BAUD = B9600 ;
long DATABITS = CS8 ;
long STOPBITS = 0 ;
long PARITYON = 0 ;
long PARITY = 0 ;
struct termios newtio ; //place for old and new port settings for serial port
2020-07-26 19:28:58 +00:00
_blocking_mode = blocking ;
2019-10-16 22:53:09 +00:00
//int fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
int fd = open ( tty , O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY ) ;
if ( fd < 0 ) {
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_ERR , " Unable to open port: %s \n " , tty ) ;
2019-10-16 22:53:09 +00:00
return - 1 ;
}
2020-07-26 19:28:58 +00:00
LOG ( RSSD_LOG , LOG_DEBUG , " Openeded serial port %s \n " , tty ) ;
2019-10-16 22:53:09 +00:00
2020-07-26 19:28:58 +00:00
if ( _blocking_mode ) {
// http://unixwiz.net/techtips/termios-vmin-vtime.html
// Not designed behaviour, but it's what we need.
fcntl ( fd , F_SETFL , 0 ) ;
newtio . c_cc [ VMIN ] = 1 ;
2020-08-28 19:12:38 +00:00
//newtio.c_cc[VTIME]= 0; // This will wait indefinatly. (not the best if panel / rs485 adapter is down)
newtio . c_cc [ VTIME ] = 50 ; // (1 to 255) 1 = 0.1 sec, 255 = 25.5 sec
LOG ( RSSD_LOG , LOG_INFO , " Set serial port %s to blocking mode \n " , tty ) ;
2020-07-26 19:28:58 +00:00
} else {
int flags = fcntl ( fd , F_GETFL , 0 ) ;
fcntl ( fd , F_SETFL , flags | O_NONBLOCK | O_NDELAY ) ;
newtio . c_cc [ VMIN ] = 0 ;
2020-08-28 19:12:38 +00:00
newtio . c_cc [ VTIME ] = 1 ; // This should be 0 if we have readahead before write
LOG ( RSSD_LOG , LOG_INFO , " Set serial port %s to non blocking mode \n " , tty ) ;
2020-07-26 19:28:58 +00:00
}
2019-10-16 22:53:09 +00:00
tcgetattr ( fd , & _oldtio ) ; // save current port settings
// set new port settings for canonical input processing
newtio . c_cflag = BAUD | DATABITS | STOPBITS | PARITYON | PARITY | CLOCAL | CREAD ;
newtio . c_iflag = IGNPAR ;
newtio . c_lflag = 0 ; // ICANON;
newtio . c_oflag = 0 ;
tcflush ( fd , TCIFLUSH ) ;
tcsetattr ( fd , TCSANOW , & newtio ) ;
2020-07-26 19:28:58 +00:00
LOG ( RSSD_LOG , LOG_DEBUG , " Set serial port %s io attributes \n " , tty ) ;
2019-10-16 22:53:09 +00:00
return fd ;
}
2020-08-28 19:12:38 +00:00
# endif
2019-10-16 22:53:09 +00:00
2020-07-26 19:28:58 +00:00
void close_blocking_serial_port ( )
{
if ( _blocking_fds > 0 ) {
LOG ( RSSD_LOG , LOG_INFO , " Forcing close of blocking serial port, ignore following read errors \n " ) ;
close_serial_port ( _blocking_fds ) ;
} else {
LOG ( RSSD_LOG , LOG_ERR , " Didn't find valid blocking serial port file descripter \n " ) ;
}
}
2019-10-16 22:53:09 +00:00
/* close tty port */
void close_serial_port ( int fd )
{
2023-06-20 00:15:45 +00:00
unlock_port ( fd ) ;
2019-10-16 22:53:09 +00:00
tcsetattr ( fd , TCSANOW , & _oldtio ) ;
close ( fd ) ;
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_DEBUG_SERIAL , " Closed serial port \n " ) ;
2019-10-16 22:53:09 +00:00
}
2023-06-04 21:17:48 +00:00
bool serial_blockingmode ( )
{
return _blocking_mode ;
}
2019-10-16 22:53:09 +00:00
// Send an ack packet to the Aqualink RS8 master device.
// fd: the file descriptor of the serial port connected to the device
// command: the command byte to send to the master device, NUL if no command
//
// NUL = '\x00'
// DLE = '\x10'
// STX = '\x02'
// ETX = '\x03'
//
// masterAddr = '\x00' # address of Aqualink controller
//
//msg = DLE+STX+dest+cmd+args
//msg = msg+self.checksum(msg)+DLE+ETX
// DLE+STX+DEST+CMD+ARGS+CHECKSUM+DLE+ETX
2020-08-28 19:12:38 +00:00
/*
2019-10-16 22:53:09 +00:00
void print_hex ( char * pk , int length )
{
int i = 0 ;
for ( i = 0 ; i < length ; i + + )
{
printf ( " 0x%02hhx| " , pk [ i ] ) ;
}
printf ( " \n " ) ;
}
*/
void send_pentair_command ( int fd , unsigned char * packet_buffer , int size )
{
unsigned char packet [ AQ_MAXPKTLEN ] ;
int i = 0 ;
packet [ 0 ] = NUL ;
packet [ 1 ] = PP1 ;
packet [ 2 ] = PP2 ;
packet [ 3 ] = PP3 ;
packet [ 4 ] = PP4 ;
//packet[i++] = 0x00; // from
//packet[i++] = // to
2019-10-18 13:52:20 +00:00
for ( i = 5 ; i - 5 < size ; i + + ) {
2019-10-16 22:53:09 +00:00
//printf("added 0x%02hhx at position %d\n",packet_buffer[i-4],i);
if ( i = = 6 ) {
// Replace source
2019-10-18 13:52:20 +00:00
//packet[i] = 0x00;
// Don't replace source
packet [ i ] = packet_buffer [ i - 5 ] ;
2019-10-16 22:53:09 +00:00
} else if ( i = = 9 ) {
// Replace length
//packet[i] = 0xFF;
2019-10-18 13:52:20 +00:00
packet [ i ] = ( unsigned char ) size - 5 ;
2019-10-16 22:53:09 +00:00
} else {
2019-10-18 13:52:20 +00:00
packet [ i ] = packet_buffer [ i - 5 ] ;
2019-10-16 22:53:09 +00:00
}
//packet[i] = packet_buffer[i-4];
}
packet [ + + i ] = NUL ; // Checksum
packet [ + + i ] = NUL ; // Checksum
generate_pentair_checksum ( & packet [ 1 ] , i ) ;
packet [ + + i ] = NUL ;
//logPacket(packet, i);
send_packet ( fd , packet , i ) ;
}
2019-10-18 13:52:20 +00:00
void send_jandy_command ( int fd , unsigned char * packet_buffer , int size )
2019-10-16 22:53:09 +00:00
{
unsigned char packet [ AQ_MAXPKTLEN ] ;
int i = 0 ;
packet [ 0 ] = NUL ;
packet [ 1 ] = DLE ;
packet [ 2 ] = STX ;
2019-10-18 13:52:20 +00:00
for ( i = 3 ; i - 3 < size ; i + + ) {
2019-10-16 22:53:09 +00:00
//printf("added 0x%02hhx at position %d\n",packet_buffer[i-2],i);
2019-10-18 13:52:20 +00:00
packet [ i ] = packet_buffer [ i - 3 ] ;
2019-10-16 22:53:09 +00:00
}
packet [ + + i ] = DLE ;
packet [ + + i ] = ETX ;
packet [ + + i ] = NUL ;
packet [ i - 3 ] = generate_checksum ( packet , i ) ;
send_packet ( fd , packet , + + i ) ;
}
2020-08-28 19:12:38 +00:00
/*
unsigned char tp [ ] = { PCOL_PENTAIR , 0x07 , 0x0F , 0x10 , 0x08 , 0x0D , 0x55 , 0x55 , 0x5B , 0x2A , 0x2B , 0x00 , 0x00 , 0x00 , 0x00 , 0x64 , 0x00 , 0x00 , 0x00 } ;
send_command ( 0 , tp , 19 ) ;
Should produce
{ 0xFF , 0x00 , 0xFF , 0xA5 , 0x07 , 0x0F , 0x10 , 0x08 , 0x0D , 0x55 , 0x55 , 0x5B , 0x2A , 0x2B , 0x00 , 0x00 , 0x00 , 0x00 , 0x64 , 0x00 , 0x00 , 0x00 , 0x02 , 0x9E } ;
< - - - - - - - headder - - - - > < - - type to from type - > < len > < - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > < checksum >
*/
2019-10-18 13:52:20 +00:00
//unsigned char packet_buffer[] = {PCOL_PENTAIR, 0x07, 0x0F, 0x10, 0x08, 0x0D, 0x55, 0x55, 0x5B, 0x2A, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00};
//unsigned char packet_buffer[] = {PCOL_JANDY, 0x07, 0x0F, 0x00, 0x00};
void send_command ( int fd , unsigned char * packet_buffer , int size )
{
//unsigned char packet[AQ_MAXPKTLEN];
//int i=0;
if ( packet_buffer [ 0 ] = = PCOL_JANDY ) {
2020-07-18 16:37:19 +00:00
//LOG(RSSD_LOG,LOG_ERR, "Only Jandy protocol supported at present!\n");
2019-10-18 13:52:20 +00:00
send_jandy_command ( fd , & packet_buffer [ 1 ] , size - 1 ) ;
return ;
}
if ( packet_buffer [ 0 ] = = PCOL_PENTAIR ) {
2020-07-18 16:37:19 +00:00
//LOG(RSSD_LOG,LOG_ERR, "Only Jandy protocol supported at present!\n");
2019-10-18 13:52:20 +00:00
send_pentair_command ( fd , & packet_buffer [ 1 ] , size - 1 ) ;
return ;
}
}
2020-08-28 19:12:38 +00:00
/* Clean out any NULL in serial line.
* Return 0x00 if nothing or only nuls read , or return byte if something else was read
* leave fullclean as option as may want to use this to cleanout end of message on get_packet function
*/
unsigned char cleanOutSerial ( int fd , bool fullClean )
{
unsigned char byte = 0x00 ;
while ( ( read ( fd , & byte , 1 ) = = 1 ) & & ( byte = = 0x00 | | fullClean ) ) {
//printf("*** Peek Read 0x%02hhx ***\n",byte);
}
return byte ;
}
2020-07-18 16:37:19 +00:00
2020-08-28 19:12:38 +00:00
/*
NEWPACKETADDRESSSPACE
is test to copy packet to unused address space before send , just incase tc_drain doesn ' t work correctly
and we change buffer after function call . ( shouldn ; t even happen / be needed buyt good for testing )
*/
# ifdef NEWPACKETADDRESSSPACE
void send_packet ( int fd , unsigned char * packet_buffer , int length )
{
static unsigned char packet [ AQ_MAXPKTLEN ] ;
memcpy ( packet , packet_buffer , length ) ;
# else
2019-10-16 22:53:09 +00:00
void send_packet ( int fd , unsigned char * packet , int length )
{
2020-08-28 19:12:38 +00:00
# endif
2024-03-30 16:51:31 +00:00
struct timespec elapsed_time ;
struct timespec now ;
if ( _aqconfig_ . frame_delay > 0 ) {
2024-03-30 17:01:08 +00:00
struct timespec min_frame_wait_time ;
2024-03-30 16:51:31 +00:00
struct timespec frame_wait_time ;
struct timespec remainder_time ;
min_frame_wait_time . tv_sec = 0 ;
min_frame_wait_time . tv_nsec = _aqconfig_ . frame_delay * 1000000 ;
do {
clock_gettime ( CLOCK_REALTIME , & now ) ;
timespec_subtract ( & elapsed_time , & now , & last_serial_read_time ) ;
if ( timespec_subtract ( & frame_wait_time , & min_frame_wait_time , & elapsed_time ) ) {
break ;
}
} while ( nanosleep ( & frame_wait_time , & remainder_time ) ! = 0 ) ;
}
clock_gettime ( CLOCK_REALTIME , & now ) ;
2020-07-26 19:28:58 +00:00
if ( _blocking_mode ) {
2020-08-28 19:12:38 +00:00
//int nwrite = write(fd, packet, length);
//LOG(RSSD_LOG,LOG_DEBUG, "Serial write %d bytes of %d\n",nwrite,length);
int nwrite = write ( fd , packet , length ) ;
//if (nwrite < 0)
if ( nwrite ! = length )
LOG ( RSSD_LOG , LOG_ERR , " write to serial port failed \n " ) ;
2020-07-26 19:28:58 +00:00
} else {
2024-03-30 16:51:31 +00:00
# ifdef AQ_RS_EXTRA_OPTS
2020-07-26 19:28:58 +00:00
if ( _aqconfig_ . readahead_b4_write ) {
2020-08-28 19:12:38 +00:00
if ( cleanOutSerial ( fd , false ) ! = 0x00 ) {
LOG ( RSSD_LOG , LOG_ERR , " ERROR on RS485, AqualinkD was too slow in replying to message! (please check for performance issues) \n " ) ;
cleanOutSerial ( fd , true ) ;
}
2020-07-26 19:28:58 +00:00
}
2024-03-30 16:51:31 +00:00
# endif
2020-07-26 19:28:58 +00:00
int nwrite , i ;
for ( i = 0 ; i < length ; i + = nwrite ) {
nwrite = write ( fd , packet + i , length - i ) ;
if ( nwrite < 0 )
LOG ( RSSD_LOG , LOG_ERR , " write to serial port failed \n " ) ;
2020-08-28 19:12:38 +00:00
//else
// LOG(RSSD_LOG,LOG_DEBUG, "Serial write %d bytes of %d\n",nwrite,length-1);
2020-07-26 19:28:58 +00:00
}
} // _blockine_mode
/*
# ifdef AQ_DEBUG
// Need to take this out for release
if ( getLogLevel ( RSSD_LOG ) > = LOG_DEBUG ) {
debuglogPacket ( & packet [ 1 ] , length - 2 ) ;
}
# endif
*/
2023-05-14 21:35:13 +00:00
2023-05-21 15:03:27 +00:00
// MAYBE Change this back to debug serial
2023-05-14 21:35:13 +00:00
LOG ( RSSD_LOG , LOG_DEBUG_SERIAL , " Serial write %d bytes \n " , length - 2 ) ;
2023-05-21 15:03:27 +00:00
//LOG(RSSD_LOG,LOG_DEBUG, "Serial write %d bytes, type 0x%02hhx cmd 0x%02hhx\n",length-2,packet[5],packet[6]);
2023-05-14 21:35:13 +00:00
logPacketWrite ( & packet [ 1 ] , length - 2 ) ;
2023-05-21 15:03:27 +00:00
/*
if ( getLogLevel ( PDA_LOG ) = = LOG_DEBUG ) {
char buff [ 1024 ] ;
beautifyPacket ( buff , & packet [ 1 ] , length , false ) ;
LOG ( PDA_LOG , LOG_DEBUG , " %s " , buff ) ;
}
*/
2023-05-14 21:35:13 +00:00
/*
if ( getLogLevel ( RSSD_LOG ) > = LOG_DEBUG_SERIAL | | ) {
2020-06-01 00:35:17 +00:00
// Packet is padded with 0x00, so discard for logging
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_DEBUG_SERIAL , " Serial write %d bytes \n " , length - 2 ) ;
2019-10-23 15:16:00 +00:00
logPacket ( & packet [ 1 ] , length - 2 ) ;
2023-05-14 21:35:13 +00:00
} */ /*else if (getLogLevel(RSSD_LOG) >= LOG_DEBUG) {
2020-08-28 19:12:38 +00:00
static char buf [ 1000 ] ;
beautifyPacket ( buf , & packet [ 1 ] , length - 2 ) ;
LOG ( RSSD_LOG , LOG_DEBUG , " Serial write %s \n " , buf ) ;
} */
tcdrain ( fd ) ; // Make sure buffer has been sent.
2024-03-30 16:51:31 +00:00
//if (_aqconfig_.frame_delay > 0) {
timespec_subtract ( & elapsed_time , & now , & last_serial_read_time ) ;
LOG ( RSSD_LOG , LOG_DEBUG , " Time from recv to %s send is %ld.%09ld sec \n " ,
( _blocking_mode ? " blocking " : " non-blocking " ) , elapsed_time . tv_sec ,
elapsed_time . tv_nsec ) ;
//}
2019-10-16 22:53:09 +00:00
}
void _send_ack ( int fd , unsigned char ack_type , unsigned char command )
{
const int length = 11 ;
// Default null ack with checksum generated, don't mess with it, just over right
unsigned char ackPacket [ ] = { NUL , DLE , STX , DEV_MASTER , CMD_ACK , NUL , NUL , 0x13 , DLE , ETX , NUL } ;
// Update the packet and checksum if command argument is not NUL.
if ( command ! = NUL | | ack_type ! = NUL ) {
//ackPacket[5] = 0x00 normal, 0x03 some pause, 0x01 some pause ending (0x01 = Screen Busy (also return from logn message))
ackPacket [ 5 ] = ack_type ;
ackPacket [ 6 ] = command ;
ackPacket [ 7 ] = generate_checksum ( ackPacket , length - 1 ) ;
2020-06-20 16:09:54 +00:00
if ( command = = DLE ) { // We shuld probably also check the ack type as well, just for future proofing.
// 0x10(DLE) that's not part of the headder or footer needs to be escaped AFTER with NUL, so shif everyting uo one
ackPacket [ 8 ] = ackPacket [ 7 ] ; // move the caculated checksum
ackPacket [ 7 ] = NUL ; // escape the DLE
ackPacket [ 9 ] = DLE ; // add new end sequence
ackPacket [ 10 ] = ETX ; // add new end sequence
}
2019-10-16 22:53:09 +00:00
}
2020-06-01 00:35:17 +00:00
//printf("***Send ACK (%s) ***\n",(ack_type==ACK_NORMAL?"Normal":(ack_type==ACK_SCREEN_BUSY?"ScreenBusy":"ScreenBusyDisplay")) );
2019-10-16 22:53:09 +00:00
send_packet ( fd , ackPacket , length ) ;
}
void send_ack ( int fd , unsigned char command )
{
_send_ack ( fd , ACK_NORMAL , command ) ;
}
// ack_typ should only be ACK_PDA, ACK_NORMAL, ACK_SCREEN_BUSY, ACK_SCREEN_BUSY_DISPLAY
void send_extended_ack ( int fd , unsigned char ack_type , unsigned char command )
{
_send_ack ( fd , ack_type , command ) ;
}
2023-05-14 21:35:13 +00:00
/*
2019-10-16 22:53:09 +00:00
int _get_packet ( int fd , unsigned char * packet , bool rawlog ) ;
int get_packet ( int fd , unsigned char * packet )
{
return _get_packet ( fd , packet , false ) ;
}
int get_packet_lograw ( int fd , unsigned char * packet )
{
return _get_packet ( fd , packet , true ) ;
}
int _get_packet ( int fd , unsigned char * packet , bool rawlog )
2023-05-14 21:35:13 +00:00
*/
int get_packet ( int fd , unsigned char * packet )
2019-10-16 22:53:09 +00:00
{
2020-08-28 19:12:38 +00:00
unsigned char byte = 0x00 ;
2019-10-16 22:53:09 +00:00
int bytesRead ;
int index = 0 ;
bool endOfPacket = false ;
//bool packetStarted = FALSE;
bool lastByteDLE = false ;
int retry = 0 ;
bool jandyPacketStarted = false ;
bool pentairPacketStarted = false ;
//bool lastByteDLE = false;
int PentairPreCnt = 0 ;
int PentairDataCnt = - 1 ;
2020-08-28 19:12:38 +00:00
memset ( packet , 0 , AQ_MAXPKTLEN ) ;
2019-10-16 22:53:09 +00:00
// Read packet in byte order below
// DLE STX ........ ETX DLE
// sometimes we get ETX DLE and no start, so for now just ignoring that. Seem to be more applicable when busy RS485 traffic
2020-08-28 19:12:38 +00:00
//#ifndef OLD_SERIAL_INIT .. Need to re-do ERROR like EAGAIN with new init
2019-10-16 22:53:09 +00:00
while ( ! endOfPacket ) {
2023-06-04 21:17:48 +00:00
//printf("READ SERIAL\n");
2019-10-16 22:53:09 +00:00
bytesRead = read ( fd , & byte , 1 ) ;
2020-08-28 19:12:38 +00:00
//printf("Read %d 0x%02hhx err=%d fd=%d\n",bytesRead,byte,errno,fd);
2019-10-16 22:53:09 +00:00
//if (bytesRead < 0 && errno == EAGAIN && packetStarted == FALSE && lastByteDLE == FALSE) {
2020-08-28 19:12:38 +00:00
//if (bytesRead < 0 && (errno == EAGAIN || errno == 0) &&
2023-06-04 21:17:48 +00:00
if ( bytesRead < = 0 & & ( errno = = EAGAIN | | errno = = 0 | | errno = = ENOTTY ) ) { // We also get ENOTTY on some non FTDI adapters
2020-08-28 19:12:38 +00:00
if ( _blocking_mode ) {
// Something is wrong wrong
return AQSERR_TIMEOUT ;
} else if ( jandyPacketStarted = = false & & pentairPacketStarted = = false & & lastByteDLE = = false ) {
// We just have nothing else to read
2019-10-16 22:53:09 +00:00
return 0 ;
2020-08-28 19:12:38 +00:00
} else if ( + + retry > 120 ) {
LOG ( RSSD_LOG , LOG_WARNING , " Serial read timeout \n " ) ;
//log_packet(LOG_WARNING, "Bad receive packet ", packet, index);
if ( index > 0 ) { logPacketError ( packet , index ) ; }
return AQSERR_TIMEOUT ;
2019-10-16 22:53:09 +00:00
}
2020-08-28 19:12:38 +00:00
delay ( 1 ) ;
2019-10-16 22:53:09 +00:00
} else if ( bytesRead = = 1 ) {
2020-08-28 19:12:38 +00:00
retry = 0 ;
2023-05-14 21:35:13 +00:00
if ( _aqconfig_ . log_raw_bytes )
2019-10-16 22:53:09 +00:00
logPacketByte ( & byte ) ;
if ( lastByteDLE = = true & & byte = = NUL )
{
// Check for DLE | NULL (that's escape DLE so delete the NULL)
//printf("IGNORE THIS PACKET\n");
lastByteDLE = false ;
}
else if ( lastByteDLE = = true )
{
if ( index = = 0 )
index + + ;
packet [ index ] = byte ;
index + + ;
if ( byte = = STX & & jandyPacketStarted = = false )
{
jandyPacketStarted = true ;
pentairPacketStarted = false ;
}
else if ( byte = = ETX & & jandyPacketStarted = = true )
{
endOfPacket = true ;
}
}
else if ( jandyPacketStarted | | pentairPacketStarted )
{
packet [ index ] = byte ;
index + + ;
if ( pentairPacketStarted = = true & & index = = 9 )
{
//printf("Read 0x%02hhx %d pentair\n", byte, byte);
PentairDataCnt = byte ;
}
if ( PentairDataCnt > = 0 & & index - 11 > = PentairDataCnt & & pentairPacketStarted = = true )
{
endOfPacket = true ;
PentairPreCnt = - 1 ;
}
}
else if ( byte = = DLE & & jandyPacketStarted = = false )
{
packet [ index ] = byte ;
}
// // reset index incase we have EOP before start
if ( jandyPacketStarted = = false & & pentairPacketStarted = = false )
{
index = 0 ;
}
if ( byte = = DLE & & pentairPacketStarted = = false )
{
lastByteDLE = true ;
PentairPreCnt = - 1 ;
}
else
{
lastByteDLE = false ;
if ( byte = = PP1 & & PentairPreCnt = = 0 )
PentairPreCnt = 1 ;
else if ( byte = = PP2 & & PentairPreCnt = = 1 )
PentairPreCnt = 2 ;
else if ( byte = = PP3 & & PentairPreCnt = = 2 )
PentairPreCnt = 3 ;
else if ( byte = = PP4 & & PentairPreCnt = = 3 )
{
pentairPacketStarted = true ;
jandyPacketStarted = false ;
PentairDataCnt = - 1 ;
packet [ 0 ] = PP1 ;
packet [ 1 ] = PP2 ;
packet [ 2 ] = PP3 ;
packet [ 3 ] = byte ;
index = 4 ;
}
else if ( byte ! = PP1 ) // Don't reset counter if multiple PP1's
PentairPreCnt = 0 ;
}
} else if ( bytesRead < 0 ) {
// Got a read error. Wait one millisecond for the next byte to
// arrive.
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_WARNING , " Read error: %d - %s \n " , errno , strerror ( errno ) ) ;
2019-10-16 22:53:09 +00:00
if ( errno = = 9 ) {
// Bad file descriptor. Port has been disconnected for some reason.
// Return a -1.
2020-08-28 19:12:38 +00:00
return AQSERR_READ ;
2019-10-16 22:53:09 +00:00
}
delay ( 100 ) ;
}
// Break out of the loop if we exceed maximum packet
// length.
if ( index > = AQ_MAXPKTLEN ) {
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_WARNING , " Serial packet too large \n " ) ;
2020-08-28 19:12:38 +00:00
logPacketError ( packet , index ) ;
2019-10-16 22:53:09 +00:00
//log_packet(LOG_WARNING, "Bad receive packet ", packet, index);
2020-08-28 19:12:38 +00:00
return AQSERR_2LARGE ;
2019-10-16 22:53:09 +00:00
break ;
}
}
2020-07-18 16:37:19 +00:00
// Clean out rest of buffer, make sure their is nothing else
/* Doesn't work for shit due to probe message speed, need to come back and re-think
if ( _aqconfig_ . readahead_b4_write ) {
if ( endOfPacket = = true ) {
do {
bytesRead = read ( fd , & byte , 1 ) ;
//if (bytesRead==1) { LOG(RSSD_LOG,LOG_ERR, "Cleanout buffer read 0x%02hhx\n",byte); }
if ( bytesRead = = 1 & & byte ! = 0x00 ) {
LOG ( RSSD_LOG , LOG_ERR , " SERIOUS ERROR on RS485, AqualinkD caught packet collision on bus, ignoring! \n " ) ;
LOG ( RSSD_LOG , LOG_ERR , " Error Cleanout read 0x%02hhx \n " , byte ) ;
// Run the buffer out
do {
bytesRead = read ( fd , & byte , 1 ) ;
if ( bytesRead = = 1 ) { LOG ( RSSD_LOG , LOG_ERR , " Error Cleanout read 0x%02hhx \n " , byte ) ; }
} while ( bytesRead = = 1 ) ;
return 0 ;
}
} while ( bytesRead = = 1 ) ;
}
}
*/
//LOG(RSSD_LOG,LOG_DEBUG, "Serial checksum, length %d got 0x%02hhx expected 0x%02hhx\n", index, packet[index-3], generate_checksum(packet, index));
2019-10-16 22:53:09 +00:00
if ( jandyPacketStarted ) {
if ( check_jandy_checksum ( packet , index ) ! = true ) {
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_WARNING , " Serial read bad Jandy checksum, ignoring \n " ) ;
2020-08-28 19:12:38 +00:00
logPacketError ( packet , index ) ;
2019-10-16 22:53:09 +00:00
//log_packet(LOG_WARNING, "Bad receive packet ", packet, index);
2020-08-28 19:12:38 +00:00
return AQSERR_CHKSUM ;
2019-10-16 22:53:09 +00:00
}
} else if ( pentairPacketStarted ) {
if ( check_pentair_checksum ( packet , index ) ! = true ) {
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_WARNING , " Serial read bad Pentair checksum, ignoring \n " ) ;
2020-08-28 19:12:38 +00:00
logPacketError ( packet , index ) ;
2019-10-16 22:53:09 +00:00
//log_packet(LOG_WARNING, "Bad receive packet ", packet, index);
2020-08-28 19:12:38 +00:00
return AQSERR_CHKSUM ;
2019-10-16 22:53:09 +00:00
}
}
/*
if ( generate_checksum ( packet , index ) ! = packet [ index - 3 ] ) {
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_WARNING , " Serial read bad checksum, ignoring \n " ) ;
2019-10-16 22:53:09 +00:00
log_packet ( LOG_WARNING , " Bad receive packet " , packet , index ) ;
return 0 ;
} else */ if ( index < AQ_MINPKTLEN & & ( jandyPacketStarted | | pentairPacketStarted ) ) { //NSF. Sometimes we get END sequence only, so just ignore.
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_WARNING , " Serial read too small \n " ) ;
2020-08-28 19:12:38 +00:00
logPacketError ( packet , index ) ;
2019-10-16 22:53:09 +00:00
//log_packet(LOG_WARNING, "Bad receive packet ", packet, index);
2020-08-28 19:12:38 +00:00
return AQSERR_2SMALL ;
2019-10-16 22:53:09 +00:00
}
2024-03-30 16:51:31 +00:00
//if (_aqconfig_.frame_delay > 0) {
clock_gettime ( CLOCK_REALTIME , & last_serial_read_time ) ;
//}
2020-07-18 16:37:19 +00:00
LOG ( RSSD_LOG , LOG_DEBUG_SERIAL , " Serial read %d bytes \n " , index ) ;
2023-05-30 23:14:04 +00:00
if ( _aqconfig_ . log_protocol_packets | | getLogLevel ( RSSD_LOG ) > = LOG_DEBUG_SERIAL )
2023-05-14 21:35:13 +00:00
logPacketRead ( packet , index ) ;
2019-10-16 22:53:09 +00:00
// Return the packet length.
return index ;
}
# else // PLAYBACKMODE
2020-08-28 19:12:38 +00:00
// Need to re-write this if we ever use playback mode again. Pull info from aq_serial.old.c
2020-06-01 00:35:17 +00:00
2020-08-28 19:12:38 +00:00
# endif
2020-06-01 00:35:17 +00:00
2019-10-16 22:53:09 +00:00
2017-12-30 20:12:01 +00:00
2018-04-18 00:33:17 +00:00
2018-09-02 21:45:24 +00:00
2018-04-18 00:33:17 +00:00
2019-08-18 20:54:10 +00:00
2018-03-10 23:07:49 +00:00
2017-12-30 20:12:01 +00:00
2019-10-16 22:53:09 +00:00