/*--------------------------------------------------------------------------- haakedriv.c Driver for the Phoenix P1 HAAKE thermostat Markus Zolliker, Nov 2006 ----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsm.h" #include "ease.h" #include "initializer.h" #define HAAKE_ON 1 #define HAAKE_EXT 2 #define HAAKE_RESET 3 typedef struct { EaseDriv d; char *unit; float kelvin; float t; float t2; float set; int errcnt; int status; int pumprunning; int extcontrol; int relais; int overtemp; int lowlevel; int pumpalarm; int externalarm; int coolalarm; int sensor1alarm; int sensor2alarm; int with2sensors; } Haake; static ParClass haakeClass = { "HAAKE", sizeof(Haake) }; /*----------------------------------------------------------------------------*/ int HaakeHandler(void *object) { int iret, l; Haake *drv = ParCast(&haakeClass, object); EaseBase *eab = EaseBaseCast(object); char *corr; assert(drv); if (eab->state < EASE_idle) goto quit; if (availableNetRS232(eab->ser) || availableRS232(eab->ser)) { eab->msg[0] = '\0'; l = sizeof(eab->ans); iret = readRS232TillTerm(eab->ser, eab->ans, &l); if (eab->state != EASE_expect && eab->state != EASE_lost) { if (iret == 1) { ParPrintf(eab, eError, "unexpected answer: %s", eab->ans); } goto quit; } if (iret == 1) { ParPrintf(eab, -2, "ans: %s", eab->ans); if (eab->state == EASE_lost) { if (strcmp(eab->ans, "F001") == 0) { EaseWrite(eab, "V"); } goto quit; } l = strlen(eab->ans) - 1; if (l >= 0 && eab->ans[l] != '$') { /* end char is not $ -> send again */ ParPrintf(eab, -2, "ans: %s", eab->ans); if (drv->errcnt < 10) { eab->state = EASE_read; EaseWrite(eab, NULL); /* send the same command again */ drv->errcnt++; return 0; } eab->errCode = EASE_ILL_ANS; eab->state = EASE_idle; goto error; } else { drv->errcnt = 0; } if (eab->state == EASE_lost) { goto quit; } else if (eab->cmd[0] == 'V') { if (strcmp(eab->ans, eab->version) == 0) { /* we are still connected with the same device */ } else if (*eab->version == '\0') { strlcpy(eab->version, eab->ans, sizeof(eab->version)); } else { /* version (and therefore device) changed */ eab->errCode = EASE_DEV_CHANGED; eab->state = EASE_idle; goto error; } eab->state = EASE_idle; goto quit; } else { /* eab->tmo = 20; no need to change the timeout ? */ } } if (iret != 1) { eab->errCode = iret; eab->state = EASE_idle; goto error; } eab->state = EASE_read; } else if (eab->state == EASE_expect) { if (time(NULL) > eab->cmdtime + eab->tmo) { eab->state = EASE_lost; } } else if (eab->state == EASE_lost) { if (time(NULL) > eab->cmdtime) { EaseWrite(eab, "check"); eab->state = EASE_lost; } } goto quit; error: /* EaseWriteError(eab); */ quit: return EaseHandler(eab); } /*----------------------------------------------------------------------------*/ static void HaakeParDef(void *object) { Haake *drv = ParCast(&haakeClass, object); EaseBase *eab = object; int reset = 0; ParName(""); ParFmt("%.2f"); ParTail(drv->unit); ParFloat(&drv->t, PAR_NAN); ParName("unit"); if (drv->unit == NULL) { ParAccess(usUser); } ParLogAs(NULL); ParStr(&drv->unit, NULL); ParName("t2"); if (drv->with2sensors > 0) { ParFmt("%.2f"); ParTail(drv->unit); } ParFloat(&drv->t2, PAR_NAN); ParName("set"); ParFmt("%.2f"); ParTail(drv->unit); ParFloat(&drv->set, PAR_NAN); ParName("pumprunning"); EaseUpdate(HAAKE_ON); ParInt(&drv->pumprunning, 0); ParName("extcontrol"); EaseUpdate(HAAKE_EXT); ParInt(&drv->extcontrol, 0); ParName("relais"); ParInt(&drv->relais, 0); ParName("overtemp"); ParInt(&drv->overtemp, 0); ParName("lowlevel"); ParInt(&drv->lowlevel, 0); ParName("pumpalarm"); ParInt(&drv->pumpalarm, 0); ParName("externalarm"); ParInt(&drv->externalarm, 0); ParName("coolalarm"); ParInt(&drv->coolalarm, 0); ParName("sensor1alarm"); ParInt(&drv->sensor1alarm, 0); ParName("sensor2alarm"); ParInt(&drv->sensor2alarm, 0); ParName("reset"); ParAccess(usUser); ParSave(0); ParInt(&reset, 0); if (ParActionIs(PAR_SET) > 0) { EaseSetUpdate(drv, HAAKE_RESET, 1); } ParName("with2sensors"); ParAccess(usUser); ParInt(&drv->with2sensors, 0); EaseBasePar(drv); EaseSendPar(drv); EaseDrivPar(drv, "%.2f", drv->unit); ParStdDef(); EaseMsgPar(drv); } /*----------------------------------------------------------------------------*/ static long HaakeRead(long pc, void *object) { Haake *drv = ParCast(&haakeClass, object); EaseBase *eab = object; char alarms[128]; int status; switch (pc) { default: /* FSM BEGIN ****************************** */ EasePchk(drv); EaseWrite(eab, "B"); return __LINE__; case __LINE__: /**********************************/ if (!EaseGetUpdate(drv, HAAKE_ON)) { drv->pumprunning = (eab->ans[0] != '0'); } if (!EaseGetUpdate(drv, HAAKE_EXT)) { drv->extcontrol = (eab->ans[1] != '0'); } drv->relais = (eab->ans[2] != '0'); drv->overtemp = (eab->ans[3] != '0'); drv->lowlevel = (eab->ans[4] != '0'); drv->pumpalarm = (eab->ans[5] != '0'); drv->externalarm = (eab->ans[6] != '0'); drv->coolalarm = (eab->ans[7] != '0'); drv->sensor1alarm = (eab->ans[10] != '0'); drv->sensor2alarm = (eab->ans[11] != '0'); EaseWrite(eab, "F1"); return __LINE__; case __LINE__: /**********************************/ if (eab->cmd[0] != 'F') goto fsm_quit; drv->t = atof(eab->ans) + drv->kelvin; if (drv->with2sensors < 1) goto nof2; EaseWrite(eab, "F2"); return __LINE__; case __LINE__: /**********************************/ if (eab->cmd[0] != 'F') goto fsm_quit; drv->t2 = atof(eab->ans) + drv->kelvin; if (drv->t2 < -222) { drv->t2 = PAR_NAN; } nof2: EaseWrite(eab, "S"); return __LINE__; case __LINE__: /**********************************/ if (eab->cmd[0] != 'S') goto fsm_quit; drv->set = atof(eab->ans) + drv->kelvin; skipGetSet: ParLog(drv); fsm_quit:return 0; } /* FSM END ******************************** */ } /*----------------------------------------------------------------------------*/ static long HaakeStart(long pc, void *object) { Haake *drv = ParCast(&haakeClass, object); EaseBase *eab = object; char unitcmd[8] = "W TE K"; char msg[256]; switch (pc) { default: /* FSM BEGIN ****************************** */ EasePchk(drv); EaseWrite(eab, "V"); return __LINE__; case __LINE__: /**********************************/ if (0 != strncmp(eab->version, "1P/H", 4)) { snprintf(msg, sizeof msg, "unknown temperature controller version: %s", eab->version); EaseStop(eab, msg); goto quit; } ParPrintf(drv, eLog, "connected to haake thermostat %s", eab->version); if (drv->unit == NULL) { drv->unit = strdup("K"); } if (*drv->unit == 'C') { EaseWrite(eab, "W TE C"); drv->kelvin = 0; } else { EaseWrite(eab, "W TE K"); drv->kelvin = 273.15; } return __LINE__; case __LINE__: /**********************************/ FsmCall(HaakeRead); return __LINE__; case __LINE__: /**********************************/ quit: return 0; } /* FSM END ******************************************* */ } /*----------------------------------------------------------------------------*/ static long HaakeSet(long pc, void *object) { Haake *drv = ParCast(&haakeClass, object); EaseBase *eab = object; char cmd[32]; int upd; switch (pc) { default: /* FSM BEGIN ****************************** */ EasePchk(drv); upd = EaseNextUpdate(drv); switch (upd) { case EASE_RUN: snprintf(cmd, sizeof cmd, "w sw %.5g", drv->d.targetValue - drv->kelvin); break; case HAAKE_ON: snprintf(cmd, sizeof cmd, "w ts%d", drv->pumprunning); break; case HAAKE_EXT: if (drv->extcontrol) { snprintf(cmd, sizeof cmd, "w ex"); } else { snprintf(cmd, sizeof cmd, "w in"); } break; case HAAKE_RESET: snprintf(cmd, sizeof cmd, "er"); break; default: goto quit; } if (!drv->pumprunning) { ParPrintf(eab, eWarning, "WARNING: Haake is not running"); } if (drv->relais || drv->overtemp || drv->lowlevel || drv->pumpalarm || drv->externalarm || drv->coolalarm || drv->sensor1alarm || drv->sensor2alarm) { ParPrintf(eab, eWarning, "WARNING: Haake alarm"); } EaseWrite(eab, cmd); quit: return 0; } /* FSM END ******************************************* */ } /*----------------------------------------------------------------------------*/ static int HaakeInit(SConnection * con, int argc, char *argv[], int dynamic) { /* args: MakeObject objectname haake MakeObject objectname haake */ Haake *drv; drv = EaseMakeDriv(con, &haakeClass, argc, argv, dynamic, 7, HaakeParDef, HaakeHandler, HaakeStart, NULL, HaakeRead, HaakeSet); if (drv == NULL) return 0; setRS232ReplyTerminator(drv->d.b.ser, "\r"); setRS232SendTerminator(drv->d.b.ser, "\r"); drv->d.b.tmo = 5; return 1; } /*----------------------------------------------------------------------------*/ void HaakeStartup(void) { ParMakeClass(&haakeClass, EaseDrivClass()); MakeDriver("HAAKE", HaakeInit, 0, "HAAKE Phoenix P1 thermostat"); }