/* Reads data from 2 analog inputs V1, V2 connected repectively to pins A3 and A2. NB The Ethernet shield uses A0 and A1 for connections to the SD card! "nSamp" readings are taken at sInt intervals and an average calculated. The average is then converted to milliVolts. The interval between each batch of readings is controlled using a timer Interrupt Voltage readings, preceded by "System Uptime" are written to a file on the SD card of the Ethernet Shield. The Ethernet Shield supports the SD card, and provides a platform for an Ethernet Server this serves out the data from the SD Card file as an HTML table, which can be imported to excel! Written by John Errington MSc 7 March 2016 * Includes snippets of code which are in the public domain. * from Simon Monk's Timer Library. * In particular, the Digital Clock uses code from an example given in the "Simple Timer" Library. */ #include // include Simon Monk's Timer Library Timer t; // Current Timer #include // include SD card Library File myFile; // Current file on SD Card const byte csPin = 4; // DIO pin for CS on SD Card on Ethernet Shield #include // SPI bus library #include // Ethernet library byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // MAC Address IPAddress ip(192,168,1,29); // local IP Address for server EthernetServer server(80); // port 80 is default for HTTP EthernetClient myClient; // Current Ethernet client // sampling parameters const int nSamp = 50; // Number of Samples to take for each reading const int slow = 60; // sample every 60 sec = 1 min const int fast = 6; // sample every 6 sec = 0.1 min unsigned long sInt = fast; // sampling interval in seconds //calculating voltages //these need to be floats to be passed to ConvReadings to calculate average, add offset, and scale to V float sum1 = 0; float sum2 = 0; float divisor; // later assigned = nSamp // %%%%% the offset is added to the total of 50 readings before averaging - to support greater accuracy %%%%%%% // %%%%% get the right value by looking at the reading total on the serial I/O %%%%%%% const float offset = 0; // put in correct value after calibration - make sure the lowest reading stays above zero const float V1scale2V5 = 2.438; // (=2500mV/1024) - corrected value after calibration const float V2scale2V5 = 2.438; // (=2500mV/1024) - corrected value after calibration const float V1scale25V = 25.130; // (=25000mV/1024) - corrected value after calibration const float V2scale25V = 24.960; // (=25000mV/1024) - corrected value after calibration float V1scalefactor; float V2scalefactor; // The Clock variables are declared globally so that they can be accessed anywhere. unsigned long startTime; // value of millis() when start switch pressed unsigned long currSeconds; // Current "Uptime" seconds unsigned long currMinutes; // Current "Uptime" minutes int currHours; // Current "Uptime" hours float elapsedMinutes; // (in tenths) for graphing data in Excel int timeout = 100; // time in fifths of seconds to wait for start signal char charArray[9]; // Dimension this to be size of largest data item + 1 boolean endOfLine = false; // Flag for end of line of data //Analog & digital pin assignments const byte V1Pin = 3; // analog pin 3 for voltage 1 const byte V2Pin = 2; // analog pin 2 for voltage 2 const byte StartPin = 2; // Digital pin for start switch const byte ratePin = 3; // Digital pin for rate select switch fast / slow const byte SDSSPin = 4; // Digital pin 4 is SS (slave select) for SD card - must be left as output const byte LEDPin = 5; // Digital pin 5 for LED const byte V1Scale = 6; // Digital pin 6 connects to range switch (2.5 or 25) on S1 const byte V2Scale = 7; // Digital pin 7 connects to range switch (2.5 or 25) on S2 void setup() { Ethernet.begin(mac, ip); // open the Ethernet connection server.begin(); // start the Ethernet server Serial.begin(9600); Serial.print("\nNetlogger_r6 program start"); // For Mega boards with an Ethernet shield, make sure the Wiznet chip is not selected: pinMode(10, OUTPUT); digitalWrite(10, HIGH); // davekw7x: If it's low, the Wiznet chip corrupts the SPI bus pinMode(53, OUTPUT); // 53 must be set as output on the MEGA, otherwise SD library won't work! digitalWrite(53, HIGH); // Assign modes for digital pins. All inputs use internal pullups. pinMode(StartPin, INPUT_PULLUP); pinMode(ratePin, INPUT_PULLUP); pinMode(SDSSPin, OUTPUT); // Must be output or SD card will not work pinMode(V1Scale, INPUT_PULLUP); pinMode(V2Scale, INPUT_PULLUP); pinMode(LEDPin, OUTPUT); initSDCard(); // Initialise the SD card and check for error digitalWrite(LEDPin, LOW); //LED off analogReference(INTERNAL2V56); // can not use "INTERNAL" internal 1.1v Analog Reference Voltage on MEGA // DEFAULT = 5V +- 0.25V; Alternative is INTERNAL2V56 SD.remove("test1.txt"); // Delete old data file // ready to start taking readings. First check the switches. // The rate switch may not be changed during a test run. Serial.println("\nUse switch to select sample interval - 6 sec or 1 min"); if(digitalRead(ratePin)){sInt=fast;} else{sInt=slow;} Serial.print("Sample interval seconds is "); Serial.print(sInt); //check range switrches & print range selected if(digitalRead(V1Scale)){V1scalefactor=V1scale25V;}else{V1scalefactor=V1scale2V5;} if(digitalRead(V2Scale)){V2scalefactor=V2scale25V;}else{V2scalefactor=V2scale2V5;} Serial.print("; V1 scale factor is "); Serial.print(V1scalefactor,3); Serial.print("; V2 scale factor is "); Serial.print(V2scalefactor,3); //wait for start signal. If not used: int waiting = timeout; while(digitalRead(StartPin)) { // flash led fast (4 a second) to indicate ready to start taking readings digitalWrite(LEDPin, HIGH); //LED on delay(50); digitalWrite(LEDPin, LOW); //LED off delay(200); waiting -=1; if (waiting == 0) //No start pressed: suspend execution with LED off. { Serial.println(" "); Serial.println("Start signal not detected. Powering down."); halt(); } } startTime = millis(); // actual Start Time ie when switch is pressed digitalWrite(LEDPin, HIGH); //LED on // Setup the repeating event to maintain and display the System Uptime int tickEvent = t.every(1 * 1000, updateClock); // Setup the repeating event to read and display the voltages every sInt seconds int readEvent = t.every(sInt * 1000, readAnalog); //Dont wait for the timer, take the first reading at t=0 updateClock(); readAnalog(); } void loop() { t.update(); // update the timer events listenForClient(); // check for web client request } // // Timer Event Routine to update the current "System Uptime" // void updateClock() { unsigned long actMillis; // Unsigned, to avoid rollover before 50 days has elapsed actMillis = millis() - startTime; // Correct for actual time of Switch press currSeconds = actMillis / 1000; elapsedMinutes = (float) (currSeconds + 2) / 60; // round elapsed minutes to 1 dp currMinutes = currSeconds / 60; currHours = currSeconds / 3600; currSeconds = currSeconds - currMinutes * 60; currMinutes = currMinutes - currHours * 60; } // // check range switches, read analog inputs, average, convert to mV and print to file // void readAnalog() { digitalWrite(LEDPin, LOW); //LED off //check range switches to make sure scale factor matches input sensitivity if(digitalRead(V1Scale)){V1scalefactor=V1scale25V;}else{V1scalefactor=V1scale2V5;} if(digitalRead(V2Scale)){V2scalefactor=V2scale25V;}else{V2scalefactor=V2scale2V5;} for (byte count = 0; count < nSamp; count++ ) { int reading; reading = analogRead(V1Pin); // dummy read to settle ADC Mux delay(5); reading = analogRead(V1Pin); // actual read sum1 = sum1 + reading; reading = analogRead(V2Pin); // dummy read to settle ADC Mux delay(5); reading = analogRead(V2Pin); // actual read sum2 = sum2 + reading; delay(10); } digitalWrite(LEDPin, HIGH); //LED on // we print reading values to help with calibration Serial.print("\ntotals of 50 readings are: V1: "); Serial.print(sum1,0); Serial.print("; V2: "); Serial.print(sum2,0); // Write current System Uptime and readings to SD Card file // with a semicolon between each data value myFile = SD.open("test1.txt",FILE_WRITE); // Open File if(myFile) { writeZero(currHours); // write hh:mm:ss to file myFile.print(":"); writeZero(currMinutes); myFile.print(":"); writeZero(currSeconds); myFile.print(";"); myFile.print(elapsedMinutes,1); // print total elapsed minutes myFile.print(";"); divisor = nSamp; // divisor is floating point // Calculate average, convert to voltage, and write to file convReadings(sum1, divisor, V1scalefactor); convReadings(sum2, divisor, V2scalefactor); myFile.println(""); // force new line into SD file myFile.close(); } else { // if the file didn't open, print an error: Serial.println("error opening test1.txt"); } sum1 = 0; sum2 = 0; } // write leading 0 and digits to SD card File // void writeZero(int digits) { if(digits < 10) myFile.print('0'); myFile.print(digits); } // // Calculate average sensor reading, convert to voltage and write to file // void convReadings(float total, float number, float scale) { //adjust for offset total = total + offset; float average = total / number; float mV = average * scale; // mV = average * Vref / 1.024 if(mV<0){mV=0;}; myFile.print(mV,0); // no figures after dp as resolution is not sufficient myFile.print(";"); // Note that a semicolon is written after EACH data item } // // Initialise SD Card. Halt if initialisation fails // void initSDCard() { if (!SD.begin(csPin)) { Serial.print("\nCheck SD Card"); halt(); } Serial.print("\nSD Card OK"); } // // Simulate program halt with infite do nothing loop // void halt() { unsigned int slowflash; while(1) { slowflash=60; while (slowflash!=0) { slowflash-=1; delay(1000); } // short flash led once a minute just to show its still powered on. digitalWrite(LEDPin, HIGH); //LED on delay(100); digitalWrite(LEDPin, LOW); //LED off Serial.println("Powered down. Press reset to start sampling cycle."); } } // // Listen for incoming Web Client Requests // void listenForClient() { myClient = server.available(); // listen for incoming clients if (myClient) { while (myClient.connected()) { if (myClient.available()) // read until no more characters - Chrome does 2 requests! { char c = myClient.read(); Serial.write(c); } else { startHTTP(); // send http response header fileToClient(); //Send contents of SD Card Text file to the client myClient.println(""); // end of HTML break; } } delay(1); // give the web browser time to receive the data myClient.stop(); // close the HTTP connection: Serial.println("client disconnected"); } } // // Respond to the HTTP request and print start of HTML page with headline // void startHTTP() { myClient.println("HTTP/1.1 200 OK"); myClient.println("Content-Type: text/html"); myClient.println("Connection: close"); // connection closed after completion of the response myClient.println("Refresh: 30"); // refresh the page automatically every 30 sec myClient.println(); myClient.println(""); myClient.println(""); myClient.println("

Netlogger_r5 by J Errington MSc 24 March 2016

"); myClient.print("

"); myClient.print("selected sample rate in seconds: "); myClient.print(sInt); myClient.println("

"); } // // Send contents of SD Card file to HTTP Client // void fileToClient() { myFile = SD.open("test1.txt"); // Open the SD Card file myClient.print(""); myClient.print(""); while (myFile.available()) // Read data from the file until end of file { myClient.print(""); // start HTML table row endOfLine = false; while (endOfLine == false) // repeat for each part of dataset until end of line { myClient.print(""); // end of table data cell } myClient.print(""); // end of HTML table row } myFile.close(); myClient.print("
time since startelapsed minutesV1 mVV2 mV
"); // start table data cell getData(charArray); // extract a data item from current line of file myClient.print(charArray); // write data value myClient.print("
"); // end HTML table myClient.println("
"); myClient.println(""); } // // Read data from a file and parse into data items as the line is read. SemiColon separator. // The data item is returned in a character array. No need to know how many data items in // each line or how long the data items are. Called from fileToClient // void getData(char *dataItem) { for ( byte i = 0; ; i++) // read characters from file { char c = myFile.read(); if (c != ';') // char is not a semi colon separator, so must be data { dataItem[i] = c; // add char to character array } else // must be a semi colon separator - end of data item { dataItem[i] = '\0'; // terminate the dataItem c = myFile.peek(); // peek at the next character in the file if (c == 13) // is the next char a CR - end of line? { endOfLine = true; // YES! Set the end of line flag c = myFile.read(); // Dummy read to skip past CR c = myFile.read(); // Dummy read to skip past LF } // // If we get here, we have reached the end of the data item, and posiibly the end of the // line. So we have finished reading for now. Break out of the Read Loop and return to // the caller, with the current data item and the end of line flag set true, if // appropriate. // The file pointer is pointing at the first character of the next data item, or possibly, // we have reached the end of the file // break; } } }