Skip to content

Instantly share code, notes, and snippets.

@brian-lc
Last active February 29, 2016 02:10
Show Gist options
  • Save brian-lc/1eb9867eb261f669ca25 to your computer and use it in GitHub Desktop.
Save brian-lc/1eb9867eb261f669ca25 to your computer and use it in GitHub Desktop.
Prototype code for EMF sensing to tone based alert on Particle Photon
#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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment