Accelerometer Controlled R/C Vehicle

Alycia Gailey

Jack Logue

Varun Pattani

 

 

 

 

CONTENTS

 

 

INTRODUCTION

 

          In our final design project for ECE 476: Microcontrollers, we decided to build a vehicle controlled, using accelerometers, by a remote control that wirelessly transmits, using RF technology, data to the vehicle to move in any direction.  The accelerometers will be mounted on a steering wheel shaped remote control, such that if the user wants to move forward they can lean the steering wheel forward and backward for reverse; to turn left or right they need to turn the steering wheel like steering in an actual car, all in manual mode.  In addition there is an automatic mode during which the vehicle goes in the direction requested by the user for the length of the distance requested by the user.  A liquid crystal display (LCD) is located on the remote control and displays the direction, distance and mode (manual or automatic), which is user choice using potentiometers to decide the direction and distance and mode by using a button.  We decided on this project because of its ingenuity and similarity to game console systems games, which will appeal to users especially with the ease of use (physically moving the remote control).  Additionally, we all love playing with remote controlled vehicles and we wanted to take it to the next step therefore creating an enjoyable form of entertainment for hours.

 

HIGH-LEVEL DESIGN

 

            One major rationale behind the project was to design a remote controlled car that steers and accelerates by means of an accelerometer instead of a joystick.  The accelerometer would be placed on a “steering wheel” that the user can shift left and right to steer left and right and shift forward and backward to make the car go forward or backward.  The more the steering wheel shifts left or right, the more sharply the car turns left or right.

            Another major rationale was to allow the user to make the car move a certain distance in a certain direction automatically without having to worry about the difficulties people sometimes have with maneuvering the car accurately.  The user can operate two potentiometers: one that determines the direction of the car (NW, N, NE, E, SE, S, SW, W), and another that determines the distance that the car is to travel.  When a button is pressed, the car can turn in the proper direction and move to the destination automatically.  The user can press this button again to regain manual control.

 

Remote Controller

            The remote controller consists of an Atmega32 microcontroller circuit, an RCT-433-AS RF transmitter, a 7404 inverter, an MMA6261Q 2-dimensional accelerometer, two potentiometers, a pushbutton for toggling between modes and a liquid crystal display (LCD) that shows the command direction, command distance (for the car to travel in automatic mode), and a message stating whether the controller is in manual mode or automatic mode.

            When the remote controller is powered, the accelerometer gives two outputs: one for the horizontal (left-right) direction and one for the forward-backward direction.  Each output ranges from about 1V to 2V, with 1.5V meaning no shift along that axis.  An ADC within the microcontroller converts the outputs to digital signals that are sent to the car through an inverter to the transmitter.  In manual mode, the three signals being sent out of the transmitter are an integer for the servo motor (used for steering) that codes for a PWM value; two bits for the main motor (for the wheels) that tell whether the motor moves and its direction of rotation; and the mode (0 for manual, 1 for automatic).  The code is programmed so that when the steering wheel is down flat, the car does not move.  When the steering wheel is upright, the car moves forward.  Steering the wheel left or right will move the car left or right.  When the steering wheel is tilted in towards the chest, the car goes backward.  The Y output of the accelerometer allows this implementation.

In automatic mode, the three signals being sent are the command direction (represented by an integer from 1 to 8), the traveling distance in feet and the mode number.  The mode number is supposed to tell the car what to do with the incoming information.

            As the potentiometers are rotated, the LCD update will update the command direction and distance shown.  The potentiometer values will not be used until the user presses the button to switch to automatic mode. 

 

Car

            The car circuit consists of an Atmega32 circuit, an RCT-433-AS RF receiver, a 7404 inverter chip, a servo motor, a main motor for the wheels and an H bridge for the main motor.  The car circuit repeatedly checks for an incoming wireless data packet.  When it gets a data packet, it checks the mode, which is the 3rd byte of data in the data packet.  The mode number tells the car circuit what to do with the other two bytes of data.

            If the mode number is 0, then it means manual mode.  The first byte of data is used for determining the direction that the servo motor will direct the car.  The second byte of data is used to determine whether the car goes forward, backward or doesn’t move at all.  The current motion of the car is updated accordingly.

            If the mode number is 1, then is means automatic mode.  In this mode, the first byte represents the command cardinal direction that the car must move in.  The second byte represents the distance in that direction that the car shall move.  The servo motor will change the car’s direction until the car is facing the desired direction; then the car will continue in that direction until it has traveled the whole distance.  While the car is traveling, it does not accept any inputs from the accelerometer.  If the user decides to switch back to manual mode, then the car will immediately start to move according to the accelerometer values regardless of whether it reaches its final destination in automatic mode.

 

PROGRAM DETAILS

 

Remote Controller Program Details

            When the remote controller is turned on, it is initialized to manual mode.  In manual mode, the first step is to collect information from the accelerometer.  The accelerometer outputs are fed through the analog to digital converter (ADC).  The voltage reference chosen was AVcc with an external capacitor at the AREF pin.  The ADC output is left adjusted so that the ADCH register gives sufficient information.  The analog channel selection bits are used to determine which channel is taking the ADC input for a given conversion.  Each accelerometer output has its own ADC input channel.

            The signal to be sent to the servo motor is further discretized into 5 basic directions so that the servo motor does not get too shaky.  Thus, the ADC output is split up into 5 ranges where each yields a different PWM value for the servo motor.  The numerical value sent to the car is a number from 0 to 4 where 0 codes for the highest OCR0 value and 4 codes for the lowest.  A lookup table of OCR0 values is used to do this without if-then statements.

            The signal to be sent to the main motor is discretized into 3 possible values: backward, forward, not moving.  Thus the ADC output range is divided into 3 ranges.  For the lowest range, pins 1 and 0 of PORTB are set to 00.  For the next lowest range, they are set to 11, meaning go forward.  For the highest range, they are set to 01 meaning go backward.  There is a small transition range in between the forward and reverse ranges that yields 00.

            Even in manual mode, the command direction and command distance are updated so that the user can switch the car to auto mode and make the car start moving the specified distance and direction at any time.  The command direction and distance each come from a potentiometer that is fed through two other channels of the ADC.  Since there are 8 different command directions, the ADC for command direction is divided into 8 ranges where the lowest range represents North, the second lowest Northeast, and so on.  The ADC output for distance goes through no conversion; therefore, distance is allowed to vary from 0 to 255 feet.

            An LCD update function updates the information on the LCD about command direction and command distance.  The ADC for the command direction is converted to a number from 1 to 8, then the LCD update function uses a case statement to print it out as the appropriate cardinal direction. 

            The signal is transmitted via Meghan Desai’s wireless protocol.  A special function that we put together, send_data, inputs three data values and uses the wireless protocol function, tx_me, to transmit the signal.  Within this send_data function, the individual input parameters are assembled into a character string of length 3 and inputted into the tx_me function. 

            In manual mode, the send_data function sends out the servo motor PWM, main motor signal, and mode number, which is 0.  During each iteration of the endless while loop, a button is checked to see if it has been pressed.  When the button is pressed, a short circuit forms between the two port pins that the button is connected to.  Detection of a short signals the program to enter the debounce state machine.  The debounce state machine prevents one push from being misinterpreted as multiple pushes due to inherent bounciness in the button.

            The debounce state machine has 4 states.  In state 1, the button has not been pressed at all.  When the program detects a short, the debounce function increments the pushstate to state 2, meaning maybe push.  When another short is detected, the pushstate is incremented to state 3, meaning pushed.  Here, a pushflag is set.  Setting of the pushflag signals the program to change modes.  If in mode 0, it changes to mode 1.  If in mode 1, it changes to mode 0.  When an open circuit is detected again, the pushstate goes to state 4, meaning maybe no push.  Here, a timer is initialized to 30 and is decremented every millisecond in a compare interrupt.  This allows 30 ms before a short is detected again, allowing the button to bounce.

            In automatic mode, different data values are sent over.  These values are the command direction (numbered 1 to 8), the command distance (numbered 0 to 255) and mode number, which is 1.  These values are continuously sent until the button is pushed again.  In automatic mode, the car turns into the command direction and moves to its destination.  Once in its destination, it will stay until the user switches back to manual mode.

 

