Esempio n. 1
0
static int ht680_callback(bitbuffer_t *bitbuffer) {
	bitrow_t *bb = bitbuffer->bb;
	data_t *data;
	
	for (uint8_t row = 0;row < bitbuffer->num_rows;row++){
		uint8_t *b = bb[row];
		if(bitbuffer->bits_per_row[row] == 40 && //Length of packet is 40
			(b[0] & 0x50) == 0x50 && //Sync mask 01010000
			(b[1] & 0x0A) == 0x0A && //Address always mask 00001010
			(b[3] & 0x82) == 0x82 && //Buttons(4,3) always mask 10000010
			(b[4] & 0x0A) == 0x0A){  //Buttons(2,1) always mask 00001010
			b[0] = b[0] & 0x0F; //Clear sync
			
			data = data_make("model",	"",				DATA_STRING,	"HT680 Remote control",
							 "addres",	"Addres code",	DATA_FORMAT,	"%06X", DATA_INT, (b[0]<<16)+(b[1]<<8)+b[2],
							 "button1",	"Button 1",		DATA_STRING,	(((b[4]>>4) & 0x03) == 3) ? "PRESSED" : "",
							 "button2",	"Button 2",		DATA_STRING,	(((b[4]>>6) & 0x03) == 3) ? "PRESSED" : "",
							 "button3",	"Button 3",		DATA_STRING,	((((b[3]&0x7D)>>2) & 0x03) == 3) ? "PRESSED" : "",
							 "button4",	"Button 4",		DATA_STRING,	((((b[3]&0x7D)>>4) & 0x03) == 3) ? "PRESSED" : "",
							 NULL);
			data_acquired_handler(data);
			
			return 1;
		}
	}
Esempio n. 2
0
static int thermopro_tp12_sensor_callback(bitbuffer_t *bitbuffer) {
    int iTemp1, iTemp2, good = -1;
    float fTemp1, fTemp2;
    uint8_t *bytes;
    unsigned int device, value;
    char time_str[LOCAL_TIME_BUFLEN];
    data_t *data;

    // The device transmits 16 rows, let's check for 3 matching.
    // (Really 17 rows, but the last one doesn't match because it's missing a trailing 1.)
    good = bitbuffer_find_repeated_row(bitbuffer, 5, 40);
    if (good < 0) {
        return 0;
    }

    bytes = bitbuffer->bb[good];
    if (!bytes[0] && !bytes[1] && !bytes[2] && !bytes[3]) {
        return 0; // reduce false positives
    }

    // Note: the device ID changes randomly each time you replace the battery, so we can't early out based on it.
    // This is probably to allow multiple devices to be used at once.  When you replace the receiver batteries
    // or long-press its power button, it pairs with the first device ID it hears.
    device = bytes[0];

    if(debug_output) {
        // There is a mysterious checksum in bytes[4].  It may be the same as the checksum used by the TP-11,
        // which consisted of a lookup table containing, for each bit in the message, a byte to be xor-ed into
        // the checksum if the message bit was 1.  It should be possible to solve for that table using Gaussian
        // elimination, so dump some data so we can try this.

        // This format is easily usable by bruteforce-crc, after piping through | grep raw_data | cut -d':' -f2 
        // bruteforce-crc didn't find anything, though - this may not be a CRC algorithm specifically.
        fprintf(stderr,"thermopro_tp12_raw_data:");
        for(int bit_index = 0; bit_index < 40; ++bit_index){
            fputc(bitrow_get_bit(bytes, bit_index) + '0', stderr);
        }
        fputc('\n', stderr);
    }

    iTemp1 = ((bytes[2] & 0xf0) << 4) | bytes[1];
    iTemp2 = ((bytes[2] & 0x0f) << 8) | bytes[3];

    fTemp1 = (iTemp1 - 200) / 10.;
    fTemp2 = (iTemp2 - 200) / 10.;

    local_time_str(0, time_str);
    data = data_make("time",          "",            DATA_STRING, time_str,
                     "model",         "",            DATA_STRING, MODEL,
                     "id",            "Id",          DATA_FORMAT, "\t %d",   DATA_INT,    device,
                     "temperature_1_C", "Temperature 1 (Food)", DATA_FORMAT, "%.01f C", DATA_DOUBLE, fTemp1,
                     "temperature_2_C", "Temperature 2 (Barbecue)", DATA_FORMAT, "%.01f C", DATA_DOUBLE, fTemp2,
                     NULL);
    data_acquired_handler(data);
    return 1;
}
Esempio n. 3
0
static int current_cost_callback(bitbuffer_t *bitbuffer) {
    bitbuffer_invert(bitbuffer);
    bitrow_t *bb = bitbuffer->bb;
    uint8_t *b = bb[0];

    char time_str[LOCAL_TIME_BUFLEN];
    data_t *data;
    local_time_str(0, time_str);

    uint8_t init_pattern[] = {
        0b11001100, //8
        0b11001100, //16
        0b11001100, //24
        0b11001110, //32
        0b10010001, //40
        0b01011101, //45 (! last 3 bits is not init)
    };
    unsigned int start_pos = bitbuffer_search(bitbuffer, 0, 0, init_pattern, 45);

    if(start_pos == bitbuffer->bits_per_row[0]){
        return 0;
    }
    start_pos += 45;

    bitbuffer_t packet_bits = {0};

    start_pos = bitbuffer_manchester_decode(bitbuffer, 0, start_pos, &packet_bits, 0);

    uint8_t *packet = packet_bits.bb[0];
    // Read data
    if(packet_bits.bits_per_row[0] >= 56 && ((packet[0] & 0xf0) == 0) ){
        uint16_t device_id = (packet[0] & 0x0f) << 8 | packet[1];

        uint16_t watt0 = (packet[2] & 0x7F) << 8 | packet[3] ;
        uint16_t watt1 = (packet[4] & 0x7F) << 8 | packet[5] ;
        uint16_t watt2 = (packet[6] & 0x7F) << 8 | packet[7] ;
        data = data_make("time",          "",       DATA_STRING, time_str,
                "model",         "",              DATA_STRING, "CurrentCost TX", //TODO: it may have different CC Model ? any ref ?
                //"rc",            "Rolling Code",  DATA_INT, rc, //TODO: add rolling code b[1] ? test needed
                "dev_id",       "Device Id",     DATA_FORMAT, "%d", DATA_INT, device_id,
                "power0",       "Power 0",       DATA_FORMAT, "%d W", DATA_INT, watt0,
                "power1",       "Power 1",       DATA_FORMAT, "%d W", DATA_INT, watt1,
                "power2",       "Power 2",       DATA_FORMAT, "%d W", DATA_INT, watt2,
                //"battery",       "Battery",       DATA_STRING, battery_low ? "LOW" : "OK", //TODO is there some low battery indicator ?
                NULL);
        data_acquired_handler(data);
        return 1;
    }
    return 0;
}
Esempio n. 4
0
static int waveman_callback(bitbuffer_t *bitbuffer) {
    uint8_t *b = bitbuffer->bb[0];
    /* Two bits map to 2 states, 0 1 -> 0 and 1 1 -> 1 */
    int i;
    uint8_t nb[3] = {0};
    data_t *data;
    char id_str[2];

    /* @todo iterate through all rows */

    /* Reject codes of wrong length */
    if ( 24 != bitbuffer->bits_per_row[0])
      return 0;

    /*
     * Catch the case triggering false positive for other transmitters.
     * example: Brennstuhl RCS 2044SN
     * @todo is this message valid at all??? if not then put more validation below
     *       instead of this special case
     */
    if ( 0xFF == b[0] &&
         0xFF == b[1] &&
         0xFF == b[2] )
        return 0;

    /* Test if the bit stream conforms to the rule of every odd bit being set to one */
    if (((b[0]&0x55)==0x55) && ((b[1]&0x55)==0x55) && ((b[2]&0x55)==0x55) && ((b[3]&0x55)==0x00)) {
        /* Extract data from the bit stream */
        for (i=0 ; i<3 ; i++) {
            nb[i] |= ((b[i]&0xC0)==0xC0) ? 0x00 : 0x01;
            nb[i] |= ((b[i]&0x30)==0x30) ? 0x00 : 0x02;
            nb[i] |= ((b[i]&0x0C)==0x0C) ? 0x00 : 0x04;
            nb[i] |= ((b[i]&0x03)==0x03) ? 0x00 : 0x08;
        }

    id_str[0] = 'A'+nb[0];
    id_str[1] = 0;
    data = data_make("model", NULL,   DATA_STRING, "Waveman Switch Transmitter",
        "id", NULL,      DATA_STRING, id_str,
        "channel", NULL, DATA_INT, (nb[1]>>2)+1,
        "button", NULL,  DATA_INT, (nb[1]&3)+1,
        "state", NULL,   DATA_STRING, (nb[2]==0xe) ? "on" : "off",
        NULL);
    data_acquired_handler(data);

        return 1;
    }
    return 0;
}
Esempio n. 5
0
// Acurite 609 Temperature and Humidity Sensor
// 5 byte messages
// II ST TT HH CC
// II - ID byte, changes at each power up
// S - Status bitmask, normally 0x2,
//     0xa - battery low (bit 0x80)
// TTT - Temp in Celsius * 10, 12 bit with complement.
// HH - Humidity
// CC - Checksum
//
// @todo - see if the 3rd nybble is battery/status
//
static int acurite_th_callback(bitbuffer_t *bitbuf) {
    uint8_t *bb = NULL;
    int cksum, battery_low, valid = 0;
    float tempc;
    uint8_t humidity, id, status;
    data_t *data;

    local_time_str(0, time_str);

    for (uint16_t brow = 0; brow < bitbuf->num_rows; ++brow) {
        if (bitbuf->bits_per_row[brow] != 40) {
	    continue;
	}

	bb = bitbuf->bb[brow];

	cksum = (bb[0] + bb[1] + bb[2] + bb[3]);

	if (cksum == 0 || ((cksum & 0xff) != bb[4])) {
	    continue;
	}

	tempc = acurite_th_temperature(bb);
	id = bb[0];
	status = (bb[1] & 0xf0) >> 4;
	battery_low = status & 0x8;
	humidity = bb[3];

	data = data_make(
		     "time",		"",		DATA_STRING,	time_str,
		     "model",		"",		DATA_STRING,	"Acurite 609TXC Sensor",
		     "id",		"",		DATA_INT,	id,
		     "battery",		"",		DATA_STRING,	battery_low ? "LOW" : "OK",
		     "status",		"",		DATA_INT,	status,
		     "temperature_C", 	"Temperature",	DATA_FORMAT,	"%.1f C", DATA_DOUBLE, tempc,
		     "humidity",        "Humidity",	DATA_INT,	humidity,
		     NULL);

	data_acquired_handler(data);
	valid++;
    }

    if (valid)
        return 1;

    return 0;
}
Esempio n. 6
0
static int ht680_callback(bitbuffer_t *bitbuffer) {
	bitrow_t *bb = bitbuffer->bb;
	char time_str[LOCAL_TIME_BUFLEN];
	data_t *data;

	for (uint8_t row = 0;row < bitbuffer->num_rows;row++){
		uint8_t *b = bb[row];
		if(bitbuffer->bits_per_row[row] == 40 && //Length of packet is 40
			(b[0] & 0x50) == 0x50 && //Sync mask 01010000
			(b[1] & 0x0A) == 0x0A && //Address always mask 00001010
			(b[3] & 0x82) == 0x82 && //Buttons(4,3) always mask 10000010
			(b[4] & 0x0A) == 0x0A){  //Buttons(2,1) always mask 00001010
			b[0] = b[0] & 0x0F; //Clear sync

			// Tristate coding
			char tristate[21];
			char *p = tristate;
			for(uint8_t byte = 0; byte < 5; byte++){
				for(int8_t bit = 7; bit > 0; bit -= 2){
					switch ((b[byte] >> (bit-1)) & 0x03){
						case 0x00:	*p++ = '0'; break;
						case 0x01:	*p++ = '?'; break; //Invalid code 01
						case 0x02:	*p++ = 'Z'; break; //Floating state Z is 10
						case 0x03:	*p++ = '1'; break;
						default: *p++ = '!'; break; //Unknown error
					}
				}
			}
			*p = '\0';

			local_time_str(0, time_str);
			data = data_make(
							 "time",         "",     DATA_STRING, time_str,
							 "model",	"",				DATA_STRING,	"HT680 Remote control",
							 "tristate","Tristate code",DATA_STRING,	tristate,
							 "address",	"Address",	DATA_FORMAT,	"0x%06X", DATA_INT, (b[0]<<16)+(b[1]<<8)+b[2],
							 "button1",	"Button 1",		DATA_STRING,	(((b[4]>>4) & 0x03) == 3) ? "PRESSED" : "",
							 "button2",	"Button 2",		DATA_STRING,	(((b[4]>>6) & 0x03) == 3) ? "PRESSED" : "",
							 "button3",	"Button 3",		DATA_STRING,	((((b[3]&0x7D)>>2) & 0x03) == 3) ? "PRESSED" : "",
							 "button4",	"Button 4",		DATA_STRING,	((((b[3]&0x7D)>>4) & 0x03) == 3) ? "PRESSED" : "",
							 NULL);
			data_acquired_handler(data);

			return 1;
		}
	}
Esempio n. 7
0
static int pool_temperature_sensor_callback(bitbuffer_t *bitbuffer) {
	bitrow_t *bb = bitbuffer->bb;
	data_t *data;
	char time_str[LOCAL_TIME_BUFLEN];
    local_time_str(0, time_str);
	int i,device,channel;
	float fTemp;


	for(i=1;i<8;i++){
		if(bitbuffer->bits_per_row[i]!=28){
			/*10 24 bits frame*/
			return 0;
		}
	}

/*
AAAABBBB BBBBCCCC CCCCCCCC DDEE

A: ?
B: device id (changing only after reset)
C: templerature
D: channel number
E: ?
*/

        device=(((bb[1][0]&0xF)<<4)+((bb[1][1]&0xF0)>>4));
        fTemp=((signed short)(((bb[1][1]&0xF)<<8)+bb[1][2])/10.0);
	channel=(signed short)((bb[1][3]&0xC0)>>6);

	data = data_make("time", 	"", 			DATA_STRING, 					time_str,
                     "model",		"", 			DATA_STRING, 	"TFA pool temperature sensor",
		     "id",         	"Id",			DATA_FORMAT,	"\t %d",	DATA_INT,	device,
		     "channel",        	"Channel number",	DATA_FORMAT,	"\t %d",	DATA_INT,	channel,
                     "temperature_C",	"Temperature",		DATA_FORMAT, 	"%.01f C",	DATA_DOUBLE,	fTemp,
                     NULL);
    data_acquired_handler(data);
	
    return 1; 
	
}
static int generic_temperature_sensor_callback(bitbuffer_t *bitbuffer) {
	bitrow_t *bb = bitbuffer->bb;
	data_t *data;
	char time_str[LOCAL_TIME_BUFLEN];
    local_time_str(0, time_str);
	int i,device,battery;
	float fTemp;


	for(i=1;i<10;i++){
		if(bitbuffer->bits_per_row[i]!=24){
			/*10 24 bits frame*/
			return 0;
		}
	}

	//AAAAAAAA BBCCCCCC CCCCCCCC
	//AAAAAAAA     : ID
	//BBBB         : battery ?
	//CCCCCCCCCCCC : Temp

	device=(bb[1][0]);
	battery=(bb[1][1]&0xF0)>>4;
	fTemp=(float)((signed short)(((bb[1][1]&0x3f)*256+bb[1][2])<<2))/160.0;

	data = data_make("time", 	"", 			DATA_STRING, 					time_str,
                     "model",		"", 			DATA_STRING, 	"Generic temperature sensor 1",
		     "id",         	"Id",			DATA_FORMAT,	"\t %d",	DATA_INT,	device,
                     "temperature_C",	"Temperature",		DATA_FORMAT, 	"%.02f C",	DATA_DOUBLE,	fTemp,
                     "battery",      	"Battery?",		DATA_INT,					battery,
                     NULL);
    data_acquired_handler(data);
	
    return 1; 
	
}
Esempio n. 9
0
static int steelmate_callback(bitbuffer_t *bitbuffer) {
	//if (debug_output >= 1) {
	//	fprintf(stdout, "Steelmate TPMS decoder\n");
	//	bitbuffer_print(bitbuffer);
	//	fprintf(stdout, "\n");
	//}

	char time_str[LOCAL_TIME_BUFLEN];
	local_time_str(0, time_str);
	bitrow_t *bb = bitbuffer->bb;

	//Loop through each row of data
	for (int i = 0; i < bitbuffer->num_rows; i++)
	{
		//Payload is inverted Manchester encoded, and reversed MSB/LSB order
		uint8_t preAmble, ID1, ID2, p1, tempFahrenheit, tmpbattery_mV, payload_checksum, calculated_checksum;
		uint16_t sensorID, battery_mV;
		float pressurePSI;
		char sensorIDhex[7];
		data_t *data;

		//Length must be 72 bits to be considered a valid packet
		if (bitbuffer->bits_per_row[i] != 72)
			continue;

		//Valid preamble? (Note, the data is still wrong order at this point. Correct pre-amble: 0x00 0x00 0x01)
		if (bb[i][0] != 0x00 || bb[i][1] != 0x00 || bb[i][2] != 0x7f)
			continue;

		//Preamble
		preAmble = ~reverse8(bb[i][2]);

		//Sensor ID
		ID1 = ~reverse8(bb[i][3]);
		ID2 = ~reverse8(bb[i][4]);

		//Pressure is stored as twice the PSI
		p1 = ~reverse8(bb[i][5]);

		//Temperature is stored in Fahrenheit. Note that the datasheet claims operational to -40'C, but can only express values from -17.8'C
		tempFahrenheit = ~reverse8(bb[i][6]);

		//Battery voltage is stored as half the mV
		tmpbattery_mV = ~reverse8(bb[i][7]);

		//Checksum is a sum of all the other values
		payload_checksum = ~reverse8(bb[i][8]);
		calculated_checksum = preAmble + ID1 + ID2 + p1 + tempFahrenheit + tmpbattery_mV;
		if (payload_checksum != calculated_checksum)
			continue;

		sensorID = (ID1 << 8) + ID2;
		sprintf(sensorIDhex, "0x%04x", sensorID);
		pressurePSI = (float)p1 / 2;
		battery_mV = tmpbattery_mV * 2;

		data = data_make("time", "", DATA_STRING, time_str,
			"type", "", DATA_STRING, "TPMS",
			"model", "", DATA_STRING, "Steelmate",
			"id", "", DATA_STRING, sensorIDhex,
			"pressure_PSI", "", DATA_DOUBLE, pressurePSI,
			"temperature_F", "", DATA_DOUBLE, (float)tempFahrenheit,
			"battery_mV", "", DATA_INT, battery_mV,
			"mic", "Integrity", DATA_STRING, "CHECKSUM",
			NULL);
		data_acquired_handler(data);

		return 1;
	}

	//Was not a Steelmate TPMS after all
	return 0;
}
Esempio n. 10
0
static int oil_watchman_callback(bitbuffer_t *bitbuffer) {
	uint8_t *b;
	uint32_t unit_id;
	uint16_t depth = 0;
	uint16_t binding_countdown = 0;
	uint8_t flags;
	uint8_t maybetemp;
	double temperature;
	char time_str[LOCAL_TIME_BUFLEN];
	data_t *data;
	unsigned bitpos = 0;
	bitbuffer_t databits = {0};
	int events = 0;

	local_time_str(0, time_str);

	// Find a preamble with enough bits after it that it could be a complete packet
	while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, &preamble_pattern, 6)) + 136 <=
	       bitbuffer->bits_per_row[0]) {

		// Skip the matched preamble bits to point to the data
		bitpos += 6;

		bitpos = bitbuffer_manchester_decode(bitbuffer, 0, bitpos, &databits, 64);
		if (databits.bits_per_row[0] != 64)
			continue;

		b = databits.bb[0];

		// Check for postamble, depending on last data bit
		if (bitbuffer_search(bitbuffer, 0, bitpos, &postamble_pattern[b[7] & 1], 2) != bitpos)
			continue;

		if (b[7] != crc8le(b, 7, 0x31, 0))
			continue;

		// The unit ID changes when you rebind by holding a magnet to the
		// sensor for long enough; it seems to be time-based.
		unit_id = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];

		// 0x01: Rebinding (magnet held to sensor)
		// 0x08: Leak/theft alarm
		// top three bits seem also to vary with temperature (independently of maybetemp)
		flags = b[4];

		// Not entirely sure what this is but it might be inversely
		// proportional to temperature.
		maybetemp = b[5] >> 2;
		temperature = (double)(145.0 - 5.0 * maybetemp) / 3.0;
		if (flags & 1)
			// When binding, the countdown counts up from 0x51 to 0x5a
			// (as long as you hold the magnet to it for long enough)
			// before the device ID changes. The receiver unit needs
			// to receive this *strongly* in order to change its
			// allegiance.
			binding_countdown = b[6];
		else
			// A depth reading of zero indicates no reading. Even with
			// the sensor flat down on a table, it still reads about 13.
			depth = ((b[5] & 3) << 8) | b[6];

		data = data_make("time", "", DATA_STRING, time_str,
			"model", "", DATA_STRING, "Oil Watchman",
			"id", "", DATA_FORMAT, "%06x", DATA_INT, unit_id,
			"flags", "", DATA_FORMAT, "%02x", DATA_INT, flags,
			"maybetemp", "", DATA_INT, maybetemp,
			"temperature_C", "", DATA_DOUBLE, temperature,
			"binding_countdown", "", DATA_INT, binding_countdown,
			"depth", "", DATA_INT, depth,
			NULL);
		data_acquired_handler(data);
		events++;
	}
	return events;
}
Esempio n. 11
0
static int efergy_optical_callback(bitbuffer_t *bitbuffer) { 
	unsigned num_bits = bitbuffer->bits_per_row[0];
	uint8_t *bytes = bitbuffer->bb[0];
	double power, n_imp;
	double pulsecount;
	double seconds;
	data_t *data;
        char time_str[LOCAL_TIME_BUFLEN];
 	uint16_t crc;       
	uint16_t csum1;

	if (num_bits < 64 || num_bits > 100){ 
		return 0;
		}

	// The bit buffer isn't always aligned to the transmitted data, so
	// search for data start and shift out the bits which aren't part
	// of the data. The data always starts with 0000 (or 1111 if
	// gaps/pulses are mixed up).
	while ((bytes[0] & 0xf0) != 0xf0 && (bytes[0] & 0xf0) != 0x00) 
		{
		num_bits -= 1;
		if (num_bits < 64)
			{
			return 0;
			}

		for (unsigned i = 0; i < (num_bits + 7) / 8; ++i) 
			{
			bytes[i] <<= 1;
			bytes[i] |= (bytes[i + 1] & 0x80) >> 7;
			}
		}

	// Sometimes pulses and gaps are mixed up. If this happens, invert
	// all bytes to get correct interpretation.
	if (bytes[0] & 0xf0){
		for (unsigned i = 0; i < 12; ++i) 
			{
			bytes[i] = ~bytes[i];
			}
		}

	 if (debug_output){ 
     		fprintf(stdout,"Possible Efergy Optical: ");
     		bitbuffer_print(bitbuffer);
		}
	
	// Calculate checksum for bytes[0..10]
	// crc16 xmodem with start value of 0x00 and polynomic of 0x1021 is same as CRC-CCITT (0x0000)         
	// start of data, length of data=10, polynomic=0x1021, init=0x0000	  

	  csum1 = ((bytes[10]<<8)|(bytes[11]));

   	  crc = crc16_ccitt(bytes, 10, 0x1021, 0x0);

	  if (crc == csum1)
       		{ 
       		if (debug_output) {
		fprintf (stdout, "Checksum OK :) :)\n");
        	fprintf (stdout, "Calculated crc is 0x%02X\n", crc);
        	fprintf (stdout, "Received csum1 is 0x%02X\n", csum1);
		}
		// this setting depends on your electricity meter's optical output	
		n_imp = 3200;

		pulsecount =  bytes[8];
		seconds = bytes[10];

		//some logic for low pulse count not sure how I reached this formula
		if (pulsecount < 3)
			{
			power = ((pulsecount/n_imp) * (3600/seconds));
        		}
         	else
         		{
         		power = ((pulsecount/n_imp) * (3600/30));
         		}
         	/* Get time now */
	        local_time_str(0, time_str);

		 data = data_make("time",          "",            DATA_STRING, time_str,
        			"model",         "",            DATA_STRING, "Efergy Optical",
                 		"power",       "Power KWh",     DATA_FORMAT,"%.03f KWh", DATA_DOUBLE, power,
	               		   NULL);
	 	data_acquired_handler(data);
		return 0;
		}
		else 
			{
			if (debug_output)
				{ 
 				fprintf (stdout, "Checksum not OK !!!\n");
				fprintf(stdout, "Calculated crc is 0x%02X\n", crc);
				fprintf(stdout, "Received csum1 is 0x%02X\n", csum1);
				}
			}
		return 0;
		}
