mirror of
https://github.com/esphome/esphome.git
synced 2026-11-15 17:45:57 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
736b01eb04 | ||
|
|
ec87dfd80b | ||
|
|
4f55d8bd6e | ||
|
|
ed8e24e8cb | ||
|
|
7a1c4e0b0b | ||
|
|
c38792c401 | ||
|
|
364c86c184 |
No files matched your search
@@ -1,5 +1,7 @@
|
||||
#include "i2c.h"
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -9,17 +11,25 @@ namespace esphome::i2c {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "i2c");
|
||||
|
||||
// Addresses 0x08 to 0x77; the others are reserved
|
||||
static constexpr uint8_t SCAN_FIRST_ADDRESS = 0x08;
|
||||
static constexpr uint8_t SCAN_END_ADDRESS = 0x78;
|
||||
static constexpr uint8_t SCAN_RESULT_ERROR = 0x80;
|
||||
|
||||
void I2CBus::i2c_scan_() {
|
||||
for (uint8_t address = 8; address != 120; address++) {
|
||||
auto err = write_readv(address, nullptr, 0, nullptr, 0);
|
||||
uint8_t results[SCAN_END_ADDRESS - SCAN_FIRST_ADDRESS];
|
||||
size_t count = 0;
|
||||
for (uint8_t address = SCAN_FIRST_ADDRESS; address != SCAN_END_ADDRESS; address++) {
|
||||
auto err = this->write_readv(address, nullptr, 0, nullptr, 0);
|
||||
if (err == ERROR_OK) {
|
||||
scan_results_.emplace_back(address, true);
|
||||
results[count++] = address;
|
||||
} else if (err == ERROR_UNKNOWN) {
|
||||
scan_results_.emplace_back(address, false);
|
||||
results[count++] = address | SCAN_RESULT_ERROR;
|
||||
}
|
||||
// it takes 16sec to scan on nrf52. It prevents board reset.
|
||||
arch_feed_wdt();
|
||||
}
|
||||
this->scan_results_.set(results, count);
|
||||
}
|
||||
|
||||
void I2CBus::dump_scan_results_() {
|
||||
@@ -30,11 +40,11 @@ void I2CBus::dump_scan_results_() {
|
||||
ESP_LOGCONFIG(TAG, "Found no devices");
|
||||
return;
|
||||
}
|
||||
for (const auto &s : this->scan_results_) {
|
||||
if (s.second) {
|
||||
ESP_LOGCONFIG(TAG, "Found device at address 0x%02X", s.first);
|
||||
for (uint8_t result : std::span<const uint8_t>(this->scan_results_)) {
|
||||
if (result & SCAN_RESULT_ERROR) {
|
||||
ESP_LOGE(TAG, "Unknown error at address 0x%02X", result & ~SCAN_RESULT_ERROR);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first);
|
||||
ESP_LOGCONFIG(TAG, "Found device at address 0x%02X", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -64,13 +62,13 @@ class I2CBus {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/// @brief Scans the I2C bus for devices. Devices presence is kept in an array of std::pair
|
||||
/// that contains the address and the corresponding bool presence flag.
|
||||
/// @brief Scans the I2C bus and keeps each address that answered (ERROR_OK) or returned ERROR_UNKNOWN
|
||||
void i2c_scan_();
|
||||
/// @brief Logs the scan results at config level, if the bus was scanned
|
||||
void dump_scan_results_();
|
||||
std::vector<std::pair<uint8_t, bool>> scan_results_; ///< array containing scan results
|
||||
bool scan_{false}; ///< Should we scan ? Can be set in the yaml
|
||||
/// One byte per kept address, bit 7 set for ERROR_UNKNOWN; inline for up to 4 devices
|
||||
SmallInlineBuffer<4> scan_results_;
|
||||
bool scan_{false}; ///< Should we scan ? Can be set in the yaml
|
||||
};
|
||||
|
||||
class InternalI2CBus : public I2CBus {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <set>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome::i2c::testing {
|
||||
|
||||
class ScanBus : public I2CBus {
|
||||
public:
|
||||
ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer,
|
||||
size_t read_count) override {
|
||||
this->probed.push_back(address);
|
||||
if (this->errors.contains(address))
|
||||
return ERROR_UNKNOWN;
|
||||
return this->present.contains(address) ? ERROR_OK : ERROR_NOT_ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
void scan() { this->i2c_scan_(); }
|
||||
std::vector<uint8_t> results() const {
|
||||
std::span<const uint8_t> span = this->scan_results_;
|
||||
return {span.begin(), span.end()};
|
||||
}
|
||||
|
||||
std::set<uint8_t> present;
|
||||
std::set<uint8_t> errors;
|
||||
std::vector<uint8_t> probed;
|
||||
};
|
||||
|
||||
TEST(I2CScan, ProbesTheUnreservedAddresses) {
|
||||
ScanBus bus;
|
||||
bus.scan();
|
||||
ASSERT_EQ(bus.probed.size(), 112u);
|
||||
EXPECT_EQ(bus.probed.front(), 0x08);
|
||||
EXPECT_EQ(bus.probed.back(), 0x77);
|
||||
EXPECT_TRUE(bus.results().empty());
|
||||
}
|
||||
|
||||
TEST(I2CScan, KeepsFoundAddressesInOrder) {
|
||||
ScanBus bus;
|
||||
bus.present = {0x48, 0x10, 0x76, 0x3C};
|
||||
bus.scan();
|
||||
EXPECT_EQ(bus.results(), (std::vector<uint8_t>{0x10, 0x3C, 0x48, 0x76}));
|
||||
}
|
||||
|
||||
TEST(I2CScan, MarksUnknownErrorsInBitSeven) {
|
||||
ScanBus bus;
|
||||
bus.present = {0x20, 0x77};
|
||||
bus.errors = {0x08, 0x40};
|
||||
bus.scan();
|
||||
EXPECT_EQ(bus.results(), (std::vector<uint8_t>{0x88, 0x20, 0xC0, 0x77}));
|
||||
}
|
||||
|
||||
TEST(I2CScan, StuckBusKeepsAllAddresses) {
|
||||
ScanBus bus;
|
||||
for (uint8_t address = 0x08; address < 0x78; address++)
|
||||
bus.present.insert(address);
|
||||
bus.scan();
|
||||
ASSERT_EQ(bus.probed.size(), 112u);
|
||||
EXPECT_EQ(bus.results(), bus.probed);
|
||||
}
|
||||
|
||||
TEST(I2CScan, RescanReplacesTheResults) {
|
||||
ScanBus bus;
|
||||
bus.present = {0x10, 0x20, 0x30, 0x40, 0x50};
|
||||
bus.scan();
|
||||
EXPECT_EQ(bus.results(), (std::vector<uint8_t>{0x10, 0x20, 0x30, 0x40, 0x50}));
|
||||
bus.present = {0x60};
|
||||
bus.scan();
|
||||
EXPECT_EQ(bus.results(), (std::vector<uint8_t>{0x60}));
|
||||
}
|
||||
|
||||
} // namespace esphome::i2c::testing
|
||||
Reference in new issue
Block a user