ESPHome 2026.6.0-dev
Loading...
Searching...
No Matches
i2c_bus_host.cpp
Go to the documentation of this file.
1#ifdef USE_HOST
2#if defined(__linux__)
3
4#include "i2c_bus_host.h"
6#include "esphome/core/log.h"
7
8#include <fcntl.h>
9#include <linux/i2c-dev.h>
10#include <linux/i2c.h>
11#include <sys/ioctl.h>
12#include <unistd.h>
13#include <cerrno>
14#include <cstdint>
15#include <cstring>
16
17namespace esphome::i2c {
18
19static const char *const TAG = "i2c.host";
20
22 if (this->file_descriptor_ != -1) {
23 close(this->file_descriptor_);
24 this->file_descriptor_ = -1;
25 }
26}
27
29 ESP_LOGCONFIG(TAG, "Setting up I2C bus...");
30
31 // Open I2C device file
32 this->file_descriptor_ = open(this->device_.c_str(), O_RDWR);
33 if (this->file_descriptor_ == -1) {
34 int err = errno;
35 if (err == ENOENT) {
36 this->update_error_("not found");
37 } else if (err == EACCES) {
38 this->update_error_("permission denied");
39 } else {
40 this->update_error_(std::string("failed to open: ") + strerror(err));
41 }
42 this->mark_failed();
43 return;
44 }
45
46 this->initialized_ = true;
47 ESP_LOGCONFIG(TAG, " Device: %s", this->device_.c_str());
48
49 // Run bus scan if enabled
50 if (this->scan_) {
51 this->i2c_scan_();
52 }
53}
54
56 ESP_LOGCONFIG(TAG, "I2C Bus:");
57 ESP_LOGCONFIG(TAG, " Device: %s", this->device_.c_str());
58 // Bus frequency cannot be set from userspace via i2c-dev; report it as informational only
59 ESP_LOGCONFIG(TAG, " Frequency: %u Hz (informational; not applied on host)", this->frequency_);
60
61 if (!this->first_error_.empty()) {
62 ESP_LOGE(TAG, " Setup Error: %s", this->first_error_.c_str());
63 }
64
65 if (this->scan_) {
66 ESP_LOGI(TAG, " Scan Results:");
67 for (const auto &s : this->scan_results_) {
68 if (s.second) {
69 ESP_LOGI(TAG, " 0x%02X: Found", s.first);
70 }
71 }
72 }
73}
74
75ErrorCode HostI2CBus::write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count,
76 uint8_t *read_buffer, size_t read_count) {
77 if (!this->initialized_) {
78 ESP_LOGE(TAG, "I2C bus not initialized");
80 }
81
82 ESP_LOGVV(TAG, "I2C write_readv addr=0x%02X write=%zu read=%zu", address, write_count, read_count);
83
84 // Handle special case: probe (no write data, no read data)
85 // This is used for device detection during bus scanning
86 if (write_count == 0 && read_count == 0) {
87 struct i2c_msg msg;
88 msg.addr = address;
89 msg.flags = 0;
90 msg.len = 0;
91 msg.buf = nullptr;
92
93 struct i2c_rdwr_ioctl_data rdwr_data;
94 rdwr_data.msgs = &msg;
95 rdwr_data.nmsgs = 1;
96
97 int ret = ioctl(this->file_descriptor_, I2C_RDWR, &rdwr_data);
98 if (ret < 0) {
99 int err = errno;
100 // If I2C_RDWR not supported, try SMBus Quick command (what i2cdetect uses)
101 if (err == EOPNOTSUPP || err == ENOSYS) {
102 ESP_LOGVV(TAG, "I2C_RDWR probe failed, trying SMBus Quick for addr=0x%02X", address);
103 if (ioctl(this->file_descriptor_, I2C_SLAVE, address) < 0) { // NOLINT
104 return this->map_errno_to_error_code_(errno);
105 }
106 // Use I2C_SMBUS ioctl with Quick command
107 union i2c_smbus_data data;
108 struct i2c_smbus_ioctl_data args;
109 args.read_write = I2C_SMBUS_WRITE;
110 args.command = 0;
111 args.size = I2C_SMBUS_QUICK;
112 args.data = &data;
113 ret = ioctl(this->file_descriptor_, I2C_SMBUS, &args);
114 if (ret < 0) {
115 return this->map_errno_to_error_code_(errno);
116 }
117 return ERROR_OK;
118 }
119 return this->map_errno_to_error_code_(err);
120 }
121 return ERROR_OK;
122 }
123
124 // i2c_msg.len is a 16-bit field; reject transfers that would silently truncate
125 if (write_count > UINT16_MAX || read_count > UINT16_MAX) {
126 ESP_LOGE(TAG, "I2C transfer too large: write=%zu read=%zu (max %u)", write_count, read_count,
127 (unsigned) UINT16_MAX);
128 return ERROR_TOO_LARGE;
129 }
130
131 // Prepare messages for combined write-read transaction
132 struct i2c_msg msgs[2];
133 int num_msgs = 0;
134
135 // Add write message if write data present
136 if (write_count > 0) {
137 msgs[num_msgs].addr = address;
138 msgs[num_msgs].flags = 0; // Write
139 msgs[num_msgs].len = write_count;
140 msgs[num_msgs].buf = const_cast<uint8_t *>(write_buffer);
141 num_msgs++;
142 }
143
144 // Add read message if read data requested
145 if (read_count > 0) {
146 msgs[num_msgs].addr = address;
147 msgs[num_msgs].flags = I2C_M_RD; // Read
148 msgs[num_msgs].len = read_count;
149 msgs[num_msgs].buf = read_buffer;
150 num_msgs++;
151 }
152
153 // Execute I2C transaction
154 struct i2c_rdwr_ioctl_data rdwr_data;
155 rdwr_data.msgs = msgs;
156 rdwr_data.nmsgs = num_msgs;
157
158 int ret = ioctl(this->file_descriptor_, I2C_RDWR, &rdwr_data);
159 if (ret < 0) {
160 int err = errno;
161 if (err == EOPNOTSUPP || err == ENOSYS) {
162 ESP_LOGV(TAG, "I2C_RDWR not supported, using I2C_SLAVE fallback for addr=0x%02X", address); // NOLINT
163 if (ioctl(this->file_descriptor_, I2C_SLAVE, address) < 0) { // NOLINT
164 ESP_LOGV(TAG, "I2C_SLAVE ioctl failed: %s", strerror(errno)); // NOLINT
165 return this->map_errno_to_error_code_(errno);
166 }
167 // Perform write if needed
168 if (write_count > 0) {
169 ssize_t written = ::write(this->file_descriptor_, write_buffer, write_count);
170 if (written != (ssize_t) write_count) {
171 int write_err = errno;
172 // If write() also fails with EOPNOTSUPP, try I2C_SMBUS as last resort
173 if (write_err == EOPNOTSUPP || write_err == ENOSYS) {
174 ESP_LOGV(TAG, "I2C_SLAVE write not supported, trying I2C_SMBUS for addr=0x%02X", address); // NOLINT
175 // Use I2C_SMBUS_I2C_BLOCK_DATA for writes up to 32 bytes
176 // Standard SMBus mapping: first byte is command, remaining bytes are data
177 if (write_count < 1) {
178 ESP_LOGE(TAG, "Write size too small for I2C_SMBUS");
180 }
181 if (write_count > I2C_SMBUS_BLOCK_MAX + 1) {
182 ESP_LOGE(TAG, "Write size %zu exceeds I2C_SMBUS_BLOCK_MAX+1 (%d)", write_count, I2C_SMBUS_BLOCK_MAX + 1);
184 }
185 union i2c_smbus_data data;
186 // Standard SMBus: first byte = command, rest = data
187 uint8_t command = write_buffer[0];
188 size_t data_len = write_count - 1;
189 data.block[0] = data_len;
190 if (data_len > 0) {
191 memcpy(&data.block[1], write_buffer + 1, data_len);
192 }
193
194 struct i2c_smbus_ioctl_data args;
195 args.read_write = I2C_SMBUS_WRITE;
196 args.command = command;
197 args.size = I2C_SMBUS_I2C_BLOCK_DATA;
198 args.data = &data;
199
200 ret = ioctl(this->file_descriptor_, I2C_SMBUS, &args);
201 if (ret < 0) {
202 ESP_LOGV(TAG, "I2C_SMBUS write failed: %s", strerror(errno));
203 return this->map_errno_to_error_code_(errno);
204 }
205 } else {
206 ESP_LOGV(TAG, "I2C write failed: %s", strerror(write_err));
207 return this->map_errno_to_error_code_(write_err);
208 }
209 }
210 }
211 // Perform read if needed
212 if (read_count > 0) {
213 ssize_t bytes_read = ::read(this->file_descriptor_, read_buffer, read_count);
214 if (bytes_read != (ssize_t) read_count) {
215 int read_err = errno;
216 // If read() also fails with EOPNOTSUPP, try I2C_SMBUS as last resort
217 if (read_err == EOPNOTSUPP || read_err == ENOSYS) {
218 ESP_LOGV(TAG, "I2C_SLAVE read not supported, trying I2C_SMBUS for addr=0x%02X", address); // NOLINT
219 // Use I2C_SMBUS_I2C_BLOCK_DATA for reads up to 32 bytes
220 if (read_count > I2C_SMBUS_BLOCK_MAX) {
221 ESP_LOGE(TAG, "Read size %zu exceeds I2C_SMBUS_BLOCK_MAX (%d)", read_count, I2C_SMBUS_BLOCK_MAX);
223 }
224 union i2c_smbus_data data;
225 data.block[0] = read_count;
226
227 struct i2c_smbus_ioctl_data args;
228 args.read_write = I2C_SMBUS_READ;
229 args.command = 0; // Start register/command
230 args.size = I2C_SMBUS_I2C_BLOCK_DATA;
231 args.data = &data;
232
233 ret = ioctl(this->file_descriptor_, I2C_SMBUS, &args);
234 if (ret < 0) {
235 ESP_LOGV(TAG, "I2C_SMBUS read failed: %s", strerror(errno));
236 return this->map_errno_to_error_code_(errno);
237 }
238 // I2C_SMBUS_I2C_BLOCK_DATA returns the actual byte count in block[0];
239 // a short read means we did not receive all requested bytes
240 if (data.block[0] < read_count) {
241 ESP_LOGV(TAG, "I2C_SMBUS short read: got %u, expected %zu", data.block[0], read_count);
243 }
244 // Copy data from SMBus buffer to output buffer
245 memcpy(read_buffer, &data.block[1], read_count);
246 } else {
247 ESP_LOGV(TAG, "I2C read failed: %s", strerror(read_err));
248 return this->map_errno_to_error_code_(read_err);
249 }
250 }
251 }
252 ESP_LOGVV(TAG, "I2C transaction successful (I2C_SLAVE method)"); // NOLINT
253 return ERROR_OK;
254 }
255 ESP_LOGV(TAG, "I2C transaction failed: %s", strerror(err));
256 return this->map_errno_to_error_code_(err);
257 }
258
259 ESP_LOGVV(TAG, "I2C transaction successful");
260 return ERROR_OK;
261}
262
264 switch (err) {
265 case ENXIO:
267 case ETIMEDOUT:
268 return ERROR_TIMEOUT;
269 case EINVAL:
271 case ENODEV:
272 case ENOTTY:
274 case EOPNOTSUPP:
275 case ENOSYS:
276 // Operation not supported - some I2C adapters don't support zero-length transactions
277 ESP_LOGVV(TAG, "I2C adapter does not support this operation (likely zero-length probe)");
279 default:
280 ESP_LOGV(TAG, "Unmapped error code: %d (%s)", err, strerror(err));
281 return ERROR_UNKNOWN;
282 }
283}
284
285void HostI2CBus::update_error_(const std::string &error) {
286 if (this->first_error_.empty()) {
287 this->first_error_ = error;
288 }
289 ESP_LOGE(TAG, "[%s] %s", this->device_.c_str(), error.c_str());
290}
291
292} // namespace esphome::i2c
293
294#else
295#error "HostI2CBus is only supported on Linux"
296#endif // defined(__linux__)
297#endif // USE_HOST
uint8_t address
Definition bl0906.h:4
void mark_failed()
Mark this component as failed.
ErrorCode map_errno_to_error_code_(int err)
void update_error_(const std::string &error)
ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, size_t read_count) override
void dump_config() override
bool scan_
Should we scan ? Can be set in the yaml.
Definition i2c_bus.h:61
std::vector< std::pair< uint8_t, bool > > scan_results_
array containing scan results
Definition i2c_bus.h:60
ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len, bool stop=true)
Definition i2c_bus.h:52
void i2c_scan_()
Scans the I2C bus for devices.
Definition i2c.cpp:12
ErrorCode read(uint8_t address, uint8_t *buffer, size_t len)
Definition i2c_bus.h:48
__int64 ssize_t
Definition httplib.h:178
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition i2c_bus.h:12
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:14
@ ERROR_TOO_LARGE
requested a transfer larger than buffers can hold
Definition i2c_bus.h:19
@ ERROR_INVALID_ARGUMENT
method called invalid argument(s)
Definition i2c_bus.h:15
@ ERROR_TIMEOUT
timeout while waiting to receive bytes
Definition i2c_bus.h:17
@ ERROR_NOT_ACKNOWLEDGED
I2C bus acknowledgment not received.
Definition i2c_bus.h:16
@ ERROR_NOT_INITIALIZED
call method to a not initialized bus
Definition i2c_bus.h:18
@ ERROR_UNKNOWN
miscellaneous I2C error during execution
Definition i2c_bus.h:20
const char int const __FlashStringHelper va_list args
Definition log.h:74
int written
Definition helpers.h:1045