/ firmware / src / filesystems / eeprom.cpp
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