//Define the arrays that will contain the data
#define DATABUFFERSIZE 255
const byte headerByte = 0x5A; // or '!', or whatever your start character is
byte byteRead;
byte HAI_send[DATABUFFERSIZE];
byte HAI_receive[DATABUFFERSIZE];
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[i]);
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[i]);
//calculate high and low byte of the CRC
//byte low_byte = 0xff & crc;
//byte high_byte = crc >> 8;
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[i]);
//calculate high and low byte of the CRC
byte low_byte = 0xff & crc;
byte high_byte = crc >> 8;
HAI_send[8] = low_byte;// (flagNumber);
HAI_send[9] = high_byte;// (flagNumber);
}//END SetFlagTo
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
//TBD command 0x01
}
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 getTemperature (int Zone){
//TBD - command
}
//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
//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;
byte messageSize=DATABUFFERSIZE; //length of the message, at the beginning it can be the maximum value
boolean storeVal; //flag to define if the incomingbyte have to be put in the array
while(Serial1.available()>0){
Serial.println("listening...");//DEBUG
byte incomingbyte = Serial1.read();
if(incomingbyte==headerByte){
Serial.println("Reading HAI message... ");//DEBUG
dataBufferIndex = 1; //Initialize our dataBufferIndex variable
storeVal = true;
messageSize=2;//so i read at least 2 bytes
}//END if
if(storeVal){
//Let's check our index here, and abort if we're outside our buffer size
//We use our define here so our buffer size can be easily modified
if(dataBufferIndex == 2){
//this is the length of the message - I put it in a variable
messageSize = incomingbyte + 2 +2; //adding the 2 CRC values and Header and length - this is the final index
}//END if databufferIndex==2
if(dataBufferIndex == DATABUFFERSIZE){
//Oops, our index is pointing to an array element outside our buffer.
dataBufferIndex = 0;
break;
}
else{
HAI_receive[dataBufferIndex++] = incomingbyte;
//dataBuffer[dataBufferIndex] = 0; //null terminate the C string
if (dataBufferIndex==messageSize){//We have read all the data
return true;
}
}//END else
}
else{
}
}
//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
//setflag is 9 byte long including CRC
Serial.print("Sent to HAI:");
for (int i = 1; i <= 9; i++) {
Serial1.write(HAI_send[i]); //to HAI
Serial.print(HAI_send[i],HEX);//for debugging - comment out in final release
Serial.write (" ");
}//END for
Serial.println();
//For debug the content is sent on Serial
Serial.print("received:");
for (int i=1;i<=9;i++){
Serial.print (HAI_receive[i],HEX);
Serial.print (" ");
}//END for
Serial.println();
}//END IF inchar == 'f'
if (inChar == 'l') {
//TEST SetFlag Routine
HAI_login(); //login to HAI console sets up the HAI_send with the message
// NOTE: the send function must be generalised void SendToHAI (byte lenghtOfMessage) - could be omitted and derived from 2nd element of the array
// I don't know how to pass an array as a function argument in C (pointers?) but for the purpose
// the global HAI_send and HAI_receive arrays are just fine.
//send the message
//login is 9 byte long including CRC
for (int i = 1; i <= 9; i++) {
Serial1.write(HAI_send[i]); //to HAI
Serial.print(HAI_send[i],HEX); //for debugging - comment out in final release
Serial.write (" ");
}//END FOR int
Serial.println();
}//END if intchar='l'
}//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
if (getSerialArray()){ //the function places valid series of bytes in the HAI_receive[] array
//For debug the content is sent on Serial
for (int i=1;i<=9;i++){
Serial.print (HAI_receive[i],HEX);
Serial.print (" ");
}//END for
Serial.println();
//CRC CHECKING GOES HERE
//HAI_receive PARSING GOES HERE
}//END if getSerialArray
}//END loop()