float Vcal= 268.97; // (230V x 13) / (9V x 1.2) = 276.9 Calibration for UK AC-AC adapter 77DB-06-09 //float Vcal=276.9; //const float Vcal= 260; // Calibration for EU AC-AC adapter 77DE-06-09 const float Vcal_USA= 130.0; //Calibration for US AC-AC adapter 77DA-10-09 boolean USA=false; const float phase_shift= 1.7; const int no_of_samples= 1662; const int no_of_half_wavelengths= 30; const int timeout= 2000; //emonLib timeout const int ACAC_DETECTION_LEVEL= 3000; const byte min_pulsewidth= 110; // minimum width of interrupt pulse (default pulse output meters = 100ms) const int TEMPERATURE_PRECISION= 11; //9 (93.8ms),10 (187.5ms) ,11 (375ms) or 12 (750ms) bits equal to resplution of 0.5C, 0.25C, 0.125C and 0.0625C const byte MaxOnewire= 6; #define ASYNC_DELAY 375 // DS18B20 conversion delay - 9bit requres 95ms, 10bit 187ms, 11bit 375ms and 12bit resolution takes 750ms //------------------------------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------------------------------- //----------------------------emonTx V3 hard-wired connections--------------------------------------------------------------------------------------------------------------- const byte LEDpin= 6; // emonTx V3 LED const byte DS18B20_PWR= 19; // DS18B20 Power const byte DIP_switch1= 8; // RF node ID (default no chance in node ID, switch on for nodeID -1) switch off D9 is HIGH from internal pullup const byte DIP_switch2= 9; // Voltage selection 230 / 110 V AC (default switch off 230V) - switch off D8 is HIGH from internal pullup const byte battery_voltage_pin= 7; // Battery Voltage sample from 3 x AA const byte pulse_countINT= 1; // INT 1 / Dig 3 Terminal Block / RJ45 Pulse counting pin(emonTx V3.4) - (INT0 / Dig2 emonTx V3.2) const byte pulse_count_pin= 3; // INT 1 / Dig 3 Terminal Block / RJ45 Pulse counting pin(emonTx V3.4) - (INT0 / Dig2 emonTx V3.2) #define ONE_WIRE_BUS 5 // DS18B20 Data //------------------------------------------------------------------------------------------------------------------------------------------- //Setup DS128B20 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); byte allAddress [MaxOnewire][8]; // 8 bytes per address byte numSensors; //------------------------------------------------------------------------------------------------------------------------------------------- //-----------------------RFM12B / RFM69CW SETTINGS---------------------------------------------------------------------------------------------------- byte RF_freq=RF12_433MHZ; // Frequency of RF69CW module can be RF12_433MHZ, RF12_868MHZ or RF12_915MHZ. You should use the one matching the module you have. byte nodeID = 8; // emonTx RFM12B node ID int networkGroup = 210; boolean RF_STATUS = 1; // Enable RF // Note: Please update emonhub configuration guide on OEM wide packet structure change: // https://github.com/openenergymonitor/emonhub/blob/emon-pi/configuration.md typedef struct { int power1, power2, power3, power4, Vrms, temp[MaxOnewire]; unsigned long pulseCount; } PayloadTX; // create structure - a neat way of packaging data for RF comms PayloadTX emontx; //------------------------------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------------------------------- //Random Variables //boolean settled = false; boolean CT1, CT2, CT3, CT4, ACAC, DS18B20_STATUS; byte CT_count=0; volatile byte pulseCount = 0; unsigned long pulsetime=0; // Record time of interrupt pulse unsigned long start=0; const char helpText1[] PROGMEM = // Available Serial Commands "\n" "Available commands:\n" " i - set node IDs (standard node ids are 1..30)\n" " b - set MHz band (4 = 433, 8 = 868, 9 = 915)\n" " g - set network group (RFM12 only allows 212, 0 = any)\n" " s - save config to EEPROM\n" " v - Show firmware version\n" ; static void showString (PGM_P s); #ifndef UNIT_TEST // IMPORTANT LINE! //------------------------------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------------------------------- //SETUP //------------------------------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------------------------------- void setup() { pinMode(LEDpin, OUTPUT); pinMode(DS18B20_PWR, OUTPUT); pinMode(pulse_count_pin, INPUT_PULLUP); // Set emonTx V3.4 interrupt pulse counting pin as input (Dig 3 / INT1) emontx.pulseCount=0; // Make sure pulse count starts at zero digitalWrite(LEDpin,HIGH); //DIP SWITCHES pinMode(DIP_switch1, INPUT_PULLUP); pinMode(DIP_switch2, INPUT_PULLUP); wdt_enable(WDTO_8S); // Enable reset watchdog Serial.begin(115200); Serial.print("emonTx V3.4 Discrete Sampling V"); Serial.println(version*0.1); Serial.println("OpenEnergyMonitor.org"); Serial.println(" "); if (RF_STATUS==1){ load_config(); // Load RF config from EEPROM (if any exists) #if (RF69_COMPAT) Serial.print("RFM69CW"); #else Serial.print("RFM12B"); #endif if (digitalRead(DIP_switch1)==LOW) nodeID--; // IF DIP switch 1 is switched on then subtract 1 from nodeID Serial.print(" Node: "); Serial.print(nodeID); Serial.print(" Freq: "); if (RF_freq == RF12_433MHZ) Serial.print("433Mhz"); if (RF_freq == RF12_868MHZ) Serial.print("868Mhz"); if (RF_freq == RF12_915MHZ) Serial.print("915Mhz"); Serial.print(" Group: "); Serial.println(networkGroup); Serial.println(" "); } if (RF_STATUS==1){ rf12_initialize(nodeID, RF_freq, networkGroup); // initialize RFM12B/rfm69CW for (int i=10; i>=0; i--) // Send RF test sequence (for factory testing) { emontx.power1=i; rf12_sendNow(0, &emontx, sizeof emontx); delay(100); } rf12_sendWait(2); emontx.power1=0; } if (digitalRead(DIP_switch2)==LOW) USA=true; // IF DIP switch 2 is switched on then activate USA mode if (USA==true){ // if USA mode is true Vcal=Vcal_USA; // Assume USA AC/AC adatper is being used, set calibration accordingly Vrms = Vrms_USA; /// USE 110V for USA apparent power } Serial.println("POST.....wait 10s"); Serial.println("'+++' then [Enter] for RF config mode"); Serial.println("(Arduino IDE Serial Monitor: make sure 'Both NL & CR' is selected)"); for (int i=0; i<5; i++){ //delay 10s delay(1000); wdt_reset(); //this line must be called faster than 8s, otherwise ATmega watchfog will kick in a resret the unit in event of a crash } if (analogRead(1) > 0) {CT1 = 1; CT_count++;} else CT1=0; // check to see if CT is connected to CT1 input, if so enable that channel if (analogRead(2) > 0) {CT2 = 1; CT_count++;} else CT2=0; // check to see if CT is connected to CT2 input, if so enable that channel if (analogRead(3) > 0) {CT3 = 1; CT_count++;} else CT3=0; // check to see if CT is connected to CT3 input, if so enable that channel if (analogRead(4) > 0) {CT4 = 1; CT_count++;} else CT4=0; // check to see if CT is connected to CT4 input, if so enable that channel if ( CT_count == 0) CT1=1; // If no CT's are connect ed CT1-4 then by default read from CT1 wdt_reset(); //this line must be called faster than 8s, otherwise ATmega watchfog will kick in a resret the unit in event of a crash // Quick check to see if there is a voltage waveform present on the AC-AC Voltage input // Check consists of calculating the RMS value from 100 samples of the voltage input. start = millis(); while (millis() < (start + 7000)){ // If serial input of keyword string '+++' is entered during 10s POST then enter config mode if (Serial.available()){ if ( Serial.readString() == "+++\r\n"){ Serial.println("Entering config mode..."); showString(helpText1); // char c[]="v" config(char('v')); while(1){ wdt_reset(); if (Serial.available()){ config(Serial.read()); } } } } } digitalWrite(LEDpin,LOW); // Calculate if there is an AC-AC adapter on analog input 0 double vrms = calc_rms(0,1780) * (Vcal * (3.3/1024) ); if (vrms>90) ACAC = 1; else ACAC=0; wdt_reset(); //this line must be called faster than 8s, otherwise ATmega watchfog will kick in a resret the unit in event of a crash if (ACAC) { for (int i=0; i<10; i++) // indicate AC has been detected by flashing LED 10 times { digitalWrite(LEDpin, HIGH); delay(200); digitalWrite(LEDpin, LOW); delay(300); } } else { delay(1000); digitalWrite(LEDpin, HIGH); delay(2000); digitalWrite(LEDpin, LOW); // indicate DC power has been detected by turing LED on then off } //################################################################################################################################ //Setup and test for presence of DS18B20 //################################################################################################################################ digitalWrite(DS18B20_PWR, HIGH); delay(100); sensors.begin(); sensors.setWaitForConversion(false); // disable automatic temperature conversion to reduce time spent awake, conversion will be implemented manually in sleeping // http://harizanov.com/2013/07/optimizing-ds18b20-code-for-low-power-applications/ numSensors=(sensors.getDeviceCount()); if (numSensors > MaxOnewire) numSensors=MaxOnewire; //Limit number of sensors to max number of sensors byte j=0; // search for one wire devices and // copy to device address arrays. while ((j < numSensors) && (oneWire.search(allAddress[j]))) j++; delay(500); digitalWrite(DS18B20_PWR, LOW); wdt_reset(); //this line must be called faster than 8s, otherwise ATmega watchfog will kick in a resret the unit in event of a crash if (numSensors==0) DS18B20_STATUS=0; else DS18B20_STATUS=1; //################################################################################################################################ if (DEBUG==1) { Serial.print("CT 1 Cal "); Serial.println(Ical1); Serial.print("CT 2 Cal "); Serial.println(Ical2); Serial.print("CT 3 Cal "); Serial.println(Ical3); Serial.print("CT 4 Cal "); Serial.println(Ical4); delay(1000); Serial.print("RMS Voltage on AC-AC is: ~"); Serial.print(vrms,0); Serial.println("V"); if (ACAC) { Serial.println("AC-AC detected - Real Power measure enabled"); Serial.println("assuming pwr from AC-AC (jumper closed)"); if (USA==true) Serial.println("USA mode active"); Serial.print("Vcal: "); Serial.println(Vcal); Serial.print("Phase Shift: "); Serial.println(phase_shift); } else { Serial.println("AC-AC NOT detected - Apparent Pwr measure enabled"); Serial.print("Assuming VRMS: "); Serial.print(Vrms); Serial.println("V"); Serial.println("Assuming power from batt / 5V USB - power save enabled"); } if (CT_count==0) { Serial.println("NO CT's detected"); } else { if (CT1) Serial.println("CT 1 detected"); if (CT2) Serial.println("CT 2 detected"); if (CT3) Serial.println("CT 3 detected"); if (CT4) Serial.println("CT 4 detected"); } if (DS18B20_STATUS==1) { Serial.print("Detected Temp Sensors: "); Serial.println(numSensors); } else { Serial.println("No temperature sensor"); } Serial.print("CT1 CT2 CT3 CT4 VRMS/BATT PULSE"); if (DS18B20_STATUS==1){Serial.print(" Temperature 1-"); Serial.print(numSensors);} Serial.println(" "); delay(500); } else { Serial.end(); } ct1.current(1, Ical1); // CT ADC channel 1, calibration. calibration (2000 turns / 22 Ohm burden resistor = 90.909) ct2.current(2, Ical2); // CT ADC channel 2, calibration. ct3.current(3, Ical3); // CT ADC channel 3, calibration. ct4.current(4, Ical4); // CT ADC channel 4, calibration. calibration (2000 turns / 120 Ohm burden resistor = 16.66) high accuracy @ low power - 4.5kW Max @ 240V if (ACAC) { ct1.voltage(0, Vcal, phase_shift); // ADC pin, Calibration, phase_shift ct2.voltage(0, Vcal, phase_shift); // ADC pin, Calibration, phase_shift ct3.voltage(0, Vcal, phase_shift); // ADC pin, Calibration, phase_shift ct4.voltage(0, Vcal, phase_shift); // ADC pin, Calibration, phase_shift } attachInterrupt(pulse_countINT, onPulse, FALLING); // Attach pulse counting interrupt pulse counting for(byte j=0;j 500) { time_to_loose = sleeptime-500; } // lose an additional 500ms here (measured timing) Sleepy::loseSomeTime(time_to_loose); // sleep or delay in milliseconds } } // end loop //------------------------------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------------------------------------------- // SEND RF //------------------------------------------------------------------------------------------------------------------------------------------- void send_rf_data() { rf12_sleep(RF12_WAKEUP); rf12_sendNow(0, &emontx, sizeof emontx); //send temperature data via RFM12B using new rf12_sendNow wrapper rf12_sendWait(2); if (!ACAC) rf12_sleep(RF12_SLEEP); //if powered by battery then put the RF module to sleep between readings } double calc_rms(int pin, int samples) { unsigned long sum = 0; for (int i=0; i min_pulsewidth) { pulseCount++; //calculate Wh elapsed from time between pulses } pulsetime=millis(); } int get_temperature(byte sensor) { float temp=(sensors.getTempC(allAddress[sensor])); if ((temp<125.0) && (temp>-55.0)) return(temp*10); //if reading is within range for the sensor convert float to int ready to send via RF } //------------------------------------------------------------------------------------------------------------------------------------------- #endif // IMPORTANT LINE!