static int danfoss_CFR_callback(bitbuffer_t *bitbuffer) { bitrow_t *bb = bitbuffer->bb; // Validate package unsigned bits = bitbuffer->bits_per_row[0]; if (bits >= 246 && bits <= 262) { // Package is likely 254 always fprintf(stdout, "Danfoss CFR Thermostat:\n"); bitbuffer_print(bitbuffer); return 1; } return 0; }
int main(int argc, char **argv) { fprintf(stderr, "bitbuffer:: test\n"); bitbuffer_t bits = {0}; fprintf(stderr, "TEST: bitbuffer:: The empty buffer\n"); bitbuffer_print(&bits); fprintf(stderr, "TEST: bitbuffer:: Add 1 bit\n"); bitbuffer_add_bit(&bits, 1); bitbuffer_print(&bits); fprintf(stderr, "TEST: bitbuffer:: Add 1 new row\n"); bitbuffer_add_row(&bits); bitbuffer_print(&bits); fprintf(stderr, "TEST: bitbuffer:: Fill row\n"); for (int i=0; i < BITBUF_COLS*8; ++i) { bitbuffer_add_bit(&bits, i%2); } bitbuffer_print(&bits); fprintf(stderr, "TEST: bitbuffer:: Add row and fill 1 column too many\n"); bitbuffer_add_row(&bits); for (int i=0; i <= BITBUF_COLS*8; ++i) { bitbuffer_add_bit(&bits, i%2); } bitbuffer_print(&bits); fprintf(stderr, "TEST: bitbuffer:: Clear\n"); bitbuffer_clear(&bits); bitbuffer_print(&bits); fprintf(stderr, "TEST: bitbuffer:: Add 1 row too many\n"); for (int i=0; i <= BITBUF_ROWS; ++i) { bitbuffer_add_row(&bits); } bitbuffer_add_bit(&bits, 1); bitbuffer_print(&bits); return 0; }
static int lightwave_rf_callback(bitbuffer_t *bitbuffer) { bitrow_t *bb = bitbuffer->bb; // Validate package // Transmitted pulses are always 72 // Pulse 72 (delimiting "1" is not demodulated, as gap becomes End-Of-Message - thus expected length is 71 if ((bitbuffer->bits_per_row[0] == 71) && (bitbuffer->num_rows == 1)) // There should be only one message (and we use the rest...) { // Polarity is inverted bitbuffer_invert(bitbuffer); // Expand all "0" to "10" (bit stuffing) // row_in = 0, row_out = 1 bitbuffer_add_row(bitbuffer); for (unsigned n=0; n < bitbuffer->bits_per_row[0]; ++n) { if (bitrow_get_bit(bb[0], n)) { bitbuffer_add_bit(bitbuffer, 1); } else { bitbuffer_add_bit(bitbuffer, 1); bitbuffer_add_bit(bitbuffer, 0); } } // Check length is correct // Due to encoding there will be two "0"s per byte, thus message grows to 91 bits if (bitbuffer->bits_per_row[1] != 91) return 0; // Check initial delimiter bit is "1" unsigned bit_idx = 0; uint8_t delimiter_bit = bitrow_get_bit(bb[1], bit_idx++); if (delimiter_bit == 0) return 0; // Decode error // Strip delimiter bits // row_in = 1, row_out = 2 bitbuffer_add_row(bitbuffer); for(unsigned n=0; n<10; ++n) { // We have 10 bytes delimiter_bit = bitrow_get_bit(bb[1], bit_idx++); if (delimiter_bit == 0) return 0; // Decode error for(unsigned m=0; m<8; ++m) { bitbuffer_add_bit(bitbuffer, bitrow_get_bit(bb[1], bit_idx++)); } } // Final delimiter bit will be missing - so do not check... // Decode bytes to nibbles // row_in = 2, row_out = 3 bitbuffer_add_row(bitbuffer); for(unsigned n=0; n<10; ++n) { // We have 10 bytes/nibbles int nibble = lightwave_rf_nibble_from_byte(bb[2][n]); if (nibble < 0) { if (debug_output) { fprintf(stderr, "LightwaveRF. Nibble decode error %X, idx: %u\n", bb[2][n], n); bitbuffer_print(bitbuffer); } return 0; // Decode error } for (unsigned m=0; m<4; ++m) { // Add nibble one bit at a time... bitbuffer_add_bit(bitbuffer, (nibble & (8 >> m)) >> (3-m)); } } // Print out generic decode // Decoded nibbles are in row 3 fprintf(stdout, "LightwaveRF:\n"); fprintf(stdout, "ID = 0x%X%X%X\n", bb[3][2], bb[3][3], bb[3][4]); fprintf(stdout, "Subunit = %u\n", (bb[3][1] & 0xF0) >> 4); fprintf(stdout, "Command = %u\n", bb[3][1] & 0x0F); fprintf(stdout, "Parameter = %u\n", bb[3][0]); if (debug_output) { bitbuffer_print(bitbuffer); fprintf(stderr, " Row 0 = Input, Row 1 = Zero bit stuffing, Row 2 = Stripped delimiters, Row 3 = Decoded nibbles\n"); } return 1; }
static void classify_signal() { unsigned int i, k, max = 0, min = 1000000, t; unsigned int delta, count_min, count_max, min_new, max_new, p_limit; unsigned int a[3], b[2], a_cnt[3], a_new[3], b_new[2]; unsigned int signal_distance_data[4000] = {0}; struct protocol_state p = {0}; unsigned int signal_type; if (!signal_pulse_data[0][0]) return; for (i = 0; i < 1000; i++) { if (signal_pulse_data[i][0] > 0) { //fprintf(stderr, "[%03d] s: %d\t e:\t %d\t l:%d\n", //i, signal_pulse_data[i][0], signal_pulse_data[i][1], //signal_pulse_data[i][2]); if (signal_pulse_data[i][2] > max) max = signal_pulse_data[i][2]; if (signal_pulse_data[i][2] <= min) min = signal_pulse_data[i][2]; } } t = (max + min) / 2; //fprintf(stderr, "\n\nMax: %d, Min: %d t:%d\n", max, min, t); delta = (max - min)*(max - min); //TODO use Lloyd-Max quantizer instead k = 1; while ((k < 10) && (delta > 0)) { min_new = 0; count_min = 0; max_new = 0; count_max = 0; for (i = 0; i < 1000; i++) { if (signal_pulse_data[i][0] > 0) { if (signal_pulse_data[i][2] < t) { min_new = min_new + signal_pulse_data[i][2]; count_min++; } else { max_new = max_new + signal_pulse_data[i][2]; count_max++; } } } if (count_min != 0 && count_max != 0) { min_new = min_new / count_min; max_new = max_new / count_max; } delta = (min - min_new)*(min - min_new) + (max - max_new)*(max - max_new); min = min_new; max = max_new; t = (min + max) / 2; fprintf(stderr, "Iteration %d. t: %d min: %d (%d) max: %d (%d) delta %d\n", k, t, min, count_min, max, count_max, delta); k++; } for (i = 0; i < 1000; i++) { if (signal_pulse_data[i][0] > 0) { //fprintf(stderr, "%d\n", signal_pulse_data[i][1]); } } /* 50% decision limit */ if (min != 0 && max / min > 1) { fprintf(stderr, "Pulse coding: Short pulse length %d - Long pulse length %d\n", min, max); signal_type = 2; } else { fprintf(stderr, "Distance coding: Pulse length %d\n", (min + max) / 2); signal_type = 1; } p_limit = (max + min) / 2; /* Initial guesses */ a[0] = 1000000; a[2] = 0; for (i = 1; i < 1000; i++) { if (signal_pulse_data[i][0] > 0) { // fprintf(stderr, "[%03d] s: %d\t e:\t %d\t l:%d\t d:%d\n", // i, signal_pulse_data[i][0], signal_pulse_data[i][1], // signal_pulse_data[i][2], signal_pulse_data[i][0]-signal_pulse_data[i-1][1]); signal_distance_data[i - 1] = signal_pulse_data[i][0] - signal_pulse_data[i - 1][1]; if (signal_distance_data[i - 1] > a[2]) a[2] = signal_distance_data[i - 1]; if (signal_distance_data[i - 1] <= a[0]) a[0] = signal_distance_data[i - 1]; } } min = a[0]; max = a[2]; a[1] = (a[0] + a[2]) / 2; // for (i=0 ; i<1 ; i++) { // b[i] = (a[i]+a[i+1])/2; // } b[0] = (a[0] + a[1]) / 2; b[1] = (a[1] + a[2]) / 2; // fprintf(stderr, "a[0]: %d\t a[1]: %d\t a[2]: %d\t\n",a[0],a[1],a[2]); // fprintf(stderr, "b[0]: %d\t b[1]: %d\n",b[0],b[1]); k = 1; delta = 10000000; while ((k < 10) && (delta > 0)) { for (i = 0; i < 3; i++) { a_new[i] = 0; a_cnt[i] = 0; } for (i = 0; i < 1000; i++) { if (signal_distance_data[i] > 0) { if (signal_distance_data[i] < b[0]) { a_new[0] += signal_distance_data[i]; a_cnt[0]++; } else if (signal_distance_data[i] < b[1] && signal_distance_data[i] >= b[0]) { a_new[1] += signal_distance_data[i]; a_cnt[1]++; } else if (signal_distance_data[i] >= b[1]) { a_new[2] += signal_distance_data[i]; a_cnt[2]++; } } } // fprintf(stderr, "Iteration %d.", k); delta = 0; for (i = 0; i < 3; i++) { if (a_cnt[i]) a_new[i] /= a_cnt[i]; delta += (a[i] - a_new[i])*(a[i] - a_new[i]); // fprintf(stderr, "\ta[%d]: %d (%d)", i, a_new[i], a[i]); a[i] = a_new[i]; } // fprintf(stderr, " delta %d\n", delta); if (a[0] < min) { a[0] = min; // fprintf(stderr, "Fixing a[0] = %d\n", min); } if (a[2] > max) { a[0] = max; // fprintf(stderr, "Fixing a[2] = %d\n", max); } // if (a[1] == 0) { // a[1] = (a[2]+a[0])/2; // fprintf(stderr, "Fixing a[1] = %d\n", a[1]); // } // fprintf(stderr, "Iteration %d.", k); for (i = 0; i < 2; i++) { // fprintf(stderr, "\tb[%d]: (%d) ", i, b[i]); b[i] = (a[i] + a[i + 1]) / 2; // fprintf(stderr, "%d ", b[i]); } // fprintf(stderr, "\n"); k++; } if (override_short) { p_limit = override_short; a[0] = override_short; } if (override_long) { a[1] = override_long; } fprintf(stderr, "\nShort distance: %d, long distance: %d, packet distance: %d\n", a[0], a[1], a[2]); fprintf(stderr, "\np_limit: %d\n", p_limit); bitbuffer_clear(&p.bits); if (signal_type == 1) { for (i = 0; i < 1000; i++) { if (signal_distance_data[i] > 0) { if (signal_distance_data[i] < (a[0] + a[1]) / 2) { // fprintf(stderr, "0 [%d] %d < %d\n",i, signal_distance_data[i], (a[0]+a[1])/2); bitbuffer_add_bit(&p.bits, 0); } else if ((signal_distance_data[i] > (a[0] + a[1]) / 2) && (signal_distance_data[i] < (a[1] + a[2]) / 2)) { // fprintf(stderr, "0 [%d] %d > %d\n",i, signal_distance_data[i], (a[0]+a[1])/2); bitbuffer_add_bit(&p.bits, 1); } else if (signal_distance_data[i] > (a[1] + a[2]) / 2) { // fprintf(stderr, "0 [%d] %d > %d\n",i, signal_distance_data[i], (a[1]+a[2])/2); bitbuffer_add_row(&p.bits); } } } bitbuffer_print(&p.bits); } if (signal_type == 2) { for (i = 0; i < 1000; i++) { if (signal_pulse_data[i][2] > 0) { if (signal_pulse_data[i][2] < p_limit) { // fprintf(stderr, "0 [%d] %d < %d\n",i, signal_pulse_data[i][2], p_limit); bitbuffer_add_bit(&p.bits, 0); } else { // fprintf(stderr, "1 [%d] %d > %d\n",i, signal_pulse_data[i][2], p_limit); bitbuffer_add_bit(&p.bits, 1); } if ((signal_distance_data[i] >= (a[1] + a[2]) / 2)) { // fprintf(stderr, "\\n [%d] %d > %d\n",i, signal_distance_data[i], (a[1]+a[2])/2); bitbuffer_add_row(&p.bits); } } } bitbuffer_print(&p.bits); } for (i = 0; i < 1000; i++) { signal_pulse_data[i][0] = 0; signal_pulse_data[i][1] = 0; signal_pulse_data[i][2] = 0; signal_distance_data[i] = 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; }
static int acurite_986_callback(bitbuffer_t *bitbuf) { int browlen; uint8_t *bb, sensor_num, status, crc, crcc; uint8_t br[8]; int8_t tempf; // Raw Temp is 8 bit signed Fahrenheit float tempc; uint16_t sensor_id, valid_cnt = 0; char sensor_type; local_time_str(0, time_str); if (debug_output > 1) { fprintf(stderr,"acurite_986\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_986: row %d bits %d, bytes %d \n", brow, bitbuf->bits_per_row[brow], browlen); if (bitbuf->bits_per_row[brow] < 39 || bitbuf->bits_per_row[brow] > 43 ) { if (debug_output > 1 && bitbuf->bits_per_row[brow] > 16) fprintf(stderr,"acurite_986: skipping wrong len\n"); continue; } // Reduce false positives // may eliminate these with a beter PPM (precise?) demod. if ((bb[0] == 0xff && bb[1] == 0xff && bb[2] == 0xff) || (bb[0] == 0x00 && bb[1] == 0x00 && bb[2] == 0x00)) { continue; } // There will be 1 extra false zero bit added by the demod. // this forces an extra zero byte to be added if (browlen > 5 && bb[browlen - 1] == 0) browlen--; // Reverse the bits for (uint8_t i = 0; i < browlen; i++) br[i] = reverse8(bb[i]); if (debug_output > 0) { fprintf(stderr,"Acurite 986 reversed: "); for (uint8_t i = 0; i < browlen; i++) fprintf(stderr," %02x",br[i]); fprintf(stderr,"\n"); } tempf = br[0]; sensor_id = (br[1] << 8) + br[2]; status = br[3]; sensor_num = (status & 0x01) + 1; status = status >> 1; // By default Sensor 1 is the Freezer, 2 Refrigerator sensor_type = sensor_num == 2 ? 'F' : 'R'; crc = br[4]; if ((crcc = crc8le(br, 5, 0x07, 0)) != 0) { // XXX make debug if (debug_output) { fprintf(stderr,"%s Acurite 986 sensor bad CRC: %02x -", time_str, crc8le(br, 4, 0x07, 0)); for (uint8_t i = 0; i < browlen; i++) fprintf(stderr," %02x", br[i]); fprintf(stderr,"\n"); } continue; } if ((status & 1) == 1) { fprintf(stderr, "%s Acurite 986 sensor 0x%04x - %d%c: low battery, status %02x\n", time_str, sensor_id, sensor_num, sensor_type, status); } // catch any status bits that haven't been decoded yet if ((status & 0xFE) != 0) { fprintf(stderr, "%s Acurite 986 sensor 0x%04x - %d%c: Unexpected status %02x\n", time_str, sensor_id, sensor_num, sensor_type, status); } if (tempf & 0x80) { tempf = (tempf & 0x7f) * -1; } tempc = fahrenheit2celsius(tempf); printf("%s Acurite 986 sensor 0x%04x - %d%c: %3.1f C %d F\n", time_str, sensor_id, sensor_num, sensor_type, tempc, tempf); valid_cnt++; } if (valid_cnt) return 1; return 0; }
static int acurite_txr_callback(bitbuffer_t *bitbuf) { int browlen; uint8_t *bb; float tempc, tempf, wind_dird, rainfall = 0.0, wind_speedmph; uint8_t humidity, sensor_status, repeat_no, message_type; char channel, *wind_dirstr = ""; uint16_t sensor_id; int wind_speed, raincounter; 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) { 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_crc(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]); tempf = celsius2fahrenheit(tempc); printf("%s Acurite tower sensor 0x%04X Ch %c: %3.1F C %3.1F F %d %% RH\n", time_str, sensor_id, channel, tempc, tempf, humidity); // currently 0x44 seens to be a normal status and/or type // for tower sensors. Battery OK/Normal == 0x40 if (sensor_status != 0x44) printf("%s Acurite tower sensor 0x%04X Ch %c, Status %02X\n", time_str, sensor_id, channel, sensor_status); } // The 5-n-1 weather sensor messages are 8 bytes. if (browlen == ACURITE_5N1_BITLEN / 8) { channel = acurite_getChannel(bb[0]); sensor_id = acurite_5n1_getSensorId(bb[0],bb[1]); repeat_no = acurite_5n1_getMessageCaught(bb[0]); message_type = bb[2] & 0x3f; if (message_type == 0x31) { // Wind speed, wind direction, and rain fall wind_speed = acurite_getWindSpeed(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_raincounter > 0) { // track rainfall difference after first run rainfall = ( raincounter - acurite_raincounter ) * 0.01; if (raincounter < acurite_raincounter) { printf("%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_raincounter, raincounter); acurite_raincounter = raincounter; } } else { // capture starting counter acurite_raincounter = raincounter; printf("%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); } printf("%s Acurite 5n1 sensor 0x%04X Ch %c, Msg %02x, Wind %d kmph / %0.1f mph %0.1f° %s (%d), rain gauge %0.2f in.\n", time_str, sensor_id, channel, message_type, wind_speed, wind_speedmph, wind_dird, wind_dirstr, bb[4] & 0x0f, rainfall); } else if (message_type == 0x38) { // Wind speed, temperature and humidity wind_speed = acurite_getWindSpeed(bb[3], bb[4]); wind_speedmph = kmph2mph((float) wind_speed); tempf = acurite_getTemp(bb[4], bb[5]); tempc = fahrenheit2celsius(tempf); humidity = acurite_getHumidity(bb[6]); printf("%s Acurite 5n1 sensor 0x%04X Ch %c, Msg %02x, Wind %d kmph / %0.1f mph, %3.1F C %3.1F F %d %% RH\n", time_str, sensor_id, channel, message_type, wind_speed, wind_speedmph, tempc, tempf, humidity); } else { printf("%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); } } } return 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"); } }
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; }