What does this code do...?
- Reads ADAM-4017's channel 0 every 500 ms.
- Store the data as float in buffer.
- After repeating 60 * 2 = 120 times, a minute long Data Block has been created. Write the Data Block to SD.
Can we use the SD library...?
In order to use SD FAT32 we absolutely have to use SD Library. However, do we have enough memory?
Let's check the size of Arduino UNO's RAM size with using just following code.
void setup() {} void loop() {}
RAM: 2,048
9 | #include <SPI.h> |
71 | #include <SPI.h> <Ethernet.h> |
262 | #include <SPI.h> <Ethernet.h> <WebSocket.h> |
330 | #include <SPI.h> <Ethernet.h> <WebSocket.h> <SoftwareSerial.h> |
330 | #include <SPI.h> <Ethernet.h> <WebSocket.h> <SoftwareSerial.h> <Time.h> |
1,109 | #include <SPI.h> <Ethernet.h> <WebSocket.h> <SoftwareSerial.h> <Time.h> <SD.h> |
It may seem as if there might not be any problem, but let's check what really happens in actual code from Arduino code - 4017 Arduino Gateway posting. | |
1,126 | #include <SPI.h> <Ethernet.h> <WebSocket.h> <SoftwareSerial.h> <Time.h> excluding <SD.h> |
If we include setTime() to actually use <Time.h>, the memory becomes | 1,163 |
And then we include <SD.h>... | 1,769 |
Open a file... | 1,863 |
We only have 212 bytes left. Now we don't even have 512 bytes to create a Data Block. Even if we don't create Data Block, how are we going to manage Stack & Heap with this memory? What memory are we going to use for additional codes?
It might work sometimes... ( doesn't actually run on Arduino )
Therefore, we are going to give up on FAT32.
Now, are we going to use Arduino with bigger RAM?
http://arduino.cc/en/Main/ArduinoBoardMega2560
The biggest they have is 8K anyways.
If we have to improve on hardware continuously, why use Arduino?
Thus alternatively we are going to leave out FAT32 file system.
We are going to use 512 bytes block without using File. Why do we need File if we are only going to log?
In Arduino library copy the following 4 files into the current sketch folder.
\arduino-1.5.6-r2\libraries\SD\src\utility\ | Sd2Card.cpp |
Sd2Card.h | |
Sd2PinMap.h | |
SdInfo.h |
Now we are only going to use the following 3 functions.
- init()
- readBlock()
- writeBlock()
Current Time
Eventually we are going to use RTC; however, for this code we are using System Clock.
Library is available on the following site: http://www.pjrc.com/teensy/td_libs_Time.html
Download the Time.zip file unzip it and then Sketch > Import Library > Add Library.
Reference: http://playground.arduino.cc/Code/Time
You can also find it in Arduino Cookbook pg. 404.
Source Code
This is only part of the code. I will post the link to the full code at the bottom.
#include <SPI.h> #include <Ethernet.h> #include <WebSocket.h> #include <SoftwareSerial.h> #include "Sd2Card.h" #include <Time.h> byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0x25, 0xC4 }; byte ip[] = { 192, 168, 219, 16 }; WebSocket wsServer; // ------------------------------------------- SD Card #define SD_BASE_BLOCK 1024 // 1024 is not a special # #define LOG_BLOCK_SIZE 120 byte sdBuffer[512]; // SD card only writes in 512 bytes block. // Thus we have to have 512 bytes. byte sdStatus = 0; byte LogblockSize = 0; uint32_t currBlock; long lastPollTime = 0; Sd2Card sdCard; void SDbegin(void); void makeLogBlock(void); void update_currBlock(void); void setup() { Serial.begin(9600); Soft485.begin(9600); pinMode(DE485, OUTPUT); ADAM4017_config(); Ethernet.begin(mac, ip); wsServer.registerConnectCallback(&onConnect); wsServer.registerDataCallback(&onData); wsServer.registerDisconnectCallback(&onDisconnect); wsServer.begin(); delay(100); rx485ix = -1; // setTime(hours, minutes, seconds, days, months, years); // Time is not going to be right until we add RTC setTime(12, 0, 0, 11, 5, 14); SDbegin(); if(!sdStatus) printProgln(SD_FAIL_MSG); } void loop() { wsServer.listen(); if(sdStatus) { if((millis() - lastPollTime) >= 500) { lastPollTime = millis(); AdamPoll('0'); } } AdamReceiver(); } /* ----------------------------------------------------- D A T A B L O C K M A K I N G ----------------------------------------------------- */ // --------------------------------------------------- // SD Card Initialize // --------------------------------------------------- void SDbegin(void) { byte *pB; sdStatus = 0; pinMode(10, OUTPUT); // SS if(!(sdCard.init(SPI_HALF_SPEED, 4))) return; // SD Card Initialization Failed if(!(sdCard.readBlock(SD_BASE_BLOCK, sdBuffer))) return; // SD Card Read Failed pB = sdBuffer; // No Used Flag. New SD Card!! if(*((uint32_t*)pB) != 0xaa55a55a) { printProgln(NEW_SD_MSG); *((uint32_t*)pB) = 0xaa55a55a; currBlock = SD_BASE_BLOCK + 1; *((uint32_t*)(pB + 4)) = currBlock; if(!(sdCard.writeBlock(SD_BASE_BLOCK, sdBuffer))) return; // SD Card Write Failed } else currBlock = *((uint32_t*)(pB + 4)); // First Free Block LogBlockSize = 0; sdStatus++; printCurrBlock(); // debugging purpose } // --------------------------------------------------- // Create DATA Logging Block // --------------------------------------------------- void makeLogBlock(void) { byte i; byte *pB; float fdata; // --------- Reset buffer if it is the first time. if(!LogBlockSize) { pB = sdBuffer + 8; // insert Used Flag & // time-stamp when finished fdata = -99.999 * 10.; // -99.999 means that there is no // data. When we read them we do // /10 thus we have to do *10. i = 120; // fill it in 120 times while(i--) { *((float*)pB) = fdata; pB += 4; } i = 24; // 24 bytes padding while(i--) { *pB = 0x00; } } /* ADAM-4017 returns +dd.ddd It uses fixed 3 decimal points. To convert decimal string to float: double atof(const char* nptr) http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#ga689c9d3c4c04463aa31d329937789d06 But there is a problem. It only works with 2 decimal points. It rounds up third decimal point. Thus atof("12.345") will become 12.35. As a solution we do *10 then convert it to float. 0123456 0123456 +98.765 -> +987.65 */ // ----------------------- Insert Data into buffer // 0123456 // rx485Ln[] ">+dd.ddd" <cr> rx485Ln[3] = rx485Ln[4]; rx485Ln[4] = '.'; // *10 pB = sdBuffer + 8 + (LogBlockSize * 4); *((float*)pB) = atof(rx485Ln); LogBlockSize++; // -------------------------------- buffer is full if(LogBlockSize >= LOG_BLOCK_SIZE) { pB = sdBuffer; *((uint32_t*)pB) = 0xaa55a55a; // Used Flag pB += 4; *((time_t*)pB) = now(); // Time Stamp sdCard.writeBlock(currBlock, sdBuffer); currBlock++; LogBlockSize = 0; update_currBlock(); // Fix the next free block location } } void update_currBlock(void) { byte *pB; byte status = 0; if(sdCard.readBlock(SD_BASE_BLOCK, sdBuffer)) { pB = sdBuffer; if(*((uint32_t*)pB) == 0xaa55a55a) { *((uint32_t*)(pB + 4)) = currBlock; if(sdCard.writeBlock(SD_BASE_BLOCK, sdBuffer)) { status++; printTOD(); // display time stamp printCurrBlock(); // display next free block } } } if(!status) // something is going terribly wrong printProgln(BLOCK_UPDATE_FAIL_MSG); } void printTOD(void) { tmElements_t tm; breakTime(now(), tm); Serial.print(tm.Year - 30); // size 1970 printProg(DASH_MSG); Serial.print(tm.Month); printProg(DASH_MSG); Serial.print(tm.Day); printProg(SPACE_MSG); Serial.print(tm.Hour); printProg(COMMA_MSG); Serial.print(tm.Minute); printProg(COMMA_MSG); Serial.print(tm.Second); } void AdamReceiver(void) { char rxChr; if(Soft485.available()) { rxChr = Soft485.read(); if(rxChr == '>') rx485ix = 0; else if(rx485ix >= 0) { if(rxChr == 0x0d) { // 0123456 if(rx485ix == 7) { // >+dd.ddd<cr> wsServer.send(rx485Ln, 7); if(sdStatus) // when AD read value arrives from // ADAM-4017, log it makeLogBlock(); } rx485ix = -1; } else { rx485Ln[rx485ix++] = rxChr; if(rx485ix > 7) rx485ix = -1; } } } }
Full codes can be found in following links
No comments:
Post a Comment