Car Circuit Program Details

            The main code for the car initializes everything, then runs on an endless while loop.  The action of the main motor is represented by pins 0 and 1 of PORTB.  Pin 0 represents reverse direction.  Pin 1 represents forward direction.    For the car to not move, both pins are set to 0. Because of how they are being used, these pins are configured as output. 

            The servo motor is connected to pin 3 of PORTB, which is the output compare value of the pulse width modulator (PWM).  Timer0 is configured as fast PWM, prescaled by 1024, and the output compare pin is cleared on a compare match.  OCR0 is varied to control the direction that the servo motor moves the car.  Because varying OCR0 at high resolution makes the servo motor too shaky, we discretized the OCR0 values so that OCR0 could be only 1 of 5 possible values.  These values are in a lookup table called servo_array.  The lookup table would eliminate the need for if-then statements when determining the OCR0 value.  The specific OCR0 values chosen for the servo_array were calibrated by varying OCR0 and looking at how far the servo motor turns with each value of OCR0.  We found that OCR0 going from 10 to 40 covered the extremes in servo motor angular position.

            Pin 0 of PORTD is configured as an input so that it can receive the RF signal.

            The compass is connected to pins 3-6 of PORTD, which are configured as inputs. The compass is, by design, divided into 8 cardinal directions.  When the direction is N, S, E, or W, two of the output pins read high and two read low.  When the direction is NW, NE, SW, or SE, three of the output pins read high.  A separate function in another c file called get_direction checks the pin values in PORTD and uses a series of nested if-then-else statements to determine which cardinal direction is being represented.  It is okay to assign each cardinal direction to an arbitrary combination of PORTD pin values because the compass can be placed on the car in such a direction that when the compass reads north the car is actually facing north.  For the purpose of easier program design, the cardinal directions are numbered 1 to 8 where 1=N, 2=NE,…,8=NW.

            The main function first calls the initialization function, then goes into an endless while loop.  For each iteration of the while loop, it constantly checks for a new input packet of wireless data.  An if statement is used to give separate sets of instructions to the car in manual mode versus automatic mode.  In manual mode, the first byte of the data packet is used to index the lookup table of OCR0 values to update OCR0 for the servo motor.  The second data packet is used to determine whether the motor goes forward, backward or doesn’t move at all.  Here, an if-then statement is used to set the pins of PORTB accordingly.  If the last two bits of the second RF input byte is 01, it means 0 for forward and 1 for backward.  If they are 10, it means 1 for forward and 0 for backward.  If it is 00, it means 0 for forward and 0 for backward, meaning don’t move.  An input of 11 should never be encountered.  If encountered, then no statements in the if-then-else statement will be met and the activity of the main motor will not change.

            When the 3rd received byte of data is a 1, the program stops entering the code for mode = 0, and enters the code instructions for mode = 1.  The first time this set of instructions is executed, the motor is set to move forward and a timer is initialized to keep track of how much time the car has been moving so far.  The total travel time for the car is a function of the second byte of wireless input data (command distance) and the measured speed of the car.  After the timer and main motor are started, a variable, start, is set to 0, meaning that the next time this set of instructions executes, it will no longer be the first time of execution.  The variable, start, is reinitialized back to 1 in the mode 0 instructions.

            In the remainder of the instructions for automatic mode, the program determines how far away the current direction is from the command direction.  First the get_direction function is used to obtain the current direction that the car is facing based on the values of the PORTD pins.  The results are stored in current_dir and stored in a temporary variable called temp.  Temp is incremented in a while loop until it matches the command direction.  Each increment makes temp go 45 degrees clockwise or to the right.  If temp has to be incremented more than 4 times, it means that the car can more easily match the command direction by turning left.  An if-then-else statement is used to set OCR0 so that the car turns in the desired direction or remains going straight if the current direction is close enough (within 45 degrees) of the command direction.

            The timer is incremented in a compare interrupt that is set to 1 millisecond timebase (prescaled by 64, OCR2 = 250).  If we know the car’s speed in miles per hour, we can multiply by 5280 to get feet per hour and divide by 3600 to get feet per second.  To get milliseconds, we multiply by 1000.  This will give a conversion factor of 1/[speed(mph)/3600*5280]*1000 that the command distance is multiplied by to get the travel time in milliseconds.

            When the timer variable, t, reaches the value of travel_time, the motor is turned off (PORTB.1,0 = 00).  If per chance the command distance is so short that the car travels the command distance before reaching the command direction, then the car will also be required to face the command direction before the main motor turns off.  Once the main motor is turned off, the program will enter a while loop that executes until the mode changes back to mode 0. 

Even when the car’s motor has not turned off yet, the program still constantly checks for incoming data packets during each iteration to check for changes back to mode 0.  If the mode changes back to 0, then the instructions for mode = 0 are executed once again.  To the user, this means he/she has regained manual control of the car despite whether the car ever traveled the whole command distance.  This will keep the user from accidently sending the car off a cliff or something by making command distance too large.

            It is possible for the car circuit to be powered when the remote controller is not.  In this case, the car will receive no input packet instructions.  Certain variables must be initialized in case there is no RF reception.  Mode is initialized to 0, the servo motor OCR0 value is set to its middle value so that the servo motor makes the car face forward.   The PORTB pins 0 and 1 are both set to 0 so that the car is stationary.  Command distance is set to 0.  

            At some point, the user may turn off the remote controller when the car is still on.  The prevent the car from moving indefinitely, the car’s motor is automatically turned off after 2 seconds of no incoming data packets.  This is done with a variable t_stop, which keeps track of the amount of time passed since a data packet from the remote was received.  When the get_data function detects a data packet with the correct ID value, t_stop is initialized to 0.  From there, it is incremented in the timer2 compare interrupt, which goes every millisecond.  When a data packet comes in that is the wrong ID (interference), the command variables (command direction and command for the motor) are set so that the car faces straight forward and the motor turns off.

 

Wireless Protocol Functions

txrx_init(tx,rx,baud rate,led): initializes the UART control register to specify whether the circuit is transmitting (tx), receiving (rx) or both.  Also initializes the baud rate control register to give the inputted baud rate. 

tx_me(data,data_length,tx_id): transmits a characters string, data, of length data_length that has a transmit ID tx_id.  tx_id is an ID number that represents from which transmitter the data packet came.  This is useful for when more than one transmitter is being used or when other people present are using transmitters.

DecodeOne(msbyte): uses a lookup table to find the true transmit ID associated with the numerical value presented in the received packet

rx_reset(): Sets up the system to wait for another incoming data packet

tx_done(): returns 1 when the transmission is complete; 0 otherwise

rx_done(): returns 1 when a data packet has been received; 0 otherwise

init_getrx(): initializes the current index value to 0 for when the data packet is being read

get_next_rx_data(): gets the next char value in the received data packet.  Index value is incremented

 

HARDWARE DETAILS

 

Remote Controller Hardware Details

            The remote controller circuitry is mounted onto a “steering wheel”.   The Atmega32 circuit, accelerometer, inverter chip, potentiometers, transmitter and LCD are all powered by a 5V power supply.  The accelerometer outputs are each connected to a pin of PORTA.   Each potentiometer (for command direction and command distance) is connected to another pin of PORTA.  PORTA is the port that has the ADC inputs and is configured as all inputs.

            Pin 1 of PORTD is used to output the signals that are to be transmittedD through the RF transmitter.  Meghan’s wireless protocol requires that an inverter be placed between the MCU and RF transmitter.  Therefore, pin 1 of PORTD is connected to one of the inputs of the inverter chip.  The output goes to the transmitter.

            The RF transmitter has 4 leads: one power, one ground, one input and one antenna. Meghan Desai’s wireless protocol specs require that the antenna by ¼ wavelength long.  The RF transmitter is 433 MHz.  The speed of light in meters per second divided by 433X10^6, then divided by 4 yields a length of about 8 inches.

            The LCD has 14 pins.  The first two pins are connected to ground and power.  Seven of the other pins are connected to PORTC.  PORTC must therefore be configured as all outputs.  We include a header file, lcd.h, that allows us to easily write text to the display with simple functions.  According to the specs of this header file, pins 7-10 are disconnected while the other pins are connected to a port, preferably PORTC.  Pin 3 of PORTC is also disconnected.

 

Car Circuit Hardware Details

            The Atmega32 circuit, receiver, inverter chip and compass are all powered by the same 5V power supply.  The servo motor runs off of a 3.7V power supply, but must still be grounded from the same ground as the MCU.  The main motor runs off a separate power supply all together.  An optoisolator protects the rest of the circuit from the noise of the main motor.  An H bridge is used to channel a sufficient amount of current into the motor in either direction to allow the motor to rotate in either direction.  A 10uF capacitor is used to filter out noise on the power line due to the two motors.

            The RF receiver has 8 leads.  The first lead is the antenna, which like the transmitter must be ¼ wavelength long.  Pins 2,3,8 are grounded and pins 4,5 are powered.  Pin 6 is the analog output, which is not used.  Pin 7 is the digital output, and is  connected to pin 0 of PORTD through an inverter.  This inverter inverts the signal back to what it was when it was outputted from pin 1 of PORTD on the remote controller.

            The servo motor has 3 leads: power, ground and signal.  The signal lead is connected to pin 3 of PORTB where it is fed the PWM signal whose duty cycle determines the angular position.  The servo motor was assembled into the Lego car in such a way that when it makes a complete rotation, the wheels turns from all the way left to all the way right.

 

Difficulties and Tradeoffs

            One major source of difficulty was the wireless transmission.  One of the first obstacles was in trying to use a demo code that was designed for a 4x40 LCD as opposed to the 4x16 LCD that we had.  Because a 4x40 LED is so much longer, we were unable to see the complete payload.   We also realized that inverters needed to be placed between the MCU and each RF component.  Even after we got the demo code to work successfully, the wireless transmission system was finicky.  Sometimes it worked and sometimes it mysteriously stopped working even when virtually nothing was changed.

            Another tricky part was working the wireless protocol demo code into our code.  We found difficulty in transitioning from using the wireless protocol functions in the demo code to using them in our code.  Sometimes we would have to start at the demo code and modify it until it resembled our code.  There was some challenge to figuring out how much of the demo code was necessary for our code and how much of it was not.  In other words, we really needed to have an understanding of what each part of the demo code did.  For example, at one point we discovered this line in the demo code that was necessary to determine whether the device transmits or receives.

            There were additional challenges to debugging when we were seated near other people who also were using 433 MHz RF.  Sometimes we would get random numbers.  It was often unclear whether transmission was due to interference or the system not working.  At one point there were people nearby transmitting in GHz.  Our data never showed up on the LCD then because we were receiving their data at a much higher rate.  We also seemed to get interference from other sources of wireless transmissions, such as laptops and Wi-Fi routers.  We are not completely sure about it, but our hypothesis is based on that fact that project would work perfectly in areas without any wireless traffic and would always stop working in places high in traffic, and additionally Professor Belina suggested that Wi-Fi would interfere with our transmissions.  Additionally, we had another problem that whenever we lost transmission due to interference or anything like that our chip would completely shut off until we either reset it or completely reload the program back onto the chip.

            When we tried to achieve bidirectional transmission, we ran into even more difficulties.  We figured that if we programmed the Atmega to alternate between transmit and receive mode every so many times a second, then one direction of transmission could be achieved at a time.  We found during testing that when we got a block of code to receive successfully, then added another block of code to transmit a few times a second, that the receiver would just stop receiving.  This is a problem because we want to remote controller to constantly send accelerometer data to the car to move it adequately without having to allow long periods of time in between to receive data for the car’s speed and direction.  If the remote controller needs a large block of time to adequately receive a data packet from the car, then the user would not be able to alter the motion of the car very quickly.  It would only cause frustration to the user, only for the purpose of giving the user information about the car’s motion that he/she may not even care about.

            We found out the hard way that the wireless protocol system that we were using was insufficient for the fast bidirectional communication that we wanted for a playable remote controlled car.

            Another major source of difficulty was in the main motor.  We had tremendous difficulty in getting the proper power supply (voltage and current) to the motor.  We could not power the motor adequately off the proto board because it did not deliver the proper amount of current.  When we used this other power supply, the motor started to get overpowered.

            When we finally did get the main motor to rotate, we found that when we touched it slightly it would stop rotating.  Not only that, it needed a push just to get rotating again.  When we got a more powerful motor, we found that the Lego car that we built was far too heavy even for the powerful RC motor we found.  We had to make a smaller less impressive looks car without all the fancy shocks and gears we had before.  Even then the car barely moved when we put it onto the floor. 

            It also took hours to get the H Bridge to draw the right amount of current.  This was extremely difficult to figure out the reasoning behind why this was not working.  We tried many basic H Bridge designs from many websites trying to get the motor to start moving.  However, every single design we tried never seemed to draw enough current or enough voltage and sometimes both were lacking.  As a result, we decided to try having more and more complex circuits with higher gain power transistors such as the TIP 31 and TIP 32.  This did not work as well even though these transistors were rated at around 4 Amps they still would not draw more than 1.5 Amps, which our motor needed.  Then we decided that we could try and pull more current and add some gain using a Darlington setup with two transistors together, which did not come close to working.  On our second to last attempt, we tried hooking up power MOSFET transistors, while this did get the motor moving it was definitely not enough power or torque to get the vehicle moving on the ground with the friction.  Finally, we tried the power Darlington transistors TIP102 and TIP107 and using a simple H bridge circuit shown in the appendix, we were able to get the motor to move as fast as possible with the power and amount of weight on the vehicle.

            It was hard to get the compass successfully hooked up right because all the pins were arranged in a circle instead of straight lines.  The power pins and ground pins were all spread out.  Each power pin and ground pin was connected to power and ground via a separate wire on a little board.  By the time all the power pins, ground pins and signal pins were hooked up, the board was a big mess of solder at the bottom.  This solder mess could explain why we took awhile to get one of the pins on the compass to work.  We found the soldering problem when we checked the circuit for unwanted shorts.  We found that one of the signal pins was shorted to ground and we fixed it.

 

