Introduction

The main code for this project can be found on my Git site. The overall structure for it is as follows: More detail about specific interesting challenges follows.

I2C reassignment

The TMAG5273's are powered from the RP2040's GPIO pins rather than from the main 3.3V power supply, to allow them to be turned on in sequence. Each "bank" of sensors is activated one at a time, the sensors on that power bus are reassigned. The code then scans the I2C bus as a diagnostic test to make sure the addresses were correctly reassgined, before activating the next power bus. A helper function called which_sensor() figures out which I2C interface and default I2C device each sensor uses, so the code can just address them by X,Y coordinates. The core code to do this looks like this:
  for (y=0; y < N_SENSOR_ROWS; y++) {
    pinMode(row_power_pin[y],OUTPUT);       
    digitalWrite(row_power_pin[y],HIGH);    // Activate power pin
    delay(1);
    for (x=0; x<N_SENSOR_COLUMNS; x++) { // For each sensor on this pin
      // Figure out its default I2C address and interface
      which_sensor(x, y, &i2c_dev, &default_i2c_addr, &new_i2c_addr);    
      if (i2c_dev == 0) {                   // Which I2C interface to use
        tmag_object = &tmag5273_i2c0;
      } else {
        tmag_object = &tmag5273_i2c1;
      }
      tmag_object->switchSensor(default_i2c_addr);
      tmag_object->modifyI2CAddress(new_i2c_addr); // change I2C address
    }
    delay(1);
    Serial.println("Scanning I2C0");
    scanner(&Wire);                   // Scan I2C buses and print to serial
    Serial.println("Scanning I2C1");
    scanner(&Wire1);
  }          
    

Graphics Routines

A separate code file called fieldcam_graphics.cpp has utility drawing functions for drawing arrows and buttons onscreen. These routines were developed during Interface and Application Programming week, and are documented there.

Data Smoothing

Sensor data is inherently noisy. The TMAG sensors are set for their maximum integration time, lowest-noise setting, but even so they hop around quite a bit when trying to measure the Earth's magnetic field. To solve this, I created an autoregressive smoothing function that averages the last few measurements together:
Bx_smooth = a * Bx_smooth + (1-a) * Bx_new
a is the autoregression constant. When it is zero, no smoothing is applied. When it is near 1, the new data has only a small impact on the smoothed data. While any value of a between 0 and 1 is valid, my code allows three options: a = 0, a = 1/2, a = 1/4. This avoids slow floating-point multiplication. I really like the a = 1/4 setting: the arrows respond smoothly to external fields, with relatively little noise, and the smoothing introduces just a little bit of lag that creates a nice smooth "weighty" feel.