Compare commits

...
3 changed files with 97 additions and 14 deletions

No files matched your search

+18 -8
View File
@@ -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);
}
}
}
+4 -6
View File
@@ -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 {
+75
View File
@@ -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