Using a single webpage to operate multiple Arduinos with ethernet shields.

I am working on a project to use a bunch of arduinos with Wiznet Ethernet shields and I want to control them all using a single web page. My HTML is a little shaky and I am not sure of the command(s) I need to pass from the webpage to the arduino. I think once I get one, the rest will be pretty much cut and paste.

Some points I want to make:

*First all the arduinos will have a static IP address and will be working on a non-internet connected local network. (ie: My issues are not with networking in general)

*I can start a server off the arduino and do the example where it shows the pins off it's served webpage and I mostly understand how that happens. And I understand how an Arduino serves a webpage to a browser. Since I am using multiple arduinos to control multiple processes and want them all on one webpage; serving a page from an arduino is not going to work.

*All the functions of what I want the arduinos to perform have been coded and tested.

Here is what I want to do in a nutshell:

have a web toggle switch like this: Creating Sleek On/Off Button with CSS3 - Hongkiat control a variable on the arduino board to start and stop the functions.

Here is the pseudo code:

On the arduino: While X is 0 perform nothing // X is the variable that the webpage would send.(or taken as the variable)
While X is 1 start functions

On the webpage:
When user clicks on button: send 0 or 1 to the arduino depending on whether the previous state was On or Off ( 0 if it was on, and 1 if it was off.)

I need the commands for both the arduino to receive the variable from the webpage and the html to send the variable to the arduino. the code for the button is in CSS.

I imagine that I can frame this in a single webpage and make a header and description of what the Arduino is doing and then copy and paste to build another frame with another toggle switch...

I have tried looking at other toggle switches and I think someone asked a similar question, but it delved into more networking issues than my question.

Thank you for your help in advance. I am still learning as well.

here is the HTML code for the button:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title>CSS3 Buttons</title>
	<link rel="stylesheet" href="style2.css">
	<script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
	<script type="text/javascript">
		$(document).ready(function(){
			$('#button').on('click', function(){
				$(this).toggleClass('on');
			});
		});
	</script>
</head>
<body>
	<section>
		<a href="#" id="button">&#xF011;</a>
		<span></span>
	</section>
</body>
</html>
}

Since I am using multiple arduinos to control multiple processes and want them all on one webpage; serving a page from an arduino is not going to work.

Sure it can. The links in the web page to the arduinos that did not serve the original page need to be urls with the IP address of the arduinos. You may need to have the arduinos to send back the status 204: so the browser does not try to load a new page. Below are examples of full urls. You would use ip address.

<a href="http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$21$01$80$78">Bird</a>|
<a href="http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$79$01$80$8C">Sony</a>|
<a href="http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$A5$01$80$64">Window</a>|
<a href="http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$DC$01$80$78">Fireplace</a>|

<a href="http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$42$01$80$8C">P/T Cam</a>|
<a href="http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$F2$01$80$50">Fan</a>|
<a href="http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$6E$01$80$A0">Troll</a>|
<a href="http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$2C$01$80$50">Lamp</a>|
<a href="http://zoomkat.no-ip.org/cgi/ZK2PTB.bat?00&140&01&038">I-mouse</a>|





<a href="http://web.comporium.net/~shb/486X66.htm">486x66</a>
<a href="http://web.comporium.net/~shb/ezservo1.htm">ezservo1</a>
<a href="http://web.comporium.net/~shb/wc2000-PT-script.htm">ezscript</a>


 


<a href="http://127.0.0.1/cgi-bin/notetest.bat">notetest</a>

 


<input type="button" value="Pan Left" 
onmousedown="location.href 
('http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$DC$01$80$78');" 
onmouseup="location.href 
('http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$79$01$80$8C');"/> 

<input type="button" value="Pan Right" 
onmousedown="location.href 
('http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$21$01$80$78');" 
onmouseup="location.href 
('http://zoomkat.no-ip.org:88/cgi-bin/echoo.bat?$00$80$79$01$80$8C');"/>

So what you are saying is to have one of the arduinos a server using the server sketch and the rest of them (like 5 or 6) make them clients attached to a single arduino.

So when I break this HTML down:

href= "http:// zoomkat.no-ip.org:88 / cgi-bin / echoo.bat? $00$80$21$01$80$78 "> Bird

html call the IP of the client Arduino cgi binary call? echos the command the MAC address and the variable ('bird') that is being sent to the client arduino?

please correct me. if I have this wrong.

The below test code is what I would try if I had three arduinos setup with ethernet shields. In this code setup each arduino will have its own lan ip address, and can send the control web page to the browser. The web page has control buttons for all three arduinos. With modifications for dynamic ip address urls and router port forwarding, the control page could be hosted at an off site web service, or just from the desktop of a desired pc. I don't currently have the arduinos to test my self, but I've used similar setups in years past. As usual, YMMV!

