๐ BaseI2c API Reference
๐ Overview
BaseI2c is the abstract base class for I2C (Inter-Integrated Circuit) device communication in the
HardFOC system.
It provides a unified interface for communicating with sensors, displays, memory devices,
and other I2C peripherals with comprehensive error handling and device management.
โจ Features
- ๐ฏ Device-Centric Design - Each instance represents a single I2C device with pre-configured address
- ๐ Multi-Speed Support - Standard (100kHz), Fast (400kHz), and Fast+ (1MHz) modes
- ๐ก Register Operations - Convenient read/write register methods for easy sensor access
- ๐ Device Discovery - Built-in device scanning and presence detection
- โฐ Configurable Timeouts - Per-operation timeout control for reliable communication
- ๐ก๏ธ Error Recovery - Automatic bus recovery and error handling mechanisms
- ๐ Performance Monitoring - Comprehensive statistics and bus health diagnostics
- ๐ง Lazy Initialization - Resources allocated only when needed
๐ Header File
1
#include "inc/base/BaseI2c.h"
๐ฏ Type Definitions
๐จ Error Codes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
enum class hf_i2c_err_t : hf_u8_t {
I2C_SUCCESS = 0, // โ
Success
I2C_ERR_FAILURE = 1, // โ General failure
I2C_ERR_NOT_INITIALIZED = 2, // โ ๏ธ Not initialized
I2C_ERR_ALREADY_INITIALIZED = 3, // โ ๏ธ Already initialized
I2C_ERR_INVALID_PARAMETER = 4, // ๐ซ Invalid parameter
I2C_ERR_NULL_POINTER = 5, // ๐ซ Null pointer
I2C_ERR_OUT_OF_MEMORY = 6, // ๐พ Out of memory
I2C_ERR_BUS_BUSY = 7, // ๐ Bus busy
I2C_ERR_BUS_ERROR = 8, // ๐ฅ Bus error
I2C_ERR_BUS_ARBITRATION_LOST = 9, // โ๏ธ Arbitration lost
I2C_ERR_BUS_NOT_AVAILABLE = 10, // ๐ซ Bus not available
I2C_ERR_BUS_TIMEOUT = 11, // โฐ Bus timeout
I2C_ERR_DEVICE_NOT_FOUND = 12, // ๐ Device not found
I2C_ERR_DEVICE_NACK = 13, // ๐ซ Device NACK
I2C_ERR_DEVICE_NOT_RESPONDING = 14, // ๐ Device not responding
I2C_ERR_INVALID_ADDRESS = 15, // ๐ Invalid device address
I2C_ERR_DATA_TOO_LONG = 16, // ๐ Data too long
I2C_ERR_READ_FAILURE = 17, // ๐ Read failure
I2C_ERR_WRITE_FAILURE = 18, // โ๏ธ Write failure
I2C_ERR_TIMEOUT = 19, // โฐ Operation timeout
I2C_ERR_HARDWARE_FAULT = 20, // ๐ฅ Hardware fault
I2C_ERR_COMMUNICATION_FAILURE = 21, // ๐ก Communication failure
I2C_ERR_VOLTAGE_OUT_OF_RANGE = 22, // โก Voltage out of range
I2C_ERR_CLOCK_STRETCH_TIMEOUT = 23, // โฑ๏ธ Clock stretch timeout
I2C_ERR_INVALID_CONFIGURATION = 24, // โ๏ธ Invalid configuration
I2C_ERR_UNSUPPORTED_OPERATION = 25, // ๐ซ Unsupported operation
I2C_ERR_INVALID_CLOCK_SPEED = 26, // ๐ป Invalid clock speed
I2C_ERR_PIN_CONFIGURATION_ERROR = 27, // ๐ Pin configuration error
I2C_ERR_SYSTEM_ERROR = 28, // ๐ป System error
I2C_ERR_PERMISSION_DENIED = 29, // ๐ Permission denied
I2C_ERR_OPERATION_ABORTED = 30 // ๐ Operation aborted
};
๐ Statistics Structure
1
2
3
4
5
6
7
8
9
10
11
12
struct hf_i2c_statistics_t {
hf_u32_t total_transactions; // ๐ Total I2C transactions
hf_u32_t successful_transactions; // โ
Successful transactions
hf_u32_t failed_transactions; // โ Failed transactions
hf_u32_t timeout_count; // โฐ Timeout occurrences
hf_u32_t nack_count; // ๐ซ NACK count
hf_u32_t arbitration_lost_count; // โ๏ธ Arbitration lost count
hf_u32_t bus_error_count; // ๐ฅ Bus error count
hf_u32_t bytes_transmitted; // ๐ค Total bytes transmitted
hf_u32_t bytes_received; // ๐ฅ Total bytes received
hf_u32_t average_transaction_time_us; // โฑ๏ธ Average transaction time
};
๐ฉบ Diagnostics Structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct hf_i2c_diagnostics_t {
bool bus_healthy; // ๐ข Overall bus health
bool sda_line_state; // ๐ก SDA line state (true = high)
bool scl_line_state; // ๐ SCL line state (true = high)
bool bus_locked; // ๐ Bus lock status
hf_i2c_err_t last_error_code; // โ ๏ธ Last error code
hf_u32_t last_error_timestamp_us; // ๐ Last error timestamp
hf_u32_t consecutive_errors; // ๐ Consecutive error count
hf_u32_t error_recovery_attempts; // ๐ Error recovery attempts
float bus_utilization_percent; // ๐ Bus utilization percentage
hf_u32_t average_response_time_us; // โฑ๏ธ Average device response time
hf_u32_t clock_stretching_events; // ๐ Clock stretching events
hf_u32_t active_device_count; // ๐ Active device count
};
๐๏ธ Class Interface
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class BaseI2c {
public:
// ๐ง Lifecycle management
virtual ~BaseI2c() noexcept = default;
BaseI2c(const BaseI2c&) = delete;
BaseI2c& operator=(const BaseI2c&) = delete;
bool EnsureInitialized() noexcept;
bool EnsureDeinitialized() noexcept;
bool IsInitialized() const noexcept;
// ๐ Core operations (pure virtual)
virtual bool Initialize() noexcept = 0;
virtual bool Deinitialize() noexcept = 0;
// ๐ก Basic I2C operations
virtual hf_i2c_err_t Write(const hf_u8_t* data, hf_u16_t length,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept = 0;
virtual hf_i2c_err_t Read(hf_u8_t* data, hf_u16_t length,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept = 0;
virtual hf_i2c_err_t WriteRead(const hf_u8_t* write_data, hf_u16_t write_length,
hf_u8_t* read_data, hf_u16_t read_length,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept = 0;
// ๐ Register operations (convenience methods)
virtual hf_i2c_err_t WriteRegister(hf_u8_t register_address, hf_u8_t value,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept = 0;
virtual hf_i2c_err_t ReadRegister(hf_u8_t register_address, hf_u8_t& value,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept = 0;
virtual hf_i2c_err_t WriteRegisters(hf_u8_t register_address, const hf_u8_t* data, hf_u16_t length,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept = 0;
virtual hf_i2c_err_t ReadRegisters(hf_u8_t register_address, hf_u8_t* data, hf_u16_t length,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept = 0;
// ๐ Device information and management
virtual bool IsDevicePresent(hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept = 0;
virtual hf_u8_t GetDeviceAddress() const noexcept = 0;
virtual hf_frequency_hz_t GetClockSpeed() const noexcept = 0;
virtual hf_i2c_err_t SetClockSpeed(hf_frequency_hz_t clock_speed_hz) noexcept = 0;
// ๐ Diagnostics and monitoring
virtual hf_i2c_err_t GetStatistics(hf_i2c_statistics_t& statistics) const noexcept = 0;
virtual hf_i2c_err_t GetDiagnostics(hf_i2c_diagnostics_t& diagnostics) const noexcept = 0;
virtual hf_i2c_err_t ResetStatistics() noexcept = 0;
virtual hf_i2c_err_t ResetDiagnostics() noexcept = 0;
};
๐ฏ Core Methods
๐ง Initialization
1
bool EnsureInitialized() noexcept;
Purpose: ๐ Lazy initialization - automatically initializes I2C if not already done
Returns: true if successful, false on failure
Usage: Call before any I2C operations
๐ก Basic Communication
1
2
3
4
5
6
7
hf_i2c_err_t Write(const hf_u8_t* data, hf_u16_t length,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept;
hf_i2c_err_t Read(hf_u8_t* data, hf_u16_t length,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept;
hf_i2c_err_t WriteRead(const hf_u8_t* write_data, hf_u16_t write_length,
hf_u8_t* read_data, hf_u16_t read_length,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept;
Purpose: ๐ค๐ฅ Basic I2C read/write operations
Parameters: Data buffers, lengths, and optional timeout
Returns: Error code indicating success or failure
๐ Register Operations
1
2
3
4
hf_i2c_err_t WriteRegister(hf_u8_t register_address, hf_u8_t value,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept;
hf_i2c_err_t ReadRegister(hf_u8_t register_address, hf_u8_t& value,
hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept;
Purpose: ๐ Convenient register read/write for sensor devices
Parameters: Register address, data value, optional timeout
Returns: Error code indicating success or failure
๐ Device Management
1
2
3
bool IsDevicePresent(hf_timeout_ms_t timeout_ms = HF_TIMEOUT_DEFAULT_MS) noexcept;
hf_u8_t GetDeviceAddress() const noexcept;
hf_frequency_hz_t GetClockSpeed() const noexcept;
Purpose: ๐ Device detection and configuration query
Returns: Device presence, address, or clock speed information
๐ก Usage Examples
๐ก๏ธ Temperature Sensor (LM75A)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "inc/mcu/esp32/EspI2c.h"
class LM75ATemperatureSensor {
private:
EspI2c sensor*;
static constexpr hf_u8_t LM75A_ADDRESS = 0x48;
static constexpr hf_u8_t TEMP_REGISTER = 0x00;
static constexpr hf_u8_t CONFIG_REGISTER = 0x01;
public:
LM75ATemperatureSensor() : sensor*(I2C_NUM_0, LM75A_ADDRESS, 400000) {}
bool initialize() {
// ๐ Initialize I2C communication
if (!sensor*.EnsureInitialized()) {
printf("โ Failed to initialize LM75A I2C\n");
return false;
}
// ๐ Check if device is present
if (!sensor*.IsDevicePresent()) {
printf("โ LM75A not found at address 0x%02X\n", LM75A_ADDRESS);
return false;
}
// โ๏ธ Configure sensor for normal operation
hf_i2c_err_t result = sensor*.WriteRegister(CONFIG_REGISTER, 0x00);
if (result != hf_i2c_err_t::I2C_SUCCESS) {
printf("โ Failed to configure LM75A: %s\n", HfI2CErrToString(result).data());
return false;
}
printf("โ
LM75A temperature sensor initialized\n");
return true;
}
float read_temperature() {
// ๐ Read temperature register (2 bytes)
hf_u8_t temp_data[2];
hf_i2c_err_t result = sensor*.ReadRegisters(TEMP_REGISTER, temp_data, 2);
if (result != hf_i2c_err_t::I2C_SUCCESS) {
printf("โ Failed to read temperature: %s\n", HfI2CErrToString(result).data());
return -999.0f; // Error value
}
// ๐งฎ Convert raw data to temperature (LM75A format: 9-bit, 0.5ยฐC resolution)
hf_i16_t raw_temp = (temp_data[0] << 8) | temp_data[1];
raw_temp >>= 7; // Shift to get 9-bit value
float temperature = raw_temp * 0.5f;
printf("๐ก๏ธ Temperature: %.1fยฐC\n", temperature);
return temperature;
}
bool is_connected() {
return sensor*.IsDevicePresent(100); // 100ms timeout
}
};
๐ OLED Display (SSD1306)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class SSD1306Display {
private:
EspI2c display*;
static constexpr hf_u8_t SSD1306_ADDRESS = 0x3C;
static constexpr hf_u8_t COMMAND_MODE = 0x00;
static constexpr hf_u8_t DATA_MODE = 0x40;
public:
SSD1306Display() : display*(I2C_NUM_0, SSD1306_ADDRESS, 400000) {}
bool initialize() {
// ๐ Initialize display I2C
if (!display*.EnsureInitialized()) {
printf("โ Failed to initialize SSD1306 I2C\n");
return false;
}
// ๐ Check if display is connected
if (!display*.IsDevicePresent()) {
printf("โ SSD1306 not found at address 0x%02X\n", SSD1306_ADDRESS);
return false;
}
// ๐จ Initialize display with command sequence
const hf_u8_t init_commands[] = {
COMMAND_MODE,
0xAE, // Display OFF
0x20, 0x00, // Memory addressing mode = horizontal
0xB0, // Page start address = 0
0xC8, // COM scan direction
0x00, // Low column start address
0x10, // High column start address
0x40, // Display start line = 0
0x81, 0x3F, // Contrast control
0xA1, // Segment re-map
0xA6, // Normal display
0xA8, 0x3F, // Multiplex ratio
0xA4, // Resume to RAM content display
0xD3, 0x00, // Display offset
0xD5, 0xF0, // Display clock divide ratio
0xD9, 0x22, // Pre-charge period
0xDA, 0x12, // COM pins hardware configuration
0xDB, 0x20, // VCOM deselect level
0x8D, 0x14, // Charge pump setting = enable
0xAF // Display ON
};
hf_i2c_err_t result = display*.Write(init_commands, sizeof(init_commands));
if (result != hf_i2c_err_t::I2C_SUCCESS) {
printf("โ Failed to initialize SSD1306: %s\n", HfI2CErrToString(result).data());
return false;
}
printf("โ
SSD1306 OLED display initialized\n");
return true;
}
void clear_display() {
// ๐งน Clear display buffer
hf_u8_t clear_data[129]; // Command byte + 128 data bytes
clear_data[0] = DATA_MODE;
for (int i = 1; i < 129; i++) {
clear_data[i] = 0x00;
}
// ๐ Send clear command for each page (8 pages total)
for (int page = 0; page < 8; page++) {
// Set page address
hf_u8_t page_cmd[] = {COMMAND_MODE, 0xB0 + page, 0x00, 0x10};
display*.Write(page_cmd, sizeof(page_cmd));
// Clear the page
display*.Write(clear_data, sizeof(clear_data));
}
printf("๐งน Display cleared\n");
}
void write_pixel(hf_u8_t x, hf_u8_t y, bool on) {
// ๐ Write single pixel (simplified implementation)
if (x >= 128 || y >= 64) return;
hf_u8_t page = y / 8;
hf_u8_t bit = y % 8;
// Set position
hf_u8_t pos_cmd[] = {COMMAND_MODE, 0xB0 + page, x & 0x0F, 0x10 + (x >> 4)};
display*.Write(pos_cmd, sizeof(pos_cmd));
// Write pixel data
hf_u8_t pixel_data[] = {DATA_MODE, on ? (1 << bit) : 0};
display*.Write(pixel_data, sizeof(pixel_data));
}
};
๐พ EEPROM Memory (24C256)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
class EEPROM24C256 {
private:
EspI2c eeprom*;
static constexpr hf_u8_t EEPROM_ADDRESS = 0x50;
static constexpr hf_u16_t PAGE_SIZE = 64; // 64-byte page size
static constexpr hf_u16_t TOTAL_SIZE = 32768; // 32KB total
public:
EEPROM24C256() : eeprom*(I2C_NUM_0, EEPROM_ADDRESS, 400000) {}
bool initialize() {
// ๐ Initialize EEPROM I2C
if (!eeprom*.EnsureInitialized()) {
printf("โ Failed to initialize EEPROM I2C\n");
return false;
}
// ๐ Test EEPROM presence by reading first byte
if (!eeprom*.IsDevicePresent()) {
printf("โ EEPROM not found at address 0x%02X\n", EEPROM_ADDRESS);
return false;
}
printf("โ
24C256 EEPROM initialized (32KB)\n");
return true;
}
bool write_byte(hf_u16_t address, hf_u8_t value) {
if (address >= TOTAL_SIZE) {
printf("โ Address 0x%04X out of range\n", address);
return false;
}
// ๐ Write single byte (address + data)
hf_u8_t write_data[] = {
static_cast<hf_u8_t>(address >> 8), // High address byte
static_cast<hf_u8_t>(address & 0xFF), // Low address byte
value
};
hf_i2c_err_t result = eeprom*.Write(write_data, sizeof(write_data));
if (result != hf_i2c_err_t::I2C_SUCCESS) {
printf("โ Failed to write EEPROM byte: %s\n", HfI2CErrToString(result).data());
return false;
}
// โฐ Wait for write cycle to complete (typical 5ms)
vTaskDelay(pdMS_TO_TICKS(10));
return true;
}
bool read_byte(hf_u16_t address, hf_u8_t& value) {
if (address >= TOTAL_SIZE) {
printf("โ Address 0x%04X out of range\n", address);
return false;
}
// ๐ Set address then read data
hf_u8_t addr_data[] = {
static_cast<hf_u8_t>(address >> 8), // High address byte
static_cast<hf_u8_t>(address & 0xFF) // Low address byte
};
hf_i2c_err_t result = eeprom*.WriteRead(addr_data, sizeof(addr_data), &value, 1);
if (result != hf_i2c_err_t::I2C_SUCCESS) {
printf("โ Failed to read EEPROM byte: %s\n", HfI2CErrToString(result).data());
return false;
}
return true;
}
bool write_page(hf_u16_t start_address, const hf_u8_t* data, hf_u16_t length) {
// โ
Validate parameters
if (start_address >= TOTAL_SIZE || length == 0 || length > PAGE_SIZE) {
printf("โ Invalid page write parameters\n");
return false;
}
if ((start_address + length) > TOTAL_SIZE) {
printf("โ Page write would exceed EEPROM size\n");
return false;
}
// ๐ Check page boundary alignment
hf_u16_t page_start = start_address & ~(PAGE_SIZE - 1);
if (start_address != page_start) {
printf("โ ๏ธ Warning: Write not page-aligned\n");
}
// ๐ Prepare write data (address + data)
hf_u8_t write_buffer[PAGE_SIZE + 2];
write_buffer[0] = static_cast<hf_u8_t>(start_address >> 8);
write_buffer[1] = static_cast<hf_u8_t>(start_address & 0xFF);
for (hf_u16_t i = 0; i < length; i++) {
write_buffer[i + 2] = data[i];
}
hf_i2c_err_t result = eeprom*.Write(write_buffer, length + 2);
if (result != hf_i2c_err_t::I2C_SUCCESS) {
printf("โ Failed to write EEPROM page: %s\n", HfI2CErrToString(result).data());
return false;
}
// โฐ Wait for write cycle to complete
vTaskDelay(pdMS_TO_TICKS(10));
printf("โ
Wrote %u bytes to EEPROM at address 0x%04X\n", length, start_address);
return true;
}
bool read_sequential(hf_u16_t start_address, hf_u8_t* data, hf_u16_t length) {
if (start_address >= TOTAL_SIZE || length == 0) {
return false;
}
if ((start_address + length) > TOTAL_SIZE) {
length = TOTAL_SIZE - start_address; // Clamp to available space
}
// ๐ Set starting address
hf_u8_t addr_data[] = {
static_cast<hf_u8_t>(start_address >> 8),
static_cast<hf_u8_t>(start_address & 0xFF)
};
hf_i2c_err_t result = eeprom*.WriteRead(addr_data, sizeof(addr_data), data, length);
if (result != hf_i2c_err_t::I2C_SUCCESS) {
printf("โ Failed to read EEPROM sequence: %s\n", HfI2CErrToString(result).data());
return false;
}
printf("โ
Read %u bytes from EEPROM starting at 0x%04X\n", length, start_address);
return true;
}
};
๐ I2C Bus Scanner
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
class I2CBusScanner {
private:
EspI2c scanner*;
static constexpr hf_u8_t SCAN_START = 0x08;
static constexpr hf_u8_t SCAN_END = 0x77;
public:
I2CBusScanner() : scanner*(I2C_NUM_0, SCAN_START, 100000) {} // Any address for scanning
bool initialize() {
return scanner*.EnsureInitialized();
}
void scan_bus() {
printf("๐ Scanning I2C bus...\n");
printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
hf_u16_t found_count = 0;
for (hf_u8_t row = 0; row < 8; row++) {
printf("%02X: ", row * 16);
for (hf_u8_t col = 0; col < 16; col++) {
hf_u8_t address = row * 16 + col;
if (address < SCAN_START || address > SCAN_END) {
printf(" ");
continue;
}
// ๐ Test device presence at this address
EspI2c test_device(I2C_NUM_0, address, 100000);
test_device.EnsureInitialized();
if (test_device.IsDevicePresent(50)) { // 50ms timeout
printf("%02X ", address);
found_count++;
} else {
printf("-- ");
}
}
printf("\n");
}
printf("\n๐ฏ Found %u I2C device(s)\n", found_count);
if (found_count > 0) {
print_common_devices();
}
}
private:
void print_common_devices() {
printf("\n๐ Common I2C devices:\n");
printf(" 0x20-0x27: PCF8574 I/O Expander\n");
printf(" 0x3C, 0x3D: SSD1306 OLED Display\n");
printf(" 0x48-0x4F: LM75A Temperature Sensor\n");
printf(" 0x50-0x57: 24C256 EEPROM\n");
printf(" 0x68: DS1307 RTC, MPU6050 IMU\n");
printf(" 0x76, 0x77: BMP280 Pressure Sensor\n");
}
};
๐ Performance and Diagnostics
๐ Statistics Monitoring
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void monitor_i2c_performance(BaseI2c& device) {
hf_i2c_statistics_t stats;
hf_i2c_err_t result = device.GetStatistics(stats);
if (result == hf_i2c_err_t::I2C_SUCCESS) {
printf("๐ I2C Performance Statistics:\n");
printf(" ๐ Total Transactions: %u\n", stats.total_transactions);
printf(" โ
Successful: %u (%.1f%%)\n",
stats.successful_transactions,
(float)stats.successful_transactions / stats.total_transactions * 100.0f);
printf(" โ Failed: %u\n", stats.failed_transactions);
printf(" โฐ Timeouts: %u\n", stats.timeout_count);
printf(" ๐ซ NACKs: %u\n", stats.nack_count);
printf(" ๐ค Bytes TX: %u\n", stats.bytes_transmitted);
printf(" ๐ฅ Bytes RX: %u\n", stats.bytes_received);
printf(" โฑ๏ธ Avg Time: %u ฮผs\n", stats.average_transaction_time_us);
}
}
void monitor_i2c_health(BaseI2c& device) {
hf_i2c_diagnostics_t diag;
hf_i2c_err_t result = device.GetDiagnostics(diag);
if (result == hf_i2c_err_t::I2C_SUCCESS) {
printf("๐ฉบ I2C Bus Health:\n");
printf(" ๐ข Bus Healthy: %s\n", diag.bus_healthy ? "Yes" : "No");
printf(" ๐ก SDA Line: %s\n", diag.sda_line_state ? "HIGH" : "LOW");
printf(" ๐ SCL Line: %s\n", diag.scl_line_state ? "HIGH" : "LOW");
printf(" ๐ Bus Locked: %s\n", diag.bus_locked ? "Yes" : "No");
printf(" ๐ Utilization: %.1f%%\n", diag.bus_utilization_percent);
printf(" ๐ Active Devices: %u\n", diag.active_device_count);
printf(" ๐ Recovery Attempts: %u\n", diag.error_recovery_attempts);
if (diag.consecutive_errors > 0) {
printf(" โ ๏ธ Consecutive Errors: %u\n", diag.consecutive_errors);
printf(" โ ๏ธ Last Error: %s\n", HfI2CErrToString(diag.last_error_code).data());
}
}
}
๐ก๏ธ Error Handling Best Practices
๐ฏ Robust Communication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
hf_i2c_err_t safe_i2c_read_with_retry(BaseI2c& device, hf_u8_t reg, hf_u8_t& value) {
const int max_retries = 3;
int retry_count = 0;
while (retry_count < max_retries) {
hf_i2c_err_t result = device.ReadRegister(reg, value, 100); // 100ms timeout
switch (result) {
case hf_i2c_err_t::I2C_SUCCESS:
return result; // Success!
case hf_i2c_err_t::I2C_ERR_BUS_BUSY:
case hf_i2c_err_t::I2C_ERR_TIMEOUT:
// ๐ Transient errors - retry after delay
retry_count++;
printf("โ ๏ธ I2C busy/timeout, retry %d/%d\n", retry_count, max_retries);
vTaskDelay(pdMS_TO_TICKS(10));
break;
case hf_i2c_err_t::I2C_ERR_DEVICE_NACK:
case hf_i2c_err_t::I2C_ERR_DEVICE_NOT_RESPONDING:
// ๐ Check device presence
if (!device.IsDevicePresent(50)) {
printf("โ Device not present\n");
return result;
}
retry_count++;
vTaskDelay(pdMS_TO_TICKS(5));
break;
default:
// ๐ฅ Permanent error
printf("โ I2C Error: %s\n", HfI2CErrToString(result).data());
return result;
}
}
printf("โ I2C operation failed after %d retries\n", max_retries);
return hf_i2c_err_t::I2C_ERR_TIMEOUT;
}
๐๏ธ Performance Considerations
โก Optimization Tips
- ๐ Clock Speed - Use highest speed supported by all devices (100kHz, 400kHz, 1MHz)
- ๐ Transaction Size - Larger transactions are more efficient than many small ones
- โฐ Timeouts - Use appropriate timeouts based on device characteristics
- ๐ Retries - Implement retry logic for transient errors
- ๐ Monitoring - Use statistics to identify performance bottlenecks
๐ Typical Performance Ranges
| Clock Speed | Throughput | Use Cases |
|โโโโโโ|โโโโโ-|โโโโโ|
| 100kHz (Standard) | ~10KB/s | Basic sensors, simple devices |
| 400kHz (Fast) | ~40KB/s | Most sensors, displays, memory |
| 1MHz (Fast+) | ~100KB/s | High-speed data acquisition |
๐งต Thread Safety
The BaseI2c class is not thread-safe.
For concurrent access from multiple tasks, use appropriate synchronization mechanisms.
๐ Related Documentation
- EspI2c API Reference - ESP32-C6 I2C implementation
- BaseGpio API Reference - GPIO interface for I2C pins
- HardwareTypes Reference - Platform-agnostic type definitions