Can the Xenon serve web page if attached to Ethernet FeatherWing?

Is it possible to use the Xenon to server a web page for simple control of mesh.
For example.
Xenon is connected to my network by the Ethernet featherwing.
I then access the Xenon local IP address to access the webpage.
I then use the Web page to send mesh.publish via the xenon to other devices on the mesh network?

I know you could use the Core to possibly serve a webpage but it has no mesh, Would this be possible at all? I can not find any documentation to suggest this is or is no possible.?
Thanks

I have served up a webpage with an arduino uno connected to an Ethernet shield. The code is similar so I would imagine you can do it with the xenon.
I had memory size issues with the arduino though so I had to load the static strings into the eeprom memory if I remember right.

1 Like

Don’t happen to know where an example code is? I tried the webserver library but did not have much luck.

Let me dig out my arduino code, I’ll send it along in a bit.

1 Like

I’m 99.9% positive you can do this.

1 Like

Here is what I dug out from years ago. I am a hack so don’t trash the code.

/*--------------------------------------------------------------
  Program:      eth_websrv_SD
 
  Description:  Arduino webserver that shows a webpage with the measured heat index data displayed in a series of google charts.
                The heat index is calculated by measuring the temperature and humidity and then performing a calculation.
                The displayed webpage will show the current data with a quick visual indicator and basic configuration information.
                There will be three charts on the webpage.
                  One will be the current day with five minute intervals.  The highest value during the five minute interval will be used.
                  The second will be the past 7 days worth of data, with 30 minute intervals.  The highest value during the interval will be used.
                  The third chart will show the past 30 days worth of data, with 90 minute intervales.  The highest value during the interval will be used.
                  
                  TODO:
                  1.  Have to figure out how to send e-mails.
                  2.  Make statement about further data being kept on SD card.
                  2.  Must figure out how to get the time set.
                      a.  email_sd_test.ino has a thing that samples time from the pachube server
                      b.  There is also a way to set the date/time via the url parameters
               
  
  Hardware:     Arduino Uno and official Arduino Ethernet
                shield. Should work with other Arduinos and
                compatible Ethernet shields.
                2Gb micro SD card formatted FAT16
               
  Software:     Developed using Arduino 1.0.3 software
                Should be compatible with Arduino 1.0 +
                SD card contains web page called index.htm
 
  References:   - WebServer example by David A. Mellis and
                  modified by Tom Igoe
                - SD card examples by David A. Mellis and
                  Tom Igoe
                - Ethernet library documentation:
                  http://arduino.cc/en/Reference/Ethernet
                - SD Card library documentation:
                  http://arduino.cc/en/Reference/SD
 
  Date:         10 January 2013
  Author:       W.A. Smith, http://startingelectronics.com
-------------------------------------------------------------- */
 
// The defines & includes required to get all of the required functionality /////////////////////////////////////////////////////////////////////////
#include <SPI.h>
#include <Ethernet.h>         // This is the Ethernet library
#include <SD.h>               // This is the SD Card library
#include <DHT22.h>            // This is the temperature sensor library
#include <Time.h>             // This is the time library
 
// Defines for the DHT22 Instance ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  #define DHT22_PIN A2           // Data wire is plugged into port 0 on the Arduino
  DHT22 myDHT22(DHT22_PIN);      // Setup a DHT22 instance
  char* errorValue = "E4.htm";   //  The error value returned by the sensor
  float farenheitTemp = 0;       //  The farenheit temperature calculated from the celcius temp
  float humidity = 0;            //  The directly read humidity value
  float heatIndexTemp = 0;       //  The heat index value calculated from temp and humidity
  float delayHeatIndexTemp = 80; //  The heat index value calculated from temp and humidity
  const float decay = 0.006738;  // The decay coefficient.  This came from e^(-1/0.2)
  const float LPF = 0.04;        // The Low Pass Filter coefficient
 
// The email program flow values ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  char* levelNow = "L4.htm";                 // Current temp threshold level
  char* levelPrev = "L4.htm";                         // Temp threshold level the system was at on previous scan
 
// Ethernet configuration stuff here ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
byte mac[] = {0x90, 0xA2, 0xDA, 0x0D, 0x65, 0xC0};  // This is the MAC address from the sticker on the board -  Ethernet Shield
IPAddress ip(192, 168, 0, 200); // IP address, may need to change depending on network
EthernetServer server(80);  // create a server at port 80
 
// SD card configuration stuff here /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
File webFile;
 
// Time control variables /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
char* j = "0";
char* newHour = "00";
char* newMinute = "00";
char* newDay = "00";
char* newMonth = "00";
char* newYear = "0000";
 
// General program flow values here ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int currMin = 0;               // This is the current minute value that is to be used later for decision making.
int prevMin = 0;               // This is the previous minute value that is to be used later for decision making.
int currHour = 0;              // This is the current hour value that is to be used later for decision making.
boolean dayData = false;       // This is used to know that the 5 min interval data has been written.
 
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
//                                                                   ARDUINO REQUIRED FUNCTIONS
//
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  This routine is required by the Arduino sketch.  It is a one time execution
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
    Ethernet.begin(mac, ip);  // initialize Ethernet device
    server.begin();           // start to listen for clients
   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("one.htm")) {
        //Serial.println("ERROR - Can't find one.htm file!");
        return;  // can't find index file
    }
    //Serial.println("SUCCESS - Found files.");
   
    // Set the time
    setTime(21,11,00,8,1,2013);
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  This routine is required by the Arduino sketch.  This is the main() function in regular C.
//  It constantly loops through, executing the code as it does
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
 
    boolean bailOut = false;       // Used to know when the first line of the POSTed webpage has been received
    boolean sawFlag = false;       // Used to know if the '?' symbol has been seen
    char input[4];                              // The variable used to store the POSTed data into, limited to 100
    int i = 0;                                   // Just a loop counter
    int j = 0;                                   // Just a loop counter
   
    currMin = minute();            // Get the current minute value for looping determination
   
    if (currMin != prevMin){                     // THIS BIT WILL OUTPUT THE TEMP DATA FOR THE GRAPHS IF IT IS A NEW MINUTE
      readSensor();                              // Read the  temp and humidity sensor and calculate the heat index temp
      if ((currMin % 5) == 0){
        exportData("day.htm",false);             // Export weekly data       
        currHour = (hour() % 3);                 // Get current hour, and divide by three.
       
        if ((currMin == 0) || (currMin == 30)){  //This was a switch statement but eliminated it to reduce sketch size
          exportData("week.htm",true);           // Export weekly data
          if (currHour == 0){                    // Remainder of hour/3 is zero so hour is 0,3,6,9,12,15,18,21 which is when output should be made when minute = 0.
            exportData("month.htm",true);        // Export monthly data
          }
        }       
      }
      prevMin = currMin;
    } 
    
    // THIS BIT IS THE WEBSERVER SECTION LISTENING FOR REQUESTS AND SENDING DATA
    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
                if (c == '?'){                         // If it sees the ? set a flag to store input
                  if (sawFlag == true){
                    sawFlag = false;
                  }
                  else{
                    sawFlag = true;
                  }
                  
                }
                if ((!bailOut) && (sawFlag) && (c!='?')){    // If no line feed and a ? mark seen, read the input
                    // This should parse the input html string to get the date and time from the string.
//                  if (i==2){
//                    newMonth[0] = input[i-2];
//                    newMonth[1] = input[i-1];
//                    Serial.print(newHour);
//                  } 
          //        if (i==5){
            //        newMinute[0] = input[i-2];
              //      newMinute[1] = input[i-1];
                //    Serial.print(newMinute);
                  //} 
//                  if (i==8){
  //                  newMonth[0] = input[i-2];
    //                newMonth[1] = input[i-1];
      //              Serial.print(newMonth);
        //          } 
          //        if (i==11){
            //        newDay[0] = input[i-2];
              //      newDay[1] = input[i-1];
                //    Serial.print(newDay);
                  //} 
                  Serial.print(c);
                  input[i] = c;                           // Store into the character array
                  i++;                    // Increment the character array position 
               }
                                          
                if (c == '\n' && currentLineIsBlank) {           // respond to client only after last line received
                   
                    outputClient("One.htm", client);             // The first static section of the webpage is opened includes a a standard http response header
                    client.print(farenheitTemp);                 // The measured farenheit temperature is output to the client
                    outputClient("Two.htm", client);             // This is the transition betwnee the temp and humidity
                    client.print(humidity);                      // The measured humidity is output to the client
                    outputClient("Three.htm", client);           // This is the transition between humidty and the feels like temperature
                    client.print(heatIndexTemp);                 // The heat index temperature is output to the client
                    outputClient(errorValue, client);            // Now evaluate the warning condition and determine what file to open
                    outputClient(levelNow, client);              // Now evaluate the warning condition and determine what file to open
                    outputClient("email.htm", client);           // Send the email data information
                    outputClient("Four.htm", client);            // Send the header for the daily chart
                    outputClient("day.htm", client);             // Now send the data for the weekly chart.              
                    outputClient("Five.htm", client);            // Now send the transition between the daily and weekly charts.
                    outputClient("week.htm", client);            // Now send the data for the weekly chart.                
                    outputClient("Six.htm", client);             // Now send the transition between the weekly and monthly charts.
                    outputClient("month.htm", client);           // Now send the data for the monthly chart.
                    outputClient("Seven.htm", client);           // Send the end of the page page file                   
 
                    break;
                }
            } // 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)
   
    
}
 
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
//                                                               WEBSERVER AND FILE MANIPULATION VARIABLES
//
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  This routine will write the sensor data to the data file used to generate the weekly data graph.
//  TODO:  Build error handling into the routine to catch bad card writes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void exportData(char* currFile, boolean writeDate)
{
     // open the file. note that only one file can be open at a time,   
    webFile = SD.open(currFile, FILE_WRITE);        // open web data page file
    if (webFile) {
      //webFile.println(",");
      webFile.print(",['");
      if (writeDate==true){
        webFile.print(month());
        webFile.print("-");     
        webFile.print(day());
        webFile.print("-");  
        webFile.print(year());
        webFile.print(" ");           
      } 
      webFile.print(hour());
      webFile.print(":");
      if (minute()<10){
        webFile.print('0');
      } 
      webFile.print(minute());
      webFile.print("',");
      webFile.print(delayHeatIndexTemp);     
      webFile.println(", 90, 105, 120]");
      webFile.close();
    }
    //Serial.println("Called write to file."); 
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  This routine will open the specified file, and output it if it is available
//  TODO:  Build error handling into the routine to catch bad card writes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void outputClient(char* currFile, EthernetClient client)
{
 
    webFile = SD.open(currFile);                 // open the file containing the data for the chart with the past 24 hours shown
    if (webFile) {
      if (webFile.size() > 5000){                // See if there is a days worth of data to show TODO: set size properly
        webFile.seek(webFile.size() - 5000);     // Back up to the starting point
        while(webFile.peek()!=91){               // See if a '[' is the file position
          if (webFile.position() > 1){           // Don't backup past the file starting position
            webFile.seek(webFile.position()-1);  // Backup one file position
          }
          else{
            webFile.seek(0);                     // It was at the 1st of the file, go to the starting point
            break;                               // Exit the while loop
          }
          }
        } 
        while(webFile.available()) {
            client.write(webFile.read());       // Send the chart data to the client
        }
        webFile.close();
    }   
    
    //Serial.println("Called write to file.");      
    
}
 
 
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
//                                                                   TEMPERATURE FUNCTIONS
//
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  This routine will read information from the temp/humidity sensor.  It is the Sparkfun SEN-10167
//  The routine will read from the sensor, and if it is a valid read will calculate the heat index and the
//  Temperature warning level using the filtered heat index value
//  If the data is not read, then the temperature will not be changed.  An error flag will be set.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void readSensor(){
 
  delay(2000);  // The sensor can only be read from every 1-2s, and requires a minimum 2 second warm-up after power-on.
 
  DHT22_ERROR_t errorCode;
 
  errorCode = myDHT22.readData();
 
  switch(errorCode){
    case DHT_ERROR_NONE:
      errorValue = "E0.htm";
      //errorDescription = "Data Read OK";
      farenheitTemp = myDHT22.getTemperatureC();
      farenheitTemp = (((9.0/5.0)*farenheitTemp)+32);
      humidity = myDHT22.getHumidity();
      heatIndex(farenheitTemp, myDHT22.getHumidity());
      break;
     
    case DHT_ERROR_CHECKSUM:
      errorValue = "E1.htm";
      //errorDescription = "Checksum Error, maybe bad values";
      farenheitTemp = myDHT22.getTemperatureC();
      farenheitTemp = (((9.0/5.0)*farenheitTemp)+32);
      humidity = myDHT22.getHumidity();
      heatIndex(farenheitTemp, myDHT22.getHumidity());
      break;
     
    case DHT_BUS_HUNG:
      errorValue = "E2.htm";
      //errorDescription = "BUS Hung, no values read";
      break;
     
    default:
      errorValue = "E3.htm";
 
//    case DHT_ERROR_NOT_PRESENT:
//      errorValue = "E3.htm";
//      //errorDescription = "Sensor not present.";
//      break;
     
//    case DHT_ERROR_ACK_TOO_LONG:
//      errorValue = "E3.htm";
//      //errorDescription = "Data Timeout, no values read";
//      break;
     
//    case DHT_ERROR_SYNC_TIMEOUT:
//      errorValue = "E3.htm";
//      //errorDescription = "Sync Timeout, no values read";
//      break;
     
//    case DHT_ERROR_DATA_TIMEOUT:
//      errorValue = "E3.htm";
//      //errorDescription = "Data Timeout, no values read";
//      break;     
      
//    case DHT_ERROR_TOOQUICK:
//      errorValue = "E3.htm";
//      //errorDescription = "Polled too quick, no values read";
//      break;     
  }
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  This routine will calculate the heat index temperature.  Equation is from Wikipedia as of April 2012.  Calculated values match
//  those of NOAA and other websites and the readily available heat index charts.
//  Per accepted practice, heat index calclulation is only performed if the temperature is above 80 degrees, or the
//  relative humidity is above 40%
//  If neither are true then heat index temperature will be equal to measured temperature.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void heatIndex(float temp, float RH){
 
  // If temp is above 80 and humidity is above 40, compute the heat index temp.
  if (temp>80.0){                                 
    if (RH>=40.0){
      heatIndexTemp=16.923+((1.85212*(0.1))*temp);
      heatIndexTemp = heatIndexTemp + (5.37941*RH)-((1.00254*(0.1))*temp*RH);
      heatIndexTemp = heatIndexTemp + (9.41695*(0.001)*(temp*temp))+(7.28898*(0.001)*(RH*RH));
      heatIndexTemp = heatIndexTemp + (3.45372*(0.0001)*(temp*temp)*RH)-(8.14971*(0.0001)*temp*(RH*RH));
      heatIndexTemp = heatIndexTemp + (1.02102*(0.00001)*(temp*temp)* (RH*RH))-(3.8646*(0.00001)*(temp*temp*temp));
      heatIndexTemp = heatIndexTemp + (2.91583*(0.00001)*(RH*RH*RH))+(1.42721*(0.000001)*(temp*temp*temp)*RH);
      heatIndexTemp = heatIndexTemp + (1.97483*(0.0000001)*temp*(RH*RH*RH));
      heatIndexTemp = heatIndexTemp - (2.18429*(0.00000001)*(temp*temp*temp)*(RH*RH));
      heatIndexTemp = heatIndexTemp + (8.43296*(0.0000000001)* (temp*temp)*(RH*RH*RH))-(4.81975* (0.00000000001)*(temp*temp*temp)*(RH*RH*RH));
    }
    else
    {
      heatIndexTemp = temp;
    }
  }
  else
  {
    heatIndexTemp = temp;
  }
 
  
  //  Calculate a delayed decaying value, then run it through a LPF to create a smoothed value determine warning levels. 
  //  The equation is:  Previous + LPFCoefficient * ((Actual + Previous*DecayCoeffient)-Previous)
  //  the value is calculated incrementally to avoid issues with computation
 
  // Calculate the decay, which is:  (Actual + Previous*DecayCoeffient)
  temp = (delayHeatIndexTemp*decay);  //The multiplactive part of the decay or Previous*DecayCoeffient
  temp = heatIndexTemp + temp;        //The final part and the resulting decayed signal which is (Actual + Previous*DecayCoeffient)
 
  // Calculate the LPF result of the decayed signal, which is: 
  temp = (temp-delayHeatIndexTemp);  // The summing portion of the LPF, which is ((Actual + Previous*DecayCoeffient) - Previous)
  temp = LPF*temp;                   // The LPF part of the signal, which is LPFCoefficient * ((Actual + Previous*DecayCoeffient)-Previous)
 
  // Calculate the final decayed, filtered result
  delayHeatIndexTemp = delayHeatIndexTemp + temp;  // The full equation result, Previous + LPFCoefficient * ((Actual + Previous*DecayCoeffient)-Previous)
 
  
  // Now, using the smoothed heat index temperatue, determine what the current warning level state is.
  if ((delayHeatIndexTemp>90) && (delayHeatIndexTemp<=105)){
    levelNow = "L3.htm";                                                  // Above 90 and less than 105, so at level 3, as set by Corporate
  }
  else if ((delayHeatIndexTemp>105) && (delayHeatIndexTemp<=120)){
    levelNow = "L2.htm";                                                  // Above 105 and less than 120, so at level 2, as set by Corporate
  }
  else if (delayHeatIndexTemp>120){
    levelNow = "L1.htm";                                                  // Above 120, so at level 1, as set by Corporate
  }
  else{
    levelNow = "L4.htm";                                                  // Below 90, so at level 4, as set by Corporate
  }
}

I’m very much a hack also, it will give me a place to start. Thank you.

You need to enable the EtherWing as shown here
https://docs.particle.io/reference/device-os/firmware/xenon/#ethernet

Otherwise the TCPStack won’t be enabled.

And you should be able to use the WebServer library - unfortunately this librar does not provide usage exables. But there is another library WebDuino which contains samples and these should be running with only minor changes.

2 Likes

Thanks @ScruffR and @jwheasi. I have a web page running on the Xenon now and can control other mesh devices over mesh.

Ill post the code here once I tidy it up so others can use/modify.

5 Likes