diff --git a/Makefile b/Makefile index 62130ec..ef954d8 100755 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ all: wh1080_rf -wh1080_rf: wh1080_rf.o bcm2835.o bmp085.o wunderground.o mqtt.o - $(CC) -lm -lconfig -lmosquitto wh1080_rf.o bcm2835.o bmp085.o wunderground.o mqtt.o -o wh1080_rf +wh1080_rf: wh1080_rf.o bcm2835.o bmp280.o wunderground.o mqtt.o + $(CC) -lm -lconfig -lmosquitto wh1080_rf.o bcm2835.o bmp280.o wunderground.o mqtt.o -o wh1080_rf wh1080_rf.o: wh1080_rf.c $(CC) $(CFLAGS) wh1080_rf.c @@ -12,8 +12,8 @@ bcm2835.o: bcm2835.c $(CC) $(CFLAGS) bcm2835.c -bmp085.o: bmp085.c - $(CC) $(CFLAGS) bmp085.c +bmp280.o: bmp280.c + $(CC) $(CFLAGS) bmp280.c wunderground.o: wunderground.c $(CC) $(CFLAGS) wunderground.c @@ -22,7 +22,7 @@ $(CC) $(CFLAGS) mqtt.c clean: - rm -f wh1080_rf.o bcm2835.o bmp085.o wunderground.o mqtt.o wh1080_rf + rm -f wh1080_rf.o bcm2835.o bmp280.o wunderground.o mqtt.o wh1080_rf install: wh1080_rf install wh1080_rf /usr/local/sbin/wh1080_rf diff --git a/bmp085.c b/bmp085.c deleted file mode 100755 index 384a5ee..0000000 --- a/bmp085.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * pcf8563_i2c_rtc.c - example of accessing a PCF8563 via the BSC0 (I2C) peripheral on a BCM2835 (Raspberry Pi) - * - * Copyright 2012 Kevin Sangeelee. - * Released as GPLv2, see - * - * This is intended as an example of using Raspberry Pi hardware registers to drive an RTC chip. Use at your own risk or - * not at all. As far as possible, I've omitted anything that doesn't relate to the RTC or the Raspi registers. There are more - * conventional ways of doing this using kernel drivers, though these are harder to follow. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "bcm2835.h" - - -//////////////// -// main() -//////////////// - -char eeprom[22] = { 0, 0, }; -short ac1, ac2, ac3, b1, b2, mb, mc, md; -unsigned short ac4, ac5, ac6; - -float temperature_deg_c; -float pressure_hpa; - -int read_bmp085(float altitude) { - - if(map_peripheral(&gpio) == -1) { - printf("Failed to map the physical GPIO registers into the virtual memory space.\n"); - return -1; - } - //if(map_peripheral(&bsc0) == -1) { - if(map_peripheral(&bsc1) == -1) { - printf("Failed to map the physical BSC1 (I2C) registers into the virtual memory space.\n"); - return -1; - } - - /* BSC0 is on GPIO 0 & 1 */ - *gpio.addr &= ~0x3f; // Mask out bits 0-5 of FSEL0 (i.e. force to zero) - *gpio.addr |= 0x24; // Set bits 0-5 of FSEL0 to binary '100100' - - // Read eeprom data if the array is empty - // I2C Device Address 0x77 (hardwired into the chip, 0xEE & 0xEF) - if((unsigned short)*eeprom == 0) { - - // Device 0x77, register 0xaa, read into buf, 22 bytes - i2c_read(0x77, 0xaa, eeprom, 22); - - ac1 = (short)eeprom[0] << 8 | eeprom[1]; - ac2 = (short)eeprom[2] << 8 | eeprom[3]; - ac3 = (short)eeprom[4] << 8 | eeprom[5]; - ac4 = (unsigned short)eeprom[6] << 8 | eeprom[7]; - ac5 = (unsigned short)eeprom[8] << 8 | eeprom[9]; - ac6 = (unsigned short)eeprom[10] << 8 | eeprom[11]; - b1 = (short)eeprom[12] << 8 | eeprom[13]; - b2 = (short)eeprom[14] << 8 | eeprom[15]; - mb = (short)eeprom[16] << 8 | eeprom[17]; - mc = (short)eeprom[18] << 8 | eeprom[19]; - md = (short)eeprom[20] << 8 | eeprom[21]; - - // Test values - //ac1 = 408, ac2 = -72, ac3 = -14383, ac4 = 32741, ac5 = 32757, ac6 = 23153; - //b1 = 6190, b2 = 4, mb = -32768, mc = -8711, md = 2868; - // Also include 'ut = 27898' , 'up = 23843', and 'oss = 0' - //printf("%d %d %d %d %d %d\n", ac1, ac2, ac3, ac4, ac5, ac6); - //printf("%d %d %d %d %d\n", b1, b2, mb, mc, md); - } - - - char ut_buf[2]; - char up_buf[2]; - - char cmd_ut = 0x2e; - char cmd_up[] = {0x34, 0x74, 0xb4, 0xf4}; - int oss_delay[] = {4500, 7500, 13500, 25500}; - int oss = 1; // This is an index into the above array - - /* - * Get Uncompensated Temperature from BMP085 - */ - i2c_write(0x77, 0xf4, &cmd_ut, 1); - usleep(4500); // just wait the maximum possible time for conversion - i2c_read(0x77, 0xf6, ut_buf, 2); - - long ut = (long)ut_buf[0] << 8 | ut_buf[1]; - - // Temperature compensation algorithm (derived from datasheet) - long x1 = ((ut - ac6) * ac5) >> 15; - long x2 = (mc * (1 << 11)) / (x1 + md); - long b5 = x1 + x2; - long t = (b5 + 8) >> 4; - - temperature_deg_c = (float)t / 10; - printf("Temperature: %0.1fC\n", temperature_deg_c); - - int idx; - float p0 = 0; - - for(idx=0; idx < 2; idx++) { - /* - * Get Uncompensated Pressure from BMP085, based on the OverSampling Setting - * of (0, 1, 2, or 3). This determines accuracy, conversion delay, and power consumption. - */ - i2c_write(0x77, 0xf4, &cmd_up[oss], 1); - usleep(oss_delay[oss]); // wait according to the chosen oss mode - i2c_read(0x77, 0xf6, up_buf, 3); - - long up = (((long)up_buf[0] << 16) | ((long)up_buf[1] << 8) | up_buf[2]) >> (8 - oss); - - // Pressure compensation algorithm (derived from datasheet) - long b6 = b5 - 4000; - x1 = (b2 * (b6 * b6 >> 12)) >> 11; - x2 = ac2 * b6 >> 11; - long x3 = x1 + x2; - long b3 = (((ac1 * 4 + x3) << oss) + 2) >> 2; - x1 = ac3 * b6 >> 13; - x2 = (b1 * (b6 * b6 >> 12)) >> 16; - x3 = ((x1 + x2) + 2) >> 2; - unsigned long b4 = ac4 * (unsigned long)(x3 + 32768) >>15; - unsigned long b7 = ((unsigned long)up - b3) * (50000 >> oss); - long p = b7 < 0x80000000 ? (b7 * 2) / b4 : (b7 / b4) * 2; - x1 = (p >> 8) * (p >> 8); - x1 = (x1 * 3038) >> 16; - x2 = (-7357 * p) >> 16; - p = p + (x1 + x2 + 3791) / 16; - - p0 += (float)p / powf(1.0f - (altitude / 44330), 5.255f); - - usleep(100000); - } - p0 /= 2; - pressure_hpa = p0 / 100; - printf("Pressure p0 (sea level): %0.1f hPa\n", pressure_hpa); - - unmap_peripheral(&gpio); - unmap_peripheral(&bsc0); - - return 0; -} diff --git a/bmp280.c b/bmp280.c new file mode 100755 index 0000000..399f13b --- /dev/null +++ b/bmp280.c @@ -0,0 +1,18 @@ +#include +#include +#include + +float pressure_hpa; + +int read_bmp280(float altitude) { + FILE * fp; + float station_pressure, sea_level_pressure; + fp = fopen("/sys/bus/iio/devices/iio:device0/in_pressure_input", "r"); + fscanf(fp, "%f", &station_pressure); + fclose(fp); + sea_level_pressure = station_pressure / powf(1.0f - (altitude / 44330), 5.255f); + printf("Sea level pressure: %.4f\n", sea_level_pressure); + pressure_hpa = sea_level_pressure * 10; + + return 0; +} diff --git a/mqtt.c b/mqtt.c index 92b71bb..8b39b08 100644 --- a/mqtt.c +++ b/mqtt.c @@ -60,7 +60,7 @@ ) { char * msg; - int msglen = asprintf(&msg, "{\"temp\": %.1f, \"humidity\": %.1f, \"windAveMs\": %.1f, \"windGustMs\": %.1f, \"windDir\": \"%s\", \"totalRain\": %.f, \"pressure\": %.f}", + int msglen = asprintf(&msg, "{\"temp\": %.1f, \"humidity\": %.1f, \"windAveMs\": %.1f, \"windGustMs\": %.1f, \"windDir\": \"%s\", \"totalRain\": %.f, \"pressure\": %.1f}", TemperatureC, Humidity, WindAveMs, WindGustMs, WindDirection, TotalRainMm, PressureHpa); if (msglen < 0) { return false; diff --git a/wh1080_rf.c b/wh1080_rf.c index ced35a9..5a9f7dc 100755 --- a/wh1080_rf.c +++ b/wh1080_rf.c @@ -181,7 +181,7 @@ } // pressure function and value -extern int read_bmp085(float altitude); +extern int read_bmp280(float altitude); extern float pressure_hpa; #define CONFIG_FILE "/etc/wh1080.conf" @@ -192,46 +192,75 @@ //calculate_values(bytes2); //return -1; - // Read configuration file - config_t cfg; - config_setting_t *mqtt_setting; - const char *mqtt_host; - int mqtt_port; - const char * mqtt_topic; - const char * mqtt_user; - const char * mqtt_pass; + // Read configuration file + config_t cfg; + config_setting_t *mqtt_settings, *bmp280_settings, *wu_settings; + const char *mqtt_host; + int mqtt_port; + const char *mqtt_topic; + const char *mqtt_user; + const char *mqtt_pass; + double altitude; + const char *wu_stationId, *wu_password; - config_init(&cfg); - if (!config_read_file(&cfg, CONFIG_FILE)) { - fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), - config_error_line(&cfg), config_error_text(&cfg)); - config_destroy(&cfg); - exit(-1); - } + config_init(&cfg); + if (!config_read_file(&cfg, CONFIG_FILE)) { + fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), + config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + exit(-1); + } - mqtt_setting = config_lookup(&cfg, "mqtt"); - if (!mqtt_setting) { - printf("Required settings for MQTT not found.\n"); - config_destroy(&cfg); - exit(-1); - } - if (!(config_setting_lookup_string(mqtt_setting, "host", &mqtt_host) && - config_setting_lookup_int(mqtt_setting, "port", &mqtt_port) && - config_setting_lookup_string(mqtt_setting, "topic", &mqtt_topic))) { - printf("MQTT requires host, port and topic.\n"); - config_destroy(&cfg); - exit(-1); - } - if (!config_setting_lookup_string(mqtt_setting, "user", &mqtt_user)) { - mqtt_user = NULL; - } - if (!config_setting_lookup_string(mqtt_setting, "pass", &mqtt_pass)) { - mqtt_pass = NULL; - } - // initialize weather underground module - WUnderground_Init(); - if (!MQTT_init(mqtt_host, mqtt_port, mqtt_user, mqtt_pass, mqtt_topic)) { - exit(-1); + // BMP280 options + bmp280_settings = config_lookup(&cfg, "bmp280"); + if (!bmp280_settings) { + printf("No settings for BMP280 pressure sensor, disabling.\n"); + } else { + if (!config_setting_lookup_float(bmp280_settings, "altitude", &altitude)) { + printf("No alititude provided, setting to 0 metres above sea level.\n"); + altitude = 0.0f; + } + } + + // MQTT options + mqtt_settings = config_lookup(&cfg, "mqtt"); + if (!mqtt_settings) { + printf("No settings for MQTT found, disabling.\n"); + } else { + if (!(config_setting_lookup_string(mqtt_settings, "host", &mqtt_host) && + config_setting_lookup_int(mqtt_settings, "port", &mqtt_port) && + config_setting_lookup_string(mqtt_settings, "topic", &mqtt_topic))) { + printf("MQTT requires host, port and topic.\n"); + config_destroy(&cfg); + exit(-1); + } + if (!config_setting_lookup_string(mqtt_settings, "user", &mqtt_user)) { + mqtt_user = NULL; + } + if (!config_setting_lookup_string(mqtt_settings, "pass", &mqtt_pass)) { + mqtt_pass = NULL; + } + // Attempt to initialize MQTT as a client + if (!MQTT_init(mqtt_host, mqtt_port, mqtt_user, mqtt_pass, mqtt_topic)) { + printf("Unable to initialize MQTT.\n"); + config_destroy(&cfg); + exit(-1); + } + } + + // WUnderground options + wu_settings = config_lookup(&cfg, "wunderground");; + if (!wu_settings) { + printf("No settings for WUnderground found, disabling.\n"); + } else { + if (!(config_setting_lookup_string(wu_settings, "station", &wu_stationId) && + config_setting_lookup_string(wu_settings, "password", &wu_password))) { + printf("WUnderground requires station ID and password.\n"); + config_destroy(&cfg); + exit(-1); + } + // initialize weather underground module + WUnderground_Init(wu_stationId, wu_password); } uint8_t packet_sig = 0xfa; @@ -474,11 +503,11 @@ // at this point, we can do other stuff that requires the RT scheduler - #ifdef USE_BMP085 - read_bmp085(ALTITUDE_M); // read pressure, calculate for the given altitude - #endif + if (bmp280_settings) { + read_bmp280(altitude); // read pressure, calculate for the given altitude + } - calculate_values(bytes, pressure_hpa); + calculate_values(bytes, pressure_hpa, (wu_settings != NULL), (mqtt_settings != NULL)); // Wait for remainder of 47 seconds in standard scheduler until we can expect the next read scheduler_standard(); @@ -495,7 +524,6 @@ } count = 0; - // LED off *(gpio.addr + (0x28 >> 2)) = 1 << 22; } @@ -507,6 +535,7 @@ // Currenty unreachable close(fd); config_destroy(&cfg); + // Todo: clean up MQTT, WUnderground unmap_peripheral(&gpio); unmap_peripheral(&timer_arm); @@ -516,7 +545,7 @@ char *direction_name[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; -void calculate_values(unsigned char *buf, float pressure_hpa) { +void calculate_values(unsigned char *buf, float pressure_hpa, bool send_wu, bool send_mqtt) { unsigned short device_id = ((unsigned short)buf[0] << 4) | (buf[1] >> 4); unsigned short temperature_raw = (((unsigned short)buf[1] & 0x0f) << 8) | buf[2]; @@ -544,13 +573,18 @@ printf("Wind speed: %0.2f m/s, Gust Speed %0.2f m/s, %s\n", wind_avg_ms, wind_gust_ms, direction_str); printf("Wind speed: %0.1f mph, Gust Speed %0.1f mph, %s\n", wind_avg_mph, wind_gust_mph, direction_str); printf("Total rain: %0.1f mm\n", rain); - printf("Pressure (sea level): %0.1f hpa\n", pressure_hpa); + printf("Pressure (sea level): %0.1f hpa\n", pressure_hpa); - // submit observation to weather underground - WUnderground_Observation(temperature, humidity, wind_avg_mph, wind_gust_mph, direction_str, rain, pressure_hpa); + if (send_wu) { + // submit observation to weather underground + WUnderground_Observation(temperature, humidity, wind_avg_mph, + wind_gust_mph, direction_str, rain, pressure_hpa); + } - // publish to MQTT - MQTT_observation(temperature, humidity, wind_avg_ms, wind_gust_ms, direction_str, rain, pressure_hpa); + if (send_mqtt) { + // publish to MQTT + MQTT_observation(temperature, humidity, wind_avg_ms, wind_gust_ms, direction_str, rain, pressure_hpa); + } } /* diff --git a/wh1080_rf.h b/wh1080_rf.h index 3371cff..4f0303d 100755 --- a/wh1080_rf.h +++ b/wh1080_rf.h @@ -1,8 +1,7 @@ +#define _GNU_SOURCE -//#define USE_BMP085 -//#define ALTITUDE_M 210.0f -#define ALTITUDE_M 6.0f +#include void scheduler_realtime(); void scheduler_standard(); -void calculate_values(unsigned char *buf, float pressure_hpa); +void calculate_values(unsigned char *buf, float pressure_hpa, bool send_wu, bool send_mqtt); diff --git a/wunderground.c b/wunderground.c index 59125d3..695d911 100644 --- a/wunderground.c +++ b/wunderground.c @@ -25,16 +25,9 @@ #include #include "wunderground.h" -// file that contains weather underground station ID (first line) and -// password (second line) -#define CREDENTIALS "/etc/wunderground/creds.txt" - // API url #define WUURL "http://rtupdate.wunderground.com/weatherstation/updateweatherstation.php" -// maximum lengths of station id and password -#define STATIONIDLENGTH 50 -#define PASSWORDLENGTH 50 // maximum command length #define CMDLENGTH 1024 @@ -55,9 +48,7 @@ #define PI 3.14159265359 // module variables -static char gStationId[STATIONIDLENGTH]; -static char gPassword[PASSWORDLENGTH]; - +char *gStationId, *gPassword; static int CurrentDay = -1; static double RainatStartofDay = 0.0; @@ -192,19 +183,14 @@ // initializes the module void WUnderground_Init ( - void + const char *stationId, + const char *password ) { int i; - // read in credentials - FILE *fp = fopen(CREDENTIALS, "r"); - fgets(gStationId, STATIONIDLENGTH, fp); - strtok(gStationId, "\n"); - fgets(gPassword, PASSWORDLENGTH, fp); - strtok(gPassword, "\n"); - fclose(fp); - + gStationId = strdup(stationId); + gPassword = strdup(password); // no rain data for (i = 0; i < SAMPLESPERHOUR; i++) RainSamples[i] = 0.0; // no wind vectors @@ -215,6 +201,15 @@ } } +void WUnderground_Destroy() { + if (gStationId) { + free(gStationId); + } + if (gPassword) { + free(gPassword); + } +} + // submits an observation void WUnderground_Observation ( diff --git a/wunderground.h b/wunderground.h index f8971af..46476db 100644 --- a/wunderground.h +++ b/wunderground.h @@ -8,7 +8,8 @@ // initializes the module extern void WUnderground_Init ( - void + const char *stationId, + const char *password ); // submits an observation