Sunday, October 06, 2024

Introduction to the blog

Introduction


This blog describes ongoing progress in the development of a G gauge Garden Railway from its inception to the present day.    

NEW (29 June 2024)

 
NEWS
The blog has now had well over a million visitors. Many thanks to all those who have provided me with support, suggestions and feedback over the years.


When I became interested in building my own garden railway I spent a considerable amount of time (and money) on books, videos, DVDs and scouring the internet for information, ideas and inspiration. When I eventually started construction, I used some of the ideas I had discovered, but also experimented with my own approaches. This blog outlines how I have gone about constructing my own garden railway. My aim is to provide the sort of information I was looking for when I was getting started, and also to share what I've learned (or 'borrowed' from others). I've tried to include a few 'How I ........' postings interspersed with occasional 'Progress Reports'. I do not profess to be any kind of expert - what I offer here is an opportunity for you to metaphorically look over my shoulder to see how I have gone (and am going) about this fascinating hobby.

As this is a blog, the various posts are presented in reverse chronological order (ie the most recent first). To see a categorised list of contents, go to the Blog Contents Page.


If you are thinking about building your own garden railway, then why not join the 16mm Association or the G Scale Society - you'll get plenty more advice and opportunities to visit other peoples' garden railways
. Alternatively, browse through the G Scale Central website - there's plenty more guidance here and an opportunity to sound out the views of others through the G Scale Central discussion forum.


The Blog


The advantages of blogging are that it is immediate and uncomplicated when creating and uploading information. The other, of course, is that with Blogger it is free. The major disadvantage is that I have minimal control over how the postings are presented. The blogging system adds the most recent information to the start of the blog, hence the postings appear in reverse chronological order (most recent first, oldest last). Whilst there is a list of postings on the right hand side, it's not particularly easy to see what is there. This introduction is an attempt to provide you with a contents list of the postings organised into categories so, hopefully, you see if what you are looking for is presented in this blog. To ensure that it always appears at the start of the blog, I update its content and set its presentation date into the future each time I add a new posting.

Powered by WebRing.

How I created my own 3D printed 45mm gauge powered chassis

I have never felt particularly confident about making my own powered chassis. It's partly because I don't really have the workshop equipment needed for accurate metal working, but also because I don't really have the skills and aptitude necessary for the sort of precision engineering which the creation of reliable working mechanisms require. However, with the advent and my acquisition of a 3D printer that has now changed.

It all started when I constructed a Glyn Valley Beyer Peacock locomotive and powered it with a secondhand Bachmann motor block from a tramcar.

I was very disappointed with the loco's hauling capabilities. Clearly, a mechanism designed for a tramcar wasn't expected to pull heavy loads!

The beauty of the Glyn Valley locos is that they were designed to run along the roadside, and so their motions were covered with side-skirts (so horses wouldn't be frightened). Thus, any sort of mechanism can be used to power a model, as it wouldn't be visible.

And so, I decided to have a go at using my TinkerCAD designing skills and my 3D printing capabilities to create my own powered chassis. After all, filament 3D printers can work down to 0.2mm of accuracy.

My first design made use of the ubiquitous 12v MFA Como 918D gearbox motor. These have been used extensively in garden railway locomotives for years.

The motor quite happily sat between the axles, driving one of them via bevel gears (also obtainable from MFA Como). The power was then transmitted to the second axle via a toothed belt and toothed pulleys ....

 ... acquired through eBay


 

 The wheels were provided by Essel Engineering with 4mm diameter holes for axles (the bore of the bevel gears). The beauty of 3D CAD and printing is that I could easily tweak the design to, for example, ensure the bevel gears meshed perfectly.

This fitted snugly inside the skirts of the GVT loco and she was given a test run.

However, I was disappointed to find that it was only marginally more powerful. The eight wagon goods train in this above video barely made it up the 1:40 gradient on full power. I needed to find a more powerful gearbox motor.

I opted for a MFA Como 940DLN 51:1 gearbox motor which was considerably larger than the 918D and so the chassis needed to be redesigned to accommodate it.

The toothed belt was replaced with connecting rods and fly cranks  attached to the ends of the extended axles with grub screws inserted into square nuts trapped in the fly cranks.





The loco now performed a lot more powerfully - actually she is now far more powerful than she needs to be - the 51:1 gear ratio is probably lower geared than is necessary which means her top speed is somewhat reduced.

I rested on my laurels for a year or so until I encountered another chassis problem. A 3D printed Ruston diesel loco body which I had designed to fit on to a HGLW Deluxe powered chassis. Whilst I was pleased with the loco body, it just didn't look right perched on top of the HGLW chassis. Nothing wrong with the chassis - they are sturdily designed and easy to construct - it just wasn't appropriate (see How I created a sort of Ruston diesel).


 So, another bespoke chassis was required. This time, rather than going down the MFA Como route, I decided to explore Polulo geared motors which, according to their data sheets, provide higher torque than their MFA equivalents - though they are more expensive.

The wheels are Bachmann 24.5mm metal wheelsets on 3mm axles.

For its size, the chassis is quite powerful. I've not yet been able to test it to its limit, as the wheels slip before it stalls, but I suspect it will have more pulling power than the first GVT loco chassis I constructed.

I ended up abandoning the Polulo gear motors in favour of much cheaper GA25-370 gear motors from AliExpress. These have proved to be just as powerful and reliable.

 The bevel gears also came from AliExpress......

..... as did the top-hat bearings.

 The Delrin chain and sprockets were provided by MotionCo.

This chassis enabled me to more realistically mount the body far closer to the rails.

Since this build, I have used an identical chassis to power a Ruston Proctor oil engine (see How I constructed a Ruston Proctor loco), ...

......and, with some modification to allow the motor to fit inside the vertical boiler, ......

 ...... a de Winton locomotive. This did not require chain drive as it has coupling rods mounted to inserts on the wheels (for more information see - How I created a de Winton vertical boilered loco.

So, how did I go about the design process for the powered chassis?

 

How I designed the chassis

As has been mentioned, my 3D CAD package of choice is TinkerCAD. It is not the most powerful or sophisticated package, but I find it easy to use and more than capable of the design tasks I have used it for over the years.

I have provided some tutorials on making use of TinkerCAD plus there are dozens (if not hundreds) of video tutorials on YouTube.

What I found essential was to recreate a 3D version of the motor and the bearings.

I could then 'build' the chassis on screen.

As can be seen, the chassis is little more than a series of simple building-block shapes.

With TinkerCAD, any shape can be turned into a 'hole', such as the motor assembly.

When a 'hole' such as this is combined or 'grouped' with another shape, such as this red block......

... the 'hole' creates a concave image of itself in the other shape

In the same way, 'holey' copies of the axles and the bearings can create the correct sized and shaped holes in the main chassis block.


And so, the various components can be grouped together to create a bespoke chassis block. In this version, the base is removable to allow Delrin sprockets and chain to be more easily fitted. It also has three sets of axle holes to easily allow for different wheelbases.

This chassis is available as a free download on the Gardenrails.org forum. You will need to register to download the file, but access is free (though voluntary donations for the running of the forum are always gratefully received).

 

Construction of the chassis

Once the two parts of the chassis had been printed out.......

..... the bearing bushes were inserted into the four axle holes. I have tried using superglue to hold them in place but, as they are made from oilite bronze, they aren't susceptible to gluing.

As mentioned above, I used Bachmann 24.5mm dia wheelsets. The wheels can easily be removed from the axles with a bit of gentle force and a twisting action.

 I found that the 3mm bore Delrin sprockets needed to be opened out slightly with a 3mm drill, otherwise they were an impossibly tight fit on the axles.

With one wheel removed from each wheelset, the axles were then inserted with a washer between the wheel and the chassis. The Delrin sprocket was also placed on the axle with a spacer washer ........

..... before the second wheel was put back on to the axle.

This process was repeated for the other axle, but with a 3mm bore bevel gear also inserted.

Before the second wheel was attached, the Delrin chain was fitted, the slack provided by the end of the axle being handy for tensioning.

The second wheel was then placed on the axle.

 A 4mm bore bevel gear was attached to the motor shaft and the motor slipped into its housing. Some adjustment was required to the positioning of the bevel gears to ensure they meshed smoothly (a cigarette paper's gap should be left between the teeth).

 The base was then attached .......

.... and held in place with cable ties inserted either side of the motor and tightened to hold the motor in place.


 I left the extended axles on at this stage, as I wasn't sure whether the chassis would be used in a loco with fly cranks. If not, the ends of the axles will be easily removed with a slitting disk in my mini-drill.

I have been pleasantly surprised at how quiet and powerful the chassis are - as evidenced in these videos.

The only limitation to the load the de Winton (and the other locos) could pull was adhesion - the amount of weight I could squeeze in was limited by the available space. 

Armed with the above experience and accumulated confidence, I am now happy to experiment with other powered chassis. I just need to track down some decent spoked loco wheels (or print my own!).

Monday, September 16, 2024

How I radio control my semaphore signals - Part 2

 Introduction

Ten years ago, after constructing 19 semaphore signals for my railway (see How I constructed 19 semaphore signals), I put some of them under radio control using a Picaxe microprocessor and a cheap keyfob transmitter (see How I radio-controlled some of my signals).

However, whilst the Picaxe programming of the signal arms movement was excellent (thanks to my mate Greg), the system as a whole was quite cumbersome. The relays on the receiver-board needed a 12v supply, for which I used a lead-acid battery, and four signals needed to be hard-wired to the receiver/controller. As I wanted to be able to remove the signals at the end of each running session, they needed weatherproof plugs and sockets and the control box either needed to be weatherproof or removable.

What I really wanted was to make each signal self-contained, so it could be easily deployed and removed - no central control-box and no trailing wires.

 Over the past ten years, I have been looking-out for something which would be suitable. What I really needed was a compact, low-voltage (and cheap!) receiver, which could be housed beside or beneath each signal so when I spotted this key fob RC system on AliExpress,....

.... I was delighted. It seemed to fulfil my needs; it could operate off a 3.7v supply (ie one li-ion cell) and each of the four receivers had a small footprint (22.5mm x 11mm) (see - qiachip.com )

What I needed next was an Arduino microprocessor with a similarly small footprint. Whilst I could have used a Picaxe 8M, I would have also needed a board on which to mount it and also some additional components. As it turned out, a Seeeduino XIAO (21mm x 18mm) seemed to fit the bill, especially as I had been learning Arduino coding for another project (see Controlling a loco with Arduino and Bluetooth). Furthermore, it could similarly operate from a 3.7v supply.

  All I needed now was a very small servo. Again, I was fortunate in unearthing a tiny  (21mm x 15mm x 12mm) linear servo, which seemed ideal. It, too, could be powered by 3.7v.

  And, finally, I needed some small lithium-ion cells. Although I had a couple of AA (14500) cells to hand, I would need another 17 to power-up all the signals. A quick scan of eBay revealed that the cheapest cells were 16340s (ie 16mm diameter x 34mm long).

Although these claim to be 2800mAh, I know from experience that this needs to be taken with an enormous slab of salt. Also, for the price, I would have to treat them with extreme caution - especially when charging. However, at only around £1GBP per cell, I was willing to give them a try.



 Programming

So, with the hardware now sorted-out, I now needed to figure out the Arduino coding to make it work.

The receiver could set up to provide either a momentary 3.3v (HIGH) or a 0v (LOW) output - ideal for triggering the Seeeduino. In addition, it's relatively straightforward to provide outputs to control servos with Arduino coding.

My initial code simply sent the servo either up or down, with an output on Pin 6 dependent on a 0v (LOW) triggering signal on Pin 0. 

#include <servo.h> //Loads the servo command library
Servo myservo; //Names the servo "myservo"


//variables
int sigstate=0; //Flags the state of the signal (1 = raised 0 = lowered)

void setup() { //The inital set-up procedure
 myservo.attach(6); //Attaches myservo to Pin 6
 myservo.write(0); //Sends myservo to 0 degrees
 pinMode(0,INPUT_PULLUP);//Sets Pin 0 to input and sets it initially to HIGH
} //End of the setup procedure

void loop() { //The main loop procedure
  if (sigstate==0){ //Checks the signal state - If lowered (ie sigstate 0) .....
    raiseit(); // .... it raises the signal
    sigstate=1; // and sets the sigstate to raised (ie 1)
    delay (500); // Pauses half a second
  }
else { //If the signal is already raised ......
  lowerit(); // .... the signal is lowered
  sigstate=0; // and sets the sigstate flag to 0
  delay (500); // then waits half a second
}
} //End of the main loop

void raiseit(){ //The raise sub procedure
myservo.write(180); // Sends the servo to 180 degrees
}

void lowerit(){ //The lower sub procedure
myservo.write(0); // Sends the servo to 0 degrees
}

 Having satisfied myself that the hardware was performing as it should, I increased the sophistication of the programming to move the signal arms more slowly and include some sort of bounce at the end of each movement.

#include <Servo.h> //Loads the servo command library
Servo myservo; //Names the servo


//variables
int sigstate=1; //Logs the state of the signal arm (1=raised, 0=lowered)
int val; //Used to record the state of the triggering input from the receiver
int x=0; //Used in For..... loops
float y; //Allows for variable y to have a floating decimal value
int z; //Also used in For.... loops

void setup() { //Initialising instructions
 myservo.attach(6); //Connects a servo output to Pin 6
 myservo.write(140); //Sends the servo to 40 degrees (ie raised)
 pinMode(0,INPUT_PULLUP); //Sets pin 0 to HIGH by default
} //End of initialising 

void loop() { //Main program loop
val=digitalRead(0); //Checks the input from the receiver on pin 0
if (val==0) { //If pin 0 has gone LOW (ie if it has received input from the rx)
  if (sigstate==0){ // ..... and if the signal is already lowered .....
    raiseit(); //... then go to the sub procedure to raise the signal
    sigstate=1; //... and set the signal state flag to raised
    delay (500); //Wait half a second
  }
else { //Alternatively, if the signal state is already raised ....
  lowerit(); //... then call the lowering sub procedure....
  sigstate=0; //.... and set the sigstate flag to lowered.
  delay (500); //Pause for half a second
}
}
}//End of Main program loop

void raiseit(){ //Start of the raising sub procedure
  x=40; //Sets the value of x to 40 (the lowered position of the servo)
  do {
    delay (10);
    myservo.write(x); //Move the servo to the value of x
    x++; //Increase x by 1
  } while (x<140); //Carry on doing the above while x is less than 140 (the upper position of the servo)

for(z=140;z>120;z--) { //The first bounce .....
  myservo.write(z);
  delay(10);
}
for (z=120;z<140;z++){ //.... and back again
  myservo.write(z);
  delay(5);
}
for(z=140;z>130;z--) { //Second (lesser) bounce .....
  myservo.write(z);
  delay(10);
}
for (z=130;z<140;z++){ //.... and back again.
  myservo.write(z);
  delay(5);
}
  myservo.write(140); //Makes sure the signal is raised
}//End of the raising sub procedure

void lowerit(){ //Start of the lowering sub procedure
  x=100; //Sets x to 100
  y=1; // and y to 1
  z=140; // and z to 140 (the raised position of the servo)
do {
  myservo.write(z); //Sends the servo to z degrees
  delay(x); //Waits for x milliseconds
  x=x-y; //Reduces x by the value of y
  if (x<0){x=5;} //Makes sure x doesn't become negative
  y=y+0.5; //Exponentially increases the value of y (it gets larger increasingly faster)
  z--; //Decreases the value of z by 1 (ie the position of the servo by 1 degree)
} while (z>40); //Do the above until z reaches the lowered value for the servo

for(z=40;z<60;z++) { //First bounce ......
  myservo.write(z);
  delay(5);
}
for (z=60;z>40;z--){ //..... and back again
  myservo.write(z);
  delay(5);
}
for(z=40;z<50;z++) { //Second (lesser) bounce ......
  myservo.write(z);
  delay(5);
}
for (z=50;z>40;z--){ //.... and back again
  myservo.write(z);
  delay(5);
}
} //End of the lowering sub procedure

This code was tested and seemed to work quite well, though the bounces needed a bit more tweaking.

Before working on improving the base, I decided to add another feature - the ability to turn on and off the signal lamp by holding down the button on the transmitter for more than two seconds. I consulted various online sources, but wasn't entirely sure how to incorporate the code into my simple procedure, and so asked ChatGPT to lend a hand. I was pleasantly surprised by the result. The code was very well written, with copious explanatory comments and notes. I can thoroughly recommend using ChatGPT to assist with Arduino programming - but the instructions you give it need to be very precise. For example - https://chatgpt.com/share/219dc577-5a3e-4659-86fc-83adb1a66580

Armed with the additional information provided by ChatGPT, I modified the coding to include the LED switching capability.

#include <servo.h> //Loads the servo command library
Servo myservo; //Creates a name for the servo

//variables
bool sigstate=1;//1 = signal raised, 0 = signal lowered
int val; //Val used to record when trigger is received from receiver
int x=0; //Variable used in various for.... loops
int z; //Ditto
int uplim=150; //Upper limit for servo (in degrees 0-180) - Change if necessary
int lowlim=50; //Lower limit for servo - Change if necessary
float y; //Variable used to count milliseconds when input is triggered from receiver
bool ledState = true;     // Variable to store the current state of the LED - initally set to ON
unsigned long triggerStartTime = 0;  // Variable to store the time when trigger pin was first held low
unsigned long triggerEndTime = 0; //As above but stores when trigger signal is no longer low

//Constants
int trigpin=0; //Arduino pin used for trigger input from receiver
int sigpin=6; //Pin used for servo output
int ledpin=3; //Pin used for LED output
const unsigned long holdTime = 2000;  // Time in milliseconds that the pin must be held low

void setup() { //Initialising instructions
 myservo.attach(sigpin); //Tells Arduino the pin used for the servo
 myservo.write(uplim); //Sets the servo to its upper limit for the start of the session
 pinMode(trigpin,INPUT_PULLUP); //Defaults the trigger input to HIGH
 pinMode (ledpin,OUTPUT); //Sets the LED pin to output mode
 digitalWrite(ledpin, ledState);  // Sets the initial state of the LED
} //End of initialising instructions

void loop() { //Start of main program loop
   if (digitalRead(trigpin) == LOW) { //Checks the state of the trigger input to start measuring the time it is help LOW
    if (triggerStartTime == 0) { //
      triggerStartTime = millis();  // Record the time when pin 0 is first held low
    }
    
    do { 
      triggerEndTime=millis(); //Records the time when the trigger input is no longer LOW
   }while (digitalRead(trigpin)==LOW);
       if (triggerEndTime - triggerStartTime >= holdTime) { // Check if pin 0 has been held low for the required time
      ledState = !ledState;               // If so, toggle the LED state (LOW to HIGH or vice versa)
      digitalWrite(ledpin, ledState); //Turns on or off the LED
      triggerStartTime = 0;
      triggerEndTime =0;      // Resets the timers
    } 
  
  else { //If the trigger isn't held LOW for the required time, then .....
    triggerStartTime = 0; // Resets the timers
    triggerEndTime =0;  
 if (sigstate==1){ //Checks if signal is raised
    lowerit(); //If so, jumps to the signal lowering sub procedure
    sigstate=0; //Updates the flag showing the state of the signal
    delay (500); //Waits for half a second
  }
else { //So, if the signal isn't raised .....
  raiseit(); //Jumps to the signal raising sub procedure
  sigstate=1; //Updates the flag to show the signal is raised
  delay (500); //Waits for half a second
}
}
}
} //End of main program loop

void raiseit(){ //Start of signal raising sub routine
  x=lowlim; //Start off at the lower limit for the signal
  do {
    delay (10); //Wait 10 milliseconds
    myservo.write(x); //Set the position of the servo to x degrees
    x++; //Increase x by 1
  } while (x<uplim); //Keeps doing the above until the upper limit is reached

for(z=uplim;z>uplim-30;z--) { //First bounce
  myservo.write(z);
  delay(10);
}
for (z=uplim-30;z<uplim;z++){ //Return to upper limit position
  myservo.write(z);
  delay(5);
}
for(z=uplim;z>uplim-20;z--) { //Second bounce
  myservo.write(z);
  delay(10);
}
for (z=uplim-20;z<uplim;z++){ //Return to upper limit position
  myservo.write(z);
  delay(5);
}
  myservo.write(uplim);
} //End of raising sub routine

void lowerit(){ //Start of lowering sub routine
  x=100; //Initial value for delay in dropping the signal arm (slow to begin)
  y=1; //Start of how much the delay is decreased
  z=uplim; //Initial value for position of servo
do {
  myservo.write(z); //Sets the servo to z degrees
  delay(x); //Pauses x milliseconds
  x=x-y; //Decreases the pasue time by y seconds
  if (x<0){x=5;} //Makes sure the pause doesn't become negative
  y=y+0.5; //Exponentially increases the reduction in the pause between each servo step (so the drop gets faster)
  z--;//Decreases the position of the servo by one degree
} while (z>lowlim); //Does all the above until the lower limit is reached

for(z=lowlim;z<lowlim+20;z++) { //First bounce
  myservo.write(z);
  delay(5);
}
for (z=lowlim+20;z>lowlim;z--){ //Return to lower limit position
  myservo.write(z);
  delay(5);
}
for(z=lowlim;z<lowlim+10;z++) { //Second bounce
  myservo.write(z);
  delay(5);
}
for (z=lowlim+10;z>lowlim;z--){ //Return to lower limit position
  myservo.write(z);
  delay(5);
}
} //End of Signal Lower sub rountine

 With the hardware and software now working as intended, it was time to work on improving the signal baseplates.

 

Baseplates

 These were designed and tweaked with my go-to 3D CAD package - TinkerCAD. It may not be the most sophisticated 3D CAD package, but it is simple to use and, so far, I've never had a file which my printers can't handle.

Once all the pieces had been printed out.....


 ... the first task was to wire-up the SPDT sub-miniature slide switch as this would be inaccessible later.

The connections were shrouded in heatshrink tubing to avoid accidental short-circuits.

The switch was fixed into the holes provided with small self-tapping screws.


I decided the li-ion cells would be removable for charging. As indicated above, I was concerned about their quality and reliabilty and so wanted to be able to charge the cells externally (in fact, I will charge them outdoors as an extra precaution!).

Two pieces of 30 x 8 x 0.5mm brass strip were cut, ......

...... folded in half ........

..... and one side given an extra fold at the half-way point.

These brass contacts were then glued into the slots provided.........

...... with a few dabs of superglue to hold them in place.

A 1S BMS li-ion battery protection board was given heatshrink shrouding over its metal strip contacts ......

..... before the ends of the contacts were soldered ......

...... to the brass contacts.

The wire from the centre terminal of the slide switch was trimmed and soldered to the P+ contact on the BMS board.

A 60mm length of 1.5mm dia copper wire (stripped from some twin and earth mains cable) was bent into a U shape (the base being 10mm in length) and a piece of black hook-up wire soldered to the middle of the base.

This was then inserted into the holes beneath the battery compartment in the base, as shown.

The black wire from the copper U was then soldered to another three pieces of black hook-up wire (each approx 50mm in length) and the ends of the wires soldered to the P- terminal on the BMS board.

 The receiver was temporarily connected to a spare li-ion cell to power it up.

The button on the receiver was pressed eight times (to remove any previous settings) and then once the LED had flashed to confirm the settings were cleared, it was pressed once (for momentary mode) and the A key on the keyfob transmitter pressed to bind the receiver to the A button on the transmitter.

Thus, when the A button on the keyfob was pressed, the receiver's LED came on and went out when the button was released.

 The supplied wires on the receiver..........

..... were then removed (with a soldering iron), including the aerial lead. The connecting wires are too fat for the confined space within the base and the aerial lead, as supplied, is too long.

New (thinner) wires were then soldered to the pads on the receiver, and the aerial wire was reduced in length by around 25mm and resoldered to its pad.

The receiver was then shrouded in clear heat-shrink tubing ......

... and positioned below the battery cradle. It was a tight fit, but the space was designed for it.

The output lead from the slide switch was then soldered to the power lead for the receiver and another two red wires attached (one for the Seeeduino board and the other for the servo).


 The soldered connections were then shrouded in heat shrink tubing.

Pins 0, 3 and 6, and the VIN and GND terminals were tinned on the underside of the Seeeduino board,

One of the black leads was soldered to the GND terminal and a read lead soldered to the VIN terminal.

The servo mount was prepared ........


.... and a servo attached to it using 1.4mm diameter self tapping screws, ........

..... which I had obtained (very cheaply) as part of a set from eBay.

 A 40mm length of 1.5mm diameter brass wire (or rod) was inserted into a hole in the actuating arm of the servo and bent to prevent it from working loose.

The servo mount was then slotted into its housing, and.......

....... the white (signal) lead from the servo soldered to Pin 6 on the Seeeduino board. At the same time, a yellow lead was soldered to Pin 3 (for the LED) and the brown lead from the negative output of the receiver was soldered to Pin 0.

The red and black leads from the servo were soldered to the last remaining red and back leads from the switch and BMS board.

The slack in the leads was then carefully taken up by routing them behind the USB port on the Seeeduino board and coiling them in the void behind the servo.

A 16340 li-ion cell was then inserted into the battery compartment ......

..... and the switch turned on to check whether everything was functioning as expected - i.e., when the A button on the transmitter was pressed, the servo responded.

So far, so good! I was now ready to attach the signal.

The correct sized socket was selected (because the base of each signal varies slightly dependent on how well, or badly, it was planed). In the event, a piece of 1mm thick plasticard had to be inserted to take up a small amount of slack.

The end of the brass rod from the servo linkage was bent and inserted into the hole in the end of the balance arm. Finding the correct length was a case of trial and improvement. None of my signals is of a uniform design, and so some tweaking was required. This also included tweaking the uplim and lowlim constants in the Arduino code to fine-tune the travel of the servo arm.

The end of the ladder was trimmed to line-up with the ends of the U shaped copper bracket (see above) and the ends filed to remove any paint, ready for tinning with solder.

The legs of the copper bracket were then soldered to the base of the ladder.

The yellow lead from the LED output pin of the Seeeduino was threaded through a hole drilled near the base of the signal post, as was the aerial lead.

The yellow lead was soldered to the end of the copper tape (self adhesive slug barrier tape) which was attached to the signal post during construction (see How I constructed 19 semaphore signals)

The end of the aerial was taped to the base of the signal post with 'invisible' Sellotape.

A 150R resistor was soldered to the positive leg of the LED in the signal lamp and the shrouded in black heatshrink tubing.

A short length of black hook-up wire was soldered to the negative lead of the LED and shrouded in black heatshrink tube. The end of the lead was then soldered to the loop at the top of the ladder.

And the end of the resistor on the positive lead was soldered to the upper end of the copper tape. Thus the LED was connected to the negative output from the battery (via the ladder) and the positive output from Pin 3 on the Seeeduino board.

The unit was turned on and the LED function tested by holding transmitter button A down for three seconds to turn the LED on and off.

The final touch was to paint the top of the baseplate with black acrylics and touch-up various unpainted parts (eg the base of the ladder and the soldered joints on the copper strip) with black or white acrylics and the signal was ready to go.


NOTE: I hadn't realised, when ordering, that the servos come as left or right handed and so, rather than redesigning the baseplate to accommodate the slight differences, I simply invert the right-handed servos. This required a rewrite of the Arduino code. The video above was taken before I rewrote this code.

#include <Servo.h> //Loads the commands to control servos
Servo myservo; //Names the servo we shall be using


//variables
bool sigstate=1; //Logs the position of the signal (1=raised, 0 = lowered)
int x=0; //Variable used in various For.... loops
int z; //Ditto
float y; //Ditto
int uplim=60; //Sets the upper limit for the servo (0 to 180 degrees) - Change as required
int lowlim=150; //Sets the lower limit for the servo (0 to 180 degrees) - Change as required
bool ledState = true;     // Variable to store the current state of the LED (Set initially to ON)
unsigned long triggerStartTime = 0;  // Stores the time when the trigger pin was first held LOW
unsigned long triggerEndTime = 0; // Stores when the trigger pin is no longer LOW

//Constants
const int ledpin=3; //Arduino pin to which the LED is connected
const int trigpin=0; //Pin to which the receiver's trigger signal is received
const int sigpin=6; //Pin to which the servo is attached
const unsigned long holdTime = 2000;  // Time in milliseconds that the pin must be held low for LED to be turned on or off (ie 2 seconds)


void setup() { //Start of the setup instructions
 myservo.attach(sigpin); //Connect a servo to the signal pin
 myservo.write(uplim); //Raise the signal
 pinMode(trigpin,INPUT_PULLUP); //Set the trigger input pin to normally HIGH
 pinMode (ledpin,OUTPUT); //Set the LED pin as an output
 digitalWrite(ledpin, ledState);  // Turn on the LED
} //End of set up instructions

void loop() { //Main program loop
   if (digitalRead(trigpin) == LOW) { //Check if the trigger input is LOW
    if (triggerStartTime == 0) { //If the start time hasn't already been set .....
      triggerStartTime = millis();  // ... set it to record the when the pin first went LOW
    }
 
    do {
      triggerEndTime=millis();
   }while (digitalRead(trigpin)==LOW); //Keep checking the trigger pin is LOW
       if (triggerEndTime - triggerStartTime >= holdTime) {   // Check if the trigger pin has been held low for the required time
      ledState = !ledState;   // If so, toggle the LED state (from on to off or vice versa)
      digitalWrite(ledpin, ledState); //Turn on or off the LED
      triggerStartTime = 0; //Reset the start time ....
      triggerEndTime =0;      // ..... and the end time to zero
    } 
  
  else { //If the trigger pin hasn't been held low for the required time, then .....
    triggerStartTime = 0; //Reset the start ....
    triggerEndTime =0;  //.... and end times to zero
 if (sigstate==1){ //If the signal is raised ......
    lowerit(); // .... then carry out the lowering sub routine
    sigstate=0; //And set the state of the signal to lowered
    delay (500); // Wait half a second
  }
else {  //If the signal isn't raised ......
  raiseit(); //...jump to the raising signal sub routine
  sigstate=1; //Set the signal state flag to raised
  delay (500); //Wait half a second
}
}
}
}//End of main loop

void raiseit(){ //Start of signal raising sub rountine
  x=lowlim; //Set x to the lower limit of the servo
  do {
    delay (10); //wait ten milliseconds
    myservo.write(x); //Set the servo to x degrees
    x--; //Decrease x by one degree
  } while (x>uplim); //Keep doing the above until the upper limit is reached

for(z=uplim;z<uplim+40;z++) { //First bounce
  myservo.write(z);
  delay(5);
}
for (z=uplim+40;z>uplim;z--){ //Return to the upper limit
  myservo.write(z);
  delay(5);
}
for(z=uplim;z<uplim+40;z++) { //Second bounce
  myservo.write(z);
  delay(10);
}
for (z=uplim+40;z>uplim;z--){ //Return to the upper limit
  myservo.write(z);
  delay(10);
}
  myservo.write(uplim); //Double check the signal is at the upper limit (not really necessary)
} //End of the signal raising sub routine

void lowerit(){ //Start of the signal lowering sub routine
  x=100; //Initially set x to 100
  y=1; //Initially set y to 1
  z=uplim; //Initially set z to the upper limit
do {
  myservo.write(z); //Set the servo position to z
  delay(x); //Wait for x milliseconds
  x=x-y; //Decrease x by y milliseconds
  if (x<0){x=5;} //Makes sure x doesn't become negative
  y=y+0.5; //Exponentially increase the value of y (to gradually decrease the delay between each drop of a degree)
  z++; //Increase z by one degree
} while (z<lowlim); //Keep doing the above until the lower limit is reached

for(z=lowlim;z>lowlim-20;z--) { //First bounce
  myservo.write(z);
  delay(5);
}
for (z=lowlim-20;z<lowlim;z++){ //Return to lower limit
  myservo.write(z);
  delay(5);
}
for(z=lowlim;z>lowlim-10;z--) { //Second bounce
  myservo.write(z);
  delay(5);
}
for (z=lowlim-10;z<lowlim;z++){ //Return to lower limit
  myservo.write(z);
  delay(5);
}
myservo.write(lowlim); //Double checks the servo has reached the lower limit
} //End of lower signal sub routine

It will take me a while to motorise all my signals. So far, I have only three completed - each one is taking me around a day - but that is because I am also in the process of tweaking and refining the coding and design of the baseplate. Hopefully, once this has been done I will be able to speed-up the process.

 

The .STL file for the baseplate can be downloaded FOC from - https://gardenrails.org/forum/viewtopic.php?t=15008