MCP23S17-E/SP - Arduinio Uno R4 Wifi - Test

nope. Spagetti :slight_smile:

I was working out how to make the wire harness and connectors. The origial idea was to use CD74HC4067. but then MCP23S17 was suggesed because they can be much faster. I setup a test with three MCP23S17 and each with HES off pin GPB0


#include <SPI.h>
#include <Adafruit_MCP23X17.h>

Adafruit_MCP23X17 mcp[3];
uint8_t hwAddresses[] = {0, 2, 4};  // MCP0, MCP2, MCP1 based on A0–A2 wiring
const char* labels[] = {"MCP0", "MCP2", "MCP1"};  // Logical mapping
const uint8_t CS_PIN = 10;
const uint8_t SENSOR_PIN = 8;  // GPB0

bool lastState[3] = {true, true, true};  // Assume all start HIGH (no magnet)

void setup() {
  Serial.begin(115200);
  SPI.begin();

  for (int i = 0; i < 3; i++) {
    if (!mcp[i].begin_SPI(CS_PIN, &SPI, hwAddresses[i])) {
      Serial.print("Failed to init ");
      Serial.println(labels[i]);
      while (1);
    }
    mcp[i].pinMode(SENSOR_PIN, INPUT);
    mcp[i].digitalWrite(SENSOR_PIN, HIGH);  // Enable internal pull-up
  }

  Serial.println("Monitoring Hall sensors on GPB0 of MCP0, MCP2, MCP1...");
}

void loop() {
  for (int i = 0; i < 3; i++) {
    bool current = mcp[i].digitalRead(SENSOR_PIN);
    if (current != lastState[i]) {
      Serial.print(labels[i]);
      Serial.print(": ");
      Serial.println(current ? "NO MAGNET" : "MAGNET DETECTED");
      lastState[i] = current;
    }
  }
  delay(50);  // Fast enough for HED transitions, low serial spam
}

Serial output is .. less then expected. Reads some times off one then the other. My guess is with niodinium magnet I am creating too much interference in such a small bread board area, and…. I am not correct in wire / logic. This allso has a 10k pullup for each HES. Which one of reasons for the use of the MCP23S17 was the “automatic pullup”…. but kind of giving up hope for that.


08:05:42.376 -> MCP0: NO MAGNET
08:05:43.727 -> MCP2: MAGNET DETECTED
08:05:43.793 -> MCP0: MAGNET DETECTED
08:05:44.189 -> MCP0: NO MAGNET
08:05:44.189 -> MCP2: NO MAGNET
08:05:44.912 -> MCP2: MAGNET DETECTED
08:05:44.945 -> MCP0: MAGNET DETECTED
08:05:45.043 -> MCP0: NO MAGNET
08:05:45.043 -> MCP2: NO MAGNET
08:05:51.466 -> MCP2: MAGNET DETECTED
08:05:51.763 -> MCP2: NO MAGNET

Do you think this is actually a feasible project. You will have over 576 wires just for the sensor outputs alone.

This is wrong.
You need to set the mcp pinMode to INPUT_PULLUP

uint8_t hwAddresses[] = {0, 2, 4};  // MCP0, MCP2, MCP1 based on A0–A2 wiring

This makes no sense for SPI. You select the chip by using the CS pin

The comment is for the I2C version not SPI

I suggest you try the MCP23xxx_buton.ino example with one IC and one HES.
See if you can make that work.

Context may help a bit on that project. I am tackling one of the harder game boards in number of sensor counts but this is one of ten boards, each board has sub games.

This is my first program / website etc. As much a learning experience as making something (I at least) think is cool. Think of it like pinball like games (t01-t10) but in modular table “leaves”

All the game website and front end works, just down to making the boads. Starting off with ugly draft ones then will make nice ones of hardwood. I have draft test INO for each component (LEDs, OLED, HES, Motors, Light Sensors etc…) , but now need to build boards and then game code.

See if you can make one work.
Try the example code MCP23xxx_buton.ino

more testing: for sake of simplicity i will call them “MCP###” where the ### is the A0-A1 ID setting of which pins go to ground.

Pulling back to simple. MCP000 and MCP001. both with HES off GPB0. SHOULD scan both via ID in a cycle and then report if it got scan MCP000 HES or MCP001 HES. But it works solid on MCP000 reading. but when I touch magnet to MCP001 it reads out six or so magnet detects in log and NOT listing them as MCP001.

#include <SPI.h>
#include <Adafruit_MCP23X17.h>

Adafruit_MCP23X17 mcp[2];
uint8_t hwAddresses[] = {0, 4};              // MCP000, MCP001
const char* labels[] = {"MCP000", "MCP001"};
bool active[2] = {false, false};

const uint8_t CS_PIN = 10;
const uint8_t SENSOR_PIN = 8;    // GPB0 = pin 1

bool lastState[2] = {true, true};  // Assume HIGH (no magnet)

void setup() {
  Serial.begin(115200);
  SPI.begin();

  for (int i = 0; i < 2; i++) {
    if (mcp[i].begin_SPI(CS_PIN, &SPI, hwAddresses[i])) {
      active[i] = true;
      mcp[i].pinMode(SENSOR_PIN, INPUT);
      mcp[i].digitalWrite(SENSOR_PIN, HIGH);  // Optional pull-up
      Serial.print("Initialized ");
      Serial.println(labels[i]);
    } else {
      Serial.print("Skipped ");
      Serial.println(labels[i]);
    }
  }

  Serial.println("Monitoring Hall sensors on MCP000 and MCP001...");
}

void loop() {
  for (int i = 0; i < 2; i++) {
    if (!active[i]) continue;

    bool current = mcp[i].digitalRead(SENSOR_PIN);
    if (current != lastState[i]) {
      Serial.print(millis());
      Serial.print(" ms - ");
      Serial.print(labels[i]);
      Serial.print(": ");
      Serial.println(current ? "NO MAGNET" : "MAGNET DETECTED");
      lastState[i] = current;
    }
  }
  delay(10);  // Fast enough for responsiveness, low enough for clarity
}

Logs:


0:06:30.098 -> 16321 ms - MCP000: MAGNET DETECTED
10:06:30.623 -> 16834 ms - MCP000: NO MAGNET
10:06:31.282 -> 17506 ms - MCP000: MAGNET DETECTED
10:06:31.645 -> 17849 ms - MCP000: NO MAGNET
10:06:32.436 -> 18651 ms - MCP000: MAGNET DETECTED
10:06:32.535 -> 18744 ms - MCP000: NO MAGNET

And if I move to dedicated CS to be a pin for each vs on for each chain of 8 MCP, I will need 32 pins for just CS and so this will be a fail on design.

You mentioned in earlier thread Hall effect sensors that “push” vs have to be “pulled” googled that but not seeing logic.

Also open to other suggestions. I considered other chips such as FPGA but that turned into dozens of optins and no clear path to purchase one and get pin connections to each HES.

Try this circuit and code

// Reads a button attached to a MCP23XXX pin.

#include <Adafruit_MCP23X17.h>

#define BUTTON_PIN 1  // MCP23XXX pin button is attached to

// only used for SPI
#define CS_PIN 6

Adafruit_MCP23X17 mcp;

void setup() {
  Serial.begin(9600);
  //while (!Serial);
  Serial.println("MCP23xxx Button Test!");

  if (!mcp.begin_SPI(CS_PIN)) {
    Serial.println("Error.");
    while (1);
  }

  // configure pin for input with pull up
  mcp.pinMode(BUTTON_PIN, INPUT_PULLUP);

  Serial.println("Looping...");
}

void loop() {
  // LOW = pressed, HIGH = not pressed
  if (!mcp.digitalRead(BUTTON_PIN)) {
    Serial.println("Button Pressed!");
    delay(250);
  }
}

I did match the diagram everything pin for pin but for one item the VDD to +5v notes 0.1u to ground. I don’t have what I think is a 1mili ohm resistor. I double checked twice the pin / cable.

Here is output that loops:


5:26:50.436 -> Button Pressed!
15:26:50.701 -> Button Pressed!
15:26:50.964 -> Button Pressed!
15:26:51.229 -> Button Pressed!
15:26:51.492 -> Button Pressed!
15:26:51.756 -> Button Pressed!
15:26:52.022 -> Button Pressed!
15:26:52.285 -> Button Pressed!
15:26:52.544 -> Button Pressed!
15:26:52.807 -> Button Pressed!
15:26:53.067 -> Button Pressed!
15:26:53.362 -> Button Pressed!
15:26:53.627 -> Button Pressed!
15:26:53.890 -> Button Pressed!
15:26:54.154 -> Button Pressed!

weither I use a magnet or not.. log output does not change. Sooo.. either User error on cable, or that 0.1u resistor really is important.

It's not a resistor it is a 0.1uF Capacitor and it is required.

To help debug, remove the hall sensor and just use a wire that you can connect and disconnect to ground

Whoops!

#define BUTTON_PIN 1  // MCP23XXX pin button is attached to

Should be pin 0

#define BUTTON_PIN 0  // MCP23XXX pin button is attached to

You can test without the capacitor but in the final build you must have it.

In all your diagrams you show the Arduino UNO R4 being powered by 5V being fed into the power jack. This is not sufficient to power the internal regulators correctly. You need some overhead, that is you need at least 7V to make things work. As this is one of Jim's strong things, I am surprised he hasn't picked this up so far.

The other thing is exactly what Hall effect sensor are you using? You have been quite vague on this. Is it in fact the A1101 - to A1106 from Allegro? If so have you read the data sheet?

I am concerned about this piece of code you use in the loop function.

This creates a new array each time round the loop. Better to define this as a global variable.

Also have you thought how your Hall effect sensor and its wiring are going to be seen underneath the display? Are you expecting the sensor'r range to be so great as to be detected underneath the LED matrix? Have you tried a test to see if it has?

Finally I think you will have to put a baffle matrix between the LEDs to stop the colours from the matrix merging into each other at the edges.

I believe your goal in this test is to check if sensor is solid and consistent. If you have other plans to build on, I am glad to pivot to your base work from that. But below is test I setup to start “small and expand” in my testing.

PS: I dug around and found .1u ceramic capacitor (104) and put it on bread board between pins 9 and 10. So each MCP has noted capacitor setup.

My foundation code that has solid test. where each of the 16 outputs with the sensor are solid and consistent.

#include <SPI.h>

// MCP23S17 SPI settings
const uint8_t CS_PIN = 2; // Chip select pin for Group 1 (CS0)
const uint8_t MCP_ADDR = 0x20; // Address 0 (A0=A1=A2=GND)

// MCP23S17 registers (for bank=0 mode)
#define IODIRA  0x00 // I/O direction for Port A (1=input, 0=output)
#define IODIRB  0x01 // I/O direction for Port B
#define GPPUA   0x0C // Pull-up enable for Port A
#define GPPUB   0x0D // Pull-up enable for Port B
#define GPIOA   0x12 // Input state for Port A
#define GPIOB   0x13 // Input state for Port B

// Track previous states for change detection
uint8_t lastStateA = 0xFF; // Assume all HIGH (no magnet) initially
uint8_t lastStateB = 0xFF;

void setup() {
  Serial.begin(9600);
  SPI.begin();
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH); // Deselect chip

  // Initialize MCP23S17
  writeRegister(IODIRA, 0xFF); // All GPA0-GPA7 as inputs
  writeRegister(IODIRB, 0xFF); // All GPB0-GPB7 as inputs
  writeRegister(GPPUA, 0xFF); // Enable pull-ups on GPA0-GPA7
  writeRegister(GPPUB, 0xFF); // Enable pull-ups on GPB0-GPB7
}

void loop() {
  // Read both ports
  uint8_t stateA = readRegister(GPIOA); // GPA0-GPA7
  uint8_t stateB = readRegister(GPIOB); // GPB0-GPB7

  // Check for changes on Port A
  for (int i = 0; i < 8; i++) {
    bool currentBit = (stateA >> i) & 0x01; // Current state of GPA[i]
    bool lastBit = (lastStateA >> i) & 0x01; // Previous state of GPA[i]
    if (currentBit != lastBit) {
      Serial.print("MCP000-GPA");
      Serial.print(i);
      Serial.print("-");
      Serial.print(currentBit ? "1" : "0");
      Serial.println(currentBit ? " - No Magnet" : " - Magnet Detected");
    }
  }

  // Check for changes on Port B
  for (int i = 0; i < 8; i++) {
    bool currentBit = (stateB >> i) & 0x01; // Current state of GPB[i]
    bool lastBit = (lastStateB >> i) & 0x01; // Previous state of GPB[i]
    if (currentBit != lastBit) {
      Serial.print("MCP000-GPB");
      Serial.print(i);
      Serial.print("-");
      Serial.print(currentBit ? "1" : "0");
      Serial.println(currentBit ? " - No Magnet" : " - Magnet Detected");
    }
  }

  // Update previous states
  lastStateA = stateA;
  lastStateB = stateB;

  delay(100); // Poll every 100ms for debouncing
}

// Write to MCP23S17 register
void writeRegister(uint8_t reg, uint8_t value) {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(MCP_ADDR << 1); // Write command (address + write bit)
  SPI.transfer(reg); // Register address
  SPI.transfer(value); // Data
  digitalWrite(CS_PIN, HIGH);
}

// Read from MCP23S17 register
uint8_t readRegister(uint8_t reg) {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer((MCP_ADDR << 1) | 0x01); // Read command (address + read bit)
  SPI.transfer(reg); // Register address
  uint8_t value = SPI.transfer(0x00); // Read data
  digitalWrite(CS_PIN, HIGH);
  return value;
}

Test Output Serial. Very consistent.

MCP000-GPA7-0 - Magnet Detected
MCP000-GPA7-1 - No Magnet
MCP000-GPA7-0 - Magnet Detected
MCP000-GPA7-1 - No Magnet
MCP000-GPA7-0 - Magnet Detected
MCP000-GPA7-1 - No Magnet
<snip other 14 tests>
MCP000-GPB0-0 - Magnet Detected
MCP000-GPB0-1 - No Magnet
MCP000-GPB0-0 - Magnet Detected
MCP000-GPB0-1 - No Magnet

##########

Upon that is my next phase of the test where I add the second “bank” of MCPs

# Wiring Diagram for t02


Arduino UNO R4 WiFi
+-------------------+
| D2  ---- CS0 ---- Group 1 (8x MCP23S17-E, Addr 0-7) --+-- A3144 Sensors (128)
| D3  ---- CS1 ---- Group 2 (8x MCP23S17-E, Addr 0-7) --+-- A3144 Sensors (128)
| D4  ---- CS2 ---- Group 3 (8x MCP23S17-E, Addr 0-7) --+-- A3144 Sensors (128)
| D5  ---- CS3 ---- Group 4 (8x MCP23S17-E, Addr 0-7) --+-- A3144 Sensors (128)
| D6  ---- CS4 ---- Group 5 (8x MCP23S17-E, Addr 0-7) --+-- A3144 Sensors (64 used)
| D11 ---- MOSI ----+------------------------------------+
| D12 ---- MISO ----+------------------------------------+
| D13 ---- SCK  ----+------------------------------------+
| +5V  ---- VDD, /RESET (via 10kΩ) ----------------------+
| GND  ---- VSS -----------------------------------------+
+-------------------+

Group N (8x MCP23S17-E):
+-------------------+
| MCP23S17-E (Addr 0) | Pin 15 (A0)=GND, Pin 16 (A1)=GND, Pin 17 (A2)=GND
| MCP23S17-E (Addr 1) | Pin 15 (A0)=+5V, Pin 16 (A1)=GND, Pin 17 (A2)=GND
| ...
| MCP23S17-E (Addr 7) | Pin 15 (A0)=+5V, Pin 16 (A1)=+5V, Pin 17 (A2)=+5V
| Pin 9 (VDD) ---- +5V
| Pin 10 (VSS) --- GND
| Pin 18 (/RESET) -+-- 10kΩ -- +5V
| Pin 11 (/CS) ---- Arduino DN (CSN, e.g., D2 for Group 1)
| Pin 12 (SCK) ---- Arduino D13
| Pin 13 (SI) ----- Arduino D11 (MOSI)
| Pin 14 (SO) ----- Arduino D12 (MISO)
| GPA0-GPA7, GPB0-GPB7 ---- A3144 Sensors (open-collector, internal pull-ups)
+-------------------+

*Note: 0.1uF capacitor noted as required. Likely to keep voltage consistent as this is prone to noise for readings that are consistent. Need to RTFM a bit on this.

I wired up in diagram what would be bank CS1, MCP000. Pins on MCP 14-12 on same breadboard line as CS0, MCP000. MCP pin 18 over to same breadboard line to use common 10kohm resistor . In theory pin D2 is group CS0 with one MCP000, pin D3 is group CS1 with MCP000

#include <SPI.h>

