I found a neat fact about the xbee communication modules. Having too high a send rate can botch the accuracy of your transmission (although you should test it yourself, may be just the way my software works). I had two xbees transmitting strings at 57600 baud and quite a bit of noise was present. Turning it down to 4800 resulted in perfect transmission. This is a bit slow, but I will be determining the max transmission rate that doesn't give me noise.
Showing posts with label Electronics. Show all posts
Showing posts with label Electronics. Show all posts
Monday, February 27, 2012
A note about xbee serial communication accuracy
Monday, January 9, 2012
Simple Arduino double-to-string function
Because Arduino language is rather low-level (C-based), such a function is not so straightforward or available as one might think.
Just input the double and the number of decimal places desired. The function:
//Rounds down (via intermediary integer conversion truncation)String doubleToString(double input,int decimalPlaces){if(decimalPlaces!=0){String string = String((int)(input*pow(10,decimalPlaces)));if(abs(input)<1){if(input>0)string = "0"+string;else if(input<0)string = string.substring(0,1)+"0"+string.substring(1);}return string.substring(0,string.length()-decimalPlaces)+"."+string.substring(string.length()-decimalPlaces);}else {return String((int)input);}}
Sunday, January 1, 2012
Changing Arduino Output PWM frequency
Here is a very useful post about changing the output PWM frequency on the Arduino (post #5). It was useful for me because I wanted a frequency over 20 kHz for DC motor control (>20 kHz is above the hearing range).
Getting PWM frequency and duty cycle with Arduino
Here is a simple code that reads a PWM signal and calculates its frequency and duty cycle in real time. The lack of an oscilloscope motivated this code.
The star of the show is the built-in 'pulseIn()' function, which gives the length of a low or high pulse. With these two measurements the calculation of duty cycle and frequency of the PWM signal is straightforward.
I have two different versions, simple and slightly longer.
The slightly longer code takes a user-specified time delay over which the maximum length is taken to help prevent truncation of a signal. For example, if an input pulse of 10 ms has already been running for 2 ms and the pulseIn function is called, it may return 8 ms. I am actually not sure if this is the way pulseIn works, but I tried both. With the default Arduino PWM frequency of approximately 490 Hz (http://arduino.cc/en/Reference/analogWrite), the slightly longer code returns 490 +/- 3 Hz, while the simple code returns 500 +/- 3 Hz. I tested this over low and high duty cycles. So it appears the slightly longer code has slightly better accuracy.
Simple code:
//Reads a PWM signal's duty cycle and frequency.#define READ_PIN 13#define PWM_OUTPUT 10static double duty;static double freq;static long highTime = 0;static long lowTime = 0;static long tempPulse;void setup(){pinMode(READ_PIN,INPUT);Serial.begin(9600);analogWrite(PWM_OUTPUT,230);}void loop(){readPWM(READ_PIN);Serial.println(freq);Serial.println(duty);}//Takes in reading pins and outputs pwm frequency and duty cycle.void readPWM(int readPin){highTime = 0;lowTime = 0;tempPulse = pulseIn(readPin,HIGH);if(tempPulse>highTime){highTime = tempPulse;}tempPulse = pulseIn(readPin,LOW);if(tempPulse>lowTime){lowTime = tempPulse;}freq = ((double) 1000000)/(double (lowTime+highTime));duty = (100*(highTime/(double (lowTime+highTime))));}
Slightly longer code:
//Reads a PWM signal's duty cycle and frequency.#define READ_PIN 13#define READ_DELAY 100#define PWM_OUTPUT 10static double duty;static double freq;static long highTime = 0;static long lowTime = 0;static long tempPulse;static long lastSeen;void setup(){pinMode(READ_PIN,INPUT);Serial.begin(9600);analogWrite(PWM_OUTPUT,230);}void loop(){readPWM(READ_PIN);Serial.println(freq);Serial.println(duty);}//Takes in reading pins and outputs pwm frequency and duty cycle.void readPWM(int readPin){highTime = 0;lowTime = 0;lastSeen = millis();while((millis()-lastSeen)<read_delay){< div="">tempPulse = pulseIn(readPin,HIGH);if(tempPulse>highTime){highTime = tempPulse;}}lastSeen = millis();while((millis()-lastSeen)<read_delay){< div="">tempPulse = pulseIn(readPin,LOW);if(tempPulse>lowTime){lowTime = tempPulse;}}freq = ((double) 1000000)/(double (lowTime+highTime));duty = (100*(highTime/(double (lowTime+highTime))));}
Monday, December 26, 2011
A note about Arduino serial
When reading strings through Arduino Serial, I usually make a string variable and concatenate onto it as each Serial.read() character comes in.
Suppose Serial communication occurs one string at a time. If you collect each string with a while(Serial.available()) loop, a problem arises. The first three characters, if the string is longer than three characters, is truncated. Why?
I fixed this problem by adding a small delay (~5 ms, could be less, I did not play with it and it depends on the amount of code between the delay and character reception) after every three characters. It appears Serial communication is done in packets of three, with a small delay in between, making Serial.available() false every three characters for a brief period of time.
Simple LCD Character Display and Arduino Uno
It is easy to hook up and use a character display to an Arduino. Turns out, a specific driver that comes with LCD displays that is very common is the HD44780 driver and there is an Arduino library specifically for it: LiquidCrystal.
It is very easy to use in software and in hardware. The following picture is from the documentation for my screen that I bought on eBay (JHD-204A), showing the pin configuration and power characteristics.
The official Arduino documentation on LiquidCrystal is all you need and it can be found at: http://www.arduino.cc/en/Tutorial/LiquidCrystal. Actually no other parts than connecting wires are required. The potentiometer mentioned in the above reference is just for contrast adjustment.

I personally prefer wire wrapping to soldering when I can, mainly because I am not good at soldering and I do not like toxic fumes. Here I had to solder the header pins onto the LCD display pins, but after that wire wrapping all the way. It was fun. Wire wrapping wire and header pins are an awesome combination.
One inevitably runs into errors when installing the driver for the Arduino Uno on my Windows 7 PC. I thought it was just an error on my computer specifically but apparently it is the norm for now. The solution is found on the official Arduino website here. They acknowledge that the error occurs no matter what, "despite [Window's] best efforts".
It is very easy to use in software and in hardware. The following picture is from the documentation for my screen that I bought on eBay (JHD-204A), showing the pin configuration and power characteristics.
The official Arduino documentation on LiquidCrystal is all you need and it can be found at: http://www.arduino.cc/en/Tutorial/LiquidCrystal. Actually no other parts than connecting wires are required. The potentiometer mentioned in the above reference is just for contrast adjustment.

I personally prefer wire wrapping to soldering when I can, mainly because I am not good at soldering and I do not like toxic fumes. Here I had to solder the header pins onto the LCD display pins, but after that wire wrapping all the way. It was fun. Wire wrapping wire and header pins are an awesome combination.
One inevitably runs into errors when installing the driver for the Arduino Uno on my Windows 7 PC. I thought it was just an error on my computer specifically but apparently it is the norm for now. The solution is found on the official Arduino website here. They acknowledge that the error occurs no matter what, "despite [Window's] best efforts".
Tuesday, December 20, 2011
Using an XBOX controller for non-XBOX things
If you cut the cord on the wired controller there are 4 wires: red and black for power, and green and white for data signals. So apparently if you simply hookup up the power and leave the data wires floating you cannot immediately utilize some of its basic parts, such as the potentiometers. Now that I think about it, I guess I should have tried to ground the signal wires to see if the pots and stuff work, instead of leaving the signal wires floating.
What I did was I found the potentiometer solder points and soldered directly to those, providing power and using the signal output. There are potentiometers on the left and right triggers, and 4 total on both joysticks. All of the potentiometers have three grouped pins in a row, and the middle is the signal. I supplied power from the Arduino to the outer pins and hooked up the middle signal to an Arduino input pin.
Supplying the main power to the controller does let the buttons work, however. These are pressure-sensitive touch pads that go low when pressure is applied, so they are pulled up. There is a space next to these pads available for a wire to be soldered. I had to scratch off the lacquer with a razor blade for the solder to stick.
The left and right "bumper" buttons are simple momentary push buttons. No confusion there, just use a DMM to find where you would want to connect.
Using the XBOX controller for its basic components is pretty straightforward, but soldering may be a little trouble because one of the potentiometer's pins are underneath the trigger assemblies.
What I did was I found the potentiometer solder points and soldered directly to those, providing power and using the signal output. There are potentiometers on the left and right triggers, and 4 total on both joysticks. All of the potentiometers have three grouped pins in a row, and the middle is the signal. I supplied power from the Arduino to the outer pins and hooked up the middle signal to an Arduino input pin.
Supplying the main power to the controller does let the buttons work, however. These are pressure-sensitive touch pads that go low when pressure is applied, so they are pulled up. There is a space next to these pads available for a wire to be soldered. I had to scratch off the lacquer with a razor blade for the solder to stick.
The left and right "bumper" buttons are simple momentary push buttons. No confusion there, just use a DMM to find where you would want to connect.
Using the XBOX controller for its basic components is pretty straightforward, but soldering may be a little trouble because one of the potentiometer's pins are underneath the trigger assemblies.
Sunday, December 11, 2011
Omniwheel vehicle speed consistency
I am currently working on an omniwheel drive vehicle with a rather... unique control system. I am estimating it will be done by January. It involves wireless Arduino serial communication via XBEE, water-jet cut custom aluminum parts, h-bridge PWM DC motor controllers, and mutilated IKEA furniture, haha.
This post is about some speed control considerations on a vector-based control algorithm. Depending on what direction you going in, the speed will differ, because the wheel orientations change. Obviously, for a good human-operated vehicle this speed difference must not be too great. If the direction is aligned with the wheel's normal rolling direction, the speed will be at a maximum. The wheel most aligned with the direction of movement will dictate the top speed of the vehicle. All other wheels will provide moment balance and extra torque.
Below are some plots showing the normalized (meaning min is 0, max is 1) speed vs. direction, in radians. The different plots are for different numbers of motors/wheels installed on the vehicle.


As you can see, for 3 and 4 motors the speed difference is quite significant. For three motors, the lowest speed depending on the direction of vehicle movement can be about 14% lower than the normal speed (in the graphs it would be 1). Four motors fares even worse, which can be surprising at first glance. Four motors give a minimum speed around 30% lower than the normal speed.
A few interesting patterns can be seen here. The number of dips equals the number of wheels/motors if the number of wheels/motors is odd, and twice the number of wheels/motors if the number is even. Also, locally the speed difference is minimized if the number of wheels/motors is odd. 5 wheels/motors is a reasonable number that gives an acceptable speed difference, <5%. Of course, all of this can be fixed to give virtually no speed difference consistent speed in the controller's software.
This was a very interesting investigation! The code to generate the plots above was written in MATLAB and is included below (MATLAB is not free, but SCILAB is! And it is very similar! It will probably run the code with little or no modification!):
%FPS bot testing
offsetAngles = [0 0 0 0 0];%Just the difference in angle between frontmost motor and forward direction.
motorCounts = [3 4 5 6 7];
motorAngles = 2*pi./motorCounts;
for j = 1:numel(motorCounts)
mvec = zeros(motorCounts(j),2);
for k = 1:motorCounts(j)
mvec(k,:) = [cos(offsetAngles(j)+(k-1)*motorAngles(j)) -sin(offsetAngles(j)+(k-1)*motorAngles(j))];
end
resolution = 10000;
mag = zeros(resolution,1);
theta = linspace(0,2*pi,resolution)';
directionVector = zeros(resolution,2);
directionVector(:,1) = cos(theta);
directionVector(:,2) = sin(theta);
currentMag = zeros(motorCounts(j),1);
for k=1:resolution
for i=1:motorCounts(j)
currentMag(i) = abs(mvec(i,:)*directionVector(k,:)');
end
mag(k) = max(currentMag);
end
figure;
plot(theta,mag);
ylabel('Normalized speed magnitude');
xlabel('Direction (radians)');
title(['Normalized speed magnitude vs. direction, ' num2str(motorCounts(j)) ' motors']);
end
Friday, February 18, 2011
Easy pulse width modulation (PWM) with PICs to control brushless motor ESCs, servos, and other things
Today I present a brushless motor controlled by an electronic speed controller (ESC) and a PIC microcontroller. The PIC microcontroller performs the function of a R/C reciever by taking in a signal (here generated by a pot) and outputting the appropriate signal (here a PWM square wave) for the ESC to use to determine the rotation speed of the brushless motor.
Simple background: this brushless motor was bought at Hobbyking for less than $20 and can put out something like 600W. Although that is pretty high, it is not to be used for wheeled vehicles, which have highly dynamic loads and high current spikes. These brushless motors are intended for R/C planes' propellers, which have very steady and predictable loads. I have it connected to an electronic speed controller (ESC), which I bought, that dishes out the current at the right times to the brushless motor based on a simple pulse-width modulated signal it receives. The ESC is necessary because brushless motors operate in a totally different way from DC motors. Brushless motors are 3-phase, and as a result have three wires that need to be connected to a power source. Where you could just apply a steady voltage to DC motor and the commutator takes care of current alternation, brushless motors require more deliberate control. This control is not the subject of this post. I use a PIC to easily create and control a PWM signal using PIC's onboard timers for the ESC to interpret. So this post is basically about programming PICs specifically, to generate a PWM signal, of any flavor or kind if you like.
The signal generated here is a standard servo signal of 50 Hz (period 20 ms) with a square wave pulse that ranges in width from 1 to 2 ms.
What the software does: The software uses PIC's Timer1 and Timer0 and interrupts to make the signal, whose length varies (of the signal that can vary from 1 to 2 ms, the 50 Hz cycle is fixed) based on an analog input from a potentiometer. The PIC is constantly polling and converting the signal from a potentiometer to a value from 0 to 255, while in the background the Timers are counting down. Timer1 has a fixed time length (of course defined by me) of 20 ms, and Timer0 has a length from 1 ms to 2 ms, determined by the ADC. Timer1 dictates when the pulse, whose length is determined by Timer0, occurs. Notice that every cycle there is at least a period of 18 ms where the signal is low. The timers counting down and the potentiometer signal polling occurs simultaneously. When the Timers countdown, interrupts stop the PIC from whatever it was doing, executes a specified procedure, and then the PIC resumes whatever it was doing. In this case, the interrupts would be used to stop and start signals.
The c code:
The c code is turned into a hex file via MPLAB and I program the microcontroller with the PICKit 2. The microcontroller is a PIC16F690. Analog-to-digital conversion signal (from potentiometer) input is at A0 and ouput PWM signal is at C0. There is an input at C1 for a momentary pushbutton, which needs to be held in order for an input at C1 to be high. This is for safety, as I want it to be easy to turn the motor off by just letting go of the pushbutton. So no matter how high a speed the potentiometer is set to, nothing will happen unless the pushbutton is held.
Since the ADC gives values from 0 to 255, the resolution, or how many speed levels, you have is limited to 256. So it does not matter whether you use Timer0 or Timer1 for determining the signal length (Timer1 has a much higher temporal resolution). I use Timer0 because it probably takes less time for the PIC to do the arithmetic of converting the ADC value to the appropriate number. Timer0 uses 8 bits to define counter number (up to 255 in decimal), while Timer1 uses 16 bits (up to 65535 in decimal).
Remember, when using PICs as standalone portable devices, two things should be done: there should be a capacitor across the power supply pins (here, .1 microFarad, and yes this is absolutely necessary, else the PIC just keeps turning off and on) and a resistor tying MCLR pin to the power supply (here 10 kOhm). Also, I tied the input C1 pin to ground, so that electrical noise wont make it go high and low unintentionally.
Simple background: this brushless motor was bought at Hobbyking for less than $20 and can put out something like 600W. Although that is pretty high, it is not to be used for wheeled vehicles, which have highly dynamic loads and high current spikes. These brushless motors are intended for R/C planes' propellers, which have very steady and predictable loads. I have it connected to an electronic speed controller (ESC), which I bought, that dishes out the current at the right times to the brushless motor based on a simple pulse-width modulated signal it receives. The ESC is necessary because brushless motors operate in a totally different way from DC motors. Brushless motors are 3-phase, and as a result have three wires that need to be connected to a power source. Where you could just apply a steady voltage to DC motor and the commutator takes care of current alternation, brushless motors require more deliberate control. This control is not the subject of this post. I use a PIC to easily create and control a PWM signal using PIC's onboard timers for the ESC to interpret. So this post is basically about programming PICs specifically, to generate a PWM signal, of any flavor or kind if you like.
The signal generated here is a standard servo signal of 50 Hz (period 20 ms) with a square wave pulse that ranges in width from 1 to 2 ms.
What the software does: The software uses PIC's Timer1 and Timer0 and interrupts to make the signal, whose length varies (of the signal that can vary from 1 to 2 ms, the 50 Hz cycle is fixed) based on an analog input from a potentiometer. The PIC is constantly polling and converting the signal from a potentiometer to a value from 0 to 255, while in the background the Timers are counting down. Timer1 has a fixed time length (of course defined by me) of 20 ms, and Timer0 has a length from 1 ms to 2 ms, determined by the ADC. Timer1 dictates when the pulse, whose length is determined by Timer0, occurs. Notice that every cycle there is at least a period of 18 ms where the signal is low. The timers counting down and the potentiometer signal polling occurs simultaneously. When the Timers countdown, interrupts stop the PIC from whatever it was doing, executes a specified procedure, and then the PIC resumes whatever it was doing. In this case, the interrupts would be used to stop and start signals.
The c code:
//Using default Internal Clock of 4 Mhz
//To use TMR1 at 50 Hz and 1:1 prescaler, set offset to 45535.
//TMR0 can vary from 7 to 132, or 2 ms to 1 ms, at 1:1 prescaler.
//Use TMR0 to control pulse width, even though TMR1 has much higher resolution, because TMR1 offset is 16 bits and may require lots of time to compute proper TMR1 offset from ADC value.
#include
__CONFIG(INTIO & WDTDIS & PWRTEN & MCLRDIS & UNPROTECT \
& UNPROTECT & BORDIS & IESODIS & FCMDIS);
unsigned int pulse = 0;
//Interrupt function
static void interrupt isr(void)
{
if(T0IF)
{
RC0 = 0;
}
if(TMR1IF)
{
TMR1IF = 0;
//Start 50 Hz cycle period.
TMR1L = 0b11011111;
TMR1H = 0b10110001;
//Turn on variable pulse.
RC0 = 1;
if(RC1 == 1) //safety switch (must be pressed).
{
TMR0 = pulse;
T0IF = 0;
}
else //off.
{
TMR0 = 132;
T0IF = 0;
}
}
}
void main(void)
{
//Setting ports.
TRISA = 0x00001001; // Set A0 (for ADC) and A3 (MCLR) to input, others output
PORTA = 0x00;
TRISC = 0b00000010; //Set C1 to input for safety on switch.
PORTC = 0x00;
//ADC configuration.
ADCON1 = 0x00;//So that ADC conversion clock is set to Fosc/2.
ADCON0 = 0b00000001;//ADC enabled.
ANSEL = 0x01; // Set A0 to analog, others digital
//Timer configuration
OPTION = 0b00000010; // Timer0 configuration, 1:8 prescaler.
TMR1L = 0b11011111;
TMR1H = 0b10110001;
T1CON = 0b00000001; // Timer1 configuration, including 1:1 Prescaler and TMR1ON = 1.
//Timer interrupt enabling.
T0IE = 1;
TMR1IE = 1; // Timer1 interrupt enable, needed with PEIE and GIE bits set.
PEIE = 1; // Peripheral interrupt enable, for Timer1.
//General interrupt enabling.
GIE = 1; // Global interrupt enable, for Timer1 and Timer0.
while(1)
{
GODONE=1; // initiate conversion.
while(GODONE) continue; // Wait for conversion to finish.
pulse = 7+ADRESH*125/255;
}
}
The c code is turned into a hex file via MPLAB and I program the microcontroller with the PICKit 2. The microcontroller is a PIC16F690. Analog-to-digital conversion signal (from potentiometer) input is at A0 and ouput PWM signal is at C0. There is an input at C1 for a momentary pushbutton, which needs to be held in order for an input at C1 to be high. This is for safety, as I want it to be easy to turn the motor off by just letting go of the pushbutton. So no matter how high a speed the potentiometer is set to, nothing will happen unless the pushbutton is held.
Since the ADC gives values from 0 to 255, the resolution, or how many speed levels, you have is limited to 256. So it does not matter whether you use Timer0 or Timer1 for determining the signal length (Timer1 has a much higher temporal resolution). I use Timer0 because it probably takes less time for the PIC to do the arithmetic of converting the ADC value to the appropriate number. Timer0 uses 8 bits to define counter number (up to 255 in decimal), while Timer1 uses 16 bits (up to 65535 in decimal).
Remember, when using PICs as standalone portable devices, two things should be done: there should be a capacitor across the power supply pins (here, .1 microFarad, and yes this is absolutely necessary, else the PIC just keeps turning off and on) and a resistor tying MCLR pin to the power supply (here 10 kOhm). Also, I tied the input C1 pin to ground, so that electrical noise wont make it go high and low unintentionally.
Thursday, January 20, 2011
PIC servo control with ADC Input
I have not used PIC in a long while, as it has been replaced by the Arduino. The reasons are that I have to program the PIC at a lower level of abstraction, the PIC does not have all of the built-in convenient functionality of the Arduino, and the PIC is harder to debug.
Even so, I have programmed a servo controller with a PIC that takes in an analog value from the Arduino (via PWM, a rectifier capacitor can be placed), and translates this to a 50 Hz PWM servo signal with pulse widths ranging from 0.6 ms to 2.4 ms for the control of a HS-311 servo. If your servo requires different pulse lengths it is easy to change in the code.
The reasons for making a PIC servo controller are two:
-The PIC is much cheaper. Ordering from Microchip I can get the PIC16F690 for less than $2 a piece.
-The Arduino servo library control gets messed up when you change the timer settings. On the same Arduino I wanted to control the servos, I changed the timers so that the PWM operates at above 30 kHz.
I programmed a PIC16F690 using PicKit 2 (there is now a newer PicKit 3) and the free MPLAB IDE using the also-free HITECH compiler.
The software utilizes Timer 0 for the 50 Hz signal refresh rate and Timer 1 for the pulse length. This was done because a higher resolution of pulse width was achieved with Timer 1. Several very useful calculators for finding the Timer offset value settings to correspond to the desired times can be found by Google searching 'PIC timer calculator'. I have one that I got a few years back, but I cannot seem to find it now; maybe it updated.
Friday, January 7, 2011
The manual wire wrap tool: a microelectronics hobbyist's best friend for solderless connections
Tired of soldering? Tired of breathing in potentially toxic fumes? Then the wire wrap is just for you. Look it up on Google for pics and how to use it (and where to buy!). It's awesome. I just discovered it. It allows you to effortlessly strip and coil thin wire (I was using 30 gauge, for PIC pin outputs) tightly around a thicker pin (DIP extensions, those common square-cross-sectioned headers, etc.). Surprisingly (to me) strong in tension; could not pull it off without breaking whatever it was attached to first. If you venture away from PCB boards, this is a must have, especially since it is a simple and cheap tool.
Wednesday, January 5, 2011
Correctly using PIC in a device disconnected from the PicKit/computer
So I was trying to switch transistors with outputs from the PIC pins, for a project I will post probably tomorrow. The output voltage (digital I/O) was like .6 V when the PIC was running on a 4.5 V source. All that was needed was to tie the MCLR pin high (I used a 15k Ohm resistor). The pin voltage then went to 2.8 V, which was sufficiently high to finally bias the transistor base. I do not know why it is not equal to 4.5 V, but I do not really care presently (EDIT: Ah, it was just a software error). So for basic functionality for a PIC used on a standalone circuit (i.e. taking it off a PicKit dev board), you just need a .1uF capacitor across power supply anode and cathode and the MCLR pin tied high.
Of course, you could just disable MCLR. However my c code compiler for PIC (PICC Lite) does not recognize the bit as MCLRE, or the CONFIG register. So... if you know put it in the comments please.
Of course, you could just disable MCLR. However my c code compiler for PIC (PICC Lite) does not recognize the bit as MCLRE, or the CONFIG register. So... if you know put it in the comments please.
Friday, December 24, 2010
Wireless PWM with ADC: Xbee with No Microcontroller (Direct I/O)
Here we look at two xbees communicating, with no microcontroller. In this example one takes acts as an ADC (Analog to Digital Converter), and sends the data to the recieving xbee, which outputs a pwm signal proportional to the ADC reading.
The Xbee pins have oneto-one correspondence; Pin AD3 on one Xbee only communicates with Pin AD3 on the other, etc.
The pwm signal is at 15625 Hz, which is ideal for PWM (pulse-width modulation) motor control if I am not mistaken (there will soon be another page up with microcontroller-less xbees controlling a 24V DC, 10A, 135W motor).
The xbees in this example are using Lady Ada's adapter. With this adapter, the setup is quite simple, as it comes with a voltage regulator and a regulated 3V output. The configuration is made simple as well by its FTDI-cable-ready pinouts. However, to get to the VREF (for ADC), AD2 (for analog signal input), and PWM2 (PWM output) pins, extra jumpers were needed to be soldered. The adapter leaves these pins accesible, but as holes.
Connecting and configuring the xbee is quite simple. There are many tutorials online. For configuration, this example utilized the Lady Ada's tutorial. For mcu-less communication, the configuration code Rob Faludi's website was referenced (my circuit was quite different), as well as the xbee product manual.
I have read that only the AD1 pin will communicate with PWM1, and only AD2 will only communicate to PWM2. I chose PWM2 (for no important reason).
On the ADC xbee, I jumpered the 3V regulated output to VREF and also used the 3V output on the pot. To power the xbees, I used a ~4-4.5 V source on the 5V input on the adapter. 3.30V was measured at 100% duty cycle on the PWM output.
The configuration code is below. Good luck!
For potentiometer reader (controller):
//pot (potentiometer) reader configuration
//initialization: type "+++", WAIT few seconds for ok,
//then type "AT" and press enter, get ok, then issue commands.
//(numbers are in hexadecimal format)
ATID 4064
ATMY 40
ATDL 64
ATD1 2
ATIR A //(A=10 in hexadecimal format)
ATIT 1
ATWR
For PWM outputter (reciever):
//pwm outputter
//initialization: type "+++", WAIT few seconds for ok,
//then type "AT" and press enter, get ok, then issue commands.
//(numbers are in hexadecimal format)
ATID 4064
ATMY 64
ATDL 40
ATP1 2
ATIU 1
ATIA 40
ATWR
Arduino Xbee Wireless Full-Range Joystick FPS Servo Turret
Here we look at a full-range servo turret controlled wirelessly by Arduino via Xbee modules. The title says "fps" because if you were to have the point of view of the pencil tip, the joystick would control like an fps video game.
It works by moving the turret whenever the joystick is moved outside the neutral center position, and stays at whatever position its at when the joystick is moved back to the center position. Just like an fps video game.
You may have noticed, one joystick is superfluous in this demo. That will be used when (or if) I create an "fps bot", which would have a live wireless camera, omni wheels, and other things to allow for an fps control of a physical robot that has all of the movement freedom of a typical fps video game.
This project utilized Xbee modules with the FTDI-ready adapter that can be found at www.adafruit.com, Arduino Duemilanove, HS-311 Standard servos, and these adafruit.com joysticks.
Here's an Arduino Reference, if you need it. For the Xbees, plenty of resources online can help you configure and set it up. Good luck!
Below are two codes, one for the controller and one for the reciever. If you guys need help, comment on my corresponding Youtube video and I will try to get to it, as I am on Youtube quite often.
Controller code:
//CONTROLLER CODE
//Feel free to use this code however you like, but please credit lordvon and this site!
//'ud' and 'lr' are the pot signals for the up/down and left/right pots, respectively.
//(Joysticks and their pot values are not perfect, so some things have to be empirically
//determined. The serial read function is a great tool for collecting the values.)
//range of ud: 29-1014 (middle: around 479-540; avg:~510, center:+/-35)
//range of lr: 0-960 (middle: around 419-471; avg:445, center:+/-30)
//minimum range extreme (from avg) magnitude: 445
//maximum tolerance value: 35
//Print values as 'topic:value', where 'topic' denotes either 'ud' or 'lr',
//and 'value' is the associated pot value.
//Reciever is prompted to record by ':', and ended by ','.
//"" gives characters, '' gives characters in integer form.
//Empirically determined and/or arbitrary constants.
#define ud1 0
#define lr1 1
#define highpulse 2400
#define lowpulse 600
#define lr1avg 445
#define ud1avg 510
#define tolerance1 40
#define range1 445
#define maxadd 35
int ud1val;
int lr1val;
int lr1pulse;
int ud1pulse;
int lr1previous;
int ud1previous;
boolean update;
void setup()
{
Serial.begin(9600);
//initialize servo to its center position.
lr1previous = lr1avg;
ud1previous = ud1avg;
Serial.print(':');
Serial.print((highpulse-lowpulse)/2+lowpulse);
Serial.print(',');
Serial.print((highpulse-lowpulse)/2+lowpulse);
}
void loop()
{
ud1val = analogRead(ud1);
lr1val = analogRead(lr1);
update = (abs(lr1avg-lr1val) > tolerance1) || (abs(ud1avg-ud1val) > tolerance1);
if (update)
{
lr1val = newval(lr1val,(lr1avg-range1),(lr1avg+range1),lr1avg,lr1previous);
ud1val = newval(ud1val,(ud1avg-range1),(ud1avg+range1),ud1avg,ud1previous);
lr1previous = lr1val;
ud1previous = ud1val;
lr1pulse = map(lr1val,(lr1avg-range1),(lr1avg+range1),lowpulse,highpulse);
ud1pulse = map(ud1val,(ud1avg-range1),(ud1avg+range1),lowpulse,highpulse);
//':' is packet delimiter, ',' is UD/LR delimiter.
//Each cycle of delimiters denote a packet.
//Packet: UD value first, then LR value. (:UD,LR:)
Serial.print(':');
Serial.print(ud1pulse);
Serial.print(',');
Serial.print(lr1pulse);
delay(25);
update = false;
}
}
int newval(int val, int minval, int maxval, int avgval, int previousval)
{
int increment;
increment = val-avgval;
if (increment > 0)
{
increment = increment - tolerance1;
}
else if (increment < 0) { increment = increment + tolerance1; } increment = map(increment,0,range1-tolerance1,0,maxadd); val = increment + previousval; if (!(val <= maxval) || !(val >= minval))
{
if (val > maxval)
{
val = maxval;
}
else
{
val = minval;
}
}
return val;
}
Reciever code:
//RECIEVER CODE
//Feel free to use this code however you like, but please credit lordvon and this site!
#include <Servo.h>
Servo elevation;
Servo azimuth;
#define highpulse 2400
#define lowpulse 600
char received;
char pulsechar[4];
int counter=0;
int pulse;
boolean delimiter;
void setup()
{
Serial.begin(9600);
while (!Serial.available()){}
received = Serial.read();
elevation.attach(12);
azimuth.attach(13);
}
void loop()
{
// if (Serial.available())
// {
// Serial.println(char(Serial.read()));
// }
delimiter = (received == ',') || (received == ':');
if (!delimiter)
{
counter = 0;
while (!delimiter)
{
while (!Serial.available()){}
pulsechar[counter] = received;
counter += 1;
received = Serial.read();
delimiter = (received == ',') || (received == ':');
}
pulsechar[counter] = '\0';
pulse = atoi(pulsechar);
switch (received)
{
case ',':
azimuth.writeMicroseconds(pulse);
break;
case ':':
elevation.writeMicroseconds(pulse);
break;
}
}
else
{
while (!Serial.available()){}
received = Serial.read();
}
}
Nintendo DS Touchscreen Arduino Servo Turret
Here we describe how to set up a full-range 3-servo turret controlled by a Nintendo DS touchscreen and Arduino Duemilanove with an ATMega328 microcontroller. The magnitude of the distance from the center of the touchscreen to the touched position will control the angle of the servo on top for elevation, while the azimuth, or rotation of the ground plane, will be controlled by basically polar coordinates on the DS touchscreen.
It's just a demo; I did this to become familiar with Arduino microcontrollers. You can mount a bb gun controlled by a simple switch. Making it wireless may be one of my next projects.
One of the things that make the Arduino really easy to use is the libraries. These are basically sets of functions that achieve a certain category of tasks. In this project we use the "Servo" library. Information on this library can be found here.
Below is the code. The ranges of movement corresponding to the ranges of touch can be easily adjusted by changing the constants defined at the beginning, so you can make the turret easier to use.
Circuit details:
--See code comments for connections to the touchscreen (X1,Y2,X2,Y1,Xin,Yin).
--Connect the servos (HT-311) to an appropriate power source (red to positive, black to negative).
--Group the signal wires (yellow) of the azimuth (ground-plane) servos together and connect them to digital pin 11.
--Connect the elevation servo signal wire to digital pin 12.
--Be sure to connect the ground of the arduino to the ground of the servo power supply. I do not know why this is necessary, haha, and it took me a while to figure out.
--That's it! Here's an Arduino Reference, if you need it.
//Feel free to use this code however you like, but please credit lordvon and this site!
//Code was adapted from that found on (http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1243499684/0).
#include <Servo.h>
Servo azimuth;
Servo elevation;
// Empirically determined; Voltage between an output high and output low pin.
#define Vref 4.46
#define highpulse 2400
#define lowpulse 600
#define quarter (highpulse-lowpulse)/4
#define s1high lowpulse+quarter/2
#define s2high s1high+quarter
#define s3high s2high+quarter
#define s4high s3high+quarter
#define sdivide .7071
#define maxmag 420
//When looking at DS screen with connector bottom left,
// the connections are from left to right:
//TOP, X1
//LEFT, Y2
//BOTTOM, X2
//RIGHT, Y1
// Digital pin connections (used to drive power)
#define X1 2 // TOP to Digital output 5
#define Y2 3 // LEFT to digital output 2
#define X2 4 // BOTTOM to digital output 3
#define Y1 5 // RIGHT to digital output 4
// Analog inputs
#define Xin 3 // From X1
#define Yin 4 // From Y1
// set initial touched position
int touchX;
int touchY;
int xpos;
int ypos;
float cosine;
float sine;
float mag;
int apulse;
int epulse;
void setup()
{
azimuth.attach(11);
elevation.attach(12);
}
void loop()
{
if (touched())
{
xpos = touchX - 511;
ypos = touchY - 511;
if(!(xpos == 0 and ypos == 0))
{
mag = sqrt(pow(ypos,2) + pow(xpos,2));
sine = ypos / mag;
if(abs(sine)>=sdivide)
{
cosine = xpos / mag;
if(ypos>0)
{
apulse = map(cosine*10000,-7071,7071,s2high,s3high);
}
else
{
if(xpos >= 0)
{
apulse = map(cosine*10000,7071,0,s4high,highpulse);
}
else
{
apulse = map(cosine*10000,0,-7071,lowpulse,s1high);
}
}
}
else
{
if(xpos>0)
{
apulse = map(sine*10000,7071,-7071,s3high,s4high);
}
else
{
apulse = map(sine*10000,-7071,7071,s1high,s2high);
}
}
}
azimuth.writeMicroseconds(apulse);
if(mag>maxmag or mag<0)
{
mag=maxmag/2;
}
epulse = map(mag,0,maxmag,highpulse,lowpulse);
elevation.writeMicroseconds(epulse);
}
}
boolean touched()
{
boolean touch = false;
// Horizontal routine Y1:Y2::Vcc:Gnd
pinMode(Y2, OUTPUT);
digitalWrite(Y2, LOW);
pinMode(Y1, OUTPUT);
digitalWrite(Y1, HIGH);
// X1,X2 high impedance (input mode)
pinMode(X1, INPUT);
pinMode(X2, INPUT);
// wait a bit, then read from X1
delay(10);
touchX = analogRead(Xin);
// Vertical routine X1:X2::Vcc:Gnd
pinMode(X2, OUTPUT);
digitalWrite(X2, LOW);
pinMode(X1, OUTPUT);
digitalWrite(X1, HIGH);
// Y1,Y2 high impedance (input mode)
pinMode(Y1, INPUT);
pinMode(Y2, INPUT);
// wait a bit, then read from Y1
delay(10);
touchY = analogRead(Yin);
// Only read touch if coords are below 1000 and above 0
// (stops errors with certain screens)
if(touchX < 1023 and touchX > 0 and touchY < 1023 and touchY > 0)
{
touch = true;
}
return touch;
}
Subscribe to:
Posts (Atom)