Esempio n. 12
0
static int tpms_citroen_decode(bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos) {
    char time_str[LOCAL_TIME_BUFLEN];
    data_t *data;
    unsigned int start_pos;
    bitbuffer_t packet_bits = {0};
    uint8_t *b;
    int state;
    char state_str[3];
    unsigned id;
    char id_str[9];
    int flags;
    int repeat;
    int pressure;
    int temperature;
    int battery;
    char code_str[7];
    int crc;

    bitbuffer_invert(bitbuffer);
    start_pos = bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, 88);
    b = packet_bits.bb[0];

    if (b[6] == 0 || b[7] == 0) {
        return 0; // sanity check failed
    }

    crc = b[1]^b[2]^b[3]^b[4]^b[5]^b[6]^b[7]^b[8]^b[9];
    if (crc != 0) {
        return 0; // bad checksum
    }

    state = b[0]; // not covered by CRC
    sprintf(state_str, "%02x", state);
    id = b[1]<<24 | b[2]<<16 | b[3]<<8 | b[4];
    sprintf(id_str, "%08x", id);
    flags = b[5]>>4;
    repeat = b[5]&0x0f;
    pressure = b[6];
    temperature = b[7];
    battery = b[8];
    sprintf(code_str, "%02x%02x%02x", pressure, temperature, battery);

    local_time_str(0, time_str);
    data = data_make(
        "time",         "",     DATA_STRING, time_str,
        "model",        "",     DATA_STRING, "Citroen",
        "type",         "",     DATA_STRING, "TPMS",
        "state",        "",     DATA_STRING, state_str,
        "id",           "",     DATA_STRING, id_str,
        "flags",        "",     DATA_INT, flags,
        "repeat",       "",     DATA_INT, repeat,
//        "pressure_bar", "Pressure",    DATA_FORMAT, "%.03f bar", DATA_DOUBLE, (double)pressure*0.0125,
//        "temperature_C", "Temperature", DATA_FORMAT, "%.0f C", DATA_DOUBLE, (double)temperature-50.0,
//        "battery_mV",   "Battery", DATA_INT, battery_mV,
        "code",         "",     DATA_STRING, code_str,
        "mic",          "",     DATA_STRING, "CHECKSUM",
        NULL);

    data_acquired_handler(data);
    return 1;
}
Esempio n. 13
0
/*
 * This callback handles several Acurite devices that use a very
 * similar RF encoding and data format:
 *:
 * - 592TXR temperature and humidity sensor
 * - 5-n-1 weather station
 * - 6045M Lightning Detectur with Temperature and Humidity
 */
