Table of Contents

Avoiding Buffer Overflows with the MQTT PubSub Client

The MQTT PubSub client callback passes the payload parameter as a pointer to a byte array and not a pointer to a char array - this is due to the content of the publish payload being application specific. In other words, from the point of view of the protocol, the payload can be a string, a file, and image, etc. By contrast, the topic parameter is indeed expected to be a string, hence the buffer pointer char *topic among the function parameters.

The result is that just casting the payload byte parameter to a const char * and converting to a String type will not work because the ending null character will be missing. Given the published payload:

{"action":"subscribed"}

handled by the PubSub callback:

void mqttCallback(char *topic, byte *payload, unsigned int length) {
  String msgTopic = String(topic);
  String msgPayload = (const char *) payload;
  //...

will result in the msgPayload string containing garbage:

{"action":"subscribed"}d}"

Perhaps a simpler way of dealing with the payload parameter, in case the expected content is a string, would be to create a null-terminated character array:

void mqttCallback(char *topic, byte *payload, unsigned int length) {
  String msgTopic = String(topic);
  // payload is not null terminated and casting will not work
  char msgPayload[length + 1];
  snprintf(msgPayload, length + 1, "%s", payload);
  Serial.println("Message received on topic: " + String(topic) + " with payload: " + String(msgPayload));
  // ...

Now String(msgPayload) will display the string properly. Unfortunately, a lot of available examples incorrectly convert the payload parameter resulting in small yet ever-increasing memory corruption over time which leads to stability issues with the Arduino device.

Force-Flash devices with Arduino

For WeMoS ESP8266 the following trick can be used to force-flash an Arduino sketch. First, connect the flash pin to ground (ie: D3 on WeMoS mini), plug in the USB cable to flash a new sketch and then reset the board.

Similarly WeMoS ESP32 devices, along the lines of LOLIN that do not have a flash pin can be force-flashed by grounding the EN pin (typically wired to the reset button on the board), starting a flash operation from Arduino, and when the "Connecting" message pops up with the dots (and sometimes dashes), releasing the EN pin.

These methods are very useful in case the ESP pins that correspond to serial communication, or ESP pins that mess with the booting sequence of the ESP, are used up such that the only way to flash the device is to force-flash the device.

ZX Spectrum Lehmer Random Number Generator for Arduino

The following code is a translation of the lolcode random number generator to Arduino/C++.

///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2024 - License: GNU MIT        //
///////////////////////////////////////////////////////////////////////////
// from https://grimore.org/fuss/lolcode#random_number_generator         //
// the parameter "m" represents the upper bound of the random number     //
///////////////////////////////////////////////////////////////////////////
int lrng = 65536;
int wasSpectrumLehmer(int m) {
  int g = 75;
  int n = 65537;
  int c = 0;
 
  do {
    lrng = (g * (lrng + c)) % n;
  } while(++c < m);
 
  return lrng % m;
}

The code is somewhat useful given that generating random numbers, even PRNG numbers, seems to be tricky on Arduino and with various caveats depending on the methods being used.

Calling the wasSpectrumLehmer function in order to generate random numbers can be performed as such:

  int i = spectrumLehmer(100);
  Serial.println(i);
  i = spectrumLehmer(100);
  Serial.println(i);
  delay(1000);

and two random numbers should be printed to the serial console.

Convert a MAC Address to a Colon-separated String

In order to convert a MAC address to a colon-spaced string, the following function can be used:

///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2015 - License: GNU MIT        //
///////////////////////////////////////////////////////////////////////////
const char* macToString(byte* mac) {
  char* buff = (char *)malloc(18 * sizeof(char));
  sprintf(buff, "%02x:%02x:%02x:%02x:%02x:%02x", 
    bootMAC[0], 
    bootMAC[1],
    bootMAC[2], 
    bootMAC[3], 
    bootMAC[4], 
    bootMAC[5]
  );
  return (const char*) buff;
}

For example, in order to obtain a colon-separated string of the WiFi MAC address, call the function like this:

byte mac[6];
WiFi.macAddress(mac);
String wifiMAC = macToString(mac);