Excluding a bit in a comparison

Finally relized how helpful bitwise opperations are, but I'm still having a hard time understanding the syntax "a bit".

I'm trying to prove out a shift opperation for my braille keyer so I can shift letters to numbers as done in braille. Going to use the 8th button to do the shifting, thus fliping the most significant bit. I'm trying to do a comparison that would exclude that bit to read some of the same values from my existing conversion array (a,b,c,d,e,f,g,h,i,j,) as numbers (1,2,3,4,5,6,7,8,9,0,)

Now there may be a better way to achieve what I'm doing all together, (im open to suggestions) but mainly I'm currious how to exclude the 8th bit once I have determined it is 1? In this way only the other buttons will be compared against the array.

Here is where I'm at so far. Please ignore the convert argument was using this function to convert both ways.

#define ENCODEAMT 32 // size is defined to structure iteration amount
byte byteToBraille [3][ENCODEAMT] // brialle convertion array
{
  { // input in characters
    'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', 8, ' ', '-',';','.', '?',   }
  ,
  { //corrisponding braille binary output in decimal form, read from least significant bit
    1 , 5 , 3 , 11, 9 , 7 , 15, 13, 6 , 14, 17, 21, 19, 27, 25, 23, 31, 29, 22,  30, 49, 53, 46, 51, 59, 57 ,64, 32, 48, 40, 34,  43,   } 
};//each bit in the corrisponding bytes represents a "bump" state


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

  for(byte i=0;i<255;i++)
  {
    Serial.print(i);
    Serial.print(" - ");
    Serial.write(brailleConvert(i, 0));
    Serial.println();
  }

}

void loop()
{
}


byte brailleConvert(byte letter, boolean convert)
{//if conversion is desired set true, set false for "if braille" return letter opperation
  if(letter & (1<<7))
  {//if most significant bit is flaged "shift is pressed"
    for(byte i=0; i<10; i++)
    {
      if(letter ~ (1<<7) == (byteToBraille[!convert][i]))
      {// for a matching letter in the array
        return (byteToBraille[convert][i]-48);
      }// return the corrisponding translation
    }
      if(letter ~ (1<<7) == (byteToBraille[!convert][10]))
      {
        return 48;//'0'
      }
  }
  else
  {
    for(byte i=0; i<ENCODEAMT;i++)
    {
      if(letter == (byteToBraille[!convert][i]))
      {// for a matching letter in the array
        return (byteToBraille[convert][i]);
      }// return the corrisponding translation
    }
  }
  return 0;
}

Currently the tilde mucks this up, was under the notition that appying "not" to the particular bit would work, but this syntax is definately wrong.

[quote author=Paul Beaudet link=topic=246154.msg1759982#msg1759982 date=1402412586]
but mainly I'm currious how to exclude the 8th bit once I have determined it is 1?[/quote]

Mask it out:

0b10001001 & 0b01111111 = 0b00001001;

I don't really understand what you are trying to do.

It looks like for each letter there is a corresponding braille number - as in your 2 arrays.

If you want to convert (say) the letter 'b' to the number 5 you need to find the position in the array where 'b' occurs and use that index to find a number in the other array.

The index position (2) for 'b' can be derived from 'b' - 'a' (in other words subtracting the ascii code for 'a' from the ascii code for 'b'. You need a slightly different approach for the characters after 'z'.

There doesn't seem to be any need to but the letters into an array.

With that in mind, I don't understand why you need to mask a bit which is done as @Arrch explained.

...R

Note we start counting bits at zero so the eighth bit is called bit seven. :slight_smile:

@arcch Thank you, should have figured that myself.

I'm still missing something though after adding, still no ascii numbers returned

#define ENCODEAMT 32 // size is defined to structure iteration amount
byte byteToBraille [3][ENCODEAMT] // brialle convertion array
{
  { // input in characters
    'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', 8, ' ', '-',';','.', '?',     }
  ,
  { //corrisponding braille binary output in decimal form, read from least significant bit
    1 , 5 , 3 , 11, 9 , 7 , 15, 13, 6 , 14, 17, 21, 19, 27, 25, 23, 31, 29, 22,  30, 49, 53, 46, 51, 59, 57 ,64, 32, 48, 40, 34,  43,     } 
};//each bit in the corrisponding bytes represents a "bump" state


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

  for(byte i=0;i<255;i++)
  {
    Serial.print(i);
    Serial.print(" - ");
    Serial.write(brailleConvert(i));
    Serial.println();
  }

}

void loop()
{
}


byte brailleConvert(byte letter)
{//if conversion is desired set true, set false for "if braille" return letter opperation
  if(letter & 0b10000000)
  {//if most significant bit is flaged "shift is pressed"
    for(byte i=0; i<10; i++)
    {
      if(letter & 0b01111111 == (byteToBraille[1][i]))
      {// for a matching letter in the array
        return (byteToBraille[0][i]-48);//subtracting 48 from the lower case char results in the corresponding number
      }// return the corrisponding translation
    }
    if(letter & 0b01111111 == (byteToBraille[1][10]))
    {
      return 48;//'0'
    }
  }
  else
  {
    for(byte i=0; i<ENCODEAMT;i++)
    {
      if(letter == (byteToBraille[1][i]))
      {// for a matching letter in the array
        return (byteToBraille[0][i]);
      }// return the corrisponding translation
    }
  }
  return 0;
}

@Robin2 I'm "shifting" through masking to save memory, if you are suggesting I could just add more to the array.

The Braille number (array 1) is the expected input to correspond with the ascii to output (array 0). The incoming information (letter) is set bit by bit by an array of 8 buttons. Thus a button combination is pressed, and readable ascii is sent to a Bluefruit EZey Key.

@Grumpy_Mike haha got to be careful with my language, was correct in the code though (-:

I'm still missing something though after adding, still no ascii numbers returned

You are never calling the conversion function anywhere so the compiler probably removed it. Even if it didn't how is the code in the brailleConvert function ever going to run?

@Grumpy_Mike, brailleConvert is called in one of the serial writes

    Serial.write(brailleConvert(i));

Right now in my output I get all of the ascii letters for their corresponding "braille numbers", but none for ascii numbers that I'm trying to create in the shift case. My guess is my syntax is bad because of my inexperience with bitwise operations. But at this point, this might be a glaringly obvious flow issue that's embarrassingly hiding in plain sight.

The for loop should iterate through all possible bytes except for 255 right?

Maybe my brain is unusually slow today but I have no idea what shifting or masking is doing for you.

And I thought you needed less array, not more.

...R

@robin in ascii you can flip the 3rd bit to get a cap ( I think... definitely one of those 8 bits :)) Basically I'm trying to achieve a similar thing with numbers.

For more context on the project you can look at the whole thing here - GitHub - PaulBeaudet/braille_neotype_testing: Using braille to test the haptic concepts of the Neotype project
which as of yesterday is no longer braille

I'd like to think my comments explain it okay, but please rant to me if it needs to be more descriptive. As far ideas about doing the conversion with less I'm all ears, there is likely a better method to achieve the same thing. I'm just the guy that is have a hard time figuring out how to properly implement a working bit mask. :slight_smile:

That works on the spark core, but I will likely port back to the uno if there is interest and I can figure how to pwm in sync on 6 pagers.

Referring to Reply #8 ...

The way to do a bit mask was explained in Reply #1.

I'm just trying to figure out what your overall project is supposed to do because I still don't know (especially having looked at your Github link :)). And I can't see any link between your request for info about bitmasks and the transformation between the two arrays you have posted.

...R

by "inversing" the if and the loops you might get simpler code and your array can even be 1 dimensional!

#define BRAILLESHIFT 0b10000000
#define BRAILLEMASK  0b01111111

byte brailleConvert(byte letter)
{
  byte le = letter & BRAILLEMASK;     // calc once
  
  for (byte i=0; i<ENCODEAMT; i++)   // go through whole array
  {
    if ( le == byteToBraille[1][i] )   // if it matches
    {
      if (letter & BRAILLESHIFT) return '0' + i;    // return digit
      return 'a' + i;  // or a char
    }
  }
  return 0;
}

does this make sense to you?

@robtillaart Great simplification! I understand now, this really made my day.

Should have realized a comparison was unnecessary with "letter & mask"
If something can be seen through the mask its true!

Originally went 2d on the array because special characters follow a less convenient pattern
In the case of just alpha characters though the following achieves exactly what I was looking for

#define ENCODEAMT 26 // size is defined to structure iteration amount
byte byteToBraille [2][ENCODEAMT] // Braille conversion array
{
    1 , 5 , 3 , 11, 9 , 7 , 15, 13, 6 , 14, 17, 21, 19, 27, 25, 23, 31, 29, 22,  30, 49, 53, 46, 51, 59, 57,  
};//each bit in the corresponding bytes represents a "bump" state


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

  for(byte i=0;i<255;i++)
  {
    Serial.print(i);
    Serial.print(" - ");
    Serial.write(brailleConvert(i));
    Serial.println();
  }

}

void loop()
{
}

