View previous topic :: View next topic |
Author |
Message |
Woody
Joined: 11 Sep 2003 Posts: 83 Location: Warmenhuizen - NL
|
CayenneLPP data format |
Posted: Wed Oct 26, 2022 10:22 am |
|
|
As part of a current project I have to send a couple of battery voltages over LoRaWan (The Things Network / TTN ). For payload encoding TTN uses something called Cayenne LPP. This is supposedly a smart and small protocol (I am not completely convinced).
Anyway, an analog value in Cayenne is the decimal equivalent of a hex value. Difficult for me to explain, but for instance, if I want to send the value 12.00 I have to send the hex value 0x04b0 in the packet.
The hardware takes in the battery voltage, over a 6k8/1k resistor divider. So 16V in gives me 2.051V that I lead to the PIC. There the ADC (VREF2.048V) delivers a 12 bit ADC value. So the 12,7V of a healthy 12V lead acid battery gives me a readout of 0xcb8.
This hex value has to be translated to 0x4F6, to give me the wanted 12.7 in Cayenne.
After staring at this for some time I came up with the next Frankencode:
Code: |
void calc_bat2(int16 value) {
// My reasoning: the 12bits ADC with a VREF of 2.048V gives me 2.048V/4096 = .5mv / step. At 2.048V the real battery voltage is 16V,
// which gives me 16V/4096 = 3.9 mV / step
// This makes 1 battery volt 1V/3.9mV per step = 256 steps. .1V = 25 steps, and .01V = 2 steps.
int16 i=0;
while (value >=256) {
i+=100;
value-=256;
}
while (value >= 25) {
i+=10;
value-=25;
}
while (value >= 2) {
i+=1;
value-=2;
}
// Stuff the calculated values in the loramsg[] array
loramsg[BAT2_VOL_WH]=make8(i,1);
loramsg[BAT2_VOL_WL]=make8(i,0);
}
|
It does the job, sort-of, but I do not like it. Has anyone a suggestion for a better way to do this? |
|
|
Jerson
Joined: 31 Jul 2009 Posts: 125 Location: Bombay, India
|
|
Posted: Wed Oct 26, 2022 11:00 am |
|
|
you could try something like this
Code: |
// more accuracy using long variables
value = ((int32)value * 39L)/100
|
or perhaps if you can tolerate some error in calculation
Code: |
// implement value = value*(39/100) using only int16
value = value / 10;
value = 39 * value;
value = value / 10;
|
Hope this helps _________________ Regards
Jerson Fernandes |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19548
|
|
Posted: Wed Oct 26, 2022 11:01 am |
|
|
The Cayenne LPP format is a 16bit signed integer *100.
so 12=1200 = 0x4B0
Your ADC seems to be returning 3.9mV/bit. So you can simply code this
as:
Code: |
void calc_bat2(int16 value) {
signed int32 temp;
temp=value*620LL;
temp=temp/256;
// Stuff the calculated values in the loramsg[] array
loramsg[BAT2_VOL_WH]=make8(temp,1);
loramsg[BAT2_VOL_WL]=make8(temp,0);
}
|
This gives an efficient division by 2.422, which is the needed conversion.
Edit - reverse the division and the multiplication |
|
|
Woody
Joined: 11 Sep 2003 Posts: 83 Location: Warmenhuizen - NL
|
|
Posted: Wed Oct 26, 2022 1:15 pm |
|
|
Thanks guys, for putting me in the right direction.
I am not completely clear about the solution, however. If I use the proposed values I get a very large voltage (77).
I get that I need to divide by some power of 2. So if I want an outcome of 0x4F6 (1270) while I have an ACD measured value of 0xCB8 (3256), I multiply 1270 * 256 and divide the outcome by 3256 I get 99.85.
If I then change the 620LL to 100LL I get Cayenne values close to the ones I measure, but still with some error. What do I misunderstand? I searched high and low but have to ask: What is the function of the LL? Some sort of alignment? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19548
|
|
Posted: Thu Oct 27, 2022 12:59 am |
|
|
The point was I got the division and the multiplication the wrong way round.
You need:
Code: |
temp=value*256LL;
temp=temp/620;
|
|
|
|
Woody
Joined: 11 Sep 2003 Posts: 83 Location: Warmenhuizen - NL
|
|
Posted: Thu Oct 27, 2022 1:36 am |
|
|
Ah, I see. That does give me the expected output :-)
For my understanding, how did you get to these numbers? And what does the LL notation do?
Kind regards,
Paul |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19548
|
|
Posted: Thu Oct 27, 2022 1:59 am |
|
|
LL forces the constant to be an int32. Results in this multiplication being
done as int32. Could overrun otherwise.
The numbers are down to thinking. The value needs to be a normal result
times 100. So for a result of 12, we need 1200. Now your ADC is going to
give you 3076 for this. So we need to divide by 2.56, but do this in integer.
I got it the wrong way round the first time, but in fact the most efficient
way to do this is to do:
*100
/256
That should give the right result, and /256 in binary is one of the fastest
divisions.
1/2.56 = 0.39
0.39 *256 (since this is the simple division - you can actually not do this
at all by simply taking bytes 1 & 2 of the result value instead of 0 & 1).
gives just under 100.
So:
Code: |
void calc_bat2(int16 value) {
signed int32 temp;
temp=value*100LL;
// Stuff the calculated values in the loramsg[] array
loramsg[BAT2_VOL_WH]=make8(temp,2); //note taking the second
loramsg[BAT2_VOL_WL]=make8(temp,1); //ant third bytes.
}
|
Should be really efficient!... |
|
|
Woody
Joined: 11 Sep 2003 Posts: 83 Location: Warmenhuizen - NL
|
|
Posted: Thu Oct 27, 2022 2:20 am |
|
|
WOW!
I have not looked at the code yet, but it took the 'ROM used down form 5984 to 5660 bytes. A lot more efficient.
As always, thanks for the explanation. I learn from it. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19548
|
|
Posted: Thu Oct 27, 2022 2:34 am |
|
|
The basic 'idea' is a really useful one in control electronics. If you need
to do something equivalent to an FP operation, you may well be able to do
is with scaled integers. If you then remember that you can divide an integer
by 256, by just taking the upper bytes (in your case you are lucky since
though the format supports 'signed' your values are always going to be
+ve, so you don't have to worry about the sign bit).
So multiplying the float division we want by 256, we get:
0.39*256=99.84
Now this is so close to 100, that the error will be tiny. Result in computing
terms a really efficient way of getting the answer!.
Touch wood, I have got it right.... |
|
|
Woody
Joined: 11 Sep 2003 Posts: 83 Location: Warmenhuizen - NL
|
|
Posted: Thu Oct 27, 2022 2:34 am |
|
|
WOW!
I have not looked in detail at the code yet, but it takes the 'ROM used' down from 5984 to 5660 bytes. A lot more efficient.
As always, thanks for the explanation. I learn from it!
Regards,
Paul
Sorry, double posting.... |
|
|
|