Originally published by Oct 7, 2019
borkus- stock.adobe.com
Binary and boolean sometimes seem like buzzwords, particularly “binary,” but that’s just because people become so fond of this mode of thinking once they get into it. Hint: They’re the cool geeks with Ferraris.
At the heart of things, binary is a machine thing, and if you want to control machines, especially microcontrollers, then you have to dig into binary once in a while. Especially the Arduino Uno (atmega328p), which has only 2KB SRAM. It’s a lean system that requires you to be clever if you want it to run large programs. Storing large arrays in PROGMEM and EEPROM (flash) will only get you so far.
The question, “why not just use the decimal system?” is very relevant, and must first be answered. You’ll learn to love boolean logic more once you understand why the binary system was designed in the first place.
Consider a transistor, which may be either “ON” or “OFF” at any given time. That transistor can describe two values (2**1). However, what if you want to count a bit higher? Add another transistor, and you can count to 4 (2**2), three (2**3) let you count to 8, and so forth. For your learning pleasure, pop open a python shell, and copy-paste the following to see the possible states up to 8 bits / 1 byte:
1 2 3 4 |
for i in range( 1, 8+1 ): print("Possible states for %i bits => %i (%s)" % ( i, 2**i, bin((2**i-1)))) |
Which outputs:
1 2 3 4 5 6 7 8 9 10 |
Possible states for 1 bits => 2 (0b1) Possible states for 2 bits => 4 (0b11) Possible states for 3 bits => 8 (0b111) Possible states for 4 bits => 16 (0b1111) Possible states for 5 bits => 32 (0b11111) Possible states for 6 bits => 64 (0b111111) Possible states for 7 bits => 128 (0b1111111) Possible states for 8 bits => 256 (0b11111111) |
And up to 16 bits / 2 bytes:
1 2 3 4 |
for i in range( 9, 16+1 ): print( "Possible states for %i bits => %i (%s)" % ( i, 2**i, bin((2**i-1)))) |
Which outputs:
1 2 3 4 5 6 7 8 9 10 |
Possible states for 9 bits => 512 (0b111111111) Possible states for 10 bits => 1024 (0b1111111111) Possible states for 11 bits => 2048 (0b11111111111) Possible states for 12 bits => 4096 (0b111111111111) Possible states for 13 bits => 8192 (0b1111111111111) Possible states for 14 bits => 16384 (0b11111111111111) Possible states for 15 bits => 32768 (0b111111111111111) Possible states for 16 bits => 65536 (0b1111111111111111) |
It’s impossible to count 65,535 Pokemons (including the starting score of 0) using only 16 fingers and toes, using only the decimal system. But, with binary, we can!
If it seems obscure, don’t be annoyed. Think of each binary digit as a transistor: 1 is “ON” and if 0 is “OFF.” To describe the number 255 using the decimal system would require 256 transistors (including 0), whereas with the binary system, we can describe 256 states with just eight transistors. It’s “0b11111111,” eight bits to one byte. It saved a lot of space in the early computer labs, where transistors weren’t tiny or effective (have you ever seen a vacuum tube?).
This is how our tablets and smartphones counted the number of Pokemons we caught when playing Pokémon Go. There’s no upper limit except memory.
When using Arduino, we have the “byte” data type (really uint_8t) to tell the compiler that we want a value in the range 0-255. However, boolean variables are much easier! Boolean variables can be represented with just one transistor, where true equals “ON,” and false equals “OFF.” You might have a tiny little state machine to determine if your motor is running, and declare it globally, before the setup() function:
1 2 3 |
bool is_motor_running = false ; // or 0 |
When the motor is turned on, you’ll want to update that, with
1 2 3 4 |
// flip it, else use true/1 bool is_motor_running = ! is_motor_running ; |
You can use this to control the behavior of your system, if, for instance, your Arduino shouldn’t do anything else while the motor is running.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
while ( 1 ) { if ( is_motor_running ) { // It does indeed run! // If something-something, check stuff. // Is motor still running? if( ! is_motor_running ) break ; delay( 100 ) ; } // It's not running, do something! else if ( ! is_motor_running ) { Serial.println( F( "*Mumble*mumble* Motor inactive..." ) ) ; // Do something, anything } } |
You’ve probably noticed the “!,” which is the logical negation operator. It means “NOT” in human-speak. The relational operators available in Arduino C++ are “==,” “!=,” “>=,” “<=,” “>,” but today is all about the boolean logic. Concerning ourselves with the relationship between variables isn’t relevant (such as determining if one value is greater than another), since the logical operators “!,” logical negation, “&&,” logical AND, and “||,” logical OR are more than enough to create AWESOME complex expressions. Though sometimes called binary operators, let’s not stray from the path.
If you’re feeling up to it, have a look at more advanced logic.
Following the binary bits above, let’s use the logical operators in a practical manner. We do use the Arduino IDE, see this for a quick introduction.
Now, let’s count to “1” in binary!
1 2 3 4 5 6 |
byte Sum = 0 bool Transistor1 = true ; if ( Transistor1 ) Sum = 1 else if ( ! Transistor1 ) Sum = 0 |
It’s unimpressive but very powerful. Let’s make a longer compound expression!
[ begin Boolean_logical_operators.ino ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
/* Try changing the values of t1, t2, t3, t4 to various combinations of true/false. 16 possible states (4**2), decimal 0 to 15, binary 0b0000 to 0b1111. */ // 0b1010 bool t1 = true ; // Transistor 1: it's on! bool t2 = false ; // Transistor 2: it's off! bool t3 = true ; // Transistor 3: it's on! bool t4 = false ; // Transistor 4: it's off! bool did_serial_entry = false ; // Did user input data? const byte targetSum = 0b0111 ; // 7 void setup( void ) { Serial.begin( 9600 ) ; Serial.setTimeout( 2500 ) ; // Timeout in milliseconds } void loop( void ) { byte Sum = 0 ; /* Try changing the values of t1, t2, t3, t4 to various combinations of true/false. 16 possible states (4**2), decimal 0 to 15, binary 0b0000 to 0b1111. */ Serial.println( F( "[!] Enter binary in range '0 0 0 0' to '1 1 1 1' and hit ENTER" ) ) ; // Input over Serial Monitor? Press CTRL+SHIFT+M to open it. // Note that Serial.available() is 'true' if any serial input // is in buffer... Is '> 0' really necessary here? if( Serial.available() > 0 ) { t1 = Serial.parseInt() ; t2 = Serial.parseInt() ; t3 = Serial.parseInt() ; t4 = Serial.parseInt() ; // Flush serial buffer while( Serial.available() ) Serial.read() ; Serial.print( "[!] Got \"" ) ; Serial.print( t1 ) ; Serial.print( " " ) ; Serial.print( t2 ) ; Serial.print( " " ) ; Serial.print( t3 ) ; Serial.print( " " ) ; Serial.print( t4 ) ; Serial.println("\"") ; // Keep state, only print MINIGAME if true did_serial_entry = true ; } else did_serial_entry = false ; // 0b0000 if ( ! t1 && ! t2 && ! t3 && ! t4 ) Sum = 0 ; // 0b0001 else if ( ! t1 && ! t2 && ! t3 && t4 ) Sum = 1 ; // 0b0010 else if ( ! t1 && ! t2 && t3 && ! t4 ) Sum = 2 ; // 0b0011 else if ( ! t1 && ! t2 && t3 && t4 ) Sum = 3 ; // 0b0100 else if ( ! t1 && t2 && ! t3 && ! t4 ) Sum = 4 ; // 0b0101 else if ( ! t1 && t2 && ! t3 && t4 ) Sum = 5 ; // 0b0110 else if ( ! t1 && t2 && t3 && ! t4 ) Sum = 6 ; // 0b0111 else if ( ! t1 && t2 && t3 && t4 ) Sum = 7 ; // 0b1000 else if ( t1 && ! t2 && ! t3 && ! t4 ) Sum = 8 ; // 0b1001 else if ( t1 && ! t2 && ! t3 && t4 ) Sum = 9 ; // 0b1010 else if ( t1 && ! t2 && t3 && ! t4 ) Sum = 10 ; // 0b1011 else if ( t1 && ! t2 && t3 && t4 ) Sum = 11 ; // 0b1100 else if ( t1 && t2 && ! t3 && ! t4 ) Sum = 12 ; // 0b1101 else if ( t1 && t2 && ! t3 && t4 ) Sum = 13 ; // 0b1110 else if ( t1 && t2 && t3 && ! t4 ) Sum = 14 ; // 0b1111 else if ( t1 && t2 && t3 && t4 ) Sum = 15 ; Serial.print( "Sum (DEC) = " ) ; Serial.println( Sum, DEC ) ; Serial.print( "Sum (BIN) = " ) ; Serial.println( Sum, BIN ) ; // MINIGAME // Did user enter data? if( did_serial_entry ) { if ( Sum > targetSum || Sum < targetSum ) // Or '!=' Serial.println( F( "[!] You swing and you miss! Try again!" ) ) ; if ( Sum < targetSum ) Serial.println( F( "[!] HINT! Go higher ..." ) ) ; else if ( Sum > targetSum ) Serial.println( F( "[!] HINT! Go lower ..." ) ) ; else if ( Sum == targetSum ) { Serial.println( F( "[!] You win!" ) ) ; for ( int it = 0 ; it < 3 ; it ++ ) { for ( int it2 = 0 ; it2 < 25 ; it2 ++ ) { Serial.print( ";) " ) ; delay( 25 ) ; } Serial.println() ; } // *Celebratory pause* delay( 2500 ) ; } } delay( 2500 ) ; } |
[ end Boolean_logical_operators.ino ]
Hit CTRL+SHIFT+M to pop open the serial monitor, and input a 4-bit value, delimited by space. Something like “1 0 1 0” will do nicely. Then, press “Send.” You’ll see output like the following:
If you’re the lucky one, favored by the rising sun:
This Arduino sketch demonstrates many uses of boolean logic. Wherever the truth must be found, or its opposite, we’re using boolean logic.
The example in 2.1 is a toy, created only to demonstrate logical operators. This, however, is a useful state-tracking relay module, for you to modify and tinker with. Copy the code into a new sketch in your Arduino IDE, and upload it with CTRL+U. Then, view the serial monitor with CTRL+SHIFT+M (or “python3 -m serial.tools.miniterm” on Linux/MacOS, TeraTerm/Putty on Windows). If you input “0” and send it, you can view current relay states, either “ON” or “OFF.”
If you input “1,” you’ll switch relay 1, “ON” if it was “OFF” and “OFF” if it was “ON.” Sending “2” will accomplish the same thing for relay 2. For both relays, state is kept with “bool” variables (“relay1State” and “relay2State”), and for each relay, an LED will light up if it’s indeed “ON.”
It’s not necessary to have anything except an Arduino to use the program. Play before you make!
The code below is written specifically to make you comfortable with using booleans in Arduino IDE.
On the hardware side of things, I’ve never trusted those blue 5V relays for heavy loads, but they’re confirmed good up to ~200W. Enjoy your making!
2x 2N7000 N-channel MOSFET | https://www.newark.com/on-semiconductor/2n7000/n-channel-mosfet-60v-200ma-to/dp/58K9650 |
2x ROHM SLR343BC4TT32 3mm blue LEDs | https://www.avnet.com/shop/us/products/rohm/slr343bc4tt32-3074457345627700657?CMP=EMA_ECIA_inventoryfeed_VSE?aka_re=1 |
2x BAT86 schottky diode | https://www.newark.com/search?st=bat86%20schottky%20diode |
Arduino Uno or Arduino Nano | https://store.arduino.cc/arduino-uno-rev3 |
2x 4.7 kiloohms + 2x 470 ohms resistors | https://www.newark.com/multicomp/cfr0w4je006kil/resistor-kit-carbon-film-axial/dp/24M1011 |
Breadboard | https://www.newark.com/twin-industries/tw-e41-1020/breadboard-solderless-830-tie/dp/56T0251 |
Dupont wires | https://www.newark.com/adafruit/759/kit-contents/dp/88W2571 |
2x 5V relays | https://www.newark.com/omron-electronic-components/g5le-1a4-dc5/relay-spst-no-250vac-30vdc-10a/dp/83F5375 |
Connect everything as shown in the diagram below; first on breadboard, then on protoboard. That’s how we do it.
The only gotcha is getting the BAT86 schottky diodes right. You must have the cathodes (black ring on BAT86, usually a white ring) facing the positive terminals on the relays (the right side in the image above), or there will be problems (short circuit). The ring marks the cathode (k); make sure it’s lined up to the positive terminals! See this picture, zoom in on the blue relays:
Here’s a picture of the assembled build. The extra things on the protoboard with the Arduino Nano aren’t in the scope of this article, and are also wildly uninteresting.
!prettyStateMachine, is it true?
You can view the code below:
[ begin prettyStateMachine.ino ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
/* Toggle relays on/off relative to their previous states. It's boolean! */ const byte relay1LED = 4 ; // D4 const byte relay1Pin = 6 ; // D6 const byte relay2LED = 8 ; // D8 const byte relay2Pin = 10 ; // D10 bool relay1State = false ; // or 0, 'OFF' bool relay2State = false ; // or 0, 'OFF' byte buffer = 0 ; void setup( void ) { Serial.begin( 9600 ) ; Serial.setTimeout( 500 ) ; pinMode( relay1LED, OUTPUT ) ; pinMode( relay1Pin, OUTPUT ) ; pinMode( relay2LED, OUTPUT ) ; pinMode( relay2Pin, OUTPUT ) ; } void loop( void ) { Serial.println( F( "[!] 0=show states\n[!] 1=flip relay 1\n[!] 2=flip relay 2" ) ) ; Serial.println( F( "[?] Input: " ) ) ; buffer = Serial.parseInt() ; // Returns 0 on timeout switch( buffer ) { case 0: Serial.print( F( "[!] Relay 1 => " ) ) ; if ( relay1State ) Serial.println( "ON" ) ; else if ( ! relay1State ) // 'else' is enough Serial.println( "OFF" ) ; Serial.print( F( "[!] Relay 2 => " ) ) ; if ( relay2State ) Serial.println( "ON" ) ; else if ( ! relay2State ) // 'else' is enough Serial.println( "OFF" ) ; break ; case 1: if( relay1State ) { digitalWrite( relay1Pin, LOW ) ; digitalWrite( relay1LED, LOW ) ; //relay1State = false ; // OK relay1State = ! relay1State ; // Better } else if ( ! relay1State ) { // 'else' is enough digitalWrite( relay1Pin, HIGH ) ; digitalWrite( relay1LED, HIGH ) ; //relay1State = true ; // OK relay1State = ! relay1State ; // Better } break ; case 2: if( relay2State ) { digitalWrite( relay2Pin, LOW ) ; digitalWrite( relay2LED, LOW ) ; //relay2State = false ; // OK relay2State = ! relay2State ; // Better } else if ( ! relay2State ) { // 'else' is enough digitalWrite( relay2Pin, HIGH ) ; digitalWrite( relay2LED, HIGH ) ; //relay2State = true ; // OK relay2State = ! relay2State ; // Better } break ; } delay( 2000 ) ; } |
[ end prettyStateMachine.ino ]
Now, we have state tracking, i.e., we can view current relay states, and can flip relays at will. We’ve covered compound expressions with logical operators, and you can use this code as a template for future projects.
Greenhouse hydroponics controller with RTC, HIGHLY OPTIMIZED indoor temperature controller with DHT22, and DS18B20 arrays on wireless sensors, or even driving an HVAC smartly: You define the behavior of your tech!
Learn more about the powerful capabilities of Arduino: