2

I need to write a program that takes a user inputted HEX value and converts it to binary, then an LED blinks according to the number of 1's in the binary. For instance, if the value is 0xABC then there are number of 1's are 7 so LED will blink 7 times.

I only just started Arduino so not sure how to let user input a value nor do I know how to convert HEX to BIN but I'm assuming that once I convert the HEX to BIN I can just use a loop to count the number of 1's yet I'm unsure as to actually how to do that. This is what I've written so far.

  byte val=0xABC;
int ledPin=13;

int countSetBits(unsigned int n) 
{ 
    unsigned int c; 
    for (c=0;n>0;n=n&(n-1)) 
        c++; 
    return c; 
}


void blink(int n)
{
    for (int i = 0; i < n; i++)
    {
        digitalWrite(ledPin,HIGH);
        delay(500);
        digitalWrite(ledPin, LOW);
        delay(500);
    }
}

void setup()
{
    Serial.begin(9600);
    pinMode(ledPin,OUTPUT);
    blink(countSetBits(val));
}

void loop()
{   }

Any help would be appreciated. EDIT: If I want to take a number from a user and then make the LED blink according to the number of 1's in its binary form, how would I implement that?

  • 3
    byte val=0xABC; that's not going to fit in a byte. Use an int instead (int val=0xABC;) – Gerben Mar 19 '17 at 19:34
3

You are almost there. Now you just have to read and parse the hex numbers supplied by the user.

The simplest way to accept numeric user input is with Serial.parseInt(). This is not always ideal since it involves blocking the program until it either receives an invalid character or the reading times out. It is also not applicable to your situation because it only works with decimal numbers.

The better standard way to get the input is to put the incoming bytes into a buffer until you catch a specific character designated as an end of message marker. This is typically a CR ('\r' in C++) or an LF ('\n'). This technique is more involved than just calling parseInt() but it is often preferred because it can be done in a non-blocking way. See for example this simple command line interpreter.

In your case, however, you do not need to buffer the incoming bytes. You can instead parse the hex number while you read it, like this:

void loop()
{
    if (Serial.available()) {
        static unsigned int val;
        int c = Serial.read();
        if (c >= '0' && c <= '9') {
            val = 16 * val + (c - '0');
        } else if (c >= 'a' && c <= 'f') {
            val = 16 * val + (c - 'a' + 10);
        } else if (c >= 'A' && c <= 'F') {
            val = 16 * val + (c - 'A' + 10);
        } else {  // End of number.
            blink(countSetBits(val));
            val = 0;  // reset for next round
        }
    }
}
  • If I add this to the loop section of my program will it work? I don't have a board to test on right now – AleiusN Mar 20 '17 at 14:15
  • I tested it on an Uno using Serial.println(val, 16); instead of blink(countSetBits(val));, and it did correctly echo the values I entered with screen. – Edgar Bonet Mar 20 '17 at 14:35
1

You might do it much simpler... Create a fixed array that for each hex character possible (0..F) has the number of 1's counted.

Than iterate through the string, sum up the values of 1's from the array and make a LED blink that many times.

Something in pseudo code:

// Number of 1's: 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
boolean ones[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, ...                }; 
string str = "ABC";
int total = 0;
for (index = 0; index < str.length; index++)
{
   if string[index] <= '9' 
       total += ones[string[index] - '0'];
   else
       total += ones[string[index] - 'A' + 10];
}
1

You could use BIT SHIFTING looks flash, but is actually really useful.

const unsigned char value = 0x55;  // Input
int blinks = 0;                    // Times to blink.

for (int i = 0; i < 8; ++i)
{ 
    blinks += ((value >> i) & 0x01); // Could just blink the light if its true.
}

(value >> i) takes value and moves the number right i places. So if value is Hex 55 (0x55) that is 01010101 in binary. On the first time around the loop when i is 0 (value >> 0) will be 0x55 (unchanged). 0x55 & 0x01 is a logical and between the two numbers, so any bits that are set in both will be set in the result. Since 0x01 only has the least significant bit set (LSB) then we only need to look at the LSB of 0x55, which we know is set. therefore blinks is incremented by one. On the second time around the loop value >> i is 0x2A (00101010) because value has been shifted right one place. So 0x2A & 0x01 = 0, so blink isn't altered.

etc.

The answer at the end of the loop is blinks == 4.

  • The OP already solved the problem of counting the bits set, and his solution looks more efficient than yours. – Edgar Bonet Mar 20 '17 at 15:40
  • I just timed both solutions, both inside a function taking a uint8_t argument and returning an int. The OP's function takes from 14 to 86 cycles, depending on the number of set bits found. Yours takes 281 CPU cycles irrespective of its input. – Edgar Bonet Mar 20 '17 at 16:07
0

Any help would be appreciated.

your code is in pretty good shape - there are some minor issues that need to be taken care of. the countSetBits() is a fairly standard implementation and in this case returns the right value:

enter image description here

I sped up the blinky rate for convenience.

a good starting out place.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.