Monday, May 28, 2012
FM Radio Aux Input Mod.
The car i currently drive is a Ford 92' Taurus. The radio sucks; its as simple as that. It has a broken tape player, but the radio and the coordinating buttons still work. Sometimes i wanted to listen to my iPod and not the radio, so I decided a few weeks ago that i wanted AUX input.
But what would be any Aux input without a charger? I guessed, and confirmed with a DMM, that the ICs ran on 5v. Then i connected a USB female plug to the GND and +5v pins i found in the radio to create a charger. This doesn't work for iPod because Apple is annoying and certain voltages have to be on D+ and the D- pins of the USB for it to start charging. With older generations like my iPod Touch 1G, the voltages are less picky, but the voltages have to be more precise for newer generations. If D+ and D- are both 2v it charges at about 500ma(like a computer).With 2.8v on D- and 2v on D+ it charges at about 1A. Pinout below:
First, of course, i took the radio out of the car. Ford wants you to buy some stupid tool to get it out, but instead i used a clothes hanger :P. Basically, you push some long metal rods into the 1/8in holes(four of them, but you can do one side at a time, so two at a time.) on the front of the radio and this pushes in the springs and releases the radio.
On the back it has two harnesses and one-wire interface that runs to Volume, Preset, and Seek buttons next to the steering wheel(very useful, I think :)). One harness is for the power and other stuff like that, and the other goes to the speakers(the amp is built into the radio).
To take the top cover off of the radio, it was just some Torx screw and i easily got them out with this handy tool set.
Taking out the tape deck to get to the PCB.
Some nice little solenoids and a motor in there.
Radio top PCB
Radio bottom PCB
Audio amps on the heatsink
The Radio IC decodes the FM signal to audio, i think. Tape decoding IC does similar. Mixer IC takes the Tape and FM audio signals and switches between them/adds EQ/balance/fade etc. The main IC controls all of the other ICs/LCD/buttons/
Now comes the real reverse engineering. At first i was going to find the tape audio line, as it didn't work anyways, and inject aux audio there. As i was trying to find this tape audio line, i found it very difficult; I had narrowed it down to a few wires coming form the tape IC area going to the mixer, but i didn't know for sure. So i connected a little speaker(GND and the other wire free) and probed around on the mixer IC to find the audio lines. With this method i found both the FM and tape audio lines. Since the volume is adjusted by the buttons in the mixer IC and not in the tape decoding area on the PCB, i cut the tape audio trace and soldered in the headphone jack. If i didn't cut the trace, the mixer IC would receive noise from the tape decoding ICs and the aux audio at the same time which would be bad. I threw in a tape(so the radio changes to that channel in the mixer IC) and hoped for the best. Surprise, it didn't work very well! The audio was only playing on the left side, and sounded really distorted. After i thought about it, i found it strange that i only found one input line from the tape ICs to the mixer chip when it is stereo audio. I went back to the PCB and found another trace that ran form the tape ICs to the mixer, but when i played a tape, this line pure noise; strange, right? I went through all the pins on the mixer IC with the little speaker and i could still only find one audio channel for both the tape and FM that sounded like audio and not distortions. Maybe the tape and FM ICs put out a LR mixed audio line that is later split? I dunno. So that plan didn't work.
Next i though maybe i should bypass the mixer IC all together and inject the aux audio into the amps directly. I could turn the radio volume all the way down so nothing was coming out of the mixer IC(since that is where the volume is adjusted) to the the amps and so the aux input shouldn't have any noise issues. But first i had to find the audio line inputs to the amps.
I knew that harness A(look at the pic below) was for power etc, so mostly ignored that, but harness B is for the speakers. Every other pin on B is GND and between the GNDs are the speaker pins. From looking inside i already knew where the speaker amps were, but i couldn't find ANY info on them because they are propitiatory. So i had to use a DMM(multimeter) and its continuity function to trace backwards. I forgot that i cant test continuity through capacitors, so that plan didn't work until i tested for continuity until the caps, and then jumped to the other side of the cap and continued testing. I drew a little pinout on paper of the IC and used process of elimination to find the audio inputs to the amps. I started by using harness B to find the out put pins on the amp ICs and crossing those out on my pinout, and then i found GND, VCC, and any other pins that were linked together(if they were linked together, it wouldn't make sense that they were inputs, would they?) In the end, it was pretty obvious; four small traces(for each speaker) running to the mixer IC.
Since the audio jacks on pretty much any Mp3 player has only left and right, i soldered the front/back left speaker amp inputs together and connected that to the left pin of the audio jack and did the same for the right. I turned down the volume to 0 on the radio, connected the iPod to the audio jack and it worked! Crisp, clean and on demand music! To use the FM radio, i just unplug the iPod and turn the volume back up on the radio
But, how do i get these voltages? A voltage divider, of course. Below, V1 is 5v, 0V is GND, V2 is the output voltage, in my case. R1 and R2 are resistors and depending on their values/ratio, the voltage output on V2 varies. This is a great calculator for finding what resistors you need. i found that with R1 being 68kohms and R2 being 47kohms, i get 2v on V2. With R1 being 22kohms and R2 being 27kohms, i get 2.75v on V2. But the radio voltage wasn't exactly 5c; more like 4.91, so that changes the values a bit.
One thing that i found strange was that the voltage from the divider on both pins was .2v lower than the calculator said it would be. Even though the Radio was putting out 4.91v, that still isnt enough to drop the voltage .2v. So i choose resistors that would give me 3v theoretically on D-(2.8v really) and 2.2v on D+(2v really). It worked great; I got almost exactly 2.8 and 2v.
Adding in the USB and audio jack.
Used some Sugru to hold the connectors in.
Still, i have one last issue. If i plug in both the charger and the audio cable in at once, i get this strange ticking sound through the speakers, but not when i have just the audio cable. Maybe add a cap to the usb plug? But, overall, this mod worked out quite well!
Sunday, May 13, 2012
er9x Quadcopter Setup
Now that i have the er9x firmware on the Turnigy 9x, i have to change some of the settings so that it works with MWC. I'd recommend you read through the er9x manual here. It tells you how to navigate the menus and pretty much anything you need to do to get started.
First, you need to calibrate the sticks so that the TX knows how far the sticks can move. My values were completely wrong when i first flashed the er9x firmware, so this is required.
1. Press and hold the + button for a second to get into the radio setup menu.
2. Press + again to get to screen 6/6
3. Follow onscreen instructions.
Next, I changed some cosmetic settings.
1. go to the Radio Setup menu again and go to 1/6, Use the UP/DN buttons to move between options.
2. Set the Owner Name by moving to it and then pressing Menu. Now use UP/DN to change the character and +/- to change position in the name.
3. I also changed the Beeper(how loud it is), Battery Warning(good for use with LiPos), and Inactivity Alarm(if i forget to turn it off :)) options here. You can change the TX Mode at the very last option(default is 2).
Now for the more useful mixing, trim, channel setup.
1. From the home screen, press and hold the - button for a second to get into the Model Setup screen.
2. All of the lines should be blank and the first one having a * next to it meaning it is selected.
3. Now press - to go to 2/10; Name, Trim Inc, Proto PPM, E. Limits, and Trainer are the settings you might have to change.
4. Go to the 10/10 menu and select the Simple 4Ch Template; its a good way to start.
5. Go to 5/10; this has tons of features, but all we want to do is change which channels output what signal. Move down until Ch1 is selected, then press menu. The top option of the menu is the only one that needs to be changed. I choose AIL(roll) because the Ch1 port on the RX is connected to the roll pin on the MWC board. Once you have the correct setting, press exit. Now go down until Ch2 is underlined and do the same as you did for Ch1. And then for Ch3, Ch4, Ch5(AUX 1 for me), Ch6(AUX 2 for me).
AIL is roll, ELE is pitch, THR is throttle, RUD is yaw, P1 is the Hov.Pit pot, P2 is the Gear pot, P3 is the Pit. Trim pot.
6. Go to 6/10; this allows you to change Subtrim(what the center value of the stick is when centered), and End Points(what the values are for when the stick is all the way UP/DN or L/R.) Subtrim for each channel needs to be at 1500, and the low point needs to be 1000, high point 2000. They wont be perfect, but get them as close as possible(you can set a dead zone with the MWC code if you need too). The first column is Subtrim, next is low point, last is high point. The row is for each channel that you just set is step 5. So, use UN/DN to move between channels, and +/- to move between Subtrim/low/high, Menu to select value to change, then +/- to change value, Menu to finalize value. This is where the MWC computer GUI comes in; it allows you to see in live time where the center is of each channel and how high/low they go. So, get your quad all powered up and connected to the GUI.
a. Move all sticks(and AUX pots) to center, and then set the subtrim with +/- for each channel so that all values in the GUI are very close to 1500.
b. Setting low/high points; for Ch1 I choose AIL(roll), so ill select the low point for Ch1, move the AIL(roll) stick all the way to the left, then adjust the vlaue with +/- until it is at 1000 int the GUI for roll. Then I'll select the high point and move the stick all the way to the right and then adjust the vlaue with +/- until it is at 2000 int the GUI for roll.
Aux Switches/Channels
Here i will go through how to setup two Aux channels(5+6) for the 3pos F.MODE switch and the 2Pos AIL switch.
1. Go to the Mixer(5/10) and go down so that your first Aux channel(most likely CH5) is underlined and hold menu(this creates a MIX in CH5).
2. Source should be Half, Weight -100, then go down to Switch, and change it to ID0. Go back to the previous menu(EXIT button).
3. Make sure CH5 is underlined again and hold menu. This creates another MIX in CH5.
4. Source should be Half, Weight 100, then go down to Switch, and change it to ID2. Go back to the previous menu.
5. Done with 3Pos.
6. Move the cursor so that CH6 is underlined and hold menu.
7. Source should be FULL, Weight 100, then go down to Switch, and change it to AIL. This is not the AIL stick, it is the AIL switch; this also applies for the ELE, THR, RUD switches. Look at page 9 of the manual PDF to see the names of the switches(or just look on the TX). Go back to the previous menu.
8. Done! Now go to the main screen and press DN until you get to the screen that shows you the current values of the channels. Test the switches. Now you can go into the MitiWii config GUI and change the function of the switches.
What about Trainer mode?
1. After i got all of the stuff setup for regular control, i went back to 1/10 in the Model Setup menu, moved to the model I made, and held down Menu(this duplicates the model). Then i changed the name to Trainer or something so i could distinguish between the two. Then i went to the Radio Setup 1/6, and turned on trainer mode.
2. 2/6 of Radio Setup is the Trainer menu. The THR on the left stands for your THR channel. Change OFF to := if you want the trainer to have complete control of the channel, += means they have partial control. Change the % to the percentage of the channel you want the trainer to control. Then change the ch1 to the channel of the trainer that is THR(or AIL or whatever).
HUURAY YOUR DONE!
First, you need to calibrate the sticks so that the TX knows how far the sticks can move. My values were completely wrong when i first flashed the er9x firmware, so this is required.
1. Press and hold the + button for a second to get into the radio setup menu.
2. Press + again to get to screen 6/6
3. Follow onscreen instructions.
Next, I changed some cosmetic settings.
1. go to the Radio Setup menu again and go to 1/6, Use the UP/DN buttons to move between options.
2. Set the Owner Name by moving to it and then pressing Menu. Now use UP/DN to change the character and +/- to change position in the name.
3. I also changed the Beeper(how loud it is), Battery Warning(good for use with LiPos), and Inactivity Alarm(if i forget to turn it off :)) options here. You can change the TX Mode at the very last option(default is 2).
Now for the more useful mixing, trim, channel setup.
1. From the home screen, press and hold the - button for a second to get into the Model Setup screen.
2. All of the lines should be blank and the first one having a * next to it meaning it is selected.
3. Now press - to go to 2/10; Name, Trim Inc, Proto PPM, E. Limits, and Trainer are the settings you might have to change.
4. Go to the 10/10 menu and select the Simple 4Ch Template; its a good way to start.
5. Go to 5/10; this has tons of features, but all we want to do is change which channels output what signal. Move down until Ch1 is selected, then press menu. The top option of the menu is the only one that needs to be changed. I choose AIL(roll) because the Ch1 port on the RX is connected to the roll pin on the MWC board. Once you have the correct setting, press exit. Now go down until Ch2 is underlined and do the same as you did for Ch1. And then for Ch3, Ch4, Ch5(AUX 1 for me), Ch6(AUX 2 for me).
AIL is roll, ELE is pitch, THR is throttle, RUD is yaw, P1 is the Hov.Pit pot, P2 is the Gear pot, P3 is the Pit. Trim pot.
6. Go to 6/10; this allows you to change Subtrim(what the center value of the stick is when centered), and End Points(what the values are for when the stick is all the way UP/DN or L/R.) Subtrim for each channel needs to be at 1500, and the low point needs to be 1000, high point 2000. They wont be perfect, but get them as close as possible(you can set a dead zone with the MWC code if you need too). The first column is Subtrim, next is low point, last is high point. The row is for each channel that you just set is step 5. So, use UN/DN to move between channels, and +/- to move between Subtrim/low/high, Menu to select value to change, then +/- to change value, Menu to finalize value. This is where the MWC computer GUI comes in; it allows you to see in live time where the center is of each channel and how high/low they go. So, get your quad all powered up and connected to the GUI.
a. Move all sticks(and AUX pots) to center, and then set the subtrim with +/- for each channel so that all values in the GUI are very close to 1500.
b. Setting low/high points; for Ch1 I choose AIL(roll), so ill select the low point for Ch1, move the AIL(roll) stick all the way to the left, then adjust the vlaue with +/- until it is at 1000 int the GUI for roll. Then I'll select the high point and move the stick all the way to the right and then adjust the vlaue with +/- until it is at 2000 int the GUI for roll.
Aux Switches/Channels
Here i will go through how to setup two Aux channels(5+6) for the 3pos F.MODE switch and the 2Pos AIL switch.
1. Go to the Mixer(5/10) and go down so that your first Aux channel(most likely CH5) is underlined and hold menu(this creates a MIX in CH5).
2. Source should be Half, Weight -100, then go down to Switch, and change it to ID0. Go back to the previous menu(EXIT button).
3. Make sure CH5 is underlined again and hold menu. This creates another MIX in CH5.
4. Source should be Half, Weight 100, then go down to Switch, and change it to ID2. Go back to the previous menu.
5. Done with 3Pos.
6. Move the cursor so that CH6 is underlined and hold menu.
7. Source should be FULL, Weight 100, then go down to Switch, and change it to AIL. This is not the AIL stick, it is the AIL switch; this also applies for the ELE, THR, RUD switches. Look at page 9 of the manual PDF to see the names of the switches(or just look on the TX). Go back to the previous menu.
8. Done! Now go to the main screen and press DN until you get to the screen that shows you the current values of the channels. Test the switches. Now you can go into the MitiWii config GUI and change the function of the switches.
What about Trainer mode?
1. After i got all of the stuff setup for regular control, i went back to 1/10 in the Model Setup menu, moved to the model I made, and held down Menu(this duplicates the model). Then i changed the name to Trainer or something so i could distinguish between the two. Then i went to the Radio Setup 1/6, and turned on trainer mode.
2. 2/6 of Radio Setup is the Trainer menu. The THR on the left stands for your THR channel. Change OFF to := if you want the trainer to have complete control of the channel, += means they have partial control. Change the % to the percentage of the channel you want the trainer to control. Then change the ch1 to the channel of the trainer that is THR(or AIL or whatever).
HUURAY YOUR DONE!
Turnigy 9x TX Mods
The Turnigy 9x is a great beginner TX, but it definitely needs some adjustments to make it better. I first added a reliable battery, then fixed the trainer mode, and finally, put the er9x firmware on it.
Battery Mod
This is almost a must do because the stock AA holder is very unreliable and can become disconnected during flight(yikes, quad on the loose...). I've seen two main fixes for this; Li-Ion batteries, or Li-Fe/Po transmitter packs.
Trainer Mode Fix
I did this mod so that i don't have to unplug the transmitter module in the TX every time I want to do trainer mode or use the 9x for a simulator.
Cut the trace with an knife and make sure that it is cut with a multimeter. Now, you have two options; you can put a 1k resistor in between the two blue circled solder points, or, you can scrape off the light green part
above and below the trace until you hit copper, and then solder the 1k resistor on the two scraped off places.
er9x Firmware
This mod replaces the stock Turnigy firmware and replaces it with the er9x firmware which has way more features, and maybe even the best part; it disables the beeper if you want! This requires a AVR ISP of some sort; i used a Arduino, but those other fancy ones work too :). You''ll obviously need to crack open your case, so you might as well do the trainer mode fix at the same time.
Inside the TX:
You can either just have a cable that comes out(through a pre-made hole in the battery compartment) like i did, or you can cut a little hole in the case a put a standard ISP header in there(probably better).
Now you need to get the Arduino ready. ...And this is where it went all wrong. Apparently the Arduino ISP sketch that is provided with the IDE isn't fully working in 1.0, so i had to change a few things to get it to work.
1. The serial buffer size needs to be changed so that the Arduino can keep up with the data(er9x firmware) being sent to it over serial. To do this, find your arduino directory(arduino-1.0 somewhere), then go to: arduino-1.0\hardware\arduino\cores\arduino\ and open HardwareSerial.cpp in a text editor. Search for #define SERIAL_BUFFER_SIZE and change its value from 64 to 128.
2. The current Arduino ISP sketch cant handle the large EEPROM, so use this sketch instead. Upload it now:
Now for the Arduino hardware side:
1. Unplug your Arduino form the computer and connect the wires from the TX that you just soldered as follows:
Arduino Pin: 9x transmitter
10: RST
11: MOSI
12: MISO
13: SCK
5v: 5v
Gnd: Gnd
Joystick PPM Interface
This isn't really a mod, but it should make flying easier by hooking up a PC gaming joystick. Nothing too fancy, just a old game port joystick that is connected to a Arduino which sends PPM signals to the TX trainer port.......later
Battery Mod
This is almost a must do because the stock AA holder is very unreliable and can become disconnected during flight(yikes, quad on the loose...). I've seen two main fixes for this; Li-Ion batteries, or Li-Fe/Po transmitter packs.
Trainer Mode Fix
I did this mod so that i don't have to unplug the transmitter module in the TX every time I want to do trainer mode or use the 9x for a simulator.
![]() |
| First, take out all the screws in the back of the case |
![]() |
| and unplug the cable connecting the two halves. |
![]() |
| Look at the back half and find the trace pointed to in the picture. |
![]() |
| Done! |
er9x Firmware
This mod replaces the stock Turnigy firmware and replaces it with the er9x firmware which has way more features, and maybe even the best part; it disables the beeper if you want! This requires a AVR ISP of some sort; i used a Arduino, but those other fancy ones work too :). You''ll obviously need to crack open your case, so you might as well do the trainer mode fix at the same time.
Inside the TX:
![]() |
| Once you have your TX open, solder wires onto the labeled solder pads. |
![]() |
| Then, label the other ends of the wires so you don't mix them up later(that would be bad). |
Now you need to get the Arduino ready. ...And this is where it went all wrong. Apparently the Arduino ISP sketch that is provided with the IDE isn't fully working in 1.0, so i had to change a few things to get it to work.
1. The serial buffer size needs to be changed so that the Arduino can keep up with the data(er9x firmware) being sent to it over serial. To do this, find your arduino directory(arduino-1.0 somewhere), then go to: arduino-1.0\hardware\arduino\cores\arduino\ and open HardwareSerial.cpp in a text editor. Search for #define SERIAL_BUFFER_SIZE and change its value from 64 to 128.
2. The current Arduino ISP sketch cant handle the large EEPROM, so use this sketch instead. Upload it now:
// this sketch turns the Arduino into a AVRISP
// using the following pins:
// 10: slave reset
// 11: MOSI
// 12: MISO
// 13: SCK
// Put an LED (with resistor) on the following pins:
// 9: Heartbeat - shows the programmer is running
// 8: Error - Lights up if something goes wrong (use red if that makes sense)
// 7: Programming - In communication with the slave
//
// October 2010 by Randall Bohn
// - Write to EEPROM > 256 bytes
// - Better use of LEDs:
// -- Flash LED_PMODE on each flash commit
// -- Flash LED_PMODE while writing EEPROM (both give visual feedback of writing progress)
// - Light LED_ERR whenever we hit a STK_NOSYNC. Turn it off when back in sync.
//
// October 2009 by David A. Mellis
// - Added support for the read signature command
//
// February 2009 by Randall Bohn
// - Added support for writing to EEPROM (what took so long?)
// Windows users should consider WinAVR's avrdude instead of the
// avrdude included with Arduino software.
//
// January 2008 by Randall Bohn
// - Thanks to Amplificar for helping me with the STK500 protocol
// - The AVRISP/STK500 (mk I) protocol is used in the arduino bootloader
// - The SPI functions herein were developed for the AVR910_ARD programmer
// - More information at http://code.google.com/p/mega-isp
#include "pins_arduino.h" // defines SS,MOSI,MISO,SCK
#define SCK 13
#define MISO 12
#define MOSI 11
#define RESET 10
#define LED_HB 9
#define LED_ERR 8
#define LED_PMODE 7
#define PROG_FLICKER true
#define HWVER 2
#define SWMAJ 1
#define SWMIN 18
// STK Definitions
#define STK_OK 0x10
#define STK_FAILED 0x11
#define STK_UNKNOWN 0x12
#define STK_INSYNC 0x14
#define STK_NOSYNC 0x15
#define CRC_EOP 0x20 //ok it is a space...
void pulse(int pin, int times);
void setup() {
Serial.begin(19200);
pinMode(LED_PMODE, OUTPUT);
pulse(LED_PMODE, 2);
pinMode(LED_ERR, OUTPUT);
pulse(LED_ERR, 2);
pinMode(LED_HB, OUTPUT);
pulse(LED_HB, 2);
}
int error=0;
int pmode=0;
// address for reading and writing, set by 'U' command
int here;
uint8_t buff[256]; // global block storage
#define beget16(addr) (*addr * 256 + *(addr+1) )
typedef struct param {
uint8_t devicecode;
uint8_t revision;
uint8_t progtype;
uint8_t parmode;
uint8_t polling;
uint8_t selftimed;
uint8_t lockbytes;
uint8_t fusebytes;
int flashpoll;
int eeprompoll;
int pagesize;
int eepromsize;
int flashsize;
}
parameter;
parameter param;
// this provides a heartbeat on pin 9, so you can tell the software is running.
uint8_t hbval=128;
int8_t hbdelta=8;
void heartbeat() {
if (hbval > 192) hbdelta = -hbdelta;
if (hbval < 32) hbdelta = -hbdelta;
hbval += hbdelta;
analogWrite(LED_HB, hbval);
delay(40);
}
void loop(void) {
// is pmode active?
if (pmode) digitalWrite(LED_PMODE, HIGH);
else digitalWrite(LED_PMODE, LOW);
// is there an error?
if (error) digitalWrite(LED_ERR, HIGH);
else digitalWrite(LED_ERR, LOW);
// light the heartbeat LED
heartbeat();
if (Serial.available()) {
avrisp();
}
}
uint8_t getch() {
while(!Serial.available());
return Serial.read();
}
void fill(int n) {
for (int x = 0; x < n; x++) {
buff[x] = getch();
}
}
#define PTIME 30
void pulse(int pin, int times) {
do {
digitalWrite(pin, HIGH);
delay(PTIME);
digitalWrite(pin, LOW);
delay(PTIME);
}
while (times--);
}
void prog_lamp(int state) {
if (PROG_FLICKER)
digitalWrite(LED_PMODE, state);
}
void spi_init() {
uint8_t x;
SPCR = 0x53;
x=SPSR;
x=SPDR;
}
void spi_wait() {
do {
}
while (!(SPSR & (1 << SPIF)));
}
uint8_t spi_send(uint8_t b) {
uint8_t reply;
SPDR=b;
spi_wait();
reply = SPDR;
return reply;
}
uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
uint8_t n;
spi_send(a);
n=spi_send(b);
//if (n != a) error = -1;
n=spi_send(c);
return spi_send(d);
}
void empty_reply() {
if (CRC_EOP == getch()) {
Serial.print((char)STK_INSYNC);
Serial.print((char)STK_OK);
} else {
error++;
Serial.print((char)STK_NOSYNC);
}
}
void breply(uint8_t b) {
if (CRC_EOP == getch()) {
Serial.print((char)STK_INSYNC);
Serial.print((char)b);
Serial.print((char)STK_OK);
}
else {
error++;
Serial.print((char)STK_NOSYNC);
}
}
void get_version(uint8_t c) {
switch(c) {
case 0x80:
breply(HWVER);
break;
case 0x81:
breply(SWMAJ);
break;
case 0x82:
breply(SWMIN);
break;
case 0x93:
breply('S'); // serial programmer
break;
default:
breply(0);
}
}
void set_parameters() {
// call this after reading paramter packet into buff[]
param.devicecode = buff[0];
param.revision = buff[1];
param.progtype = buff[2];
param.parmode = buff[3];
param.polling = buff[4];
param.selftimed = buff[5];
param.lockbytes = buff[6];
param.fusebytes = buff[7];
param.flashpoll = buff[8];
// ignore buff[9] (= buff[8])
// following are 16 bits (big endian)
param.eeprompoll = beget16(&buff[10]);
param.pagesize = beget16(&buff[12]);
param.eepromsize = beget16(&buff[14]);
// 32 bits flashsize (big endian)
param.flashsize = buff[16] * 0x01000000
+ buff[17] * 0x00010000
+ buff[18] * 0x00000100
+ buff[19];
}
void start_pmode() {
spi_init();
// following delays may not work on all targets...
pinMode(RESET, OUTPUT);
digitalWrite(RESET, HIGH);
pinMode(SCK, OUTPUT);
digitalWrite(SCK, LOW);
delay(50);
digitalWrite(RESET, LOW);
delay(50);
pinMode(MISO, INPUT);
pinMode(MOSI, OUTPUT);
spi_transaction(0xAC, 0x53, 0x00, 0x00);
pmode = 1;
}
void end_pmode() {
pinMode(MISO, INPUT);
pinMode(MOSI, INPUT);
pinMode(SCK, INPUT);
pinMode(RESET, INPUT);
pmode = 0;
}
void universal() {
int w;
uint8_t ch;
fill(4);
ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]);
breply(ch);
}
void flash(uint8_t hilo, int addr, uint8_t data) {
spi_transaction(0x40+8*hilo,
addr>>8 & 0xFF,
addr & 0xFF,
data);
}
void commit(int addr) {
if (PROG_FLICKER) prog_lamp(LOW);
spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0);
if (PROG_FLICKER) {
delay(PTIME);
prog_lamp(HIGH);
}
}
//#define _current_page(x) (here & 0xFFFFE0)
int current_page(int addr) {
if (param.pagesize == 32) return here & 0xFFFFFFF0;
if (param.pagesize == 64) return here & 0xFFFFFFE0;
if (param.pagesize == 128) return here & 0xFFFFFFC0;
if (param.pagesize == 256) return here & 0xFFFFFF80;
return here;
}
void write_flash(int length) {
fill(length);
if (CRC_EOP == getch()) {
Serial.print((char) STK_INSYNC);
Serial.print((char) write_flash_pages(length));
} else {
error++;
Serial.print((char) STK_NOSYNC);
}
}
uint8_t write_flash_pages(int length) {
int x = 0;
int page = current_page(here);
while (x < length) {
if (page != current_page(here)) {
commit(page);
page = current_page(here);
}
flash(LOW, here, buff[x++]);
flash(HIGH, here, buff[x++]);
here++;
}
commit(page);
return STK_OK;
}
#define EECHUNK (32)
uint8_t write_eeprom(int length) {
// here is a word address, get the byte address
int start = here * 2;
int remaining = length;
if (length > param.eepromsize) {
error++;
return STK_FAILED;
}
while (remaining > EECHUNK) {
write_eeprom_chunk(start, EECHUNK);
start += EECHUNK;
remaining -= EECHUNK;
}
write_eeprom_chunk(start, remaining);
return STK_OK;
}
// write (length) bytes, (start) is a byte address
uint8_t write_eeprom_chunk(int start, int length) {
// this writes byte-by-byte,
// page writing may be faster (4 bytes at a time)
fill(length);
prog_lamp(LOW);
for (int x = 0; x < length; x++) {
int addr = start+x;
spi_transaction(0xC0, (addr>>8) & 0xFF, addr & 0xFF, buff[x]);
delay(45);
}
prog_lamp(HIGH);
return STK_OK;
}
void program_page() {
char result = (char) STK_FAILED;
int length = 256 * getch() + getch();
char memtype = getch();
// flash memory @here, (length) bytes
if (memtype == 'F') {
write_flash(length);
return;
}
if (memtype == 'E') {
result = (char)write_eeprom(length);
if (CRC_EOP == getch()) {
Serial.print((char) STK_INSYNC);
Serial.print(result);
} else {
error++;
Serial.print((char) STK_NOSYNC);
}
return;
}
Serial.print((char)STK_FAILED);
return;
}
uint8_t flash_read(uint8_t hilo, int addr) {
return spi_transaction(0x20 + hilo * 8,
(addr >> 8) & 0xFF,
addr & 0xFF,
0);
}
char flash_read_page(int length) {
for (int x = 0; x < length; x+=2) {
uint8_t low = flash_read(LOW, here);
Serial.print((char) low);
uint8_t high = flash_read(HIGH, here);
Serial.print((char) high);
here++;
}
return STK_OK;
}
char eeprom_read_page(int length) {
// here again we have a word address
int start = here * 2;
for (int x = 0; x < length; x++) {
int addr = start + x;
uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF);
Serial.print((char) ee);
}
return STK_OK;
}
void read_page() {
char result = (char)STK_FAILED;
int length = 256 * getch() + getch();
char memtype = getch();
if (CRC_EOP != getch()) {
error++;
Serial.print((char) STK_NOSYNC);
return;
}
Serial.print((char) STK_INSYNC);
if (memtype == 'F') result = flash_read_page(length);
if (memtype == 'E') result = eeprom_read_page(length);
Serial.print(result);
return;
}
void read_signature() {
if (CRC_EOP != getch()) {
error++;
Serial.print((char) STK_NOSYNC);
return;
}
Serial.print((char) STK_INSYNC);
uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00);
Serial.print((char) high);
uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);
Serial.print((char) middle);
uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);
Serial.print((char) low);
Serial.print((char) STK_OK);
}
//////////////////////////////////////////
//////////////////////////////////////////
////////////////////////////////////
////////////////////////////////////
int avrisp() {
uint8_t data, low, high;
uint8_t ch = getch();
switch (ch) {
case '0': // signon
error = 0;
empty_reply();
break;
case '1':
if (getch() == CRC_EOP) {
Serial.print((char) STK_INSYNC);
Serial.print("AVR ISP");
Serial.print((char) STK_OK);
}
break;
case 'A':
get_version(getch());
break;
case 'B':
fill(20);
set_parameters();
empty_reply();
break;
case 'E': // extended parameters - ignore for now
fill(5);
empty_reply();
break;
case 'P':
start_pmode();
empty_reply();
break;
case 'U': // set address (word)
here = getch() + 256 * getch();
empty_reply();
break;
case 0x60: //STK_PROG_FLASH
low = getch();
high = getch();
empty_reply();
break;
case 0x61: //STK_PROG_DATA
data = getch();
empty_reply();
break;
case 0x64: //STK_PROG_PAGE
program_page();
break;
case 0x74: //STK_READ_PAGE 't'
read_page();
break;
case 'V': //0x56
universal();
break;
case 'Q': //0x51
error=0;
end_pmode();
empty_reply();
break;
case 0x75: //STK_READ_SIGN 'u'
read_signature();
break;
// expecting a command, not CRC_EOP
// this is how we can get back in sync
case CRC_EOP:
error++;
Serial.print((char) STK_NOSYNC);
break;
// anything else we will return STK_UNKNOWN
default:
error++;
if (CRC_EOP == getch())
Serial.print((char)STK_UNKNOWN);
else
Serial.print((char)STK_NOSYNC);
}
}
// using the following pins:
// 10: slave reset
// 11: MOSI
// 12: MISO
// 13: SCK
// Put an LED (with resistor) on the following pins:
// 9: Heartbeat - shows the programmer is running
// 8: Error - Lights up if something goes wrong (use red if that makes sense)
// 7: Programming - In communication with the slave
//
// October 2010 by Randall Bohn
// - Write to EEPROM > 256 bytes
// - Better use of LEDs:
// -- Flash LED_PMODE on each flash commit
// -- Flash LED_PMODE while writing EEPROM (both give visual feedback of writing progress)
// - Light LED_ERR whenever we hit a STK_NOSYNC. Turn it off when back in sync.
//
// October 2009 by David A. Mellis
// - Added support for the read signature command
//
// February 2009 by Randall Bohn
// - Added support for writing to EEPROM (what took so long?)
// Windows users should consider WinAVR's avrdude instead of the
// avrdude included with Arduino software.
//
// January 2008 by Randall Bohn
// - Thanks to Amplificar for helping me with the STK500 protocol
// - The AVRISP/STK500 (mk I) protocol is used in the arduino bootloader
// - The SPI functions herein were developed for the AVR910_ARD programmer
// - More information at http://code.google.com/p/mega-isp
#include "pins_arduino.h" // defines SS,MOSI,MISO,SCK
#define SCK 13
#define MISO 12
#define MOSI 11
#define RESET 10
#define LED_HB 9
#define LED_ERR 8
#define LED_PMODE 7
#define PROG_FLICKER true
#define HWVER 2
#define SWMAJ 1
#define SWMIN 18
// STK Definitions
#define STK_OK 0x10
#define STK_FAILED 0x11
#define STK_UNKNOWN 0x12
#define STK_INSYNC 0x14
#define STK_NOSYNC 0x15
#define CRC_EOP 0x20 //ok it is a space...
void pulse(int pin, int times);
void setup() {
Serial.begin(19200);
pinMode(LED_PMODE, OUTPUT);
pulse(LED_PMODE, 2);
pinMode(LED_ERR, OUTPUT);
pulse(LED_ERR, 2);
pinMode(LED_HB, OUTPUT);
pulse(LED_HB, 2);
}
int error=0;
int pmode=0;
// address for reading and writing, set by 'U' command
int here;
uint8_t buff[256]; // global block storage
#define beget16(addr) (*addr * 256 + *(addr+1) )
typedef struct param {
uint8_t devicecode;
uint8_t revision;
uint8_t progtype;
uint8_t parmode;
uint8_t polling;
uint8_t selftimed;
uint8_t lockbytes;
uint8_t fusebytes;
int flashpoll;
int eeprompoll;
int pagesize;
int eepromsize;
int flashsize;
}
parameter;
parameter param;
// this provides a heartbeat on pin 9, so you can tell the software is running.
uint8_t hbval=128;
int8_t hbdelta=8;
void heartbeat() {
if (hbval > 192) hbdelta = -hbdelta;
if (hbval < 32) hbdelta = -hbdelta;
hbval += hbdelta;
analogWrite(LED_HB, hbval);
delay(40);
}
void loop(void) {
// is pmode active?
if (pmode) digitalWrite(LED_PMODE, HIGH);
else digitalWrite(LED_PMODE, LOW);
// is there an error?
if (error) digitalWrite(LED_ERR, HIGH);
else digitalWrite(LED_ERR, LOW);
// light the heartbeat LED
heartbeat();
if (Serial.available()) {
avrisp();
}
}
uint8_t getch() {
while(!Serial.available());
return Serial.read();
}
void fill(int n) {
for (int x = 0; x < n; x++) {
buff[x] = getch();
}
}
#define PTIME 30
void pulse(int pin, int times) {
do {
digitalWrite(pin, HIGH);
delay(PTIME);
digitalWrite(pin, LOW);
delay(PTIME);
}
while (times--);
}
void prog_lamp(int state) {
if (PROG_FLICKER)
digitalWrite(LED_PMODE, state);
}
void spi_init() {
uint8_t x;
SPCR = 0x53;
x=SPSR;
x=SPDR;
}
void spi_wait() {
do {
}
while (!(SPSR & (1 << SPIF)));
}
uint8_t spi_send(uint8_t b) {
uint8_t reply;
SPDR=b;
spi_wait();
reply = SPDR;
return reply;
}
uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
uint8_t n;
spi_send(a);
n=spi_send(b);
//if (n != a) error = -1;
n=spi_send(c);
return spi_send(d);
}
void empty_reply() {
if (CRC_EOP == getch()) {
Serial.print((char)STK_INSYNC);
Serial.print((char)STK_OK);
} else {
error++;
Serial.print((char)STK_NOSYNC);
}
}
void breply(uint8_t b) {
if (CRC_EOP == getch()) {
Serial.print((char)STK_INSYNC);
Serial.print((char)b);
Serial.print((char)STK_OK);
}
else {
error++;
Serial.print((char)STK_NOSYNC);
}
}
void get_version(uint8_t c) {
switch(c) {
case 0x80:
breply(HWVER);
break;
case 0x81:
breply(SWMAJ);
break;
case 0x82:
breply(SWMIN);
break;
case 0x93:
breply('S'); // serial programmer
break;
default:
breply(0);
}
}
void set_parameters() {
// call this after reading paramter packet into buff[]
param.devicecode = buff[0];
param.revision = buff[1];
param.progtype = buff[2];
param.parmode = buff[3];
param.polling = buff[4];
param.selftimed = buff[5];
param.lockbytes = buff[6];
param.fusebytes = buff[7];
param.flashpoll = buff[8];
// ignore buff[9] (= buff[8])
// following are 16 bits (big endian)
param.eeprompoll = beget16(&buff[10]);
param.pagesize = beget16(&buff[12]);
param.eepromsize = beget16(&buff[14]);
// 32 bits flashsize (big endian)
param.flashsize = buff[16] * 0x01000000
+ buff[17] * 0x00010000
+ buff[18] * 0x00000100
+ buff[19];
}
void start_pmode() {
spi_init();
// following delays may not work on all targets...
pinMode(RESET, OUTPUT);
digitalWrite(RESET, HIGH);
pinMode(SCK, OUTPUT);
digitalWrite(SCK, LOW);
delay(50);
digitalWrite(RESET, LOW);
delay(50);
pinMode(MISO, INPUT);
pinMode(MOSI, OUTPUT);
spi_transaction(0xAC, 0x53, 0x00, 0x00);
pmode = 1;
}
void end_pmode() {
pinMode(MISO, INPUT);
pinMode(MOSI, INPUT);
pinMode(SCK, INPUT);
pinMode(RESET, INPUT);
pmode = 0;
}
void universal() {
int w;
uint8_t ch;
fill(4);
ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]);
breply(ch);
}
void flash(uint8_t hilo, int addr, uint8_t data) {
spi_transaction(0x40+8*hilo,
addr>>8 & 0xFF,
addr & 0xFF,
data);
}
void commit(int addr) {
if (PROG_FLICKER) prog_lamp(LOW);
spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0);
if (PROG_FLICKER) {
delay(PTIME);
prog_lamp(HIGH);
}
}
//#define _current_page(x) (here & 0xFFFFE0)
int current_page(int addr) {
if (param.pagesize == 32) return here & 0xFFFFFFF0;
if (param.pagesize == 64) return here & 0xFFFFFFE0;
if (param.pagesize == 128) return here & 0xFFFFFFC0;
if (param.pagesize == 256) return here & 0xFFFFFF80;
return here;
}
void write_flash(int length) {
fill(length);
if (CRC_EOP == getch()) {
Serial.print((char) STK_INSYNC);
Serial.print((char) write_flash_pages(length));
} else {
error++;
Serial.print((char) STK_NOSYNC);
}
}
uint8_t write_flash_pages(int length) {
int x = 0;
int page = current_page(here);
while (x < length) {
if (page != current_page(here)) {
commit(page);
page = current_page(here);
}
flash(LOW, here, buff[x++]);
flash(HIGH, here, buff[x++]);
here++;
}
commit(page);
return STK_OK;
}
#define EECHUNK (32)
uint8_t write_eeprom(int length) {
// here is a word address, get the byte address
int start = here * 2;
int remaining = length;
if (length > param.eepromsize) {
error++;
return STK_FAILED;
}
while (remaining > EECHUNK) {
write_eeprom_chunk(start, EECHUNK);
start += EECHUNK;
remaining -= EECHUNK;
}
write_eeprom_chunk(start, remaining);
return STK_OK;
}
// write (length) bytes, (start) is a byte address
uint8_t write_eeprom_chunk(int start, int length) {
// this writes byte-by-byte,
// page writing may be faster (4 bytes at a time)
fill(length);
prog_lamp(LOW);
for (int x = 0; x < length; x++) {
int addr = start+x;
spi_transaction(0xC0, (addr>>8) & 0xFF, addr & 0xFF, buff[x]);
delay(45);
}
prog_lamp(HIGH);
return STK_OK;
}
void program_page() {
char result = (char) STK_FAILED;
int length = 256 * getch() + getch();
char memtype = getch();
// flash memory @here, (length) bytes
if (memtype == 'F') {
write_flash(length);
return;
}
if (memtype == 'E') {
result = (char)write_eeprom(length);
if (CRC_EOP == getch()) {
Serial.print((char) STK_INSYNC);
Serial.print(result);
} else {
error++;
Serial.print((char) STK_NOSYNC);
}
return;
}
Serial.print((char)STK_FAILED);
return;
}
uint8_t flash_read(uint8_t hilo, int addr) {
return spi_transaction(0x20 + hilo * 8,
(addr >> 8) & 0xFF,
addr & 0xFF,
0);
}
char flash_read_page(int length) {
for (int x = 0; x < length; x+=2) {
uint8_t low = flash_read(LOW, here);
Serial.print((char) low);
uint8_t high = flash_read(HIGH, here);
Serial.print((char) high);
here++;
}
return STK_OK;
}
char eeprom_read_page(int length) {
// here again we have a word address
int start = here * 2;
for (int x = 0; x < length; x++) {
int addr = start + x;
uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF);
Serial.print((char) ee);
}
return STK_OK;
}
void read_page() {
char result = (char)STK_FAILED;
int length = 256 * getch() + getch();
char memtype = getch();
if (CRC_EOP != getch()) {
error++;
Serial.print((char) STK_NOSYNC);
return;
}
Serial.print((char) STK_INSYNC);
if (memtype == 'F') result = flash_read_page(length);
if (memtype == 'E') result = eeprom_read_page(length);
Serial.print(result);
return;
}
void read_signature() {
if (CRC_EOP != getch()) {
error++;
Serial.print((char) STK_NOSYNC);
return;
}
Serial.print((char) STK_INSYNC);
uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00);
Serial.print((char) high);
uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);
Serial.print((char) middle);
uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);
Serial.print((char) low);
Serial.print((char) STK_OK);
}
//////////////////////////////////////////
//////////////////////////////////////////
////////////////////////////////////
////////////////////////////////////
int avrisp() {
uint8_t data, low, high;
uint8_t ch = getch();
switch (ch) {
case '0': // signon
error = 0;
empty_reply();
break;
case '1':
if (getch() == CRC_EOP) {
Serial.print((char) STK_INSYNC);
Serial.print("AVR ISP");
Serial.print((char) STK_OK);
}
break;
case 'A':
get_version(getch());
break;
case 'B':
fill(20);
set_parameters();
empty_reply();
break;
case 'E': // extended parameters - ignore for now
fill(5);
empty_reply();
break;
case 'P':
start_pmode();
empty_reply();
break;
case 'U': // set address (word)
here = getch() + 256 * getch();
empty_reply();
break;
case 0x60: //STK_PROG_FLASH
low = getch();
high = getch();
empty_reply();
break;
case 0x61: //STK_PROG_DATA
data = getch();
empty_reply();
break;
case 0x64: //STK_PROG_PAGE
program_page();
break;
case 0x74: //STK_READ_PAGE 't'
read_page();
break;
case 'V': //0x56
universal();
break;
case 'Q': //0x51
error=0;
end_pmode();
empty_reply();
break;
case 0x75: //STK_READ_SIGN 'u'
read_signature();
break;
// expecting a command, not CRC_EOP
// this is how we can get back in sync
case CRC_EOP:
error++;
Serial.print((char) STK_NOSYNC);
break;
// anything else we will return STK_UNKNOWN
default:
error++;
if (CRC_EOP == getch())
Serial.print((char)STK_UNKNOWN);
else
Serial.print((char)STK_NOSYNC);
}
}
Now for the Arduino hardware side:
1. Unplug your Arduino form the computer and connect the wires from the TX that you just soldered as follows:
Arduino Pin: 9x transmitter
10: RST
11: MOSI
12: MISO
13: SCK
5v: 5v
Gnd: Gnd
2. Put a 120ohm resistor between +5v and Reset on the Arduino. This stops the Arduino from rebooting in the ISP process.
Next up is the er9x firmware buring software.
2. When you open it, it should ask if you want to download the latest er9x firmware, click yes.
3. Go to burn->configure in the eepe software
select the avrisp programmer.
select m64 mcu.
select port your Arduino is connected to.
type -b 19200 in extra arguments.
4. You should probably backup the firmware, memory and EEPROM using the options in the burn menu( i didn't...oops) in case of an issue with the er9x firmware.
5. Go to burn->Flash Firmware to TX and select the firmware you saved from step 2.
6. Should say complete after a minute or two!!
Joystick PPM Interface
This isn't really a mod, but it should make flying easier by hooking up a PC gaming joystick. Nothing too fancy, just a old game port joystick that is connected to a Arduino which sends PPM signals to the TX trainer port.......later
Tuesday, May 1, 2012
Arduino Composite TV out
I've been wanting to interface my Arduino with a monitor of some sort with RGB, but it didn't seem to be much of a possibility for such a memory limited device. But then i found this tvout library that uses composite video! It is only in black and white, but still pretty awesome. The first thing i thought of to do was make a space invaders type game with that Parallax joystick from some time ago...and its coming along quite well.
I have created a "starry" background and a ship bitmap so far(its a simple monochrome bitmap made in paint that can be converted to code and displayed using the library). Basically how it works: the loop reads the stick values, changes the X/Y coordinates of where the ship will be, then draws the "stars" and then the ship. This obviously happens many times a second so it looks fairly fluid on screen. So, currently I can move the ship around on a starry background and i'm starting to work on projectiles.
Here's some code in progress.
I have created a "starry" background and a ship bitmap so far(its a simple monochrome bitmap made in paint that can be converted to code and displayed using the library). Basically how it works: the loop reads the stick values, changes the X/Y coordinates of where the ship will be, then draws the "stars" and then the ship. This obviously happens many times a second so it looks fairly fluid on screen. So, currently I can move the ship around on a starry background and i'm starting to work on projectiles.
Here's some code in progress.
////////////////////BITMAPS!/////////////////////////////////////
//120 pixels wide,96 pixels high
PROGMEM const unsigned char BckGnd[] = {
120,96,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x10,0x10,0x00,0x00,0x00,0x40,0x00,0x40,0x20,0x04,0x08,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x20,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x40,0x00,0x00,0x00,
0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x08,0x00,0x00,0x00,0x40,0x00,
0x04,0x02,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,
0x00,0x00,0x00,0x80,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x00,0x40,0x00,0x00,
0x20,0x02,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x10,
0x00,0x40,0x00,0x80,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x40,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,
0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
0x20,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x10,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x02,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x10,
0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x08,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,
0x00,0x40,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,
0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
0x20,0x00,0x40,0x00,0x08,0x10,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x02,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x04,0x00,0x00,0x80,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x08,0x10,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x04,0x00,
0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x10,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x04,
0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x00,
0x10,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x80,
0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x02,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,
0x04,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x08,0x00,
0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x40,0x00,0x00,0x10,
0x00,0x00,0x00,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,
0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x80,0x04,0x00,0x00,0x08,0x00,0x01,0x00,
0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
PROGMEM const unsigned char Ship[] = {
8,7,
0x18,
0x18,
0x3C,
0xBD,
0xBD,
0xFF,
0xFF};
////////////////////END OF BITMAPS!/////////////////////////////////////
/*
RCA video plug:
Sync(Pin 9) -> 1k -> center prong
Video(pin 7) -> 470ohm -> center prong
Gnd -> outside casing
*/
#include <TVout.h>
#include <fontALL.h>
TVout TV; //init the TV object
//resolution of output
#define TVresW 120
#define TVresH 96
// x/y limits for ship
#define MaxRight (TVresW-8)
#define MaxDown (TVresH -7)
//this program uses a lot of memory, so i use bytes here because they are large enough for this purpose.
//where the ship is on screen
byte CurXPos = (TVresW-8)/2; //centers ship horizontally
byte CurYPos = TVresH -7; //(total height) - 7(ship height), moves ship to bottom
#define XaxisPin A2
int Xval=0; //raw stick data, x axis
#define YaxisPin A1
int Yval=0; //raw stick data, y axis
#define FireBtn 0 //aka digital 2 interrupt
volatile long lastDebounceTime = 0; // the last time the interrupt was triggered
#define debounceDelay 10 // the debounce time; decrease if quick button presses are ignored
volatile int fireState = LOW; //initial button state
volatile long lastFire = 0;
byte FireY =0;
byte FireX = 0;
void setup()
{
//Serial.begin(115200);
attachInterrupt(FireBtn,FireEvent, RISING); //fire button interrupt
TV.begin(NTSC,TVresW,TVresH); //initialize TV
delay(1000); //wait for TV to detect input
TV.bitmap(0,0,BckGnd); //displays the "stars"
TV.select_font(font8x8); //each char is 8x8 pixels
TV.print((TVresW-80)/2,(TVresH-8)/2,"Ship Test!"); //80 pixel long message, 8 tall
TV.bitmap(CurXPos,CurYPos,Ship); //displays the ship
delay(250);
}
void loop()
{
GetSticks(); //get raw data
MoveShip(); //move ship accordingly on screen
CheckFire(); //testing
}
void GetSticks()
{
Yval = analogRead(YaxisPin);
Xval = analogRead(XaxisPin);
/*
Serial.print(Xval);
Serial.print(" ");
Serial.println(Yval);
*/
if(Xval < 512) //move left
{
Xval = map(Xval,512,0,0,-4);
}
else//its >= 512
{
Xval = map(Xval,512,1023,0,4); //move right
}
if(Yval < 512) //move down
{
Yval = map(Yval,512,0,0,4);
}
else//its >= 512 //move up
{
Yval = map(Yval,512,1023,0,-4);
}
}
void MoveShip()
{
//get new pos
CurXPos += Xval;
CurYPos += Yval;
if (CurXPos > MaxRight) //check horizontal limits
{
CurXPos = 0;
}
else if(CurXPos <= 0)
{
CurXPos = MaxRight;
}
if (CurYPos > MaxDown) //check vertical limits
{
CurYPos = 0;
}
else if(CurYPos <= 0)
{
CurYPos = MaxDown;
}
//clear screen + display new ship
TV.bitmap(0,0,BckGnd); // used to be "TV.clear_screen();", but the BckGnd bitmap covers the enite screen, so not needed.
TV.bitmap(CurXPos,CurYPos,Ship);
/*
Serial.print(CurXPos);
Serial.print(",");
Serial.println(CurYPos);
*/
}
/*
void DrawFire() //works well at drawing a bullet, but stops the program whiles its doing it because of the for loop.
{
byte FireX = CurXPos + 4;
byte FireY = CurYPos;
for(byte i = FireY; i>=0; i--)
{
TV.set_pixel(FireX,i+1,0);
delay(2);
TV.set_pixel(FireX,i,1);
delay(2);
}
}
*/
//untested attempt at "drawing" fire(a bullet)
void FireEvent()
{
if ((millis() - lastDebounceTime) > debounceDelay)
{
lastDebounceTime = millis();
FireX = CurXPos + 4;
FireY = CurYPos;
fireState = true;
TV.set_pixel(FireX,FireY+1,0);
TV.set_pixel(FireX,FireY,1);
}
}
void CheckFire()
{
if(fireState && millis() > (lastFire + 2) && FireY >0)
{
FireY--;
lastFire = millis();
TV.set_pixel(FireX,FireY+1,0);
TV.set_pixel(FireX,FireY,1);
}
}
//120 pixels wide,96 pixels high
PROGMEM const unsigned char BckGnd[] = {
120,96,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x10,0x10,0x00,0x00,0x00,0x40,0x00,0x40,0x20,0x04,0x08,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x20,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x40,0x00,0x00,0x00,
0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x08,0x00,0x00,0x00,0x40,0x00,
0x04,0x02,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,
0x00,0x00,0x00,0x80,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x00,0x40,0x00,0x00,
0x20,0x02,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x10,
0x00,0x40,0x00,0x80,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x40,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,
0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,
0x20,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x10,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x02,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x10,
0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x08,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,
0x00,0x40,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,
0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,
0x20,0x00,0x40,0x00,0x08,0x10,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x02,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,
0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x04,0x00,0x00,0x80,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x08,0x10,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x04,0x00,
0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x10,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x04,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x04,
0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x00,
0x10,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x80,
0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x02,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,
0x04,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x08,0x00,
0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x40,0x00,0x00,0x10,
0x00,0x00,0x00,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,
0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x80,0x04,0x00,0x00,0x08,0x00,0x01,0x00,
0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
PROGMEM const unsigned char Ship[] = {
8,7,
0x18,
0x18,
0x3C,
0xBD,
0xBD,
0xFF,
0xFF};
////////////////////END OF BITMAPS!/////////////////////////////////////
/*
RCA video plug:
Sync(Pin 9) -> 1k -> center prong
Video(pin 7) -> 470ohm -> center prong
Gnd -> outside casing
*/
#include <TVout.h>
#include <fontALL.h>
TVout TV; //init the TV object
//resolution of output
#define TVresW 120
#define TVresH 96
// x/y limits for ship
#define MaxRight (TVresW-8)
#define MaxDown (TVresH -7)
//this program uses a lot of memory, so i use bytes here because they are large enough for this purpose.
//where the ship is on screen
byte CurXPos = (TVresW-8)/2; //centers ship horizontally
byte CurYPos = TVresH -7; //(total height) - 7(ship height), moves ship to bottom
#define XaxisPin A2
int Xval=0; //raw stick data, x axis
#define YaxisPin A1
int Yval=0; //raw stick data, y axis
#define FireBtn 0 //aka digital 2 interrupt
volatile long lastDebounceTime = 0; // the last time the interrupt was triggered
#define debounceDelay 10 // the debounce time; decrease if quick button presses are ignored
volatile int fireState = LOW; //initial button state
volatile long lastFire = 0;
byte FireY =0;
byte FireX = 0;
void setup()
{
//Serial.begin(115200);
attachInterrupt(FireBtn,FireEvent, RISING); //fire button interrupt
TV.begin(NTSC,TVresW,TVresH); //initialize TV
delay(1000); //wait for TV to detect input
TV.bitmap(0,0,BckGnd); //displays the "stars"
TV.select_font(font8x8); //each char is 8x8 pixels
TV.print((TVresW-80)/2,(TVresH-8)/2,"Ship Test!"); //80 pixel long message, 8 tall
TV.bitmap(CurXPos,CurYPos,Ship); //displays the ship
delay(250);
}
void loop()
{
GetSticks(); //get raw data
MoveShip(); //move ship accordingly on screen
CheckFire(); //testing
}
void GetSticks()
{
Yval = analogRead(YaxisPin);
Xval = analogRead(XaxisPin);
/*
Serial.print(Xval);
Serial.print(" ");
Serial.println(Yval);
*/
if(Xval < 512) //move left
{
Xval = map(Xval,512,0,0,-4);
}
else//its >= 512
{
Xval = map(Xval,512,1023,0,4); //move right
}
if(Yval < 512) //move down
{
Yval = map(Yval,512,0,0,4);
}
else//its >= 512 //move up
{
Yval = map(Yval,512,1023,0,-4);
}
}
void MoveShip()
{
//get new pos
CurXPos += Xval;
CurYPos += Yval;
if (CurXPos > MaxRight) //check horizontal limits
{
CurXPos = 0;
}
else if(CurXPos <= 0)
{
CurXPos = MaxRight;
}
if (CurYPos > MaxDown) //check vertical limits
{
CurYPos = 0;
}
else if(CurYPos <= 0)
{
CurYPos = MaxDown;
}
//clear screen + display new ship
TV.bitmap(0,0,BckGnd); // used to be "TV.clear_screen();", but the BckGnd bitmap covers the enite screen, so not needed.
TV.bitmap(CurXPos,CurYPos,Ship);
/*
Serial.print(CurXPos);
Serial.print(",");
Serial.println(CurYPos);
*/
}
/*
void DrawFire() //works well at drawing a bullet, but stops the program whiles its doing it because of the for loop.
{
byte FireX = CurXPos + 4;
byte FireY = CurYPos;
for(byte i = FireY; i>=0; i--)
{
TV.set_pixel(FireX,i+1,0);
delay(2);
TV.set_pixel(FireX,i,1);
delay(2);
}
}
*/
//untested attempt at "drawing" fire(a bullet)
void FireEvent()
{
if ((millis() - lastDebounceTime) > debounceDelay)
{
lastDebounceTime = millis();
FireX = CurXPos + 4;
FireY = CurYPos;
fireState = true;
TV.set_pixel(FireX,FireY+1,0);
TV.set_pixel(FireX,FireY,1);
}
}
void CheckFire()
{
if(fireState && millis() > (lastFire + 2) && FireY >0)
{
FireY--;
lastFire = millis();
TV.set_pixel(FireX,FireY+1,0);
TV.set_pixel(FireX,FireY,1);
}
}
Sunday, April 15, 2012
[Quadcopter] Build Log and How To Guide
The goal of this guide is to provide a RC noob a tutorial on how to build a fairly standard quadcopter (not quite there yet :)).
Basics
So, what is a quadcopter? Wikipedia says: "A quadcopter is a multicopter that is lifted and propelled by four rotors. Quadrotors are classified as rotorcraft, as opposed to fixed-wing aircraft, because their lift is generated by a set of revolving propellers. Unlike most helicopters, quadrotors generally use symmetrically pitched blades; these can be adjusted as a group, a property known as 'collective'. Control of vehicle motion is achieved by altering the rotation rate of one or more rotor discs, thereby changing its torque load and thrust/lift characteristics." Well, that sounds funny, what does it actually look like?
In this picture, you can easily see all the important parts of the quadcopter. The most obvious things are probably the motors and propellers. The motors spin around at a few thousand RPM and so as the props spin with the motors they produce thrust. Here are some typical motors and props:
Next you'll probably notice the frame. This is commonly wood, aluminum, carbon fiber or glass fiber and has some sort of center plate on which 4 arms are attached. On the ends of these arms, are the motors. Here are some common frames:

In the center of the frame is the flight controller, receiver and battery. Flight controller is connected to all of the motors, and the receiver. It controls the direction, height, and speed etc of the quadcopter, by, as wiki said, varying the RPMs of the motors (and therefor the amount of thrust). A picture of a FC (there are MANY different types of FCs):
Mounted on the arms between the motors and the center plate, you can see the motor controllers (called an ESC (electronic speed controller)) It is connected to the FC, battery, and motor. The three red wires at the top are connect to the motor and the red/black wires at the bottom go to the battery. The black three pin connector in the upper left of the pic below is connected to the FC so that the ESC can be controlled (and therefor the motor too).
The receiver connects to the FC and receives the signals from the transmitter that control the quadcopter. It has multiple channels for the different control inputs of the quadcopter:
The battery is a LiPo which has a higher power to weight ratio compared to a NiMH battery, for example.
A common TX is the second image below. In the first picture you can see the most important part of the TX; the "sticks". There are two sticks, and each can move left/right/up/down. These sticks control 4 different channels: rudder, throttle, aileron, elevator. These are based on airplane controls, but still make sense for a quadcopter. Rudder controls the Z axis of the quad, yaw, or spinning without moving left/right/forward/back etc. Throttle controls the RPM of the motors and thus making it go higher/move faster. Aileron is Y, roll, or tilting left/right. Elevator is X, pitch, or tilting forward/back. There are two main "modes" of a TX; mode 1 has the throttle stick on the right and ailerons on the left, and mode 2 is the opposite.
Theory
Materials
To build a quad you basically need:
4x motors
4x ESCs
4x Props (2 CW and 2 CCW)
1x LiPo battery monitor
1x Frame
1x FC
1x TX/RX
1x Battery Charger
1x Battery (extras are good)
Misc: wire, plugs, zipties, nuts/bolts, sodlering iron, general tools.
HobbyKing (to buy stuff)
RCGroups (help!)
Frame
Starting from scratch, the frame is probably the first thing to pick as its design/size depends on what you want to do and changes the rest of the build. 400mm to 700mm motor to motor fairly standard for most quads. Most times beginners build their frames out of aluminum or wood. More expensive(most times better) are made of carbon fiber. For your first quad, though, it'd be better to but a pre-fabricated frame. HobbyKing is now really starting to get into the quadcopter stuff and has quite a selection of frames now. But, whatever you do, don't buy one of those 10$ cheap wood frames as they break very easily. The rest of the frames there do look ok, but the motor mounts on the Talon style frames are a bit weak; you can get some better ones off ebay. HK is not the only place, though, google will find you hundreds of frames. Dialfonzo, from the RCgroups forums, also has some nice frames.
Wood
A wood frame is probably the easiest for a beginner to DIY frames because wood is a easy material to work with. 3/8in to 3/4in square poplar or hardwood is what i have seen the most, but i think 3/8 is a bit to flexible and as i saw at the Home Depot, severely twisted. But, I found some nice 1/2in hardwood that wasn't twisted very much and it has worked fairly well. On any type of frame i still like to use two CF plates with the four arms squished between to create a X or + shape. The next important thing is how it's mounted. Currently, my wood arms have two holes in them spaced .75in apart with bolts going though it and the CF; I'm guessing that it will also crack down the center like the crappy CF square tube that HK sells, so, i recommend to use something that squeezes the arms between the center plates and not bolt into the arms. You can probably get away with mounting the motors directly to the arms if you extend the ends of the arms past the prop so that the props cant hit the ground in a crash.
Aluminum
This my favorite option and is very popular because it is easily accessible, fairly light, and strong: typically, 3/8 to 1in in square tube/tube is used. A good thing to note is the wall thickness of the tube; you want pretty much the thinnest you can get, otherwise the weight is too much most times. 1.64in or 1mm is good. Regular tubes are stronger than square tubes of the same specs. With Aluminum you can, with fewer issues, bolt the arms directly to the center plates, and the motors to the arms, unlike CF and wood.
Carbon Fiber
If you are going for CF, make sure you get the proper cross-weaved stuff. Even then, it'd be better not to drill into it; instead use clamping type mechanisms to hold the arms into the base and the motors onto the arms. If you get the CF square tube from HK which only has CF going one direction and you drill into it for the mounts, it will fail. Again, square tube would be better because it is not circular(of course haha) and you will know for sure that the motor are on straight.
Motors + Props
These two are inevitably intertwined (along with pretty much everything else in a quad) and greatly depend on what you want to do with your quad. Most times a frame will give recommendations for motors and props. Generally, the bigger the quad, the lower the Kv (how fast the bladed spins, stands for rpm/volt) and the bigger the prop. Smaller quads have a higher Kv (blade spins faster) and smaller props. This is because the quad doesn't have room for the bigger props, so it must use a higher Kv to achieve sufficient thrust. With bigger props, you have slower reaction times, but more stability. For choosing a motor, i would pick the props first. You will want to choose the biggest(most times) that will fit on your quad with at least few inches between the FC and other props. But, you can also spread the motors out even more for better stability, generally. Once you find the props, find a motor that can spin them with efficiency and a good amount of thrust. The total weight of your quad should be about 1/3 of the total motor thrust (2/3rds, for good measure, of 1/2 of the total thrust). flybrushless.com is a great place to look at for thrust data, and Google also works pretty well too. 700 to 800Kv is good for 11in props, 900 to about 1100kv is10in props, 1200 to 1300kv or so use 9in props, and so on. eCalc does a very good job at getting you in the ball park of how your quad will perform. The calculated hover throttle needs to be 50% or less. Oh, and, thrust depends on the voltage the motor is receiving. The higher the voltage, the higher the amperage/thrust/Kv, but the motor might not be able to handle that. All motors have a rating, its better not to go over it. Commonly, multirotors will be 11.1v or 3s. It is also very helpful to look at other people designs and how well they work.
ESCs
This is probably one of the easiest things to pick out. These controls the motors depending on what signal the FC sends. After you've found your motors and how much current they're going to draw with the props you choose, you can choose the ESC. If one of your motors draws 18amps at full throttle, you wouldn't want a 18A ESC beacuse that would put a lot of stress on the ESC. Instead you would want to get a 25A ESC or even 30A. You should also get one that has a fast refresh rate; 400hz is really good and the Turnigy Plush/Basic are some of the most used on quads. Also pay attention to the voltage rating.
Battery
This may seem daunting to a beginner to pick out and may result in bad things if chosen incorrectly, but with a few tips it is quite easy to decide. First thing to consider is weight; it will most likely be the heaviest thing on your quad. If your battery is too heavy, it can badly affect your quad, and weight pretty much always is directly related to capacity. Since you know that your AUW should be about 1/3 of the total thrust of the motors and so the battery should be the last thing you pick to get to this total weight. Make sure you pick a 3s battery, for example, if you have 3s motors/ESCs.
Next thing to look at is amp draw. If you have four motors that draw 18A each, that's 72 amps total, and a 2200mah battery fits your weight and run time requirement, you would need at least a 32C battery as 2200mah = 2.2Ah and 72total amp draw divided by 2.2Ah is 32.7C (aka total amp draw divided by battery capacity in Amp hour). Again, like the ESCs, higher is better, so, maybe 45C? Continuing with that, lets find the flight time. (2.2Ah / 72A) * 60 = 1.88min. the equation is: (battery capacity in Amp hour divided by total amp draw) times 60. Those were calculations at 100% throttle. You can just use eCalc for most calculations, anyway.
if your quad weighs 1kg and your motors supply 4kg in total(1kg each) at max throttle. and the total amp draw is 72A. the hover amperage would be 72/4(the quad is 1/4th of the motor thrust) which is 18A hover thrust. so doing the flight time calculation again: (2.2Ah / 18A) * 60 = 7.33min hover time.
FC
This (the flight controller) greatly depends on your tastes; Arduino based such as MultiWii works for most people but it requires some tinkering to get working and is great after that. KK boards are very cheap, gyro only, and fairly typical for beginners. SM32 based FCs are more powerful and work a little better accordingly and are MultiWii compatible such as TimeCop's Free Flight board. But, for most people the additional processing power of STM32 doesn't really make a difference. Then there is the NAZA and Wookong which are the best for minimal setup required, besides RTF, of course.
Build
Here are my specs:
Custom frame - aluminum arms/CF plate
Pololu MinIMU9-- L3G4200D Gyro, LSM303Accelerometer/Compass
Motors
I started out with the motors first. I had to do a few things to get them working for my particular build: put the propeller and base mounts on, cut off axles on the bottom, and put the bullet connectors and heat shrink on.
I dumped all the extra parts from the motors into a little container so I could find the stuff easier.
Cut off axle with air powered dremel.
Prop mount and bolts
A little locktite to keep the bolts in.
All the prop mounts done.
All done with bullet connectors and heatshrink!
As you can see in the last image, these ESCs need to be connected in parallel with the battery. then the three red wires go to the motor. If the motor spins the wrong way, switch any two of the three red wires.
Soldered on bullet connectors
All done.
ESCs soldered together in parallel "spider" arrangement with XT60 connector. I soldered the ESCs together semi-permanently, but some may like bullet/XT60/etc connectors so that repairs are easier.
Custom Frame
This uses a 1.5mm CF center plate with four aluminum square tubes with holes drilled in them. I also took off the X motor mounts and mounted the motor directly to the arm which is way better and stronger. I have found that aluminum is the only material that i will drill holes into as wood and CF will crack, so, aluminum is my new favorite build material until i can get some proper boom/motor mounts for CF. The 4in PVC landing gear are awesome and were made of 3/8in wide pieces cut off of a 4in piece of pipe for each arm.
I was testing the BLHeli silabs firmware, sot that why there is not heatshrink on the ESCs in these pics.
Battery
The battery came with bullet connectors, but i planned on using XT60 connectors. The charger also used the XT60 connector.
Original battery
The wires got a little close...
All done
Charger
Before cahrging any sort of LiPo, its good to know some background; They do explode! Also, read through this post at RCGroups forum. It gives a general overview of LiPos, how to charge them, and not make them blow up. :)
ATX PSU used to power charger using the included cables. Balance and main charge connectors from the battery are connected to the charger
When first turned on, the charger looks like this.
If LiPo is not already selected(see previous image), press the Type button until the correct battery is selected, then press Enter.
Then it will look like this. Press "+" to go to LiPo Balance and then press Enter to select/loop through the charge settings, then "+" and "-" to change values. When ready to charge, press and hold the Enter button. Then Enter to confirm. After Charging has begun, press "+" to see individual cell voltage.
Connecting the FC!
The standard MWC board has four common areas: Power in , RX in, sensor hook ups, and ESC outputs. Lets use the MWC SE Lite board as an example.You will hopefully find some sort of diagram for any board you buy from the place you bought it.
Power
With this board, like most others, the power is supplied through one of the ESC outputs(bottom center, labeled M1-M6) through the ESCs internal BEC(a most times 5v power supply). So if you plug in a ESC with a 5v BEC, you got power! Also, you should check what voltage the BEC is and what voltage your board needs--they must be the same.
RX channels
This is really simple; just get some male to male servo cables and connect throttle from the RX to the throttle on the FC. Do the same for all the other channels. All MWC boards will have one AUX channel for selecting between level/acro, but the MWC SE and many other boards have two. The 5v from ESC goes through to the FC and then is sent to the RX through the cables that link the RX and FC.
Connections:
(RX Channel), FC Pin
Throttle 2
Roll 4
Pitch 5
Yaw 6
Aux 1 7
Aux 2 12
Note: if your RX supports PPM Sum (outputs all the channels onto one pin), you can plug that into pin 2 (throttle) and have all the other pins open for other stuff.
Sensors
The MWC SE has the sensors built in to the board, so you don't have to work about adding them. Most IMUs that can be bought now have internal level shifters (because most IMUs are 3.3v and most FCs are 5v) and pullup resistors which make it 5v compatible. So, if you have one of these, you can just connect SDA to SDA, SCL to SCL, GND to GND, and 5v to Vcc in on the IMU and your done. Here is a thread by me that shows a few more IMUs.
ESC outputs
-simply plug the servo connector from the ESCs to the motor ports. Check to make sure you have the ESC in the correct port for its place on the quad, look here under Connecting Motors and Servos for what ESCs go to what port.
-Something worth mentioning is that there are two types of BECs: Switching and Linear. If you have ESCs with a switching BEC, you MUST remove the red(+5v) wire coming from all but one ESC. If you dont, your ESCs(specifically, the BEC inside) will get VERY hot. If you have linear BECs, you dont have to remove the red wire, but most people still do.
Programming/GUI
The MWC boards use a serial interface to program and to adjust settings in the GUI. Most times a FTDI adapter is used.
TX Setup
Using the standard firmware that comes with the 9x, a few settings need to be changed for multiwii compatibility. Here is the instruction manual.
1. The sticks need to be set to Mode 2. (Menu/Sys Settings/Stick Set)
2. The TX should be in ACRO mode. (Menu/System Settings/Type)
3. You need to enable ch5, maybe ch6, and assign them to the switches so that you can switch between Accel/Gyro etc in flight. (Menu\Func\Aux-Ch)
4. It's not required, but you can calibrate the sticks value range:
-Push the right lower trim tab to the left (aileron on a mode 2 TX), and the left side trim tab up (Throttle trim on a mode 2 tx), and while holding them, turn the Tx on.
You will get a screen that shows the version number of the firmware, with four zeroes (0000) at the top of the screen.
-Center both sticks in their range in all axis, and press the menu button until it beeps and release. The screen number will change to 0001.
-Move the right stick to the upper right corner, and press menu, and you should get a beep, and the number on the screen will change to 0002.
-Put the right stick in the lower left corner, and press menu, and get a beep, and the number will change to 0003.
-Release the right stick, and put the left stick in the upper right corner, and press menu, get the beep, and the number will change to 0004.
-Put the left stick in the lower left corner, and press menu again, get the beep and the number will change to 0005.
-Center both sticks, and press menu, get the beep. You are done calibrating the sticks.
5. Some also say you need to set the ATV/servo range settings (Menu\Func\E. Trim):
pitch (elevator): 50%
roll (aileron): 50%
yaw (rudder): 100%
Initial gain pot values is 50%. Increase until it starts to oscillate rapidly, then back of until it is stable again. Fast forward flight needs lower gain. But I found mine to work just fine at 100%.
5. You can find the "DISPLAY" option in the Func menu, and that will display the values of the sticks so you know it is working.
6. Here is how to use the sticks on the TX to arm/disarm etc the quad with multiwii v2.0:
MWC GUI/more TX Setup
Basic MWC code config
-First, you need to get the latest MultiWii code and the latest Arduino IDE. Once you have the code open and have the config.h tab selected, you need to uncomment the orientation your quad will be in(QUADP, QUADX etc) under "The type of multicopter". Right below "type of multicopter", change I2C speed and I2C pullups if need be for your sensors. Then scroll down a bit until you find some #define lines for the gyro, acc, and mag. Like before uncomment the sensors you have or uncomment a board if yours is there. You could also search for RCAUXPIN and change that if you have a second auxiliary channel.
-If you have a ITG3200 or MPU6050 gyro, go to the Low pass filter setting section and un comment the 42Hz filter for your gyro. This can significantly stabilize your quad. I have the L3G4200D gyro which doesnt have LPF settings built into the code, but i found this post which has some code for my gyro that goes in the sensor tab of the code. Just find the old code and paste over it with the new code. But the best fix for reducing vibrations etc, is to fix it at the source--balance motors, props and get some good anti-vibration padding for the FC.
-#define MINTHROTTLE When you arm your quad, this is the value that is sent to the ESCs. it should be a little below the value that makes the motors spin up. This should be a little bit less than the min value of your throttle channel, aka,1095.
-#define MAXTHROTTLE This is the max value of the ESCs(aka full power); when the throttle stick is all the way up, this value is sent to the ESCs. Should be the max value of your throttle channel, AKA, 1905.
-#define MINCOMMAND is the value sent to the ESCs when disarmed. This should be below the minimum throttle, AKA 1095.
4. It's not required, but you can calibrate the sticks value range:
-Push the right lower trim tab to the left (aileron on a mode 2 TX), and the left side trim tab up (Throttle trim on a mode 2 tx), and while holding them, turn the Tx on.
You will get a screen that shows the version number of the firmware, with four zeroes (0000) at the top of the screen.
-Center both sticks in their range in all axis, and press the menu button until it beeps and release. The screen number will change to 0001.
-Move the right stick to the upper right corner, and press menu, and you should get a beep, and the number on the screen will change to 0002.
-Put the right stick in the lower left corner, and press menu, and get a beep, and the number will change to 0003.
-Release the right stick, and put the left stick in the upper right corner, and press menu, get the beep, and the number will change to 0004.
-Put the left stick in the lower left corner, and press menu again, get the beep and the number will change to 0005.
-Center both sticks, and press menu, get the beep. You are done calibrating the sticks.
5. Some also say you need to set the ATV/servo range settings (Menu\Func\E. Trim):
pitch (elevator): 50%
roll (aileron): 50%
yaw (rudder): 100%
Initial gain pot values is 50%. Increase until it starts to oscillate rapidly, then back of until it is stable again. Fast forward flight needs lower gain. But I found mine to work just fine at 100%.
5. You can find the "DISPLAY" option in the Func menu, and that will display the values of the sticks so you know it is working.
6. Here is how to use the sticks on the TX to arm/disarm etc the quad with multiwii v2.0:
Basic MWC code config
-First, you need to get the latest MultiWii code and the latest Arduino IDE. Once you have the code open and have the config.h tab selected, you need to uncomment the orientation your quad will be in(QUADP, QUADX etc) under "The type of multicopter". Right below "type of multicopter", change I2C speed and I2C pullups if need be for your sensors. Then scroll down a bit until you find some #define lines for the gyro, acc, and mag. Like before uncomment the sensors you have or uncomment a board if yours is there. You could also search for RCAUXPIN and change that if you have a second auxiliary channel.
-If you have a ITG3200 or MPU6050 gyro, go to the Low pass filter setting section and un comment the 42Hz filter for your gyro. This can significantly stabilize your quad. I have the L3G4200D gyro which doesnt have LPF settings built into the code, but i found this post which has some code for my gyro that goes in the sensor tab of the code. Just find the old code and paste over it with the new code. But the best fix for reducing vibrations etc, is to fix it at the source--balance motors, props and get some good anti-vibration padding for the FC.
-#define MINTHROTTLE When you arm your quad, this is the value that is sent to the ESCs. it should be a little below the value that makes the motors spin up. This should be a little bit less than the min value of your throttle channel, aka,1095.
-#define MAXTHROTTLE This is the max value of the ESCs(aka full power); when the throttle stick is all the way up, this value is sent to the ESCs. Should be the max value of your throttle channel, AKA, 1905.
-#define MINCOMMAND is the value sent to the ESCs when disarmed. This should be below the minimum throttle, AKA 1095.
Setting up TX Channels in the GUI
-There are two main things we need to look at for now: transmitter channel values and the sensor data. First make sure the quad is disarmed and move all the sticks to their center positions. Now look at the TX channel values; they should all be around 1500 when centered. If not, go to your TX function menu and look for subtrim. Subtrim allows you to change where the center point is of the channels, so change the subtrim for each channel so that the values are at 1500. Then move each stick to its lower limit; it should be near 1095. If not, go to E. point in the function menu and change the percentages for each channel to change how low or high the channel value goes. If you move the selector to RUD(aka yaw), for example, you can move the yaw stick from side to side to select the high and low range value percentage. Use + and - to change the value. Do the same for each stick except for the upper limit of the channel value range; it should be 1905. Now all channels should have a midpoint of 1500 and lower limit of 1095 and a upper limit of 1905. If you cant for some reason change the center value of the TX channels, find the code #define MIDRC and change it to your center value. Also, if the channel values jump around a bit(jitter + or - a few) and cause minor drift, find #define DEADBAND and uncomment it to enable a deadband around the pitch/roll stick centers.
Check for Gyro Orientation
-Then click on the attitude meter so that it changes to pitch/roll indicators. Now, when you tilt your quad to the left, the roll line should tilt to the quad angle. Check the same for pitch(front/back tilting). If pitch/roll don't line up with the quad, then you'll need to change the GYRO_ORIENTATION part of the config code. It looks like this:
#define GYRO_ORIENTATION(X, Y, Z) {gyroADC[ROLL] = -Y; gyroADC[PITCH] = X; gyroADC[YAW] = Z;} Specifically, if it rolls right when you tilt it left, -Y would become Y.
So, say pitch and roll are switched, you need to change (X, Y, Z) to (Y, X, Z). Y is pitch, X is roll, and Z is Yaw. If roll was reversed(whwen you tilt the quad left, mw-wingui shows it tilting right, for example), you'd need to change gyroADC[ROLL] = -Y to gyroADC[ROLL] = Y. Note the "-" sign.
-If your quad is "yawing" the wrong way find the #define YAW_DIRECTION 1 part and comment it out and uncomment the #define YAW_DIRECTION -1 line. If you push the yaw stick right the front of the multirotor should turn right
Check for Motor Rotation
Look here under Connecting Motors and Servos to find motor rotation(arrows around the motors) for all types of multirotors. If the motor is spinning the wrong way, switch any two wires (of the three going to the motor) and it will reverse direction. It also shows, as i said before, the port that the ESCs should be connected to on the FC(numbers in the motors).
ESC Setup--This is done after the TX channels are set to 1095/1500/1905.
To set the throttle range of the ESCs:
Do this for each ESC. You can also get/make a 1-4 adapter so you can program all of them at once.
1. Connect the ESC to the throttle channel of the receiver (In my case it was ch3). Make sure the motor is connected to the ESC, as it produces the diagnostic beeps, but no props on because a malfunction may cause a prop to go spinning around at 100% throttle...
1. Connect the ESC to the throttle channel of the receiver (In my case it was ch3). Make sure the motor is connected to the ESC, as it produces the diagnostic beeps, but no props on because a malfunction may cause a prop to go spinning around at 100% throttle...
2. Turn on the radio and put the throttle stick to maximum position
3. Now connect the ESC to the battery
4. The motor should do a 1 2 3 beep followed by two short beeps. Immediately after the two
short beeps, put the throttle stick down all the way, the motors should make 3 short beeps
(representing the 3 cells of the battery) and then finally make a 1 long beep, indicating that the
throttle range has been set (calibrated).
There are two options here: a programming card or using "throttle stick programming". I somehow forgot to put the programming card in my shopping cart from the HK wish list, so, i had to use the second method. Its pretty similar to setting the throttle range until step 4.
1. Connect the ESC to the throttle channel of the receive (In my case it was ch3). Make sure the motor is connected to the ESC, as it produces the diagnostic beeps, but no props on because a malfunction may cause a prop to go spinning around at 100% throttle...
2. Turn on the radio and put the throttle stick to maximum position
3. Now connect the ESC to the battery
4. Wait for a musical tune from the ESC
5. After the tone should be a sequence of beeps: setting after "(...)" is what it should be
one short beep is the brake setting(off, on) off
two short beeps is battery type(Li-xx, Ni-xx) Ni-xx
three short beeps is cut off type(Soft-Cut, Cut-off) Soft-Cut
four short beeps is cut off voltage(Low, Med, High) Low
one long is startup mode(Normal, Soft, Very Soft) Normal
one long and a short beep is timing(Low, Med, High) Med is typical.
one long and two short is factory reset
two long beeps is end setup mode and resumes normal operation.
Then it plays the musical tone and loops through the beeps again.
6. To select a setting to change, wait until right after the correct beep code(step 5), and then quickly bring the throttle stick down. This selects the setting.
7. Now, the ESC should beep a few times depending on how many options the particular setting has. To set the option(in parentheses in step 5), just move the throttle stick back up immediately after the correct option beep. The ESC will then continue with the sequence of beeps until it is unconnected or the "exit setup" setting is selected.
That was probably confusing, watch this video instead. :)
Before you Fly
Do general checks:
-Make sure all connections are tight
-TX in the right mode etc
-Nothing loose
Props
Now that everything else is done put the props on. You probably cant tell, but i have the props on upside down in the picture below of my first build. For some reason when i was putting them on, i didn't pay attention to the shape(like an airfoil) and put them on upside down. Most times the writing(most times a brand name and prop size/pitch) is facing up.
This caused a lack of power( loss of 50%!), and yaw issues. Here are some pics to help with prop orientation. Motor rotation is shown by the arrows around the motors.
This shows the direction that the motors need to spin!
Pusher/CW on front back, Tractor/CCW/ on left/right (for a "+" quad)
Before you Fly
Do general checks:
-Make sure all connections are tight
-TX in the right mode etc
-Nothing loose
-Do training in flight sim:
Follow this guide: HobbyKing Forums - How-to: Using your transmitter for simulator use. Make sure you turn on training mode( Menu/ Func/Trainer), and don't turn on the TX power when using it for the simulator as it seems to not work(will turn on automatically when the the audio cable is plugged in). That was how it worked at first, but then it stopped working and the power switch, along with the trainer switch, had to be on for it to work! Its so weird. After i got the er9x firmware on it, i have had no issues!
Post Setup "Stuff"
TX Mods
battery holder, trainer mode mod, er9x software mod
How to setup er9x for MWC
General Problems
ESC smokes: try switching the bad ESC with a working one to see if it is the ESC or motor(if the new ESC smokes, it is the motor and if the new ESC doesn't smoke, it was just a bad ESC) OR switch the motor with another known good one. if the ESC still smokes then it is the ESC, if it doesnt smoke again, it was the motor(touching phases), but that requires that the ESC works (the one that smoked).
PID Tuning
This will make your quad way more stable. I can guarantee that everyone's PID settings are different because all quads are slightly different in structure. Here are some guides until i figure out how to explain it: http://www.rcgroups.com/forums/showthread.php?t=1375728
http://www.rcgroups.com/forums/showthread.php?t=1261382&page=498&#post17216625
http://www.rcgroups.com/forums/showpost.php?p=17215161&postcount=7460
http://www.rcgroups.com/forums/showthread.php?t=1261382&page=545#post17311692
Here are some short how-to statements:
P: This is probably the easiest. if the quad oscillates by shaking it or in flight, lower P. if it doesn't oscillate then increase P until it does and the decrease a bit until it is stable.
I: "An easy way I found to get the "I" value right.. I run up the motors while holding it above my head then pull back some pitch to tilt the quad up at the front, if it falls back level again then I will increase I until it just stays there.."
D: This is how fast the multirotor moved on the axis. if it is sluggish, increase D (remember, that means a LOWER number as it is a negative value). If you have oscillations you can either decrease D (remember, that means a HIGHER number as it is a negative value - i.e. further negative from zero) or P.
"PID" Stand
This is a most times wooden stand that limits the quad to one axis of rotation(pitch or roll). It limits the quad to one axis(roll or pitch) and is used to do PID tuning. Mine is quite simple; just a "+" shaped base with two support 2x4s that have some 1/2in PVC mounts for the quad arms.
After doing some more tuning, i find it easier to just go fly it after making changes rather than using this thing.
Flashing the ESCs with a after market will provide a much more stable multirotor. Check out the Atmel(by Britt) or the Silabs(by me) flashing guides at oddcopter.com.
Prop Balancing
This is quite simple too; a square base with two hard drive magnets mounted about 4in away from each other on some scrap wood. In the center is a simple piece of all thread ground to a point to reduce the friction on the one end that does touch the magnet(one end is floating, one is on the magnet).
To balance the props, find which side is heavier using the balancer and add some sticky tape to the tip of the leading edge of the lighter side. Some people also remove some material on the heavy side instead.
ACC Tuning
The ACC(accelerometer) keeps the quad at a constant level, except for pitch/roll stick input and then snaps back to level, and always needs trimming from default settings. You can do ACC trimming without a PID stand, it just probably harder. To trim the accelerometer so that it holds the quad level, do the following;
1. Put the quad into level mode with the Aux channel and attempt to lift off. It will most likely not lift-off level. Note which side(or sides) that lift of first.
2. Disable the motors(double check), and move the throttle to 100%.
3. If your right motor lifted off first, for example, you need to trim in that direction. So, move the roll stick to the right, hold it for about a second, and then bring it back to center. This is one "tick" and if you have a LED on your FC, you will see it blink for each tick.
4. This one tick may not be enough, so you need to arm the motors again and see if it is level. If not level, repeat #3.
Motor Rewinding
Rewinding a motor isn't that hard, just look at this site. You need to make sure you have the correct number of turns on each tooth, the correct termination(delta or wye), correct wire size, and the winding type(LRK,dLRK, ABC).
In my case:
The motor was originally 924kV, so from this picture i can tell that i need 12 turns and delta termination(delta because that's what the motor was when i took it apart). For wire size: the bigger the better, but you must still be able to fit all the windings on the teeth, and in my case someone said 25AWG would work. I went to a nearby 3E and got some 25AWG clear enameled magnet wire. The motor had 12 teeth and 14 magnet, so i had to use the dLRK winding method. Always wind the wire clockwise on the stator tooth(that is unless you're doing dLRK in which some are CCW).
In my case:
The motor was originally 924kV, so from this picture i can tell that i need 12 turns and delta termination(delta because that's what the motor was when i took it apart). For wire size: the bigger the better, but you must still be able to fit all the windings on the teeth, and in my case someone said 25AWG would work. I went to a nearby 3E and got some 25AWG clear enameled magnet wire. The motor had 12 teeth and 14 magnet, so i had to use the dLRK winding method. Always wind the wire clockwise on the stator tooth(that is unless you're doing dLRK in which some are CCW).
Done rewinding...you can see the sharpie mark i used for marking a tooth as the starting point.
After thoughts...
Always double check your prop orientation--it can cause many issues if you think you have them on right when they aren't (guess how i know...).
Be nice to your motors! Don't put the props on upside down/too big of props and cause the to burn out. Some other motor alternatives for this size of quad are the: Turnigy 2217 16turn, KD A22-20L, A20-20L, KD 36-28S, Park450(1200kv), or Turnigy D3530/14. The 22-20Ls would probably the most powerful of the bunch. flybrushless and ecalc are very helpful for finding the right prop.
Get a 2200mah 3s LiPo(or so) instead of a 4000mah like i did. The 2200 is about 40% of the weight and so you will get more agile flying, but less flight time. use the 4000mah for FPV etc.
A LiFe or a LiPo TX battery works much better than using inconsistent AAs and their battery holder.
I would recommend this IOI FC board for multiwii as it is even cheaper than my DIY FC solution was and has more sensors!
Hide the ESCs in the center of the frame as it looks so much nicer!
Unless you have access to a CNC or similar, it's a good idea not to build your first frame ;).
My (re)Builds
First
I learned A LOT from this first build of my quad copter- the arm mount/center hub idea didn't work to well, neither did the motor mounts.
- get some CF mounts for the arms (for center plate and motors) that don't put holes in the CF arms.
-still haven't really decided on a battery holder on landing gear.
-pay attention to how you put the props on :D
-thoroughly research prop + amp draw.
-either buy a cheap(and awesome) bambu IOI FC, or make a FC on a PCB -- saves mounting hassle.
pics: Look here to see the CAD drawing of it. It's quite simple with only four 10mm square CF tubes and some 1.5mm CF sheet.
I put some blue tape roughly where I needed to cut and then marked exactly where i needed with a pencil.
Done cutting
Again used blue tape so that i can mark my holes. Both pieces of CF are taped together so that the holes will line up perfectly.
3/8in(10mm) "+" pencil colored shape is where the square tube would sit when assembled. Not perfectly lined up holes, but it worked out fine.
CF pieces will assemble to the blue highlighted CF piece.
Motor mounted.
Done!
Second Build
Made a few changes:
-using pool noodles for landing gear
-props on correctly :P
-smaller battery; the 4000mah works pretty good too. the flights are really long :D
-drilled four holes into the arms so that i cant bolt it directly to the frame and motors (bad idea).
-FC a bit cleaner, still need to mount better.
Third Build
-Still using CF, but had dowel and guerrilla glue inserts- really strong!
-Real battery holder.
-Proper FC mount.
Dowel and guerrilla glue insert
Hiding the power spider in between the CF plates this time!
Looks much better.
Battery strap holder.
New proper FC mount.
Need to tie down motor wires, ESCs, and RX.
Fourth Build
-This is the current build and is in the build log section.
Subscribe to:
Posts (Atom)































.jpg)








































