Help with random images on a nokia lcd

I'm trying to display a random image and display it on a lcd. The code is modified from a sketch which cycled through a few images, however I would like the first image to be followed by a random image from a selection of 4.
I have tried to combine two sketches, but the random image doesn't work.
Any guidance would be appreciated.

/**
 */

#include <Nokia_LCD.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

const uint8_t amount_of_images = 4;

const char* const image [amount_of_images] PROGMEM={
  "image1",
  "image2",
  "image3",
  "image4"
};

const unsigned char image0 [] PROGMEM = {
};

const unsigned char image1 [] PROGMEM = {

};

const unsigned char image2 [] PROGMEM = {
 
};

const unsigned char image3 [] PROGMEM = {
};


const unsigned char image4 [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0xF0, 0xE8, 0xF0, 0xB0, 0x38, 0x28, 0x20, 0x20, 0x20, 0x20,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xC0, 0x70, 0x18, 0x0C, 0x06, 0x02, 0x02, 0x06, 0x1C, 0x30, 0x20, 0x60, 0xC0, 0xC0, 0x40,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0xC0, 0xE0, 0x00, 0x80, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x1F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x3C, 0x94, 0xF2, 0x3B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81,
0x00, 0x01, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x1A, 0x0A, 0x0E, 0x7E, 0x0F, 0x01, 0x03, 0x0F, 0x1A,
0x32, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0xE0, 0x78, 0x2E, 0x23, 0xA1, 0xE0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0D, 0x19, 0x31, 0xE1, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xC0, 0xC0, 0xF8, 0xC0, 0x60, 0x78, 0x40, 0x40, 0x40,
0x00, 0x00, 0x00, 0x00, 0x80, 0xE4, 0x30, 0x98, 0x86, 0x83, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x03, 0xC3, 0x47, 0x4D,
0x79, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x1C, 0x06, 0x7F, 0x03, 0x06,
0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x63, 0x71, 0x39, 0x2E, 0x33, 0x10, 0x12,
0x10, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0xF8, 0x0C, 0xF4, 0x04, 0x04, 0x08, 0x08, 0x1A, 0x10,
0x10, 0x31, 0x23, 0x6C, 0x78, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x60, 0xC0, 0xF8, 0xC0, 0xF0, 0x90, 0x80,
0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1D, 0x32, 0x63, 0x42, 0x43, 0x62, 0x33,
0x19, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x31, 0x19, 0x0D, 0x3F,
0x01, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 
};

Nokia_LCD lcd(PB4 /* CLK */, PB3 /* DIN */, PB2 /* DC */, PB1 /* CE */, PB0 /* RST */);

volatile bool watchdogBarked = false;

enum WatchDogTimeout {
  WDT_16ms = 0,
  WDT_32ms,
  WDT_64ms,
  WDT_128ms,
  WDT_250ms,
  WDT_500ms,
  WDT_1sec,
  WDT_2sec,
  WDT_4sec,
  WDT_8sec
};

/**
  Watchdog interrupt routine to be triggered when watchdog times out.
*/
ISR(WDT_vect) {
  watchdogBarked = true;
}

void goToSleep() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  power_all_disable ();
  sleep_enable();
  sleep_cpu(); // Sleep here and wait for the interrupt
  sleep_disable();
  power_all_enable(); // power everything back on
}

/**
    Sets up watchdog to be triggered (once) after the specified time
    @param wdt  the watchdog timeout duration
*/
void triggerWatchDogIn(WatchDogTimeout wdt) {
  // Adopted from InsideGadgets (www.insidegadgets.com)
  byte timeoutVal = wdt & 7;
  if (wdt > 7) {
    timeoutVal |= (1 << 5);
  }
  timeoutVal |= (1 << WDCE);

  MCUSR &= ~(1 << WDRF);
  WDTCR |= (1 << WDCE) | (1 << WDE); // Start timed sequence
  WDTCR = timeoutVal;
  WDTCR |= _BV(WDIE);
  wdt_reset(); // Pat the dog
}

/**
   A utility method to derive a watchdog timeout's duration
   @param wdt the watchdog timeout
   @return    the amount of milliseconds corresponding to a watchdog timeout
*/
unsigned long getTimeoutDuration(WatchDogTimeout wdt) {
  return 1 << (wdt + 4);
}


