Laser Tag S05 – File System & MilesTagClone

Love Jesus, Love Code

Time 30

Make a copy of the code from the last tutorial and save it as “NodeMCU_LaserTag_S05”.

During a laser tag game, it is important for laser tag guns to have unique player IDs and be assigned to teams. If everyone had the same player name or team ID, it would be very difficult to keep track of who killed who. You may have noticed that when you reset your NodeMcu board, the player ID and team ID revert back to their original settings.

In theory, you could have unique player names by:

1. Accessing the web interface each time you boot up the gun (not ideal).
2. Modifying the player ID and team ID in the program, recompiling, and uploading it for each gun.

However, there is a better way to store the player ID and team ID using the nodeMCU file system with littleFS. This file system allows values to remain even after firmware updates. This makes it simple to update your entire gun collection by using the same firmware, while each gun keeps its unique player ID.

You can read more about the file system here: https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html

Using LittleFS

LittleFS is already included in the core files for nodeMCU, so there is no need to install it separately into the Arduino software. Since we will be creating the files within the program instead of uploading them to the file system, there is also no need to install the upload tool.

In the tab labelled NodeMCU_LaserTag_05, right below the code for the web page, add the following code to load the LittleFS library.

// LittleFS library
#include "LittleFS.h"

In the Arduino sketch, in to the same tab locate the setup routine. Insert the code setupFileSystem(); below the line that contains: setupWebActions();

Let’s tidy up the setup loop to ensure a smoother flow:

void setup() {
  Serial.begin(74880);   // Serial communication set up to help with debugging. 
  setupLEDStrip();
  setupOTA();
  setupWebActions();
  setupFileSystem();
}

The subroutine “initiliseFileSystem()” has not been created yet. We will create it in another tab called “FileSystem”.

First, we need to set up the file system to ensure everything is working properly. After that, we can proceed to read from the file. If LittleFS.begin() returns TRUE, it means that the file system has been mounted correctly and we can proceed with reading and writing files. Please add the following code:

void setupFileSystem(){

  if(LittleFS.begin()){
    readPlayerFile();
  }
  else{
    Serial.println("An Error has occurred while mounting LittleFS");
    debugInfo += "An Error has occurred while mounting LittleFS <br>";
  }
}

Read player.txt

A flowchart illustrating the process of reading the file has been provided below:

Flowchart: reading the file

When the data file does not exist on the system, the program will retain the existing values and not update them from memory. Processing of the file only occurs when it is present.

Below is a representation of the data stored in the player.txt file. In this particular case, the player is represented in hexadecimal, so 0F in hexadecimal is equivalent to 15 in decimal. You can calculate it manually or use a hex calculator like the one found at: https://www.calculator.net/hex-calculator.html. In the MilesTag game, player 15 is named “Click”. In the example below, the teamID is set to 0, indicating the red team.

File Contents

We will add the following code that reads the data into the tab “FileSystem” beneath the code mentioned above. This code will then retrieve the playerID and TeamID from the stored file.

void readPlayerFile(){// read file player.txt
//The first byte will be the playerID, the second byte will be the teamID
  File playerFile = LittleFS.open("player.txt", "r");
  String playerFileContents;
  if (playerFile) {
    playerFileContents = playerFile.readString();
    Serial.print ("saved file contents: ");
    Serial.println(playerFileContents);
    //convert file string to variables
    playerID = strtol(playerFileContents.substring(0,2).c_str(), NULL, 16);
    Serial.println("playerID: " + String(playerID));
    teamID = strtol(playerFileContents.substring(2,4).c_str(), NULL, 16);
    Serial.println("teamID: " + String(teamID));
  }
  else {
    Serial.print("No Player Data");
  }
  playerFile.close();
}

I have used println statements, which will output any errors to the console screen. On the first run, we would expect the serial output to be “No Player Data”.

The file content is read into a string called “playerFileContents”

