Laser tag S06 – Display

Love Jesus, Love Code

Time: 30 minutes

In this section, we will aim to get the display working. The display will eventually be used to show the IP address for the device, the player name, give stats on health, ammo, clips, game time and display who shot you and how much damage they did. In this tutorial, we will program all of these things into the game, even though they are not all working yet.

For this tutorial, we will be using the 0.96 inch OLED IIC display (2 colour). These displays are quite neat and affordable.

Make an existing copy of the code from the last tutorial, saving it as: NodeMCU_LaserTag_S06

To get the display to work, you will need to install two libraries to your Arduino software:

  1. Adafruit SSD1306 library (2.3.1 works)
  2. Adafruit GFX library (1.10.10 works)

To install the libraries, in the Arduino IDE, go to Tools –> Manage Libraries. Then search and install the two libraries above. I have listed the versions that I know have worked in the past. Newer versions sometimes change things.

1. Call the libraries in the code

Add the following Code after variables have been declared the main program tab between the code for the web page and the //LittleFS library

// Set up OLED device
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

You will notice the code above calls the wire library which is for the IIC communication to the display, and the two libraries we installed previously. The code above also sets the size of the screen. We have 128 x 64 pixels to display. The SCREEN_WIDTH is 128 pixels and the SCREEN_HEIGHT is 64 pixels

2. Initialise the OLED display in the start routine

Add the following code to the start routine:

// Start the OLED display
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
  Serial.println(F("SSD1306 allocation failed"));
  debugInfo += "SSD1306 allocation failed for OLED display </br>";
}
else {
displayUpdate();
Serial.println("OLED display is now operational");
}

The above code simply checks to see if the display is working. If it is not, it will print a message to the serial monitor. Since we are likely to be doing troubleshooting through Wi-Fi and a webpage, we will add error messages to a variable called “debugInfo” that can be easily called later on.

The subroutine displayUpdate(); will be explained later.

3. displayUpdate() Subroutine

Make a new tab in Arduino called “Display”. We will include all of our code for the display subroutine in this section.

void displayUpdate(){
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.setTextSize(2);
  display.print(String("Hello World"));
  display.invertDisplay(true);
  //update display
  display.display();
}

The display should now say “Hello World”. The text goes over to the 2nd line. Since this is a 2 colour OLED the top two lines of text are yellow and the bottom lines are blue. The text size is 2 which means it takes up 2 text lines. You will notice that I have inverted the colours using the display.invertDisplay(true); command.

Some commands that we have used includes:

  • display.clearDisplay(); = all pixels are turned off
  • display.display(); = updates the display
  • display.setCursor(x,y); = sets the position for where the text should start writing

4. Display game data

Some important data to display on the screen include:

  • Player Name
  • Time left
  • Ammo
  • Health
  • Who just shot you (This will be programmed in at a later time)

We will need to declare some new variables to store these values. These will be stored in the main program just after the “// Game variables in order of Cloning Data” section.

// Current Data on Player
int shotsLeft = magSize; // Current ammunition
int health = respawnHealth; // Current health
int gameState = 2; // 0=game over, 1= in play, 2 = paused
int timeLeft = gameLength*60;
int clips = magazines; // current magazines

We will often start the game with a certain amount of health, or with a certain amount of clips. The original values need to be retained for respawning, so this will often involve two different variables, the respawn value and the current value.

Go back to the Display.ino tab and add the following code for the screen. You will also notice it is possible to change direction of text, and draw triangles and squares.

void displayUpdate(){
  // Clear the buffer.
  display.setRotation(0);
  display.clearDisplay();
  //display data to screen
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.setTextSize(2);
  display.print(String(health));
  display.write(3);
  display.print(String(magazines));
  display.write(15);
  display.print(String(shotsLeft));
  display.setCursor(0,16);
  // don't display player hit until player is hit
  if (damage[hitNo]>0){
    display.setTextSize(2);
    display.write(1);
    display.println(" " + String(playerName(player[hitNo])));
    display.write(3);
    display.print(" -" + String(damage[hitNo])); 
    Serial.println("player[hitNo] = " + String(player[hitNo]) + "hitNo = " + String(hitNo));
  }
  else{
    display.setTextSize(1);
    display.println(WiFi.localIP());
  }
  //Code to out zero in for seconds less than ten on display
  display.setTextSize(2);
  display.setCursor(12,48);
  if ((timeLeft % 60) < 10) display.println(""+ String(timeLeft / 60) +":0"+ timeLeft % 60);
  else display.println(""+ String(timeLeft / 60) +":"+ timeLeft % 60);
    
  display.drawTriangle(0, 48, 10, 48, 5, 55, WHITE);
  display.drawTriangle(0, 62, 10, 62, 5, 55, WHITE);
  display.setTextSize(1);
  display.fillRect(114,48,128,56,BLACK);
  display.setCursor(80,48);
  display.println(weaponName(fireSelect));
  display.setCursor(80,56);
  display.println(myGunDamage);
  display.setCursor(16,0);
  display.setRotation(1);
  display.println(playerName(playerID));
  display.display();
}

Compile and upload the binary

You will notice that the player name is “unknown” and the IP address is not displayed. This is because the screen was updated before these variables were read from the file. You could add another “displayUpdate();” at the end of the startup loop.

The display should look similar to below:

The screen shows that the health is 36, there are 99 shots and 99 clips. There are 12 minutes left in the game. The gun mode is fully automatic with a damage of 5. The player name is Gonzo. The IP address is 192.168.137.136.

Many of the values are simply placeholders that we will fix at a later time. The IP address is correct and will make it easier to find the IP address on your device once it connects to the network.

You will also notice that you can fit quite a lot of data on the screen. We have used big text for the important data.

Christian Content

Hebrews 8
3 Every high priest is appointed to offer both gifts and sacrifices, and so it was necessary for this one also to have something to offer.4 If he were on earth, he would not be a priest, for there are already priests who offer the gifts prescribed by the law. 5 They serve at a sanctuary that is a copy and shadow of what is in heaven. This is why Moses was warned when he was about to build the tabernacle: “See to it that you make everything according to the pattern shown you on the mountain.” 6 But in fact the ministry Jesus has received is as superior to theirs as the covenant of which he is mediator is superior to the old one, since the new covenant is established on better promises.

The screen we just created has a number of placeholders to show us what is to come. When programming, it is difficult to get everything working all at once. The technical word for this in programming is a stub. It is a piece of code that is a temporary substitute for yet-to-be-developed code. It allows part of the software solution to be tested without the whole project needing to be completed.

In the Bible, the final solution is not revealed immediately. God puts in place temporary solutions until the final solution is realised. In the Old Testament, God created the old priest system. The priest system works temporarily, but was never intended to be the final solution. Jesus was the final solution for the priest system. Once Jesus came as the final solution, there was no need to keep the old system. The old system was inferior, and did not actually do the job properly to start with, just like a software stub. So when Jesus came, the old priest and sacrificial system became obsolete and needed to be put aside to make way for the new and better system.

This is why the Bible is divided up into two testaments. At one time, God’s people lived under the Old Testament, but once Jesus came, God’s people needed to start living under the New Testament. The New Testament still speaks highly of the Old Testament, so while the sacrificial system of the Old Testament is obsolete, it still teaches us about how God has worked through history, what God is like, and what we are like, and how we can serve God and others. The Old Testament also teaches us about the old sacrificial system, which helps us better understand the New Testament. The New Testament contains a better way, with Jesus being the sacrifice for our sins, and God giving us a new heart that makes us want to serve him.

Leave a Reply

%d bloggers like this: