trouble with converting/splitting numbers

Hi all,

First of all, thank you all for any assistance you can offer me.
Let me start by telling you that i know very little to nothing about coding,
and i am learning it as i go by google and forums.

The problem i am having is as follows:
I start with reading in a analog value (0-1023), which i then split up in 4 seperate numbers.

  analogDEC = analogRead(1);   //uitlezen analoge waarde + opsplitsen
  int num1 = (analogDEC / 1000) % 10;
  int num2 = (analogDEC / 100) % 10;
  int num3 = (analogDEC / 10) % 10;
  int num4 = (analogDEC) % 10;

When i print these numbers into the monitor, everything is fine.

After this i want to convert those numbers into seperate binary codes, so i get the full number in BCD.
Now i have try'd this in a number of ways already, but i never get the desired effect.

I know there are alot better ways to do this (with functions), but as for now i am to stupid to understand most of the functions.

At first i try'd a 'switch..case' (with, and without the (), with " and ', without anything):

  switch (num1) {
    case 0: {
        int BCD1 = (0000);
      }
      break;
    case 1: {
        int BCD1 = (0001);
      }
      break;
    case 2: {
        int  BCD1 = (0010);
      }
      break;
    case 3: {
        int BCD1 = (0011);
      }
      break;
    case 4: {
        int BCD1 = (0100);
      }
      break;
    case 5: {
        int BCD1 = (0101);
      }
      break;
    case 6: {
        int BCD1 = (0110);
      }
      break;
    case 7: {
        int BCD1 = (0111);
      }
      break;
    case 8: {
        int BCD1 = (1000);
      }
      break;
    case 9: {
        int BCD1 = (1001);
      }
      break;

When that didnt work i try'd a 'if..if else', also with various (), ', ", ...)

  if (num4 == 0) {
    int BCD4 = 0000;
  }
  else if (num4 == 1) {
    int BCD4 = 0001;
  }
  else if (num4 == 2) {
    int BCD4 = 0010;
  }
  else if (num4 == 3) {
    int BCD4 = 0011;
  }
else if (num4 == 4){
  int BCD4 = 0100;
}
else if (num4 == 5){
int BCD4 = 0101;
}
else if (num4 == 6){
int BCD4 = 0110;
}
else if (num4 == 7) {
  int BCD4 = 0111;
}
else if (num4 == 8) {
  int BCD4 = 1000;
}
else if (num4 == 9) {
  int BCD4 = 1001;
}

After all this not working, i thought that maybe the problem was in my types.
So instead of using "int" i try'd using "char", i also split up my numbers again:

if (num1 == 0) {
    char BCD1Num1['0'];
    char BCD1Num2['0'];
    char BCD1Num3['0'];
    char BCD1Num4['0'];
  }
  else if (num1 == 1) {
    char BCD1Num1['0'];
    char BCD1Num2['0'];
    char BCD1Num3['0'];
    char BCD1Num4['1'];
  }
  else if (num1 == 2) {
    char BCD1Num1['0'];
    char BCD1Num2['0'];
    char BCD1Num3['1'];
    char BCD1Num4['0'];
  }
  else if (num1 == 3) {
    char BCD1Num1['0'];
    char BCD1Num2['0'];
    char BCD1Num3['1'];
    char BCD1Num4['1'];
  }
  else if (num1 == 4) {
    char BCD1Num1['0'];
    char BCD1Num2['1'];
    char BCD1Num3['0'];
    char BCD1Num4['0'];
  }
  else if (num1 == 5) {
    char BCD1Num1['0'];
    char BCD1Num2['1'];
    char BCD1Num3['0'];
    char BCD1Num4['1'];
  }
  else if (num1 == 6) {
    char BCD1Num1['0'];
    char BCD1Num2['1'];
    char BCD1Num3['1'];
    char BCD1Num4['0'];
  }
  else if (num1 == 7) {
    char BCD1Num1['0'];
    char BCD1Num2['1'];
    char BCD1Num3['1'];
    char BCD1Num4['1'];
  }
  else if (num1 == 8) {
    char BCD1Num1['1'];
    char BCD1Num2['0'];
    char BCD1Num3['0'];
    char BCD1Num4['0'];
  }
  else if (num1 == 9) {
    char BCD1Num1['1'];
    char BCD1Num2['0'];
    char BCD1Num3['0'];
    char BCD1Num4['1'];
  }

I have try'd this with a number of different ways aswell, without the ', with ", using = 1/0.
Every time i try to print anything, while using "int" or "char", i get 0 in the monitor, like it doesnt select a "case" or a "if"

Since i am using "char" in that last method, i'm not sure IF i can print that in the serial monitor?
Since i have to tell the monitor if i want it in DEC, HEX, OCT or BIN.

So i thought, let me continu my code and print something in string:

if ((BCD1Num1[0]) &&  (BCD1Num2[0]) && (BCD1Num3[0]) && (BCD1Num4[0])) {
      Serial.println("the BCD value is 0");
    }
    else if ((BCD1Num1 == '0') &&  (BCD1Num2 == '0') && (BCD1Num3 == '0') && (BCD1Num4 == '1')) {
      Serial.println("the BCD1 value is 1");
    }
    else if ((BCD1Num1 == 0) &&  (BCD1Num2 == 0) && (BCD1Num3 == 1) && (BCD1Num4 == 0)) {
      Serial.println("the BCD1 value is 2");
    }

//and so on...
//with as final "else"

 else {
      Serial.println("the BCD1 value is unknown");
    }

as you can see, i also try'd different ways here, all with no succes.

In this case i always get the "else" => Value unknown.

I would attach the full code below, but unfortunatly i cannot. (exceeds maximum characters)

  • i am kinda ashamed to put such a ugly code here :x.

I hope you guys can make me a little bit smarter and help me understand what i am doing wrong (if not everything).

Thank you!

This may not work for your specific purpose, but the following will print the BCD of a 16bit integer:

int integer = 1023;

byte firstByte = 0;
byte secondByte = 0;
byte thirdByte = 0;
byte fourthByte = 0;

firstByte = integer >> 12;
secondByte = (integer >> 8) & 0xF;
thirdByte = (integer >> 4) & 0xF;
forthByte = integer & 0xF;

Serial.print("BCD of "); Serial.print(integer); Serial.println(" is:);
Serial.print(firstByte,BIN); Serial.print(" "); Serial.print(secondByte,BIN); Serial.print(" "); 
Serial.print(thirdByte,BIN); Serial.print(" "); Serial.println(fourthByte,BIN); Serial.println();

Hi Power_Broker,

Thank you for your response,
If it is not to much trouble, could you explain what exactly it is you are doing?

specially this part:

firstByte = integer >> 12;
secondByte = (integer >> 8) & 0xF;
thirdByte = (integer >> 4) & 0xF;
forthByte = integer & 0xF;

I saw this code somewhere before, but i dont really understand what it does, specially the HEX part (0xF)

Thank you!

DeathAspire:
Hi Power_Broker,

Thank you for your response,
If it is not to much trouble, could you explain what exactly it is you are doing?

specially this part:

firstByte = integer >> 12;

secondByte = (integer >> 8) & 0xF;
thirdByte = (integer >> 4) & 0xF;
forthByte = integer & 0xF;




I saw this code somewhere before, but i dont really understand what it does, specially the HEX part (0xF)

Thank you!

just a number...

0xF = 0b00001111 = 15 (base 10)

look at learning bitmath

The >> and << operators are called arithmetic shifts. These take the numbers and literally shifts them a certain number of bits to either the left or right. For instance:

10010110 << 0 = 10010110
10010110 << 1 = 00101100
10010110 << 2 = 01011000
10010110 << 3 = 10110000

Likewise:

10010110 >> 0 = 10010110
10010110 >> 1 = 01001011
10010110 >> 2 = 00100101
10010110 >> 3 = 00010010

The “&” operator does a bitwise AND operation. For example:

10010110 & 11110000 = 10010000

The above equation can also be written in hex as:

0x96 & 0xF0 = 0x90

else if (num4 == 2) {
    int BCD4 = 0010;
  }
  else if (num4 == 3) {
    int BCD4 = 0011;
  }
else if (num4 == 4){
  int BCD4 = 0100;

You've got two problems with this code you posted earlier.
The first is scope, and the second is base.

Hi,
The & function may look easier in this form;

A 1 0 0 1 0 1 1 0
B 1 1 1 1 0 0 0 0
A&B 1 0 0 1 0 0 0 0

Sorry Table function the editor doesn't line up well
So by AND A with B. the result here is the 4 MSB of A, the 4 LSB of A removed and replaced with 0000.

This is called binary masking.

Tom... :slight_smile:

Hi DeathAspire,

Maybe I cannot understand exactly, what is your need, but some thoughts.
You splitted the number to its digits by divide and remainder operators. That is fine, num1-num4 are numbers (with values 0-9). Then I lost the yarn, what you would like with BCD numbers.

As I know (I hope we think the same) “BCD” means Binary Coded Decimal numbers, where 4 bits represents one decimal digit. If I am right, and you want to construct a BCD from digits stored in num1-num4, then you have to do like this:

bcdNum = (num1 << 12) | (num2 << 8) | (num3 << 4) | num4;

For example, if your starting number (analogDEC) is 1234, then num1 will be 1, num2 will be 2, num3 will be 3 and num4 will be 4. Converting these values to BCD with the equation above, bcdNum will be 4660, which is “1234” in hexadecimal and “0001 0010 0011 0100” in binary notation. As you see, the result is representing one digit in every 4 bits. Is this convertion you wanted?

BTW the “<<” operator shifts left the value on its left by the value on its right. In the above equation that means that the numbers will be shifted to the 12th, 8th, 4th and 0th (the num4) bits of result.

Hi all,

Thank you for all the responses and help offered.
I updated my knowledge on binary masking a bit.

If i understand correctly, every variable in the program will be in binary,
so using BulldogLowell's method, if i have a value of 685 the following will happen:

685 in Binary is 0010 1010 1101.

So "firstByte = Value >> 12;" will shift all digits 12 spots to the right, i.e.: it will result in 0, since i have no "thousand" range in this example

"secondByte = (value >> 8 ) & 0xF;" will shift all digits 8 spots to the right, leaving me with 0010, the 0xF tells the code to only keep the final 4 digits, and discard everything else, resulting in 0010.

"thirdByte = (value >> 4) & 0xF;" shifts 4 spots to the right, leaving me with 0010 1010, again with 0xF making me keep only the final 4 digits, => 1010.

"fourthByte = value & 0xF;" will then result in 1101.

This will yield the following result:
firstByte = non existing so = 0000
secondByte = 6 = 0010 which results in "2" instead of "6"
thirdByte = 8 = 1010 which results in "10" instead of "8"
fourthByte = 5 = 1101 wich results in "13" instead of "5"

So either i understand it completely wrong, or this doesnt result in a BCD code as i know it.

have a look at (which you adapt to any number of digits)
http://www.eng.utah.edu/~nmcdonal/Tutorials/BCDTutorial/BCDConversion.html

or use division ?

// decimal to BCD using division
void bcd2(int data, int bcd[])
{
    int i, j, divisor=1000;
    for(i=3; i>=0; i--)
    {
        bcd[i]=data/divisor;
        data %= divisor;
        divisor /=10;
    #ifdef DEBUG
        printf("BCD2 loop %d bcd[loop] %d\n", i, bcd[i]);
    #endif
    }
}


int main(void)
{

    int i, j, data=1685,decimal=0, bcd[5]={0};
    printf("data %d in hex %x\n", data, data);
    bcd2(data, bcd);
    printf("\nBCD2 result ");
    for (j=3; j>=0; j--)
       printf(" %d", bcd[j]);
    // BCD to decimal
    decimal=0;
    for (j=3; j>=0; j--)
        decimal=(decimal*10) + bcd[j];
    printf("\nresult in decimal %d\n", decimal);

}

a run gives

data 1685 in hex 695

BCD2 result  1 6 8 5
result in decimal 1685

685 in Binary is 0010 1010 1101.

But it seems to me that your expectation is that you are converting from BCD, not binary.

DeathAspire:
This will yield the following result:
firstByte = non existing so = 0000
secondByte = 6 = 0010 which results in "2" instead of "6"
thirdByte = 8 = 1010 which results in "10" instead of "8"
fourthByte = 5 = 1101 wich results in "13" instead of "5"

So either i understand it completely wrong, or this doesnt result in a BCD code as i know it.

Very close. You understand the masking part quite well! The only hangup is when you say "which results in 10" and "which results in 13". Remember, BCD is like a mix between binary and hex representations. What you should've said was the following:

firstByte = non existing so = 0000
secondByte = 6 = 0010 which results in "2" instead of "6"
thirdByte = 8 = 1010 which results in "A" instead of "8"
fourthByte = 5 = 1101 wich results in "D" instead of "5"

This is because A = 10 and D = 13. Remember that you can only represent each four bits with a single character (D is a single character while 13 is not).

I forgot about the HEX part for a moment.

Let me try to explain what it is i am trying to do.

I made a 1ms clock pulse to pin 11, so output is 1ms high, 1ms low.
I connected this to a counter, which counts till 36 pulses, on the 36th pulse it resets to 0 and sets pin 5 high for 1ms, then back to low.
This sends a signal to a seperate circuit board to reset its own counter (to make sure they run syncronised).

void setup() {
  // put your setup code here, to run once:
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(A0, INPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:

  digitalWrite(clockpulse, HIGH); //1ms clock
  digitalWrite(11, HIGH);
  delay(1);
  digitalWrite(clockpulse, LOW);
  digitalWrite(11, LOW);
  delay(1);

  if (clockpulse, HIGH) {  //clockpuls counter

    counter ++ ;
  }

  if (counter == 36) {     //reset counter

    digitalWrite(5, HIGH);
    delay(1);
    digitalWrite(5, LOW);
    counter = 0;
  }

I then read out the analog value, which i need to convert to BCD.
I need this, because i need to send a output on every "1" on a specific clock pulse.

For example:
If i have the value of 685 like in the previous post, in BCD this will be 0000 0110 1000 0101.
so if we connect this to the clockpulse, clockpulse 1 to 5 will do nothing, 6 will set a output in HIGH state,
7 is HIGH aswell, 8 is LOW, 9 HIGH, etx...

Why would i need this?
The output sends a signal to the other circuit board, which uses a "shift register", after recieving 4 bits, it sends the BCD/BIN value out to a "BCD to 7segment" IC, which in return will do its job, and in the end we have 4 7segment displays that display the original value.

The circuit board is made to be able to connect 8 displays to it, including the .'s that makes 36 clockpulses.

So i actually need every single digit in binary, and i need that binary in single digits aswell.
i.e. 6 = 0110 = 0, 1, 1, 0.

I hope this makes my problem a bit clearer?

Since the "Serial.print" function is able to convert numbers to binary, isnt there a way to "print" the value into a variable?
So basicly split the numbers:

  analogValue = analogRead(1);   //uitlezen analoge waarde + opsplitsen
  int num1 = (analogValue / 1000) % 10;
  int num2 = (analogValue / 100) % 10;
  int num3 = (analogValue / 10) % 10;
  int num4 = (analogValue) % 10;

When i print this with Serial.println(num1-4) it displays 0, 6, 8, 5.
But when i print it with Serial.println(num1-4, BIN) it displays 0, 110, 1000, 101.
This is exactly what i need, but i know of no way to get those numbers into a variable which i can use in my code.

DeathAspire:
This is exactly what i need, but i know of no way to get those numbers into a variable which i can use in my code.

If it is exactly what you need, why do you need something different? What do you want instead?

Hi DeathAspire,

Yesterday I posted a code that, I think, should satisfy your need. You began well to “decompose” a number into its digits, only these digit values have to be put next to each other by each 4 bits:

bcdNum = (num1 << 12) | (num2 << 8) | (num3 << 4) | num4;

After this “composing” bcdNum can be shifted out to the display registers. I would not go to string manipulations with such a simple conversion. However, the conversion could be done by a cycle, instead of fixed four digit splitting, but I am not sure it is worth the effort.

Hi,
Check this link, has a practical description of binary to BCD.

http://www.eng.utah.edu/~nmcdonal/Tutorials/BCDTutorial/BCDConversion.html

Tom... :slight_smile:

Try this demo

uint8_t num1 = 1;
uint8_t num2 = 2;
uint8_t num3 = 3;
uint8_t num4 = 4;


void setup() {

  Serial.begin(115200); 

}

void loop() {

  uint8_t msByte = (num1 << 4) | (num2);
  uint8_t lsByte = (num3 << 4) | (num4);
  if (msByte < 10) Serial.print("0");//Human readable demo
  Serial.print(msByte, HEX);
  if (lsByte < 10) Serial.print("0");
  Serial.println(lsByte, HEX);
  //Serial.write(msByte);//Use .write() in the production code
  //Serial.write(lsByte);
  if (++num1 == 10) num1 = 0;
  if (++num2 == 10) num2 = 0;
  if (++num3 == 10) num3 = 0;
  if (++num4 == 10) num4 = 0;
  delay(1000);                                                                                                                                 

}

@Power_Broker
The print shows me the exact numbers i need, but i need these numbers in a variable so i can use those to split, and check if on clockpulse x, the corresponding digit is 1 or 0
This is why i i asked if it is possible to get the “serial.print” result into a variable

My apologies if it is unclear what i am trying to do.

the code szilvasyz showed is actually doing what i need, when i print the bcdNum as BIN, it gives me the BCD notation of the full number. (0000 0110 1000 0101)
)

But when i split up those numbers using devide and remainder, and try to print it (serial.println(bcdNum1-16, BIN), it shows me the original number in BIN instead of BCD. (0000 0001 1101 1100)
How is this possible when every digit (bcdNum1 to 16) is supposed to have only 1 or 0 in it

int bcdNum = (num1 << 12) | (num2 << 8) | (num3 << 4) | num4;
Serial.println(bcdNum, BIN); // this prints 110 1000 0101
                                       //                 6     8       5
  int bcdNum1 = (bcdNum / 1000000000000000) % 10;
  int bcdNum2 = (bcdNum / 100000000000000) % 10;
  int bcdNum3 = (bcdNum / 10000000000000) % 10;
  int bcdNum4 = (bcdNum / 1000000000000) % 10;
  int bcdNum5 = (bcdNum / 100000000000) % 10;
  int bcdNum6 = (bcdNum / 10000000000) % 10;
  int bcdNum7 = (bcdNum / 1000000000) % 10;
  int bcdNum8 = (bcdNum / 100000000) % 10;
  int bcdNum9 = (bcdNum / 10000000) % 10;
  int bcdNum10 = (bcdNum / 1000000) % 10;
  int bcdNum11 = (bcdNum / 100000) % 10;
  int bcdNum12 = (bcdNum / 10000) % 10;
  int bcdNum13 = (bcdNum / 1000) % 10;
  int bcdNum14 = (bcdNum / 100) % 10;
  int bcdNum15 = (bcdNum / 10) % 10;
  int bcdNum16 = (bcdNum) % 10;

   Serial.print(bcdNum1, BIN);
  Serial.print(bcdNum2, BIN);
  Serial.print(bcdNum3, BIN);
  Serial.print(bcdNum4, BIN);
  Serial.print(bcdNum5, BIN);
  Serial.print(bcdNum6, BIN);
  Serial.print(bcdNum7, BIN);
  Serial.print(bcdNum8, BIN);
  Serial.print(bcdNum9, BIN);
  Serial.print(bcdNum10, BIN);
  Serial.print(bcdNum11, BIN);
  Serial.print(bcdNum12, BIN);
  Serial.print(bcdNum13, BIN);
  Serial.print(bcdNum14, BIN);
  Serial.print(bcdNum15, BIN);
  Serial.print(bcdNum16, BIN);
// this prints 0 0000 0000 0001 1101 1100

I must be doing something wrong, or overlooking something. But i have no idea what :-(.

EDIT:
After reading Indev2’s post, i looked up the Serial.write() thingy, but as far as i can tell it sends a complete byte (0110) at once, which wont work for me since i need every digit seperatly.

Thanks for sticking around for so long, i know it can be frustrating if someone doesnt understand “simple” things.

@DeathAspire

You can't send just 4 bits at a time, my method sends a BCD byte, or 4 digits in two bytes, why can't the other end of your project decode as such?

In essence 8 data bits have to go in one byte (standard serial) period!
How the bits are interpreted receiving side is up to you.

Hi DeathAspire,

As I see from one of your earlier post, you want to send the BCD form of a number (for example read from ADC) to an external circuit using serial “shiftout”. That is, for example, if you have 685 as the number value, you need to get 4-bit “streams” like 0110, 1000, 0101, because these are the representations of the digits “6”, “8” and “5”. When you have the number bcdNum, that contains all these 4-bit pieces (nibbles), I cannot figure out, what you want to split more.

With bcdNum you only have to do a loop, and shift out bits to the external circuit. I think, you need something like this:

int i;

for (i=0; i<16; i++) {

  // set external data bit to next bit of BCD value
  digitalWrite( extDTA , (bcdNum & 1) != 0 ? HIGH : LOW );

  // pulse external clock signal to shift in the data into external shit register
  digitalWrite( extCLK , HIGH );
  delay(1);
  digitalWrite( extCLK , LOW );
  delay(1);

  // shift right the entire BCD number
  bcdNum >>= 1;

}

This code will shift out to the external shift register the entire 16 bit bcdNum (4 digits, each is 4 bits) bit by bit.

If your program does timings different way (for example with millis() in the loop section), the code needs to be modified, but the theory is the same: bcdNum has to be shifted out bit by bit to the external circuit.

Another modification should be done if your external shift register is waiting for the most significant BCD bit in the first place of “stream”. In this case you have to mask the 15th bit instead of 0th in the data line setting code, and bcdNum has to be shifted left instead of shifting right at end of the loop.