ESPHome  2024.4.1
bh1750.cpp
Go to the documentation of this file.
1 #include "bh1750.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace bh1750 {
6 
7 static const char *const TAG = "bh1750.sensor";
8 
9 static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001;
10 static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits
11 static const uint8_t BH1750_COMMAND_MT_REG_LO = 0b01100000; // last 5 bits
12 static const uint8_t BH1750_COMMAND_ONE_TIME_L = 0b00100011;
13 static const uint8_t BH1750_COMMAND_ONE_TIME_H = 0b00100000;
14 static const uint8_t BH1750_COMMAND_ONE_TIME_H2 = 0b00100001;
15 
16 /*
17 bh1750 properties:
18 
19 L-resolution mode:
20 - resolution 4lx (@ mtreg=69)
21 - measurement time: typ=16ms, max=24ms, scaled by MTreg value divided by 69
22 - formula: counts / 1.2 * (69 / MTreg) lx
23 H-resolution mode:
24 - resolution 1lx (@ mtreg=69)
25 - measurement time: typ=120ms, max=180ms, scaled by MTreg value divided by 69
26 - formula: counts / 1.2 * (69 / MTreg) lx
27 H-resolution mode2:
28 - resolution 0.5lx (@ mtreg=69)
29 - measurement time: typ=120ms, max=180ms, scaled by MTreg value divided by 69
30 - formula: counts / 1.2 * (69 / MTreg) / 2 lx
31 
32 MTreg:
33 - min=31, default=69, max=254
34 
35 -> only reason to use l-resolution is faster, but offers no higher range
36 -> below ~7000lx, makes sense to use H-resolution2 @ MTreg=254
37 -> try to maximize MTreg to get lowest noise level
38 */
39 
41  ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str());
42  uint8_t turn_on = BH1750_COMMAND_POWER_ON;
43  if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
44  this->mark_failed();
45  return;
46  }
47 }
48 
49 void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f) {
50  // turn on (after one-shot sensor automatically powers down)
51  uint8_t turn_on = BH1750_COMMAND_POWER_ON;
52  if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
53  ESP_LOGW(TAG, "Turning on BH1750 failed");
54  f(NAN);
55  return;
56  }
57 
58  if (active_mtreg_ != mtreg) {
59  // set mtreg
60  uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
61  uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
62  if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
63  ESP_LOGW(TAG, "Setting measurement time for BH1750 failed");
64  active_mtreg_ = 0;
65  f(NAN);
66  return;
67  }
68  active_mtreg_ = mtreg;
69  }
70 
71  uint8_t cmd;
72  uint16_t meas_time;
73  switch (mode) {
74  case BH1750_MODE_L:
75  cmd = BH1750_COMMAND_ONE_TIME_L;
76  meas_time = 24 * mtreg / 69;
77  break;
78  case BH1750_MODE_H:
79  cmd = BH1750_COMMAND_ONE_TIME_H;
80  meas_time = 180 * mtreg / 69;
81  break;
82  case BH1750_MODE_H2:
83  cmd = BH1750_COMMAND_ONE_TIME_H2;
84  meas_time = 180 * mtreg / 69;
85  break;
86  default:
87  f(NAN);
88  return;
89  }
90  if (this->write(&cmd, 1) != i2c::ERROR_OK) {
91  ESP_LOGW(TAG, "Starting measurement for BH1750 failed");
92  f(NAN);
93  return;
94  }
95 
96  // probably not needed, but adjust for rounding
97  meas_time++;
98 
99  this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
100  uint16_t raw_value;
101  if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
102  ESP_LOGW(TAG, "Reading BH1750 data failed");
103  f(NAN);
104  return;
105  }
106  raw_value = i2c::i2ctohs(raw_value);
107 
108  float lx = float(raw_value) / 1.2f;
109  lx *= 69.0f / mtreg;
110  if (mode == BH1750_MODE_H2)
111  lx /= 2.0f;
112 
113  f(lx);
114  });
115 }
116 
118  LOG_SENSOR("", "BH1750", this);
119  LOG_I2C_DEVICE(this);
120  if (this->is_failed()) {
121  ESP_LOGE(TAG, "Communication with BH1750 failed!");
122  }
123 
124  LOG_UPDATE_INTERVAL(this);
125 }
126 
128  // first do a quick measurement in L-mode with full range
129  // to find right range
130  this->read_lx_(BH1750_MODE_L, 31, [this](float val) {
131  if (std::isnan(val)) {
132  this->status_set_warning();
133  this->publish_state(NAN);
134  return;
135  }
136 
137  BH1750Mode use_mode;
138  uint8_t use_mtreg;
139  if (val <= 7000) {
140  use_mode = BH1750_MODE_H2;
141  use_mtreg = 254;
142  } else {
143  use_mode = BH1750_MODE_H;
144  // lx = counts / 1.2 * (69 / mtreg)
145  // -> mtreg = counts / 1.2 * (69 / lx)
146  // calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
147  // -> mtreg = 50000*(10/12)*(69/lx)
148  int ideal_mtreg = 50000 * 10 * 69 / (12 * (int) val);
149  use_mtreg = std::min(254, std::max(31, ideal_mtreg));
150  }
151  ESP_LOGV(TAG, "L result: %f -> Calculated mode=%d, mtreg=%d", val, (int) use_mode, use_mtreg);
152 
153  this->read_lx_(use_mode, use_mtreg, [this](float val) {
154  if (std::isnan(val)) {
155  this->status_set_warning();
156  this->publish_state(NAN);
157  return;
158  }
159  ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), val);
160  this->status_clear_warning();
161  this->publish_state(val);
162  });
163  });
164 }
165 
167 
168 } // namespace bh1750
169 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
void read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function< void(float)> &f)
Definition: bh1750.cpp:49
uint16_t i2ctohs(uint16_t i2cshort)
Definition: i2c.h:128
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
ErrorCode read(uint8_t *data, size_t len)
reads an array of bytes from the device using an I2CBus
Definition: i2c.h:160
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
void setup() override
Definition: bh1750.cpp:40
mopeka_std_values val[4]
ErrorCode write(const uint8_t *data, size_t len, bool stop=true)
writes an array of bytes to a device using an I2CBus
Definition: i2c.h:186
No error found during execution of method.
Definition: i2c_bus.h:13
void update() override
Definition: bh1750.cpp:127
void status_clear_warning()
Definition: component.cpp:166
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:151
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
float get_setup_priority() const override
Definition: bh1750.cpp:166
constexpr const char * c_str() const
Definition: string_ref.h:68
void dump_config() override
Definition: bh1750.cpp:117
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
const StringRef & get_name() const
Definition: entity_base.cpp:10
stm32_cmd_t * cmd
Definition: stm32flash.h:96