OldRabbit
Published © LGPL

Pimp up a model-railways "wind-mill"

Servo, Stepper and some LED's mounted into a 1:120 model of an "old wooden corn-mill" - making the model to an "living" highlight.

IntermediateShowcase (no instructions)2,125
Pimp up a model-railways "wind-mill"

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Elegoo Stepper Motor
28BYJ-48 5V Stepper Motor + ULN2003 Motor Driver Board for Arduino
×1
APM on-off-on momentary switch single-contact
or comparable
×1
Hook Up Wire Kit, 22 AWG
Hook Up Wire Kit, 22 AWG
mainly red and blue
×1
LED (generic)
LED (generic)
I used one white and two orange LED*s
×3

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
for sensitive electronic works
Solder Wire, Lead Free
Solder Wire, Lead Free
Solder Flux, Soldering
Solder Flux, Soldering
PCB Holder, Soldering Iron
PCB Holder, Soldering Iron
Tape, Electrical Insulation
Tape, Electrical Insulation

Story

Read more

Schematics

Parts and connections.

To put the real components welded into the model, keep the wiring short and well insulated.

Code

Code for Arduino Nano

Arduino
The Arduino-code used in my model. Feel free to use and modify.
/*
 * "Servo-Angle-and-Stepper-Speed-controled-by-TimeData-Array-in-a-endless-loop"
 *
 * Keeps a 1:120 - "Windmill"-model moving. Turns the blades in slightly different speeds 
 *  and is moving the blades "into the wind".
 *  Additional lighting makes the model visible in "nights".
 *  
 *  A servo-motor will be handled to reach certain angles after certain times.
 *  Working with free fillable arrays TimeSteps[] in seconds and ServoAngles[] in 
 *  degrees between 0° and 180°.
 *  This will be done continously. After reaching last dataset, again start the first...
 *  The acting speed of the servo will be influenced in ServoSpeed[] array.
 *  Alternatively a momentary button is usable to shorten the times / switch 
 *  over from one servo-angle in the dataset to the next.
 *  
 *  All times the internal LED will be kept blinking 0,5s / 0,5s.
 *  Monitoring via serial monitoring on PC-screen (if connected).
 *  
 *  Project Windmill   by N.W.Petzold - 01-2020
 */
 #include <Servo.h>
 #include <Stepper.h>
// Important: Keep all times the amount of integer-values per array even to the other arrays! 
// Count it carafully!
// All Timesteps will be multipied with 1000 -later- to use command "millis".
// Example with 15 data (0-14 counting) and 15 the also reset-value.

int DataCounter = 0;
int ResetCountVal = 15; // This is the amount of datasets in the arrays, for automatic restart.
int TimeSteps[] =   {15,10,20,15,10,25,20,25, 10, 15, 30, 20,15,25,15}; // in seconds (millis will be calculated)
int ServoAngles[] = {90, 80,70,55,60,65,75,90,120,140,100,80,90,55,70}; // Dergrees between 0 and 180
long ServoSpeed[] = {30,50,40,30,40,50,60,70, 80, 90, 50, 30,35,40,50}; // delay between each degree in millis
int StepperSpeed[] ={30,40,30,30,30,40,50,40, 30, 30, 30, 30,45,40,40}; // value between 0 and 100
/*
 *  ATTENTION!
 *  Pin-Usage as follows:
 *  - Stepper: 2,3,4,5
 *  - LED's  : 6,7
 *  - Servo  : 10
 *  - Buttons: 11,12
 *  
 */
int ServoPin = 10;     // here is the servomotor data-line connected.
Servo myservo;       // create servo object to control a servo
//
const int stepsPerRevolution = 400;
Stepper myStepper(stepsPerRevolution, 2, 3, 4, 5); 
//
// Variable Values permanently changing if program runs:
// SERVO
unsigned long previousSERVOMillis = 0;
unsigned long SERVOinterval = 0;       // will be loaded with "TimeSteps"
unsigned long currentSERVOMillis = 0;
//
unsigned long previousAngleMillis = 0;
unsigned long AngleInterval = 0;       // will be needed in the "servohandler"
unsigned long currentAngleMillis = 0;
//
// Values for keeping the internal LED constantly blinking 
const int ledPin =  LED_BUILTIN;
int ledState = LOW;   
unsigned long previousLEDMillis = 0;
unsigned long currentLEDMillis = 0;
const long LEDinterval = 500; 
//
//
int counter = 0;
int buttonState;
// adapt in the arrays the amount of Buttons you want to use.
// Reset at "2": 0/1 = On / Off --> higher reset values makes more possible with one button!
// But than the %2 (modulo 2 - command) does not work in the button-subroutine
int lastButtonState[4];
int buttonPushCounter[4]; // how often was a certain keypad-key pushed... see serial monitor
int buttonToggling[4];    //array stores button-pushes as "numbers" 0...9 or higher as you programmed till "reset" value
int buttonCountRes[ ] = {4,3,2,2}; //  type in when the counter shall restart e.g. 2 for value [0] or 9 for value [3] leads to "0".
//
// Toggle-Buttons-visualisation
const int LED1 = 6;    // D6  // comment out, what you do not need
const int LED2 = 7;    // D7 
//
// Toggled momentarily push-buttons
const int Button1 = 11;    // D11
const int Button2 = 12;    // D12
//
// end of declarations
//
//
void ServoHandler(int NewAngle, int LastAngle, long AngleDelay){
    int AngleToBeSet;
    // 
    currentAngleMillis = millis();
    AngleInterval = previousAngleMillis + AngleDelay;       // will be loaded with "TimeSteps"
    if(currentAngleMillis >= AngleInterval){
        if (NewAngle > LastAngle) {LastAngle++; AngleToBeSet = LastAngle; Serial.print(DataCounter); Serial.print(".Step - Servo-Angle + : "); Serial.print(AngleToBeSet); Serial.print(" Wait: [sec.] "); Serial.println(TimeSteps[DataCounter]);}
        if (NewAngle < LastAngle) {LastAngle--; AngleToBeSet = LastAngle; Serial.print(DataCounter); Serial.print(".Step - Servo-Angle - : "); Serial.print(AngleToBeSet); Serial.print(" Wait: [sec.] "); Serial.println(TimeSteps[DataCounter]);}
        myservo.write(AngleToBeSet); 
        
    previousAngleMillis = currentAngleMillis;
} // end if...
} // end ServoHandler
//
//
void ButtonCounter(int buttonState1, int lastButtonState1, int buttonNo) {
  int x = 0;
  int counter = 0;
    // STATUS CHANGED
    if (buttonState1 != lastButtonState1) {
    if (buttonState1 == 1) {
        buttonPushCounter[buttonNo]++;
        if (buttonPushCounter[buttonNo] >= buttonCountRes[buttonNo]) {buttonPushCounter[buttonNo] = 0;}
      //delay(20);
      if (buttonPushCounter[0] == 0){digitalWrite(LED1,LOW); digitalWrite(LED2,LOW);} // Two LED's  : 1 off, 2 off
      if (buttonPushCounter[0] == 1){digitalWrite(LED1,HIGH);digitalWrite(LED2,LOW);} //              1 on   2 off
      if (buttonPushCounter[0] == 2){digitalWrite(LED1,LOW); digitalWrite(LED2,HIGH);} //              1 off  2 on
      if (buttonPushCounter[0] == 3){digitalWrite(LED1,HIGH);digitalWrite(LED2,HIGH);} // Reset is 4,  1 on   2 on
      //
      if (buttonPushCounter[0] == 0){Serial.println("LED1 Off / LED2 Off");}
      if (buttonPushCounter[0] == 1){Serial.println("LED1 ON  / LED2 Off");}
      if (buttonPushCounter[0] == 2){Serial.println("LED1 Off / LED2 ON");}
      if (buttonPushCounter[0] == 3){Serial.println("LED1 ON  / LED2 ON");}
      //
      if (buttonPushCounter[1] == 0){Serial.println("Servo and Stepper moving");}
      if (buttonPushCounter[1] == 1){Serial.println("Servo OFF Stepper moving");}
      if (buttonPushCounter[1] == 2){Serial.println("Servo OFF Stepper OFF");}
      //
    } // end if buttonstate ==1 ...
  } // end if buttonstate != ...
  lastButtonState[buttonNo] = buttonState1;
          
} // End of button handling sequence.     
//  
void setup() {
  // Internal LED
  pinMode(ledPin, OUTPUT);
  pinMode(LED1,OUTPUT);
  pinMode(LED2,OUTPUT);
  pinMode(Button1,INPUT);
  pinMode(Button2,INPUT);
  
  currentLEDMillis = millis();
  currentSERVOMillis = millis();
  
  // Servo-Motor
  pinMode(ServoPin, OUTPUT);
  myservo.attach(ServoPin);  // attaches the servo on pin 9 to the servo object
  //  
  // Buttons
  buttonState = 0;
  lastButtonState[0] = 0;
  lastButtonState[1] = 0;
  //
  // Serial Monitor
  Serial.begin(9600);
  Serial.println("Program started.");
  //
  DataCounter = 0;
} // end of "setup()"

