I am running out of memory with the below code. Why is that? I have had much larger programs without even getting close to running out of memory.
Secondly, if I need a board with a larger memory and a linear 5V ADC, which should I go for? I plan to add a lot more code to this, planning to install a LCD to it too.
#include <Arduino.h>
int readPin1 = A0;
int readingA0;
float V1 = 0;
// CD4066 switch control pins
int switchPin1 = 2;
int switchPin2 = 3;
int switchPin3 = 4;
int switchPin4 = 5;
// Switching frequency variables
unsigned long switchInterval = 3; // Switching interval in microseconds for 300 kHz
unsigned long lastSwitchMillis = 0;
bool switchState = true; // Initial state
#define NUM_READINGS 600 // Change this to 1000
int readings[NUM_READINGS];
int currentReadingIndex = 0;
float movingAverage = 0;
void setup()
{
pinMode(readPin1, INPUT);
// Set CD4066 switch control pins as OUTPUT
pinMode(switchPin1, OUTPUT);
pinMode(switchPin2, OUTPUT);
pinMode(switchPin3, OUTPUT);
pinMode(switchPin4, OUTPUT);
Serial.begin(9600);
// Initialize movingAverage to the initial value of readingA0
for (int i = 0; i < NUM_READINGS; ++i)
{
readings[i] = analogRead(readPin1);
movingAverage += readings[i];
}
movingAverage /= NUM_READINGS;
}
void loop()
{
readingA0 = analogRead(readPin1);
// Switch electrodes at a rate of 300 kHz
if ((micros() - lastSwitchMillis) >= switchInterval)
{
lastSwitchMillis = micros();
switchState = !switchState;
if (switchState)
{
// Connect the first electrode to 5V and the second electrode to A0
digitalWrite(switchPin1, HIGH);
digitalWrite(switchPin2, LOW);
digitalWrite(switchPin3, LOW);
digitalWrite(switchPin4, HIGH);
}
else
{
// Connect the first electrode to A0 and the second electrode to 5V
digitalWrite(switchPin1, LOW);
digitalWrite(switchPin2, HIGH);
digitalWrite(switchPin3, HIGH);
digitalWrite(switchPin4, LOW);
}
}
// Update the moving average
movingAverage += (readingA0 - readings[currentReadingIndex]) / static_cast<float>(NUM_READINGS);
// Store the new reading
readings[currentReadingIndex] = readingA0;
// Increment the current reading index
currentReadingIndex = (currentReadingIndex + 1) % NUM_READINGS;
// Print the average every 1000 readings
if (currentReadingIndex == 0)
{
// Serial.print("Moving average: ");
Serial.println(movingAverage);
}
}
I am actually not even happy with 600. I'd like to average a thousand values. FIrstly because my analog serial print is still not a straight line, secondly for fun sake.
The ESP32 ADC has to be calibrated like any other ADC. However it's input range is restricted to 150mV ~ 3100mV (does not go to zero) but is supposedly "linear" in that range.
What do you mean by calibration? I've never calibrated arduino adc. Also since the CD4066 switches at logic 5V i think I'd need a 5V board, else I'd have to add up more hardware.
Why keep 600 or 1000 readings - what good does it do you? If all you really need is the average value, look into 'leaky integrator' postings on this forum.
readingA0 = analogRead(readPin1);
Highly recommend you put that in a loop, all by itself, and calculate how quickly it can run. I think you'll find you're not going to reach 300 kHz. You might get beyond 1kHz, depending on how you approach the rest of your code.
I wrote the below code to find out what was the actual frequency. Its around 5.5KHz only. I need to be at "at least 10KHz".
#include <Arduino.h>
int readPin1 = A0;
int readingA0;
float V1 = 0;
// CD4066 switch control pins
int switchPin1 = 2;
int switchPin2 = 3;
int switchPin3 = 4;
int switchPin4 = 5;
// Switching frequency variables
unsigned long switchInterval = 3; // Switching interval in microseconds for 300 kHz
unsigned long lastSwitchMillis = 0;
bool switchState = true; // Initial state
#define NUM_READINGS 600 // Change this to 1000
int readings[NUM_READINGS];
int currentReadingIndex = 0;
float movingAverage = 0;
unsigned long analogReadCounter = 0; // Counter for analogRead operations
unsigned long startTime = 0; // Time at the start of the measurement interval
void setup()
{
pinMode(readPin1, INPUT);
// Set CD4066 switch control pins as OUTPUT
pinMode(switchPin1, OUTPUT);
pinMode(switchPin2, OUTPUT);
pinMode(switchPin3, OUTPUT);
pinMode(switchPin4, OUTPUT);
Serial.begin(115200);
// Initialize movingAverage to the initial value of readingA0
for (int i = 0; i < NUM_READINGS; ++i)
{
readings[i] = analogRead(readPin1);
movingAverage += readings[i];
}
movingAverage /= NUM_READINGS;
}
void loop()
{
// Record the start time of the measurement interval
if (analogReadCounter == 0)
{
startTime = micros();
}
readingA0 = analogRead(readPin1);
// Increment the counter for analogRead operations
analogReadCounter++;
// Switch electrodes at a rate of 300 kHz
if ((micros() - lastSwitchMillis) >= switchInterval)
{
lastSwitchMillis = micros();
switchState = !switchState;
if (switchState)
{
// Connect the first electrode to 5V and the second electrode to A0
digitalWrite(switchPin1, HIGH);
digitalWrite(switchPin2, LOW);
digitalWrite(switchPin3, LOW);
digitalWrite(switchPin4, HIGH);
}
else
{
// Connect the first electrode to A0 and the second electrode to 5V
digitalWrite(switchPin1, LOW);
digitalWrite(switchPin2, HIGH);
digitalWrite(switchPin3, HIGH);
digitalWrite(switchPin4, LOW);
}
}
// Update the moving average
movingAverage += (readingA0 - readings[currentReadingIndex]) / static_cast<float>(NUM_READINGS);
// Store the new reading
readings[currentReadingIndex] = readingA0;
// Increment the current reading index
currentReadingIndex = (currentReadingIndex + 1) % NUM_READINGS;
// Print the average every 1000 readings
if (currentReadingIndex == 0)
{
Serial.print("Moving average: ");
Serial.println(movingAverage);
// Calculate and print the actual frequency
unsigned long elapsedTime = micros() - startTime;
float actualFrequency = static_cast<float>(analogReadCounter) / (elapsedTime / 1e6); // Convert to Hz
Serial.print("Actual Frequency: ");
Serial.print(actualFrequency);
Serial.println(" Hz");
// Reset counters for the next measurement interval
analogReadCounter = 0;
}
}
An ADC measures a voltage relative to a reference voltage. Most people use the Vcc (5V) for the reference voltage on the UNO and Nano. The 5V has a nominal accuracy of +/- 1% and will vary a little depending on the load and digital noise. So the accuracy of your ADC reading will never be any bettor than +/- 1%. That's usually acceptable for most people and you really can't calibrate out that error.
If you need more accuracy, you should use an external reference. The ADC has a reference input (AREF) where you can supply a more accurate and cleaner reference to the ADC or you can use the internal reference but that does require calibration.