TESTING METHODS

 

            The LCD was a key part in testing.  Instead of printing onto the hyperterminal, we used the LCD.  When something did not print on the LCD when it was supposed to, we would toggle the LED in a certain part of the code in the while loop to see if that part of the code is executed repeatedly.

            On the remote side, we printed out the accelerometer outputs to ensure that the accelerometer was successfully sending in data.  On the car side we also used an LCD to monitor the effectiveness of wireless transmission.  One thing we commonly did was print out the wirelessly transmitted data in its raw form.  The next step would be to print it out in its more final form (i.e. cardinal direction in letters instead of integers). 

            We used the LCD demo code frequently because sometimes nothing would print on the LCD.  We also used the wireless protocol demo code frequently because wireless transmission frequently stopped working.

            In manual mode, we would transmit the number from 0 to 4 representing the discretized accelerometer X values, and a number 0 to 2 represent the three possible values for the main motor.  These two values are critical in controlling the car manually.

            In auto mode it was a little more complicated.  We printed the actual direction next to the command direction sent from the remote.  We also printed out OCR0 to make sure that OCR0 makes the car turn left or right depending on the relationship between command and actual direction.  We also would print “run” onto the LCD to mean that the car’s motor is currently running.  All of this allowed us to debug without having to connect to any motors.

            For the motor we tested it by connecting the motor to batteries and seeing whether it would run with the amount of power supplied, current and voltage respectively.  Additionally, to test the H Bridge all those numerous times, we used the power from either batteries or the protoboard on the ECE 476 benches and then using the oscilloscope we measured the voltage outputted out of the terminals.  This was effective at first but then when we connected the motor to the terminals we could never get it to turn on because of the lack of current, so this became the new standard of measuring whether an H Bridge was effective, which was connecting a motor to the H Bridge and see if it was enough power.

           

RESULTS

 

            Data is transmitted from the remote controller to the car at about 4 times per second.  Therefore in manual mode, the direction of the car is updated at about 4 Hz.  Because of the shakiness of the servo motor, the direction of the car cannot be chosen by the user to a very high accuracy.  The direction is only discretized into 5 possibilities.

            Because of difficulties in just getting the motor to move, we decided it would be too much to ask for the user to determine the direction of the main motor with the accelerometer.  Instead, we decided to only determine whether the motor moves at all and in what direction.

            The user also can only determine the command direction to a limited accuracy because the compass only has 8 cardinal directions on it.  For instance, the user cannot choose a command direction halfway between NW and W.  Therefore, the maximum discrepancy between the desired command direction and the closest possible command direction is 22.5 degrees.

            The whole system is fairly playable aside from the unreliability of the main motor.  The user simply turns on the remote control and the car.  The default starting point is manual mode, which is how users usually operate remote controlled cars.  The user can steer the wheel left or right and the car will move left or right.  A simple turn of the knobs on the steering wheel allows the user to adjust the command direction and command distance.  A push of the button sends the car off.  An additional push of the button stops the car from moving on its own.

            The safety considerations are fairly minimal.  The biggest concern we can think of is what may happen if the equipment got soaked with water.  Perhaps there may be a risk of the holder of the remote getting electrocuted.  As such, we would recommend placing a clear warning on the car and remote controller that the device should be kept dry at all times.  In addition, we think that the circuitry should be covered preferably by an insulator to prevent the user from touching the inner circuitry, which may get pretty hot.

 

CONCLUSION

 

            We had experienced much more difficulty than we had anticipated.  Overall, we thought that the whole project seemed straight forward, but there were many complications from the unreliability of wireless reception to power supply problems with the main motor.  We thought that we were going to have a nice big fancy Lego car with shocks and huge gears (10:1 gear ratio) moving at 25 mph possibly.  Instead we found surprising difficulty just getting the motor to move at all, more difficulty in getting it to move the wheels with the gears and even more difficulty getting the car to move when it was placed on the ground.  We had to make many adjustments, particularly to the motor we were using and to the design of the Lego car.  The Lego car had to reduce in size dramatically so the RC motor could move it.  The RC motor, which can go up to 25 mph, had to be cleaned out.  There still was difficulty after it was cleaned out.  Now instead of the big robust fast-moving car we pictured, we have a little car with 3:1 gear ratio that moves a little if we are lucky.

            We also needed to make adjustments with how the servo motor was controlled so that it would not be so shaky.  As a result, we did not get that nice continuous control of direction by the user that we had expected.  We also could not allow the user to continuously control the speed with the accelerometer as we had thought we could because it was so hard just to get the motor to move the car at all.

            For awhile we went around in circles, but once we committed to unidirectional transmission alone, there was progress to a working product.  The “steering wheel” was able to control the car’s wheels nicely.  We decided to make it so that when the user places the steering wheel down, the wheels don’t move.  The wheels only move forward when the steering wheel is upright and backward when the steering wheel is tilted inwards.  We conclude that it would be a better use of any person’s time who wants to use RF wireless technology to write their own protocol because it would have taken less time to do that and would have had the ability to be bidirectional.  Additionally, we would like to point out that the best H Bridge to use for an RC motor is the one we ended up with, using Darlington BJTs instead of trying to deal with other circuits shown on other websites.  However, finally we managed to get everything to work well together and the vehicle moves effectively in the manual and automatic modes, using the digital compass well.

 

Intellectual Property

 

            We used the wireless protocol written and designed by Meghan Desai.  The RF transmitter and receiver circuits also were designed by a corporation from which we bought the circuits.  We also based our custom PC board on Bruce Land’s design that is shown on the ECE 476 website.  Finally, the H Bridge was borrowed from one of the TA’s that was in the class last year, Tytus Mak and Daniel DiBernardo, and was the most effective.

 

RF Frequency Laws and Safety Regulations

 

          In our robotic vehicle design project, we will need to take a few safety issues into consideration.  Firstly is the controller for the robotic vehicle, which will be manipulating the vehicle wirelessly.  We will be using an RF (radio frequency) protocol that will follow all safety regulations instituted by ANSI and IEEE for using such wireless technologies without affecting humans at all since the FCC does not mandate any federal regulations as of yet and recommends using these as the federal standards.  This will be done by limiting the frequency between 100-300 MHz and using 0.1-0.4 W/kg as stated by ANSI and the IEEE, which is done by using the protocol suggested by Meghan Desai. 

 

 

Back to the Top
APPENDICES

Pictures

 

 

Figure 1: Starting of the Testing Circuits

 

Figure 2: Original design of vehicle

 

Figure 3: Actual gear design from original design (took many hours of work)

 

 

Figure 4: New sleek, lighter design vehicle

 

 

Figure 5: Circuit on the Steering Wheel Remote Control

 

 

Figure 6: Circuit on the Vehicle

 

 

Figure 7: Final Vehicle Design with Circuit on top

 

 

Schematics

Figure 8: Circuit schematic/pin diagram of the remote controller.  At upper left is the pushbutton; at upper right is the LCD; at middle left is the accelerometer; at lower left are the potentiometers for adjusting command distance and direction; at lower right is the transmitter connected to the MCU through an inverter.

 

Figure 9: Circuit schematic/pin diagram mounted onto the car.  At the top is the pinout of the digital compass followed by the RF receiver pinout.  At the bottom is the servo motor, which shares ground with the MCU.  Once again, there is an inverter between the MCU and wireless component.

 

Figure 10: H-bridge circuit that drives the motor.  The gate logic at the top represents the relationship between pins 0 and 1 of PORTB and the direction of the car

 

PARTS LIST

 

Part Number

Supplier

Quantity

Unit Price

RCT-433-AS

Bruce Land

1

$4

RCR-433-RP

Bruce Land

1

$4

Atmega32 chip

Avnet

2

$0

1490 Digital Sensor

Dinsmore Sensor

1

$15

3904 BJT

Bruce Land

4

$0.50

AA battery

Target

9

$0.42

9V battery

Target

2

$2

Button

Bruce Land