static int acurite_txr_callback(bitbuffer_t *bitbuf) {
    int browlen, valid = 0;
    uint8_t *bb;
    float tempc, tempf, wind_dird, rainfall = 0.0, wind_speed, wind_speedmph;
    uint8_t humidity, sensor_status, sequence_num, message_type;
    char channel, *wind_dirstr = "";
    char channel_str[2];
    uint16_t sensor_id;
    int raincounter, temp, battery_low;
    uint8_t strike_count, strike_distance;
    data_t *data;


    local_time_str(0, time_str);

    if (debug_output > 1) {
        fprintf(stderr,"acurite_txr\n");
        bitbuffer_print(bitbuf);
    }

    for (uint16_t brow = 0; brow < bitbuf->num_rows; ++brow) {
	browlen = (bitbuf->bits_per_row[brow] + 7)/8;
	bb = bitbuf->bb[brow];

	if (debug_output > 1)
	    fprintf(stderr,"acurite_txr: row %d bits %d, bytes %d \n", brow, bitbuf->bits_per_row[brow], browlen);

	if ((bitbuf->bits_per_row[brow] < ACURITE_TXR_BITLEN ||
	     bitbuf->bits_per_row[brow] > ACURITE_5N1_BITLEN + 1) &&
	    bitbuf->bits_per_row[brow] != ACURITE_6045_BITLEN) {
	    if (debug_output > 1 && bitbuf->bits_per_row[brow] > 16)
		fprintf(stderr,"acurite_txr: skipping wrong len\n");
	    continue;
	}

	// There will be 1 extra false zero bit added by the demod.
	// this forces an extra zero byte to be added
	if (bb[browlen - 1] == 0)
	    browlen--;

	if (!acurite_checksum(bb,browlen - 1)) {
	    if (debug_output) {
		fprintf(stderr, "%s Acurite bad checksum:", time_str);
		for (uint8_t i = 0; i < browlen; i++)
		    fprintf(stderr," 0x%02x",bb[i]);
		fprintf(stderr,"\n");
	    }
	    continue;
	}

	if (debug_output) {
	    fprintf(stderr, "acurite_txr Parity: ");
	    for (uint8_t i = 0; i < browlen; i++) {
		fprintf(stderr,"%d",byteParity(bb[i]));
	    }
	    fprintf(stderr,"\n");
	}


	// tower sensor messages are 7 bytes.
	// @todo - see if there is a type in the message that
	// can be used instead of length to determine type
	if (browlen == ACURITE_TXR_BITLEN / 8) {
	    channel = acurite_getChannel(bb[0]);
	    sensor_id = acurite_txr_getSensorId(bb[0],bb[1]);
	    sensor_status = bb[2]; // @todo, uses parity? & 0x07f
	    humidity = acurite_getHumidity(bb[3]);
	    tempc = acurite_txr_getTemp(bb[4], bb[5]);
            sprintf(channel_str, "%c", channel);
            battery_low = sensor_status >>7;
		
            data = data_make(
                    "time",			"",		DATA_STRING,	time_str,
                    "model",	        	"",		DATA_STRING,	"Acurite tower sensor",
                    "id",			"",		DATA_INT,	sensor_id,
                    "channel",  		"",     	DATA_STRING, 	&channel_str,
                    "temperature_C", 	"Temperature",	DATA_FORMAT,	"%.1f C", DATA_DOUBLE, tempc,
                    "humidity",         "Humidity",	DATA_INT,	humidity,
                    "battery",          "Battery",    	DATA_INT, 	battery_low,
                    "status",		"",		DATA_INT,	sensor_status,
                    NULL);

            data_acquired_handler(data);
            valid++;
	}

	// The 5-n-1 weather sensor messages are 8 bytes.
	if (browlen == ACURITE_5N1_BITLEN / 8) {
        if (debug_output) {
            fprintf(stderr, "Acurite 5n1 raw msg: %02X %02X %02X %02X %02X %02X %02X %02X\n",
                bb[0], bb[1], bb[2], bb[3], bb[4], bb[5], bb[6], bb[7]);
        }
	    channel = acurite_getChannel(bb[0]);
        sprintf(channel_str, "%c", channel);        
	    sensor_id = acurite_5n1_getSensorId(bb[0],bb[1]);
	    sequence_num = acurite_5n1_getMessageCaught(bb[0]);
	    message_type = bb[2] & 0x3f;
        battery_low = (bb[2] & 0x40) >> 6;

	    if (message_type == ACURITE_MSGTYPE_WINDSPEED_WINDDIR_RAINFALL) {
            // Wind speed, wind direction, and rain fall
            wind_speed = acurite_getWindSpeed_kph(bb[3], bb[4]);
            wind_speedmph = kmph2mph(wind_speed);
            wind_dird = acurite_5n1_winddirections[bb[4] & 0x0f];
            wind_dirstr = acurite_5n1_winddirection_str[bb[4] & 0x0f];
            raincounter = acurite_getRainfallCounter(bb[5], bb[6]);
            if (acurite_5n1t_raincounter > 0) {
                // track rainfall difference after first run
                // FIXME when converting to structured output, just output
                // the reading, let consumer track state/wrap around, etc. 
                rainfall = ( raincounter - acurite_5n1t_raincounter ) * 0.01;
                if (raincounter < acurite_5n1t_raincounter) {
                    fprintf(stderr, "%s Acurite 5n1 sensor 0x%04X Ch %c, rain counter reset or wrapped around (old %d, new %d)\n",
                        time_str, sensor_id, channel, acurite_5n1t_raincounter, raincounter);
                    acurite_5n1t_raincounter = raincounter;
                }
            } else {
                // capture starting counter
                acurite_5n1t_raincounter = raincounter;
                fprintf(stderr, "%s Acurite 5n1 sensor 0x%04X Ch %c, Total rain fall since last reset: %0.2f\n",
                time_str, sensor_id, channel, raincounter * 0.01);
            }
                
            data = data_make(
                "time",         "",   DATA_STRING,    time_str,
                "model",        "",   DATA_STRING,    "Acurite 5n1 sensor",
                "sensor_id",    NULL,   DATA_FORMAT,    "0x%02X",   DATA_INT,       sensor_id,   
                "channel",      NULL,   DATA_STRING,    &channel_str,
                "sequence_num",  NULL,   DATA_INT,      sequence_num,
                "battery",      NULL,   DATA_STRING,    battery_low ? "OK" : "LOW",
                "message_type", NULL,   DATA_INT,       message_type,
                "wind_speed",   NULL,   DATA_FORMAT,    "%.1f mph", DATA_DOUBLE,     wind_speedmph,
                "wind_dir_deg", NULL,   DATA_FORMAT,    "%.1f", DATA_DOUBLE,    wind_dird,
                "wind_dir",     NULL,   DATA_STRING,    wind_dirstr,
                "rainfall_accumulation",     NULL,   DATA_FORMAT,    "%.2f in", DATA_DOUBLE,    rainfall,
                "raincounter_raw",  NULL,   DATA_INT,   raincounter,
                NULL);

            data_acquired_handler(data);

	    } else if (message_type == ACURITE_MSGTYPE_WINDSPEED_TEMP_HUMIDITY) {
            // Wind speed, temperature and humidity
            wind_speed = acurite_getWindSpeed_kph(bb[3], bb[4]);
            wind_speedmph = kmph2mph(wind_speed);
            tempf = acurite_getTemp(bb[4], bb[5]);
            tempc = fahrenheit2celsius(tempf);
            humidity = acurite_getHumidity(bb[6]);

            data = data_make(
                "time",         "",   DATA_STRING,    time_str,
                "model",        "",   DATA_STRING,    "Acurite 5n1 sensor",
                "sensor_id",    NULL,   DATA_FORMAT,    "0x%02X",   DATA_INT,       sensor_id,   
                "channel",      NULL,   DATA_STRING,    &channel_str,
                "sequence_num",  NULL,   DATA_INT,      sequence_num,
                "battery",      NULL,   DATA_STRING,    battery_low ? "OK" : "LOW",
                "message_type", NULL,   DATA_INT,       message_type,
                "wind_speed",   NULL,   DATA_FORMAT,    "%.1f mph", DATA_DOUBLE,     wind_speedmph,
                "temperature_F", 	"temperature",	DATA_FORMAT,    "%.1f F", DATA_DOUBLE,    tempf,
                "humidity",     NULL,	DATA_FORMAT,    "%d",   DATA_INT,   humidity,
                NULL);
            data_acquired_handler(data);
            
	    } else {
            fprintf(stderr, "%s Acurite 5n1 sensor 0x%04X Ch %c, Status %02X, Unknown message type 0x%02x\n",
                time_str, sensor_id, channel, bb[3], message_type);
	    }
	}

	if (browlen == ACURITE_6045_BITLEN / 8) {
	    channel = acurite_getChannel(bb[0]);  // same as TXR
	    sensor_id = (bb[1] << 8) | bb[2];     // TBD 16 bits or 20?
	    humidity = acurite_getHumidity(bb[3]);  // same as TXR
	    message_type = bb[4] & 0x7f;
	    temp = bb[5] & 0x7f; // TBD Not sure if this is the temp.
	    strike_count = bb[6] & 0x7f;
	    strike_distance = bb[7] & 0x7f;


	    printf("%s Acurite lightning 0x%04X Ch %c Msg Type 0x%02x: %d C %d %% RH Strikes %d Distance %d -",
		   time_str, sensor_id, channel, message_type, temp, humidity, strike_count, strike_distance);

	    // FIXME Temporarily dump message data until the decoding improves.
	    // Include parity indicator.
	    for (int i=0; i < browlen; i++) {
		char pc;
		pc = byteParity(bb[i]) == 0 ? ' ' : '*';
		fprintf(stdout, " %02x%c", bb[i], pc);
	    }
	    printf("\n");
	}

    }
Esempio n. 14
0
static int nexus_callback(bitbuffer_t *bitbuffer) {
    bitrow_t *bb = bitbuffer->bb;
    data_t *data;

    char time_str[LOCAL_TIME_BUFLEN];

    if (debug_output > 1) {
       fprintf(stderr,"Possible Nexus: ");
       bitbuffer_print(bitbuffer);
    }

    uint8_t id;
    uint8_t channel;
    int16_t temp;
    uint8_t humidity;
    int r = bitbuffer_find_repeated_row(bitbuffer, 3, 36);

    /** The nexus protocol will trigger on rubicson data, so calculate the rubicson crc and make sure
      * it doesn't match. By guesstimate it should generate a correct crc 1/255% of the times.
      * So less then 0.5% which should be acceptable.
      */
    if (!rubicson_crc_check(bb) &&
        r >= 0 &&
        bitbuffer->bits_per_row[r] <= 37 && // we expect 36 bits but there might be a trailing 0 bit
        bb[r][0] != 0 &&
        bb[r][1] != 0 &&
        bb[r][2] != 0 &&
        bb[r][3] != 0) {

        /* Get time now */
        local_time_str(0, time_str);

        /* Nibble 0,1 contains id */
        id = bb[r][0];

        channel = (bb[r][1]&0x03) + 1;

        /* Nible 3,4,5 contains 12 bits of temperature
         * The temerature is signed and scaled by 10 */
        temp = (int16_t)((uint16_t)(bb[r][1] << 12) | (bb[r][2] << 4));
        temp = temp >> 4;
        humidity = (uint8_t)(((bb[r][3]&0x0F)<<4)|(bb[r][4]>>4));

        // Thermo
        if (bb[r][3] == 0xF0) {
        data = data_make("time",          "",            DATA_STRING, time_str,
                         "model",         "",            DATA_STRING, "Nexus Temperature",
                         "id",            "House Code",  DATA_INT, id,
                         "channel",       "Channel",     DATA_INT, channel,
                         "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp/10.0,
                         NULL);
        data_acquired_handler(data);
        }
        // Thermo/Hygro
        else {
        data = data_make("time",          "",            DATA_STRING, time_str,
                         "model",         "",            DATA_STRING, "Nexus Temperature/Humidity",
                         "id",            "House Code",  DATA_INT, id,
                         "channel",       "Channel",     DATA_INT, channel,
                         "temperature_C", "Temperature", DATA_FORMAT, "%.02f C", DATA_DOUBLE, temp/10.0,
                         "humidity",      "Humidity",    DATA_FORMAT, "%u %%", DATA_INT, humidity,
                         NULL);
        data_acquired_handler(data);
        }
        return 1;
    }
Esempio n. 15
0
static int template_callback(bitbuffer_t *bitbuffer) {
    char time_str[LOCAL_TIME_BUFLEN];
    uint8_t *bb;
    uint16_t brow, row_nbytes;
    uint16_t sensor_id = 0;
    uint8_t msg_type, r_crc, c_crc;
    int16_t value;
    data_t *data;
    int valid = 0;


    /*
     * Early debugging aid to see demodulated bits in buffer and
     * to determine if your limit settings are matched and firing
     * this callback.
     *
     * 1. Enable with -D -D (debug level of 2)
     * 2. Delete this block when your decoder is working
     */
    //    if (debug_output > 1) {
    //        fprintf(stderr,"new_tmplate callback:\n");
    //        bitbuffer_print(bitbuffer);
    //    }

    local_time_str(0, time_str);

    /*
     * bit buffer will contain multiple rows, many of them empty.
     * Typically a complete message will be contained in a single
     * row if long and reset limits are set correctly.
     * May contain multiple message repeats.
     * Message might not appear in row 0, if protocol uses
     * start/preamble periods of different lengths.
     */

    for (brow = 0; brow < bitbuffer->num_rows; ++brow) {
    bb = bitbuffer->bb[brow];

    /*
     * Validate message and reject invalid messages as
     * early as possible before attempting to parse data..
     *
     * Check "message envelope"
     * - valid message length
     * - valid preamble/device type/fixed bits if any
     * - Data integrity checks (CRC/Checksum/Parity)
     */

    if (bitbuffer->bits_per_row[brow] != 68)
        continue;

    /*
     * number of bytes in row.
     *
     * Number of decoded bits may not be a multiple of 8.
     * bitbuffer row will have enough bytes to contain
     * all bytes, so round up.
     */
    row_nbytes = (bitbuffer->bits_per_row[brow] + 7)/8;


    /*
     * Reject rows that don't start with the correct start byte
     * Example message should start with 0xAA
     */
    if (bb[0] != MYDEVICE_STARTBYTE)
        continue;

    /*
     * Check message integrity (CRC/Checksum/parity)
     *
     * Example device uses CRC-8
     */
    r_crc = bb[row_nbytes - 1];
    c_crc = crc8(bb, row_nbytes - 1, MYDEVICE_CRC_POLY, MYDEVICE_CRC_INIT);
    if (r_crc != c_crc) {
        // example debugging output
        if (debug_output >= 1)
        fprintf(stderr, "%s new_tamplate bad CRC: calculated %02x, received %02x\n",
            time_str, c_crc, r_crc);

        // reject row
        continue;
    }

    /*
     * Now that message "envelope" has been validated,
     * start parsing data.
     */

    msg_type = bb[1];
    sensor_id = bb[2] << 8 | bb[3];
    value = bb[4] << 8 | bb[5];

    if (msg_type != MYDEVICE_MSG_TYPE) {
        /*
         * received an unexpected message type
         * could be a bad message or a new message not
         * previously seen.  Optionally log debug putput.
         */
        continue;
    }

    data = data_make("time", "", DATA_STRING, time_str,
        "model", "", DATA_STRING, "New Template",
        "id", "", DATA_INT, sensor_id,
        "data","", DATA_INT, value,
        NULL);

    data_acquired_handler(data);

    valid++;
    }

    // Return 1 if message successfully decoded
    if (valid)
    return 1;

    return 0;
}
Esempio n. 16
0
static int newkaku_callback(bitbuffer_t *bitbuffer) {
    /* Two bits map to 2 states, 0 1 -> 0 and 1 1 -> 1 */
    /* Status bit can be 1 1 -> 1 which indicates DIM value. 4 extra bits are present with value */
    /*start pulse: 1T high, 10.44T low */
    /*- 26 bit:  Address */
    /*- 1  bit:  group bit*/
    /*- 1  bit:  Status bit on/off/[dim]*/
    /*- 4  bit:  unit*/
    /*- [4 bit:  dim level. Present if [dim] is used, but might be present anyway...]*/
    /*- stop pulse: 1T high, 40T low */
    data_t *data;
    bitrow_t *bb = bitbuffer->bb;
    int i;
    uint8_t tmp = 0;
    uint8_t unit = 0;
    uint8_t packet = 0;
    uint8_t bitcount = 0;
    uint32_t kakuid = 0;
    uint8_t dv = 0;
    char *group_call, *command, *dim;
    char time_str[LOCAL_TIME_BUFLEN];
    local_time_str(0, time_str);

    if (bb[0][0] == 0xac || bb[0][0] == 0xb2) {//always starts with ac or b2
        // first bit is from startbit sequence, not part of payload!
        // check protocol if value is 10 or 01, else stop processing as it is no valid KAKU packet!
        //get id=24bits, remember 1st 1 bit = startbit, no payload!
        for (packet = 0; packet < 6; packet++) {//get first part kakuid
            tmp = bb[0][packet] << 1;
            if ((bb[0][packet + 1]&(1 << 7)) != 0) {// if set add bit to current
                tmp++;
            }

            for (bitcount = 0; bitcount < 8; bitcount += 2) {//process bitstream, check protocol!

                if (((tmp << bitcount & (0x80)) == 0x80)&((tmp << bitcount & (0x40)) == 0)) {
                    //add 1
                    kakuid = kakuid << 1;
                    kakuid++;
                } else
                    if (((tmp << bitcount & (0x80)) == 0)&((tmp << bitcount & (0x40)) == 0x40)) {
                    kakuid = kakuid << 1;
                    //add 0
                } else {
                    return 0; //00 and 11 indicates packet error. Do exit, no valid packet
                }
            }
        }
        tmp = bb[0][6] << 1; //Get last part ID
        for (bitcount = 0; bitcount < 4; bitcount += 2) {
            if (((tmp << bitcount & (0x80)) == 0x80)&((tmp << bitcount & (0x40)) == 0)) {
                //add 1
                kakuid = kakuid << 1;
                kakuid++;
            } else
                if (((tmp << bitcount & (0x80)) == 0)&((tmp << bitcount & (0x40)) == 0x40)) {
                //= add bit on kakuid
                kakuid = kakuid << 1;
                //add 0
            } else {
                return 0; //00 and 11 indicates packet error. no valid packet! do exit
            }
        }
        //Get unit ID
        tmp = bb[0][7] << 1;
        if ((bb[0][8]&(1 << 7)) != 0) {// if set add bit to current
            tmp++;
        }
        for (bitcount = 0; bitcount < 8; bitcount += 2) {//process bitstream, check protocol!
            if (((tmp << bitcount & (0x80)) == 0x80)&((tmp << bitcount & (0x40)) == 0)) {
                //add 1
                unit = unit << 1;
                unit++;
            } else
                if (((tmp << bitcount & (0x80)) == 0)&((tmp << bitcount & (0x40)) == 0x40)) {
                unit = unit << 1;
                //add 0
            } else {
                return 0; //00 and 11 indicates packet error. Do exit, no valid packet
            }
        }
        group_call = (((bb[0][6] & (0x04)) == 0x04)&((bb[0][6] & (0x02)) == 0)) ? "Yes" : "No";
        command = (((bb[0][6] & (0x01)) == 0x01)&((bb[0][7] & (0x80)) == 0)) ? "On" : "Off";
        if (((bb[0][6] & (0x01)) == 0x01)&((bb[0][7] & (0x80)) == 0x80)) {//11 indicates DIM command, 4 extra bits indicate DIM value
            dim = "Yes";
            tmp = bb[0][8] << 1; // get packet, loose first bit

            if ((bb[0][9]&(1 << 7)) != 0) {// if bit is set Add to current packet
                tmp++;
                for (bitcount = 0; bitcount < 8; bitcount += 2) {//process last bit outside
                    if (((tmp << bitcount & (0x80)) == 0x80)&((tmp << bitcount & (0x40)) == 0)) {
                        //add 1
                        dv = dv << 1;
                        dv++;
                    } else
                        if (((tmp << bitcount & (0x80)) == 0)&((tmp << bitcount & (0x40)) == 0x40)) {
                        dv = dv << 1;
                        //add 0
                    } else {
                        return 0; //00 and 11 indicates packet error. Do exit, no valid packet
                    }
                }
            }
        } else {
            dim = "No";
        }

        data = data_make("time",          "",            DATA_STRING, time_str,
                         "model",         "",            DATA_STRING, "KlikAanKlikUit Wireless Switch",
                         "id",            "",            DATA_INT, kakuid,
                         "unit",          "Unit",        DATA_INT, unit,
                         "group_call",    "Group Call",  DATA_STRING, group_call,
                         "command",       "Command",     DATA_STRING, command,
                         "dim",           "Dim",         DATA_STRING, dim,
                         "dim_value",     "Dim Value",   DATA_INT, dv,
                         NULL);
        data_acquired_handler(data);

        return 1;
    }
    return 0;
}