Change internal logic from relays to an Arduino
The setup() Function
In Listing 1, lines 1-17 comprise the Arduino setup
function. In this section, you tell the Arduino what you want each pin to do, set up and turn on any special functions, and define initial variables (although that's not needed in this particular code).
Listing 1
Buzzwire
01 void setup() { 02 // put your setup code here, to run once: 03 pinMode ( 2 , INPUT_PULLUP ); 04 pinMode ( 3 , INPUT_PULLUP ); 05 06 pinMode ( 8 , OUTPUT ); 07 pinMode ( 9 , OUTPUT ); 08 pinMode ( 10 , OUTPUT ); 09 pinMode ( 11 , OUTPUT ); 10 11 digitalWrite ( 8, HIGH ); 12 digitalWrite ( 9, HIGH ); 13 digitalWrite ( 10, HIGH ); 14 digitalWrite ( 11, HIGH ); 15 16 Serial.begin(19200); 17 } 18 19 void loop() { 20 // put your main code here, to run repeatedly: 21 int iInput2 = 0; // Wand 1 22 int iInput3 = 0; // Wand 2 23 int iOut1State = 0; // General output for Hand 1 24 int iOut2State = 0; // General output for Hand 2 25 int iMotorReset1 = 0; // Tracks if motor 1 has been shut off due to long contact 26 int iMotorReset2 = 0; // Tracks if motor 2 has been shut off due to long contact 27 unsigned long ulReset1 = 0; // Time in millis to turn off the Hand 1 buzzer and light 28 unsigned long ulReset2 = 0; // Time in millis to tunr off the hand 2 buzzer and light 29 unsigned long ulMotor1 = 0; // Time in millis to turn off the hand 1 motor for long contact 30 unsigned long ulMotor2 = 0; // Time in millis to turn off the hand 2 motor for long contact 31 32 while ( 1 ) 33 { 34 iInput2 = digitalRead ( 2 ); // Are we touching the hand 1 puzzle? 35 iInput3 = digitalRead ( 3 ); // Are we touching the hand 2 puzzle? 36 37 if ( iInput2 == LOW ) // If we're touching the hand 1 puzzle... 38 { 39 ulReset1 = millis() + 500; // Set a timer for 1 second from now 40 if ( ulMotor1 == 0 ) ulMotor1 = millis() + 10000; // Set a timeout for 10 seconds from now 41 if ( iMotorReset1 == 0 ) { 42 Serial.println ( "Motor On" ); 43 digitalWrite ( 8 , LOW ); // Turn on the motor if it hasn't been disabled 44 digitalWrite ( 9 , LOW ); // Turn on the light 45 } 46 47 } 48 else // We're not touching the puzzle 49 { 50 iMotorReset1 = 0; // Clear the timeout flag 51 //ulMotor1 = 0; // Clear the timeout timer 52 Serial.println ( "Motor Off" ); 53 } 54 55 if ( iInput3 == LOW ) // Same as above but hand 2 56 { 57 ulReset2 = millis() + 500; 58 if ( ulMotor2 == 0 ) ulMotor2 = millis() + 10000; 59 if ( iMotorReset2 == 0 ) { 60 digitalWrite ( 10 , LOW ); 61 digitalWrite ( 11 , LOW ); 62 } 63 } 64 else 65 { 66 iMotorReset2 = 0; 67 //ulMotor2 = 0; 68 } 69 70 if ( millis() > ulMotor1 && ulMotor1 != 0 ) // If current time (millis) is greater than the motor timeout AND we're watching for a timeout... 71 { 72 digitalWrite ( 8 , HIGH ); // Turn off the motor 73 digitalWrite ( 9 , HIGH ); 74 iMotorReset1 = 1; // Set the reset flag so we don't turn it on again 75 Serial.println ( "Motor Timeout" ); 76 } 77 78 if ( millis() > ulMotor2 && ulMotor2 != 0 ) 79 { 80 digitalWrite ( 10 , HIGH ); 81 digitalWrite ( 11 , HIGH ); 82 iMotorReset2 = 1; 83 } 84 85 if ( millis() > ulReset1 ) // Is it time to reset the buzzer / light? 86 { 87 digitalWrite ( 8 , HIGH ); // If so turn them off 88 digitalWrite ( 9 , HIGH ); 89 ulMotor1 = 0; 90 } 91 92 if ( millis() > ulReset2 ) 93 { 94 digitalWrite ( 10 , HIGH ); 95 digitalWrite ( 11 , HIGH ); 96 ulMotor2 = 0; 97 } 98 } 99 }
The pinMode
functions (lines 3-9) set the Arduino input and output status. The first argument is the pin number, and the second argument is the mode. I use INPUT_PULLUP
for pins 2 and 3, which are where the wires from the handles come in. A PULLUP
mode tells the Arduino to turn on the pull-up resistor of a GPIO pin.
A pull-up resistor is the electronic equivalent of a default value. It uses a weak resistance to force the pin to V+. An incoming signal can literally overpower the resistor and pull it to ground. Without the pull-up resistor, electrical noise can easily make the circuit operate sporadically. Because in this case the wiring would be connected to nothing when not touching the puzzle, the pull-up setting provides a solid change of state.
Arduino pins 8-11 (lines 6-9) are set to OUTPUT
. In this mode the pin will be either 5V or ground depending on its state. The digitialWrite
functions in lines 11-14 set the initial states of the relays.
All states are set to HIGH
to turn the relays off. Most relay modules you order online (I got mine from Amazon) operate in an inverted state. That is, a ground signal will turn them on and anything above a threshold level (usually about 1.2V) will turn them off. This setup makes them compatible with either 3.3V or 5V microcontrollers. Because the decision is either grounded or not grounded, it doesn't matter how high the voltage goes over the threshold, as long as it is within the limits of the board.
The last line of the setup
section calls Serial.begin
, which turns on the built-in USB serial port on the Arduino. In this case, I'm only using it to send debug messages to the Arduino console, but for bigger projects you could also use it to communicate with a companion computer program.
The loop() Function
The rest of the program is the loop
section. The first thing I do here is define a number of variables, as shown in Table 3.
Table 3
Program Variables
Line No. | Type | Name | Description |
---|---|---|---|
21 |
Integer |
iInput2 |
Stores digitalRead value from first handle |
22 |
Integer |
iInput3 |
Stores digitalRead value from second handle |
23 |
Integer |
iOut1State |
Stores whether indicators should be on for first handle |
24 |
Integer |
iOut2State |
Stores whether indicators should be on for second handle |
25 |
Integer |
iMotorReset1 |
Tracks whether motor 1 has been turned off because the handle has been in contact with the puzzle too long |
26 |
Integer |
iMotorReset2 |
Tracks whether motor 2 has been turned off because the handle has been in contact with the puzzle too long |
27 |
Unsigned long |
ulReset1 |
Time in millis to turn off the first motor in normal operation |
28 |
Unsigned long |
ulReset2 |
Time in millis to turn off the second motor in normal operation |
29 |
Unsigned long |
ulMotor1 |
Time in millis to turn off the first motor because its been in contact with the puzzle too long |
30 |
Unsigned long |
ulMotor2 |
Time in millis to turn off the second motor because its been in contact with the puzzle too long |
The loop
function cycles infinitely, but I don't want to redefine all of my variables on every pass, so after I finish declaring my variables at the top of the section, I start another infinite (while
) loop in line 32 that runs the rest of the code.
The digitalRead
functions in lines 34 and 35 determine whether the handle is touching the puzzle. This state is stored in iInput2
and iInput3
.
Touching the Puzzle
If the handle is touching the puzzle (i.e., the value of iInput2
in line 37 is LOW
), lines 38-47 whip into action. Line 39 uses millis
(see the "Electrical Noise" box) and adds 500 to get a time half a second in the future. This value is stored in ulReset1
. Line 40 does the same thing for ulMotor1
if it is currently zero (not set). It also sets its time for 10 seconds (10,000ms) in the future instead of half a second.
Electrical Noise
One of the things I discovered when reworking the exhibit was that the motors that provided the handle vibration caused more electrical noise than expected. The Arduino was sensitive enough to pick up this noise as a touch, whereas the original relays did not. This new design challenge had to be overcome, and it was solved by adding a filter capacitor to each motor.
Filter capacitors can remove electrical noise that leaks back into your circuit. I described them to a coworker like this: Imagine you're at the local amusement park and you've just gotten some tasty snacks while you wait in a long line for the next roller coaster. Once you eat everything you still have all of your trash. If there's a trash can at the front of the line, you can deposit it there. Otherwise you're trying to hold onto all of your trash while you ride the roller coaster and it flies all over the park.
Filter capacitors are like the trash cans at the front of the line. They collect the extra unwanted bits while the stuff you want (electricity) goes through. Digital electronics are much more sensitive than relay coils, and this noise problem didn't exist in the original project, so the original designer didn't have to consider it.
The if
in line 41 checks to see whether the motor has already been shut off because the handle has been touching the puzzle for too long. If not, lines 43 and 44 use digitalWrite
to turn on the relays and, by extension, the motor, light, and buzzer.
« Previous 1 2 3 4 Next »
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Gnome 48 Debuts New Audio Player
To date, the audio player found within the Gnome desktop has been meh at best, but with the upcoming release that all changes.
-
Plasma 6.3 Ready for Public Beta Testing
Plasma 6.3 will ship with KDE Gear 24.12.1 and KDE Frameworks 6.10, along with some new and exciting features.
-
Budgie 10.10 Scheduled for Q1 2025 with a Surprising Desktop Update
If Budgie is your desktop environment of choice, 2025 is going to be a great year for you.
-
Firefox 134 Offers Improvements for Linux Version
Fans of Linux and Firefox rejoice, as there's a new version available that includes some handy updates.
-
Serpent OS Arrives with a New Alpha Release
After months of silence, Ikey Doherty has released a new alpha for his Serpent OS.
-
HashiCorp Cofounder Unveils Ghostty, a Linux Terminal App
Ghostty is a new Linux terminal app that's fast, feature-rich, and offers a platform-native GUI while remaining cross-platform.
-
Fedora Asahi Remix 41 Available for Apple Silicon
If you have an Apple Silicon Mac and you're hoping to install Fedora, you're in luck because the latest release supports the M1 and M2 chips.
-
Systemd Fixes Bug While Facing New Challenger in GNU Shepherd
The systemd developers have fixed a really nasty bug amid the release of the new GNU Shepherd init system.
-
AlmaLinux 10.0 Beta Released
The AlmaLinux OS Foundation has announced the availability of AlmaLinux 10.0 Beta ("Purple Lion") for all supported devices with significant changes.
-
Gnome 47.2 Now Available
Gnome 47.2 is now available for general use but don't expect much in the way of newness, as this is all about improvements and bug fixes.