Running only one concurrent instance of a function

I’ve got an Arduino Micro connected to 3 sensors and a color LCD display.

The sensors are:

  • ADXL345 Digital Accelerometer
  • Analog temperature sensor
  • Microphone reading volume as voltage

The display shows the reported values from one sensor - starting with the accelerometer. When the mic hears sound above a prescribed level, the LCD changes to display the next sensor - analog temperature.

This cycle repeats and again when the mic hears sound above a prescribed level, the display writes microphone voltage values.

THE PROBLEM: My “mode counter” is incrementing too quickly. It will count through a value before the LCD has a chance to display the sensor values for that case.

In my code, I’d point you towards the getVolumeVoltage() function as the potential offender. Immediately below is the Serial.print showing the ++ happening too quickly.

Gonna check the volume voltage…
Mode is: 0
I know my volts!: 0.15
Gonna check the volume voltage…
Mode is: 0
I know my volts!: 3.30
voltsV > switchVolts:
Mode will be increased by 1Gonna check the volume voltage…
Mode is: 1
I know my volts!: 1.35
voltsV > switchVolts:
Mode will be increased by 1Gonna check the volume voltage…
Mode is: 2
I know my volts!: 0.17
Gonna check the volume voltage…
Mode is: 2

// For switching between sensors
unsigned int mode = 0;

// For the Microphone
const int sampleWindow = 50; // Sample window width in mS (50 mS = 20Hz)
const int sampleWindowV = 50; // Sample window width in mS (50 mS = 20Hz)
unsigned int sample;
unsigned int sampleV;

boolean running = false;

/* Assign a unique ID to this sensor at the same time */
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);

#if defined(__SAM3X8E__)
    #undef __FlashStringHelper::F(string_literal)
    #define F(string_literal) string_literal
#endif

// Option 1: use any pins but a little slower
Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, mosi, sclk, rst);

// Option 2: must use the hardware SPI pins
// (for UNO thats sclk = 13 and sid = 11) and pin 10 must be
// an output. This is much faster - also required if you want
// to use the microSD card (see the image drawing example)
//Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, rst);
float p = 3.1415926;



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

  // If your TFT's plastic wrap has a Green Tab, use the following:
  tft.initR(INITR_GREENTAB); // initialize a ST7735R chip, green tab

  //Serial.println("init");

  tft.fillScreen(ST7735_BLACK);
  tft.setRotation(1); // comment-in and use rotateText() t derive desired orientation
  delay(1000);
  
  // SENSOR TEST - REVISED FOR TFT
  tft.println("Accelerometer Test"); tft.println("");
  
  /* Initialise the sensor */
  if(!accel.begin())
  {
    /* There was a problem detecting the ADXL345 ... check your connections */
    tft.println("Ooops, no ADXL345 detected ... Check your wiring!");
    while(1);
  }

  /* Set the range to whatever is appropriate for your project */
  accel.setRange(ADXL345_RANGE_16_G);
  // displaySetRange(ADXL345_RANGE_8_G);
  // displaySetRange(ADXL345_RANGE_4_G);
  // displaySetRange(ADXL345_RANGE_2_G);
  
}

void loop() {

  Serial.print("In the loop and Mode is: ");  
  Serial.println(mode);
  switch (mode) {
    case 0:
      printAccereometerValues();
      break;
    case 1:
      printTemperatureValues();
      break;
    case 2:
       printMicVoltageValues();
      break;      
  }    

}

void clearTheScreen() {
  tft.setCursor(0, 0);
  tft.fillScreen(ST7735_BLACK);
  tft.setTextColor(ST7735_WHITE);
  tft.setTextSize(0); 
}

void printAccelereometerValues() {
  clearTheScreen();
  int maxScreenLineCount = 16;
  
  for (int lineCounter = 0; lineCounter < maxScreenLineCount; lineCounter++) { 
    // turn the pin on:
    /* Get a new sensor event */ 
    sensors_event_t event; 
    accel.getEvent(&event);
    
    /* Display the results (acceleration is measured in m/s^2) */
    printMode();
    tft.setTextColor(ST7735_BLUE);
    tft.print("X:"); tft.print(event.acceleration.x); tft.print(" ");
    tft.setTextColor(ST7735_GREEN);
    tft.print("Y:"); tft.print(event.acceleration.y); tft.print(" ");
    tft.setTextColor(ST7735_WHITE);
    tft.print("Z:"); tft.print(event.acceleration.z); tft.print("");tft.println("ms2 ");
    getVolumeVoltage();
    //delay(100);
  }
}

void printTemperatureValues() {
  clearTheScreen();
  int maxScreenLineCount = 16;
  
  for (int lineCounter = 0; lineCounter < maxScreenLineCount; lineCounter++) { 
    //getting the voltage reading from the temperature sensor
     int reading = analogRead(sensorPin);  
     
     // converting that reading to voltage, for 3.3v arduino use 3.3
     float voltage = reading * 5.0;
     voltage /= 1024.0; 
     
     // print out the voltage
     //tft.print(voltage); tft.println(" volts");
     
     // now print out the temperature
     float temperatureC = (voltage - 0.5) * 100 ;  //converting from 10 mv per degree wit 500 mV offset
                                                   //to degrees ((voltage - 500mV) times 100)
     //tft.print(temperatureC); tft.println(" degrees C");
     
     printMode();
     // now convert to Fahrenheit
     float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
     tft.print(temperatureF); tft.println(" degrees F");
     //delay(100);
     getVolumeVoltage();
  }
}

void printMicVoltageValues() {
  clearTheScreen();
  int maxScreenLineCount = 16;
  
  for (int lineCounter = 0; lineCounter < maxScreenLineCount; lineCounter++) { 
     unsigned long startMillis= millis();  // Start of sample window
     unsigned int peakToPeak = 0;   // peak-to-peak level
  
     unsigned int signalMax = 0;
     unsigned int signalMin = 1024;
  
     // collect data for 50 mS
     while (millis() - startMillis < sampleWindow)
     {
        sample = analogRead(1);
        if (sample < 1024)  // toss out spurious readings
        {
           if (sample > signalMax)
           {
              signalMax = sample;  // save just the max levels
           }
           else if (sample < signalMin)
           {
              signalMin = sample;  // save just the min levels
           }
        }
     }
     peakToPeak = signalMax - signalMin;  // max - min = peak-peak amplitude
     double volts = (peakToPeak * 3.3) / 1024;  // convert to volts
     
     printMode();
     tft.print("Volume volate is: ");
     tft.println(volts);
     getVolumeVoltage();
  }  
}

void displayRange(void)
{
  tft.print  ("Range:         +/- "); 
  
  switch(accel.getRange())
  {
    case ADXL345_RANGE_16_G:
      tft.print  ("16 "); 
      break;
    case ADXL345_RANGE_8_G:
      tft.print  ("8 "); 
      break;
    case ADXL345_RANGE_4_G:
      tft.print  ("4 "); 
      break;
    case ADXL345_RANGE_2_G:
      tft.print  ("2 "); 
      break;
    default:
      tft.print  ("?? "); 
      break;
  }  
  tft.println(" g");  
}


// Used to identify orientation values that can be assigned.
void rotateText() {
  for (uint8_t i=0; i<4; i++) {
    //tft.fillScreen(ST7735_BLACK);
    //Serial.println(tft.getRotation(), DEC);

    tft.setCursor(0, 30);
    tft.setTextColor(ST7735_RED);
    tft.setTextSize(1);
    tft.print("Rotation value is: ");
    tft.println(tft.getRotation()+1);
  
    tft.setRotation(tft.getRotation()+1);
  }
}

