Go Down

Topic: Help with webserver and sd card logging (Read 695 times) previous topic - next topic

pubman

I am working on my first Arduino project which is an electricity demand meter. The basic setup is that it counts pulses on one pin does some simple math and shows the demand on lcd the screen. It also logs this demand number and uses a web server to show the demand and allow you to download the logs. It is very much a hodgepodge of different scripts I have found online. My current problem I can't seem to figure out is that it keeps crashing and restarting. Originally I had the logging working but when I tried to add the file serving portion with the webserver the logging broke and the rebooting started. If anyone can take a look and see any obvious problems (and my awful coding practice) that would be a huge help. Thanks in advance.

Code: [Select]

#include <LiquidCrystal.h>
#include <Ethernet.h>
#include <Server.h>
#include <Udp.h>
#include <SPI.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <SD.h>
#define BUFSIZ 128

// Setup NIC
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x30, 0x92 };
byte ip[] = { 192,168,100,90  };
char rootFileName[] = "index.htm";
Server server(80);

// SD Card
const int chipSelect = 4;
Sd2Card card;
SdVolume volume;
SdFile file;
SdFile root;

// NTP Time stuff
unsigned int localPort = 8888;  // local port to listen for UDP packets
//byte timeServer[] = {192, 43, 244, 18}; // time.nist.gov NTP server
byte timeServer[] = {192, 168, 100, 5};  // time server ip address
const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

const int buttonPin = 2;    // the pin that the pushbutton is attached to
const double demandConstant = .72 * 4; //constant from utility to multiply count
const int demandChargeTime = 15; //number of minutes utility bases demand
const int demandLimit = 100; //number to warn when demand goes higher
double currentDemand = 0; // Current kW in last XX minutes
int demandLoopCount = 0;
int countArray[demandChargeTime];

LiquidCrystal lcd(9, 3, 8, 14, 5, 6, 7); //RS,RW,E,d4,d5,d6,d7
//int backLight = 2;    // will control the backlight

int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup() {
 //setup LCD
 lcd.begin(16, 2);//setup coloumn and rows
 lcd.clear();

 // start the Ethernet connection and the server:
 Ethernet.begin(mac, ip);
 Udp.begin(localPort);
 server.begin();
 
 // initialize the button pin as a input:
 pinMode(buttonPin, INPUT);

 Serial.begin(9600);
 
 // make sure that the default chip select pin is set to
 // output, even if you don't use it:
 lcd.setCursor(0,0);
 lcd.clear();
 lcd.print("Initial SD");
 lcd.clear();

 PgmPrint("Free RAM: ");
 Serial.println(FreeRam());  

 // Initialize SD Card
 pinMode(10, OUTPUT);              
 digitalWrite(10, HIGH);              

 if (!card.init(SPI_HALF_SPEED, 4))
 {
   lcd.print("SD Error - Card");
 }  
 if (!volume.init(&card))
 {
   lcd.print("SD Error - Vol");
 }
 
 PgmPrint("Volume is FAT");
 Serial.println(volume.fatType(),DEC);
 Serial.println();

 if (!root.openRoot(&volume)) Serial.print("openRoot failed");
 
 PgmPrintln("Files found in all dirs:");
 root.ls(LS_R);
 
 lcd.clear();
 lcd.print("SD card OK");
 delay(1000);
 
 lcd.clear();
 lcd.print("Syncing Time...");  
 
 //sync time to NTP
 getNTP();

 lcd.clear();
 lcd.setCursor(0,0);
 lcd.print(month());
 lcd.print("/");
 lcd.print(day());
 lcd.print("/");
 lcd.print(year());
 lcd.setCursor(0,1);
 lcd.print(hour());
 lcd.print(":");
 lcd.print(minute());
 lcd.print(" EST");
 delay(3000);
 lcd.clear();
   
 Alarm.timerRepeat(5, Demand); //Run demand loop every 60 secs, 5 for testing
 Alarm.timerRepeat(8, logData); //Run LogData every 5min = 300
   
}



