CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

CayenneLPP data format

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Woody



Joined: 11 Sep 2003
Posts: 83
Location: Warmenhuizen - NL

View user's profile Send private message

CayenneLPP data format
PostPosted: Wed Oct 26, 2022 10:22 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Wed Oct 26, 2022 11:00 am     Reply with quote

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: 19537

View user's profile Send private message

PostPosted: Wed Oct 26, 2022 11:01 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Oct 26, 2022 1:15 pm     Reply with quote

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: 19537

View user's profile Send private message

PostPosted: Thu Oct 27, 2022 12:59 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Oct 27, 2022 1:36 am     Reply with quote

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: 19537

View user's profile Send private message

PostPosted: Thu Oct 27, 2022 1:59 am     Reply with quote

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!... Very Happy
Woody



Joined: 11 Sep 2003
Posts: 83
Location: Warmenhuizen - NL

View user's profile Send private message

PostPosted: Thu Oct 27, 2022 2:20 am     Reply with quote

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: 19537

View user's profile Send private message

PostPosted: Thu Oct 27, 2022 2:34 am     Reply with quote

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!. Very Happy

Touch wood, I have got it right....
Woody



Joined: 11 Sep 2003
Posts: 83
Location: Warmenhuizen - NL

View user's profile Send private message

PostPosted: Thu Oct 27, 2022 2:34 am     Reply with quote

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....
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group