Project

General

Profile

Decoding received byte stream (negative int) in python

Added by vrillusions about 5 years ago

So here’s the format of what comes in

OK 2 149 99 0 0 0 0 1 0

Each two byte pair represents an integer, one for each port. On port 4 (the ‘1 0’) I have a door sensor with the 1 meaning it’s open. Port 1 is a temperature sensor. To get around dealing with floats I multiply whatever temperature I get by 100. So 5.8 becomes 580. This all works correctly until negative temperatures come in to play.

So ‘149 99’ gives 25493 which after dividing by 100 gives 254.93. My guess is the actual temperature is 254.93 - 256 which gives 1.07C which sounds about right. And in fact subtracting 256 would work.. when I know it’s negative.
Currently I have something like this
<pre>
>>> byte1 = 149
>>> byte2 = 99
>>> val = byte1 + (byte2 << 8)
>>> val = val / 100.0
>>> val
254.93
>>> if val > 128:
… val = val
256

>>> val
–1.0699999999999932

Now that all works but once it gets to all this conversion stuff my pyton is a little rusty and figured someone else here has run in to this already. Tried searching but it seems like everyone’s conversion issue is always a little different.


Replies (5)

RE: Decoding received byte stream (negative int) in python - Added by gbloice about 5 years ago

I think there’s something up with your values. Some verbose info follows:

The float you have in the Jeenode is always a signed value, so you will need to convert it to a 16 bit signed value and scale it accordingly. You stated the scale value is 100, so presumably you have some C++ code similar to:

payload.temp = (int)(temp * 100);

The Arduino is a little-endian architecture so for a 16 bit integer transmitted over the wire the first byte is the low byte of the value and the second byte is the high part.

For signed integers (in 2’s complement form) the highest valued bit position in the high byte is the sign bit, the remaining bits hold the value, and the possible values range from 32768 to 32767. A negative number has the sign bit set, so negative numbers must have a value greater than 128 in the high byte, i.e. the second byte in the transmitted data.
Your data is apparently 149 99, so the high byte doesn’t have the sign bit set and thus doesn’t appear to be a negative number.
Can you post the code in your sketch that converts the float to an integer for transmission?
You can also try modifying the sketch to transmit know constants so you can check the conversion routines on your receiver, e.g. payload.temp = (int)(20.0 * 100);
You might also note that using a scale factor of 100, you are limiting your temperature range to approx +
32°, is this OK for your application? If not you can reduce the scale factor to 10, as I think it’s unlikely your temperature sensor has any more than 0.1° accuracy.

RE: Decoding received byte stream (negative int) in python - Added by gbloice about 5 years ago

I forgot to add,

When converting byte streams in Python I often use the struct module:

@
>>>import struct
>>>struct.unpack(‘<h’, chr(149) + chr(99))[0]
25493
@

RE: Decoding received byte stream (negative int) in python - Added by vrillusions about 5 years ago

Here’s the whole sketch on the jeenode (the remote transmitting one). TL;DR you are right I’m multiplying 100 before sending it.

// This reads a magnetic door sensor. At least with this style
// the sensor is normally open and the circuit closes when the
// two sensors are next to each other.

// Connect one wire to digital and other to ground

#include 

ISR(WDT_vect) { Sleepy::watchdogEvent(); }


//#define DEBUG          // Comment out to disable
#define JEELINK_ID 1   // Jeelink is id 1 (or 0 to broadcast)
#define NETGROUP 100   // Netgroup everyone is on
#define NODE_ID 2      // id of THIS node

#define DOOR_OPEN 1    // 1 means open
#define DOOR_CLOSED 0  // 0 means closed
#define DIO_OFFSET 3   // DIO is port num + 3

//Basic macro for debugging
#ifdef DEBUG
  #define DEBUG_PRINT(x) Serial.println(x)
#else
  #define DEBUG_PRINT(x)
#endif

