- Part 1: Mechanical Design Basics.
- Part 2: Balancing the Elevator.
- Part 3: Electronics (optocoupler, H-Bridge, and more).
- Part 4 (current): Software to control the Arduino.
In this last part, we will be describing the Arduino software that controls our Lego elevator. But, let’s first define the desired behavior. As we have described in the part dedicated to the electronics, our circuit provides two push-buttons. One is supposed to make the elevator go up, and the other is for going down. When no switch is pressed, the elevator should stand still. Besides, the elevator should also stop when it reaches the maximum or the minimum height. This behavior is demoed in Video 1, where we have made the elevator move an iPhone up and down upon request.
Video 1: Arduino Controlled Lego Elevator in Action
Now, let’s see the Arduino’s program that drives our elevator (available for download under a free software license). Every Arduino program has 3 parts: (1) variable and constant definitions, (2) a setUp( ) function, and (3) a loop( ) function. The setUp( ) function is run only once on startup, while the loop() function is run repeatedly until the Arduino is turned off. Now, let’s analyze the content of our program.
- Variable and constant definitions: We define in Listing 1 constants that represent the elevator speeds, and pins we’ll be using. This will make our program easier to understand. It also helps updating the program in case you want use different pins that we did. You only have to change the pin numbers here. There no variable definition, since our program does not need any.
Listing 1: Variable and Constant Definitions Section
- The setUp( ) function: This is the right place to set up variables and assign modes of the 6 pins used to drive and sense the elevator (see Listing 2). We also set up the PWM (Pulse Width Modulation) frequency to its highest value for pins that control the motor. The goal here is to avoid the buzzer effect. The explanation of this lies in the fact the speed of the motor is approximatively proportional the current and hence the voltage delivered to the H-Bridge by the Arduino. The Lego motor can go pretty fast. To make it rotate at a fraction of its maximum speed we use Arduino pins in analog mode where the output can be set between 0 (corresponds to 0 V) and 255 (corresponds to 5 V). The output signal is not a continuous voltage as one might expect. It is instead a sequence of pluses that switch back and forth between 0 V and 5 V. The Arduino output value defines the pulse width. The longer the pulse width is, the closer the average voltage is to 5 V. A consequence of using PWM is that the motor emits a high pitched sound. One can suppress it by introducing some electronics to turn the Arduino generated pulses into DC voltage. We have chosen to solve the problem using a software trick. That is simply make the pulse frequency higher than the maximum frequency that can be perceived by a human ear. This is achieved by the setPwmFrequency() function.
Listing 2: The setUp( ) function
- The loop() function: Here we decide what to do on every iteration (see Listing 3), that is deciding and triggering one out of four behaviors of the elevator: go down (goDown() function), go up (goUp() function), compensate for gravity (compensateGravity() function) or turn off the motor ( stopMotor() function). The decision to go down, go up, or compensate for gravity is obtained by dedicated functions (i.e. shouldGoDown(), shouldGoUp(), and shouldCompensateGravity()). Using functions with clear naming helps making the program easy to understand. We also increase clarity by reducing the amount of if/else blocks thanks to the use of return to end the current iteration. For example, if the elevator should go down, we return current iteration once the goDown() function is over. This acts like a break in a loop, since the loop() function is called repeatedly.
Listing 3: The loop( ) function
Functions goDown() and goUp() do basically the same thing. They set the output values of the two Arduino pins that drive the motor, to make the elevator either go down or up. As we have already mentioned, the decision to go up or down relies on functions shouldGoDown() and shouldGoUp(). Again, these functions perform similar tasks: each one analyzes states of a push-button (pressed or released) and a sensor for detecting either min or max height. For example, function shouldGoUp() answers true if the user presses the button for going up. But, it returns false if the maximum height sensor is pressed or if the up push-button is released.
The compensateGravity() function sets the motor speed to make the elevator go up, but at a small speed and torque. The goal here is to compensate for the gravity and make the elevator stand still at the level reached when releasing the control buttons. This speed is chosen empirically according to weight of the carried smartphone carried. The gravity cancellation behavior introduces oscillations when reaching the minimum height, while the go down push-button is still pressed. This is why we have introduced the shouldCompensateGravity() function that answers false in this situation. To avoid this undesirable effect, we activate the fourth behavior implemented by the stopMotor() function.