//zoomkat 12-18-14
//simple button GET with iframe code
//for three arduinos communication
//open serial monitor to see what the arduino receives
//use the ' instead of " in html ilnes 
//address will look like http://192.168.1.102:84/ when submited
//for use with W5100 based ethernet shields

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //ethernet shield mac address

byte ip[] = { 192, 168, 1, 102 }; // arduino IP in lan main S1
//byte ip[] = { 192, 168, 1, 103 }; // arduino IP in lan slave S2
//byte ip[] = { 192, 168, 1, 104 }; // arduino IP in lan slave S3

byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port

String readString; 

//////////////////////

void setup(){

  pinMode(4, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, gateway, subnet);
  server.begin();

  //enable serial data print 
  Serial.begin(9600); 
  Serial.println("servertest1"); // so I can keep track of what is loaded
}

void loop(){
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string 
          readString += c; 
          //Serial.print(c);
        } 

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging 

          //now output HTML data header
             if(readString.indexOf('?') >=0) { //don't send new page
               client.println("HTTP/1.1 204 Zoomkat");
               client.println();
               client.println();  
             }
             else {
          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE>Arduino GET test page</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");

          client.println("<H1>Zoomkat's simple Arduino button</H1>");
          
          //S1 main
          client.println("<a href='http://192.168.1.102:84/?on1' target='inlineframe'>S1 ON</a>"); 
          client.println("<a href='http://192.168.1.102:84/?off' target='inlineframe'>S1 OFF</a>"); 
          
          //S2 slave, change to 103
          client.println("

<a href='http://192.168.1.102:84/?on1' target='inlineframe'>S2 ON</a>"); 
          client.println("<a href='http://192.168.1.102:84/?off' target='inlineframe'>S2 OFF</a>"); 
          
          //S3 slave, change to 104
          client.println("

<a href='http://192.168.1.102:84/?on1' target='inlineframe'>S3 ON</a>"); 
          client.println("<a href='http://192.168.1.102:84/?off' target='inlineframe'>S3 OFF</a>"); 

          client.println("<IFRAME name=inlineframe style='display:none'>");          
          client.println("</IFRAME>");

          client.println("</BODY>");
          client.println("</HTML>");
             }

          delay(1);
          //stopping client
          client.stop();

          ///////////////////// control arduino pin
          if(readString.indexOf("on1") >0)//checks for on
          {
            digitalWrite(4, HIGH);    // set pin 4 high
            Serial.println("Led On");
          }
          if(readString.indexOf("off") >0)//checks for off
          {
            digitalWrite(4, LOW);    // set pin 4 low
            Serial.println("Led Off");
          }
          //clearing string for next read
          readString="";

        }
      }
    }
  }
}

Thanks Zoomcat.

I think this website is really going to help:

https://www.teleduino.org

Thank you ZoomCat. Once I figured it out it became clear. Yes you do use a server on the Arduino, but the key is to reset the arduino after the command. Also thank you to: ArduinoAndy for the software reset routine.

Here is my code for a super simple Toggle Switch..it took me forever to put it together, but it was well worth the climb:

Arduino Code:

// SPI and Ethernet Library Headers
#include <SPI.h>
#include <Ethernet.h>
//MAC and IP Address

byte mac[] = { 
  0x90, 0xA2, 0xDA, 0x0F, 0x23, 0x7F };
IPAddress ip(192,168,1,170);

EthernetServer server(80);
String readString;



//First, set up the relay
#define RELAYSIREN   6                       
#define RELAYDISPLAY 7                   
#define RELAYLIGHT   8                       
#define RELAYNA      9



void software_Reset() // Restarts program from beginning but does not reset the peripherals and registers
{
asm volatile ("  jmp 0");  
} 


 void alarmOn()     // Turns the Alarm On
 {
   digitalWrite(RELAYSIREN,LOW);
   digitalWrite(RELAYDISPLAY, LOW);
   digitalWrite(RELAYLIGHT, LOW);
    
 }
 
  
  void alarmOff() // Turns the Alarm Off
 {
   digitalWrite(RELAYSIREN,HIGH);
   digitalWrite(RELAYDISPLAY, HIGH);
   digitalWrite(RELAYLIGHT, HIGH);
    
 }

void setup() { //call this once at the beginning
   //Setup the Pinmode for the alarm
  digitalWrite(RELAYSIREN, HIGH); //starts the siren off
  pinMode(RELAYSIREN, OUTPUT); // sets pin as output for Siren
  digitalWrite(RELAYDISPLAY, HIGH); //starts the display off
  pinMode(RELAYDISPLAY, OUTPUT); //sets the Display pin to output
  digitalWrite(RELAYLIGHT, HIGH); //sets the light off
  pinMode(RELAYLIGHT, OUTPUT); //sets the pin for the light to output
  
  
  

  
    Serial.begin(9600); //start up serial port
    // Start the Ethernet Connection and the Server
  
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());

}

