// Sketch for EmonTX v2 - Serial connection - Interrupt or Timer based pulse detection // Version 1.1 // // Dependencies // Dallas Temperature Control: http://milesburton.com/Main_Page?title=Dallas_Temperature_Control_Library // Emonlib Library: https://github.com/openenergymonitor/EmonLib // JeeLib: http://github.com/jcw/jeelib // One Wire: http://www.pjrc.com/teensy/td_libs_OneWire.html // Simple Timer: http://playground.arduino.cc/Code/SimpleTimer const int SAMPLE_INTERVAL = 10000; // Set to time (ms) between each send const bool CT1 = 1; // Set to 0 to disable CT channel 1 const bool CT2 = 1; // Set to 0 to disable CT channel 2 const bool CT3 = 1; // Set to 0 to disable CT channel 3 const bool TEMP = 1; // Set to 0 to disable Temperature Channel const bool RP1 = 1; // Set to 0 to disable Real Power on CT1 channel (requires ac/ac adaptor) const bool RP2 = 1; // Set to 0 to disable Real Power on CT1 channel (requires ac/ac adaptor) const bool RP3 = 1; // Set to 0 to disable Real Power on CT1 channel (requires ac/ac adaptor) const bool VRMS = 1; // Set to 0 to disable VRMS channel (requires ac/ac adaptor) const bool PULS = 1; // Set to 0 to disable PULSE channel (requires pulse sensor) const bool RF = 0; // Set to 0 to disable RFM12B forwarding. const float CT_CALIB = 83.33; // Calibration coef for standard YHDC SCT-013-000 CT. Set to 111.11 when 18ohm burden resistor, 83.33 when 24ohm burden resistor const float VO_CALIB = 227.59; // Calibration coef for the Voltage Sensor (AC/AC Adaptor). Change to value for Adaptor in use. const float VO_PHASE_SHIFT = 1.7; // Phase shift value for the Voltage Sensor const float VOLTAGE = 240.0; // Set to nominal voltage of supply const int MYNODEID = 5; // Node ID of Rx (range 0-30) const int NETWORK = 210; // Network group (can be in the range 1-250) #define RFFREQ RF12_868MHZ // Freq of RF12B can be RF12_433MHZ, RF12_868MHZ or RF12_915MHZ. Match freq to module const int FILTERSETTLETIME = 30000; // Initial wait time (ms) for sensor filters to settle const int PULSEPIN = 3; // Pulse is detected on this pin const float PPWH = 0.00317; // Meter pulses per wh value - example 1000 pulses/kwh = 1 pulse per wh const int LEDPIN = 9; // emonTX LED uses this pin const int ONE_WIRE_BUS = 4; // Data wire is on port 2 of the Arduino /* Variables */ bool settled; // Used to control one time wait for settle of filters after boot int pulsePower; // Calculated (average) watts per hour leading to a pulse int pulseCount; // Sent count of pulses, resets to 0 after send unsigned long pulseTime; // Pulse time used in pulse power calculation unsigned long lastTime; // Pulse time used in pulse power calculation /* Suppport for unclean pulse detection using timer */ const bool TIMERPULSE = 1; // ** If clean pulse set to 0 (use interupt), if not set to 1 and set PULSEPIN_ values const int PULSEPIN_POLL_INTERVAL = 49; // ** How often the pulse pin state, LOW or HIGH, is tested (ms) - e.g 49ms to (mostly) avoid tests coinciding with the 20ms cycle of a 50Hz supply. const int PULSEPIN_PULSE_LOWS = 12; // ** A pulse is detected if this number of LOWS (max 15) follow a HIGH (the last high on going to LOwS) /* Variables */ unsigned int pulsePinStates; // ** 16 Pulse states, HIGH or LOW, are stored as bits, 1 or 0, in this variable /* includes followed by related instance create commands */ #include #include SimpleTimer timer; // Create SimpleTimer instance #include OneWire oneWire(ONE_WIRE_BUS); // Setup a OneWire instance for OneWire devices (not just Maxim/Dallas temperature ICs) #include DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature DeviceAddress sensor; #include EnergyMonitor ct1, ct2, ct3; // Create EnregyMonitor instances for all CT sensor channels /* Send record type 'NODE' with sensor data in CSV format to Serial out */ void send_node_data() { digitalWrite(LEDPIN, HIGH); delay(2); digitalWrite(LEDPIN, LOW); // flash LED settled = (settled | (millis() > FILTERSETTLETIME)); // Detect if now after the intial wait for filters to settle /* The receiving script waits for a complete line. Values are printed as we go. */ if (settled) { Serial.print("NODE"); if (CT1) { Serial.print(","); Serial.print(int(ct1.calcIrms(1480) * VOLTAGE)); } if (CT2) { Serial.print(","); Serial.print(int(ct2.calcIrms(1480) * VOLTAGE)); } if (CT3) { Serial.print(","); Serial.print(int(ct3.calcIrms(1480) * VOLTAGE)); } if (RP1) { Serial.print(","); ct1.calcVI(20,2000); Serial.print(int(ct1.realPower)); } // Cause calculation of and print realpower if (RP2) { Serial.print(","); ct2.calcVI(20,2000); Serial.print(int(ct2.realPower)); } if (RP3) { Serial.print(","); ct3.calcVI(20,2000); Serial.print(int(ct3.realPower)); } if (VRMS) { Serial.print(","); Serial.print(ct1.Vrms); } if (TEMP) { Serial.print(","); sensors.requestTemperatures(); Serial.print(sensors.getTempCByIndex(0)); } if (PULS) { Serial.print(","); Serial.print(pulsePower); Serial.print(","); Serial.print(pulseCount); pulseCount = 0; } Serial.println(" "); delay(100); } } /* Called when Pulse detected */ void onPulse() { pulseTime = micros(); pulseCount++; // Pulse counter pulsePower = int((3600000000.0 / (pulseTime - lastTime)) / PPWH); // Calculate power of last pulse lastTime = pulseTime; // Keep the pulse time - used to calculate time between pulses. } /* Support for unclean pulse detection */ void DetectPulse() { // ** Called by a timer pulsePinStates = (pulsePinStates << 1) | digitalRead(PULSEPIN); // ** Add state to a history of 16 PULSEPIN states if ((pulsePinStates << (15 - PULSEPIN_PULSE_LOWS)) == 0xF000) onPulse(); // ** onPulse when HIGH followed by x LOWs (a stable fall) } void setup() { pinMode(LEDPIN, OUTPUT); // emonTX LED set for output use to indicate sends digitalWrite(LEDPIN, HIGH); delay(10000); /* Data is sent over Serial - sends that starts with # are treated as comment by the receiving script */ Serial.begin(9600); Serial.println("#EmonTX v2 - Serial - Pulse - v1.1" ); if (CT1) ct1.currentTX(1, CT_CALIB); // (ADC input, calibration) if (CT2) ct2.currentTX(2, CT_CALIB); if (CT3) ct3.currentTX(3, CT_CALIB); if (RP1) ct1.voltageTX(VO_CALIB, VO_PHASE_SHIFT); // (calibration, phase_shift) if (RP2) ct2.voltageTX(VO_CALIB, VO_PHASE_SHIFT); if (RP3) ct3.voltageTX(VO_CALIB, VO_PHASE_SHIFT); if (TEMP) { sensors.begin(); if(!sensors.getAddress(sensor, 0)) Serial.println("#Unable to find a DS18B20 Temperature Sensor"); } if (PULS) { if (TIMERPULSE) { // ** Timer driven pulse detection For unclean Pulse data that bounces or suffers noise pinMode(PULSEPIN, INPUT); // ** Change the pulse pin to input digitalWrite(PULSEPIN, HIGH); // ** Enable it's internal pull-up resistor so HIGH is normal state timer.setInterval(PULSEPIN_POLL_INTERVAL, DetectPulse); // ** Configure timer to call pulsepin history / pulse detect code } else { // ** For clean Pulse that can use Interrupt attachInterrupt(1, onPulse, FALLING); // Pulse pin uses IRQ1 } } timer.setInterval(SAMPLE_INTERVAL, send_node_data); // Configure the timer to send sensor data if(RF) { rf12_initialize(MYNODEID, RFFREQ, NETWORK); //Initialize RFM12 with defined settings Serial.print("#RF Freq: "); Serial.print(RFFREQ); Serial.print("#Network: "); Serial.println(NETWORK); } } void loop() { timer.run(); // ensure SimpleTimer tasks run if (RF && rf12_recvDone()) { // Handle RF forwarding - Uses BITWISE operators if (rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0) { // A valid general broadcast packet Serial.print("RFNODE,"); // Send record type RFNODE, NodeID and payload in CSV format Serial.print(rf12_hdr & 0x1f); // Print nodeID from header for (byte i = 0; i < rf12_len; i += 2) { // Print payload values in CSV format Serial.print(","); Serial.print((rf12_data[i+1] << 8) | (rf12_data[i] & 0xff)); } Serial.println(" "); } } }