// TEMP421 code thanks to http://ka1kjz.com (my bookmark no longer points to the source
// posting though, should still be on there somewhere
// TEMP421 pinout (pins are counted if sensor is on right):
// D - pin3
// G - pin1
// + - pin2
// A - pin4
PortI2C temp_port1 (1 /*, PortI2C::KHZ400 */); // Not sure what the commented out part is for
DeviceI2C temperature1 (temp_port1, 0x2A);  // I2C Address of 0x2A

// Currently just have sensor connected to port 4
//int DIO1 = 1 + DIO_OFFSET;
//int DIO2 = 2 + DIO_OFFSET;
//int DIO3 = 3 + DIO_OFFSET;
int DIO4 = 4 + DIO_OFFSET;

int temp_lo = 0;
int temp_high = 0;
float tempc = 0;

void get_temperature() {
  temperature1.send();
  temperature1.write(0x00); // high byte is on register 0
  temperature1.receive();
  temp_high = temperature1.read(1);
  temperature1.stop();

  temperature1.send();
  temperature1.write(0x10); // low byte on register 0x10
  temperature1.receive();
  temp_lo = temperature1.read(1);
  temperature1.stop();

  tempc = (float)temp_lo / 256;
  tempc = tempc + temp_high;
}

void setup() {
#ifdef DEBUG
  Serial.begin(57600);
#endif
  DEBUG_PRINT("Initializing");

  rf12_initialize(2, RF12_915MHZ, 100);

  pinMode(DIO4, INPUT);
  digitalWrite(DIO4, HIGH);  // turn on pull up
}

void loop() {
  int door_status_4 = digitalRead(DIO4);
  word payload[4] = {0,0,0,0};

  // 4th word is index 3
  payload[3] = door_status_4;

#ifdef DEBUG
  if (door_status_4 == DOOR_OPEN) {
    DEBUG_PRINT("Door open");
  }
  else {
    DEBUG_PRINT("Door closed");
  }
#endif

  get_temperature();

  DEBUG_PRINT(tempc);

  payload[0] = int(tempc * 100);

  rf12_sleep(RF12_WAKEUP);
  rf12_sendNow(JEELINK_ID, &payload, sizeof payload);
  rf12_sendWait(2);
  rf12_sleep(RF12_SLEEP);

  Sleepy::loseSomeTime(60000);
}

(edit)
Forgot to add that this is currently live at https://www.stathat.com/cards/L9pu2XfAza9I . Current outdoor temperature is 0c and it’s showing 3.75c and falling which is what I’d expect for it being inside a garage. And yeah at one point I was using struct and then I’m like what’s the most basic way to get this working.

The full listener is at https://github.com/vrillusions/jeelink-receiver (it’s still very much in alpha status but “it works ship it!”)

RE: Decoding received byte stream (negative int) in python - Added by Jas2 almost 5 years ago

Hi vrillusions

How are you ?

:)

Did you find a simple resource to show you how to use stathat ?

I am using https://thingspeak.com/channels/11286 (this is my live 15min update of temps in the house) which works.

Stathat looks good though and I see you can show latest month, day and week which is very desirable.

I only have a timestamp, temperature1, temperature2, temperature3 to publish, every 15 mins !

Can stathat put many plots on the same chart.

Does anyone know an internet of things online charting facility that does this and have views for latest month day and week ?

RE: Decoding received byte stream (negative int) in python - Added by vrillusions almost 5 years ago

Yep it looks like stathat can do that. Don’t have any examples since I still just have the one temperature graph. but I have a couple other stats I was messing with and I’m able to stack them together under the analyze tab. Worth mentioning that stathat is only free for up to 10 stats. You would have 3 (one for temperature1, one for temperature2, and one for temperature3). It will show it for past year, month, week, and day. There’s also 12 hour and 1hour zooms but not in free version. The pay version is really expensive. \$99/mo for unlimited. Rather steep jump there. But it does work nice and don’t expect to go over 10 any time soon.

Although it’s a more DIY thing but check out http://munin-monitoring.org/ . it’s not too difficult to make your own plugin. But since you’d be hosting it you don’t have any limits.

    (1-5/5)