Author Topic: Nerfo meter - measuring the speed of Nerf darts - final setup  (Read 1757 times)

0 Members and 1 Guest are viewing this topic.

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Nerfo meter - measuring the speed of Nerf darts - final setup
« on: August 24, 2019, 11:12:46 pm »
In the final week of the summer holidays I would like to do some scientific experimenting and constructing together with my son (9).

The goal is to measure the speed of Nerf darts. The setup will contain 2 laser beam light ports. The frame will be 3d printed in one piece.

On the right side 2 pcb's (green rectangles) will be vertically mounted with the photodiodes (blue circles) on the backside.

At first the change of exposure is measured using an oscilloscoop. With that it should be possible to do very precise time determinations. Maybe in the future a dedicated timer and Arduino with some user interface will do the timing. So the signalling part of the schematic can be implemented later on. But it is better to already save some space for it.

About the schema:
The component values are chosen mostly by guessing. They will be calculated later on.
The laserdiode emission is automatically regulated, so it should be possible to react fast on only a 10% drop of exposure. (The photo diode will be in a "tunnel"). Another reason is to not get the U1B op amp in positive saturation.
The transition from exposed to non exposed should be optimal in speed. Going from non exposed to exposed is less important.

Feel free to comment!
« Last Edit: September 30, 2019, 10:05:40 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline Kleinstein

  • Super Contributor
  • ***
  • Posts: 7354
  • Country: de
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #1 on: August 25, 2019, 07:53:23 am »
For fast reaction the photo-diode should be operated with some bias voltage (e.g. 3-5 V) in reverse direction. This reduces the diode capacitance - usually by something like a factor of 3 to 10 and makes the sensor correspondingly faster.
The feedback path in the transimpedance-amplifier should not use a pot. The exact amplification should not matter that much. Pots tend to have more parasitic capacitance to ground and this can lead to instability. In addition they can pick up more stray signals / hum.
For high speed the TIA gain should not be too large, as this slow down the TIA. Some 20 K is a common values for a fast TIA.

The adjustment should be better done at the trigger level of the comparator. For use with a DSO, there is no need for the comparator - a series resistor at the OPs output would be a good idea to isolate it from capacitive load (cable).  µCs tend to already include a comparator (the AVR internal one seems to be quite good), but often only one.
For the simple TIA shown the good old LM393 should be fast enough.

It is odd to use current regulation for the Laser(s): the modules normally just need a constant voltage, and bare laser diodes may need actual power regulation and extra optics.  One might get away with bright LEDs (red or IR), if there is little background light.
 
The following users thanked this post: HendriXML

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #2 on: August 25, 2019, 09:53:46 am »
Thanks for the reply!

For fast reaction the photo-diode should be operated with some bias voltage (e.g. 3-5 V) in reverse direction. This reduces the diode capacitance - usually by something like a factor of 3 to 10 and makes the sensor correspondingly faster.
I should have placed the photo diode differently. The bias voltage is only 500 mV, I would like to keep it battery powered so I don't have a negative rail.

The feedback path in the transimpedance-amplifier should not use a pot. The exact amplification should not matter that much. Pots tend to have more parasitic capacitance to ground and this can lead to instability.
The pot will essentially determine the optical power of the laser diode. I will experiment with it to get 25% of the max current, and then replace it with a normal resistor. This capacitance to ground is also an issue with those 10 turn trim-pots?

The comparator will collapse at 90% exposure relative to the (longer term) regulated one.

Regulating the laser diode is done for the above reason, but also to counter attack longterm aging of the diode and the drop in brightness while in operation.

The setup will have delays and inaccuracy's in it. I hope however to make them as consistent as possible between the two gates  :-+.
« Last Edit: August 25, 2019, 11:02:46 am by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline Kleinstein

  • Super Contributor
  • ***
  • Posts: 7354
  • Country: de
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #3 on: August 25, 2019, 11:50:20 am »
500 mV of reverse voltage for the PD is already better than zero bias. With battery operation the available voltage is limited, especially as the TLC072 is not rail to rail.

There should be no need to use a relatively power hungry zener diode to stabilize the 3.3 V level. Normally just a low power 5 V LDO like MCP1703 should be good enough. Even just the battery voltage can be stable enough. R6 also draws quite some power.

I now understand the laser regulation,  using the same photo-diode as the detector.  I would still adjust SensorVoltRef. and keep the TIA fixed. One may have to choose a suitable resistor though (e.g. 5 K, 10 K, 22K maybe 47 K). How critical a pot is depends on the resistance - at lower resistance (e.g. 10 K) a pot is probably OK, especially a relatively small form factor.
The Laser control still looks odd, as Q2 has emitter and collector swapped.
Worst case the 2N7002 will need more gate voltage to get some 30 mA out.

 

Offline wilfred

  • Frequent Contributor
  • **
  • Posts: 897
  • Country: au
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #4 on: August 25, 2019, 12:32:12 pm »
Circuits designed to measure the shutter speed of cameras would be a source of ideas for a sensor. These typically shone a light through to  a sensor mounted at the film plane. They would set a counter running when the sensor received light. Your application would probably invert that and use the dart to block the light. With the time the light was blocked as the dart passed the sensor and the length of the dart you have what you need.

I once had a device like this that used a 2N5777 http://www.njsemi.com/datasheets/2N5777%20-%202N5780.pdf
https://archive.org/stream/ETIA197/ETI%201977-10%20October#page/n45/mode/2up
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #5 on: August 25, 2019, 02:35:57 pm »
The orientation of Q2 was indeed wrong, nice catch.

I dropped the zener diode. The circuit will be powered  by a stabilized 5V source (https://www.eevblog.com/forum/projects/battery-voltage-to-5v-regulation/msg2323938/#msg2323938 - without the current shutdown) which will be powered by 5 batteries. That circuit I've put in a nice 3D printed box, so it is easy to use in these kind of experiments. It gives an indication that the batteries are too low, so that is a nice feature.
The resistor divider keeps things from the rails. I used a TLC072 because I have them on shelve.

The power consumption is not a major design spec, because the experiments will be short in duration.

The "constant" current using the 2 diodes and the transistor is not one I really like, normally it's very inaccurate due to tolerances and temperatures, diode currents and what not. But in this situation it is more used as a current limiter. However a resistor could have done that trick as well. But it's good to build up some experience. And I get two of these circuits, so I can see how well they behave compared to each other.
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline Kleinstein

  • Super Contributor
  • ***
  • Posts: 7354
  • Country: de
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #6 on: August 25, 2019, 02:48:17 pm »
To reduce the temperaure effect of the diodes + transistor current limit, one could use a LED in stead of the 2 diodes. Red / orange leds have a TC that is relatively similar to the transistor BE voltage TC. So the resulting current limit can be quite stable. However the MOSFETs on resistance will still have quite some TC, so the current would still go down with temperature.

In Principle one could get away without the FET: the Transistor could still be controlled by the OP, with the LED limiting the maximum base voltage.
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #7 on: August 25, 2019, 02:53:46 pm »
Circuits designed to measure the shutter speed of cameras would be a source of ideas for a sensor. These typically shone a light through to  a sensor mounted at the film plane. They would set a counter running when the sensor received light. Your application would probably invert that and use the dart to block the light. With the time the light was blocked as the dart passed the sensor and the length of the dart you have what you need.

I once had a device like this that used a 2N5777 http://www.njsemi.com/datasheets/2N5777%20-%202N5780.pdf
https://archive.org/stream/ETIA197/ETI%201977-10%20October#page/n45/mode/2up
Using 2 gates the circuit can work without knowledge of the dart length. We plan on shooting different ones (and calculate their kinetic energy). Using a bit more distance also lowers the relative errors.

Talking about shutter speeds, I would have like to take some carefully timed photo's with a more advanced setup. But my camera doesn't have a mirror lock function so it will be probably too slow. Using the dart speed it should be possible to calculate the moment of an impact. Using a flash could give some nice action photo.

I will test my camera's responds and measure the time between (remote) firing the shutter and the flash moment to see how long it takes and whether it is repeatable.
« Last Edit: August 25, 2019, 07:37:54 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #8 on: August 25, 2019, 03:19:02 pm »
To reduce the temperaure effect of the diodes + transistor current limit, one could use a LED in stead of the 2 diodes. Red / orange leds have a TC that is relatively similar to the transistor BE voltage TC. So the resulting current limit can be quite stable. However the MOSFETs on resistance will still have quite some TC, so the current would still go down with temperature.

In Principle one could get away without the FET: the Transistor could still be controlled by the OP, with the LED limiting the maximum base voltage.
Nice to know about the leds.

I once did a few test with some 2N3904's and found out that the BE (saturation) voltage (?) where quite  different between each other. Because of the low shunt voltage the differences had a large impact on the current limiting. Eventually I'll look in to some cheap constant current IC's.
« Last Edit: August 25, 2019, 05:36:56 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #9 on: August 25, 2019, 11:44:57 pm »
It became a bit to crowded on the small perfboard so the current limiting is now done via a resistor.

There's also a blue led which signals whether the laser exposes the photo diode or is blocked. Handy for checking stuff.

To bad the soldering on the perfboard also involves "creating tracks" otherwise it was much easier for my son to do it. But first I'll test it on a breadboard, C1, R8, R9 need to be determined yet.
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #10 on: August 26, 2019, 10:06:20 pm »
Today we had fun creating the laser and sensor mounting model in Fusion 360. It's not finished, the "beam tunnel" will be subtracted, but only a small diameter. It will be drilled trough to get a better result.

I keep my fingers crossed that the alignments will be ok. I will drill the laser tunnel first and see where the red dot goes after installing the laser.

The PCB will be vertically mounted, the stands however won't be printed. Because the laserdiode has very short leads to the op amp, I hope the EM interference is minimal. In another setup with longer leads turning devices on and off influenced too much.

The length will be 200 mm, the distance between the gates about 150 mm.
« Last Edit: August 26, 2019, 10:11:05 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #11 on: August 26, 2019, 10:46:38 pm »
The model had to loose some weight, there's still a lot to improve in that regard, but everything under 200 gr of PLA is fine by me.
« Last Edit: August 26, 2019, 10:48:19 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #12 on: August 27, 2019, 12:22:38 pm »
I mounted the circuit on a breadboard.

The laser regulation is oscillating at 240 kHz measuring SensorVolt. The PID regulation is probably to fast. I'll try to finetune that. It may also that the TIA might need some a little capacitor as feedback (but that would slow down the response).

When the laser is driven by a 90 Hz AWG signal the SensorVolt falls in about 2 us. The 10% fall will happen a bit faster.

If a Nerf dart would travel at 15 m/s this would correspond to 30 um (that very little compared to the 150000 um between the sensors and even small relative to the beam/sensor diameter).
« Last Edit: August 27, 2019, 12:39:06 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts
« Reply #13 on: August 28, 2019, 10:16:02 pm »
After some problems with a LM311, which wasn't a LM311 the circuit is now working.

I slowed down the laser power response at Q1, so the output is regulated nicely. But the PID will to be tuned a bit more to have "the slowness" evenly spread.

Also I changed the voltage at which the comparator collapses. One of the LM211 inputs needs to be 1.5V from the positive rail. In the previous schematic it was very close to that with no real benefits.

Potmeter RV1 is now replaced by R13, with a value of 5K (If I remember correctly).

Will do some final tuning and benchmarking later. From what I saw today the circuit works fast enough for the purpose.
« Last Edit: August 28, 2019, 10:17:52 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts - response times
« Reply #14 on: August 29, 2019, 11:32:12 am »
I did some testing of the sensing response times using incrementing the laser diode current. At some point the TIA op amp will saturate.

Lower power means less response time.

This is a good reason to automatically control the laser power like the circuit does.

Red is input square wave
Blue is amplified sensor signal
Yellow is comparator output.
« Last Edit: September 05, 2019, 01:01:24 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts - response times
« Reply #15 on: August 29, 2019, 11:50:12 am »
I also did some testing breaking the beam with a nerf dart.

The sensor curves look very similar because it is mostly determined by the slowness of the nerf dart.

When the dart hits the beam it will gradually block more of the beam and make the exposure on the sensor less.

If that takes 100 us than with the speed of 15 m/s the distance at which this happens would be 1500 um thus 1.5mm. This indeed near the diameter of the laser beam/photo diode.

This principle will lower the accuracy of one gate measurement, however because it's repeated on the second one the precision might be much better.

This will depend on how well the sensors are lit.

But measuring distance with 1 mm accuracy would anyways be a bit of a challenge. So the distance travelled would be something like 150mm +/- 3 mm.
« Last Edit: August 29, 2019, 11:52:03 am by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts - response times
« Reply #16 on: August 29, 2019, 12:04:35 pm »
Also shooting the dart straight/perpendicular is important. Maybe the setup should of less width, so its more obvious when the aiming was wrong.
But we will be shooting different diameters of darts, so that's not a great solution.

Having two vertical gates as well to check straightness would be nice, but for now to complex.
« Last Edit: August 29, 2019, 12:10:37 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts - mounting device
« Reply #17 on: September 02, 2019, 12:27:08 pm »
So the mounting device is 3D printed. It came out great (17 hours). I had only printed the laser beam holes to 1.5 mm. The idea was to drill them more accurately. Letting the drill follow that path. That worked perfectly for one laserdiode hole (6 mm). The other one is slightly non perpendicular. I really could have used a bench drill. However the lasers are Chinese so they got angles them self. With a bit of matching and turning the sensor will be lit just fine!

However the next time I would add larger holes in the 3D model. Going from 1.5 - 2 - 2.5 - 3 - 3.5 - 4 - 4.5 - 5 - 5.5 - 6 and drilling very slowly takes a lot of time, however this is critical not to heat up the drill.

I also made a PCB layout just for laying out the used components in order and usable with a breadboard. It takes a little effort, but my son will be soldering one of the PCB's so the session shouldn't take too long.
« Last Edit: September 02, 2019, 12:35:11 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts - got results!
« Reply #18 on: September 05, 2019, 12:39:59 pm »
Yesterday my son did a very good job on soldering the components. Most of them we did from the the top of the perfboard. That makes it more easy to bend the legs to form traces. (Also faster) That part I've done alone. I'll post some pics of the finished product.

So I hooked the 2 gates on 2 scope channels for a testing out a script.

The script uses SCPI to set things up, retrieve the data and does some calculating. In the script I just took the number of samples between the zero crossings.
Because of the 250MSa/s, this is off course very accurate.

The accuracy of the experiment is in that regard entirely determined by the distance between the gates/laser beams and the place at which the beam is registered as broken. The distance error will be around 2 mm combining those errors.
So that would make the speed calculation around 1% accurate.

Because the error will be mostly systematic, this setup can surely be used in calculating the added power to the Nerf shooter when modding with more precision. However this we won't do, because then it would probably loose it's function as a toy.

The next step would be to have the time measured without using a scope.

Getting data gate A
Sample number: 874998

Getting data gate B
Sample number: 3334790
Sample difference: 2459792
Time difference: 9,83917 ms
Speed: 15,24519 m/s
Speed: 54,88269 km/u

« Last Edit: September 06, 2019, 12:44:51 am by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts - got results using a scope!
« Reply #19 on: September 06, 2019, 11:31:04 am »
This is how the setup looks like. On the back is a battery voltage to 5V regulator https://www.eevblog.com/forum/projects/battery-voltage-to-5v-regulation/msg2280605/?all#lastPost in a 3D printed box. The nice thing of this box is that the air slots are zigzagged, so you can't see inside the box or poke a straight object in it.

With this project https://www.eevblog.com/forum/projects/very-stable-temperature-control/msg2514615/#msg2514615 the thermal dissipation capacity of the box and heatsink inside could be determined by measuring the power it takes to keep for instance a stable 60 deg. That is something I'd like to experiment with in the future.

As also can be seen the laser is regulated to a very dim light source.
« Last Edit: September 06, 2019, 11:35:34 am by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts - got results using a scope!
« Reply #20 on: September 06, 2019, 02:58:07 pm »
One thing that happened while using longer leads and my bench power supply was some instability with the comparator output. Because it drives a LED the VCC would drop a bit which would lead to a drop in the ref voltage. This would stop after a few ups and downs because of the hysteresis. Using my 5V regulator this has not happened, it may react just a bit faster on a voltage drop or just the shorter leads make the difference.

However I want to process the signal digitally and like it to behave as designed. Adding C5 on the ref voltage dividers makes it behave just fine.
« Last Edit: September 14, 2019, 05:14:11 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
I found an AT Mega2560 Pro board on Ali Express, which is a lot smaller and can be soldered on more easily. This one has an Cristal resonator, so it should be pretty accurate. Many times more that the distance part of the speed measurement.

Timing on the AT Mega is discussed in this thread in more depth. (Timers and interrupts, TCXO's)
https://www.eevblog.com/forum/projects/measuring-time-span-very-precise/?all#lastPost

Essentially the script uses 2 timers: timer 4 and timer 5. Each of them has a pin which when triggered saves the current counter to a location and schedules an interrupt.

The counters are made in sync. So one pin can be used to get the starting counter (photo gate A), the other one the ending counter (photo gate B). Some info from the ATMega2560 datasheet that is of importance:

Timer 4
Capture pin: PL0
Board: Pin 49 = PORTL0 (ICP4)

Timer 5
Capture pin: PL1
Board: Pin 48 = PORTL1 (ICP5)

Timer n
Capture register: ICRn
Count register: TCNTn
Control register B: TCCRnB
Bit 7: Noise canceling
Bit 6: Input capture edge select: 0: falling; 1: Rising
Bit 2..0: Input clock source: 000: none; 001: 1x; 110 External clock falling
Compare registers OCRnA, OCRnB, OCRnC
TIMSKn: Interrupt mask register
Bit 5: Capture interrupt: 0: off; 1: on;
Bit 3: Output compare C interrupt: 0: off; 1: on;
Bit 2: Output compare B interrupt: 0: off; 1: on;
Bit 1: Output compare A interrupt: 0: off; 1: on;
Bit 0: Overflow interrupt: 0: off; 1: on;

Global registers
GTCCR: General timer counter register - halt timers
Bit 7: Synchronization mode: 1: sync mode; 0: normal mode;
Bit 1: PRASY: ?
Bit 0: PSRSYNC: Reset for synchronische timers

Code: [Select]
#include <U8glib.h>
#include <HardwareSerial.h>

U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_NO_ACK);

enum  CaptureState_t { csStart, csCapture4, csErrorCapture5 };
enum  DisplayState_t { dsUnused, dsUninitialized, dsInitialized };

typedef uint16_t sequence_t;
typedef uint64_t counter_t;

struct Measurement_t
{
  sequence_t Sequence;
  counter_t DiffCount;
  bool Active;
  Measurement_t* Previous;
  Measurement_t* Next;
};

struct DisplayMeasurements_t
{
  sequence_t Sequence;
  counter_t DiffCount;
  DisplayState_t State;
  char DisplayData[19];
  // 0001: xxx.xxx km/h
};



volatile counter_t TimerCycles4A = 0;
volatile counter_t TimerCycles4B = 0;
volatile counter_t TimerCycles4O = 0;
volatile counter_t TimerCycles5A = 0;
volatile counter_t TimerCycles5B = 0;
volatile counter_t TimerCycles5O = 0;
volatile counter_t CaptureCount4;
volatile counter_t CaptureCountDiff;
volatile uint16_t DisplayUpdateSequence = 0;
volatile CaptureState_t CaptureState = csStart;

sequence_t CurrentMeasurementSequence = 1;
uint16_t PrevDisplayUpdateSequence = 0;
CaptureState_t PrevCaptureState = csStart;
double MilliSecondsTickPeriod = 1000.0 / 16000000.0;

const uint16_t HighCountA = 0xFFFF / 3;
const uint16_t HighCountB = 0xFFFF / 3 * 2;
const uint8_t BufferCount = 10;
const uint8_t MeasurementDisplayCount = 5;

Measurement_t MeasurementBuffers[BufferCount];
Measurement_t* Head = &MeasurementBuffers[0];
DisplayMeasurements_t  MeasurementDisplayBuffers[MeasurementDisplayCount];


ISR(TIMER4_COMPA_vect)
{
  TimerCycles4A++;
}

ISR(TIMER4_COMPB_vect)
{
  TimerCycles4B++;
}

ISR(TIMER4_OVF_vect)
{
  TimerCycles4O++;
}

ISR(TIMER4_CAPT_vect)
{
  DisplayUpdateSequence++;
  uint16_t CaptureCount = ICR4;
  CaptureState = csCapture4;
  // Using the cycle count that is linked to the captured counter
  if (CaptureCount < HighCountA)
    CaptureCount4 = (TimerCycles4B << 16) + CaptureCount;
  else if (CaptureCount < HighCountB)
    CaptureCount4 = (TimerCycles4O << 16) + CaptureCount;
  else
    CaptureCount4 = ((TimerCycles4A - 1) << 16) + CaptureCount;
}



ISR(TIMER5_COMPA_vect)
{
  TimerCycles5A++;
}

ISR(TIMER5_COMPB_vect)
{
  TimerCycles5B++;
}

ISR(TIMER5_OVF_vect)
{
  TimerCycles5O++;
}

ISR(TIMER5_CAPT_vect)
{
  uint16_t CaptureCount = ICR5;
  uint64_t CaptureCount5;
  if (CaptureState != csCapture4)
  {
    CaptureState = csErrorCapture5;
    return;
  }
  // Using the cycle count that is linked to the captured counter
  if (CaptureCount < HighCountA)
    CaptureCount5 = (TimerCycles5B << 16) + CaptureCount;
  else if (CaptureCount < HighCountB)
    CaptureCount5 = (TimerCycles5O << 16) + CaptureCount;
  else
    CaptureCount5 = ((TimerCycles5A - 1) << 16) + CaptureCount;

  CaptureState = csStart;
  DisplayUpdateSequence++;

  Head = Head->Previous;
  Head->Sequence = CurrentMeasurementSequence;
  Head->DiffCount = CaptureCount5 - CaptureCount4;
  Head->Active = true;
  CurrentMeasurementSequence++;
}


// The setup() function runs once each time the micro-controller starts
void setup()
{
  //  Serial.begin(115200);
  //  Serial.println("Setup started");
  noInterrupts();

  // pinMode(48, INPUT);
  // pinMode(49, INPUT);

   // Halt timers
  GTCCR = (1 << TSM) | (1 << PSRASY) | (1 << PSRSYNC);

  // enable capture, compare a, compare b, and overflow interrupts
  TIMSK4 = (1 << ICIE4) | (1 << OCIE4A) | (1 << OCIE4B) | (1 << TOIE4);
  TIMSK5 = (1 << ICIE5) | (1 << OCIE5A) | (1 << OCIE5B) | (1 << TOIE5);

  TCCR4A = 0;
  TCCR5A = 0;

  // no noise cancelling, falling edge, no prescaling
  TCCR4B = (0 << ICNC4) | (0 << ICES4) | ((0 << CS42) | (0 << CS41) | (1 << CS40));
  TCCR5B = (0 << ICNC5) | (0 << ICES5) | ((0 << CS52) | (0 << CS51) | (1 << CS50));

  OCR4A = HighCountA;
  OCR5A = HighCountA;

  OCR4B = HighCountB;
  OCR5B = HighCountB;

  TCNT4 = 0;
  TCNT5 = 0;

  // just to be sure..
  TimerCycles4A = 0;
  TimerCycles4B = 0;
  TimerCycles4O = 0;
  TimerCycles5A = 0;
  TimerCycles5B = 0;
  TimerCycles5O = 0;

  // Continue timers
  GTCCR = 0;

  for (uint16_t i = 1; i < BufferCount; i++)
  {
    MeasurementBuffers[i - 1].Next = &MeasurementBuffers[i];
    MeasurementBuffers[i].Previous = &MeasurementBuffers[i - 1];
  }
  MeasurementBuffers[0].Previous = &MeasurementBuffers[BufferCount - 1];
  MeasurementBuffers[BufferCount - 1].Next = &MeasurementBuffers[0];
  MeasurementBuffers[0].Active = false;
  for (uint16_t i = 0; i < MeasurementDisplayCount; i++)
  {
    MeasurementDisplayBuffers[i].State = dsUnused;
    MeasurementDisplayBuffers[i].DisplayData[4] = ':';
    MeasurementDisplayBuffers[i].DisplayData[5] = ' ';

    MeasurementDisplayBuffers[i].DisplayData[14] = 'k';
    MeasurementDisplayBuffers[i].DisplayData[15] = 'm';
    MeasurementDisplayBuffers[i].DisplayData[16] = '/';
    MeasurementDisplayBuffers[i].DisplayData[17] = 'h';
    MeasurementDisplayBuffers[i].DisplayData[18] = 0;
  }

  interrupts();
  u8g.firstPage();
  do {
  } while (u8g.nextPage());
  DisplayUpdateSequence++;
  //  Serial.println("Setup is ready");
}


// Add the main program code into the continuous loop() function
void loop()
{
  // these must be constistent
  noInterrupts();
  uint16_t LocDisplayUpdateSequence = DisplayUpdateSequence;
  CaptureState_t LocCaptureState = CaptureState;
  Measurement_t* RunningMeasurement = Head;
  for (uint8_t i = 0; i < MeasurementDisplayCount; i++)
  {
    if (RunningMeasurement->Active)
    {
      if (!((MeasurementDisplayBuffers[i].State == dsInitialized) && (MeasurementDisplayBuffers[i].Sequence == RunningMeasurement->Sequence)))
      {
        MeasurementDisplayBuffers[i].State = dsUninitialized;
        MeasurementDisplayBuffers[i].DiffCount = RunningMeasurement->DiffCount;
        MeasurementDisplayBuffers[i].Sequence = RunningMeasurement->Sequence;
      }
    }
    else
      MeasurementDisplayBuffers[i].State = dsUnused;
    RunningMeasurement = RunningMeasurement->Next;
  }
  interrupts();

  if ((LocCaptureState != PrevCaptureState) || (LocDisplayUpdateSequence != PrevDisplayUpdateSequence))
  {
    for (uint8_t i = 0; i < MeasurementDisplayCount; i++)
    {
      if (MeasurementDisplayBuffers[i].State == dsUninitialized)
      {
        uint16_t Seq = MeasurementDisplayBuffers[i].Sequence;
        MeasurementDisplayBuffers[i].DisplayData[3] = '0' + Seq % 10;

        Seq = Seq / 10;
        if (Seq == 0)
          MeasurementDisplayBuffers[i].DisplayData[2] = ' ';
        else
          MeasurementDisplayBuffers[i].DisplayData[2] = '0' + Seq % 10;

        Seq = Seq / 10;
        if (Seq == 0)
          MeasurementDisplayBuffers[i].DisplayData[1] = ' ';
        else
          MeasurementDisplayBuffers[i].DisplayData[1] = '0' + Seq % 10;

        Seq = Seq / 10;
        if (Seq == 0)
          MeasurementDisplayBuffers[i].DisplayData[0] = ' ';
        else
          MeasurementDisplayBuffers[i].DisplayData[0] = '0' + Seq % 10;

        double MilliSecs = MeasurementDisplayBuffers[i].DiffCount * MilliSecondsTickPeriod;
        double Speed = 150 * 3.6 / MilliSecs;
        dtostrf(Speed, 7, 3, &MeasurementDisplayBuffers[i].DisplayData[6]);

        MeasurementDisplayBuffers[i].DisplayData[13] = ' ';
        MeasurementDisplayBuffers[i].State = dsInitialized;
      }
    }
    u8g.setFont(u8g_font_profont11);
    u8g.firstPage();
    do {
      switch (LocCaptureState)
      {
      case csStart:
      {
        u8g.drawStr(0, 8, "Shoot!!");
        break;
      }
      case csCapture4:
      {
        //Serial.println();
 //       Serial.println("Timer running..");
        u8g.drawStr(0, 8, "Timer started");
        break;
      }
      case csErrorCapture5:
      {
        //       Serial.println("Timer hasn't  started!!");
        u8g.drawStr(0, 8, "Timer hasn't  started!!");
        break;
      }
      default:
        u8g.drawStr(0, 8, "??");
        //Serial.println("??");
        break;
      }
      uint8_t LineTop = 20;
      for (uint8_t i = 0; i < MeasurementDisplayCount; i++)
      {
        if (MeasurementDisplayBuffers[i].State == dsInitialized)
        {
          u8g.drawStr(4, LineTop, MeasurementDisplayBuffers[i].DisplayData);
          LineTop += 10;
        }
      }
    } while (u8g.nextPage());
    PrevCaptureState = LocCaptureState;
    PrevDisplayUpdateSequence = LocDisplayUpdateSequence;
  }
}


« Last Edit: September 27, 2019, 10:58:31 am by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
The script uses circular measurement buffers, because only 5 can be shown at a time I used a max of 10 measurements, but in theory many more measurements can be stored in FIFO order.

Each measurement is prepared (initialized) before going in to a display loop. To keep things fast that is essential, because anything in that loop is done multiple time when drawing "pages" (which mean parts of the screen buffer). (Thus saving buffer memory)

Because the main loop is light weight the responsiveness is kept high.
« Last Edit: September 27, 2019, 11:14:15 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
I updated the code to use a more recent library U8g2lib. Also I had some stability issues with the display, turning the busclock down seems to solve that.

This library can now update an entire screenbuffer at once (consuming more memory).

Code: [Select]
#include <U8g2lib.h>
 #define DEBUG 0
 #if DEBUG
 #define SERIALOUT;
 #endif
 #ifdef SERIALOUT
 #include <HardwareSerial.h>
 #endif
 #ifdef U8X8_HAVE_HW_I2C
 #include <Wire.h>
 #endif

 #ifdef SERIALOUT
 #define DEBUG_PRINT(x)  Serial.println (x)

 #define DEBUG_PRINT(x)
 #endif

U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

enum  CaptureState_t { csStart, csCapture4, csErrorCapture5 };
enum  DisplayState_t { dsUnused, dsUninitialized, dsInitialized };

typedef uint16_t sequence_t;
typedef uint64_t counter_t;

struct Measurement_t
{
  sequence_t Sequence;
  counter_t DiffCount;
  bool Active;
  Measurement_t* Previous;
  Measurement_t* Next;
};

struct DisplayMeasurements_t
{
  sequence_t Sequence;
  counter_t DiffCount;
  DisplayState_t State;
  char DisplayData[19];
  // 0001: xxx.xxx km/h
};



volatile counter_t TimerCycles4A = 0;
volatile counter_t TimerCycles4B = 0;
volatile counter_t TimerCycles4O = 0;
volatile counter_t TimerCycles5A = 0;
volatile counter_t TimerCycles5B = 0;
volatile counter_t TimerCycles5O = 0;
volatile counter_t CaptureCount4;
volatile counter_t CaptureCountDiff;
volatile uint16_t DisplayUpdateSequence = 0;
volatile CaptureState_t CaptureState = csStart;

sequence_t CurrentMeasurementSequence = 1;
uint16_t PrevDisplayUpdateSequence = 0;
CaptureState_t PrevCaptureState = csStart;
double MilliSecondsTickPeriod = 1000.0 / 16000000.0;

const uint16_t HighCountA = 0xFFFF / 3;
const uint16_t HighCountB = 0xFFFF / 3 * 2;
const uint8_t BufferCount = 20;
const uint8_t MeasurementDisplayCount = 5;

Measurement_t MeasurementBuffers[BufferCount];
Measurement_t* Head = &MeasurementBuffers[0];
DisplayMeasurements_t  MeasurementDisplayBuffers[MeasurementDisplayCount];


ISR(TIMER4_COMPA_vect)
{
  TimerCycles4A++;
}

ISR(TIMER4_COMPB_vect)
{
  TimerCycles4B++;
}

ISR(TIMER4_OVF_vect)
{
  TimerCycles4O++;
}

ISR(TIMER4_CAPT_vect)
{
  DisplayUpdateSequence++;
  uint16_t CaptureCount = ICR4;
  CaptureState = csCapture4;
  // Using the cycle count that is linked to the captured counter
  if (CaptureCount < HighCountA)
    CaptureCount4 = (TimerCycles4B << 16) + CaptureCount;
  else if (CaptureCount < HighCountB)
    CaptureCount4 = (TimerCycles4O << 16) + CaptureCount;
  else
    CaptureCount4 = ((TimerCycles4A - 1) << 16) + CaptureCount;
}



ISR(TIMER5_COMPA_vect)
{
  TimerCycles5A++;
}

ISR(TIMER5_COMPB_vect)
{
  TimerCycles5B++;
}

ISR(TIMER5_OVF_vect)
{
  TimerCycles5O++;
}

ISR(TIMER5_CAPT_vect)
{
  uint16_t CaptureCount = ICR5;
  uint64_t CaptureCount5;
  if (CaptureState != csCapture4)
  {
    CaptureState = csErrorCapture5;
    return;
  }
  // Using the cycle count that is linked to the captured counter
  if (CaptureCount < HighCountA)
    CaptureCount5 = (TimerCycles5B << 16) + CaptureCount;
  else if (CaptureCount < HighCountB)
    CaptureCount5 = (TimerCycles5O << 16) + CaptureCount;
  else
    CaptureCount5 = ((TimerCycles5A - 1) << 16) + CaptureCount;

  CaptureState = csStart;
  DisplayUpdateSequence++;

  Head = Head->Previous;
  Head->Sequence = CurrentMeasurementSequence;
  Head->DiffCount = CaptureCount5 - CaptureCount4;
  Head->Active = true;
  CurrentMeasurementSequence++;
}


// The setup() function runs once each time the micro-controller starts
void setup()
{
 #ifdef SERIALOUT
  Serial.begin(115200);
 #endif
  DEBUG_PRINT("Setup started");

  noInterrupts();

  pinMode(48, INPUT_PULLUP);
  pinMode(49, INPUT_PULLUP);

  // Halt timers
  GTCCR = (1 << TSM) | (1 << PSRASY) | (1 << PSRSYNC);

  // enable capture, compare a, compare b, and overflow interrupts
  TIMSK4 = (1 << ICIE4) | (1 << OCIE4A) | (1 << OCIE4B) | (1 << TOIE4);
  TIMSK5 = (1 << ICIE5) | (1 << OCIE5A) | (1 << OCIE5B) | (1 << TOIE5);

  TCCR4A = 0;
  TCCR5A = 0;

  // no noise cancelling, falling edge, no prescaling
  TCCR4B = (0 << ICNC4) | (0 << ICES4) | ((0 << CS42) | (0 << CS41) | (1 << CS40));
  TCCR5B = (0 << ICNC5) | (0 << ICES5) | ((0 << CS52) | (0 << CS51) | (1 << CS50));

  OCR4A = HighCountA;
  OCR5A = HighCountA;

  OCR4B = HighCountB;
  OCR5B = HighCountB;

  TCNT4 = 0;
  TCNT5 = 0;

  // just to be sure..
  TimerCycles4A = 0;
  TimerCycles4B = 0;
  TimerCycles4O = 0;
  TimerCycles5A = 0;
  TimerCycles5B = 0;
  TimerCycles5O = 0;

  // Continue timers
  GTCCR = 0;

  for (uint16_t i = 1; i < BufferCount; i++)
  {
    MeasurementBuffers[i - 1].Next = &MeasurementBuffers[i];
    MeasurementBuffers[i].Previous = &MeasurementBuffers[i - 1];
  }
  MeasurementBuffers[0].Previous = &MeasurementBuffers[BufferCount - 1];
  MeasurementBuffers[BufferCount - 1].Next = &MeasurementBuffers[0];
  MeasurementBuffers[0].Active = false;
  for (uint16_t i = 0; i < MeasurementDisplayCount; i++)
  {
    MeasurementDisplayBuffers[i].State = dsUnused;
    MeasurementDisplayBuffers[i].DisplayData[4] = ':';

    MeasurementDisplayBuffers[i].DisplayData[14] = 'k';
    MeasurementDisplayBuffers[i].DisplayData[15] = 'm';
    MeasurementDisplayBuffers[i].DisplayData[16] = '/';
    MeasurementDisplayBuffers[i].DisplayData[17] = 'h';
    MeasurementDisplayBuffers[i].DisplayData[18] = 0;
  }

  interrupts();
  //u8g2.firstPage();
  //do
  //{
  //} while (u8g2.nextPage());

  DisplayUpdateSequence++;
  u8g2.setBusClock(50000);
  //u8g2.setBusClock(200000);
  u8g2.begin();
  DEBUG_PRINT("Setup is ready");

}


// Add the main program code into the continuous loop() function
void loop()
{
  // these must be constistent
  noInterrupts();
  uint16_t LocDisplayUpdateSequence = DisplayUpdateSequence;
  CaptureState_t LocCaptureState = CaptureState;
  Measurement_t* RunningMeasurement = Head;
  for (uint8_t i = 0; i < MeasurementDisplayCount; i++)
  {
    if (RunningMeasurement->Active)
    {
      if (!((MeasurementDisplayBuffers[i].State == dsInitialized) && (MeasurementDisplayBuffers[i].Sequence == RunningMeasurement->Sequence)))
      {
        MeasurementDisplayBuffers[i].State = dsUninitialized;
        MeasurementDisplayBuffers[i].DiffCount = RunningMeasurement->DiffCount;
        MeasurementDisplayBuffers[i].Sequence = RunningMeasurement->Sequence;
      }
    }
    else
      MeasurementDisplayBuffers[i].State = dsUnused;
    RunningMeasurement = RunningMeasurement->Next;
  }
  interrupts();

  if ((LocCaptureState != PrevCaptureState) || (LocDisplayUpdateSequence != PrevDisplayUpdateSequence))
  {
    for (uint8_t i = 0; i < MeasurementDisplayCount; i++)
    {
      if (MeasurementDisplayBuffers[i].State == dsUninitialized)
      {
        uint16_t Seq = MeasurementDisplayBuffers[i].Sequence;
        MeasurementDisplayBuffers[i].DisplayData[3] = '0' + Seq % 10;

        Seq = Seq / 10;
        if (Seq == 0)
          MeasurementDisplayBuffers[i].DisplayData[2] = ' ';
        else
          MeasurementDisplayBuffers[i].DisplayData[2] = '0' + Seq % 10;

        Seq = Seq / 10;
        if (Seq == 0)
          MeasurementDisplayBuffers[i].DisplayData[1] = ' ';
        else
          MeasurementDisplayBuffers[i].DisplayData[1] = '0' + Seq % 10;

        Seq = Seq / 10;
        if (Seq == 0)
          MeasurementDisplayBuffers[i].DisplayData[0] = ' ';
        else
          MeasurementDisplayBuffers[i].DisplayData[0] = '0' + Seq % 10;

        double MilliSecs = MeasurementDisplayBuffers[i].DiffCount * MilliSecondsTickPeriod;
        double Speed = 150 * 3.6 / MilliSecs;
        dtostrf(Speed, 8, 3, &MeasurementDisplayBuffers[i].DisplayData[5]);

        MeasurementDisplayBuffers[i].DisplayData[13] = ' ';
        MeasurementDisplayBuffers[i].State = dsInitialized;
      }
    }
    u8g2.setFont(u8g_font_profont11);
    u8g2.clearBuffer();
    //u8g2.firstPage();
    //do {
    switch (LocCaptureState)
    {
    case csStart:
    {
      u8g2.drawStr(0, 8, "Shoot!!");
      DEBUG_PRINT("Shoot");
      break;
    }
    case csCapture4:
    {
      DEBUG_PRINT("Timer started");
      u8g2.drawStr(0, 8, "Timer started");
      break;
    }
    case csErrorCapture5:
    {
      DEBUG_PRINT("Timer hasn't  started!!");

      u8g2.drawStr(0, 8, "Timer hasn't  started!!");
      break;
  }
    default:
      u8g2.drawStr(0, 8, "??");
      DEBUG_PRINT("??");
      break;
}
    uint8_t LineTop = 20;
    for (uint8_t i = 0; i < MeasurementDisplayCount; i++)
    {
      if (MeasurementDisplayBuffers[i].State == dsInitialized)
      {
        u8g2.drawStr(4, LineTop, MeasurementDisplayBuffers[i].DisplayData);
        DEBUG_PRINT(MeasurementDisplayBuffers[i].DisplayData);
        LineTop += 10;
      }
    }
    //} while (u8g2.nextPage());
    u8g2.sendBuffer();
    PrevCaptureState = LocCaptureState;
    PrevDisplayUpdateSequence = LocDisplayUpdateSequence;
  }
}

“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 

Offline HendriXML

  • Frequent Contributor
  • **
  • Posts: 718
  • Country: nl
    • KiCad-BOM-reporter
Re: Nerfo meter - measuring the speed of Nerf darts - final setup
« Reply #24 on: September 30, 2019, 10:20:56 pm »
To finalize the project I printed an enclosure that has the mega board and the display mounted.

I'm glad that printing the 45 deg display mount went ok.

For mounting the board I used the board photo as a reference, which resulted in accurate screw holes locations. The hex screw have heat shrink added to them, to isolate the screw from the board, not to harm it and to give a good fit for the 2 rather large holes.

Under the left-down location of the atmega chip, there's another stand, but without a screw. The board is mounted in such way that every pin could have been used.
« Last Edit: September 30, 2019, 11:50:57 pm by HendriXML »
“I ‘d like to reincarnate as a dung beetle, ‘cause there’s nothing wrong with a shitty life, real misery comes from high expectations”
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf