Offline
Newbie
Karma: 0
Posts: 42
|
 |
« on: July 05, 2012, 08:39:03 pm » |
Hello everyone. I have written a program that uses a simple hardware interface with one button, which when pressed by the user, initiates a sequence of flashing LEDs over a short time and only after an elapsed period of pause. The user then cannot press the button to create another sequence within the specified pause time, which I have set to 10 seconds. There are also two separate LEDs to show whether the pause has elapsed, i.e. if the system is "armed." I created my LED sequence with a function in my program that uses delays and HIGH/LOW to create the LED flashes. I would instead like to read text files from an SD card, and have my program sequenctially pick from these files to generate the sequence of lights. Here's my program: #include <Bounce.h>
int MAST = 2; //mast int YAPORT = 3; //yardarm port side int YASTBD = 4; //yardarm starboard side int BOW = 5; //bow
int REDled = 8; int GREENled = 7; int ARMED = 0;
//Sequence int SEQUENCEbutton = 10; unsigned long SequenceLastFired;
//sequence reset time, during which sequence button will not work after pressed (should be eventually set to 15 minutes) int SequenceResetTime = 10000;
//button debouncing Bounce SEQUENCEbouncer = Bounce (SEQUENCEbutton,1);
void setup () { pinMode (MAST, OUTPUT); pinMode (BOW, OUTPUT); pinMode (YAPORT, OUTPUT); pinMode (YASTBD, OUTPUT); pinMode (SEQUENCEbutton, INPUT); digitalWrite (MAST, LOW); digitalWrite (BOW, LOW); digitalWrite (YAPORT, LOW); digitalWrite (YASTBD, LOW); Serial.begin (9600); }
void loop () { if (ARMED == 0) { digitalWrite (REDled, HIGH); digitalWrite (GREENled, LOW); } if (ARMED == 1) { digitalWrite (REDled, LOW); digitalWrite (GREENled, HIGH); } if ((millis() - SequenceLastFired) > SequenceResetTime) { ARMED = 1; } else { ARMED = 0; } //check for button presses SEQUENCEbouncer.update(); int SEQUENCEbuttonState = SEQUENCEbouncer.read(); if (SEQUENCEbuttonState == HIGH && (millis() - SequenceLastFired > SequenceResetTime)) { SEQUENCEfire(); } Serial.println (ARMED); }
//=======================SEQUENCE CODE=============================== void SEQUENCEfire () { ARMED = 0; digitalWrite (REDled, HIGH); digitalWrite (GREENled, LOW); //FOGHORN CODE HERE? delay (1000); digitalWrite (MAST, HIGH); delay (100); digitalWrite (MAST, LOW); delay (800); digitalWrite (BOW, HIGH); delay (100); digitalWrite (BOW, LOW); delay (300); digitalWrite (MAST, HIGH); digitalWrite (BOW, HIGH); delay (100); digitalWrite (MAST, LOW); digitalWrite (BOW, LOW); delay (450); digitalWrite (MAST, HIGH); delay (100); digitalWrite (MAST, LOW); delay (1200); digitalWrite (BOW, HIGH); delay (100); digitalWrite (BOW, LOW); delay (950); digitalWrite (MAST, HIGH); digitalWrite (BOW, HIGH); delay (100); digitalWrite (MAST, LOW); digitalWrite (BOW, LOW); delay (250); digitalWrite (MAST, HIGH); digitalWrite (BOW, HIGH); delay (100); digitalWrite (MAST, LOW); digitalWrite (BOW, LOW); delay (250); digitalWrite (MAST, HIGH); digitalWrite (BOW, HIGH); delay (100); digitalWrite (MAST, LOW); digitalWrite (BOW, LOW); delay (250); digitalWrite (MAST, HIGH); digitalWrite (BOW, HIGH); delay (100); digitalWrite (MAST, LOW); digitalWrite (BOW, LOW); delay (250); SequenceLastFired = millis();
}
The above program functions as desired. I will post the new program in a second post in this thread due to size limitations (hope this is OK).
|
|
|
|
« Last Edit: July 05, 2012, 08:41:59 pm by geek_tk »
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 42
|
 |
