This library allows to parse and work with data sent by the Paradigma pelletti oven.
Revision 0:4873b21e0bca, committed 2013-06-22
- Comitter:
- leihen
- Date:
- Sat Jun 22 10:59:05 2013 +0000
- Child:
- 1:9c48326ad8c9
- Commit message:
- Fixes
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ParadigmaBase.h Sat Jun 22 10:59:05 2013 +0000 @@ -0,0 +1,48 @@ +#ifndef __PARADIGMABASE_H__ +#define __PARADIGMABASE_H__ + + +typedef unsigned char byte; +typedef short word; +typedef unsigned short uword; +typedef unsigned long ulong; + + +__packed class ParadigmaDword +{ + protected: + unsigned long m_data; + + public: + ParadigmaDword() : m_data(0) {} + + /** On the MBED we need to swap the high and lowbytes after reading from stream. + * this function will be called from reading routine. + */ + void adjustEndiness() { m_data = ((m_data>>24)&0xFF) | ((m_data>>8)&0x0000FF00) | ((m_data<<8)&0x00FF0000) | ((m_data<<24)&0xFF000000); } + + ParadigmaDword& operator=(ulong d) { m_data = d; return *this; } + ParadigmaDword& operator=(ParadigmaDword d) { m_data = d.m_data; return *this; } + + operator ulong() const { return m_data; } +}; + +__packed class ParadigmaWord +{ + protected: + unsigned short m_data; + + public: + ParadigmaWord() : m_data(0) {} + + /** On the MBED we need to swap the high and lowbytes after reading from stream. + * this function will be called from reading routine. + */ + void adjustEndiness() { m_data = ((m_data&0xFF)<<8) | ((m_data>>8)&0xFF); } + + ParadigmaWord& operator=(word d) { m_data = d; return *this; } + ParadigmaWord& operator=(ParadigmaWord d) { m_data = d.m_data; return *this; } + + operator word() const { return m_data; } +}; +#endif // __PARADIGMABASE_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ParadigmaData.cpp Sat Jun 22 10:59:05 2013 +0000 @@ -0,0 +1,171 @@ +#include "ParadigmaData.h" + +#define DEBUG +#include "debug.h" + + + +ParadigmaMonitorData& ParadigmaMonitorData::operator<<(char c) +{ + int nLen = 0; + if (m_activeDataBlock != Invalid) { + // The actual data block had been detected already, so write into the data block + m_Buffer[m_actualPos++] = c; + // Build checksum on the fly + m_checksum += c; + // Now check if all bytes have been received already + if (m_activeDataBlock == ParadigmaMonitorDataset1) { + // for datablock 1 + nLen = sizeof(m_Data1); + if (nLen == m_actualPos) { + // all bytes received, so reset counter and check if checksum is matching + if (m_checksum != 0) { + ERR("Data corruption error. Received invalid data ! (checksum is %02x)", m_checksum); + } else { + memcpy(&m_Data1, m_Buffer, nLen); + m_Data1.Aussentemp.adjustEndiness(); + m_Data1.Warmwassertemp.adjustEndiness(); + m_Data1.Kesselvorlauf.adjustEndiness(); + m_Data1.Kesselruecklauf.adjustEndiness(); + m_Data1.RaumtemperaturHK1.adjustEndiness(); + m_Data1.RaumtemperaturHK2.adjustEndiness(); + m_Data1.VorlauftemperaturHK1.adjustEndiness(); + m_Data1.VorlauftemperaturHK2.adjustEndiness(); + m_Data1.RuecklauftemperaturHK1.adjustEndiness(); + m_Data1.RuecklauftemperaturHK2.adjustEndiness(); + m_Data1.PuffertemperaturOben.adjustEndiness(); + m_Data1.PuffertemperaturUnten.adjustEndiness(); + m_Data1.Zirkulationstemperatur.adjustEndiness(); + INFO("*********************** received new data !"); + callBack(); + } + invalidateHeader(); + } + } else { + // for datablock 2 + nLen = sizeof(m_Data2); + if (nLen == m_actualPos) { + // all bytes received, so reset counter and check if checksum is matching + if (m_checksum != 0) { + ERR("Data corruption error. Received invalid data ! (checksum is %02x)", m_checksum); + } else { + memcpy(&m_Data2, m_Buffer, nLen); + m_Data2.RaumsollHK1.adjustEndiness(); + m_Data2.RaumsollHK2.adjustEndiness(); + m_Data2.VorlaufsollHK1.adjustEndiness(); + m_Data2.VorlaufsollHK2.adjustEndiness(); + m_Data2.Warmwassersolltemp.adjustEndiness(); + m_Data2.Puffersolltemp.adjustEndiness(); + m_Data2.BetriebsstundenKessel.adjustEndiness(); + m_Data2.AnzahlKesselstarts.adjustEndiness(); + m_Data2.StoercodeKessel.adjustEndiness(); + INFO("*********************** received new data !"); + callBack(); + } + invalidateHeader(); + } + } + } else { + // Still trying to detect a valid data block, check to see if a valid address-part is here + switch (m_actualPos) { + case 0 : // block type + if ( (c== ParadigmaParameters) || (c== ParadigmaVariables)) { + m_Header.block_type = (ParadigmaBlockType_t)c; + m_actualPos++; + m_checksum = c; + INFO("Block start !\n"); + } + break; + + case 1 : // length + m_Header.block_length = c; + m_actualPos++; + m_checksum += c; + INFO("Block len !\n"); + break; + + case 2 : // Message ID + if ( c == ParadigmaMessage ) { + m_Header.message_id = (ParadigmaMessageID_t)c; + m_actualPos++; + m_checksum += c; + } else { + invalidateHeader(); + INFO("Rejected due to incorrect Message ID %d\n", c); + } + break; + + case 3 : // + if ( (c == ParadigmaMonitorDataset1) || (c == ParadigmaMonitorDataset2)) { + m_Header.dataset_type = (ParadigmaDatasetType_t)c; + m_actualPos = 0; + m_checksum += c; + m_activeDataBlock = (ParadigmaDatasetType_t)c; + INFO("Dataset Number !\n"); + } else { + invalidateHeader(); + INFO("Rejected due to incorrect Dataset number %d\n", c); + } + break; + + default: + invalidateHeader(); + break; + } + } + return *this; +} + +/** Function will reset all header and actual position information so that search for valid header + * will be reset and starting from beginning with receival of next character. + * + * Will have to clearup the header itsself and the checksum. Also the actual data block information + * needs to be invalidated. Actual position will also be reset + */ +void ParadigmaMonitorData::invalidateHeader() +{ + // Invalidate Header information + memset(&m_Header, 0, sizeof(m_Header)); + // Set active data block information to invalid + m_activeDataBlock = (ParadigmaDatasetType_t)Invalid; + // Reset actual position with in Buffer so that next char starts from beginning + m_actualPos = 0; + // Reset the checksum, because it will be calculated on the fly. + m_checksum = 0; +} + +/** Function will let user select which temperature to retrieve */ +ParadigmaTemperature ParadigmaMonitorData::getTemperature(ParadigmaTemperatureSelector_t sel) +{ + ParadigmaTemperature temp; + switch(sel) { + // Temperatures from Monitor Data 1 + case T_aussen : temp = m_Data1.Aussentemp; break; + case T_warm_wasser : temp = m_Data1.Warmwassertemp; break; + case T_kessel_vorlauf : temp = m_Data1.Kesselvorlauf; break; + case T_kessel_ruecklauf : temp = m_Data1.Kesselruecklauf; break; + case T_HK1_raum : temp = m_Data1.RaumtemperaturHK1; break; + case T_HK2_raum : temp = m_Data1.RaumtemperaturHK2; break; + case T_HK1_vorlauf : temp = m_Data1.VorlauftemperaturHK1; break; + case T_HK2_vorlauf : temp = m_Data1.VorlauftemperaturHK2; break; + case T_HK1_ruecklauf : temp = m_Data1.RuecklauftemperaturHK1; break; + case T_HK2_ruecklauf : temp = m_Data1.RuecklauftemperaturHK2; break; + case T_puffer_oben : temp = m_Data1.PuffertemperaturOben; break; + case T_puffer_unten : temp = m_Data1.PuffertemperaturUnten; break; + case T_zirkulation : temp = m_Data1.Zirkulationstemperatur; break; + + // Temperatures form Monitor Data 2 + case T_HK1_raum_soll : temp = m_Data2.RaumsollHK1; break; + case T_HK2_raum_soll : temp = m_Data2.RaumsollHK2; break; + case T_HK1_vorlauf_soll : temp = m_Data2.VorlaufsollHK1; break; + case T_HK2_vorlauf_soll : temp = m_Data2.VorlaufsollHK2; break; + case T_warm_wasser_soll : temp = m_Data2.Warmwassersolltemp; break; + case T_puffer_soll : temp = m_Data2.Puffersolltemp; break; + + default: // Invalid selecion ! + ERR("**** An unknown selection for the temperature was made !"); + break; + } + + return temp; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ParadigmaData.h Sat Jun 22 10:59:05 2013 +0000 @@ -0,0 +1,211 @@ +#ifndef __PARADIGMA_DATA_H__ +#define __PARADIGMA_DATA_H__ + +#include "mbed.h" +#include "ParadigmaBase.h" +#include "ParadigmaTemperature.h" +#include "ParadigmaDateTime.h" + +#include <string> +using namespace std; + +/** ParadigmaBlockType_t enumerates valid values for the block type. The block type is always the first + * data byte in a response from paradigma. There are currently two known values. + */ +typedef __packed enum { + ParadigmaVariables = 0xFC, + ParadigmaParameters = 0xFD, +} ParadigmaBlockType_t; + +/** ParadigmaMessageID_t specifies the content of the transmitted object. Currently only one valid value + * is known which indicates a valuid variable or parameter block is being transmitted */ +typedef __packed enum { + ParadigmaMessage = 0x0C, +} ParadigmaMessageID_t; + +/** ParadigmaDatasetType_t specifies which type of information is contained in the datablock. Currently + * known values are : Dataset1 and Dataset2 for Variables, and Parameters for different settings which + * can be changed and queried by the user. + */ +typedef __packed enum { + ParadigmaMonitorDataset1 = 0x01, + ParadigmaMonitorDataset2 = 0x02, + ParadigmaParameterset = 0x03, + ParadigmaUnknown1 = 0x14, + Invalid = 0xFF +} ParadigmaDatasetType_t; + +typedef char ParadigmaParameterAddress_t[3]; + +const ParadigmaParameterAddress_t ParadigmaAdresses[] = { + {0x00, 0x02, 0x2A}, // Heizkreis 1 + {0x01, 0x88, 0x2A}, // Heizkreis 2 + {0x03, 0x0E, 0x09}, // Warmwasser + {0x03, 0xF7, 0x0F}, // Anlagendaten Kessel/Puffer und Zirkulation + {0x03, 0xE6, 0x12}, // Wartung Telefonnummer + {0x03, 0x17, 0x70}, // Warmwasserprogramm 1 + {0x03, 0x87, 0x70}, // Warmwasserprogramm 2 + {0x04, 0x06, 0x70}, // Zirkulationszeitprogramm 1 + {0x04, 0x76, 0x70}, // Zirkulationszeitprogramm 2 + {0x00, 0x2C, 0x70}, // Heizzeitprogramm 1 HK1 + {0x00, 0x9C, 0x70}, // Heizzeitprogramm 2 HK1 + {0x01, 0x0C, 0x70}, // Heizzeitprogramm 3 HK1 + {0x01, 0xB2, 0x70}, // Heizzeitprogramm 1 HK2 + {0x02, 0x22, 0x70}, // Heizzeitprogramm 2 HK2 + {0x02, 0x92, 0x70}, // Heizzeitprogramm 3 HK2 + {0x05, 0x08, 0x03} // Anlagendaten Kessel/Puffer 2 +}; + +/** ParadigmaBlockHeader_t represents the header of any sent block by paradigma. */ +typedef __packed struct { + ParadigmaBlockType_t block_type; // 0xFC for normal observable variables, 0xFD for parameter blocks + unsigned char block_length; + ParadigmaMessageID_t message_id; // always 0x0C + ParadigmaDatasetType_t dataset_type; +} ParadigmaBlockHeader_t; + +typedef __packed struct { + ParadigmaDateTime DateTime; + ParadigmaTemperature Aussentemp; // (in 0,1 Grad Schritten) + ParadigmaTemperature Warmwassertemp; // (in 0,1 Grad Schritten) + ParadigmaTemperature Kesselvorlauf; // (in 0,1 Grad Schritten) + ParadigmaTemperature Kesselruecklauf; // (in 0,1 Grad Schritten) + + ParadigmaTemperature RaumtemperaturHK1; // (in 0,1 Grad Schritten) + ParadigmaTemperature RaumtemperaturHK2; // (in 0,1 Grad Schritten) + ParadigmaTemperature VorlauftemperaturHK1; // (in 0,1 Grad Schritten) + ParadigmaTemperature VorlauftemperaturHK2; // (in 0,1 Grad Schritten) + ParadigmaTemperature RuecklauftemperaturHK1; // (in 0,1 Grad Schritten) + ParadigmaTemperature RuecklauftemperaturHK2; // (in 0,1 Grad Schritten) + ParadigmaTemperature PuffertemperaturOben; // (in 0,1 Grad Schritten) + ParadigmaTemperature PuffertemperaturUnten; // (in 0,1 Grad Schritten) + ParadigmaTemperature Zirkulationstemperatur; // (in 0,1 Grad Schritten) + + byte Checksumme; +} MonDta1_t; + +typedef __packed struct { + ParadigmaTemperature RaumsollHK1; + ParadigmaTemperature RaumsollHK2; + ParadigmaTemperature VorlaufsollHK1; + ParadigmaTemperature VorlaufsollHK2; + ParadigmaTemperature Warmwassersolltemp; + ParadigmaTemperature Puffersolltemp; + __packed struct { + word PHK1:1; + word PHK2:1; + word PK:1; + word Mischer1Auf:1; + word Mischer1Zu:1; + word Mischer2Auf:1; + word Mischer2Zu:1; + word ULV:1; + word PZ:1; + word B1:1; + word Taster:1; + word LONModul:1; + word OTModul:1; + word reserver:3; + } ZustandAusgaenge; + ParadigmaDword BetriebsstundenKessel; + ParadigmaDword AnzahlKesselstarts; + ParadigmaWord StoercodeKessel; + byte StoercodeFuehler; + byte BetriebsartHK1; + byte NiveauHK1; + byte BetriebsartHK2; + byte NiveauHK2; + byte LeistungPHK1; + byte LeistungPHK2; + byte LeistungPK; + + byte Checksumme; +} MonDta2_t; + + +/** Class ParadigmaMonitorData encapsulates the data representation and parsing of the + * data blocks that are being sent by the paradigma heater. The class also provides + * a stream type functionality to detect and parse the data from a stream of chars. + */ +class ParadigmaMonitorData +{ + FunctionPointer m_Callback; + + MonDta1_t m_Data1; + MonDta2_t m_Data2; + + char m_Buffer[50]; + ParadigmaBlockHeader_t m_Header; + ParadigmaDatasetType_t m_activeDataBlock; + int m_actualPos; + char m_checksum; + + void invalidateHeader(); + void callBack() { m_Callback.call(); } + word swapWord(word d) { return ((d&0xFF)<<8) | ((d>>8)&0xFF); } + ulong swapDWord(ulong d) { return ((d>>24)&0xFF) | ((d>>8)&0x0000FF00) | ((d<<8)&0x00FF0000) | ((d<<24)&0xFF000000); } + public: + /** Public constructor. Will initialize all internal data and variables. */ + ParadigmaMonitorData(): m_checksum(0) { memset(&m_Data1, 0, sizeof(m_Data1)); memset(&m_Data2, 0, sizeof(m_Data2)); invalidateHeader(); } + /** Access function to get the transmitted date time object. + * @returns : the date and time objects of the transmitted data. + */ + ParadigmaDateTime getDateTime() { return m_Data1.DateTime; } + /** Access function to the Outside air temperature (AUSSENTEMP). + * @returns : the outside air temperature. + */ + ParadigmaTemperature getAussentemp() { return m_Data1.Aussentemp; } + /** Access function to the warm water temperature (WARMWASSERTEMP). + * @returns : the warm water temperature. + */ + ParadigmaTemperature getWarmwassertemp() { return m_Data1.Warmwassertemp; } + /** Access function to the 'kessel vorlauf' temperature. + * @returns : the temperature of the 'kessel vorlauf'. + */ + ParadigmaTemperature getKesselvorlauf() { return m_Data1.Kesselvorlauf; } + /** Access function to the 'kessel ruecklauf' temperature. + * @returns : the temperature of the 'kessel ruecklauf'. + */ + ParadigmaTemperature getKesselruecklauf() { return m_Data1.Kesselruecklauf; } + /** Access function to the room temperature of heating circuit 1. + * @returns : the room temperature of heating circuit 1. + */ + ParadigmaTemperature getRaumtemperaturHK1() { return m_Data1.RaumtemperaturHK1; } + /** Access function to the room temperature of heating circuit 2. + * @returns : the room temperature of heating circuit 2. + */ + ParadigmaTemperature getRaumtemperaturHK2() { return m_Data1.RaumtemperaturHK2; } + ParadigmaTemperature getVorlauftemperaturHK1() { return m_Data1.VorlauftemperaturHK1; } + ParadigmaTemperature getVorlauftemperaturHK2() { return m_Data1.VorlauftemperaturHK2; } + ParadigmaTemperature getRuecklauftemperaturHK1() { return m_Data1.RuecklauftemperaturHK1; } + ParadigmaTemperature getRuecklauftemperaturHK2() { return m_Data1.RuecklauftemperaturHK2; } + ParadigmaTemperature getPuffertemperaturOben() { return m_Data1.PuffertemperaturOben; } + ParadigmaTemperature getPuffertemperaturUnten() { return m_Data1.PuffertemperaturUnten; } + ParadigmaTemperature getZirkulationstemperatur() { return m_Data1.Zirkulationstemperatur; } + + ParadigmaTemperature getRaumsollHK1() { return m_Data2.RaumsollHK1;} + ParadigmaTemperature getRaumsollHK2() { return m_Data2.RaumsollHK2;} + ParadigmaTemperature getVorlaufsollHK1() { return m_Data2.VorlaufsollHK1;} + ParadigmaTemperature getVorlaufsollHK2() { return m_Data2.VorlaufsollHK2;} + ParadigmaTemperature getWarmwassersolltemp() { return m_Data2.Warmwassersolltemp;} + ParadigmaTemperature getPuffersolltemp() { return m_Data2.Puffersolltemp;} + + /** Function will let the user select the temperature to be returned. + * @param sel : Will specify which temperature value to return. + * @returns : the temperature object as selected by param sel. + */ + ParadigmaTemperature getTemperature(ParadigmaTemperatureSelector_t sel); + + ulong getBetriebsstundenKessel() { return (ulong)m_Data2.BetriebsstundenKessel; } + ulong getAnzahlKesselstarts() { return (ulong)m_Data2.AnzahlKesselstarts; } + word getStoercodeKessel() { return (word)m_Data2.StoercodeKessel; } + byte getStoercodeFuehler() { return (byte)m_Data2.StoercodeFuehler; } + + + ParadigmaMonitorData& operator<<( char c ); + ParadigmaMonitorData& operator<<( char Buffer[] ); + + void attach( void (*fct)(void) ) { m_Callback.attach(fct);} +}; + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ParadigmaDateTime.h Sat Jun 22 10:59:05 2013 +0000 @@ -0,0 +1,32 @@ +#ifndef __PARADIGMADATETIME_H__ +#define __PARADIGMADATETIME_H__ + +#include <string> + +__packed class ParadigmaDateTime +{ + protected: + int bcdToDec(char n) const { return (((unsigned)n)>>4)*10 + (n&0x0f); } + + private: + unsigned char m_Date; + unsigned char m_Month; + unsigned char m_Minute; + unsigned char m_Hour; + + + public: + ParadigmaDateTime() : m_Date(0), m_Month(0), m_Minute(0), m_Hour(0) {} + + operator string() const { char Buffer[15]; sprintf(Buffer, "%02d.%02d, %02d:%02d", getDate(), getMonth(), getHour(), getMinute()); return Buffer; } + + std::string getDateString() { char Buffer[15]; sprintf(Buffer, "%02d.%02d", getDate(), getMonth()); return Buffer; } + std::string getTimeString() { char Buffer[15]; sprintf(Buffer, "%02d:%02d", getHour(), getMinute()); return Buffer; } + + unsigned char getDate() const { return bcdToDec(m_Date); } + unsigned char getMonth() const { return bcdToDec(m_Month); } + unsigned char getMinute() const { return bcdToDec(m_Minute); } + unsigned char getHour() const { return bcdToDec(m_Hour); } +}; + +#endif // __PARADIGMADATETIME_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ParadigmaTemperature.h Sat Jun 22 10:59:05 2013 +0000 @@ -0,0 +1,55 @@ +#ifndef __PARADIGMATEMPERATURE_H__ +#define __PARADIGMATEMPERATURE_H__ + +#include <string> +#include "ParadigmaBase.h" + + + +typedef enum { + // Temperatures from Monitor Data 1 + T_aussen, + T_warm_wasser, + T_kessel_vorlauf, + T_kessel_ruecklauf, + T_HK1_raum, + T_HK2_raum, + T_HK1_vorlauf, + T_HK2_vorlauf, + T_HK1_ruecklauf, + T_HK2_ruecklauf, + T_puffer_oben, + T_puffer_unten, + T_zirkulation, + // Temperatures form Monitor Data 2 + T_HK1_raum_soll, + T_HK2_raum_soll, + T_HK1_vorlauf_soll, + T_HK2_vorlauf_soll, + T_warm_wasser_soll, + T_puffer_soll +} ParadigmaTemperatureSelector_t; + + +__packed class ParadigmaTemperature +{ + unsigned short m_temp; + + public: + ParadigmaTemperature() : m_temp(0) {} + + operator string() const { char Buffer[14]; sprintf(Buffer, "%3d,%1d C", m_temp/10, m_temp%10); return Buffer; } + operator float() const { return ((float)m_temp)/10.0f; } + + ParadigmaTemperature& operator=(float f) { m_temp = (int)ceil(f*10.0f); return *this; } + ParadigmaTemperature& operator=(ParadigmaTemperature &p) { m_temp = p.m_temp; return *this; } + + public: + + /** On the MBED we need to swap the high and lowbytes after reading from stream. + * this function will be called from reading routine. + */ + void adjustEndiness() { m_temp = ((m_temp&0xFF)<<8) | ((m_temp>>8)&0xFF); } +}; + +#endif \ No newline at end of file