I got my RC522 I2C card reader working by connecting it directly to the I2C port (on my M5Stack Fire) but wanted it to work on my I2C hub. I've cobbled together a sketch (below) to do that and it is working fine, but only if I create an instance of MFRC522 before the setup() and then again after setting up the RFID. I understand why the second instance has to be created/updated after the hub has been set up, but was wondering whether there is a better way of doing this. Perhaps using a pointer.
#include <Wire.h>
#include "MFRC522_I2C.h"
#include "ClosedCube_TCA9548A.h"
#include <M5Stack.h>
#define PaHub_I2C_ADDRESS 0x70
ClosedCube::Wired::TCA9548A tca9548a;
// 0x28 is RFID i2c address on port2.
MFRC522 mfrc522(0x28); // Create MFRC522 instance.
void setup() {
//RFID setup
uint8_t returnCode = 0;
uint8_t channel = 2;
tca9548a.address(PaHub_I2C_ADDRESS);
returnCode = tca9548a.selectChannel(channel);
Wire.beginTransmission(channel);
// 0x28 is RFID i2c address on port2.
MFRC522 mfrc522(0x28); // Create MFRC522 instance.
returnCode = Wire.endTransmission();
M5.begin();
M5.Power.begin();
M5.Lcd.fillScreen( BLACK );
M5.Lcd.setCursor(0, 0);
M5.Lcd.setTextColor(YELLOW);
M5.Lcd.setTextSize(2);
M5.Lcd.fillScreen( BLACK );
M5.Lcd.setCursor(0, 0);
M5.Lcd.println("M5StackFire MFRC522");
Serial.begin(115200); // Initialize serial communications with the PC
Wire.begin(); // Initialize I2C
//Serial.println(F("Hello from Steve"));
mfrc522.PCD_Init(); // Init MFRC522
ShowReaderDetails(); // Show details of PCD - MFRC522 Card Reader details
Serial.println(F("Scan PICC to see UID, type, and data blocks..."));
M5.Lcd.println("Scan PICC to see UID, type, and data blocks...");
}
void loop() {
// Look for new cards, and select one if present
if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) {
delay(50);
return;
}
// Now a card is selected. The UID and SAK is in mfrc522.uid.
// Dump UID
Serial.print(F("Card UID:"));
M5.Lcd.println(" ");
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
Serial.print(mfrc522.uid.uidByte[i], HEX);
M5.Lcd.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
M5.Lcd.print(mfrc522.uid.uidByte[i], HEX);
}
Serial.println();
}
void ShowReaderDetails() {
// Get the MFRC522 software version
byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
Serial.print(F("MFRC522 Software Version: 0x"));
Serial.print(v, HEX);
if (v == 0x91)
Serial.print(F(" = v1.0"));
else if (v == 0x92)
Serial.print(F(" = v2.0"));
else
Serial.print(F(" (unknown)"));
Serial.println("");
// When 0x00 or 0xFF is returned, communication probably failed
if ((v == 0x00) || (v == 0xFF)) {
Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?"));
}
}
If I remove the creation from setup() the sketch doesn't work, it hangs. I think it is probably necessary to give the RC522 I2C address AFTER the I2C hub has been set up, otherwise only the hub is directly connected to the I2C bus and not the RS522.
If I remove the global creation the sketch complains that "mfrc522 was not declared in this scope" originating in the function ShowReaderDetails().
BTW, I don't "want" two instances, I just cannot get the sketch to work without creating two instances. I don't think this can be good programming and that is why I am asking for a better solution.
The TCA9548 is a I2C multiplexer, not a "hub". Could you do me a favor and call it a "mux" or "multiplexer" or "i2c multiplexer" ?
The constructor of the MFRC522_I2C only initializes the variables, not the hardware. That is how it is supposed to be. In the examples is also a reset pin in the second parameter. You don't need the reset ?
Creating the 'tca9548a' and 'mfrc22' object before setup() is good.
You should do a Wire.begin() in setup() before the I2C bus is used.
The tca9548a.address() (initialize variable) and tca9548a.selectChannel() (set hardware channel) don't do that for you.
The MFRC522 library has a PCD_Init() function to start the hardware. I suggest you do that after the I2C mux is set.
Could you use the Serial Monitor and remove the M5Stack, I don't know if the M5Stack library has influence on the I2C bus. Try to put everything in the right order.
[ADDED] By the way, the Wire.beginTransmission() does not start something on the I2C bus and the Wire.endTransmission() may not be used to stop or end something on the I2C bus.
I have carefully chosen my word (at least this time ), so I think that I'm right and the reference is confusing.
The Wire.endTransmission() does not end something that is going on the hardware I2C bus. It is the full I2C session with START, reading ACK, sending data, and STOP.
beginTransmission and endTransmission are almost self-explanatory to the coder. The library reference documents the usage of the subroutines, not the protocol on the bus. Hardware abstraction is the most important part of a software development system.
I don't think so. The constructor in that library requires 2 parameters. The third parameter has a default, but the second does not. So I think because @steveinaustria 's code compiles with a single parameter for the constructor, he must be using a different library to that one or another one I found, or at least a different version. Maybe the constructor in that library/version does more than simply create the object and store the parameter values. Maybe it initialises the hardware also, which would explain what @steveinaustria is saying about setting up the i2c multiplexer before creating the mfrc522 object. If so, that's a poor way to make a library work, and switching to a different library/version would solve the problem.