void loop() { //main loop
int PowerControl;
char inputData[25];
EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
      Serial.write(c);  
   readString +=c;
   
    if (c > 0) { //if we read something 
    
        if(readString.indexOf("on")>0) {
            alarmOn();//if we read a on, Alarm On
            PowerControl = 1;
            c=0;
             
   }
       if(readString.indexOf("off")>0) {
           alarmOff(); //if we read a off, Alarm Off
           PowerControl = 0;
              
   } 

        c = 0; //reset
  if(PowerControl == 0) {    //Resets the Arduino for the next toggle
    software_Reset();

  }

}
}}
}}

and here is the super simple HTML for the webpage to call the Arduino:

<HTML>

	<p><a href = "http://192.168.1.170/?on"> On </a> </p>
	<p><a href = "http://192.168.1.170/?off"> Off </a> </p>

</HTML>

Now I have to add in the simple button I want and then format the frames. Also stop the browser from trying to GET and eventually timing out.

You can also use RF module, BT Module or Infrared Module in controlling other Arduino's, and built one Arduino as a Server that can connect either LAN or WEB. We done this kind of style and it works.

Well my previous code kinda worked, at home, but it was horrible when I hooked the relays up to stuff. It would turn on ok, but would not turn off. So back to the drawing board I went.

I spent many days thinking about this and then it dawned on me..use sockets and python.

So set up the Arduino as a UDP server.

Change the Char* to a integer using atoi()

and

use a python gui to toggle the thing..I am hoping I can implement Skulpt (www.skulpt.org) into this project to make it universal. (on any browser)

Here is the code for the Arduino:

/*
  UDPSendReceive.pde:
 This sketch receives UDP message strings, prints them to the serial port
 and sends an "acknowledge" string back to the sender
 
 A Processing sketch is included at the end of file that can be used to send 
 and received messages for testing with a computer.
 
 created 21 Aug 2010
 by Michael Margolis
 
 This code is in the public domain.
 */


#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008


// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {  
  0x90, 0xA2, 0xDA, 0x0F, 0x24, 0x17 };
IPAddress ip(192, 168, 1, 177);

unsigned int localPort = 8888;      // local port to listen on

// buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
char  ReplyBuffer[] = "acknowledged";       // a string to send back

// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

#define R1 22


void setup() {
  // start the Ethernet and UDP:
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);

  Serial.begin(9600);
  
  pinMode(R1, OUTPUT);
  digitalWrite(R1, HIGH); //Sets the relay off to start with
}

void loop() {
  // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if(packetSize)
  {
    Serial.print("Received packet of size ");
    Serial.println(packetSize);
    Serial.print("From ");
    IPAddress remote = Udp.remoteIP();
    for (int i =0; i < 4; i++)
    {
      Serial.print(remote[i], DEC);
      if (i < 3)
      {
        Serial.print(".");
      }
    }
    Serial.print(", port ");
    Serial.println(Udp.remotePort());
    
    
        

    // read the packet into packetBufffer
    Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE);
    Serial.println("Contents:");
    Serial.println(packetBuffer);
    
    int testNum = atoi(packetBuffer); // A simple way to make sure the message gets through! Changes Char* to INT
    Serial.println(testNum);
    if(testNum == 1) {
        digitalWrite(R1, LOW); //Turns on the Relay
        
    }
    if(testNum == 2) {
      digitalWrite(R1, HIGH);  //Turns off the Relay
    }
      
    // send a reply, to the IP address and port that sent us the packet we received
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(ReplyBuffer);
    Udp.endPacket();
  }
  delay(10);
}

The following is the Python Code for the button:

from Tkinter import *
import socket

target_host = "192.168.1.177"
target_port = 8888

class Application(Frame):
    """The GUI with 2 Buttons for now """

    def __init__(self, master):

        """Initialize the frame"""
        Frame.__init__(self,master)
        self.grid() 
        self.create_widgets()
        

    def create_widgets(self):
        """create the buttons"""

        #Criticality Alarm

        self.button1 = Button(self, text = "On")
        self.button1["command"] = self.turnOn
        self.button1.grid()

        self.button2 = Button(self, text = "Off")
        self.button2["command"] = self.turnOff
        self.button2.grid()

    def turnOn(self):
        client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        client.sendto("1",(target_host, target_port))
        data, addr = client.recvfrom(4096)
        
        
    def turnOff(self):
        client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        client.sendto("2",(target_host, target_port))
        data, addr = client.recvfrom(4096)
        
        