« Reply #1 on: July 05, 2012, 08:39:43 pm » |
I have given writing a new program to read the text files from the SD card a go in place of my delay-based sequencing, yet I am not achieving the desired results. One of the files on the SD card looks like this: 100,00000000 500,01000001 700,00000000 1000,10000010 1200,00011000 1400,01000001 1600,00011000 1800,00000000 2000,10000010 2200,00100100 2500,01000001 2700,00000000 3000,00000000 3500,01000001 3700,00000000 4000,10000010 4200,00011000 4500,01000001 4700,00011000 4900,10000010 5100,00100100 5300,01000001 5500,00000000 8000,00000000
And here is my new program. #include <Bounce.h> #include <SdFat.h> #include <SdFatUtil.h>
int outputPins[4] = {2,3,4,5}; long millisSinceStart = 0; int currentEventPtr = 0;
//arming indicator LEDs int REDled = 8; int GREENled = 7; int ARMED = 0;
struct Event { long eventTimestamp; byte address; } ;
//SD card setup Sd2Card card; SdVolume volume; SdFile root; SdFile file; int offset = 2; int fileCount = 7; // count + 1 char name[] = "03.txt"; uint8_t fileCounter = 3;
// store error strings in flash to save RAM #define error(s) error_P(PSTR(s)) void error_P(const char* str) { PgmPrint("error: "); if (card.errorCode()) { PgmPrint("SD error: "); } while(1); }
//Sequence int SEQUENCEbutton = 6; unsigned long SequenceLastFired; int sequenceSize; Event sequence[150];
//sequence reset time, during which sequence button will not work after pressed (should be eventually set to 15 minutes) int SequenceResetTime = 10000;
//button debouncing Bounce SEQUENCEbouncer = Bounce (SEQUENCEbutton,1);
void setup () { pinMode (SEQUENCEbutton, INPUT); for (int i = 0; i < 4; i++) { pinMode(outputPins[i] + offset, OUTPUT); } if (!card.init(SPI_HALF_SPEED)) error("card.init failed"); if (!volume.init(&card)) error("volume.init failed"); if (!root.openRoot(&volume)) error("openRoot failed"); for (int i = 0; i < 4; i++) { digitalWrite(i + offset, LOW); delay(10); } Serial.begin(9600); }
void loop () { if (ARMED == 0) { digitalWrite (REDled, HIGH); digitalWrite (GREENled, LOW); } if (ARMED == 1) { digitalWrite (REDled, LOW); digitalWrite (GREENled, HIGH); } if ((millis() - SequenceLastFired) > SequenceResetTime) { ARMED = 1; } else { ARMED = 0; } //check for button presses SEQUENCEbouncer.update(); int SEQUENCEbuttonState = SEQUENCEbouncer.read(); if (SEQUENCEbuttonState == HIGH && (millis() - SequenceLastFired > SequenceResetTime)) { SEQUENCEfire(); }
//=================HOW DOES THIS CONTROL THE SEQUENCE???================================= millisSinceStart = millis(); if(millisSinceStart > (sequence[currentEventPtr].eventTimestamp)) { checkBitOut(sequence[currentEventPtr].address); currentEventPtr++; if(currentEventPtr >= sequenceSize) { currentEventPtr = 0; file.close(); for (int i = 0; i < 4; i++) { digitalWrite(i+offset, LOW); delay(10); } } } // ============================================================================
Serial.println (ARMED); }
dir_t dir;
//=======================SEQUENCE CODE=============================== void SEQUENCEfire () { // ARMED = 0; // digitalWrite (REDled, HIGH); // digitalWrite (GREENled, LOW); //FOGHORN CODE HERE? for (int i = 0; i < 4; i++) { digitalWrite(i + offset, LOW); delay(10); } sequenceSize = 0; if (file.open(&root, name, O_READ)) { } else{ fileCounter = 0; name[0] = fileCounter/10 + '0'; name[1] = fileCounter%10 + '0'; if (!file.open(&root, name, O_READ)){ error("file.open failed"); }else{ } } fileCounter++; // sequential name[0] = fileCounter/10 + '0'; name[1] = fileCounter%10 + '0'; int n; char buf[32]; int sequenceIndex = 0; int index = 0; char timestampString[10] = {0,0,0,0,0,0,0,0,0,0}; boolean isByteString = false; byte currentByte = B00000000; // READ THROUGH EACH CHARACTER IN THE FILE while ((n = file.read(buf, sizeof(buf))) > 0) { // LOOP THROUGH BUFFER for (int i = 0; i < n; i++) { // DELIMITER HIT - ADD PREVIOUS TO ARRAY if(buf[i] == ',' || buf[i] == '\n'){ // TIMESTAMP if(isByteString == false) { index++; timestampString[index] = '\0'; long ts = atoi(timestampString); sequence[sequenceIndex].eventTimestamp = ts; char timestampString[10] = {0,0,0,0,0,0,0,0,0,0}; index = 7; } // BYTE if(isByteString == true) { sequence[sequenceIndex].address = currentByte; currentByte = B00000000; index = 0; } isByteString = !isByteString; // SWITCHES BACK AND FORTH BETWEEN TIMESTAMP AND BYTE } // END - DELIMITER CODE if(buf[i] == '\n'){ sequenceSize++; sequenceIndex++; }
// BUILD DATA TYPES if(buf[i] != ',' && buf[i] != '\n' && buf[i] != '\r' && buf[i] != ' '){ // TIMESTAMP if(isByteString == false) { timestampString[index] = char(buf[i]); index++; } // BYTE if(isByteString == true) { if(char(buf[i]) == '1'){ currentByte = currentByte | (1 << index); } if(char(buf[i]) == '0'){ currentByte = currentByte | (0 << index); } index--; } } // END - BUILD DATA TYPES } // END - LOOP THROUGH BUFFER } // END - LOOP THROUGH FILE
//Serial.println("Done Loading \n"); resetMillis(); currentEventPtr = 0; SequenceLastFired = millis(); }
void checkBitOut(byte myDataOut) { int i=0; int pinState; for (i=7; i>=0; i--) { if ( myDataOut & (1<<i) ) { pinState= 1; } else { pinState= 0; } digitalWrite(i + offset, pinState); } }
extern volatile unsigned long timer0_millis;
void resetMillis() { cli(); // disable interrupts timer0_millis = 0; sei(); // enable interrupts }
After uploading, my REDled glows red, and then after my 10s pause, the LEDs begin flashing in the sequence from the SD card for about 30 seconds, and then the program crashes and prints "error:" to the Serial port. So, my program is reading the text file on the card, but my arming system and button press are not working, and then it crashes. The program should load, the REDled should be red for 10 seconds, then turn off while the GREENled turns on, and then the program should only read the sequence file from the SD card when the user presses the button. Then, after the sequence finishes, the button should not be active for the specified pause time. Any help would be much appreciated, thanks.
|
|
|
|
« Last Edit: July 11, 2012, 07:26:32 pm by geek_tk »
|
Logged
|
|
|
|
|
Seattle, WA USA
Online
Brattain Member
Karma: 316
Posts: 35550
Seattle, WA USA
|
 |
« Reply #2 on: July 06, 2012, 10:37:48 pm » |
Event sequence[150]; Each element of the array is 5 bytes. 150 of them use 750 bytes. #include <SdFat.h> #include <SdFatUtil.h> Reading and writing to the SD card is done in 512 byte chunks. Those 512 bytes need to be stored somewhere. Just these two items are using well over half of your memory, if you have a 328 based Arduino. Constant strings take up memory, too. I suspect that you are running out of memory. if(isByteString == false) { index++; timestampString[index] = '\0'; long ts = atoi(timestampString); sequence[sequenceIndex].eventTimestamp = ts; char timestampString[10] = {0,0,0,0,0,0,0,0,0,0}; index = 7; }
A local variable that goes out of scope almost immediately is hardly useful use of memory. timestampString[index] = char(buf[i]); The char() macro is for people that don't understand casting. Casting a char to a char is hardly necessary. resetMillis(); Why? Calendars roll over at the end of the month. People manage. Clocks roll over at the end of the day. People manage.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 42
|
 |
« Reply #3 on: July 11, 2012, 07:23:55 pm » |
A local variable that goes out of scope almost immediately is hardly useful use of memory. What alternative would you suggest? I'm having trouble understanding some of what my program is doing, as I did not write all of it, it came partly from a friend and partly from online resources. I would be greatful if I could better understand it so i can address some of PaulS's concerns and hopefully write e better, more memory efficient program. For starters, my text files contain lines like this: 100,00000000 500,01000001 ...etc. The 100 is meant to be a millisecond value, the binary number is meant to refer to digital pins 0 through 7, which corresponds to which LEDs are lit when the function checkBitOut is passed the data from the text file. I am wondering why the function checkBitOut seems to work without use of PORTD declaration in the setup? void checkBitOut(byte myDataOut) { int i=0; int pinState; for (i=7; i>=0; i--) { if ( myDataOut & (1<<i) ) { pinState= 1; } else { pinState= 0; }
Also, in the above function, there is an "if" stament with a bitwise &. This if statement does not test a condition....which is confusing to me.
I added some serial for debugging (and yes I know that pins 0 and 1 won't work with Serial enabled...), and I find the values of "myDataOut" in BIN are 10000000, then 01000000, then 00100000, etc. The values of (1<<i) start at 10000000, then go to 01000000, etc. So this "if" statement is using the & operator to set the pin to HIGH if "myDataOut' and "(1<<i)" are equal as i goes from 7 to 0? Or am I not understanding what is actually going on here?
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Online
Brattain Member
Karma: 316
Posts: 35550
Seattle, WA USA
|
 |
« Reply #4 on: July 12, 2012, 05:03:00 am » |
What alternative would you suggest? That would depend on why you are defining the variable. char timestampString[10] = {0,0,0,0,0,0,0,0,0,0}; What, exactly, is this for? You declare and initialize the array, but never reference it.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 42
|
 |
« Reply #5 on: July 12, 2012, 01:57:18 pm » |
@PaulS - The variable (I think) is being defined so that when the program reads one text file, it timestamps when it was read so that it can move onto reading the next one. I did not write this bit of the code so I am not sure. Isn't the char timestampString array referenced below where it is declared in the part of my program that reads the text files from the SD card? I have attempted to modify my code to make use of a Finite State Machine approach to have it cycle through the states I want: Idle at power on (lasts for 3 seconds) Startup (lasts for 6 seconds) Armed Firing Pausing Armed So far, so good - the program loads, the states cycle through, and I am able to get the "firing" state to read the first text file from the card: 100,00111100 500,00100000 650,00010000 1000,00011000 1250,00011000 1400,00000000 1600,00011000 1800,00000000 2000,00000000 2200,00100000 2500,00000000 2700,00000000 3000,00000000 3500,00000000 3700,00000000 4000,00000000 4200,00011000 4500,00000000 4700,00011000 4900,00011100 5150,00100100 5300,00000000 5500,00000000 8000,00000000
And light up my LEDs. Then, after it runs through the sequence, the FSM transitions into "Pausing,"' then back to "armed" after the prescribed pause time; however, when it is sent back into "firing" with another button press, it does not read the next text file. This code was originally written to read the text files one after another after another with an random intermission in between - so I know my probelm at the moment is that I need to change the code in the function loadSequence() or add a counter variable in the firing state to match the text files, so that each time the FSM cycles into Firing, it reads the next text file on the card. I am not well versed at all in the SD library - I didn't write the original code - can anyone help?
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 42
|
 |
« Reply #6 on: July 12, 2012, 01:58:28 pm » |
Here's my program: #include <Bounce.h> #include <SdFat.h> #include <SdFatUtil.h> #include <FiniteStateMachine.h>
int REDled = 8; int GREENled = 9;
//Sequence int SEQUENCEbutton = 6; unsigned long SequenceLastFired;
//Variables for Timing unsigned long StartupEnteredTime; unsigned long ArmedEnteredTime; unsigned long FiringEnteredTime; unsigned long PausingEnteredTime;
unsigned long StartFiring;
State Idle = State (DSIdle); //idle state State Startup = State (DSStartupStart,DSStartupUpd,DSStartupExit); //startup state State Armed = State (DSArmedStart,DSArmedUpd,DSArmedExit); //Armed state State Firing = State (DSFiringStart,DSFiringUpd,DSFiringExit); //cannons firing state State Pausing = State (DSPausingStart,DSPausingUpd,DSPausingExit);//pause state FSM DSStateMachine = FSM(Idle); //initialize DS State Machine, start in state: Idle
//sequence reset time, during which sequence button will not work after pressed (should be eventually set to 15 minutes) int SequenceResetTime = 15000;
//button debouncing Bounce SEQUENCEbouncer = Bounce (SEQUENCEbutton,3);
//SD card setup Sd2Card card; SdVolume volume; SdFile root; SdFile file;
//Text files on card names and quantity //int offset = 2; int fileCount = 7; // count + 1
//struct for variables in text file struct Event { long eventTimestamp; byte address; } ;
//LED or Cannon Pins int outputPins[4] = {2,3,4,5}; int sequenceSize; long millisSinceStart = 0; int currentEventPtr = 0;
char name[] = "03.txt"; uint8_t fileCounter = 3;
Event sequence[150];
// store error strings in flash to save RAM #define error(s) error_P(PSTR(s)) void error_P(const char* str) { PgmPrint("mystery error: "); if (card.errorCode()) { PgmPrint("SD error: "); } while(1); }
//====================SETUP CODE TO RUN ONCE=====================
void setup () { pinMode (SEQUENCEbutton, INPUT); for (int i = 2; i < 6; i++) { pinMode(outputPins[i], OUTPUT); }
if (!card.init(SPI_HALF_SPEED)) error("card.init failed"); if (!volume.init(&card)) error("volume.init failed"); if (!root.openRoot(&volume)) error("openRoot failed"); for (int i = 2; i < 6; i++) { digitalWrite(i, LOW); delay(10); } Serial.begin(9600); //REMOVED loadSequence(); }
//===================MAIN PROGRAM LOOP CODE===================== void loop (){ if (millis() > 3000 && DSStateMachine.isInState(Idle)){ { DSStateMachine.transitionTo(Startup); } } DSStateMachine.update(); }
dir_t dir;
//State machines //Idle------------------------------/ void DSIdle (){ Serial.println("In State = Idle"); for (int i = 2; i < 6; i++) { digitalWrite(i, LOW); } digitalWrite(GREENled, LOW); digitalWrite(REDled, HIGH); }
//Startup----------------------------/ void DSStartupStart (){ Serial.println("In State = Startup"); for (int i = 2; i < 6; i++) { digitalWrite(i, LOW); } digitalWrite(GREENled, LOW); digitalWrite(REDled, HIGH); StartupEnteredTime = millis(); Serial.println(StartupEnteredTime); } void DSStartupUpd (){ if (millis() - StartupEnteredTime > 6000) { DSStateMachine.transitionTo(Armed); } }
void DSStartupExit () { }
//Armed------------------------------/ void DSArmedStart (){ for (int i = 2; i < 6; i++) { digitalWrite(i, LOW); } Serial.println("In State = Armed"); digitalWrite(GREENled, HIGH); digitalWrite(REDled, LOW); ArmedEnteredTime = millis();
} void DSArmedUpd (){ SEQUENCEbouncer.update(); int SEQUENCEbuttonState = SEQUENCEbouncer.read();
if (SEQUENCEbuttonState == HIGH) { DSStateMachine.transitionTo(Firing); } }
void DSArmedExit () { }
//FIRING---------------------------/ void DSFiringStart() { Serial.println("In State = Firing"); loadSequence(); }
void DSFiringUpd() { StartFiring = millis(); Serial.println(StartFiring); delay(1000); Serial.println("timestamp"); Serial.println(sequence[currentEventPtr].eventTimestamp); if(StartFiring > (sequence[currentEventPtr].eventTimestamp)) { loadBinaryCannonSequences(sequence[currentEventPtr].address); currentEventPtr++; if(currentEventPtr >= sequenceSize) { currentEventPtr = 0; file.close(); for (int i = 2; i < 6; i++) { digitalWrite(i, LOW); delay(10); SequenceLastFired = millis(); DSStateMachine.transitionTo(Pausing); } } // loadSequence(); } // THIS CODE NEEDS TO STOP THE SEQUENCE AFTER 20 SECONDS, OR POSSIBLY WHEN THE eventTimestamp == 9999999? // if (millis() - (sequence[currentEventPtr].eventTimestamp) > 20000) // { // DSStateMachine.transitionTo(Pausing); // } // SequenceLastFired = millis(); } void DSFiringExit() { } //PAUSING------------------------------/
void DSPausingStart(){ Serial.println("In State = Pausing"); digitalWrite(REDled, HIGH); digitalWrite(GREENled,LOW); PausingEnteredTime = millis(); }
void DSPausingUpd(){ if (millis() - PausingEnteredTime > SequenceResetTime){ DSStateMachine.immediateTransitionTo(Armed); } } void DSPausingExit() { } //=======================LOAD SEQUENCE CODE=============================== void loadSequence () { for (int i = 2; i < 6; i++) { digitalWrite(i, LOW); delay(10); } sequenceSize = 0; if (file.open(&root, name, O_READ)) { } else{ fileCounter = 0; name[0] = fileCounter/10 + '0'; name[1] = fileCounter%10 + '0'; if (!file.open(&root, name, O_READ)){ error("file.open failed"); }else{ } } fileCounter++; // sequential name[0] = fileCounter/10 + '0'; name[1] = fileCounter%10 + '0'; int n; char buf[32]; int sequenceIndex = 0; int index = 0; char timestampString[10] = {0,0,0,0,0,0,0,0,0,0}; boolean isByteString = false; byte currentByte = B00000000; // Serial.println(name); // READ THROUGH EACH CHARACTER IN THE FILE while ((n = file.read(buf, sizeof(buf))) > 0) { // LOOP THROUGH BUFFER for (int i = 0; i < n; i++) { // DELIMITER HIT - ADD PREVIOUS TO ARRAY if(buf[i] == ',' || buf[i] == '\n') { // TIMESTAMP if(isByteString == false) { index++; timestampString[index] = '\0'; long ts = atoi(timestampString); sequence[sequenceIndex].eventTimestamp = ts; char timestampString[10] = {0,0,0,0,0,0,0,0,0,0}; index = 7; } // BYTE if(isByteString == true) { sequence[sequenceIndex].address = currentByte; currentByte = B00000000; index = 0; } isByteString = !isByteString; // SWITCHES BACK AND FORTH BETWEEN TIMESTAMP AND BYTE } // END - DELIMITER CODE if(buf[i] == '\n'){ sequenceSize++; sequenceIndex++; }
// BUILD DATA TYPES if(buf[i] != ',' && buf[i] != '\n' && buf[i] != '\r' && buf[i] != ' '){ // TIMESTAMP if(isByteString == false) { timestampString[index] = char(buf[i]); index++; } // BYTE if(isByteString == true) { if(char(buf[i]) == '1'){ currentByte = currentByte | (1 << index); } if(char(buf[i]) == '0'){ currentByte = currentByte | (0 << index); } index--; } } // END - BUILD DATA TYPES } // END - LOOP THROUGH BUFFER } // END - LOOP THROUGH FILE
resetMillis(); currentEventPtr = 0; } //==================THIS CODE FIRES THE CANNONS BASED ON TEXT FILES===== void loadBinaryCannonSequences(byte BinaryPinSequence) { int i=0; int pinState; for (i=7; i>=0; i--) { if ( BinaryPinSequence & (1<<i) ) { pinState= 1; } else { pinState= 0; } digitalWrite(i, pinState); } }
//==========================this code resets the millis() timer
extern volatile unsigned long timer0_millis; void resetMillis() { cli(); // disable interrupts timer0_millis = 0; sei(); // enable interrupts }
|
|
|
|
|
Logged
|
|
|
|
|
Offline
God Member
Karma: 9
Posts: 836
|
 |
« Reply #7 on: July 12, 2012, 11:56:21 pm » |
It seems a bit of a waste of space to use 8 characters to store 8 bits of information.
If you want to use a "text" file to store your data, I suggest you learn hexadecimal numbers so your 0,0,0,1,0,0,1,1 gets stored as 03 (hex).
|
|
|
|
|
Logged
|
|
|
|
|
|