c - Arduino Drone project, when output is "loaded" (even cap to gnd), input "steering" command starts to glitch -
arduino drone project, whenever output "loaded" ("open-circuit" signal-in pin esc or cap ground), input "steering" command starts glitch (go low values << 1000).
the motor speed function of both steering command, throttle. (in test-case 1 motor seen in code below, unmotorspeed = unthrottlein +/- unsteeringin)
when hooked scope, physical input signals (steering , throttle coming receiver) great, , swapped input pins make sure there wasn't problem between receiver , arduino. problem seems coming software, doesn't make sense, since when there isn't "load" attached, input , output values fine , clean. (i put "load" in quotes because it's open circuit --> super high impedance input signal electronic speed controller (esc), don't ground complete circuit).
would able check out code , see if i'm missing something?
at point, quick workaround not write new glitchy values motor , keep old speed values whenever new speed lower old speed (and these @ on 50khz, huge jump in 1 step bit crazy).
note: in code, total output of unmotorspeed leaves servothrottle pin. original naming didn't end changing... it's clear if read code , see variables.
update: weird thing is, ran setup without changes , worked...i reflashed arduino multiple times make sure wasn't lucky glitch, , kept on working, set up. dropped remote on ground , moved few of wires on breadboard around, , things went wonky ways after putting setup together. idk do!
// --> starting code found at: rcarduino.blogspot.com // see related posts - // http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html with.html #include <servo.h> // assign channel in pins #define throttle_in_pin 3 #define steering_in_pin 2 // assign channel out pins #define throttle_out_pin 9 //#define steering_out_pin 9 // servo objects generate signals expected electronic speed controllers , servos // use objects output signals read in // example code provides straight pass through of signal no custom processing servo servothrottle; //servo servosteering; // these bit flags set in bupdateflagsshared indicate // channels have new signals #define throttle_flag 1 #define steering_flag 2 // holds update flags defined above volatile uint8_t bupdateflagsshared; // shared variables updated isr , read loop. // in loop immediatley take local copies isr can keep ownership of // shared ones. access these in loop // first turn interrupts off nointerrupts // take copy use in loop , turn interrupts on // possible, ensures able receive new signals volatile uint16_t unthrottleinshared; volatile uint16_t unsteeringinshared; // these used record rising edge of pulse in calcinput functions // not need volatile used in isr. if wanted // refer these in loop , isr need declared volatile uint32_t ulthrottlestart; uint32_t ulsteeringstart; //uint32_t ulauxstart; void setup() { serial.begin(9600); // attach servo objects, these generate correct // pulses driving electronic speed controllers, servos or other devices // designed interface directly rc receivers servothrottle.attach(throttle_out_pin); // using pinchangeint library, attach interrupts // used read channels attachinterrupt(digitalpintointerrupt(throttle_in_pin), calcthrottle,change); attachinterrupt(digitalpintointerrupt(steering_in_pin), calcsteering,change); } void loop() { // create local variables hold local copies of channel inputs // these declared static thier values retained // between calls loop. static uint16_t unthrottlein; static uint16_t unsteeringin; static uint16_t difference; static uint16_t unmotorspeed; // variable stores overall motor speed static uint8_t bupdateflags; // local copy of update flags // check shared update flags see if channels have new signal if(bupdateflagsshared) { nointerrupts(); // turn interrupts off while take local copies of shared variables // take local copy of channels updated in case need use in rest of loop bupdateflags = bupdateflagsshared; // in current code, shared values populated // copy them without testing flags // in future change, lets // copy when flags tell can. if(bupdateflags & throttle_flag) { unthrottlein = unthrottleinshared; } if(bupdateflags & steering_flag) { unsteeringin = unsteeringinshared; } // clear shared copy of updated flags have taken updates // still have local copy if need use in bupdateflags bupdateflagsshared = 0; interrupts(); // have local copies of inputs, can turn interrupts on // interrupts on, can no longer use shared copies, interrupt // service routines own these , update them @ time. during update, // shared copies may contain junk. luckily have our local copies work :-) } //serial.println(unsteeringin); // processing here onwards // use local values unauxin, unthrottlein , unsteeringin, shared // variables unauxinshared, unthrottleinshared, unsteeringinshared owned // interrupt routines , should not used in loop // following code provides simple pass through // initial test, arduino pass through // receiver input if arduino not there. // should used confirm circuit , power // before attempting custom processing in project. // checking see if channel value has changed, indicated // flags. simple pass through don't need check, // more complex project new signal requires significant processing // allows calculate new values when have new inputs, rather // on every cycle. ///// if-else chain commented out determine/prove problem steering signal --> buggy! if(unsteeringin < 1400) // if steering joystick moved left { difference = 1400 - unsteeringin; if(unthrottlein - difference >= 0) unmotorspeed = unthrottlein - difference; } else if(unsteeringin > 1550) //if steering joystick moved right (needs tweaked, works now) { difference = unsteeringin - 1600; if(unthrottlein + difference < 2000) unmotorspeed = unthrottlein + difference; } else { unmotorspeed = unthrottlein; } //serial.println(unmotorspeed); //serial.println(unsteeringin); //serial.println(unthrottlein); if(bupdateflags) { //serial.println(servothrottle.readmicroseconds()); if(servothrottle.readmicroseconds() != unmotorspeed) { servothrottle.writemicroseconds(unmotorspeed); serial.println(unmotorspeed); } } bupdateflags = 0; } // simple interrupt service routine void calcthrottle() { // if pin high, rising edge of signal pulse, lets record value if(digitalread(throttle_in_pin) == high) { ulthrottlestart = micros(); } else { // else must falling edge, lets time , subtract time of rising edge // gives use time between rising , falling edges i.e. pulse duration. unthrottleinshared = (uint16_t)(micros() - ulthrottlestart); // pulse duration // use set throttle flag indicate new throttle signal has been received bupdateflagsshared |= throttle_flag; } } void calcsteering() { if(digitalread(steering_in_pin) == high) { ulsteeringstart = micros(); } else { unsteeringinshared = (uint16_t)(micros() - ulsteeringstart); // pulse duration bupdateflagsshared |= steering_flag; } }
you should read documentation of attachinterrupt() - in section "about interrupt service routines" gives information on how functions behave when called interrupt. micros()
states:
micros() works initially, start behaving erratically after 1-2 ms.
i believe means after isr has been running more 1ms, rather 1 ms in general, may not apply in case, might need consider how doing timing in isr. that's problem arduino - terrible documentation!
one definite problem may cause fact unsteeringinshared
non-atomic. 16 bit value on 8 bit hardware requires multiple instructions read , write , process can interrupted. therefore possible read 1 byte of value in loop()
context , have both bytes changed interrupt context before read second byte, 2 halves of 2 different values.
to resolve problem either disable interrupts while reading:
nointerrupts() ; unsteeringin = unsteeringinshared ; interrupts() ;
or can spin-lock read:
do { unsteeringin = unsteeringinshared ; } while( unsteeringin != unsteeringinshared ) ;
you should same unthrottleinshared
too, although why not see problem unclear - perhaps not problem observing, problem in case.
alternatively if 8 bit resolution sufficient encode input atomic 8 bit value thus:
uint8_t unsteeringinshared ; ... int32_t timeus = micros() - ulsteeringstart - 1000 ; if( timeus < 0 ) { unsteeringinshared = 0 ; } else if( timeus > 1000 ) { unsteeringinshared = 255; } else { unsteeringinshared = (uint8_t)(time * 255 / 1000) ; }
of course changing scale 1000 2000 0 255 need changes rest of code. example convert value x
in range 0 255 a servo pulse width:
pulsew = (x * 1000 / 256) + 1000 ;