Friday, December 14, 2018

Simon part 2

For an index to all my stories click this text.

This story shows how I finished my version of the Simon Game which I described in the story you can re-read here: https://lucstechblog.blogspot.com/2018/12/simon.html

Now the game setup was finished, meaning hardware and software worked without any bugs, I started thinking about a casing for the game. I did not want to leave it as a bread-board prototype as my girlfriend liked the game and wanted to play it more often.

First step was to put all the hardware on stripboard. Well that was easy as the schematics are not complicated. Fritzing was used to document all the parts.You can find Fritzing here: http://fritzing.org/home/
I used two pieces of stripboard.




The first was used to put the buttons on.
Make sure you cut the strips below the buttos so you will have independend contacts and no short-circuit.

The second piece of stripboard was used to put the Attiny85 on as well as the resistor and capacitor for the neopixels.




Here also make sure you cut the strips below the Attiny 85 to prevent short-circuit.
Also be aware that you are looking at the top of the stripboard, being the component side. The actual strips are on the bottom side.

The neopixels were just soldered as a string with some wires between them like I showed in the Neopixel intro which you can re-read here: http://lucstechblog.blogspot.com/2015/10/neopixels-ws2812-intro.html

When the hardware was finished I could start drawing my casing. I am using Tinkercad for this . Tinkercad is outstanding for simple designs. And as I am no industrial designer my designs generally are simple. You can find Tinkercad here: https://www.tinkercad.com/#/


 

I started with a casing for the batteries. I power my Simon game from 3 AA batteries as they will last a long time. The Attiny85 does not consume much and the Neopixels are never lit all at the same time. The box is printed and the contacts are made from bended paperclips.




Next a simple box as casing that would fit the batteries and the board with the Attiny85.



In real world it looks like the picture above.



And last the lid for the box. I added an on-off switch in the power line. I just had to do some minor filing before all fitted.



And here is the end-result.

The STL files (to print your own casing) can be found on github with this link:

https://github.com/Lucvolders/Simon

Simon version 2 ???

By popular demand (meaning pressure by girlfriend) I am thinking about building version 2. This will have a speaker that will play a tone when a color is shown (or the button is pressed) and a display (read Oled .96 inch) for keeping the score. But please do not hold your breath while waiting for it.

Besides that it will be more expensive to build due to the screen and the use of a bigger microcontroller as we will need more I/O pins.



This was the last post for this year. I whish you all a happy christmass and hope to welcome you all again in good health in the new year.

Till next time
and don't waste all your time playing ;)

Luc Volders

Friday, December 7, 2018

Simon

For an index off all my stories click this text

Simon is an electronic memory game invented by Ralph H. Baer and Howard J. Morrison and was develloped and sold by Studio 54 in 1978. It took till 1980 before it was available in Europe. The original Simon is as far as I know not sold anymore but there are many derivates available.

Simon as a game is still very popular.

How does it work.




The layout (mostly in a circle) shows 4 colours. A random color lights up and you press the button with the same color. Next step is that the same color lights up followed by another random color. You press the buttons in the same sequence of the colors shown etc etc etc. The first steps are easy to remember but it gets more and more difficult as the sequence grows. It is very addictive.

You can find a version for windows here: http://www.memozor.com/other-free-memory-games-online/simon-games/simon-game
And you can find many variations for Android here: https://play.google.com/store/apps/collection/search_results_cluster_apps?clp=ggEHCgVzaW1vbg%3D%3D:S:ANO1ljIRByI

I have seen several versions of Simon on the internet so it is not like this has never done before. But I wanted to make my own version: hardware and software. And most of all it is a fun project for a rainy afternoon. Considering the total cost it could be a nice present for under the Christmas tree. And indeed you still have time to build one for Christmas.

Let's buid a simon game.

Most versions I saw on the internet are build with an Arduino Uno and use 4 pushbuttons and 4 leds. Therefore they need 8 I/O pins.
I am building my version with 4 pushbuttons and a string of neopixels. Therefore I only need 5 I/O pins.

Using my multiple buttons on I/O pins trick I could even reduce the I/O pins to 3 for the pushbuttons and 1 for the Neopixels. I will publish that trick in an upcoming story on this weblog. So keep on coming back.

For a detailed introduction on Neopixels read this story:
http://lucstechblog.blogspot.com/2015/10/neopixels-ws2812-intro.html

Hardware




The first version I build used the ESP8266 as a microcontroller programmed in Arduino.




The breadboard layout is simple. Just 4 pushbuttons connected to 4 I/O pins (D3,D5,D6 and D7) and a string of 4 neopixels connected to D8.
As you can see I used a separate power supply (USB wall socket) for the neopixels and for the ESP. In the end it proved that this was not necessary as the Wemos D1 supplies enough current through the 5Volt output (pin 5V) to drive the 4 neopixels.




Therefore this complete setup can be powered from a powerbank as the photo shows.

Software.

The software is in Arduino code and therefore can easily be ported between Arduino, Attiny85 and ESP8266 or ESP32 models. Choose whatever suits you best. The version presented here is the ESP8266 (Wemos D1 Mini) version. So adjust the pin numbers for your preferred microcontroller.


/* Simon Says by Luc Volders
 *  ESP8266 version
 *  1 = green  0,255,0
 *  2 = red 255,0,0
 *  3 = blue 0,0,255
 *  4 = yellow 255,255,0
*/


#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN    D8
#define PIXEL_COUNT 7

int i = 0;
int j = 0;
int k = 0;
int found = 0;
int mustbe = 0;
int num = 0;
int playnum = 0;
int startprog = 1;
int turns = 0;
int turnarray[100];

Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

void setup()
{
  randomSeed(analogRead(0));
  delay(10);
  pinMode(D3, INPUT_PULLUP); // geen 
  pinMode(D5, INPUT_PULLUP); // red
  pinMode(D6, INPUT_PULLUP); // blue
  pinMode(D7, INPUT_PULLUP); // yellow
  strip.begin();
  strip.show();
}

void loop()
{
  start();
}

void start()
{
  if (digitalRead(D3) == LOW or digitalRead(D5) == LOW or digitalRead(D6) == LOW or digitalRead(D7) == LOW)
  {
    strip.setPixelColor(0, 0, 255, 0);
    strip.setPixelColor(1, 0, 255, 0);
    strip.setPixelColor(2, 0, 255, 0);
    strip.setPixelColor(3, 0, 255, 0);
    strip.show();
    delay (500);
    startprog = 0;
  }
  if (startprog == 1)
  {
    initprog();
  }
  if (startprog == 0)
  {
    startsequence();
  }
}

void initprog()
{
  for (i = 0; i < 4; i++)
  {
    strip.setPixelColor(0, 0, 0, 0);
    strip.setPixelColor(1, 0, 0, 0);
    strip.setPixelColor(2, 0, 0, 0);
    strip.setPixelColor(3, 0, 0, 0);
    strip.setPixelColor(4, 0, 0, 0);
    strip.setPixelColor(5, 0, 0, 0);
    strip.setPixelColor(6, 0, 0, 0);
    strip.setPixelColor(i, 0, 255, 0);
    strip.show();
    delay(100);
  }
}

void keydetect()
{
  if (startprog == 1)
  {
    strip.setPixelColor(0, 0, 0, 0);
    strip.setPixelColor(1, 0, 0, 0);
    strip.setPixelColor(2, 0, 0, 0);
    strip.setPixelColor(3, 0, 0, 0);
    strip.setPixelColor(4, 0, 0, 0);
    strip.setPixelColor(5, 0, 0, 0);
    strip.setPixelColor(6, 0, 0, 0);
    strip.show();
    delay (100);
    strip.setPixelColor(0, 0, 0, 255);
    strip.setPixelColor(1, 0, 0, 255);
    strip.setPixelColor(2, 0, 0, 255);
    strip.setPixelColor(3, 0, 0, 255);
    strip.show();
    delay (100);
    strip.setPixelColor(0, 0, 0, 0);
    strip.setPixelColor(1, 0, 0, 0);
    strip.setPixelColor(2, 0, 0, 0);
    strip.setPixelColor(3, 0, 0, 0);
  }
  startprog = 0;
  startsequence();
}


void testsequence ()
{
  for (k = 1; k <= turns; k++)
  {
    found = 0;
    do
    {
      delay(10);
    }
    while (digitalRead(D3) == HIGH && digitalRead(D5) == HIGH && digitalRead(D6) == HIGH && digitalRead(D7) == HIGH);
    mustbe = turnarray[k];

    // =========================================================================================
    // test for right button
    // =========================================================================================

    if (mustbe == 1)
    {
      if (digitalRead(D3) == LOW)
      {
        strip.setPixelColor(0, 0, 255, 0);
        strip.setPixelColor(1, 0, 0, 0);
        strip.setPixelColor(2, 0, 0, 0);
        strip.setPixelColor(3, 0, 0, 0);
        strip.setPixelColor(4, 0, 0, 0);
        strip.setPixelColor(5, 0, 0, 0);
        strip.setPixelColor(6, 0, 0, 0);
        strip.show();
        delay(500);
        found = 1;
      }
    }

    if (mustbe == 2)
    {
      // button 2 ==> 101
      if (digitalRead(D5) == LOW)
      {
        strip.setPixelColor(0, 0, 0, 0);
        strip.setPixelColor(1, 255, 0, 0);
        strip.setPixelColor(2, 0, 0, 0);
        strip.setPixelColor(3, 0, 0, 0);
        strip.setPixelColor(4, 0, 0, 0);
        strip.setPixelColor(5, 0, 0, 0);
        strip.setPixelColor(6, 0, 0, 0);
        strip.show();
        delay(500);
        found = 1;
      }
    }

    if (mustbe == 3)
    {
      // button 3 ==> 110
      if (digitalRead(D6) == LOW)
      {
        strip.setPixelColor(0, 0, 0, 0);
        strip.setPixelColor(1, 0, 0, 0);
        strip.setPixelColor(2, 0, 0, 255);
        strip.setPixelColor(3, 0, 0, 0);
        strip.setPixelColor(4, 0, 0, 0);
        strip.setPixelColor(5, 0, 0, 0);
        strip.setPixelColor(6, 0, 0, 0);
        strip.show();
        delay(500);
        found = 1;
      }
    }

    if (mustbe == 4)
    {
      // button 4 ==> 100
      if (digitalRead(D7) == LOW) //
      {
        strip.setPixelColor(0, 0, 0, 0);
        strip.setPixelColor(1, 0, 0, 0);
        strip.setPixelColor(2, 0, 0, 0);
        strip.setPixelColor(3, 255, 255, 0);
        strip.setPixelColor(4, 0, 0, 0);
        strip.setPixelColor(5, 0, 0, 0);
        strip.setPixelColor(6, 0, 0, 0);
        strip.show();
        delay(500);
        found = 1;
      }
    }

    if (found == 0) // FAULT FAULT FAULT

    {
      for (j = 1; j < 10; j++)
      {
        strip.setPixelColor(0, 255, 0, 0);
        strip.setPixelColor(1, 255, 0, 0);
        strip.setPixelColor(2, 255, 0, 0);
        strip.setPixelColor(3, 255, 0, 0);
        strip.setPixelColor(4, 0, 0, 0);
        strip.setPixelColor(5, 0, 0, 0);
        strip.setPixelColor(6, 0, 0, 0);
        strip.show();
        delay(120);
        strip.setPixelColor(0, 0, 0, 0);
        strip.setPixelColor(1, 0, 0, 0);
        strip.setPixelColor(2, 0, 0, 0);
        strip.setPixelColor(3, 0, 0, 0);
        strip.setPixelColor(4, 0, 0, 0);
        strip.setPixelColor(5, 0, 0, 0);
        strip.setPixelColor(6, 0, 0, 0);
        strip.show();
        delay(120);

      }
      i = 0;
      j = 0;
      k = 0;
      found = 0;
      mustbe = 0;
      num = 0;
      playnum = 0;
      startprog = 1;
      turns = 0;
      delay(1000);
      start();
    }



    // ============================================================================
    // end test right button
    // ============================================================================

    strip.setPixelColor(0, 0, 0, 0);
    strip.setPixelColor(1, 0, 0, 0);
    strip.setPixelColor(2, 0, 0, 0);
    strip.setPixelColor(3, 0, 0, 0);
    strip.setPixelColor(4, 0, 0, 0);
    strip.setPixelColor(5, 0, 0, 0);
    strip.setPixelColor(6, 0, 0, 0);
    strip.show();
    delay(500);
  }// end for i to turns
} // end void testsequence

