Random number problem on ESP8266 running Arduino IDE

Hi I'm new to the forum so I'm sorry if this is not the right place to ask the question but;

I have some code I'm writing which I want to use to inflate and deflate air rams under a platform to create the feeling of a ship rolling on the sea, the interface is from a tablet, over WiFi using Node Red on a Raspberry Pi to update the perimeters via mqtt - so far so good.

I want to include a random factor to the timing, so it doesn't have too mechanical a feel to the motion - but I am having trouble generating random numbers.

I tried the Arduino random() function, but that returned only very low numbers, so I installed the ESP8266TrueRandom library but have the same issue.

Using ESP8266TrueRandom.random(10000) I rarely get a number generated above 250, and most of the results are sub 100. I have searched online but can't find any information, is anyone able to shed some light on why this is happening / how to fix it?

Here is the part of the code where I'm generating and using the random number:

  currentMillis = millis();  //syncs the currentMillis value to the current time 
  
  duration = ((durationRAW * (platformspeed/100)));  //Sets the duration value to a percentage of platform speed 

  RandFactor = ESP8266TrueRandom.random(RandomRAW);  //Creates a random number upto the random amount set via mqtt

  // Alternates between the two halves of the cycle and turns on rams 1 and 2.  Timing includes a random value
  if (currentMillis - FlipTime >= platformspeed + RandFactor)
  {
    { if (flipflop == HIGH)
    {
      {
      flipflop = LOW;
      Ram1OnTime = currentMillis;
      Ram1State = HIGH;
      Serial.print(RandFactor);
      Serial.println();
      }
    }
    else
    {
      {
      flipflop = HIGH;
      Ram2OnTime = currentMillis;
      Ram2State = HIGH;
      }
    }
    FlipTime = currentMillis;
    } 
  }
  
  // Write the values to the pins
  
    digitalWrite(ram1, Ram1State);
    digitalWrite(ram2, Ram2State);

Everything else works perfectly, the sequence and mqtt communication is doing what I want - I just can't get the randomisation part to work.

Thanks in advance for any help or insight with this problem.

Post examples of the output, and post ALL the code (preferably, the minimum code that demonstrates the problem), so that we can see how you have declared variables.

Thanks for the quick reply!

Here is the complete code I'm using:

#include <ESP8266TrueRandom.h>

// Loading the ESP8266WiFi library and the PubSubClient library
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// Change the credentials below, so your ESP8266 connects to your router
const char* ssid = "x";
const char* password = "x";

// Change the variable to your Raspberry Pi IP address, so it connects to your MQTT broker
const char* mqtt_server = "192.168.0.125";

// Initializes the espClient
WiFiClient espClient;
PubSubClient client(espClient);

//variables and starting values for the main parameters
int platformspeed = 500;
int duration = 250;
int durationRAW = 50;
int offset = 125;
int offsetRAW = 50;
int RandomRAW = 0;
int RandFactor = 0;

// GPIO pin numbers for each output on the ESP8266
const int ram1 = 0;
const int ram2 = 2;
const int ram3 = 4;
const int ram4 = 5;

//Carries the current time value
unsigned long currentMillis = 0;
// Timestamps from when each ram turns on for the timer
unsigned long Ram1OnTime = 0;
unsigned long Ram2OnTime = 0;
unsigned long Ram3OnTime = 0;
unsigned long Ram4OnTime = 0;
unsigned long FlipTime = 0;

//keeps the state of the cycle
int flipflop = LOW;

// Keeps the state of each output
int Ram1State = LOW;             
int Ram2State = LOW;
int Ram3State = LOW;
int Ram4State = LOW;

// Don't change the function below. This functions connects your ESP8266 to your router
void setup_wifi() 
{
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("WiFi connected - ESP IP address: ");
  Serial.println(WiFi.localIP());
}

// These functions are executed when a device publishes a message to a topic that your ESP8266 is subscribed to
// Sends a message via serial for debugging

void callback(String topic, byte* message, unsigned int length) {
  Serial.print("New value for [ ");
  Serial.print(topic);
  Serial.print(" ] ");
  String messageTemp;
  
//Change the asci values to text 
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
   Serial.println();

// When a message is received on the topic platform/speed, set the variable platformspeed to the new value 
  if(topic=="platform/speed")
     {
     message[length] = '\0'; // Make payload a string by NULL terminating it.
     platformspeed = atoi((char *)message); //Convert the string to an interger 
     //Serial.print("Changing Speed to ");
     //Serial.print(platformspeed);
     }
// If a message is received on the topic platform/duration, set the variable duration to the new value
  if(topic=="platform/duration")
     {
     message[length] = '\0'; // Make payload a string by NULL terminating it.
     durationRAW = atoi((char *)message);
     //Serial.print("Changing duration to ");
     //Serial.print(durationRAW);
     }
// If a message is received on the topic platform/offset, set the variable offset to the new value
  if(topic=="platform/offset")
     {
     message[length] = '\0'; // Make payload a string by NULL terminating it.
     offsetRAW = atoi((char *)message);
     //Serial.print("Changing offset to ");
     //Serial.print(offsetRAW);
     }
// If a message is received on the topic platform/random, set the variable offset to the new value
  if(topic=="platform/random")
     {
     message[length] = '\0'; // Make payload a string by NULL terminating it.
     RandomRAW = atoi((char *)message);
     Serial.print("Changing Rand Factor to ");
     Serial.print(RandomRAW);
     }
  Serial.println();
}
// This functions reconnects your ESP8266 to your MQTT broker
  void reconnect() {         // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");  
// Subscribe or resubscribe to a topic
// You can subscribe to more topics (to control more LEDs in this example)
      client.subscribe("platform/speed");
      client.subscribe("platform/duration");
      client.subscribe("platform/offset");
      client.subscribe("platform/random");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}
// The setup function sets your ESP GPIOs to Outputs, starts the serial communication at a baud rate of 115200
// Sets your mqtt broker and sets the callback function
// The callback function is what receives messages

void setup() {
  pinMode(ram1, OUTPUT);
  pinMode(ram2, OUTPUT);
  pinMode(ram3, OUTPUT);
  pinMode(ram4, OUTPUT);
  
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  randomSeed(ESP8266TrueRandom.random());
}
 // Here is the loop which runs the in and outputs
void loop()
{
  // This ensures that you ESP is connected to your broker
  if (!client.connected()) { reconnect(); }
  client.loop();
   
  currentMillis = millis();  //syncs the currentMillis value to the current time 
  
  offset = ((offsetRAW * (platformspeed/100)));   //Sets the offset value to a percentage of platform speed 
  
  duration = ((durationRAW * (platformspeed/100)));  //Sets the duration value to a percentage of platform speed 

  RandFactor = ESP8266TrueRandom.random(RandomRAW);  //Creates a random number between 1 and the random amount set via mqtt

  // Alternates between the two halves of the cycle and turns on rams 1 and 3.  Timing includes a random value
  if (currentMillis - FlipTime >= platformspeed + RandFactor)
  {
    { if (flipflop == HIGH)
    {
      {
      flipflop = LOW;
      Ram1OnTime = currentMillis;
      Ram1State = HIGH;
      Serial.print(RandFactor);
      Serial.print(", ");
      }
    }
    else
    {
      {
      flipflop = HIGH;
      Ram3OnTime = currentMillis;
      Ram3State = HIGH;
      }
    }
    FlipTime = currentMillis;
    } 
  }
  
  //Turn rams 2 and 4 on after the offset amount and off after the duration
  
  if ((currentMillis - Ram1OnTime >= offset) && (currentMillis - Ram1OnTime - offset <= duration)) 
  {
    { Ram2State = HIGH;}
  }
  else
  {
    { Ram2State = LOW;}
  }
 if ((currentMillis - Ram3OnTime >= offset) && (currentMillis - Ram3OnTime - offset <= duration))
    { 
      {Ram4State = HIGH;}
    }
   else
  {
    { Ram4State = LOW;}
  }
  
 // Check to see if rams 1 and 3 have reached their duration and switch them off if they have
 
  if (currentMillis - Ram1OnTime >= duration)
  {
    {Ram1State = LOW; } 
  }
 if (currentMillis - Ram3OnTime >= duration)
  {
    {Ram3State = LOW; }  
  }  
  
  // Write the values to the pins
  
    digitalWrite(ram1, Ram1State);
    digitalWrite(ram2, Ram2State); 
    digitalWrite(ram3, Ram3State);
    digitalWrite(ram4, Ram4State);
}

And here is a sample of the random numbers I'm having generated:

New value for [ platform/random ] 100
Changing Rand Factor to 100

6, 4, 3, 1, 6, 1, 1, 0, 1, 5, 0, 6, 4, 0, 7, 8, 0, 0, 14, 6, 3, 12, 3, 4, 12, 5, 0, 6, 2, 1, 2, 0, 6, 7, 0, 8, 1, 3, 0, 3, 2, 0, 2, 10, 2, 2, 4, 10, 0, 0, 1, 0, 3, 6, 0, 14, 2, 12, 8, 1, 3, 0, 0, 12, 1, 7, 5, 1, 2, 2, 5, 1, 1, 4, 5, 1, 0, 3, 4, 0, 10, 2, 0, 0, 7, 7, 5, 0, 7, 1, 3, 5, 2, 4, 0, 7, 12, 0, 3, 0, 3, 2, 5, 8, 5, 0, 2, 0, 6, 0, 7, 1, 7, 7, 2, 3, 10, 1, 17, 0, 3, 4, 6, 12, 7, 8, 8, 0, 4, 2, 7, 2, 0, 5, 12, 11, 3, 2, 1, 0, 4, 2, 1, 0, 2, 1, 8, 0, 4, 1, 0, 5, 1, 1, 11, 4, 2, 3, 4, 2, 0, 10, 0, 5, 3, 5, 9, 2, 3, 0, 2, 0, 3, 0, 2, 0, 9, 7, 3, 4, 9, 1, 0, 0, 14, 0, 9, 3, 0, 1, 0, 2, 2, 9, 1, 12, 1, 1, 3, 8, 0, 5, 7, 13, 7, 0, 0, 16, 2, 3, 2, 1, 10, 15, 11, 18, 5, 2, 5, 5, 0, 2, 11, 7, 4, 2, 3, 7, 5, 13, 3, 9, 2, 1, 5, 2, 0, 3, 1, 5, 5, 5, 5, 0, 0, 6, 5, 15, 6, 6, 4, 6, 5, 4,

I suggest to read the library docs more carefully, and experiment with the smallest program possible that generates random numbers using it.

The following line line, contrary to the comment, should generate integers between and including 0 and 99, for randomRAW = 100.

RandFactor = ESP8266TrueRandom.random(RandomRAW);  //Creates a random number between 1 and the random amount set via mqtt

From the docs:

ESP8266TrueRandom.random(n)
This generates a random number between 0 and (n-1). So random(6) will generate numbers between 0 and 5.

jremington:
I suggest to read the library docs more carefully, and experiment with the smallest program possible that generates random numbers using it.

Thanks for the reply, I will try this and update the thread - I might also just try swapping out the ESP8266 for another one to see if the results are the same with a different board.

The following line line, contrary to the comment, should generate numbers between and including 0 and 99, for randomRAW = 100.

Yes, that's true - but it still doesn't explain why the output I'm getting never goes above 19 and is heavily weighted toward the lower numbers. That's the issue I'm stuck with....

Make NO ASSUMPTIONS. Print out the value of randomRAW immediately before the function is called. It could be corrupted by other program errors. Immediately after the call, print out the return value.

That is the entire point of experimenting with a small program.

Cutting the code right down to this:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ESP8266TrueRandom.h>

//variables and starting values for the main parameters
int RandomRAW = 100;
int RandFactor = 0;

// GPIO pin numbers for each output on the ESP8266
const int ram1 = 2;

// Keeps the state of each output
int Ram1State = LOW;             

void setup() {
  pinMode(ram1, OUTPUT);
  Serial.begin(115200);
}

void loop()
{
  RandFactor = ESP8266TrueRandom.random(RandomRAW);  //Creates a random number between 0 and the random amount set via mqtt -1
  Serial.print(RandFactor);
  Serial.print(", ");
  delay (1000);
  digitalWrite(ram1, HIGH);
  delay (500);
  digitalWrite(ram1, LOW); 
}

I get a result that is exactly what I'd expect for the type of values, (below) so now I will try adding back the Wifi and mqtt one at a time and see if I can find the cause of the problem.

Sample output:

46, 75, 19, 68, 67, 42, 54, 59, 1, 90, 10, 24, 41, 44, 57, 8, 72, 34, 60, 73, 86, 38, 57, 99, 63, 31, 82, 36, 94, 35, 93, 16, 90, 98, 9, 75, 75, 16, 32, 64, 95, 70, 52, 29, 9, 10, 29, 17, 48, 59, 92, 72, 10, 98, 53, 49, 94, 64, 78, 23, 92, 2, 80, 87, 31, 15, 72, 53, 15, 71, 11, 52, 77, 88, 5, 98, 11, 21, 6, 9, 73, 95, 81, 99, 25, 56, 16, 80, 81, 77, 80, 30, 41, 25, 36, 25, 55, 36, 19, 46, 83, 64, 46, 62, 82, 69, 55, 51, 92, 14, 83, 17, 9, 44, 72, 24, 22, 63, 18, 35, 84, 14, 86, 9, 75, 49, 62, 90, 53, 79, 63, 38, 83, 37, 68, 4, 41, 84, 16, 21,

Update:

Connected to the WiFi and still getting the expected results.

Will try now with mqtt running as well.....

Update #2:

I have WiFi + MQTT both running and connected and still getting the expected results.

Now I will try putting all the other bits of code back one at a time and see if I can find what the problem is.

Too much trouble to try the suggestion in reply #6?

jremington:
Too much trouble to try the suggestion in reply #6?

I think I misunderstood, I had thought I was doing this by serial printing it on the very next line here:

  RandFactor = ESP8266TrueRandom.random(RandomRAW);  //Creates a random number between 0 and the random amount set via mqtt -1
  Serial.print(RandFactor);

But now I see that I am in fact printing RandFactor and not RandomRAW, I will try this now....

First of all, thank you so much for your help with this!

So far, I have established that it is definitely something in the main void loop that is causing it, I took out, and have now put back, all the other code. One piece at a time, checking the result in between.

Now, in order to do this:

jremington:
Make NO ASSUMPTIONS. Print out the value of randomRAW immediately before the function is called. It could be corrupted by other program errors. Immediately after the call, print out the return value.

I replaced:

RandFactor = ESP8266TrueRandom.random(RandomRAW);  //Creates a random number between 0 and the random amount set via mqtt -1
  Serial.print(RandFactor);
  Serial.print(", ");

With:

void loop()
{
   // This ensures that you ESP is connected to your broker and reconnects it if needed
   if (!client.connected()) { 
    reconnect();
    }
  client.loop();

  RandFactor = ESP8266TrueRandom.random(RandomRAW);  //Creates a random number between 0 and the random amount set via mqtt -1
  Serial.print(RandomRAW);
  Serial.print("/");
  Serial.print(RandFactor);
  Serial.print(" ");
  delay (1000);
  digitalWrite(ram1, HIGH);
  delay (500);
  digitalWrite(ram1, LOW); 
}

I get the result you would expect as follows:

100/71 100/67 100/81 100/67 100/7 100/21 100/22 100/14 100/51 100/75 100/51 100/36 100/7 100/24 100/0 100/59 100/96 100/63 100/15 100/1 100/74 100/70 100/52 100/28 100/8 100/3 100/94 100/44 100/79 100/51 100/33 100/19 100/10 100/33 100/40 100/34 100/93 100/32 100/36 100/18 100/11 100/84 100/87 100/90 100/23 100/46 100/15 100/57 100/28 100/14 100/76 100/62 100/40 100/52 100/58 100/32

However, when I try putting it in to my original void loop code I get a very different result. Everything outside of the loop is the same.

If I use:

void loop()
{
   // This ensures that you ESP is connected to your broker and reconnects it if needed
   if (!client.connected()) { 
    reconnect();
    }
  client.loop();

  currentMillis = millis();  //syncs the currentMillis value to the current time 
  
  offset = ((offsetRAW * (platformspeed/100)));   //Sets the offset value to a percentage of platform speed 
  
  duration = ((durationRAW * (platformspeed/100)));  //Sets the duration value to a percentage of platform speed 

  RandFactor = ESP8266TrueRandom.random(RandomRAW);  //Creates a random number between 1 and the random amount set via mqtt

  // Alternates between the two halves of the cycle and turns on rams 1 and 3.  Timing includes a random value
  if (currentMillis - FlipTime >= platformspeed + RandFactor)
  {
    { if (flipflop == HIGH)
    {
      {
      flipflop = LOW;
      Ram1OnTime = currentMillis;
      Ram1State = HIGH;
      Serial.print(RandomRAW);
      Serial.print("/");
      Serial.print(RandFactor);
      Serial.print(" ");
      }
    }
    else
    {
      {
      flipflop = HIGH;
      Ram3OnTime = currentMillis;
      Ram3State = HIGH;
      }
    }
    FlipTime = currentMillis;
    } 
  }
  
  //Turn rams 2 and 4 on after the offset amount and off after the duration
  
  if ((currentMillis - Ram1OnTime >= offset) && (currentMillis - Ram1OnTime - offset <= duration)) 
  {
    { Ram2State = HIGH;}
  }
  else
  {
    { Ram2State = LOW;}
  }
 if ((currentMillis - Ram3OnTime >= offset) && (currentMillis - Ram3OnTime - offset <= duration))
    { 
      {Ram4State = HIGH;}
    }
   else
  {
    { Ram4State = LOW;}
  }
  
 // Check to see if rams 1 and 3 have reached their duration and switch them off if they have
 
  if (currentMillis - Ram1OnTime >= duration)
  {
    {Ram1State = LOW; } 
  }
 if (currentMillis - Ram3OnTime >= duration)
  {
    {Ram3State = LOW; }  
  }  
  
  // Write the values to the pins
  
    digitalWrite(ram1, Ram1State);
    digitalWrite(ram2, Ram2State); 
    digitalWrite(ram3, Ram3State);
    digitalWrite(ram4, Ram4State);
}

