Keeping Binary States with Integers

With the help of a couple of forum topics, I’ve been able to piece together a simple way to keep state using the bits (0, 1) of an integer value. If you aren’t familiar with binary bits in a byte (or an integer), you can read up in the Binary number Wikipedia article. However, I’ll do a little explaining here, too! Be prepared, this is a little lengthy.

For example, let’s say we have a relay control board that can control 4 power outlets. Ideally, we’d like to keep track of the state of each of those outlets. Fortunately, an outlet is (or should be) either ON or OFF (true or false, 1 or 0). Got that? Now, one way to keep track of those states is to use 4 different variables, one for each state like so:

bool relay1 = true; // On
bool relay2 = false; // Off
bool relay3 = false; // Off
bool relay4 = true; // On

It’s pretty simple and intuitive. However, if you want to poll those states using the Spark Cloud, you’d have to expose all 4 variables and poll each one separately.

// https://api.spark.io/v1/your_core_id/relay1

Spark.variable("relay1", &relay1, BOOLEAN);

// https://api.spark.io/v1/your_core_id/relay2
Spark.variable("relay2", &relay2, BOOLEAN);

// https://api.spark.io/v1/your_core_id/relay3
Spark.variable("relay3", &relay3, BOOLEAN);

// https://api.spark.io/v1/your_core_id/relay4
Spark.variable("relay4", &relay4, BOOLEAN);

That’s a lot of lines of code (well, not really), but it’s a lot of polling. If it takes 1 second to poll each variable, that’s 4 seconds. That doesn’t sound too long of a time until you realize that it feels like a lot of time to most internet users. Don’t believe me? Read this article that Akamai published a few years ago. Yeah, you’ve lost your users. Ouch.

What if you could send the states for all 4 relays in a single request? Yeah, you could build a string to do that. But, if you can package those states into an integer, you could even send back those states when you call a function declared in Spark.function(). Now that is fancy!

Confused yet? Don’t worry, it’s not terrible. Consider your 4 relay states as 1’s and 0’s. Now, visualize them in a row, side-by-side. We’ll use the example above where relays 1 and 4 are on while the other two are off. Just look at them from 1 to 4: 1 0 0 1. Wait… That looks like the binary for the number 9! How about 1 and 4 are off and 2 and 3 are on: 0 1 1 0 = 6. No relays on: 0 0 0 0 = 0. All relays on: 1 1 1 1 = 15. All those relay states are stored inside a regular-looking integer that can be polled with Spark.variable() or returned in Spark.function(). Keep in mind, though, that binary bits go from right to left. The smallest value is always on the right.

But how do you implement such magic! Here’s a sample firmware to illustrate. I have omitted some of the setup for setting pin modes and digital writes to make sure everything is off.

// Some early function definitions.  These are what make the magic happen.
// Thanks to @bko for posting these in another thread!
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))

// Variable to hold the state of all 4 relays
// We will assume they are all initially off.
uint8_t relayStates = 0;

void setup() {
    // The function that will turn our relays on and off
    Spark.function("toggleRelay", toggleRelay);

    // Expose the relayStates variable for independent polling without having to call a function
    Spark.variable("relayStates", &relayStates, INT);

    // Set your pin modes, digital writes, and whatnot here
}

void loop() {
    // Intentionally empty
}


