How to add .js and .css files in a arduino-SD card hosted website ?

I don't really know if this is the best place to put this- I hesitated betwee network, interfacing and storage as my projet is about the use of an Arduino Due with a micro SD card, on which my website is loaded, and an ethernet shield.

My problem is :

I just want to add a .js and a .css file to my SD card to implement style and some common functions for my website, and use these files for each web page . But this seems not to work, as i tried to call them on each page (in ) : (files are called fonctionExterne.js and stylesheet.css)

<script type="text/javascript" src="fonctionExterne.js"></script> 
<link rel="stylesheet" type="text/css" href="stylesheet.css">

So the files are not used properly ; i added in my arduino code the following lines to call them, just after calling .htm pages (the pages are called properly if i add the functions included in my javascript page directly in .htm pages) :

File myFile = SD.open("fonctionExterne.js");
            if (myFile) {
              while (myFile.available()) {
                client.write(myFile.read());
              }
              myFile.close();
            }
          
                  File myFile1 = SD.open("stylesheet.css");
            if (myFile1) {
              while (myFile1.available()) {
                client.write(myFile1.read());
              }
              myFile1.close();
            }

So could someone explain me how to do this properly? (just add .js and .css files in a arduino-SD card hosted website )
Thank you very much !

So could someone explain me how to do this properly?

I'm sure they can at http://shippets-r-us.com. Here, strangely, we need to see ALL of your code.

But this seems not to work

Without knowing what actually happens, and what you expect to happen, and what you've written the code to make happen, all we can do is offer condolences. So, consider them offered.

Alright. I was considering not to flood this page, so here is all my code (arduino side) :

The interesting part is between the /////////// marks

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
// size of buffer used to capture HTTP requests
#define REQ_BUF_SZ   60

// MAC address from Ethernet shield sticker under board
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 200, 20); // IP address, may need to change depending on network
EthernetServer server(80);  // create a server at port 80
File webFile;               // the web page file on the SD card
char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0;              // index into HTTP_req buffer
boolean LED_state[4] = {0}; // stores the states of the LEDs

void setup()
{
    // disable Ethernet chip
   // pinMode(10, OUTPUT);
   // digitalWrite(10, HIGH);
    
    Serial.begin(9600);       // for debugging
    
    // initialize SD card
    Serial.println("Initializing SD card...");
    if (!SD.begin(4)) {
        Serial.println("ERROR - SD card initialization failed!");
        return;    // init failed
    }
    Serial.println("SUCCESS - SD card initialized.");
    // check for index.htm file
    if (!SD.exists("index.htm")) {
        Serial.println("ERROR - Can't find index.htm file!");
        return;  // can't find index file
    }
    Serial.println("SUCCESS - Found index.htm file.");
    // switches on pins 2, 3 and 5
    pinMode(2, INPUT);
    pinMode(3, INPUT);
    pinMode(5, INPUT);
    // LEDs
    pinMode(13, OUTPUT);
    pinMode(7, OUTPUT);
    pinMode(8, OUTPUT);
    pinMode(9, OUTPUT);
    
    Ethernet.begin(mac, ip);  // initialize Ethernet device
    server.begin();           // start to listen for clients
}

void loop()
{
    EthernetClient client = server.available();  // try to get client

    if (client) {  // got client?
        boolean currentLineIsBlank = true;
        while (client.connected()) {
            if (client.available()) {   // client data available to read
                char c = client.read(); // read 1 byte (character) from client
                // limit the size of the stored received HTTP request
                // buffer first part of HTTP request in HTTP_req array (string)
                // leave last element in array as 0 to null terminate string (REQ_BUF_SZ - 1)
                if (req_index < (REQ_BUF_SZ - 1)) {
                    HTTP_req[req_index] = c;          // save HTTP request character
                    req_index++;
                }
                // last line of client request is blank and ends with \n
                // respond to client only after last line received
                if (c == '\n' && currentLineIsBlank) {
                    // send a standard http response header
                    client.println("HTTP/1.1 200 OK");
                    // remainder of header follows below, depending on if
                    // web page or XML page is requested
                    // Ajax request - send XML file
                    if (StrContains(HTTP_req, "ajax_inputs")) {
                        // send rest of HTTP header
                        client.println("Content-Type: text/xml");
                        client.println("Connection: keep-alive");
                        client.println();
                        SetLEDs();
                        // send XML file containing input states
                        XML_response(client);
                    }
                    else {  // web page request
                        // send rest of HTTP header
                        client.println("Content-Type: text/html");
                        client.println("Connection: keep-alive");
                        client.println();
                        // send web page
                     // open web page depending on request :
                    
                      if (StrContains(HTTP_req, ".htm"))      { //if we are already on the website
                        
                      String str(HTTP_req);//convert HTTP_req into a string
                      String html(".htm");//identify .htm as a string
                      String adress = str.substring(5, str.lastIndexOf(html)+4);//isolate the web page adress
                      char page[str.lastIndexOf(html)-5];//create a char at desired size
                      adress.toCharArray(page, str.lastIndexOf(html));//convert previously acquired web page adress into a char
                      webFile=SD.open(page);//open page
                      }
                      else webFile=SD.open("index.htm");//in other case (first access to website for example), directly access to index
                    
                     if (webFile) {
                            while(webFile.available()) {
                                client.write(webFile.read()); // send web page to client
                            }
                            webFile.close();
                        }
                        /////////////////////////
           File myFile = SD.open("fonctionExterne.js");
            if (myFile) {
              while (myFile.available()) {
                client.write(myFile.read());
              }
              myFile.close();
            }
          
                  File myFile1 = SD.open("stylesheet.css");
            if (myFile1) {
              while (myFile1.available()) {
                client.write(myFile1.read());
              }
              myFile1.close();
            }
                //////////////////////////// 
                    }
                    // display received HTTP request on serial port
                    Serial.print(HTTP_req);
                    // reset buffer index and all buffer elements to 0
                    req_index = 0;
                    StrClear(HTTP_req, REQ_BUF_SZ);
                    break;
                }
                // every line of text received from the client ends with \r\n
                if (c == '\n') {
                    // last character on line of received text
                    // starting new line with next character read
                    currentLineIsBlank = true;
                } 
                else if (c != '\r') {
                    // a text character was received from client
                    currentLineIsBlank = false;
                }
            } // end if (client.available())
        } // end while (client.connected())
        delay(1);      // give the web browser time to receive the data
        client.stop(); // close the connection
    } // end if (client)
}


    return 0;
}

I removed a useless part of code at the end, because too long post elsewise

My html source code :

<!DOCTYPE html>
<html>
    <head>
        <title>Arduino Ajax I/O</title>
    <script type="text/javascript" src="fonctionExterne.js"></script> 
     <link rel="stylesheet" type="text/css" href="stylesheet.css">
        <script>
		 var smonip ="192.168.240.1"; 
		strLED1 = "";
		strLED2 = "";
		strLED3 = "";
		strLED4 = "";
		var LED3_state = 0;
		var LED4_state = 0;
		function GetArduinoIO()
		{
			nocache = "&nocache=" + Math.random() * 1000000;
			var request = new XMLHttpRequest();
			request.onreadystatechange = function()
			{
				if (this.readyState == 4) {
					if (this.status == 200) {
						if (this.responseXML != null) {
							// XML file received - contains analog values, switch values and LED states
							var count;
							// get analog inputs
							var num_an = this.responseXML.getElementsByTagName('analog').length;
							for (count = 0; count < num_an; count++) {
								document.getElementsByClassName("analog")[count].innerHTML =
									this.responseXML.getElementsByTagName('analog')[count].childNodes[0].nodeValue;
							}
							// get switch inputs
							var num_an = this.responseXML.getElementsByTagName('switch').length;
							for (count = 0; count < num_an; count++) {
								document.getElementsByClassName("switches")[count].innerHTML =
									this.responseXML.getElementsByTagName('switch')[count].childNodes[0].nodeValue;
							}
							// LED 1
							if (this.responseXML.getElementsByTagName('LED')[0].childNodes[0].nodeValue === "checked") {
								document.LED_form.LED1.checked = true;
							}
							else {
								document.LED_form.LED1.checked = false;
							}
							// LED 2
							if (this.responseXML.getElementsByTagName('LED')[1].childNodes[0].nodeValue === "checked") {
								document.LED_form.LED2.checked = true;
							}
							else {
								document.LED_form.LED2.checked = false;
							}
							// LED 3
							if (this.responseXML.getElementsByTagName('LED')[2].childNodes[0].nodeValue === "on") {
								document.getElementById("LED3").innerHTML = "LED 3 is ON (D8)";
								LED3_state = 1;
							}
							else {
								document.getElementById("LED3").innerHTML = "LED 3 is OFF (D8)";
								LED3_state = 0;
							}
							// LED 4
							if (this.responseXML.getElementsByTagName('LED')[3].childNodes[0].nodeValue === "on") {
								document.getElementById("LED4").innerHTML = "LED 4 is ON (D9)";
								LED4_state = 1;
							}
							else {
								document.getElementById("LED4").innerHTML = "LED 4 is OFF (D9)";
								LED4_state = 0;
							}
						}
					}
				}
			}
			// send HTTP GET request with LEDs to switch on/off if any
			request.open("GET", "ajax_inputs" + strLED1 + strLED2 + strLED3 + strLED4 + nocache, true);
			request.send(null);
			setTimeout('GetArduinoIO()', 1000);
			strLED1 = "";
			strLED2 = "";
			strLED3 = "";
			strLED4 = "";
		}
		// service LEDs when checkbox checked/unchecked
		function GetCheck()
		{
			if (LED_form.LED1.checked) {
				strLED1 = "&LED1=1";
			}
			else {
				strLED1 = "&LED1=0";
			}
			if (LED_form.LED2.checked) {
				strLED2 = "&LED2=1";
			}
			else {
				strLED2 = "&LED2=0";
			}
		}
		function GetButton1()
		{
			if (LED3_state === 1) {
				LED3_state = 0;
				strLED3 = "&LED3=0";
			}
			else {
				LED3_state = 1;
				strLED3 = "&LED3=1";
			}
		}
		function GetButton2()
		{
			if (LED4_state === 1) {
				LED4_state = 0;
				strLED4 = "&LED4=0";
			}
			else {
				LED4_state = 1;
				strLED4 = "&LED4=1";
			}
		}
		
		
		
		
function demandepage(value,smonip)
  { 
  
  //alert(' demandepage ' + value + 'smonip '+ smonip );
  var Scommande = value + "?login=moi&Ip=" + smonip;
 self.location.href=Scommande; 

  }/**/
	</script>
	<style>
		.IO_box {
			float: left;
			margin: 0 20px 20px 0;
			border: 1px solid blue;
			padding: 0 5px 0 5px;
			width: 120px;
		}
		h1 {
			font-size: 120%;
			color: blue;
			margin: 0 0 10px 0;
		}
		h2 {
			font-size: 85%;
			color: #5734E6;
			margin: 5px 0 5px 0;
		}
		p, form, button {
			font-size: 80%;
			color: #252525;
		}
		.small_text {
			font-size: 70%;
			color: #737373;
		}
	</style>
    </head>
    <body onload="GetArduinoIO()">
	
	
	<button id="page1"      onclick="demandepage('Page1.htm',smonip);"      >Page 1</button>
    <button id="page2"      onclick="demandepage('Page2.htm',smonip);"      >Page 2</button>



        <h1>Arduino Ajax I/O</h1>
        <div class="IO_box">
			<h2>Analog Inputs</h2>
			<p class="small_text">A0 used by Ethernet shield</p>
			<p class="small_text">A1 used by Ethernet shield</p>
			<p>A2: <span class="analog">...</span></p>
			<p>A3: <span class="analog">...</span></p>
			<p>A4: <span class="analog">...</span></p>
			<p>A5: <span class="analog">...</span></p>
		</div>
		<div class="IO_box">
			<h2>Switch Inputs</h2>
			<p class="small_text">D0: used by serial RX</p>
			<p class="small_text">D1: used by serial TX</p>
			<p>Switch 1 (D2): <span class="switches">...</span></p>
			<p>Switch 2 (D3): <span class="switches">...</span></p>
			<p class="small_text">D4: used by Ethernet shield</p>
			<p>Switch 3 (D5): <span class="switches">...</span></p>
		</div>
		<div class="IO_box">
			<h2>LEDs Using Checkboxes</h2>
			<form id="check_LEDs" name="LED_form">
				<input type="checkbox" name="LED1" value="0" onclick="GetCheck()" />LED 1 (D13)


				<input type="checkbox" name="LED2" value="0" onclick="GetCheck()" />LED 2 (D7)


			</form>
		</div>
		<div class="IO_box">
			<h2>LEDs Using Buttons</h2>
			<button type="button" id="LED3" onclick="GetButton1()">LED 3 is OFF (D8)</button>


			<button type="button" id="LED4" onclick="GetButton2()">LED 4 is OFF (D9)</button>


			<p class="small_text">D10 to D13 used by Ethernet shield</p>
		</div>
    </body>
</html>

So here we are ; my point is to use a .js file ("fonctionExterne") to contain some functions such as "demandepage", which is used to navigate from a page to another, as well as a common stylesheet (.css) file.

So I tried to call these file at beginning of html code, as well as to open them in Arduino code (between the two "/////////////" marks). But this is'nt working, so is there a way to do this?

Thanks Paul :wink:

This code will serve .js and .css files from a SD card. Maybe you can "borrow" what you need from it.
http://playground.arduino.cc/Code/WebServerST

Bear in mind, the file names must be an 8.3 format.

Thanks for the help Tim, but after taking a close look at your link, i didnt found what i am looking for :~

SuperLM:
Thanks for the help Tim, but after taking a close look at your link, i didnt found what i am looking for :~

Then you must be more specific. I have tested that code with html code similar to yours, and the web browser downloads all the included files, like .js, .css, and .ico files just like an Apache server.

Alright ; I should have missed something, so could you show me wich exact part do you use to add .js or .css file to your html code ? Thank you very much :slight_smile:

These are correct if the files are 8.3 format. Your original file names are too long. Insert them into your html doc.

<script type="text/javascript" src="myjs.js"></script> 
<link rel="stylesheet" type="text/css" href="mystyle.css">

The web browser will request these files just like your main html doc after loading your main html doc. It will also request favicon.ico. My code will respond by uploading those files when the web browser requests them if the files exist on the SD card.

Ok, i have changed my file names to 8.3 format ("fe.js" and "stysh.css" ), thanks for the tip, but still not working.... =(
Are you opening those files in your arduino code, or do you just use

<script type="text/javascript" src="fonctionExterne.js"></script> 
<link rel="stylesheet" type="text/css" href="stylesheet.css">

in the head of html code?
Thanks!

You do not need to do anything on the Arduino. When the web browser (IE, Chrome, Firefox, etc) downloads the html doc, it will make three separate requests for those additional files just like if you entered them in your browser address bar. If your server IP is 1.2.3.4, then this is what the web browser sends to your server get those files:
http://1.2.3.4/fe.js
http://1.2.3.4/stysh.css
http://1.2.3.4/favicon.ico

Alright, thanks for this;
nevertheless i forgot to tell, when i just tried to call them, the source code has appeared when i called index.htm, on the actual page... so how can i deal with that ?
Further more, what is favicon.ico ?

Thanks!

i just removed some tests on my arduino code, in which i tried to call these files, and so now my page is normal, but i still cannot access to functions implemented in my javascript and css file :frowning:
Is this bound to the "favico.ico" file?

The index.htm file is a .htm type file, and that should be displayed as html.

The favicon.ico file is the icon. On the Arduino forum, look at the tab at the top above the address bar. It is the Arduino logo.
http://forum.arduino.cc/favicon.ico
You can add your own icon to your webpage with that file.

Try to download those files with your web browser. If they do not download, it should give you a error 404 message.

You should see a message on the Arduino server serial monitor when the web browser attempts to download those files. What does it say?

Yes, it seems to download it :

Initializing SD card...
SUCCESS - SD card initialized.
SUCCESS - Found index.htm file.
GET / HTTP/1.1
Host:  IP
Connection: keep-alivGET /fe.js HTTP/1.1
Host: IP
Connection: keepGET /stysh.css HTTP/1.1
Host: IP
Connection: GET /favicon.ico HTTP/1.1
Host: IP
ConnectionGET /ajax_inputs&nocache=314361.3541033119 HTTP/1.1
Host: GET /ajax_inputs&nocache=327329.34528030455 HTTP/1.1
Host:GET /ajax_inputs&nocache=138122.49177135527 HTTP/1.1
Host:GET /ajax_inputs&nocache=725961.0828477889 HTTP/1.1
etc....

that's what on my serial monitor. I removed the actual IP and replaced by "IP".
We can see that stysh.css and fe.js are attenpted to be used, but as fe.js contains the function used to handle request to other pages, and i cannot go to other pages (the button is uneffective), i conclude these files are not used or not downloaded... (this function works fine when included in each .htm file)

Where do these requests come from? What do you expect the Arduino to do with these requests?

ConnectionGET /ajax_inputs&nocache=314361.3541033119 HTTP/1.1
Host: GET /ajax_inputs&nocache=327329.34528030455 HTTP/1.1
Host:GET /ajax_inputs&nocache=138122.49177135527 HTTP/1.1
Host:GET /ajax_inputs&nocache=725961.0828477889 HTTP/1.1
etc....

I think these requests are bound to some analog inputs on arduino, which are displayed and refreshed every second ; do you think this is the reason why the .css and .js files are not used?

You must set the "Connection: close" on your initial request. It appears you are sending "Connection: keep-alive".

Connection: keep-alivGET /fe.js HTTP/1.1

It's not what i am doing with the webFile.close ?

if (c == '\n' && currentLineIsBlank) {
                    // send a standard http response header
                    client.println("HTTP/1.1 200 OK");
                    // remainder of header follows below, depending on if
                    // web page or XML page is requested
                    // Ajax request - send XML file
                    if (StrContains(HTTP_req, "ajax_inputs")) {
                        // send rest of HTTP header
                        client.println("Content-Type: text/xml");
                        client.println("Connection: keep-alive");
                        client.println();
                        SetLEDs();
                        // send XML file containing input states
                        XML_response(client);
                    }
                    else {  // web page request
                        // send rest of HTTP header
                        client.println("Content-Type: text/html");
                        client.println("Connection: keep-alive");
                        client.println();
                        // send web page
                     // open web page depending on request :
                    
                      if (StrContains(HTTP_req, ".htm"))      { //if we are already on the website
                        
                      String str(HTTP_req);//convert HTTP_req into a string
                      String html(".htm");//identify .htm as a string
                      String adress = str.substring(5, str.lastIndexOf(html)+4);//isolate the web page adress
                      char page[str.lastIndexOf(html)-5];//create a char at desired size
                      adress.toCharArray(page, str.lastIndexOf(html));//convert previously acquired web page adress into a char
                      webFile=SD.open(page);//open page
                      }
                      else webFile=SD.open("index.htm");//in other case (first access to website for example), directly access to index
                    
                     if (webFile) {
                            while(webFile.available()) {
                                client.write(webFile.read()); // send web page to client
                            }
                            webFile.close();
                    }
                    }
                    // display received HTTP request on serial port
                    Serial.print(HTTP_req);
                    // reset buffer index and all buffer elements to 0
                    req_index = 0;
                    StrClear(HTTP_req, REQ_BUF_SZ);
                    break;
                }

You are sending "Connection: keep-alive". That should be "Connection: close".

client.println("Connection: keep-alive");

edit: My server code is not set to use XML, ajax, php, asp or the like. You must add that to the server code if you want to use those extensions.