It's a long one, fair warning. Sorry in advance for those of you who read my code...
I recently adapted the chFIFODataLogger sketch for the Uno for a specific purpose of mine. I made some relatively minor changes and everything was working fine...until it wasn't. Last night, I changed the script so that, instead of logging the data from my sensors (using IR break beams) every few milliseconds, I would log data only whenever the state of my sensors changed. In other words, I wrote a little conditional to check if the state of any pin (I have 7 sensors) had changed from one sample to the next; if so, then write the data. This obviously saves a ton of space on my SD cards and makes the process a bit less cumbersome, but now I am having a weird problem. My code is pasted below and I'm also pasting the Serial monitor output. Right now, because my SD cards are in the mail, I am outputting the data to the Serial monitor instead of an SD card.
What is happening is this: I run the sketch, and everything works. For 77 records. The size of the buffer I am using is 50 records; it fills to 50, dumps the buffer, then fills to 27 - and starts over from the beginning. Here is what my Serial output looks like with some notation from me. For these samples I am literally wiggling the IR LED in front of a single sensor, tripping the beam on and off to test the code.
Serial monitor output:
5 (Minutes the code will run for)
Input mouse name (Prompt for user input)
Filename: E:\Dir\Dir\Mouse (Readout of user input)
Type Start to Start (Prompt for user input)
Delay, Sensor 1, Sensor 2, Sensor 3, Sensor 4, Sensor 5, Sensor 6, Sensor 7, Time, Error (Header)
2958948,0,0,0,1,0,0,0,2959,0 (Sensor readout - Delay between samples, value of Sensors 1-7, Timestamp in MS, and overrun error counter)
1 (Number of records in buffer - this is displayed only for debugging the current problem)
1136640,0,0,0,0,0,0,0,4096,0
2
5120,0,0,0,1,0,0,0,4101,0
3
2283520,0,0,0,0,0,0,0,6385,0
4
66560,0,0,0,1,0,0,0,6451,0
5
250880,0,0,0,0,0,0,0,6702,0
6
174080,0,0,0,1,0,0,0,6876,0
7
204800,0,0,0,0,0,0,0,7081,0
8
117760,0,0,0,1,0,0,0,7199,0
9
199680,0,0,0,0,0,0,0,7398,0
10
122884,0,0,0,1,0,0,0,7521,0
11
209920,0,0,0,0,0,0,0,7731,0
12
133116,0,0,0,1,0,0,0,7864,0
13
194560,0,0,0,0,0,0,0,8059,0
14
107520,0,0,0,1,0,0,0,8166,0
15
179204,0,0,0,0,0,0,0,8346,0
16
117756,0,0,0,1,0,0,0,8463,0
17
199684,0,0,0,0,0,0,0,8663,0
18
76796,0,0,0,1,0,0,0,8740,0
19
209920,0,0,0,0,0,0,0,8950,0
20
102400,0,0,0,1,0,0,0,9052,0
21
168960,0,0,0,0,0,0,0,9221,0
22
117760,0,0,0,1,0,0,0,9339,0
23
174084,0,0,0,0,0,0,0,9513,0
24
102396,0,0,0,1,0,0,0,9615,0
25
204800,0,0,0,0,0,0,0,9820,0
26
143360,0,0,0,1,0,0,0,9963,0
27
143364,0,0,0,0,0,0,0,10107,0
28
133116,0,0,0,1,0,0,0,10240,0
29
107520,0,0,0,0,0,0,0,10347,0
30
158720,0,0,0,1,0,0,0,10506,0
31
107520,0,0,0,0,0,0,0,10614,0
32
148480,0,0,0,1,0,0,0,10762,0
33
97280,0,0,0,0,0,0,0,10859,0
34
143364,0,0,0,1,0,0,0,11003,0
35
102396,0,0,0,0,0,0,0,11105,0
36
133124,0,0,0,1,0,0,0,11238,0
37
112636,0,0,0,0,0,0,0,11351,0
38
138240,0,0,0,1,0,0,0,11489,0
39
107520,0,0,0,0,0,0,0,11597,0
40
174084,0,0,0,1,0,0,0,11771,0
41
66560,0,0,0,0,0,0,0,11837,0
42
168960,0,0,0,1,0,0,0,12006,0
43
107520,0,0,0,0,0,0,0,12114,0
44
419836,0,0,0,1,0,0,0,12534,0
45
51200,0,0,0,0,0,0,0,12585,0
46
327680,0,0,0,1,0,0,0,12913,0
47
51200,0,0,0,0,0,0,0,12964,0
48
133120,0,0,0,1,0,0,0,13097,0
49 (Buffer is full)
76800,0,0,0,0,0,0,0,13174,0
0 (So start over)
230400,0,0,0,1,0,0,0,13404,0
1
128000,0,0,0,0,0,0,0,13532,0
2
163844,0,0,0,1,0,0,0,13696,0
3
87040,0,0,0,0,0,0,0,13783,0
4
102396,0,0,0,1,0,0,0,13885,0
5
66560,0,0,0,0,0,0,0,13952,0
6
839680,0,0,0,1,0,0,0,14792,0
7
97284,0,0,0,0,0,0,0,14889,0
8
143356,0,0,0,1,0,0,0,15032,0
9
578560,0,0,0,0,0,0,0,15611,0
10
15360,0,0,0,1,0,0,0,15626,0
11
199680,0,0,0,0,0,0,0,15826,0
12
51204,0,0,0,1,0,0,0,15877,0
13
204796,0,0,0,0,0,0,0,16082,0
14
61440,0,0,0,1,0,0,0,16143,0
15
363524,0,0,0,0,0,0,0,16507,0
16
61436,0,0,0,1,0,0,0,16568,0
17
122880,0,0,0,0,0,0,0,16691,0
18
40960,0,0,0,1,0,0,0,16732,0
19
143364,0,0,0,0,0,0,0,16875,0
20
20476,0,0,0,1,0,0,0,16896,0
21
143360,0,0,0,0,0,0,0,17039,0
22
40960,0,0,0,1,0,0,0,17080,0
23
133120,0,0,0,0,0,0,0,17213,0
24
66560,0,0,0,1,0,0,0,17280,0
25
117760,0,0,0,0,0,0,0,17398,0
26
97280,0,0,0,1,0,0,0,17495,0
27 (Buffer is NOT full)
102400,0,0,0, (So start over?)
I'm pretty perplexed, but I know it must be something simple I am missing...I have tried two different boards, and that is not the problem. I have tried increasing and decreasing the buffer size, baud rate, etc. and that has not solved it. Any input? I am quite new to ChibiOS and coding for Arduino's, so any input would be great...
Code:
Code: Select all
// Data logger based on a FIFO to decouple SD write latency from data
// acquisition timing.
//
// The FIFO uses two semaphores to synchronize between tasks.
#include <ChibiOS_AVR.h>
#include <SdFat.h>
//
// interval between points in units of 1024 usec
const uint16_t intervalTicks = 5;
String filename = "";
int startTime;
boolean timeIndex = true;
//5 minutes = 300000
//30 minutes = 1800000
#define runTime 300000 //Run time in milliseconds
//------------------------------------------------------------------------------
// SD file definitions
//const uint8_t sdChipSelect = SS;
//SdFat sd;
//SdFile file;
//------------------------------------------------------------------------------
// Fifo definitions
// size of fifo in records
const size_t FIFO_SIZE = 50;
// count of data records in fifo
SEMAPHORE_DECL(fifoData, 0);
// count of free buffers in fifo
SEMAPHORE_DECL(fifoSpace, FIFO_SIZE);
// data type for fifo item
struct FifoItem_t
{
uint32_t usec;
int IR1_value;
int IR2_value;
int IR3_value;
int IR4_value;
int IR5_value;
int IR6_value;
int IR7_value;
boolean stateChange;
int error;
};
#define IR1 4
#define IR2 5
#define IR3 6
#define IR4 7
#define IR5 8
#define IR6 9
#define IR7 10
// array of data items
FifoItem_t fifoArray[FIFO_SIZE];
//------------------------------------------------------------------------------
// Declare a stack with 32 bytes beyond task switch and interrupt needs.
static WORKING_AREA(waThread1, 32);
static msg_t Thread1(void *arg) {
// index of record to be filled
int previousState[7] = {0, 0, 0, 0, 0, 0, 0};
size_t fifoHead = 0;
//Initialize error counter
int error = 0;
while (1) {
chThdSleep(intervalTicks);
// get a buffer
// NOTE: If I uncomment these lines, the code stays in this conditional indefinitely
// if (chSemWaitTimeout(&fifoSpace, TIME_IMMEDIATE) != RDY_OK) {
// // fifo full indicate missed point
// error++;
// continue;
// }
FifoItem_t* p = &fifoArray[fifoHead];
p->usec = micros();
p->stateChange = false; // Default is no change occurred from previous sample
// Read sensors
p->IR1_value = digitalRead(IR1);
p->IR2_value = digitalRead(IR2);
p->IR3_value = digitalRead(IR3);
p->IR4_value = digitalRead(IR4);
p->IR5_value = digitalRead(IR5);
p->IR6_value = digitalRead(IR6);
p->IR7_value = digitalRead(IR7);
// If any sensor's value has changed
if (p->IR1_value != previousState[0] |
p->IR2_value != previousState[1] |
p->IR3_value != previousState[2] |
p->IR4_value != previousState[3] |
p->IR5_value != previousState[4] |
p->IR6_value != previousState[5] |
p->IR7_value != previousState[6])
{
p->stateChange = true; //Then there was a state change
}
//Store current sensor values as new previous state values
previousState[0] = p->IR1_value;
previousState[1] = p->IR2_value;
previousState[2] = p->IR3_value;
previousState[3] = p->IR4_value;
previousState[4] = p->IR5_value;
previousState[5] = p->IR6_value;
previousState[6] = p->IR7_value;
//If error, then count it
p->error = error;
error = 0;
// If there was a state change
if (p->stateChange == true)
{
// Then signal new data
chSemSignal(&fifoData);
// and advance FIFO index
fifoHead = fifoHead < (FIFO_SIZE - 1) ? fifoHead + 1 : 0;
}
}
return 0;
}
//------------------------------------------------------------------------------
void setup() {
Serial.begin(115200); //115200 baud gives the fewer overrun errors at 2ms resolution; 57600 is ok for 5ms resolution
//Define pins and set internal pull-down resistors - DO NOT CHANGE FROM HIGH STATE!!
pinMode(IR1, INPUT);
digitalWrite(IR1, HIGH);
pinMode(IR2, INPUT);
digitalWrite(IR2, HIGH);
pinMode(IR3, INPUT);
digitalWrite(IR3, HIGH);
pinMode(IR4, INPUT);
digitalWrite(IR4, HIGH);
pinMode(IR5, INPUT);
digitalWrite(IR5, HIGH);
pinMode(IR6, INPUT);
digitalWrite(IR6, HIGH);
pinMode(IR7, INPUT);
digitalWrite(IR7, HIGH);
Serial.println(runTime/60000); //Show runTime in minutes
String inData = ""; // String to hold input data.
String inStart = ""; //String to hold input data
// wait for USB Serial
while (!Serial) // While serial is not ready.
{
; // Waiting for serial to be ready.
}
Serial.println (F("Input mouse name")); //Prompt user for mouse ID
int breakIndex = 0; //Initialize index for line break
// Read Mouse name from serial
while (breakIndex == 0) //While the user has not entered /n
{
while (Serial.available () > 0) // (If)While there is data comming from the serial port...
{
int inChar = Serial.read (); // Read data from the serial port into inChar.
inData += (char) inChar; // Concatenates the contents of inChar as char to the end of inData.
delay (10); // Waits for 10 milliseconds between each byte read, so that we don't double read the incoming byte and allow the hardware some time to flush it.
if ((char) inChar == '\n') // It the current read character is a new line...
{
Serial.print ("Filename: "); // Outputs "Echo: " to the Serial port, without a new line.
filename = "E:\\Dir\\Dir\\" + inData; //Create filename (directory changed to Dir to mask my name!)
Serial.println (filename); // Outputs the filename
breakIndex = 1; //Exit while loop, we're done here
}
}
}
int inChar = -1;
// I am testing this in the Serial monitor while waiting for my SD shield/cards to arrive in the mail...
// open file
// if (!sd.begin(sdChipSelect)
// || !file.open(inData, O_CREAT | O_WRITE | O_TRUNC)) {
// Serial.println(F("SD problem"));
// sd.errorHalt();
// }
Serial.println ("Type Start to Start"); //Prompt user for Start command
breakIndex = 0; //Reset break index
while (breakIndex == 0) //While the user has not entered the Start command
{
while (Serial.available () > 0) //While there is data in the Serial monitor
{
int inChar = Serial.read(); //Read the data
inStart += (char) inChar; //Concatenate the array
delay(10); //Wait for flush
if (inStart == "Start") //If the user entered Start, then start the program
{
Serial.print("Delay");
Serial.print(", ");
Serial.print("Sensor 1");
Serial.print(", ");
Serial.print("Sensor 2");
Serial.print(", ");
Serial.print("Sensor 3");
Serial.print(", ");
Serial.print("Sensor 4");
Serial.print(", ");
Serial.print("Sensor 5");
Serial.print(", ");
Serial.print("Sensor 6");
Serial.print(", ");
Serial.print("Sensor 7");
Serial.print(", ");
Serial.print("Time");
Serial.print(", ");
Serial.println("Error");
breakIndex = 1;
}
}
}
// start kernel
// Now we have some critical info & the SD card is initialized, let's get started
chBegin(mainThread);
while (1);
}
//------------------------------------------------------------------------------
// main thread runs at NORMALPRIO
void mainThread() {
// FIFO index for record to be written
size_t fifoTail = 0;
// time in micros of last point
uint32_t last = 0; // Last time of sample
uint32_t temp = 0; // Gotta love variables named "temp"
bool overrunError = false; // Initialize overrun state as false
// start producer thread
chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO + 1, Thread1, NULL);
// start SD write loop
if (timeIndex == true) //Capture immediate time to zero out run time clock
{
startTime = millis(); //Taking time here accounts for overhead in previous loops; this is out time 0
// runTime = (millis() - startTime) + runTime; //Adjust runTime for startTime - if we want 300000 milliseconds of runTime, then the appropriate runTime is 300000 + startTime
timeIndex = false; //Don't enter this loop again
temp = micros(); //More temp variables! This is used for the very first sampling time readout - just to make importing data easier later.
}
while ((millis() - startTime) < runTime) { //While we have not been running for the full run duration
// wait for next data point
chSemWait(&fifoData);
FifoItem_t* p = &fifoArray[fifoTail];
if (fifoTail >= FIFO_SIZE) fifoTail = 0;
// print interval between points
if (last) {
Serial.print(p->usec - last);
} else {
Serial.print(micros() - temp);
}
last = p->usec;
// Print sensor values
Serial.write(',');
Serial.print(p->IR1_value);
Serial.write(',');
Serial.print(p->IR2_value);
Serial.write(',');
Serial.print(p->IR3_value);
Serial.write(',');
Serial.print(p->IR4_value);
Serial.write(',');
Serial.print(p->IR5_value);
Serial.write(',');
Serial.print(p->IR6_value);
Serial.write(',');
Serial.print(p->IR7_value);
Serial.write(',');
Serial.print((millis() - startTime)); //Time in milliseconds
Serial.write(',');
Serial.println(p->error); //Print any errors
// remember error
if (p->error) overrunError = true;
// release record
chSemSignal(&fifoSpace);
// advance FIFO index
fifoTail = fifoTail < (FIFO_SIZE - 1) ? fifoTail + 1 : 0;
Serial.println(fifoTail);
}
// close file, print stats and stop
// file.close();
Serial.println(F("Done"));
Serial.print(F("Thread1 unused stack: "));
Serial.println(chUnusedStack(waThread1, sizeof(waThread1)));
Serial.print(F("Heap/Main unused: "));
Serial.println(chUnusedHeapMain());
if (overrunError) {
Serial.println();
Serial.println(F("** overrun errors **"));
while (1);
}
}
//------------------------------------------------------------------------------
void loop() {
// not used
}