Reading a json file from the NodeMCU's SPIFFS

I am working on a smart IR receiver that reads an IR signal, decodes it and saves that decoded info in a json file stored on the NodeMCU’s SPIFFS. This info is then displayed on the embedded web server in a table.

Now my problem is that even though I correctly save the signal’s info in the json file (and when I try to print it in console it looks perfect), the son file is not correctly passed to my web server and the table remains empty. I am using jQuery to read the file and make the changes in the HTML. All the web server files (HTML, css & JavaScript) were tested first on my local machine with an example Json file of the same structure I am using on my SPIFFS so I know for sure the problem is not in there.

It is worth noting that I faced this problem when developing on my local machine, where the table would remain empty even though the json file is not, but it was fixed when I used the Live Server extension on VSCode. However, I don’t understand if this is the problem how is it that there is no server to pass the json file on when all my web interface files are correctly passed and loaded from the SPIFFS.

I will include a portion of my code, and of course all help is appreciated.

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WiFiMulti.h> 
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>
#include<FS.h>   // Include the SPIFFS library
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <ArduinoJson.h>

ESP8266WiFiMulti wifiMulti;     // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'

ESP8266WebServer server(80);    // Create a webserver object that listens for HTTP request on port 80

String getContentType(String filename); // convert the file extension to the MIME type
bool handleFileRead(String path);       // send the right file to the client (if it exists)

// Declaring a variable to hold JSON object size
//const size_t capacity = JSON_OBJECT_SIZE(10);

// Creating a JSON document to hold signal entry
//DynamicJsonDocument doc(capacity);

// Declaring array size
const size_t array_capacity = JSON_ARRAY_SIZE(16) + JSON_OBJECT_SIZE(1) + 16*JSON_OBJECT_SIZE(9);

// Creating a JSON document to hold all signal entries in a nested array
DynamicJsonDocument array_doc(array_capacity);

// Creating a nested JSON array to hold all signal entries
JsonArray signals = array_doc.createNestedArray("signals");

// Declaring a JSON object to hold single signal entry
JsonObject signal_entry;

// Declaring method that save JSON object to JSON file stored on SPIFFS
void saveJSON();

// Declaring method that prints JSON file stored on SPIFFS
void printJSON();

void setup(void){
  Serial.begin(115200);         // Start the Serial communication to send messages to the computer
  delay(10);
  Serial.println('\n');

  Serial.println("Connecting ...");
  int i = 0;
  while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above
    delay(250);
    Serial.print('.');
  }

SPIFFS.begin();                           // Start the SPI Flash Files System 
   
  server.begin();                           // Actually start the server
  Serial.println("HTTP server started");

  server.onNotFound([]() {                              // If the client requests any URI
    if (!handleFileRead(server.uri()))                  // send it if it exists
      server.send(404, "text/plain", "404: Not Found"); // otherwise, respond with a 404 (Not Found) error
  });

void loop(void){
  server.handleClient();                    // Listen for HTTP requests from clients

//Creating a JSON object from the JSON document
    signal_entry = signals.createNestedObject();

    //Assigning correct values to keys in JSON object
    signal_entry["date"] = currentDate;
    signal_entry["protocol_type"] = protocol_type;
    signal_entry["bitNr_device"] = bitNr_device;
    signal_entry["bitNr_op"] = bitNr_op;
    signal_entry["codeDec_device"] = codeDec_device;
    signal_entry["codeDec_op"] = codeDec_op;
    signal_entry["codeHex_device"] = codeHex_device;
    signal_entry["codeHex_op"] = codeHex_op;
    signal_entry["codeBin_device"] = codeBin_device;
    signal_entry["codeBin_op"] = codeBin_op;
    
    serializeJsonPretty(signal_entry, Serial); 
    Serial.println("");
    
    saveJSON();
   }
}

String getContentType(String filename) { // convert the file extension to the MIME type
  if (filename.endsWith(".html")) return "text/html";
  else if (filename.endsWith(".css")) return "text/css";
  else if (filename.endsWith(".js")) return "application/javascript";
  else if (filename.endsWith(".ico")) return "image/x-icon";
  return "text/plain";
}

bool handleFileRead(String path) { // send the right file to the client (if it exists)
  Serial.println("handleFileRead: " + path);
  if (path.endsWith("/")) path += "index.html";         // If a folder is requested, send the index file
  String contentType = getContentType(path);            // Get the MIME type
  if (SPIFFS.exists(path)) {                            // If the file exists
    File file = SPIFFS.open(path, "r");                 // Open it
    size_t sent = server.streamFile(file, contentType); // And send it to the client
    file.close();                                       // Then close the file again
    return true;
  }
  Serial.println("\tFile Not Found");
  return false;                                         // If the file doesn't exist, return false
}

void saveJSON(){
  File jsonFile = SPIFFS.open("signals.json", "w");
  if (serializeJsonPretty(array_doc, jsonFile) == 0) {
    Serial.println("Failed to write to file");
  }
  jsonFile.close();
}

void printJSON(){
  uint8_t* pBuffer;
  File testFile = SPIFFS.open("signals.json", "r");
  if(testFile){
    unsigned int fileSize = testFile.size();
    pBuffer = (uint8_t*)malloc(fileSize + 1);
    testFile.read(pBuffer, fileSize);
    pBuffer[fileSize] = '\0';
    Serial.println((char*)pBuffer);                // Print the file to the serial monitor.
    testFile.close();
  }
  else{
    Serial.println("Failed to read to file");
  }
  free(pBuffer);
}

esp8266

When you go to "http://your-server/signals.json" does it show you the JSON data correctly?

Could you post your jQuery/HTML code as well?

Also try adding the content type for JSON as well. In your getContentType() function:

else if (filename.endsWith(".json")) return "application/json";

I added json to my getContentType function, but it didn’t fix it.
I’ll attach my HTML and jQuery code here as well

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="author" content="Faberdash">

    <title>IR Signal Analyzer</title>

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
    <link href="./style.css" rel="stylesheet">

</head>
<body>
    <div class="container">
        <div class="header clearfix">
            <nav>
                <ul class="nav nav-pills pull-right">
                    <li role="presentation" class="active"><a href="index.html">Home</a></li>
                </ul>
            </nav>
            <h2 class="text-muted"> <span id="signature">IR</span> Signal Analyzer</h2>
        </div>
        <div class="recieve-msg">
            <h3 id="recieve-state">IR sensor ready to recieve signal...</h3>
        </div>
        

        <div class="jumbotron">
            <div>
                <table class="table table-striped table-dark" id="signal-log">
                    <thead id="table-head">
                        <tr>
                        <th scope="col">Time recieved</th>
                        <th scope="col">Protocol Type</th>
                        <th scope="col">Number of bits (Address Code)</th>
                        <th scope="col">Number of bits (Operation Code)</th>
                        <th scope="col">Address Code (DEC)</th>
                        <th scope="col">Operation Code (DEC)</th>
                        </tr>
                    </thead>
                    <tbody>
                        
                    </tbody>
                </table>
                <button type="button" class="btn btn-info" id="second-layer"> More Info </button>
                <button type="button" class="btn btn-outline-danger" id="clear"> Clear Log </button>
            </div>
        </div>

        <footer class="footer">
            <p>&copy; 2020 Faberdash</p>
          </footer>

    </div> 



    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
    <script src="./main.js"></script>
</body>
</html>
$(document).ready(function () {
    // Reading JSON file 
    $.getJSON("../signals.json", function (data) {
        var signals_list = data.signals;    // Store list of JSON objects in a variable

        var arrItems = [];  // The array to store all signal entries
        $.each(signals_list, function (index, value) {
            arrItems.push(value);       // Push the values inside the array
        });

        // Extract elements' keynames 
        var col = [];
        for(var i = 0; i < arrItems.length; i++){
            for (var key in arrItems[i]){
                if (col.indexOf(key) === -1) {
                    col.push(key);
                }
            }
        }

        var table = document.getElementById("signal-log");  // Get the signals' table by ID

        // Add signals' info dynamically to the table as rows
        for(var i = 0; i < arrItems.length; i++){
            var tr = table.insertRow(-1);

            for(var j = 0; j < 6; j++){
                var tableCell = tr.insertCell(-1);
                //var reading = col[j];
                //tableCell.innerHTML = arrItems[i].reading.value;
                tableCell.innerHTML = arrItems[i][col[j]];
            }
        }
    });
});

I just copied your files to a local web server and they seem to work fine. Unless there's something wrong with your json data or how it's saved on the NodeMCU.

You didn't answer my first question, when you open the json file manually does it work? Open your browser and go to "nodemcu-ip/signals.json", does it show you the correct json data?

Also try removing the dots from the beginning of your jQuery request:

$.getJSON("/signals.json", function (data) {

They are not necessary because they try to go one directory backwards.

To see if there are any errors open your browser's "Developer Tools" and check the "console". If there are any javascript errors they will be shown in the "console" tab of the developer tools. Also check the "network" tab. Any requests you make will be shown in the "network" tab so if any of your requests are failing it will be shown there. (You might have to refresh the page after enabling the developer tools for your requests to show up in the network tab).
If you don't know how to open the developer tools an internet search will tell you how to do it based on your browser.