This is the arduino segway clone main article describing major functionality
For a long time i have admired the early implementors of the arduino segway design concepts in their own builds. One of the earliest arduino segway builders was Trevor blackwell. I studied carefully his design but found it too difficult to do by myself.
There were several other arduino segway clone designs which inspired me such as the Zzaag arduino segway project www.zzaag.org which also published the software. Elektor magazine also published a design https://www.elektor.nl/projecten/elektorwheelie.985979.lynkx which included software.
The elektor PCB’s are not separately available but you can get them from Guimocircuito in Portugal http://www.guimocircuito.com/product_info.php?products_id=516. You get two circuit boards for 28€. The large one houses the processor and motor control parts and the other small print is used for the piggy back mount of the gyro and accelerometer. The elektor design has the ACS755 SCB-100 current sensing IC which is used as a safety device in case of problems with the motors.
Then there was the arduino segway clone design of Günter Gerold of Gerold – Elektronik who also was so generous as to publish all his results online. I especially liked his work on gearsensors to capture the actual speed of the seguino and the improvements he made to the motor driver circuits.
I wanted to take the best of all these designs and put them into one circuit, but still have the lowest cost of all the projects (the zzaag project). In thinking about the design i also came up with several additional improvements which i will describe later.
I started off by buying a kit version of the arduino segway clone printed circuit board from Zzaag and built it with some modifications to the microprocessor board as per the schematic below.
They were small changes but needed for the overall design to work. The bottom right part of the circuit is the new reset function which allows a reset of the microprocessor with a push on a button.
I needed to bring out the wire pins which are the SCL and SDA pins. The PCB wires had to be cut to do that and the respective pins on the GPIO connector connected up to Atmega pins 17 and 29 respectively.
I also cut the positive power from the GPIO connector and connected it to Atmega pin 27. These changes allow 6 LED’s to be connected to the GPIO interface and you can still have the buzzer to connected.
Four LED’s are used for the battery level, one for heartbeat and one is the power indicator and is allways on when the power supply is on. They are connected through the GPIO connector which is a 2 * 5 pin connector.
I also added a LED to visualise the power supply and finally made the two changes recommended on Günter Gerold’s site to allow better operation of the steering poti and allow correct battery level reading. These were the small 47 and 100k resistors.
- R106 from 68k to 100k. This increases the range of the voltage measurements. All WOF Software assumes 100k.
- R107 from 4k7 to 47k. This makes the steering signal slower and eliminates glitches.
Finally i added the serial connection TXD and RXD to the RS232 connector pins. If you look then the pinout is the same as a standard FDTI connector so that this type of connector can be connected directly here.
There is a bridge connection between VCC and output from 24Volt to VCC chip.
JP4 can be used to connect the I2C gyro and accelerometer such as the wii motion plus and nunchuck.
You can also see the large heatsinks and resistors which are used to prevent overheating of the voltage regulators. I did this to both boards.
The voltage regulator on the secondary board was also introduced to reduce heat dissipation. The resistors were 330Ohm 2W on the main PCB and two 100Ohm 2W resistors on the secondary PCB.
!!!!!! The 330Ohm resistor is not shown on the main PCB drawing. !!!!
software for arduino segway clone
I also wanted the development software to be as easy to use as the arduino. Looking at the Elektor/Zzaag design I noticed that the processor was an Atmega32 processor. This processor is pin compatible with the larger and more powerfull Atmega644P having 64K of flash memory and 4K RAM with a 2K EEPROM. The beauty of this fact is that you can use the Sanguino software to create a fully arduino compatible system. www.sanguino.cc. So I bought a Sanguino processor which was preprogrammed with the arduino bootloader from http://www.bhasha.co.cc/product.php?id_product=34. In addition i bought a FDTI ready made cable here Serial-Converter which connects to the 6 pin FDTI connector which the above mods creates on the microprocessor pcb.
You can see that the connector is 5 pins wide and the FDTI connector 6. The black pin sticks out beyond the connector. You can then see that all the other pins line up.
Innitially I powered it through the USB port to check everything was OK and I was able to get the processor to buzz the buzzer on the Zzaag processor PCB using the following program.
The blink program blinks all the 6 LEDs.
Blink program [spoiler]
/* Do segway clone innitial test to blink the LED's Created 2 January 2010 by Krulkip */ int LED1 = 18; // LED1 connected to digital pin18 PC2 of segway clone int LED2 = 19; int LED3 = 20; int LED4 = 21; int LED5 = 22; int LED6 = 23; void setup() { pinMode(LED1, OUTPUT); // initialize the digital pin as an output on segway clone: pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(LED5, OUTPUT); pinMode(LED6, OUTPUT); } void loop() { digitalWrite(LED1, HIGH); // set LED1 of segway clone on digitalWrite(LED2, HIGH); digitalWrite(LED3, HIGH); digitalWrite(LED4, HIGH); digitalWrite(LED5, HIGH); digitalWrite(LED6, HIGH); delay(500); // wait for a half second digitalWrite(LED1, LOW); // set LED1 off digitalWrite(LED2, LOW); digitalWrite(LED3, LOW); digitalWrite(LED4, LOW); digitalWrite(LED5, LOW); digitalWrite(LED6, LOW); delay(1000); // wait for a second }
[/spoiler]
Buzzer program [spoiler]
/* Do arduino segway clone innitial test to piep the buzzer Created 2 January 2010 by Krulkip */ int Buzzer = 11; // Buzzer connected to digital pin11 PD3 void setup() { pinMode(Buzzer, OUTPUT); // initialize the digital pin as an output: } void loop() { digitalWrite(Buzzer, HIGH); // set the Buzzzer on delay(500); // wait for a second digitalWrite(Buzzer, LOW); // set the Buzzer off delay(1000); // wait for a second }
[/spoiler]
Download the files here Blink.pde Buzz.pde
here are some additional test programs which can make some sounds and also blink the leds
[spoiler]
// Test program for arduino segway clone // blinks all the LED's, // and uses tone library with htttl songs // also displays all connected analog ports with gyro accelrometer footswitch and battery monitor to serial port // written for modified sanguino with ATMega644P // by krulkip v1.0.0 23 august 2010 #include <avr/pgmspace.h> #include <Tone.h> #include "WProgram.h" Tone tone1; #define OCTAVE_OFFSET 0 #define isdigit(n) (n >= '0' && n <= '9') int notes[] = { 0, NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4, NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6, NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7 }; //char *song = "MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d"; char *song = "pacman:d=4,o=5,b=112:32b,32p,32b6,32p,32f#6,32p,32d#6,32p,32b6,32f#6,16p,16d#6,16p,32c6,32p,32c7,32p,32g6,32p,32e6,32p,32c7,32g6,16p,16e6,16p,32b,32p,32b6,32p,32f#6,32p,32d#6,32p,32b6,32f#6,16p,16d#6,16p,32d#6,32e6,32f6,32p,32f6,32f#6,32g6,32p,32g6,32g#6,32a6,32p,32b.6"; //char *song = "wwtbam1:d=4,o=5,b=140:16g,16c6,16d#6,16g6,16c7,16g6,16d#6,16c6,16g,16c6,16d#6,16g6,16c7,16g6,16d#6,16c6,16g#,16c6,16d#6,16g#6,16c7,16g#6,16d#6,16c6,16g#,16c6,16d#6,16g#6,16c7,16g#6,16d#6,16c6,16f#,16c6,16d#6,16f#6,16c7,16f#6,16d#6,16c6,16f#,16c6,16d#6,16f#6,16c7,16f#6,16d#6,16c6,16g,16b,16d6,16g6,16b6,16g6,16d6,16b,16g,16b,16d6,16g6,16b6,16g6,16d6,16b"; //char *song = "Barbie girl:d=4,o=5,b=125:8g#,8e,8g#,8c#6,a,p,8f#,8d#,8f#,8b,g#,8f#,8e,p,8e,8c#,f#,c#,p,8f#,8e,g#,f#"; //char *song = "KnightRider:d=4,o=5,b=63:16e,32f,32e,8b,16e6,32f6,32e6,8b,16e,32f,32e,16b,16e6,d6,8p,p,16e,32f,32e,8b,16e6,32f6,32e6,8b,16e,32f,32e,16b,16e6,f6,p"; //char *song = "PinkPanther:d=4,o=5,b=160:8d#,8e,2p,8f#,8g,2p,8d#,8e,16p,8f#,8g,16p,8c6,8b,16p,8d#,8e,16p,8b,2a#,2p,16a,16g,16e,16d,2e"; //char *song = "axelf:d=4,o=5,b=160:f#,8a.,8f#,16f#,8a#,8f#,8e,f#,8c.6,8f#,16f#,8d6,8c#6,8a,8f#,8c#6,8f#6,16f#,8e,16e,8c#,8g#,f#"; //char *song = "Xfiles:d=4,o=5,b=125:e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,g6,f#6,e6,d6,e6,2b.,1p,g6,f#6,e6,d6,f#6,2b.,1p,e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,e6,2b"; //char *song = "YMCA:d=4,o=5,b=160:8c#6,8a#,2p,8a#,8g#,8f#,8g#,8a#,c#6,8a#,c#6,8d#6,8a#,2p,8a#,8g#,8f#,8g#,8a#,c#6,8a#,c#6,8d#6,8b,2p,8b,8a#,8g#,8a#,8b,d#6,8f#6,d#6,f.6,d#.6,c#.6,b.,a#,g#"; int buzzer = 11; int yellow2ledPin = 23; int yellow1ledPin = 18; int red1ledPin = 19; int green2ledPin = 20; int green1ledPin = 22; int red2ledPin = 21; int DB[] = {green1ledPin,green2ledPin,yellow1ledPin,yellow2ledPin,red1ledPin,red2ledPin}; void setup() { Serial.begin(9600); tone1.begin(buzzer);// buzzer is on PIN 28 play_rtttl(song); pinMode(green1ledPin, OUTPUT); pinMode(green2ledPin, OUTPUT); pinMode(yellow1ledPin, OUTPUT); pinMode(yellow2ledPin, OUTPUT); pinMode(red1ledPin, OUTPUT); pinMode(red2ledPin, OUTPUT); for (int i=0;i<8;i++){ for(int j=0;j<6;j++){ digitalWrite(DB[j], LOW);delay(50); } for(int j=0;j<6;j++){ digitalWrite(DB[j], HIGH);delay(50); } } for (int i=0;i<8;i++){ for(int j=0;j<6;j++){ digitalWrite(DB[j], LOW);delay(50); } for(int j=0;j<6;j++){ digitalWrite(DB[5-j], HIGH);delay(50); } } for (int i=0;i<8;i++){ for(int j=0;j<6;j++){ digitalWrite(DB[j], LOW);delay(50);digitalWrite(DB[j], HIGH);delay(50); } } } void loop() { delay(1800); for (int i=0;i<8;i++){ for(int j=0;j<6;j++){ digitalWrite(DB[j],LOW);delay(50);digitalWrite(DB[j],HIGH);delay(50);} } delay(1000); } void play_rtttl(char *p) { // Absolutely no error checking in here on arduino segway clone byte default_dur = 4; byte default_oct = 6; int bpm = 63; int num; long wholenote; long duration; byte note; byte scale; // format: d=N,o=N,b=NNN: // find the start (skip name, etc) while(*p != ':') p++; // ignore name p++; // skip ':' // get default duration if(*p == 'd') { p++; p++; // skip "d=" num = 0; while(isdigit(*p)) { num = (num * 10) + (*p++ - '0'); } if(num > 0) default_dur = num; p++; // skip comma } Serial.print("ddur: "); Serial.println(default_dur, 10); // get default octave if(*p == 'o') { p++; p++; // skip "o=" num = *p++ - '0'; if(num >= 3 && num <=7) default_oct = num; p++; // skip comma } Serial.print("doct: "); Serial.println(default_oct, 10); // get BPM if(*p == 'b') { p++; p++; // skip "b=" num = 0; while(isdigit(*p)) { num = (num * 10) + (*p++ - '0'); } bpm = num; p++; // skip colon } Serial.print("bpm: "); Serial.println(bpm, 10); // BPM usually expresses the number of quarter notes per minute wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole note (in milliseconds) Serial.print("wn: "); Serial.println(wholenote, 10); // now begin note loop while(*p) { // first, get note duration, if available num = 0; while(isdigit(*p)) { num = (num * 10) + (*p++ - '0'); } if(num) duration = wholenote / num; else duration = wholenote / default_dur; // we will need to check if we are a dotted note after // now get the note note = 0; switch(*p) { case 'c': note = 1; break; case 'd': note = 3; break; case 'e': note = 5; break; case 'f': note = 6; break; case 'g': note = 8; break; case 'a': note = 10; break; case 'b': note = 12; break; case 'p': default: note = 0; } p++; // now, get optional '#' sharp if(*p == '#') { note++; p++; } // now, get optional '.' dotted note if(*p == '.') { duration += duration/2; p++; } // now, get scale if(isdigit(*p)) { scale = *p - '0'; p++; } else { scale = default_oct; } scale += OCTAVE_OFFSET; if(*p == ',') p++; // skip comma for next note (or we may be at the end) // now play the note if(note) { Serial.print("Playing: "); Serial.print(scale, 10); Serial.print(' '); Serial.print(note, 10); Serial.print(" ("); Serial.print(notes[(scale - 4) * 12 + note], 10); Serial.print(") "); Serial.println(duration, 10); tone1.play(notes[(scale - 4) * 12 + note]); delay(duration); tone1.stop(); } else { Serial.print("Pausing: "); Serial.println(duration, 10); delay(duration); } } }
[/spoiler]
[spoiler]
//example use of tone library with arduino segway clone //by krulkip v1.0.0 23 august 2010 #include <avr/pgmspace.h> #include <Tone.h> Tone tone1; #define OCTAVE_OFFSET 0 #define isdigit(n) (n >= '0' && n <= '9') int notes[] = { 0, NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4, NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6, NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7 }; //char *song = "MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d"; char *song = "pacman:d=4,o=5,b=112:32b,32p,32b6,32p,32f#6,32p,32d#6,32p,32b6,32f#6,16p,16d#6,16p,32c6,32p,32c7,32p,32g6,32p,32e6,32p,32c7,32g6,16p,16e6,16p,32b,32p,32b6,32p,32f#6,32p,32d#6,32p,32b6,32f#6,16p,16d#6,16p,32d#6,32e6,32f6,32p,32f6,32f#6,32g6,32p,32g6,32g#6,32a6,32p,32b.6"; //char *song = "wwtbam1:d=4,o=5,b=140:16g,16c6,16d#6,16g6,16c7,16g6,16d#6,16c6,16g,16c6,16d#6,16g6,16c7,16g6,16d#6,16c6,16g#,16c6,16d#6,16g#6,16c7,16g#6,16d#6,16c6,16g#,16c6,16d#6,16g#6,16c7,16g#6,16d#6,16c6,16f#,16c6,16d#6,16f#6,16c7,16f#6,16d#6,16c6,16f#,16c6,16d#6,16f#6,16c7,16f#6,16d#6,16c6,16g,16b,16d6,16g6,16b6,16g6,16d6,16b,16g,16b,16d6,16g6,16b6,16g6,16d6,16b"; //char *song = "Barbie girl:d=4,o=5,b=125:8g#,8e,8g#,8c#6,a,p,8f#,8d#,8f#,8b,g#,8f#,8e,p,8e,8c#,f#,c#,p,8f#,8e,g#,f#"; //char *song = "KnightRider:d=4,o=5,b=63:16e,32f,32e,8b,16e6,32f6,32e6,8b,16e,32f,32e,16b,16e6,d6,8p,p,16e,32f,32e,8b,16e6,32f6,32e6,8b,16e,32f,32e,16b,16e6,f6,p"; //char *song = "PinkPanther:d=4,o=5,b=160:8d#,8e,2p,8f#,8g,2p,8d#,8e,16p,8f#,8g,16p,8c6,8b,16p,8d#,8e,16p,8b,2a#,2p,16a,16g,16e,16d,2e"; //char *song = "axelf:d=4,o=5,b=160:f#,8a.,8f#,16f#,8a#,8f#,8e,f#,8c.6,8f#,16f#,8d6,8c#6,8a,8f#,8c#6,8f#6,16f#,8e,16e,8c#,8g#,f#"; //char *song = "Xfiles:d=4,o=5,b=125:e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,g6,f#6,e6,d6,e6,2b.,1p,g6,f#6,e6,d6,f#6,2b.,1p,e,b,a,b,d6,2b.,1p,e,b,a,b,e6,2b.,1p,e6,2b"; //char *song = "YMCA:d=4,o=5,b=160:8c#6,8a#,2p,8a#,8g#,8f#,8g#,8a#,c#6,8a#,c#6,8d#6,8a#,2p,8a#,8g#,8f#,8g#,8a#,c#6,8a#,c#6,8d#6,8b,2p,8b,8a#,8g#,8a#,8b,d#6,8f#6,d#6,f.6,d#.6,c#.6,b.,a#,g#"; int buzzer = 22; void setup() { Serial.begin(9600); tone1.begin(11);// buzzer is on PIN 11 of segway clone play_rtttl(song); } void loop() { delay(1000); } void play_rtttl(char *p) { // Absolutely no error checking in here on arduino segway clone byte default_dur = 4; byte default_oct = 6; int bpm = 63; int num; long wholenote; long duration; byte note; byte scale; // format: d=N,o=N,b=NNN: // find the start (skip name, etc) while(*p != ':') p++; // ignore name p++; // skip ':' // get default duration if(*p == 'd') { p++; p++; // skip "d=" num = 0; while(isdigit(*p)) { num = (num * 10) + (*p++ - '0'); } if(num > 0) default_dur = num; p++; // skip comma } Serial.print("ddur: "); Serial.println(default_dur, 10); // get default octave if(*p == 'o') { p++; p++; // skip "o=" num = *p++ - '0'; if(num >= 3 && num <=7) default_oct = num; p++; // skip comma } Serial.print("doct: "); Serial.println(default_oct, 10); // get BPM if(*p == 'b') { p++; p++; // skip "b=" num = 0; while(isdigit(*p)) { num = (num * 10) + (*p++ - '0'); } bpm = num; p++; // skip colon } Serial.print("bpm: "); Serial.println(bpm, 10); // BPM usually expresses the number of quarter notes per minute wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole note (in milliseconds) Serial.print("wn: "); Serial.println(wholenote, 10); // now begin note loop while(*p) { // first, get note duration, if available num = 0; while(isdigit(*p)) { num = (num * 10) + (*p++ - '0'); } if(num) duration = wholenote / num; else duration = wholenote / default_dur; // we will need to check if we are a dotted note after // now get the note note = 0; switch(*p) { case 'c': note = 1; break; case 'd': note = 3; break; case 'e': note = 5; break; case 'f': note = 6; break; case 'g': note = 8; break; case 'a': note = 10; break; case 'b': note = 12; break; case 'p': default: note = 0; } p++; // now, get optional '#' sharp if(*p == '#') { note++; p++; } // now, get optional '.' dotted note if(*p == '.') { duration += duration/2; p++; } // now, get scale if(isdigit(*p)) { scale = *p - '0'; p++; } else { scale = default_oct; } scale += OCTAVE_OFFSET; if(*p == ',') p++; // skip comma for next note (or we may be at the end) // now play the note if(note) { Serial.print("Playing: "); Serial.print(scale, 10); Serial.print(' '); Serial.print(note, 10); Serial.print(" ("); Serial.print(notes[(scale - 4) * 12 + note], 10); Serial.print(") "); Serial.println(duration, 10); tone1.play(notes[(scale - 4) * 12 + note]); delay(duration); tone1.stop(); } else { Serial.print("Pausing: "); Serial.println(duration, 10); delay(duration); } } }
[/spoiler]