Code snippets

1. Wait without delay

Probably every hobbyist who starts to program Arduinos stumbles over the problem of delaying a function sequence without blocking a task to be carried out at the same time, in short: he is confronted with the delay() problem.

Many others before me have shown that the problem can be easily solved as follows:

	
    static unsigned long msPrevious = millis();
    if (millis() - msPrevious >= msWait)
    {
      doSomething();          // do the task
      msPrevious = millis();  // keep the current millis for the next call
    }
	
Instead of thinking about these lines over and over, let's pack them simply in an easy-to-remember function call:
	
    static unsigned long msPrevious = millis();
    if (waitIsOver(msPrevious, msWait)) doSomething();
    
If we call the function waitIsOver() in different places in the main program or other functions, we have to remember the msPrevious from function call to function call. We do this by giving the function a reference to a static variable as an argument when it is called. Let's write the whole thing as a ternary expression, we finally get the following function:
	
    /**
     * Returns true, as soon as msWait milliseconds have passed.
     * Supply a reference to an unsigned long variable to hold 
     * the previous milliseconds.
     */
    bool waitIsOver(unsigned long &msPrevious, unsigned long msWait)
    {
      return (millis() - msPrevious >= msWait) ? (msPrevious = millis(), true) : false;
    }
	

2. Heartbeat

Often we would like to have an LED blink in our program, be it as a warning, as an error indication or to display normal functionality. Because this is quickly written down as a combination of
digitalWrite(pinLed,!(digitalRead(pinLed))) and delay(msWait), we don't bother to write a more general function that might be useful in other situations. But wouldn't it be nice to have a function that allows us to specify the number of pulses per unit of time and the pulse duration or duty cycle? So let's do it!

I don't know who was the first to release a flasher with the modulo approach presented here, but this solution seems to have been known since the first days of the Arduino. See forum.arduino.cc or Uri Shaked

	
    const uint8_t PIN_HEARTBEAT = LED_BUILTIN;
    void heartbeat(unsigned long nBeats, unsigned long nSecs, unsigned long dutyOrPulseWidth, bool duty=true)
    {
      if (duty) dutyOrPulseWidth = dutyOrPulseWidth < 100 ? dutyOrPulseWidth : 50;
      unsigned long period = 1000 * nSecs / nBeats;
      unsigned long pulseWidth = duty ? period * dutyOrPulseWidth / 100 : dutyOrPulseWidth;
      digitalWrite(PIN_HEARTBEAT, millis() % period < pulseWidth ? LOW : HIGH);
    }	
    
Call the function in the main loop or in case of an unrecoverable error in a blocking endless loop like this:
	
    while(true) heartbeat(3,1,50);
	

Some examples:

	
    Example1: heartbeat(7, 13, 15) 
              flash the LED 7 times in 13 seconds with a dutycycle of 15%
			  
    Example2: heartbeat(8, 15, 4);
              period = 15000/8 = 1875ms, pulsewidth = 4% = 75ms (default duty=true)

    Example3: heartbeat(8, 15, 4, false);
              period = 15000/8 = 1875ms, pulsewidth = 4ms (duty=false)
    

3. Simple Square Wave Generator

The heartbeat() function presented above can also be used in general as a function generator. To increase the possible frequency range, we use the micros() function instead of millis(). If we are using multiple function generators, we may also need a phase shift. With these additions, our function generator looks like this:

	
    digitalWrite(PIN_PHASE_0, (micros() + phase_0) % period < pulsewidth ? HIGH : LOW);
    
To make things a bit clearer, let's implement a 50 Hz 3-phase system, but slow it down by a factor of 100 so that we can observe the voltages of the phases with 3 LEDs.
	
    const uint8_t PIN_PHASE_0 = D3;
    const uint8_t PIN_PHASE_1 = D4;
    const uint8_t PIN_PHASE_2 = D5;

    const uint32_t period     = 2000000;      // ==> 2 s / 0.5 Hz
    const uint32_t pulsewidth = 1000000;      // ==> 1 s / 50 % dutycycle

    constexpr uint32_t phase_0 = 0;               // 0°
    constexpr uint32_t phase_1 = period / 3;      // 120°
    constexpr uint32_t phase_2 = 2 * period / 3;  // 240°

    void setup() 
    {
      pinMode(PIN_PHASE_0, OUTPUT);
      pinMode(PIN_PHASE_1, OUTPUT);
      pinMode(PIN_PHASE_2, OUTPUT);
    }

    // 3-phase generator
    void loop() 
    {
      digitalWrite(PIN_PHASE_0, (micros() + phase_0) % period < pulsewidth ? HIGH : LOW);
      digitalWrite(PIN_PHASE_1, (micros() + phase_1) % period < pulsewidth ? HIGH : LOW);
      digitalWrite(PIN_PHASE_2, (micros() + phase_2) % period < pulsewidth ? HIGH : LOW);
    }	
	
As shown in the program its easy to generate 3-phase square waves like this:
   
             _____       _____       _____    
    Phase 0 |     |_____|     |_____|     |_____|    0°
                 _____       _____       _____
    Phase 1 ____|     |_____|     |_____|     |__  120°
            __       _____       _____       ____
    Phase 2   |_____|     |_____|     |_____|      240°
    

In theory we can get a minimal period of 2 μs and a minimal pulsewidth of 1 μs but in praxis the modulo operation, the digitalWrite() function and the main loop are continuously called and take a lot of time. Further the Arduino reference says that the micros() counter has at best a resolution of 4 μs. With an Arduino Uno some 100 Hz with tolerable jitter are realistic but with an ESP32 the limit is much higher.

pros
- simple implementation in one line of code
- very long periodes achievable
- synchronized but phase-shifted signals can be easily generated
cons
at frequencies > 100 Hz the jitter is considerable

4. Show nearby WiFi networks

				
    /**
     * Use a raw string literal to print
     * nearby WiFi networks with SSID und RSSI 
     */
    void printNearbyNetworks()
    {
      int n = WiFi.scanNetworks();
      Serial.printf(R"(
Nearby WiFi networks:
--------------------
)");
      for (int i = 0; i < n; i++)
      {
        Serial.printf("%s\t%d\r\n", WiFi.SSID(i).c_str(), WiFi.RSSI(i));
      }
    }
				

5. Show WiFi connection details

				
    /**
     * Use a raw string literal to print a formatted
     * string of WiFi connection details
     */
    void printConnectionDetails()
    {
      Serial.printf(R"(
Connection Details:
------------------
  SSID       : %s
  Hostname   : %s
  IP-Address : %s
  MAC-Address: %s
  RSSI       : %d (received signal strength indicator)
  )", WiFi.SSID().c_str(),
      WiFi.hostname().c_str(),  // ESP8266
      // WiFi.getHostname(),    // ESP32 
      WiFi.localIP().toString().c_str(),
      WiFi.macAddress().c_str(),
      WiFi.RSSI());  
    }
				

6. Initializing the WiFi

The routine below tries to initialize the WiFi connection and handles all possible failures. The processor is restarted after 5 retries. Please notice that we need a call to WiFi.config() to set the hostname of an ESP32 so it is visible in the router.

				
    void initWiFi()
    {
      uint8_t retries = 0;
      constexpr char hostname[] = "esp-host";
      Serial.printf("Connecting to %s ...", ssid);
      //WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);  // this line only needed for ESP32 to set the hostname
      //WiFi.setHostname(hostname);  // ESP32
      WiFi.hostname(hostname);       // ESP8266
  
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED) 
      {
        delay(1000);
        if (retries++ > 5) ESP.restart();  // restart processor after 5 failed attempts
	
        switch(WiFi.status())
        {
          case WL_IDLE_STATUS:      // 0
            Serial.printf("WiFi is idle\n");
          break;
          case WL_NO_SSID_AVAIL:    // 1
            Serial.printf("WiFi no matching SSID found: %s\n", WiFi.SSID().c_str());
            Serial.printf("... cannot proceed\n");
          break;
          case WL_SCAN_COMPLETED:   // 2
            Serial.printf("WiFi scan completed\n");
          break;
          case WL_CONNECTED:        // 3 (never reached because of loop condition)
            Serial.printf("WiFi connected\n");
          break;      
          case WL_CONNECT_FAILED:   // 4
            Serial.printf("WiFi connection failed\n");
          break;
          case WL_CONNECTION_LOST:  // 5
            Serial.printf("WiFi connection lost\n");
          break;
          case WL_DISCONNECTED:     // 6
            Serial.printf("WiFi disconnected\n");
          break;
          default:
          break;
        }
      }
      Serial.printf("... WiFi connected.\n");
      printNearbyNetworks();
      printConnectionDetails();
      Serial.printf("\ninitWiFi ===> done\n"); 
    }				
				

7. Debounced button click

Debouncing buttons is daily business of the microcontroller hobbyist. Dozens of solutions have been proposed, all more or less useful or elegant. I find the approach of Elliot Williams particularly attractive: Part I and Part II

I put the code of Elliot into the function buttonClicked(). Their usage is demonstrated by the little sketch below.

				
    const uint8_t PIN_BUTTON = 15;
	
    bool buttonClicked(uint8_t pinButton)
    {
      static uint8_t button_history = 0;
      bool clicked = false;    

      button_history = button_history << 1;
      button_history |= (uint8_t)digitalRead(pinButton);
  
      if ((button_history & 0b11000011) == 0b11000000)
      { 
        clicked = true;
        button_history = 0;
      }
      return clicked;
    }

    void setup() 
    {
      pinMode(PIN_BUTTON, INPUT_PULLUP);
      pinMode(LED_BUILTIN, OUTPUT);
    }

    void loop() 
    {
      if (buttonClicked(PIN_BUTTON)) digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
      delayMicroseconds(150);  // needed to decrease the sampling rate to prevent glitches
    }				
				

8. Printing 2-dimensional arrays

We can only print out the elements of a 2-dimensional array if we know its dimensions, i.e. the number of rows and number of columns. We know the type (int, float, etc.) of an array element and thus also its size in bytes. From this we can calculate the number of rows and columns as follows:

					
// myIntArray is an array of array[4] of int
int myIntArray[][4] =
{
  { 9, 8, 7, 6 },
  { 5, 4, 3, 2 },
  { 1, 1, 1, 1 },
  { 2, 2, 2, 2 },
  { 1, 0,-1,-2 }
};

constexpr int A = sizeof(myIntArray);           // nbr of bytes the whole array takes in memory
constexpr int E = sizeof(myIntArray[0][0]);     // nbr of bytes an array element takes 
constexpr int C = sizeof(myIntArray[0]) / E;    // nbr of columns is (size of first row) / (size of one element) 
constexpr int R = sizeof(myIntArray) / (C * E); // nbr of rows is (size of array / (nbr of columns * size of one element)	
					
With this we can write a function that produces a formatted output of all array elements.

void printIntArray(int *arr, int nRows, int nColumns)
{
  for(int r = 0; r < nRows; r++)
  {
    for (int c = 0; c < nColumns; c++)
    {
      Serial.printf("%d\t", arr[r * nColumns + c]); 
    } 
    Serial.printf("\r\n");   
  }
}					
					
We pass the function a pointer to the array and the number of rows and columns as arguments: printIntArray((int *)myIntArray, R, C); With this function we can output any 2-dimensional integer arrays. But what do we do when we have an array whose elements are not integers, for example, an array of strings like this, actually an array of pointers to null-terminated strings?
					
// myStringArray is an array of array[5] of pointer to const char
const char* myStringArray[][5] = 
{
  { "Fee-",        "fi-",   "fo-",    "fum.",   "I"          },
  { "smell",       "the",   "blood",  "of",     "an"         },
  { "Englishman.", "Be",    "he",     "alive,", "or"         },
  { "be",          "he",    "dead,",  "I'll",   "grind"      },
  { "his",         "bones", "to",     "make",   "my"         },
  { "bread.",      "(Jack", "and",    "the",    "Beanstalk)" }
};

constexpr int SA = sizeof(myStringArray);             // nbr of bytes the whole array takes in memory
constexpr int SE = sizeof(myStringArray[0][0]);       // nbr of bytes an array element takes ( a pointer)
constexpr int SC = sizeof(myStringArray[0]) / SE;     // nbr of columns is (size of first row) / (size of one element)
constexpr int SR = sizeof(myStringArray) / (SC * SE); // nbr of rows is (size of array / (nbr of columns * size of one element)
					
Now our print function looks like this:

void printStringArray(const char** arr, int nRows, int nColumns)
{
  for(int r = 0; r < nRows; r++)
  {
    for (int c = 0; c < nColumns; c++)
    {
      Serial.printf("%s ", arr[r * nColumns + c]); 
    } 
    Serial.printf("\r\n");   
  }
}					
					
And we call it like this: printStringArray((const char**)myStringArray, SR, SC); Its nearly the same code but only the data type and the format-string in the print statement differ. Hadn't we heard of templates a long time ago in the C ++ lecture? Of course! With a template as the first argument in the function, it is certainly possible to write a function printArray for several data types. Now the only thing that worries us is the format string in printf(). That is why we are now using cout, the standard C ++ method for output.
					
template <class arrType>
void printArray(arrType arr, int nRows, int nColumns)
{
  for(int r = 0; r < nRows; r++)
  {
    for (int c = 0; c < nColumns; c++)
    {
      cout << arr[r * nColumns + c] << " ";
    } 
    cout << "\r\n";   
  }  
}
					
Finally, we get a small program with which we can demonstrate the output of integer, string, and float arrays.

#include <iostream>

using namespace std;

// myIntArray is an array of array[4] of int
int myIntArray[][4] =
{
  { 9, 8, 7, 6 },
  { 5, 4, 3, 2 },
  { 1, 1, 1, 1 },
  { 2, 2, 2, 2 },
  { 1, 0,-1,-2 }
};

constexpr int A = sizeof(myIntArray);           // number of bytes the whole array takes in memory
constexpr int E = sizeof(myIntArray[0][0]);     // number of bytes an array element takes 
constexpr int C = sizeof(myIntArray[0]) / E;    // number of columns is (size of first row) / (size of one element) 
constexpr int R = sizeof(myIntArray) / (C * E); // number of rows is (size of array / (number of columns * size of one element)


// myStringArray is an array of array[5] of pointer to const char
const char* myStringArray[][5] = 
{
  { "Fee-",        "fi-",   "fo-",    "fum.",   "I"          },
  { "smell",       "the",   "blood",  "of",     "an"         },
  { "Englishman.", "Be",    "he",     "alive,", "or"         },
  { "be",          "he",    "dead,",  "I'll",   "grind"      },
  { "his",         "bones", "to",     "make",   "my"         },
  { "bread.",      "(Jack", "and",    "the",    "Beanstalk)" }
};

constexpr int SA = sizeof(myStringArray);             // number of bytes the whole array takes in memory
constexpr int SE = sizeof(myStringArray[0][0]);       // number of bytes an array element takes ( a pointer)
constexpr int SC = sizeof(myStringArray[0]) / SE;     // number of columns is (size of first row) / (size of one element)
constexpr int SR = sizeof(myStringArray) / (SC * SE); // number of rows is (size of array / (number of columns * size of one element)

float myFloatArray[][2] = 
{
  { 3.14159265, 355.0 / 113.0 },  // pi    and continued fraction approximation
  { 2.71828182, 193.0 / 71.0  },  // e
  { 0.57721566, 228.0 / 395.0 }   // gamma
};

constexpr int FA = sizeof(myFloatArray);
constexpr int FE = sizeof(myFloatArray[0][0]);
constexpr int FC = sizeof(myFloatArray[0]) / FE;
constexpr int FR = sizeof(myFloatArray) / (FC * FE);

template <class arrType>
void printArray(arrType arr, int nRows, int nColumns)
{
  for(int r = 0; r < nRows; r++)
  {
    for (int c = 0; c < nColumns; c++)
    {
      cout << arr[r * nColumns + c] << " ";
    } 
    cout << "\r\n";   
  }  
}

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

  Serial.printf("Array size is: %d bytes, Element size is %d, %d rows x %d columns\r\n", A, E, R, C);
  printArray((int *)myIntArray, R, C); 

  Serial.printf("\nArray size is: %d bytes, Element size is %d, %d rows x %d columns\r\n", SA, SE, SR, SC);
  printArray((const char**)myStringArray, SR, SC);  

  Serial.printf("\nArray size is: %d bytes, Element size is %d, %d rows x %d columns\r\n", FA, FE, FR, FC);
  printArray((float *)myFloatArray, FR, FC);  
}					
				
As a result we get:

Array size is: 80 bytes, Element size is 4, 5 rows x 4 columns
9 8 7 6
5 4 3 2
1 1 1 1
2 2 2 2
1 0 -1 -2

Array size is: 120 bytes, Element size is 4, 6 rows x 5 columns
Fee- fi- fo- fum. I
smell the blood of an
Englishman. Be he alive, or
be he dead, I'll grind
his bones to make my
bread. (Jack and the Beanstalk)

Array size is: 48 bytes, Element size is 8, 3 rows x 2 columns
3.14159 3.14159
2.71828 2.71831
0.577216 0.577215