Code continued on next post

pubman

Code: [Select]

void loop() {
   
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      lcd.setCursor(0,1);
      lcd.print("*");
    }
    else {
      // if the current state is LOW then the button wend from on to off:
     lcd.setCursor(0,1);
     lcd.print(" ");
    }
  }
  //save the current state as the last state,for next time through the loop
  lastButtonState = buttonState;

  //need this for repeated loops to work
  Alarm.delay(1);
 
  webServer();
}

void Demand()
{
  if(demandLoopCount <= (demandChargeTime-2))
  { 
    countArray[demandLoopCount] = buttonPushCounter;
    demandLoopCount++;
  }
  else {
    for(int i=0; i <= (demandChargeTime-2); i++)
    {
      countArray[i] = countArray[i+1];
    }
   
    //demandLoopCount should be 15 here
    countArray[(demandChargeTime-1)] = buttonPushCounter;
    currentDemand = demandConstant * (countArray[(demandChargeTime-1)]-countArray[0]);

    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Demand ");
    lcd.print(currentDemand);
    lcd.print(" kW");
  }
}

void webServer()
{
  char clientline[BUFSIZ];
  char *filename;
  int image = 0;
  int index = 0;

  Client client = server.available();
  if (client) {
    boolean current_line_is_blank = true;

    index = 0;

    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        if (c != '\n' && c != '\r') {
          clientline[index] = c;
          index++;
          if (index >= BUFSIZ)
            index = BUFSIZ -1;

          continue;
        }

        clientline[index] = 0;
        filename = 0;

        Serial.println(clientline);

        if (strstr(clientline, "GET / ") != 0) {
          filename = rootFileName;

        }
        if (strstr(clientline, "GET /") != 0) {
          if (!filename) filename = clientline + 5;

          (strstr(clientline, " HTTP"))[0] = 0;

          Serial.println(filename);

          if (! file.open(&file, filename, O_READ)) {
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<h2>Error 404</h2>");
            client.println("<s2>The file does not exist.<s2>");
            client.println("");
            break;
          }

          Serial.println("Opened!");
          //File types
          client.println("HTTP/1.1 200 OK");
          if (strstr(filename, ".htm") != 0)
            client.println("Content-Type: text/html");
          else if (strstr(filename, ".css") != 0)
            client.println("Content-Type: text/css");
          else if (strstr(filename, ".png") != 0)
            client.println("Content-Type: image/png");
          else if (strstr(filename, ".jpg") != 0)
            client.println("Content-Type: image/jpeg");
          else if (strstr(filename, ".gif") != 0)
            client.println("Content-Type: image/gif");
          else if (strstr(filename, ".3gp") != 0)
            client.println("Content-Type: video/mpeg");
          else if (strstr(filename, ".pdf") != 0)
            client.println("Content-Type: application/pdf");
          else if (strstr(filename, ".js") != 0)
            client.println("Content-Type: application/x-javascript");
          else if (strstr(filename, ".xml") != 0)
            client.println("Content-Type: application/xml");
          else
            client.println("Content-Type: text");
          client.println();

          int16_t c;
          while ((c = file.read()) >= 0) {
            //Serial.print((char)c); //Prints all HTML code to serial (For debuging)
            client.print((char)c); //Prints all HTML code for web page
          }


          // client.print("end");

          file.close();

        }
        else {
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>Error 404</h2>");
          client.println("");
        }
        break;
      }
    }

    delay(1);

    client.stop();
  }

}


void logData()
{
if (demandLoopCount == 14)
{
    // open the file. note that only one file can be open at a time,
    // so you have to close this one before opening another.
   
    String fixMonth = String(month());
    String fixDay = String(day());
 
    if (month() < 10)
    { 
      fixMonth = "0" + String(month());           
    }
    if (day() < 10)
    { 
      fixDay = "0" + String(day());   
    }
       
    String filename = String(year()) + fixMonth + fixDay + ".csvv";
   
    char filename1[filename.length()];
    filename.toCharArray(filename1, filename.length());
   
    File dataFile = SD.open(filename1, FILE_WRITE);
    // if the file is available, write to it:
    if (dataFile) {
      String time =  String(hour()) + ":" + String(minute());
      String data = time + "," +  tempToAscii(currentDemand); 
      dataFile.println(data);
      dataFile.close();
      lcd.setCursor(15,1);
      lcd.print("|");
      delay(1000);
      lcd.setCursor(15,1);
      lcd.print(" ");
      Serial.println(data);
  } 
 
  // if the file isn't open, pop up an error:
  else {
    Serial.println("Error");
  }
}
}

void getNTP()
{
  Udp.begin(localPort);
   
  sendNTPpacket(timeServer); // send an NTP packet to a time server

    // wait to see if a reply is available
  delay(1000); 
 
  if ( Udp.available() ) { 
    Udp.readPacket(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord; 
           
    // now convert NTP time into everyday time:
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;     
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears; 
     
    setTime(epoch);
    int dst = hour() - 5; //adjust for standard time
    setTime(dst,minute(),second(),day(),month(),year());
   
  }
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(byte *address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:    
  Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE,  address, 123); //NTP requests are to port 123
}

char* tempToAscii(float temp)
// convert long to type char and store in variable array ascii
{
  char ascii[20];// needs to be this big to hold a type float

  int frac;
  int rnd;

    rnd = (unsigned int)(temp*1000)%10;
    frac=(unsigned int)(temp*100)%100;  //get three numbers to the right of the deciaml point.
    if (rnd>=5) frac=frac+1;
    itoa((int)temp,ascii,10);           // I changed it to 2 decimal places
    strcat(ascii,".");

  if (frac<10)  {itoa(0,&ascii[strlen(ascii)],10); }   // if fract < 10 should print .0 fract ie if fract=6 print .06

    itoa(frac,&ascii[strlen(ascii)],10); //put the frac after the deciaml
   
  return ascii;
}

bjoernhoefer

Hi, sorry I have to tell you "I can't help" you.

But if it helps you (just a little bit) your code helped me very much!

I was looking for a solution to query a NTP and not only set the time...

Thank you very much (or many as the leningrad cowboys say...)

PaulS

Code: [Select]
#include <LiquidCrystal.h>
#include <Ethernet.h>
#include <Server.h>
#include <Udp.h>
#include <SPI.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <SD.h>
#define BUFSIZ 128

That's a lot of libraries, each of which makes demands on SRAM. Which Arduino are you using? In other words, how much SRAM do you have? Have you used the FreeMemory() function (it's search time...) to see how much SRAM you still have left? -37 is not a good number. 8)

KiwiMorpheus

Have a look at my code for Sending Email with a correct Date/Time header. I have some NTP code that works perfectly, not to mention DHCP!

http://arduino.cc/forum/index.php/topic,63432.msg462587.html#msg462587

al1fch

Not clear when browsing rapidly your code :

In case you use "official' Ethershield do you carefully manage chipselect for each chip on SPI bus (SD and W5100) ?

-> You must deselect one when dealing with other. (only one CS pin low : pin 4  when SD, pin10 when ethernet)

"Note that because the W5100 and SD card share the SPI bus, only one can be active at a time. If you are using both peripherals in your program, this should be taken care of by the corresponding libraries. If you're not using one of the peripherals in your program, however, you'll need to explicitly deselect it. To do this with the SD card, set pin 4 as an output and write a high to it. For the W5100, set digital pin 10 as a high output"  as mentionned on Ethersield page.

pubman

Hi guys, thanks for all the replies on this. I didn't get any notification emails for replies to the thread. Going to pull out my project and check out the suggestions. Thanks again.

Go Up