F19: Smacman

From Embedded Systems Learning Academy
Jump to: navigation, search

SMACMAN

Smacman

Abstract

Our team has created a unique 2 player game involving a central screen of 64x64 LED matrix and two auxiliary controllers. The two opponents will face one another and will have a paddle on their side. A ball will travel between players which they have to deflect away. Additionally, each player will have a “monster” that will continuously move towards the ball and try to eat the ball. If a monster eats the ball, that opponent of the owner of the monster will lose. Therefore, it is beneficial for a player to try to place the ball closer to their monster and further away from the opponent’s monster in order to score. Additionally, if the ball contacts the side of the screen behind the player’s paddle, they will also lose. As the game progresses, the speed of the ball will increase and eventually, there will be variation in the movement of monster. The controllers(xBee--XB24-AWI) will be wireless and communicate with the master board which runs the game and drives the 64x64 LED matrix.The MP3 module(VS 1053) reads music files from an SD card and plays the music through a speaker interfaced with the board in the background.

Objectives

The main objective of the project is to develop a 2D two player Smacman game. Other milestones to achieve are as below:

  • Change the direction and speed of the monster differently at different levels, depending on the direction of the ball on LED Matrix in real time.
  • The controller which does wireless transfer of accelerometer values is used to control the movement of the ball
  • Play music in the background and game sounds using MP3 Decoder.
  • Design PCB for Master and the Controller which will interface all the peripheral devices to the SJ-two boards.

Introduction

The project is broadly divided into three nodes:

1. Master Node: The master node (one SJ2 board) receives the data from the controller nodes via XBee module which is used to control the paddle movement which will eventually control the game. It repeatedly sends and receives packets with the controllers to obtain accelerometer/button information and update the score states of the controllers so that they can display the scores to the players. The Master also uses the information from the Controllers to update the paddle movements as per user's input. The master node also controls visual display on the LED Matrix and the game logic.

2. Controller Node: The controller node (two SJ2 boards) consist of the XBee modules which transmits control packets to the master node using fault-tolerant broadcast wireless UART protocol. These process and send information from the onboard I2C accelerometer and pushbuttons to the Master as well as display the players' scores via an I2C 7-segment display.

3. Music Node: The music node (one SJ2 board) provides background music to the game which enhances the experience of the game.

About the game

  • Both Players should try to hit the ball with the paddle placed on their side.
  • Press switch on the controller SJ-Two board to start the game.
  • Tilt the controller left or right to move the paddle.
  • Hit the ball with your paddle before the monster eats it to get a score.
  • To manipulate the speed and the direction of the ball move the paddle in the same or in the opposite direction of the ball.
  • Avoid the monster of opposite player to eat the ball in your side, if this happens then the game is over, else the ball continues to move between the paddles.
  • The speed of the monster increases when the ball will move in opposite direction to catch the ball.
  • Level 1 has the monster of opposite player moving at your side along the edges. If any player exceeds a score of 33 then me move to Level 2.
  • Level 2 has the monster of opposite player moving along x-axis at your side. If any player exceeds a score of 66 then me move to Level 3.
  • Level 3 has the monster of opposite player moving along x-axis and y-axis at your side. If any player reaches a score of 100 then that player wins the game.
  • Both the Players can play and pause the game anytime and resume from where the game was paused.

Team Members & Responsibilities

  • Nick Schiffer
    • XBEE, Controller Functionality, Communication Architecture, Game Logic Development, Enclosures, PCB Designing.
  • Mohit Ingale
    • LED Driver, Game Logic Development, Enclosures, PCB Designing.
  • Ayesha Siddiqua
    • Graphic Driver, Game Logic Development.
  • Shreya Patankar
    • Splash Screen Graphics Driver, Game Logic Development, MP3 Decoder, Wikimedia Update.

Delivery Schedule

Project Repository Link: Github Project

Week# Date Task Status Actual Completion Date
1 10/1/2019
  • Submission of Project Proposal.
  • Complete
  • 10/1/2019
2 10/15/2019
  • Create GitLab Repository.
  • Go through the projects and research about components.
  • Distributing the roles among team members.
  • Research Required Components.
  • Submit Schedule and Components List.
  • Complete
  • 10/14/2019
3 10/22/2019
  • Familiarize with 64x64 LED Matrix Datasheet.
  • Familiarize with xbee & accelerometer datasheet.
  • Familiarize with Mp3 Decoder VS-1053 datasheet.
  • Complete
  • 11/8/2019
