Combining HEX Digits

I'm testing out some code to determine how to manipulate the upper byte of a 4 byte number. I created this test but it's not displaying what I would expect.

    byte arr[] = { 0x80, 0x7F, 0xC0, 0x3F };

    unsigned long result = ((unsigned long)arr[0]<<24) | ((unsigned long)arr[1]<<16 ) | arr[2]<<8 | arr[3];

    Serial.println(result, HEX);

Displays "FFFFC03F" and I would have expected "807FC03F"

I plan on using this manipulation to pass the combined number to IRRemote. I need to test a series of codes so I need to loop through values of arr[0] (arr[1] is just ~arr[0] and arr[3]=~arr[2]) and then combine the bytes to pass to the IR send command.

I must be missing something obvious but I don't see it!

arr[2] is promoted to int then shifted left eight bits. int is signed.

Can you work out the rest?

You could try an UNION to do this, but why not just use an unsigned long directly?

unsigned long myVar = 0x807FC03F;

.

This works:

  unsigned long result = (unsigned long)arr[0]<<24 | (unsigned long)arr[1]<<16  | (unsigned long)arr[2]<<8 | (unsigned long)arr[3];

You're missing a cast

 unsigned long result = ((unsigned long)arr[0] << 24) | ((unsigned long)arr[1] << 16 ) | ((unsigned long)arr[2] << 8) | (unsigned long)arr[3];

The last cast (for arr[3]) is not necessary but added for consistency.

The alternative can be to make use of a union.

union IRCODE
{
  unsigned long val;
  byte bytes[4];
};

IRCODE ircode;

void setup()
{
  Serial.begin(115200);

  ircode.bytes[0] = 0x3F;
  ircode.bytes[1] = 0xC0;
  ircode.bytes[2] = 0x7F;
  ircode.bytes[3] = 0x80;
  
  byte arr[] = { 0x80, 0x7F, 0xC0, 0x3F };

  unsigned long result = ((unsigned long)arr[0] << 24) | ((unsigned long)arr[1] << 16 ) | ((unsigned long)arr[2] << 8) | (unsigned long)arr[3];

  Serial.println(ircode.val, HEX);
  Serial.println(result, HEX);
}

sterretje:
The alternative can be to make use of a union.

I'm thinking that will make the results compiler and processor dependent.

True, hence the "can be" :wink:

Thanks for the help. I'm trying the web IDE and perhaps that's causing some unexpected results. I ran your code with the casting and that does work. I was working on this last night and added code to loop through the possible codes but I'm having trouble with that also for some reason.

uint32_t lower_part = 0xC03F;
uint32_t codes[0xFF];
byte runXTimes = 1;
//byte arr[] = { 0x80, 0x7F, 0xC0, 0x3F };

void setup() {
  Serial.begin(115200);
  Serial.println("Begining the Code Now");
}

void loop() {
  if (runXTimes){
    for (uint32_t value = 0; value < 0xFF; value++) {
        uint32_t compliment = ~value;
        codes[value] = (value << 24) | (compliment << 16) | (lower_part);
        Serial.println(codes[value], HEX);
        }
        runXTimes--;
  }

//  unsigned long result = (unsigned long)arr[0]<<24 | (unsigned long)arr[1]<<16  | (unsigned long)arr[2]<<8 | (unsigned long)arr[3];
//  Serial.print("This is result: ");
//  Serial.println(result, HEX);

}

but I'm having trouble with that also for some reason.

That is barely more acceptable than "it doesn't work". Barely.

The code does something. You expect it to do something. You have explained neither one.

Sorry for the lack of details. I'm having 2 problems.

  1. I added the runXTimes flag so the code would only loop once. The code continually loops instead of only running once.

  2. When I break and look at the data sent to the terminal window I am getting this:

...

FF1BC03F
FF1AC03F
FF19C03F
FF18C03F
FF17C03F
FF16C03F
FF15C03F
FF14C03F
...

The top byte is FF instead of incrementally changing

  1. I added the runXTimes flag so the code would only loop once. The code continually loops instead of only running once.

I would have used a bool, initialized to true, and set to false in the body of the if statement.

I would look at Print::print() to see if there is an overload for uint32_t. I doubt that there is. So, you need to determine which close match method is actually invoked, to see if it is behaving correctly.

The online reference only states "val: the value to print - any data type" so that doesn't help me much to determine if an overload is occurring or not.

I was trying to use Putty (I'm running Win7) because the serial monitor in the Arduino IDE doesn't clear. Sometimes I get non-stop data streaming (in either Putty or Serial Monitor) but most time I don't get any output.

I initially had my board connected to a USB 3.0 hub and I thought that might be an issue so I switched to a USB2.0 port on the PC and am getting the same results. If I reset the board I can see it enumerate initially as COM15 bootloader and then COM14 so that might be causing an issue with Putty?

I also tried chaning runXTimes flag to a boolean but that didn't seem to help as I get the same inconsistent results.

I thought this would only take a little bit to run these tests but I must be jinxed.

so that doesn't help me much to determine if an overload is occurring or not.

You can look at the Print.h and/or Print.cpp files, to see that there are many print() methods.

I'm not sure how the rest of that last post helps. The device that receives the serial data and/or the port that the data arrives at are not germaine.

What everyone else said: one of the values is being treated as signed and then sign-extended when it is promoted.

I would guess that the culprit is [nobbc]arr[2]<<8[/nobbc]. This gets promoted twice: once to int (to do the shift) and then to unsigned long (for the bitwise or). The Bad Thing is happenning at that second promotion.

Works fine under gcc. Don't know why avr-gcc would be different.

I'm using the code that I listed in post #7 except I eliminated the loop and am using

irsend.sendNEC(codes[value], 32)

I measured the signal on the oscilloscope and confirmed that the data is incorrect and FF7FC03F is being sent instead of 807FC03F

I confirmed with that the following code works with Serial.println

 unsigned long result = ((unsigned long)arr[0] << 24) | ((unsigned long)arr[1] << 16 ) | ((unsigned long)arr[2] << 8) | (unsigned long)arr[3];

but I don't see why this doesn't work if all the variables are defined as uint32_t

codes[value] = (value << 24) | (compliment << 16) | (lower_part);

boolrules:
Works fine under gcc. Don't know why avr-gcc would be different.

16 bit int vs 32 bit int.

GeorgeIoak:
but I don't see why this doesn't work if all the variables are defined as uint32_t

codes[value] = (value << 24) | (compliment << 16) | (lower_part);

You have an example that "doesn't work" when value, compliment and lower_part are all uint32_t?

GeorgeIoak:
but I don't see why this doesn't work if all the variables are defined as uint32_t

codes[value] = (value << 24) | (compliment << 16) | (lower_part);
        uint32_t compliment = ~value;

compliment has a lot of '1' bits at the top, even after shifting.

@oqibidipo the variable compliment is defined as uint32_t at the top of the program (see latest code below). Sorry, but I'm still confused why the unsigned long definition seemed to work but using uint32_t doesn't work in this code.

#include <IRremote.h>

IRsend irsend;

uint32_t lower_part = 0xC03F;
uint32_t codes[0xFF];
bool runXTimes = true;
uint32_t value = 0;
uint32_t compliment;

void setup()
{
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

void loop() {
      int khz = 38; // 38kHz carrier frequency for the NEC protocol
      value = 0x80;
      compliment = ~value;
      codes[value] = (value << 24) | (compliment << 16) | (lower_part);
      irsend.sendNEC(codes[value], 32); //ON/OFF
      //irsend.sendNEC(0x807FC03F, 32); //ON/OFF
  delay(5000); //In this example, the signal will be repeated every 5 seconds, approximately.
}