// MCP23S17 SPI settings
const uint8_t CS_PINS[2] = {2, 3}; // CS pins: CP0 (D2), CP1 (D3)
const uint8_t MCP_ADDR = 0x20; // Address 0 (A0=A1=A2=GND) for both MCPs
const char* GROUP_NAMES[2] = {"CP0", "CP1"}; // Group identifiers

// MCP23S17 registers (for bank=0 mode)
#define IODIRA  0x00 // I/O direction for Port A (1=input, 0=output)
#define IODIRB  0x01 // I/O direction for Port B
#define GPPUA   0x0C // Pull-up enable for Port A
#define GPPUB   0x0D // Pull-up enable for Port B
#define GPIOA   0x12 // Input state for Port A
#define GPIOB   0x13 // Input state for Port B

// Track previous states for change detection (one per MCP)
uint8_t lastStateA[2] = {0xFF, 0xFF}; // Assume all HIGH (no magnet) initially
uint8_t lastStateB[2] = {0xFF, 0xFF};

void setup() {
  Serial.begin(9600);
  SPI.begin();
  
  // Initialize CS pins
  for (int i = 0; i < 2; i++) {
    pinMode(CS_PINS[i], OUTPUT);
    digitalWrite(CS_PINS[i], HIGH); // Deselect chip
  }

  // Initialize both MCP23S17 chips
  for (int i = 0; i < 2; i++) {
    writeRegister(i, IODIRA, 0xFF); // All GPA0-GPA7 as inputs
    writeRegister(i, IODIRB, 0xFF); // All GPB0-GPB7 as inputs
    writeRegister(i, GPPUA, 0xFF); // Enable pull-ups on GPA0-GPA7
    writeRegister(i, GPPUB, 0xFF); // Enable pull-ups on GPB0-GPB7
  }
}

void loop() {
  // Read both MCPs
  for (int chip = 0; chip < 2; chip++) {
    uint8_t stateA = readRegister(chip, GPIOA); // GPA0-GPA7
    uint8_t stateB = readRegister(chip, GPIOB); // GPB0-GPB7

    // Check for changes on Port A
    for (int i = 0; i < 8; i++) {
      bool currentBit = (stateA >> i) & 0x01; // Current state of GPA[i]
      bool lastBit = (lastStateA[chip] >> i) & 0x01; // Previous state
      if (currentBit != lastBit) {
        Serial.print(GROUP_NAMES[chip]);
        Serial.print("-MCP000-GPA");
        Serial.print(i);
        Serial.println(currentBit ? " - No Magnet" : " - Magnet Detected");
      }
    }

    // Check for changes on Port B
    for (int i = 0; i < 8; i++) {
      bool currentBit = (stateB >> i) & 0x01; // Current state of GPB[i]
      bool lastBit = (lastStateB[chip] >> i) & 0x01; // Previous state
      if (currentBit != lastBit) {
        Serial.print(GROUP_NAMES[chip]);
        Serial.print("-MCP000-GPB");
        Serial.print(i);
        Serial.println(currentBit ? " - No Magnet" : " - Magnet Detected");
      }
    }

    // Update previous states
    lastStateA[chip] = stateA;
    lastStateB[chip] = stateB;
  }

  delay(100); // Poll every 100ms for debouncing
}

// Write to MCP23S17 register for specified chip
void writeRegister(uint8_t chip, uint8_t reg, uint8_t value) {
  digitalWrite(CS_PINS[chip], LOW);
  SPI.transfer(MCP_ADDR << 1); // Write command (address + write bit)
  SPI.transfer(reg); // Register address
  SPI.transfer(value); // Data
  digitalWrite(CS_PINS[chip], HIGH);
}

// Read from MCP23S17 register for specified chip
uint8_t readRegister(uint8_t chip, uint8_t reg) {
  digitalWrite(CS_PINS[chip], LOW);
  SPI.transfer((MCP_ADDR << 1) | 0x01); // Read command (address + read bit)
  SPI.transfer(reg); // Register address
  uint8_t value = SPI.transfer(0x00); // Read data
  digitalWrite(CS_PINS[chip], HIGH);
  return value;
}
      Serial.print(i);
      Serial.println(currentBit ? " - No Magnet" : " - Magnet Detected");
    }
  }

  // Update previous states
  lastStateA = stateA;
  lastStateB = stateB;

  delay(100); // Poll every 100ms for debouncing
}

// Write to MCP23S17 register
void writeRegister(uint8_t reg, uint8_t value) {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(MCP_ADDR << 1); // Write command (address + write bit)
  SPI.transfer(reg); // Register address
  SPI.transfer(value); // Data
  digitalWrite(CS_PIN, HIGH);
}

// Read from MCP23S17 register
uint8_t readRegister(uint8_t reg) {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer((MCP_ADDR << 1) | 0x01); // Read command (address + read bit)
  SPI.transfer(reg); // Register address
  uint8_t value = SPI.transfer(0x00); // Read data
  digitalWrite(CS_PIN, HIGH);
  return value;
}

Everything is solid until I plug in CS1, MCP000 pin 11 (CSS) .. I don’t even have to plug it into anything. just wire in and wiggle it and I get outputs like

MCP000-GPB6 - Magnet Detected
MCP000-GPB7 - Magnet Detected
MCP000-GPA7 - Magnet Detected
MCP000-GPA0 - No Magnet
MCP000-GPA1 - No Magnet
MCP000-GPA2 - No Magnet
MCP000-GPA3 - No Magnet
MCP000-GPA4 - No Magnet
MCP000-GPA5 - No Magnet

if I pull wire out. I do still get consistent reads from CS0, MCP000.

So my question is:

  1. You mentioned push based hall effect sensor. Can you give me RTFM location for this?

  2. On paper the design of shared SO,SI,SCK with each channel having pin for each “group”, seems logical. scan would cycle through each MCP by address 0-7 from each CS “group” channel, and during that time slice. But peiece I am not sure on is if this is a “pull” how does it shut off/exclude the signals sent. I guess CS is pin to say “your my focus” (all others state low and go to sleep on outputs), then within that MCP group, cycle via ID through MCP0-7 and each sub 1-16 scans. So in theory.. this works… Hopeing this is just a wire / noise control adjustment I need to make vs scrap design.

Thanks as always for wisdom.

So you ignored my advice about what is happening with the bool array and added another line that did exactly the same thing. Each time round the for loop That is 8 loops long a new array is created with the same name as the previous one but it is a new loop.

I am not bothered if this appears "work" at the moment, eventually it will byte you on the bum.

Also what about the other things I asked you? Like exactly what Hall effect sensor you are using? Do a web search for a part number and pick one from a magor distributor, like mouser, not the ones offering you data sheets these are often rubbish.

Sorry. I did not even see your thread response to now.. Let me respond to your points and address. And as always to those who troll forums to help others.. thx.

“ UNO R4 being powered by 5V being fed into the power jack” → Understood but not a concern. I have a 50 amp dc power supply as the design will feed DC rail scross six tables of game boards at a given time, so ya.. All boards have thick wire of +12 GRN line from which I have for each board a pair of 12v to 5v buck convertors.

