Random Function omitting certain numbers

The braille flashcards are just there to aid the player, and are not linked to the code in any way. It is just pure coincidence or luck that the player has a flashcard that matches the challenge. In the video it was pure chance that the challenge was “L” and I happened to have been dealt a flash card for “L”.

Later on, I will use the unused braille patterns as a Bonus; perhaps giving the player a reward for guessing correctly, a Mars bar, free go, or a date with the next door neighbour, anything really.

i'm running your code on my laptop, trying to understand "what" it's intended to do.

it's hard for me to understand the audio in the video, volume was low

looks like the code starts in gameMode 0:

  • in mode 0 it waits for button-6 to be pressed to start before advancing to mode 1
  • in mode 1 it randomly selects one of 30 characters and plays audio of the character that was selected
  • in mode 2 it simple plays "countdownTrack"
  • in mode 3 it monitors button presses for 12.5 seconds
  • in mode 4 it checks if the button presses match the pattern, playing a random success sound if the do or a random fail sound if the don't, going back to mode 0

is this correct?

looks like there's no way to change a value once a button is pressed. pressing a button could toggle the value

a single button could be used to start the test as well as indicate that the input is ready to be tested.

doesn't look like there's anyway to retry to current char, perhaps displaying the correct pattern after 3 fails

Yes, that looks correct. Only in line 202 the green button is set to zero and becomes a reset button, as shown in the video for the letter “a” that I deliberately got wrong, then used the green button to reset the LEDs so I select the correct option.

Line 209 is the function to check what buttons have been pressed.

Inside that function from line 381 is the code to reset the LEDs and Button States.

One thing to mention, is that in the video I have marked the buttons as per the braille keyboard, but in this code they are numbered from 0 to 5 - another reason why I want to drastically change the code in favour of shift registers.

I'm not too sure what shift registers would do. Don't you have enough pins for what you want to achieve on a UNO ?

you can change the inner representation as the 6 dots status could easily fit in a byte as the low significant bits and have a proper structure gathering all the information so that you can easily search for a braille code (or given a letter find its braille representation)

