Pages: [1]   Go Down
Author Topic: Help with webserver and sd card logging  (Read 667 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
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;
}
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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...)
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 634
Posts: 50243
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
#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. smiley-cool
Logged

Auckland, New Zealand
Offline Offline
Newbie
*
Karma: 0
Posts: 4
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

France
Offline Offline
God Member
*****
Karma: 4
Posts: 972
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Pages: [1]   Go Up
Jump to: