I am building a ballistic chronograph. It basically measures the speed of an object (in this case a bullet) by measuring the time the object takes from when it passes through one IR emitter/sensor pair until it passes through another, and knowing how far apart the sensors are. I will eventually be displaying results on a touchscreen on one Arduino (an Uno), and I don't think a single Arduino could easily handle both screen operation and super-accurate timing. Therefore, I am using a second Arduino Uno for the timing and velocity calculation, which receives data about the distance between the sensors (in case it is changed by the user) from the display Arduino, which can in turn receive it from the touchscreen. It then sends back the velocity. I am using I2C for communication between the Arduinos, but there's obviously something I'm missing, because I'm getting strange results.
Here's the code for the display Arduino:
/* Ballistic chronograph display unit. Takes data over I2C from sensor unit and displays it over serial monitor (for now).
*
* Written November 6 2015 -
* By Andre W.
*/
#include <Wire.h>
int distance_thous = 3250; //distance between the two IR sensors - this will be settable by the user, so it needs to come from the display Arduino
byte b1 = 0; //less significant byte of distance int
byte b2 = 0; //more significant byte of distance int
int velocity = 0; //holds returned value from sensor Arduino
void setup() {
Wire.begin(7);
Wire.onRequest(data_out);
Wire.onReceive(data_in);
Serial.begin(9600);
}
void loop() {
delay(50);
}
void data_out() {
b1 = (distance_thous - (distance_thous % 256)) / 256; //takes more significant byte from distance int and puts it in b1
b2 = distance_thous % 256; //takes less significant byte from distance int and puts it in b2
Wire.write(b1);
Wire.write(b2);
}
void data_in(int incoming) { //receives and displays velocity from sensor unit
velocity = Wire.read();
Serial.println(velocity);
}
b1 ends up being 12, b2 is 178 with my example distance value of 3.25 inches. I'm pretty sure that this is being calculated correctly by my formula. Here's the sensor Arduino code:
/* Ballistic chronograph sensor unit. Takes input from two infrared sensors sets, calculates velocity of the object in inches/second, and sends it over I2C to the display Arduino.
*
* Written November 6 2015 -
* By Andre W.
*/
#include <Wire.h>
unsigned long time_microseconds = 0;
unsigned long distance_mils = 0; //variable that will store the distance between the two sensor sets in millionths of an inch
int velocity = 0;
int sensor_one = 8;
int sensor_two = 9;
unsigned long b1 = 0; //more significant byte of distance in thousandths of an inch
unsigned long b2 = 0; //less significant byte of distance in thousandths of an inch
void setup() {
Wire.begin();
pinMode(sensor_one, INPUT);
pinMode(sensor_two, INPUT);
}
void loop() {
delay (50);
Wire.requestFrom(7, 2); //requests and stores distance bytes
if (Wire.available()) {
b1 = Wire.read();
b2 = Wire.read();
}
distance_mils = ((b1*256)+b2)*1000; //reassembles distance int and converts it from thousandths of an inch to millionths.
Watch(); //call function to check first sensor
}
void Watch() {
while(digitalRead(sensor_one) == LOW) { //waits while sensor is pulling pin low; when IR beam is broken, sensor will "open" and pullup resistor will pull pin high
}
Count(); //when sensor goes high, start counting
}
void Count() {
time_microseconds = micros();
while(digitalRead(sensor_two) == LOW) {
}
time_microseconds = micros() - time_microseconds;
Send();
}
void Send() {
velocity = distance_mils/time_microseconds; //computes velocity
Wire.beginTransmission(7); //sends velocity to display arduino
Wire.write(velocity);
Wire.endTransmission();
time_microseconds = 0; //resets values and returns to void loop() to repeat process
velocity = 0;
delay(100);
}
When I did a Serial.read check on the sensor unit to see if b1 and b2 were coming through correctly, I got b1 = 178 and b2 = 255, rather than 12 and 178. This throws everything off, of course.
There must be something I'm missing about how to use the Wire library.
Sorry if this is really obvious, but I can't figure it out.
Thanks,
Andre
electromagnet:
and I don't think a single Arduino could easily handle both screen operation and super-accurate timing.
Think again... Just give priority to the speed detection. That's not a problem for you, you have bigger problems...
Because, a bullet is pretty fast! A quick google reveils a minimum speed of 180m/s which is 590 ft/s or around 7000inch/s! In you code you talk about a distance of just 3,25 inch. So it just takes less then 500us for a bullet to pass both sensors. Even if i ignore if the sensors are fast enough (because if the bullet is 0.5 inch it takes just +-70us to pass a sensor. That is damn fast...) the 500us is fast. Not impossible for the arduino but very fast! And if you just poll a pin with digitalRead() you piss away time and make it already pretty inaccurate. And then you have micros() which is not that accurate either. And then you have the inaccuracy of the Arduino, especially when it has an RC resonator...
But thinking about I picked a very low bullet speed (I find like 400m/s for a 9mm) things become pretty impossible.
So, and/or:
be sure the sensors can even spot a bullet
Use the pin interrupts (pin 2 and 3) instead of polling
Use a timer instead of micros()
Increase the distance quite a bit
Use a faster micro controller
And for the I2C problem, try using a unsigned int and just do
unsigned int distance_thous = 3250;
Wire.write(distance_thous >> 8); //MSB aka most significant byte
Wire.write(distance_thous); //Should just send the LSB aka least significant byte
Only one Wire.write may be used in a OnRequest handler. You have to use a single Wire.write that transmits two bytes. Either with a byte buffer, or a pointer to an integer or so.
Is the distance 3250 inch ? That is 80 meters. The I2C is not ment to be used with long distances, it is ment for no more than about 50cm (20 inch).
Measuring the time it takes for the bullet to pass between the two sensors can be done very accurately, entirely in hardware, using one of the hardware timers. There would be absolutely nothing for the software to do other than setup the timer before the bullet is fired, the come back later and read the timer after the time has been measured.
There is not a reason in the word a single Arduino cannot do the measurement and display. Both jobs required extremely little bandwidth, and only a few lines of code.
Damn, you seem to be right (after digging deep into the twi lib. Kind of stupid... But just plain retarded it's not mentioned on the reference page....
The distance isn't in inch but in thou of mils aka 1/1000 of an inch.
@ Ray, you're right but how accurate... 1us difference is quite a difference in speed... The default RC resonators are not that accurate...
And do you know the minimum pulse time for the interrupt to trigger?
septillion:
Damn, you seem to be right (after digging deep into the twi lib. Kind of stupid... But just plain retarded it's not mentioned on the reference page....
The distance isn't in inch but in thou of mils aka 1/1000 of an inch.
@ Ray, you're right but how accurate... 1us difference is quite a difference in speed... The default RC resonators are not that accurate...
And do you know the minimum pulse time for the interrupt to trigger?
The timers can run at 16MHz, which gives you 62.5 nSec resolution. If using a crystal instead of a resonator, timing will be accurate to within a few PPM. Pulse width is not important, because it can easily be stretched by some trivial external hardware (i.e. - a $.25 555 timer). And if the timer can't see the pulse, you d@mn sure won't see it in software. Typical minimum input pulse width will be 2-3 clocks, or ~200 nSec.
Yeay, you're right But I have no idea it the hardware of the TS is indeed capable of detecting a bullet...So has to test that first. After that, think of a complete different v2 software version But using 1 is as good as two
Thanks for all the tips! I'm seriously rethinking my design.
How does this sound:
use only one Arduino
use interrupts
use the Arduino's clock with no prescaling to get fairly high accuracy (as mentioned, 62.5 nS)
I'm not sure how to go about some of this. With the interrupts, how much of a rising edge do you need for them to register it as such? The datasheet for my sensors says they have a rise time of 10uS, which is quite slow. About the shortest time that a bullet would be blocking the sensor is maybe 3 uS (erring on the short side). Would this be a sufficient rise on the interrupt to trigger it? Also, can someone explain how to directly use the timers? I don't quite get it yet.
For those worrying about the short distance between the sensors, it's only 3.25 inches right now because I''m testing this on a breadboard. In the final version, I'm thinking that it'll be more like 18 inches.
It's not about interrupts, so much as configuring and reading an input capture using a timer. The fact that the capture activates an interrupt is just a convenience.
Your sensors do sound very slow - I would fix that first.