/* emonTX Temperature and power pulse ------------------------------------------------------------------------------------------------------------ -ID- -Node Type- 0 - Special allocation in JeeLib RFM12 driver - reserved for OOK use 1-4 - Control nodes 5-10 - Energy monitoring nodes 11-14 --Un-assigned -- 15-16 - Base Station & logging nodes 17-30 - Environmental sensing nodes (temperature humidity etc.) 31 - Special allocation in JeeLib RFM12 driver - Node31 can communicate with nodes on any network group ------------------------------------------------------------------------------------------------------------- TODO: set kamser pins */ #include // make sure V12 (latest) is used if using RFM69CW #include #include #include //********************************************Kamstrup Stuff Start******************************************** #include //Kamstrup setup // Kamstrup Multical 601 word const kregnums[] = { 0x003C,0x0050,0x0056,0x0057,0x0059,0x004a,0x0044 }; char* kregstrings[] = { "Energy","Current Power","Temperature t1","Temperature t2","Temperature diff", "Flow", "Volumen 1" }; #define NUMREGS 7 // Number of registers above #define KAMBAUD 1200 // Units char* units[65] = {"","Wh","kWh","MWh","GWh","j","kj","Mj", "Gj","Cal","kCal","Mcal","Gcal","varh","kvarh","Mvarh","Gvarh", "VAh","kVAh","MVAh","GVAh","kW","kW","MW","GW","kvar","kvar","Mvar", "Gvar","VA","kVA","MVA","GVA","V","A","kV","kA","C","K","l","m3", "l/h","m3/h","m3xC","ton","ton/h","h","hh:mm:ss","yy:mm:dd","yyyy:mm:dd", "mm:dd","","bar","RTC","ASCII","m3 x 10","ton x 10","GJ x 10","minutes","Bitfield", "s","ms","days","RTC-Q","Datetime"}; //********************************************Kamstrup Stuff End******************************************** #define RF_freq RF12_868MHZ // Frequency of RF12B module can be RF12_433MHZ, RF12_868MHZ or RF12_915MHZ. You should use the one matching the module you have. const int nodeID = 10; // emonTx RFM12B node ID const int networkGroup = 210; // emonTx RFM12B wireless network group - needs to be same as emonBase and emonGLCD const int UNO = 1; // Set to 0 if your not using the UNO bootloader (i.e using Duemilanove) - All Atmega's shipped from OpenEnergyMonitor come with Arduino Uno bootloader #define RF69_COMPAT 0 // set to 1 to use RFM69CW //********************************************Kamstrup Stuff Start******************************************** // Pin definitions #define PIN_KAMSER_RX 1 // Kamstrup IR interface RX #define PIN_KAMSER_TX 1 // Kamstrup IR interface TX //********************************************Kamstrup Stuff End******************************************** #define ONE_WIRE_BUS 4 // Data wire is plugged into port 2 on the Arduino OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature. // By using direct addressing its possible to make sure that as you add temperature sensors // the temperature sensor to variable mapping will not change. // To find the addresses of your temperature sensors use the: **temperature_search sketch** DeviceAddress address_T1 = { 0x28, 0x22, 0x70, 0xEE, 0x02, 0x00, 0x00, 0xB8 }; typedef struct { int power; // power int pulse; // pulses int T1; // temperature sensor 1 //********************************************Kamstrup Stuff Start******************************************** int Energy; int CurrentPower; int Temperaturet1; int Temperaturet2; int Temperaturediff; int Flow; int Volumen1; int Volumen2; //********************************************Kamstrup Stuff Start******************************************** } Payload; Payload emontx; const int LEDpin = 9; // Pulse counting settings long pulseCount = 0; // Number of pulses, used to measure energy. unsigned long pulseTime,lastTime; // Used to measure power. double power, elapsedWh; // power and energy int ppwh = 10; // 1000 pulses/kwh = 1 pulse per wh - Number of pulses per wh - found or set on the meter. //********************************************Kamstrup Stuff Start******************************************** // Kamstrup optical IR serial #define KAMTIMEOUT 2000 // Kamstrup timeout after transmit #define POLLINTERVAL 20000 // Polling interval SoftwareSerial kamSer(PIN_KAMSER_RX, PIN_KAMSER_TX, false); // Initialize serial // last poll variable long lastpoll; //********************************************Kamstrup Stuff End******************************************** void setup() { Serial.begin(57600); Serial.println("emonTX Temperature power pulse example"); Serial.println("OpenEnergyMonitor.org"); //********************************************Kamstrup Stuff Start******************************************** // setup kamstrup serial pinMode(PIN_KAMSER_RX,INPUT); pinMode(PIN_KAMSER_TX,OUTPUT); kamSer.begin(KAMBAUD); // initialize lastpoll value lastpoll = 0; //********************************************Kamstrup Stuff End******************************************** sensors.begin(); rf12_initialize(nodeID, RF_freq, networkGroup); // initialize RF rf12_sleep(RF12_SLEEP); pinMode(LEDpin, OUTPUT); digitalWrite(LEDpin, HIGH); attachInterrupt(1, onPulse, FALLING); // KWH interrupt attached to IRQ 1 = pin3 - hardwired to emonTx pulse jackplug. For connections see: http://openenergymonitor.org/emon/node/208 } void loop() { //********************************************Kamstrup Stuff Start******************************************** // check if it is time to do a poll if(millis() - lastpoll > POLLINTERVAL or lastpoll == 0) { // poll the Kamstrup registers for data for (int kreg = 0; kreg < NUMREGS; kreg++) { kamReadReg(kreg); delay(100); } digitalWrite(PIN_LED, digitalRead(PIN_KAMSER_RX)); delay(1000); // update lastpoll lastpoll = millis(); //********************************************Kamstrup Stuff End******************************************** sensors.requestTemperatures(); // Send the command to get temperatures emontx.T1 = sensors.getTempC(address_T1) * 100; emontx.pulse = pulseCount; pulseCount=0; Serial.print(emontx.power); Serial.print("W "); Serial.println(emontx.pulse); send_rf_data(); // *SEND RF DATA* - see emontx_lib digitalWrite(LEDpin, HIGH); delay(2); digitalWrite(LEDpin, LOW); // flash LED } } // kamReadReg - read a Kamstrup register float kamReadReg(unsigned short kreg) { byte recvmsg[30]; // buffer of bytes to hold the received data float rval; // this will hold the final value // prepare message to send and send it byte sendmsg[] = { 0x3f, 0x10, 0x01, (kregnums[kreg] >> 8), (kregnums[kreg] & 0xff) }; kamSend(sendmsg, 5); // listen if we get an answer unsigned short rxnum = kamReceive(recvmsg); // check if number of received bytes > 0 if(rxnum != 0){ // decode the received message rval = kamDecode(kreg,recvmsg); // print out received value to terminal (debug) Serial.print(kregstrings[kreg]); Serial.print(": "); Serial.print(rval); Serial.print(" "); Serial.println(); return rval; } } // kamSend - send data to Kamstrup meter void kamSend(byte const *msg, int msgsize) { // append checksum bytes to message byte newmsg[msgsize+2]; for (int i = 0; i < msgsize; i++) { newmsg[i] = msg[i]; } newmsg[msgsize++] = 0x00; newmsg[msgsize++] = 0x00; int c = crc_1021(newmsg, msgsize); newmsg[msgsize-2] = (c >> 8); newmsg[msgsize-1] = c & 0xff; // build final transmit message - escape various bytes byte txmsg[20] = { 0x80 }; // prefix int txsize = 1; for (int i = 0; i < msgsize; i++) { if (newmsg[i] == 0x06 or newmsg[i] == 0x0d or newmsg[i] == 0x1b or newmsg[i] == 0x40 or newmsg[i] == 0x80) { txmsg[txsize++] = 0x1b; txmsg[txsize++] = newmsg[i] ^ 0xff; } else { txmsg[txsize++] = newmsg[i]; } } txmsg[txsize++] = 0x0d; // EOF // send to serial interface for (int x = 0; x < txsize; x++) { kamSer.write(txmsg[x]); } } // kamReceive - receive bytes from Kamstrup meter unsigned short kamReceive(byte recvmsg[]) { byte rxdata[50]; // buffer to hold received data unsigned long rxindex = 0; unsigned long starttime = millis(); kamSer.flush(); // flush serial buffer - might contain noise byte r; // loop until EOL received or timeout while(r != 0x0d){ // handle rx timeout if(millis()-starttime > KAMTIMEOUT) { Serial.println("Timed out listening for data"); return 0; } // handle incoming data if (kamSer.available()) { // receive byte r = kamSer.read(); if(r != 0x40) { // don't append if we see the start marker // append data rxdata[rxindex] = r; rxindex++; } } } // remove escape markers from received data unsigned short j = 0; for (unsigned short i = 0; i < rxindex -1; i++) { if (rxdata[i] == 0x1b) { byte v = rxdata[i+1] ^ 0xff; if (v != 0x06 and v != 0x0d and v != 0x1b and v != 0x40 and v != 0x80){ Serial.print("Missing escape "); Serial.println(v,HEX); } recvmsg[j] = v; i++; // skip } else { recvmsg[j] = rxdata[i]; } j++; } // check CRC if (crc_1021(recvmsg,j)) { Serial.println("CRC error: "); return 0; } return j; } // kamDecode - decodes received data float kamDecode(unsigned short const kreg, byte const *msg) { // skip if message is not valid if (msg[0] != 0x3f or msg[1] != 0x10) { return false; } if (msg[2] != (kregnums[kreg] >> 8) or msg[3] != (kregnums[kreg] & 0xff)) { return false; } // decode the mantissa long x = 0; for (int i = 0; i < msg[5]; i++) { x <<= 8; x |= msg[i + 7]; } // decode the exponent int i = msg[6] & 0x3f; if (msg[6] & 0x40) { i = -i; }; float ifl = pow(10,i); if (msg[6] & 0x80) { ifl = -ifl; } // return final value return (float )(x * ifl); } // crc_1021 - calculate crc16 long crc_1021(byte const *inmsg, unsigned int len){ long creg = 0x0000; for(unsigned int i = 0; i < len; i++) { int mask = 0x80; while(mask > 0) { creg <<= 1; if (inmsg[i] & mask){ creg |= 1; } mask>>=1; if (creg & 0x10000) { creg &= 0xffff; creg ^= 0x1021; } } } return creg; } //********************************************Kamstrup Stuff End******************************************** // The interrupt routine - runs each time a falling edge of a pulse is detected void onPulse() { lastTime = pulseTime; //used to measure time between pulses. pulseTime = micros(); pulseCount++; //pulseCounter emontx.power = int((3600000000.0 / (pulseTime - lastTime))/ppwh); //Calculate power }