Last active
February 29, 2016 02:10
Revisions
-
BreakPointer revised this gist
Feb 29, 2016 . 1 changed file with 0 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,7 +5,6 @@ #include "math.h" // Config Values #define AUDIO_FREQ 44 //44usec ~22,050hz #define SAMP_FREQ 260 // ~3.84kHz - 64 samples per 60hz cycle #define ADC_SAMPLES 256 // 4 full cycles captured (256 makes sample division a bit shift operation) -
BreakPointer revised this gist
Feb 29, 2016 . 1 changed file with 23 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -12,9 +12,10 @@ #define ON_THRESHOLD 1.0 // Device is on when current goes over 1 amp #define SAMP_TIMER TIMER5 #define AUD_TIMER TIMER6 #define MAX_LONG 2147483647 // Setting up IO Pins #define AUDIO_L DAC1 // No stereo sound (yet) but we use both DAC pins in the circuit #define AUDIO_R DAC2 #define AMP_ACTIVE D5 #define OB_LED D7 @@ -39,6 +40,8 @@ double irms = 0.0; double prevIrms = 0.0; bool active_on = false; String status = ""; unsigned long last_off_time = MAX_LONG; long time_delta; void setup() { audioamp.begin(); @@ -70,12 +73,24 @@ void loop() { if ((prevIrms < ON_THRESHOLD) && (irms > ON_THRESHOLD)){ active_on = true; } else { // else it was on and is still on (or off and still off) // Check if it was on and is now off if ((irms < ON_THRESHOLD) && active_on){ // Note: My tea kettle cycles the power on/off as it // gets closer to the desired temperature. From analysis // it seems to cycle on/off with about 5 seconds in between periods. // Storing that time when it shut off last_off_time = millis(); active_on = false; } // Checking to see if it was off more than 10 seconds ago // Note: If continually powered, and attached device is not used for ~24 days this will trigger continuously time_delta = (long) (millis() - last_off_time); if (time_delta >= 10000){ last_off_time = MAX_LONG; // setting to max long value so that we don't continually trigger the sound after the tea is done start_play(); Particle.publish("kettle_status", "ready"); } } @@ -171,6 +186,7 @@ void start_play(void) { pause_sampler(); // added to prevent audio slow down caused by analogRead() wave_ix = 0; digitalWrite(OB_LED, HIGH); toggleLedStatus(true); // visual indicator audioamp.enableChannel(true, true); audio_clock.begin(play_wave, AUDIO_FREQ, uSec, AUD_TIMER); } @@ -179,6 +195,7 @@ void stop_play(void) { audio_clock.end(); audioamp.enableChannel(false, false); digitalWrite(OB_LED, LOW); toggleLedStatus(false); wave_ix = 0; resume_sampler(); // added to prevent audio slow down caused by analogRead() } @@ -189,18 +206,17 @@ void reportUsage(String eventName, double irms){ Particle.publish(eventName, msg); } void toggleLedStatus(bool device_active){ if (device_active){ // On status_pixel.setBrightness(150); status_pixel.setPixelColor(0, status_pixel.Color(0,255,0)); } else { // Off status_pixel.setBrightness(0); status_pixel.setPixelColor(0, status_pixel.Color(0,0,0)); } status_pixel.show(); } -
BreakPointer revised this gist
Feb 26, 2016 . 1 changed file with 106 additions and 49 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,62 +1,67 @@ #include "neopixel/neopixel.h" #include "SparkIntervalTimer/SparkIntervalTimer.h" #include "Adafruit_TPA2016.h" #include "wave_data.h" #include "math.h" // Config Values #define ADC_BITS 12 // 12-bit ADC resolution for Particle Photon #define AUDIO_FREQ 44 //44usec ~22,050hz #define SAMP_FREQ 260 // ~3.84kHz - 64 samples per 60hz cycle #define ADC_SAMPLES 256 // 4 full cycles captured (256 makes sample division a bit shift operation) #define ON_THRESHOLD 1.0 // Device is on when current goes over 1 amp #define SAMP_TIMER TIMER5 #define AUD_TIMER TIMER6 // Setting up IO Pins #define AUDIO_L DAC1 #define AUDIO_R DAC2 #define AMP_ACTIVE D5 #define OB_LED D7 #define I_SENSE A0 #define PIXEL_PIN D3 // NEO PIXELS Set pixel COUNT, PIN and TYPE #define PIXEL_COUNT 1 #define PIXEL_TYPE WS2812 SYSTEM_MODE(AUTOMATIC); Adafruit_NeoPixel status_pixel = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE); Adafruit_TPA2016 audioamp = Adafruit_TPA2016(); IntervalTimer sample_clock; IntervalTimer audio_clock; volatile int sample_ix = 0; volatile int wave_ix = 0; volatile int sample_data[ADC_SAMPLES]; double irms = 0.0; double prevIrms = 0.0; bool active_on = false; String status = ""; void setup() { audioamp.begin(); status_pixel.begin(); pinMode(OB_LED, OUTPUT); pinMode(AMP_ACTIVE, OUTPUT); digitalWrite(AMP_ACTIVE, HIGH); pinMode(AUDIO_L, OUTPUT); pinMode(AUDIO_R, OUTPUT); for(int i = 0; i < 8; i++){ toggleLedStatus(true); delay(100); toggleLedStatus(false); delay(50); } // can't start samples before we flash the LED's // Not sure why but it causes the problems with the NeoPixels start_samples(); // boot sound start_play(); } void loop() { @@ -69,24 +74,54 @@ void loop() { // or check if it was on and is now off if ((irms < ON_THRESHOLD) && active_on){ active_on = false; start_play(); Particle.publish("kettle_status", "alert"); } } if (active_on){ status = "kettle_on"; } else { status = "kettle_off"; } reportUsage(status, irms); prevIrms = irms; delay(1000); } void start_samples(){ // initialize the data array for (int i=0; i < ADC_SAMPLES; i++){ sample_data[i] = 2048; // 2048 is the mid-point voltage value } resume_sampler(); } void stop_sample(){ pause_sampler(); sample_ix = 0; } void resume_sampler(){ sample_clock.begin(grab_sample, SAMP_FREQ, uSec, SAMP_TIMER); } void pause_sampler(){ sample_clock.end(); } void grab_sample() { // aquiring the analog reading slows down the timers for some reason. // To work around this issue we'll just pause sampling while audio is playing sample_data[sample_ix] = analogRead(I_SENSE); // reset the sample location if = to the sample count if (sample_ix < ADC_SAMPLES){ sample_ix ++; } else{ sample_ix = 0; } } // Will calculate the RMS with whatever @@ -123,8 +158,8 @@ double calcIrms() void play_wave(void) { if (wave_ix < frame_count) { int v = wave_data[wave_ix]; analogWrite(AUDIO_L, v); analogWrite(AUDIO_R, v); wave_ix++; } else { @@ -133,17 +168,39 @@ void play_wave(void) { } void start_play(void) { pause_sampler(); // added to prevent audio slow down caused by analogRead() wave_ix = 0; digitalWrite(OB_LED, HIGH); audioamp.enableChannel(true, true); audio_clock.begin(play_wave, AUDIO_FREQ, uSec, AUD_TIMER); } void stop_play(void) { audio_clock.end(); audioamp.enableChannel(false, false); digitalWrite(OB_LED, LOW); wave_ix = 0; resume_sampler(); // added to prevent audio slow down caused by analogRead() } void reportUsage(String eventName, double irms){ String msg = String::format("%.3f Amps", irms); Particle.publish(eventName, msg); } // will flash on/off every time it is called void toggleLedStatus(bool device_active){ if (device_active){ status_pixel.setBrightness(150); status_pixel.setPixelColor(0, status_pixel.Color(0,255,0)); } else { status_pixel.setBrightness(0); status_pixel.setPixelColor(0, status_pixel.Color(0,0,0)); } status_pixel.show(); } -
BreakPointer created this gist
Feb 18, 2016 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,149 @@ #include "SparkIntervalTimer/SparkIntervalTimer.h" #include "Adafruit_TPA2016.h" #include "wave_data.h" #include "math.h" #define ADC_BITS 12 // 12-bit ADC resolution for Particle Photon #define SAMP_FEQ 260 // ~3.84kHz - 64 samples per 60hz cycle #define ADC_SAMPLES 256 // 4 full cycles captured (256 makes sample division a bit shift operation) #define ON_THRESHOLD 1.0 // Device is on when current goes over 1 amp IntervalTimer sample_clock; IntervalTimer audio_clock; volatile int sample_ix = 0; volatile int wave_ix = 0; int sample_data[ADC_SAMPLES]; Adafruit_TPA2016 audioamp = Adafruit_TPA2016(); const int audioL = DAC1; const int audioR = DAC2; const int amp_active = D5; const int led = D7; const int iPin = A0; double irms = 0.0; double prevIrms = 0.0; bool active_on = false; String status = ""; void setup() { // initialize the data array for (int i=0; i < ADC_SAMPLES; i++){ sample_data[i] = 2048; } sample_clock.begin(grab_sample, SAMP_FEQ, uSec); pinMode(led, OUTPUT); pinMode(amp_active, OUTPUT); digitalWrite(amp_active, LOW); audioamp.begin(); pinMode(audioL, OUTPUT); pinMode(audioR, OUTPUT); } void grab_sample(void) { sample_data[sample_ix] = analogRead(iPin); // reset the sample location if = to the sample count if (sample_ix < ADC_SAMPLES){ sample_ix ++; } else{ sample_ix = 0; } } void stop_sample(void){ sample_clock.end(); sample_ix = 0; } void loop() { irms = calcIrms(); // if it was off and is now on if ((prevIrms < ON_THRESHOLD) && (irms > ON_THRESHOLD)){ active_on = true; } else { // it was on and is still on, or off and is stil off // or check if it was on and is now off if ((irms < ON_THRESHOLD) && active_on){ active_on = false; digitalWrite(amp_active, HIGH); start_play(); Particle.publish("kettle_status", "alert"); } } if (active_on){ status = "kettle_on"; } else { status = "kettle_off"; } reportUsage(status, irms); prevIrms = irms; delay(1000); } void reportUsage(String eventName, double irms){ String msg = String::format("%.3f Amps", irms); Particle.publish(eventName, msg); } // Will calculate the RMS with whatever // values are in the data sample buffer double calcIrms() { double Irms; double Vrms; uint32_t vSum = 0; uint32_t vAvg = 0; double vVal = 0; int vAdj = 0; for (int n = 0; n < ADC_SAMPLES; n++){ vAvg += sample_data[n]; } // vAvg is the mid-point of the voltage over the samples // subtracting the midpoint from the measured voltages // gives us the +/- voltages to use for the Vrms vAvg = vAvg >> 8; // int val offset. Divide by number of samples (256) for (int n = 0; n < ADC_SAMPLES; n++) { vAdj = sample_data[n] - vAvg; // int val adjusted measurment vSum += (vAdj * vAdj); // Squaring the value, adding it to the accumulator } vVal = vSum >> 8; // divide by number of samples (256); Vrms = sqrt(vVal) * 0.0008; // 12-bit ADC w/ 3.3 maxV gives 3.3V/4096 units Irms = Vrms * 20.15; return Irms; // Iprimary = Isecondary * CT ratio } void play_wave(void) { if (wave_ix < frame_count) { int v = wave_data[wave_ix]; analogWrite(audioL, v); analogWrite(audioR, v); wave_ix++; } else { stop_play(); } } void start_play(void) { audioamp.enableChannel(true, true); wave_ix = 0; digitalWrite(led, HIGH); audio_clock.begin(play_wave, 44, uSec); //44usec ~22,050hz } void stop_play(void) { analogWrite(audioL, 0); analogWrite(audioR, 0); audioamp.enableChannel(false, false); audio_clock.end(); digitalWrite(led, LOW); wave_ix = 0; }