๐ŸšŒ 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.