Laser Tag S05 – File System

Love Jesus, Love Code

Time 30

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

During a laser tag game, it is important for laser tag guns to have unique playerID and to be allocated to a team. If everyone had the same player name or team ID it would be very difficult to keep track of who killed who. You might have noticed that when you reset your NodeMcu board, the playerID and teamID return back to the original settings once the NodeMCU board is reset.

You could in theory have unique player names by:

  1. Accessing the web interface each time you boot up the gun (not ideal)
  2. For each gun, modify the PlayerID and teamID in the program, recompile and upload.
  3. keep reading and find out a better way

A better way is to store the playerID and teamID in the nodeMCU file system using littleFS. The file system allows values to remain, even after you update the firmware. This makes an update to your entire gun collection super simple, as you can use the same firmware, and each gun will keep it’s unique playerID.

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

Using LittleFS

LittleFS is included in the core files for nodeMCU so it does not need to be installed into the Arduino software, it is already there. We will not be uploading files to the file system, but rather creating the files within the program. This means we do not need to install the upload tool either.

Just under the code for the web page in the tab labelled NodeMCU_LaserTag_05 add the following code to load the littleFS library.

// LittleFS library
#include "LittleFS.h"

In the same tab in Arduino, scroll down to the setup routine and put in the code setupFileSystem(); under the line with the content: setupWebActions();

We will clean up the setup loop so it looks like:

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

The subroutine initiliseFileSytem(); has not been created yet. We will put this into another tab. Create a tab called “FileSystem”.

We will need to setup the file system first, to make sure everything is working, then read the from the file. If LittleFS.begin() returns TRUE, then the file system is mounted correctly, and we can proceed to read and write files. 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 for how the file is read has been drawn below:

Flowchart: reading the file

When the data file does not exist on the system, the existing values from the program will be kept and not updated from memory. It is only when the data file is present, then the file will be processed.

Below is a representation of the data stored in the player.txt file. In the case below the player is in Hexidecimal, so 0F in hexadecimal equals 15 in decimal. You could work it out manually or use a hex calculator like what you find at: https://www.calculator.net/hex-calculator.html . In the MilesTag game player 15 has the name “Click”. In the example below, the teamID is equal to 0 and is the red team.

File Contents

We will add the following code that reads the data into the tab “FileSystem” under the code from above. This code will then seek to read the playerID and TeamID from the sotred 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 we get 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 rather than 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 being “player.txt”.

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

The print command use used to add to the file. Notice 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 in long term memory. Every time we take input from the web page, we should update the file in long term memory.

Navigate to your WebActions tab and add the command writePlayerFile(); so 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

On the Arduino IDE got to Sketch –> Export Compiled Binary, then export the compiled BIN file.

You should notice that the player name and the teamID remain the same on the web page when the Arduino is rebooted. The only issue is that the player colour stays yellow. This is because the file is loaded after the LED strip is setup. To fix this we need to change the order in our setup loop so that setupFileSystem(); happens before setupLEDStrip();. I’m sure you can work out how to fix this!

New section: Save Milestag variables to file? Here or somewhere else?

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 who they are, then it causes all kinds of chaos. The game won’t work correctly, and the guns will become nothing more than a mindless $10 toy gun.

The Bible reminds us that when we read the Bible, we are to put it into practice. We need to remember who we are, and how to apply the Bible in our lives. The result is good works, and being blessed in what you do.

It is amazing that this verse talks so positively about the Bible. It is called the perfect law of freedom. Many people see the word of God as a law of slavery. But sin is deceptive. Sin is the one that enslaves us to keep on sinning, but obeying God’s law frees as from the slavery of sin. Sometimes we forget how good God and his word actually is, and this is a real problem with our own memory.

Leave a Reply