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.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Perl: Arduino Project

    With a few lines of self-written firmware and a simple Perl script, Perlmeister Mike Schilli trains one of his Linux computers with a plugged in Arduino board to switch electrical equipment on and off using a relay. Enchanting.

  • Python’s Tkinter Library

    Use Tkinter to control your Rasp Pi projects from a smartphone or tablet.

  • Serial Communications

    We explore serial communications, from the electrical specs to protocols and libraries, with an example of serial communication with an Arduino.

  • Escape Room Puzzle

    A digital puzzle presents a challenge for young people in an escape room.

  • PySimpleGUI

    Use the same code for your Python GUI and web apps.

comments powered by Disqus
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.

Learn More

News