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"); } }