diff --git a/Makefile b/Makefile index 1014394..7c20ab6 100755 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ ifeq ($(DEBUG), true) endif SL_SRC = serial_logger.c aq_serial.c utils.c packetLogger.c -LR_SRC = log_reader.c aq_serial.c utils.c +LR_SRC = log_reader.c aq_serial.c utils.c packetLogger.c PL_EXSRC = aq_serial.c PL_EXOBJ = aq_serial_player.o PL_SRC := $(filter-out aq_serial.c, $(SRCS)) diff --git a/README.md b/README.md index 7cd0681..2f7cdc7 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,10 @@ Designed to mimic AqualinkRS6 All Button keypad and (like the keypad) is used to * http://aqualink.ip/simple.html <- (Simple opion if you don't like the above) * http://aqualink.ip/simulator.html <- (RS8 All Button Control Panel simulator) # +# Update in Release 1.3.8 +* Fixed PDA mode from 1.3.7 +* Added SWG Boost to PDA +* More updates to protocol code for Jandy and Pentair. # Update in Release 1.3.7 * PDA SUPPORT IS BROKEN IN 1.3.7 DON'T UPGRADE IF YOU'RE USING PDA Mode * PDA Note:- Due to changes to speed up programming the control panel, PDA mode does not function correctly, I will come back and fix this, but I don't have the time for this release. diff --git a/aq_programmer.c b/aq_programmer.c index 1a0cdbc..7cf40b3 100644 --- a/aq_programmer.c +++ b/aq_programmer.c @@ -121,7 +121,7 @@ unsigned char pop_aq_cmd(struct aqualinkdata *aq_data) // Are we in programming mode if (aq_data->active_thread.thread_id != 0) { if ( ((_pgm_command == KEY_MENU || aq_data->active_thread.ptype == AQ_SET_TIME) && aq_data->last_packet_type == CMD_STATUS) || - (aq_data->active_thread.ptype != AQ_SET_TIME && last_sent_was_cmd == false) || + (pda_mode() == false && aq_data->active_thread.ptype != AQ_SET_TIME && last_sent_was_cmd == false) || (pda_mode() == true && aq_data->last_packet_type == CMD_STATUS) //(pda_mode() == true && last_sent_was_cmd == false) ) { @@ -132,8 +132,10 @@ unsigned char pop_aq_cmd(struct aqualinkdata *aq_data) cmd = _pgm_command; _pgm_command = NUL; logMessage(LOG_DEBUG, "RS SEND cmd '0x%02hhx' (programming)\n", cmd);*/ + } else if (_pgm_command != NUL) { + logMessage(LOG_DEBUG, "RS Waiting to send cmd '0x%02hhx' (programming)\n", _pgm_command); } else { - logMessage(LOG_DEBUG, "RS Waiting to send cmd '0x%02hhx'\n", _pgm_command); + logMessage(LOG_DEBUG, "RS SEND cmd '0x%02hhx' empty queue (programming)\n", cmd); } } else if (_stack_place > 0 && aq_data->last_packet_type == CMD_STATUS ) { cmd = _commands[0]; @@ -286,7 +288,8 @@ void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data) type != AQ_GET_AUX_LABELS && #endif type != AQ_GET_POOL_SPA_HEATER_TEMPS && - type != AQ_SET_FRZ_PROTECTION_TEMP) { + type != AQ_SET_FRZ_PROTECTION_TEMP && + type != AQ_SET_BOOST) { logMessage(LOG_ERR, "Selected Programming mode '%d' not supported with PDA mode control panel\n",type); return; } @@ -646,6 +649,12 @@ STOP BOOST POOL */ int val = atoi((char*)threadCtrl->thread_args); + if (pda_mode() == true) { + set_PDA_aqualink_boost(aq_data, val); + cleanAndTerminateThread(threadCtrl); + return ptr; + } + logMessage(LOG_DEBUG, "programming BOOST to %s\n", val==true?"On":"Off"); if ( select_menu_item(aq_data, "BOOST POOL") != true ) { @@ -1313,8 +1322,15 @@ void waitfor_queue2empty() delay(50); } - if (_pgm_command != NUL) + if (_pgm_command != NUL) { + if (pda_mode()) { + // Wait for longer in PDA mode since it's slower. + while ( (_pgm_command != NUL) && ( i++ < 100) ) { + delay(100); + } + } logMessage(LOG_WARNING, "Send command Queue did not empty, timeout\n"); + } } diff --git a/aq_serial.c b/aq_serial.c index 888d839..9f03850 100644 --- a/aq_serial.c +++ b/aq_serial.c @@ -31,8 +31,638 @@ #include "config.h" #include "packetLogger.h" +//#define USE_AQ_SERIAL_OLD + +#ifndef USE_AQ_SERIAL_OLD // Substansial changes to core component, make sure we can role back easily + +static struct termios _oldtio; + +void send_packet(int fd, unsigned char *packet, int length); +//unsigned char getProtocolType(unsigned char* packet); + +bool _pda_mode = false; +void set_pda_mode(bool mode) +{ + if (mode) + logMessage(LOG_NOTICE, "AqualinkD is using PDA mode\n"); + + _pda_mode = mode; +} +bool pda_mode() +{ + return _pda_mode; +} + +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: + return "Ack"; + 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: + return "PDA C_HlightChar"; + break; + case CMD_IAQ_MSG: + return "iAq Message"; + break; + case CMD_IAQ_MENU_MSG: + return "iAq Menu"; + break; + 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) +{ + if (generate_checksum(packet, length) == packet[length-3]) + return true; + + 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])) { + logMessage(LOG_ERR, "Pentair checksum is accurate but length is not\n"); + 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 +*/ +int init_serial_port(const char* tty) +{ + 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 + + //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) { + logMessage(LOG_ERR, "Unable to open port: %s\n", tty); + return -1; + } + + logMessage(LOG_DEBUG_SERIAL, "Openeded serial port %s\n",tty); + + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK | O_NDELAY); + newtio.c_cc[VMIN]= 0; + newtio.c_cc[VTIME]= 1; + logMessage(LOG_DEBUG_SERIAL, "Set serial port %s to non blocking mode\n",tty); + + 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); + + logMessage(LOG_DEBUG_SERIAL, "Set serial port %s io attributes\n",tty); + + return fd; +} + +/* close tty port */ +void close_serial_port(int fd) +{ + tcsetattr(fd, TCSANOW, &_oldtio); + close(fd); + logMessage(LOG_DEBUG_SERIAL, "Closed serial port\n"); +} + + + + + + + +// 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 + + +void print_hex(char *pk, int length) +{ + int i=0; + for (i=0;i <-- type to from type-> <------------------------------ data ----------------------------------------> +*/ + +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 + for (i=5; i-4 < size; i++) { + //printf("added 0x%02hhx at position %d\n",packet_buffer[i-4],i); + if (i==6) { + // Replace source + packet[i] = 0x00; + } else if (i==9) { + // Replace length + //packet[i] = 0xFF; + packet[i] = (unsigned char)size-6; + } else { + packet[i] = packet_buffer[i-4]; + } + + //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); +} + +//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) { + //logMessage(LOG_ERR, "Only Jandy protocol supported at present!\n"); + send_pentair_command(fd, packet_buffer, size); + return; + } + + packet[0] = NUL; + packet[1] = DLE; + packet[2] = STX; + + for (i=3; i-2 < size; i++) { + //printf("added 0x%02hhx at position %d\n",packet_buffer[i-2],i); + packet[i] = packet_buffer[i-2]; + } + + packet[++i] = DLE; + packet[++i] = ETX; + packet[++i] = NUL; + + packet[i-3] = generate_checksum(packet, i); + + send_packet(fd,packet,++i); +} + +void send_packet(int fd, unsigned char *packet, int length) +{ + + int nwrite, i; + for (i=0; i= LOG_DEBUG_SERIAL) { + //char buf[30]; + //sprintf(buf, "Sent %8.8s ", get_packet_type(packet+1, length)); + //log_packet(buf, packet, length); + logPacket(packet, length); + } +} + +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); + } + + 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); +} + +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) +{ + unsigned char byte; + 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; + + // 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 + + while (!endOfPacket) { + bytesRead = read(fd, &byte, 1); + //if (bytesRead < 0 && errno == EAGAIN && packetStarted == FALSE && lastByteDLE == FALSE) { + if (bytesRead < 0 && errno == EAGAIN && + jandyPacketStarted == false && + pentairPacketStarted == false && + lastByteDLE == false) { + // We just have nothing to read + return 0; + } else if (bytesRead < 0 && errno == EAGAIN) { + // If we are in the middle of reading a packet, keep going + if (retry > 20) { + logMessage(LOG_WARNING, "Serial read timeout\n"); + //log_packet(LOG_WARNING, "Bad receive packet ", packet, index); + logPacketError(packet, index); + return 0; + } + retry++; + delay(10); + #ifdef TESTING + } else if (bytesRead == 0 && jandyPacketStarted == false && pentairPacketStarted == false) { + // Probably set port to /dev/null for testing. + //printf("Read loop return\n"); + return 0; + #endif + } else if (bytesRead == 1) { + if (rawlog) + 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. + logMessage(LOG_WARNING, "Read error: %d - %s\n", errno, strerror(errno)); + if(errno == 9) { + // Bad file descriptor. Port has been disconnected for some reason. + // Return a -1. + return -1; + } + delay(100); + } + + // Break out of the loop if we exceed maximum packet + // length. + if (index >= AQ_MAXPKTLEN) { + logPacketError(packet, index); + logMessage(LOG_WARNING, "Serial packet too large\n"); + //log_packet(LOG_WARNING, "Bad receive packet ", packet, index); + return 0; + break; + } + } + + //logMessage(LOG_DEBUG, "Serial checksum, length %d got 0x%02hhx expected 0x%02hhx\n", index, packet[index-3], generate_checksum(packet, index)); + if (jandyPacketStarted) { + if (check_jandy_checksum(packet, index) != true){ + logPacketError(packet, index); + logMessage(LOG_WARNING, "Serial read bad Jandy checksum, ignoring\n"); + //log_packet(LOG_WARNING, "Bad receive packet ", packet, index); + return 0; + } + } else if (pentairPacketStarted) { + if (check_pentair_checksum(packet, index) != true){ + logPacketError(packet, index); + logMessage(LOG_WARNING, "Serial read bad Pentair checksum, ignoring\n"); + //log_packet(LOG_WARNING, "Bad receive packet ", packet, index); + return 0; + } + } +/* + if (generate_checksum(packet, index) != packet[index-3]){ + logMessage(LOG_WARNING, "Serial read bad checksum, ignoring\n"); + 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. + logPacketError(packet, index); + logMessage(LOG_WARNING, "Serial read too small\n"); + //log_packet(LOG_WARNING, "Bad receive packet ", packet, index); + return 0; + } + + logMessage(LOG_DEBUG_SERIAL, "Serial read %d bytes\n",index); + logPacket(packet, index); + // Return the packet length. + return index; +} + +#else // PLAYBACKMODE + +#endif + + +#else //USE_AQ_SERIAL_OLD + + + + //#define BLOCKING_MODE -#define PENTAIR_LENGTH_FIX static struct termios _oldtio; @@ -77,21 +707,6 @@ void log_packet(int level, char *init_str, unsigned char* packet, int length) //logMessage(LOG_DEBUG_SERIAL, buff); } - - - - - - - - - - - - - - - const char* get_packet_type(unsigned char* packet , int length) { static char buf[15]; @@ -586,6 +1201,34 @@ bool check_jandy_checksum(unsigned char* packet, int length) } 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])) { + //logMessage(LOG_ERR, "Pentair checksum is accurate but length is not\n"); + return true; + } + + return false; +} +/* +bool check_pentair_checksum_old(unsigned char* packet, int length) { int i, sum, n; n = packet[8] + 9; @@ -594,17 +1237,12 @@ bool check_pentair_checksum(unsigned char* packet, int length) sum += (int) packet[i]; } -#ifndef PENTAIR_LENGTH_FIX - if (sum == (packet[length-1] * 256 + packet[length])) - return true; -#else if (sum == (packet[length-2] * 256 + packet[length-1])) return true; -#endif return false; } - +*/ int _get_packet(int fd, unsigned char* packet, bool rawlog); int get_packet_new_lograw(int fd, unsigned char* packet) @@ -693,9 +1331,6 @@ int _get_packet(int fd, unsigned char* packet, bool rawlog) { endOfPacket = true; PentairPreCnt = -1; -#ifndef PENTAIR_LENGTH_FIX - index--; -#endif } } else if (byte == DLE && jandyPacketStarted == false) @@ -1020,3 +1655,5 @@ int get_packet_old(int fd, unsigned char* packet) return index; } +#endif // USE_OLD + diff --git a/aq_serial.h b/aq_serial.h index fad7487..94b2d8f 100644 --- a/aq_serial.h +++ b/aq_serial.h @@ -6,6 +6,13 @@ #define CONNECTION_ERROR "ERROR No connection to RS control panel" + +// Protocol types +#define PCOL_JANDY 0xFF +#define PCOL_PENTAIR 0xFE +#define PCOL_UNKNOWN 0xFD + + // packet offsets #define PKT_DEST 2 #define PKT_CMD 3 @@ -205,7 +212,8 @@ SPILLOVER IS DISABLED WHILE SPA IS ON #define SWG_STATUS_CHECK_PCB 0x80 // check PCB 0x80 -#define CMD_PDA_0x05 0x05 +#define CMD_PDA_0x04 0x04 // No idea, might be building menu +#define CMD_PDA_0x05 0x05 // No idea #define CMD_PDA_0x1B 0x1b #define CMD_PDA_HIGHLIGHT 0x08 #define CMD_PDA_CLEAR 0x09 @@ -245,7 +253,7 @@ typedef enum { } protocolType; -int init_serial_port(char* tty); +int init_serial_port(const char* tty); void close_serial_port(int file_descriptor); void set_pda_mode(bool mode); bool pda_mode(); @@ -257,6 +265,8 @@ void send_ack(int file_descriptor, unsigned char command); void send_extended_ack(int fd, unsigned char ack_type, unsigned char command); //void send_cmd(int file_descriptor, unsigned char cmd, unsigned char args); int get_packet(int file_descriptor, unsigned char* packet); +int get_packet_lograw(int fd, unsigned char* packet); + int get_packet_new(int fd, unsigned char* packet); int get_packet_new_lograw(int fd, unsigned char* packet); //void close_serial_port(int file_descriptor, struct termios* oldtio); diff --git a/aqualinkd.c b/aqualinkd.c index a709729..13fd664 100644 --- a/aqualinkd.c +++ b/aqualinkd.c @@ -1010,10 +1010,11 @@ void caculate_ack_packet(int rs_fd, unsigned char *packet_buffer) { if (_config_parameters.pda_sleep_mode && pda_shouldSleep()) { logMessage(LOG_DEBUG, "PDA Aqualink daemon in sleep mode\n"); return; - } else if (packet_buffer[PKT_CMD] != CMD_STATUS) - send_ack(rs_fd, NUL); - else - send_ack(rs_fd, pop_aq_cmd(&_aqualink_data)); + //} else if (packet_buffer[PKT_CMD] != CMD_STATUS) // Moved logic to pop_aq_cmd() + // send_extended_ack(rs_fd, ACK_PDA, NUL); + } else { + send_extended_ack(rs_fd, ACK_PDA, pop_aq_cmd(&_aqualink_data)); + } } else if (_aqualink_data.simulate_panel && _aqualink_data.active_thread.thread_id == 0) { // We are in simlator mode, ack get's complicated now. @@ -1171,9 +1172,7 @@ void main_loop() } if (_config_parameters.log_raw_RS_bytes) - packet_length = get_packet_new_lograw(rs_fd, packet_buffer); - else if (_config_parameters.read_pentair_packets) - packet_length = get_packet_new(rs_fd, packet_buffer); + packet_length = get_packet_lograw(rs_fd, packet_buffer); else packet_length = get_packet(rs_fd, packet_buffer); diff --git a/log_reader.c b/log_reader.c index 2d25c0c..23dfda1 100644 --- a/log_reader.c +++ b/log_reader.c @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include #include "aq_serial.h" #include "utils.h" @@ -49,6 +52,41 @@ void logiAqualinkMsg(unsigned char *packet_buffer, int packet_length) { } +void read_bin_file(char *filename) +{ + unsigned char packet_buffer[AQ_MAXPKTLEN]; + int packet_length; + int i=0; + + setLoggingPrms(LOG_DEBUG_SERIAL , false, NULL, NULL); + startPacketLogger(false,true); + + int fd = open(filename, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY); + if (fd < 0) { + logMessage(LOG_ERR, "Unable to open port: %s\n", filename); + return; + } + + while ( (packet_length = get_packet_new(fd, packet_buffer)) > -1){ + printf("----------------\n"); + //logPacket(packet_buffer, packet_length); + } + +} + +void create_bin_file(FILE *in, FILE *out) { + char hex[6]; + unsigned char byte; + + while ( fgets ( hex, 6, in ) != NULL ) /* read a line */ + { + byte = (int)strtol(hex, NULL, 16); + //printf("read 0x%02hhx, %s\n",byte,hex); + fwrite(&byte, 1,1, out); + //return; + } +} + int main(int argc, char *argv[]) { //unsigned char packet_buffer[10]; @@ -56,13 +94,30 @@ int main(int argc, char *argv[]) { int packet_length = 0; char hex[5]; int i; + FILE *outfile = NULL; + bool writeb = false; //int num; //int pi; - if (argc < 1 || access( argv[1], F_OK ) == -1 ) { + if (strcmp(argv[1], "-rb") == 0) { + read_bin_file(argv[2]); + return 0; + } + else if (argc < 1 || access( argv[1], F_OK ) == -1 ) { fprintf(stderr, "ERROR, first param must be valid log file\n"); return 1; } + + else if (strcmp(argv[2], "-b") == 0) { + outfile = fopen ( argv[3], "w" ); + if ( outfile == NULL ) + { + fprintf(stderr, "ERROR, Couldn't open out file `%s`\n", argv[3]); + perror ( argv[3] ); /* why didn't the file open? */ + return 1; + } + writeb = true; + } FILE *file = fopen ( argv[1], "r" ); if ( file == NULL ) @@ -70,6 +125,13 @@ int main(int argc, char *argv[]) { perror ( argv[1] ); /* why didn't the file open? */ return 1; } + + if (writeb) { + create_bin_file(file, outfile); + fclose ( file ); + fclose ( outfile ); + return 0; + } char line [ 128 ]; /* or other suitable maximum line size */ while ( fgets ( line, sizeof line, file ) != NULL ) /* read a line */ @@ -85,20 +147,22 @@ int main(int argc, char *argv[]) { } packet_length--; + if (packet_buffer[PKT_DEST] == 0x33 && packet_buffer[PKT_CMD] == 0x25) - logiAqualinkMsg(packet_buffer, packet_length); + logiAqualinkMsg(packet_buffer, packet_length); //if (packet_buffer[PKT_CMD] == 0x24 || packet_buffer[PKT_CMD] == 0x25) { - printf("To 0x%02hhx, type %15.15s, length %2.2d ", packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length),packet_length); - fputs ( line, stdout ); + printf("To 0x%02hhx, type %15.15s, length %2.2d\n", packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length),packet_length); + fputs ( line, stdout ); //printf("Message : '"); //fwrite(packet_buffer + 4, 1, packet_length-7, stdout); //printf("'\n"); //} + } + fclose ( file ); - return 0; } diff --git a/pda_aq_programmer.c b/pda_aq_programmer.c index cbf01a9..d32e5cc 100644 --- a/pda_aq_programmer.c +++ b/pda_aq_programmer.c @@ -156,16 +156,25 @@ bool loopover_devices(struct aqualinkdata *aq_data) { /* if charlimit is set, use case insensitive match and limit chars. + if charlimit is -1, use VERY loose matching. */ bool find_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, int charlimit) { int i=pda_m_hlightindex(); int min_index = -1; int max_index = -1; + int index = -1; int cnt = 0; logMessage(LOG_DEBUG, "PDA Device programmer looking for menu text '%s'\n",menuText); - int index = (charlimit == 0)?pda_find_m_index(menuText):pda_find_m_index_case(menuText, charlimit); + if (charlimit == 0) + index = pda_find_m_index(menuText); + else if (charlimit > 0) + index = pda_find_m_index_case(menuText, charlimit); + else if (charlimit == -1) + index = pda_find_m_index_loose(menuText); + + //int index = (charlimit == 0)?pda_find_m_index(menuText):pda_find_m_index_case(menuText, charlimit); if (index < 0) { // No menu, is there a page down. "PDA Line 9 = ^^ MORE __" if (strncasecmp(pda_m_line(9)," ^^ MORE", 10) == 0) { @@ -267,9 +276,18 @@ bool find_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, int charli return waitForPDAMessageHighlight(aq_data, index, 10); } -bool select_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, bool waitForNextMenu) { +bool _select_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, bool waitForNextMenu, bool loose); - if ( find_pda_menu_item(aq_data, menuText, 0) ) { +bool select_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, bool waitForNextMenu){ + return _select_pda_menu_item(aq_data, menuText, waitForNextMenu, false); +} +bool select_pda_menu_item_loose(struct aqualinkdata *aq_data, char *menuText, bool waitForNextMenu){ + return _select_pda_menu_item(aq_data, menuText, waitForNextMenu, true); +} +bool _select_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, bool waitForNextMenu, bool loose) { + + int matchType = loose?-1:0; + if ( find_pda_menu_item(aq_data, menuText, matchType) ) { send_cmd(KEY_PDA_SELECT); logMessage(LOG_DEBUG, "PDA Device programmer selected menu item '%s'\n",menuText); @@ -386,6 +404,17 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { ret = waitForPDAnextMenu(aq_data); } break; + case PM_BOOST: + if (pda_m_type() == PM_HOME) { + ret = select_pda_menu_item(aq_data, "MENU", true); + } else if (pda_m_type() == PM_MAIN) { + ret = select_pda_menu_item_loose(aq_data, "BOOST", true); + } else { + send_cmd(KEY_PDA_BACK); + ret = waitForPDAnextMenu(aq_data); + } + //printf("****MENU SELECT RETURN %d*****\n",ret); + break; case PM_SET_TEMP: if (pda_m_type() == PM_HOME) { ret = select_pda_menu_item(aq_data, "MENU", true); @@ -421,8 +450,7 @@ bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) { return false; break; } - logMessage(LOG_DEBUG, "PDA Device programmer request for menu %d, current %d\n", - menu, pda_m_type()); + logMessage(LOG_DEBUG, "PDA Device programmer request for menu %d, current %d\n", menu, pda_m_type()); } if (pda_m_type() != menu) { logMessage(LOG_ERR, "PDA Device programmer didn't find a requested menu %d, current %d\n", menu, pda_m_type()); @@ -812,6 +840,49 @@ bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val) { //return true; } +bool set_PDA_aqualink_boost(struct aqualinkdata *aq_data, bool val) +{ + if (! goto_pda_menu(aq_data, PM_BOOST)) { + logMessage(LOG_ERR, "Error finding BOOST menu\n"); + return false; + } + + + + // Should be on the START menu item + if (val == true) { // Turn on should just be enter + if (strstr(pda_m_hlight(), "START") != NULL) + send_cmd(KEY_PDA_SELECT); + else { + logMessage(LOG_ERR, "Error finding BOOST START menu\n"); + return false; + } + } else { + /* + // Should be select options PAUSE | RESTART | STOP + int i=0; + for (i=0; i < 6; i++) { + send_cmd(KEY_PDA_DOWN); + waitForPDAMessageTypes(aq_data,CMD_PDA_HIGHLIGHT,CMD_PDA_HIGHLIGHTCHARS,10); + if (strstr(pda_m_hlight(), "STOP") != NULL) { + send_cmd(KEY_PDA_SELECT); + break; + } + } + if (i >= 6) + logMessage(LOG_ERR, "Error finding BOOST STOP menu\n"); + // Should be select options PAUSE | RESTART | STOP + // so press down twice then select + */ + // NSF This is really crap, but can't get above to work, need to come back and check menu items against selections. + send_cmd(KEY_PDA_DOWN); + send_cmd(KEY_PDA_DOWN); + send_cmd(KEY_PDA_SELECT); + } + + return true; +} + bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, bool isPool) { char label[10]; int *cur_val; diff --git a/pda_aq_programmer.h b/pda_aq_programmer.h index 60034b0..8fae057 100644 --- a/pda_aq_programmer.h +++ b/pda_aq_programmer.h @@ -20,6 +20,8 @@ bool get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data); bool get_PDA_aqualink_aux_labels(struct aqualinkdata *aq_data); +bool set_PDA_aqualink_boost(struct aqualinkdata *aq_data, bool val); + //void pda_programming_thread_check(struct aqualinkdata *aq_data); #endif // AQ_PDA_PROGRAMMER_H_ \ No newline at end of file diff --git a/pda_menu.c b/pda_menu.c index 86da6a3..f9f64b9 100644 --- a/pda_menu.c +++ b/pda_menu.c @@ -61,6 +61,7 @@ char *pda_m_line(int index) // return NULL; } +// Find exact menu item int pda_find_m_index(char *text) { int i; @@ -73,6 +74,7 @@ int pda_find_m_index(char *text) return -1; } +// Fine menu item case insensative int pda_find_m_index_case(char *text, int limit) { int i; @@ -85,6 +87,20 @@ int pda_find_m_index_case(char *text, int limit) return -1; } + +// Find menu item very loose +int pda_find_m_index_loose(char *text) +{ + int i; + + for (i = 0; i < PDA_LINES; i++) { + //printf ("+++ Compare '%s' to '%s' index %d\n",text,pda_m_line(i),i); + if (strstr(pda_m_line(i), text) != NULL) + return i; + } + + return -1; +} /* // Same as above but strip whitespace from menu item (NOT text parameter) int pda_find_m_index_swcase(char *text, int limit) @@ -148,7 +164,9 @@ pda_menu_type pda_m_type() } else if (strncasecmp(_menu[0]," LABEL AUX", 12) == 0 && // Will have number ie AUX4 strncasecmp(_menu[2]," CURRENT LABEL ", 16) == 0) { return PM_AUX_LABEL_DEVICE; - } + } else if (strstr(_menu[0],"BOOST")) { // This is bad check, but PDA menus are BOOST | BOOST POOL | BOOST SPA, need to do better. + return PM_BOOST; + } return PM_UNKNOWN; } diff --git a/pda_menu.h b/pda_menu.h index 61c6f9d..2cb0677 100644 --- a/pda_menu.h +++ b/pda_menu.h @@ -27,7 +27,8 @@ typedef enum pda_menu_type { PM_SETTINGS, PM_EQUIPTMENT_CONTROL, PM_EQUIPTMENT_STATUS, - PM_PALM_OPTIONS // This seems to be only older revisions + PM_PALM_OPTIONS, // This seems to be only older revisions + PM_BOOST } pda_menu_type; /* @@ -74,6 +75,7 @@ char *pda_m_line(int index); pda_menu_type pda_m_type(); int pda_find_m_index(char *text); int pda_find_m_index_case(char *text, int limit); +int pda_find_m_index_loose(char *text); //int pda_find_m_index_swcase(char *text, int limit); #endif diff --git a/release/aqualinkd b/release/aqualinkd index 9b1e099..a817c68 100755 Binary files a/release/aqualinkd and b/release/aqualinkd differ diff --git a/release/serial_logger b/release/serial_logger index 2c96361..375dc65 100755 Binary files a/release/serial_logger and b/release/serial_logger differ diff --git a/serial_logger.c b/serial_logger.c index a402f1d..8f92df9 100644 --- a/serial_logger.c +++ b/serial_logger.c @@ -315,7 +315,7 @@ int main(int argc, char *argv[]) { } //packet_length = get_packet(rs_fd, packet_buffer); - packet_length = get_packet_new(rs_fd, packet_buffer); + packet_length = get_packet(rs_fd, packet_buffer); if (packet_length == -1) { // Unrecoverable read error. Force an attempt to reconnect. diff --git a/tmp.rs b/tmp.rs new file mode 100644 index 0000000..25166ee Binary files /dev/null and b/tmp.rs differ diff --git a/version.h b/version.h index f3c620d..bb2d4a4 100644 --- a/version.h +++ b/version.h @@ -1,4 +1,4 @@ #define AQUALINKD_NAME "Aqualink Daemon" -#define AQUALINKD_VERSION "1.3.7" +#define AQUALINKD_VERSION "1.3.8"