Trains.com

Subscriber & Member Login

Login, or register today to interact in our online community, comment on articles, receive our newsletter, manage your account online and more!

Status of my servo controller project

4556 views
31 replies
1 rating 2 rating 3 rating 4 rating 5 rating
  • Member since
    July 2009
  • From: lavale, md
  • 4,678 posts
Posted by gregc on Sunday, September 23, 2018 5:52 AM

randy

PM me if your interested in discussing this.   i'd really like to see you post code that is more readable and reusable by others.

BTW, object oriented coding can be done in many languages, including assembly.  C is no exception.

greg

greg - Philadelphia & Reading / Reading

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Saturday, September 22, 2018 9:57 PM

 BTW, i DO understand the concept of creating an array of STRUCT for the various variables, then each Metroo call would be like chkbutton(1) or chkbutton(2), or moveservo(1), moveservo(2), etc. and yes, if I expanded to 12 servers, I would just have to change my array declaration to 12 and bingo, now it runs 12 (well, there's have to be some setting of the pin definitions in there - for the inputs and the outputs). Is that more proper by OOP standards? Probably. Is it easier for someone who doesn't know these things to figure out? Probably not. Famous last workds "It will never grow beyond this" but - it won't.  The smaller chips are cheaper, because they are used in dime a dozen Arduinos, and the bigger ones with more I/O either aren't fully capable in a PDIP package, or just plain aren't available. So in this case it's true, i will never design one that could use the same code but drive 6 servos, or something like that. At this point, I am almost willing to say I will NEVER use SMD components (except as resistors for resistor wheelsets - and possibly LEDs for loco lighting) because I simply can not see well enough to work with the tiny stuff, magnifiers or not. Wider pitch things, I probably could drag colder. But the finer stuff? I'm not going to invest in a board assembly microsofpe, or a reflow oven. ANd BGA parts - forget about it, that is NOT a DIY thing. Those "Fix your video card" YouTube videos - yeah, they are kidding themselves if they thing they are truly reflowing all the balls under that GPU. Plus - I'd rather not run long runs of servo cable everywhere, and 2 is a nice number because it works for a crossover. I went through this over and over - the idea that if I eliminated the remote control (which isn't needed for a ayrd) I could add a third servo - but I fall a couple of pins short, even with the single relay method. So si settled on a standard board that will drive 2 servos, asosciated LEDs and switches, and have a remote control option. In fact I MIGHT use the remote input to drive the yard ones using an input matrix for route controol, instead of 2 buttons for every turnout. But I figured if I designed all kinds of different boards for every special conditioon on the layout, I'd spend all my time designing hardware and not building a layout. So - one standard board, the same everywhere, no mucking with special conditions everywhere.

                                      --Randy


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Saturday, September 22, 2018 8:58 PM

 It's just that one specific thing I don't understand where you are headed. First you say it will elminate the need for an "is it moving" flag but then it does. 

 Then you suggest changing where I make the Metro function calls. They HAVE to be in the loop, and reducing the whole thing to just one metro block would defeat the purpose of allowing servo 2 to be triggered while servo 1 is in motion. 

 I can theoretically remove one test and assume the direction is always correct, and just test if the servo is at min or at max instead of having that test then follow it up with a test for which way it was going, which is what tells em what lights to turn on and what relays to set. That may be a safe assumption to make, since if it gets to the min value, it MUST have been going to the Normal positon, and if if it gets to the max, it must have been going to the Reverse position. By combining that logic, I can eliminate one if block. But I can't elminate the direction flag, unless I break the move routines into two complete sets, one for each direction, and call the MoveToNormal if the normal button is pressed and MoveTo Reverse is the reverse button is pressed. Then perhaps I can eliminate the multiplication in the math and the normal side would just subtract the step and the reverse side would jsut add the step. OK, but that's still not a whole lot simpler, as the servo move stuff needs to be within its own Metro block to allow asynchronous operation of the program.

 The strict requirement for object dependency is exactly why my control program will NOT be written in vb.net. It's next to impossible to do what is EXTREMELY simple (and completely understandable) with pre .Net versions - and the example used in many VB CMRI programs. That being that an array of controls on a form is exactly that, an array - and can be referenced by an array subscript as well as the name. To use the enforced pure OOP in VB.Net - yes, I was able to make a sample CTC panel but it required having code in each object. OK, easy enough to do I suppose by making a custom class and then using that throughout the form. But the 'old' not strictly OOP method of actually allowing the encapsulating object to have references to the encapsulated objects is FAR easier to understand. It's not ALWAYS better, these new ways. I'd still rather write a program that needed to read data in Foxpro as opposed to VB.Net simple because I don't need that absolutely unecessary intermediate XML layer. But that ship has sailed and now we are stuck.

 I'm not saying my code is perfect by any means. I'm sure as I tweak it I will find other things to improve - I am pretty sure across the whole thing there are redundancies I can remove - but I also am taking the safe approach and initializing every variable even if the first thing that happes is it gets set to some stored value. Rember that these are completely independent programs, this program will in no way interact with servo controller #2 controlling the turnouts at the next crossover down the track. This is a single purpose device - move the srvos. Relays optional - heck I may remove them entirely. Certainly the "power' relay, because I will never use any of those old Shonohara turnouts that might need that feature. But - there is nothing more modular needed here - if I am more careful with my breadboard space utilization I can add a second one of these, load the same code into another Nano, and have 4 working servos. But on an individual basis - I have no intent of making a more massive version that drives a half dozen servos. There just aren;t enough pins. A multi-node system is just that - multiple nodes of the same thing, there is no conflict with variables or anything of the sort, they are connected only via the I/O pins. The comm nodes with the RS484 ports are also going to be simple - no real logic on them at all other than decoding a couple of input pins to set the address. Otherwise it's more or less the same code put out by others that reads the bus and looks for input or output messages addressed to it, and transfers the data and unpacks it into bits to send to the port expander chips. But that's another thread. 

 Again - is it perfect? No. Does it work? Aftr running it for a few hours at the Modeler's Meet, yes, it does work just fine as-is. Am I done with the code and ready to call it complete? No. There are things I do want to change up. And some features I want to see if I can add. 

 I get that people are following this. What they need to understand is that this is a special case of a fairly fancy bit of kit, not unlike the commercial products,a dn that the make servos go back and forth with an Arduino does NOT take 100's of line sof code. It takes a couple dozen tops, and most of that is just setting up the pins. The extra code in mine is to make is go slow, light up LEDs, and have additional controls beisdes the manual pushbuttons. And store the last position. That's at least half the code right there. It currently comes out to 605 lines, counting the whitespace and comments. It compiles to 5.5k, considing the most basic Blink 1 LED sketch uses almost 1K, that's not too bad. Absolutely optimized? Clearly noot, but I never claimed it was. I will make some changes, but at some point there is a diminishing return. I have no intention of spending weeks on the code design, expecially since it actually works now. I will clean it up where I can, and add more comments - then I will consider it good to go.

                                     --Randy 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    July 2009
  • From: lavale, md
  • 4,678 posts
Posted by gregc on Saturday, September 22, 2018 6:37 PM

BMMECNYC
This is why Im not really interested in Arduino.  I have no desire to write or debug 100s of lines of code,

Randy

the modelers on the forum respect your comments.  I think they will look at your code as an example of how to write Arduino code.   It would be good if it was a high quality example.

While I've written/debugged lots of embedded code over my career, I've also spent a lot of time reviewing, debugging, fixing and rewriting code written by others that was insufficiently tested, took the simple approach, ignored cases or wasn't maintainable.

I'm sorry i'm not clear enough for you to understand my comments for modularity, isolating functionality and minimizing inter-dependency between different parts of the code (OOD), all of which make code more readable and reusable by others.   this also helps w/ multi-node systems.

Not sure I can be any clearer without showing code (try PM).  In Elements of Programming Style Kernighan describes how to program by exposing bugs and showing how to improve code in examples from textbooks on programming and journal articles (they didn't make up bad examples).   They show what makes code bad and how to make it better.

 

 

 

greg - Philadelphia & Reading / Reading

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Friday, September 21, 2018 1:19 PM

 If I was making a device to control a half dozen or more servos, I probably would make it a function with the appropriate structures. Having come from a strictly proceduarl environment (BASIC and FORTRAN, and direct machine labguage programming are where I started), I have a tough time with the whole "it makes it easier" thing because to me it does not. I much more complex routine and a structure definition, as opposed to taking code I know works and doing a quick copy/paste (and yes, slight edit - change all the 1's to 2's - not exactly rocket science). Faster? I doubt it is much faster in execution, but it surely is faster to write it my way. If I needed more than 2 - yeah, then it starts getting painful to copy/paste/edit AND it could cause issues needing too many time routines. So it would need a different approach. Never said this was totally scalable. Half of it might go away - after seeing how the new Peco Code 70 turnouts are being done, and the mention that this design would trickle over to the Code 83 line as well, I might not need ANY of that relay stuff.

 Using a #DEFINE absolutely saves memory in the Atmel enviroonment, variables are stored in the precioous RAM, substituting a symbol using #DEFINE doesn't shorten the code but it never places that value in the RAM stack. The way I have defined non-const integers does use memory. The actual variable itself that gets set to some #DEFINEd symbol still uses the same amount of memory. servo1dir = -1 uses the same memory as servo1dir = NORMAL. But I'm not even remotely worried aboout it, I'm still using only a small fraction of it, and this is probably the biggest program for the whole system, I don't think the CMRI node will need as much code. Given that some of the smaller Atmega micros actually cost MORE, there's no cost factor driving to make the code absolutely as tiny as possible - and there are likely C tricks I am not familiar with that could make it even smaller, at the expense of readability. I've seen some fo them discussed. I also remember the "one liner" BASIC contests they would run in 80 Micro magazine - some pretty amazing proograms in just one line but unless you were expert+ level in BASIC you would NEVER be able to figure out what it did. Efficient for some values of efficient.

 I'm still not sure what you mean, that's EXACTLY what it is doing right now. It makes one Metro call in the loop() and then moves the servo if the move flag is set or does nothing if it is not. The only difference is, I have 2 of them, one for each servo, rather than making a function out of it and passing which servo I want to test. EEPROM is ONLY being updated if a servo moves. I tested and perfected this with a LOT of serial.print and serial.println commands in each routeine, encapsualted in #ifdef debug /#endif blocks, when I turn debug on it actually does not write to the EEPROM but prints to the serial monitor when it would be doing so and what it would be writing. If the thing just sits there with no buttons pressed, it does nothing. Only when you push a button and the servo moves does it write anything to EEPROM. Anything else would quickly wear out the memory cells.

Awkward? How so? I already have variables defined that are needed by the servo move routing anyway (I guess they don't have to be variables, but then I'd have to go through the entire code to change the endpoints or the stepping rate) and they are global variables anyway. All it does is set servo1pos or servo2pos to somehing other than the endpoint to get it past the move loop. I'd have to firther break the test up into independent IF statements to get away from that, actually making it longer - that way the test would fall through as long as the direction was changed and I could remove the code from the button detection. perhaps that would be more obvious. Actually - the button code does NOT need to know the endpointys, just the current position and the step size. The endpoint variables do not appear in my button code. I have direction and I have step size - the code in the button routin basically sets servo1move to true and sets servo1pos to servo1pos + (servo1dir * servo1step)  (and yes I always use parentheses in math even if technically it's not needed. ANd I was never much of a fan of C's += and -+ and similar combined operators and half the time I completely forget about them and just write out the whole equation instead of shortcutting it.

                               --Randy


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    July 2009
  • From: lavale, md
  • 4,678 posts
Posted by gregc on Friday, September 21, 2018 10:06 AM

 

rrinker
  Possibly. But there are only 2 servos, so I thought it just as easy to simply copy and paste the code and make individual routines.

isn't it more like copy-paste-edit.   don't you need to change all the servo1... variable names to servo2.

an array of statically initialized structures can be used to maintain the state of each servo and a pointer to the structure passed to a common servo routine.   Much less code and you don't need to make the same change in multiple places in the code.

rrinker
 Probably. Most verything else is a CONST or #DEFINE. I could then leave out the comments further up in the code where they are declared which says which is which. As it is, I'm using maybe 8% of the variable space in the rather small RAM of the mega328.

do you think replacing a constant in the code with a symbol,  enum/#define saves memory?

rrinker
 servo1move gets set to false when the servo reaches an endpoint. It gets set to true when a button is pressed for the direcion opposite the current position, or a remote input changes to the opposite position. It's needed because the Metro code is within the Arduino loop() and this gets called ever 50ms regardless. If everything is stationary, thent here is no reason to change those variables or update the EEPROM. Withotu tracking this and skipping over the endpoint check when the servo is not moving, it was writing an update to the EEPROM ever 50ms. Not good.

yes, you need servo1move because of the of the way the code is written

wouldn't it be simpler, more maintainable and less code, to have one call to the Metro code in the Arduino loop() and code that determines when to run the servo code for each servo based on a flag (i.e. servoMove == { CW, CCW}) set by button code and cleared when the servo is in position?  similar to what you are doing but it is tested outside the servo code so that it doesn't need to be repeatedly called. ...

couldn't the EEPROM be updated outside the servo code only if any of the servos moved or anythinhg else affecting EEPROM contents has changed?

rrinker
 Again, this is only a small part of the code. The pressing of a button sets the servo position to the current position + one increment or - one increment, so the first call to this code after a button is pressed causes it to fall past the "check if at end" and do just the simple move the servo

Wow!  that seems awkward.   probably a -2 (Do not submit) git review

Now the button test code needs to know the endpoint positions of each servo.    That implies that the button test and servo code need to be running on the the same Arduino unless you want to share servo positions between Arduino nodes.

wouldn't it be easier for the button code to simply change the servo state (i.e. servoMove)  and let the servo code be the only code that affects servo the position variable?

 

greg - Philadelphia & Reading / Reading

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Thursday, September 20, 2018 3:07 PM

 BTW you're not THAT far, come out to the Reading Modelers Meet and I can show you the whole thing, and some of my other stuff. You cna register at the door, plus there is a low cost spectator admission which means you can come in and see everything, just not participate in the clinics or layout tours. But in addition to the meet stuff you also get access to the RCT&HS museum

                                          --Randy


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Thursday, September 20, 2018 3:03 PM

 Took a copy and paste. I know the problem - the provider that hosts this site for Kalmbach has the site behind a proxy (and probably load balancer as well), which is how it should be. But, they have the connection timeout set too low for someone who types slow, needs to go look stuff up while posting, or types long messages.

                                       --Randy


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Thursday, September 20, 2018 3:02 PM

OK, let's try this again

gregc

Randy

i realize your code is a work in progress.

i ran your code in simulation (C on my laptop).   but first some general comments:

  • looks like this code using servo specific variable names (e.g. servo1pos) need to be duplicated for every servo.   A generic routing specifying a servo by number and using arrays of structures (hor classes) to maintain state for each servo would improve maintainability.

 Possibly. But there are only 2 servos, so I thought it just as easy to simply copy and paste the code and make individual routines.

gregc
  • would be better to use enum or #define for direction (e.g. CW/CCW) instead of 1/-1

 Probably. Most verything else is a CONST or #DEFINE. I could then leave out the comments further up in the code where they are declared which says which is which. As it is, I'm using maybe 8% of the variable space in the rather small RAM of the mega328.

gregc
  • i don't see a need to update the servo position doing servodir * servostep since you have separate lines for each case.  You can do servopos += or servopos -= servostep.

 There's only one line for the plain move, where it needs to adjust the position by the step - it falls to that line if it has not reached the end of movement. The only place it's broken out is when it HAS reached the end, then it has to do differnet things depending on which extreme it has reached. The line in question is execued for both directions, same line of code. So it needs the direction factor.

gregc
  • if setsavepos() and writepos() are EEPROM write, are you writing all values or just the changes for the one servo

 Setsavepos() does a bitmask to change a single byte variable to store the position of both servos. 0x00 means both normal, 0x0F means 1 reverse, 2 normal, 0xF0 means 2 reverse, 1 normal, and 0xFF means both reverse. What gets written to the EEPROM is 2 bytes, one is a counter and one is the position byte. The counter loops from 0 to 255, and when it rolls over, the memory position is advanced by 2 to start the next block. Every pair of locations gets written to 256 times, and one pass through the memory pointer is 500 steps. The 328 has 1K EEPROM, starting at address 1010 I store version information and the memory location pointer, when gets initialized on the first run and then read to know that there is already valid data on subsequent runs. The memory pointer is loaded, then the counter and current position are loaded, the servos and LEDs set, and the loop starts. When you change a servo position, the counter is incremented. If it doesn;t roll over, then the current counter and current position bytes are written to the memory pointer address. If the couner rolls over, then the memory pointer is incrememented by 2 and gets written to the storage location it uses. The counter, now 0, and the position byte get written to the new pointer address and things continue.

gregc
  • not sure how servo1move gets set.   Its use implies this code is invoked repeatedly.

 servo1move gets set to false when the servo reaches an endpoint. It gets set to true when a button is pressed for the direcion opposite the current position, or a remote input changes to the opposite position. It's needed because the Metro code is within the Arduino loop() and this gets called ever 50ms regardless. If everything is stationary, thent here is no reason to change those variables or update the EEPROM. Withotu tracking this and skipping over the endpoint check when the servo is not moving, it was writing an update to the EEPROM ever 50ms. Not good.

gregc

i found that once the servo pos was set to an endpoint (e.g. min or max), the "if (servo1pos <= servo1min ... servomax <= ..." test prevented moving the servo in the opposite direction.   It seems the test should be

     if (CW == servodir)  {
          if (servo1Max > servo1pos) {
Servo1.write (servo1pos += servostep);
return 1;
}
}
.
.
.
return 0;

if the code gets beyond (hence the return) the tests for CW and CCW, it can set LEDs, EEPROM and return 0 indicating done.

 Again, this is only a small part of the code. The pressing of a button sets the servo position to the current position + one increment or - one increment, so the first call to this code after a button is pressed causes it to fall past the "check if at end" and do just the simple move the servo.

gregc
 

not clear if you call this or similar routines every cycle or just when a particular servo needs to move (i.e. why servo1move?).

 That's exactly why it's there, this code runs every 50ms. See above.

gregc

i downloaded the Metro library.  It's pretty thin.  not sure of your need to manage multiple operations simultaneously on a single arduino.  (I think the Metro code should be called outside the servo code to determine when the servo code should be invoked while checking other things).

one approach is to repeatedly call the routine, when needed, with a specified direction either periodically/aperiodically.   It returns 1 if not done and 0 when done.

 

 
 That's pretty much what it's doing. The if(servo1Metro.check == 1) checks to see if the routine shoudl run. There's a replacement for Metro that does more, but it also means you have to manage your own resets. Metro is stupid simple, it's really more or less encapsulating a test for millis() - previousmillis > thresholid in ms conditional combined with a previousmillis = millis() 
 
Yes, this is verison 1. There are some other things I want to add, like possibly disconnecting the servo at end of travel to reduce current draw and noise. But this version does work, doesn;t seem to care how much I press the buttons, it always moves to the position of the last button press, even if that means going back to where it just came from. And attempting to press buttons simultaneously also works, no missed presses. Better than trying to do it all linearly - it takes 3 seconds at the current settings to move the servo from one end to the other. The last thing I want is the whoel thing to go unresponsive for that time. Metro makes that easy. A good example of how NOT to do it are those Lenz stationary decoders, forget the model number. They are mainly meant for twin coil motors but they have a rather long maximum pulse duration so they can sort of work with Tortoises - they just cut power at the end. However, the biggest downfall is that they completely stop responding for the duration of the pulse output. So you tell it to move output 1 with a 5 second pulse duraton, and for the entire 5 seconds it does not respond to any futher commands. And no, it doesn't queue them an act on them in turn - it just ignores all DCC input. Fine when you set th epusle width for a couple of uS to throw a solenoid, but the long pulse duration settings are utterly useless.
                                        --Randy

Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Thursday, September 20, 2018 11:53 AM

 And once again a long and detailed reply gets eaten by the site... and of course Chrome updated today and while up until yestrday I could just refresh and it would warn me about reposting the same form data, today - nothing. 

                                  --Randy


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    July 2009
  • From: lavale, md
  • 4,678 posts
Posted by gregc on Thursday, September 20, 2018 8:25 AM

Randy

i realize your code is a work in progress.

i ran your code in simulation (C on my laptop).   but first some general comments:

  • looks like this code using servo specific variable names (e.g. servo1pos) need to be duplicated for every servo.   A generic routing specifying a servo by number and using arrays of structures (hor classes) to maintain state for each servo would improve maintainability.
  • would be better to use enum or #define for direction (e.g. CW/CCW) instead of 1/-1
  • i don't see a need to update the servo position doing servodir * servostep since you have separate lines for each case.  You can do servopos += or servopos -= servostep.
  • if setsavepos() and writepos() are EEPROM write, are you writing all values or just the changes for the one servo
  • not sure how servo1move gets set.   Its use implies this code is invoked repeatedly.

i found that once the servo pos was set to an endpoint (e.g. min or max), the "if (servo1pos <= servo1min ... servomax <= ..." test prevented moving the servo in the opposite direction.   It seems the test should be

     if (CW == servodir)  {
          if (servo1Max > servo1pos) {
Servo1.write (servo1pos += servostep);
return 1;
}
}
.
.
.
return 0;

if the code gets beyond (hence the return) the tests for CW and CCW, it can set LEDs, EEPROM and return 0 indicating done.

 

not clear if you call this or similar routines every cycle or just when a particular servo needs to move (i.e. why servo1move?).

i downloaded the Metro library.  It's pretty thin.  not sure of your need to manage multiple operations simultaneously on a single arduino.  (I think the Metro code should be called outside the servo code to determine when the servo code should be invoked while checking other things).

one approach is to repeatedly call the routine, when needed, with a specified direction either periodically/aperiodically.   It returns 1 if not done and 0 when done.

greg - Philadelphia & Reading / Reading

  • Member since
    January 2009
  • From: Bakersfield, CA 93308
  • 6,526 posts
Posted by RR_Mel on Thursday, September 20, 2018 8:10 AM

Randy
 
FYI
I was doing some bench testing with the SG90s and I found out that the servos are not flakey at the end of travel.  I bought a Turnigy Servo Tester a couple of years ago for dinking around with the servos at my workbench and it is the guilty culprit.  I was looking at the signal with my scope and when the sweep from the Turnigy starts back from ~.1ms the signal jumps and it’s the same returning from ~2ms.  That doesn’t happen from the Arduino.  When I run a sweep from the Arduino both the SG90 and 3.7g servos work perfect.
 
What through me is Futaba and Airtronics servos aren’t squirrely on Turnigy like the micro servos.  The Airtronics is the best of the bunch as well as the largest, super power and super smooth.
 
 
Mel
 
 
My Model Railroad   
 
Bakersfield, California
 
I'm beginning to realize that aging is not for wimps.
 

  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Wednesday, September 19, 2018 4:56 PM

 With less travel, you just need to use smaller steps, I don't know how you are doing the servo move, but I use the Metro library to do multiple timing dependent things all at the same time - check for button presses, move the servos, check for remote inputs. You create a Metro object for each item, setting the delay in milliseconds, and then in the main loop you just check each metro object with an if. Makes it easy peasey to do multiple things without all sorts of variables and checking against millis(). And you get automatic debounce of the buttons, since it only checks the state once per however many microseconds you configure it for. For my servos, right now I have it set to move a specified increment every 50 milliseconds. The the following code in the loop checks for the servo and either moves it or, if it is at the end position, sets the LEDs and other variables, and then writes the EEPROM.

if (servo1Metro.check() == 1) {   // Servo 1 movement control
    // Move interval for Servo 1
    if (servo1move) {
      // Servo is moving)
      if (servo1pos <= servo1min || servo1pos >= servo1max) {
        // if we reached the end of travel
          if (servo1dir == 1) {
            // reverse
            sw1nstate=LOW;
            sw1rstate=HIGH;
            sw1frstate = HIGH;
            sw1prstate = LOW;
            servo1pos = servo1max;
            servo1move = false;
          }
          else {
            // normal
            sw1nstate=HIGH;
            sw1rstate=LOW;
            sw1frstate = LOW;
            sw1prstate = LOW;
            servo1pos = servo1min;
            servo1move = false;
          }
          digitalWrite(SW1FR, sw1frstate);
          Servo1.write(servo1pos);
          digitalWrite(SW1NLED, sw1nstate);
          digitalWrite(SW1RLED, sw1rstate);
          digitalWrite(SW1PR, sw1prstate);
          setsavepos();
          writepos();
      }
      else {
        // Just move the servo, we are in the middle
        servo1pos = servo1pos + (servo1dir * servo1step);
        Servo1.write(servo1pos);
      }
    }
  }

servo1dir is 1 is moving towards reversed, -1 if moving towards normal. servo1step is the incrememnt per time step, currently 2. servo1min and servo1max are the endpoints, I'm using 20 and 140 in this demo just to get a long travel - the servos aren't attached to turnouts so it's easy to see them move. servo1move is true if the servo is in the middle of travel, or false if it is at the endpoints - so the functions don't keep triggering. sw1nstate and sw1rstate are high and low depending on the endpoint, and controlt he indicator LEDs. sw1prstate is the power control relay, and sw1frstate is the frog polarity relay.

I don't think I'd go much higher than making a move every 50ms, it will start to get jerky. Larger increments likewise make it more jerky. For a shorter throw, perhaps an increment of 1 every 60ms might be ok, so for a 53 degree swing it would take 3.18 seconds to move end to end. About the same as it take mine to go 20-140 in steps of 2 ever 50ms.

 I was also worried that the time it takes to do EEPROM writes would mess with the motion, but I can start one moving, wait a bit, then start the second one moving, and when the first one finishes and updates the EEPROM, the second one doesn't miss a beat. ANd it's always writing 2 bytes to EEPROM, sometimes 4, when the counter rolls over - that's how I spread the writes across the entire memory space to get the most possible writes with no location getting over 100k writes, which is the datasheet maximum allowed (although others have done empirical testing and gotten closer to 1 million writes per cell before seeing any errors). No worries though, 100k writes is a LOT of position changed per day for more years than I expect to be needing these things to work, and they will never be operating every day, even I won't be playing with the trains 4 hours a day 7 days a week.

                                         --Randy

 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    January 2009
  • From: Bakersfield, CA 93308
  • 6,526 posts
Posted by RR_Mel on Wednesday, September 19, 2018 4:31 PM

Actually it was a rather easy fix by cutting the servo travel to 53°.  I wanted more swing but this should work pretty good.
 
 
 
 
By adding a couple of lever action micro switches to the side of the servo that the actuator could trip they could power the frog if needed as well as position indicators.
 
 
 
Mel
 
 
My Model Railroad   
 
Bakersfield, California
 
I'm beginning to realize that aging is not for wimps.
 
  • Member since
    January 2009
  • From: Bakersfield, CA 93308
  • 6,526 posts
Posted by RR_Mel on Wednesday, September 19, 2018 2:07 PM

Being stuck in my chair I got out a Tower Pro SG 90 and a code 83 Atlas Custom Line #6 along with my digital calipers and this is what I come up with.
 
 
I was aiming at 90° servo swing so that it can have a realistic slow movement.  I haven’t tried the design yet but normally if it works on my CAD it works when I build it.
 
It still requires a 2” hole but that’s a bunch easier on my knees than crawling under, shucks I can’t even walk today without pain let alone crawl.
 
EDIT:
 
I found an error, the throw arm moves down in the center position .1".  A keeper at the pivot point and a slot on the actuator arm would fix it.
 
 
Mel
 
 
My Model Railroad   
 
Bakersfield, California
 
I'm beginning to realize that aging is not for wimps.
 
  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Wednesday, September 19, 2018 12:08 PM

 That's a lot like the one I put together just to test, the servo is stuck right to the bottom of the turnout like a Peco switch motor. Not sure if I want to go that way or not - since my layout will be new, I think I'd rather drill a hole than cut out a big opening to fit a Peco motor/servo from the top. Two layouts ago it was also foam, and I mounted my Tortoises from the top, but the last one, while also foam, I mounted the servos on the bottom and just drilled a hole. This time it will be plywood, so I'm leaning towards the bottom mount.

                                         --Randy

 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    January 2009
  • From: Bakersfield, CA 93308
  • 6,526 posts
Posted by RR_Mel on Wednesday, September 19, 2018 8:04 AM

rrinker

 

 That's perhaps the one downside of Mel's one motor for 4 sets of points approach - it has to be all in pretty good adjustment to get them to all equally move. With a Tortoise, it makes sense, they aren't cheap. But considering 4 servos are still cheaper than one Tortoise, each could be individually powered without all the complex mechanical design.

 

Randy
 
That’s what I have in mind, but . . . .  I can’t help hearing “if it ain’t broke don’t fix it” rattling around in my skull.
 
That’s been with me all my life and I believe I’ll stick with it replacing switch machines.  My intent is to only switch to servo control on an as needed basis as the 30 year old Atlas turnouts or under table #65s quit.  I’ve been doing my Peco conversion for a couple of years and they most likely will be working when I’m not here anymore.
 
For now my plan is to come up with a mount similar to my Peco conversion using servos that can just drop in from the top.  My knees and back crapped out several years ago so crawling under is a no go.
 
 
 
I’m not doing well right now so this project is temporally on hold.  I get severe Arthritis flareups in my ankles that can last for weeks so until I’m back to normal nothing much gets done away from my workbench chair.
 
 
Mel
 
 
My Model Railroad   
 
Bakersfield, California
 
I'm beginning to realize that aging is not for wimps.
 
  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Wednesday, September 19, 2018 7:05 AM

 That's sort of the idea of figuring a mount that goes over center. Although I'm not sure even that is needed, actually. Before I hooked up all of the Tam Valley Singlet controllers on my old layout, I would just reach under and turn the servo by hand. Even with no power applied there was no issue keeping the points tight enough. You definitely don't need to go a full 0-180 to get to a point where the back pressure is pushing the servo further in the direction of travel instead of resisting it.

                                         --Randy

 

 That's perhaps the one downside of Mel's one motor for 4 sets of points approach - it has to be all in pretty good adjustment to get them to all equally move. With a Tortoise, it makes sense, they aren't cheap. But considering 4 servos are still cheaper than one Tortoise, each could be individually powered without all the complex mechanical design.


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    July 2009
  • From: lavale, md
  • 4,678 posts
Posted by gregc on Wednesday, September 19, 2018 6:31 AM

RR_Mel
To maintain rail contact to insure the moving rail is close enough to prevent a wheel from climbing the point and derailing the servo needs to apply slight pressure against the rail. 

wouldn't the servo be more likely to hold it position w/o being powered/pulsed if the endpoints are near their extremes?

any back pressure would NOT be tangential, causing the arm to rotate

even if 0-180 deg travel is not possible, anything near the extremes may be enough

greg - Philadelphia & Reading / Reading

  • Member since
    July 2006
  • From: Bradford, Ontario
  • 15,797 posts
Posted by hon30critter on Wednesday, September 19, 2018 12:41 AM

Randy:

Great progress!

Dave

I'm just a dude with a bad back having a lot of fun with model trains, and finally building a layout!

  • Member since
    January 2009
  • From: Bakersfield, CA 93308
  • 6,526 posts
Posted by RR_Mel on Tuesday, September 18, 2018 8:58 PM

I missed stated the current, the servo idle current its self is 14 ma and with slight pressure on the actuator increases the 14ma (no signal) by an additional 4ma CCW and 13ma CW.
 
 
Mel
 
 
My Model Railroad   
 
Bakersfield, California
 
I'm beginning to realize that aging is not for wimps.
 
  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Tuesday, September 18, 2018 7:57 PM

 I still think you are OK current-wise. It's hard to find specs on the 9G servos, but 14ma is nearly as low as a Tortoise, and the 4ma side is lower than any Tortoise will stall at. Given that the average operating current of the servo is somewhere around 400ma, with a peak of near 1A, 14ma is nothing.

                                               --Randy

 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    January 2009
  • From: Bakersfield, CA 93308
  • 6,526 posts
Posted by RR_Mel on Tuesday, September 18, 2018 6:14 PM

Thank you Henry, if you would like to see more of my Wizardry here is a link to the construction of the Mel made double crossover.
 
 
Back in the early 90s and again in the early 2000s I tried every manufactured code 83 double crossover made.  The only one that would pass my large flange Rivarossi Cab Forwards without problems was the Fast Track and I found it to complicated (frog power) for what I wanted.  The answer was to make my own.  All my Cab Forwards would easily clear code 83 Atlas Custom Line Turnouts without any problems so I did it to it.
 
 
Mel
 
 
My Model Railroad   
 
Bakersfield, California
 
I'm beginning to realize that aging is not for wimps.
 
  • Member since
    December 2015
  • From: Shenandoah Valley
  • 9,094 posts
Posted by BigDaddy on Tuesday, September 18, 2018 5:47 PM

RR_Mel
One Tortoise operates all four sets of point rails on my Mel made double crossover.

You've posted that phrase before but this is the first time I've seen a picture.

I nominate you for MR Wizard of 2018. 

Henry

COB Potomac & Northern

Shenandoah Valley

  • Member since
    January 2009
  • From: Bakersfield, CA 93308
  • 6,526 posts
Posted by RR_Mel on Tuesday, September 18, 2018 5:39 PM

The Tower Pro servos are squirrelly between 0 and 10 so keeping the swing to 90° between 45 and 135 works the best for me.  I’ve had pretty good luck using 90° bellcranks on my turnouts in the past.
 
 
 
One Tortoise operates all four sets of point rails on my Mel made double crossover.
 
 
My double crossover has worked flawlessly for several years with one Tortoise.  A servo should work but I’m worried about the idle current, not a problem with a Tortoise.  Maybe a larger servo would work better than a Tower Pro.
 
I’m going to experiment with a Mel mod Atlas turnout to see how much it takes to move the throw bar with the Peco type spring action.
 
 
 
 
Mel
 
 
My Model Railroad   
 
Bakersfield, California
 
I'm beginning to realize that aging is not for wimps.
 
  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Tuesday, September 18, 2018 4:52 PM

 If the current is much higher in one direction, you probably can back off a little ont he high side and still have it push hard enough. It might be catching on the hole, so it's just bending the wire and not actually putting any more pressure on the throwbar. I ran into that a few times when I didn;t have the hole cleaned out quite good enough, but since mine was through foam, it was easy enough to clean it out without taking everything apart. Next time it will be plywood, so not as easy to fix. I'd really like to just stick the servos right to the underside, on their sides, with no special mount, but the laser cut plywood mounts that Tam Valley has make it pretty easy and might be worth the cost. Or an excuse to get a laser cutter and make my own.

 I have one I was using for testing that uses the old Tam Valley/Motrak resin mount, stuck right to the button of a Peco turnout. Without removing the spring, it needs hardly any servo movement to move the points. But that would require routing out a huge hole under every turnout to drop it all in from the top. I'd rather use some sort of mount that allows for a bit of an over center action, so it's not the servo holding the points, but the springyness of the wire. Short servo throw and at the ends barely any force back on the servo unless you try to manually pull the points to the opposite side.

 One thing - most of these servos can't actually do the whole range allowed by the Servo library. I find most of them can only go about 15 to 165 tops, try to go any closer to 0 or 180 and they just stall and draw high current. Default values in my prototype at 40 to 140, and that's actually too far for the directly mounted example I mentioned above. It never reaches either limit without buzzing loudly, so i quickly pulled it and connected a loose servo that could turn freely. Final numbers won't come until I decide on a mount and mock it up with a real turnout. 

                                       --Randy

 


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    January 2009
  • From: Bakersfield, CA 93308
  • 6,526 posts
Posted by RR_Mel on Tuesday, September 18, 2018 12:55 PM

I’m going to be using MEGA2560 R3 for my servo controllers.  I’m using a UNO for testing.  So you would say the max 13ma idle current shouldn’t hurt the servo motor.  If I cut back the movement the servo it won’t reliably keep the points touching the rails.  I played around with the fulcrum point to get the best throw arm ratio and centering.  One out of three or so operations end up at 10 ma to 13ma on the CW end, the CCW end stays at about 4ma every time.
 
I guess I will let a servo sit at 13 ma at idle and see what happens.
 
You are correct about the high current, if I hold the actuator arm the current easily goes past one amp, they are pretty powerful for their size.  Normal movement is under 30ma, servo speed doesn’t have any impact on the current.
 
By the way I bypass the Arduino regulators in all of my Arduinos, I power the processors with 5 volts from 8 amp DC to DC convertors powered from a 12 volt 30 amp switching power supply.  I’m afraid that klutzy Mel would screw them up using the 12 volt input by over loading them.  My norm is always over think and over build my power supplies.  I also mounted a digital voltmeter on top of every convertor for constant monitoring.
 
 
Mel
 
 
My Model Railroad   
 
Bakersfield, California
 
I'm beginning to realize that aging is not for wimps.
 
  • Member since
    February 2002
  • From: Reading, PA
  • 30,002 posts
Posted by rrinker on Tuesday, September 18, 2018 11:47 AM

 You aren't doing anything wrong. The onl way to get a servo to 0 current draw is to cut the power off completely (Servo.disconnect() in Arduino does not cut the power, just the signal). Ordinarily a servo always draws some power as it maintains position. How much depends on how hard it has to keep pushing, up to the limit of the servo. I use those same servos - and they cna draw pretty much current if jammed up. Just try to gently force one out of position once it reaches the end of travel. They buzz loudly and current spikes way up. Not going to harm the Arduino, the power does not come from the signal line, and you should not power more than one servo from the Arduino's on board regulator. If you're only getting 4ma then they won't heat up, and you've got that tweaked about as close to perfect as you can.

 The rest is all mechanical. The way you are pivoting the actuating wire, the distance between that fulcrum and the servo vs the distance between the fulcrum and the throwbar, and the thickness of the music wire used all determine just how hard you have to push the servo to maintain a good point/stock rail contact. I don;t use ver heavy wire, .032 was fine for Atlas turnouts through 4" of extruded foam plus cork roadbed on top. I could move the points and when I let them go they would snap against the stock rail, yet the servo was quiet. I used the Tam Valley controllers on that layout, and never had a problem with the servos heating up. Never measured the current draw. I had about 10, running on the track power bus of my Zephyr (rails were powered from a separate booster). Your current readings are well below what my prototype is drawing, at least according to my bench power supply. I get nearly 200ma just sitting there, no servos moving (and they aren;t physically hooked to anything either, just sitting on the bench). That's with all relays de-energized as well. When the relays move it spikes higher - I had to set the current limit over 500ma to keep it from tripping to constant current mode. Not sure what the Nano draws, I have 2 LEDs o at all times at 13ma each, whatever the servos draw when not stressed, and there are 8 inputs all using the internal pullups for now. ANd the flashing hearbeat LED. No other loads when the turnouts are both in normal positon - that releases both relays, since I configured it such that the most common position would have no power to any of the relay coils. I'm not worried, I have 3 amp capacity buck convertors to feed each unit from the aux power bus. I thnk the final circuit with a plain ATMega 328 will draw less.

                                         --Randy


Modeling the Reading Railroad in the 1950's

 

Visit my web site at www.readingeastpenn.com for construction updates, DCC Info, and more.

  • Member since
    January 2009
  • From: Bakersfield, CA 93308
  • 6,526 posts
Posted by RR_Mel on Tuesday, September 18, 2018 9:45 AM

Randy
 
I have a servo question for you.  To maintain rail contact to insure the moving rail is close enough to prevent a wheel from climbing the point and derailing the servo needs to apply slight pressure against the rail.  I’m using the Tower Pro 90 servos with a UNO driver and Atlas Custom Line turnouts.  The servo doesn’t draw the same amount of current at the end of activation.  The servo idle current varies from 4ma to 13ma on each activation from the UNO.
 
I’m worried about long term current draw heating up the servo motor.  If I back off the slight end travel current to 0 the rail points can shift a bit with the vibration from moving locomotives enough to catch a wheel flange.
 
What am I doing wrong?
 
 
Mel
 
 
My Model Railroad   
 
Bakersfield, California
 
I'm beginning to realize that aging is not for wimps.
 

Subscriber & Member Login

Login, or register today to interact in our online community, comment on articles, receive our newsletter, manage your account online and more!

Users Online

There are no community member online

Search the Community

ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT
Model Railroader Newsletter See all
Sign up for our FREE e-newsletter and get model railroad news in your inbox!