void loop() {
  // put your main code here, to run repeatedly:
  // Buttons
  buttonState = digitalRead(Button1);
  ButtonCounter(buttonState, lastButtonState[0], 0);
  buttonState = digitalRead(Button2);
  ButtonCounter(buttonState, lastButtonState[1], 1);

          /* Change here the "usage of the lights" as you may need:
          *  for "on/off"-(TOGGLING)-only the "buttonCountRes" shall be 2.
          *  if (buttonToggling[0] == 0){digitalWrite(LED1,LOW);}
          *  if (buttonToggling[0] == 1){digitalWrite(LED1,HIGH);}
          *  ... and so one for the other buttons[1]...[3]
          */ 
  // Now go though Datasets: start at zero till "ResetCountVal"...
  currentSERVOMillis = millis();  // actual "running time" value
  //
    SERVOinterval = (TimeSteps[DataCounter]*1000);           // Value from Array will be prepared for "millis"-comparison
    if (currentSERVOMillis - previousSERVOMillis >= SERVOinterval) { // if interval is over, do something
        previousSERVOMillis = currentSERVOMillis;
        /* Servo handling:
         *  The servo handling has to be outside of this IF-loop! 
         *  Because here we do not act as fast as possible - with only one value to be set. 
         *  We act with variable speed with short break between each degree, till target-angle is reached.
         *  myservo.read(); gives the actual (written-)position on arduino output-pin.
         *  The target-position is in the array at actual counter position ServoAngles[DataCounter].
         *  The "speed" is the waiting time between the single servo steps to make the moving more or less slowly.
         */
        //
        // Next Dataset / Reset if end of array reached.
        DataCounter++;
        if (DataCounter>=ResetCountVal){DataCounter=0;}
  } // closing "if"
    // Blocking or Enabling Movement of SERVO...
        if (buttonPushCounter[1] == 0){  // if buttonPushCounter is not 1 or 2 Servo is moving
        ServoHandler(ServoAngles[DataCounter],myservo.read(),ServoSpeed[DataCounter]);
        }
// end of Servo handling
//
// STEPPER Handling
    // Blocking or Enabling Movement of STEPPER...
        if (buttonPushCounter[1] == 0 or buttonPushCounter[1] == 1) {  // if buttonPushCounter is 2 Stepper is moving
        myStepper.setSpeed(StepperSpeed[DataCounter]);
        myStepper.step(stepsPerRevolution / 100);
        }
// Stepper handling finished.
//
// Internal LED handling:
currentLEDMillis = millis();
        if (currentLEDMillis - previousLEDMillis >= LEDinterval) {
          previousLEDMillis = currentLEDMillis;
          // if the LED is off turn it on and vice-versa:
            if (ledState == LOW) {
              ledState = HIGH;
            } else {
              ledState = LOW;
            } // closing else...
        } // closing "if..."
        // set the LED with the ledState of the variable:
        digitalWrite(ledPin, ledState);
// internal LED handling finished
//
} // end of "loop()" routine

Credits

OldRabbit
0 projects • 1 follower
Contact

Comments

Please log in or sign up to comment.