eeprom.cpp
1 #include "eeprom.h" 2 #include <config.h> 3 #include <i2c.h> 4 5 #include <Arduino.h> 6 7 AT24C32 filesystems::eeprom::IC(config::eeprom::I2C_ADDR, Wire1); 8 9 using filesystems::eeprom::IC; 10 11 bool filesystems::eeprom::initialize() { 12 config::I2CSensorConfig sensor_config = {config::I2CSensorKind::EEPROM_AT24C32, 1, config::eeprom::I2C_ADDR, config::i2c::DIRECT_CHANNEL}; 13 bool found = false; 14 for (size_t index = 0; index < config::i2c_topology::DEVICE_COUNT; index++) { 15 const config::I2CSensorConfig &candidate = config::i2c_topology::DEVICES[index]; 16 if (candidate.kind == config::I2CSensorKind::EEPROM_AT24C32) { 17 sensor_config = candidate; 18 found = true; 19 break; 20 } 21 } 22 if (!found) return false; 23 24 hardware::i2c::DeviceAccessCommand command = { 25 .bus = sensor_config.bus == 0 ? hardware::i2c::Bus::Bus0 : hardware::i2c::Bus::Bus1, 26 .mux_channel = sensor_config.mux_channel, 27 .wire = nullptr, 28 .ok = false, 29 }; 30 if (!hardware::i2c::accessDevice(&command)) return false; 31 32 filesystems::eeprom::IC = AT24C32(sensor_config.address, *command.wire); 33 IC.read(0); 34 hardware::i2c::clearSelection(); 35 return IC.getLastError() == 0; 36 } 37 38 // ───────────────────────────────────────────────────────────────────────────── 39 // Tests 40 // ───────────────────────────────────────────────────────────────────────────── 41 #ifdef PIO_UNIT_TESTING 42 43 #include <testing/utils.h> 44 45 46 #define TEST_BASE 3900 47 48 static void test_eeprom_init(void) { 49 GIVEN("Wire1 is available"); 50 test_ensure_wire1(); 51 hardware::i2c::initialize(); 52 53 WHEN("the EEPROM is detected"); 54 TEST_ASSERT_TRUE_MESSAGE(filesystems::eeprom::initialize(), 55 "device: EEPROM not detected on bus 1"); 56 TEST_PRINTF("EEPROM detected, size=%u bytes", IC.length()); 57 } 58 59 static void test_eeprom_write_read_byte(void) { 60 GIVEN("the EEPROM is initialized"); 61 filesystems::eeprom::initialize(); 62 63 WHEN("a byte is written and read back"); 64 IC.write(TEST_BASE, 0xAB); 65 TEST_ASSERT_EQUAL_HEX8_MESSAGE(0xAB, IC.read(TEST_BASE), 66 "device: byte mismatch"); 67 IC.write(TEST_BASE, 0x00); 68 } 69 70 static void test_eeprom_put_get_struct(void) { 71 GIVEN("the EEPROM is initialized"); 72 filesystems::eeprom::initialize(); 73 74 WHEN("a struct is written and read back via put/get"); 75 76 struct { uint16_t co2; float temp; } reading = { 415, 23.5f }; 77 IC.put(TEST_BASE, reading); 78 79 struct { uint16_t co2; float temp; } readback = {0, 0.0f}; 80 IC.get(TEST_BASE, readback); 81 82 TEST_ASSERT_EQUAL_UINT16_MESSAGE(415, readback.co2, 83 "device: co2 mismatch in struct"); 84 TEST_ASSERT_FLOAT_WITHIN_MESSAGE(0.01f, 23.5f, readback.temp, 85 "device: temp mismatch in struct"); 86 87 struct { uint16_t co2; float temp; } zeros = {0, 0.0f}; 88 IC.put(TEST_BASE, zeros); 89 } 90 91 static void test_eeprom_buffer_roundtrip(void) { 92 GIVEN("the EEPROM is initialized"); 93 filesystems::eeprom::initialize(); 94 95 WHEN("a buffer is written and read back"); 96 97 uint8_t write_buf[16]; 98 for (int i = 0; i < 16; i++) write_buf[i] = i + 0x10; 99 100 IC.writeBuffer(TEST_BASE + 20, write_buf, 16); 101 TEST_ASSERT_EQUAL_UINT8_MESSAGE(0, IC.getLastError(), 102 "device: error on writeBuffer"); 103 104 uint8_t read_buf[16] = {0}; 105 IC.readBuffer(TEST_BASE + 20, read_buf, 16); 106 107 TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(write_buf, read_buf, 16, 108 "device: buffer mismatch"); 109 110 uint8_t zeros[16] = {0}; 111 IC.writeBuffer(TEST_BASE + 20, zeros, 16); 112 } 113 114 static void test_eeprom_update_skips_same(void) { 115 GIVEN("the EEPROM is initialized"); 116 filesystems::eeprom::initialize(); 117 118 WHEN("update is called with the same value"); 119 120 IC.write(TEST_BASE + 40, 0x42); 121 IC.update(TEST_BASE + 40, 0x42); 122 TEST_ASSERT_EQUAL_UINT8_MESSAGE(0, IC.getLastError(), 123 "device: error on update with same value"); 124 TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x42, IC.read(TEST_BASE + 40), 125 "device: value changed after no-op update"); 126 127 IC.update(TEST_BASE + 40, 0x99); 128 TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x99, IC.read(TEST_BASE + 40), 129 "device: value not changed after real update"); 130 131 IC.write(TEST_BASE + 40, 0x00); 132 } 133 134 static void test_eeprom_last_byte(void) { 135 GIVEN("the EEPROM is initialized"); 136 filesystems::eeprom::initialize(); 137 138 WHEN("the last byte is written and read back"); 139 140 uint16_t last = config::eeprom::TOTAL_SIZE - 1; 141 IC.write(last, 0x77); 142 TEST_ASSERT_EQUAL_HEX8_MESSAGE(0x77, IC.read(last), 143 "device: last byte mismatch"); 144 TEST_ASSERT_EQUAL_UINT8_MESSAGE(0, IC.getLastError(), 145 "device: error on last byte access"); 146 IC.write(last, 0x00); 147 } 148 149 static void test_eeprom_page_boundary_buffer(void) { 150 GIVEN("the EEPROM is initialized"); 151 filesystems::eeprom::initialize(); 152 153 WHEN("a buffer crossing a page boundary is written and read back"); 154 155 uint16_t addr = config::eeprom::PAGE_SIZE - 4; 156 uint8_t write_buf[8] = {0xA0, 0xA1, 0xA2, 0xA3, 0xB0, 0xB1, 0xB2, 0xB3}; 157 IC.writeBuffer(addr, write_buf, 8); 158 TEST_ASSERT_EQUAL_UINT8_MESSAGE(0, IC.getLastError(), 159 "device: error on cross-page write"); 160 161 uint8_t read_buf[8] = {0}; 162 IC.readBuffer(addr, read_buf, 8); 163 TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(write_buf, read_buf, 8, 164 "device: cross-page buffer mismatch"); 165 166 uint8_t zeros[8] = {0}; 167 IC.writeBuffer(addr, zeros, 8); 168 } 169 170 void filesystems::eeprom::test() { 171 MODULE("EEPROM"); 172 RUN_TEST(test_eeprom_init); 173 RUN_TEST(test_eeprom_write_read_byte); 174 RUN_TEST(test_eeprom_put_get_struct); 175 RUN_TEST(test_eeprom_buffer_roundtrip); 176 RUN_TEST(test_eeprom_update_skips_same); 177 RUN_TEST(test_eeprom_last_byte); 178 RUN_TEST(test_eeprom_page_boundary_buffer); 179 } 180 181 #endif