Web Client and ThingSpeak Client - possible?

Hi - I've been tearing my hair out on this one - I've basically tracked my error down to the fact i seem to be using two ethernet clients on the same sketch.

If i create a new sketch and paste in the Thingspeak tweet demo (from here : Update Twitter with ThingTweet and Arduino + Ethernet Shield – About Things | A Hans Scharler Blog ) then mix in a basic web server from the examples menu, when i try to upload to Thingspeak using the standard demo code, i get a connection error. using either example in isolation works perfectly.

I'm trying to create an Arduino with ethernet that can sit quietly, and host its own little web site, then tweet/upload to thingspeak/pachube whenever something happens near its sensors - is there a better way of going about this?

If I'm not being stupid (a fairly good chance seeing as I'm pretty new to this), I can post the code so you can see just what ive come up with.

Hopefully someone can help!

heres a tiny snippet - my code is getting out of control!

Client client(tsserver, 80); //used for the thingspeak connections

Server server(80);// Initialize the Ethernet server library
Client webclient = server.available();//-- whenever this is enabled, the arduino wont do a thing once started. when commented out, it works.

if i put the webclient line in the client render web page function, the tweet and thingspeak code wont run.

If I'm not being stupid (a fairly good chance seeing as I'm pretty new to this), I can post the code so you can see just what ive come up with.

Without seeing your code, it's tempting to say that you are being stupid (for not posting it in the first place), but I'll resist. I'll just encourage you to post it.

Keep in mind that the Arduino doesn't have a huge amount of memory. As a guess, I'd say that you are using more than you have, which is the cause of your problem. By commenting out some of it, you reduce the amount of memory required, so the rest functions.

haha! i did ask for it so go ahead!

here is an abridged version of the code - it is supposed to do other stuff (like use dhcp and tell my via two leds what its ip address is, etc) but ive stripped it back to what you see - and ive taken out my thingspeak codes too.

hopefully you can make sense of it - at this stage its basically a couple of examples mashed inelegantly together!

i think its to do with client (for thingspeak) and webclient (for posting back a web page to a client) ......

#include <SPI.h>
#include <Client.h>
#include <Ethernet.h>
#include <EthernetDHCP.h>
#include <Server.h>
#include <Udp.h>
 
String sVersion = "v8"; 
long lastTweetTime = 0;  
int iRefresh=0;
byte mac[]     = { 0xD4, 0x28, 0xB2, 0xFF, 0xA0, 0xA1 }; // Must be unique on local network
byte ip[]      = { 17, 2,   0,  16 };                // Must be unique on local network
byte gateway[] = { 17, 2,   0,   1 };
byte subnet[]  = { 255, 255, 255,   0 };

Server server_web(80);// Initialize the Ethernet server library

 static unsigned long prevTime = 0; // Time between IP lease refreshes.
  
//Sensor Variables:
int iLoop = 0;
String readString = String(30);
int ledPin = 9;
boolean LEDON = false; //LED status flag
boolean ISDARK = false; 
int torchPin = 8;
float hiTemp=0;
float lowTemp =0;
float hiLight=0;
float lowLight =0;


byte server[]  = { 184, 106, 153, 149 };         // IP Address for the ThingSpeak API
String thingtweetAPIKey = "";  // Write API Key for a ThingSpeak Channel
Client client(server, 80);
const int updateThingSpeakInterval = 30000;        // Time interval in milliseconds to update ThingSpeak (30000 = 30 seconds)
const long updateTwitterInterval = 5000;     // Time interval in milliseconds to update Twitter (3600000 = 1 hour)
String writeAPIKey = "";   

boolean lastConnected = false;
boolean alreadyserving =false;

 void setup()
{  

  Ethernet.begin(mac, ip, gateway, subnet);
  Serial.begin(9600);
   
  // SetupDHCPEthernet(); 
 //  Serial.println(ip_to_str(ip));
  
    //EthernetDHCP.begin(mac, 1);

   // Ethernet.begin(mac, ip);// <-- if using a staic IP hard coded.
   delay(1000);
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);  
  pinMode(A5, INPUT);  
  pinMode(2, INPUT);
  pinMode(3, OUTPUT); 
  pinMode(5, OUTPUT); 
  pinMode(6, OUTPUT); 
  pinMode(7, INPUT);  
  pinMode(8, OUTPUT); 
  pinMode(9, OUTPUT); 
  analogWrite(3, 255);
  analogWrite(5, 255);
  analogWrite(6, 255);    
  
} 

boolean busy = false;

String sTwitterMsg="";

  long lastConnectionTime = 0; 
 const int updateInterval = 5000;   
 
void loop()
{  
   
  
  // Print Update Response to Serial Monitor
  if (client.available())
  {
    char c = client.read();
    Serial.print(c);
  }
  
  // Disconnect from ThingSpeak
  if (!client.connected() && lastConnected)
  {
    Serial.println();
    Serial.println("...disconnected.");
    Serial.println();
    client.stop();
  }
  
  lastConnected = client.connected();

    //button is pressed and been a while since last tweet:
    if (analogRead(A5)<200 && (millis() - lastTweetTime) > updateTwitterInterval)
  {    
     String analogPin0 = String(analogRead(A0), DEC);
      // Update ThingSpeak
  if(!client.connected() && (millis() - lastConnectionTime > updateInterval))
  {
    updateThingSpeak("field1="+analogPin0);
  }
  
  lastConnected = client.connected();
  }

 ServeBasicWebPage();
}


void updateThingSpeak(String tsData)
{
  if (client.connect())
  { 
    Serial.println("Connected to ThingSpeak...");
    Serial.println();
        
    client.print("POST /update HTTP/1.1\n");
    client.print("Host: api.thingspeak.com\n");
    client.print("Connection: close\n");
    client.print("X-THINGSPEAKAPIKEY: "+writeAPIKey+"\n");
    client.print("Content-Type: application/x-www-form-urlencoded\n");
    client.print("Content-Length: ");
    client.print(tsData.length());
    client.print("\n\n");

    client.print(tsData);
    
    lastConnectionTime = millis();
    
    resetCounter = 0;
    
  }
  else
  {
    Serial.println("Connection Failed.");   
    Serial.println();    
    resetCounter++;    
  //  if (resetCounter >=5 ) {resetEthernetShield();}
    lastConnectionTime = millis(); 
  }
}

// Utility function to nicely format an IP address.
const char* ip_to_str(const uint8_t* ipAddr)
{
  static char buf[16];
  sprintf(buf, "%d.%d.%d.%d\0", ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]);
  return buf;
} 


void ServeBasicWebPage()
{  
// Create a client connection
Client webclient = server_web.available();
  if (webclient) {
    while (webclient.connected()) {
   if (webclient.available()) {
    char c = webclient.read();
     //read char by char HTTP request
    if (readString.length() < 100)
      {
        //store characters to string
        readString += c; //replaces readString.append(c);
      }
        //output chars to serial port
     Serial.print(c);
        //if HTTP request has ended
        if (c == '\n') {
          //dirty skip of "GET /favicon.ico HTTP/1.1"
         // if (readString.indexOf("?") <0)
          //{ Serial.print("Skipping");
            //skip everything
          //}
         // else
          //lets check if LED should be lighted
        if(readString.indexOf("L=1") >0)//replaces if(readString.contains("L=1"))
           {
             //led has to be turned ON
             digitalWrite(ledPin, HIGH);    // set the LED on
             LEDON = true;
           }else{
             //led has to be turned OFF
             digitalWrite(ledPin, LOW);    // set the LED OFF
             LEDON = false;
           }
          // now output HTML data starting with standart header
          webclient.println("HTTP/1.1 200 OK");
          webclient.println("Content-Type: text/html");
          webclient.println();
          //set background to yellow
          webclient.print("<body><font size=""2"" face=""Arial"" >");// style=background-color:yellow>");          
           webclient.println(readString);
         webclient.println(  LEDON);                    
         webclient.println("<P></P>LED Control. 1=On, 0=Off.");
          //address will look like http://192.168.1.110/?L=1 when submited
          webclient.println("<form method=get name=LED><input type=submit name=L value=1><input type=submit name=L value=0></form>");

          //printing LED status
          webclient.print("LED Status: ");
          if (LEDON)
              webclient.println("<font color='green' size='5'>ON</font>");
          else
              webclient.println("<font color='grey' size='5'>OFF</font>");
            
              iLoop++;
                 webclient.println(". Loop " +String(iLoop));  
        
       float temperature = analogRead(1) * .004882814;  //getting the voltage reading from the temperature sensor
 temperature = (temperature - .5) * 100;          //converting from 10 mv per degree wit 500 mV offset
     
      webclient.print(". Temperature : "); //to degrees ((volatge - 500mV) times 100)
 webclient.print(temperature );
 webclient.print(" degrees centigrade." );
 
          webclient.println("<hr />" + sVersion);
          webclient.println("<hr />IP Address : ");
 
 //  webclient.println(  ip_to_str(ipAddress));
//webclient.println(  ip_to_str(ip ));

  webclient.println("<hr />Useful Links:
" );

 // webclient.println("<hr />Log:
"+ sLog );
          webclient.println("</body></html>");
          //clearing string for next read
          readString="";
          //stopping client
          webclient.stop();
            }
          }
        }
      } 
}
 static unsigned long prevTime = 0; // Time between IP lease refreshes.

Global OR static. Not both.

String readString = String(30);

Why is readString being initialized with the value "30"?

Client client(server, 80);

Some more meaningful names would be appropriate. server should be ts_server or ts_serverAddr, to avoid confusion. client should be ts_client, to avoid confusion with external access clients.

  if (client.available())

What if client isn't connected? Shouldn't that be checked, first.

     String analogPin0 = String(analogRead(A0), DEC);

Resources are at a premium on the Arduino. Wasting them is not a good idea. The String(analogRead(A0), DEC) part of that statement creates a String object. The assignment part copies that string. After that, the first string goes out of scope.

     String analogPin0(analogRead(A0), DEC);

Creates one String, instead.

  if(!client.connected() && (millis() - lastConnectionTime > updateInterval))
  {
    updateThingSpeak("field1="+analogPin0);
  }

Confusing. The updateThingSpeak() function should be concerned about whether the Arduino is connected to the server, or not. The loop() function shouldn't care.

          //dirty skip of "GET /favicon.ico HTTP/1.1"
         // if (readString.indexOf("?") <0)
          //{ Serial.print("Skipping");
            //skip everything
          //}
         // else
          //lets check if LED should be lighted

This code isn't causing your problem, is it? No. So get rid of it, rather than commenting it out.

It isn't clear what is happening when you upload THIS code. Please explain what does and what does not work.

Have you ruled out memory as an issue?

Hi again - thanks very much for the code comments - they were useful - I'll bear them in mind when coding in future.

The code I posted was a stripped down version so had some bits left over. So, I've mashed up the Examples->WebServer and the ThingSpeak tutorial from http://community.thingspeak.com/arduino/ThingSpeakClient.pde to make a "better" example.

Basically it works, but stops when I try and add any of my "magic". I moved the web page update code to its own function (DealWithWebPage), and did the same with the Thingspeak code (DealWithThingSpeak), then just wrote their function names into Loop. If I try to alter the Loop to only call the ThingSpeak update routine if a button is pressed, the update no longer works.

If you have time, could you take a very quick look at the code below, specifically the Loop code? It would be very much appreciated.

Could it be a basic understanding of the Arduino issue on my part in that I press the button and the DealWithThingSpeak code starts, then I let go of the button (connected to Digital2) and the chip reverts back to normal running, no matter how far through the Thingspeak code it has got? However, if I hold down the button, it just prints "Updating ThinkSpeak" repeatedly but doesn't run the ThingSpeak code.

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

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 17,2,0,16 };

// Initialize the Ethernet server library
// with the IP address and port you want to use 
// (port 80 is default for HTTP):
Server web_server(80);


// ThingSpeak Settings
byte ts_server[]  = { 184, 106, 153, 149 }; // IP Address for the ThingSpeak API
String writeAPIKey = "code here";    // Write API Key for a ThingSpeak Channel
const int updateInterval = 5000;        // Time interval in milliseconds to update ThingSpeak   
Client ts_client(ts_server, 80);

// ThingSpeak Variable Setup
long lastConnectionTime = 0; 
boolean lastConnected = false;
int resetCounter = 0;



void setup()
{
  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  web_server.begin();
  Serial.begin(9600);
   delay(1000);
}

void loop()
{
  
  //Note : with the button check (connected to D2) in place, the ThingSpeak update starts, but then stops without completing.
  //If the digital(2) check is commented out, the thingspeak update works perfectly.
  if (digitalRead(2)==LOW ){
     Serial.println("Updating Thingspeak");
     DealWithThingSpeak();  
   }
  
  DealWithWebPage(); 
}


void DealWithThingSpeak()
{
    //Code from ThingSpeak Example
  String analogPin0 = String(analogRead(A0), DEC);
  
  // Print Update Response to Serial Monitor
  if (ts_client.available())
  {
    char c = ts_client.read();
    Serial.print(c);
  }
  
  // Disconnect from ThingSpeak
  if (!ts_client.connected() && lastConnected)
  {
    Serial.println();
    Serial.println("...disconnected.");
    Serial.println();
    
    ts_client.stop();
  }
  
  // Update ThingSpeak
   if(!ts_client.connected()  && (millis() - lastConnectionTime > updateInterval))
   {
    updateThingSpeak("field1="+analogPin0);
   }
  
  lastConnected = ts_client.connected();
}
 

void updateThingSpeak(String tsData)
{
  if (ts_client.connect())
  { 
    Serial.println("Connected to ThingSpeak...");
    Serial.println();
        
    ts_client.print("POST /update HTTP/1.1\n");
    ts_client.print("Host: api.thingspeak.com\n");
    ts_client.print("Connection: close\n");
    ts_client.print("X-THINGSPEAKAPIKEY: "+writeAPIKey+"\n");
    ts_client.print("Content-Type: application/x-www-form-urlencoded\n");
    ts_client.print("Content-Length: ");
    ts_client.print(tsData.length());
    ts_client.print("\n\n");

    ts_client.print(tsData);
    
    lastConnectionTime = millis();
    
    resetCounter = 0;
    
  }
  else
  {
    Serial.println("Connection Failed.");   
    Serial.println();    
    resetCounter++;    
    if (resetCounter >=5 ) {resetEthernetShield();}
    lastConnectionTime = millis(); 
  }
}

void resetEthernetShield()
{
  Serial.println("Resetting Ethernet Shield.");   
  Serial.println();
  
  ts_client.stop();
  delay(1000);
  
  Ethernet.begin(mac, ip);//, gateway, subnet);
  delay(1000);
}



void DealWithWebPage()
{
   //Code from web server example
  // listen for incoming clients
  Client web_client = web_server.available();
  if (web_client) {
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (web_client.connected()) {
      if (web_client.available()) {
        char c = web_client.read();
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          web_client.println("HTTP/1.1 200 OK");
          web_client.println("Content-Type: text/html");
          web_client.println();

          // output the value of each analog input pin
          for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
            web_client.print("analog input ");
            web_client.print(analogChannel);
            web_client.print(" is ");
            web_client.print(analogRead(analogChannel));
            web_client.println("
");
          }
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    web_client.stop();
  }
}
  if (digitalRead(2)==LOW ){

You'd need to describe how the switch is connected to the digital pin. The pull-up resistor on pin 2 is not activated, and pin 2 is not defined as an INPUT pin (though that should be the default state). Without activating the pull-up resistor, the check for LOW presumes that an external pull-up resistor is connected. Is that true?

You aren't debouncing the switch, or triggering the update only on transition from not-pressed to pressed, so as long as the switch is pressed, the function should be called. Presuming, of course, that the switch IS wired correctly.

Splitting comments to a narrower width would be appreciated. Scrolling inside the code window is difficult when the code window is wider than the browser.

Hi - yes, the button is connected to a 10k resistor (I'm using the guide I got with the Arduino ARDX kit for how to set up breadboard circuits - it's a button with 5v, and a 10k resistor).

I'll go and search for debouncing - is there a simple way of telling it to start function1 and hold off on others until the function1 has completed? something like:

boolean busy = false;
if (busy==false)
{busy=true;
function1();
busy=false;
}

If you call a function, that function runs until it reaches the end, or a return statement earlier than that. So, your proposed code wouldn't add any value.

Cool - that's how I expected it to work : call a function and it executes until finished, then returns to the loop.

Thanks for the help - it's lead me to rewrite the code so it works, and the tip you posted in another thread about the IDE auto formatting tool was invaluable! A nice debounce check and tighter memory management - I now have a working Arduino Uno + ethernet + LCD that tweets when it detects motion, as well as uploading light and temperature and stat info to Pachube and ThingSpeak, as well as serving up a simple web page and displaying run time info on the LCD - amazing what these little gadgets can do (if they get some half decent programming in them!).

Thanks again!

(if they get some half decent programming in them!)

So true.