So, I think this is a programming question (possibly a math question?), but I am not sure where the problem is.
Basically, I want to create an idealized waveform in code, and compare it to a real waveform read from an ADC. If the measured waveform deviates too far from the idealized waveform, I can trigger an event.
I am reading the waveform using an ADS1115. The waveform is sinusoidal measuring from 0 to 5 volts. I am also using an h11aa1 opto-isolator to generate a pulse during the trough of the waveform which triggers an interrupt on my ESP32. Here is what those signals look like on my scope. Edit: Probe 1 (yellow) is the waveform that's fed into my ADC, and probe 2 (blue) is the pulse generated by the opto-isolator.
As the title mentions, I can't get the measured waveform and the idealized waveform to sync up in the serial plotter. They drift in and out of sync with each other as shown below. Edit: The blue line is the measured wave form, the orange line, is my idealized wave form.
I didn't expect the two waves to perfectly match up (especially without adjustment), but I wasn't expecting them to drift. The frequency of the measured waveform is tied to the grid, so it is pretty stable. I was expecting the idealized value to be consistently out of phase with the measured waveform, because each interrupt should reset/resync the waves.
Does anyone have any idea what I am missing or doing wrong?
Here is the sketch:
#include <ADS1X15.h>
#define WAVEFORM_GAIN 0.0129F
#define WAVEFORM_OFFSET 2.2352D
#define PERIOD (1000000 / (double)60) // Period in microseconds = 1 microsecond over 60 hz
#define SIN_PERIOD (1 / (PERIOD / TWO_PI)) // The period multiplier
#define PEAK_VOLTAGE 173.0D
#define ZERO_CROSS_INTERRUPT_PIN 18
volatile unsigned long _lastZeroCross = 0;
void IRAM_ATTR zeroCrossHandler() {
// This doesn't detect the zerocross, but instead detects the trough of the wave
unsigned long cross = (micros() - 1666.6667); // The detection is 500us long centered on the trough. Therefore, the last zero happened 1/4 of a period minus 2,500us
unsigned long diff = (_lastZeroCross > cross) ? _lastZeroCross - cross : cross - _lastZeroCross;
// Each pulse should be very nearly 16,666us apart.
if (_lastZeroCross == 0 || (diff < 16700 && diff > 16600)) {
_lastZeroCross = cross;
}
}
ADS1115 ADS(0x48);
void setupADC() {
Serial.println("Setting up ADC ... ");
Wire.begin();
Wire.setClock(400000);
ADS.begin();
ADS.setGain(0);
ADS.setDataRate(7);
ADS.setMode(1);
ADS.readADC(0); // Forces settings to be applied.
Serial.println("Complete! Gain: " + String(ADS.getGain()) + " | Sample Rate: " + String(ADS.getDataRate()));
}
void setup() {
Serial.begin(115200);
Serial.println("");
setupADC();
pinMode(ZERO_CROSS_INTERRUPT_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(ZERO_CROSS_INTERRUPT_PIN), zeroCrossHandler, RISING);
uint32_t freq = getXtalFrequencyMhz();
Serial.println("");
Serial.println("Setup Complete! | CPU: " + String(freq) + "Mhz\n");
}
void loop() {
double value = 0;
float adc0Voltage = 0;
// Request value of waveform
ADS.requestADC(0);
while (ADS.isBusy()) {
// block until value is ready
}
adc0Voltage = ADS.toVoltage(ADS.getValue());
if (adc0Voltage > 0.05) {
value = (adc0Voltage - WAVEFORM_OFFSET) / WAVEFORM_GAIN;
}
unsigned long now = micros();
double elapsedPeriodTime = (now > _lastZeroCross ? now - _lastZeroCross : _lastZeroCross - now);
double idealizedValue = (PEAK_VOLTAGE * sin(elapsedPeriodTime * (double)SIN_PERIOD));
Serial.println("variable_1:" + String(value) + ",variable_2:" + String(idealizedValue));
// This delay allows the time between measurements to be consistent.
// Without it, the plotter becomes really hard to read
delayMicroseconds(3000);
}