root = Tk()
root.title("Criticality Alarm")
root.geometry("400x200")

app = Application(root)

root.mainloop()

This gives you excellent ethernet access to your arduino!

Welcome to the wonderful world of UDP. Best when used on a localnet like you are doing. I've been using UDP for a while now with great results.

edit: If you are on a localnet, you can tell if the packet delivery worked. This only works on a localnet! Otherwise it determines if the next device en route to the destination took the packet.

    // replace this
    Udp.endPacket();

    // with this
    if(Udp.endPacket()) Serial.println(F("Send ok"));
    else Serial.println(F("Send fail"));

This is from EthernetUdp.h

// Finish off this packet and send it
// Returns 1 if the packet was sent successfully, 0 if there was an error
virtual int endPacket();

I spent many days thinking about this and then it dawned on me..use sockets and python.

Unfortunately that limits your arduino use to a pc running python.

Nah, just a python script. You can make a JavaScript page that does a simple UDP call and that will make it browser accessible.

Here is the JS

var PORT = 8888;
var HOST = '192.168.1.177';

var dgram = require('dgram');
var message = new Buffer('1');

var client = dgram.createSocket('udp4');
client.send(message, 0, message.length, PORT, HOST, function(err, bytes) {
    if (err) throw err;
    console.log('UDP message sent to ' + HOST +':'+ PORT);
    client.close();
});

The above code gives only one command(the #1) which in my arduino sketch would throw my relays on. You could just adjust the JavaScript to fit your needs. Python was just easy to get up and running. JavaScript was always possible and what I will probably move to.

Now I have to add in the simple button I want and then format the frames. Also stop the browser from trying to GET and eventually timing out.

I've used the below page format for controlling a web devices for many years.

/zoomkat 10-6-13
//simple button GET with iframe code
//open serial monitor to see what the arduino receives
//use the ' instead of " in html ilnes 
//address will look like http://192.168.1.102:84/ when submited
//for use with W5100 based ethernet shields

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //ethernet shield mac address
byte ip[] = { 192, 168, 1, 102 }; // arduino IP in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port

String readString; 

//////////////////////

void setup(){

  pinMode(4, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, gateway, subnet);
  server.begin();

  //enable serial data print 
  Serial.begin(9600); 
  Serial.println("servertest1"); // so I can keep track of what is loaded
}

void loop(){
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string 
          readString += c; 
          //Serial.print(c);
        } 

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging 

          //now output HTML data header
             if(readString.indexOf('?') >=0) { //don't send new page
               client.println("HTTP/1.1 204 Zoomkat\r\n\r\n");
             }
             else {
          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE>Arduino GET test page</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");

          client.println("<H1>Zoomkat's simple Arduino button</H1>");
          
          client.println("<a href='/?on1' target='inlineframe'>ON</a>"); 
          client.println("<a href='/?off' target='inlineframe'>OFF</a>"); 

          client.println("<IFRAME name=inlineframe style='display:none'>");          
          client.println("</IFRAME>");

          client.println("</BODY>");
          client.println("</HTML>");
             }

          delay(1);
          //stopping client
          client.stop();

          ///////////////////// control arduino pin
          if(readString.indexOf("on1") >0)//checks for on
          {
            digitalWrite(4, HIGH);    // set pin 4 high
            Serial.println("Led On");
          }
          if(readString.indexOf("off") >0)//checks for off
          {
            digitalWrite(4, LOW);    // set pin 4 low
            Serial.println("Led Off");
          }
          //clearing string for next read
          readString="";

        }
      }
    }
  }
}

Zoomcat, I realize this, and yes it works but I give you a few advantages to doing it through UDP vs TCP and direct arduino HTML.

  1. UDP is connectionless. You open a socket, send a packet, and close the socket.

  2. Once you have the Algorithm and decide the commands of what you want the Arduino to do You no longer have to mess with it unless you want it to do something else.

  3. You can actually send a real variable that can be manipulated on the arduino. Now you might be able to do the same thing with atoi() through TCP, but you are getting headers and other garbage sent to the Arduino. Using udp simple are sending an actual variable and in my specific case I am sending an integer(1 turns it on, 2 shuts it off) Every time. Ie being able to use == instead of >.

4 You can change the HTML/CSS/JS client side, and continue to change/upgrade it.Without having to add a SD card to the arduino you can add pictures and do all sorts of things. Plus you can now use a nice GUI to make your HTML like googles design studio.

Also Serial.println("") HTML code is ugly! It works, but is really bad form IMO. Maybe it is okay for a simple Arduino file server, but it is a biatch to code on an arduino that way.