4 10/29/2019
  • Make Gitlab Repository for individual tasks for all modules
  • Introduce Naming Convention
  • Develop Graphics Drivers and Implementation of displaying basic monster on LED Matrix
  • Calibrating Accelerometer values for 2 Players
  • Complete
  • 11/8/2019
5 11/5/2019
  • Develop code to send Accelerometer values to move the paddle for both the players
  • Basic LED Display testing for boundary conditions.
  • Develop game specific APIs to draw objects like monster, paddle, ball of led driver
  • Project report update on the wiki.
  • Complete
  • 11/12/2019
6 11/12/2019
  • Develop Algorithm Design for Game Logic for all 3 levels of game
  • Transmission of Accelerometer values for 2 players from their respective controllers to the master
  • Code for the splash screen(Intro screen)
  • Complete
  • 11/19/2019
7 11/19/2019
  • Code for MP3 Decoder for the background music
  • 7 segment display for players score from controller
  • Code to display the score on matrix
  • Code Integration for the moving of paddle on basis of accelerometer values from controllers for both the players
  • Complete
  • 11/26/2019
8 11/26/2019
  • Develop Play/Pause/Stop functionality
  • Debug and Test the Play/Pause/Stop functionality
  • Complete
  • 11/30/2019
9 12/3/2019
  • Design PCB for Controller & master and generate Gerber files, finalize PCB manufacture
  • Integrating calibrated accelerometer, push-button functionalities and wireless transfer from controller boards
  • Complete
  • 12/10/2019
10 12/10/2019
  • Integration of subsystems
  • 3-D Printing of enclosures for matrix and the player's controller
  • Fixing the bugs during testing
  • Complete
  • 12/15/2019
11 12/17/2019
  • Final bug fixes and troubleshooting.
  • Complete wiki report and final demo.
  • In-Progress
  • 12/17/2019

Parts List & Cost

Item# Part Desciption Vendor Qty Cost
1 SJTwo Boards Purchased from Preet 4 $200.00
2 64x64 RGB LED Matrix Sparkfun 1 $75.00
3 Wiring Components and Cable Waveshare 1 $20.00
4 3D printer filament spool(s) HATCHBOX 2 $40.00
5 XBEE Modules From Preet and Adafruit 3 $27.00
6 Batteries Local 2 $4.00
7 I2C 7-seg screens Adafruit 2 $18.00
8 XBee Programmer Boards Waveshare 3 $35.00

Design & Implementation

Hardware and PCB Design

PIN Configuration

PIN# Pin Desciption uC PIN
R1 PIN for Red terminal of RGB LED for upper half of LED Matrix P2_0
G1 PIN for Green terminal of RGB LED for upper half of LED Matrix P2_1
B1 PIN for Blue terminal of RGB LED for upper half of LED Matrix P2_2
R2 PIN for Red terminal of RGB LED for lower half of LED Matrix P2_4
G2 PIN for Green terminal of RGB LED for lower half of LED Matrix P2_5
B2 PIN for Blue terminal of RGB LED for lower half of LED Matrix P2_6
A Mux pin for row selection P2_7
B Mux pin for row selection P2_8
C Mux pin for row selection P2_9
D Mux pin for row selection P0_16
E Mux pin for row selection P0_15
OE Output Enable P1_20
LATCH Latching Data P1_23
CLK CLock signal to understand one bit has been detected P1_28
VCC VCC Supply VCC
GND Ground GND
TX_3 UART Transmit for XBEE P4_28
RX_3 UART Receive for XBEE P4_29
SCK_2 CLOCK for SPI bus for MP3 Decoder P1_0
MOSI_2 MOSI for SPI bus for MP3 Decoder P1_1
MISO_2 MISO for SPI bus for MP3 Decoder P1_4
SDA_2 I2C Data line for 7 Segment P0_10
SCL_2 I2C Clock line for 7 Segment P0_11
Switch Switch GPIO P0_1


PCB Design

There are many softwares available for PCB design, among them Eagle is more popular. We chose EasyEDA for PCB design over Eagle because, it is an online free software so we din't have to worry about the license. We faced many issues while using this software but we found a good tutorial on youtube. The issues which we faced are discussed in the issue section of this wiki. The steps involved in the PCB design process are described in the next section.

Schematic Design:

Our project can be divided into two main circuits, first is game display circuit and second is a console circuit. We decided to design separate PCB for both circuits as doing so can make wiring easy and improve the user experience.

Master Controller PCB schematic:
Master Controller PCB Schematic



We have created on top PCB to mount on SJTwo board. One circuit will do the wire interfacing of SJTwo board to xbee, mp3 decoder, led matrix. This is the master board and it will recieve values from all the controllers and peripherals. This is our Main Circuit. We Also have a power jack to power all the peripheral devices so that we don't have to power SJTwo board from PC while we are playing the game.

Controller PCB schematic:
Controller PCB Schematic


We have created on top PCB to mount on SJTwo board. One circuit will do the wire interfacing of SJTwo board to xbee of the two game controllers which will in turn send the values of accelerometer to the master Xbee interfaced PCB. We Also have a power jack to power all the peripheral devices so that we don't have to power SJTwo board from PC while we are playing the game.

PCB Layout:

After schematic design, the most important step is to connect the PCB layout. In EasyEDA components is not automatically associated with it's PCB footprint, so it's user's responsibility to connect right PCB footprint with the right components. In our case, we did not have the footprint of JLC connector and Rocker switch. so, we created it in a different window and saved it. The amazing feature of KiCAD is once we have done with PCB layout we can visualize PCB in 3D by pressing just one button. Below are actual screenshots of both, PCB layout & their 3D model.

PCB layout Designed on EasyEDA:
PCB Schematic Master 3D view
PCB Schematic Controller 3D view
Printed PCB :

We chose JLC PCB to manufacture our PCB because, it is the cheapest and convenient option available for any prototype PCB manufacturing. It just cost us $2 for quantity of 10 PCB and $12 for shipment. We received manufactured PCB in just 3 days. Followings are actual photos of manufactured & assembled PCBs.

PCB with Copper layer for Master
PCB with Copper layer for Controller

Hardware Interface

Main Controller Design Diagram
Controller Design Diagram


Hardware design diagram above gives an overview of the entire system which consists of the two SJ-One controllers: one board is used as Control Module and other board is used as the Display Module.

  • The Console Module uses the onboard accelerometer on SJ-One which is interfaced via I2C protocol. The calibrated accelerometer values are then used to determine the basket position on the LED Matrix.
  • The Display Module SJ-One board is used to control a 32*32 RGB LED Matrix.This matrix displays the basket, eggs shot from the cannon.The movement of the basket is as per the orientation value received from the Control Module, through Wireless Module. It also consists of Peizo buzzer which is controlled via PWM pin.

Hardware Components Design, Description Implementation

RGB LED Matrix

Intro Splash Screen
LED Matrix Schematic

A 64x64 RGB LED Matrix Panel is used as a display. It has 4096 full-color RGB LEDs. Each LED can be independently addressed and controlled. It requires at 13 digital GPIOs to control the LED matrix. The led matrix has 2 IDC connectors DATA_IN and DATA_OUT on the back, we can cascade multiple panels and make a huge screen together. The LED display is constructed using a decoder to decode address rows. A 64 bit Shift register should be clocked for enabling the color. When the row is low we need to select which all pixels in the column are to be configured with the required color. This is done by clocking the 64Bit shift register. There are 6 64Bit registers each for R1 R2 B1 B2 G1 G2. A single clock is interfaced to all these 6 64bit shift registers. So at once we shift and fill the required color for the column display. Once the clocking and shifting the register is completed we need to latch this data to the register. The register data is sent out to all the row lines and that Row line which is pulled low by the decoder will receive this data and corresponding pixels are turned on.

RGB LED matrix pins:

S.NO RGB LED pins Function
1 R1 High R data
2 G1 High G data
3 B1 High B data
4 R2 Low R data
5 G2 Low G data
6 B2 Low B data
7 A Row select A
8 B Row select B
9 C Row select C
10 D Row select D
11 E Row select E
12 CLK Clock signal.
13 OE Output enables to cascade LED panel.
14 LAT Latch denotes the end of data.
15 VCC 5V
16 GND GND
Row Selection

The RGB matrix has a 5:32 decoder. This decoder takes 5 bits as input and decodes the corresponding row to be kept low. There are five address inputs to the display marked A, B, C, D and E. Based on the truth table, only one input is active (low) at a time. The outputs of the decoder are connected to a P-Channel MOSFET because the decoder itself can only handle low currents and cannot drive a row of LEDs directly. The P-Channel MOSFET provides the high current needed to drive a row of LEDs. The decoder outputs are shared between every 32 outputs. Since, the 5-to-32 address decoder select rows in parallel like 1 and 33, 2 and 34, repeating that pattern until row 32 and 64.

Column Selection

The column data is stored in a 64-bit serial-in, a parallel-out shift register. The shift register is designed to work with LEDs and implements a constant-current system that ensures the LED brightness remains uniform. R1 G1 B1 is used for configuring the color for the top half of the display and R2 G2 B2 for the bottom half of the display. For 64 rows using with 32 outputs of the decoder, we must have unique data for each row pair. When Row 1 and Row 33 are selected, we must provide each row with unique data. This forces us to use two different shift registers for each row pair, an upper register, and a lower register. Because of this, the display is divided into an upper half and a lower half. The data is shifted into the top half via the R1, G1 and B1 signals on the connector. The bottom halves data is supplied by the R2, G2 and B2 signals on the connector. Then, since our display is 64 pixels wide, we use 64-bit shift registers to hold all 64 bits of pixel data for a single row. Each half of the display is controlled by 3 shift registers, one for each color. First, Row 1 and Row 33 are selected then 64 bits of data are shifted into each color’s shift register (R1, G1, B1), and then latched. At the same time, 64 bits of data are also shifted into each color’s shift register for the bottom half (R2, G2, B2), and then latched. The process repeats 31 more times, each time incrementing the rows are selected until every line has been updated.

In order to display something on the LED panels the following steps need to be performed by the driver

  • Shift the pixel data for row 1 into the top column drivers and the pixel data for row 33 into the bottom column drivers using the R1, G1, B1, R2, G2, and B2 data inputs and the SCLK shift clock signal.
  • Clear the blanking signal to blank the display.
  • Set the address input to 1.
  • Latch the contents of the column driver's shift registers to the output registers using the LATCH signal.
  • Make the blanking signal high to display rows 1 and 33.
  • Repeat the process for each of the pairs of rows in the display for 31 times.
Specifications:

The LED display used in our project is a 64*64 pixel panel with a pixel pitch of 3mm. More specifications are tabulated.

  • Operating voltage: DC 5V
  • Average power consumption: <500W/㎡
  • Maxim Power Consumption: <1000w/㎡
  • Pixel: 64x64=4096
  • Level of viewing Angle: ≧160°
  • Control mode: Synchronous control
  • Drive mode: 1/16 scan rate
  • Repetition frequency: ≧60Hz
  • White Balance Brightness: ≧1200cd/㎡
  • Refresh frequency : ≧300Hz
  • MTTF: ≧5000 hours
  • Service Life: 75000~100000 hours
  • Pixel pitch: 3mm
  • Thickness: 11mm
Code Snippet for updation of LED matrix:
void led_matrix__update_display() {
 for (int i = 0; i < 32; i++) {
   led_matrix__disable_display();
   led_matrix__disable_latch();
   led_matrix__select_row(i); // will select i and i + 32 rows at same time
   for (int j = 63; j >= 0; j--) { // shift data with MSB getting shifted-in first
     ((frame_buffer[i][RED_PLANE] >> j) & 1) ? gpio__set(r1) : gpio__reset(r1);
     ((frame_buffer[i][GREEN_PLANE] >> j) & 1) ? gpio__set(g1) : gpio__reset(g1);
     ((frame_buffer[i][BLUE_PLANE] >> j) & 1) ? gpio__set(b1) : gpio__reset(b1);
     ((frame_buffer[i + 32][RED_PLANE] >> j) & 1) ? gpio__set(r2) : gpio__reset(r2);
     ((frame_buffer[i + 32][GREEN_PLANE] >> j) & 1) ? gpio__set(g2) : gpio__reset(g2);
     ((frame_buffer[i + 32][BLUE_PLANE] >> j) & 1) ? gpio__set(b2) : gpio__reset(b2);
     gpio__set(clk);
     gpio__reset(clk); // shift in all 3 color bits at once for top half/bottom half registers
     led_matrix__enable_latch();
     led_matrix__disable_latch();
   }
   // at this point, all 3 shift registers should be filled with corresponding row data in frame_buffer
   led_matrix__enable_latch(); // push shift register contents down to output registers
   led_matrix__enable_display();
   delay__us(250);
 }
 led_matrix__disable_display();

}

MP3 Decoder

We have used the SPI bus to communicate between the MP3 Decoder and the SJ-Two Board. We faced issues while writing to the SD Card they are mentioned in the technical issues section. We are reading data of .wav file from SD Card from our board and sending it to the decoder which will in turn play the background music.

