Help with bitwise/byte "packing" please

TL:DR…longer explanation below if you want more details.
Given the structs below…how would you repack the data into an int for flash storage and to receive custom user defined colors and times? Or would you even mess with it at all? I know it is a bitwise thing but I haven’t used any bitwise operators and I’m a bit confused from what I have read so before I start my bitwise education I want to make sure it is something I should be doing.

typedef struct {
  byte startHr;
  byte startMin;
  byte endHr;
  byte endMin;
  byte lightMode;
} user_time_t;

typedef strut {
  byte red;
  byte blue;
  byte green;
  byte brightness;
  byte wait;
} user_color_t;

typedef struct {
  uint8_t version;
  user_time_t userDayNeutral;
  user_color_t userDayColor;
  user_time_t userNoGetUp;
  user_color_t userNoColor;
  user_time_t userAskGetUp;
  user_color_t userAskColor;
  user_time_t userCanGetUp;
  user_color_t userCanColor;
} config_data_t;

Long Version
I’ve been working on a new project I am calling the “Lemmy Light” which uses an internet button to light up an enclosure and tell our early waking little one if it is ok to get up or not. Lemmy Light is actually short for: “Please for the love the of all that is Holy Let Me sleep” Light. She is five so she loves the colors and rainbow effect I have setup for daytime and hasn’t woken us up before 7am since I put it in her room. I digress, I am using this project as an opportunity to make a fully functioning “product” with app and all so I am trying to do things correctly. I have defaults I have sunk into the code and want to allow for user config via an app, thinking Ionic or hoping I can just tweak the stock Particle app. I will be storing user config in flash using the method described here: How do you get user entered config data to firmware app?
and have read the storing Binary states in an int: Keeping Binary States with Integers but I have several bytes I want to pack into the struct I will store on flash and will be the data that my Particle.function receives to set custom user config (color and times). How would I go about this or is it even worth it?

Hi @LukeUSMC

I think you want a union in C which is way to say “this area of memory can be interpreted in different ways, as different datatypes.” Here is an example you can crib from (I did not check the byte order but you probably don’t care).

typedef union _allAccessParts_ {

   uint32_t uintpart32;

   struct {
     uint16_t mspart16;
     uint16_t lspart16;
   }hw;

   struct {

      uint8_t upperbyte;
      uint8_t midupperbyte;
      uint8_t midlowerbyte;
      uint8_t lowerbyte;

   } b;

} allAccess;

You will need to know what sizeof() returns for all the types you use so you can count bytes.

@LukeUSMC, the EEPROM emulation used by Particle are based on byte boundaries, not integer. So if you do any packing, it will need to be byte-based. The EEPROM commands , put() for example, can take any structure you define and it will store its bytes automatically for you.

The smallest entity you can store is a byte. So one way to do bit-packing is to use a byte or word (16 bits) as a bit field. You can then define a macro to access the bits:

#define BIT(n) (1 << n)

You can use #defines to identity each bit for easy reference.

You then use it to access the bits. That way, your access is the same, regardless of the size of the structure you’re accessing. You would write your code, for example, as:

#define  ENABLE_BIT  5

if (status & BIT(1)) {
   // Do something if bit 1 is set
} elseif (~status | BIT(2) {
   // Else, do something if bit 2 is cleared
} else  {
   // Set bits 1 and 2
   status |= BIT(1) | BIT(2)
   // Clear bits 0 and 4
   status &= ~(BIT(0) | BIT(4))
   // Toggle bit 5 
   status ^= BIT(ENABLE_BIT)
}

:wink:

What everyone said above. Plus, here’s an example of bit-width fields in C:

#include "Particle.h"

unsigned long lastCheck = 0;

typedef struct {
	byte startHr;
	byte startMin;
	byte endHr;
	byte endMin;
	byte lightMode;
} user_time_t1;

typedef union {
	struct {
		int startHr:5; // 0-23
		int startMin:6; // 0-60
		int endHr:5; // 0-23
		int endMin:6; // 0-60
		int lightMode:2; // ??? can be larger
	} parts;
	int value;
} user_time_t2;


void setup() {
	Serial.begin(9600);
}

void loop() {
	if (millis() - lastCheck > 10000) {
		lastCheck = millis();

		Serial.printlnf("sizeof(user_time_t1)=%u offsetof(user_time_t1, lightMode)=%u", sizeof(user_time_t1), offsetof(user_time_t1, lightMode));

		Serial.printlnf("sizeof(user_time_t2)=%u", sizeof(user_time_t2));

		user_time_t2 t;
		t.parts.startHr = 12;
		t.parts.startMin = 0;
		t.parts.endHr = 22;
		t.parts.startMin = 59;
		t.parts.lightMode = 2;
		Serial.printlnf("t.value=0x%x", t.value);

	}
}

Output:

sizeof(user_time_t1)=5 offsetof(user_time_t1, lightMode)=4
sizeof(user_time_t2)=4
t.value=0x80b76c
2 Likes

Thank you all! Since I have smart guys here…I have another question. Is there a way to not have to recreate this for each mode? The “userDayColor” being a reference/var of some sort? Otherwise I have the same (5) lines for each mode. There must be an easier way!

      this->s_user_config.userDayColor.red = red;
      this->s_user_config.userDayColor.blue = blue;
      this->s_user_config.userDayColor.green = green;
      this->s_user_config.userDayColor.brightest = brightness;
      this->s_user_config.userDayColor.speed = speed;

This is some code that I use for storing colors in one integer and still be able to access each component seperately.
You could use the alpha channel as your brightness.

/* Types required for RGB-LED control */
struct ARGB_COLOR {
	byte B;  // blue  channel
	byte G;  // green channel
	byte R;  // red   channel
	byte A;  // alpha channel to indicate RGB.control true (!= 0) / false (== 0)
};

union COLOR {
	unsigned int value;
	ARGB_COLOR argb;
};


// usage
COLOR col;
col.value = 0x80FF00FF;
int red = col.argb.R;

And for your “reference” question, you could use something like this

user_color_t* usrColor;

usrColor = &(this->s_user_config.userColorDay); // this can be passed as parameter 
usrColor->red = red; 

And using arrays (maybe in conjunction with enum for named indexing) you can put that in a loop too.

Soooo many possibilities.

2 Likes