1: Arduino simuleert EIbotboard (Inkscape Plugin)

‘The Original Egg-bot’ van Evil Mad Scientist is erg populair geworden doordat de elektronica van deze robot, genaamd de EibotBoard, erg goed wist te communiceren met Inkscape. Inkscape is een vector tekenapplicatie die ontwikkelaars de kans biedt om extensies te programmeren. Deze extensies worden geprogrammeerd in Python en kunnen functies toevoegen aan het tekenprogramma. De maker en bedenker van de EibotBoard heeft een ingewikkeld programma ontwikkeld dat met één druk op de knop, een getekend vectorafbeelding kan laten uittekenen door de robot.

De extensie berekent aan de hand van de tekening een lijst met opdrachten die uitgevoerd moeten worden. De lijst bestaat uit het aantal millimeters dat iedere stappenmotor moet draaien, of bevat informatie over de gewenste stand van de Servo, die de penhouder aanstuurt. Als alle opdrachten berekend zijn start de extensie een seriële communicatie met de EibotBoard, waarna een opdracht gestuurd wordt naar de EibotBoard, de extensie wacht vervolgens op bevestiging van voltooiing waarna een volgende opdracht gestuurd wordt. Dit herhaalt zich totdat de totale opdrachtenlijst voltooid is en de tekening is getekend.

Wat de opdrachten van de EibotBoard zo speciaal maakt is de ontwikkelde taal die de bedenker van EibotBoard heeft bedacht. De opdracht voor het aansturen van stappen motoren is als volgt ingedeeld: SM xwaarde ywaarde tijdsduur. Wat belangrijk is, is dat de genoemde stappen (of afstand) precies binnen een bepaalde tijd moet zijn uitgevoerd en niet sneller klaar mogen zijn dan de voorgeschreven tijd.

Uitleg voorbeeld stappen opdracht

Uitleg voorbeeld stappen opdracht

Ik beschouw heel even een ei als een cilinder, die ik voor het gemak uitgestreken heb tot een recht vierkant: Stel een vectortekening bestaat uit een rechte lijn die van rechtsboven naar linksonder gaat. Beide stappenmotoren moeten hierbij samenwerken, het is niet mogelijk om eerst de stappenmotor de laten draaien voor de X as om vervolgens de stappenmotor van de Y as te laten draaien. Het is van groot belang dat de snelheid van de stappenmotoren in een bepaalde verhouding werken, anders klopt de richtingscoëfficiënt van die lijn niet.

De opbouw van de opdrachtentaal is hierop gebaseerd, de verhouding tussen de afstanden van de x as en de y as vertelt iets over de richting van de lijn. De laatste variabele, de tijdsduur, is eigenlijk niet van heel groot belang. Wel kan de dikte enigszins aangepast worden door de snelheid aan te passen, een vlugge beweging zorgt immers voor een dunnere lijn dan een langzame beweging.

Python plugin focus

De extensie die ontwikkeld is voor de Eggbot bestaat uit verschillende onderdelen. Om ervaring op te doen heb ik me in een deel val de programmatuur verdiept, om vervolgens aanpassingen aan te kunnen brengen.

1. Vectoren omzetten in rechte lijnen met verschillende lengtes

2. Opdrachtenlijst opstellen aan de hand van de rechte lijnen

3. Controleren of EibotBoard is aangesloten op computer

4. Seriële communicatie opzetten, bevestigen dat EibotBoard is verbonden

5. Opdrachten versturen, wachten op akkoord en herhalen

Dieper ingang op stappen:

3. Als eerste gaat het programma na op welk besturingssysteem de extensie gedraaid wordt.


import sys

platform = sys.platform.lower()

if platform == 'win32':
 from eggbot_scanwin32 import *
elif platform == 'darwin':
 from eggbot_scanosx import *
elif platform == 'linux2':
 from eggbot_scanlinux import *
else:
 from eggbot_scanposix import *

Bij Windows gaat het programma vervolgens in de Windows registry opzoek naar een COM port, aan de hand van een uniek VID. Ieder apparaat Arduino UNO/Mega/EiBotBoard heeft een ander VID.


import _winreg
import re

def findEiBotBoards():
 hReg = _winreg.ConnectRegistry( None, _winreg.HKEY_LOCAL_MACHINE )
 hKey = _winreg.OpenKey( hReg, r"SYSTEM\CurrentControlSet\Enum\USB\VID_04D8&PID_FD92" )
 nKeys,nVals,nTime = _winreg.QueryInfoKey( hKey )
 for i in range( nKeys ):
 dev = _winreg.EnumKey( hKey, i )
 hKey2 = _winreg.OpenKey( hKey, dev )
 try:
 fname,t = _winreg.QueryValueEx( hKey2, "FriendlyName" )
 match = re.search( r".*\((.*)\)$", fname )
 yield match.group( 1 )
 except GeneratorExit:
 _winreg.CloseKey( hKey )
 _winreg.CloseKey( hReg )
 raise StopIteration
 except:
 pass
 finally:
 _winreg.CloseKey( hKey2 )

# The next two lines may not executed when our caller
 # succeeds in finding an Eggbot device: in that case
 # the caller may do a "break" which then triggers the
 # "except GeneratorExit" clause above.
 _winreg.CloseKey( hKey )
 _winreg.CloseKey( hReg )

def findPorts():
 found = 0
 hReg = _winreg.ConnectRegistry( None, _winreg.HKEY_LOCAL_MACHINE )
 hKey = _winreg.OpenKey( hReg, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports" )
 nKeys,nVals,nTime = _winreg.QueryInfoKey( hKey )
 for i in range( nVals ):
 n,v,t = _winreg.EnumValue( hKey, i )
 if n[0:3] == 'COM':
 found = 1
 try:
 if n[-1] == ':':
 yield n[:-1]
 else:
 yield n
 except GeneratorExit:
 _winreg.CloseKey( hKey )
 _winreg.CloseKey( hReg )
 raise StopIteration

# The next two lines may not executed when our caller
 # succeeds in finding an Eggbot device: in that case
 # the caller may do a "break" which then triggers the
 # "except GeneratorExit" clause above.
 _winreg.CloseKey( hKey )
 _winreg.CloseKey( hReg )

# If we didn't find anything, then produce COM1, COM2, COM3, ..., COM99
 if found == 0:
 for i in range( 1, 100 ):
 yield "COM" + str( i )

if __name__ == '__main__':
 print "Looking for EiBotBoards"
 for port in findEiBotBoards():
 print " ", port

print "Looking for COM ports"
 for port in findPorts():
 print " ", port

De extensie gaat dus opzoek naar het nummer van COM port die de computer heeft toegeschreven aan de EibotBoard toen die voor het eerst verbonden werd met de computer. Een COM port is een virtuele seriële port.

4. Als deze COM port gevonden wordt maakt het programma een verbinding. Vervolgen voert het een aantal controles uit om te bevestigen óf het programma is verbonden met (de juiste) EibotBoard.

 def EggbotOpenSerial( self ):
 if not bDryRun:
 self.serialPort = self.getSerialPort()
 else:
 self.serialPort = open( DRY_RUN_OUTPUT_FILE, 'w' )

if self.serialPort is None:
 inkex.errormsg( gettext.gettext( "Unable to find an Eggbot on any serial port. :(" ) )

def testSerialPort( self, strComPort ):
 '''
 look at COM1 to COM20 and return a SerialPort object
 for the first port with an EBB (eggbot board).

YOU are responsible for closing this serial port!
 '''

try:
 serialPort = serial.Serial( strComPort, timeout=1 ) # 1 second timeout!

serialPort.setRTS() # ??? remove
 serialPort.setDTR() # ??? remove
 serialPort.flushInput()
 serialPort.flushOutput()

time.sleep( 0.1 )

serialPort.write( 'v\r' )
 strVersion = serialPort.readline()

if strVersion and strVersion.startswith( 'EBB' ):
 # do version control here to check the firmware...
 return serialPort
 serialPort.close()
 except serial.SerialException:
 pass
 return None

De EibotBoard is zo geprogrammeerd dat iedere seconde een ‘OK’ tekst wordt gestuurd, hiermee kan de extensie bevestigen dat de EibotBoard aan staat en goed verbonden is. Ook stuurt de programma ter controle de letter ‘v’. Normaliter antwoord de Eggbotboard met een tekst met daarin de versie van firmware die op dat moment draait op het board.

5. De opdrachten worden verstuurd met behulp van voorgeprogrammeerde bibliotheken en stelt eigenlijk weinig voor. Er wordt gewacht totdat de EibotBoard ‘OK’ terugstuurt, waarna de volgend instructie opgestuurd kan worden.


def doCommand( self, cmd ):
 try:
 self.serialPort.write( cmd )
 response = self.serialPort.readline()
 if ( response != 'OK\r\n' ):
 if ( response != '' ):
 inkex.errormsg( 'After command ' + cmd + ',' )
 inkex.errormsg( 'Received bad response from EBB: ' + str( response ) + '.' )
 #inkex.errormsg('BTW:: Node number is ' + str(self.nodeCount) + '.')

else:
 inkex.errormsg( 'EBB Serial Timeout.' )

except:
 pass

De EibotBoard is een mooi stuk technologie maar is erg duur voor haar kunnen. Qua rekenkracht en hardware zou een Arduino met twee stappenmotorboards dezelfde taak kunnen overnemen, helaas is de Firmware van de EibotBoard niet openbaar (en overigens in C geschreven). Aan mij was dus de taak de EibotBoard te ‘reverse-engineeren’ om  een Arduino precies hetzelfde te laten doen. Ik heb alle commandos bestudeerd en ervoor gezorgd dat de Arduino de instructies op dezelfde manier uitvoert en dat de Arduino de extensie gerust stelt door tijdig ‘OK’ te sturen. Ook heb ik de extensie van Python aan moeten passen om ervoor te zorgen dat de extensie geen verbinding met de EiBotBoard wil maken, maar dat de exentie op zoek gaat naar een Arduino UNO

De Inkscape Extensie kantinkscape extension

De extensie geschreven in Python heeft meerdere methoden om de juiste COM port te vinden. De eerste methode zoekt naar ‘oude’ COM porten waar de extensie voorheen verbinding mee heeft gemaakt. De tweede methode gaat op zoek naar de Winreg HKEY_LOCAL_MACHINE lijst kijken. Deze lijst wordt bijgehouden door het windows bestuuringsysteem. In deze lijst zijn alle apparaten die ooit verbonden zijn met de computer gecategorieseerd. Ieder product heeft een eigen uniek nummer. Door in het programma dit nummer aan te passen naar het unieke Arduino UNO nummer zal de extensie alle COM porten krijgen die een Arduino UNO zijn. De derde methode is simpelweg alle COM porten van 0 tot 99 uit te proberen die ooit zijn ‘geactiveerd’. Zodra een methode een COM port heeft gevonden, wordt deze getest. Dit wordt gedaan door verbinding aan te gaan met deze comport. Om te bevestigen of de verbinding succesvol is, verstuurd de extensie ‘v\n\r’. Dit is de letter v met een newline en een carriagereturn (enter). De EibotBoard is voorgeprogrammeerd om dat de versie terug te sturen, de extensie luistert dus naar antwoord. Als de extensie een antwoord terugkrijgt met de sleutelwoorden ‘EBB firmware version’ dan is de test succesvol. Zo niet dan gaat de extensie een andere COM port zoeken aan de hand van één van de drie methoden om vervolgens die weer te testen.

De Arduino kant

Om bepaalde opdrachten uit te voeren aan de hand van een ontvangen serieel bericht, heb ik gebruik gemaakt van een bibliotheek geschreven door Steven Cogswell. Door deze voorgeschreven functies kan je heel gemakkelijk een bepaalde functie koppelen aan het ontvangen van een bepaald signaalwoord. Ik heb uiteindelijk maar enkele  commands uit de lijst geïmiteerd: v, SP en SM en QB. De defintieve versie van de Sketch kan gevonden worden op: https://github.com/gabber1/EggbotSimulator

Serial Command

De bibliotheek van Steven Cogswell werkt als volgt: Men stelt een bepaalde codewoord in, als dit codewoord wordt ontvangen, wordt een bepaalde functie geactiveerd. Ook kan de bibliotheek luisteren naar een codewoord en de letters die dat codewoord volgen opslaan in een variabelen. Zo kan je bijvoorbeeld SM 10 ontvangen en kan je de gevraagde afstand interpreteren.

Een voorbeeld:


// Demo Code for SerialCommand Library
// Steven Cogswell
// May 2011

#include <SerialCommand.h>

#define arduinoLED 13 // Arduino LED on board

SerialCommand sCmd; // The demo SerialCommand object

void setup() {
 pinMode(arduinoLED, OUTPUT); // Configure the onboard LED for output
 digitalWrite(arduinoLED, LOW); // default to LED off

Serial.begin(9600);

// Setup callbacks for SerialCommand commands
 sCmd.addCommand("ON", LED_on); // Turns LED on
 sCmd.addCommand("OFF", LED_off); // Turns LED off
 sCmd.addCommand("HELLO", sayHello); // Echos the string argument back
 sCmd.addCommand("P", processCommand); // Converts two arguments to integers and echos them back
 sCmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")
 Serial.println("Ready");
}

void loop() {
 sCmd.readSerial(); // We don't do much, just process serial commands
}
void LED_on() {
 Serial.println("LED on");
 digitalWrite(arduinoLED, HIGH);
}

void LED_off() {
 Serial.println("LED off");
 digitalWrite(arduinoLED, LOW);
}

void sayHello() {
 char *arg;
 arg = sCmd.next(); // Get the next argument from the SerialCommand object buffer
 if (arg != NULL) { // As long as it existed, take it
 Serial.print("Hello ");
 Serial.println(arg);
 }
 else {
 Serial.println("Hello, whoever you are");
 }
}
void processCommand() {
 int aNumber;
 char *arg;

Serial.println("We're in processCommand");
 arg = sCmd.next();
 if (arg != NULL) {
 aNumber = atoi(arg); // Converts a char string to an integer
 Serial.print("First argument was: ");
 Serial.println(aNumber);
 }
 else {
 Serial.println("No arguments");
 }

arg = sCmd.next();
 if (arg != NULL) {
 aNumber = atol(arg);
 Serial.print("Second argument was: ");
 Serial.println(aNumber);
 }
 else {
 Serial.println("No second argument");
 }
}

// This gets set as the default handler, and gets called when no other command matches.
void unrecognized(const char *command) {
 Serial.println("What?");
}

De basis van mijn programma is dus het volgende:

#include <SerialCommand.h>

SerialCommand sCmd; // The demo SerialCommand object

void setup() {
 Serial.begin(9600);
 sCmd.addCommand("SM", processCommand);
 sCmd.addCommand("SP", processCommand2);
 sCmd.addCommand("v", returnversion);
 sCmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")

 Serial.println("Ready");
}

void loop() {

 sCmd.readSerial(); // We don't do much, just process serial commands

}

void unrecognized(const char *command) {
 Serial.write(0);
}
 void returnversion()
 {
 Serial.println("EBB Version 2.1");
 }

void processCommand() {
 char *arg;

int aNumber;

 arg = sCmd.next();
 if (arg != NULL) {
 aNumber = atoi(arg); // Converts a char string to an integer

 //motor A aNumber stappen opschuiven
 }
 else {
 Serial.write(1);// geen getal ontvangen?
 }

arg = sCmd.next();
 if (arg != NULL) {
 aNumber = atol(arg);

 //motor B aNumber stappen opschuiven
 }
 else {
 Serial.write(2);//maar een getal ontvangen?
 }
 void processCommand2() {
 char *arg;

int iNumber = atoi(arg);
 if (arg != NULL) {
 iNumber = atoi(arg); // Converts a char string to an integer
 if (iNumber == 1)
 {
 //servo op
 }
 else
 {
 //servo neer
 }
 }
}

}

Ik heb de bibliotheek moeten aanpassen op twee aspecten: De opdrachten worden door de extensie met comma’s geschreven en niet met een spatie. De bibliotheek is geschreven om opdrachten die als ‘SM 1 1 1’ binnenkomen en niet voor opdrachten die als ‘SM,1,1,1’ geschreven zijn. De tweede aanpassing die ik aan heb moeten brengen heeft te maken met het einde van een opdracht. Er kan gekozen worden voor een ‘carriage return’ of voor een ‘newline’. De extensie gebruikt ‘toevallig’ precies het andere character om aan te geven dat het einde van de opdracht is aangekomen. Dit moet dus ook aangepast worden anders zal de extensie alle opdrachten als één lange opdracht opvatten, zonder einde.

Zoals eerder vermeld is het erg belangrijk dat beide motoren tegelijkertijd klaar zijn met het uitvoeren van hun aantal stappen. De verhouding in snelheid is cruciaal voor de richting van de lijn die getekend wordt. Aangezien je in AccelStepper alleen de maximale snelheid en de maximale acceleratie kan instellen moet je een creatieve oplossing bedenken.
vt diagrammen

De AccelStepper die de grootste opdracht heeft, zal de maximale acceleratie en maximale topsnelheid krijgen (hangt af van soort stappenmotor), de andere stappenmotor zal een snelheid en acceleratie krijgen die in verhouding staat tot de verhouding in afstand.

</pre>
#include <SerialCommand.h>
 #include <AccelStepper.h>

AccelStepper motorA(4, 2, 4, 3, 5);
AccelStepper motorB(4, 8, 10, 9, 11);
long interval = 1000;
SerialCommand sCmd; // The demo SerialCommand object
boolean turning = false;
long previousMillis = 0;
int stepsPerRevolution = 2048;
float ratio;
void setup() {
 Serial.begin(9600);
 sCmd.addCommand("SM", processCommand); // Converts two arguments to integers and echos them back

 sCmd.addCommand("SP", processCommand2);
 sCmd.addCommand("v", returnversion);
 sCmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")
 motorA.setMaxSpeed(2000.0);
 motorA.setAcceleration(100.0);
 motorB.setMaxSpeed(2000.0);
 motorB.setAcceleration(100.0);
 turning = false;
 Serial.println("OK");
 Serial.write(1);
}

void loop() {
 if(turning == false)
 {
 unsigned long currentMillis = millis();
 sCmd.readSerial(); // We don't do much, just process serial commands
 if(currentMillis - previousMillis > interval) {
 // save the last time you blinked the LED
 previousMillis = currentMillis;
 // Serial.write(244);
 Serial.println("OK");
 }
 }

 while(turning == true)
 {

 motorA.run();
 motorB.run();
 if (motorA.distanceToGo() == 0 && motorB.distanceToGo() == 0 && turning == true)
 {
 // stepper.setDirection(STOP);
 //Serial.println("OK");
 //Serialwrite(244);
Serial.println("OK");
 turning = false;

 }
 }

}

void unrecognized(const char *command) {
 Serial.println("OK");
}
void processCommand() {
 float x;
 char *arg;
arg = sCmd.next();
 // Serial.println("We're in processCommand");
 arg = sCmd.next();
 if (arg != NULL) {
 x = atof(arg); // Converts a char string to an integer
 //Serialprint("x: ");
 //Serialprintln(x);
 // Serial.print("First argument was: ");
 // Serial.println(aNumber*5.688);
 // Serial.println(2048/360);

}
 else {
 //Serialwrite(1);
 }

float y;
 arg = sCmd.next();
 if (arg != NULL) {
 y = atof(arg);
 // Serial.print("Second argument was: ");
 //Serial.println(aNumber*5.688);
 //Serial.println(2048/360);

ratio = x/y;
 //Serial//Serialprint("Y: ");
 //Serialprintln(y);
 //Serialprint("Ratio: ");
 //Serialprintln(ratio);

if (ratio >=1 || y==0 && x>0)
 {
 //x>y
 motorA.setMaxSpeed(2000.0);
 motorA.setAcceleration(100.0);
 motorB.setMaxSpeed(2000.0/ratio);
 motorB.setAcceleration(100.0/ratio);
 }
 if (ratio <1 && ratio >0)
 {
 //y>x
 motorA.setMaxSpeed(2000.0*ratio);
 motorA.setAcceleration(100.0*ratio);
 motorB.setMaxSpeed(2000.0);
 motorB.setAcceleration(100.0);
 }
 if (ratio >=-1 && ratio <0)
 {
 //x>y
 motorA.setMaxSpeed(2000.0*-ratio);
 motorA.setAcceleration(100.0*-ratio);
 motorB.setMaxSpeed(2000.0);
 motorB.setAcceleration(100.0);
 }
 if (ratio< -1)
 {
 //y>x
 motorA.setMaxSpeed(2000.0*-ratio);
 motorA.setAcceleration(100.0*-ratio);
 motorB.setMaxSpeed(2000.0);
 motorB.setAcceleration(100.0);
 }
 if (y==0 && x<0)
 {
 //Serialprintln("Reached this point112");
 motorA.setMaxSpeed(2000.0);
 motorA.setAcceleration(100.0);
 }
 if(y != 0)
 {
 motorB.move(y*5.688);
 }
 if (x!= 0)
 {
 motorA.move(x*5.688);

}
 turning = true;
 }
 else {
 //Serialwrite(2);
 }
 // Serial.println("We're in processCommand");
 arg = sCmd.next();
 if (arg != NULL) {
 x = atoi(arg); // Converts a char string to an integer
 // Serial.print("Third argument was: ");
 turning = true;
 //Serial.println(aNumber);
 }
 else {
 // Serial.println("No third argument");
 }
}
 void returnversion()
 {
 Serial.println("EBB Version 2.1");
 }
 void processCommand2() {
 char *arg;

int iNumber = atoi(arg);
 if (arg != NULL) {
 iNumber = atoi(arg); // Converts a char string to an integer
 if (iNumber == 1)
 {
 //servo op
 }
 else
 {
 //servo neer
 }
 }
}
<pre>

Een groot gedeelte van de code vermijdt problemen met negatieve getallen. 5.688 is de constante die het aantal stappen omzet naar graden. (Hierdoor betekent SM 100 -50) x=100 graden, y = -50 graden. Er zitten in dit programma nog veel ‘debug berichten’, deze zijn gemarkeerd als commentaar, zodat het programma werkt met Inkscape.

Accelstepper werkt met de veronderstelling dat de derde en vierde kabel zijn omgekeerd (sommige stappen motoren hebben deze pinout). Aangezien mijn stappenmotoren dat niet hebben en hier dus niet voor gecompenseerd moet worden, is de volgorde van pins afwijkend.

 

arduino 2 stepper 1 servo_bb

github:https://github.com/gabber1/EggbotSimulator


#include <GSerialCommand.h>
#include <AccelStepper.h>
#include <Servo.h>

AccelStepper motorA(4, 2, 4, 3, 5);
AccelStepper motorB(4, 8, 10, 9, 11);

Servo myservo;

long interval = 1000;
GSerialCommand sCmd; // The demo SerialCommand object
boolean turning = false;
long previousMillis = 0;
int stepsPerRevolution = 2048;
int STEPSPERREVOLUTION = 3200;
double MAXSPEED = 400.0;
double MAXACCELERATION = 400.0;
float ratio;
void setup() {

 Serial.begin(9600);

 sCmd.addCommand("SM", processCommand); // Converts two arguments to integers and echos them back
 sCmd.addCommand("SP", processCommand2);
 sCmd.addCommand("QB", returnposition);
 sCmd.addCommand("v", returnversion);
 sCmd.setDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?")

 myservo.attach(12);

 motorA.setMaxSpeed(MAXSPEED);
 motorA.setAcceleration(MAXACCELERATION);
 motorB.setMaxSpeed(MAXSPEED);
 motorB.setAcceleration(MAXACCELERATION);
 turning = false;

}

void loop() {
 if(turning == false)
 {
 unsigned long currentMillis = millis();
 sCmd.readSerial();
 if(currentMillis - previousMillis > interval)
 {
 previousMillis = currentMillis;
 }
 }
 while(turning == true)
 {

 motorA.run();
 motorB.run();
 if (motorA.distanceToGo() == 0 && motorB.distanceToGo() == 0 && turning == true)
 {

 Serial.println("OK\n\r");
 turning = false;
 }
 }

}

void unrecognized(const char *command)
{
 Serial.println("OK\n\r");
}
void processCommand() {
 float x;
 float d;
 char *arg;
 arg = sCmd.next();
 if (arg != NULL)
 {
 d = atof(arg);
 }

 arg = sCmd.next();
 if (arg != NULL)
 {
 x = atof(arg);
 }

 float y;
 arg = sCmd.next();
 if (arg != NULL)
 {
 y = atof(arg);
 if( x == 0 && y == 0)
 {
 delay(d);
 Serial.println("OK\n\r");
 }
 else
 {
 ratio = x/y;

 if (ratio >=1 || y==0 && x>0)
 {
 //x>y
 motorA.setMaxSpeed(MAXSPEED);
 motorA.setAcceleration(MAXACCELERATION);
 motorB.setMaxSpeed(MAXSPEED/ratio);
 motorB.setAcceleration(MAXACCELERATION/ratio);
 }
 if (ratio <1 && ratio >0)
 {
 //y>x
 motorA.setMaxSpeed(MAXSPEED*ratio);
 motorA.setAcceleration(MAXACCELERATION*ratio);
 motorB.setMaxSpeed(MAXSPEED);
 motorB.setAcceleration(MAXACCELERATION);
 }
 if (ratio >=-1 && ratio <0)
 {
 //x>y
 motorA.setMaxSpeed(MAXSPEED*-ratio);
 motorA.setAcceleration(MAXACCELERATION*-ratio);
 motorB.setMaxSpeed(MAXSPEED);
 motorB.setAcceleration(MAXACCELERATION);
 }
 if (ratio< -1)
 {
 //y>x
 motorA.setMaxSpeed(MAXSPEED*-ratio);
 motorA.setAcceleration(MAXACCELERATION*-ratio);
 motorB.setMaxSpeed(MAXSPEED);
 motorB.setAcceleration(MAXACCELERATION);
 }
 if (y==0 && x<0)
 {

 motorA.setMaxSpeed(MAXSPEED);
 motorA.setAcceleration(MAXACCELERATION);
 }
 if(y != 0)
 {
 motorB.move(y*5.688*360/STEPSPERREVOLUTION);
 }
 if (x!= 0)
 {
 motorA.move(x*5.688*360/STEPSPERREVOLUTION);

 }
 turning = true;
 }
 }
}

void returnposition()
 {
 Serial.println("0");
 Serial.println("OK\n\r");
 }

 void returnversion()
 {
 Serial.println("EBB Version 2.1-");
 Serial.println("OK\n\r");
 }
 void processCommand2() {
 char *arg;
 arg = sCmd.next();
 int iNumber = atoi(arg);

 if (arg != NULL)
 {
 iNumber = atoi(arg);
 if (iNumber == 1)
 {
 myservo.write(0);
 }
 else
 {
 myservo.write(180);
 }
 }
 Serial.println("OK\n\r");
}