void playsequence()
{
  for (i = 1; i <= turns; i++)
  {
    playnum = turnarray[i];
    if (playnum == 1)
    {
      strip.setPixelColor(0, 0, 255, 0);
      strip.setPixelColor(1, 0, 0, 0);
      strip.setPixelColor(2, 0, 0, 0);
      strip.setPixelColor(3, 0, 0, 0);
      strip.show();
    }
    if (playnum == 2)
    {
      strip.setPixelColor(0, 0, 0, 0);
      strip.setPixelColor(1, 255, 0, 0);
      strip.setPixelColor(2, 0, 0, 0);
      strip.setPixelColor(3, 0, 0, 0);
      strip.show();
    }
    if (playnum == 3)
    {
      strip.setPixelColor(0, 0, 0, 0);
      strip.setPixelColor(1, 0, 0, 0);
      strip.setPixelColor(2, 0, 0, 255);
      strip.setPixelColor(3, 0, 0, 0);
      strip.show();
    }
    if (playnum == 4)
    {
      strip.setPixelColor(0, 0, 0, 0);
      strip.setPixelColor(1, 0, 0, 0);
      strip.setPixelColor(2, 0, 0, 0);
      strip.setPixelColor(3, 255, 255, 0);
      strip.show();
    }
    delay(1000);
    strip.setPixelColor(0, 0, 0, 0);
    strip.setPixelColor(1, 0, 0, 0);
    strip.setPixelColor(2, 0, 0, 0);
    strip.setPixelColor(3, 0, 0, 0);
    strip.show();
    delay(500);
  }
  do
  {
    delay(10);
  }
  while (digitalRead(D3) == HIGH && digitalRead(D5) == HIGH && digitalRead(D6) == HIGH && digitalRead(D7) == HIGH);
  testsequence();
}


void startsequence()
{
  /*
   *  1 = green  0,255,0
   *  2 = red 255,0,0
   *  3 = blue 0,0,255
   *  4 = yellow 255,255,0
  */
  turns = turns + 1;
  num = random (1, 5);
  turnarray[turns] = num;
  startprog = 0;
  strip.setPixelColor(0, 0, 0, 0);
  strip.setPixelColor(1, 0, 0, 0);
  strip.setPixelColor(2, 0, 0, 0);
  strip.setPixelColor(3, 0, 0, 0);
  strip.show();
  delay(500);
  playsequence();
}


Now before you start nagging: yes I know this software can very much be optimised. However in this form everyone might be able to read and understand what is happening so anyone can alter it to its own needs. Besides that, neither speed or memory space are a problem here.


Lets look at some parts of the software.

#include <Adafruit_NeoPixel.h>
#define PIXEL_PIN    D8
#define PIXEL_COUNT 4

As you can see you need the Adafruit Neopixel library. With the new Arduino IDE's it is a piece of cake to install new libraries so that should not give you any problems.

The setup starts with:
  randomSeed(analogRead(0));

Please make sure that this is indeed the first line in your program. The next lines will set the IO pins as input with a pull-up resistor. If you do not define the randomseed first you will not get a random figure.

In the start() part we wait till a key is pressed. Until that happens the Neopixels wil give a seqeunce of green pixels.
When a button is pressed all pixels will briefly be green and then one pixel will get the first random color choosen in the startsequence() routine.

When the first color has been choosen by startsequence() the program moves to playsequence() where the colors will be shown. The first time there will be just 1 color. The second time the array turnarray[] will have 2 values etc etc etc.

When the color sequence has been shown the program jumps to the testsequence() routine. There your keypresses will be chequed against the values in the array. If you pressed the correct buttons the variable turns is incremented and the program jumps to startsequence() again to pick the next random color.

This keeps repeating till you make an error.

If you make an error the testsequence() routine will notice. The routine will clear all variables and the program will start again.

Next step.

Attiny85

The ESP is a bit of overkill. No it is a lot of overkill !!!

Nowadays everything seems to be reeling around the ESP8266 and ESP32 but for small projects like this we seem to forget the Arduino and it's little broter the Attiny85. And the Attiny85 is well equipped for this project. So let's exchange the ESP8266 for an Attiny85. This microcontroller has 5 digital I/O pins and works at 8Mhz which is sufficient. 




As you can see the breadboard setup is not much different. However there is one extra feature which gives this setup an extra feature: it works on batteries !! 



So for the end result we can choose wether we want to power it using a power bank or plain AA or even AAA batteries. I was able to play Simon in this setup for several hours on just AAA batteries.
 

The software alterations are also minimal. In the program just replace the IO pin references to the Attiny references:

D3  ==> 0
D5  ==> 1
D6  ==> 2
D7  ==> 3
D8  ==> 4

Thats all !!!

How to play Simon.

A) the program starts by sequencing all neopixels in green till you press a button
B) If a button is pressed the Neopixels will all briefly light up green then dim
C) A random color is choosen
D) The program waits till you press the right button
E) When the right color button is chosen the program picks another random
color and will display the first and second after eachtother
F) The program then waits till you press in sequence the right color buttons
G) If the right sequence has been pushed the program loops back to step E and another color is added etc etc etc
H) if you pressed the wrong color button all Neopixels will flash red for several times and the program restarts.

Next stop: Stripboard layout and casing

Till then
Have fun

Luc Volders