“Hall effect sensor are you using?” → A3144 with 10k ohm resitors (but hoping to use common per MCP pull up to avoid a LOT of soldering :slight_smile:

“.. creates a new array each time round the loop. Better to define this as a global variable…” - Got it. makes sense.

“..Are you expecting the sensor'r range to be so great as to be detected underneath the LED matrix”. → so my “test of one A3144 to the single MCP23S17 to the Arduinio UNO R4 Wifi was a lenght of about 12-14” as that is the longest wire I can expect. How this translates to the rats nest that will be under the table and or my building harness to avoid rats.. Working on that. My current idea is to use some old SCSI cables (ya.. have lots of those) and old controller and strip it off board and create those as contact briges to the MCP rail of chips. But that is after I figure out this way to nest 500+ sensors :slight_smile:

“..matrix between the LEDs to stop the colours from the matrix merging into each other at the edges” → Yup. This board plus others that lean heavily into LED color as part of game play will have to stick with primary colors that show easily: white,red,green,blue,yellow. And on top of the LEDs will be a “sheet” of paper with location of each LED “hole punch” and “wax paper” as difusion filter. I tested this and keeps them directed and difuses the light so not too bright. Plus LEDs are 90% reduced in brightness as default to keep powe draw down.

Current test: two MCP23S17-E. Both set to register 000 (A0-A2 aka pins 15-17 are to ground) , Both MCP are tied to same bread board line for pin 14 which goes to arduino D12, pins 13 from each to D11, pins 12 to D13.

#include <SPI.h>

// MCP23S17 SPI settings
const uint8_t CS_PINS[2] = {2, 3}; // CS pins: CP0 (D2), CP1 (D3)
const uint8_t MCP_ADDR = 0x20; // Address 0 (A0=A1=A2=GND)
const char* GROUP_NAMES[2] = {"CP0", "CP1"}; // Group identifiers
const uint8_t NUM_CHIPS = 2;

// MCP23S17 registers (for bank=0 mode)
#define IODIRA  0x00 // I/O direction for Port A (1=input, 0=output)
#define IODIRB  0x01 // I/O direction for Port B
#define GPPUA   0x0C // Pull-up enable for Port A
#define GPPUB   0x0D // Pull-up enable for Port B
#define GPIOA   0x12 // Input state for Port A
#define GPIOB   0x13 // Input state for Port B

// Track previous states for change detection
uint8_t lastStateA[2] = {0xFF, 0xFF}; // Assume all HIGH (no magnet) initially
uint8_t lastStateB[2] = {0xFF, 0xFF};

void setup() {
  Serial.begin(9600);
  SPI.begin();
  
  // Initialize CS pins
  for (int i = 0; i < NUM_CHIPS; i++) {
    pinMode(CS_PINS[i], OUTPUT);
    digitalWrite(CS_PINS[i], HIGH); // Deselect chip
  }

  // Initialize both MCP23S17 chips and verify
  for (int i = 0; i < NUM_CHIPS; i++) {
    writeRegister(i, IODIRA, 0xFF); // All GPA0-GPA7 as inputs
    writeRegister(i, IODIRB, 0xFF); // All GPB0-GPB7 as inputs
    writeRegister(i, GPPUA, 0xFF); // Enable pull-ups on GPA0-GPA7
    writeRegister(i, GPPUB, 0xFF); // Enable pull-ups on GPB0-GPB7

    // Verify initialization by reading back GPPUA
    uint8_t gppua = readRegister(i, GPPUA);
    if (gppua != 0xFF) {
      Serial.print(GROUP_NAMES[i]);
      Serial.println("-MCP000: Failed to initialize GPPUA");
    }
  }

  // Print initial pin states for debugging
  for (int chip = 0; chip < NUM_CHIPS; chip++) {
    uint8_t stateA = readRegister(chip, GPIOA);
    uint8_t stateB = readRegister(chip, GPIOB);
    Serial.print(GROUP_NAMES[chip]);
    Serial.print("-MCP000: Initial GPA state: 0x");
    Serial.print(stateA, HEX);
    Serial.print(", GPB state: 0x");
    Serial.println(stateB, HEX);
  }
}

void loop() {
  for (int chip = 0; chip < NUM_CHIPS; chip++) {
    // Read both ports
    uint8_t stateA = readRegister(chip, GPIOA); // GPA0-GPA7
    uint8_t stateB = readRegister(chip, GPIOB); // GPB0-GPB7

    // Detect changed bits on Port A
    uint8_t changedA = stateA ^ lastStateA[chip]; // XOR to find changed bits
    if (changedA) {
      for (int i = 0; i < 8; i++) {
        if (changedA & (1 << i)) { // Check if bit i changed
          bool currentBit = (stateA >> i) & 0x01;
          Serial.print(GROUP_NAMES[chip]);
          Serial.print("-MCP000-GPA");
          Serial.print(i);
          Serial.println(currentBit ? " - No Magnet" : " - Magnet Detected");
        }
      }
    }

    // Detect changed bits on Port B
    uint8_t changedB = stateB ^ lastStateB[chip]; // XOR to find changed bits
    if (changedB) {
      for (int i = 0; i < 8; i++) {
        if (changedB & (1 << i)) { // Check if bit i changed
          bool currentBit = (stateB >> i) & 0x01;
          Serial.print(GROUP_NAMES[chip]);
          Serial.print("-MCP000-GPB");
          Serial.print(i);
          Serial.println(currentBit ? " - No Magnet" : " - Magnet Detected");
        }
      }
    }

    // Update previous states
    lastStateA[chip] = stateA;
    lastStateB[chip] = stateB;
  }

  delay(100); // Poll every 100ms for debouncing
}

// Write to MCP23S17 register for specified chip
void writeRegister(uint8_t chip, uint8_t reg, uint8_t value) {
  digitalWrite(CS_PINS[chip], LOW);
  SPI.transfer(MCP_ADDR << 1); // Write command (address + write bit)
  SPI.transfer(reg); // Register address
  SPI.transfer(value); // Data
  digitalWrite(CS_PINS[chip], HIGH);
}

// Read from MCP23S17 register for specified chip
uint8_t readRegister(uint8_t chip, uint8_t reg) {
  digitalWrite(CS_PINS[chip], LOW);
  SPI.transfer((MCP_ADDR << 1) | 0x01); // Read command (address + read bit)
  SPI.transfer(reg); // Register address
  uint8_t value = SPI.transfer(0x00); // Read data
  digitalWrite(CS_PINS[chip], HIGH);
  return value;
}

I have also found that if I disconnect ALL contacts from CP1 MCP000 (no ground power, shared pins 14-12 or D3. So just standalone test of CP0 MCP000 .. if its sitting quite.. I can test and read consistent each sensor for all 16 positions. but if I ‘shake’ the bread board it spits out

CP0-MCP000-GPB4 - No Magnet
CP0-MCP000-GPB5 - No Magnet
CP0-MCP000-GPB6 - No Magnet
CP0-MCP000-GPB7 - No Magnet
CP1-MCP000-GPA0 - Magnet Detected
CP1-MCP000-GPA1 - Magnet Detected
CP1-MCP000-GPA2 - Magnet Detected
CP1-MCP000-GPA3 - Magnet Detected
CP1-MCP000-GPA4 - Magnet Detected
CP1-MCP000-GPA5 - Magnet Detected
CP1-MCP000-GPA6 - Magnet Detected
CP1-MCP000-GPA7 - Magnet Detected
CP1-MCP000-GPB0 - Magnet Detected

I am new to bread boards and this whole space. So what I A$$umed was board = solid contact for this development testing.. may be an assumption that for this use case I cannot lean into. but before I give up on bread boards and order PCB grid to solder even a test.. I kind of want to know if my idea is crazy :slight_smile:

You are having problems making a single hall sensor work, do you actually think you would have no problems soldering more than 576 wires using ptotoboards?

I personally would not attempt this project without making a PCB

Why did you do that? Sounds a bit crazy.

When I looked up that part I got a warning at the start of the data sheet that this part was discontinued on October 31, 2005.

While no doubt there are still some swanning about out there as people probably bought them at a knockdown price at the time, that is still 20 years since they were last made. So you might have trouble getting hold of the number you need.

If you use a common pull up resistor then all the sensors will read the same thing. The resistors on the data lines need to be independent.

Given this basic misunderstanding of basic electronics I think you need to scale the shop of your project down quite a lot.
Given the scope of your project I would not use LEDs at all but large OLED/LCD screen. However as the limiting factor would be how densely you can pack the sensors you might look at that first. You might have to go to surface mount hall sensors, but that will limit the size of your playing pieces.

I would agree with that.

@penguinpages but what is your budget for this project, given that you will probably have to go through several iterations before you get it right?

I would use this sensor

and this IC (28SSOP package)

Both are SMD devices. I'd make a 12 x 12 PCB and use four of then for the 24 x 24 array.

I like the price. I like the idea of push vs pull. But still trying to figure out how this could pivot to map to my use. I need each sensor to be close to the LED such that a maget set on it, or passed over triggers.

Here is my draft of t0203a - tic-tac-toe

Quadrant of HES in the grid are grouped and you can “play by selection” and AI can play against you.

So for each LED I drill small hole beside it and push the pins through to back side, and bend over the sensor up against the LED.

I wired and soldered up my first “four rows” and was going to use that against my first test run of the MCP board logic that I had drafted, when I ran into this scanning / inconsistent scan issue in my bread board test.

One of my goals was to also some how remove the need for each sensor to have an indivisual 10k ohm resistor. Which on paper I think is now possible.

Questions:

  1. MCP23S17 package you linked to to me seems much harder to solder against and use. Which is why I picked the ones I have. I had planned on mounting each MCP within each row so wires to its 16 sensors would be close… but had not worked that out and with resitors, it became hard to figure out how… was working on it :slight_smile:. I think I may follow recommendation that I need to purchase solder bread board and solder connections to something fixed and so reduce noise, but need to vet out the design changes.

  2. I love idea of push vs pull. But datasheet on those SL1634SH do not show size, AND, how do I get them close up agaisnt LED, and then legs under the board. I looked at site and did not see version of SL1634SH that had package with long legs.

As I said they are SMD for PCB