MP3 Decoder
The Pins used for the interfacing are:
  • XDCS: Chip select for SDI pin
  • XCS: chip select for SCI pin
  • SCK: clock from master for SCI
  • SI: Slave input and master output
  • SO: Slave output and master input
  • RESET: reset for the decoder
  • DREQ: data request
When we recieve any data from the SJ-Two board then we send data which is read from the SD card on the decoder from the slave decoder. Thus we are able to read the file and play the music in the background.
Code snippet for reading music
void sdcard_handle__read_song_task(void *songQueueHandle) {
  xQueueHandle *xSongQueue = (xQueueHandle *)songQueueHandle;
  uint8_t data_buffer[VS1053_DATABUFFERLEN];
  uint32_t bytes_read = VS1053_DATABUFFERLEN;
  FIL mySong;
  while (1) {
    FRESULT ret_code = f_open(&mySong, "pacman_death.mp3", FA_READ);
    if (ret_code != FR_OK) {
      SMACMAN__DEBUG_PRINTF("File Not Opened %i\n", ret_code);
      fprintf(stderr, "File Not Opened %i\n", ret_code);
      if (ret_code == 3) {
        vTaskDelay(1000);
      }
    } else {
      bytes_read = VS1053_DATABUFFERLEN;
      SMACMAN__DEBUG_PRINTF("File opened\n");

      while (!mp3__ready_for_data())
        ;
      while (!(bytes_read < VS1053_DATABUFFERLEN)) {

        FRESULT f_read_ret_code = f_read(&mySong, data_buffer, VS1053_DATABUFFERLEN, &bytes_read);
        if (f_read_ret_code != FR_OK) {
          SMACMAN__DEBUG_PRINTF("File not Read %i\n", f_read_ret_code);
        }
        // for (int i = 0; i < bytes_read; i++)
        //   xQueueSend(*xSongQueue, &data_buffer[i], portMAX_DELAY);
        xQueueSend(*xSongQueue, data_buffer, portMAX_DELAY);
        SMACMAN__DEBUG_PRINTF("Data Sent to queue\n");
      }
      SMACMAN__DEBUG_PRINTF("Closing File\n");

      f_close(&mySong);
    }
  }
  //   return true;
}

Controller Module

Accelerometer:

Accelerometer Detection
The MMA8452Q which is a smart low-power, three-axis, capacitive micromachined accelerometer with 12 bits of resolution is used in our project. Accelerometers are electromechanical devices that are used to sense acceleration that can be of various forms, for instance gravity.
In our project,we have calibrated the accelerometer based on the values of X,Y and Z co-ordinates corresponding to different orientations of the accelerometer to control the horizontal movement of the paddle in the game to hit the ball . This has also helped us decide and control the speed of the basket. As shown in the code snippet below the x movement on the right is determined based on the x co-ordinate and the console sensitivity which you wish to set.Similarly movement on the left can also be calculated for further use.

Code snippet for Accelerometer Calibration:

static void controller_comm__master_request_accel(controller_comm_s controller, controller_comm__role_e player_to_request) {
    controller_comm__message_s request_message = {0};
    request_message.recipient    = player_to_request;
    request_message.sender       = CONTROLLER_COMM__ROLE_MASTER;
    request_message.message_type = CONTROLLER_COMM__MESSAGE_TYPE_REQUEST_ACCEL_VAL;
    if (player_to_request == CONTROLLER_COMM__ROLE_PLAYER_1) {
        request_message.data = controller_comm__player_1_score;
    }
    else if (player_to_request == CONTROLLER_COMM__ROLE_PLAYER_2) {
        request_message.data = controller_comm__player_2_score;
    }
    controller_comm__send_message(controller, request_message);
    return;
}


Wireless Module:

XBee S1
For wireless communication we utilized the XBee S1 module. This module provides a "transparent" UART interface to the Master and Controller boards. The XBee S1 is designed by Digi Inc. and allows for relatively easy programming via the XTCU programming suite using a XBee USB programmer. This device internally uses 802.15.4 to wirelessly send UART frames between modules. Because UART is generally used as a point-to-point protocol, a custom protocol needed to be designed to allow broadcast and multi-node functionality. This can be observed in the controller_comm.h/c files in the repository. This protocol borrows concepts from UDP and TCP protocols. A transaction begins with a defined start byte(0xDE). This allows for better synchronization between frames comprised of multiple bytes. After a device has received a Start byte, it then receives and processes a header byte. This byte is a structured bit field that encodes the Sender, Recipient, and Message Type as shown below.
typedef enum {
    CONTROLLER_COMM__MESSAGE_COMPONENT_START_BYTE,
    CONTROLLER_COMM__MESSAGE_COMPONENT_RECIPIENT,
    CONTROLLER_COMM__MESSAGE_COMPONENT_SENDER,
    CONTROLLER_COMM__MESSAGE_COMPONENT_TYPE,
    CONTROLLER_COMM__MESSAGE_COMPONENT_DATA_BYTE1,
    CONTROLLER_COMM__MESSAGE_COMPONENT_DATA_BYTE2,
    CONTROLLER_COMM__MESSAGE_COMPONENT_STOP_BYTE,
} controller_comm__message_components_e;

typedef struct {
    controller_comm__role_e recipient;
    controller_comm__role_e sender;
    controller_comm__message_type_e message_type;
    uint16_t data;
} controller_comm__message_s;

typedef union {
	char byte;
	struct {
	    char             : 2;
	    char message_type: 2;
	    char sender:       2;
            char recipient:    2; 
	} __attribute__((packed));
} controller_comm__message_header_t;

When the Master requests the accelerometer positions from the controllers, it piggybacks the current scores in the same request message. This way the controllers' internal score states are always updated. If the controllers detect that the score has changed relatively to what it is aware of, it will write both the player 1 and player 2 scores to the I2C 7-seg display. The Sender and Recipient fields allow all the controller being addressed to reply accordingly to whichever node addressed it. Additionally, the Message Type encodes what kind of message is being sent as shown below. The "button pressed" event is encoded in its own message type. This lets the Master know if the button for a specific controller has been pressed since the last time the Master communicated with it.

typedef enum {
    CONTROLLER_COMM__MESSAGE_TYPE_REQUEST_ACCEL_VAL,
    CONTROLLER_COMM__MESSAGE_TYPE_SEND_ACCEL_VAL,
    CONTROLLER_COMM__MESSAGE_TYPE_SEND_ACCEL_VAL_BTN_PRESSED,
} controller_comm__message_type_e;

The goal of the controller_comm module was to provide a very abstract, easy-to-use interface that could easily be integrated into the game logic without having to understand in technicalities of the driver. As a result, the following simple APIs are exposed to the implementer.

controller_comm_s controller_comm__init(controller_comm__role_e role, uart_e uart, gpio_s gpio_tx, gpio_s gpio_rx);
uint16_t controller_comm__get_player_1_accel();
uint16_t controller_comm__get_player_2_accel();
bool controller_com__get_player_1_button();
bool controller_com__get_player_2_button();
controller_comm__controller_tilt_e controller_comm__get_player_1_tilt();
controller_comm__controller_tilt_e controller_comm__get_player_2_tilt();
bool controller_comm__update_player_score(controller_comm__role_e player, uint16_t score);
void controller_comm__freertos_task(void *controller_comm_struct);

Depending on the Message Type, the Data Bytes of the frame represent different information. As described above, in the CONTROLLER_COMM__MESSAGE_TYPE_REQUEST_ACCEL_VAL message type, the data bytes represent the scores for both controller. In the CONTROLLER_COMM__MESSAGE_TYPE_SEND_ACCEL_VAL and CONTROLLER_COMM__MESSAGE_TYPE_SEND_ACCEL_VAL_BTN_PRESSED message types, the data bytes represent the 12 bit accelerometer values. When being sent, the driver decomposes the uint16_t accelerometer value and recombines in upon receipt. After the data bytes are received, a Stop Byte (0xAD)is sent. This further helps with synchronization and if any of the bytes timeout or the Stop Byte is not valid, the transaction is aborted and will be retriggered. The Master polls the controllers every 25ms and as a result, there is little to no latency between controller movement, score updates, and visual screen feedback.

Tasks and Flow Control

State Machine



Software Design in Display module:

Master Task:
  • It controls the flow of the game.
  • INIT_STATE is the first state which creates all the tasks used in the game and then suspends them all till the controllers are connected.
  • Once the controllers are connected then we move to state IN_PROGRESS_STATE.
  • IN_PROGRESS_STATE takes care to resume the corresponding level tasks.
  • The score update is checked here to automatically shift from one level to another.
  • When a button provided on the controller is pressed then we move to the IN_PAUSE_STATE.
  • IN_PAUSE_STATE the score is updated on the led matrix until the button on the controllers is pressed again.
  • When the button on the controllers is pressed by both players to show that they are ready we move to the IN_PROGRESS_STATE again.
