Code Optimization Possible or Faster Processor?

Hey Guys -

I'm working on a project currently and have hit a roadblock. I've got an Arduino Nano which is doing quite a lot (from my perspective) thus not operating at the speed needed. After research, I did change how I lit the LEDs so it's sent directly to the port which saved a lot of cycles, but am wondering if I can go any further.

To note, when running code with just the 1st step below alone; values are read from the analog pin almost faster than I can read them in serial monitor. When all code is introduced, its read once every 2-3 seconds. I need it to update everything as fast as possible.

The current code is attached, but the loop basically consists of...

  • Getting a value from an analog pin
  • Does some math
  • Checks rotary encoder state
  • Lights some LEDs (depending on read values)
  • Reads DHT22
  • Writes value to servo
  • Updates OLED display

If nothing dramatic can be done, any suggestions for a faster microprocessor would be appreciated. To note, it is needed in a tiny space, so the smaller; the better. It also would need to support at least 12 digital & 4 analog inputs. If nothing like that exists, at least I've learned a ton from this... :slight_smile:

Note: I considered the MKR ZERO, then found out it only supported 3.3V per pin which would call for almost complete redesign of project so prefer not to use it if possible...

Thanks!

Real-PKE-ForumPost.ino (6.76 KB)

Why are you updating the LCD every pass of loop()?

You're updating "static" fields, every pass. For example:

    display.setCursor(0,20);
    display.print("Temperature: ");

Just update what changes and do it a few times a second.

You're also reading probePin every pass. analogRead() takes time; do you really need it updated every pass?

If val is greater than one then you, among other things, call LEDPattern which has a boatload of 100mS delays.

You should re-write the code using millis() instead of delay() and think about when things really need to happen vs every pass of loop().

The processor is very fast delay() is..

Realistically, how fast can temperature and humidity change? Surely not milliseconds. Depending on the library, a DHT sensor reading can be extremely slow, or just slow. It was a mistake to invest all that time in optimizing digital output without analyzing where the loop time is actually being consumed. Just a quick glance at your sketch tells me there are dozens of things you can do to speed it up, and a faster processor probably won't help much.

A lot of your math can probably be rewritten to use faster integer math instead of floating point math, but that may actually be a minor problem compared with the constant unnecessary sensor reads and screen updates.

Try sprinkling in some prints of millis() value to see what is actually slowing it down.

Unless you change these global settings on the fly, you should only have to do this once, in setup(), not almost every time you print:

    display.setTextSize(1);
    display.cp437(true);

Does as fast as possible take into account the sensing period of the DHT22 of 2 seconds?

The sampling rate is .5 seconds for 4 readings over 2 seconds, you'll get the same reading for the 4 readings.

So if you got 1 million readings a second, you get 2 million readings of the same data.

Very fast is not really possible in this case.

Idahowalker:
Does as fast as possible take into account the sensing period of the DHT22 of 2 seconds?

The sampling rate is .5 seconds for 4 readings over 2 seconds, you'll get the same reading for the 4 readings.

So if you got 1 million readings a seconds you get 2 million readings of the same data.

Yes, and this supports the desirability of using a non-blocking library to drive it. Also the sensor can produce results much faster than the temperature or humidity can actually change.

Thanks Guys -

OK, a few answers...

  • Yes, the Analog 0 read needs to be done as often as possible.

  • The DHT22 readings are only needed every 5-10 seconds or so, though. How can I make this so while A0 still reads as fast as possible? (still a newbie with coding :slight_smile: ) I tried adding it to it's own function as well as the OLED part of Loop, but once I did and verified; I got errors in the stating that the variables referenced by OLED & set by DHT22 were not defined.

  • I'll move the static LCD data to setup() to start - good suggestion

  • Concerning the delays for LEDs, I'm wanting the LEDs to move in certain pattern which speeds up or slows down based on the value of the "val" variable which is calculated from A0 read. Is there a way to make this more efficient?

  • Good suggestion about adding millis part to find what parts are taking time. Will do that and/or serial prints and test.

Thanks for the comments - would appreciate any others!

Hey Guys -

So did some testing and the issue causing the slowdown is more than likely due to the delays in the LEDPattern function. I looked into using millis instead, but have an issue I can't figure out...

All the current function does (snippet below) is light up LEDs in a pattern, however; references the variable "turn_time" (calculated from analog read) for the delay time so that the pattern gets faster or slower depending on the set variable. Since the value is dynamic, I'm not sure how to define it with millis. Any suggestions?

Current LEDPattern Function

void LEDPattern () {
  PORTD |= (1 << PD4);
  delay(turn_time);
  PORTD &= ~(1 << PD4);
  delay(turn_time);
  PORTD |= (1 << PD6);
  delay(turn_time);
  PORTD &= ~(1 << PD6);
  delay(turn_time);
  PORTB |= (1 << PB0);
  delay(turn_time);
  PORTB &= ~(1 << PB0);
  delay(turn_time);
  PORTB |= (1 << PB2);
  delay(turn_time);
  PORTB &= ~(1 << PB2);
  delay(turn_time);
  PORTB |= (1 << PB1);
  delay(turn_time);
  PORTB &= ~(1 << PB1);
  delay(turn_time);
  PORTD |= (1 << PD7);
  delay(turn_time);
  PORTD &= ~(1 << PD7);
  delay(turn_time);
  PORTD |= (1 << PD5);
  delay(turn_time);
  PORTD &= ~(1 << PD5);
}

And turn_time is, no doubt on the order of tens, or even hundreds, of milliseconds, because mere humans can't see anything much faster than that. So, you are wasting the majority of your processor time doing nothing in delay().

You need to completely re-write your LED code to use millis() to control timing, and the make ONE change to the LEDs during each pass of loop, so you're not wasting all that time. See the countless "Blink without delay()" tutorials.

Thanks for the reply...

Yes, I know. I've looked at many blink without delay options, but all I've found example using a static amount of time between LED blinks. My question is how to best integrate since the time in this code is dynamic.

Thanks

It's absolutely NO different at all. You just fetch the delay time from a variable each time you update the delay. Your turn-time is already a variable, isn't it? Change the value of turn_time, and the timing changes accordingly.

Hey Guys -

Finally found a solution to get it working the way I wanted using the ezOutput library as I tested it thoroughly on a dev board with just LEDs. Once I added it to my code and uploaded, nothing happened except for serial monitor throwing out "SSD1306 allocation failed." From what I can find, it seems to be a RAM issue as commenting out the new code allows it to work properly. I tried removing some unnecessary code & even tried without DHT library, but still didn't work :frowning:

I'm now trying to get it to work without an additional library, but can't get sequencing to work correctly. Any help would be appreciated. Details are below...

Goal
To get 7 LEDs (really 14, but two per pin) to light up one at a time from left to right then right to left (Kind of like Kit from knight rider)

Note
If it makes a difference, I plan to use variable or variable /w math in place of each value of array so that speed changes within loop based on analog readings. Using static numbers to test.

Code I Can't Get to Work
Starts out great as each light up for a second each in sequence but then falls apart seemingly no matter what I try in array :frowning:

const byte ledPins[] = {4,5,6,7,8,9,10};
const byte NUMBER_OF_LEDS = sizeof(ledPins);
unsigned long startTimes[NUMBER_OF_LEDS] = {};
byte indexes[NUMBER_OF_LEDS] = {0};
const byte MAX_NUMBER_OF_PERIODS = 10;
unsigned long periods[][MAX_NUMBER_OF_PERIODS] = //periods for each LED. zero indicates end of sequence
{
{1000, 1000, 7000, 1000, 7000},
{2000, 1000, 6000, 1000, 6000},
{3000, 1000, 5000, 1000, 5000},
{4000, 1000, 4000, 1000, 4000},
{5000, 1000, 3000, 1000, 3000},
{6000, 1000, 2000, 1000, 2000},
{7000, 1000, 1000, 1000, 1000}
};

void setup()
{
Serial.begin(115200);
for (int led = 0; led < NUMBER_OF_LEDS; led++)
{
pinMode(ledPins[led], OUTPUT);
}
}

void loop()
{
unsigned long currentTime = millis();
for (int led = 0; led < NUMBER_OF_LEDS; led++) //iterate through the LEDs
{
if (currentTime - startTimes[led] >= periods[led][indexes[led]]) //? time to change ?
{
digitalWrite(ledPins[led], !digitalRead(ledPins[led])); //flip led state
startTimes[led] = currentTime; //save start time of state
indexes[led]++; //increment index
}
if (periods[led][indexes[led]] == 0) //if next period is zero (end of sequence)
{
indexes[led] = 0; //reset period index for this LED
}
} //end for loop
}

Thanks again!

I suggest you to try Rgb Led Strip or WS2812B Indirizzabile 5050 LED RGB.
You can use only 1 digital pin and also make a great light effects like fade.
Adafruit NeoPainter and NeoPixel Painter are two great libraries.

About "traditional" leds, you can take some inspiration by following code

const byte NUMBER_OF_LEDS = 7;
const byte ledPins[] = {4,5,6,7,8,9,10};


unsigned long lastMillis = 0; 
int changeAfter = 1000; //pause btw led change. Static but you can use array to have dynamic intervals
byte currentActiveLED = 0;
bool rightDirection = true;



void setup() {
  // put your setup code here
  Serial.begin(115200);
  for (int led = 0; led < NUMBER_OF_LEDS; led++)
  {
    pinMode(ledPins[led], OUTPUT);
  }
  // light up led in 0 position
  digitalWrite(ledPins[0], HIGH); //flip led state
}


void loop() {
  
    unsigned long currentTime = millis();
    if(currentTime - lastMillis > changeAfter)  
    {
        //after "changeAfter" ms change led

        //off current index
        digitalWrite(ledPins[currentActiveLED], LOW); //flip led state
        Serial.print("off led idx ");
        Serial.println(currentActiveLED);

        if(rightDirection)
        {
          //left to right
          if(currentActiveLED != NUMBER_OF_LEDS - 1)
          {
              currentActiveLED++;
          }
          else
          {
            //reach end of led strip
            currentActiveLED--;
            rightDirection = false;
          }
            
        }
        else
        {
          //right to left
          if(currentActiveLED == 0)
          {
            //to left and reach 0 poition
            currentActiveLED++;
            rightDirection = true;
          }
          else
          {
            currentActiveLED--;
          }
        
        }
        
        Serial.print("on led idx ");
        Serial.println(currentActiveLED);
        
        digitalWrite(ledPins[currentActiveLED], HIGH); //Light on CurrentLed
        lastMillis = millis();
    }


}

Thanks for your help!

Testing now. One more question if I may...

To save on cycles, I tried applying millis parameters to the DHT22's part of the loop. Below is before and after. Once I did and compiled, it said that the "f" (one of the two floats DHT writes to) was not declared in the scope. I then tried adding "float f" to the beginning of the script which allowed me to compile, but when watching serial monitor; it displayed "f" as "0.00" except for every 2 seconds where it would temporarily display as the correct temp. What am I missing?

Working Code (within loop)

float f = dht.readTemperature(true);
float h = dht.readHumidity();
if (isnan(h) || isnan(f)) {
Serial.println("Failed to read from DHT sensor!");
}

Non-Working Code (In same place within loop + "unsigned long lastDHT = 0;" & "int dhtReadInterval = 2000; added to beginning of code)

if(millis() - lastDHT > dhtReadInterval)
{
lastDHT = millis();
float f = dht.readTemperature(true);
float h = dht.readHumidity();
if (isnan(h) || isnan(f)) {
Serial.println("Failed to read from DHT sensor!");
}
}

Any thoughts? Thanks!

UPDATE: Seemed to fix this by using "int" instead of "float", but still would prefer float so can have decimals + curious as to why it seems to clear when if statement finishes. Thanks

Sorry I did not follow all the topic, but I cannot undestund where you useing f and h varaible in this code.
If you need to update f and h every "dhtReadInterval" you should declare outside the if statement.


float f;
float h;
if(millis() - lastDHT > dhtReadInterval)
{
  lastDHT = millis();
  f = dht.readTemperature(true);
  h = dht.readHumidity();
  if (isnan(h) || isnan(f)) {
     Serial.println(“Failed to read from DHT sensor!”);
 }

//and here you can use f and h float

}

If you do something like this with your LEDPattern() function, it won't block you other code

void LEDPattern () {
  static unsigned long lastTime;
  static uint8_t state;

  if ( millis() - lastTime < turn_time ) {
    // not time to pdate
    return;
  }

  // update pattern
  lastTime = millis();
  switch (state++) {
    case 0:
      PORTD |= (1 << PD4);
      break;
    case 1:
      PORTD &= ~(1 << PD4);
      break;
    case 2:
      PORTD |= (1 << PD6);
      break;
    case 3:
      PORTD &= ~(1 << PD6);
      break;
    case 4:
      PORTB |= (1 << PB0);
      break;
    case 5:
      PORTB &= ~(1 << PB0);
      break;
    case 6:
      PORTB |= (1 << PB2);
      break;
    case 7:
      PORTB &= ~(1 << PB2);
      break;
    case 8:
      PORTB |= (1 << PB1);
      break;
    case 9:
      PORTB &= ~(1 << PB1);
      break;
    case 10:
      PORTD |= (1 << PD7);
      break;
    case 11:
      PORTD &= ~(1 << PD7);
      break;
    case 12:
      PORTD |= (1 << PD5);
      break;
    case 13:
      PORTD &= ~(1 << PD5);
      break;
    default:
      state = 0;
  }
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.