Electronic dice application for the mBuino platform. Notes: The mBuino starts in Demo mode. In Demo mode the LEDs are lighted, showing sweeps and demo rolls. Connect a button or tilt-switch between P0.4 and GND. No soldering required! When the switch is triggered mBuino goes into Roll mode. In Roll mode mBuino starts with rapid flickering LEDs. Each subsequent switch trigger will perform a roll of the dice, of which the result is shown for ten seconds unless the switch is triggered for another roll. To preserve power, Power down mode is entered after inactivity in Demo mode or Roll mode. Press any button to revive.

Dependencies:   mbed

Fork of mBuino_Dice by Valentin Ivanov

mBuino_Dice

The hardware: No soldering required

As you can see in the picture gallery below, this is a very simple mBuino project, requiring only one extra component: a switch such as a push button or a tilt switch.

Push button

The push-button I used fits neatly between P0.4 and GND. By just bending the legs flat to the PCB, the button attached sturdy enough to make a usable connection.

Tilt switch

The tilt switch version is much more fun. No pushing, just a shake to roll the dice! In the tilt switch version two short wires were used to connect the switch. The wires were twisted around the legs of the tilt switch without any soldering! By adjusting the horizontal level of the switch you can make the dice react more sensitively to small movement.

For a production version, I would of course make it all a bit more durable by soldering the connections and using hot glue to keep everything in place. But going that route, I would also want to make a nicely fitting box, perhaps use other LEDs in a traditional dice-eye position and add an on-off switch...

main.cpp

Committer:
maxint
Date:
2014-09-16
Revision:
6:e52dbebd7465
Parent:
5:e2f89a6f4d6c

File content as of revision 6:e52dbebd7465:

/*
** mBuino_Dice
** 
** This electronic dice application allows the user to throw an electronic dice by the press of a button
** Setup requirements: connect any type of switch between P0.4 and GND.
** On mBuino P0.4 is close to GND and the two pitch distance is exactly the same as a mini-switch! 
**
** 0.1.140916 Using the Outrageous Circuits mBuino platform instead of LPC11U24 (available now)
**            Renamed LED ports to newly available mBuino LED constants
**            Earlier power down after dice roll, showing by flashes slowing down
** 0.1.140915 Energy use in power down mode down to 3 uA, credits: Andy A
** 0.1.140910 Power down after one iteration of demo or ten seconds of wating, credits: Andy A
** 0.1.140908 Added deep sleep mode after one iteration of demo or ten seconds of wating, credits: Andy A and Erik Olieman
**            Added explanatory comments and incorporated suggestion by Erik Olieman
**            Improved randomness as per suggestion of Andy A
** 0.1.140901 First version mad for mBuino on mbed.org by maxint on 1-sep-2014
**
** Feel free to use this code anyway you want, but please acknowledge my work by mentioning maxint as creator.
**
*/


#include "mbed.h"

DigitalOut LED[] = {(LED1), (LED2), (LED3), (LED4), (LED5), (LED6), (LED7)};// declare 7 LEDs

InterruptIn ActionButton(P0_4);  // declare the input for the button (or for other single switch, such as movement switch). On mBuino P0.4 is close to GND
AnalogIn RandomIn(P0_14); // use the random noise on this analog input to seed the random generator

// Define the LED pattern of each dice number
// Can be made more compact by using binary values instead of a nested array.
bool DicePattern[6][7]=
{
    {0, 0, 0, 1, 0, 0, 0 },
    {0, 1, 0, 0, 0, 1, 0 },
    {0, 1, 0, 1, 0, 1, 0 },
    {1, 0, 1, 0, 1, 0, 1 },
    {1, 0, 1, 1, 1, 0, 1 },
    {1, 1, 1, 0, 1, 1, 1 }
};

/*
// TODO: Suggestion by Erik Olieman: alternatively BusOut can be used to combine all used pins in one bus that can be set using a single write
BusOut LEDS(P0_7, P0_8, P0_2, P0_20, P1_19, P0_17, P0_23);
char byteDice[6]=
{
0x08,   // 0B00001000
0x22,   // 0B00100010
0x2A,   // 0B00101010
0x55,   // 0B01010101
0x5D,   // 0B01011101
0x77    // 0B01110111
};
*/

Timer tWait;    // time used for tickcounts

DigitalIn progMode(P0_3);       // need to disable pull-down for minimal power deep sleep
void myPowerDown(uint8_t uMode=0x02)
{   // Go into power-down mode (0x2) to safe most power. Use onboard or off-board interrupt triggered button to revive
    // See https://mbed.org/forum/electronics/topic/5138/
    // Alternative: Go into deep sleep mode (0x1) to safe less power. Is quicker than power-down. Also revived using onboard button or interrupt.
    // See http://mbed.org/forum/repo-52552-mBuino_low_power_led_flasher-community/topic/5130/
    // Power usage (CR3032 battery life):
    //   Running 13mA (1.5 day) 
    //   Sleep 7mA (3 days)
    //   Deep-sleep 480uA (43 days)
    //   Power-down 120uA (170 days)
    //   Power-down with fix 3uA (years)
    progMode.mode(PullNone);    // fix the 120uA. see http://mbed.org/forum/electronics/topic/5138/?page=1#comment-25338
    
    LPC_PMU->PCON = uMode;     // 0x2 = power down, 0x1 = deep sleep
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
    LPC_SYSCON->PDAWAKECFG &= 0xFFFFF800;
    __WFI();
}

void SwitchAllLeds(bool fOn)
{   // switch all leds on or off
    // Suggestion by Erik Olieman: alternatively BusOut can be used to combine all used pins in one bus that can be set using a single write
    for(int x = 0; x < 7; x++)
    {
        LED[x] = fOn?1:0;   // turn on or off
    }
}

void BlinkAllLeds(float flDelay=0.2)
{   // blink all leds
    SwitchAllLeds(true);
    wait(flDelay);
    SwitchAllLeds(false);
    wait(flDelay);
}

void BlinkOneLed(int nLed=0, float flDelayOn=0.2, float flDelayOff=0.2)
{   // blink just one led once, for the specified time
    LED[nLed] = 1; // turn on
    wait(flDelayOn); // delay

    LED[nLed] = 0; // turn off
    wait(flDelayOff); // delay
}

void SweepSingleLed(bool fLeftToRight=true, float flDelay=0.2)
{   // sweep a single led from left to rigth or vise-versa
    for(int n=0; n<7; n++)
        BlinkOneLed(fLeftToRight?n:6-n, flDelay, 0);
}

void SweepAllLeds(bool fLeftToRight=true, float flDelay=0.1)
{   // light all leds up from left to rigth or vise-versa and then switch them of from reverse direction
    // leds on, left to right
    for(int n=0; n<7; n++)
    {
        LED[fLeftToRight?n:6-n] = 1; // turn on
        wait(flDelay); // delay
    }
    // leds off, right to left
    for(int n=0; n<7; n++)
    {
        LED[!fLeftToRight?n:6-n] = 0; // turn off
        wait(flDelay); // delay
    }
}

void SwitchDiceLed(int nNumber, bool fOn=true)
{   // switch the leds of a particular dice-number on or off
    if(nNumber<1) nNumber=1;
    if(nNumber>6) nNumber=6;
    
    for(int n=0; n<7; n++)
        if(DicePattern[nNumber-1][n])
            LED[n]=fOn;
}

void BlinkDiceLed(int nNumber, float flDelayOn=0.6, float flDelayOff=0.1)
{   // blink a particular dice value
    SwitchDiceLed(nNumber, true);
    wait(flDelayOn);
    SwitchDiceLed(nNumber, false);
    wait(flDelayOff);
}

int RollDice(int nRolls=10, int nBlinks=2, float flDelayRoll=0.15)
{   // roll the dice and show the outcome value
    int nDiceValue;

    // improve randomness: seed the random generator with the background noise of an analog input combined with passed time at user triggered moment
    srand(RandomIn.read_u16()+tWait.read_us());
    
    // first roll the dice
    for(int n=0; n<nRolls; n++)
    {
        nDiceValue=rand()%6+1;
        BlinkDiceLed(nDiceValue, flDelayRoll, 0);
    }
    
    // then show the result
    nDiceValue=rand()%6+1;
    for(int n=0; n<nBlinks; n++)
        BlinkDiceLed(nDiceValue, flDelayRoll, 0);
    SwitchDiceLed(nDiceValue, true);        // return while keeping dice led's on
    
    return(nDiceValue);
}

bool fButtonPressed=false;      // if the button is pressed, mBuino will switch from demo mode to play mode. 
bool fDemoDone=false;

void interruptButtonPressed()
{
    fButtonPressed=true;
}

void setup(void)
{   // perform initialisations
    srand(RandomIn.read_u16());     // seed the random generator with the background noise of an analog input
    
    ActionButton.fall(interruptButtonPressed); // using the fall requires the button to be unpressed before the interrupt is triggered again
    tWait.start();      // start the timer
}

void loop(void)
{   // run forever
    if(!fDemoDone && !fButtonPressed)
    {   // Run an entertaining demo show until button is pressed
        // For all parts of the show we check to see if the actionbutton was pressed to stop the show asap
        if(!fButtonPressed)
            SweepAllLeds(true);
        if(!fButtonPressed)
            SweepAllLeds(false);
        if(!fButtonPressed)
            SweepAllLeds(true, 0.05);
        if(!fButtonPressed)
            SweepAllLeds(false, 0.05);
        
        // show the dice numbers from one to six
        for(int n=1; n<=6; n++)
            if(!fButtonPressed)
                BlinkDiceLed(n);

        if(!fButtonPressed)
            SweepSingleLed(true, 0.2);
        if(!fButtonPressed)
            SweepSingleLed(false, 0.1);
        
        // show three random dice throws
        for(int n=0; n<3; n++)
        {
            if(!fButtonPressed)
            {
                RollDice();
                wait(1);
                SwitchAllLeds(false);
            }
        }

        // blink all leds as the same time at increasing and decreasing speeds
        for(float flDelay=0.3; flDelay>0; flDelay-=0.05)
            if(!fButtonPressed)
                BlinkAllLeds(flDelay);
        for(float flDelay=0.05; flDelay<0.4; flDelay+=0.05)
            if(!fButtonPressed)
                BlinkAllLeds(flDelay);
        
        if(!fButtonPressed)
            wait(1);
            
        if(!fButtonPressed)
            myPowerDown();   // enter dpower-down mode after one demo iteration
    }
    else
    {   // demo mode is ended, roll the dice upon each button press
        fDemoDone=true;
        fButtonPressed=false;
        
        unsigned uTickStop=tWait.read_ms()+10000;
        float ftDelay=0.01;
        while(!fButtonPressed)
        {   // flash the LEDS to show we wait for a roll call
            SweepAllLeds(true, ftDelay);
            SweepAllLeds(false, ftDelay);

            if(!fButtonPressed && tWait.read_ms()>uTickStop)
                myPowerDown();   // enter power-down mode when waited for more than 10 seconds without button pressed
            else
                ftDelay=(10-((float)uTickStop-tWait.read_ms())/1000)/100;  // make the fast flashes slow down while closer to power down mode
        }
        fButtonPressed=false;
        
        // roll the dice and show the outcome while waiting max. 10 seconds
        int nDiceValue=RollDice();
        wait(1);
        fButtonPressed=false;
        
        uTickStop=tWait.read_ms()+10000;
        ftDelay=0.01;
        while(!fButtonPressed && tWait.read_ms()<uTickStop)
        {
            BlinkDiceLed(nDiceValue, 0.2, ftDelay);  // to indicate waiting for a new roll, flash the thrown dice value...
            ftDelay=(10-((float)uTickStop-tWait.read_ms())/1000)/100;  // make the fast flashes slow down while closer to power down mode
        }
        SwitchAllLeds(false);

        if(!fButtonPressed)
            myPowerDown();   // enter power-down mode when waited for more than 10 seconds without button pressed
    }
}

int main()
{   // implement Arduino-style setup() and loop()
    setup();
    while(true)
        loop();
}