Paddle Task:
  • Individual paddle assigned to each player.
  • Testing of the paddle movement was done initially using the switches.
  • Controller accelerometer values supplied to the master using UART protocol were later used to control the movement of the paddle.
Ball Task:
  • Ball task is designed to take into account the velocity, direction and movement of the ball in the upper and lower half of the matrix.
  • It takes care of the cumulative score update and of restarting frame after each score update.
  • The logic of varying the velocity and angle of the ball upon the collision with the paddle is implemented.
  • If the paddle moves in the direction of the ball then the velocity of the ball is increased and decreased for the opposite case.
 Pacman Task:
  • Individual pacman task written for each level.
  • Pacman in first level moves along the edges and moves randomly. Whenever the ball comes in contact with the pacman then the score of the corresponding player will be updated.
  • Pacman in second level moves in the center along x-axis of the side assigned to the opponent and takes into account the direction and speed of the ball. Whenever the ball comes in contact with the pacman then the score of the corresponding player will be updated.
  • Pacman in third level moves in the the side assigned to the opponent in both x-axis and y-axis. It takes into account the direction and speed of the ball. Whenever the ball comes in contact with the pacman then the score of the corresponding player will be updated.
Controller Task:
  • If the "role" parameter of the controller is Master, the task polls each controller for their accelerometer values while also updating their internal score states and receiving button states. It also verifies that the responses are legitimate before updating the internal states.
  • If the "role" of the controller is Player 1 or Player 2, the task waits for requests from the Master node. Upon receiving this information, the task updates its internal states and writes this information to the 7-seg LED matrix. It then sends a proper response back to the master including accelerometer values and button press events.
Code Snippet for score the update to the controller done by the Ball task:
void set_players_score(uint8_t score_green, uint8_t score_blue) {
 uint16_t transfer_score = 0;
 green_player_score = score_green;
 blue_player_score = score_blue;
 transfer_score = (score_blue * 100) + score_green;
 printf("Transfer Score = %i\n", transfer_score);
 controller_comm__update_player_score(CONTROLLER_COMM__ROLE_PLAYER_2, transfer_score);
 controller_comm__update_player_score(CONTROLLER_COMM__ROLE_PLAYER_1, transfer_score);
}
Code Snippet for score the detecting collision with the pacman in Ball task:
   if (blue_collided == 1) {
     blue_collided = clear_blue_collided();
     score_blue += cummulative_score;
     set_players_score(score_green, score_blue);
     change_game_state_to = IN_SCORE_STATE;
     xQueueSend(*(ball_parameters.state_queue), &change_game_state_to, 0);
     cummulative_score = 1;
     SMACMAN__DEBUG_PRINTF("BLUE player score: %d   GREEN player score: %d\n", score_blue, score_green);
     set_blue_pacman_start();
     set_green_pacman_start();
     ball_setup(&ball);
   }
   if (green_collided == 1) {
     green_collided = clear_green_collided();
     score_green += cummulative_score;
     set_players_score(score_green, score_blue);
     change_game_state_to = IN_SCORE_STATE;
     xQueueSend(*(ball_parameters.state_queue), &change_game_state_to, 0);
     cummulative_score = 1;
     SMACMAN__DEBUG_PRINTF("BLUE player score: %d   GREEN player score: %d\n", score_blue, score_green);
     set_blue_pacman_start();
     set_green_pacman_start();
     ball_setup(&ball);
   }

Game Casing

Smacman is a two Player game. It is quiet inconvinient to play the game with all the wires and components lying around. So to facilitate this we 3D printed a basic enclosure for the two players controller as well as the Led matrix, which is big enough to accommodate zigbee module, MP3 Decoder and speakers, Master Board. We needed to put together all components in a single enclosure. The enclosure includes the spacing for SJTWO board with PCB mounted on top and the MP3 Decoder along with speakers.

Case Design
Casing
Circuit Along with all the components
Entire Game Casing

Testing & Technical Challenges

Pacman Movement Testing

Issues, Challenges and solutions to get past them

1. RGB LED Matrix Display driver

a) Setting up the LED Matrix was a bit of a challenge. Started with glowing a single row and column. Getting the initial hold on the exact row and column position was a bit tricky as one can easily get confuse on the starting and ending row and column. Once we got over it we moved on to design game objects separately.

b) Flickering of led matrix was solved by doing the following:

  • The frequency of refreshing the led matrix was increased to show smooth transition from one frame to another.
  • Led matrix is very sensitive to the inputs provided to it. Initially the flickering was thought to be because of the data lines and row selection lines. But later with further diagnoses it was discovered that the power supply line was creating issues. Changing the adapter eliminated the flickering.

c) As the game consists of many objects that exist simultaneously on the screen clearing the trail of each object was bit of a problem.

  • Initially clearing the LED matrix from time to time assigned to one task. But later each object dealt with its own clearance, which eliminated the effect of clearance of one object with the display of other objects.

d) Pacman and Ball detection of the bounadry:

  • Pacman object detecting the boundary and making the decision of moving left or right was a bit tricky. Started with one boundary condition and then proceeded with the rest boundary conditions.

e) Updating the movement of each object on the LED matrix was clumsy in the beginning as individual objects where updated as soon as a movement was detected in the object.

  • But when the frequency of refreshing rate was increased the update of the LED matrix was moved to one task which led to the smooth movement of the objects on the screen.

e) The varying Pacman velocity with the ball movement in the lower and the upper half of the matrix was difficult.

  • Set and get functions are used to get the updated position of the ball for each movement.
Game Frame
Score
Ball Testing


2. Challenges in the designing of the controller modules for the paddle movement and to get score from the master

a) SJ2 UART frames were corrupted while sending over XBEE.

  • The XBEE firmware parameters had to be carefully calibrated to match the SJ2 as well as using a 38400 bps bit rate. The SJ2 UART driver had to be slightly modified match as well.

b) UART is not meant to be a broadcast protocol.

  • A custom wireless broadcast/multi-node wireless protocol and driver had to be designed to be robust with dropped frames and packets being handled gracefully. (Described above)

3. Designing the MP3 Decoder

a) Earlier we used the ssp2 Driver written by Preet for SPI interfacing to write the data to MP3 Decoder. but somehow we were not able to detect the slave properly because of which we could not communicate with the decoder.

  • Resolution : We wrote our own simple SPI driver which sends the data to the MP3 Decoder for playing the music in background.

b) While reading data from the SD Card sometimes we faced few issues like hardware error occured in disk i/o and physical drive cannot work.

  • Resolution : The default driver was using mount later option for SD Card we changed it to mount immediately and the issue got resolved.

4. Dimensions of power pins for the PCB design

a) Accommodate the exact dimensions of the 5V Power pin on the PCB design.

  • Since there is no datasheet available for the 64x64 RGB LED Matrix, hence it was a difficult task. We used a basic approximation to determine the pin dimensions that would fit inside the PCB design such that it works just fine.


4. Integration Issues a) Controller Logic and Game Logic were developed independently.

  • Careful time and consideration had to be taken to ensure that the APIs were able to be stitched together. This actually ended up going smoother than we had anticipated. The Inter-board communication driver was designed to handle almost everything regarding the controllers, providing very abstract and simple APIs. This allowed for relatively smooth integration.

b) After game and controller logic, the main SJ2 did not have enough processing power to adequately driver the music functionality.

  • A standalone SJ2 board was used for the game soundtrack. This eased the load on our main board which was starting to effect our gameplay.

Conclusion

At the start, the concepts that we aimed to implement were not trivial but if accomplished, would lead to an awesome project. We realized early on that each one of us had particular strengths and that it would be critical for us to leverage these effectively. We effectively parallelized our tasks and made sure to regularly synchronize to avoid accruing too much technical debt. In the end, we were able to accomplish more features than we had initially devised. This project has helped us to gain more practical exposure to building a real time embedded systems and decompose and solve new problems that don't come from a textbook. We are thankful to Preet, who motivated and guided us through out the course work. This is surely going to help us for our interview preparation and in grabbing an internship.

Project Video

Project Testing Video

Project Video Link

Project Source Code

Acknowledgement

We would like to thank our Professor Preetpal Kang for all his awesome lectures and explanations of the embedded system topics, they could not have been explained any better than this! Not only did we enjoy working on this project but it also gave us an overall learning experience and precious life lessons. The extra knowledge in Preet's Class is surely going to help us to prepare for our interview preparations. We would also like to thank the ISA members for always being around and ready for helping whenever required.

References

WIRELESS CONTROLLER
MP3 DECODER
RGB LED Matrix Interfacing and Designing
General/Miscellaneous