OK, thanks to the help of many people in the forum I finally manged to get a version of the code that I can consider "working". In fact the code needs much much refining and optimization (I am no programmer). The sketch I am posting will allow you to login with the OP II panel, send commands, set flag values and receive answers and it also has examples on how to parse the response from the board.
Many many parts are still missing and must be completed, but I feel I need some feedback.
I think I'll put all the details on hardware and software development on a web page or a pdf, for now I'll sum up the hardware I used:
The sketch goes as follows:
//Define the arrays that will contain the data
#define DATABUFFERSIZE 255
//deine message types for easier human readable parsing in code
#define COMMAND 0x0F
const byte headerByte = 0x5A; // or '!', or whatever your start character is
byte byteRead;
byte HAI_send[DATABUFFERSIZE];
byte HAI_receive[DATABUFFERSIZE];
byte receivedMessageSize;;//size of the received message
const byte HAI_OK[] = { 0x5A, 0x01, 0x05, 0xC1, 0x93}; // Standard acknowledge OK answer
const byte HAI_KO[] = { 0x5A, 0x01, 0x05, 0xC1, 0x92}; // Standard acknowledge but NOT OK answer
const byte PASSWORD[] = { 0, 0, 0, 0 }; //the master code used to login with the HAI console
// PLEASE NOTE: THIS WILL BE SENT IN PERFECTLY CLEAR MODE (NO ENCRYPTION) OVER THE SERIAL PORT
// THIS IS HOW HAI DOES IT, THE SERIAL PROTOCOL ISN'T ENCRYPTED SINCE IT REQUIRES PHYSICAL ACCESS TO
// THE PANEL
//==============CRC ROUTINE===========
//From HAI docs:
/* starting with the message length byte, call Update_CRC for each byte
of the message passing the message byte in Data. The low byte of CRC will contain
the low byte of the CRC-16 remainder and should be sent first. The high byte of
CRC will contain the high byte of the CRC-16 remainder and should be sent last. */
//----------------------------------------------------------------------
//DAVIDE may 12 2014:
//I rewrote the routine from the HAI docs translating form the turbo Pascal version,
//which was easier for me than the C version that used pointers and did not run on Arduino
//Someone who knows how to program should modify it and maybe write a CRC append routine
unsigned int crc16_update(unsigned int crc, byte x)
{
static int const Poly = 0xA001; // CRC-16 polynomial
int i;
bool flag;
crc ^= x;
for (i=0; i<8; i++)
{
flag = (crc & 1) != 0;
crc = (crc >> 1);
if (flag)
crc ^= Poly;
}//END for
return crc;
}//END crc16_update()
/*
//calculates crc for an entire output message, putting the result in the 2 bytes after message ending
unsigned int messageOutCrc (){ (//no args because length is in the 2nd place of the array
unsigned int crc = 0, i;
unsigned byte totalLength; //total length of the message in the array, including header, length but excluding the 2 crc bytes
totalLength = HAI_send[2] + 2 //+2 is for accounting the header and length bytes that are not computed in the length byte
for (i = 2; i <= totalLength; i++) //i=2 because the CRC is WITHOUT message header HAI_send [2] is message length (excluding header and length) so I add +2
crc = crc16_update(crc, HAI_send);
HAI_send[totalLength + 1] = lowByte (crc);//penultimate byte of the message CRC1
HAI_send[totalLength + 2] = highByte(crc);//last byte of the message CRC2
}//END messageOutCrc*/
//LOGIN - composes the login string starting from the master code
void HAI_login (){
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x05; //message length
HAI_send[3] = 0x20; //it's a login
HAI_send[4] = PASSWORD[0]; //first digit of the code
HAI_send[5] = PASSWORD[1];
HAI_send[6] = PASSWORD[2];
HAI_send[7] = PASSWORD[3];
unsigned int crc = 0, i;
for (i = 2; i <= 7; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
HAI_send[8] = lowByte (crc);
HAI_send[9] = highByte(crc);
}//END HAI_login
/*The COMMAND message is used to send an immediate control command to the HAI controller. Commands are
provided to control lights, appliances, temperatures, security, and messaging. Each command follows the same
format: a single byte command, followed by a single byte parameter, and then a two byte secondary parameter. The
command message is formatted as follows:
Start character 0x5A
Message length 0x05
Message type 0x0F
Data 1 Command
Data 2 Parameter 1
Data 3 High byte of parameter 2
Data 4 Low byte of parameter 2
CRC 1 varies
CRC 2 varies */
//compose the array for a set flag command (command 0x0C = set flag P2 to value P1)
void SetFlagTo (int flagNumber, byte level){
//using base 1 for the array for human readability
//the first part of the set flag command are fixed values:
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x05; //message length
HAI_send[3] = 0x0F; //it's a command
HAI_send[4] = 0x0C; //it's a set flag command: 0x0B to increment counter - 0x0C to decrement counter
HAI_send[5] = level; //the level to set to
// then we must calculate high and low byte of the Flag number
//byte low_byte = 0xff & flagNumber;
//byte high_byte = flagNumber >> 8;
//...and append to the previous part
HAI_send[6] = highByte(flagNumber);
HAI_send[7] = lowByte (flagNumber);
//now the CRC
//this should become a function unsigned int messageOutCrc (byte length) - length could be determined from the 2nd element
//no array passed to the function (don't know how to) - data will be get and put in the HAI_send global array
unsigned int crc = 0, i;
for (i = 2; i <= 7; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
HAI_send[8] = lowByte (crc);
HAI_send[9] = highByte (crc);
}//END SetFlagTo
void IncrementFlag (int flagNumber){
//using base 1 for the array for human readability
//the first part of the set flag command are fixed values:
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x05; //message length
HAI_send[3] = 0x0F; //it's a command
HAI_send[4] = 0x0B; //it's a set flag command: 0x0B to increment counter - 0x0C to decrement counter
HAI_send[5] = 0; //parameter 1 not specified in the HAI doc: must set to ??
// then we must calculate high and low byte of the Flag number
//...and append to the previous part, High byte first
HAI_send[6] = highByte(flagNumber);
HAI_send[7] = lowByte (flagNumber);
//now the CRC
//this should become a function unsigned int messageOutCrc (byte length) - length could be determined from the 2nd element
//no array passed to the function (don't know how to) - data will be get and put in the HAI_send global array
unsigned int crc = 0, i;
for (i = 2; i <= 7; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
HAI_send[8] = lowByte (crc);
HAI_send[9] = highByte (crc);
}//END IncrementFlag
void SetUnitOn (int unitNumber, byte time){ //time is 0 = permanent OR 1-99 seconds OR 101-199 for n-100 minutes OR 201-218 for n-200 hours
//using base 1 for the array for human readability
//the first part of the set flag command are fixed values:
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x05; //message length
HAI_send[3] = 0x0F; //it's a command
HAI_send[4] = 0x01; //it's a unit ON command
HAI_send[5] = time; //parameter 1
// then we must calculate high and low byte of the Flag number
//...and append to the previous part, High byte first
HAI_send[6] = highByte(unitNumber);
HAI_send[7] = lowByte (unitNumber);
//now the CRC
//this should become a function unsigned int messageOutCrc (byte length) - length could be determined from the 2nd element
//no array passed to the function (don't know how to) - data will be get and put in the HAI_send global array
unsigned int crc = 0, i;
for (i = 2; i <= 7; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
HAI_send[8] = lowByte (crc);
HAI_send[9] = highByte (crc);
}
void SetUnitOff (int unitNumber, byte time){ //time is 0 = permanent OR 1-99 seconds OR 101-199 for n-100 minutes OR 201-218 for n-200 hours
//TBD - command 0x00
}
void executeButton (int buttonNumber){
//TBD - command 0x07
}
void getSystemStatus (){
//using base 1 for the array for human readability
//the first part of the set flag command are fixed values:
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x01; //message length
HAI_send[3] = 0x13; //it's a system status request
//now the CRC
//this should become a function unsigned int messageOutCrc (byte length) - length could be determined from the 2nd element
//no array passed to the function (don't know how to) - data will be get and put in the HAI_send global array
unsigned int crc = 0, i;
for (i = 2; i <= 3; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
HAI_send[4] = lowByte (crc);
HAI_send[5] = highByte (crc);
}
//to be improved for multi-zone polling
void getTemperature (int startZone, int endZone){
//using base 1 for the array for human readability
//the first part of the set flag command are fixed values:
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x03; //message length
HAI_send[3] = 0x19; //it's a aux temp poll
HAI_send[4] = startZone; //Data 1 = first sensor to poll
HAI_send[5] = endZone; //Data 2 = last sensor to poll - for the moment one at a time
//now the CRC
//this should become a function unsigned int messageOutCrc (byte length) - length could be determined from the 2nd element
//no array passed to the function (don't know how to) - data will be get and put in the HAI_send global array
unsigned int crc = 0, i;
for (i = 2; i <= 5; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
//calculate high and low byte of the CRC
byte low_byte = 0xff & crc;
byte high_byte = crc >> 8;
HAI_send[6] = low_byte;// (CRC 1);
HAI_send[7] = high_byte;// (CRC 2);
}
//=====SEND DATA TO HAI ==============
void sendToHAI() {
int i=0;
Serial.print("Sent to HAI:");//DEBUG
for (i=1;i<= (HAI_send[2] + 2 + 2); i++){//HAI_send[2] is message length +2 for headerand length bytes +2 for CRC bytes
Serial1.write(HAI_send); //to HAI
Serial.print(HAI_send,HEX);//for debugging - comment out in final release
Serial.write (" ");
}//END for
Serial.println();//DEBUG
}//END SendTo HAI
//convert Omni temperature to C
float omniToC (byte omniT) {
//OmniT is just 0 when -40 °C and every OmniT step is 0.5 °C
//omnitoF in a little more complicated, I think the best thing to do is convert the °C value to °F
return (omniT*0.5 - 40);
}//END OmnitoC
//===========================SERIAL READ=============================
//the serial array function comes from http://jhaskellsblog.blogspot.it/2011/05/serial-comm-fundamentals-on-arduino.html?m=1
//with modifications since the HAI protocol doesn't have end byte but a length instead, also it doesn't read strings or chars
//and doesn't need NULL termination
boolean getSerialArray(){
static byte dataBufferIndex=0;
static byte messageSize=DATABUFFERSIZE; //length of the message, at the beginning it can be the maximum value
static boolean storeVal=false; //flag to define if the incomingbyte has to be put in the array
boolean ended = false; //flag for end of message
byte incomingbyte;
while(Serial1.available()>0){
//Serial.print("Receiving data: ");//DEBUG
//Serial.println(dataBufferIndex);
incomingbyte = Serial1.read();//put data in incomingbyte variable
//Serial.print("Data received: ");
//Serial.println(incomingbyte,HEX);
if(incomingbyte==headerByte ){ //&& dataBufferIndex == 0){ //it is possible that a generic data byte could be 5A so check if data Buffer==0
dataBufferIndex = 1; //Initialize our dataBufferIndex variable
storeVal = true; //all the values will be saved from now on
}//END if
if(storeVal){
if (dataBufferIndex == 2) { //it's the byte containing the message length - set the variable accordingly
HAI_receive[dataBufferIndex++] = incomingbyte;
messageSize = incomingbyte + 2 + 2; //adding the 2 CRC values and Header and length - this is the final index
//Serial.print ("Message length set to: ");
//Serial.println(messageSize);
//Serial.print ("Read data, now proceed to data n. ");
//Serial.println(dataBufferIndex);
} else if (dataBufferIndex == DATABUFFERSIZE) {//Buffer overflow - do nothing
storeVal=false;
dataBufferIndex=0;
ended=true;
return false;
break;
} else if (dataBufferIndex == messageSize) { //this is the last one - put it in the array and return true
HAI_receive[dataBufferIndex] = incomingbyte;
receivedMessageSize=messageSize;
storeVal = false;
//Serial.print ("Last data read, it was n. ");
//Serial.println(dataBufferIndex);
dataBufferIndex=0;
return true;
break;
} else { //it's a generic byte of the message - put it in the array
HAI_receive[dataBufferIndex++] = incomingbyte;
//Serial.print ("Read data, now proceed to data n. ");
//Serial.println(dataBufferIndex);
}//END else
}//END if (storeval)
}//END while Serial1.available()
//We've read in all the available Serial data, and don't have a valid string yet, so return false
return false;
} // END getSerialArray
//======MAIN PROGRAM==================================
void setup() {
// Turn the Serial Protocol ON
Serial.begin(9600); //for debug
Serial1.begin(9600); //for communications with HAI panel
while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only
}
delay(2000);
}//END Setup()
void loop() {
if (Serial.available()){
char inChar = (char)Serial.read();
if (inChar == 'f') {
SetFlagTo (417,100); //set flag 417 to X value
sendToHAI();
SetUnitOn (4, 0); // 0= infinite time
sendToHAI();
}//END IF inchar == 'f'
if (inChar == 'l') {
//TEST SetFlag Routine
HAI_login(); //login to HAI console sets up the HAI_send with the message
sendToHAI(); //send the content of HAI_send array to the OPII
}//END if intchar='l'
if (inChar == 't') {
//TEST get temperature routine
getTemperature(49,59); //get temperature of zone 49 = 0x31 = first temperature sensor
sendToHAI();
}//END if intchar='t'
if (inChar == 's') {
//TEST get system status
getSystemStatus(); //get system status
Serial.println("Message ready");
sendToHAI();
}//END if intchar='s'
}//IF Serial.available
/*
//TEST routine that echoes the Serial input to the Serial1 (connected to HAI with a TTL to RS232 adapter)
// check if data has been sent from the computer:
if (Serial.available()) {
// read the most recent byte
byteRead = Serial.read();
//ECHO the value that was read, back to the serial port.
Serial1.write(byteRead);
}
//TEST routine that echoes the Serial1 input from HAI to the Serial port of Arduino for debbugging
if (Serial1.available()) {
// read the most recent byte
byteRead = Serial1.read();
//ECHO the value that was read, back to the serial port.
Serial.write(byteRead);
}//END IF */
// SERIAL READ
//There should be a check to wait a few millis until HAI answers. If it times out = no answer, else proceed
if (Serial1.available()>0){
if (getSerialArray()){ //the function places valid series of bytes in the HAI_receive[] array
Serial.print ("HAI response: ");
//For debug the content is sent on Serial
for (int i=1;i<=receivedMessageSize;i++){
Serial.print (HAI_receive,HEX);
Serial.print (" ");
}//END for
Serial.println("now parsing...");
//CRC CHECKING GOES HERE
//HAI_receive PARSING GOES HERE
switch (HAI_receive[3]){
case 0x14: //SYSTEM STATUS
//can do anything with the data, as a demo it prints out some of them to serial
//the codes and structure for each message are in the HAI omniLINK documentation
Serial.print("Year: ");
Serial.print(HAI_receive[5]);
Serial.print(" - Month: ");
Serial.print(HAI_receive[6]);
Serial.print(" - Day: ");
Serial.println(HAI_receive[7]);
Serial.print ("time: ");
Serial.print(HAI_receive[9]); //HH 8 is day of the week
Serial.print (":");
Serial.print(HAI_receive[10]);//MM
Serial.print (":");
Serial.print(HAI_receive[11]);//SS
break;
case 0x1A: //TEMPERATURE SENSOR
byte numberOfSensors = (HAI_receive[2]-1)/4;
for (int i=0;i<numberOfSensors;i++){
Serial.print("sensor ");
Serial.print(i+1);
Serial.print(": T= ");
Serial.print(omniToC(HAI_receive[5 + 4*i]));
Serial.println(" C");
}//END for
break;
}//END switch
}//END if getSerialArray
}//END if Serial1.available()>0
}//END loop()
Many many parts are still missing and must be completed, but I feel I need some feedback.
I think I'll put all the details on hardware and software development on a web page or a pdf, for now I'll sum up the hardware I used:
- Arduino Leonardo (Arduino UNO or MEGA will work but require some minimal change to the code - you can ask me anytime) (26 € - 30 $);
- Serial to TTL converter (I used this: http://www.ebay.it/itm/RS232-to-TTL-5V-Converter-Board-2-canali-/280817956305?pt=Componenti_elettronici_attivi&hash=item41620df5d1&_uhb=1 WARNING! I tried with no success 3 cheaper ones like this: http://www.ebay.it/itm/Mini-RS232-to-TTL-level-converter-module-Raspberry-Pi-WRT54g-PIC-AVR-Arduino-/251532679703?pt=UK_Computing_Other_Computing_Networking&hash=item3a90841a17&_uhb=1 so I advise you NOT to buy this one); (10 € - 15 $)
- a RJ11 cable to connect to the HAI serial port (the wiring is in the HAI installation guide or in some thread in this forum) connected to the aforementioned interface; (I got one for free cutting a telephone cord, just check it has 4 wires in it, since some telephones only use 2).
The sketch goes as follows:
//Define the arrays that will contain the data
#define DATABUFFERSIZE 255
//deine message types for easier human readable parsing in code
#define COMMAND 0x0F
const byte headerByte = 0x5A; // or '!', or whatever your start character is
byte byteRead;
byte HAI_send[DATABUFFERSIZE];
byte HAI_receive[DATABUFFERSIZE];
byte receivedMessageSize;;//size of the received message
const byte HAI_OK[] = { 0x5A, 0x01, 0x05, 0xC1, 0x93}; // Standard acknowledge OK answer
const byte HAI_KO[] = { 0x5A, 0x01, 0x05, 0xC1, 0x92}; // Standard acknowledge but NOT OK answer
const byte PASSWORD[] = { 0, 0, 0, 0 }; //the master code used to login with the HAI console
// PLEASE NOTE: THIS WILL BE SENT IN PERFECTLY CLEAR MODE (NO ENCRYPTION) OVER THE SERIAL PORT
// THIS IS HOW HAI DOES IT, THE SERIAL PROTOCOL ISN'T ENCRYPTED SINCE IT REQUIRES PHYSICAL ACCESS TO
// THE PANEL
//==============CRC ROUTINE===========
//From HAI docs:
/* starting with the message length byte, call Update_CRC for each byte
of the message passing the message byte in Data. The low byte of CRC will contain
the low byte of the CRC-16 remainder and should be sent first. The high byte of
CRC will contain the high byte of the CRC-16 remainder and should be sent last. */
//----------------------------------------------------------------------
//DAVIDE may 12 2014:
//I rewrote the routine from the HAI docs translating form the turbo Pascal version,
//which was easier for me than the C version that used pointers and did not run on Arduino
//Someone who knows how to program should modify it and maybe write a CRC append routine
unsigned int crc16_update(unsigned int crc, byte x)
{
static int const Poly = 0xA001; // CRC-16 polynomial
int i;
bool flag;
crc ^= x;
for (i=0; i<8; i++)
{
flag = (crc & 1) != 0;
crc = (crc >> 1);
if (flag)
crc ^= Poly;
}//END for
return crc;
}//END crc16_update()
/*
//calculates crc for an entire output message, putting the result in the 2 bytes after message ending
unsigned int messageOutCrc (){ (//no args because length is in the 2nd place of the array
unsigned int crc = 0, i;
unsigned byte totalLength; //total length of the message in the array, including header, length but excluding the 2 crc bytes
totalLength = HAI_send[2] + 2 //+2 is for accounting the header and length bytes that are not computed in the length byte
for (i = 2; i <= totalLength; i++) //i=2 because the CRC is WITHOUT message header HAI_send [2] is message length (excluding header and length) so I add +2
crc = crc16_update(crc, HAI_send);
HAI_send[totalLength + 1] = lowByte (crc);//penultimate byte of the message CRC1
HAI_send[totalLength + 2] = highByte(crc);//last byte of the message CRC2
}//END messageOutCrc*/
//LOGIN - composes the login string starting from the master code
void HAI_login (){
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x05; //message length
HAI_send[3] = 0x20; //it's a login
HAI_send[4] = PASSWORD[0]; //first digit of the code
HAI_send[5] = PASSWORD[1];
HAI_send[6] = PASSWORD[2];
HAI_send[7] = PASSWORD[3];
unsigned int crc = 0, i;
for (i = 2; i <= 7; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
HAI_send[8] = lowByte (crc);
HAI_send[9] = highByte(crc);
}//END HAI_login
/*The COMMAND message is used to send an immediate control command to the HAI controller. Commands are
provided to control lights, appliances, temperatures, security, and messaging. Each command follows the same
format: a single byte command, followed by a single byte parameter, and then a two byte secondary parameter. The
command message is formatted as follows:
Start character 0x5A
Message length 0x05
Message type 0x0F
Data 1 Command
Data 2 Parameter 1
Data 3 High byte of parameter 2
Data 4 Low byte of parameter 2
CRC 1 varies
CRC 2 varies */
//compose the array for a set flag command (command 0x0C = set flag P2 to value P1)
void SetFlagTo (int flagNumber, byte level){
//using base 1 for the array for human readability
//the first part of the set flag command are fixed values:
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x05; //message length
HAI_send[3] = 0x0F; //it's a command
HAI_send[4] = 0x0C; //it's a set flag command: 0x0B to increment counter - 0x0C to decrement counter
HAI_send[5] = level; //the level to set to
// then we must calculate high and low byte of the Flag number
//byte low_byte = 0xff & flagNumber;
//byte high_byte = flagNumber >> 8;
//...and append to the previous part
HAI_send[6] = highByte(flagNumber);
HAI_send[7] = lowByte (flagNumber);
//now the CRC
//this should become a function unsigned int messageOutCrc (byte length) - length could be determined from the 2nd element
//no array passed to the function (don't know how to) - data will be get and put in the HAI_send global array
unsigned int crc = 0, i;
for (i = 2; i <= 7; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
HAI_send[8] = lowByte (crc);
HAI_send[9] = highByte (crc);
}//END SetFlagTo
void IncrementFlag (int flagNumber){
//using base 1 for the array for human readability
//the first part of the set flag command are fixed values:
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x05; //message length
HAI_send[3] = 0x0F; //it's a command
HAI_send[4] = 0x0B; //it's a set flag command: 0x0B to increment counter - 0x0C to decrement counter
HAI_send[5] = 0; //parameter 1 not specified in the HAI doc: must set to ??
// then we must calculate high and low byte of the Flag number
//...and append to the previous part, High byte first
HAI_send[6] = highByte(flagNumber);
HAI_send[7] = lowByte (flagNumber);
//now the CRC
//this should become a function unsigned int messageOutCrc (byte length) - length could be determined from the 2nd element
//no array passed to the function (don't know how to) - data will be get and put in the HAI_send global array
unsigned int crc = 0, i;
for (i = 2; i <= 7; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
HAI_send[8] = lowByte (crc);
HAI_send[9] = highByte (crc);
}//END IncrementFlag
void SetUnitOn (int unitNumber, byte time){ //time is 0 = permanent OR 1-99 seconds OR 101-199 for n-100 minutes OR 201-218 for n-200 hours
//using base 1 for the array for human readability
//the first part of the set flag command are fixed values:
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x05; //message length
HAI_send[3] = 0x0F; //it's a command
HAI_send[4] = 0x01; //it's a unit ON command
HAI_send[5] = time; //parameter 1
// then we must calculate high and low byte of the Flag number
//...and append to the previous part, High byte first
HAI_send[6] = highByte(unitNumber);
HAI_send[7] = lowByte (unitNumber);
//now the CRC
//this should become a function unsigned int messageOutCrc (byte length) - length could be determined from the 2nd element
//no array passed to the function (don't know how to) - data will be get and put in the HAI_send global array
unsigned int crc = 0, i;
for (i = 2; i <= 7; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
HAI_send[8] = lowByte (crc);
HAI_send[9] = highByte (crc);
}
void SetUnitOff (int unitNumber, byte time){ //time is 0 = permanent OR 1-99 seconds OR 101-199 for n-100 minutes OR 201-218 for n-200 hours
//TBD - command 0x00
}
void executeButton (int buttonNumber){
//TBD - command 0x07
}
void getSystemStatus (){
//using base 1 for the array for human readability
//the first part of the set flag command are fixed values:
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x01; //message length
HAI_send[3] = 0x13; //it's a system status request
//now the CRC
//this should become a function unsigned int messageOutCrc (byte length) - length could be determined from the 2nd element
//no array passed to the function (don't know how to) - data will be get and put in the HAI_send global array
unsigned int crc = 0, i;
for (i = 2; i <= 3; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
HAI_send[4] = lowByte (crc);
HAI_send[5] = highByte (crc);
}
//to be improved for multi-zone polling
void getTemperature (int startZone, int endZone){
//using base 1 for the array for human readability
//the first part of the set flag command are fixed values:
HAI_send[1] = 0x5A; //header
HAI_send[2] = 0x03; //message length
HAI_send[3] = 0x19; //it's a aux temp poll
HAI_send[4] = startZone; //Data 1 = first sensor to poll
HAI_send[5] = endZone; //Data 2 = last sensor to poll - for the moment one at a time
//now the CRC
//this should become a function unsigned int messageOutCrc (byte length) - length could be determined from the 2nd element
//no array passed to the function (don't know how to) - data will be get and put in the HAI_send global array
unsigned int crc = 0, i;
for (i = 2; i <= 5; i++) //i=2 because the CRC is WITHOUT message header
crc = crc16_update(crc, HAI_send);
//calculate high and low byte of the CRC
byte low_byte = 0xff & crc;
byte high_byte = crc >> 8;
HAI_send[6] = low_byte;// (CRC 1);
HAI_send[7] = high_byte;// (CRC 2);
}
//=====SEND DATA TO HAI ==============
void sendToHAI() {
int i=0;
Serial.print("Sent to HAI:");//DEBUG
for (i=1;i<= (HAI_send[2] + 2 + 2); i++){//HAI_send[2] is message length +2 for headerand length bytes +2 for CRC bytes
Serial1.write(HAI_send); //to HAI
Serial.print(HAI_send,HEX);//for debugging - comment out in final release
Serial.write (" ");
}//END for
Serial.println();//DEBUG
}//END SendTo HAI
//convert Omni temperature to C
float omniToC (byte omniT) {
//OmniT is just 0 when -40 °C and every OmniT step is 0.5 °C
//omnitoF in a little more complicated, I think the best thing to do is convert the °C value to °F
return (omniT*0.5 - 40);
}//END OmnitoC
//===========================SERIAL READ=============================
//the serial array function comes from http://jhaskellsblog.blogspot.it/2011/05/serial-comm-fundamentals-on-arduino.html?m=1
//with modifications since the HAI protocol doesn't have end byte but a length instead, also it doesn't read strings or chars
//and doesn't need NULL termination
boolean getSerialArray(){
static byte dataBufferIndex=0;
static byte messageSize=DATABUFFERSIZE; //length of the message, at the beginning it can be the maximum value
static boolean storeVal=false; //flag to define if the incomingbyte has to be put in the array
boolean ended = false; //flag for end of message
byte incomingbyte;
while(Serial1.available()>0){
//Serial.print("Receiving data: ");//DEBUG
//Serial.println(dataBufferIndex);
incomingbyte = Serial1.read();//put data in incomingbyte variable
//Serial.print("Data received: ");
//Serial.println(incomingbyte,HEX);
if(incomingbyte==headerByte ){ //&& dataBufferIndex == 0){ //it is possible that a generic data byte could be 5A so check if data Buffer==0
dataBufferIndex = 1; //Initialize our dataBufferIndex variable
storeVal = true; //all the values will be saved from now on
}//END if
if(storeVal){
if (dataBufferIndex == 2) { //it's the byte containing the message length - set the variable accordingly
HAI_receive[dataBufferIndex++] = incomingbyte;
messageSize = incomingbyte + 2 + 2; //adding the 2 CRC values and Header and length - this is the final index
//Serial.print ("Message length set to: ");
//Serial.println(messageSize);
//Serial.print ("Read data, now proceed to data n. ");
//Serial.println(dataBufferIndex);
} else if (dataBufferIndex == DATABUFFERSIZE) {//Buffer overflow - do nothing
storeVal=false;
dataBufferIndex=0;
ended=true;
return false;
break;
} else if (dataBufferIndex == messageSize) { //this is the last one - put it in the array and return true
HAI_receive[dataBufferIndex] = incomingbyte;
receivedMessageSize=messageSize;
storeVal = false;
//Serial.print ("Last data read, it was n. ");
//Serial.println(dataBufferIndex);
dataBufferIndex=0;
return true;
break;
} else { //it's a generic byte of the message - put it in the array
HAI_receive[dataBufferIndex++] = incomingbyte;
//Serial.print ("Read data, now proceed to data n. ");
//Serial.println(dataBufferIndex);
}//END else
}//END if (storeval)
}//END while Serial1.available()
//We've read in all the available Serial data, and don't have a valid string yet, so return false
return false;
} // END getSerialArray
//======MAIN PROGRAM==================================
void setup() {
// Turn the Serial Protocol ON
Serial.begin(9600); //for debug
Serial1.begin(9600); //for communications with HAI panel
while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only
}
delay(2000);
}//END Setup()
void loop() {
if (Serial.available()){
char inChar = (char)Serial.read();
if (inChar == 'f') {
SetFlagTo (417,100); //set flag 417 to X value
sendToHAI();
SetUnitOn (4, 0); // 0= infinite time
sendToHAI();
}//END IF inchar == 'f'
if (inChar == 'l') {
//TEST SetFlag Routine
HAI_login(); //login to HAI console sets up the HAI_send with the message
sendToHAI(); //send the content of HAI_send array to the OPII
}//END if intchar='l'
if (inChar == 't') {
//TEST get temperature routine
getTemperature(49,59); //get temperature of zone 49 = 0x31 = first temperature sensor
sendToHAI();
}//END if intchar='t'
if (inChar == 's') {
//TEST get system status
getSystemStatus(); //get system status
Serial.println("Message ready");
sendToHAI();
}//END if intchar='s'
}//IF Serial.available
/*
//TEST routine that echoes the Serial input to the Serial1 (connected to HAI with a TTL to RS232 adapter)
// check if data has been sent from the computer:
if (Serial.available()) {
// read the most recent byte
byteRead = Serial.read();
//ECHO the value that was read, back to the serial port.
Serial1.write(byteRead);
}
//TEST routine that echoes the Serial1 input from HAI to the Serial port of Arduino for debbugging
if (Serial1.available()) {
// read the most recent byte
byteRead = Serial1.read();
//ECHO the value that was read, back to the serial port.
Serial.write(byteRead);
}//END IF */
// SERIAL READ
//There should be a check to wait a few millis until HAI answers. If it times out = no answer, else proceed
if (Serial1.available()>0){
if (getSerialArray()){ //the function places valid series of bytes in the HAI_receive[] array
Serial.print ("HAI response: ");
//For debug the content is sent on Serial
for (int i=1;i<=receivedMessageSize;i++){
Serial.print (HAI_receive,HEX);
Serial.print (" ");
}//END for
Serial.println("now parsing...");
//CRC CHECKING GOES HERE
//HAI_receive PARSING GOES HERE
switch (HAI_receive[3]){
case 0x14: //SYSTEM STATUS
//can do anything with the data, as a demo it prints out some of them to serial
//the codes and structure for each message are in the HAI omniLINK documentation
Serial.print("Year: ");
Serial.print(HAI_receive[5]);
Serial.print(" - Month: ");
Serial.print(HAI_receive[6]);
Serial.print(" - Day: ");
Serial.println(HAI_receive[7]);
Serial.print ("time: ");
Serial.print(HAI_receive[9]); //HH 8 is day of the week
Serial.print (":");
Serial.print(HAI_receive[10]);//MM
Serial.print (":");
Serial.print(HAI_receive[11]);//SS
break;
case 0x1A: //TEMPERATURE SENSOR
byte numberOfSensors = (HAI_receive[2]-1)/4;
for (int i=0;i<numberOfSensors;i++){
Serial.print("sensor ");
Serial.print(i+1);
Serial.print(": T= ");
Serial.print(omniToC(HAI_receive[5 + 4*i]));
Serial.println(" C");
}//END for
break;
}//END switch
}//END if getSerialArray
}//END if Serial1.available()>0
}//END loop()