1

I am making a "Electronic Dice".

I have 7 LEDs to form the output, and this works fine.

My problem is getting a random result on the output.

int die_face = 1;

void loop()
{
    if (analogRead(BUTTON_PIN) == HIGH)
    {
        paintDieFace((die_face % 6) + 1);
    }

    die_face += 1;
}

The problem is, that roughly 95% of the time I get a 1.
Most of the time it's not a 1, it's a 2, or very rarely a 3.

I've tried paintDieFace() with millis()%6+1 too, but always get the same result. When it's painted with random(1,7); I get 1, 2 or 3 100% of the time.

Is this some sort of timing issue? I just don't get it.

Just to clarify - The paintDieFace() function is working perfectly, I'm logging the result to serial too.

  • This code is for a high-school student workshop, so it really needs to be kept simple. – Kingsley Aug 19 '14 at 22:02
  • I tested your code and I don't have this problem. – NobodyNada - Reinstate Monica Aug 19 '14 at 22:37
2

First of all, look at this line of code:

if (analogRead(BUTTON_PIN) == HIGH)

You probably want to use digitalRead(). Note that it is okay to use digitalRead() on an analog pin, just make sure to use the right pin number (A0, etc.)

From the Arduino Source:

#define HIGH 0x1
#define LOW  0x0

(Ignore the 0x, that is just specifying that it's hexadecimal. However, 0 and 1 are the same in any base)

So, really, this function is only activated when analogRead() returns 1, which could possibly cause a problem later if it doesn't check at the right time. Note: now is a good time to remind you about debouncing...

For your actual code, since you want it to be simple, I suggest you forget that code that you wrote. Arduino has a built in random library, which works unless you have some special need for a random number.

Your final code should look like something like this in the end:

void loop() {
    if (analogRead(BUTTON_PIN) == HIGH) {
        paintDieFace(random(1, 6);
    }
}

Note that you should also use this code here in your setup (make sure that analog pin is floating: nothing should be attached to it):

randomSeed(analogRead(A1));

It actually isn't random, just pseudo-random. That means it relies on an equation to generate what seem to be random numbers. Because there has to be a default value of where the equation starts (the seed), it will repeat the same numbers every time you run it.

If you're wondering how this works, know that the analog pin is affected by a ton of external factors when there is no other electric signal attached.

  • De...BOUNCING, not denouncing. 8) – TDHofstetter Aug 20 '14 at 1:21
  • 1
    for a HS project it doesn't matter, but in terms of security, randomSeed(analogRead(A1)); is terrible. random() is deterministic, and it's not hard to try all 1024 sequences. If you do that, run random() in the loop so that it's unpredictable how many times it's been called, and thus unpredictable in terms of the next result. randomSeed((analogRead(A1)+1)*millis()*micros()*(analogRead(A1)+1)) is better – dandavis Apr 7 '17 at 19:39
  • 1
    @dandavis two issues with that: a) its stronger for sure, but still incredibly weak. You shouldn't be using a pseudorandom generator if it needs to be secure end of discussion. B) since seeding is traditionally at the start of the program, millis and micros are going to be very similar each time, if not the exact same. Of course, seeding when the button was first pressed may overcome this partially, but there are ways to overcome this (hold down the button before plugging it in so the first iteration of the loop reads HIGH, advanced cameras and timers, etc.) – Anonymous Penguin Apr 7 '17 at 21:12
  • 1
    @AnonymousPenguin: good point about the timing. i'm used to 8266s, where you can put that after the wifi connect part, which takes 1-3 seconds... a prior delay(analogRead(A0)%250) adds cheap entropy from micros(). an empty wire connected only to A0 collects noise and widens the sample range considerably, might be worth noting in the answer. "security" was probably the wrong word; i'm just suggesting low-effort/cost ways to make a game (much) harder to cheat using an app... – dandavis Apr 7 '17 at 22:07
  • 1
    @dandavis very true, although I'm sure the teacher would be pleased if the student did the work to reverse engineer the random functions and build a separate program that calculated sequences with the random seed and matched a sequence to the appropriate seed. :) – Anonymous Penguin Apr 7 '17 at 22:12
0

I've had better success with this version.

I did not work out the problem with the first version. Although I did find the rear legs of the push-button were lighting up a LED - but only when pushed. As the result is only displayed when the button is not pushed, I don't see how it could make a difference.

I believe the modulus of 6 is a problem, perhaps this does really output more ones than anything else. Obviously looping the dice_number variable 1->6 removes the necessity for it.

#define LEDS_34_PIN 2  // Output pins used
#define LEDS_16_PIN 3
#define LEDS_25_PIN 4
#define LED_7_PIN   5

#define BUTTON_PIN  6  // Input


unsigned char dice_number  = 0;     // this is what will be shown on the die face

void setup()
{
    // Tell the Arduino we're writing to some pins
    pinMode(LEDS_34_PIN, OUTPUT);
    pinMode(LEDS_16_PIN, OUTPUT);
    pinMode(LEDS_25_PIN, OUTPUT);
    pinMode(LED_7_PIN,   OUTPUT);

    // ... and reading a button
    pinMode(BUTTON_PIN, INPUT);


    // Do a testing-dance first
    for (int i=1; i<=6; i++)
    {
        paintDieFace(i);
        delay(500);
    }
    paintDieFace(0);

}


// Show a dice-dot-style number
// based on LEDs laid out like
//    [1]     [4]
//    [2] [7] [5]
//    [3]     [6]
void paintDieFace(unsigned char number)
{
    // Turn off all LEDs
    digitalWrite(LEDS_34_PIN, LOW); 
    digitalWrite(LEDS_16_PIN, LOW); 
    digitalWrite(LEDS_25_PIN, LOW); 
    digitalWrite(LED_7_PIN,   LOW);

    if (number == 1 || number == 3 || number == 5 || number > 6)
    {
        digitalWrite(LED_7_PIN, HIGH);
    }
    if (number >= 2)
    {
        // LEDs 3 & 4 are on for all numbers greater-than 1
        digitalWrite(LEDS_34_PIN, HIGH); 
    }
    if (number >= 4)
    {
        // LEDs 1 & 6 are on for 4, 5 and 6
        digitalWrite(LEDS_16_PIN, HIGH); 
    }
    if (number >= 6)
    {
        // LEDs 2 & 5 are only on for 6 (or more)
        digitalWrite(LEDS_25_PIN, HIGH); 
    }
}


void loop()
{
    int button_pushed = digitalRead(BUTTON_PIN);    
    if (button_pushed == HIGH)
    {
        // The user has pushed the button, change the die reading 
        paintDieFace(dice_number);
        delay(20);  // flicker the LEDs if button held in
    }

    dice_number += 1;
    if (dice_number == 7)
        dice_number = 1;
}
  • Just as a sidenote. Remember that you need a pull-down resistor on the button pin, to get reliable data from digitalRead(BUTTON_PIN). – Gerben Aug 20 '14 at 16:27
  • @Gerben: it's cheaper/easier to just set the pin mode to INPUT_PULLUP and hook button to GND... – dandavis Apr 7 '17 at 19:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.