ML2016-2

Controlling 4 Servos from a Nano (and next time with JMRI):


In this, and the next article, we are going to hook up 4 servo motors to an Arduino Nano (or Uno, yes, same difference) with a toggle switch to change their position and two LEDs for each to show which way they went. In the next article we will add an SPI CAN controller to this and another Nano to get messages from JMRI to control the same servos, just like the new LCC (or maybe close) does it.


Imagine seeing this as one of your electrical AP requirements. Yes, you get all 4 indicated turnouts done here, just don’t copy the schematics, draw them on a napkin and submit.


       

Arduino Nano, solder pins to the bottom side


First the hardware, an Arduino Nano with a mini USB connector and programmer built in, a Funduino “Prototype Shield I/O Module Extension Board”, just search Ebay for this $3 part, the Nano is $2.19 with free shipping at this time (search for “Arduino Nano USB”), and I have found that the parts coming from Hong Kong always shows up quicker than China...maybe Murphy is just in the correct spot for that to occur every time.

Nano Prototype Shield


And of course we need to create a suitable 5V power supply that will be able to power the four servo motors, since, as mentioned before, the servos will draw more power than your USB cable can provide. The big connector on the Shield only goes to the Nano’s Vin pin, and it is also not a good idea to rely on the Nano’s regulator to power the motors. Good news, for a mere $0.48, a low dropout 5V regulator from Digikey, the MC7805CT, will fit easily into the holes on the side of the Shield labeled Vin (12V), GND and 5V above. 1.5 Amps should be plenty.


5V regulator with pins in order: Input, GND and Output


So two things to do, bend the 5V pin on the Nano to the inside, so it does not connect to the Shield at all and then solder the MC7805 into the Shield as shown below:


Bend Nano 5V pin and MC7805CT soldered into Shield


Now, without the Nano, a 7 to 12 Vdc power supply plugged into the 2.1 mm power socket, should make the shield POWER LED turn on bright green. And when you insert the Nano, it’s own red POW LED should turn on too.

1.5 Amp enabled Shield and Nano


While you are ordering and spending money, get the SPI CAN controllers as well too. Search for “MCP2515 CAN SPI Module Arduino”, about $2.16 today. You’ll need 2 to get started, since your computer node needs one too. For every future module you add, you would only need one more.


SPI CAN Transceiver, for next article


Always modify (change the on or off time) and load the Blink example onto your Arduino by itself to make sure your USB cable and software are in working order.  Then you plug the Nano in on top of the Shield and power it from the 5V supply, witnessing that your LED on pin 13 is still blinking.


NanoML2016-2.png

Servos will connect to the yellow circles (pins D3,D5,D6,D9), inputs to the Cyan circles (pins D2, D4, D7, D8) and LEDs to the Green/Red circles (pins A0-A3) [Future SPI to blue circles].


Now you will connect the 4 servos to four of the pins that are PWM capable. The numbers in purple on the outside edges of the figure above are the Arduino pin numbers we like to use (and not the Atmel Atmega328P chip’s grey pin numbers, closer to the middle). Keep in mind that you could attach more servos if you like, but would need to use software implementations to create the 1 ms to 2 ms pulses required. So, in order to reserve pins (D10, D11, D12 and D13) for the SPI CAN bus device in our next article, we will use pins D3, D5, D6 and D9 for our 4 servos here.


Notice how on the Shield, the black, red and blue connectors are already set up for plugging in a servo directly?  On the servo, the darkest wire in the cable (usually black or brown) goes to the black pin (call it ground or common), the middle pin should be 5V and red on the cable too and then the control wire, white, yellow or orange, goes to the blue pin on the board.


4 Servos connected to Nano and Shield


In a hurry to test them? Sure...


// ML2016-2a.ino, test 4 servos on D3, D5, D6 and D9


#include <Servo.h>


#define SERVOPIN01   3

#define SERVOPIN02   5

#define SERVOPIN03   6

#define SERVOPIN04   9


#define SERVORIGHT  95

#define SERVOLEFT  105


Servo myServo1;                   // create servo object 1

Servo myServo2;                   // create servo object 2

Servo myServo3;                   // etc.

Servo myServo4;


void setup() {

 myServo1.attach( SERVOPIN01 );  // attach servo object 1 to pin 3

 myServo2.attach( SERVOPIN02 );  // attach servo object 2 to pin 3

 myServo3.attach( SERVOPIN03 );  // etc.

 myServo4.attach( SERVOPIN04 );

} // setup


