I am trying to comprehend Bluetooth serial1 limitations. Academic discussion.
In4b post your code - I fear it will only confuse the issue.
So I need to record two analog inputs every 0.001 seconds - working beautify. So I do have a timed interrupt working during the serial1 use.
While doing the above I tried a loop with this code at 38400 baud (preferred default value)
Void interruptRoutine()
{ // this happens every 0.001 second
}
Void loop()
{
// some code to limit doing this for 4000 cycles.
Serial1.println("123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100");
// I need to keep reducing the number between 1 and 100 until it can successfully send 4000 strings to determine my limitations. (if I was not at work)
// some code to close
}
and for about 15 scans it worked fine then it just locked up or sent random characters.
So at some point I hit a limit. (I need to get to work but I will try fewer chars in about 12 hours)
Just what are the limits of Bluetooth and a serial1.print?
Do you mean how much can you print in an interrupt service routine? That's easy. Until the buffer gets full. Once that happens, print() blocks until there is room. But, since room is made only when interrupts happen, which is not the case in an ISR, room will never be made, so print() will never return.
There is a method to determine how much room there is in the buffer, but, it is best to simply not print() in an ISR.
So I am testing seat head restraint so a mate and I can put carbon fiber race car seats in our daily drivers legally.
A concept here -
This code will go in the arduino on the sphere and it will all crash into the seat.
Accelerometers will monitor the impact.
I have an Arduino Mega encoder on the pendulum's pivot and I can measure speed and transmit the data I need so that is good to go.
This code is on an Arduino Mega R3 with a servo, 2 accelerators (only reading one right now.) and a bt module.
#include <TimerOne.h> //http://playground.arduino.cc/Code/Timer1
#include <Servo.h> // should be standard library function
// Servo configuratoin group
Servo myservo; // create servo object to control a servo to release the test sphere
int pos = 0; // variable to store the servo position
const int ledPin = 13; // the number of the LED pin
int serial_length;
int i;
int thisChar;
// Interrupt configuratoin group -
// importaintly Volitile variables can be moved from one subroutine to another.
volatile int arrayOverlapCount = 0; // use volatile for shared variables
volatile int countSerialSend = 0;
volatile int anaStoreA[1805];
volatile int anaStoreB[1805];
volatile int interruptCounts = 0;
volatile int interruptLimit = 4000;
volatile bool scanPermissive = LOW;
// Analog input configuration
volatile int sensorPin = A0; // select the input pin for the potentiometer
volatile int AccelA0 = 0; // variable to store the value coming from the sensor
// managing the release and data transfer
int sendCount = 0;
int catchUp = 0;
String concatString;
String concatStringA;
String concatStringB;
String concatStringC;
String concatStringD;
String concatStringE;
String theComma;
String CrLf;
int k = 0;
int openedPosition = 120;
int closedPosition = 60;
unsigned long valueA; // holds a copy of the arrayOverlapCount
unsigned long valueB; // holds a copy of the arrayOverlapCount
int sendCountbuffer;
/*
* Set up the Arduino
*/
void setup(void)
{
// Set up the servo control
myservo.attach(9); // attaches the servo on pin 9 to the servo object
pinMode(ledPin, OUTPUT);
//Set up the timer for the interrupt
Timer1.initialize(1000); // set in microseconds
Timer1.attachInterrupt(grabAnalogX2); // grabAnal to run every 0.001 seconds when value of timer1 interrupt = 1000
// Set up the comms for Bluetooth connection
Serial.begin(57600); //300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, *IDEAL FOR THE AMOUNT OF DATA 57600**, or 115200
Serial1.begin(38400);
myservo.write(closedPosition); // tell servo to go to position in variable 'pos'
delay(500);
Serial.println("ok to run by RS232 or USB");
Serial1.println("ok to run via bluetooth");
CrLf = "\r\n";
theComma = ",";
}
/*
* Timed interrupt routine
* The interrupt will trigger a read of the analogs, and keep
* track of how many times it has been called.
*/
void grabAnalogX2 (void) // routine called by the timed interrupt to read analogues up to the limit of reads set by interruptLimit
{
if (scanPermissive == HIGH)
{ //Scan the analog inputs for acceleromter data
if (interruptCounts < interruptLimit)
{
interruptCounts = interruptCounts + 1; // track the number of times the interrupt has been called
if (arrayOverlapCount < 1800 ) // limit the array size to 1800 x 2 = 3600
{
arrayOverlapCount = arrayOverlapCount + 1; // increase when data stored
}
else
{
arrayOverlapCount = 0; // go back to the first array pointer to capture more data in the limited memory.
}
AccelA0 = analogRead(sensorPin);
anaStoreA[arrayOverlapCount] = AccelA0;
anaStoreB[arrayOverlapCount] = interruptCounts; // the interrupt cycle count
}//End scan the analog inputs for acceleromter data
else // limit = 4000 - STOP scanning
{
scanPermissive = LOW;
}
} //end Scan the analog inputs for acceleromter data because interruptLimit reached
} //end interrupt routine
/*
* Main loop.
*
* Check for a serial command
* read the accelerometers
* overlap reading of the accelerometers by printing the data
* Manage the overlap
* park the servo when complete
*/
void loop(void)
{ //Start void loop
/*
* Manage the Release of the Sphere
*/
if (scanPermissive == LOW) // IF recording not taking place then read the serial port
{
if (Serial1.available() > 0)
{
serial_length = Serial1.available();
for (i = 0; i <= serial_length; i++)
{
thisChar = Serial1.read();
if (thisChar == 100) {
scanPermissive = HIGH;
digitalWrite(ledPin, HIGH); // turn LED on:
myservo.write(openedPosition); // tell servo to go to open position
}// end finding "d" byte 100
} // end for loop empting the serial buffer to stop repeat triggers
} // end looking for serial from PC
} // scanning accell now, do not read serial port
/*
* compile and send data to the serial port.
*
* data loss occurs if string gets too long. Close to max now.
*/
sendCountbuffer = sendCount + 20;
if (interruptCounts > sendCountbuffer)
{
sendCount = sendCount + 1; // Track the total number of serial prints.
if (countSerialSend < 1800 )
{
countSerialSend = countSerialSend + 1; // increment the array pointer
}
else
{
countSerialSend = 0; // go back to the first array pointer.
k = 0;
}
valueA = anaStoreA[countSerialSend]; // the interrupt cycle count
valueB = anaStoreB[countSerialSend]; // the interrupt cycle count
k = k + 1;
if (k == 1)
// { concatStringA = valueA + theComma + valueB + CrLf;
{ concatStringA = valueB + CrLf;
}
if (k == 2)
//{ concatStringB = valueA + theComma + valueB + CrLf;
{ concatStringB = valueB + CrLf;
}
if (k == 3)
// { concatStringC = valueA + theComma + valueB + CrLf;
{ concatStringC = valueB + CrLf;
}
if (k == 4)
// { concatStringD = valueA + theComma + valueB + CrLf;
{ concatStringD = valueB + CrLf;
}
if (k == 5)
// { concatStringE = valueA + theComma + valueB + CrLf;
{ concatStringE = valueB + CrLf;
concatString = concatStringA + concatStringB + concatStringC + concatStringD + concatStringE;
//Serial.print(concatString); // data loss occurs if string gets too long. Close to max now.
//Serial1.print(concatString); // data loss occurs if string gets too long. Close to max now.
Serial1.println("123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100");
k = 0;
concatStringA = "";
concatStringB = "";
concatStringC = "";
concatStringD = "";
concatStringE = "";
} // end (k == 5)
} // end test for data before sending data
/*
* Scanning complete so close the servo
*/
if (scanPermissive == LOW) // scanning takes about 4 seconds so don't close the servo until scanning complete
{ // not reading the accelerometers
if (sendCountbuffer >= interruptLimit) // limit = 4000 - STOP printing
{ // park servo, reset the counters
digitalWrite(ledPin, LOW); // turn LED off:
myservo.write(closedPosition); // tell servo to go to position in variable 'pos'
interruptCounts = 0; // Initiate the readings again
arrayOverlapCount = 0;
sendCount = 0;
countSerialSend = 0;
k = 0;
sendCountbuffer = 0;
concatStringA = "";
concatStringB = "";
concatStringC = "";
concatStringD = "";
concatStringE = "";
Serial1.flush();
}
} // completed parking servo, resetting the counters
}//end void loop
On the serial monitor the keyboard character "d" will release the sphere by having a servo pull a pin.
The data will stream back to me once the scanning starts.
I only have enough board memory for 2 x 1800 samples
So I right over the first samples while I keep sending. I can do roughly 3600 to 4000 before it overwrites unsent data on the USB (but I need BT)
It worked fine on the USB and at 57600 baud. My Bluetooth got a power glitch and defaulted to 38400 so I figure keep to its limits.
Data stream bulk has been an issue, I am just trying to quantify it.
Lastly I had hoped to keep everything as a string (and copy paste into excel) so anyone can read it when it arrives on the PC.
Can you provide an English (not code) description of how the system is intended to work.
It looks like it releases a pendulum and then starts taking analog readings at 1msec intervals. But it is not clear what it is trying to store or what it is trying to do with the stored values, or when. And I don't understand where (in terms of time) the ISR fits in with the rest of the stuff.
You are using Strings (capital S) which don't work well in the limited memory of an Arduino. Use strings (small s) which are char arrays terminated with 0.
My immediate sense is that your ISR code is complicated. And if you only want to read at 1msec intervals I'm not sure any ISR is needed - why not just time it with micros() ?
Thanks for the interest in my project. Your posts have been very helpful to get me this far.
How did I get here?
The 0.001 second samples comes from -
Vehicle Standard (Australian Design Rule 3/03 – Seats and Seat Anchorages) 2006
1.3.3. Time recording:
the instrumentation shall enable the action to be recorded throughout its duration and readings to be made to within one one-thousandth of a second;
the beginning of the impact at the moment of first contact between the headform and the item being tested shall be detected on the recordings used for analysing the test.
I have a local mentor who suggested the timed interrupt would be the easiest way to ensure I accurately sample every 1/1000 second so I willingly complied to his suggestion.
For the most part the code works using USB but as it will be swinging into a seat from a 4 plus meter pivot, Bluetooth made a little more sense.
The Strings / strings - - - I may not be the end user so I was hoping to use strings so anyone could copy paste into Excel from the monitor.
It looks like it releases a pendulum and then starts taking analog readings at 1msec intervals. But it is not clear what it is trying to store or what it is trying to do with the stored values, or when. And I don't understand where (in terms of time) the ISR fits in with the rest of the stuff.
Ultimately I want to store A0 and A1.
These are 2 x Accelerometer ADXL377 Range: ±200g
As they meet the design specification.
I had it in my mind to
Start recording the A0 A1 signals every 1/1000 sec into the biggest array the memory could manage. 2 x 1800 locations.
Then while I gather the A0 A1 data start streaming the saved values to the PC.
I worked out I could do this about 3000 times before the 1800 array over wrote itself.
Serial.println A0 , A1
I then concatenated the string to capture 5 lots of A0 A1 and managed 4000 samples before overwriting the 1800 array.
Serial.print A0[1] , A1[1] CrLf A0[2] , A1[2] CrLf A0[3] , A1[3] CrLf A0[4] , A1[4] CrLf A0[5] , A1[5] CrLf
I have thought about not overwriting the array
release the sphere (via servo)
Wait (delay) for the sphere to get close to striking the seat and then capture the 1800 array values and then methodically send them to the PC. No overwriting.
Serial.println A0[0 - 1800] , A1[0 - 1800]
I fear I might stuff up the timing and miss the impact.
But it will work.
So I am not sure how to proceed. Struggle with the limitations of Bluetooth and write some smarter code or just do it with a window of 1.8 seconds.
As for the interrupt routine, just doing what was suggested to ensure I could prove a 1/1000 sample rate.
Dave_vo:
..R
1.3.3. Time recording:
the instrumentation shall enable the action to be recorded throughout its duration and readings to be made to within one one-thousandth of a second;
the beginning of the impact at the moment of first contact between the headform and the item being tested shall be detected on the recordings used for analysing the test.
This text does NOT say that recordings must be made at the rate of 1000 per sec. It only says they must be made "throughout" and that the time of each reading must be "to within one one-thousandth of a second". I have no idea what they actually expect you to do - it is badly worded for what it is. In fact it sounds like they want you to record times, rather than anything else - but times of what?. I suggest you ask the relevant authorities.
The Strings / strings - - - I may not be the end user so I was hoping to use strings so anyone could copy paste into Excel from the monitor.
The use of Strings or strings is completely irrelevant as far as Excel is concerned. You can just as easily send string data to Excel. And you will be better able to do so if your program does not crash because the Strings have screwed up your memory. Keep in mind that you want to keep as much memory as possible free for your data.
I need to think some more about the rest of your comments.
What are A0 and A1 measuring ?
The code in Reply #4 seems only to use A0.
For an experiment of this type I would be tempted to record all the data and when that is finished send it to the PC.
Would it be sufficient to record the ADC values as 8-bit values - that would double the number of samples you can save.
Yes I will send after impact and completion of data collection.
Embarrassed to ask this question.
How do I convert the analog value to byte?
I have it in my mind to send value A0 "," A1 CrLf
This is not the way to do it but excel likes it without anyone having to think about formatting the values after a copy paste from the monitor.
Just realised I will have to rescale the info into -200g to +200g anyway so it will require an excel macro of sorts.
So the real dumb question is - what is the best method to send 2 byte values to excel so Excel knows that they are 2 separate values and not a continuous stream from one source?
I was using the comma and CrLf to keep them apart.
Dave_vo:
How do I convert the analog value to byte?
I would need to do some experiments myself.
The long way would be to shift the INT 2 places right (divide by 4) and then copy the value to a byte variable. I "THINK" if you copy an INT to a byte it lops off the high bits - hence the need to divide by 4 so you only lose the low bits. A few lines of code would be as quick as this paragraph.
what is the best method to send 2 byte values to excel so Excel knows that they are 2 separate values
Comma Separated Values (CSV) is a very well established system and easy to implement on the Arduino.