void toggleRelay(String command) {
    // Clean up our incoming command a little
    command.trim();
    command.toUpperCase();
    
    // All commands are "RELAY1ON", "RELAY1OFF", etc
    // These are set explicitly so there is no doubt as to whether
    // a relay will be on or off after you "toggle" it.
    // Relay 1 on
    if(command.equals("RELAY1ON")) {
        bitWrite(relayStates, 0, 1); // Write a binary 1 to the 0th (first) place in the relayStates integer

    // Relay 1 off
    } else if(command.equals("RELAY1OFF")) {
        bitWrite(relayStates, 0, 0); // Write a binary 0 to the 0th (first) place in the relayStates integer

    // Relay 2 on
    } else if(command.equals("RELAY2ON")) {
        bitWrite(relayStates, 1, 1); // Write a binary 1 to the 1st (second) place in the relayStates integer

    // Relay 2 off
    } else if(command.equals("RELAY2OFF")) {
        bitWrite(relayStates, 1, 0); // Write a binary 0 to the 1st (second) place in the relayStates integer

   // Repeat the same pattern for relays 3 and 4

    // If we did not like the command, return a "-1" indicating failure to the function caller
    } else
        return -1;

    // Now we loop through 4 bits in our integer to read back the states and tell digitalWrite() what to do
    // Please note that my relay is a little backwards, so I have to use a "!" before bitRead() to
    // set the proper state.
    for(uint8_t i=0; i<4; i++)
        digitalWrite(i, !bitRead(relayStates, i)); // Read the ith bit

    // Once everything is done, return the states of all 4 relays in a single integer!
    return relayStates;
}

Wasn’t that exciting?!

I know you’re thinking “Great, but how the heck do I manipulate it in my browser JavaScript?” We’re in luck, because JavaScript has a method that can take that integer and convert it into a string representation of the binary value. Once you make your Cloud call to toggleRelay(), take the integer returned (we’ll say it’s in response.return_value) and convert it using (response.return_value).toString(2). You can refer to the JavaScript number .toString() documentation for more information. Now, you can iterate through your string and toggle whatever buttons, icons, etc!

Here’s what my final app looks like with a couple of the relays toggled.

Yes, I put a temperature sensor on everything I can!

I hope this helps or inspires someone!

Note: As I was writing this, I discovered that it might be better to put relay 1’s state in the 4th bit instead of the first (the 0th). That should make it a little easier to iterate since much of society reads from the left to the right. I haven’t personally tested that version, so I did not include it. I will follow up soon to see how it went and report back any modifications.

5 Likes

+1

I used the same technique for storing state in my Game of Life that’s 128*64 square…

But interestingly enough, the motivation was different… Storing 8192 booleans takes 8k of RAM (40%)
http://docs.spark.io/#/firmware/data-types-boolean

So instead, I went with 1024 bytes and indexed them appropriately…Savings of x8 memory :smile:

Bravo!

+1

This is great! I’ve implemented something similar to keep track of my relay status, but I’m trying to update my status integer based off the values from digitalRead(). I came up with the following function, but it ain’t pretty. Does anyone know of a cleaner way to do this?

Relay state to status code mapping
0 = 0 0 0 0
1 = 0 0 0 1
2 = 0 0 1 0
3 = 0 0 1 1
4 = 0 1 0 0
5 = 0 1 0 1
6 = 0 1 1 0
7 = 0 1 1 1
8 = 1 0 0 0
9 = 1 0 0 1
10 = 1 0 1 0
11 = 1 0 1 1
12 = 1 1 0 0
13 = 1 1 0 1
14 = 1 1 1 0
15 = 1 1 1 1

void updateStatus(){
	if(digitalRead(STATION1) == HIGH && digitalRead(STATION2) == HIGH && digitalRead(STATION3) == HIGH && digitalRead(STATION4) == HIGH){
		stationStatus = 0;
	}
	if(digitalRead(STATION1) == HIGH && digitalRead(STATION2) == HIGH && digitalRead(STATION3) == HIGH && digitalRead(STATION4) == LOW){
		stationStatus = 1; 
	}
	if(digitalRead(STATION1) == HIGH && digitalRead(STATION2) == HIGH && digitalRead(STATION3) == LOW && digitalRead(STATION4) == HIGH){
		stationStatus = 2; 
	}
	if(digitalRead(STATION1) == HIGH && digitalRead(STATION2) == HIGH && digitalRead(STATION3) == LOW && digitalRead(STATION4) == LOW){
		stationStatus = 3; 
	}
	if(digitalRead(STATION1) == HIGH && digitalRead(STATION2) == LOW && digitalRead(STATION3) == HIGH && digitalRead(STATION4) == HIGH){
		stationStatus = 4; 
	}
	if(digitalRead(STATION1) == HIGH && digitalRead(STATION2) == LOW && digitalRead(STATION3) == HIGH && digitalRead(STATION4) == LOW){
		stationStatus = 5; 
	}
	if(digitalRead(STATION1) == HIGH && digitalRead(STATION2) == LOW && digitalRead(STATION3) == LOW && digitalRead(STATION4) == HIGH){
		stationStatus = 6; 
	}
	if(digitalRead(STATION1) == HIGH && digitalRead(STATION2) == LOW && digitalRead(STATION3) == LOW && digitalRead(STATION4) == LOW){
		stationStatus = 7; 
	}
	if(digitalRead(STATION1) == LOW && digitalRead(STATION2) == HIGH && digitalRead(STATION3) == HIGH && digitalRead(STATION4) == HIGH){
		stationStatus = 8; 
	}
	if(digitalRead(STATION1) == LOW && digitalRead(STATION2) == HIGH && digitalRead(STATION3) == HIGH && digitalRead(STATION4) == LOW){
		stationStatus = 9; 
	}
	if(digitalRead(STATION1) == LOW && digitalRead(STATION2) == HIGH && digitalRead(STATION3) == LOW && digitalRead(STATION4) == HIGH){
		stationStatus = 10; 
	}
	if(digitalRead(STATION1) == LOW && digitalRead(STATION2) == HIGH && digitalRead(STATION3) == LOW && digitalRead(STATION4) == LOW){
		stationStatus = 11; 
	}
	if(digitalRead(STATION1) == LOW && digitalRead(STATION2) == LOW && digitalRead(STATION3) == HIGH && digitalRead(STATION4) == HIGH){
		stationStatus = 12; 
	}
	if(digitalRead(STATION1) == LOW && digitalRead(STATION2) == LOW && digitalRead(STATION3) == HIGH && digitalRead(STATION4) == LOW){
		stationStatus = 13; 
	}
	if(digitalRead(STATION1) == LOW && digitalRead(STATION2) == LOW && digitalRead(STATION3) == LOW && digitalRead(STATION4) == HIGH){
		stationStatus = 14; 
	}
	if(digitalRead(STATION1) == LOW && digitalRead(STATION2) == LOW && digitalRead(STATION3) == LOW && digitalRead(STATION4) == LOW){
		stationStatus = 15; 
	}
}

Its a very classical use case for bit operations :smile:

void updateSatus() {
	stationStatus = ((digitalRead(STATION1) == HIGH ? 0 : 1)<<3) +
		((ditigalRead(STATION2) == HIGH ? 0 : 1)<<2) +
		((ditigalRead(STATION3) == HIGH ? 0 : 1)<<1) +
		(ditigalRead(STATION4) == HIGH ? 0 : 1);
}

Or even cleaner (though less understandable):

void updateSatus() {
	stationStatus =
		(digitalRead(STATION1) == HIGH ? 0 : 8) +
		(ditigalRead(STATION2) == HIGH ? 0 : 4) +
		(ditigalRead(STATION3) == HIGH ? 0 : 2) +
		(ditigalRead(STATION4) == HIGH ? 0 : 1);
}

Brilliant! Very clean indeed :slight_smile: thanks!

I actually find the second one more understandable. I’m still not sure what you did in the first one.

:slight_smile: no worries, let me try to explain.

All values are represented as sum of bits, as example, a single byte:
http://people.scs.carleton.ca/~armyunis/notes/datastorage_files/Byte.gif
The << operator is shifting/moving the bits of a value to the left.
digitalRead(STATION1) == HIGH ? 0 : 1 creates a high or low bit.
The << 3 moves this created bit to the 4th position - representing an 8 in decimal.

The result is basically exactly the same as
digitalRead(STATION1) == HIGH ? 0 : 8
just with better readability in terms of "which station represents which byte", as each
bit is explicitly moved to its spot.

At the end its just a matter of taste :wink:

Oh! I just was not familiar with the operator, I’ve almost never done bitwise operations.