void loop() {

 myServo1.write( SERVORIGHT );   // move servo object 1 somewhere

 delay( 100 );                   // delay a little bit

 myServo1.write( SERVOLEFT );    // move servo object 1 back

 delay( 100 );

 myServo2.write( SERVORIGHT );   // move servo object 2 somewhere

 delay( 100 );

 myServo2.write( SERVOLEFT );    // move servo object 2 back

 delay( 100 );


 // … and repeat with the myServo3 and 4 too.

} // loop

So now that we have 4 servo motors moving each at a time, we are going to do a bit of big software magic and use OOP (Object Orientated Programming) to slow the servo’s down and decide by using time, which motor has to move when.  Don’t worry, if you don’t understand OOP (yet), you already used it with Servo myServo3; and myServo1.write( SERVOLEFT );.  No one showed you the code for the class Servo or the write() method in it, and you never need to see it either.  Isn’t that just awesome?  First the code and then the LED and switch hookup.

/* Rrrduino Servo controller

*  

*  Control 4 servo motors with 4 inputs and 8 LEDs showing which way the servo

*  is  commanded

*  

*  We are avoiding the SPI pins, 10-13 (https://www.arduino.cc/en/reference/SPI)

*  to allow for a CAN controller in a simple LCC example soon

*/


// SPI not used yet, but used as a placeholder to remind us

// pin 10,11,12 and 13 has another purpose soon

#include <SPI.h>

// need servo library, correct?

#include <Servo.h>


#define SERVO1PIN 3

#define SERVO2PIN 5

#define SERVO3PIN 6

#define SERVO4PIN 9


#define SERVO1INPIN 2

#define SERVO2INPIN 4

#define SERVO3INPIN 7

#define SERVO4INPIN 8


// could also use 14,15,16 and 17 here:

#define SERVO1LEDPIN A0

#define SERVO2LEDPIN A1

#define SERVO3LEDPIN A2

#define SERVO4LEDPIN A3


// set the positions for the servos to go to, easy to change to move each one to a

// different position on each end, could also make it adjustable and save the values

// in EEPROM (will do that in the article after next.

#define SERVOLEFT    75

#define SERVORIGHT  105

#define SERVOCENTER  90

#define UPDATETIME  100


unsigned long now; // global now for use in timing


// a little bit of OOP (Object Orientated Programming):

// creating a class to control a Servo at a slower speed

// we set a commanded position, depending on the state of an input pin

// and then at every update interval, we move the position one step closer to

// the commanded position. want to move faster or slower, decrease or increase the

// update delay, or increase/decrease the distance you travel at every update.

// default is 1 step per update.

class SlowerServo : public Servo { // Inherit Servo and add to it...

 public:   

   SlowerServo( unsigned long deltaTime ) {

     l_deltaTime = deltaTime;     // set the delay time between each step

     i_pos = SERVORIGHT;          // set the initial position

     i_commanded_pos = i_pos;     // and commanded is set to current pos, but the

                                  // motor will only move once “init()” is called

                                  // and then after that with every “update()” call.

   } // constructor


   // this changes position from one to the other, true for LEFT and false for RIGHT

   void setPosLeft( bool b_left ) {

     if ( b_left == true ) {

       i_commanded_pos = SERVOLEFT;

       digitalWrite( i_led_pin, HIGH );            // set the LED accordingly

     } else {

       i_commanded_pos = SERVORIGHT;

       digitalWrite( i_led_pin, LOW );             // set the LED accordingly

     } // if true

   } // setPosLeft


   // check the input pin and change the output accordingly

   bool updateInput() {

     if ( digitalRead( i_in_pin ) == true ) // if the pin is high

       setPosLeft( true );                  // go left, the default with internal pullup

     else

       setPosLeft( false );                 // else go right     

   } // updateInput


   // assign the pins for the servo, input and LED output

   void init( int servo_pin, int input_pin, int led_pin, unsigned long mils ) {

     i_led_pin = led_pin;                    // remember which pin

     pinMode( i_led_pin, OUTPUT );           // LED pin as an output

     

     i_in_pin = input_pin;                   // remember which pin

     pinMode( i_in_pin, INPUT_PULLUP );      // use the internal PULLUP to

                                             // avoid an external resistor on the

                                             // the switch

     updateInput();                          // test and set LED since we are up


     l_before = mils;                        // save the time

     attach( servo_pin );                    // make this a servo on this pin                   

   } // init

   

   // update gets called as often as possible. we check the current time and compute if it

   // is time to do something, like moving to the next step.

   int update() {

     if ( now - l_before >= l_deltaTime ) {

       l_before = l_before + l_deltaTime;  // update before to be one timestep closer to now

       if ( i_pos == i_commanded_pos ) {

                                           // do nothing if we are there

       } else {

         if ( i_pos < i_commanded_pos )    // are we there yet?

           i_pos = i_pos + 1;              // increase position by 1

         else                              // go the other way

           i_pos = i_pos - 1;              // increase position by 1          

       } // if else

       write( i_pos );                     // make the servo move!

     } // if time

   } // update


 private:

   unsigned long l_deltaTime;              // how many milliseconds before we move again

   unsigned long l_before;                 // the last time we tried to move

   unsigned  int i_pos;                    // the position we are at

   unsigned  int i_commanded_pos;          // the position we need to go to

   unsigned  int i_in_pin;                 // which pin is our input pin

   unsigned  int i_led_pin;                // which pin is our LED output pin

}; // class SlowerServo


// We create the object with only the delay time it needs to say how often to move,

// setting the pins are done with an “init()” call to avoid any hardware pin setups

// in the constructor.

SlowerServo myServo1( UPDATETIME/2 );    // this one will move faster

SlowerServo myServo2( 2*UPDATETIME );    // this one will move slower

SlowerServo myServo3( UPDATETIME );      // this one will move too

SlowerServo myServo4( 2*UPDATETIME );    // this one will move slower



void setup() {

 Serial.begin( 115200 );

 Serial.println( "Starting ML2016-2 version 1.0 ..." );

       // how do you know what software

       // and version is loaded on your Nano?

       // Broadcast it on startup! Ctrl-Shift-M

       // in IDE will bring up Serial Monitor

 

 // all hardware related work does not go smooth in the class’s constructor,

 // so, it is all moved to the “init()” method

 myServo1.init( SERVO1PIN, SERVO1INPIN, SERVO1LEDPIN, millis() );

 myServo2.init( SERVO2PIN, SERVO2INPIN, SERVO2LEDPIN, millis() );

 myServo3.init( SERVO3PIN, SERVO3INPIN, SERVO3LEDPIN, millis() );

 myServo4.init( SERVO4PIN, SERVO4INPIN, SERVO4LEDPIN, millis() );

 delay( 500 );          // some little delay, just because.

} // setup


void loop() {

 // get the milliseconds since startup:

 now = millis();


 // check every servo’s input:

 myServo1.updateInput();

 myServo2.updateInput();

 myServo3.updateInput();

 myServo4.updateInput();  

 

 // then check each servo if it needs to update:

 myServo1.update();

 myServo2.update();

 myServo3.update();

 myServo4.update();

} // loop


/* Sketch uses 4,536 bytes (14%) of program storage space. Maximum is 30,720 bytes.

Global variables use 354 bytes (17%) of dynamic memory, leaving 1,694 bytes for local variables. Maximum is 2,048 bytes. */


So, copy the code above in a new file in the IDE (the Arduino software on the computer), save it as something you’ll remember. Watch out for the comments at the end of the lines, when they wrap around to the next line without a // you have now added “code” that is not “code”. With the Nano connected to the computer’s USB port, select the  Board (Nano) and Port from the Tools menu. And then click the Upload button (Ctrl-U) to compile and upload the code.


Switches hooked between input pins and GND, LEDs (each with own resistor) hooked between output pins and red to GND and green to 5 Vreg.


So, code is on the Nano, and the switches connected to the input pins and GND. The input pins are using the internal PULLUP resistors in the Atmega to have the pin pulled up to the internal 5V when the switch is not connected to GND. Each red LED has its 1 kOhm resistor between an output pin and GND, so it is on when the pin is HIGH. Each green LED has its 1 kOhm between an output pin and the regulated 5V and will turn on when the pin goes low, the opposite of the red LED. When the output pin is in a high impedance mode, like an input, both LEDs would be on, but dimmer than normal, since the current needs to flow through 2 resistors and would have two LED voltage drops. So a short dimmed state while your Nano is turning on getting ready.


So, toggle a switch and the servo starts moving the other direction. With little trouble you could have the LEDs flashing while it is moving, or make the pin an input and have both on.


Final product, 4 servos and LEDs controlled by 4 switches.


What could this be used for? Throw 4 turnouts on a layout, control 4 directional turnouts under your new car system, or automated some fair ground scenes with moving things. And imagine what the possibilities could be, when in the next article, we connect all this to JMRI???


ALL DONE!



MC7805CT datasheet: http://www.mccsemi.com/up_pdf/MC7805CT(TO-220).pdf


And of course all possible with servos starting at $2.34: http://www.hobbyking.com/hobbyking/store/__9549__Turnigy_8482_TG9e_Eco_Micro_Servo_1_5kg_0_10sec_9g.html


Funduino "Servo Connector Board"


Comments