A small collection of mini synths - Korg Monotron, Dubreq Stylophone, Bleep Labs Nebulophone.
RC Car Active Power Distribution I am also planning to bring this monster back to life -
Its two RC Cars joined together to make a twin motor 4wd car with independent throttle control to the front and rear axles. The result is a small car with way too much power, its very much in the spirit of a Group B rally car or the twin motored Suzuki Escudo rally car.
I have just set up a Google+ page for the Arduino Basics blog posts. If you haven't already tried it, Google+ is a great way to develop communities of like-minded individuals. It is a great way to share and ask questions. I held off from joining Google+, but now that I see it's advantages - I really like it. Anyway, I will still be blogging in the usual way, however, if you happen to be using Google+, please make sure to come and visit my page.
Click on the link below to have a look: Arduino Basics Google+ Page.
The IRTEMP module from Freetronics is an infrared remote temperature sensor that can be incorporated into your Arduino / microcontroller projects. It can scan a temperature between -33 to +220 C, and can be operated using a 3.3 to 5V power supply. It can be powered directly from the Arduino 5V pin. This module can also provide an ambient temperature reading if required. The Servo used in this project is a SG-5010 standard servo which will be utilised to display the temperature reading from the IRTEMP module.
Gauge parts: Paper (to print the face of the gauge), and some glue to stick it to the wood. MDF Standard panel (3mm width) - for the top and base of the gauge. Galvanized bracket (25x25x40mm) Timber screws: Hinge-long threads csk head Phillips drive (4G x 12mm)
/* ------------------------------------------------------- Analog IR Temperature Gauge: written by ScottC on 1st Dec 2012. http://arduinobasics.blogspot.com/2012/12/arduino-basics-analog-ir-temperature.html
* Some of the code was adapted from a sketch by Andy Gelme (@geekscape) * For more information on using the IRTEMP see www.freetronics.com/irtemp * IRTemp library uses an Arduino interrupt: * If PIN_CLOCK = 2, then Arduino interrupt 0 is used * If PIN_CLOCK = 3, then Arduino interrupt 1 is used ---------------------------------------------------------*/
#include "IRTemp.h" #include <Servo.h>
Servo servo1; staticconstbyte PIN_DATA = 2; staticconstbyte PIN_CLOCK = 3; // Must be either pin 2 or pin 3 staticconstbyte PIN_ACQUIRE = 4;
/* If you want the ambient temperature instead - then use the code below. */ //float ambientTemperature = irTemp.getAmbientTemperature(SCALE); //printTemperature("Ambient", ambientTemperature);
Ambient temperature: If you want to get the ambient temperature from the IRTEMP module, then have a look at lines 58-59. Servo Angles: You will notice on line 36, the maximum servo angle used was 175. This value was obtained through trial and error (see below).
Calibrating the servo angles You may need to calibrate your servo in order to move through an angle of 0 to 180 degrees without straining the motor.Change the minAngle on line 35to a safe value (for example: 10), and the maxAngle on line 36 to a value like 170. Remove the comment tag (//) on line 76, and then run the sketch. Lower the minAngle until it reaches the minimum value on the gauge, making sure that the servo doesn't sound like it is straining to keep it in position.
Add the comment tag (//) back in, and then take out the comment tag for line 79. And follow a similar process, until you reach the maximum value on the gauge. Once again, make sure that the servo is not making a straining noise to hold it at that value. Make sure to add the comment tag back in, when you have finished the calibration.
In this example, the servo's minAngle value was 0, and maxAngle value was 175 after calibration, however, as you can see from the video, the physical range of the servo turned out to be 0 to 180 degrees.
The Temperature Gauge Picture
The following gauge was created in Microsoft Excel using an X-Y chart. Data labels were manually repositioned in order to get the desired numerical effect.
This post provides a quick introduction to Direct Digital Synthesis (DDS) on the Arduino Due. The sample sketch outputs a sinewave at a 44.1Khz sample rate with 12 bit resolution using the built in Digital To Analog Converter (DAC).
Direct Digital Synthesis - Introduction DDS is a common approach to generating sinewave output from digital systems. The approach is based around outputting samples from a description of the required waveform held in memory. The description is an array of samples which can be traversed to plot the waveform onscreen or by using DDS can generate a sinewave output at a selected frequency.
The 600 sample 12 Bit sine wave table used in the sketch below plotted on screen using processing -
The 128 sample 8 Bit wave tables used in the Illutron B synth -
Direct Digital Synthesis - Sample Rate To generate an output waveform we need a way to regularly update the output value, we do this by using a timer interrupt. The frequency of this interrupt is termed the sample rate, in our case we are using a 44.1Khz sample rate meaning that we have an interrupt triggering 44,100 times per second.
As this interrupt is triggering at such a high rate it is very important that we keep it efficient, this is the reason for using a pre computed wavetable rather than computing the sine samples on the fly.
Direct Digital Synthesis - Frequency Generation We can generate different frequencies by changing the rate at which we traverse through the wave table. One pass through the wavetable provides one complete cycle of the selected waveform (sine in our case). If we pass through the table once in 44,100 interrupts we generate a frequency of 1Hz, if we pass through 100 times faster, we get a sinewave output at a frequency of 100Hz.
Direct Digital Synthesis - Implementation and Terminology The following terms are commonly used in reference to DDS and variations are used throughout the RC Arduino audio projects.
1) Phase Accumulator - this is an array index which points to the current sample in the selected wavetable. 2) Phase Increment - this value is used to advance the Phase Accumulator each time the interrupt is called.
Example usage of the phase accumulator used inside the timer interrupt to select the next sample in the wave table array -
// Update the phase accumulator by adding the phase increment ulPhaseAccumulator += ulPhaseIncrement;
// get the current sample from the sine table using the phase accumulator as the index uint32_t ulOutput = nSineTable[ulPhaseAccumulator>>20];
Direct Digital Synthesis - Fixed Point Maths To generate a full range of frequencies we will often need to use fractional values for our phase accumulator.
The usual method of working with fractional values is to use the float data type however in DDS we need our interrupts to run as fast as possible. With very few exceptions microcontrollers are not able to process the float data type at high speed, the solution is to use fixed point integer maths.
Fixed Points For Higher Integer Precision There are many applications of fixed point arithmetic but in DDS the main use is to provide increased precision - equivalent to having many decimal points while still using the high speed integer (whole number) data type.
To get extra precision we use more bits than we need to represent our data range. For example the sample code below has a wave table size of 600 samples so the range of we need for our phase accumulator is 0 to 599.
We can represent this range with 10 bits but if we use 16 bits we have 6 additional bits of precession.
16 Bit Integer
Bit
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
Value
32768
16384
8192
4096
2048
1024
512
256
128
64
32
16
8
4
2
1
Fixed Point 10.6 using 16 Bit Integer
Bit
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
Value
1024
512
256
128
64
32
16
8
4
2
1
0.5
0.25
0.125
0.0625
0.0313
We use this additional precision to perform fractional addition to the phase accumulator, the 10.6 format gives 1/32 precision as the least significant bit.
Fixed point addition - we know its fixed point, the compiler doesn't
// Update the phase accumulator by adding the phase increment ulPhaseAccumulator += ulPhaseIncrement;
This line of code is actually adding two fixed point numbers, part of the trick is that the compiler does not know this, it sees them simply as two integers which it processes using very fast hardware integer addition.
When we want to use the phase accumulator as the array index, we perform an arithmetic shift six places to the right, this drops off the six bits of additional precession and leaves us with a 10 bit integer value for the array index.
// drop the six bits of additional precision and access the remaining 10 bits as an integer array index in the range 0 - 599
The only trick to fixed point maths is to figure out a scheme which will work for your project and stick to it. Remember the compiler does not know that you are treating part of the number as fractions so the responsibility is on you the programmer to keep consistency.
Fixed Point In The RCArduino DDS Example Sketch The standard data type for the 32 bit Arduino Due is a 32 bit integer, this gives us scope for very high levels of precession in integer maths. The scheme used for the phase increment and phase accumulator variables in the sample sketch is 12.20 (12+20 = 32 Bits) this can be seen in the following line of code where the precision is shifted out to access the pure 12 bit integer value -
Where ever you see code that makes extensive use of '>>' operations you might be looking at fixed point arithmetic, in a follow up post we will look at how we perform fast fixed point multiplication. This is another key to fast and flexible synthesizer engines.
Fixed Point Multiplication is used in Audio synthesis to apply envelopes and amplitude modulation.
Envelope Applied to a DDS sine wave using Fixed Point Maths in the Illutron B Project -
DDS Sinewave for Arduino Due - The Sketch Use a potentiometer connected to analog pin 0 to control the pitch. To hear the output you can use an amplifier circuit such as this one used throughout RCArduino - http://rcarduino.blogspot.com/2012/08/adding-audio-to-arduino-projects.html The sinewave is output through DAC0 - Note that the DAC does not provide a full 0-3.3volt swing, this is a hardware limitation of the SAM3X8E chip.
Caution : The SAM3X8E microcontroller at the heart of the Arduino Due is less able to sink and source current than the AVR family of chips used in the 8-Bit Arduinos. See the update below for the most recent recommendations -
Update 28/03/2013 : There is little information on the SAM3X8E DAC in the datasheet however Arduino forum used 'stimmer' has found recommendations on a closely related chip which suggest that a 2K Ohm resistor should be placed as a series current limiting resistor between the DAC Output and the load. This assumes that the load has no resistance of its own which will be the case if you accidentally short something, if you know the resistance of your load, you can reduce this value, if not, its a reasonable starting point and will protect against accidents. For more information see the original thread here - http://arduino.cc/forum/index.php/topic,139733.15.html
// RCArduino DDS Sinewave for Arduino Due // RCArduino DDS Sinewave by RCArduino is licensed under a Creative Commons Attribution 3.0 Unported License. // Based on a work at rcarduino.blogspot.com.
// For helpful background information on Arduino Due Timer Configuration, refer to the following link // thanks to Sebastian Vik // http://arduino.cc/forum/index.php?action=post;topic=130423.15;num_replies=20
// For background information on the DDS Technique see // http://interface.khm.de/index.php/lab/experiments/arduino-dds-sinewave-generator/
// For audio sketches making extensive use of DDS Techniques, search the RCArduino Blog // for the tags Audio or synth
// These are the clock frequencies available to the timers /2,/8,/32,/128 // 84Mhz/2 = 42.000 MHz // 84Mhz/8 = 10.500 MHz // 84Mhz/32 = 2.625 MHz // 84Mhz/128 = 656.250 KHz // // 44.1Khz = CD Sample Rate // Lets aim for as close to the CD Sample Rate as we can get - // // 42Mhz/44.1Khz = 952.38 // 10.5Mhz/44.1Khz = 238.09 // best fit divide by 8 = TIMER_CLOCK2 and 238 ticks per sample // 2.625Hmz/44.1Khz = 59.5 // 656Khz/44.1Khz = 14.88
// 84Mhz/44.1Khz = 1904 instructions per tick
// the phase accumulator points to the current sample in our wavetable uint32_t ulPhaseAccumulator = 0; // the phase increment controls the rate at which we move through the wave table // higher values = higher frequencies volatile uint32_t ulPhaseIncrement = 0; // 32 bit phase increment, see below
// full waveform = 0 to SAMPLES_PER_CYCLE // Phase Increment for 1 Hz =(SAMPLES_PER_CYCLE_FIXEDPOINT/SAMPLE_RATE) = 1Hz // Phase Increment for frequency F = (SAMPLES_PER_CYCLE/SAMPLE_RATE)*F #define SAMPLE_RATE 44100.0 #define SAMPLES_PER_CYCLE 600 #define SAMPLES_PER_CYCLE_FIXEDPOINT (SAMPLES_PER_CYCLE<<20) #define TICKS_PER_CYCLE (float)((float)SAMPLES_PER_CYCLE_FIXEDPOINT/(float)SAMPLE_RATE)
// to represent 600 we need 10 bits // Our fixed point format will be 10P22 = 32 bits
// We have 521K flash and 96K ram to play with
// Create a table to hold the phase increments we need to generate midi note frequencies at our 44.1Khz sample rate #define MIDI_NOTES 128 uint32_t nMidiPhaseIncrement[MIDI_NOTES];
// fill the note table with the phase increment values we require to generate the note void createNoteTable(float fSampleRate) { for(uint32_t unMidiNote = 0;unMidiNote < MIDI_NOTES;unMidiNote++) { // Correct calculation for frequency Serial.print(unMidiNote); Serial.print(" "); float fFrequency = ((pow(2.0,(unMidiNote-69.0)/12.0)) * 440.0); Serial.print(fFrequency); Serial.print(" ");
// Create a table to hold pre computed sinewave, the table has a resolution of 600 samples #define WAVE_SAMPLES 600 // default int is 32 bit, in most cases its best to use uint32_t but for large arrays its better to use smaller // data types if possible, here we are storing 12 bit samples in 16 bit ints uint16_t nSineTable[WAVE_SAMPLES];
// create the individual samples for our sinewave table void createSineTable() { for(uint32_t nIndex = 0;nIndex < WAVE_SAMPLES;nIndex++) { // normalised to 12 bit range 0-4095 nSineTable[nIndex] = (uint16_t) (((1+sin(((2.0*PI)/WAVE_SAMPLES)*nIndex))*4095.0)/2); Serial.println(nSineTable[nIndex]); } }
void setup() { Serial.begin(9600);
createNoteTable(SAMPLE_RATE); createSineTable();
/* turn on the timer clock in the power management controller */ pmc_set_writeprotect(false); pmc_enable_periph_clk(ID_TC4);
// enable timer interrupts on the timer TC1->TC_CHANNEL[1].TC_IER=TC_IER_CPCS; TC1->TC_CHANNEL[1].TC_IDR=~TC_IER_CPCS;
/* Enable the interrupt in the nested vector interrupt controller */ /* TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number (=(1*3)+1) for timer1 channel1 */ NVIC_EnableIRQ(TC4_IRQn);
// this is a cheat - enable the DAC analogWrite(DAC0,0); }
void loop() { // read analog input 0 drop the range from 0-1024 to 0-127 with a right shift 3 places, // then look up the phaseIncrement required to generate the note in our nMidiPhaseIncrement table uint32_t ulInput = analogRead(0); ulPhaseIncrement = nMidiPhaseIncrement[ulInput>>3]; }
void TC4_Handler() { // We need to get the status to clear it and allow the interrupt to fire again TC_GetStatus(TC1, 1);
ulPhaseAccumulator += ulPhaseIncrement; // 32 bit phase increment, see below
// if the phase accumulator over flows - we have been through one cycle at the current pitch, // now we need to reset the grains ready for our next cycle if(ulPhaseAccumulator > SAMPLES_PER_CYCLE_FIXEDPOINT) { // DB 02/Jan/2012 - carry the remainder of the phase accumulator ulPhaseAccumulator -= SAMPLES_PER_CYCLE_FIXEDPOINT; }
// get the current sample uint32_t ulOutput = nSineTable[ulPhaseAccumulator>>20];
// we cheated and user analogWrite to enable the dac, but here we want to be fast so // write directly dacc_write_conversion_data(DACC_INTERFACE, ulOutput); }
The first RCArduino project for the Arduino Due is the Quick And Dirty Synth. The synth is a simple showcase for audio output through the DAC running at the Audio CD sample rate of 44.1Khz.
The showcase is based on the simplest synth engine I could create - 3 counters counting up at a rate controlled by three analog inputs.
It might sound simple but its a surprisingly rich sounding synth engine, here is how it works -
Two of the counters generate ramp waveforms - think about it, they are counting up from 0 to 4095, when they overflow, they go back to 0 and start the count again.
Example Ramp Output -
Example Ramp At A Higher Frequency -
These ramp waveforms are summed together at the output to generate a more complex waveform - two ramp waves of independent frequency superimposed on each other.
Example - Two triangle at frequency F and 3F mixed together to create a new output waveform.
The third ramp waveform is used to control the pitch. It is not mixed with the output waveforms instead it achieves pitch control by resetting the first two waveforms.
Output waveform reset at frequency determined by third counter - notice that the counter is not directly present in the output but controls the repetition/synchronization of the output which in turn creates the pitch.
To hear the technique in action connect three potentiometers to your Arduino Due on analog inputs 0,1,2 and an audio amplifier to DAC0.
Caution : The SAM3X8E microcontroller at the heart of the Arduino Due is less able to sink and source current than the AVR family of chips used in the 8-Bit Arduinos. I would suggest using a series resistor of around 500 Ohms when connecting the Arduino Due DAC to an external device. See this thread on the Arduino forum for more information - http://arduino.cc/forum/index.php/topic,139733.0.html
// RCArduino Quick And Dirty Synth for Arduino Due// RCArduino Quick And Dirty Synth by RCArduino is licensed under a Creative Commons Attribution 3.0 Unported License. // Based on a work at rcarduino.blogspot.com. // For helpful background information on Arduino Due Timer Configuration, refer to the following link // thanks to Sebastian Vik // http://arduino.cc/forum/index.php?action=post;topic=130423.15;num_replies=20 // The following folders within the arduino install provide access to source code and documentation // its very low level, the documentation is next to useless, but it might help someone // C:\arduino-1.5.1r2\hardware\arduino\sam\system\libsam\source // C:/arduino-1.5.1r2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/html/tc1.html // C:/arduino-1.5.1r2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/html/SAM3XA.html // C:\arduino-1.5.1r2\hardware\arduino\sam\system\CMSIS\CMSIS\Documentation // These are the clock frequencies available to the timers /2,/8,/32,/128 // 84Mhz/2 = 42.000 MHz // 84Mhz/8 = 10.500 MHz // 84Mhz/32 = 2.625 MHz // 84Mhz/128 = 656.250 KHz // // 44.1Khz = CD Sample Rate // Lets aim for as close to the CD Sample Rate as we can get - // // 42Mhz/44.1Khz = 952.38 // 10.5Mhz/44.1Khz = 238.09 // best fit divide by 8 = TIMER_CLOCK2 and 238 ticks per sample // 2.625Hmz/44.1Khz = 59.5 // 656Khz/44.1Khz = 14.88 // 84Mhz/44.1Khz = 1904 instructions per tick // These variables represent our synth engine, its similar to a vastly simplified Auduino and has a similar sound. // Phase Accumulator controls the main pitch, grain1 and grain two phase accumulator control the pitch for the two grains // The grainPhaseAccumulators are essentially simple counters, when you count up and overflow back to 0, you generate a ramp waveform // we generate two and mix them together. uint32_t ulPhaseAccumulator = 0; // 32 bit phase accumulator, if we shift >> 20 bits we get a 12 bit value for our output ADC volatile uint32_t ulPhaseIncrement = 0; // 32 bit phase increment, see below uint32_t ulGrain1PhaseAccumulator = 0; // 32 bit phase accumulator, if we shift >> 20 bits we get a 12 bit value for our output ADC volatile uint32_t ulGrain1PhaseIncrement = 0; // 32 bit phase increment, see below uint32_t ulGrain2PhaseAccumulator = 0; // 32 bit phase accumulator, if we shift >> 20 bits we get a 12 bit value for our output ADC volatile uint32_t ulGrain2PhaseIncrement = 0; // 32 bit phase increment, see below // full waveform = 0 to 4294967296 // Phase Increment for 1 Hz =(4294967296/44100) = 1Hz // Phase Increment for frequency F = (4294967296/44100)*F #define SAMPLE_RATE 44100.0 #define TICKS_PER_CYCLE (4294967296.0/SAMPLE_RATE) // Create a table to hold the phase increments we need to generate midi note frequencies at our 44.1Khz sample rate #define MIDI_NOTES 128 uint32_t nMidiPhaseIncrement[MIDI_NOTES]; void createNoteTable(float fSampleRate) { for(uint32_t unMidiNote = 0;unMidiNote < MIDI_NOTES;unMidiNote++) { // Correct calculation for frequency Serial.print(unMidiNote); Serial.print(" "); float fFrequency = ((pow(2.0,(unMidiNote-69.0)/12.0)) * 440.0); Serial.print(fFrequency); Serial.print(" "); nMidiPhaseIncrement[unMidiNote] = fFrequency*TICKS_PER_CYCLE; Serial.println(nMidiPhaseIncrement[unMidiNote]); } } void setup() { Serial.begin(9600); createNoteTable(SAMPLE_RATE); /* turn on the timer clock in the power management controller */ pmc_set_writeprotect(false); pmc_enable_periph_clk(ID_TC4); /* we want wavesel 01 with RC */ TC_Configure(/* clock */TC1,/* channel */1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK2); TC_SetRC(TC1, 1, 238); // sets <> 44.1 Khz interrupt rate TC_Start(TC1, 1); // enable timer interrupts on the timer TC1->TC_CHANNEL[1].TC_IER=TC_IER_CPCS; TC1->TC_CHANNEL[1].TC_IDR=~TC_IER_CPCS; /* Enable the interrupt in the nested vector interrupt controller */ /* TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number (=(1*3)+1) for timer1 channel1 */ NVIC_EnableIRQ(TC4_IRQn); // this is a cheat - enable the DAC analogWrite(DAC0,0); } void loop() { // read analog input 0 drop the range from 0-1024 to 0-127 with a right shift 3 places, // then look up the phaseIncrement required to generate the note in our nMidiPhaseIncrement table uint32_t ulInput = analogRead(0); ulPhaseIncrement = nMidiPhaseIncrement[ulInput>>3]; // set the phase increment for grains 1 and 2, we do not want a precise frequency for these, // they set the character of the note rather than pitch ulGrain1PhaseIncrement = analogRead(1)<<18; ulGrain2PhaseIncrement = analogRead(2)<<18; } void TC4_Handler() { // We need to get the status to clear it and allow the interrupt to fire again TC_GetStatus(TC1, 1); ulPhaseAccumulator += ulPhaseIncrement; // 32 bit phase increment, see below // if the phase accumulator over flows - we have been through one cycle at the current pitch, // now we need to reset the grains ready for our next cycle if(ulPhaseAccumulator < ulPhaseIncrement) { ulGrain1PhaseAccumulator = 0; ulGrain2PhaseAccumulator = 0; } else { // increment the grains ulGrain1PhaseAccumulator += ulGrain1PhaseIncrement; ulGrain2PhaseAccumulator += ulGrain2PhaseIncrement; } // mix the grains by adding them together - to the result of adding these two 32bit values into the 12 bit ADC Output // we shift both values right 21 places to sum two 11 bit values giving a 12 bit result. uint32_t ulOutput = (ulGrain1PhaseAccumulator>>21)+(ulGrain2PhaseAccumulator>>21); // we cheated and user analogWrite to enable the dac, but here we want to be fast so // write directly dacc_write_conversion_data(DACC_INTERFACE, ulOutput); }
The latest version of the lap timer includes two new features -
1) External Audio - There is now an option to use a very simple amplifier circuit which is small enough to fit inside most project boxes. This will give you a lot more volume when you are using the system outdoors.
2) Countdown - With countdown switched on, the lap timer will beep to count down the last few seconds of the current lap time - it adds a little extra pressure - to beat the lap you have to beat the beeps.
Before we get to the build, here are some user pictures of current builds, if you have pictures of your own build I am happy to share them.
ALLY - Transponder mounted in touring car chassis
Howie314 - Using an LCD Shield for a fast build
This build by Arduino Forum User Howie314 uses a LCD Keypad Shield, it keeps the size down and means there is very little soldering required to build the project. Many LCD Shields are available and all work in a similar manner meaning that you can get a lap timer up and running quickly with almost no soldering required.
If someone would design an LCD Shield with a rotary encoder as well as buttons we could have the perfect lap timer interface.
Howie314 is one of the first using the most recent version of the code with the new options of external audio and countdown. Additional options that I am considering in the near future are 1) Window magnet transponders and 2) AIM Transponders. See the end of post for more details.
Howie also did some work on the user interface as the LCD Shield uses a single analog pin to read multiple buttons. It makes sense to offer a version of the build along based on these readily available shields so I will be taking a look at Howies modifications with a plan to offer a single version of the project which can be run on standalone builds or LCD Shields - props to Howie.
ALLY's Build and Enclosure
Arduino forum user ALLY has used a standalone LCD and soldered his own buttons. This gives a bit more flexibility in the type of display and enclosures you can use, but also requires more work to build.
ALLY Transponder Mount
Here is a shot of ALLY's transponder mounted in his RC Touring car. Notice that the IR Emitter is mounted on a small post, this is a nice solution as the LED is well away from any areas that would impact in a crash.
If you want to try something similar make sure to keep the wires to the LED short. I tried wires of about 15 cm which unfortunately acted as an antenna causing radio interference.
ALLY's Carbon
ALLY has used a large carbon fibre effect enclosure, there is lots of room to add indicator LEDs, or one of my favorite features - a small amplifier for optional external speakers.
Lap Timer Piezo Buzzer and External Audio One of the most useful features of the lap timer is instant audio feedback, this is particularly important with RC Car racing and Kart racing where corners are fractions of a second apart and checking a display is not an option.
The feedback is deliberately simple but totally effective - one beep to confirm a lap and two beeps if its a new best lap.
To add the audio features we have two options
1) A Piezo Buzzer 2) An External Speaker
I have found it useful to have both options in my build, using the quieter peizo for testing near my home and the external audio to overcome the additional noise at the track, the latest code includes a menu to switch between these two output options.
Piezo With Transistor Driver To get as much volume as possible from the buzzer we can drive it using the battery voltage rather than the regulated 5 volts from the Arduino. To switch the higher battery voltage from an Arduino pin we use a NPN transistor connected between the piezo (the load) and ground. This arrangement allows us to switch the higher voltages and currents through the Peizo and can also be applied to other loads such as high powered LEDs, relays and other components you may want to use in your future projects.
External Audio Through A Speaker The Peizo option can be used without adding the external audio option, but if you want more volume in your build read on -
In order to power a speaker we need a few more components to drive the extra current, one easy to use option is the LM386 Audio Amp chip. This is used extensively throughout the RCArduino blog in a variety of audio projects, full details and videos of the chip in action cab be found here -
The LM386 Audio Amp can be build to a very compact form factor to fit your chosen enclosure -
Parts List 1 x LM386-N4 2 x 100uf Electrolytic Capacitors 1 x 0.1uf Capacitor 1 x 100Ohm Resistor 1 x 10K Potentiometer1 x Phono socket for speaker connection
Schematic - Note the audio output is from analog pin 5, for simplicity only the audio components are shown, as there
For the latest code, contact me Duane B though the Arduino forum.
Future Developments Some common requests that I will be adding in the near future are -
1) Support for individual transponders - this will take some experimenting, but it should be possible to create a simple transponder scheme which will allow several lap timers to be used simultaneously with each car paired to a specific lap timer.
2) Support for magnetic transponders - I am told that many kart tracks use a magnetic strip under the track to activate transponders which are similar to magnetic window sensors, I hope to work with an Arduino forum user to add support for this option, it should be simple, but I have nowhere to test the system locally.
Inspired by Howie314's use of an off the shelf LCD Shield I will also add support for a build along based on this quick start option.
If you have a build you want to share, please submit some pictures,
This is the first in a series of posts introducing simple audio effects that can be used in micro controller projects.
Next Week - Bit Crushing effects
The Delay effect is one of the simplest and most effective enhancements we can add to our audio projects.
The delay effect works by recording the output as it is being generated and then mixing this sound back in with itself - after a delay. The result should be familiar to anyone who has every played an electric guitar through an amp with reverb.
In the case of the Auduino synthesizer the result is a mild echo effect and slightly smoother, more metallic sound - the effect can be turned on or off through a push button in the code provided below.
How do we create the delay effect Delay is very simple to add in a microcontroller, all we need is a block of memory to record the output in.
The larger the block of memory, the longer the delay we can record and the deeper the effect.
In this case we are using a 1K block of memory in the array named sDelayBuffer -
// Duane B // rcarduino.blogspot.com // 15/11/2012 // Very simple ring buffer delay // we record the output in this array // and then mix it back in with the output as the buffer wraps around // can be switched on and off by a button on DELAY_BUTTON #define MAX_DELAY 1024 unsigned char sDelayBuffer[MAX_DELAY]; unsigned int nDelayCounter = 0; unsigned char bDelay;
The other modification is inside the interrupt service routine which generates the Audiuno output, essentially what we are doing is adding the sound we recorded 1/8th of a second ago on top of the current output value -
// Duane B // rcarduino.blogspot.com // 15/11/2012 // add a button to set bDelay true or false to turn delay on and off if(bDelay) { // Output to PWM (this is faster than using analogWrite) // Here we add the delay buffer to the output value, this produces // an subtle echo effect, the delay buffer is effectivley replaying the sound from // 1/8th of a second ago.
LED_PORT |= 1 << LED_BIT; // Faster than using digitalWrite PWM_VALUE = (output + (sDelayBuffer[nDelayCounter]))>>1;
// add the new output to the buffer so we can use it when the buffer next wraps around sDelayBuffer[nDelayCounter] = PWM_VALUE; nDelayCounter++; if(nDelayCounter == MAX_DELAY) { nDelayCounter = 0; } } else { LED_PORT &= ~(1 << LED_BIT); // Faster than using digitalWrite
PWM_VALUE = output; }
We test whether delay is enabled, if it is we calculate the output value by adding the initial output to the earlier recorded output from our delay buffer. After outputting this combined value we record it in the delay buffer replacing the value we just used. Over time, the code cycles through the delay buffer over and over again, mixing the current output with a sample from 1/8th of a second back - a bit like playing your instrument in a large hall where the distinct sound is the result of the current sound being constantly mixed with its echo.
Thats all there is to generating delay in a micro controller synth engine - exactly the same code is used to create the delay effect in the RCArduino Five Dollar Synthesizer.
The RCArduino Five Dollar Synthesizer is another audio project enhanced with this delay effect -
Further Development The amount of delay we can provide is determined to the memory we use to record the samples. In the Auduino we are using 1K which at an 8K play back rate gives us 125ms of delay. This can be increased by bit crushing the samples - using 4 bits per sample we get 250ms, 2 bits gets us half a second, with 1 bit we can get a whole second. Unfortunately initial experiments suggest that the effect is largely lost when applying these techniques, its a bit like shouting into a cave and getting a different echo back - your ears just don't buy it.
Auduino with delay is a very slight modification by Duane B (rcarduino) to the original work of Peter Knight.
Notes - This code also include the volatile fix which allows the Auduino to work correctly in Arduino 1.0 and later - Remember to use a pull up or pull down resistor if you are not using a push button or switch for the delay button or if your more comfortable modifying the code, replace the button code with true or false. - LED 13, now indicates whether delay is on or off.
// Auduino, the Lo-Fi granular synthesiser // // by Peter Knight, Tinker.it http://tinker.it // // Help: http://code.google.com/p/tinkerit/wiki/Auduino // More help: http://groups.google.com/group/auduino // // Analog in 0: Grain 1 pitch // Analog in 1: Grain 2 decay // Analog in 2: Grain 1 decay // Analog in 3: Grain 2 pitch // Analog in 4: Grain repetition frequency // // Digital 3: Audio out (Digital 11 on ATmega8) // // Changelog: // 19 Nov 2008: Added support for ATmega8 boards // 21 Mar 2009: Added support for ATmega328 boards // 7 Apr 2009: Fixed interrupt vector for ATmega328 boards // 8 Apr 2009: Added support for ATmega1280 boards (Arduino Mega)
// Changing these will also requires rewriting audioOn()
#if defined(__AVR_ATmega8__) // // On old ATmega8 boards. // Output is on pin 11 // #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 5 #define PWM_PIN 11 #define PWM_VALUE OCR2 #define PWM_INTERRUPT TIMER2_OVF_vect
#elif defined(__AVR_ATmega1280__) // // On the Arduino Mega // Output is on pin 3 // #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 7 #define PWM_PIN 3 #define PWM_VALUE OCR3C #define PWM_INTERRUPT TIMER3_OVF_vect #else // // For modern ATmega168 and ATmega328 boards // Output is on pin 3 // #define PWM_PIN 3 #define PWM_VALUE OCR2B #define LED_PIN 13 #define LED_PORT PORTB #define LED_BIT 5 #define PWM_INTERRUPT TIMER2_OVF_vect #endif
// Duane B // rcarduino.blogspot.com // 15/11/2012 // Very simple ring buffer delay // we record the output in this array // and then mix it back in with the output as the buffer wraps around // can be switched on and off by a button on DELAY_BUTTON #define MAX_DELAY 1024 unsigned char sDelayBuffer[MAX_DELAY]; unsigned int nDelayCounter = 0; unsigned char bDelay;
// set pin mode and turn on pull up so that default mode // is PENTATONIC, pull the pin low to switch to smooth pinMode(SMOOTH_PIN,INPUT); digitalWrite(SMOOTH_PIN,HIGH); }
void loop() { // The loop is pretty simple - it just updates the parameters for the oscillators. // // Avoid using any functions that make extensive use of interrupts, or turn interrupts off. // They will cause clicks and poops in the audio.
// defaults to pentatonic stepped tones, pull pin low for smooth frequency without distinct tones // syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4;
// updated 29/01/2013 // pull the DELAY_BUTTON pin high for delay, low for no delay // use either a pull up/pull down resistor // or a pull up resistor with a toggle switch between the pin and ground bDelay = digitalRead(DELAY_BUTTON);
// Stepped mapping to MIDI notes: C, Db, D, Eb, E, F... //syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL));
syncPhaseAcc += syncPhaseInc; if (syncPhaseAcc < syncPhaseInc) { // Time to start the next grain grainPhaseAcc = 0; grainAmp = 0x7fff; grain2PhaseAcc = 0; grain2Amp = 0x7fff; // LED_PORT ^= 1 << LED_BIT; // Faster than using digitalWrite }
// Increment the phase of the grain oscillators grainPhaseAcc += grainPhaseInc; grain2PhaseAcc += grain2PhaseInc;
// Convert phase into a triangle wave value = (grainPhaseAcc >> 7) & 0xff; if (grainPhaseAcc & 0x8000) value = ~value; // Multiply by current grain amplitude to get sample output = value * (grainAmp >> 8);
// Repeat for second grain value = (grain2PhaseAcc >> 7) & 0xff; if (grain2PhaseAcc & 0x8000) value = ~value; output += value * (grain2Amp >> 8);
// Make the grain amplitudes decay by a factor every sample (exponential decay) grainAmp -= (grainAmp >> 8) * grainDecay; grain2Amp -= (grain2Amp >> 8) * grain2Decay;
// Scale output to the available range, clipping if necessary output >>= 9; if (output > 255) output = 255;
// Duane B // rcarduino.blogspot.com // 15/11/2012 // add a button to set bDelay true or false to turn delay on and off if(bDelay) { // Output to PWM (this is faster than using analogWrite) // Here we add the delay buffer to the output value, this produces // an subtle echo effect, the delay buffer is effectivley replaying the sound from // 1/8th of a second ago.
LED_PORT |= 1 << LED_BIT; // Faster than using digitalWrite PWM_VALUE = (output + (sDelayBuffer[nDelayCounter]))>>1;
// add the new output to the buffer so we can use it when the buffer next wraps around sDelayBuffer[nDelayCounter] = PWM_VALUE; nDelayCounter++; if(nDelayCounter == MAX_DELAY) { nDelayCounter = 0; } } else { LED_PORT &= ~(1 << LED_BIT); // Faster than using digitalWrite