/**
   Blocks and stays in deep sleep until the specified time has elapsed
   using the current watchdog timeout.
   @param sleepDuration     how long to stay in deep sleep in milliseconds
   @param timeoutInterval   the watchdog timeout interval
*/
void stayInDeepSleepFor(unsigned long sleepDuration, WatchDogTimeout timeoutInterval = WDT_16ms) {
  unsigned long sleepTime = 0;

  // Start by triggering the watchdog to wake us up every `timeoutInterval`
  triggerWatchDogIn(timeoutInterval);
  while (sleepTime <= sleepDuration) {
    // Sleep until an interrupt occurs (external, change or watchdog)
    goToSleep();
    // Verify we woke up because of the watchdog and not
    // a spurious wake up due to some other unrelated interrupt.
    if (watchdogBarked) {
      // Note down that we have processed the watchdog bark
      watchdogBarked = false;
      // Increase the time we have already slept
      sleepTime += getTimeoutDuration(timeoutInterval);
    }
  }
  wdt_disable(); // Disable watchdog so it stops barking
}

void setup() {
  // Initialize the screen
  lcd.begin();
  lcd.setCursor(0, 0);
}

void loop() {
  // put your main code here, to run repeatedly:
  // Draw the intro pic on your screen
  lcd.draw(image0, 504);
  stayInDeepSleepFor(2000);
  // Draw the random image on your screen
  lcd.draw(random (image), 504);
  stayInDeepSleepFor(10000);
}

The random() function gives a random number.

random(max)
random(min, max)

Parameters

  • min - lower bound of the random value, inclusive (optional)
  • max - upper bound of the random value, exclusive

Returns
A random number between min and max-1 (long) .

Here

lcd.draw(random (image), 504);

random (image) would give a random number between 0 and 'image-1' which is not defined, as far as I can see. What you want is random(amount_of_images)

byte randomImageNumber = random(amount_of_images);
switch (randomImageNumber) {
  case 0:
    lcd.draw(image0, 504);
    break;
  case 1:
    lcd.draw(image1, 504);
    break;
  case 2:
    lcd.draw(image2, 504);
    break;
  case 2:
    lcd.draw(image3, 504);
    break;
}

I think it may also be done using pointers such as

unsigned long * imageAddress[4] = {&image0,&image1,&image2,&image3};
lcd.draw(imageAddress[random(amount_of_images)],504);

But this should be confirmed...

I have used the random amount_of_images as you suggested and it works, but the images are not random. They are out of sequence, but they are in the same "out of sequence" order every time.
I'm not sure if that makes sense.

Take a look at the reference for random and look at the section on randomSeed.

I took a look at the randomSeed page and it looks like that the thing i need. I'm at a loss, as to where to put it in the sketch. At the moment i have placed it here

void loop() {
  // put your main code here, to run repeatedly:
  // Draw the intro pic on your screen
  lcd.draw(image, 504);
  stayInDeepSleepFor(2000);
  //Draw a random image 
  byte randomImageNumber = (randomSeed, amount_of_images);
switch (randomImageNumber) {
  case 0:
    lcd.draw(image0, 504);
    stayInDeepSleepFor(10000);
  case 1:
    lcd.draw(image1, 504);
    stayInDeepSleepFor(10000);
  case 2:
    lcd.draw(image2, 504);
    stayInDeepSleepFor(10000);
  case 3:
    lcd.draw(image3, 504);
    stayInDeepSleepFor(10000);
}
}

The sketch compiles, which is an unusual achievement for me, but now I just see the first image and no random ones.
The full code is bellow, I removed the images as they were too big for the forum to handle.

#include <Nokia_LCD.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

const uint8_t amount_of_images = 4;

const char* const image [amount_of_images] PROGMEM={
  "image0",
  "image1",
  "image2",
  "image3"
};

const unsigned char image [] PROGMEM = { 
};
const unsigned char image0 [] PROGMEM = {
};

const unsigned char image1 [] PROGMEM = {
};

const unsigned char image2 [] PROGMEM = {

};

const unsigned char image3 [] PROGMEM = {
 
};

Nokia_LCD lcd(PB4 /* CLK */, PB3 /* DIN */, PB2 /* DC */, PB1 /* CE */, PB0 /* RST */);

volatile bool watchdogBarked = false;

enum WatchDogTimeout {
  WDT_16ms = 0,
  WDT_32ms,
  WDT_64ms,
  WDT_128ms,
  WDT_250ms,
  WDT_500ms,
  WDT_1sec,
  WDT_2sec,
  WDT_4sec,
  WDT_8sec
};

/**
  Watchdog interrupt routine to be triggered when watchdog times out.
*/
ISR(WDT_vect) {
  watchdogBarked = true;
}

void goToSleep() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  power_all_disable ();
  sleep_enable();
  sleep_cpu(); // Sleep here and wait for the interrupt
  sleep_disable();
  power_all_enable(); // power everything back on
}

/**
    Sets up watchdog to be triggered (once) after the specified time
    @param wdt  the watchdog timeout duration
*/
void triggerWatchDogIn(WatchDogTimeout wdt) {
  // Adopted from InsideGadgets (www.insidegadgets.com)
  byte timeoutVal = wdt & 7;
  if (wdt > 7) {
    timeoutVal |= (1 << 5);
  }
  timeoutVal |= (1 << WDCE);

  MCUSR &= ~(1 << WDRF);
  WDTCR |= (1 << WDCE) | (1 << WDE); // Start timed sequence
  WDTCR = timeoutVal;
  WDTCR |= _BV(WDIE);
  wdt_reset(); // Pat the dog
}