void getVolumeVoltage() {
  if ( running == false ) {
    running = true;
    Serial.println("Gonna check the volume voltage..");  
    Serial.print("Mode is: ");  
    Serial.println(mode);
  
     unsigned long startMillisV= millis();  // Start of sample window
     unsigned int peakToPeakV = 0;   // peak-to-peak level
    
     unsigned int signalMaxV = 0;
     unsigned int signalMinV = 1024;
    
     // collect data for 50 mS
     while (millis() - startMillisV < sampleWindowV){   
        sampleV = analogRead(1);
        if (sampleV < 1024)  // toss out spurious readings
        {
           if (sampleV > signalMaxV)
           {
              signalMaxV = sampleV;  // save just the max levels
           }
           else if (sampleV < signalMinV)
           {
              signalMinV = sampleV;  // save just the min levels
           }
        }
     }
     peakToPeakV = signalMaxV - signalMinV;  // max - min = peak-peak amplitude
     double voltsV = (peakToPeakV * 3.3) / 1024;  // convert to volts
     Serial.print("I know my volts!: "); 
     Serial.println(voltsV);
     
     double switchVolts = 0.60;  // volume level required to switch sensors
     if ( running = true ) {
       if ( voltsV > switchVolts ) {
         Serial.println("voltsV > switchVolts: "); 
         Serial.print("Mode will be increased by 1");
         mode++;
       }
       if ( mode == 3 ) {
         mode = 0;
       }
     }
  }
  running = false;  
}

void printMode() {
  tft.print("Mode is: ");
  tft.println(mode);
}

where in your excessively long code, is mode being incremented ?

void getVolumeVoltage() {
  if ( running == false ) {
    running = true;
    Serial.println("Gonna check the volume voltage..");  
    Serial.print("Mode is: ");  
    Serial.println(mode);
  
     unsigned long startMillisV= millis();  // Start of sample window
     unsigned int peakToPeakV = 0;   // peak-to-peak level
    
     unsigned int signalMaxV = 0;
     unsigned int signalMinV = 1024;
    
     // collect data for 50 mS
     while (millis() - startMillisV < sampleWindowV){   
        sampleV = analogRead(1);
        if (sampleV < 1024)  // toss out spurious readings
        {
           if (sampleV > signalMaxV)
           {
              signalMaxV = sampleV;  // save just the max levels
           }
           else if (sampleV < signalMinV)
           {
              signalMinV = sampleV;  // save just the min levels
           }
        }
     }
     peakToPeakV = signalMaxV - signalMinV;  // max - min = peak-peak amplitude
     double voltsV = (peakToPeakV * 3.3) / 1024;  // convert to volts
     Serial.print("I know my volts!: "); 
     Serial.println(voltsV);
     
     double switchVolts = 0.60;  // volume level required to switch sensors
     if ( running = true ) {
       if ( voltsV > switchVolts ) {
         Serial.println("voltsV > switchVolts: "); 
         Serial.print("Mode will be increased by 1");
         mode++;
       }
       if ( mode == 3 ) {
         mode = 0;
       }
     }
  }
  running = false;  
}

If you want the three things to be done, repeatedly, in succession, then shouldn't you be incrementing the mode variable by one, after you do each of the three things ?

How, by definition, can the only one of a thing be concurrent?

I am still unclear on exactly what your problem is.

Your process of cycling through the mode states is unclear, but in your supposed output, all three events seem to have happened.

If the problem is that it is happening too fast, then you need to realised that the loop( ) function will happen over and over again, as fast as the processor can do all the instructions. That could be hundreds or thousands of times per second.

If that is happening too fast for you to read the answers on the screen, then you need to slow that process down a little, at least until you have the device fully tested.

The easiest ( although not necessarily the best ) way to do this, is to use the delay ( ) function somewhere in your program. The first thing to try, would be to add a line with

    delay(1000) ;

directly before the final closing curly brace in your loop( ) function.

You use the function getVolumeVoltage() to set the value of mode. And you call that function at the end of each different display function.

I would shift the call to getVolumeVoltage() into loop() and remove it from the other functions. I think that will help to clarify the way the program logic operates, as well as removing some unnecessary duplication.

And a better name for the function might be updateMode() ???

...R

     if ( running = true ) {

Why are you assigning a value to running using an if statement?

JimboZA:
How, by definition, can the only one of a thing be concurrent?

He alone is unanimous in his definition...

@paulS - thanks much for pointing that out. That's problematic.