123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- #include <stdio.h>
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "esp_system.h"
- #include "driver/gpio.h"
- #include "driver/i2c.h"
- #include "esp_log.h"
- #include "sdkconfig.h"
- #include "lux.h"
- #include "mqtt.h"
- #include "config.h"
- // Code from:
- // https://github.com/espressif/esp-idf/blob/release/v4.4/examples/peripherals/i2c/i2c_simple/main/i2c_simple_main.c
- #ifdef ENABLE_LUX_SENSOR
- static const char *TAG = "LUX";
- #include <stdbool.h>
- bool _tsl2561AutoGain = true;
- tsl2561IntegrationTime_t _tsl2561IntegrationTime = TSL2561_INTEGRATIONTIME_101MS;
- tsl2561Gain_t _tsl2561Gain = TSL2561_GAIN_1X;
- #define I2C_MASTER_SCL_IO I2C_MASTER_SCL /*!< GPIO number used for I2C master clock */
- #define I2C_MASTER_SDA_IO I2C_MASTER_SDA /*!< GPIO number used for I2C master data */
- #define I2C_MASTER_NUM 0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
- #define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */
- #define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
- #define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
- #define I2C_MASTER_TIMEOUT_MS 1000
- /** TSL2561 I2C Registers */
- enum {
- TSL2561_REGISTER_CONTROL = 0x00, // Control/power register
- TSL2561_REGISTER_TIMING = 0x01, // Set integration time register
- TSL2561_REGISTER_THRESHHOLDL_LOW = 0x02, // Interrupt low threshold low-byte
- TSL2561_REGISTER_THRESHHOLDL_HIGH = 0x03, // Interrupt low threshold high-byte
- TSL2561_REGISTER_THRESHHOLDH_LOW = 0x04, // Interrupt high threshold low-byte
- TSL2561_REGISTER_THRESHHOLDH_HIGH = 0x05, // Interrupt high threshold high-byte
- TSL2561_REGISTER_INTERRUPT = 0x06, // Interrupt settings
- TSL2561_REGISTER_CRC = 0x08, // Factory use only
- TSL2561_REGISTER_ID = 0x0A, // TSL2561 identification setting
- TSL2561_REGISTER_CHAN0_LOW = 0x0C, // Light data channel 0, low byte
- TSL2561_REGISTER_CHAN0_HIGH = 0x0D, // Light data channel 0, high byte
- TSL2561_REGISTER_CHAN1_LOW = 0x0E, // Light data channel 1, low byte
- TSL2561_REGISTER_CHAN1_HIGH = 0x0F // Light data channel 1, high byte
- };
- static esp_err_t i2c_master_init(void)
- {
- int i2c_master_port = I2C_MASTER_NUM;
- i2c_config_t conf = {
- .mode = I2C_MODE_MASTER,
- .sda_io_num = I2C_MASTER_SDA_IO,
- .scl_io_num = I2C_MASTER_SCL_IO,
- .sda_pullup_en = GPIO_PULLUP_ENABLE,
- .scl_pullup_en = GPIO_PULLUP_ENABLE,
- .master.clk_speed = I2C_MASTER_FREQ_HZ,
- };
- i2c_param_config(i2c_master_port, &conf);
- return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
- }
- static esp_err_t tsl2561_register_read(uint8_t reg_addr, uint8_t *data, size_t len)
- {
- return i2c_master_write_read_device(I2C_MASTER_NUM, CONFIG_TSL2561_I2C_ADDRESS, ®_addr, 1, data, len, I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);
- }
- void write8(uint8_t reg, uint8_t value)
- {
- uint8_t data[2];
- data[0] = reg;
- data[1] = value;
- i2c_master_write_to_device(I2C_MASTER_NUM, CONFIG_TSL2561_I2C_ADDRESS, data, 2, I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);
- }
- uint16_t read16(uint8_t reg)
- {
- uint8_t data[2];
- tsl2561_register_read(reg, data, 2);
- return (data[1] << 8) | data[0];
- }
- uint8_t read8(uint8_t reg)
- {
- uint8_t data;
- tsl2561_register_read(reg, &data, 1);
- return data;
- }
- void setIntegrationTime(tsl2561IntegrationTime_t time)
- {
- /* Enable the device by setting the control bit to 0x03 */
- /* Update the timing register */
- write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, time | _tsl2561Gain);
- /* Update value placeholders */
- _tsl2561IntegrationTime = time;
- }
- void setGain(tsl2561Gain_t gain)
- {
- /* Update the timing register */
- write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, _tsl2561IntegrationTime | gain);
- /* Update value placeholders */
- _tsl2561Gain = gain;
- }
- void enableAutoRange(bool enable)
- {
- _tsl2561AutoGain = enable ? true : false;
- }
- void enable(void)
- {
- /* Enable the device by setting the control bit to 0x03 */
- write8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWERON);
- }
- void delay(uint32_t ms)
- {
- vTaskDelay(ms / portTICK_RATE_MS);
- }
- void getData (uint16_t *broadband, uint16_t *ir)
- {
- /* Wait x ms for ADC to complete */
- switch (_tsl2561IntegrationTime)
- {
- case TSL2561_INTEGRATIONTIME_13MS:
- delay(TSL2561_DELAY_INTTIME_13MS); // KTOWN: Was 14ms
- break;
- case TSL2561_INTEGRATIONTIME_101MS:
- delay(TSL2561_DELAY_INTTIME_101MS); // KTOWN: Was 102ms
- break;
- default:
- delay(TSL2561_DELAY_INTTIME_402MS); // KTOWN: Was 403ms
- break;
- }
- /* Reads a two byte value from channel 0 (visible + infrared) */
- *broadband = read16(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN0_LOW);
- /* Reads a two byte value from channel 1 (infrared) */
- *ir = read16(TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN1_LOW);
- }
- void getLuminosity (uint16_t *broadband, uint16_t *ir)
- {
- bool valid = false;
- /* If Auto gain disabled get a single reading and continue */
- if(!_tsl2561AutoGain)
- {
- getData (broadband, ir);
- return;
- }
- /* Read data until we find a valid range */
- bool _agcCheck = false;
- do
- {
- uint16_t _b, _ir;
- uint16_t _hi, _lo;
- tsl2561IntegrationTime_t _it = _tsl2561IntegrationTime;
- /* Get the hi/low threshold for the current integration time */
- switch(_it)
- {
- case TSL2561_INTEGRATIONTIME_13MS:
- _hi = TSL2561_AGC_THI_13MS;
- _lo = TSL2561_AGC_TLO_13MS;
- break;
- case TSL2561_INTEGRATIONTIME_101MS:
- _hi = TSL2561_AGC_THI_101MS;
- _lo = TSL2561_AGC_TLO_101MS;
- break;
- default:
- _hi = TSL2561_AGC_THI_402MS;
- _lo = TSL2561_AGC_TLO_402MS;
- break;
- }
- getData(&_b, &_ir);
- /* Run an auto-gain check if we haven't already done so ... */
- if (!_agcCheck)
- {
- if ((_b < _lo) && (_tsl2561Gain == TSL2561_GAIN_1X))
- {
- /* Increase the gain and try again */
- setGain(TSL2561_GAIN_16X);
- /* Drop the previous conversion results */
- getData(&_b, &_ir);
- /* Set a flag to indicate we've adjusted the gain */
- _agcCheck = true;
- }
- else if ((_b > _hi) && (_tsl2561Gain == TSL2561_GAIN_16X))
- {
- /* Drop gain to 1x and try again */
- setGain(TSL2561_GAIN_1X);
- /* Drop the previous conversion results */
- getData(&_b, &_ir);
- /* Set a flag to indicate we've adjusted the gain */
- _agcCheck = true;
- }
- else
- {
- /* Nothing to look at here, keep moving ....
- Reading is either valid, or we're already at the chips limits */
- *broadband = _b;
- *ir = _ir;
- valid = true;
- }
- }
- else
- {
- /* If we've already adjusted the gain once, just return the new results.
- This avoids endless loops where a value is at one extreme pre-gain,
- and the the other extreme post-gain */
- *broadband = _b;
- *ir = _ir;
- valid = true;
- }
- } while (!valid);
- }
- uint32_t calculateLux(uint16_t broadband, uint16_t ir)
- {
- unsigned long chScale;
- unsigned long channel1;
- unsigned long channel0;
-
- /* Make sure the sensor isn't saturated! */
- uint16_t clipThreshold;
- switch (_tsl2561IntegrationTime)
- {
- case TSL2561_INTEGRATIONTIME_13MS:
- clipThreshold = TSL2561_CLIPPING_13MS;
- break;
- case TSL2561_INTEGRATIONTIME_101MS:
- clipThreshold = TSL2561_CLIPPING_101MS;
- break;
- default:
- clipThreshold = TSL2561_CLIPPING_402MS;
- break;
- }
- /* Return 65536 lux if the sensor is saturated */
- if ((broadband > clipThreshold) || (ir > clipThreshold))
- {
- return 65536;
- }
- /* Get the correct scale depending on the intergration time */
- switch (_tsl2561IntegrationTime)
- {
- case TSL2561_INTEGRATIONTIME_13MS:
- chScale = TSL2561_LUX_CHSCALE_TINT0;
- break;
- case TSL2561_INTEGRATIONTIME_101MS:
- chScale = TSL2561_LUX_CHSCALE_TINT1;
- break;
- default: /* No scaling ... integration time = 402ms */
- chScale = (1 << TSL2561_LUX_CHSCALE);
- break;
- }
- /* Scale for gain (1x or 16x) */
- if (!_tsl2561Gain) chScale = chScale << 4;
- /* Scale the channel values */
- channel0 = (broadband * chScale) >> TSL2561_LUX_CHSCALE;
- channel1 = (ir * chScale) >> TSL2561_LUX_CHSCALE;
- /* Find the ratio of the channel values (Channel1/Channel0) */
- unsigned long ratio1 = 0;
- if (channel0 != 0) ratio1 = (channel1 << (TSL2561_LUX_RATIOSCALE+1)) / channel0;
- /* round the ratio value */
- unsigned long ratio = (ratio1 + 1) >> 1;
- unsigned int b, m;
- #ifdef TSL2561_PACKAGE_CS
- if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1C))
- {b=TSL2561_LUX_B1C; m=TSL2561_LUX_M1C;}
- else if (ratio <= TSL2561_LUX_K2C)
- {b=TSL2561_LUX_B2C; m=TSL2561_LUX_M2C;}
- else if (ratio <= TSL2561_LUX_K3C)
- {b=TSL2561_LUX_B3C; m=TSL2561_LUX_M3C;}
- else if (ratio <= TSL2561_LUX_K4C)
- {b=TSL2561_LUX_B4C; m=TSL2561_LUX_M4C;}
- else if (ratio <= TSL2561_LUX_K5C)
- {b=TSL2561_LUX_B5C; m=TSL2561_LUX_M5C;}
- else if (ratio <= TSL2561_LUX_K6C)
- {b=TSL2561_LUX_B6C; m=TSL2561_LUX_M6C;}
- else if (ratio <= TSL2561_LUX_K7C)
- {b=TSL2561_LUX_B7C; m=TSL2561_LUX_M7C;}
- else if (ratio > TSL2561_LUX_K8C)
- {b=TSL2561_LUX_B8C; m=TSL2561_LUX_M8C;}
- #else
- if ((ratio >= 0) && (ratio <= TSL2561_LUX_K1T))
- {b=TSL2561_LUX_B1T; m=TSL2561_LUX_M1T;}
- else if (ratio <= TSL2561_LUX_K2T)
- {b=TSL2561_LUX_B2T; m=TSL2561_LUX_M2T;}
- else if (ratio <= TSL2561_LUX_K3T)
- {b=TSL2561_LUX_B3T; m=TSL2561_LUX_M3T;}
- else if (ratio <= TSL2561_LUX_K4T)
- {b=TSL2561_LUX_B4T; m=TSL2561_LUX_M4T;}
- else if (ratio <= TSL2561_LUX_K5T)
- {b=TSL2561_LUX_B5T; m=TSL2561_LUX_M5T;}
- else if (ratio <= TSL2561_LUX_K6T)
- {b=TSL2561_LUX_B6T; m=TSL2561_LUX_M6T;}
- else if (ratio <= TSL2561_LUX_K7T)
- {b=TSL2561_LUX_B7T; m=TSL2561_LUX_M7T;}
- else if (ratio > TSL2561_LUX_K8T)
- {b=TSL2561_LUX_B8T; m=TSL2561_LUX_M8T;}
- #endif
- unsigned long temp;
- temp = ((channel0 * b) - (channel1 * m));
- /* Do not allow negative lux value */
- if (temp < 0) temp = 0;
- /* Round lsb (2^(LUX_SCALE-1)) */
- temp += (1 << (TSL2561_LUX_LUXSCALE-1));
- /* Strip off fractional portion */
- uint32_t lux = temp >> TSL2561_LUX_LUXSCALE;
- /* Signal I2C had no errors */
- return lux;
- }
- uint32_t readLuxSensor(void)
- {
- uint16_t broadband;
- uint16_t ir;
-
- getLuminosity(&broadband, &ir);
- uint32_t lux = calculateLux(broadband, ir);
- if( lux > 65000 ) lux=65000; // Set a maximum value
- return lux;
- }
- static void tsl2561_task(void * pvParameter)
- {
- uint8_t data[2];
- static uint32_t totalLux = 0;
- static uint16_t loopCnt = 0;
- TickType_t vLastWakeTime = xTaskGetTickCount();
- ESP_ERROR_CHECK(i2c_master_init());
- ESP_LOGI(TAG, "I2C initialized successfully");
- delay(200);
- enable();
- delay(200);
- /* Read the TSL2561_REGISTER_ID register, on power up the register should have the value 0x05
- The returned value consists of:
- Bit 7:4 = Part number
- Bit 3:0 = Revision number
-
- 0x50 = TSL2561T/FN/CL Rev:0
- */
-
- uint8_t id = read8(TSL2561_COMMAND_BIT | TSL2561_REGISTER_ID);
- ESP_LOGI(TAG, "WHO_AM_I = %02X", id);
- setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS);
- setGain(TSL2561_GAIN_1X);
- // Do an initial delay to make the different tasks to work out of sync to each other (not send all data at same time)
- vTaskDelayUntil( &vLastWakeTime, 5000 / portTICK_PERIOD_MS );
- while (1)
- {
- uint32_t lux = readLuxSensor();
- totalLux += lux;
- loopCnt++;
- ESP_LOGI(TAG, "Lux: %u", lux);
- // Once every minute, calc mean lux and send to server.
- if( loopCnt == 6 ) {
- char mqtt_s[50];
- char value_s[10];
- totalLux /= 6;
- sprintf(mqtt_s,"garage/lux");
- sprintf(value_s,"%u",totalLux);
- ESP_LOGI(TAG, "Topic:%s Data:%s", mqtt_s, value_s);
- sendMQTTMessage(mqtt_s, value_s);
- totalLux = 0;
- loopCnt = 0;
- }
-
- vTaskDelayUntil( &vLastWakeTime, 10000 / portTICK_PERIOD_MS );
- }
- }
- void init_tsl2561()
- {
- xTaskCreate(&tsl2561_task, "tsl2561_task", 2048, NULL, 5, NULL);
- }
- #endif
|