1

$0.50

Servo motor

Jack Logue

1

$0

Lego Car

Jack Logue

1

$0

RC motor

Providence Hobbies

1

$15

2x16 LCD

Newhaven Display

1

$0

Hose

Lowe’s

1

$2.00

Valve

Lowe’s

1

$1.26

Wood

Lowe’s

1

$1.50

Small solder board

Bruce Land

5

$1

TIP102 and TIP107

Jack Logue

4

$0

MMA6261Q

Freescale Semiconductor

1

$0

 

Header socket plug

Bruce Land

102 pins

$0.05/pin

Potentiometer

Jack Logue

2

$0

2AA Battery Holder

RadioShack

1

$1.50

4AA Battery Holder

RadioShack

1

$2.00

4AA Battery Holder

Varun Pattani

1

$0

Custom PC Board

Bruce Land

2

$5

DIP Socket

Bruce Land

4

$0.50

 

 

 

 

 

 

Total Cost

$75.63 (63 cents over)

 

 

DIVISION OF LABOR

Jack Logue:

  • Design and construction of the Lego car
  • Supplying the servo motor, main motor and batteries
  • Writing the car code
  • Soldering the compass circuit together
  • Soldering the transmitter circuit
  • Soldering the car circuit together
  • Soldering together one of the Atmega circuits
  • Mounting the chips on the vehicle
  • Getting the main motor to work
  • Debugging the wireless system and other code
  • Writing part of the paper

 

Varun Pattani

  • Design and construction of the H bridge for the motor
  • Soldering random wires within the circuit
  • Writing random code
  • Mounting the chips on the vehicle
  • Getting the main motor to work
  • Debugging the wireless system
  • Debugging the control code and car code
  • Writing part of the paper

 

Alycia Gailey

  • Writing the control code
  • Debugging all of the car and control code
  • Soldering together one of the Atmega circuits
  • Soldering the receiver circuit
  • Debugging the wireless system
  • Writing main part of paper

REMOTE CONTROL CODE

 

//Includes

#include<mega32.h>

#include<.\txrx.c>

#include<.\tx module.c>

#include<.\rx module.c>

#include <delay.h>

#include <stdio.h>

#include <stdlib.h>

 

//definitions

#define begin {

#define end }

 

#asm

      .equ __lcd_port=0x15

#endasm

#include <lcd.h>

 

#define tx_id 6

#define LCDwidth 16

#define threshold 100

#define setThreshTX 500

 

 

//declarations

char data[data_length],adc_motor,adc_servo,pwm_motor,pwm_servo,mode;

char lcd_buffer[17];

char cmd_dir,distance,dir,speed,direction[4];

unsigned char b_auto,pushstate0,pushflag0;

unsigned int tx_time,travel_time,timer, threshTX;

char x,y;

 

//**********************************************************

//timer 0 compare ISR

interrupt [TIM0_COMP] void timer0_compare(void)

begin

//timers to decide how long between transmissions and how long to travel    

  if (tx_time>0) tx_time--;

  if (timer>0) timer--;

  if (travel_time>0) travel_time--;     

end 

 

 

//=====================================

void debounce_auto(void) //debounces button on remote control

begin

  switch (pushstate0)

  begin

    case 1:

      if (~(PINB.0))

      begin

        pushstate0 = 2;

        debounce_auto();

      end

      else pushstate0 = 1;  //not necessary

      break;

    case 2:

      if (~(PINB.0))

      begin

        pushstate0 = 3;

        pushflag0 = 1;

      end

      else pushstate0 = 1;

      break;

     case 3:

       if (PINB.0) pushstate0 = 4;

       else pushstate0 = 3;

       break;

     case 4:

       if (~(PINB.0)) pushstate0 = 3;

       else

       begin

         pushstate0 = 1;

         timer = 30;

         //timer0 = 0;

       end

       break;

   end

end

 

//**************************************************

void lcd_update() //this method updates the LCD  with the basic format whenever needed

begin

  //lcd_clear();

  lcd_gotoxy(1,1);

   

    switch (cmd_dir)

    begin

    case 1: {sprintf(lcd_buffer,"N "); break;}

    case 2: {sprintf(lcd_buffer,"NE"); break;}

    case 3: {sprintf(lcd_buffer,"E "); break;}

    case 4: {sprintf(lcd_buffer,"SE"); break;}

    case 5: {sprintf(lcd_buffer,"S "); break;}

    case 6: {sprintf(lcd_buffer,"SW"); break;}

    case 7: {sprintf(lcd_buffer,"W "); break;}

    case 8: {sprintf(lcd_buffer,"NW"); break;}

    end 

    lcd_puts(lcd_buffer);

  lcd_gotoxy(12,1);

    sprintf(lcd_buffer,"%3i",distance);

    lcd_puts(lcd_buffer);

 

  lcd_gotoxy(0,0);

    //sprintf(lcd_buffer,"%i",pwm_servo);

    lcd_putsf("dir");

  lcd_gotoxy(12,0);

    //sprintf(lcd_buffer,"%i",pwm_motor);

    lcd_putsf("dist");

     

  delay_ms(10);

end

 

//**********************************************************

//initialization

void init()

begin

   DDRB=0b11111110;

        //Used for LED array, output

      DDRC = 0xff;

      PORTC=0xff; //starting off

 

      //PORTD used for TX.

      //d7 led, d1 TX, d0 RX, d2..6 unused

      DDRD = 0b11111110;

      PORTD.2=0; //turn d2 led on as power-on led

                         

      //set up timer 0 

      TIMSK=0b10000010;             //turn on timer 0 cmp match ISR

      OCR0 = 250;  //set the compare re to 250 time ticks

      OCR2 = 63;   //set the compare to 250 time ticks, 1 ms time base

      //prescalar to 64 and turn on clear-on-match

      TCCR0 = 0b00001011;

      TCCR2 = 0b00001011;

     

      txrx_init(1,0,249,1);//TX only - 4000 baud - led on

     

//init the A to D converter

  //channel zero/ left adj /INTERNAL Aref 2.56 V

  ADMUX = 0b11100000;  

  //enable ADC and set prescaler to 1/128*16MHz=125,000

  //and clear interrupt enable

  //and start a conversion

  ADCSR = 0b11000111;

  //start another conversion

  ADCSR.6=1;

 

  //LCD Setup, PORTC

  lcd_init(LCDwidth);

  lcd_clear(); 

 

     //init the UART

   //UCSRB = 0x00111000;

   //UBRRL = 103;

 

//init all basic timer variables

  travel_time = 0;

  time = 0; 

  cmd_dir = 1;

  speed = 0;

  mode = 0;

  pushstate0 = 1;

  b_auto = 1; 

  tx_time = 0;

  threshTX = 0;

 

  direction[0] = 3;

  direction[1] = 3;

  direction[2] = 3;

  direction[3] = 3;

 

      #asm ("sei");

end

 

void main(void)

begin

   init();

  

   while(1)

   begin 

//      if(rxdone()==1)

//      begin

//                 PORTD.2 = ~PORTD.2;

//                 k=0;

//                 init_getrx();

//                 while(rx_empty()!=1)

//                 begin       my_rx_data[k] = get_next_rx_data();

//                         k++;

//                 end  

//                 // 0 - 170

//                 // 1 - start_char

//                 // 2 - ID

//                 // 3 - length

//                 // 4 - number

//                 // 5 to x - payload

//                 rx_reset(MAX_RX_LENGTH); 

//                

//                 rx_id = decodeOne(my_rx_data[2]);

//                 if (rx_id == 6)

//                 begin

//                    //payload

//                    i=0; j=1; 

//                    lcd_gotoxy(1,current_line);

//                    lcd_putsf("                ");

//                    while(i<my_rx_data[3])

//                    begin

//                            //PORTD.2 = ~PORTD.2;

//                            sprintf(lcd_buffer,"%-i",my_rx_data[(char)(i+5)]);

//                            lcd_gotoxy(j,current_line);

//                                    lcd_puts(lcd_buffer);

//                                    i++; j=j+4;

//                                    if(j >= 16) i = my_rx_data[3];

//                    end

//                                        //current_line++;    

//                 end

//

//      end         

  

     //mode 0 ==> manual

     //mode 1 ==> automatic

    

     if (timer==0)

       begin

         PORTB.0 = 1;

         PORTB.1 = 0;

         debounce_auto();

       end

       if (pushflag0)

       begin

         if (mode==1) mode = 0;

         else mode = 1;

         pushflag0 = 0;

       end    

      

       ADMUX = 0b01100000;

       ADCSR.6 = 1;

       while (ADCSR.6==1);

       adc_servo = ADCH;     //determine direction  

       ADMUX = 0b01100001;

       ADCSR.6 = 1;

       while (ADCSR.6);      

       adc_motor = ADCH;     //determine car speed

      

       //Convert ADC output to OCR for servo motor PWM

       if (adc_servo<100) pwm_servo = 0;

       else if (adc_servo<130) pwm_servo = 1;

       else if (adc_servo<170) pwm_servo = 2;

       else if (adc_servo<220) pwm_servo = 3;

       else if (adc_servo>220) pwm_servo = 4;

    

       if (adc_motor<90) pwm_motor = 0b00;

       else if (adc_motor<150) pwm_motor=0b10;

       else if (adc_motor<170) pwm_motor = 0b00;

       else if (adc_motor>170) pwm_motor=0b01;    

      

       //allow user to adjust command direction

       ADMUX = 0b01100010;

       ADCSR.6 = 1;

       while (ADCSR.6);

       if (ADCH<32) cmd_dir = 1;

       else if (ADCH<64) cmd_dir = 2;

       else if (ADCH<96) cmd_dir = 3;

       else if (ADCH<128) cmd_dir = 4;

       else if (ADCH<160) cmd_dir = 5;

       else if (ADCH<192) cmd_dir = 6;

       else if (ADCH<224) cmd_dir = 7;

       else cmd_dir = 8; 

//          if (cmd_dir<8) cmd_dir++;

//          else cmd_dir = 1;  

 

         //allow user to adjust distance to travel

       ADMUX = 0b01100011;

       ADCSR.6 = 1;

       while (ADCSR.6);

       distance = ADCH;        

        

       lcd_update();       //update LCD

      

     if (mode==0)  

     begin  

       lcd_gotoxy(4,1);

       lcd_putsf("manual");

       //get accelerometer data for x and y direction       

      

       if (tx_time==0)

       begin        //send OCR values for servo and motor to car

         //PORTD.2 = ~PORTD.2;

         putchar(0xff);

         putchar(0x00);

         send_data(tx_id,pwm_servo,pwm_motor,mode);

         tx_time = threshold; 

       end     

      

     end  //end while manual mode

    

     //Automatic mode*********************************          

     if (mode==1)

     begin

       //for (i = 0; i < 3; i++)

       //begin

       PORTD.2 = ~PORTD.2;

       lcd_gotoxy(4,1);

       lcd_putsf("auto  ");

       if (tx_time==0)

       begin

         putchar(0xff);

         putchar(0x00);

         send_data(tx_id,cmd_dir,distance,mode);    

         tx_time = threshold;

       end

       //end                                    

       //travel_time = distance/10    

       //threshTX = setThreshTX;

       //while (threshTX > 0);    

       //txrx_init(1,1,249,1);//RX only - 4000 baud - led on 

       //while (travel_time>0);

       //begin

       //  delay_ms(1);

       //  get_data();

       //  if (the_data[6])

       //  begin

       //    mode = 0;

       //    break;

       //  end

       //end            

       //mode = 0;

     end                   //when car reaches final destination, back to manual mode

   end //end while

end    //main                          

 

CAR CODE

 

//Includes

#include<mega32.h>

#include<.\txrx.c>

#include<.\car_tx.c>

#include<.\direction.c>

#include <delay.h>

#include <stdio.h>

#include <stdlib.h>

 

//definitions

//definitions

#define MAX_RX_LENGTH 16

#define data_buffer 32

 

//declarations

 

char my_rx_data[MAX_RX_LENGTH];              

 

//abs value

#define abs(a) ((a < 0)? –a : a )

 

//pwm needed to start motor

#define motor_start 5 //00

#define cruise_spd 20 //10

 

#define tx_id 5

 

#define spd_threshold 40 //wait 150ms before updating pwm for motor and steering so the car doesn't blow up

#define txrx_threshold 50 //wait 250ms before txrx again

//declarations

 

//cmd1=servo

//cmd2=motor

//or

//cmd1=direction

//cmd2=distance

char cmd1,cmd2,mode,last_mode,lastspeed,currentspd,current_dir,start,stop,t_stop;

char rx_id;

char lcd_buffer[16], current_line, i, j, k,temp;

//char rx_data[3];

 

unsigned char txrx_time,speed;

unsigned int spd_time,travel_time,t;

 

flash unsigned char servo_array[5] = {40,30,25,15,10};

 

//**********************************************************

//timer 0 compare ISR

interrupt [TIM2_COMP] void timer2_compare(void)

begin     

      if (spd_time<spd_threshold) spd_time++;

      if (txrx_time<txrx_threshold) txrx_time++;

      if (t<travel_time) t++;

      if (t_stop<2000) t_stop++;

      if (t_stop==2000)

      begin

                   my_rx_data[4] = 2;

                   my_rx_data[5] = 0;

                   cmd2 = 0;

       end

end 

//**********************************************************

void get_data(void)

begin

   //PORTD.2 = ~PORTD.2;

   if(rxdone()==1)

        begin

                PORTD.2 = ~PORTD.2;

                k=0;

                init_getrx();

                while(rx_empty()!=1)

                begin      

                        my_rx_data[k] = get_next_rx_data();

                        k++;

                end  

                // 0 - 170

                // 1 - start_char

                // 2 - ID

                // 3 - length

                // 4 - number

                // 5 to x - payload

                rx_reset(MAX_RX_LENGTH); 

               

                 rx_id = decodeOne(my_rx_data[2]); 

                 if (rx_id==6) t_stop = 0;

                 if (t_stop==2000)

                 begin

                   my_rx_data[4] = 2;

                   my_rx_data[5] = 0;

                   cmd2 = 0;

                 end

               

 

        end

end

//**********************************************************

//initialization

void init(void)

begin

      cmd1=2;

      cmd2=0;

      mode=0;

      last_mode = 0;

      t_stop = 0;     //time since the last data packet was received

       

      current_line=1;

     

      current_dir=1;

      lastspeed=0;

  //OCR0 is servo, B.3

  DDRB = 0x0f;

  //pin b.0 is forward/rev select

  PORTB.0=0; 

  PORTB.1=0;

 

  //instead use b.0 and b.1 for motor

  //D.7:OC2:output

  //D.3-6:Compass:inputs

  //D.2:LED:output

  //D.1:transmit:output

  //D.0:receive:input

      DDRD = 0b10000110; 

 

  TCCR0 = 0b01101101;  //prescale timer0 by 1024, fast PWM mode, clear on Comp Match

  TCCR2 = 0b00000100;  //prescale timer0 by 64, normal mode

                               

      //set up timer 0 

      TIMSK=0b10000000;             //turn on timer 2 cmp match ISR

      OCR0 = servo_array[2];          //set the compare re to 250 time ticks

      OCR2 = 250;   //set the compare to 250 time ticks, 1 ms time base

     

      txrx_init(1,1,249,1);//TX and RX - 4000 baud - led on

     

  //init receive info

  rx_reset(MAX_RX_LENGTH);       

  //initialize variables

  PORTD.2 = 0;

  spd_time=0;

  txrx_time=0;

  travel_time = 0;

  temp=0;

  t=0; 

 

  my_rx_data[4] = 2;

  my_rx_data[5] = 0;

  my_rx_data[6] = 0;

     

      #asm ("sei");

end

 

void main(void)

begin 

  init();

 

 

 

  while(1)

  begin

     //do manual mode, update OCR0 every ms   

    

      if (mode!=last_mode)

    begin

//       lcd_gotoxy(1,1);

//                lcd_putsf("                ");

    end

    last_mode = mode;

   

      if (mode==0)

      begin

        get_data();

        

        //PORTD.2 = ~PORTD.2;

      //set current steering and motor speed

                 

                  begin

          //PORTD.2=~PORTD.2; 

          cmd1=my_rx_data[4];

          //motor in manual mode, command distance in auto mode

          cmd2=my_rx_data[5];

          //auto vs manual

          mode=my_rx_data[6];                     

         

                   spd_time = 0;

                   start = 1; 

                   stop = 0; 

                   OCR0 = servo_array[cmd1];

           

            if (cmd2==0)  //no move

            begin

               PORTB.0=0;

               PORTB.1=0;

            end

            else if (cmd2==1)//reverse

            begin

               PORTB.0=1;

               PORTB.1=0;

            end 

            else if (cmd2==2) //forward

            begin

               PORTB.0=0;

               PORTB.1=1;

            end

                             

                              //set steering

            

                  end

      end   //end manual mode

   //do auto mode

      else if ((mode==1)) //& (~stop))

      begin                   

                  PORTD.2=~PORTD.2;

                  begin

            //PORTD.2=~PORTD.2;

            spd_time = 0;       //reset timer

           

            if (start==1)

            begin

              //turn on main motor

              PORTB.1 = 1;

              PORTB.0 = 0;

              t = 0;                   //make motor run for specified time

              start = 0;                 

              //travel time = distance/rate*1000 = distance/(5*5280/3600)*1000

              travel_time = (unsigned int)(cmd2)*140;  //assuming about 5mph

 

            end

            if (t<100)

            begin

              get_data();         //give system 0.1 seconds to collect data packet

              cmd1=my_rx_data[4];

              cmd2=my_rx_data[5];

            end  

            get_data();

            mode = my_rx_data[6];        //always update the mode from remote control

                             

                              current_dir = get_direction();    //always update current direction

     

 

                              //steering                                  

                              temp = current_dir;

                              i = 0;   

                              while (temp!=cmd1)            //determine whether to turn left or right

                              begin

                                    i++;

                                    if (temp<8) temp++;

                                    else temp = 1;

                              end        

                             

                              if (i==0) OCR0 = servo_array[2];       //turn car left, right or straight

                              if (i<5) OCR0=servo_array[0];

                              else OCR0=servo_array[4];

  

            //car reaches final destination

            if ((t==travel_time)) //& (cmd1==current_dir))

            begin

              PORTB.1 = 0;              //turn off motor

              PORTB.0 = 0;

              //stop = 1;           

             

              while (mode==1)

              begin

                get_data();

                mode = my_rx_data[6];

              end                      

            end   

           

                  end      

      end //end if

     

      end//while                    

 

end //main

 

OTHER C FILES

 

Txrx.c by Meghan Desai

//Includes

#include <mega32.h>

 

//definitions

#define MAX_LENGTH_RX 32

#define MAX_LENGTH_TX_PKT 32

#define MAX_LENGTH_TX_DATA 16

#define synch_char 0b10101010  //170

#define start_char 0b11001010  //202

#define end_char 0b11010100    //212

 

//declarations - general

unsigned char txrx_i;

//declarations - RX

unsigned char rx_, decoded_byte, rx_byte, rx_done, rx_length, buffer, rx_started, max_rx_length, rx_led;

char rx_data[MAX_LENGTH_RX];

 

//declarations - TX

unsigned char tx_data_length, tx_pkt_length, tx_id, tx_byte, tx_ing, tx_pkt_byte, tx_data_byte, tx_led;        

char tx_data[MAX_LENGTH_TX_PKT];

char in_data[MAX_LENGTH_TX_DATA];

 

//the encoding...

flash char code[16] = {0b10001011,0b10001101,0b10010011,0b10010101,0b10010110,

0b10011001,0b10011010,0b10011100,0b10100011,0b10100101,0b10100110,0b10101001,

0b10101100,0b10110001,0b10110010,0b10110100};

 

 

/****************************************************************************************

** GENERAL FUNCTIONS ********************************************************************

****************************************************************************************/

 

//**********************************************************

void txrx_init(int tx, int rx, int baud_num, char led) //00-none, 10-tx only, 01-rx only, 11-both

{

      //setup the transmission...

      UCSRB.0 = 0;  //Bit 0 - TXB8

      UCSRB.1 = 0;  //Bit 1 - RXB8

      UCSRB.2 = 0;  //Bit 2 - UCSZ2

      UCSRB.3 = tx; //Bit 3 - TXEN

      UCSRB.4 = rx; //Bit 4 - RXEN

      UCSRB.5 = tx; //Bit 5 - UDRIE

      UCSRB.6 = 0;  //Bit 6 - TXCIE

      UCSRB.7 = rx; //Bit 7 - RXCIE

      UBRRL = baud_num;   

      tx_byte=MAX_LENGTH_TX_PKT;

      tx_ing=0;

        tx_led=led;

      rx_done=1;

        rx_led=led;

 }

 

//**********************************************************

char decodeOne(char msbyte)

{

      txrx_i=0;

        buffer=start_char;

      while(txrx_i<16) {

                  if(msbyte==code[txrx_i])

                              buffer=txrx_i;

                  txrx_i++;

        }

        return buffer;

}

 

//**********************************************************

char decode(char msbyte, char lsbyte)

{

      txrx_i=0;

      while(txrx_i<16) {

                  if(msbyte==code[txrx_i])

                              buffer=txrx_i;

                  txrx_i++;

      }

      txrx_i=0;

      while(txrx_i<16) {

                  if(lsbyte==code[txrx_i]) {

                              buffer = (buffer<<4);

                              buffer = buffer | txrx_i;

                  }

                  txrx_i++;

      }

      return buffer;

}

 

 

/****************************************************************************************

** RECEIVE SIDE FUNCTIONS ***************************************************************

****************************************************************************************/

 

//**********************************************************

//RX Complete ISR

interrupt [14] void RX_complete(void)

{

        if(rx_done==0)

        {

              if (rx_byte==0)

                  {

                              rx_data[rx_byte]=UDR;

                 

                          if(rx_data[0]==synch_char) rx_started=1;

                              else rx_started=0;

                                          

                          if(rx_data[0]==synch_char & rx_started)

                              {

                              rx_byte++;

                                if(rx_led==1) PORTD.7=0;

                              }                                             

              }

                  else if (rx_byte==1)

              {

                              rx_data[rx_byte]=UDR;

                  if(rx_data[0]==synch_char && rx_data[1]==start_char)

                          {

                                          rx_byte++;

                              }                                             

                  }             

              else

                  {

                  rx_data[rx_byte]=UDR;               

                          if (rx_byte==4)

                              {

                          rx_length = decode(rx_data[3],rx_data[4]);                                 

                          }             

                              else if(rx_byte>=max_rx_length || rx_data[rx_byte]==212)

                  {

                              if(rx_led==1) PORTD.7=1;

                                      rx_done=1;

                              }

                  rx_byte++;

                  }                               

        } 

}

 