byte brailleConvert(byte brailleIn)
{
  byte maskedValue = brailleIn & 127; 
                                                                               // 127 = 0111-1111 // mask the 7th bit out   
  for (byte i=0; i<ENCODEAMT; i++)   
  {                                                                            // for all of the key mapping 
    if ( maskedValue == byteToBraille[1][i] ) 
    {                                                         // given a match absent of the 7th bit (there will be two sets of ENCODEAMT values)
      if (brailleIn & 128)
      {                                                                      // 128 = 1000-0000 // if( 7th bit is fliped high )
        if(maskedValue < 17){return '0' + i;} 
                                                   // absent of the 7th bit, if(lower shift, less than 17 or 'k' equivalent) {return corresponding number}
        break;                                                          // else return 0! avoid irrelivent shifts (filtering out 16 results)
      } 
      return 'a' + i;                                                 // return char
    }
  }
  return 0;
}

Thank you!

byte byteToBraille [2][ENCODEAMT] // Braille conversion array
{
1 , 5 , 3 , 11, 9 , 7 , 15, 13, 6 , 14, 17, 21, 19, 27, 25, 23, 31, 29, 22, 30, 49, 53, 46, 51, 59, 57,
};//each bit in the corresponding bytes represents a "bump" state

should be

byte byteToBraille [ENCODEAMT] // Braille conversion array
{
1 , 5 , 3 , 11, 9 , 7 , 15, 13, 6 , 14, 17, 21, 19, 27, 25, 23, 31, 29, 22, 30, 49, 53, 46, 51, 59, 57,
};//each bit in the corresponding bytes represents a "bump" state

reduces the memory needed too :slight_smile:
(the rest of the code needs some minor change, homework :wink:

Good catch, it compiled because I was consistent about my mistake, haha

Thanks again,

now I going to build it for all the ascii characters. Now that I see this approach I think I'll find the simplest way to do this going all or nothing.

Please post your code when finished, we will have another look.

and please change
Serial.begin(9600);
to
Serial.begin(115200);
is way faster, leaves more time for processing :wink:

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

  for(int i=0;i<255;i++)//for all of the byte address space
  {
    if(byte writeable = uniTranslator(i))
    {
      Serial.print(i);
      Serial.print(" - ");
      if(writeable== 8 || writeable == 32)
      {//special cases with unclear serial results
        Serial.print(writeable);
        Serial.print("-raw");
      }
      else
      {
        Serial.write(writeable);
      }
      Serial.println();
    }
  }

}

void loop()
{
}

byte uniTranslator(byte base)
{
  static byte chordPatterns[] {1,5,48,56,2,24,33,6,4,14,28,12,40,30,7,18,31,3,16,32,51,45,8,35,54,49,};
  #define PATTERNSIZE sizeof(chordPatterns)
  
  if(base == 128){return 8;}//Express conversion: Backspace // Backspace doubles as second level shift for special chars
  if(base == 64){return 32;}//Express conversion: Space // Space also doubles as the first shift in a chord
  
  for (byte i=0; i<PATTERNSIZE; i++)   
  {// for all of the key mapping   
    if ( (base & 63) == chordPatterns[i] ) 
    {//pattern match regardless most significant 2 bits // 63 = 0011-1111 // mask the 6th and 7th bit out
      if ((base & 192) == 192){break;} //third level shift *combination holding space and backspace
      if (base & 64)//first level shift *combination with space
      {// 64 = 0100-0000 // if( 6th bit is flipped high )
        //if(lower shift, less than 10th result) {return corresponding number}
        if(i<10){return '0' + i;} //a-j cases (ascii numbers)
        if(i<25){return 23 + i;}  //k-y cases ( !"#$%&'()*+'-./ )
        if(i==26){break;}         //z case (unassigned)
      } 
      if (base & 128)//second level shift *combination with backspace
      {//128 = 1000-0000 // if(7th bit is high) 
        if(i<7){return ':' + i;}//a-g cases ( :;<=>?@ )
        if(i<13){return 84 + i;}//h-m cases ( [\]^_`  )
        if(i<17){return 110 + i;}//n-q cases( {|}~    ) 
        break;                   //other cases unassigned
      }
      return 'a' + i;// return plain char based on position in the array given no shift
    }
  }
  return 0;
}

This does basically everything, though its made mostly convenient to the efficiency of the function as opposed to a layout of shift pattern that might be actually efficient to a user.

Sorry, ignored the speed change just for a test, but I'll keep that in mind in the future.

Keep in mind that the output of this function is pushed through a debouncing/hold detection function afterwards. Holds produces the typical "shift" case most are use to replacing the lower with an upper case. Which is why that type of shift is ignored here. The raw button data is being pushed through this function.. which now when I think about it is is probably really inefficient... but I guess I'm going to find out if it even matters as I use the keyer.

Using this function in the main body of code is going to be another adventure. A function doing the same thing in inverse is also needed to drive the 6 hapic feedback pagers with 8 signals.... Think I'll just make shift animations when 6th and 7th bits to go through the haptic messaging function...