here is a little wokwi example with that structure and using the Toggle library for handling the buttons. each time you click one of the button the matching LED goes on and a search is run to see if the current pattern exists (and if so it's printed out)

may be that could give you some ideas.

Well that certainly works, I hadn’t considered the toggle function, that would definitely tidy up the input, and also do away with the reset button. Doing so, would mean that the visually impaired person would have to remember what buttons they had pressed I guess.

There’s a lot there in your code that I need to study, but I am still persistent with wanting to use shift registers, as there doesn’t need to be any visual output on the console (apart from the lit LEDs), and using them will make the lookup tables obsolete. We just need the final binary button input value (after the 12 sec countdown) to compare with the challenge given. For example: the challenge given “for” resides in audio track 00063.mp3 (don’t forget, that we send that same bit pattern to the audio module for playing the challenge initially). The successful final button(s) pressed input would need to be b00111111, which is the same binary value that would be sent to the shift register driving the LEDs. And as before, a random “success” track will be played. I’ll try and put together some code that I think might work.

A simple logic sequence might go something like this:

  1. Produce a random number. Say 63
  2. Send that number to the audio module for sounding. Say track number 000063.mp3
  3. Start countdown track. Say for 12 seconds.
  4. Allow keyboard input via shift register during countdown
  5. Send keyboard input value to shift register driving LEDs (as each key is pressed, the binary value produced is either "OR" or "AND" to update the previous bit value)
  6. Countdown ended
  7. Compare the random number with the value from the keyboard input shift register (might well be 63) to give either a match or not.
  8. Send appropriate command to audio module to play a random "fail" or "correct" accordingly.

| J-M-L Jackson
August 29 |

  • | - |

roblund:

another reason why I want to drastically change the code in favour of shift registers.

I'm not too sure what shift registers would do. Don't you have enough pins for what you want to achieve on a UNO ?

you can change the inner representation as the 6 dots status could easily fit in a byte as the low significant bits and have a proper structure gathering all the information so that you can easily search for a braille code (or given a letter find its braille representation)

here is a little wokwi example with that structure and using the Toggle library for handling the buttons. each time you click one of the button the matching LED goes on and a search is run to see if the current pattern exists (and if so it's printed out)

wokwi.com

brailleStuff - Wokwi ESP32, STM32, Arduino Simulator

Run IoT and embedded projects in your browser: ESP32, STM32, Arduino, Pi Pico, and more. No installation required!

may be that could give you some ideas.

i tried restructuring your code and partially testing it on my laptop

  • the start/done button is used to start the test as well as indicate when setting the cell pattern is complete
  • it uses a single byte value to represent the 6-bit braille cell
  • a table associates button pins with optional LED pins
  • button presses toggle bits in a single byte value representing the braille cell value as well as the corresponding LED
  • the braille cell value for the selected character and pattern set by the buttons is a simple comparision
  • the single byte braille cell value could be used to drive a shift register but i believe there are more than enough pins available
  • button and LED code is substantially reduced and those files eliminated
  • the ordering of functions is such that they are defined before use, hence loop() and setup() are at the bottom
void initDtmf ()
void playDTMF (uint8_t number, long duration) {
void chimeAll () {
void sendCommand ()
void playNext ()
void playTrack (int soundTrack)   //play a selected track
void playbackVolume (int vol)
void resetLEDs ()
chkButs () {
void loop ()
void setup ()

i believe these changes changes both simplifiy (remove redunancy), make the code code easier to read (once you grok it) and make it easier to maintain

// braille game
//    press button to start
//    set 6 cell LEDs with 6 buttons
//    press button when done or wait 12sec

#include <Arduino.h>
#include "SoftwareSerial.h"

SoftwareSerial Serial2 (9, 10);  // RX, TX  with voltage divider pin 10

// -----------------------------------------------------------------------------
// define button/led associations, 0 indicates no LED

enum { Off = HIGH, On = LOW };

struct ButLed  {
    byte PinBut;
    byte PinLed;

    byte butState;
}
butLeds [] = {
    { 2, A0 },
    { 3, A1 },
    { 4, A2 },

    { 5, A3 },
    { 6, A4 },
    { 7, A5 },

    { 8, 0  },
};
const int NbutLed = sizeof(butLeds)/sizeof(ButLed);
const int DoneBut = 6;

// -----------------------------------------------------------------------------
const unsigned long msecGameTime = 12500;
      unsigned long msecGameStart;;

int gameMode;
int brailleChar;

struct Braille {
    unsigned char   cell;
    char            c;
}
braille [] =  {
   { 0x01, 'a' },
   { 0x03, 'b' },
   { 0x09, 'c' },
   { 0x19, 'd' },
   { 0x11, 'e' },
   { 0x0B, 'f' },
   { 0x1B, 'g' },
   { 0x13, 'h' },
   { 0x0A, 'i' },
   { 0x1A, 'j' },
   { 0x05, 'k' },
   { 0x07, 'l' },
   { 0x0D, 'm' },
   { 0x1D, 'n' },
   { 0x15, 'o' },
   { 0x0F, 'p' },
   { 0x1F, 'q' },
   { 0x17, 'r' },
   { 0x0E, 's' },
   { 0x1E, 't' },
   { 0x25, 'u' },
   { 0x27, 'v' },
   { 0x3A, 'w' },
   { 0x2D, 'x' },
   { 0x3D, 'y' },
   { 0x35, 'z' },

   { 0x2F, '[' },
   { 0x3F, '\\' },
   { 0x37, ']' },
   { 0x2E, '^' },
};

const int Nbraille = sizeof(braille)/sizeof(Braille);

// -----------------------------------------------------------------------------
#include <Tone.h>

Tone freq1;
Tone freq2;

// Dial numbers 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 (To align with array 0)
const int DTMF_freq1[] = {
    1209, 1336, 1477, 1209, 1336, 1477, 1209, 1336, 1477, 1336 };
const int DTMF_freq2[] = {
    697,  697,  697,  770,  770,  770,  852,  852,  852,  941 };

// -------------------------------------
void initDtmf ()
{
    freq1.begin (13);  // 560 ohm resistor - resistor not used
    freq2.begin (12);  // 560 ohm resistor - resistor not used
}

// -------------------------------------
void playDTMF (uint8_t number, long duration) {
 // printf ("    %s: %d\n", __func__, number);

    freq1.play (DTMF_freq1[number], duration);
    freq2.play (DTMF_freq2[number], duration);
}

// -------------------------------------
void chimeAll () {
    printf ("  %s:\n", __func__);

    uint8_t phone_number[] = { 0, 1, 2, 3, 4, 5, };

    for (unsigned i = 0; i < sizeof (phone_number); i++) {
        playDTMF (phone_number[i], 500);
        delay (600);
    }
}

// -----------------------------------------------------------------------------

byte commandLength;    //length in bytes of command being sent
byte command[6];       //array that stores the bytes of the command
int16_t checkSum = 0;  //checksum of command

// -------------------------------------
void sendCommand ()
{
    printf ("    %s:", __func__);

    for (int q = 0; q < commandLength; q++) {
        Serial2.write (command[q]);
#if 1
        Serial.print (command[q], HEX);
#else
        printf (" %02X", command[q]);
#endif
    }
    printf ("\n");
}

// -------------------------------------
void playNext () 
{
    printf ("  %s:\n", __func__);

    command[0] = 0xAA;  //first byte says it's a command
    command[1] = 0x06;
    command[2] = 0x00;
    command[3] = 0xB0;
    commandLength = 4;
    sendCommand ();
}


// -------------------------------------
void playTrack (int soundTrack)   //play a selected track
{
    printf ("  %s: %d\n", __func__, soundTrack);

    //select track
    command[0] = 0xAA;  //first byte says it's a command
    command[1] = 0x07;
    command[2] = 0x02;
    command[3] = (soundTrack >> 8) & 0xFF;  //snh...track HIGH bit
    command[4] = soundTrack & 0xFF;         //SNL... track low bit
    checkSum = 0;
    for (int q = 0; q < 5; q++) {
        checkSum += command[q];
    }
    command[5] = (checkSum & 0xFF);  //SM check bit... low bit of the sum of all previous values
    commandLength = 6;
    sendCommand ();
}

// -------------------------------------
//sets the device volume...0 - 30
void playbackVolume (int vol)
{
    printf ("  %s:\n", __func__);

    if (vol > 30) {  //check within limits
        vol = 30;
    }
    command[0] = 0xAA;  //first byte says it's a command
    command[1] = 0x13;
    command[2] = 0x01;
    command[3] = vol;  //volume
    checkSum = 0;
    for (int q = 0; q < 4; q++) {
        checkSum += command[q];
    }
    command[4] = (checkSum & 0xFF);  //SM check bit... low bit of the sum of all previous values
    commandLength = 5;
    sendCommand ();
}
// -----------------------------------------------------------------------------
void resetLEDs ()
{
    printf ("  %s:\n", __func__);

    for (int n = 0; n < NbutLed; n++)  {
        if (0 != butLeds [n].PinLed)
            digitalWrite (butLeds [n].PinLed, Off);
    }
}

// -----------------------------------------------------------------------------
// button monitor routine that toggles LED state and sets cellPattern
const int NoBut = -1;
int  cellState;

int
chkButs () {
    printf (" %s:\n", __func__);

    for (int n = 0; n < NbutLed; n++)  {
        byte but = digitalRead (butLeds [n].PinBut);
        if (butLeds [n].butState != but)  {     // state change
            butLeds [n].butState  = but;
            delay (20);                         // debounce

            if (LOW == but)  {                  // pressed
                int dot    = 1 << n;
                cellState ^= dot;               // toggle dot

                // set led to current dot state
                digitalWrite (butLeds [n].PinLed, cellState & dot);

                printf ("   %s: %d pressed\n", __func__, n);
                return n;
            }
            else
                printf ("   %s: %d released\n", __func__, n);
        }
    }

    return NoBut;
}

// -----------------------------------------------------------------------------
void loop ()
{
    printf ("loop: gameMode %d\n", gameMode);

    unsigned long msec = millis ();

    switch (gameMode) {
    case 0:  // start
        resetLEDs ();
        if (DoneBut == chkButs ())
            gameMode = 1;
        break;

    case 1:  // choose random braille letter
        randomSeed (msec);
        brailleChar = random (30);
        printf ("    braille char %d\n", brailleChar);

        playTrack (brailleChar +1);
        msecGameStart = msec;
        gameMode = 3;
        break;

    case 3:
#define TrkCountdown 31
        playTrack (TrkCountdown);  // keeps repeating?

        // wait until done or timeout
        if (msec - msecGameStart >= msecGameTime || DoneBut == chkButs ())
            gameMode = 4;
        break;

    case 4:
        if (cellState == braille [brailleChar].cell)
            playTrack (random (32, 41));    // play random success sound
        else
            playTrack (random (42, 51));    // play random fail sound
        delay (8000);   // wait for sound to finish
        gameMode = 0;
        break;

    default:
        break;
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);
    Serial2.begin (9600);  // Start Software Serial

    initDtmf ();

    for (int n = 0; n < NbutLed; n++)  {
        // configure and capture each button state
        pinMode (butLeds [n].PinBut, INPUT_PULLUP);
        butLeds [n].butState = digitalRead (butLeds [n].PinBut);

        // check if valid LED
        if (0 == butLeds [n].PinLed)
            continue;

        // configure and show the each LED works
        pinMode (butLeds [n].PinLed, OUTPUT);
        digitalWrite (butLeds [n].PinLed, On);
        delay (500);
        digitalWrite (butLeds [n].PinLed, Off);
        delay (200);
    }
}

sure, I added that in just to debug? shift registers do work but requires more hardware and you'll need to maintain the current code anyway...

of course the idea would be to let the person play with the buttons (toggling between ON and OFF if he made a mistake) for 12 seconds and then comparing the code with the expected one (instead of checking the code each time you press on a button.)

Because nothing is blocking, that's an easy one to do.

I loaded up the above code, and it nearly works. All six LEDs light up at switch on, and stay on. (no LED sequence or MFDT at switch on). Pressing the green button makes a weird sound with no challenge. Pressing the button do toggle the LEDs but there is no MFDT. “Fail” sounds are heard, but all LEDs then remain lit. I’ll have a good look at the code to see if I can find the issues.

Also, I can concede that, after looking at the above code, that shift registers would not be suitable on this occasion.

The only gripe I still have, is that I am convinced that by numbering the audio track to the same number as the braille pattern would eliminate the need for the lookup table.

i didn't extensively test the code, not knowing if you would even bother to understand it. I believe the restructuing makes it easier to maintain, to debug, enhance and see new ways things can be done

reverse what Off/On are

isn't the above code doing that? remember they are off by 1.

but tables are needed for the braille cell value and i believe associating the buttons and LEDs

instead of executing this repeatedly each loop iteration, does the above need to be executed periodically, allowing the audio to complete before restarting?

Well yes, I only started with the Arduino at the beginning of last year and have learnt extensively from studying other people’s code, and that’s still true today. I really do appreciate the content of all 50+ responses on this Topic.

The countdown track should only be played once, in “gameMode 2”. It should still be playing during “gameMode 3” when the button input is expected, but not executing again until the next round of challenge.

Regarding the lookup table, the braille pattern is actually given initially by the random number function. The same number is then used to play the same numbered track and then to compare the button pattern input - this is where I’m confusing everyone including myself.

I think that the bit of code at line 236 (my code), where the “button states” comparison is executed is what I need to change. My original thought process was that I was hoping to use a shift register routine for the input buttons, which would have given me an 8 bit final value at the end of the countdown period, which could have been used for the comparison. But incorporating the MFDT would be difficult or not possible.

So I am guessing that I need to update the binary pattern of another variable, say “keyed_button_number”, which records every time a button is toggled, and use that for comparison. For example, if button no.1 is pressed then bit one of the variable is set; then if button no.4 is pressed then bit 4 of the variable is set, (not affecting bit 1 or the other bits), giving a value of 9. The random number generated would have been 9, and the challenge track no. 000009.mp3 would have played. This way there would be no need to remember the state of the buttons going forward.

The LEDs are only a side show, giving visual feedback of the actual buttons pressed, and are not directly reliant on the current braille pattern in use.

This is what chkButs() does

butLed[] is a table of button and LED pins. A for-loop scans the button pins moitoring for a state change and if a button is pressed.

when it detects a button press, it toggles the corresponding bit in cellState and sets/clears the LED correspondingly. That bit is determined by int dot = 1 << n; , shifting 1 by the value of n, the index of the button/led. dot is 1 when n is 0, 2 when n is 1, 4 for 2, 8 for 3, 16 or 0x10 for n is 4, 0x20 for 5.

cellState will have a 6-biy value depending on the button presses that can be compated to the cell field in braille [], for example, 0x07 for letter 'l'.

Ah, I see that now. Thanks very much for the explanation.

Recap: assuming I have now numbered all the audio track numbers to coincide with the braille patterns 1 to 63 (no zero).

From your code: Example for “L”, “brailleChar” == “cellState” == 000007.mp3

Random number 7 is generated, which plays track 0000007.mp3 (“L”)

Final button input stays in “cellState” with a value of 7

Comparison “brailleChar” with “cellState” is true, so play random “success” track.

so 'v', which is 0x27 == 39, is ...39.mp3, 'y', 0x3D is ...61.mp3 ??

why not a.mp3, b.mp3, c.mp3, ...

Yep, spot on…. I have an Excel Spreadsheet with all the old and new values. Can I post it here?

The DY Player (DY-HV8F) requires files to be numbered, and more importantly, saved to it in order - if you send 000002.mp3 to it before 000001.mp3 the DY Player will read 000002 as the first file in it’s memory.

https://www.digitaltown.co.uk/images/components/DYSV5W/DYSV5WCircuitButtonsLed.png

https://youtu.be/TxMwZyBVwEg?si=G7hLOKK0pu-AOsht

Sorry, inserted links wrong in previous attempts, this one should work

DY-HV8F

Data sheet

DY-HV8F Data Sheet

If it’s the DFPlayer then the name itself does not even matter - it’s indeed only the order under which it was copied over in the file system table of the SD card.