IntelliServo is a project aiming to transform regular hobby servos into intelligent ones by replacing their original boards. By doing so the servo has upgraded capabilities, such as being able to read its position, temperature and current consumption. Multiple servos can now be daisy-chained and controlled over I2C from a microcontroller, or over USB from a computer. https://github.com/bqlabs/IntelliServo

Dependencies:   IAP USBDevice mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers IntelliServoMain.cpp Source File

IntelliServoMain.cpp

00001 //--------------------------------------------------------------
00002 //--    IntelliServo
00003 //--    I2C servo with temperature and current sensors
00004 //--------------------------------------------------------------
00005 //--    BQ
00006 //--------------------------------------------------------------
00007 //--    Firmware created by 
00008 //--        Alvaro Ferran Cifuentes (alvaroferran)
00009 //--------------------------------------------------------------
00010 //--    Released on March 2016
00011 //--    under the GPL v3
00012 //--------------------------------------------------------------
00013 
00014 //Select servo
00015 //#define FUTABA_S3003
00016 #define TURNIGY_1268HV
00017 
00018 
00019 
00020 #include "mbed.h"
00021 #include "USBSerial.h"
00022 #include "IAP.h"
00023 #include "IntelliServoConfig.h"
00024 #define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
00025 
00026 //EEPROM values
00027 #define     TARGET_ADDRESS  64 //First non-reserved address in EEPROM
00028 #define     BYTE_SIZE       1
00029 
00030 //USBSerial pc;
00031 IAP     iap;
00032 
00033 I2CSlave slave(P0_5, P0_4); //SDA, SCL
00034 
00035 PwmOut mot1(P0_8);
00036 PwmOut mot2(P0_9);
00037 AnalogIn   pot(P0_11);
00038 AnalogIn   current(P0_13);
00039 AnalogIn   temperature(P0_14); 
00040 
00041 void setAngle(int16_t);
00042 int16_t getAngle();
00043 int16_t getCurrent();
00044 int16_t getTemp();
00045 void changeAddress(uint8_t);
00046 void setEepromByte(int, uint8_t);
00047 uint8_t getEepromByte(int);
00048 int eepromAddres=1; //After TARGET_ADDRESS
00049 
00050 
00051 
00052 /********MAIN***************************************************************/
00053 
00054 int main() {
00055     
00056     char inI2C[10];
00057     char outI2C[2];
00058     int8_t slaveRegister;
00059     int16_t angleIn, angleOut, currentOut, tempOut;
00060     bool angleSet=false;
00061     int8_t disable=1;
00062     
00063     mot2.period_us(33.33f);  // 30kHz 
00064     mot1.period_us(33.33f);  
00065     mot1.write(0.00f);
00066     mot2.write(0.00f);
00067 
00068     //Uncomment to reset default i2c address
00069     //setEepromByte(PLACE,(uint8_t) 0x01);
00070 
00071     uint8_t inValue, i2cAddress=0x02;
00072     inValue=getEepromByte(eepromAddres);
00073     if(inValue!= (uint8_t) 0 ) i2cAddress=inValue; 
00074     slave.address(i2cAddress<<1);    
00075 
00076 
00077     while(1){
00078              
00079         //setAngle(90);
00080       
00081              
00082         int i = slave.receive();
00083         switch (i) {           
00084             
00085             case I2CSlave::WriteAddressed:      //Receive
00086                 
00087                 slave.read(inI2C, 10);
00088                 slaveRegister=inI2C[0];
00089 
00090                 switch(slaveRegister){
00091                     case 0x0A:  //Change address
00092                         changeAddress(inI2C[1]);
00093                     break;
00094                     
00095                     case 0x01:  //Disable motor
00096                         disable=inI2C[1];
00097                     break;
00098                     
00099                     case 0x02:  //Set angle
00100                         angleIn=(int16_t)  (inI2C[1] << 8) | inI2C[2];
00101                         disable=0; 
00102                         angleSet=true;
00103                     break;
00104                 }
00105                 
00106                 
00107             break;
00108             
00109             
00110             case I2CSlave::ReadAddressed:       //Reply
00111                                
00112                 switch(slaveRegister){
00113                     case 0x04:  //Get angle
00114                         outI2C[0]= (angleOut >> 8) & 0xFF;
00115                         outI2C[1]= angleOut & 0xFF;
00116                     break;
00117                     
00118                     case 0x06:  //Get current
00119                         outI2C[0]= (currentOut >> 8) & 0xFF;
00120                         outI2C[1]= currentOut & 0xFF;
00121                     break;
00122                     
00123                     case 0x08:  //Get temperature
00124                         outI2C[0]= (tempOut >> 8) & 0xFF;
00125                         outI2C[1]= tempOut & 0xFF;
00126                     break;
00127                 }
00128                 
00129                 slave.write(outI2C, 2); 
00130                 
00131             break;
00132             
00133         }
00134         for(int i = 0; i < 10; i++) inI2C[i] = 0;  
00135         
00136        
00137         
00138        //Keep setting the angle so the servo stays energized
00139        if(angleSet==true){ //Make sure servo only starts moving when a value has been sent
00140             if(disable==0)
00141                 setAngle(angleIn);
00142             else {
00143                 mot1.write(0.00f);
00144                 mot2.write(0.00f);
00145             }
00146        }
00147         
00148        angleOut=getAngle();
00149        currentOut=getCurrent();
00150        tempOut=getTemp();
00151     
00152       
00153     }
00154     
00155 }
00156 
00157 
00158 
00159 
00160 
00161 
00162 /********SET ANGLE**********************************************************/
00163 //PID setting based on Angel Espeso's example (http://roble.uno/control-pid-barra-y-bola-arduino/)
00164 
00165 void setAngle(int16_t desiredAngle){
00166 
00167     desiredAngle=constrain(desiredAngle,-1,maxAngle);  //Limit legal angles
00168     
00169     int16_t currentAngle=0, lastAngle, error=0, lastError;
00170     int eCount=0;
00171     float I=0;
00172     while(1){
00173         
00174         wait_ms(1);
00175         
00176         //Read current angle 
00177         lastAngle=currentAngle;
00178         currentAngle=getAngle();
00179         
00180         //Error
00181         lastError=error;
00182         error=desiredAngle-currentAngle;
00183         
00184         //Calculate average speed
00185         float v[5];   //Speed vector
00186         for (int i=0; i<5; i++) // Move all speed values one space to the left to make space for the newest one
00187             v[i] =v [i+1];
00188         v[4] = (error-lastError); // Add last speed
00189         float vel=0;
00190         for (int i=0; i<5; i++)     // Average speed
00191             vel += v[i];
00192         vel /= 5;
00193         vel/=100;
00194                 
00195         //I 
00196         if(abs(error)<10 && abs(error)>0.2)
00197             I+=error*Ki;
00198         else 
00199             I=0;
00200         
00201         //Voltage calculation
00202         float pwr=Kp*error+Kd*vel+I;
00203         
00204         //Motor control
00205         float speed=0;//0.06; 
00206         if(pwr>0){
00207             mot1.write(1-(abs(pwr)+speed)); 
00208             mot2.write(1); 
00209         }else {
00210             mot2.write(1-(abs(pwr)+speed)); 
00211             mot1.write(1);
00212         }
00213         
00214         //pc.printf("Pot Angle: %d   Pwr: %.2f  Current: %.2f\n" ,currentAngle ,abs(pwr)+speed ,current.read()*100/0.055);
00215     
00216         //Angle stable
00217         if(abs(currentAngle-lastAngle)<=1){     //Difference in angle instead of error to avoid blocking when angle is not recheable
00218             eCount++;
00219             if(eCount>5) 
00220                 break;
00221         }
00222         
00223         
00224     }
00225 }
00226 
00227 
00228 
00229 /********GET ANGLE**********************************************************/
00230 
00231 int16_t getAngle(){
00232     float aF=0;
00233     int16_t aI=0;
00234     for(int i=0; i<5; i++)
00235          aF+=(pot.read()*100-5)*2.439; 
00236     aI=(int16_t)constrain(aF/5,0,maxAngle);
00237     return aI;
00238 }
00239 
00240 
00241 
00242 /********GET CURRENT*********************************************************/
00243 
00244 int16_t getCurrent(){
00245     float cF=0;
00246     int16_t cI=0;
00247     for(int i=0; i<5; i++)
00248          cF+=current.read()*100/0.055; 
00249     cI=(int16_t)cF/5;
00250     return cI;
00251 }
00252 
00253 
00254 
00255 
00256 
00257 
00258 /********GET TEMPERATURE****************************************************/
00259 
00260 int16_t getTemp(){ 
00261     return (int16_t) (temperature.read()*3300-500)/10;
00262 }
00263 
00264 
00265 
00266 
00267 /********CHANGE ADDRESS*****************************************************/
00268 
00269 void changeAddress(uint8_t address){
00270     setEepromByte(eepromAddres, address);
00271     slave.address(address<<1);  
00272 }
00273 
00274 
00275 /********EEPROM R&W*********************************************************/
00276 //EEPROM acces code based on Dave Tech's example (https://developer.mbed.org/users/kstech/code/EepromTest/)
00277 
00278 typedef union data {
00279     uint8_t b;
00280     char  s[1];
00281 } myData; 
00282 
00283   
00284 void setEepromByte(int place, uint8_t incomingByte){
00285     myData setByte;
00286     char someBytes[1];
00287     setByte.b = incomingByte;
00288     for(int i=0; i<sizeof(someBytes); i++)
00289         someBytes[i]=setByte.s[i];
00290     iap.write_eeprom( someBytes, (char*) (TARGET_ADDRESS+place), BYTE_SIZE );
00291 } 
00292  
00293 uint8_t getEepromByte(int place){
00294     myData getByte;
00295     char someBytes[1];
00296     iap.read_eeprom( (char*)(TARGET_ADDRESS+place), someBytes, BYTE_SIZE );
00297     for(int i=0; i<sizeof(someBytes); i++)
00298         getByte.s[i]=someBytes[i];
00299     return getByte.b;
00300 }