Fastest Sampling Rate Possible?

Hi,

I am developing a basic seismic recorder system. The basic idea is:

The program waits for the trigger input to get above a threshold. Once the threshold is overcome the
while loop used takes samples and records time values up to a set time. The way the program is running I am getting a sample every millisecond. I would like to increase this greatly. As my programming skills are limited I was wondering could someone point out what is limiting my sampling speed or if there is any way I can improve it.
Any help would be greatly appreciated.

#define geo1 A0                       // Geophone 1
#define trig A7                       // trigger swithch
int long unsigned time =0.0;         // Time variable
int long unsigned timeinitial =0.0;   // Time variable
void setup() {
  Serial.begin(115200);              // Commence communication
  while(analogRead(trig)<50){         // Waits for trigger to activate
  }
  timeinitial=micros();               // Takes time trigger activated
}
void loop() {

  while (time< 0.01e6) {              // Waits for time to be > 0.01s
    time=(micros())-timeinitial;      // Calculates present time
    Serial.print(time/1e6,5);        
    Serial.print(',');
    Serial.println(analogRead(geo1));
  }
}

How many characters are you sending to the serial port every sample? Even at 115200 baud, that is about 11,000 characters per second.

Eleven characters = 1ms.

edit: Even if your drop the serial prints, the max is about 10,000 samples per second on a 16MHz Arduino.

you can get a higher sample rate if you go to 8 bits sampling.

you can use a higher baud rate for sending the data to a PC, but the PC terminal program must support it.
I’ve done 250.000 and 500.000 baud.

You do math in the loop, better avoid that and just send raw data which is less bytes. On the PC side you can unpack and improve it.

your time variable is of overtyped type, but definitely not a float so assigning 0.0 will not work

improving names of variables makes comments obsolete

so that would make something like

#define GEOPHONE_1 A0
#define TRIGGER    A7 
#define THRESHOLD  50

unsigned long time = 0;        
unsigned long timeinitial = 0; 

void setup() 
{
  Serial.begin(115200);

  // Waits for trigger to activate
  while(analogRead(TRIGGER) < THRESHOLD); 

  timeinitial = micros();
}

void loop() 
{
  int sample = analogRead(GEOPHONE_1);
  
  if (sample > THRESHOLD)
  {
    Serial.print(time - timeinitial);    
    Serial.print(',');
    Serial.println(sample);
  }
}

robtillaart:
you can get a higher sample rate if you go to 8 bits sampling.

you can use a higher baud rate for sending the data to a PC, but the PC terminal program must support it.
I’ve done 250.000 and 500.000 baud.

I am using the in built serial monitor which seems to have a highest baud setting of 115200.

robtillaart:
so that would make something like

#define GEOPHONE_1 A0

#define TRIGGER    A7
#define THRESHOLD  50

unsigned long time = 0;        
unsigned long timeinitial = 0;

void setup()
{
 Serial.begin(115200);

// Waits for trigger to activate
 while(analogRead(TRIGGER) < THRESHOLD);

timeinitial = micros();
}

void loop()
{
 int sample = analogRead(GEOPHONE_1);
 
 if (sample > THRESHOLD)
 {
   Serial.print(time - timeinitial);    
   Serial.print(’,’);
   Serial.println(sample);
 }
}

Yes this code is somewhat faster but I need to stop recording at a particular time. Any suggestions?
Thanks for your reply

here a modified analogRead that can sample faster (be aware more noise), the default value of 7 gives same speed as analogRead

use at own risk!

int analogReadP(uint8_t pin, uint8_t prescale=7)
{
    uint8_t ADCSRA_TMP = ADCSRA;
    if (prescale > 7) prescale = 7;
    ADCSRA = (ADCSRA | 0x07) & (0xF8 | prescale);

    int value = analogRead(pin);

    ADCSRA = ADCSRA_TMP;

    return value;
}

a test run with all 8 possible values, note there is loop overhead in the numbers, but it should give you some idea what can be done.

ADCTEST: default    : 112028 usec (1000 calls)
ADCTEST: prescale 0: 4832 usec (1000 calls)
ADCTEST: prescale 1: 4812 usec (1000 calls)
ADCTEST: prescale 2: 6392 usec (1000 calls)
ADCTEST: prescale 3: 9772 usec (1000 calls)
ADCTEST: prescale 4: 16184 usec (1000 calls)
ADCTEST: prescale 5: 30160 usec (1000 calls)
ADCTEST: prescale 6: 56144 usec (1000 calls)
ADCTEST: prescale 7: 112032 usec (1000 calls)

with a max 10 second sample time…

#define GEOPHONE_1 A0
#define TRIGGER    A7 
#define THRESHOLD  50
#define SAMPLETIME (10*1000*1000UL)

unsigned long time = 0;        
unsigned long timeinitial = 0; 

void setup() 
{
  Serial.begin(115200);

  // Waits for trigger to activate
  while(analogRead(TRIGGER) < THRESHOLD); 

  timeinitial = micros();
}

void loop() 
{
  int sample = analogRead(GEOPHONE_1);
  Serial.print(micros() - timeinitial);    
  Serial.print(',');
  Serial.println(sample);

  if (micros() - timeinitial >= SAMPLETIME) while(1); // stop after SAMPLETIME
}

robtillaart:
here a modified analogRead that can sample faster (be aware more noise), the default value of 7 gives same speed as analogRead

use at own risk!

int analogReadP(uint8_t pin, uint8_t prescale=7)

{
    uint8_t ADCSRA_TMP = ADCSRA;
    if (prescale > 7) prescale = 7;
    ADCSRA = (ADCSRA | 0x07) & (0xF8 | prescale);

int value = analogRead(pin);

ADCSRA = ADCSRA_TMP;

return value;
}




a test run with all 8 possible values, note there is loop overhead in the numbers, but it should give you some idea what can be done.


ADCTEST: default    : 112028 usec (1000 calls)
ADCTEST: prescale 0: 4832 usec (1000 calls)
ADCTEST: prescale 1: 4812 usec (1000 calls)
ADCTEST: prescale 2: 6392 usec (1000 calls)
ADCTEST: prescale 3: 9772 usec (1000 calls)
ADCTEST: prescale 4: 16184 usec (1000 calls)
ADCTEST: prescale 5: 30160 usec (1000 calls)
ADCTEST: prescale 6: 56144 usec (1000 calls)
ADCTEST: prescale 7: 112032 usec (1000 calls)

A quick test shows that prescale values 0 and 1 do not result in a meaningful value
Added a potentiometer to see value.

ADCTEST: prescale 0: 5328 usec (1000 calls)
1023
ADCTEST: prescale 1: 5328 usec (1000 calls)
1023
ADCTEST: prescale 2: 6916 usec (1000 calls)
128
ADCTEST: prescale 3: 10248 usec (1000 calls)
130
ADCTEST: prescale 4: 17072 usec (1000 calls)
130
ADCTEST: prescale 5: 30196 usec (1000 calls)
129
ADCTEST: prescale 6: 56136 usec (1000 calls)
129
ADCTEST: prescale 7: 112036 usec (1000 calls)
130

so the code becomes

int analogReadP(uint8_t pin, uint8_t prescale=7)
{
    uint8_t ADCSRA_TMP = ADCSRA;
    prescale = constrain(prescale, 2, 7);
    ADCSRA = (ADCSRA | 0x07) & (0xF8 | prescale);

    int value = analogRead(pin);

    ADCSRA = ADCSRA_TMP;

    return value;
}