diff --git a/Makefile b/Makefile
index 15fd2ff..c77d79f 100755
--- a/Makefile
+++ b/Makefile
@@ -29,7 +29,7 @@ INCLUDES = -I/nas/data/Development/Raspberry/aqualink/aqualinkd
# Add inputs and outputs from these tool invocations to the build variables
# define the C source files
-SRCS = aqualinkd.c utils.c config.c aq_serial.c init_buttons.c aq_programmer.c net_services.c json_messages.c pda_menu.c mongoose.c
+SRCS = aqualinkd.c utils.c config.c aq_serial.c init_buttons.c aq_programmer.c net_services.c json_messages.c pda.c pda_menu.c pda_aq_programmer.c mongoose.c
SL_SRC = serial_logger.c aq_serial.c utils.c
PDA_SRC = pda_test.c pda_menu.c aq_serial.c utils.c
diff --git a/README.md b/README.md
index 54d65ef..5275094 100644
--- a/README.md
+++ b/README.md
@@ -59,15 +59,26 @@ Designed to mimic AqualinkRS6 All Button keypad, and just like the keypad you ca
## All Web interfaces.
-* http://aqualink.ip/ <- (New UI)
-* http://aqualink.ip/old <- (If you prefer the old UI, this is not maintained)
-* http://aqualink.ip/simple.html <- (Anothr opion if you don't like the above)
+* http://aqualink.ip/ <- (Standard WEB UI
+* 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.0
+* Large update for PDA only control panels (Majority of this is ballle98 work)
+* Can distinguish between AquaPalm and PDA supported control panels.
+* PDA Freeze & Heater setpoints now supported.
+* Added PDA Sleep mode so AqualinkD can work inconjunction with a real Jandy PDA.
+* Speeded up many PDA functions.
+* Fixed many PDA bugs.
+* Non PDA specific updates :-
+* Can get button labels from control panel (not in PDA mode)
+* RS485 Logging so users can submit information on Variable Speed Pumps & other devices for future support.
+* Force SWG status on startup, rather than wait for pump to turn on.
+* General bug fixes and improved code in many areas.
## Update in Release 1.2.6f
* Solution to overcome bug in Mosquitto 1.6.
* Fixed Salt Water Generator when % was set to 0.
-* Added support for different SWG % for pool & spa.
+* Added support for different SWG % for pool & spa. (SWG reports and sets the mode that's currently active)
* Increased speed of SWG messages.
* Few other bug fixes (Thanks to ballle98)
## Update in Release 1.2.6e (This is a quick update, please only use if you need one of the items below.)
diff --git a/aq_programmer.c b/aq_programmer.c
index b6b5185..97b883e 100644
--- a/aq_programmer.c
+++ b/aq_programmer.c
@@ -26,12 +26,14 @@
#include "utils.h"
#include "aq_programmer.h"
#include "aq_serial.h"
+#include "pda.h"
#include "pda_menu.h"
#include "init_buttons.h"
+#include "pda_aq_programmer.h"
bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string);
bool select_menu_item(struct aqualinkdata *aq_data, char* item_string);
-void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data);
+//void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data);
void cancel_menu(struct aqualinkdata *aq_data);
@@ -43,17 +45,18 @@ void *get_aqualink_pool_spa_heater_temps( void *ptr );
void *get_aqualink_programs( void *ptr );
void *get_freeze_protect_temp( void *ptr );
void *get_aqualink_diag_model( void *ptr );
+void *get_aqualink_aux_labels( void *ptr );
void *threadded_send_cmd( void *ptr );
void *set_aqualink_light_colormode( void *ptr );
void *set_aqualink_PDA_init( void *ptr );
void *set_aqualink_SWG( void *ptr );
-void *get_aqualink_PDA_device_status( void *ptr );
-void *set_aqualink_PDA_device_on_off( void *ptr );
+//void *get_aqualink_PDA_device_status( void *ptr );
+//void *set_aqualink_PDA_device_on_off( void *ptr );
bool waitForButtonState(struct aqualinkdata *aq_data, aqkey* button, aqledstate state, int numMessageReceived);
-bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived);
+//bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived);
bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* message2, int numMessageReceived);
bool push_aq_cmd(unsigned char cmd);
@@ -204,12 +207,18 @@ void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data)
struct programmingThreadCtrl *programmingthread = malloc(sizeof(struct programmingThreadCtrl));
if (pda_mode() == true) {
+ pda_reset_sleep();
if (type != AQ_PDA_INIT &&
+ type != AQ_PDA_WAKE_INIT &&
type != AQ_PDA_DEVICE_STATUS &&
- type != AQ_PDA_DEVICE_ON_OFF) {
+ type != AQ_SET_POOL_HEATER_TEMP &&
+ type != AQ_SET_SPA_HEATER_TEMP &&
+ type != AQ_SET_SWG_PERCENT &&
+ type != AQ_PDA_DEVICE_ON_OFF &&
+ type != AQ_GET_POOL_SPA_HEATER_TEMPS ) {
logMessage(LOG_ERR, "Selected Programming mode '%d' not supported with PDA mode control panel\n",type);
return;
- }
+ }
}
programmingthread->aq_data = aq_data;
@@ -289,6 +298,12 @@ void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data)
return;
}
break;
+ case AQ_PDA_WAKE_INIT:
+ if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_PDA_wakeinit, (void*)programmingthread) < 0) {
+ logMessage (LOG_ERR, "could not create thread\n");
+ return;
+ }
+ break;
case AQ_SET_SWG_PERCENT:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_SWG, (void*)programmingthread) < 0) {
logMessage (LOG_ERR, "could not create thread\n");
@@ -306,6 +321,12 @@ void aq_programmer(program_type type, char *args, struct aqualinkdata *aq_data)
logMessage (LOG_ERR, "could not create thread\n");
return;
}
+ break;
+ case AQ_GET_AUX_LABELS:
+ if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_aux_labels, (void*)programmingthread) < 0) {
+ logMessage (LOG_ERR, "could not create thread\n");
+ return;
+ }
break;
default:
logMessage (LOG_ERR, "Don't understand thread type\n");
@@ -478,17 +499,13 @@ void *set_aqualink_SWG( void *ptr )
int val = atoi((char*)threadCtrl->thread_args);
val = setpoint_check(SWG_SETPOINT, val, aq_data);
- // Just recheck it's in multiple of 5.
- /*
- if (0 != (val % 5) )
- val = ((val + 5) / 10) * 10;
- if (val > SWG_PERCENT_MAX) {
- val = SWG_PERCENT_MAX;
- } else if ( val < SWG_PERCENT_MIN) {
- val = SWG_PERCENT_MIN;
+ if (pda_mode() == true) {
+ set_PDA_aqualink_SWG_setpoint(aq_data, val);
+ cleanAndTerminateThread(threadCtrl);
+ return ptr;
}
- */
+
logMessage(LOG_DEBUG, "programming SWG percent to %d\n", val);
if ( select_menu_item(aq_data, "SET AQUAPURE") != true ) {
@@ -541,258 +558,37 @@ void *set_aqualink_SWG( void *ptr )
return ptr;
}
-bool select_pda_main_menu(struct aqualinkdata *aq_data)
-{
- int i=0;
- // Check to see if we are at the main menu
- if (pda_m_type() == PM_MAIN) {
- return true;
- }
- // First send back
- send_cmd(KEY_PDA_BACK, aq_data);
- while (_pgm_command != NUL) {
- delay(500);
- if (i++ > 6) return false;
- }
- //delay(1000);
- i=0;
- while (pda_m_type() != PM_MAIN) {
- delay(500);
- if (i++ > 6) return false;
- }
- return true;
-}
-bool wait_pda_selected_item()
-{
- int i=0;
-
- i=0;
- while (pda_m_hlightindex() == -1){
- if (i++ > 10)
- break;
- delay(100);
- }
-
- if (pda_m_hlightindex() == -1)
- return false;
- else
- return true;
-}
-
-bool select_pda_main_menu_item(struct aqualinkdata *aq_data, pda_menu_type menu_item)
-{
- int i=0;
- char *menu;
-
- if (! select_pda_main_menu(aq_data))
- return false;
-
- logMessage(LOG_DEBUG, "PDA Device programmer at main menu\n");
-
- if (menu_item == PM_MAIN)
- return true;
- else if (menu_item == PM_SETTINGS)
- menu = "MENU";
- else if (menu_item == PM_EQUIPTMENT_CONTROL)
- menu = "EQUIPMENT ON/OFF";
- else
- return false;
-
- if (!wait_pda_selected_item()){
- logMessage(LOG_ERR, "PDA Device programmer didn't find a selected item\n");
- return false;
- }
-
- while ( strncmp(pda_m_hlight(), menu, strlen(menu)) != 0 ) {
- if (_pgm_command == NUL) {
- send_cmd(KEY_PDA_DOWN, aq_data);
- logMessage(LOG_DEBUG, "PDA Device programmer selected sub menu\n");
- waitForMessage(aq_data, NULL, 1);
- }
- if (i++ > (PDA_LINES * 2))
- return false;
- delay(500);
- }
-
- send_cmd(KEY_PDA_SELECT, aq_data);
- while (_pgm_command != NUL) { delay(500); }
-
- return true;
- /*
- send_cmd(KEY_PDA_DOWN, aq_data);
- while (_pgm_command != NUL) { delay(500); }
- */
-}
-
-void *set_aqualink_PDA_device_on_off( void *ptr )
+void *get_aqualink_aux_labels( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
- int i=0;
- int found;
- waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_DEVICE_STATUS);
-
- char *buf = (char*)threadCtrl->thread_args;
- int device = atoi(&buf[0]);
- int state = atoi(&buf[5]);
+ waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_AUX_LABELS);
- if (device < 0 || device > TOTAL_BUTTONS) {
- logMessage(LOG_ERR, "PDA Device On/Off :- bad device number '%d'\n",device);
+ if ( select_menu_item(aq_data, "REVIEW") != true ) {
+ logMessage(LOG_WARNING, "Could not select REVIEW menu\n");
+ cancel_menu(aq_data);
+ cleanAndTerminateThread(threadCtrl);
+ return ptr;
+ }
+
+ if (select_sub_menu_item(aq_data, "AUX LABELS") != true) {
+ logMessage(LOG_WARNING, "Could not select AUX LABELS menu\n");
+ cancel_menu(aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
- logMessage(LOG_INFO, "PDA Device On/Off, device '%s', state %d\n",aq_data->aqbuttons[device].pda_label,state);
-
- //printf("DEVICE LABEL = %s\n",aq_data->aqbuttons[device].pda_label);
-
- if (! select_pda_main_menu_item(aq_data, PM_EQUIPTMENT_CONTROL)) {
- logMessage(LOG_ERR, "PDA Device On/Off :- can't find main menu\n");
- cleanAndTerminateThread(threadCtrl);
- return ptr;
- }
-/*
- i=0;
- while (pda_m_hlightindex() == -1){
- if (i++ > 10)
- break;
- delay(100);
- }
-*/
- delay(500);
-printf("Wait for select\n");
- if (!wait_pda_selected_item()){
- logMessage(LOG_ERR, "PDA Device programmer didn't find a selected item\n");
- return false;
- }
-printf("End wait select\n");
- i=0;
- char labelBuff[AQ_MSGLEN];
- strncpy(labelBuff, pda_m_hlight(), AQ_MSGLEN-4);
- labelBuff[AQ_MSGLEN-4] = 0;
-
- while ( (found = strcasecmp(stripwhitespace(labelBuff), aq_data->aqbuttons[device].pda_label)) != 0 ) {
- if (_pgm_command == NUL) {
- send_cmd(KEY_PDA_DOWN, aq_data);
- //printf("*** Send Down for %s ***\n",pda_m_hlight());
- waitForMessage(aq_data, NULL, 1);
- }
- if (i++ > (PDA_LINES * 2)) {
- break;
- }
- delay(500);
- strncpy(labelBuff, pda_m_hlight(), AQ_MSGLEN-4);
- labelBuff[AQ_MSGLEN-4] = 0;
- }
-
- if (found == 0) {
- //printf("*** FOUND ITEM %s ***\n",pda_m_hlight());
- if (aq_data->aqbuttons[device].led->state != state) {
- //printf("*** Select State ***\n");
- logMessage(LOG_INFO, "PDA Device On/Off, found device '%s', changing state\n",aq_data->aqbuttons[device].pda_label,state);
- send_cmd(KEY_PDA_SELECT, aq_data);
- while (_pgm_command != NUL) { delay(500); }
- } else {
- logMessage(LOG_INFO, "PDA Device On/Off, found device '%s', not changing state, is same\n",aq_data->aqbuttons[device].pda_label,state);
- }
- } else {
- //printf("*** NOT FOUND ITEM ***\n");
- logMessage(LOG_ERR, "PDA Device On/Off, device '%s' not found\n",aq_data->aqbuttons[device].pda_label);
- }
-
- select_pda_main_menu_item(aq_data, PM_MAIN);
- //while (_pgm_command != NUL) { delay(500); }
+ waitForMessage(aq_data, NULL, 5); // Receive 5 messages
cleanAndTerminateThread(threadCtrl);
// just stop compiler error, ptr is not valid as it's just been freed
return ptr;
}
-void *get_aqualink_PDA_device_status( void *ptr )
-{
- struct programmingThreadCtrl *threadCtrl;
- threadCtrl = (struct programmingThreadCtrl *) ptr;
- struct aqualinkdata *aq_data = threadCtrl->aq_data;
- int i;
-
- waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_DEVICE_STATUS);
-
- //int val = atoi((char*)threadCtrl->thread_args);
-
- logMessage(LOG_DEBUG, "PDA Device Status\n");
-
- if (! select_pda_main_menu_item(aq_data, PM_EQUIPTMENT_CONTROL)) {
- logMessage(LOG_ERR, "PDA Device Status :- can't find main menu\n");
- cleanAndTerminateThread(threadCtrl);
- return ptr;
- }
- //select_pda_main_menu_item(aq_data, "EQUIPMENT ON/OFF");
-
- // Just loop over all the dvices 18 times should do it.
- for (i=0; i < 18; i++) {
- send_cmd(KEY_PDA_DOWN, aq_data);
- while (_pgm_command != NUL) { delay(100); }
- }
-
- //printf("*** GET MAIN MENU ***\n");
-
- select_pda_main_menu_item(aq_data, PM_MAIN);
-
- //printf("*** FINISHED ***\n");
- /*
- send_cmd(KEY_PDA_BACK, aq_data);
- while (_pgm_command != NUL) { delay(500); }
- */
- cleanAndTerminateThread(threadCtrl);
-
- // just stop compiler error, ptr is not valid as it's just been freed
- return ptr;
-}
-
-void *set_aqualink_PDA_init( void *ptr )
-{
- struct programmingThreadCtrl *threadCtrl;
- threadCtrl = (struct programmingThreadCtrl *) ptr;
- struct aqualinkdata *aq_data = threadCtrl->aq_data;
- int i=0;
-
- waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_INIT);
-
- //int val = atoi((char*)threadCtrl->thread_args);
-
- //logMessage(LOG_DEBUG, "PDA Init\n", val);
-
- logMessage(LOG_DEBUG, "PDA Init\n");
-
- if (! select_pda_main_menu_item(aq_data, PM_EQUIPTMENT_CONTROL)) {
- logMessage(LOG_ERR, "PDA Init :- can't find main menu\n");
- cleanAndTerminateThread(threadCtrl);
- return ptr;
- }
- //select_pda_main_menu_item(aq_data, "EQUIPMENT ON/OFF");
-
- // Just loop over all the dvices 20 times should do it.
- for (i=0; i < 18; i++) {
- send_cmd(KEY_PDA_DOWN, aq_data);
- while (_pgm_command != NUL) { delay(500); }
- }
-
- select_pda_main_menu_item(aq_data, PM_MAIN);
-
- printf("*** PDA Init :- add code to find setpoints ***\n");
-
- // Run through menu and find freeze setpoints / heater setpoints etc.
-
- cleanAndTerminateThread(threadCtrl);
-
- // just stop compiler error, ptr is not valid as it's just been freed
- return ptr;
-}
-
void *set_aqualink_light_colormode( void *ptr )
{
@@ -894,6 +690,13 @@ void *set_aqualink_pool_heater_temps( void *ptr )
}
*/
val = setpoint_check(POOL_HTR_SETOINT, val, aq_data);
+
+ if (pda_mode() == true) {
+ set_PDA_aqualink_heater_setpoint(aq_data, val, true);
+ cleanAndTerminateThread(threadCtrl);
+ return ptr;
+ }
+
// NSF IF in TEMP1 / TEMP2 mode, we need C range of 1 to 40 is 2 to 40 for TEMP1, 1 to 39 TEMP2
if (aq_data->single_device == true ){
name = "TEMP1";
@@ -960,6 +763,13 @@ void *set_aqualink_spa_heater_temps( void *ptr )
val = MEATER_MIN;
}*/
val = setpoint_check(SPA_HTR_SETOINT, val, aq_data);
+
+ if (pda_mode() == true) {
+ set_PDA_aqualink_heater_setpoint(aq_data, val, true);
+ cleanAndTerminateThread(threadCtrl);
+ return ptr;
+ }
+
// NSF IF in TEMP1 / TEMP2 mode, we need C range of 1 to 40 is 2 to 40 for TEMP1, 1 to 39 TEMP2
if (aq_data->single_device == true ){
@@ -1148,6 +958,14 @@ void *get_aqualink_pool_spa_heater_temps( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_POOL_SPA_HEATER_TEMPS);
logMessage(LOG_NOTICE, "Getting pool & spa heat setpoints from aqualink\n");
+ if (pda_mode() == true) {
+ if (!get_PDA_aqualink_pool_spa_heater_temps(aq_data)) {
+ logMessage(LOG_ERR, "Error Getting PDA pool & spa heat protection setpoints\n");
+ }
+ cleanAndTerminateThread(threadCtrl);
+ return ptr;
+ }
+
if ( select_menu_item(aq_data, "REVIEW") != true ) {
logMessage(LOG_WARNING, "Could not select REVIEW menu\n");
cancel_menu(aq_data);
@@ -1182,6 +1000,15 @@ void *get_freeze_protect_temp( void *ptr )
waitForSingleThreadOrTerminate(threadCtrl, AQ_GET_FREEZE_PROTECT_TEMP);
logMessage(LOG_NOTICE, "Getting freeze protection setpoints\n");
+
+ if (pda_mode() == true) {
+ if (! get_PDA_freeze_protect_temp(aq_data)) {
+ logMessage(LOG_ERR, "Error Getting PDA freeze protection setpoints\n");
+ }
+ cleanAndTerminateThread(threadCtrl);
+ return ptr;
+ }
+
if ( select_menu_item(aq_data, "REVIEW") != true ) {
logMessage(LOG_WARNING, "Could not select REVIEW menu\n");
cancel_menu(aq_data);
diff --git a/aq_programmer.h b/aq_programmer.h
index 75cad40..1335402 100644
--- a/aq_programmer.h
+++ b/aq_programmer.h
@@ -8,7 +8,7 @@
#define HEATER_MAX_F 104
#define HEATER_MIN_F 36
#define FREEZE_PT_MAX_F 42
-#define FREEZE_PT_MIN_F 36
+#define FREEZE_PT_MIN_F 34
#define HEATER_MAX_C 40
#define HEATER_MIN_C 0
@@ -36,7 +36,9 @@ typedef enum {
AQ_PDA_INIT,
AQ_SET_SWG_PERCENT,
AQ_PDA_DEVICE_STATUS,
- AQ_PDA_DEVICE_ON_OFF
+ AQ_PDA_DEVICE_ON_OFF,
+ AQ_GET_AUX_LABELS,
+ AQ_PDA_WAKE_INIT
} program_type;
struct programmingThreadCtrl {
@@ -64,4 +66,12 @@ unsigned char pop_aq_cmd(struct aqualinkdata *aq_data);
int get_aq_cmd_length();
int setpoint_check(int type, int value, struct aqualinkdata *aqdata);
+
+// These shouldn't be here, but just for the PDA AQ PROGRAMMER
+void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data);
+bool push_aq_cmd(unsigned char cmd);
+void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, program_type type);
+void cleanAndTerminateThread(struct programmingThreadCtrl *threadCtrl);
+bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived);
+
#endif
diff --git a/aq_serial.c b/aq_serial.c
index bf93d23..9d56287 100644
--- a/aq_serial.c
+++ b/aq_serial.c
@@ -125,6 +125,9 @@ const char* get_packet_type(unsigned char* packet , int length)
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;
diff --git a/aq_serial.h b/aq_serial.h
index 292e91f..a4946ee 100644
--- a/aq_serial.h
+++ b/aq_serial.h
@@ -53,8 +53,8 @@
#define KEY_PDA_DOWN 0x05
#define KEY_PDA_BACK 0x02
#define KEY_PDA_SELECT 0x04
-#define KEY_PDA_PGUP 0x01
-#define KEY_PDA_PGDN 0x03
+//#define KEY_PDA_PGUP 0x01 // Think these are hot key #1
+//#define KEY_PDA_PGDN 0x03 // Think these are hot key #2
/* KEY/BUTTON CODES */
#define KEY_PUMP 0x02
@@ -178,6 +178,7 @@ SPILLOVER IS DISABLED WHILE SPA IS ON
#define SWG_STATUS_CHECK_PCB 0x80 // check PCB 0x80
#define CMD_PDA_0x05 0x05
+#define CMD_PDA_0x1B 0x1b
#define CMD_PDA_HIGHLIGHT 0x08
#define CMD_PDA_CLEAR 0x09
#define CMD_PDA_SHIFTLINES 0x0F
diff --git a/aqualink.h b/aqualink.h
index c983c1c..2c7511c 100644
--- a/aqualink.h
+++ b/aqualink.h
@@ -97,6 +97,7 @@ struct aqualinkdata
bool simulate_panel;
aqledstate service_mode_state;
aqledstate frz_protect_state;
+ unsigned char last_packet_type;
//bool last_msg_was_status;
//bool ar_swg_connected;
};
diff --git a/aqualinkd.c b/aqualinkd.c
index ef491ae..360f0f5 100644
--- a/aqualinkd.c
+++ b/aqualinkd.c
@@ -14,7 +14,6 @@
* https://github.com/sfeakes/aqualinkd
*/
-
#define _GNU_SOURCE 1 // for strcasestr & strptime
#define __USE_XOPEN 1
#include
@@ -27,7 +26,7 @@
#include
#include
-#include // Need GNU_SOURCE & XOPEN defined for strptime
+#include // Need GNU_SOURCE & XOPEN defined for strptime
#include "mongoose.h"
#include "aqualink.h"
@@ -38,99 +37,101 @@
#include "aq_programmer.h"
#include "net_services.h"
#include "pda_menu.h"
+#include "pda.h"
#include "version.h"
-
#define DEFAULT_CONFIG_FILE "./aqualinkd.conf"
-
static volatile bool _keepRunning = true;
static struct aqconfig _config_parameters;
static struct aqualinkdata _aqualink_data;
-
void main_loop();
-void intHandler(int dummy) {
+void intHandler(int dummy)
+{
_keepRunning = false;
logMessage(LOG_NOTICE, "Stopping!");
}
-
void processLEDstate()
{
- int i=0;
+ int i = 0;
int byte;
int bit;
-
- for (byte=0;byte<5;byte++)
+
+ for (byte = 0; byte < 5; byte++)
{
- for (bit=0; bit<8; bit+=2)
+ for (bit = 0; bit < 8; bit += 2)
{
- if ( ((_aqualink_data.raw_status[byte] >> (bit+1) ) & 1) == 1 )
- _aqualink_data.aqualinkleds[i].state = FLASH;
- else if ( ((_aqualink_data.raw_status[byte] >> bit) & 1) == 1 )
+ if (((_aqualink_data.raw_status[byte] >> (bit + 1)) & 1) == 1)
+ _aqualink_data.aqualinkleds[i].state = FLASH;
+ else if (((_aqualink_data.raw_status[byte] >> bit) & 1) == 1)
_aqualink_data.aqualinkleds[i].state = ON;
else
_aqualink_data.aqualinkleds[i].state = OFF;
-
+
//logMessage(LOG_DEBUG,"Led %d state %d",i+1,_aqualink_data.aqualinkleds[i].state);
i++;
}
}
// Reset enabled state for heaters, as they take 2 led states
- if ( _aqualink_data.aqualinkleds[POOL_HTR_LED_INDEX-1].state == OFF && _aqualink_data.aqualinkleds[POOL_HTR_LED_INDEX].state == ON)
- _aqualink_data.aqualinkleds[POOL_HTR_LED_INDEX-1].state = ENABLE;\
-
- if ( _aqualink_data.aqualinkleds[SPA_HTR_LED_INDEX-1].state == OFF && _aqualink_data.aqualinkleds[SPA_HTR_LED_INDEX].state == ON)
- _aqualink_data.aqualinkleds[SPA_HTR_LED_INDEX-1].state = ENABLE;
-
- if ( _aqualink_data.aqualinkleds[SOLAR_HTR_LED_INDEX-1].state == OFF && _aqualink_data.aqualinkleds[SOLAR_HTR_LED_INDEX].state == ON)
- _aqualink_data.aqualinkleds[SOLAR_HTR_LED_INDEX-1].state = ENABLE;
-/*
+ if (_aqualink_data.aqualinkleds[POOL_HTR_LED_INDEX - 1].state == OFF && _aqualink_data.aqualinkleds[POOL_HTR_LED_INDEX].state == ON)
+ _aqualink_data.aqualinkleds[POOL_HTR_LED_INDEX - 1].state = ENABLE;
+
+ if (_aqualink_data.aqualinkleds[SPA_HTR_LED_INDEX - 1].state == OFF && _aqualink_data.aqualinkleds[SPA_HTR_LED_INDEX].state == ON)
+ _aqualink_data.aqualinkleds[SPA_HTR_LED_INDEX - 1].state = ENABLE;
+
+ if (_aqualink_data.aqualinkleds[SOLAR_HTR_LED_INDEX - 1].state == OFF && _aqualink_data.aqualinkleds[SOLAR_HTR_LED_INDEX].state == ON)
+ _aqualink_data.aqualinkleds[SOLAR_HTR_LED_INDEX - 1].state = ENABLE;
+ /*
for (i=0; i < TOTAL_BUTTONS; i++) {
logMessage(LOG_NOTICE, "%s = %d", _aqualink_data.aqbuttons[i].name, _aqualink_data.aqualinkleds[i].state);
}
*/
}
-
bool checkAqualinkTime()
{
static time_t last_checked;
- time_t now = time(0); // get time now
+ time_t now = time(0); // get time now
int time_difference;
struct tm aq_tm;
time_t aqualink_time;
-
+
time_difference = (int)difftime(now, last_checked);
- if (time_difference < TIME_CHECK_INTERVAL) {
+ if (time_difference < TIME_CHECK_INTERVAL)
+ {
logMessage(LOG_DEBUG, "time not checked, will check in %d seconds", TIME_CHECK_INTERVAL - time_difference);
return true;
- } else {
+ }
+ else
+ {
last_checked = now;
//return false;
}
-
+
char datestr[DATE_STRING_LEN];
strcpy(&datestr[0], _aqualink_data.date);
strcpy(&datestr[12], " ");
strcpy(&datestr[13], _aqualink_data.time);
-
- if (strptime(datestr, "%m/%d/%y %a %I:%M %p", &aq_tm) == NULL) {
+
+ if (strptime(datestr, "%m/%d/%y %a %I:%M %p", &aq_tm) == NULL)
+ {
logMessage(LOG_ERR, "Could not convert RS time string '%s'", datestr);
last_checked = (time_t)NULL;
return true;
}
- aq_tm.tm_isdst = -1; // Force mktime to use local timezone
+ aq_tm.tm_isdst = -1; // Force mktime to use local timezone
aqualink_time = mktime(&aq_tm);
time_difference = (int)difftime(now, aqualink_time);
-
- logMessage(LOG_INFO, "Aqualink time is off by %d seconds...\n", time_difference);
-
- if(abs(time_difference) <= ACCEPTABLE_TIME_DIFF) {
+
+ logMessage(LOG_INFO, "Aqualink time is off by %d seconds...\n", time_difference);
+
+ if (abs(time_difference) <= ACCEPTABLE_TIME_DIFF)
+ {
// Time difference is less than or equal to 90 seconds (1 1/2 minutes).
// Set the return value to true.
return true;
@@ -171,24 +172,27 @@ void queueGetProgramData()
// Init string good time to get setpoints
aq_programmer(AQ_GET_POOL_SPA_HEATER_TEMPS, NULL, &_aqualink_data);
aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, &_aqualink_data);
+ if (_config_parameters.use_panel_aux_labels == true)
+ {
+ aq_programmer(AQ_GET_AUX_LABELS, NULL, &_aqualink_data);
+ }
//aq_programmer(AQ_GET_PROGRAMS, NULL, &_aqualink_data); // only displays to log at present, also seems to confuse getting set_points
}
void setUnits(char *msg)
{
- logMessage(LOG_DEBUG, "Getting temp units from message %s, looking at %c", msg, msg[strlen(msg)-1]);
+ logMessage(LOG_DEBUG, "Getting temp units from message %s, looking at %c", msg, msg[strlen(msg) - 1]);
- if (msg[strlen(msg)-1] == 'F')
- _aqualink_data.temp_units = FAHRENHEIT;
- else if (msg[strlen(msg)-1] == 'C')
- _aqualink_data.temp_units = CELSIUS;
+ if (msg[strlen(msg) - 1] == 'F')
+ _aqualink_data.temp_units = FAHRENHEIT;
+ else if (msg[strlen(msg) - 1] == 'C')
+ _aqualink_data.temp_units = CELSIUS;
else
- _aqualink_data.temp_units = UNKNOWN;
+ _aqualink_data.temp_units = UNKNOWN;
- logMessage(LOG_INFO, "Temp Units set to %d (F=0, C=1, Unknown=3)", _aqualink_data.temp_units);
+ logMessage(LOG_INFO, "Temp Units set to %d (F=0, C=1, Unknown=3)", _aqualink_data.temp_units);
}
-
void processMessage(char *message)
{
char *msg;
@@ -196,39 +200,42 @@ void processMessage(char *message)
static bool _gotREV = false;
static int freeze_msg_count = 0;
static int service_msg_count = 0;
- // NSF replace message with msg
+ // NSF replace message with msg
msg = stripwhitespace(message);
strcpy(_aqualink_data.last_message, msg);
//_aqualink_data.last_message = _aqualink_data.message;
//_aqualink_data.display_message = NULL;
-
+
//aqualink_strcpy(_aqualink_data.message, msg);
-
- logMessage(LOG_INFO, "RS Message :- '%s'\n",msg);
+
+ logMessage(LOG_INFO, "RS Message :- '%s'\n", msg);
//logMessage(LOG_NOTICE, "RS Message :- '%s'\n",msg);
-
- // Check long messages in this if/elseif block first, as some messages are similar.
+
+ // Check long messages in this if/elseif block first, as some messages are similar.
// ie "POOL TEMP" and "POOL TEMP IS SET TO" so want correct match first.
//
-
- if(stristr(msg, "JANDY AquaLinkRS") != NULL) {
+
+ if (stristr(msg, "JANDY AquaLinkRS") != NULL)
+ {
//_aqualink_data.display_message = NULL;
_aqualink_data.last_display_message[0] = '\0';
}
// If we have more than 10 messages without "Service Mode is active" assume it's off.
- if (_aqualink_data.service_mode_state == ON && service_msg_count++ > 10) {
+ if (_aqualink_data.service_mode_state == ON && service_msg_count++ > 10)
+ {
_aqualink_data.service_mode_state = OFF;
service_msg_count = 0;
}
-
+ /*
// If we have more than 10 messages without "Service Mode is active" assume it's off.
if (_aqualink_data.service_mode_state == ON && service_msg_count++ > 10) {
_aqualink_data.service_mode_state = OFF;
service_msg_count = 0;
}
-
+*/
// If we have more than 10 messages without "FREE PROTECT ACTIVATED" assume it's off.
- if (_aqualink_data.frz_protect_state == ON && freeze_msg_count++ > 10) {
+ if (_aqualink_data.frz_protect_state == ON && freeze_msg_count++ > 10)
+ {
_aqualink_data.frz_protect_state = ENABLE;
freeze_msg_count = 0;
}
@@ -236,154 +243,194 @@ void processMessage(char *message)
//else
//_aqualink_data.display_last_message = false;
- if(stristr(msg, LNG_MSG_BATTERY_LOW) != NULL) {
+ if (stristr(msg, LNG_MSG_BATTERY_LOW) != NULL)
+ {
_aqualink_data.battery = LOW;
strcpy(_aqualink_data.last_display_message, msg); // Also display the message on web UI
}
- else if(stristr(msg, LNG_MSG_POOL_TEMP_SET) != NULL) {
+ else if (stristr(msg, LNG_MSG_POOL_TEMP_SET) != NULL)
+ {
//logMessage(LOG_DEBUG, "pool htr long message: %s", &message[20]);
- _aqualink_data.pool_htr_set_point = atoi(message+20);
+ _aqualink_data.pool_htr_set_point = atoi(message + 20);
if (_aqualink_data.temp_units == UNKNOWN)
setUnits(msg);
}
- else if(stristr(msg, LNG_MSG_SPA_TEMP_SET) != NULL) {
+ else if (stristr(msg, LNG_MSG_SPA_TEMP_SET) != NULL)
+ {
//logMessage(LOG_DEBUG, "spa htr long message: %s", &message[19]);
- _aqualink_data.spa_htr_set_point = atoi(message+19);
+ _aqualink_data.spa_htr_set_point = atoi(message + 19);
if (_aqualink_data.temp_units == UNKNOWN)
setUnits(msg);
}
- else if(stristr(msg, LNG_MSG_FREEZE_PROTECTION_SET) != NULL) {
+ else if (stristr(msg, LNG_MSG_FREEZE_PROTECTION_SET) != NULL)
+ {
//logMessage(LOG_DEBUG, "frz protect long message: %s", &message[28]);
- _aqualink_data.frz_protect_set_point = atoi(message+28);
+ _aqualink_data.frz_protect_set_point = atoi(message + 28);
_aqualink_data.frz_protect_state = ENABLE;
if (_aqualink_data.temp_units == UNKNOWN)
setUnits(msg);
}
- else if(strncasecmp(msg, MSG_AIR_TEMP, MSG_AIR_TEMP_LEN) == 0) {
- _aqualink_data.air_temp = atoi(msg+MSG_AIR_TEMP_LEN);
-
- if (_aqualink_data.temp_units == UNKNOWN)
- setUnits(msg);
- }
- else if(strncasecmp(msg, MSG_POOL_TEMP, MSG_POOL_TEMP_LEN) == 0) {
- _aqualink_data.pool_temp = atoi(msg+MSG_POOL_TEMP_LEN);
+ else if (strncasecmp(msg, MSG_AIR_TEMP, MSG_AIR_TEMP_LEN) == 0)
+ {
+ _aqualink_data.air_temp = atoi(msg + MSG_AIR_TEMP_LEN);
if (_aqualink_data.temp_units == UNKNOWN)
setUnits(msg);
}
- else if(strncasecmp(msg, MSG_SPA_TEMP, MSG_SPA_TEMP_LEN) == 0) {
- _aqualink_data.spa_temp = atoi(msg+MSG_SPA_TEMP_LEN);
+ else if (strncasecmp(msg, MSG_POOL_TEMP, MSG_POOL_TEMP_LEN) == 0)
+ {
+ _aqualink_data.pool_temp = atoi(msg + MSG_POOL_TEMP_LEN);
+
+ if (_aqualink_data.temp_units == UNKNOWN)
+ setUnits(msg);
+ }
+ else if (strncasecmp(msg, MSG_SPA_TEMP, MSG_SPA_TEMP_LEN) == 0)
+ {
+ _aqualink_data.spa_temp = atoi(msg + MSG_SPA_TEMP_LEN);
if (_aqualink_data.temp_units == UNKNOWN)
setUnits(msg);
}
// NSF If get water temp rather than pool or spa in some cases, then we are in Pool OR Spa ONLY mode
- else if(strncasecmp(msg, MSG_WATER_TEMP, MSG_WATER_TEMP_LEN) == 0) {
- _aqualink_data.pool_temp = atoi(msg+MSG_WATER_TEMP_LEN);
- _aqualink_data.spa_temp = atoi(msg+MSG_WATER_TEMP_LEN);
+ else if (strncasecmp(msg, MSG_WATER_TEMP, MSG_WATER_TEMP_LEN) == 0)
+ {
+ _aqualink_data.pool_temp = atoi(msg + MSG_WATER_TEMP_LEN);
+ _aqualink_data.spa_temp = atoi(msg + MSG_WATER_TEMP_LEN);
if (_aqualink_data.temp_units == UNKNOWN)
setUnits(msg);
-
- if (_aqualink_data.single_device != true) {
+
+ if (_aqualink_data.single_device != true)
+ {
_aqualink_data.single_device = true;
- logMessage(LOG_NOTICE, "AqualinkD set to 'Pool OR Spa Only' mode\n");
+ logMessage(LOG_NOTICE, "AqualinkD set to 'Pool OR Spa Only' mode\n");
}
}
- else if(stristr(msg, LNG_MSG_WATER_TEMP1_SET) != NULL) {
- _aqualink_data.pool_htr_set_point = atoi(message+28);
+ else if (stristr(msg, LNG_MSG_WATER_TEMP1_SET) != NULL)
+ {
+ _aqualink_data.pool_htr_set_point = atoi(message + 28);
if (_aqualink_data.temp_units == UNKNOWN)
setUnits(msg);
- if (_aqualink_data.single_device != true) {
+ if (_aqualink_data.single_device != true)
+ {
_aqualink_data.single_device = true;
- logMessage(LOG_NOTICE, "AqualinkD set to 'Pool OR Spa Only' mode\n");
+ logMessage(LOG_NOTICE, "AqualinkD set to 'Pool OR Spa Only' mode\n");
}
}
- else if(stristr(msg, LNG_MSG_WATER_TEMP2_SET) != NULL) {
- _aqualink_data.spa_htr_set_point = atoi(message+27);
+ else if (stristr(msg, LNG_MSG_WATER_TEMP2_SET) != NULL)
+ {
+ _aqualink_data.spa_htr_set_point = atoi(message + 27);
if (_aqualink_data.temp_units == UNKNOWN)
setUnits(msg);
- if (_aqualink_data.single_device != true) {
+ if (_aqualink_data.single_device != true)
+ {
_aqualink_data.single_device = true;
- logMessage(LOG_NOTICE, "AqualinkD set to 'Pool OR Spa Only' mode\n");
- }
+ logMessage(LOG_NOTICE, "AqualinkD set to 'Pool OR Spa Only' mode\n");
+ }
}
- else if (stristr(msg, LNG_MSG_SERVICE_ACTIVE) != NULL) {
+ else if (stristr(msg, LNG_MSG_SERVICE_ACTIVE) != NULL)
+ {
if (_aqualink_data.service_mode_state == OFF)
- logMessage(LOG_NOTICE, "AqualinkD set to Service Mode\n");
+ logMessage(LOG_NOTICE, "AqualinkD set to Service Mode\n");
_aqualink_data.service_mode_state = ON;
service_msg_count = 0;
}
- else if (stristr(msg, LNG_MSG_FREEZE_PROTECTION_ACTIVATED) != NULL) {
+ else if (stristr(msg, LNG_MSG_FREEZE_PROTECTION_ACTIVATED) != NULL)
+ {
_aqualink_data.frz_protect_state = ON;
freeze_msg_count = 0;
strcpy(_aqualink_data.last_display_message, msg); // Also display the message on web UI
}
- else if(msg[2] == '/' && msg[5] == '/' && msg[8] == ' ') {// date in format '08/29/16 MON'
+ else if (msg[2] == '/' && msg[5] == '/' && msg[8] == ' ')
+ { // date in format '08/29/16 MON'
strcpy(_aqualink_data.date, msg);
}
- else if(strncasecmp(msg, MSG_SWG_PCT, MSG_SWG_PCT_LEN) == 0) {
- _aqualink_data.swg_percent = atoi(msg+MSG_SWG_PCT_LEN);
+ else if (strncasecmp(msg, MSG_SWG_PCT, MSG_SWG_PCT_LEN) == 0)
+ {
+ _aqualink_data.swg_percent = atoi(msg + MSG_SWG_PCT_LEN);
//logMessage(LOG_DEBUG, "Stored SWG Percent as %d\n", _aqualink_data.swg_percent);
}
- else if(strncasecmp(msg, MSG_SWG_PPM, MSG_SWG_PPM_LEN) == 0) {
- _aqualink_data.swg_ppm = atoi(msg+MSG_SWG_PPM_LEN);
+ else if (strncasecmp(msg, MSG_SWG_PPM, MSG_SWG_PPM_LEN) == 0)
+ {
+ _aqualink_data.swg_ppm = atoi(msg + MSG_SWG_PPM_LEN);
//logMessage(LOG_DEBUG, "Stored SWG PPM as %d\n", _aqualink_data.swg_ppm);
}
- else if( (msg[1] == ':' || msg[2] == ':') && msg[strlen(msg)-1] == 'M') { // time in format '9:45 AM'
+ else if ((msg[1] == ':' || msg[2] == ':') && msg[strlen(msg) - 1] == 'M')
+ { // time in format '9:45 AM'
strcpy(_aqualink_data.time, msg);
// Setting time takes a long time, so don't try until we have all other programmed data.
- if ( (_initWithRS == true) && strlen(_aqualink_data.date) > 1 && checkAqualinkTime() != true ) {
+ if ((_initWithRS == true) && strlen(_aqualink_data.date) > 1 && checkAqualinkTime() != true)
+ {
logMessage(LOG_NOTICE, "RS time is NOT accurate '%s %s', re-setting on controller!\n", _aqualink_data.time, _aqualink_data.date);
aq_programmer(AQ_SET_TIME, NULL, &_aqualink_data);
- } else {
+ }
+ else
+ {
logMessage(LOG_DEBUG, "RS time is accurate '%s %s'\n", _aqualink_data.time, _aqualink_data.date);
}
// If we get a time message before REV, the controller didn't see us as we started too quickly.
- if ( _gotREV == false ) {
- logMessage(LOG_NOTICE, "Getting control panel information\n",msg);
+ if (_gotREV == false)
+ {
+ logMessage(LOG_NOTICE, "Getting control panel information\n", msg);
aq_programmer(AQ_GET_DIAGNOSTICS_MODEL, NULL, &_aqualink_data);
_gotREV = true; // Force it to true just incase we don't understand the model#
}
}
- else if(strstr(msg, " REV ") != NULL) { // '8157 REV MMM'
+ else if (strstr(msg, " REV ") != NULL)
+ { // '8157 REV MMM'
// A master firmware revision message.
strcpy(_aqualink_data.version, msg);
_gotREV = true;
- logMessage(LOG_NOTICE, "Control Panel %s\n",msg);
- if ( _initWithRS == false) {
+ logMessage(LOG_NOTICE, "Control Panel %s\n", msg);
+ if (_initWithRS == false)
+ {
queueGetProgramData();
_initWithRS = true;
}
}
- else if(stristr(msg, " TURNS ON") != NULL) {
- logMessage(LOG_NOTICE, "Program data '%s'\n",msg);
+ else if (stristr(msg, " TURNS ON") != NULL)
+ {
+ logMessage(LOG_NOTICE, "Program data '%s'\n", msg);
}
- else if(_config_parameters.override_freeze_protect == TRUE &&
- strncasecmp(msg, "Press Enter* to override Freeze Protection with", 47) == 0 ){
+ else if (_config_parameters.override_freeze_protect == TRUE && strncasecmp(msg, "Press Enter* to override Freeze Protection with", 47) == 0)
+ {
//send_cmd(KEY_ENTER, aq_data);
aq_programmer(AQ_SEND_CMD, (char *)KEY_ENTER, &_aqualink_data);
}
- else {
- logMessage(LOG_DEBUG_SERIAL, "Ignoring '%s'\n",msg);
+ else if ((msg[4] == ':') && (strncasecmp(msg, "AUX", 3) == 0))
+ { // AUX label "AUX1:"
+ int labelid = atoi(msg + 3);
+ if (labelid > 0 && _config_parameters.use_panel_aux_labels == true)
+ {
+ // Aux1: on panel = Button 3 in aqualinkd (button 2 in array)
+ logMessage(LOG_NOTICE, "AUX LABEL %d '%s'\n", labelid + 1, msg);
+ _aqualink_data.aqbuttons[labelid+1].label = prittyString(cleanalloc(msg+5));
+ //_aqualink_data.aqbuttons[labelid + 1].label = cleanalloc(msg + 5);
+ }
+ }
+ else
+ {
+ logMessage(LOG_DEBUG_SERIAL, "Ignoring '%s'\n", msg);
//_aqualink_data.display_message = msg;
if (_aqualink_data.active_thread.thread_id == 0 &&
stristr(msg, "JANDY AquaLinkRS") == NULL &&
stristr(msg, "PUMP O") == NULL /*&& // Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON'
stristr(msg, "CLEANER O") == NULL &&
stristr(msg, "SPA O") == NULL &&
- stristr(msg, "AUX") == NULL*/) { // Catch all AUX1 AUX5 messages
+ stristr(msg, "AUX") == NULL*/
+ )
+ { // Catch all AUX1 AUX5 messages
//_aqualink_data.display_last_message = true;
strcpy(_aqualink_data.last_display_message, msg);
}
}
-
+
// Send every message if we are in simulate panel mode
if (_aqualink_data.simulate_panel)
ascii(_aqualink_data.last_display_message, msg);
@@ -393,285 +440,43 @@ void processMessage(char *message)
}
-void set_pda_led(struct aqualinkled *led, char state)
-{
- if (state == 'N')
- led->state = ON;
- else if (state == 'A')
- led->state = ENABLE;
- else if (state == '*')
- led->state = FLASH;
- else
- led->state = OFF;
-}
-
-void pass_pda_equiptment_status_item(char* msg)
-{
- static char *index;
- int i;
-
- // EQUIPMENT STATUS
- //
- // AquaPure 100%
- // SALT 25500 PPM
- // FILTER PUMP
- // POOL HEAT
- // SPA HEAT ENA
-
- // Check message for status of device
- // Loop through all buttons and match the PDA text.
- if ((index = strcasestr(msg, MSG_SWG_PCT)) != NULL)
- {
- //int aq = atoi(index + strlen(MSG_SWG_PCT));
- //printf("Aquapure PERCENT message %d\n", aq);
- _aqualink_data.swg_percent = atoi(index + strlen(MSG_SWG_PCT));
- }
- else if ((index = strcasestr(msg, MSG_SWG_PPM)) != NULL)
- {
- //int aq = atoi(index + strlen(MSG_SWG_PPM));
- //printf("Aquapure SALT message %d\n", aq);
- _aqualink_data.swg_ppm = atoi(index + strlen(MSG_SWG_PPM));
- }
- else
- {
- char labelBuff[AQ_MSGLEN + 1];
- strncpy (labelBuff, msg, AQ_MSGLEN + 1);
- msg = stripwhitespace (labelBuff);
-
- // These are listed as " FILTER PUMP "
-
- if (strcasecmp (msg, "POOL HEAT ENA") == 0)
- {
- _aqualink_data.aqbuttons[POOL_HEAT_INDEX].led->state = ENABLE;
- }
- else if (strcasecmp (msg, "SPA HEAT ENA") == 0)
- {
- _aqualink_data.aqbuttons[SPA_HEAT_INDEX].led->state = ENABLE;
- }
- else
- {
- for (i = 0; i < TOTAL_BUTTONS; i++)
- {
- if (strcasecmp (msg, _aqualink_data.aqbuttons[i].pda_label) == 0)
- {
- logMessage (LOG_DEBUG, "*** Found Status for %s = '%.*s'\n",
- _aqualink_data.aqbuttons[i].pda_label, AQ_MSGLEN,
- msg);
- // It's on (or delayed) if it's listed here.
- if (_aqualink_data.aqbuttons[i].led->state != FLASH)
- {
- _aqualink_data.aqbuttons[i].led->state = ON;
- }
- break;
- }
- }
- }
- }
-}
-
-bool process_pda_packet(unsigned char* packet, int length)
-{
- bool rtn = true;
- int i;
- char *msg;
- static bool init = false;
- static time_t _lastStatus;
-
- process_pda_menu_packet(packet, length);
-
- // NSF.
-
- //_aqualink_data.last_msg_was_status = false;
-
- //debugPacketPrint(0x00, packet, length);
-
- switch (packet[PKT_CMD]) {
-
- case CMD_ACK:
- logMessage(LOG_DEBUG, "RS Received ACK length %d.\n",length);
- break;
-
- case CMD_STATUS:
- _aqualink_data.last_display_message[0] = '\0';
- /*
- if (!init) {
- aq_programmer(AQ_PDA_INIT, NULL, &_aqualink_data);
- init=true;
- }*/
-
- // If we get a status packet, and we are on the status menu, this is a list of what's on
- // or pending so unless flash turn everything off, and just turn on items that are listed.
- // This is the only way to update a device that's been turned off by a real PDA / keypad.
- if (pda_m_type() == PM_EQUIPTMENT_STATUS) {
- //printf("*** SET ALL OFF ****\n");
- for (i = 0; i < TOTAL_BUTTONS; i++) {
- if (_aqualink_data.aqbuttons[i].led->state != FLASH) {
- _aqualink_data.aqbuttons[i].led->state = OFF;
- }
- }
- for (i = 1; i < PDA_LINES; i++) {
- pass_pda_equiptment_status_item(pda_m_line(i));
- }
- time(&_lastStatus);
- } else {
- time_t now;
- time(&now);
- if (init && difftime(now, _lastStatus) > 60){
- logMessage(LOG_DEBUG,"OVER 60 SECONDS SINCE LAST STATUS UPDATE, forcing refresh\n");
- // Reset aquapure to nothing since it must be off at this point
- _aqualink_data.pool_temp = TEMP_UNKNOWN;
- _aqualink_data.spa_temp = TEMP_UNKNOWN;
- time(&_lastStatus);
- aq_programmer(AQ_PDA_DEVICE_STATUS, NULL, &_aqualink_data);
- }
- }
- break;
- case CMD_MSG_LONG: {
- msg = (char*)packet+PKT_DATA+1;
-
- if (packet[PKT_DATA] == 0x82) { // Air & Water temp is always this ID
- // 'AIR POOL'
- // ' 86` 86` '
- // 'AIR SPA '
- // ' 86` 86` '
- // 'AIR '
- // ' 86` '
- _aqualink_data.temp_units = FAHRENHEIT; // Force FAHRENHEIT
- if (stristr(pda_m_line(1), "AIR") != NULL)
- _aqualink_data.air_temp = atoi(msg);
- if (stristr(pda_m_line(1), "SPA") != NULL) {
- _aqualink_data.spa_temp = atoi(msg+4);
- _aqualink_data.pool_temp = TEMP_UNKNOWN;
- } else if (stristr(pda_m_line(1), "POOL") != NULL) {
- _aqualink_data.pool_temp = atoi(msg+7);
- _aqualink_data.spa_temp = TEMP_UNKNOWN;
- }
- //printf("Air Temp = %d | Water Temp = %d\n",atoi(msg),atoi(msg+7));
- } else if (packet[PKT_DATA] == 0x40) { // Time is always on this ID
- // message " SAT 8:46AM "
- // " SAT 10:29AM"
- // " SAT 4:23PM "
- //printf("TIME = '%.*s'\n",AQ_MSGLEN,msg );
- //printf("TIME = '%c'\n",msg[AQ_MSGLEN-1] );
- if (msg[AQ_MSGLEN-1] == ' ') {
- strncpy(_aqualink_data.time, msg+9, 6);
- } else {
- strncpy(_aqualink_data.time, msg+9, 7);
- }
- strncpy(_aqualink_data.date, msg+5,3);
- // NSF Come back and change the above to correctly check date and time in future
-// If it wasn't a specific msg, (above) then run through and see what kind of message it is depending on the PDA menu
- } else if (pda_m_type() == PM_EQUIPTMENT_STATUS) {
- pass_pda_equiptment_status_item(msg);
- } else if (pda_m_type() == PM_EQUIPTMENT_CONTROL) {
- // These are listed as "FILTER PUMP OFF"
- char labelBuff[AQ_MSGLEN+1];
- strncpy(labelBuff, msg, AQ_MSGLEN-4);
- labelBuff[AQ_MSGLEN-4] = 0;
-
- for (i = 0; i < TOTAL_BUTTONS; i++) {
- if (strcasecmp(stripwhitespace(labelBuff), _aqualink_data.aqbuttons[i].pda_label) == 0) {
- logMessage(LOG_DEBUG, "*** Found EQ CTL Status for %s = '%.*s'\n",_aqualink_data.aqbuttons[i].pda_label, AQ_MSGLEN, msg);
- set_pda_led(_aqualink_data.aqbuttons[i].led, msg[AQ_MSGLEN-1]);
- }
- }
- } else if (pda_m_type() == PM_MAIN || pda_m_type() == PM_BUILDING_MAIN) {
- if (stristr(msg, "POOL MODE") != NULL) {
- // If pool mode is on the filter pump is on but if it is off the filter pump might be on if spa mode is on.
- if (msg[AQ_MSGLEN-1] == 'N') {
- _aqualink_data.aqbuttons[PUMP_INDEX].led->state = ON;
- } else if (msg[AQ_MSGLEN-1] == '*') {
- _aqualink_data.aqbuttons[PUMP_INDEX].led->state = FLASH;
- }
- }else if (stristr(msg, "POOL HEATER") != NULL) {
- set_pda_led(_aqualink_data.aqbuttons[POOL_HEAT_INDEX].led, msg[AQ_MSGLEN-1]);
- }
- else if (stristr(msg, "SPA MODE") != NULL) {
- // when SPA mode is on the filter may be on or pending
- if (msg[AQ_MSGLEN - 1] == 'N') {
- _aqualink_data.aqbuttons[PUMP_INDEX].led->state = ON;
- _aqualink_data.aqbuttons[SPA_INDEX].led->state = ON;
- }
- else if (msg[AQ_MSGLEN - 1] == '*')
- {
- _aqualink_data.aqbuttons[PUMP_INDEX].led->state = FLASH;
- _aqualink_data.aqbuttons[SPA_INDEX].led->state = ON;
- }
- else
- {
- _aqualink_data.aqbuttons[SPA_INDEX].led->state = OFF;
- }
- }
- else if (stristr(msg, "SPA HEATER") != NULL)
- {
- set_pda_led(_aqualink_data.aqbuttons[SPA_HEAT_INDEX].led, msg[AQ_MSGLEN-1]);
- }
- } else if (pda_m_type() == PM_UNKNOWN) {
- // Lets make a guess here and just see if there is an ON/OFF/ENA/*** at the end of the line
- // When you turn on/off a piece of equiptment, a clear screen followed by single message is sent.
- // So we are not in any PDA menu, try to catch that message here so we catch new device state ASAP.
- if ( msg[AQ_MSGLEN-1] == 'N' || msg[AQ_MSGLEN-1] == 'F' || msg[AQ_MSGLEN-1] == 'A' || msg[AQ_MSGLEN-1] == '*') {
- for (i = 0; i < TOTAL_BUTTONS; i++) {
- if (stristr(msg, _aqualink_data.aqbuttons[i].pda_label) != NULL) {
- //printf("*** Found Status for %s = '%.*s'\n",_aqualink_data.aqbuttons[i].pda_label, AQ_MSGLEN, msg);
- set_pda_led(_aqualink_data.aqbuttons[i].led, msg[AQ_MSGLEN-1]);
- }
- }
- }
- }
- // If we haven't initilixed and we are on line 4, then initilize
- if (! init && pda_m_hlightindex() == 4) {
- //printf("** INITILIZE ADD LINE BACK\n");
- aq_programmer(AQ_PDA_INIT, NULL, &_aqualink_data);
- time(&_lastStatus);
- init = true;
- }
-
- }
- //printf("** Line index='%d' Highligh='%s' Message='%.*s'\n",pda_m_hlightindex(), pda_m_hlight(), AQ_MSGLEN, msg);
- logMessage(LOG_INFO,"PDA Menu '%d' Selectedline '%s', Last line received '%.*s'\n", pda_m_type(), pda_m_hlight(), AQ_MSGLEN, msg);
- break;
- }
-
- if ( packet[PKT_CMD] == CMD_MSG_LONG ||
- packet[PKT_CMD] == CMD_PDA_HIGHLIGHT ||
- packet[PKT_CMD] == CMD_PDA_SHIFTLINES) {
- // We processed the next message, kick any threads waiting on the message.
- kick_aq_program_thread(&_aqualink_data);
- }
- return rtn;
-}
-
-bool process_packet(unsigned char* packet, int length)
+bool process_packet(unsigned char *packet, int length)
{
bool rtn = false;
static unsigned char last_packet[AQ_MAXPKTLEN];
- static char message[AQ_MSGLONGLEN+1];
+ static char message[AQ_MSGLONGLEN + 1];
static int processing_long_msg = 0;
-
+
// Check packet against last check if different.
- if (memcmp(packet, last_packet, length) == 0) {
- logMessage(LOG_DEBUG_SERIAL, "RS Received duplicate, ignoring.\n",length);
+ if (memcmp(packet, last_packet, length) == 0)
+ {
+ logMessage(LOG_DEBUG_SERIAL, "RS Received duplicate, ignoring.\n", length);
return rtn;
- } else {
+ }
+ else
+ {
memcpy(last_packet, packet, length);
+ _aqualink_data.last_packet_type = packet[PKT_CMD];
rtn = true;
}
-
- if (_config_parameters.pda_mode == true) {
+
+ if (_config_parameters.pda_mode == true)
+ {
return process_pda_packet(packet, length);
}
-
- if (processing_long_msg > 0 && packet[PKT_CMD] != CMD_MSG_LONG) {
+
+ if (processing_long_msg > 0 && packet[PKT_CMD] != CMD_MSG_LONG)
+ {
processing_long_msg = 0;
//logMessage(LOG_ERR, "RS failed to receive complete long message, received '%s'\n",message);
//logMessage(LOG_DEBUG, "RS didn't finished receiving of MSG_LONG '%s'\n",message);
processMessage(message);
}
-
- switch (packet[PKT_CMD]) {
+
+ switch (packet[PKT_CMD])
+ {
case CMD_ACK:
- logMessage(LOG_DEBUG, "RS Received ACK length %d.\n",length);
+ logMessage(LOG_DEBUG, "RS Received ACK length %d.\n", length);
break;
case CMD_STATUS:
logMessage(LOG_DEBUG, "RS Received STATUS length %d.\n", length);
@@ -694,8 +499,8 @@ bool process_packet(unsigned char* packet, int length)
}
break;
case CMD_MSG:
- memset(message, 0, AQ_MSGLONGLEN+1);
- strncpy(message, (char*)packet+PKT_DATA+1, AQ_MSGLEN);
+ memset(message, 0, AQ_MSGLONGLEN + 1);
+ strncpy(message, (char *)packet + PKT_DATA + 1, AQ_MSGLEN);
//logMessage(LOG_DEBUG_SERIAL, "RS Received message '%s'\n",message);
if (packet[PKT_DATA] == 1) // Start of long message, get them all before processing
{
@@ -707,26 +512,27 @@ bool process_packet(unsigned char* packet, int length)
break;
case CMD_MSG_LONG:
// First in sequence is normal message.
- processing_long_msg++;
- strncpy(&message[processing_long_msg*AQ_MSGLEN], (char*)packet+PKT_DATA+1, AQ_MSGLEN);
- //logMessage(LOG_DEBUG_SERIAL, "RS Received long message '%s'\n",message);
- if (processing_long_msg == 3) {
- //logMessage(LOG_DEBUG, "RS Finished receiving of MSG_LONG '%s'\n",message);
- processMessage(message);
- processing_long_msg=0;
- }
+ processing_long_msg++;
+ strncpy(&message[processing_long_msg * AQ_MSGLEN], (char *)packet + PKT_DATA + 1, AQ_MSGLEN);
+ //logMessage(LOG_DEBUG_SERIAL, "RS Received long message '%s'\n",message);
+ if (processing_long_msg == 3)
+ {
+ //logMessage(LOG_DEBUG, "RS Finished receiving of MSG_LONG '%s'\n",message);
+ processMessage(message);
+ processing_long_msg = 0;
+ }
break;
case CMD_PROBE:
- logMessage(LOG_DEBUG, "RS Received PROBE length %d.\n",length);
+ logMessage(LOG_DEBUG, "RS Received PROBE length %d.\n", length);
//logMessage(LOG_INFO, "Synch'ing with Aqualink master device...\n");
rtn = false;
break;
default:
- logMessage(LOG_INFO, "RS Received unknown packet, 0x%02hhx\n",packet[PKT_CMD]);
+ logMessage(LOG_INFO, "RS Received unknown packet, 0x%02hhx\n", packet[PKT_CMD]);
rtn = false;
break;
}
-
+
return rtn;
}
@@ -739,42 +545,64 @@ void action_delayed_request()
if (_aqualink_data.temp_units == UNKNOWN && _aqualink_data.unactioned.type != SWG_SETPOINT)
return;
- if (_aqualink_data.unactioned.type == POOL_HTR_SETOINT) {
+ if (_aqualink_data.unactioned.type == POOL_HTR_SETOINT)
+ {
_aqualink_data.unactioned.value = setpoint_check(POOL_HTR_SETOINT, _aqualink_data.unactioned.value, &_aqualink_data);
- if ( _aqualink_data.pool_htr_set_point != _aqualink_data.unactioned.value ) {
+ if (_aqualink_data.pool_htr_set_point != _aqualink_data.unactioned.value)
+ {
aq_programmer(AQ_SET_POOL_HEATER_TEMP, sval, &_aqualink_data);
- logMessage(LOG_NOTICE, "Setting pool heater setpoint to %d\n",_aqualink_data.unactioned.value);
- } else {
- logMessage(LOG_NOTICE, "Pool heater setpoint is already %d, not changing\n",_aqualink_data.unactioned.value);
+ logMessage(LOG_NOTICE, "Setting pool heater setpoint to %d\n", _aqualink_data.unactioned.value);
}
- } else if (_aqualink_data.unactioned.type == SPA_HTR_SETOINT) {
+ else
+ {
+ logMessage(LOG_NOTICE, "Pool heater setpoint is already %d, not changing\n", _aqualink_data.unactioned.value);
+ }
+ }
+ else if (_aqualink_data.unactioned.type == SPA_HTR_SETOINT)
+ {
_aqualink_data.unactioned.value = setpoint_check(SPA_HTR_SETOINT, _aqualink_data.unactioned.value, &_aqualink_data);
- if ( _aqualink_data.spa_htr_set_point != _aqualink_data.unactioned.value ) {
+ if (_aqualink_data.spa_htr_set_point != _aqualink_data.unactioned.value)
+ {
aq_programmer(AQ_SET_SPA_HEATER_TEMP, sval, &_aqualink_data);
- logMessage(LOG_NOTICE, "Setting spa heater setpoint to %d\n",_aqualink_data.unactioned.value);
- } else {
- logMessage(LOG_NOTICE, "Spa heater setpoint is already %d, not changing\n",_aqualink_data.unactioned.value);
+ logMessage(LOG_NOTICE, "Setting spa heater setpoint to %d\n", _aqualink_data.unactioned.value);
}
- } else if (_aqualink_data.unactioned.type == FREEZE_SETPOINT) {
+ else
+ {
+ logMessage(LOG_NOTICE, "Spa heater setpoint is already %d, not changing\n", _aqualink_data.unactioned.value);
+ }
+ }
+ else if (_aqualink_data.unactioned.type == FREEZE_SETPOINT)
+ {
_aqualink_data.unactioned.value = setpoint_check(FREEZE_SETPOINT, _aqualink_data.unactioned.value, &_aqualink_data);
- if ( _aqualink_data.frz_protect_set_point != _aqualink_data.unactioned.value ) {
+ if (_aqualink_data.frz_protect_set_point != _aqualink_data.unactioned.value)
+ {
aq_programmer(AQ_SET_FRZ_PROTECTION_TEMP, sval, &_aqualink_data);
- logMessage(LOG_NOTICE, "Setting freeze protect to %d\n",_aqualink_data.unactioned.value);
- } else {
- logMessage(LOG_NOTICE, "Freeze setpoint is already %d, not changing\n",_aqualink_data.unactioned.value);
+ logMessage(LOG_NOTICE, "Setting freeze protect to %d\n", _aqualink_data.unactioned.value);
}
- } else if (_aqualink_data.unactioned.type == SWG_SETPOINT) {
+ else
+ {
+ logMessage(LOG_NOTICE, "Freeze setpoint is already %d, not changing\n", _aqualink_data.unactioned.value);
+ }
+ }
+ else if (_aqualink_data.unactioned.type == SWG_SETPOINT)
+ {
_aqualink_data.unactioned.value = setpoint_check(SWG_SETPOINT, _aqualink_data.unactioned.value, &_aqualink_data);
- if (_aqualink_data.ar_swg_status == SWG_STATUS_OFF ) {
+ if (_aqualink_data.ar_swg_status == SWG_STATUS_OFF)
+ {
// SWG is off, can't set %, so delay the set until it's on.
_aqualink_data.swg_delayed_percent = _aqualink_data.unactioned.value;
- } else {
- if ( _aqualink_data.swg_percent != _aqualink_data.unactioned.value ) {
+ }
+ else
+ {
+ if (_aqualink_data.swg_percent != _aqualink_data.unactioned.value)
+ {
aq_programmer(AQ_SET_SWG_PERCENT, sval, &_aqualink_data);
- logMessage(LOG_NOTICE, "Setting SWG %% to %d\n",_aqualink_data.unactioned.value);
- } else {
- logMessage(LOG_NOTICE, "SWG % is already %d, not changing\n",_aqualink_data.unactioned.value);
- }
+ logMessage(LOG_NOTICE, "Setting SWG %% to %d\n", _aqualink_data.unactioned.value);
+ }
+ else
+ {
+ logMessage(LOG_NOTICE, "SWG % is already %d, not changing\n", _aqualink_data.unactioned.value);
+ }
}
// Let's just tell everyone we set it, before we actually did. Makes homekit happy, and it will re-correct on error.
_aqualink_data.swg_percent = _aqualink_data.unactioned.value;
@@ -785,20 +613,22 @@ void action_delayed_request()
_aqualink_data.unactioned.requested = 0;
}
-int main(int argc, char *argv[]) {
+int main(int argc, char *argv[])
+{
// main_loop ();
int i;
char *cfgFile = DEFAULT_CONFIG_FILE;
int cmdln_loglevel = -1;
-
+ bool cmdln_debugRS485 = false;
// struct lws_context_creation_info info;
// Log only NOTICE messages and above. Debug and info messages
// will not be logged to syslog.
setlogmask(LOG_UPTO(LOG_NOTICE));
- if (getuid() != 0) {
+ if (getuid() != 0)
+ {
//logMessage(LOG_ERR, "%s Can only be run as root\n", argv[0]);
fprintf(stderr, "ERROR %s Can only be run as root\n", argv[0]);
return EXIT_FAILURE;
@@ -807,14 +637,24 @@ int main(int argc, char *argv[]) {
// Initialize the daemon's parameters.
init_parameters(&_config_parameters);
- for (i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-d") == 0) {
+ for (i = 1; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-d") == 0)
+ {
_config_parameters.deamonize = false;
- } else if (strcmp(argv[i], "-c") == 0) {
+ }
+ else if (strcmp(argv[i], "-c") == 0)
+ {
cfgFile = argv[++i];
- } else if (strcmp(argv[i], "-v") == 0) {
+ }
+ else if (strcmp(argv[i], "-v") == 0)
+ {
cmdln_loglevel = LOG_DEBUG;
}
+ else if (strcmp(argv[i], "-rsd") == 0)
+ {
+ cmdln_debugRS485 = true;
+ }
}
initButtons(&_aqualink_data);
@@ -824,6 +664,9 @@ int main(int argc, char *argv[]) {
if (cmdln_loglevel != -1)
_config_parameters.log_level = cmdln_loglevel;
+ if (cmdln_debugRS485)
+ _config_parameters.debug_RSProtocol_packets = true;
+
setLoggingPrms(_config_parameters.log_level, _config_parameters.deamonize, _config_parameters.log_file);
logMessage(LOG_NOTICE, "%s v%s\n", AQUALINKD_NAME, AQUALINKD_VERSION);
@@ -834,6 +677,7 @@ int main(int argc, char *argv[]) {
logMessage(LOG_NOTICE, "Config web_directory = %s\n", _config_parameters.web_directory);
logMessage(LOG_NOTICE, "Config device_id = 0x%02hhx\n", _config_parameters.device_id);
logMessage(LOG_NOTICE, "Config read_all_devices = %s\n", bool2text(_config_parameters.read_all_devices));
+ logMessage(LOG_NOTICE, "Config use_aux_labels = %s\n", bool2text(_config_parameters.use_panel_aux_labels));
logMessage(LOG_NOTICE, "Config override frz prot = %s\n", bool2text(_config_parameters.override_freeze_protect));
#ifndef MG_DISABLE_MQTT
logMessage(LOG_NOTICE, "Config mqtt_server = %s\n", _config_parameters.mqtt_server);
@@ -849,6 +693,8 @@ int main(int argc, char *argv[]) {
logMessage(LOG_NOTICE, "Config idx SWG Percent = %d\n", _config_parameters.dzidx_swg_percent);
logMessage(LOG_NOTICE, "Config idx SWG PPM = %d\n", _config_parameters.dzidx_swg_ppm);
logMessage(LOG_NOTICE, "Config PDA Mode = %s\n", bool2text(_config_parameters.pda_mode));
+ logMessage(LOG_NOTICE, "Config PDA Sleep Mode = %s\n", bool2text(_config_parameters.pda_sleep_mode));
+ logMessage(LOG_NOTICE, "Config force SWG = %s\n", bool2text(_config_parameters.force_swg));
/* removed until domoticz has a better virtual thermostat
logMessage(LOG_NOTICE, "Config idx pool thermostat = %d\n", _config_parameters.dzidx_pool_thermostat);
logMessage(LOG_NOTICE, "Config idx spa thermostat = %d\n", _config_parameters.dzidx_spa_thermostat);
@@ -857,60 +703,72 @@ int main(int argc, char *argv[]) {
logMessage(LOG_NOTICE, "Config deamonize = %s\n", bool2text(_config_parameters.deamonize));
logMessage(LOG_NOTICE, "Config log_file = %s\n", _config_parameters.log_file);
logMessage(LOG_NOTICE, "Config light_pgm_mode = %.2f\n", _config_parameters.light_programming_mode);
+ logMessage(LOG_NOTICE, "Debug RS485 protocol = %s\n", bool2text(_config_parameters.debug_RSProtocol_packets));
// logMessage (LOG_NOTICE, "Config serial_port = %s\n", config_parameters->serial_port);
- for (i=0; i < TOTAL_BUTONS; i++) {
- logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | PDAlabel %-15s | dzidx %d\n", _aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label , _aqualink_data.aqbuttons[i].pda_label, _aqualink_data.aqbuttons[i].dz_idx);
+ for (i = 0; i < TOTAL_BUTONS; i++)
+ {
+ logMessage(LOG_NOTICE, "Config BTN %-13s = label %-15s | PDAlabel %-15s | dzidx %d\n", _aqualink_data.aqbuttons[i].name, _aqualink_data.aqbuttons[i].label, _aqualink_data.aqbuttons[i].pda_label, _aqualink_data.aqbuttons[i].dz_idx);
//logMessage(LOG_NOTICE, "Button %d\n", i+1, _aqualink_data.aqbuttons[i].label , _aqualink_data.aqbuttons[i].dz_idx);
}
- if (_config_parameters.deamonize == true) {
+ if (_config_parameters.deamonize == true)
+ {
char pidfile[256];
// sprintf(pidfile, "%s/%s.pid",PIDLOCATION, basename(argv[0]));
sprintf(pidfile, "%s/%s.pid", "/run", basename(argv[0]));
daemonise(pidfile, main_loop);
- } else {
+ }
+ else
+ {
main_loop();
}
exit(EXIT_SUCCESS);
}
-
void debugPacketPrint(unsigned char ID, unsigned char *packet_buffer, int packet_length)
{
char buff[1000];
- int i=0;
+ int i = 0;
int cnt = 0;
- cnt = sprintf(buff, "%4.4s 0x%02hhx of type %8.8s", (packet_buffer[PKT_DEST]==0x00?"From":"To"), ID, get_packet_type(packet_buffer, packet_length));
- cnt += sprintf(buff+cnt, " | HEX: ");
+ cnt = sprintf(buff, "%4.4s 0x%02hhx of type %8.8s", (packet_buffer[PKT_DEST] == 0x00 ? "From" : "To"), ID, get_packet_type(packet_buffer, packet_length));
+ cnt += sprintf(buff + cnt, " | HEX: ");
//printHex(packet_buffer, packet_length);
- for (i=0;i 0) {
+ // Send command and jump directly "busy but can receive message"
+ send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
+ delayAckCnt = MAX_BUSY_ACK; // need to test jumping to MAX_BUSY_ACK here
+ } else {
+ logMessage(LOG_NOTICE, "Sending display busy due to Simulator mode \n");
+ if (delayAckCnt < MAX_BLOCK_ACK) // block all incomming messages
+ send_extended_ack(rs_fd, ACK_SCREEN_BUSY_BLOCK, pop_aq_cmd(&_aqualink_data));
+ else if (delayAckCnt < MAX_BUSY_ACK) // say we are pausing
+ send_extended_ack(rs_fd, ACK_SCREEN_BUSY, pop_aq_cmd(&_aqualink_data));
+ else // We timed out pause, send normal ack (This should also reset the display message on next message received)
+ send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
+
+ delayAckCnt++;
+ }
+ }
+}
+
+void main_loop()
+{
struct mg_mgr mgr;
int rs_fd;
int packet_length;
unsigned char packet_buffer[AQ_MAXPKTLEN];
bool interestedInNextAck;
- int delayAckCnt = 0;
+ //int delayAckCnt = 0;
// NSF need to find a better place to init this.
//_aqualink_data.aq_command = 0x00;
@@ -962,8 +867,11 @@ void main_loop() {
_aqualink_data.service_mode_state = OFF;
_aqualink_data.battery = OK;
+ if (_config_parameters.force_swg == true)
+ _aqualink_data.swg_percent = 0;
- if (!start_net_services(&mgr, &_aqualink_data, &_config_parameters)) {
+ if (!start_net_services(&mgr, &_aqualink_data, &_config_parameters))
+ {
logMessage(LOG_ERR, "Can not start webserver on port %s.\n", _config_parameters.socket_port);
exit(EXIT_FAILURE);
}
@@ -976,11 +884,16 @@ void main_loop() {
logMessage(LOG_NOTICE, "Listening to Aqualink RS8 on serial port: %s\n", _config_parameters.serial_port);
if (_config_parameters.pda_mode == true)
- set_pda_mode(true);
+ {
+ init_pda(&_aqualink_data);
+ }
- while (_keepRunning == true) {
- while ((rs_fd < 0 || blank_read >= MAX_ZERO_READ_BEFORE_RECONNECT) && _keepRunning == true) {
- if (rs_fd < 0) {
+ while (_keepRunning == true)
+ {
+ while ((rs_fd < 0 || blank_read >= MAX_ZERO_READ_BEFORE_RECONNECT) && _keepRunning == true)
+ {
+ if (rs_fd < 0)
+ {
// sleep(1);
sprintf(_aqualink_data.last_display_message, CONNECTION_ERROR);
logMessage(LOG_ERR, "Aqualink daemon attempting to connect to master device...\n");
@@ -988,7 +901,9 @@ void main_loop() {
mg_mgr_poll(&mgr, 1000); // Sevice messages
mg_mgr_poll(&mgr, 3000); // should donothing for 3 seconds.
// broadcast_aqualinkstate_error(mgr.active_connections, "No connection to RS control panel");
- } else {
+ }
+ else
+ {
logMessage(LOG_ERR, "Aqualink daemon looks like serial error, resetting.\n");
}
rs_fd = init_serial_port(_config_parameters.serial_port);
@@ -996,99 +911,84 @@ void main_loop() {
}
packet_length = get_packet(rs_fd, packet_buffer);
- if (packet_length == -1) {
+ if (packet_length == -1)
+ {
// Unrecoverable read error. Force an attempt to reconnect.
logMessage(LOG_ERR, "Bad packet length, reconnecting\n");
blank_read = MAX_ZERO_READ_BEFORE_RECONNECT;
- } else if (packet_length == 0) {
+ }
+ else if (packet_length == 0)
+ {
//logMessage(LOG_DEBUG_SERIAL, "Nothing read on serial\n");
blank_read++;
- } else if (packet_length > 0) {
+ }
+ else if (packet_length > 0)
+ {
blank_read = 0;
-//debugPacket(packet_buffer, packet_length);
+ if (_config_parameters.debug_RSProtocol_packets) logPacket(packet_buffer, packet_length);
+
+ if (packet_length > 0 && packet_buffer[PKT_DEST] == _config_parameters.device_id)
+ {
- if (packet_length > 0 && packet_buffer[PKT_DEST] == _config_parameters.device_id) {
- /*
- send_ack(rs_fd, _aqualink_data.aq_command);
- _aqualink_data.aq_command = NUL;
- */
if (getLogLevel() >= LOG_DEBUG)
- logMessage(LOG_DEBUG, "RS received packet of type %s length %d\n", get_packet_type(packet_buffer , packet_length), packet_length);
+ logMessage(LOG_DEBUG, "RS received packet of type %s length %d\n", get_packet_type(packet_buffer, packet_length), packet_length);
- // **** NSF (Taken out while playing with Panel Simulator, put back in. ************)
- // send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
-#define MAX_BLOCK_ACK 12
-#define MAX_BUSY_ACK (50 + MAX_BLOCK_ACK)
- // Wrap the mess just for sanity, the pre-process will clean it up.
- if (! _aqualink_data.simulate_panel ||
- _aqualink_data.active_thread.thread_id != 0)
- {
- // Can only send command to status message on PDA.
- if (_config_parameters.pda_mode == true && packet_buffer[PKT_CMD] != CMD_STATUS)
- send_ack(rs_fd, NUL);
- else
- send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
- } else { // We are in simlator mode, ack get's complicated now.
- // If have a command to send, send a normal ack.
- // If we last message is waiting for an input "SELECT xxxxx", then sent a pause ack
- // pause ack strarts with around 12 ACK_SCREEN_BUSY_DISPLAY acks, then 50 ACK_SCREEN_BUSY acks
- // if we send a command (ie keypress), the whole count needs to end and go back to sending normal ack.
- // In code below, it jumps to sending ACK_SCREEN_BUSY, which still seems to work ok.
- if ( strncasecmp(_aqualink_data.last_display_message, "SELECT", 6) != 0)
- { // Nothing to wait for, send normal ack.
- send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
- delayAckCnt = 0;
- } else if ( get_aq_cmd_length() > 0 ) {
- // Send command and jump directly "busy but can receive message"
- send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
- delayAckCnt = MAX_BUSY_ACK; // need to test jumping to MAX_BUSY_ACK here
- } else {
- logMessage(LOG_NOTICE, "Sending display busy due to Simulator mode \n");
- if (delayAckCnt < MAX_BLOCK_ACK) // block all incomming messages
- send_extended_ack(rs_fd, ACK_SCREEN_BUSY_BLOCK, pop_aq_cmd(&_aqualink_data));
- else if (delayAckCnt < MAX_BUSY_ACK) // say we are pausing
- send_extended_ack(rs_fd, ACK_SCREEN_BUSY, pop_aq_cmd(&_aqualink_data));
- else // We timed out pause, send normal ack (This should also reset the display message on next message received)
- send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
-
- delayAckCnt++;
- }
- }
+ //logMessage(LOG_DEBUG, "RS received packet of type %s length %d\n", get_packet_type(packet_buffer, packet_length), packet_length);
+ //debugPacketPrint(0x00, packet_buffer, packet_length);
+ //unsigned char ID, unsigned char *packet_buffer, int packet_length)
// Process the packet. This includes deriving general status, and identifying
// warnings and errors. If something changed, notify any listeners
- if (process_packet(packet_buffer, packet_length) != false) {
+ if (process_packet(packet_buffer, packet_length) != false)
+ {
broadcast_aqualinkstate(mgr.active_connections);
}
- } else if (packet_length > 0 && _config_parameters.read_all_devices == true) {
+ // If we are not in PDA or Simulator mode, just sent ACK & any CMD, else caculate the ACK.
+ if (!_aqualink_data.simulate_panel && !_config_parameters.pda_mode)
+ send_ack(rs_fd, pop_aq_cmd(&_aqualink_data));
+ else
+ caculate_ack_packet(rs_fd, packet_buffer);
+ }
+ else if (packet_length > 0 && _config_parameters.read_all_devices == true)
+ {
//logPacket(packet_buffer, packet_length);
- if (packet_buffer[PKT_DEST] == DEV_MASTER && interestedInNextAck == true) {
- if ( packet_buffer[PKT_CMD] == CMD_PPM ) {
+ if (packet_buffer[PKT_DEST] == DEV_MASTER && interestedInNextAck == true)
+ {
+ if (packet_buffer[PKT_CMD] == CMD_PPM)
+ {
_aqualink_data.ar_swg_status = packet_buffer[5];
- if (_aqualink_data.swg_delayed_percent != TEMP_UNKNOWN && _aqualink_data.ar_swg_status == 0x00) { // We have a delayed % to set.
+ if (_aqualink_data.swg_delayed_percent != TEMP_UNKNOWN && _aqualink_data.ar_swg_status == 0x00)
+ { // We have a delayed % to set.
char sval[10];
snprintf(sval, 9, "%d", _aqualink_data.swg_delayed_percent);
aq_programmer(AQ_SET_SWG_PERCENT, sval, &_aqualink_data);
- logMessage(LOG_NOTICE, "Setting SWG %% to %d, from delayed message\n",_aqualink_data.swg_delayed_percent);
+ logMessage(LOG_NOTICE, "Setting SWG %% to %d, from delayed message\n", _aqualink_data.swg_delayed_percent);
_aqualink_data.swg_delayed_percent = TEMP_UNKNOWN;
}
_aqualink_data.swg_ppm = packet_buffer[4] * 100;
}
interestedInNextAck = false;
- } else if (interestedInNextAck == true && packet_buffer[PKT_DEST] != DEV_MASTER && _aqualink_data.ar_swg_status != 0x00 ) {
+ }
+ else if (interestedInNextAck == true && packet_buffer[PKT_DEST] != DEV_MASTER && _aqualink_data.ar_swg_status != 0x00)
+ {
_aqualink_data.ar_swg_status = SWG_STATUS_OFF;
interestedInNextAck = false;
- } else if (packet_buffer[PKT_DEST] == SWG_DEV_ID) {
+ }
+ else if (packet_buffer[PKT_DEST] == SWG_DEV_ID)
+ {
interestedInNextAck = true;
- if ( packet_buffer[3] == CMD_PERCENT && _aqualink_data.active_thread.thread_id == 0 ) {
+ if (packet_buffer[3] == CMD_PERCENT && _aqualink_data.active_thread.thread_id == 0)
+ {
// Only read SWG Percent if we are not programming, as we might be changing this
_aqualink_data.swg_percent = (int)packet_buffer[4];
}
- } else {
+ }
+ else
+ {
interestedInNextAck = false;
}
}
@@ -1097,15 +997,16 @@ void main_loop() {
logMessage(LOG_DEBUG_SERIAL, "Received Packet for ID 0x%02hhx of type %s %s\n",packet_buffer[PKT_DEST], get_packet_type(packet_buffer, packet_length),
(packet_buffer[PKT_DEST] == _config_parameters.device_id)?" <-- Aqualinkd ID":"");
}*/
-
}
mg_mgr_poll(&mgr, 0);
// Any unactioned commands
- if (_aqualink_data.unactioned.type != NO_ACTION) {
+ if (_aqualink_data.unactioned.type != NO_ACTION)
+ {
time_t now;
time(&now);
- if (difftime(now, _aqualink_data.unactioned.requested) > 2){
+ if (difftime(now, _aqualink_data.unactioned.requested) > 2)
+ {
logMessage(LOG_DEBUG, "Actioning delayed request\n");
action_delayed_request();
}
@@ -1118,7 +1019,8 @@ void main_loop() {
#endif
//}
}
-
+
+ if (_config_parameters.debug_RSProtocol_packets) closePacketLog();
// Reset and close the port.
close_serial_port(rs_fd);
// Clear webbrowser
@@ -1129,4 +1031,3 @@ void main_loop() {
logMessage(LOG_NOTICE, "Exit!\n");
exit(EXIT_FAILURE);
}
-
diff --git a/config.c b/config.c
index da0e5c9..5e5f6a2 100644
--- a/config.c
+++ b/config.c
@@ -75,11 +75,15 @@ void init_parameters (struct aqconfig * parms)
parms->deamonize = true;
parms->log_file = '\0';
parms->pda_mode = false;
+ parms->pda_sleep_mode = false;
parms->convert_mqtt_temp = true;
parms->convert_dz_temp = true;
parms->report_zero_pool_temp = false;
parms->report_zero_spa_temp = false;
parms->read_all_devices = true;
+ parms->use_panel_aux_labels = false;
+ parms->debug_RSProtocol_packets = false;
+ parms->force_swg = false;
generate_mqtt_id(parms->mqtt_ID, MQTT_ID_LEN);
}
@@ -377,6 +381,10 @@ bool setConfigValue(struct aqconfig *config_parameters, struct aqualinkdata *aqd
config_parameters->pda_mode = text2bool(value);
set_pda_mode(config_parameters->pda_mode);
rtn=true;
+ } else if (strncasecmp(param, "pda_sleep_mode", 8) == 0) {
+ config_parameters->pda_sleep_mode = text2bool(value);
+ //set_pda_mode(config_parameters->pda_mode);
+ rtn=true;
} else if (strncasecmp(param, "convert_mqtt_temp_to_c", 22) == 0) {
config_parameters->convert_mqtt_temp = text2bool(value);
rtn=true;
@@ -396,7 +404,17 @@ bool setConfigValue(struct aqconfig *config_parameters, struct aqualinkdata *aqd
} else if (strncasecmp (param, "read_all_devices", 16) == 0) {
config_parameters->read_all_devices = text2bool(value);
rtn=true;
+ } else if (strncasecmp (param, "use_panel_aux_labels", 20) == 0) {
+ config_parameters->use_panel_aux_labels = text2bool(value);
+ rtn=true;
+ } else if (strncasecmp (param, "force_SWG", 9) == 0) {
+ config_parameters->force_swg = text2bool(value);
+ rtn=true;
+ } else if (strncasecmp (param, "debug_RSProtocol_packets", 24) == 0) {
+ config_parameters->debug_RSProtocol_packets = text2bool(value);
+ rtn=true;
}
+
// removed until domoticz has a better virtual thermostat
/*else if (strncasecmp (param, "pool_thermostat_dzidx", 21) == 0) {
config_parameters->dzidx_pool_thermostat = strtoul(value, NULL, 10);
diff --git a/config.h b/config.h
index 7ae6224..7e1e37b 100644
--- a/config.h
+++ b/config.h
@@ -50,12 +50,16 @@ struct aqconfig
int light_programming_button;
bool override_freeze_protect;
bool pda_mode;
+ bool pda_sleep_mode;
bool convert_mqtt_temp;
bool convert_dz_temp;
//bool flash_mqtt_buttons;
bool report_zero_spa_temp;
bool report_zero_pool_temp;
bool read_all_devices;
+ bool use_panel_aux_labels;
+ bool force_swg;
+ bool debug_RSProtocol_packets;
//int dzidx_pool_thermostat; // Domoticz virtual thermostats are crap removed until better
//int dzidx_spa_thermostat; // Domoticz virtual thermostats are crap removed until better
//char mqtt_pub_topic[250];
@@ -69,5 +73,6 @@ void init_parameters (struct aqconfig * parms);
void readCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqualink_data, char *cfgFile);
bool writeCfg (struct aqconfig *config_parameters, struct aqualinkdata *aqdata);
bool setConfigValue(struct aqconfig *config_parameters, struct aqualinkdata *aqdata, char *param, char *value);
+char *cleanalloc(char*str);
#endif
diff --git a/net_services.c b/net_services.c
index 168d37f..5838803 100644
--- a/net_services.c
+++ b/net_services.c
@@ -33,6 +33,7 @@
#include "json_messages.h"
#include "domoticz.h"
#include "aq_mqtt.h"
+#include "pda.h"
static struct aqconfig *_aqualink_config;
@@ -761,7 +762,11 @@ void action_web_request(struct mg_connection *nc, struct http_message *http_msg)
void action_websocket_request(struct mg_connection *nc, struct websocket_message *wm) {
char buffer[50];
struct JSONwebrequest request;
-
+
+ // Any websocket request means UI is active, so don't let AqualinkD go to sleep if in PDA mode
+ if (pda_mode())
+ pda_reset_sleep();
+
strncpy(buffer, (char *)wm->data, wm->size);
buffer[wm->size] = '\0';
// logMessage (LOG_DEBUG, "buffer '%s'\n", buffer);
diff --git a/pda.c b/pda.c
new file mode 100644
index 0000000..99cacfe
--- /dev/null
+++ b/pda.c
@@ -0,0 +1,557 @@
+
+#define _GNU_SOURCE 1 // for strcasestr & strptime
+#include
+#include
+#include
+#include
+
+#include "aqualink.h"
+#include "init_buttons.h"
+#include "pda_menu.h"
+#include "utils.h"
+
+// static struct aqualinkdata _aqualink_data;
+static struct aqualinkdata *_aqualink_data;
+static unsigned char _last_packet_type;
+static unsigned long _pda_loop_cnt = 0;
+static bool _initWithRS = false;
+
+// Each RS message is around 0.5 seconds apart, so 2 mins = 120 seconds = 240 polls
+#define PDA_LOOP_COUNT 240 // 2 mins in poll (sleep timer)
+
+void init_pda(struct aqualinkdata *aqdata)
+{
+ _aqualink_data = aqdata;
+ set_pda_mode(true);
+ //clock_gettime(CLOCK_REALTIME, &_aqualink_data->last_active_time);
+ //aq_programmer(AQ_PDA_INIT, NULL, _aqualink_data); // NEED TO MOVE THIS. Can't run this here incase serial connection fails / needs to be cleaned up.
+}
+
+
+bool pda_shouldSleep() {
+ //logMessage(LOG_DEBUG, "PDA loop count %d, will sleep at %d\n",_pda_loop_cnt,PDA_LOOP_COUNT);
+ if (_pda_loop_cnt++ < PDA_LOOP_COUNT) {
+ return false;
+ } else if (_pda_loop_cnt > PDA_LOOP_COUNT*2) {
+ _pda_loop_cnt = 0;
+ return false;
+ }
+
+ return true;
+}
+
+void pda_wake() {
+
+}
+
+void pda_reset_sleep() {
+ _pda_loop_cnt = 0;
+}
+
+unsigned char get_last_pda_packet_type()
+{
+ return _last_packet_type;
+}
+
+void set_pda_led(struct aqualinkled *led, char state)
+{
+ aqledstate old_state = led->state;
+ if (state == 'N')
+ {
+ led->state = ON;
+ }
+ else if (state == 'A')
+ {
+ led->state = ENABLE;
+ }
+ else if (state == '*')
+ {
+ led->state = FLASH;
+ }
+ else
+ {
+ led->state = OFF;
+ }
+ if (old_state != led->state)
+ {
+ logMessage(LOG_DEBUG, "set_pda_led from %d to %d\n", old_state, led->state);
+ }
+}
+
+void pass_pda_equiptment_status_item(char *msg)
+{
+ static char *index;
+ int i;
+
+ // EQUIPMENT STATUS
+ //
+ // AquaPure 100%
+ // SALT 25500 PPM
+ // FILTER PUMP
+ // POOL HEAT
+ // SPA HEAT ENA
+
+ // EQUIPMENT STATUS
+ //
+ // FREEZE PROTECT
+ // AquaPure 100%
+ // SALT 25500 PPM
+ // CHECK AquaPure
+ // GENERAL FAULT
+ // FILTER PUMP
+ // CLEANER
+ //
+
+ // Check message for status of device
+ // Loop through all buttons and match the PDA text.
+ if ((index = strcasestr(msg, "CHECK AquaPure")) != NULL)
+ {
+ logMessage(LOG_DEBUG, "CHECK AquaPure\n");
+ }
+ else if ((index = strcasestr(msg, "FREEZE PROTECT")) != NULL)
+ {
+ _aqualink_data->frz_protect_state = ON;
+ }
+ else if ((index = strcasestr(msg, MSG_SWG_PCT)) != NULL)
+ {
+ _aqualink_data->swg_percent = atoi(index + strlen(MSG_SWG_PCT));
+ logMessage(LOG_DEBUG, "AquaPure = %d\n", _aqualink_data->swg_percent);
+ }
+ else if ((index = strcasestr(msg, MSG_SWG_PPM)) != NULL)
+ {
+ _aqualink_data->swg_ppm = atoi(index + strlen(MSG_SWG_PPM));
+ logMessage(LOG_DEBUG, "SALT = %d\n", _aqualink_data->swg_ppm);
+ }
+ else
+ {
+ char labelBuff[AQ_MSGLEN + 1];
+ strncpy(labelBuff, msg, AQ_MSGLEN + 1);
+ msg = stripwhitespace(labelBuff);
+
+ if (strcasecmp(msg, "POOL HEAT ENA") == 0)
+ {
+ _aqualink_data->aqbuttons[POOL_HEAT_INDEX].led->state = ENABLE;
+ }
+ else if (strcasecmp(msg, "SPA HEAT ENA") == 0)
+ {
+ _aqualink_data->aqbuttons[SPA_HEAT_INDEX].led->state = ENABLE;
+ }
+ else
+ {
+ for (i = 0; i < TOTAL_BUTTONS; i++)
+ {
+ if (strcasecmp(msg, _aqualink_data->aqbuttons[i].pda_label) == 0)
+ {
+ logMessage(LOG_DEBUG, "*** Found Status for %s = '%.*s'\n", _aqualink_data->aqbuttons[i].pda_label, AQ_MSGLEN, msg);
+ // It's on (or delayed) if it's listed here.
+ if (_aqualink_data->aqbuttons[i].led->state != FLASH)
+ {
+ _aqualink_data->aqbuttons[i].led->state = ON;
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+void process_pda_packet_msg_long_temp(const char *msg)
+{
+ // 'AIR POOL'
+ // ' 86` 86` '
+ // 'AIR SPA '
+ // ' 86` 86` '
+ // 'AIR '
+ // ' 86` '
+ _aqualink_data->temp_units = FAHRENHEIT; // Force FAHRENHEIT
+ if (stristr(pda_m_line(1), "AIR") != NULL)
+ _aqualink_data->air_temp = atoi(msg);
+
+ if (stristr(pda_m_line(1), "SPA") != NULL)
+ {
+ _aqualink_data->spa_temp = atoi(msg + 4);
+ _aqualink_data->pool_temp = TEMP_UNKNOWN;
+ }
+ else if (stristr(pda_m_line(1), "POOL") != NULL)
+ {
+ _aqualink_data->pool_temp = atoi(msg + 7);
+ _aqualink_data->spa_temp = TEMP_UNKNOWN;
+ }
+ else
+ {
+ _aqualink_data->pool_temp = TEMP_UNKNOWN;
+ _aqualink_data->spa_temp = TEMP_UNKNOWN;
+ }
+ // printf("Air Temp = %d | Water Temp = %d\n",atoi(msg),atoi(msg+7));
+}
+
+void process_pda_packet_msg_long_time(const char *msg)
+{
+ // message " SAT 8:46AM "
+ // " SAT 10:29AM"
+ // " SAT 4:23PM "
+ // printf("TIME = '%.*s'\n",AQ_MSGLEN,msg );
+ // printf("TIME = '%c'\n",msg[AQ_MSGLEN-1] );
+ if (msg[AQ_MSGLEN - 1] == ' ')
+ {
+ strncpy(_aqualink_data->time, msg + 9, 6);
+ }
+ else
+ {
+ strncpy(_aqualink_data->time, msg + 9, 7);
+ }
+ strncpy(_aqualink_data->date, msg + 5, 3);
+ // :TODO: NSF Come back and change the above to correctly check date and time in future
+}
+
+void process_pda_packet_msg_long_equipment_control(const char *msg)
+{
+ // These are listed as "FILTER PUMP OFF"
+ int i;
+ char labelBuff[AQ_MSGLEN + 1];
+ strncpy(labelBuff, msg, AQ_MSGLEN - 4);
+ labelBuff[AQ_MSGLEN - 4] = 0;
+
+ logMessage(LOG_DEBUG, "*** Checking Equiptment '%s'\n", labelBuff);
+
+ for (i = 0; i < TOTAL_BUTTONS; i++)
+ {
+ if (strcasecmp(stripwhitespace(labelBuff), _aqualink_data->aqbuttons[i].pda_label) == 0)
+ {
+ logMessage(LOG_DEBUG, "*** Found EQ CTL Status for %s = '%.*s'\n", _aqualink_data->aqbuttons[i].pda_label, AQ_MSGLEN, msg);
+ set_pda_led(_aqualink_data->aqbuttons[i].led, msg[AQ_MSGLEN - 1]);
+ }
+ }
+}
+
+void process_pda_packet_msg_long_home(const char *msg)
+{
+ if (stristr(msg, "POOL MODE") != NULL)
+ {
+ // If pool mode is on the filter pump is on but if it is off the filter pump might be on if spa mode is on.
+ if (msg[AQ_MSGLEN - 1] == 'N')
+ {
+ _aqualink_data->aqbuttons[PUMP_INDEX].led->state = ON;
+ }
+ else if (msg[AQ_MSGLEN - 1] == '*')
+ {
+ _aqualink_data->aqbuttons[PUMP_INDEX].led->state = FLASH;
+ }
+ }
+ else if (stristr(msg, "POOL HEATER") != NULL)
+ {
+ set_pda_led(_aqualink_data->aqbuttons[POOL_HEAT_INDEX].led, msg[AQ_MSGLEN - 1]);
+ }
+ else if (stristr(msg, "SPA MODE") != NULL)
+ {
+ // when SPA mode is on the filter may be on or pending
+ if (msg[AQ_MSGLEN - 1] == 'N')
+ {
+ _aqualink_data->aqbuttons[PUMP_INDEX].led->state = ON;
+ _aqualink_data->aqbuttons[SPA_INDEX].led->state = ON;
+ }
+ else if (msg[AQ_MSGLEN - 1] == '*')
+ {
+ _aqualink_data->aqbuttons[PUMP_INDEX].led->state = FLASH;
+ _aqualink_data->aqbuttons[SPA_INDEX].led->state = ON;
+ }
+ else
+ {
+ _aqualink_data->aqbuttons[SPA_INDEX].led->state = OFF;
+ }
+ }
+ else if (stristr(msg, "SPA HEATER") != NULL)
+ {
+ set_pda_led(_aqualink_data->aqbuttons[SPA_HEAT_INDEX].led, msg[AQ_MSGLEN - 1]);
+ }
+}
+
+void process_pda_packet_msg_long_set_temp(const char *msg)
+{
+ logMessage(LOG_DEBUG, "process_pda_packet_msg_long_set_temp\n");
+
+ if (stristr(msg, "POOL HEAT") != NULL)
+ {
+ _aqualink_data->pool_htr_set_point = atoi(msg + 10);
+ logMessage(LOG_DEBUG, "pool_htr_set_point = %d\n", _aqualink_data->pool_htr_set_point);
+ }
+ else if (stristr(msg, "SPA HEAT") != NULL)
+ {
+ _aqualink_data->spa_htr_set_point = atoi(msg + 10);
+ logMessage(LOG_DEBUG, "spa_htr_set_point = %d\n", _aqualink_data->spa_htr_set_point);
+ }
+}
+
+void process_pda_packet_msg_long_spa_heat(const char *msg)
+{
+ if (strncmp(msg, " ENABLED ", 16) == 0)
+ {
+ _aqualink_data->aqbuttons[SPA_HEAT_INDEX].led->state = ENABLE;
+ }
+ else if (strncmp(msg, " SET TO", 8) == 0)
+ {
+ _aqualink_data->spa_htr_set_point = atoi(msg + 8);
+ logMessage(LOG_DEBUG, "spa_htr_set_point = %d\n", _aqualink_data->spa_htr_set_point);
+ }
+}
+
+void process_pda_packet_msg_long_pool_heat(const char *msg)
+{
+ if (strncmp(msg, " ENABLED ", 16) == 0)
+ {
+ _aqualink_data->aqbuttons[POOL_HEAT_INDEX].led->state = ENABLE;
+ }
+ else if (strncmp(msg, " SET TO", 8) == 0)
+ {
+ _aqualink_data->pool_htr_set_point = atoi(msg + 8);
+ logMessage(LOG_DEBUG, "pool_htr_set_point = %d\n", _aqualink_data->pool_htr_set_point);
+ }
+}
+
+void process_pda_packet_msg_long_freeze_protect(const char *msg)
+{
+ if (strncmp(msg, "TEMP ", 10) == 0)
+ {
+ _aqualink_data->frz_protect_set_point = atoi(msg + 10);
+ logMessage(LOG_DEBUG, "frz_protect_set_point = %d\n", _aqualink_data->frz_protect_set_point);
+ }
+}
+
+void process_pda_packet_msg_long_SWG(const char *msg)
+{
+ //PDA Line 0 = SET AquaPure
+ //PDA Line 1 =
+ //PDA Line 2 =
+ //PDA Line 3 = SET POOL TO: 45%
+ //PDA Line 4 = SET SPA TO: 0%
+
+ // If spa is on, read SWG for spa, if not set SWG for pool
+ if (_aqualink_data->aqbuttons[SPA_INDEX].led->state != OFF) {
+ if (strncmp(msg, "SET SPA TO:", 11) == 0)
+ {
+ _aqualink_data->swg_percent = atoi(msg + 13);
+ logMessage(LOG_DEBUG, "SPA swg_percent = %d\n", _aqualink_data->swg_percent);
+ }
+ } else {
+ if (strncmp(msg, "SET POOL TO:", 12) == 0)
+ {
+ _aqualink_data->swg_percent = atoi(msg + 13);
+ logMessage(LOG_DEBUG, "POOL swg_percent = %d\n", _aqualink_data->swg_percent);
+ }
+ }
+}
+
+void process_pda_packet_msg_long_unknown(const char *msg)
+{
+ int i;
+ // Lets make a guess here and just see if there is an ON/OFF/ENA/*** at the end of the line
+ // When you turn on/off a piece of equiptment, a clear screen followed by single message is sent.
+ // So we are not in any PDA menu, try to catch that message here so we catch new device state ASAP.
+ if (msg[AQ_MSGLEN - 1] == 'N' || msg[AQ_MSGLEN - 1] == 'F' || msg[AQ_MSGLEN - 1] == 'A' || msg[AQ_MSGLEN - 1] == '*')
+ {
+ for (i = 0; i < TOTAL_BUTTONS; i++)
+ {
+ if (stristr(msg, _aqualink_data->aqbuttons[i].pda_label) != NULL)
+ {
+ printf("*** UNKNOWN Found Status for %s = '%.*s'\n", _aqualink_data->aqbuttons[i].pda_label, AQ_MSGLEN, msg);
+ // set_pda_led(_aqualink_data->aqbuttons[i].led, msg[AQ_MSGLEN-1]);
+ }
+ }
+ }
+}
+
+void process_pda_freeze_protect_devices()
+{
+ // PDA Line 0 = FREEZE PROTECT
+ // PDA Line 1 = DEVICES
+ // PDA Line 2 =
+ // PDA Line 3 = FILTER PUMP X
+ // PDA Line 4 = SPA
+ // PDA Line 5 = CLEANER X
+ // PDA Line 6 = POOL LIGHT
+ // PDA Line 7 = SPA LIGHT
+ // PDA Line 8 = EXTRA AUX
+ // PDA Line 9 =
+ int i;
+ logMessage(LOG_DEBUG, "process_pda_freeze_protect_devices\n");
+ for (i = 1; i < PDA_LINES; i++)
+ {
+ if (pda_m_line(i)[AQ_MSGLEN - 1] == 'X')
+ {
+ logMessage(LOG_DEBUG, "PDA freeze protect enabled by %s\n", pda_m_line(i));
+ if (_aqualink_data->frz_protect_state == OFF)
+ {
+ _aqualink_data->frz_protect_state = ENABLE;
+ break;
+ }
+ }
+ }
+}
+
+bool process_pda_packet(unsigned char *packet, int length)
+{
+ bool rtn = true;
+ int i;
+ char *msg;
+ static bool init = false;
+ static time_t _lastStatus = 0;
+
+ if (_lastStatus == 0)
+ {
+ time(&_lastStatus);
+ }
+
+ process_pda_menu_packet(packet, length);
+
+ // NSF.
+
+ //_aqualink_data->last_msg_was_status = false;
+
+ // debugPacketPrint(0x00, packet, length);
+
+ switch (packet[PKT_CMD])
+ {
+
+ case CMD_ACK:
+ logMessage(LOG_DEBUG, "RS Received ACK length %d.\n", length);
+ if (init == false)
+ {
+ aq_programmer(AQ_PDA_INIT, NULL, _aqualink_data);
+ init = true;
+ }
+ break;
+
+ case CMD_STATUS:
+ _aqualink_data->last_display_message[0] = '\0';
+
+ // If we get a status packet, and we are on the status menu, this is a list of what's on
+ // or pending so unless flash turn everything off, and just turn on items that are listed.
+ // This is the only way to update a device that's been turned off by a real PDA / keypad.
+ // Note: if the last line of the status menu is present it may be cut off
+ if (pda_m_type() == PM_EQUIPTMENT_STATUS)
+ {
+ if (_aqualink_data->frz_protect_state == ON)
+ {
+ _aqualink_data->frz_protect_state = ENABLE;
+ }
+ if (pda_m_line(PDA_LINES - 1)[0] == '\0')
+ {
+ for (i = 0; i < TOTAL_BUTTONS; i++)
+ {
+ if (_aqualink_data->aqbuttons[i].led->state != FLASH)
+ {
+ _aqualink_data->aqbuttons[i].led->state = OFF;
+ }
+ }
+ }
+ else
+ {
+ logMessage(LOG_DEBUG, "PDA Equipment status may be truncated.\n");
+ }
+ for (i = 1; i < PDA_LINES; i++)
+ {
+ pass_pda_equiptment_status_item(pda_m_line(i));
+ }
+ time(&_lastStatus);
+ }
+ else
+ {
+ time_t now;
+ time(&now);
+ if (init && difftime(now, _lastStatus) > 60)
+ {
+ logMessage(LOG_DEBUG, "OVER 60 SECONDS SINCE LAST STATUS UPDATE, forcing refresh\n");
+ // Reset aquapure to nothing since it must be off at this point
+ _aqualink_data->pool_temp = TEMP_UNKNOWN;
+ _aqualink_data->spa_temp = TEMP_UNKNOWN;
+ time(&_lastStatus);
+ aq_programmer(AQ_PDA_DEVICE_STATUS, NULL, _aqualink_data);
+ }
+ }
+ if (pda_m_type() == PM_FREEZE_PROTECT_DEVICES)
+ {
+ process_pda_freeze_protect_devices();
+ }
+ break;
+ case CMD_MSG_LONG:
+ {
+ //printf ("*******************************************************************************************\n");
+ //printf ("menu type %d\n",pda_m_type());
+
+ msg = (char *)packet + PKT_DATA + 1;
+
+ //strcpy(_aqualink_data->last_message, msg);
+
+ if (packet[PKT_DATA] == 0x82)
+ { // Air & Water temp is always this ID
+ process_pda_packet_msg_long_temp(msg);
+ }
+ else if (packet[PKT_DATA] == 0x40)
+ { // Time is always on this ID
+ process_pda_packet_msg_long_time(msg);
+ // If it wasn't a specific msg, (above) then run through and see what kind
+ // of message it is depending on the PDA menu. Note don't process EQUIPTMENT
+ // STATUS menu here, wait until a CMD_STATUS is received.
+ }
+ else {
+ switch (pda_m_type()) {
+ case PM_EQUIPTMENT_CONTROL:
+ process_pda_packet_msg_long_equipment_control(msg);
+ break;
+ case PM_HOME:
+ case PM_BUILDING_HOME:
+ process_pda_packet_msg_long_home(msg);
+ break;
+ case PM_SET_TEMP:
+ process_pda_packet_msg_long_set_temp(msg);
+ break;
+ case PM_SPA_HEAT:
+ process_pda_packet_msg_long_spa_heat(msg);
+ break;
+ case PM_POOL_HEAT:
+ process_pda_packet_msg_long_pool_heat(msg);
+ break;
+ case PM_FREEZE_PROTECT:
+ process_pda_packet_msg_long_freeze_protect(msg);
+ break;
+ case PM_AQUAPURE:
+ process_pda_packet_msg_long_SWG(msg);
+ break;
+ case PM_UNKNOWN:
+ default:
+ process_pda_packet_msg_long_unknown(msg);
+ break;
+ }
+ }
+
+ // printf("** Line index='%d' Highligh='%s' Message='%.*s'\n",pda_m_hlightindex(), pda_m_hlight(), AQ_MSGLEN, msg);
+ logMessage(LOG_INFO, "PDA Menu '%d' Selectedline '%s', Last line received '%.*s'\n", pda_m_type(), pda_m_hlight(), AQ_MSGLEN, msg);
+ break;
+ }
+ case CMD_PDA_0x1B:
+ {
+ // We get two of these on startup, one with 0x00 another with 0x01 at index 4. Just act on one.
+ // Think this is PDA finishd showing startup screen
+ if (packet[4] == 0x00) {
+ if (_initWithRS == false)
+ {
+ _initWithRS = true;
+ logMessage(LOG_DEBUG, "**** PDA INIT ****");
+ aq_programmer(AQ_PDA_INIT, NULL, _aqualink_data);
+ } else {
+ logMessage(LOG_DEBUG, "**** PDA WAKE INIT ****");
+ aq_programmer(AQ_PDA_WAKE_INIT, NULL, _aqualink_data);
+ }
+ }
+ }
+ break;
+ }
+
+ if (packet[PKT_CMD] == CMD_MSG_LONG || packet[PKT_CMD] == CMD_PDA_HIGHLIGHT ||
+ packet[PKT_CMD] == CMD_PDA_SHIFTLINES || packet[PKT_CMD] == CMD_PDA_CLEAR)
+ {
+ // We processed the next message, kick any threads waiting on the message.
+ kick_aq_program_thread(_aqualink_data);
+ }
+ return rtn;
+}
\ No newline at end of file
diff --git a/pda.h b/pda.h
new file mode 100644
index 0000000..88af360
--- /dev/null
+++ b/pda.h
@@ -0,0 +1,12 @@
+
+
+#ifndef PDA_H_
+#define PDA_H_
+
+void init_pda(struct aqualinkdata *aqdata);
+bool process_pda_packet(unsigned char* packet, int length);
+bool pda_shouldSleep();
+void pda_wake();
+void pda_reset_sleep();
+
+#endif // PDA_MESSAGES_H_
\ No newline at end of file
diff --git a/pda_aq_programmer.c b/pda_aq_programmer.c
new file mode 100644
index 0000000..e70b303
--- /dev/null
+++ b/pda_aq_programmer.c
@@ -0,0 +1,770 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#include "aqualink.h"
+#include "utils.h"
+#include "aq_programmer.h"
+#include "aq_serial.h"
+#include "pda.h"
+#include "pda_menu.h"
+#include "pda_aq_programmer.h"
+
+#include "init_buttons.h"
+
+bool waitForPDAMessageHighlight(struct aqualinkdata *aq_data, int highlighIndex, int numMessageReceived);
+bool waitForPDAMessageType(struct aqualinkdata *aq_data, unsigned char mtype, int numMessageReceived);
+bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu);
+bool wait_pda_selected_item(struct aqualinkdata *aq_data);
+bool waitForPDAnextMenu(struct aqualinkdata *aq_data);
+bool loopover_devices(struct aqualinkdata *aq_data);
+bool find_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, int charlimit);
+bool select_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, bool waitForNextMenu);
+
+static pda_type _PDA_Type;
+
+bool wait_pda_selected_item(struct aqualinkdata *aq_data)
+{
+ while (pda_m_hlightindex() == -1){
+ waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,10);
+ }
+
+ if (pda_m_hlightindex() == -1)
+ return false;
+ else
+ return true;
+}
+
+bool waitForPDAnextMenu(struct aqualinkdata *aq_data) {
+ waitForPDAMessageType(aq_data,CMD_PDA_CLEAR,10);
+ return waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,15);
+}
+
+bool loopover_devices(struct aqualinkdata *aq_data) {
+ int i;
+
+ if (! goto_pda_menu(aq_data, PM_EQUIPTMENT_CONTROL)) {
+ //logMessage(LOG_ERR, "PDA :- can't find main menu\n");
+ //cleanAndTerminateThread(threadCtrl);
+ return false;
+ }
+
+ // Should look for message "ALL OFF", that's end of device list.
+ for (i=0; i < 18 && pda_find_m_index("ALL OFF") == -1 ; i++) {
+ send_cmd(KEY_PDA_DOWN, aq_data);
+ //while (get_aq_cmd_length() > 0) { delay(200); }
+ //waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,3);
+ waitForMessage(aq_data, NULL, 1);
+ }
+
+ return true;
+}
+
+/*
+ if charlimit is set, use case insensitive match and limit chars.
+*/
+bool find_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, int charlimit) {
+ int i=pda_m_hlightindex();
+ int index = (charlimit == 0)?pda_find_m_index(menuText):pda_find_m_index_case(menuText, charlimit);
+
+ logMessage(LOG_DEBUG, "PDA Device programmer menu text '%s'\n",menuText);
+
+ if (index < 0) { // No menu, is there a page down. "PDA Line 9 = ^^ MORE __"
+ if (strncmp(pda_m_line(9)," ^^ MORE", 10) == 0) {
+ int j;
+ for(j=0; j < 20; j++) {
+ send_cmd(KEY_PDA_DOWN, aq_data);
+ //delay(500);
+ //wait_for_empty_cmd_buffer();
+ waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,2);
+ //waitForMessage(aq_data, NULL, 1);
+ index = (charlimit == 0)?pda_find_m_index(menuText):pda_find_m_index_case(menuText, charlimit);
+ if (index >= 0) {
+ i=pda_m_hlightindex();
+ break;
+ }
+ }
+ if (index < 0) {
+ logMessage(LOG_ERR, "PDA Device programmer couldn't find menu item on any page '%s'\n",menuText);
+ return false;
+ }
+ } else {
+ logMessage(LOG_ERR, "PDA Device programmer couldn't find menu item '%s'\n",menuText);
+ return false;
+ }
+ }
+
+ // Found the text we want in the menu, now move to that position and select it.
+ //logMessage(LOG_DEBUG, "******************PDA Device programmer menu text '%s' is at index %d\n",menuText, index);
+
+ if (i < index) {
+ for (i=pda_m_hlightindex(); i < index; i++) {
+ //logMessage(LOG_DEBUG, "******************PDA queue down index %d\n",i);
+ send_cmd(KEY_PDA_DOWN, aq_data);
+ }
+ } else if (i > index) {
+ for (i=pda_m_hlightindex(); i > index; i--) {
+ //logMessage(LOG_DEBUG, "******************PDA queue down index %d\n",i);
+ send_cmd(KEY_PDA_UP, aq_data);
+ }
+ }
+
+ return waitForPDAMessageHighlight(aq_data, index, 10);
+}
+
+bool select_pda_menu_item(struct aqualinkdata *aq_data, char *menuText, bool waitForNextMenu) {
+
+ if ( find_pda_menu_item(aq_data, menuText, 0) ) {
+ send_cmd(KEY_PDA_SELECT, aq_data);
+
+ logMessage(LOG_DEBUG, "PDA Device programmer selected menu item '%s'\n",menuText);
+ if (waitForNextMenu)
+ waitForPDAnextMenu(aq_data);
+
+ return true;
+ }
+
+ logMessage(LOG_ERR, "PDA Device programmer couldn't selected menu item '%s' at index %d\n",menuText, index);
+ return false;
+}
+
+bool goto_pda_menu(struct aqualinkdata *aq_data, pda_menu_type menu) {
+ //int i = 0;
+ //char *menuText;
+
+ logMessage(LOG_DEBUG, "PDA Device programmer request for menu %d\n",menu);
+
+ // Keep going back, checking each time to get to home.
+ while (pda_m_type() == PM_FW_VERSION || pda_m_type() == PM_BUILDING_HOME) {
+ //logMessage(LOG_DEBUG, "******************PDA Device programmer delay on firmware or building home menu\n");
+ delay(500);
+ }
+
+ while ( pda_m_type() != menu && pda_m_type() != PM_HOME ) {
+ if (pda_m_type() != PM_BUILDING_HOME) {
+ send_cmd(KEY_PDA_BACK, aq_data);
+ //logMessage(LOG_DEBUG, "******************PDA Device programmer selected back button\n",menu);
+ waitForPDAnextMenu(aq_data);
+ } else {
+ waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,15);
+ }
+ //logMessage(LOG_DEBUG, "******************PDA Device programmer menu type %d\n",pda_m_type());
+ //if (!wait_for_empty_cmd_buffer() || i++ > 6)
+ // return false;
+ }
+
+ if (pda_m_type() == menu)
+ return true;
+
+ switch (menu) {
+ //case PM_SYSTEM_SETUP:
+ // select_pda_menu_item(aq_data, "MENU");
+ //break;
+ case PM_EQUIPTMENT_CONTROL:
+ select_pda_menu_item(aq_data, "EQUIPMENT ON/OFF", true);
+ break;
+ case PM_PALM_OPTIONS:
+ select_pda_menu_item(aq_data, "MENU", true);
+ select_pda_menu_item(aq_data, "PALM OPTIONS", true);
+ case PM_AUX_LABEL:
+ if ( _PDA_Type == PDA) {
+ select_pda_menu_item(aq_data, "MENU", true);
+ select_pda_menu_item(aq_data, "SYSTEM SETUP", true); // This is a guess, (I have rev#)
+ select_pda_menu_item(aq_data, "LABEL AUX", true);
+ } else {
+ logMessage(LOG_ERR, "PDA in AquaPlalm mode, there is no SYSTEM SETUP / LABEL AUX menu\n");
+ }
+ break;
+ case PM_SYSTEM_SETUP:
+ if ( _PDA_Type == PDA) {
+ select_pda_menu_item(aq_data, "MENU", true);
+ select_pda_menu_item(aq_data, "SYSTEM SETUP", true); // This is a guess, (I have rev#)
+ } else {
+ logMessage(LOG_ERR, "PDA in AquaPlalm mode, there is no SYSTEM SETUP menu\n");
+ }
+ break;
+ case PM_FREEZE_PROTECT:
+ if ( _PDA_Type == PDA) {
+ select_pda_menu_item(aq_data, "MENU", true);
+ select_pda_menu_item(aq_data, "SYSTEM SETUP", true); // This is a guess, (I have rev#)
+ select_pda_menu_item(aq_data, "FREEZE PROTECT", true); // This is a guess, (I have rev#)
+ } else {
+ logMessage(LOG_ERR, "PDA in AquaPlalm mode, there is no SYSTEM SETUP / FREEZE PROTECT menu\n");
+ }
+ break;
+ case PM_AQUAPURE:
+ select_pda_menu_item(aq_data, "MENU", true);
+ select_pda_menu_item(aq_data, "SET AquaPure", true);
+ //select_pda_menu_item(aq_data, "LABEL AUX");
+ break;
+ case PM_SET_TEMP:
+ select_pda_menu_item(aq_data, "MENU", true);
+ select_pda_menu_item(aq_data, "SET TEMP", true);
+ //select_pda_menu_item(aq_data, "LABEL AUX");
+ break;
+ case PM_SET_TIME:
+ select_pda_menu_item(aq_data, "MENU", true);
+ select_pda_menu_item(aq_data, "SET TIME", true);
+ //select_pda_menu_item(aq_data, "LABEL AUX");
+ break;
+ default:
+ logMessage(LOG_ERR, "PDA Device programmer didn't understand requested menu\n");
+ return false;
+ break;
+ }
+
+ if (pda_m_type() != menu) {
+ logMessage(LOG_ERR, "PDA Device programmer didn't find a requested menu\n");
+ return true;
+ }
+
+ //logMessage(LOG_DEBUG, "******************PDA Device programmer request for menu %d found\n",menu);
+
+ return true;
+}
+
+
+
+void *set_aqualink_PDA_device_on_off( void *ptr )
+{
+ struct programmingThreadCtrl *threadCtrl;
+ threadCtrl = (struct programmingThreadCtrl *) ptr;
+ struct aqualinkdata *aq_data = threadCtrl->aq_data;
+ //int i=0;
+ //int found;
+ char device_name[15];
+
+ waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_DEVICE_STATUS);
+
+ char *buf = (char*)threadCtrl->thread_args;
+ int device = atoi(&buf[0]);
+ int state = atoi(&buf[5]);
+
+ if (device < 0 || device > TOTAL_BUTTONS) {
+ logMessage(LOG_ERR, "PDA Device On/Off :- bad device number '%d'\n",device);
+ cleanAndTerminateThread(threadCtrl);
+ return ptr;
+ }
+
+ logMessage(LOG_INFO, "PDA Device On/Off, device '%s', state %d\n",aq_data->aqbuttons[device].pda_label,state);
+
+ if (! goto_pda_menu(aq_data, PM_EQUIPTMENT_CONTROL)) {
+ logMessage(LOG_ERR, "PDA Device On/Off :- can't find main menu\n");
+ cleanAndTerminateThread(threadCtrl);
+ return ptr;
+ }
+
+ //Pad name with spaces so something like "SPA" doesn't match "SPA BLOWER"
+ sprintf(device_name,"%-14s\n",aq_data->aqbuttons[device].pda_label);
+ if ( find_pda_menu_item(aq_data, device_name, 13) ) {
+ if (aq_data->aqbuttons[device].led->state != state) {
+ //printf("*** Select State ***\n");
+ logMessage(LOG_INFO, "PDA Device On/Off, found device '%s', changing state\n",aq_data->aqbuttons[device].pda_label,state);
+ send_cmd(KEY_PDA_SELECT, aq_data);
+ while (get_aq_cmd_length() > 0) { delay(500); }
+ } else {
+ logMessage(LOG_INFO, "PDA Device On/Off, found device '%s', not changing state, is same\n",aq_data->aqbuttons[device].pda_label,state);
+ }
+ } else {
+ logMessage(LOG_ERR, "PDA Device On/Off, device '%s' not found\n",aq_data->aqbuttons[device].pda_label);
+ }
+
+ goto_pda_menu(aq_data, PM_HOME);
+ //while (_pgm_command != NUL) { delay(500); }
+
+ cleanAndTerminateThread(threadCtrl);
+
+ // just stop compiler error, ptr is not valid as it's just been freed
+ return ptr;
+
+}
+
+
+
+void *get_aqualink_PDA_device_status( void *ptr )
+{
+ struct programmingThreadCtrl *threadCtrl;
+ threadCtrl = (struct programmingThreadCtrl *) ptr;
+ struct aqualinkdata *aq_data = threadCtrl->aq_data;
+ //int i;
+
+ waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_DEVICE_STATUS);
+
+ if (! loopover_devices(aq_data)) {
+ logMessage(LOG_ERR, "PDA Device Status :- failed\n");
+ }
+
+ goto_pda_menu(aq_data, PM_HOME);
+
+ cleanAndTerminateThread(threadCtrl);
+
+ // just stop compiler error, ptr is not valid as it's just been freed
+ return ptr;
+}
+
+void *set_aqualink_PDA_init( void *ptr )
+{
+ struct programmingThreadCtrl *threadCtrl;
+ threadCtrl = (struct programmingThreadCtrl *) ptr;
+ struct aqualinkdata *aq_data = threadCtrl->aq_data;
+ //int i=0;
+
+ waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_INIT);
+
+ //int val = atoi((char*)threadCtrl->thread_args);
+
+ //logMessage(LOG_DEBUG, "PDA Init\n", val);
+
+ logMessage(LOG_DEBUG, "PDA Init\n");
+
+ if (pda_m_type() == PM_FW_VERSION) {
+ // check pda_m_line(1) to "AquaPalm"
+ if (strstr(pda_m_line(1), "AquaPalm") != NULL) {
+ _PDA_Type = AQUAPALM;
+ } else {
+ _PDA_Type = PDA;
+ }
+ char *ptr = pda_m_line(5);
+ ptr[AQ_MSGLEN+1] = '\0';
+ strcpy(aq_data->version, stripwhitespace(ptr));
+ }
+
+ // Get status of all devices
+ if (! loopover_devices(aq_data)) {
+ logMessage(LOG_ERR, "PDA Init :- can't find menu\n");
+ }
+
+ // Get heater setpoints
+ if (! get_PDA_aqualink_pool_spa_heater_temps(aq_data)) {
+ logMessage(LOG_ERR, "PDA Init :- Error getting heater setpoints\n");
+ }
+
+ // Get freeze protect setpoint, AquaPalm doesn't have freeze protect in menu.
+ if (_PDA_Type != AQUAPALM && ! get_PDA_freeze_protect_temp(aq_data)) {
+ logMessage(LOG_ERR, "PDA Init :- Error getting freeze setpoints\n");
+ }
+
+
+ goto_pda_menu(aq_data, PM_HOME);
+
+ pda_reset_sleep();
+
+ cleanAndTerminateThread(threadCtrl);
+
+ // just stop compiler error, ptr is not valid as it's just been freed
+ return ptr;
+}
+
+
+void *set_aqualink_PDA_wakeinit( void *ptr )
+{
+ struct programmingThreadCtrl *threadCtrl;
+ threadCtrl = (struct programmingThreadCtrl *) ptr;
+ struct aqualinkdata *aq_data = threadCtrl->aq_data;
+ //int i=0;
+
+ // At this point, we should probably just exit if there is a thread already going as
+ // it means the wake was called due to changing a device.
+ waitForSingleThreadOrTerminate(threadCtrl, AQ_PDA_INIT);
+
+ logMessage(LOG_DEBUG, "PDA Wake Init\n");
+
+ // Get status of all devices
+ if (! loopover_devices(aq_data)) {
+ logMessage(LOG_ERR, "PDA Init :- can't find menu\n");
+ }
+
+ cleanAndTerminateThread(threadCtrl);
+
+ // just stop compiler error, ptr is not valid as it's just been freed
+ return ptr;
+}
+
+
+bool get_PDA_freeze_protect_temp(struct aqualinkdata *aq_data) {
+
+ if ( _PDA_Type == PDA) {
+ if (! goto_pda_menu(aq_data, PM_FREEZE_PROTECT)) {
+ return false;
+ }
+ } else {
+ logMessage(LOG_INFO, "In PDA AquaPalm mode, freezepoints not supported\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool get_PDA_aqualink_pool_spa_heater_temps(struct aqualinkdata *aq_data) {
+
+ // Get heater setpoints
+ if (! goto_pda_menu(aq_data, PM_SET_TEMP)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool waitForPDAMessageHighlight(struct aqualinkdata *aq_data, int highlighIndex, int numMessageReceived)
+{
+ logMessage(LOG_DEBUG, "waitForPDAMessageHighlight index %d\n",highlighIndex);
+
+ if(pda_m_hlightindex() == highlighIndex) return true;
+
+ int i=0;
+ pthread_mutex_init(&aq_data->active_thread.thread_mutex, NULL);
+ pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
+
+ while( ++i <= numMessageReceived)
+ {
+ logMessage(LOG_DEBUG, "waitForPDAMessageHighlight last = 0x%02hhx : index %d : (%d of %d)\n",aq_data->last_packet_type,pda_m_hlightindex(),i,numMessageReceived);
+
+ if (aq_data->last_packet_type == CMD_PDA_HIGHLIGHT && pda_m_hlightindex() == highlighIndex) break;
+
+ pthread_cond_init(&aq_data->active_thread.thread_cond, NULL);
+ pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex);
+ }
+
+ pthread_mutex_unlock(&aq_data->active_thread.thread_mutex);
+
+ if (pda_m_hlightindex() != highlighIndex) {
+ //logMessage(LOG_ERR, "Could not select MENU of Aqualink control panel\n");
+ logMessage(LOG_DEBUG, "waitForPDAMessageHighlight: did not receive index '%d'\n",highlighIndex);
+ return false;
+ } else
+ logMessage(LOG_DEBUG, "waitForPDAMessageHighlight: received index '%d'\n",highlighIndex);
+
+ return true;
+}
+
+
+bool waitForPDAMessageType(struct aqualinkdata *aq_data, unsigned char mtype, int numMessageReceived)
+{
+ logMessage(LOG_DEBUG, "waitForPDAMessageType 0x%02hhx\n",mtype);
+
+ int i=0;
+ pthread_mutex_init(&aq_data->active_thread.thread_mutex, NULL);
+ pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
+
+ while( ++i <= numMessageReceived)
+ {
+ logMessage(LOG_DEBUG, "waitForPDAMessageType 0x%02hhx, last message type was 0x%02hhx (%d of %d)\n",mtype,aq_data->last_packet_type,i,numMessageReceived);
+
+ if (aq_data->last_packet_type == mtype) break;
+
+ pthread_cond_init(&aq_data->active_thread.thread_cond, NULL);
+ pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex);
+ }
+
+ pthread_mutex_unlock(&aq_data->active_thread.thread_mutex);
+
+ if (aq_data->last_packet_type != mtype) {
+ //logMessage(LOG_ERR, "Could not select MENU of Aqualink control panel\n");
+ logMessage(LOG_DEBUG, "waitForPDAMessageType: did not receive 0x%02hhx\n",mtype);
+ return false;
+ } else
+ logMessage(LOG_DEBUG, "waitForPDAMessageType: received 0x%02hhx\n",mtype);
+
+ return true;
+}
+
+bool set_PDA_numeric_field_value(struct aqualinkdata *aq_data, int val, int *cur_val, char *select_label, int step) {
+ int i;
+
+ // Should probably change below to call find_pda_menu_item(), rather than doing it here
+ // If we lease this, need to limit on the number of loops
+ while ( strncmp(pda_m_hlight(), select_label, 8) != 0 ) {
+ send_cmd(KEY_PDA_DOWN, aq_data);
+ delay(500); // Last message probably was CMD_PDA_HIGHLIGHT, so wait before checking.
+ waitForPDAMessageType(aq_data,CMD_PDA_HIGHLIGHT,2);
+ }
+
+ send_cmd(KEY_PDA_SELECT, aq_data);
+
+ if (val < *cur_val) {
+ logMessage(LOG_DEBUG, "PDA %s value : lower from %d to %d\n", select_label, *cur_val, val);
+ for (i = *cur_val; i > val; i=i-step) {
+ send_cmd(KEY_PDA_DOWN, aq_data);
+ }
+ } else if (val > *cur_val) {
+ logMessage(LOG_DEBUG, "PDA %s value : raise from %d to %d\n", select_label, *cur_val, val);
+ for (i = *cur_val; i < val; i=i+step) {
+ send_cmd(KEY_PDA_UP, aq_data);
+ }
+ } else {
+ logMessage(LOG_INFO, "PDA %s value : already at %d\n", select_label, val);
+ send_cmd(KEY_PDA_BACK, aq_data);
+ return true;
+ }
+
+ send_cmd(KEY_PDA_SELECT, aq_data);
+ logMessage(LOG_DEBUG, "PDA %s value : set to %d\n", select_label, *cur_val);
+
+ return true;
+}
+
+bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val) {
+
+ if (! goto_pda_menu(aq_data, PM_AQUAPURE)) {
+ logMessage(LOG_ERR, "Error getting setpoints menu\n");
+ }
+
+ if (aq_data->aqbuttons[SPA_INDEX].led->state != OFF)
+ set_PDA_numeric_field_value(aq_data, val, &aq_data->swg_percent, "SET SPA", 5);
+ else
+ set_PDA_numeric_field_value(aq_data, val, &aq_data->swg_percent, "SET POOL", 5);
+
+ return true;
+}
+
+bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, bool isPool) {
+ char label[10];
+ int *cur_val;
+
+ if (isPool) {
+ sprintf(label, "POOL HEAT");
+ cur_val = &aq_data->pool_htr_set_point;
+ } else {
+ sprintf(label, "SPA HEAT");
+ cur_val = &aq_data->spa_htr_set_point;
+ }
+
+ if (val == *cur_val) {
+ logMessage(LOG_INFO, "PDA %s setpoint : temp already %d\n", label, val);
+ send_cmd(KEY_PDA_BACK, aq_data);
+ return true;
+ }
+
+ if (! goto_pda_menu(aq_data, PM_SET_TEMP)) {
+ logMessage(LOG_ERR, "Error getting setpoints menu\n");
+ }
+
+ set_PDA_numeric_field_value(aq_data, val, cur_val, label, 1);
+
+ return true;
+}
+
+bool set_PDA_aqualink_freezeprotect_setpoint(struct aqualinkdata *aq_data, int val) {
+
+ if (! goto_pda_menu(aq_data, PM_FREEZE_PROTECT)) {
+ logMessage(LOG_ERR, "Error getting setpoints menu\n");
+ }
+
+ set_PDA_numeric_field_value(aq_data, val, &aq_data->frz_protect_set_point, "TEMP", 1);
+
+ return true;
+}
+
+
+/*
+bool waitForPDAMessage(struct aqualinkdata *aq_data, int numMessageReceived, unsigned char packettype)
+{
+ logMessage(LOG_DEBUG, "waitForPDAMessage %s %d\n",message,numMessageReceived);
+ int i=0;
+ pthread_mutex_init(&aq_data->active_thread.thread_mutex, NULL);
+ pthread_mutex_lock(&aq_data->active_thread.thread_mutex);
+ char* msgS;
+ char* ptr;
+
+ if (message != NULL) {
+ if (message[0] == '^')
+ msgS = &message[1];
+ else
+ msgS = message;
+ }
+
+ while( ++i <= numMessageReceived)
+ {
+ if (message != NULL)
+ logMessage(LOG_DEBUG, "Programming mode: loop %d of %d looking for '%s' received message '%s'\n",i,numMessageReceived,message,aq_data->last_message);
+ else
+ logMessage(LOG_DEBUG, "Programming mode: loop %d of %d waiting for next message, received '%s'\n",i,numMessageReceived,aq_data->last_message);
+
+ if (message != NULL) {
+ ptr = stristr(aq_data->last_message, msgS);
+ if (ptr != NULL) { // match
+ logMessage(LOG_DEBUG, "Programming mode: String MATCH\n");
+ if (msgS == message) // match & don't care if first char
+ break;
+ else if (ptr == aq_data->last_message) // match & do care if first char
+ break;
+ }
+ }
+
+ //logMessage(LOG_DEBUG, "Programming mode: looking for '%s' received message '%s'\n",message,aq_data->last_message);
+ pthread_cond_init(&aq_data->active_thread.thread_cond, NULL);
+ pthread_cond_wait(&aq_data->active_thread.thread_cond, &aq_data->active_thread.thread_mutex);
+ //logMessage(LOG_DEBUG, "Programming mode: loop %d of %d looking for '%s' received message '%s'\n",i,numMessageReceived,message,aq_data->last_message);
+ }
+
+ pthread_mutex_unlock(&aq_data->active_thread.thread_mutex);
+
+ if (message != NULL && ptr == NULL) {
+ //logMessage(LOG_ERR, "Could not select MENU of Aqualink control panel\n");
+ logMessage(LOG_DEBUG, "Programming mode: did not find '%s'\n",message);
+ return false;
+ } else if (message != NULL)
+ logMessage(LOG_DEBUG, "Programming mode: found message '%s' in '%s'\n",message,aq_data->last_message);
+
+ return true;
+}
+
+*/
+
+
+/*
+Link to two different menu's used in PDA
+http://www.poolequipmentpriceslashers.com.au/wp-content/uploads/2012/11/Jandy-Aqualink-RS-PDA-Wireless-Pool-Controller_manual.pdf
+https://www.jandy.com/-/media/zodiac/global/downloads/h/h0574200.pdf
+*/
+
+/*
+ List of how menu's display
+
+PDA Line 0 =
+PDA Line 1 = AquaPalm
+PDA Line 2 =
+PDA Line 3 = Firmware Version
+PDA Line 4 =
+PDA Line 5 = REV MMM
+PDA Line 6 =
+PDA Line 7 =
+PDA Line 8 =
+PDA Line 9 =
+
+***************** Think this is startup different rev *************
+Line 0 =
+Line 1 = PDA-PS4 Combo
+Line 2 =
+Line 3 = Firmware Version
+Line 4 =
+Line 5 = PPD: PDA 1.2
+
+PDA Line 0 =
+PDA Line 1 = AIR POOL
+PDA Line 2 =
+PDA Line 3 =
+PDA Line 4 = POOL MODE ON
+PDA Line 5 = POOL HEATER OFF
+PDA Line 6 = SPA MODE OFF
+PDA Line 7 = SPA HEATER OFF
+PDA Line 8 = MENU
+PDA Line 9 = EQUIPMENT ON/OFF
+
+PDA Line 0 = MAIN MENU
+PDA Line 1 =
+PDA Line 2 = SET TEMP >
+PDA Line 3 = SET TIME >
+PDA Line 4 = SET AquaPure >
+PDA Line 5 = PALM OPTIONS >
+PDA Line 6 =
+PDA Line 7 = BOOST POOL
+PDA Line 8 =
+PDA Line 9 =
+
+**************** OPTION 2 FOR THIS MENU ********************
+PDA Line 0 = MAIN MENU
+PDA Line 1 =
+PDA Line 2 = HELP >
+PDA Line 3 = PROGRAM >
+PDA Line 4 = SET TEMP >
+PDA Line 5 = SET TIME >
+PDA Line 6 = PDA OPTIONS >
+PDA Line 7 = SYSTEM SETUP >
+PDA Line 8 =
+PDA Line 9 = BOOST
+
+********** Guess at SYSTEM SETUP Menu (not on Rev MMM or before)************
+
+// PDA Line 0 = SYSTEM SETUP
+// PDA Line 1 = LABEL AUX >
+// PDA Line 2 = FREEZE PROTECT >
+// PDA Line 3 = AIR TEMP >
+// PDA Line 4 = DEGREES C/F >
+// PDA Line 5 = TEMP CALIBRATE >
+// PDA Line 6 = SOLAR PRIORITY >
+// PDA Line 7 = PUMP LOCKOUT >
+// PDA Line 8 = ASSIGN JVAs >
+// PDA Line 9 = ^^ MORE __
+// PDA Line 5 = COLOR LIGHTS >
+// PDA Line 6 = SPA SWITCH >
+// PDA Line 7 = SERVICE INFO >
+// PDA Line 8 = CLEAR MEMORY >
+
+
+
+PDA Line 0 = PALM OPTIONS
+PDA Line 1 =
+PDA Line 2 =
+PDA Line 3 = SET AUTO-OFF >
+PDA Line 4 = BACKLIGHT >
+PDA Line 5 = ASSIGN HOTKEYS >
+PDA Line 6 =
+PDA Line 7 = Choose setting
+PDA Line 8 = and press SELECT
+PDA Line 9 =
+
+PDA Line 0 = SET AquaPure
+PDA Line 1 =
+PDA Line 2 =
+PDA Line 3 = SET POOL TO: 45%
+PDA Line 4 = SET SPA TO: 0%
+PDA Line 5 =
+PDA Line 6 =
+PDA Line 7 = Highlight an
+PDA Line 8 = item and press
+PDA Line 9 = SELECT
+
+PDA Line 0 = SET TIME
+PDA Line 1 =
+PDA Line 2 = 05/22/19 WED
+PDA Line 3 = 10:53 AM
+PDA Line 4 =
+PDA Line 5 =
+PDA Line 6 = Use ARROW KEYS
+PDA Line 7 = to set value.
+PDA Line 8 = Press SELECT
+PDA Line 9 = to continue.
+
+PDA Line 0 = SET TEMP
+PDA Line 1 =
+PDA Line 2 = POOL HEAT 70`F
+PDA Line 3 = SPA HEAT 98`F
+PDA Line 4 =
+PDA Line 5 =
+PDA Line 6 =
+PDA Line 7 = Highlight an
+PDA Line 8 = item and press
+PDA Line 9 = SELECT
+
+
+
+
+
+PDA Line 0 = EQUIPMENT
+PDA Line 1 = FILTER PUMP ON
+PDA Line 2 = SPA OFF
+PDA Line 3 = POOL HEAT OFF
+PDA Line 4 = SPA HEAT OFF
+PDA Line 5 = CLEANER ON
+PDA Line 6 = WATERFALL OFF
+PDA Line 7 = AIR BLOWER OFF
+PDA Line 8 = LIGHT OFF
+PDA Line 9 = ^^ MORE __
+
+PDA Line 0 = EQUIPMENT
+PDA Line 1 = WATERFALL OFF
+PDA Line 2 = AIR BLOWER OFF
+PDA Line 3 = LIGHT OFF
+PDA Line 4 = AUX5 OFF
+PDA Line 5 = EXTRA AUX OFF
+PDA Line 6 = SPA MODE OFF
+PDA Line 7 = CLEAN MODE OFF
+PDA Line 8 = ALL OFF
+PDA Line 9 =
+
+*/
\ No newline at end of file
diff --git a/pda_aq_programmer.h b/pda_aq_programmer.h
new file mode 100644
index 0000000..3408838
--- /dev/null
+++ b/pda_aq_programmer.h
@@ -0,0 +1,21 @@
+
+#ifndef PDA_AQ_PROGRAMMER_H_
+#define PDA_AQ_PROGRAMMER_H_
+
+typedef enum pda_type {
+ AQUAPALM,
+ PDA
+} pda_type;
+
+void *get_aqualink_PDA_device_status( void *ptr );
+void *set_aqualink_PDA_device_on_off( void *ptr );
+void *set_aqualink_PDA_wakeinit( void *ptr );
+
+bool set_PDA_aqualink_heater_setpoint(struct aqualinkdata *aq_data, int val, bool isPool);
+bool set_PDA_aqualink_SWG_setpoint(struct aqualinkdata *aq_data, int val);
+bool set_PDA_aqualink_freezeprotect_setpoint(struct aqualinkdata *aq_data, int val);
+
+bool get_PDA_aqualink_pool_spa_heater_temps(struct aqualinkdata *aq_data);
+bool get_PDA_freeze_protect_temp(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 442afae..ef6f8e4 100644
--- a/pda_menu.c
+++ b/pda_menu.c
@@ -39,23 +39,81 @@ char *pda_m_line(int index)
// return NULL;
}
+int pda_find_m_index(char *text)
+{
+ int i;
+
+ for (i = 0; i < PDA_LINES; i++) {
+ if (strncmp(pda_m_line(i), text, strlen(text)) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+int pda_find_m_index_case(char *text, int limit)
+{
+ int i;
+
+ for (i = 0; i < PDA_LINES; i++) {
+ //printf ("+++ Compare '%s' to '%s' index %d\n",text,pda_m_line(i),i);
+ if (strncasecmp(pda_m_line(i), text, limit) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
pda_menu_type pda_m_type()
{
+
if (strncmp(_menu[1],"AIR ", 5) == 0)
- return PM_MAIN;
+ return PM_HOME;
else if (strncmp(_menu[0],"EQUIPMENT STATUS", 16) == 0)
return PM_EQUIPTMENT_STATUS;
else if (strncmp(_menu[0]," EQUIPMENT ", 16) == 0)
return PM_EQUIPTMENT_CONTROL;
else if (strncmp(_menu[0]," MAIN MENU ", 16) == 0)
- return PM_SETTINGS;
+ return PM_MAIN;
//else if ((_menu[0] == '\0' && _hlightindex == -1) || strncmp(_menu[4], "POOL MODE", 9) == 0 )// IF we are building the main menu this may be valid
- else if (strncmp(_menu[4], "POOL MODE", 9) == 0 )
- return PM_BUILDING_MAIN;
-
+ else if (strncmp(_menu[4], "POOL MODE", 9) == 0 ) {
+ if (pda_m_hlightindex() == -1)
+ return PM_BUILDING_HOME;
+ else
+ return PM_HOME;
+ }
+ else if (strncmp(_menu[0]," SET TEMP ", 16) == 0)
+ return PM_SET_TEMP;
+ else if (strncmp(_menu[0]," SET TIME ", 16) == 0)
+ return PM_SET_TIME;
+ else if (strncmp(_menu[0]," SET AquaPure ", 16) == 0)
+ return PM_AQUAPURE;
+ else if (strncmp(_menu[0]," SPA HEAT ", 16) == 0)
+ return PM_SPA_HEAT;
+ else if (strncmp(_menu[0]," POOL HEAT ", 16) == 0)
+ return PM_POOL_HEAT;
+ else if (strncmp(_menu[6],"Use ARROW KEYS ", 16) == 0 &&
+ strncmp(_menu[0]," FREEZE PROTECT ", 16) == 0)
+ return PM_FREEZE_PROTECT;
+ else if (strncmp(_menu[1]," DEVICES ", 16) == 0 &&
+ strncmp(_menu[0]," FREEZE PROTECT ", 16) == 0)
+ return PM_FREEZE_PROTECT_DEVICES;
+ else if (strncmp(_menu[3],"Firmware Version", 16) == 0 ||
+ strncmp(_menu[1]," AquaPalm", 12) == 0 ||
+ strncmp(_menu[1]," PDA-PS4 Combo", 14) == 0)
+ return PM_FW_VERSION;
+
return PM_UNKNOWN;
}
+
+
+
+
+
+
+
+
/*
--- Main Menu ---
Line 0 =
@@ -86,15 +144,105 @@ bool process_pda_menu_packet(unsigned char* packet, int length)
if (getLogLevel() >= LOG_DEBUG){print_menu();}
break;
case CMD_PDA_HIGHLIGHT:
- _hlightindex = packet[4],_menu[packet[4]];
+ // when switching from hlight to hlightchars index 255 is sent to turn off hlight
+ if (packet[4] <= PDA_LINES) {
+ _hlightindex = packet[4];
+ } else {
+ _hlightindex = -1;
+ }
+ if (getLogLevel() >= LOG_DEBUG){print_menu();}
+ break;
+ case CMD_PDA_HIGHLIGHTCHARS:
+ if (packet[4] <= PDA_LINES) {
+ _hlightindex = packet[4];
+ } else {
+ _hlightindex = -1;
+ }
if (getLogLevel() >= LOG_DEBUG){print_menu();}
break;
case CMD_PDA_SHIFTLINES:
memcpy(_menu[1], _menu[2], (PDA_LINES-1) * (AQ_MSGLEN+1) );
if (getLogLevel() >= LOG_DEBUG){print_menu();}
- break;
-
+ break;
}
return rtn;
}
+
+
+#ifdef SOME_CRAP
+bool NEW_process_pda_menu_packet_NEW(unsigned char* packet, int length)
+{
+ bool rtn = true;
+ signed char first_line;
+ signed char last_line;
+ signed char line_shift;
+ signed char i;
+
+ pthread_mutex_lock(&_pda_menu_mutex);
+ switch (packet[PKT_CMD]) {
+ case CMD_STATUS:
+ pthread_cond_signal(&_pda_menu_update_complete_cond);
+ break;
+ case CMD_PDA_CLEAR:
+ rtn = pda_m_clear();
+ break;
+ case CMD_MSG_LONG:
+ if (packet[PKT_DATA] < 10) {
+ memset(_menu[packet[PKT_DATA]], 0, AQ_MSGLEN);
+ strncpy(_menu[packet[PKT_DATA]], (char*)packet+PKT_DATA+1, AQ_MSGLEN);
+ _menu[packet[PKT_DATA]][AQ_MSGLEN] = '\0';
+ }
+ if (packet[PKT_DATA] == _hlightindex) {
+ logMessage(LOG_DEBUG, "process_pda_menu_packet: hlight changed from shift or up/down value\n");
+ pthread_cond_signal(&_pda_menu_hlight_change_cond);
+ }
+ if (getLogLevel() >= LOG_DEBUG){print_menu();}
+ update_pda_menu_type();
+ break;
+ case CMD_PDA_HIGHLIGHT:
+ // when switching from hlight to hlightchars index 255 is sent to turn off hlight
+ if (packet[4] <= PDA_LINES) {
+ _hlightindex = packet[4];
+ } else {
+ _hlightindex = -1;
+ }
+ pthread_cond_signal(&_pda_menu_hlight_change_cond);
+ if (getLogLevel() >= LOG_DEBUG){print_menu();}
+ break;
+ case CMD_PDA_HIGHLIGHTCHARS:
+ if (packet[4] <= PDA_LINES) {
+ _hlightindex = packet[4];
+ } else {
+ _hlightindex = -1;
+ }
+ pthread_cond_signal(&_pda_menu_hlight_change_cond);
+ if (getLogLevel() >= LOG_DEBUG){print_menu();}
+ break;
+ case CMD_PDA_SHIFTLINES:
+ // press up from top - shift menu down by 1
+ // PDA Shif | HEX: 0x10|0x02|0x62|0x0f|0x01|0x08|0x01|0x8d|0x10|0x03|
+ // press down from bottom - shift menu up by 1
+ // PDA Shif | HEX: 0x10|0x02|0x62|0x0f|0x01|0x08|0xff|0x8b|0x10|0x03|
+ first_line = (signed char)(packet[4]);
+ last_line = (signed char)(packet[5]);
+ line_shift = (signed char)(packet[6]);
+ logMessage(LOG_DEBUG, "\n");
+ if (line_shift < 0) {
+ for (i = first_line-line_shift; i <= last_line; i++) {
+ memcpy(_menu[i+line_shift], _menu[i], AQ_MSGLEN+1);
+ }
+ } else {
+ for (i = last_line; i >= first_line+line_shift; i--) {
+ memcpy(_menu[i], _menu[i-line_shift], AQ_MSGLEN+1);
+ }
+ }
+ if (getLogLevel() >= LOG_DEBUG){print_menu();}
+ break;
+
+ }
+ pthread_mutex_unlock(&_pda_menu_mutex);
+
+ return rtn;
+}
+#endif
\ No newline at end of file
diff --git a/pda_menu.h b/pda_menu.h
index c07dec1..06e99b4 100644
--- a/pda_menu.h
+++ b/pda_menu.h
@@ -5,6 +5,38 @@
#define PDA_LINES 10 // There is only 9 lines, but add buffer to make shifting easier
+typedef enum pda_menu_type {
+ PM_UNKNOWN,
+ PM_FW_VERSION,
+ PM_HOME,
+ PM_BUILDING_HOME,
+ PM_MAIN,
+ PM_DIAGNOSTICS,
+ PM_PROGRAM,
+ PM_SET_TEMP,
+ PM_SET_TIME,
+ PM_POOL_HEAT,
+ PM_SPA_HEAT,
+ PM_AQUAPURE,
+ PM_SYSTEM_SETUP,
+ PM_AUX_LABEL,
+ PM_FREEZE_PROTECT,
+ PM_FREEZE_PROTECT_DEVICES,
+ PM_VSP,
+ PM_SETTINGS,
+ PM_EQUIPTMENT_CONTROL,
+ PM_EQUIPTMENT_STATUS,
+ PM_PALM_OPTIONS // This seems to be only older revisions
+} pda_menu_type;
+
+/*
+typedef enum pda_home_menu_item {
+ PMI_MAIN,
+ PMI_EQUIPTMENT_CONTROL
+} pda_home_menu_item;
+*/
+
+/*
typedef enum pda_menu_type {
PM_UNKNOWN,
PM_MAIN,
@@ -13,7 +45,25 @@ typedef enum pda_menu_type {
PM_EQUIPTMENT_STATUS,
PM_BUILDING_MAIN
} pda_menu_type;
-
+*/
+/*
+typedef enum pda_menu_type {
+ PM_UNKNOWN,
+ PM_FW_VERSION,
+ PM_HOME,
+ PM_MAIN_MENU,
+ PM_EQUIPTMENT_CONTROL,
+ PM_EQUIPTMENT_STATUS,
+ PM_BUILDING_HOME,
+ PM_SYSTEM_SETUP,
+ PM_SET_TEMP,
+ PM_POOL_HEAT,
+ PM_SPA_HEAT,
+ PM_FREEZE_PROTECT,
+ PM_FREEZE_PROTECT_DEVICES,
+ PM_SET_AQUAPURE
+} pda_menu_type;
+*/
bool pda_mode();
void set_pda_mode(bool val);
bool process_pda_menu_packet(unsigned char* packet, int length);
@@ -21,5 +71,7 @@ int pda_m_hlightindex();
char *pda_m_hlight();
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);
#endif
diff --git a/release/aqualinkd b/release/aqualinkd
index 169f968..90b8442 100755
Binary files a/release/aqualinkd and b/release/aqualinkd differ
diff --git a/release/aqualinkd.conf b/release/aqualinkd.conf
index d2443e1..867a79b 100755
--- a/release/aqualinkd.conf
+++ b/release/aqualinkd.conf
@@ -60,10 +60,27 @@ report_zero_pool_temp = no
# Working RS ID's are 0x0a 0x0b 0x09 0x08 <- 0x08 is usually taken
device_id=0x0a
+# Please see forum for this, only set to yes when logging information to support
+# new devices. Inflrmation will be written to /tmp/RS485.log
+#debug_RSProtocol_packets = no
+
+#Only for PDA mode
+# set PDA mode
+#pda_mode = yes
+#
+# Put AqualinkD to sleep when in PDA mode after inactivity.
+# If you have Jandy PDA then this MUST be set to yes as the controller can only support one PDA.
+# If you don't have a Jandy PDA leave this at no as AqualinkD will be a lot quicker.
+# Sleep timer is around 2 mins of inactivity, then wake after 2 mins of sleep.
+#pda_sleep_mode = yes
+
# Read status information from other devices on the RS485 bus.
# At the moment just Salt Water Generators are supported.
read_all_devices = yes
+# If you have a SWG connected to the control panel, set this to yes.
+# AqualinkD can only detect a SWG if it's on, so after a restart you will not see/access a SWG until the the next time the pump is on.
+force_SWG = no
# Button inxed light probramming button is assigned to. (look at your button labels below)
light_programming_button = 0
@@ -91,6 +108,10 @@ spa_water_temp_dzidx=0
SWG_percent_dzidx=0
SWG_PPM_dzidx=0
+
+# Try to use labels from Control Panel.
+use_panel_aux_labels=yes
+
# Labels for standard butons (shown in web UI), and domoticz idx's
button_01_label=Filter Pump
#button_01_dzidx=37
diff --git a/release/aqualinkd.pda.conf b/release/aqualinkd.pda.conf
index e507a56..3fc9490 100644
--- a/release/aqualinkd.pda.conf
+++ b/release/aqualinkd.pda.conf
@@ -19,8 +19,8 @@ web_directory=/nas/data/Development/Raspberry/AqualinkD/web
# DEBUG would print everything possible
#log_level=DEBUG_SERIAL
-log_level=DEBUG
-#log_level=INFO
+#log_level=DEBUG
+log_level=INFO
#log_level=NOTICE
# The socket port that the daemon listens to
@@ -33,13 +33,13 @@ serial_port=/dev/ttyUSB0
override_freeze_protect = no
# mqtt stuff
-mqtt_address = trident:1883
+#mqtt_address = trident:1883
#mqtt_user = someusername
#mqtt_passwd = somepassword
#mqtt_dz_pub_topic = domoticz/in
#mqtt_dz_sub_topic = domoticz/out
-mqtt_aq_topic = aqualinkd_test
+#mqtt_aq_topic = aqualinkd
# The id of the Aqualink terminal device. Devices probed by RS8 master are:
# 08-0b, 10-13, 18-1b, 20-23, 28-2b, 30-33, 38-3b, 40-43
@@ -49,6 +49,7 @@ mqtt_aq_topic = aqualinkd_test
#device_id=0x09
device_id=0x60
pda_mode = yes
+pda_sleep_mode = yes
convert_mqtt_temp_to_c = yes
convert_dz_temp_to_c = yes
@@ -101,15 +102,15 @@ button_03_PDA_label=CLEANER
button_04_label=Waterfall
#button_04_dzidx=40
-button_04_PDA_label=AUX2
+button_04_PDA_label=WATERFALL
button_05_label=Spa Blower
#button_05_dzidx=41
-button_05_PDA_label=AUX3
+button_05_PDA_label=AIR BLOWER
button_06_label=Pool Light
#button_06_dzidx=42
-button_06_PDA_label=AUX4
+button_06_PDA_label=LIGHT
button_07_label=NONE
#button_07_dzidx=43
diff --git a/release/serial_logger b/release/serial_logger
index c3593ce..d8f5225 100755
Binary files a/release/serial_logger and b/release/serial_logger differ
diff --git a/serial_logger.c b/serial_logger.c
index 222960c..695464c 100644
--- a/serial_logger.c
+++ b/serial_logger.c
@@ -217,7 +217,7 @@ int main(int argc, char *argv[]) {
}
if (packet_buffer[PKT_DEST] == DEV_MASTER /*&& packet_buffer[PKT_CMD] == CMD_ACK*/) {
- //logMessage(LOG_DEBUG_SERIAL, "ID is in use 0x%02hhx %x\n", lastID, lastID);
+ //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;
@@ -257,7 +257,7 @@ int main(int argc, char *argv[]) {
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, "ID's found\n");
- for (i = 0; i <= sindex; i++) {
+ 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" : "");
diff --git a/utils.c b/utils.c
index 39098b3..258d38c 100644
--- a/utils.c
+++ b/utils.c
@@ -482,4 +482,50 @@ int ascii(char *destination, char *source) {
}
destination[i] = '\0';
return i;
+}
+
+char *prittyString(char *str)
+{
+ char *ptr = str;
+ char *end;
+ bool lastspace=true;
+
+ end = str + strlen(str) - 1;
+ while(end >= ptr){
+ //printf("%d %s ", *ptr, ptr);
+ if (lastspace && *ptr > 96 && *ptr < 123) {
+ *ptr = *ptr - 32;
+ lastspace=false;
+ //printf("to upper\n");
+ } else if (lastspace == false && *ptr > 54 && *ptr < 91) {
+ *ptr = *ptr + 32;
+ lastspace=false;
+ //printf("to lower\n");
+ } else if (*ptr == 32) {
+ lastspace=true;
+ //printf("space\n");
+ } else {
+ lastspace=false;
+ //printf("leave\n");
+ }
+ ptr++;
+ }
+
+ //printf("-- %s --\n", str);
+
+ return str;
+}
+
+static FILE *_packetLogFile = NULL;
+
+void writePacketLog(char *buffer) {
+ if (_packetLogFile == NULL)
+ _packetLogFile = fopen("/tmp/RS485.log", "a");
+
+ if (_packetLogFile != NULL) {
+ fputs(buffer, _packetLogFile);
+ }
+}
+void closePacketLog() {
+ fclose(_packetLogFile);
}
\ No newline at end of file
diff --git a/utils.h b/utils.h
index a94bae1..c2bdee5 100644
--- a/utils.h
+++ b/utils.h
@@ -49,7 +49,9 @@ float degFtoC(float degF);
float degCtoF(float degC);
char* stristr(const char* haystack, const char* needle);
int ascii(char *destination, char *source);
-
+char *prittyString(char *str);
+void writePacketLog(char *buff);
+void closePacketLog();
//#ifndef _UTILS_C_
extern bool _daemon_;
diff --git a/version.h b/version.h
index 584842b..e187202 100644
--- a/version.h
+++ b/version.h
@@ -1,4 +1,4 @@
#define AQUALINKD_NAME "Aqualink Daemon"
-#define AQUALINKD_VERSION "1.2.6f"
+#define AQUALINKD_VERSION "1.3.0"
diff --git a/web/controller.html b/web/controller.html
index 7d3f390..092396a 100644
--- a/web/controller.html
+++ b/web/controller.html
@@ -1178,7 +1178,8 @@
} else if (data.type == 'devices') {
check_devices(data);
resetBackgroundSize();
- window.setTimeout(get_devices, (300 * 1000)); // Check for new dvices ever 5 mins.
+ //window.setTimeout(get_devices, (300 * 1000)); // Check for new dvices ever 5 mins.
+ window.setTimeout(get_devices, (60 * 1000)); // Check for new dvices ever 1 mins.
}
}
socket_di.onclose = function() {