/**
   A utility method to derive a watchdog timeout's duration
   @param wdt the watchdog timeout
   @return    the amount of milliseconds corresponding to a watchdog timeout
*/
unsigned long getTimeoutDuration(WatchDogTimeout wdt) {
  return 1 << (wdt + 4);
}


/**
   Blocks and stays in deep sleep until the specified time has elapsed
   using the current watchdog timeout.
   @param sleepDuration     how long to stay in deep sleep in milliseconds
   @param timeoutInterval   the watchdog timeout interval
*/
void stayInDeepSleepFor(unsigned long sleepDuration, WatchDogTimeout timeoutInterval = WDT_16ms) {
  unsigned long sleepTime = 0;

  // Start by triggering the watchdog to wake us up every `timeoutInterval`
  triggerWatchDogIn(timeoutInterval);
  while (sleepTime <= sleepDuration) {
    // Sleep until an interrupt occurs (external, change or watchdog)
    goToSleep();
    // Verify we woke up because of the watchdog and not
    // a spurious wake up due to some other unrelated interrupt.
    if (watchdogBarked) {
      // Note down that we have processed the watchdog bark
      watchdogBarked = false;
      // Increase the time we have already slept
      sleepTime += getTimeoutDuration(timeoutInterval);
    }
  }
  wdt_disable(); // Disable watchdog so it stops barking
}

void setup() {
  // Initialize the screen
  lcd.begin();
  lcd.setCursor(0, 0);
}

void loop() {
  // put your main code here, to run repeatedly:
  // Draw the intro pic on your screen
  lcd.draw(image, 504);
  stayInDeepSleepFor(2000);
  //Draw a random image 
  byte randomImageNumber = (randomSeed, amount_of_images);
switch (randomImageNumber) {
  case 0:
    lcd.draw(image0, 504);
    stayInDeepSleepFor(10000);
  case 1:
    lcd.draw(image1, 504);
    stayInDeepSleepFor(10000);
  case 2:
    lcd.draw(image2, 504);
    stayInDeepSleepFor(10000);
  case 3:
    lcd.draw(image3, 504);
    stayInDeepSleepFor(10000);
}
}
  byte randomImageNumber = (randomSeed, amount_of_images);

Not like that

Did you look at the example on randomSeed()

Just do as in the example: put this in the setup

  randomSeed(analogRead(0));

Thank you to everyone who has helped me so far. I really could not have accomplished a working sketch without you.

I'm sorry, to pester people once again. The sketch works... to a point.
I get a randomness that is definitely more random that previously, but it only randomizes about 3 images, not the full 20.
Once again I have included the sketch without the images as they are too large of the forum.

/**
 */
#include <Nokia_LCD.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

const uint8_t amount_of_images = 20;

const char* const image [amount_of_images] PROGMEM={
  "image0",
  "image1",
  "image2",
  "image3",
  "image4",
  "image5",
  "image6",
  "image7",
  "image8",
  "image9",
  "image10",
  "image11",
  "image12",
  "image13",
  "image14",
  "image15",
  "image16",
  "image17",
  "image18",
  "image19"
};
const unsigned char intro [] PROGMEM = {
};
const unsigned char image0 [] PROGMEM = {
};
const unsigned char image1 [] PROGMEM = {
};
const unsigned char image2 [] PROGMEM = {
};
const unsigned char image3 [] PROGMEM = {
};
const unsigned char image4 [] PROGMEM = {
};
const unsigned char image5 [] PROGMEM = { 
};
const unsigned char image6 [] PROGMEM = {
};
const unsigned char image7 [] PROGMEM = { 
};
const unsigned char image8 [] PROGMEM = {
};
const unsigned char image9 [] PROGMEM = {  
};
const unsigned char image10 [] PROGMEM = {
};
const unsigned char image11 [] PROGMEM = {
};
const unsigned char image12 [] PROGMEM = {
};
const unsigned char image13 [] PROGMEM = {
};
const unsigned char image14 [] PROGMEM = {
};
const unsigned char image15 [] PROGMEM = {
};
const unsigned char image16 [] PROGMEM = {
};
const unsigned char image17 [] PROGMEM = {
};
const unsigned char image18 [] PROGMEM = {
};
const unsigned char image19 [] PROGMEM = { 
};
Nokia_LCD lcd(PB4 /* CLK */, PB3 /* DIN */, PB2 /* DC */, PB1 /* CE */, PB0 /* RST */);

volatile bool watchdogBarked = false;

enum WatchDogTimeout {
  WDT_16ms = 0,
  WDT_32ms,
  WDT_64ms,
  WDT_128ms,
  WDT_250ms,
  WDT_500ms,
  WDT_1sec,
  WDT_2sec,
  WDT_4sec,
  WDT_8sec
};

/**
  Watchdog interrupt routine to be triggered when watchdog times out.
*/
ISR(WDT_vect) {
  watchdogBarked = true;
}

void goToSleep() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  power_all_disable ();
  sleep_enable();
  sleep_cpu(); // Sleep here and wait for the interrupt
  sleep_disable();
  power_all_enable(); // power everything back on
}

/**
    Sets up watchdog to be triggered (once) after the specified time
    @param wdt  the watchdog timeout duration
*/
void triggerWatchDogIn(WatchDogTimeout wdt) {
  // Adopted from InsideGadgets (www.insidegadgets.com)
  byte timeoutVal = wdt & 7;
  if (wdt > 7) {
    timeoutVal |= (1 << 5);
  }
  timeoutVal |= (1 << WDCE);

  MCUSR &= ~(1 << WDRF);
  WDTCR |= (1 << WDCE) | (1 << WDE); // Start timed sequence
  WDTCR = timeoutVal;
  WDTCR |= _BV(WDIE);
  wdt_reset(); // Pat the dog
}

/**
   A utility method to derive a watchdog timeout's duration
   @param wdt the watchdog timeout
   @return    the amount of milliseconds corresponding to a watchdog timeout
*/
unsigned long getTimeoutDuration(WatchDogTimeout wdt) {
  return 1 << (wdt + 4);
}


/**
   Blocks and stays in deep sleep until the specified time has elapsed
   using the current watchdog timeout.
   @param sleepDuration     how long to stay in deep sleep in milliseconds
   @param timeoutInterval   the watchdog timeout interval
*/
void stayInDeepSleepFor(unsigned long sleepDuration, WatchDogTimeout timeoutInterval = WDT_16ms) {
  unsigned long sleepTime = 0;

  // Start by triggering the watchdog to wake us up every `timeoutInterval`
  triggerWatchDogIn(timeoutInterval);
  while (sleepTime <= sleepDuration) {
    // Sleep until an interrupt occurs (external, change or watchdog)
    goToSleep();
    // Verify we woke up because of the watchdog and not
    // a spurious wake up due to some other unrelated interrupt.
    if (watchdogBarked) {
      // Note down that we have processed the watchdog bark
      watchdogBarked = false;
      // Increase the time we have already slept
      sleepTime += getTimeoutDuration(timeoutInterval);
    }
  }
  wdt_disable(); // Disable watchdog so it stops barking
}

void setup() {
  // Initialize the screen
  lcd.begin();
  lcd.setCursor(0, 0);
  randomSeed(analogRead(0));
}

void loop() {
  // put your main code here, to run repeatedly:
  // Draw the intro pic on your screen
  lcd.draw(ball, 504);
  stayInDeepSleepFor(2000);
  //Draw a random image 
  byte randomImageNumber = random(amount_of_images);
switch (randomImageNumber) {
  case 0:
    lcd.draw(image0, 504);
    stayInDeepSleepFor(10000);
  case 1:
    lcd.draw(image1, 504);
    stayInDeepSleepFor(10000);
  case 2:
    lcd.draw(image2, 504);
    stayInDeepSleepFor(10000);
  case 3:
    lcd.draw(image3, 504);
    stayInDeepSleepFor(10000);
}
}

I am more than sure it is down to my code that i can only get a random 3 images, any guidance is appreciated.

i can only get a random 3 images,

How many did you expect ?
You only have 4 cases in switch/case

UKHeliBob:
How many did you expect ?
You only have 4 cases in switch/case

Thank you for pointing out my oversight, i'm learning... slowly.

I have another problem. Is there a way to have random seed when I have no spare analog pins?
I'm using an attiny85 for this project and it takes up all the pins.

Could you have the user press a button to start the display ?

If so then derive the value used in randomSeed() from millis() or micros() at the time the button was pressed

I intended the button to feed the power, I may need to think about changing this.

The reset pin is free... can this be used for random seed?

I intended the button to feed the power,

Which button ?

The reset pin is free... can this be used for random seed?

No, not unless you undertake surgery on the board and lose the ability to hardware reset the Arduino

I have a tactile switch that acts as a power button, it completes the circuit.

If all the pins are used is there a way to use eeprom as a random generator?

is there a way to use eeprom as a random generator?

No

Have you got any analogue pins available ?

Somehow i managed to get the randomseed to work. I'm checking to see if the results are random or just wishful thinking.
If the code works I will post the sketch later.

Magic 8 Ball Lanyard Fortune Teller Magic 8 Ball Lanyard Fortune Teller - Album on Imgur