I get this:
100/3 100/16 100/1 100/9 100/3 100/0 100/1 100/8 100/2 100/0 100/3 100/7 100/5 100/7 100/1 100/0 100/0 100/2 100/0 100/0 100/0 100/3 100/9 100/6 100/3 100/1 100/3 100/1 100/2 100/4 100/13 100/0 100/1 100/4 100/6 100/1 100/0 100/4 100/6 100/2 100/2 100/4 100/6 100/8 100/0 100/4 100/0 100/1 100/1 100/0 100/14 100/5 100/0 100/0 100/3 100/0 100/1 100/0 100/8 100/5 100/1 100/0 100/8 100/11 100/3 100/1 100/3 100/7

So it must be something I am doing in the void loop code :frowning:

But placing it in my original code directly after the function call (as you suggest), I get a normal result again (although Arduino crashed due to the high volume of data)

void loop()
{
   // This ensures that you ESP is connected to your broker and reconnects it if needed
   if (!client.connected()) {
    reconnect();
    }
  client.loop();

  currentMillis = millis();  //syncs the currentMillis value to the current time
 
  offset = ((offsetRAW * (platformspeed/100)));   //Sets the offset value to a percentage of platform speed
 
  duration = ((durationRAW * (platformspeed/100)));  //Sets the duration value to a percentage of platform speed

  RandFactor = ESP8266TrueRandom.random(RandomRAW);  //Creates a random number between 1 and the random amount set via mqtt
 Serial.print(RandomRAW);
      Serial.print("/");
      Serial.print(RandFactor);
      Serial.print(" ");

  // Alternates between the two halves of the cycle and turns on rams 1 and 3.  Timing includes a random value
  if (currentMillis - FlipTime >= platformspeed + RandFactor)
  {
    { if (flipflop == HIGH)
    {
      {
      flipflop = LOW;
      Ram1OnTime = currentMillis;
      Ram1State = HIGH;
      }
    }
    else
    {
      {
      flipflop = HIGH;
      Ram3OnTime = currentMillis;
      Ram3State = HIGH;
      }
    }
    FlipTime = currentMillis;
    }
  }
 
  //Turn rams 2 and 4 on after the offset amount and off after the duration
 
  if ((currentMillis - Ram1OnTime >= offset) && (currentMillis - Ram1OnTime - offset <= duration))
  {
    { Ram2State = HIGH;}
  }
  else
  {
    { Ram2State = LOW;}
  }
 if ((currentMillis - Ram3OnTime >= offset) && (currentMillis - Ram3OnTime - offset <= duration))
    {
      {Ram4State = HIGH;}
    }
   else
  {
    { Ram4State = LOW;}
  }
 
 // Check to see if rams 1 and 3 have reached their duration and switch them off if they have
 
  if (currentMillis - Ram1OnTime >= duration)
  {
    {Ram1State = LOW; }
  }
 if (currentMillis - Ram3OnTime >= duration)
  {
    {Ram3State = LOW; } 
  } 
 
  // Write the values to the pins
 
    digitalWrite(ram1, Ram1State);
    digitalWrite(ram2, Ram2State);
    digitalWrite(ram3, Ram3State);
    digitalWrite(ram4, Ram4State);
}

Update #3

I seem to fixed the problem, by only calling the random() function at the exact point I need it, like this:

  if (currentMillis - FlipTime >= platformspeed)
  {
    { if (flipflop == HIGH)
    {
      {
      flipflop = LOW;
      Ram1OnTime = currentMillis;
      Ram1State = HIGH;
      RandFactor = ((platformspeed/100) * (ESP8266TrueRandom.random(1,RandomRAW)));
      Serial.print(RandFactor);
      Serial.print("/");
      Serial.print(RandomRAW);
      Serial.print(",  ");
      Ram1duration = (duration + RandFactor);
      }
    }
    else
    {
      {
      flipflop = HIGH;
      Ram3OnTime = currentMillis;
      Ram3State = HIGH;
      RandFactor = ((platformspeed/100) * (ESP8266TrueRandom.random(1,RandomRAW)));
      Serial.print(RandFactor);
      Serial.print("/");
      Serial.print(RandomRAW);
      Serial.print(",  ");
      Ram3duration = (duration + RandFactor);
      }
    }
    FlipTime = currentMillis;
    } 
  }

I'm not entirely sure I understand why this works when the previous code didn't - but I'm very grateful for the help, in the end it was indeed post #6 that pointed to the solution!

Thank you @jremington :slight_smile: