10 bits (value encoded in 8 high bit + 2 low bit) into decimal ?

Julien Bayle's icon

Hi there, I'm stuck in the end of this

My 8channels FlexVolt sensors (which are very nice) output data with 10bits precision like that (reference: https://github.com/CatabeeScienceAndDesign/FlexVolt)
high8BitsFromChannel1
high8BitsFromChannel2
high8BitsFromChannel3
high8BitsFromChannel4
high8BitsFromChannel5
high8BitsFromChannel6
high8BitsFromChannel7
high8BitsFromChannel8
0b11223344 (where 11 is the lowest 2 bits from channel1, 22 is the lowest 2 bits from channel2, etc)

Each channel represent a value coming from muscle contraction.

For retrieving each value, I need to combine one of the first 8 ones and the last one.

For instance, if I want the value coming from the channel 1, if I receive 128 0 0 0 0 0 0 150, it means:
- 128 is the high 8bits from the channel 1, ok
- 150 has to be decomposed/converted and combined to the previous one

How can I retrieve my final value ...? I'm really stuck and this is ... easy, I know.

I'd do:
128 << 2 + ????????

(not sure about the thread title, I may improve this)

pdelges's icon

You should do a binary AND with some masks :
00000011 (= 3) to get the 2 last bits for channel 4
00001100 (= 12) to get the 2 bits for channel 3

11000000 (= 192) to get the 2 bits from channel 1

then shift the result to the right by the correct amount of bits.

Something like this :

Max Patch
Copy patch and select New From Clipboard in Max.

Julien Bayle's icon

Thanks for your answer, but I don't understand well as there are 8 channels.
The documentation is not enough and I asked them more.

Actually, their own application is working fine (graphes enough precision and nice smoothed data)
https://github.com/CatabeeScienceAndDesign/FlexVolt/tree/master/www
But as I bought their solution, I don't want to reverse engineering it myself to understand it and I'd prefer their answer...

pdelges's icon

Looking quickly into their flexvolt.js code, it seems that 11 bytes are expected when the system is used with 8 channels and 10bits res.
I guess 1 byte is 75, 8 bytes are the 8 MSB parts for the 8 channels, and 2 bytes are for the 8 LSB parts. You need to find the order of those 11 bytes.

So you should check wether they first send the MSBs for channels 1-4, followed by 1 byte with the 4 LSB parts then MSB for ch 5-8 followed by LSB ; or if all 8 MSB are sent followed by 2 bytes for the LSBs.

Julien Bayle's icon

hello,
sure for the first bytes.
this is why I didn't mention it.

first byte is, indeed, 75

I think my problem is the order of them yes.
as far as I observe:

With no contraction (I mean nothing happening on all sensors), I receive:
128 128 128 128 128 128 128 128 <last>
with <last> changing fast

So I think the order is :
channel1msb, channel2msb....., and the "number"

the number is : 0b1122334455667788, according to FlexVolt
nn being the 2 bits for lowest two bit

Brendan Flynn's icon

Hello Julien,

An aside before I answer this - you would most likely find 8-bit data sufficient, and it will make everything a bit easier. 8-Channels at 10-bits is pushing the limits of the Bluetooth bandwidth and the sensors processors.

You are close! You are correct in most of your surmises so far. You expect 11 bytes, with the first byte being a signature.

1. Signature (75 = 8channels, 10-bit)
2. Chan 1 MSB
3. Chan 2 MSB
...
8. Chan 7 MSB
9. Chan 8 MSB
10. Overflow LSB 11223344
11. Overflow LSB 55667788

The relevant code is in flexvolt.js . You can see where I store the two overflow bytes, then for each channel, shift the MSB left by 2 and append relevant 2 bits from overflow.

var readInd = 0, dataInd = 0;
while(readInd < (dataIn.length-api.readParams.expectedBytes) ){
    var tmp = dataIn[readInd++];
    if (tmp === api.readParams.expectedChar){
        dataTimes.push(timestamp); 
        timestamp+=timestampInterval*hardwareLogic.settings.downSampleCount;
        if (!hardwareLogic.settings.bitDepth10) {
            for (iChan = 0; iChan < hardwareLogic.settings.nChannels; iChan++){
                dataParsed[iChan][dataInd] = factor8Bit*(dataIn[readInd++] - api.readParams.offset); // centering on 0!
            }
            dataInd++;
        } else if (hardwareLogic.settings.bitDepth10) {
            tmpLow = dataIn[readInd+hardwareLogic.settings.nChannels];
            if (hardwareLogic.settings.nChannels > 4) {
              tmpLow2 = dataIn[readInd+hardwareLogic.settings.nChannels+1];
            }
            for (iChan = 0; iChan < Math.min(4, hardwareLogic.settings.nChannels); iChan++){
                dataParsed[iChan][dataInd] = factor10Bit*((dataIn[readInd++]<<2) + ((tmpLow>>(2*(3-iChan))) & 3) - api.readParams.offset); // centering on 0!
            }
            for (iChan = 4; iChan < hardwareLogic.settings.nChannels; iChan++){
                dataParsed[iChan][dataInd] = factor10Bit*((dataIn[readInd++]<<2) + ((tmpLow2>>(2*(3-iChan))) & 3) - api.readParams.offset); // centering on 0!
            }
            readInd++; // for the tmpLow read
            if (hardwareLogic.settings.nChannels > 4) {
              readInd++; // for the tmpLow2 read
            }
            dataInd++;
        }

    } else if (tmp === 116) { //'t' - voltage statement
        var batteryVoltage = dataIn[readInd++];
        console.log('INFO: Got Battery Level: ' + JSON.stringify(batteryVoltage));
        updateBatteryVoltage(batteryVoltage);
        break; // kick back out -
    } else {
        console.log('WARNING: unexpected Char '+tmp);
    }
}
Jean-Francois Charles's icon

Looks like a patch like Patrick posted should work, with an added conversion from "0 1 10 11" to "0 1 2 3" to add to your "128 << 2". For instance, like this - I'm sure we can implement more efficiently, but that's a first rough version:

Max Patch
Copy patch and select New From Clipboard in Max.

pdelges's icon

Jean-François, there is no need for a conversion : it is the number's Display format that is set to binary. A number is a number…

Max Patch
Copy patch and select New From Clipboard in Max.

Jean-Francois Charles's icon

Indeed, I missed that! I don't know what I was doing when I played with the patch!
Really, I see no reason why Julien's thing would not work now. Maybe to debug, starting with a four-channel configuration would be useful...