The string should contain two values, each with 2 digits. We get the first two digits by using the substring method. “.substring(0,2)”. The second two digits are obtained by using “.substring(2,4)”.

At this stage there is no data in the file player.txt. You could upload it through the Arduino IDE, but instead of doing that, we will just write to the player.txt file every time the playerID or teamID is changed.

Write player.txt

Using the command LittleFS.open(“player.txt”, “w+”), the “w+” will write to a file and, if the file does not exist, it will create a new one. The file is identified as “player.txt”.

When writing the player number and the team ID to the file, they need to occupy exactly 2 digits. This means that numbers less than 15 (0-F) in Hex need to have a leading 0 added. We will need to check player IDs to see if they require a leading 0.

The print command is used to add to the file. Please note the difference between “playerFile” and “Serial” in the code below.

void writePlayerFile(){ // write to file or add new file called player.txt. 
  // The first byte will be the playerID, the second byte will be the teamID
  File playerFile = LittleFS.open("player.txt", "w+");
  if (playerID <= 0x0F){ // this needs to be two digits
    playerFile.print("0"); // this needs to be two digits
    }
  playerFile.print(playerID, HEX);
  playerFile.print("0"); // TeamID always less than 2 Hex digits
  playerFile.print(teamID, HEX);
  playerFile.close();
    // test saved file
  playerFile = LittleFS.open("player.txt", "r");
  if (playerFile) {
    Serial.print ("saved file contents: ");
    Serial.println(playerFile.readString());
  }
  else {
    Serial.println("No Player Data stored");
  }
}

Command to update the file

We currently have a web page where we can update the playerID and teamID, but it does not store this information in long-term memory. Every time we receive input from the web page, we need to update the file in long-term memory.

To accomplish this, please navigate to your WebActions tab and add the command writePlayerFile(); so that it will be called after each web server /get request.

// Send a GET request for the form
  server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    // GET inputPlayerID from web form
    if (request->hasParam("inputPlayerID")) {
      inputMessage = request->getParam("inputPlayerID")->value();
      playerID = inputMessage.toInt();
    }
    // GET inputPlayerTeam
    if (request->hasParam("inputTeamID")) {
      inputMessage = request->getParam("inputTeamID")->value();
      teamID = inputMessage.toInt(); 
      teamColour(); // make sure team colour is updated
    }
    writePlayerFile();
  });
}

Update the firmware

In the Arduino IDE, go to Sketch –> Export Compiled Binary, and then export the compiled BIN file.

You should observe that the player name and the teamID remain unchanged on the web page even when the Arduino is rebooted. The only problem is that the player colour remains yellow. This is because the file is loaded after the LED strip is set up. To resolve this, we need to modify the order in our setup loop so that setupFileSystem(); occurs before setupLEDStrip();. I am confident that you can figure out how to correct this!

Christian Content

22 But be doers of the word and not hearers only, deceiving yourselves. 23 Because if anyone is a hearer of the word and not a doer, he is like a man looking at his own face in a mirror. 24 For he looks at himself, goes away, and immediately forgets what kind of man he was. 25 But the one who looks intently into the perfect law of freedom and perseveres in it, and is not a forgetful hearer but one who does good works—this person will be blessed in what he does.

James 1:22–25 (HCSB)

If our laser tag guns cannot remember their identity, it leads to chaos of various kinds. The game will not function properly, reducing the guns to mere mindless $10 toy guns.

The Bible emphasizes the importance of putting its teachings into practice. We must not only remember who we are, but also how to apply the Bible in our daily lives. This leads to good deeds and blessings in all that we do.

It is astonishing how this verse speaks so positively about the Bible. It is described as the perfect law of freedom. Many people view God’s word as a law that enslaves us. However, sin is deceptive. It is sin that enslaves us and compels us to continue sinning, whereas obeying God’s law liberates us from the bondage of sin. Sometimes, we forget the true goodness of God and his word, and this becomes a genuine issue with our own memory.

Leave a Reply