Arduino code example for ENC28J60 Ethernet upload to Thingspeak

Status
Not open for further replies.

JBeale

Well-known member
Someone requested the code I used to have an old-school Arduino type board ("Jeenode" 3.3V ATmega328p at 16 MHz) with ENC28J60 based ethernet board to upload data to Thingspeak (one data point, the analog level on A0). I have several versions of similar code but I *think* below is basically the code I had running for over 1 year without any problems (that is longest I've ever had a networked embedded system run with no maintenance). I do not know how applicable it is to Teensy 2/3 if at all. At the very least you have to put your own network config, Thingspeak API key etc.

Note also there are more recent code examples online, such as http://nathanhein.com/2013/02/getting-arduino-online-with-an-enc28j60/

Code:
/*
 ThingSpeak Client to Update Channel Feeds
 
 The ThingSpeak Client sketch is designed for the Arduino + Ethernet Shield.
 This sketch updates a channel feed with an analog input reading via the
 ThingSpeak API (http://community.thingspeak.com/documentation/)
 using HTTP POST.
 
 Getting Started with ThingSpeak:
 
    * Sign Up for New User Account - https://www.thingspeak.com/users/new
    * Create a New Channel by selecting Channels and then Create New Channel
    * Enter the Write API Key in this sketch under "ThingSpeak Settings"
 
 Created: January 25, 2011 by Hans Scharler (http://www.iamshadowlord.com)
 
 Additional Credits: Example sketches from Tom Igoe and David A. Mellis
*/

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

// Local Network Settings
byte mac[]     = { 0xD4, 0x28, 0xB2, 0xFF, 0xA0, 0xA1 }; // Must be unique on local network
byte ip[]      = { 192, 168,   0,  249 };                // Must be unique on local network
byte gateway[] = { 192, 168,   0,   1 };
byte subnet[]  = { 255, 255, 255,   0 };

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

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

void setup()
{
  Ethernet.begin(mac, ip, gateway, subnet);
  Serial.begin(9600);
  delay(1000);
}

void loop()
{
  String analogPin0 = String(analogRead(A0), DEC);
  
  // 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();
  }
  
  // Update ThingSpeak
  if(!client.connected() && (millis() - lastConnectionTime > updateInterval))
  {
    updateThingSpeak("field1="+analogPin0);
  }
  
  lastConnected = client.connected();
}

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(); 
  }
}

void resetEthernetShield()
{
  Serial.println("Resetting Ethernet Shield.");   
  Serial.println();
  
  client.stop();
  delay(1000);
  
  Ethernet.begin(mac, ip, gateway, subnet);
  delay(1000);
}
 
Hmmm... well this code uses the Ethernet library that as far as I know, works only with the Wiznet chip (original ethernet shield) and not with the microchip's enc28j60 and relative modules, for which other libraries exist (e.g. Ethercard)
 
@JBeale, the comment from vfotop1 is correct : your example does NOT use ethercard/ENC28J60 but ethernet.h and the "server" and "client" objects that are in the standard Ethernet library, only compatible with wiznet chips.

Can you post the code you talk about (used with a JeeNode) ? I have been using EtherCard on a DINo for more than one year as well, publishing a few values to cosm/xively, but I can confirm it will occasionally crash (roughly every 5-6 weeks), probably because of a DHCP bug in EtherCard that was documented somewhere on a forum.

I never saw thingspeak code for ENC28J60. So if you can find it back and share it, that would be welcome.
Tip : you should find "ether.packetLoop(ether.packetReceive());" somewhere in your loop, that's what handles TCP.
 
Can you post the code you talk about (used with a JeeNode) ? I have been using EtherCard on a DINo for more than one year as well, publishing a few values to cosm/xively, but I can confirm it will occasionally crash (roughly every 5-6 weeks), probably because of a DHCP bug in EtherCard that was documented somewhere on a forum.

I never saw thingspeak code for ENC28J60. So if you can find it back and share it, that would be welcome.

Sorry for finding the wrong example before. I believe the below code is what I used with the JeeNode (just substituted "MySecretKey") and I think I used a fixed IP address instead of DHCP to avoid issues.
Code:
// Arduino demo sketch for testing RFM12B + ENC28J60 ethernet
// Listens for RF12 messages and displays valid messages on a webpage
// Memory usage exceeds 1K, so use Atmega328 or decrease history/buffers
//
// This sketch is derived from RF12eth.pde:
// May 2010, Andras Tucsni, http://opensource.org/licenses/mit-license.php
// The EtherCard library is based on Guido Socher's driver, licensed as GPL2. 
// Based on Mods by jcw, 2010-05-20, Jeelabs.org 
// 
// Author: Glyn Hudson and Trystan lea openenergymonitor.org
// Date: 2/5/2011
//
// mods: working  John Beale - May 5-7 2011
// based on info in http://tuxgraphics.org/electronics/200905/embedded-tcp-ip-stack.shtml
// 22   162 ms   162 ms   162 ms  pepper.tuxgraphics.org [77.37.2.152]

#include <EtherCard.h>
#include <Ports.h>
#include <RF12.h> // needed to avoid a linker error :(

// ethernet interface mac address
// D4-28-B2-FF-E2-F6 
static byte mymac[6] = { 0xd4,0x28,0xb2,0xff,0xe2,0xf6 };
// ethernet interface ip address
static byte myip[4] = { 192,168,10,140 };
// gateway ip address
static byte gwip[4] = { 192,168,10,1 };
// remote website ip address and port
static byte hisip[4] = { 184,106,153,149 };  // address of remote server: ThingSpeak

static word hisport = 80;

// fixed RF12 settings
#define MYNODE 31            //node ID of nanode
#define freq RF12_915MHZ     //frequency
#define group 212            //network group 

//################################################################
//Data Structure to be received 
//################################################################
typedef struct {              //data structure to be received, must be same as on transmitter 
 int sec;
 int power;
 int temp;
} Payload;
Payload measurement; 
//########################################################

EtherCard eth;
MilliTimer requestTimer;
int test=1767;
unsigned long lastRead;    // millisecond timestamp of last analog input reading

static BufferFiller bufill;
static byte buf[300];   // a very small tcp/ip buffer is enough here


char tsHeader[] PROGMEM =    "GET /update?key=MySecretKey&field1=";
       
 char host[] PROGMEM =     "api.thingspeak.com";    
    
// called to fill in a request to send out data to the client
static word my_datafill_cb (byte fd) {
    BufferFiller bfill = eth.tcpOffset(buf);
    
    bfill.emit_p(PSTR("$F$D&field2=$D&field3=$D"), 
      tsHeader,measurement.sec,measurement.power,measurement.temp);
    bfill.emit_p(PSTR(" HTTP/1.1\r\n" "Host: $F\r\n" "\r\n"),host);
      
    return bfill.position();
}

// called when the client request is complete
static byte my_result_cb (byte fd, byte status, word off, word len) {
    Serial.print("<<< reply ");
    Serial.println((int) status);
    Serial.print((const char*) buf + off);
    return 0;
}

void setup () {
    Serial.begin(57600);
    Serial.println("\n[EtherServe test]");
    char ipaddr[18];
    mk_net_str(ipaddr,myip,4,'.',10);
    Serial.print("My IP: ");
    Serial.println(ipaddr);
        
    eth.spiInit();
    eth.initialize(mymac);
    eth.initIp(mymac, myip, 80);
    eth.clientSetGwIp(gwip);    // outgoing requests need a gateway
    eth.clientSetServerIp(hisip);
    
    rf12_initialize(MYNODE, freq,group);
    delay(1000);             // wait a second, just to be on safe side
    pinMode(0,INPUT);        // use pin 0 as (analog) input
    requestTimer.set(1); // send first request as soon as possible
    
    }
    
char okHeader[] PROGMEM = 
    "HTTP/1.0 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Pragma: no-cache\r\n"
    ;

static void homePage(BufferFiller& buf) {
   buf.emit_p(PSTR("$F\r\n"
   "<html><body>Analog Data<br>Time:$D Power:$D Temp:$D</body></html>"),
     okHeader,measurement.sec,measurement.power,measurement.temp);
}

void loop () {
    word len = eth.packetReceive(buf, sizeof buf);
    word pos = eth.packetLoop(buf, len);   // receive data from ENC28J60
 
    if (pos) {  // pos !=0 of there was a valid HTTP GET received
      bufill = eth.tcpOffset(buf);
      char* data = (char *) buf + pos;
      Serial.println(data);

       //receive buf hasn't been clobbered by reply yet
       if (strncmp("GET / ", data, 6) == 0) homePage(bufill); 
        
       eth.httpServerReply(buf,bufill.position()); // send web page data
    }
       
       
    // Receive data from RFM12
    //if (rf12_recvDone() && rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0  )  
    if (rf12_recvDone() && rf12_crc == 0 && rf12_len==sizeof(Payload) ) {
        measurement=*(Payload*) rf12_data;      //decode packet binary data into known data structure (same as Tx) http://jeelabs.org/2010/12/08/binary-packet-decoding-%E2%80%93-part-2/
        Serial.print("Data: "); Serial.println(measurement.power);        
     } else {
       if ((millis() - lastRead) > 1000) {
         lastRead = millis();
         measurement.sec = (int) (lastRead / 1000);
         measurement.power = analogRead(0); 
         measurement.temp = analogRead(1);
       }
     }
        
    
    if (eth.clientWaitingGw())
        return;
    
    if (requestTimer.poll(120000)) {
        Serial.print(">>> PUT# ");
        byte id = eth.clientTcpReq(my_result_cb, my_datafill_cb, hisport);
        Serial.println((int) id);
    }
}
 
Thanks a lot. A though nut to crack at first sight : it looks more as a server (with strings for a OK header and a html page) than as a client. Did you program thingspeak to connect to your JeeNode ? OTOH, the "PUT" at the end looks opposite.

If this sketch allows to have both a server and a client on the same Arduino-ish device with ENC28J60, that's really very very attractive ! Any volunteer to help wit the autopsia ?
 
Any volunteer to help wit the autopsia ?
Apparently, here as on other forums, this won't attract masses...
Apologies to paul for hijacking his forum, since JeeNode and ENC28J60 is not directly Teensy related. Still there is _very little_ on the subject to be found on Internet, and most is outdated, so this page gets high on Google's ranking now !

I just like to add that one fellow programmer called Phil Grant did also a sketch linking Thingspeak and ENC28J60. It's called Blank_Thingspeak_Ethercard.ino and seems recent (2014).
I didn't reproduce it here since there may be rights. It is very close to the original Pachube/Cosm/Xively example from Jeelabs, but interestingly, it uses the POST method, while your/John's code uses GET and/or PUT.
 
Hey it's only 5 years later, I'll try to fix some damage... that code I posted in 2014 was for an apparently early/ nonstandard/ obsolete version of the EtherCard library. It was doing two different things at once (both pushing data to Thingspeak, and acting as a super-simple local webserver). Anyway it does not compile with current versions of the library.

I still have that particular (now unobtainum) Jeelabs EtherShield rev0.1 device and I just tried it out today. It does work with the current library, for example in https://github.com/njh/EtherCard with the only change being the slave-select line "SS" in the ether.begin() setup call is changed to "8" because my board was wired for Pin 8 as slave select, instead of the more common Pin 10.
 
Status
Not open for further replies.
Back
Top