ADC + MCP7428 Test
#include <Adafruit_MCP4728.h>
#include <Wire.h>
#include "driver/adc.h"
#include "esp_adc/adc_oneshot.h"
Adafruit_MCP4728 mcp;
// Constants
const int ANALOG_PIN = 18; // GPIO18, ADC2_CH7
const unsigned long LIGHT_CHANGE_INTERVAL = 10000; // 10ms in microseconds
const int LED_VALUE = 310; // LED brightness value
const unsigned long SAMPLE_INTERVAL = 25; // 25 microseconds
// Variables for timing
unsigned long lastSampleMicros = 0;
unsigned long lastLightMicros = 0;
uint8_t currentLight = 0;
volatile uint16_t analogBuffer[100]; // Buffer for analog readings
volatile uint8_t bufferIndex = 0;
volatile bool bufferFull = false;
// ADC handles
adc_oneshot_unit_handle_t adc2_handle;
adc_oneshot_unit_init_cfg_t init_config2;
adc_oneshot_chan_cfg_t config;
void setup(void) {
Serial.begin(2000000); // Increased baud rate for faster data transmission
// Initialize I2C and LED control
pinMode(6, OUTPUT);
digitalWrite(6, HIGH);
Wire.begin(3, 4);
Wire.setClock(800000); // Set I2C clock to 800kHz for faster communication
delay(100); // Give I2C time to stabilize
// Try to initialize MCP4728
if (!mcp.begin()) {
Serial.println("Failed to find MCP4728 chip");
while (1) {
delay(10);
}
}
// Configure ADC
init_config2.unit_id = ADC_UNIT_2; // Using ADC2
init_config2.ulp_mode = ADC_ULP_MODE_DISABLE;
init_config2.clk_src = ADC_RTC_CLK_SRC_DEFAULT;
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config2, &adc2_handle));
// Configure ADC channel
config.atten = ADC_ATTEN_DB_11;
config.bitwidth = ADC_BITWIDTH_12;
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc2_handle, ADC_CHANNEL_7, &config)); // ADC2_CH7 for GPIO18
// Initialize all channels to 0
mcp.setChannelValue(MCP4728_CHANNEL_A, 0);
mcp.setChannelValue(MCP4728_CHANNEL_B, 0);
mcp.setChannelValue(MCP4728_CHANNEL_C, 0);
mcp.setChannelValue(MCP4728_CHANNEL_D, 0);
// Print header for Serial Plotter
Serial.println("Light_Level Channel_A Channel_B Channel_C Channel_D");
}
void loop() {
unsigned long currentMicros = micros();
// Sample analog input every 50 microseconds
if (currentMicros - lastSampleMicros >= SAMPLE_INTERVAL) {
lastSampleMicros = currentMicros;
// Read ADC
int adc_value;
if (adc_oneshot_read(adc2_handle, ADC_CHANNEL_7, &adc_value) == ESP_OK) {
analogBuffer[bufferIndex] = adc_value;
// Print the values and LED states
Serial.println(analogBuffer[bufferIndex]);
// Serial.print(" ");
// Serial.print(currentLight == 0 ? LED_VALUE : 0);
// Serial.print(" ");
// Serial.print(currentLight == 1 ? LED_VALUE : 0);
// Serial.print(" ");
// Serial.print(currentLight == 2 ? LED_VALUE : 0);
// Serial.print(" ");
// Serial.println(currentLight == 3 ? LED_VALUE : 0);
bufferIndex = (bufferIndex + 1) % 100;
if (bufferIndex == 0) {
bufferFull = true;
}
}
}
// Change lights every 10ms (10,000 microseconds)
if (currentMicros - lastLightMicros >= LIGHT_CHANGE_INTERVAL) {
lastLightMicros = currentMicros;
// Turn off all LEDs
mcp.setChannelValue(MCP4728_CHANNEL_A, 0);
mcp.setChannelValue(MCP4728_CHANNEL_B, 0);
mcp.setChannelValue(MCP4728_CHANNEL_C, 0);
mcp.setChannelValue(MCP4728_CHANNEL_D, 0);
// If we're not in the "all off" state, turn on the current LED
if (currentLight < 4) {
switch(currentLight) {
case 0:
mcp.setChannelValue(MCP4728_CHANNEL_A, LED_VALUE);
break;
case 1:
mcp.setChannelValue(MCP4728_CHANNEL_B, LED_VALUE);
break;
case 2:
mcp.setChannelValue(MCP4728_CHANNEL_C, LED_VALUE);
break;
case 3:
mcp.setChannelValue(MCP4728_CHANNEL_D, LED_VALUE);
break;
}
}
// Increment light counter
currentLight = (currentLight + 1) % 5; // 5 states: 4 LEDs + all off
}
}
This code was actually written for the tinyHEG prototype, which uses the MCP4728 DAC to control 4 LEDs. The Analog 0 pin is connected to one photodetector, which was looking at channel C.
This worked great, and we got good data from the Analog pin. So I threw the program back in to Claude and simplified it to only use one channel:
#include <Adafruit_MCP4728.h>
#include <Wire.h>
#include "driver/adc.h"
#include "esp_adc/adc_oneshot.h"
Adafruit_MCP4728 mcp;
// Constants
const int ANALOG_PIN = 18; // GPIO18, ADC2_CH7
const int LED_VALUE = 400; // LED brightness value
const unsigned long TOGGLE_INTERVAL = 25; // 100 microseconds toggle interval
const unsigned long SAMPLE_DELAY = 10; // Wait 50 microseconds after toggling before sampling
const int BATCH_SIZE = 100; // Number of samples to collect before printing
// Variables for timing and measurements
unsigned long lastToggleMicros = 0;
unsigned long lastSampleTime = 0;
bool channelCState = false;
int highSample = 0;
int lowSample = 0;
// Batch processing variables
int differenceSum = 0;
unsigned long intervalSum = 0;
int sampleCount = 0;
// ADC handles
adc_oneshot_unit_handle_t adc2_handle;
adc_oneshot_unit_init_cfg_t init_config2;
adc_oneshot_chan_cfg_t config;
void setup(void) {
Serial.begin(2000000);
// Initialize I2C and LED control
pinMode(6, OUTPUT);
digitalWrite(6, HIGH);
Wire.begin(3, 4);
Wire.setClock(800000);
delay(100);
if (!mcp.begin()) {
Serial.println("Failed to find MCP4728 chip");
while (1) {
delay(10);
}
}
// Configure ADC
init_config2.unit_id = ADC_UNIT_2;
init_config2.ulp_mode = ADC_ULP_MODE_DISABLE;
init_config2.clk_src = ADC_RTC_CLK_SRC_DEFAULT;
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config2, &adc2_handle));
// Configure ADC channel
config.atten = ADC_ATTEN_DB_11;
config.bitwidth = ADC_BITWIDTH_12;
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc2_handle, ADC_CHANNEL_7, &config));
// Initialize all channels to 0
mcp.setChannelValue(MCP4728_CHANNEL_A, 0);
mcp.setChannelValue(MCP4728_CHANNEL_B, 0);
mcp.setChannelValue(MCP4728_CHANNEL_C, 0);
mcp.setChannelValue(MCP4728_CHANNEL_D, 0);
// Initialize timing variables
lastSampleTime = micros();
// Print header
Serial.println("Average_Difference,Average_Sample_Interval_us");
}
void loop() {
unsigned long currentMicros = micros();
if (currentMicros - lastToggleMicros >= TOGGLE_INTERVAL) {
lastToggleMicros = currentMicros;
// Calculate time since last sample
unsigned long sampleInterval = currentMicros - lastSampleTime;
lastSampleTime = currentMicros;
// Turn on Channel C
mcp.setChannelValue(MCP4728_CHANNEL_C, LED_VALUE);
delayMicroseconds(SAMPLE_DELAY); // Wait for signal to stabilize
// Take high sample
if (adc_oneshot_read(adc2_handle, ADC_CHANNEL_7, &highSample) != ESP_OK) {
highSample = 0; // In case of error
}
// Turn off Channel C
mcp.setChannelValue(MCP4728_CHANNEL_C, 0);
delayMicroseconds(SAMPLE_DELAY); // Wait for signal to stabilize
// Take low sample
if (adc_oneshot_read(adc2_handle, ADC_CHANNEL_7, &lowSample) != ESP_OK) {
lowSample = 0; // In case of error
}
// Calculate difference and add to sum
int difference = highSample - lowSample;
differenceSum += difference;
intervalSum += sampleInterval;
sampleCount++;
// If we've collected BATCH_SIZE samples, calculate and print averages
if (sampleCount >= BATCH_SIZE) {
float avgDifference = (float)differenceSum / BATCH_SIZE;
float avgInterval = (float)intervalSum / BATCH_SIZE;
// Print averages
Serial.print(avgDifference);
Serial.print(",");
Serial.println(avgInterval);
// Reset counters and sums
differenceSum = 0;
intervalSum = 0;
sampleCount = 0;
}
}
}
This worked as well, so then I switched to an MCP4725, which is a single channel DAC to control the LED:
#include <Adafruit_MCP4725.h>
#include <Wire.h>
#include "driver/adc.h"
#include "esp_adc/adc_oneshot.h"
Adafruit_MCP4725 dac;
// Constants
const int ANALOG_PIN = 18; // GPIO18, ADC2_CH7
const int LED_VALUE = 400; // LED brightness value
const unsigned long TOGGLE_INTERVAL = 25; // 100 microseconds toggle interval
const unsigned long SAMPLE_DELAY = 10; // Wait 50 microseconds after toggling before sampling
const int BATCH_SIZE = 100; // Number of samples to collect before printing
// Variables for timing and measurements
unsigned long lastToggleMicros = 0;
unsigned long lastSampleTime = 0;
bool ledState = false;
int highSample = 0;
int lowSample = 0;
// Batch processing variables
int differenceSum = 0;
unsigned long intervalSum = 0;
int sampleCount = 0;
// ADC handles
adc_oneshot_unit_handle_t adc2_handle;
adc_oneshot_unit_init_cfg_t init_config2;
adc_oneshot_chan_cfg_t config;
void setup(void) {
Serial.begin(2000000);
// Initialize I2C and LED control
pinMode(6, OUTPUT);
digitalWrite(6, HIGH);
Wire.begin(3, 4);
Wire.setClock(800000);
delay(100);
if (!dac.begin(0x62)) { // Default I2C address for MCP4725
Serial.println("Failed to find MCP4725 chip");
while (1) {
delay(10);
}
}
// Configure ADC
init_config2.unit_id = ADC_UNIT_2;
init_config2.ulp_mode = ADC_ULP_MODE_DISABLE;
init_config2.clk_src = ADC_RTC_CLK_SRC_DEFAULT;
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config2, &adc2_handle));
// Configure ADC channel
config.atten = ADC_ATTEN_DB_11;
config.bitwidth = ADC_BITWIDTH_12;
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc2_handle, ADC_CHANNEL_7, &config));
// Initialize DAC to 0
dac.setVoltage(0, false);
// Initialize timing variables
lastSampleTime = micros();
// Print header
Serial.println("Average_Difference,Average_Sample_Interval_us");
}
void loop() {
unsigned long currentMicros = micros();
if (currentMicros - lastToggleMicros >= TOGGLE_INTERVAL) {
lastToggleMicros = currentMicros;
// Calculate time since last sample
unsigned long sampleInterval = currentMicros - lastSampleTime;
lastSampleTime = currentMicros;
// Set DAC output high
dac.setVoltage(LED_VALUE, false);
delayMicroseconds(SAMPLE_DELAY); // Wait for signal to stabilize
// Take high sample
if (adc_oneshot_read(adc2_handle, ADC_CHANNEL_7, &highSample) != ESP_OK) {
highSample = 0; // In case of error
}
// Set DAC output low
dac.setVoltage(0, false);
delayMicroseconds(SAMPLE_DELAY); // Wait for signal to stabilize
// Take low sample
if (adc_oneshot_read(adc2_handle, ADC_CHANNEL_7, &lowSample) != ESP_OK) {
lowSample = 0; // In case of error
}
// Calculate difference and add to sum
int difference = highSample - lowSample;
differenceSum += difference;
intervalSum += sampleInterval;
sampleCount++;
// If we've collected BATCH_SIZE samples, calculate and print averages
if (sampleCount >= BATCH_SIZE) {
float avgDifference = (float)differenceSum / BATCH_SIZE;
float avgInterval = (float)intervalSum / BATCH_SIZE;
// Print averages
Serial.print(avgDifference);
Serial.print(",");
Serial.println(avgInterval);
// Reset counters and sums
differenceSum = 0;
intervalSum = 0;
sampleCount = 0;
}
}
}