/*
* 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 .
*
* 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
#include
#include
#include
#include
#include
#include
#include
#include "aq_serial.h"
#include "utils.h"
#define SLOG_MAX 80
#define PACKET_MAX 600
#define VERSION "serial_logger V1.1"
/*
typedef enum used {
yes,
no,
unknown
} used;
*/
typedef struct serial_id_log {
unsigned char ID;
bool inuse;
} serial_id_log;
bool _keepRunning = true;
unsigned char _goodID[] = {0x0a, 0x0b, 0x08, 0x09};
unsigned char _goodPDAID[] = {0x60, 0x61, 0x62, 0x63};
unsigned char _filter[10];
int _filters=0;
bool _rawlog=false;
bool _playback_file = false;
void intHandler(int dummy) {
_keepRunning = false;
logMessage(LOG_NOTICE, "Stopping!");
if (_playback_file) // If we are reading file, loop is irevelent
exit(0);
}
#define MASTER " <-- Master control panel"
#define SWG " <-- Salt Water Generator (Aquarite mode)"
#define KEYPAD " <-- RS Keypad"
#define SPA_R " <-- Spa remote"
#define AQUA " <-- Aqualink (iAqualink?)"
#define HEATER " <-- LX Heater"
#define ONE_T " <-- Onetouch device"
#define PC_DOCK " <-- PC Interface (RS485 to RS232)"
#define PDA " <-- PDA Remote"
#define EPUMP " <-- Jandy VSP ePump"
#define CHEM " <-- Chemlink"
#define UNKNOWN " <-- Unknown Device"
#define P_VSP " <-- Pentair VSP"
#define P_MASTER " <-- Pentair Master (Probably Jandy RS Control Panel)"
#define P_SWG " <-- Salt Water Generator (Jandy mode)"
#define P_BCAST " <-- Broadcast address"
#define P_RCTL " <-- Remote wired controller"
#define P_RWCTL " <-- Remote wireless controller (Screen Logic)"
#define P_CTL " <-- Pool controller (EasyTouch)"
const char *getDevice(unsigned char ID) {
if (ID >= 0x00 && ID <= 0x03)
return MASTER;
if (ID >= 0x08 && ID <= 0x0B)
return KEYPAD;
if (ID >= 0x50 && ID <= 0x53)
return SWG;
if (ID >= 0x20 && ID <= 0x23)
return SPA_R;
if (ID >= 0x30 && ID <= 0x33)
return AQUA;
if (ID >= 0x38 && ID <= 0x3B)
return HEATER;
if (ID >= 0x40 && ID <= 0x43)
return ONE_T;
if (ID >= 0x58 && ID <= 0x5B)
return PC_DOCK;
if (ID >= 0x60 && ID <= 0x63)
return PDA;
//if (ID >= 0x70 && ID <= 0x73)
if (ID >= 0x78 && ID <= 0x7B)
return EPUMP;
if (ID >= 0x80 && ID <= 0x83)
return CHEM;
//if (ID == 0x08)
// return KEYPAD;
return UNKNOWN;
}
const char *getPentairDevice(unsigned char ID) {
if (ID >= 0x60 && ID <= 0x6F)
return P_VSP;
if (ID == 0x02)
return P_SWG;
if (ID == 0x10)
return P_MASTER;
if (ID == 0x0F)
return P_BCAST;
if (ID == 0x10)
return P_CTL;
if (ID == 0x20)
return P_RCTL;
if (ID == 0x22)
return P_RWCTL;
return UNKNOWN;
}
void advance_cursor() {
static int pos=0;
char cursor[4]={'/','-','\\','|'};
printf("%c\b", cursor[pos]);
fflush(stdout);
pos = (pos+1) % 4;
}
bool canUse(unsigned char ID) {
int i;
for (i = 0; i < 4; i++) {
if (ID == _goodID[i])
return true;
}
for (i = 0; i < 4; i++) {
if (ID == _goodPDAID[i])
return true;
}
return false;
}
char* canUseExtended(unsigned char ID) {
int i;
for (i = 0; i < 4; i++) {
if (ID == _goodID[i])
return " <-- can use for Aqualinkd";
}
for (i = 0; i < 4; i++) {
if (ID == _goodPDAID[i])
return " <-- can use for Aqualinkd (PDA mode only)";
}
return "";
}
void printHex(char *pk, int length)
{
int i=0;
for (i=0;i (log # packets) & -i & -r (raw) ie:=\n\t%s /dev/ttyUSB0 -d -p 1000 -i 0x08\n\n", argv[0]);
return 1;
}
for (i = 2; i < argc; i++) {
if (strcmp(argv[i], "-d") == 0) {
logLevel = LOG_DEBUG;
} else if (strcmp(argv[i], "-p") == 0 && i+1 < argc) {
logPackets = atoi(argv[i+1]);
} else if (strcmp(argv[i], "-i") == 0 && i+1 < argc) {
unsigned int n;
sscanf(argv[i+1], "0x%2x", &n);
_filter[_filters] = n;
_filters++;
printf("Add filter %i 0x%02hhx\n",_filters, _filter[_filters-1]);
logLevel = LOG_DEBUG; // no point in filtering on ID if we're not going to print it.
} else if (strcmp(argv[i], "-r") == 0) {
_rawlog = true;
logLevel = LOG_DEBUG;
} else if (strcmp(argv[i], "-f") == 0) {
_playback_file = true;
}
}
setLoggingPrms(logLevel, false, false, NULL);
if (_playback_file) {
rs_fd = open(argv[1], O_RDONLY | O_NOCTTY | O_NONBLOCK | O_NDELAY);
if (rs_fd < 0) {
logMessage(LOG_ERR, "Unable to open file: %s\n", argv[1]);
displayLastSystemError(argv[1]);
return -1;
}
} else {
rs_fd = init_serial_port(argv[1]);
}
signal(SIGINT, intHandler);
signal(SIGTERM, intHandler);
logMessage(LOG_NOTICE, "Logging serial information!\n");
if (logLevel < LOG_DEBUG)
printf("Please wait.");
while (_keepRunning == true) {
if (rs_fd < 0) {
logMessage(LOG_ERR, "ERROR, serial port disconnect\n");
}
//packet_length = get_packet(rs_fd, packet_buffer);
packet_length = get_packet(rs_fd, packet_buffer);
if (packet_length == -1) {
// Unrecoverable read error. Force an attempt to reconnect.
logMessage(LOG_ERR, "ERROR, on serial port\n");
_keepRunning = false;
} else if (packet_length == 0) {
// Nothing read
} else if (packet_length > 0) {
//logMessage(LOG_DEBUG_SERIAL, "Received Packet for ID 0x%02hhx of type %s\n", packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length));
if (logLevel > LOG_NOTICE)
printPacket(lastID, packet_buffer, packet_length);
if (getProtocolType(packet_buffer) == PENTAIR) {
found = false;
for (i = 0; i <= pent_sindex; i++) {
if (pent_slog[i].ID == packet_buffer[PEN_PKT_FROM]) {
found = true;
break;
}
}
if (found == false) {
pent_slog[pent_sindex].ID = packet_buffer[PEN_PKT_FROM];
pent_slog[pent_sindex].inuse = true;
pent_sindex++;
}
} else {
if (packet_buffer[PKT_DEST] != DEV_MASTER) {
found = false;
for (i = 0; i <= sindex; i++) {
if (slog[i].ID == packet_buffer[PKT_DEST]) {
found = true;
break;
}
}
if (found != true && sindex < SLOG_MAX) {
slog[sindex].ID = packet_buffer[PKT_DEST];
slog[sindex].inuse = false;
sindex++;
}
}
if (packet_buffer[PKT_DEST] == DEV_MASTER /*&& packet_buffer[PKT_CMD] == CMD_ACK*/) {
//logMessage(LOG_NOTICE, "ID is in use 0x%02hhx %x\n", lastID, lastID);
for (i = 0; i <= sindex; i++) {
if (slog[i].ID == lastID) {
slog[i].inuse = true;
break;
}
}
}
lastID = packet_buffer[PKT_DEST];
}
received_packets++;
// NSF TESTING
/*
if (packet_buffer[PKT_DEST] == 0x40) {
static int hex = 0;
//printf("Sent ack\n");
//printf("Sent ack hex 0x%02hhx\n",(unsigned char)hex);
//send_extended_ack (rs_fd, 0x8b, (unsigned char)hex);
send_extended_ack (rs_fd, 0x8b, 0x00);
hex++;
}*/
// NSF
}
if (logPackets != 0 && received_packets >= logPackets) {
_keepRunning = false;
}
if (logLevel < LOG_DEBUG)
advance_cursor();
//sleep(1);
}
logMessage(LOG_DEBUG, "\n\n");
if (logLevel < LOG_DEBUG)
printf("\n\n");
if (sindex >= SLOG_MAX)
logMessage(LOG_ERR, "Ran out of storage, some ID's were not captured, please increase SLOG_MAX and recompile\n");
logMessage(LOG_NOTICE, "Jandy ID's found\n");
for (i = 0; i < sindex; i++) {
//logMessage(LOG_NOTICE, "ID 0x%02hhx is %s %s\n", slog[i].ID, (slog[i].inuse == true) ? "in use" : "not used",
// (slog[i].inuse == false && canUse(slog[i].ID) == true)? " <-- can use for Aqualinkd" : "");
if (logLevel >= LOG_DEBUG || slog[i].inuse == true || canUse(slog[i].ID) == true) {
logMessage(LOG_NOTICE, "ID 0x%02hhx is %s %s\n", slog[i].ID, (slog[i].inuse == true) ? "in use" : "not used",
(slog[i].inuse == false)?canUseExtended(slog[i].ID):getDevice(slog[i].ID));
}
}
if (pent_sindex > 0) {
logMessage(LOG_NOTICE, "\n\n");
logMessage(LOG_NOTICE, "Pentair ID's found\n");
}
for (i=0; i < pent_sindex; i++) {
logMessage(LOG_NOTICE, "ID 0x%02hhx is %s %s\n", pent_slog[i].ID, (pent_slog[i].inuse == true) ? "in use" : "not used",
(pent_slog[i].inuse == false)?canUseExtended(pent_slog[i].ID):getPentairDevice(pent_slog[i].ID));
}
logMessage(LOG_NOTICE, "\n\n");
return 0;
}