diff --git a/code/b-parasite/config/prst_config.h b/code/b-parasite/config/prst_config.h index f0370b8..2fc7349 100644 --- a/code/b-parasite/config/prst_config.h +++ b/code/b-parasite/config/prst_config.h @@ -7,13 +7,15 @@ // the the version you're programming. The version can be found on the // b-parasite board. // #define PRST_VERSION_1_0_X -#define PRST_VERSION_1_1_X +// #define PRST_VERSION_1_1_X +#define PRST_VERSION_1_2_X // Built-in LED. #define PRST_LED_PIN NRF_GPIO_PIN_MAP(0, 28) // Deep sleep. -#define PRST_DEEP_SLEEP_IN_SECONDS 300 +// #define PRST_DEEP_SLEEP_IN_SECONDS 2 +#define PRST_DEEP_SLEEP_IN_SECONDS 2 * 3600 // Analog to digital converter (ADC). // Prints out ADC debug info, such as the values read for battery and soil @@ -29,7 +31,7 @@ // 1. Two most significant bits are set to 1; // 2. The remaining bits should not _all_ be set to 0; // 2. The remaining bits should not _all_ be set to 1; -#define PRST_BLE_MAC_ADDR "f0:ca:f0:ca:01:01" +#define PRST_BLE_MAC_ADDR "f0:ca:f0:ca:00:10" #define PRST_BLE_ADV_NAME "prst" // Total time spend advertising. #define PRST_BLE_ADV_TIME_IN_MS 1000 @@ -65,6 +67,16 @@ // Whether to produce debug messages for the LDR #define PRST_ADC_PHOTO_DEBUG 0 +// #endif // End of version-specific configuration. +#elif defined(PRST_VERSION_1_2_X) + +#define PRST_HAS_PHOTOTRANSISTOR 1 + +#define PRST_PHOTO_V_PIN NRF_GPIO_PIN_MAP(0, 29) +#define PRST_PHOTO_OUT_PIN NRF_GPIO_PIN_MAP(0, 2) + +#define PRST_ADC_PHOTO_DEBUG 1 + #endif // End of version-specific configuration. #endif // _PRST_CONFIG_H_ diff --git a/code/b-parasite/src/main.c b/code/b-parasite/src/main.c index 5c8f055..99549df 100644 --- a/code/b-parasite/src/main.c +++ b/code/b-parasite/src/main.c @@ -26,7 +26,7 @@ static void log_init(void) { static void gpio_init(void) { nrf_gpio_cfg_output(PRST_LED_PIN); nrf_gpio_cfg_output(PRST_FAST_DISCH_PIN); -#if PRST_HAS_LDR +#if PRST_HAS_LDR || PRST_HAS_PHOTOTRANSISTOR nrf_gpio_cfg_output(PRST_PHOTO_V_PIN); #endif NRF_LOG_INFO("GPIO pins inited."); @@ -67,11 +67,16 @@ static void rtc_callback() { nrf_gpio_pin_clear(PRST_FAST_DISCH_PIN); uint16_t lux = 0; -#if PRST_HAS_LDR +#if PRST_HAS_LDR || PRST_HAS_PHOTOTRANSISTOR nrf_gpio_pin_set(PRST_PHOTO_V_PIN); - prst_adc_photo_sensor_t photo_read = prst_adc_photo_read(batt_read.voltage); + + // Just while debugging. + while (1) { + nrf_delay_ms(1000); + prst_adc_photo_sensor_t photo_read = prst_adc_photo_read(batt_read.voltage); + lux = photo_read.brightness; + } nrf_gpio_pin_clear(PRST_PHOTO_V_PIN); - lux = photo_read.brightness; #endif prst_ble_update_adv_data(batt_read.millivolts, temp_humi.temp_millicelcius, diff --git a/code/b-parasite/src/prst/adc.c b/code/b-parasite/src/prst/adc.c index de57482..c46e09a 100644 --- a/code/b-parasite/src/prst/adc.c +++ b/code/b-parasite/src/prst/adc.c @@ -8,9 +8,16 @@ #include #include "prst_config.h" +#include "sdk_config.h" // 10 bits resoltuion. +#if NRFX_SAADC_CONFIG_RESOLUTION == 1 #define PRST_ADC_RESOLUTION 10 +#elif NRFX_SAADC_CONFIG_RESOLUTION == 2 +#define PRST_ADC_RESOLUTION 12 +#else +#error "NRFX_SAADC_CONFIG_RESOLUTION does not have a valid value" +#endif // NRFX_SAADC_CONFIG_RESOLUTION #define PRST_ADC_BATT_INPUT NRF_SAADC_INPUT_VDD #define PRST_ADC_BATT_CHANNEL 0 @@ -71,6 +78,7 @@ void prst_adc_init() { nrf_saadc_channel_config_t photo_channel_config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(PRST_ADC_PHOTO_INPUT); + // photo_channel_config.gain = NRF_SAADC_GAIN1_4; APP_ERROR_CHECK(nrf_drv_saadc_channel_init(PRST_ADC_PHOTO_CHANNEL, &photo_channel_config)); @@ -142,7 +150,9 @@ prst_adc_photo_sensor_t prst_adc_photo_read(double battery_voltage) { prst_adc_photo_sensor_t ret; ret.raw = raw_photo_output; ret.voltage = (3.6 * raw_photo_output) / (1 << PRST_ADC_RESOLUTION); + // ret.voltage = (2.4 * raw_photo_output) / (1 << PRST_ADC_RESOLUTION); +#if PRST_HAS_LDR // The photo resistor forms a voltage divider with a 10 kOhm resistor. // The voltage here is measured in the middle of the voltage divider. // Vcc ---- (R_photo) ---|--- (10k) ---- GND @@ -161,9 +171,55 @@ prst_adc_photo_sensor_t prst_adc_photo_read(double battery_voltage) { ret.brightness = MAX(0, MIN(mult_value / powf(photo_resistance, pow_value), UINT16_MAX)); +#elif PRST_HAS_PHOTOTRANSISTOR + // The ALS-PT19 phototransistor is a device in which the current flow between + // its two terminals is controlled by how much light there is in the ambient. + // We measure that current by calculating the voltage across a resistor that + // is connected in series with the phototransistor. + // Some infor: + // - Not all lights are the same. The ALS-PT19 has different current + // responses for incandescent and fluorescent lights and it shows no values + // for sunlight. We have to make some compromises here, aiming for a + // "reasonable" middle ground through calibration. + // - ALS-PT19' minimum voltage is 2.5 V. Our CR2032 battery has a theoretical + // range of 2.0 V to 3.0 V, but in our usage pattern it seems to spend most of + // its life around 2.6 V (https://github.com/rbaron/b-parasite/issues/1). In + // my manual tests it seems we can usually go lower than that. So while we're + // pushing it a bit, we should be okay most of the time. + // + // In order to design the value of the series resistor, we assume Vcc = 2.5V, + // and we want the upper limit of light intensity to correspond to a value of + // Vout < 2.5 V - 0.4 V (saturation) = 2.1 V. Let's call this the "direct + // sunlight" voltage across our resistor. This direct sunlight voltage will + // appear when the phototransistor outputs the direct sunlight current. + // + // In short, what we want: + // A value of R_L such that Vout is < 2.1 V (but close) when the sensor is in + // direct sunlight. While Vcc is 2.5 V, R_L = 470 Ohm outputs Vout ~ 1.7V, so + // there's still some wiggle room for even more sunnier days. + // + // Another caveat: the datasheet shows that the current in the transistor is + // relatively constant when we vary Vcc (Fig.4). This seems to be true for low + // currents (the datsheet uses 100 lx in Fig.4), but not for larger currents. + const float phototransistor_resistor = 470.0f; + const float current_sun = 3.59e-3f; + // Assuming 10000 lux for the saturation test. Calibration with a proper light + // meter would be better. + const float lux_sun = 10000.0f; + + const float current = ret.voltage / phototransistor_resistor; + ret.brightness = MAX(0, MIN(lux_sun * current / current_sun, UINT16_MAX)); + #if PRST_ADC_PHOTO_DEBUG - NRF_LOG_INFO("[adc] Read brightness level: %d (raw); %d (lux)", ret.raw, - ret.brightness); + NRF_LOG_INFO("[adc] Phototransistor current: " NRF_LOG_FLOAT_MARKER " uA", + NRF_LOG_FLOAT(1000000 * current)); +#endif // PRST_ADC_PHOTO_DEBUG +#endif // PRST_HAS_PHOTOTRANSISTOR + +#if PRST_ADC_PHOTO_DEBUG + NRF_LOG_INFO("[adc] Read brightness level: " NRF_LOG_FLOAT_MARKER + " mV %d (raw); %d (lux)", + NRF_LOG_FLOAT(1000 * ret.voltage), ret.raw, ret.brightness); #endif return ret; } diff --git a/code/b-parasite/src/prst/ble.c b/code/b-parasite/src/prst/ble.c index 6d74835..c5b9209 100644 --- a/code/b-parasite/src/prst/ble.c +++ b/code/b-parasite/src/prst/ble.c @@ -114,7 +114,7 @@ static void init_advertisement_data() { // Bit 0 of byte 0 specifies whether or not ambient light data exists in the // payload. -#if PRST_HAS_LDR +#if PRST_HAS_LDR || PRST_HAS_PHOTOTRANSISTOR service_data[0] |= 1; #endif @@ -171,7 +171,7 @@ void prst_ble_update_adv_data(uint16_t batt_millivolts, service_data[8] = soil_moisture >> 8; service_data[9] = soil_moisture & 0xff; -#if PRST_HAS_LDR +#if PRST_HAS_LDR || PRST_HAS_PHOTOTRANSISTOR service_data[16] = brightness >> 8; service_data[17] = brightness & 0xff; #endif