//**********************************************************

void rx_reset(char max_rx)

{

      rx_done=0;

      rx_started=0;

      rx_length=0;

             rx_byte=0;

      max_rx_length=max_rx;

}

 

//**********************************************************

char rxdone(void) { return rx_done; }

 

//**********************************************************

//RECEIVE SIDE ITERATOR

void init_getrx(void) { rx_ = 0; }

        

char get_next_rx_data(void)

{

        if(rx_ <= 2) return rx_data[rx_++];

        else {

                decoded_byte = decode(rx_data[rx_],rx_data[(char)(rx_+1)]);

                rx_ = rx_ + 2;

                return decoded_byte;

        }

}

 

char rx_empty(void) { return (rx_ < max_rx_length) ? 0 : 1; }

 

 

/****************************************************************************************

** TRANSMIT SIDE FUNCTIONS **************************************************************

****************************************************************************************/

 

//**********************************************************

//UDR Empty ISR

interrupt [15] void udr_empty(void)

{     

      if(tx_byte< tx_pkt_length)

      {

                  UDR=tx_data[tx_byte];

                  tx_byte++;

                  if(tx_led==1);// PORTC.7=0;

      }                                             

      if(tx_byte==tx_pkt_length)

      {

                  if(tx_led==1);// PORTC.7=1;

                tx_ing=0;

      }

} 

 

//**********************************************************

void encode(void)

{

        //sunchronization.

      tx_data[0] = synch_char; //calibration

      tx_data[1] = synch_char; //calibration

      tx_data[2] = synch_char; //synchronization

      tx_data[3] = synch_char; //synchronization

 

        //start character

      tx_data[4] = start_char;

 

        //1-byte of ID

      tx_data[5] = code[tx_id]; //Channel Id, static 0 for this unit

 

        //2-bytes of length

      tx_data[6] = code[tx_data_length>>4]; //length of data to expect.

      tx_data[7] = code[(tx_data_length<<4)>>4]; //length of data to expect.

 

        //Payload

        tx_pkt_byte=8; tx_data_byte=0;

        while(tx_data_byte<tx_data_length)

        {

                  tx_data[tx_pkt_byte] = code[in_data[tx_data_byte]>>4];

              tx_data[(char)(tx_pkt_byte+1)] = code[(in_data[tx_data_byte]<<4)>>4];

              tx_pkt_byte=tx_pkt_byte+2;

              tx_data_byte++;

        }

 

        //End character

      tx_data[tx_pkt_length-1] = end_char;

}            

 

//**********************************************************

void tx_me(char tx_data[], int length, int id)

{

        if(tx_ing==0)

        {

                txrx_i=0;

                while(txrx_i < length)

                {

                        in_data[txrx_i]=tx_data[txrx_i];

                        txrx_i++;

                }

                tx_id=id;

                tx_data_length=length;

                tx_pkt_length=length*2+9;

                encode();              

                tx_byte=0;

                tx_ing=1;

        }               

}

 

//**********************************************************

char txdone(void) { return tx_ing; }

 

tx_module

//Transmit module

 

//Includes

#include<mega32.h>

 

//definitions

#define begin {

#define end }

#define data_length 4

 

 

char Ainx,Ainy,ID,m;

unsigned int time;

 

 

 

//**********************************************************

void send_data(char ID, char Ainx, char Ainy, char m)

begin

  char data[data_length];

  data[0] = Ainx;   //x direction or command direction

  data[1] = Ainy;   //y direction or command distance

  data[2] = m;      //mode                                 

 

  tx_me(data, data_length, ID);

  if (txdone()) PORTD.2=~PORTD.2;

  PORTD.2 = ~PORTD.2;

 

end 

 

void send_compass_data(char ID, char d4, char d5, char d6, char d7)

begin

  char data[4];

  data[0] = d4;

  data[1] = d5;

  data[2] = d6;

  data[3] = d7;

 

  tx_me(data,4,ID);

end

 

car_rx.c

//Receive Unit

 

//Includes

#include<mega32.h>

#include<delay.h>

//#include<.\txrx.c>

#include <stdio.h>

 

//definitions

#define MAX_RX_LENGTH 3

#define data_buffer 32

 

//declarations

 

char my_rx_data[MAX_RX_LENGTH];

char done, length, last_replaced;

char seen_ids[4];

 

//**********************************************************

void get_data(void)

begin

      if(rxdone()==1)

      begin   

               

                k=0;

                init_getrx();

                while(rx_empty()!=1)

                begin

                          my_rx_data[k] = get_next_rx_data();

                        k++;

                end  

                // 0 - 170

                // 1 - start_char

                // 2 - ID

                // 3 - length

                // 4 - number

                // 5 to x - payload

                rx_reset(MAX_RX_LENGTH);                                  

                //if (my_rx_data[2]==rx_id) return my_rx_data;

                             

      end

end //get_data

 

direction.c

#include <Mega32.h>

#include <stdio.h>

#include <stdlib.h>

#include <delay.h>

 

 

char NW,NE,SW,SE,N,W,S,E;

 

char get_direction(void)

begin

  char dir;

 

  if ((PIND.3)&(PIND.4)&(PIND.5)) dir=8;        1110

  else if ((PIND.3)&(PIND.4)&(PIND.6)) dir=2;   1101

  else if ((PIND.3)&(PIND.4)) dir=1;            1100

  else if ((PIND.3)&(PIND.5)&(PIND.6)) dir=4;   1011

  else if ((PIND.4)&(PIND.5)&(PIND.6)) dir=6;   0111

  else if ((PIND.5)&(PIND.6)) dir=5;            0011

  else if ((PIND.4)&(PIND.5)) dir=7;            0110

  else if ((PIND.3)&(PIND.6)) dir=3;            1001

 

  return dir;

end//get_direction

 

REFERENCES

 

1.      Meghan Desai’s wireless protocol page: http://instruct1.cit.cornell.edu//courses/eceprojectsland/STUDENTPROJ/2005to2006/mpd25/report.html

2.      H Bridge design from Tytus and Daniel’s page: http://instruct1.cit.cornell.edu/courses/ee476/FinalProjects/s2007/djd33_tm253/final/final.htm

3.      IEEE code of ethics

4.      http://www.ieee.org/portal/pages/iportals/aboutus/ethics/code.html

5.      Lab 1 description of LCD pinout

6.      http://instruct1.cit.cornell.edu/courses/ee476/labs/s2008/lab1.html

7.      MMA6261Q datasheet

8.      RCT-433-AS datasheet

9.      RCR-433-RP datasheet

10.  7404 datasheet

11.  1490 Digital Sensor datasheet

 

ACKNOWLEDGEMENTS

 

We would like to thank Professor Bruce Land for having this class and letting us have as much freedom as we want with building all sorts of exciting projects.  We also wanted to thank him for his patience with all the trash and wires everywhere.  Additionally, we wanted to thank all the TA’s and Professor Belina for their advice and help in difficult situations, especially Tytus for his H Bridge design.  We wanted to thank Matt Meister for helping us get our ATMEL ATMega32’s sampled. We thank Newhaven Technologies for letting us sample their LCD’s.  Finally, we want to thank Jack’s friend for his R/C car parts.

 

Back to the Top