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

Friday, November 30, 2018

Alarm !!

For an index of all my stories click this text

This story tells about an alarm for paintings, But do not be set off by that. You can use the same alarm for doors that open, drawers and cookie jars.

As you might know by now, my girlfriend paints and often has expositions.



Due to the new and utterly stupid privacy laws in the Netherlands I had to blur out the faces from this photo.


At this moment she has an exposition in a gallery that shares its place with the local library. Nothing wrong with that, because it attracts more people. There is however a small problem and that is security. The building has security, the paintings are insured. However there is always a chance that something happens as the gallery is not manned all the time while the library is.

So I decided to design an alarm system for the paintings.

The idea

I wanted to be able to send a notification to my phone and that of some other people when a painting is agitated or moved. To do this I used several techniques and parts which have been covered in previous stories. It all comes together here.

First the movement detection. I am going to use the SW18010P vibration sensor for that. You can find the details about this sensor in this story: http://lucstechblog.blogspot.com/2018/05/vibration-detection.html

Next I am using an ESP8266 and I am going to program it in ESP-Basic. Find an introduction about ESP-Basic here: http://lucstechblog.blogspot.nl/2017/03/back-to-basic-basic-language-on-esp8266.html

The ESP is not going to do much. It just sits there and waits till agitation or movement is detected. I can not use a wall mounted power outlet. So I have to feed it with batteries. To use as less power as is possible I am going to use Deepsleep. Find details about Deepsleep here: https://lucstechblog.blogspot.com/2018/07/esp-power-reduction-withn-deepsleep.html

Next step is to send the information to mine and others phones. As Mit's App inventor has no push notification detection until now I am going to use IFTTT for this. You can find detailed information about IFTTT in this story (which has a link to the other stories about IFTTT): https://lucstechblog.blogspot.nl/2017/09/ifttt-4-send-notification-to-my-phone.html

And then there is a housing needed. The frame of the paintings is less then 2 cm thick. So I needed to design a housing that would fit into the frame and yet holds batteries and the perfboard.

Last but not least. The location has free internet that can be used by everyone. That is just what I need for this alarm to work.

The hardware.

I am going for a minimalistic design here. There has nothing to be done except detecting a movement and sending a message. So an ESP-01 will do the trick.

From the Deepsleep article (read it by clicking here
https://lucstechblog.blogspot.com/2018/07/esp-power-reduction-withn-deepsleep.html you will know that we can put the ESP8266 into Deepsleep and it can be woken by connecting RST (reset) to ground. We are going to use the second schematic from that article here. However we are going to modify it a bit as we do not need the led we used in the test-setup.




As you can see I attached the SW18010P directly to ground and the RESET pin of the ESP. To make sure the Reset pin only gets connected to Ground when needed I attached a pull-up resistor 10K to the VCC and the Reset pin. So reset will be high all the time until vibration is detected and that attaches it to Ground which wakens the ESP.




And here you can see the Stripboard version. you are looking at the soldering side. The components are in real life at the back-side.

The ESP is at the top and the antenna is at the top.




This is how it looks in real life.

What I did in this prototype is to solder the SW18010P thight against the stripboard. In my next version the SW18010P will be floating above the stripboard. I leave the leads longer and therefore there will be more space between the stripbaord and the sensor which makes it more sensitive.

Power


As the ESP01 is in Deepsleep it will consume very little power and therefore I can use batteries. 3 AA-type batteries will supply the power. Just like they did with the rain-sensor (read that story by clicking here).



The battery case I am using is just 1.6 mm high which neatly fits into the painting frame.
 

The stripboard has even less height and it's length just fits within the width of the battery case. The only thing we have to do is to expand the battery case and adapt it so the stripboard fits within.





The ESP-Basic Program

Programming the ESP in ESP-Basic is done with an on-screen editor in your web-browser which makes programming and editing very comfortable. Again: read the basics (pun intended) about ESP-Basic programming here: http://lucstechblog.blogspot.nl/2017/03/back-to-basic-basic-language-on-esp8266.html

The program is very straightforward:



cls
wprint "I am awake"
wprint wget("maker.ifttt.com/trigger/alarm/with/key/PUT-YOUR-KEY-HERE")
wprint "<br>"
button "go to sleep",[sleep]
wprint "<br>"
button "stop",[stop]
wait

[sleep]
cls
wprint "sleeping"
sleep 0
wait

[stop]
end


No fancy webpage. Just a button to start the sleep function and a button to stop the program.

What happens when you power the ESP01 up for the first time is that it sends a notification through IFTTT. This makes sure that everything works as intended and you have an internet connection. Now you can mount the ESP in the frame.




Then press the "go to sleep" button

The ESP enters Deepsleep mode for an undefined time (sleep 0). When the painting vibrates the sensor will connect Ground to the Reset pin of the ESP01 and the ESP will awake and start the default.bas program.



Just make sure in the SETTINGS page that default.bas wil automatically run when the ESP is booted. So the cycle just repeats.

IFTTT

I have written several stories about IFTTT which I suggest you read them all. Here are the links:

https://lucstechblog.blogspot.nl/2017/04/ifttt-if-this-then-that.html
https://lucstechblog.blogspot.nl/2017/05/ifttt-part-2-maker-channel.html
https://lucstechblog.blogspot.nl/2017/05/ifttt-part-3-basic-alert-over-ifttt.html
https://lucstechblog.blogspot.nl/2017/09/ifttt-4-send-notification-to-my-phone.html



Start with making a webhook (the IF section) and link it to a notification (the THEN section).

The notification



Here you can see what happens on my Android Phone when vibration is detected.


If you want a copy of the STL files of the casing for 3D printing or adjusting for your own needs just send me an email.


Till next time
Have fun !!

Luc Volders