27 Feb 2017

C Notes: Finite State Machine. Interactive Traffic Light. C10

This explanation applies to Chapter 10 of Online Course:
Embedded Systems - Shape The World UT.6.03X


By the way the course is going right now on the edX Platform. Search "Embedded Systems - Shape The World UT.6.10x and UT.6.20x"

Chapter 10 is very interesting, but in the same time and a bit difficult to understand at first look. I spent about two weeks to connect all threads into the one. So, save you time with my explanation.
I refer to TExaSware\C10_TableTrafficLight please open in Keil to have a full code.

Some tip: If you do not understand what is a struct or some basics of C - Zybooks is an awesome helper to grow up with C. Use a free opportunity until the course is active.

1. Firstly we made a struct State

struct State { 
   unsigned long Out; 
   unsigned long Time; 
   unsigned long Next[4];
};

... and then define a new data type STyp.

For what? Because we have a couple states of the machine that will use a complex data structure: Out, Time and Next[4] array.

typedef const struct State STyp; 

// Make a new constant (stored in ROM) data type based on struct State and give a type name STyp.

2. Define names of states by the sequence: 0, 1, 2, 3 and so on. 

We give names because it easy to work with, then compiler will replace names with digits. First of all you should understand table 10.2 So, you may do not have any questions here. Take a pen and redraw logic by yourself ref to professors exp.

Why do we need it at all? How it will connect, you will understand soon.

#define goN 0 
#define waitN 1 
#define goE 2 
#define waitE 3

3. Here we make a software FSM data structure from data graph or our table 10.2 It based on STyp data type, see above. See lectures 10.3 - 10.4

STyp FSM[4] = { 
    {0x21,3000,{goN,waitN,goN,waitN}}, // output, delay, next state 
    {0x22, 500,{goE,goE,goE,goE}}, 
    {0x0C,3000,{goE,goE,waitE,waitE}}, 
    {0x14, 500,{goN,goN,goN,goN}}
};

4. Here is an FSM engine. We starting from goN state.

S = goN; // Start 
while(1) { 
    LIGHT = FSM[S].Out; // set lights 
    SysTick_Wait10ms(FSM[S].Time); 
    Input = SENSOR; // read sensors 
    S = FSM[S].Next[Input]; 
}

5. Firstly we need set an output based of state. 
*** Output = g(CurrentState ) Introduction to FSMs. ***

This code doing it. But how?

LIGHT = FSM[S].Out; // set lights

// Go to FSM array into [goN] into Out and assign Out value to the LIGHT.
// It takes a "0" element from STyp FSM [4] array, because goN is defined as "0" before.
// Do not forget S = goN;

STyp FSM[4]={ 
{0x21,3000,{goN,waitN,goN,waitN}} // Output value, delay value, next states

// Move data into the port and fire the LEDs! Out value of variable is 0x21

6. Next, let's feed the SysTick timer to make 30ms pause between states.

SysTick_Wait10ms(FSM[S].Time);

// The same principle, just now do it for the argument of the function. Go into FSM[goN] and get Time value which is "3000"

7. Read inputs. 

Input = SENSOR; // read sensors PE0, PE1: 00, 01, 10, 11

// Assume we got "00" from SENSOR and placed into Input;
// Move ahead.

8. Finally, FSM defines a new state based on our accepted table/graph logic and place into variable S next state. Most interesting part of a magic with FSM!

S = FSM[S].Next[Input];

// Go to FSM[0] into Next[0x00]. // "00" we got before from input. Buttons were not pressed.
// New State is goN which is defined as "0" // 00 in binary or 0x01

9. New state is function from: where we is and what we have on input. 
*** NextState= f(CurrentState ,Input) Introduction to FSMs. ***

// Assume we got "01"
// New State is waitN which is defined as "1" // 01 in binary or 0x01

// Assume we got "10"
// New State is goE which is defined as "2" // 10 in binary or 0x02

// Assume we got "11"
// New State is waitE which is defined as "3" // 11 in binary or 0x03

That is fin, "ifless" code. 

Vita!

24 Feb 2017

The basics of Arduino game programming: Images. (Part 3)

All images in our feature game will be represented as data and stored in byte arrays. To do that we need convert *.bmp images to data.

Firstly, let's draw an image of a player or any other object. My 8x8 "Squarics" are here:








Pay attention that image canvas size of objects should be: 8x8, 16x16, 24x24, 32x32, 64x64 or 64x128 pixels for the entire screen bitmaps. Save images into monochrome 1-bit *.bmp file. Then, convert them into data using online or desktop applications:

LCD Assistant
Image to Adafruit OLED Bitmap Converter
or BitmapToC for Mac computers users.

I use BitmapToC for converting images to 8-bits per array items. You will get a code, like this:

// width: 8 height: 8
PROGMEM const unsigned char YOUR_NAME[] = {
/* 8, 8, */ <-- Remove these, like I did using /* */.
0xff,
0x81,
0xa5,
0x81,
0x81,
0xbd,
0x81,
0xff

};

Remove two first items from an array. Actually, this is width and this is height parameters. In some cases, with others graphics libraries it could be a useful data. However, for our case just comment or delete this piece of code. Get a name of object. Replace YOUR_NAME with yours one.

If you convert hexadecimal numbers into binary, you will see a binary image.

  B11111111,
  B10000001,
  B10100101,
  B10000001,
  B10000001,
  B10111101,
  B10000001,
  B11111111,

"1"s will emit light on OLED display. This is me.


Copy and paste code into Arduino editor.

For more convenient work, you may create in the root directory of project a new one file bitmaps.h and save bitmaps data into it. Don't forget to include file in main thread with a line: #include "bitmaps.h"

Let's print and debug it.

  display.clearDisplay(); // Clear display memory
  display.drawBitmap(0, 0, player2, PLAYER_W, PLAYER_H, WHITE);
  display.display(); // Output data on the screen

Inputs:

(0, 0, player2, PLAYER_W, PLAYER_H, WHITE);
  • 0, 0 – X and Y coordinates. Top left corner is a beginning.
  • player2 – Name of object.
  • PLAYER_W, PLAYER_H, – Width and height parameters.
  • WHITE – Leave as is for monochrome display.
After uploading this code into the board you should see an image of a player.

CODE:
---------------------------------------------------------------------
/*

   Game Starter Project 15.02.2017
   Board: Arduino Nano (ATmega328), OLED Display: SSD1306 i2c
   Author: Aleksey M.
   http://eekit.blogspot.com

*/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//#include "bitmaps.h"

// OLED Display Init
#define SSD1306_LCDHEIGHT 64
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

// Hardware Init
#define UP              8
#define DOWN            10
#define LEFT            9
#define RIGHT           5
#define A               A0
#define B               A1
#define SPEAKER         3
// Debugging LEDs
#define LED             2
#define ONBOARD_LED     13

// Images
#define PLAYER_H 8
#define PLAYER_W 8

PROGMEM const unsigned char player2[] = {
  /* 8, 8, */
  0xff,
  0x81,
  0xa5,
  0x81,
  0x81,
  0xbd,
  0x81,
  0xff

};

void setup() {
  // Hardware setup
  pinMode (UP, INPUT_PULLUP);
  pinMode (DOWN, INPUT_PULLUP);
  pinMode (LEFT, INPUT_PULLUP);
  pinMode (RIGHT, INPUT_PULLUP);
  pinMode (A, INPUT_PULLUP);
  pinMode (B, INPUT_PULLUP);
  pinMode (SPEAKER, OUTPUT);
  pinMode (LED, OUTPUT);
  pinMode (ONBOARD_LED, OUTPUT);
  digitalWrite(SPEAKER, LOW);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  // initialize with the I2C addr 0x3D (for the 128x64)
  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(500);

  // Clear the buffer.
  display.clearDisplay();

  // Invert Display.
  //display.invertDisplay(true);

  display.setTextSize(1);
  display.setTextColor(WHITE);
}

void loop() {
  // Game code here
  display.drawBitmap(0, 0, player2, PLAYER_W, PLAYER_H, WHITE);
  display.display();
}


22 Feb 2017

The basics of Arduino game programming: Buttons & Debugging. (Part 2)

Time to push buttons!

In this part, I'm going to show you how we can move objects within the screen and debug some pieces of code. I guess you have read and already know something about C programming language, if not – don't worry and try to analyze what I do.

Firstly, we need to initialize I/O peripherals: buttons, speaker, LEDs, display. Set enabled buttons pins as Inputs, speaker and LEDs pins to Output. Display initialization will be covered later. Learn it from examples!

  // Hardware setup
  pinMode (UP, INPUT_PULLUP);
  pinMode (DOWN, INPUT_PULLUP);
  pinMode (LEFT, INPUT_PULLUP);
  pinMode (RIGHT, INPUT_PULLUP);
  pinMode (A, INPUT_PULLUP);
  pinMode (B, INPUT_PULLUP);
  pinMode (SPEAKER, OUTPUT);
  pinMode (LED, OUTPUT);
  pinMode (ONBOARD_LED, OUTPUT);
  digitalWrite(SPEAKER, LOW);

There are several basic methods to read the pin of Arduino. As we interested only in two values of a button, pressed or not, we use a digitalRead() function which can return High either Low state.

An analogRead() function can read analog compatible pins like A0, A1, A2... and return a value in a range of 0 to 1023 for 10-bit ADC. We do not use this function in the project.

We can read button state like this:

  if (digitalRead(8) == LOW) { // 8 is a number of pin where button is connected.
    digitalWrite(LED, HIGH); // Turn LED on
  } else {
    digitalWrite(LED, LOW); // Turn LED off
  }

We compare the state of the button with LOW, because we use the PULLUP resistors, it made buttons to be in the HIGH state when they are not pushed. You can learn more details about it on the official Arduino website.

The same things:

  if (digitalRead(8) == false) {
    digitalWrite(LED, HIGH);
  } else {
    digitalWrite(LED, LOW);
  }

Also, you can read in this way:

  if ( !digitalRead(8) ) {
    digitalWrite(LED, HIGH);
  } else {
    digitalWrite(LED, LOW);
  }

As you can see I wrote "8" This is the number of a pin where a button is connected. Obviously, this is not convenient for humans. We may forget what pins got buttons, where is speaker connected etc.

Let's define names.

// Hardware Init
#define UP 8
#define DOWN 10
#define LEFT 9
#define RIGHT 5
#define A A0
#define B A1
#define SPEAKER 3
// Debugging LEDs
#define LED 2
#define ONBOARD_LED 13

Time to debug buttons! Are they properly connected and respond? Instead of writing a huge amount of the same type of code for each button I wrote testButton() function.

Try to analyze (copy and paste into Arduino IDE) and run the code.

CODE:
---------------------------------------------------------------------
/*

   Game Starter Project 15.02.2017
   Board: Arduino Nano (ATmega328), OLED Display: SSD1306 i2c
   Author: Aleksey M.
   http://eekit.blogspot.com

*/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// OLED Display Init
#define SSD1306_LCDHEIGHT 64
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

// Hardware Init
#define UP          8
#define DOWN        10
#define LEFT        9
#define RIGHT       5
#define A           A0
#define B           A1
#define SPEAKER     3
// Debugging LEDs
#define LED         2
#define ONBOARD_LED    13

void setup() {
  // Hardware setup
  pinMode (UP, INPUT_PULLUP);
  pinMode (DOWN, INPUT_PULLUP);
  pinMode (LEFT, INPUT_PULLUP);
  pinMode (RIGHT, INPUT_PULLUP);
  pinMode (A, INPUT_PULLUP);
  pinMode (B, INPUT_PULLUP);
  pinMode (SPEAKER, OUTPUT);
  pinMode (LED, OUTPUT);
  pinMode (ONBOARD_LED, OUTPUT);
  digitalWrite(SPEAKER, LOW);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  // initialize with the I2C addr 0x3D (for the 128x64)
  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(500);

  // Clear the buffer.
  display.clearDisplay();

  // Invert Display.
  //display.invertDisplay(true);

  display.setTextSize(1);
  display.setTextColor(WHITE);
}

void loop() {

  testButton(UP);
  testButton(DOWN);
  testButton(LEFT);
  testButton(RIGHT);
  testButton(A);
  testButton(B);

  // Turn LED of
  digitalWrite(LED, LOW);

  // Clear display
  display.clearDisplay();
  display.display();

}

void testButton(int button) {
  while (digitalRead(button) == LOW) {
    digitalWrite(LED, HIGH);

    // Print on the display
    display.setCursor(0, 0);
    display.println("Works!");
    display.display();
  }
}



Here is readButton() function, that we will use in the next part of Arduino game programming tutorial.

bool readButton(int button) {
  if (digitalRead(button) == LOW) {
    // Debounce. Mechanical buttons generate a flickering signal, so let button become to LOW.
    delay(20); 
    if (digitalRead(button) == LOW) {
      return true;
    }
  } 
  
  else {
    return false;
  }
  
}

Buttons are ready to use!

What have we learned? We have learned: pins initialization and definitions, buttons state readings, the basic debugging method, have a light look on the functions.

20 Feb 2017

The basics of Arduino game programming: Start & Board Setup. (Part 1)

Preamble

Have you ever played games? Sure! Would you like to build and program yours one? Follow to me!

I guarantee, that the programming of games is one of the fun parts of computer education along with brainstorm. Game programming is a complex task which always includes: a creation of logic, data flow management, debugging, optimization. Be sure, if you able to program even a simple old 8-bit game you have achieved a lot.

let's start.

Hardware is based on popular Arduino board. Nevertheless, the principles of programming can be implemented on others platforms: PCs, Texas Instrument's Launchpad, Adafruit etc.

Hardware Kit:
  1. Arduino Nano/UNO (ATmega328) 16 MHz board. Original or clone or any Arduino compatible board will good.
  2. OLED Display SSD1306 with I2C or SPI interface connection. SPI is better for speed and performance. Currently, I use I2C.
  3. Breadboard: MB102 Breadboard 830 Point Solderless.
  4. Buttons - 6 pcs.
  5. LED: Red color - 1 pcs.
  6. Transistor: 2n3904 - 1pcs. 2n2222 or any other general purpose small signal npn-type.
  7. Speaker - 1pcs. Piezo or electromagnetic type.
  8. Resistors: 240 - 270 Ohms - 4 pcs.
  9. Resistor: 1 - 10k - 1pcs.
  10. Capacitor: 22n - 100n - 1 pcs.
  11. Jumpers, Dupont Cable or 0 Ohms resistors.
Everything of the list you can get in local or online stores: Adafruit, SparkFun, Aliexpress etc. I got from China Ali!

Connections and schematic:


Breadboard
Breadboard

Schematic
Schematic
The OLED displays require a 3.3V power supply and 3.3V logic levels. Many OLED modules from China have an onboard voltage regulator, that allow you connect display directly to 5v power supply line. However, they do not have an onboard logic level converter. Fortunately, the display driver SSD1306 chip is good accepting even 5v Arduino boards logic levels. To avoid over current, I use two resistors R2, R3 by 240 - 270 Ohms.

You no need to use these resistors if you got an OLED from Adafruit, they are fully and safety compatible with any 5V microcontrollers, such as the Arduino board.

Software:
  1. Install Arduino IDE / Compiler on your machine. I use 1.8.1 version.
  2. Launch Arduino, select Tool menu -> Board: choose your target board Nano or UNO. Select  Tool menu again -> Processor: choose ATmega328.
Ok, next step is installing Adafruit libraries for managing OLED display.
  1. Select Sketch -> Include Library -> Manage Libraries
  2. Search and install: Adafruit SSD1306
  3. Search and install: Adafruit GFX Library
Almost done.

Open with text editor Adafruit_SSD1306.h 
Path: Arduino Projects Folder / libraries / Adafruit_SSD1306

Find 73 to 75 lines and edit to following:

     #define SSD1306_128_64
//   #define SSD1306_128_32
//   #define SSD1306_96_16

If you do not do this for 128 x 64 OLED display, you will get missing of lines on the display output.

Troubleshooting

Mostly, display modules from china (driver chip) use 0x3C i2c address. If you got a problem with initialization: display completely does not respond, probably the problem is on an incorrect i2c address. Be sure in the correct electric connections.

Try 0x3D, if still not working, you should scan to find a right address. Google to "MultiSpeedI2CScanner".

Electric Tips and Safety Advice
  1. Double check connection before power on.
  2. Always! Always keep by hand a metal leg of a component when you cutting out it by wire-cutters. By damn laws, it shoots straight in the eye!
CODE:
---------------------------------------------------------------------

/*

   Game Starter Project 15.02.2017
   Board: Arduino Nano (ATmega328), OLED Display: SSD1306 i2c
   Author: Aleksey M.
   http://eekit.blogspot.com

*/

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// OLED Display Init
#define SSD1306_LCDHEIGHT 64
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

// Hardware Init
#define UP          8
#define DOWN        10
#define LEFT        9
#define RIGHT       5
#define A           A0
#define B           A1
#define SPEAKER     3
// Debugging LEDs
#define LED         2
#define BOARDLED    13

void setup() {
  // Hardware setup
  pinMode (UP, INPUT_PULLUP);
  pinMode (DOWN, INPUT_PULLUP);
  pinMode (LEFT, INPUT_PULLUP);
  pinMode (RIGHT, INPUT_PULLUP);
  pinMode (A, INPUT_PULLUP);
  pinMode (SPEAKER, OUTPUT);
  pinMode (LED, OUTPUT);
  digitalWrite(SPEAKER, LOW);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  // initialize with the I2C addr 0x3D (for the 128x64)
  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(500);

  // Clear the buffer.
  display.clearDisplay();

  // Invert Display.
  //display.invertDisplay(true);

  display.setTextSize(1);
  display.setTextColor(WHITE);
}

void loop() {
  // Game code here
  display.clearDisplay();
  display.setCursor(0, 10);   
  display.println("Hello Game!");
  display.display();
}

---------------------------------------------------------------------

Upload it. Do you see "Hello Game!"? If not, check connections and repeat everything one more time.
SSD1306 OLED Display


19 Feb 2017

The basics of Arduino game programming: Objects Collision Detection. (Part 5)

Let's try to understand how we can to know are objects touching each other or not. This method could be implemented in game development, automatization, tracking etc.

We know that in most cases especially in programming a top left corner is a beginning of two-dimensional coordinates and object itself as well. Height and width are parameters of a box. This simple box is often called a bounding box.

0,0          X
  +--------------------->
  |
  |
  |    X,Y    W
  |      ---------+
Y |      |         |
  |      |         |
  |    H |         |
  |      |         |
  |      |         |
  |      +---------+
  |
  v

Imagine that two boxes are in collision. There are four situations when objects are touched. Firstly, we have a look at X's of Right edge of A and Left edge of B.

0,0          X
  +--------------------->
  |
  |      Offset
  |      +-------->X
  |    X,Y    W
  |      +---------
Y |      |         |
  |      |         |
  |    H |    A    |
  |      |         |
  |      |       ---------+
  |      +---------+       |
  |              |         |
  v              |    B    |
                 |         |
                 |         |
                 +---------+
thus,

A.x + A.width >= B.x

What we do here? Just look at the X values of two boxes. To check X's on the right edge of A box, we add an offset which is width. Follow for on the diagrams.

Next, is Left edge of A and Right edge of B. At similar manner, we are analyzing this case:

0,0          X
  +--------------------->
  |
  |
  |
  |    X,Y    W
  |      ---------+
Y |      |         |
  |      |       Offset
  |    H |    A  +-------->X
  |      |         |
  |      |       +---------
  |      +---------+       |
  |              |         |
  v              |    B    |
                 |         |
                 |         |
                 +---------+

A.x <= B.x + B.width

Ok, let's look at Y's. Bottom of A and top of B boxes.

0,0          X
  +--------------------->
  |
  |
  |
  |    X,Y    W
  |  +   +---------+
|  |   |         |
  |  |   |         |
  |  | H |    A    |
  |  |   |         |
  |  v   |       ---------+
  |  X   ---------+       |
  |              |         |
  v              |    B    |
                 |         |
                 |         |
                 +---------+

A.y + A.height >= B.y

Last one. Top of A and bottom of B.

0,0          X
  +--------------------->
  |
  |
  |
  |    X,Y    W
  |      ---------+
Y |      |         |
  |      |         |
  |    H |    A    |
  |      |         |
  |      |     + +---------+
  |      +---------+       |
  |            | |         |
  v            | |    B    |
               | |         |
               v |         |
               X ---------+

A.y <= B.y + B.height

Finally we have conclusion:

If

(A.x + A.width >= B.x)

and

(A.x <= B.x + B.width)

and

(A.y + A.height >= B.y)

and

(A.y <= B.y + B.height)

then:

"Object in a collision!"


Here is Arduino code. Look at checkCollision() function, which is identical to theoretical explanation.

void checkCollision() {
  if (( (targetX + targetW > playerX) and
        (targetY + targetH > playerY) and
        (targetX < playerX + playerW) and
        (targetY < playerY + playerH) ))
  {
    digitalWrite(LED, HIGH);
    soundStart();
  } else {
    digitalWrite(LED, LOW);
  }
}

Popular Posts