/* Frequency Controlled Switch * by Simon Sykes * * This sketch measures the frequency of the mains voltage which is then used to control a number of electrical loads. * - IO is handled using pfodApp * - Mains frequency measurement is by counting voltage cycles using an interupt * - Electrical load control uses Watts Clever plugs */ // IO is implemented using pfod over a bluetooth serial connection. // Using Serial1 and 9600 for send and receive // Serial1 D0 (RX) and D1 (TX) on Arduino Leonardo. Note Serial may need to be used on other Arduino boards // This code uses Serial1 so there is no need to remove shield when programming the board // Mains frequency measurement // Using Pin 7 [pinMainsFrequency] for mains input square wave. Counts pulses using an interupt function. 25 sec of pulses [FrequencyAveragePeriod] collected in 1 sec intervals. // Average frequency over the period is calculated. Old count data is overwritten after 25 sec. // Watts Clever plugs controlled by a 433MHz transmitter using codes recorded from remote control supplied with plugs. #include // EEPROM is used to store the setup data for each controlled device between power ups. #include // for parsing the pfodApp messages #include // this is the library for the transmitter used to control the switches. Makes this aspect very easy. ..... // Frequency measurement control constants const int pinMainsFrequency = 7; //Pin monitored for mains frequency cycles. const int NominalFrequency = 5000; //Used to initialise frequency counter. == 100 x frequency const int FrequencyAveragePeriod = 25; //Period in seconds the mains frequency is averaged over. const int InterruptsPerCycle = 2; //Number of interrupts per mains cycle. // Note measurement accuracy is a product of FrequencyAveragePeriod and InterruptsPerCycle. // Mains frequency measurement global variables. volatile long MainsFrequency = NominalFrequency; // Note to avoid floating numbers and floating number arithmitic, an integer variable is used to store frequency x 100. volatile int MainsCycleCount[FrequencyAveragePeriod + 1]; // Used to count cycles for last [FrequencyAveragePeriod] seconds and drop off old counts as new ones are made. volatile long CumulativeCount = 0; // Records cumulative cycles above or below NominalFrequency. volatile int CurrentCycleCountPeriod = 0; volatile boolean NotCounting; // Used to check if CountCycle interupt service routine has been called. boolean AutoResetCumulativeCount = false; unsigned long LastSecOutsideNominalFrequency = 0; volatile unsigned long thissec = 0; volatile unsigned long lastsec = 0; ..... // this called once void setup() { .... // Setup mains frequency measurement using Interupt Service Routine. initialiseFrequencyCounters(); pinMode(pinMainsFrequency,INPUT); //Remove PULLUP when connected to the mains. attachInterrupt(digitalPinToInterrupt(pinMainsFrequency),countCycle,CHANGE); // Counts on up and down transition of input signal (InterruptsPerCycle = 2). pinMode(13, OUTPUT); // Used to indicate mains frequency is within 0.05Hz of Nominal Frequency. ... } void loop() { byte cmd = parser.parse(); // Pass it to the parser. // Parser returns non-zero when a pfod command is fully parsed. if (cmd != 0) { // Have parsed a complete msg { to }. // handle pfodApp cmd ........... } // other loop code here ............ } void initialiseFrequencyCounters() { int initCount = (NominalFrequency/100)*InterruptsPerCycle; for (int i=0; i<=FrequencyAveragePeriod; i++) { MainsCycleCount[i] = initCount; } } // this is the frequency count interrupt routine // keep this short!! // void countCycle() { //Interupt Service Routine to count mains cycles. NotCounting = false; thissec = millis()/1000; if (thissec != lastsec) { // Skip the first two seconds as they are always short. if (lastsec >= 2 ) { //Record cumulative time. CumulativeCount += MainsCycleCount[CurrentCycleCountPeriod] - ((NominalFrequency/100)*InterruptsPerCycle); } else { // Clean up first two seconds. MainsCycleCount[CurrentCycleCountPeriod] = (NominalFrequency/100)*InterruptsPerCycle; } lastsec = thissec; incrementCurrentCycleCountPeriod(); calculateMainsFrequency(); } MainsCycleCount[CurrentCycleCountPeriod]++; } void incrementCurrentCycleCountPeriod() { CurrentCycleCountPeriod++; if (CurrentCycleCountPeriod == FrequencyAveragePeriod +1) { // wrap around to beginning of array of counts CurrentCycleCountPeriod = 0; } MainsCycleCount[CurrentCycleCountPeriod] = 0; } void calculateMainsFrequency() { MainsFrequency = 0; for (int i=0; i<=FrequencyAveragePeriod; i++) { if (i != CurrentCycleCountPeriod) { MainsFrequency += MainsCycleCount[i]; } } MainsFrequency = ((MainsFrequency * 100) / InterruptsPerCycle) / FrequencyAveragePeriod; }