I have a situation where I need to convert a value that is stored as 3 bytes to a char.The can be for example 7ED95, which is reported in 3 consequtive bytes 07, ED, 95.This value in decimal is 519573, which corresponds to a 51.9% SOC. Is the proper way to do this conversion (from the 3 bytes to the value of 51) like this: char soc = (char)(((unsigned long)value[1]<<16 + (unsigned long)value[2]<<16 + (unsigned long)value[3])/10000); I have been doing stuff like that before with unions, but can't remember how either. Thanks, Nikolay
Division is kind of nasty in 8bit. Actually very nasty. Unless you are dividing by 2, 4, 8, 16, etc. Divide by 2^N is the same as >>N, and that is the shortcut you want to do if at all possible. That said, your solution seems ok, except the value[2] should be <<8, and you probably want to add 5000 just before divide by 10000, to round correctly. If you don’t need too much precision, you might find an inaccurate kludge may be good enough. For example: 205/2048 = 0.1000976562 and that is pretty close to 0.1. So multiply by 205, then shirt right 11 spaces is pretty close to divide by 10. Multiplication is much less expensive than division. If you look at MiFromKm and KmFromMi in utils.c, you can see similar sorts of approximations to avoid big divisions. Regards, Mark.
On 22 Jun, 2015, at 4:30 pm, Nikolay Shishkov <nshishkov@yahoo.com> wrote:
I have a situation where I need to convert a value that is stored as 3 bytes to a char. The can be for example 7ED95, which is reported in 3 consequtive bytes 07, ED, 95. This value in decimal is 519573, which corresponds to a 51.9% SOC. Is the proper way to do this conversion (from the 3 bytes to the value of 51) like this:
char soc = (char)(((unsigned long)value[1]<<16 + (unsigned long)value[2]<<16 + (unsigned long)value[3])/10000);
I have been doing stuff like that before with unions, but can't remember how either.
Thanks,
Nikolay
_______________________________________________ OvmsDev mailing list OvmsDev@lists.teslaclub.hk http://lists.teslaclub.hk/mailman/listinfo/ovmsdev
Thanks again...I seem to not been able to grasp the idea and would appreciate a code example.For example byte1=0x07, byte2=0xDC, byte3=0xC0If I put these in excel and calculate 7*256*256 + 220*256 + 192 I get 520264.Then when I divide this by 10000, I get 52.0264. And this matches the 52% shown by the car. Currently my code for this conversion is: sv1 = (unsigned long)can_databuffer[4]; sv1 = sv1*65536UL; sv2 = (unsigned long)can_databuffer[5]; sv2 = sv2*256UL; sv3 = (unsigned long)can_databuffer[6]; sv3 = sv3; car_SOC = (char)((unsigned long)(sv1 + sv2 + sv3 + 5000UL)/10000UL); And the result for car_SOC is 49. How is this possible?And what would be the correct way to put these 3 bytes together and divide them by 10000 to get to the final value in char? Thanks in advance,Nikolay On Monday, June 22, 2015 3:58 PM, Mark Webb-Johnson <mark@webb-johnson.net> wrote: Division is kind of nasty in 8bit. Actually very nasty. Unless you are dividing by 2, 4, 8, 16, etc. Divide by 2^N is the same as >>N, and that is the shortcut you want to do if at all possible. That said, your solution seems ok, except the value[2] should be <<8, and you probably want to add 5000 just before divide by 10000, to round correctly. If you don’t need too much precision, you might find an inaccurate kludge may be good enough. For example: 205/2048 = 0.1000976562 and that is pretty close to 0.1. So multiply by 205, then shirt right 11 spaces is pretty close to divide by 10. Multiplication is much less expensive than division. If you look at MiFromKm and KmFromMi in utils.c, you can see similar sorts of approximations to avoid big divisions. Regards, Mark. On 22 Jun, 2015, at 4:30 pm, Nikolay Shishkov <nshishkov@yahoo.com> wrote: I have a situation where I need to convert a value that is stored as 3 bytes to a char.The can be for example 7ED95, which is reported in 3 consequtive bytes 07, ED, 95.This value in decimal is 519573, which corresponds to a 51.9% SOC. Is the proper way to do this conversion (from the 3 bytes to the value of 51) like this: char soc = (char)(((unsigned long)value[1]<<16 + (unsigned long)value[2]<<16 + (unsigned long)value[3])/10000); I have been doing stuff like that before with unions, but can't remember how either. Thanks, Nikolay _______________________________________________ OvmsDev mailing list OvmsDev@lists.teslaclub.hk http://lists.teslaclub.hk/mailman/listinfo/ovmsdev _______________________________________________ OvmsDev mailing list OvmsDev@lists.teslaclub.hk http://lists.teslaclub.hk/mailman/listinfo/ovmsdev
Nikolay, Please take care with RAM usage. It is very very tight on these little microcontrollers. I think the simplest way for yours is something like this: (((unsigned long)(can_databuffer[4)<<16) + ((unsigned long)(can_databuffer[5)<<8) + ((unsigned long)(can_databuffer[6)) + 5000UL) / 10000UL Not sure why you are getting a rounding error. Are you sure that the SOC hasn’t dropped between logging the data and plugging in OVMS? Here is what I get: $ echo 'ibase=16; scale=2; 07DCC0/02710' | bc 51.52 Regards, Mark.
On 25 Jun, 2015, at 7:35 am, Nikolay Shishkov <nshishkov@yahoo.com> wrote:
Thanks again... I seem to not been able to grasp the idea and would appreciate a code example. For example byte1=0x07, byte2=0xDC, byte3=0xC0 If I put these in excel and calculate 7*256*256 + 220*256 + 192 I get 520264. Then when I divide this by 10000, I get 52.0264. And this matches the 52% shown by the car.
Currently my code for this conversion is: sv1 = (unsigned long)can_databuffer[4]; sv1 = sv1*65536UL; sv2 = (unsigned long)can_databuffer[5]; sv2 = sv2*256UL; sv3 = (unsigned long)can_databuffer[6]; sv3 = sv3;
car_SOC = (char)((unsigned long)(sv1 + sv2 + sv3 + 5000UL)/10000UL);
And the result for car_SOC is 49. How is this possible? And what would be the correct way to put these 3 bytes together and divide them by 10000 to get to the final value in char?
Thanks in advance, Nikolay
On Monday, June 22, 2015 3:58 PM, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Division is kind of nasty in 8bit. Actually very nasty. Unless you are dividing by 2, 4, 8, 16, etc. Divide by 2^N is the same as >>N, and that is the shortcut you want to do if at all possible.
That said, your solution seems ok, except the value[2] should be <<8, and you probably want to add 5000 just before divide by 10000, to round correctly.
If you don’t need too much precision, you might find an inaccurate kludge may be good enough.
For example:
205/2048 = 0.1000976562
and that is pretty close to 0.1. So multiply by 205, then shirt right 11 spaces is pretty close to divide by 10. Multiplication is much less expensive than division.
If you look at MiFromKm and KmFromMi in utils.c, you can see similar sorts of approximations to avoid big divisions.
Regards, Mark.
On 22 Jun, 2015, at 4:30 pm, Nikolay Shishkov <nshishkov@yahoo.com <mailto:nshishkov@yahoo.com>> wrote:
I have a situation where I need to convert a value that is stored as 3 bytes to a char. The can be for example 7ED95, which is reported in 3 consequtive bytes 07, ED, 95. This value in decimal is 519573, which corresponds to a 51.9% SOC. Is the proper way to do this conversion (from the 3 bytes to the value of 51) like this:
char soc = (char)(((unsigned long)value[1]<<16 + (unsigned long)value[2]<<16 + (unsigned long)value[3])/10000);
I have been doing stuff like that before with unions, but can't remember how either.
Thanks,
Nikolay
_______________________________________________ OvmsDev mailing list OvmsDev@lists.teslaclub.hk <mailto:OvmsDev@lists.teslaclub.hk> http://lists.teslaclub.hk/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.teslaclub.hk <mailto:OvmsDev@lists.teslaclub.hk> http://lists.teslaclub.hk/mailman/listinfo/ovmsdev <http://lists.teslaclub.hk/mailman/listinfo/ovmsdev>
_______________________________________________ OvmsDev mailing list OvmsDev@lists.teslaclub.hk http://lists.teslaclub.hk/mailman/listinfo/ovmsdev
First, 0x07DCC0 is supposed to be 515264, not 520264. Looks like you added 5000 in your spreadsheet. Second, you don't show your declarations for sv1 & sv2, which could be part of the problem. Personally, I would start with the following code (on a 32-bit machine): unsigned long sv = (unsigned)can_databuffer[4]; sv <<= 8; sv |= (unsigned)can_databuffer[5]; sv <<= 8; sv |= (unsigned)can_databuffer[6]; car_SOC = (sv + 5000) / 10000UL; or, if you want to follow Mark's general advice, the last line would be: car_SOC = ((sv + 5000) * 3355) >> 25; The 25 bit shift is exactly the same as dividing by 33554432, so multiplying by 3355 before shifting is the same as multiplying by 0.00009998679161 or dividing by 10001.321 (with only 0.0132% error). I'm just not sure whether 32-bit math is efficient on this platform. Since you're converting a 20-bit number to 7-bit (0%..100%), then I assume you could leave off the 3rd byte, can_databuffer[6], and still be accurate enough. unsigned short sv = (unsigned)can_databuffer[4]; sv <<= 8; sv |= (unsigned)can_databuffer[5]; car_SOC = ((sv + 33) * 13) >> 9; Note that the 16-bit value jump by about 40 for every 1% SOC, so I was adding 20 to round off. The actual value would be 19.53125 if float were efficient. That doesn't quite reach 100%, so I fudged by adding 33 instead. That's not enough to round up the lowest values too much, because 1% will still be 1%. The second code example above was a bit tricky, because lopping off the last byte divides the input by 256, but combining that with the original 1/10000 scaling means you need to divide by 39.0625 (I just did some calculations on an envelope to come up with the code above, but tested it with values of 100%, 99%, 2%, and 1% to make sure it's close enough). I can't quite calculate the percent error of dropping a byte and then using multiply/shift instead of divide. Maybe someone else can review this. Brian On Jun 24, 2015, at 4:35 PM, Nikolay Shishkov <nshishkov@yahoo.com> wrote:
I seem to not been able to grasp the idea and would appreciate a code example. For example byte1=0x07, byte2=0xDC, byte3=0xC0 If I put these in excel and calculate 7*256*256 + 220*256 + 192 I get 520264. Then when I divide this by 10000, I get 52.0264. And this matches the 52% shown by the car.
Currently my code for this conversion is: sv1 = (unsigned long)can_databuffer[4]; sv1 = sv1*65536UL; sv2 = (unsigned long)can_databuffer[5]; sv2 = sv2*256UL; sv3 = (unsigned long)can_databuffer[6]; sv3 = sv3;
car_SOC = (char)((unsigned long)(sv1 + sv2 + sv3 + 5000UL)/10000UL);
And the result for car_SOC is 49. How is this possible? And what would be the correct way to put these 3 bytes together and divide them by 10000 to get to the final value in char?
Thanks in advance, Nikolay
^this. Nice ;-) Mark.
On 25 Jun, 2015, at 2:47 pm, HONDA S-2000 <s2000@audiobanshee.com> wrote:
First, 0x07DCC0 is supposed to be 515264, not 520264. Looks like you added 5000 in your spreadsheet.
Second, you don't show your declarations for sv1 & sv2, which could be part of the problem.
Personally, I would start with the following code (on a 32-bit machine):
unsigned long sv = (unsigned)can_databuffer[4]; sv <<= 8; sv |= (unsigned)can_databuffer[5]; sv <<= 8; sv |= (unsigned)can_databuffer[6]; car_SOC = (sv + 5000) / 10000UL;
or, if you want to follow Mark's general advice, the last line would be:
car_SOC = ((sv + 5000) * 3355) >> 25;
The 25 bit shift is exactly the same as dividing by 33554432, so multiplying by 3355 before shifting is the same as multiplying by 0.00009998679161 or dividing by 10001.321 (with only 0.0132% error).
I'm just not sure whether 32-bit math is efficient on this platform. Since you're converting a 20-bit number to 7-bit (0%..100%), then I assume you could leave off the 3rd byte, can_databuffer[6], and still be accurate enough.
unsigned short sv = (unsigned)can_databuffer[4]; sv <<= 8; sv |= (unsigned)can_databuffer[5]; car_SOC = ((sv + 33) * 13) >> 9;
Note that the 16-bit value jump by about 40 for every 1% SOC, so I was adding 20 to round off. The actual value would be 19.53125 if float were efficient. That doesn't quite reach 100%, so I fudged by adding 33 instead. That's not enough to round up the lowest values too much, because 1% will still be 1%.
The second code example above was a bit tricky, because lopping off the last byte divides the input by 256, but combining that with the original 1/10000 scaling means you need to divide by 39.0625 (I just did some calculations on an envelope to come up with the code above, but tested it with values of 100%, 99%, 2%, and 1% to make sure it's close enough). I can't quite calculate the percent error of dropping a byte and then using multiply/shift instead of divide. Maybe someone else can review this.
Brian
On Jun 24, 2015, at 4:35 PM, Nikolay Shishkov <nshishkov@yahoo.com> wrote:
I seem to not been able to grasp the idea and would appreciate a code example. For example byte1=0x07, byte2=0xDC, byte3=0xC0 If I put these in excel and calculate 7*256*256 + 220*256 + 192 I get 520264. Then when I divide this by 10000, I get 52.0264. And this matches the 52% shown by the car.
Currently my code for this conversion is: sv1 = (unsigned long)can_databuffer[4]; sv1 = sv1*65536UL; sv2 = (unsigned long)can_databuffer[5]; sv2 = sv2*256UL; sv3 = (unsigned long)can_databuffer[6]; sv3 = sv3;
car_SOC = (char)((unsigned long)(sv1 + sv2 + sv3 + 5000UL)/10000UL);
And the result for car_SOC is 49. How is this possible? And what would be the correct way to put these 3 bytes together and divide them by 10000 to get to the final value in char?
Thanks in advance, Nikolay
_______________________________________________ OvmsDev mailing list OvmsDev@lists.teslaclub.hk http://lists.teslaclub.hk/mailman/listinfo/ovmsdev
Thank you all for the help with this. It was difficult for me to bend my mind around this.I was not able to test Brian's suggestion, but after many tests (printing bytes values and intermediate results) I ended up with something very similar.Thanks again,Nikolay On Thursday, June 25, 2015 8:52 AM, Mark Webb-Johnson <mark@webb-johnson.net> wrote: ^this. Nice ;-) Mark.
On 25 Jun, 2015, at 2:47 pm, HONDA S-2000 <s2000@audiobanshee.com> wrote:
First, 0x07DCC0 is supposed to be 515264, not 520264. Looks like you added 5000 in your spreadsheet.
Second, you don't show your declarations for sv1 & sv2, which could be part of the problem.
Personally, I would start with the following code (on a 32-bit machine):
unsigned long sv = (unsigned)can_databuffer[4]; sv <<= 8; sv |= (unsigned)can_databuffer[5]; sv <<= 8; sv |= (unsigned)can_databuffer[6]; car_SOC = (sv + 5000) / 10000UL;
or, if you want to follow Mark's general advice, the last line would be:
car_SOC = ((sv + 5000) * 3355) >> 25;
The 25 bit shift is exactly the same as dividing by 33554432, so multiplying by 3355 before shifting is the same as multiplying by 0.00009998679161 or dividing by 10001.321 (with only 0.0132% error).
I'm just not sure whether 32-bit math is efficient on this platform. Since you're converting a 20-bit number to 7-bit (0%..100%), then I assume you could leave off the 3rd byte, can_databuffer[6], and still be accurate enough.
unsigned short sv = (unsigned)can_databuffer[4]; sv <<= 8; sv |= (unsigned)can_databuffer[5]; car_SOC = ((sv + 33) * 13) >> 9;
Note that the 16-bit value jump by about 40 for every 1% SOC, so I was adding 20 to round off. The actual value would be 19.53125 if float were efficient. That doesn't quite reach 100%, so I fudged by adding 33 instead. That's not enough to round up the lowest values too much, because 1% will still be 1%.
The second code example above was a bit tricky, because lopping off the last byte divides the input by 256, but combining that with the original 1/10000 scaling means you need to divide by 39.0625 (I just did some calculations on an envelope to come up with the code above, but tested it with values of 100%, 99%, 2%, and 1% to make sure it's close enough). I can't quite calculate the percent error of dropping a byte and then using multiply/shift instead of divide. Maybe someone else can review this.
Brian
On Jun 24, 2015, at 4:35 PM, Nikolay Shishkov <nshishkov@yahoo.com> wrote:
I seem to not been able to grasp the idea and would appreciate a code example. For example byte1=0x07, byte2=0xDC, byte3=0xC0 If I put these in excel and calculate 7*256*256 + 220*256 + 192 I get 520264. Then when I divide this by 10000, I get 52.0264. And this matches the 52% shown by the car.
Currently my code for this conversion is: sv1 = (unsigned long)can_databuffer[4]; sv1 = sv1*65536UL; sv2 = (unsigned long)can_databuffer[5]; sv2 = sv2*256UL; sv3 = (unsigned long)can_databuffer[6]; sv3 = sv3;
car_SOC = (char)((unsigned long)(sv1 + sv2 + sv3 + 5000UL)/10000UL);
And the result for car_SOC is 49. How is this possible? And what would be the correct way to put these 3 bytes together and divide them by 10000 to get to the final value in char?
Thanks in advance, Nikolay
_______________________________________________ OvmsDev mailing list OvmsDev@lists.teslaclub.hk http://lists.teslaclub.hk/mailman/listinfo/ovmsdev
participants (3)
-
HONDA S-2000 -
Mark Webb-Johnson -
Nikolay Shishkov