I am trying to connect two Unos via I2C. The idea is to have the SLAVE connected to 5 MFRC522 and when they detect the correct tags, send a signal to the MASTER to begin a different puzzle of disconnecting wires in proper sequence with sound bits and music played at correct times using an SD Card player.
Both puzzles work perfectly, separately. (This is my first attempt at using I2C, FYI).
When I tried to have them talk to each other via I2C, the speaker is chirping periodically and the Slave's completed RFID puzzle does NOT trigger the Master's audio to begin. When powered on, the Master reads "Activated" in the serial monitor, but I had attempted for it to begin in Deactivate mode.
Since the puzzles work great separately, and the only wiring difference is A4-A4, A5-A5, and GND-GND, I'm assuming it must be the code. PLEASE HELP!!!
TEA-SET-SLAVE
/**
* "Object Placement" Puzzle
*
* This puzzle requires the player to place one or more items in the
* correct location, at which point an RFID tag embedded in the object
* is detected by a sensor.
* When all sensors read the tag of the correct object, the puzzle is solved.
*
* Demonstrates:
* - SPI interface shared between several devices
* - RFID library
*/
// DEFINES
// Provides debugging information over serial connection if defined
#define DEBUG
// LIBRARIES
// Standard SPI library comes bundled with the Arduino IDE
#include <SPI.h>
// Download and install from https://github.com/miguelbalboa/rfid
#include <MFRC522.h>
#include <Wire.h>
// CONSTANTS
// The number of RFID readers
const byte numReaders = 5;
// Each reader has a unique Slave Select pin
const byte ssPins[] = {2, 3, 4, 5, 6};
// The sequence of NFC tag IDs required to solve the puzzle
const String correctIDs[] = {"ddc729b0", "ad292eb0", "ed2e31b0", "1df331b0", "cdec2eb0"};
const byte resetPin = 7;
// GLOBALS
// Initialise an array of MFRC522 instances representing each reader
MFRC522 mfrc522[numReaders];
// The tag IDs currently detected by each reader
String currentIDs[numReaders];
int x = 0;
// Function to send a boolean value from sender to receiver via I2C.
void sendBooleanValue(bool value, int address) {
// Begin I2C communication as the sender.
Wire.begin(8);
// Write the boolean value to the I2C bus.
Wire.write(value);
// End I2C communication.
Wire.endTransmission();
}
/**
* Initialisation
*/
void setup() {
#ifdef DEBUG
// Initialise serial communications channel with the PC
Serial.begin(9600);
Serial.println(F("Serial communication started"));
#endif
// Initialize the I2C communication.
Wire.begin();
// Set the address of the receiver.
int receiverAddress = 8;
// We set each reader's select pin as HIGH (i.e. disabled), so
// that they don't cause interference on the SPI bus when
// first initialised
for (uint8_t i=0; i<numReaders; i++) {
pinMode(ssPins[i], OUTPUT);
digitalWrite(ssPins[i], HIGH);
}
// Initialise the SPI bus
SPI.begin();
for (uint8_t i=0; i<numReaders; i++) {
// Initialise the reader
// Note that SPI pins on the reader must always be connected to certain
// Arduino pins (on an Uno, MOSI=> pin11, MISO=> pin12, SCK=>pin13)
// The Slave Select (SS) pin and reset pin can be assigned to any pin
mfrc522[i].PCD_Init(ssPins[i], resetPin);
// Set the gain to max - not sure this makes any difference...
// mfrc522[i].PCD_SetAntennaGain(MFRC522::PCD_RxGain::RxGain_max);
#ifdef DEBUG
// Dump some debug information to the serial monitor
Serial.print(F("Reader #"));
Serial.print(i);
Serial.print(F(" initialised on pin "));
Serial.print(String(ssPins[i]));
Serial.print(F(". Antenna strength: "));
Serial.print(mfrc522[i].PCD_GetAntennaGain());
Serial.print(F(". Version : "));
mfrc522[i].PCD_DumpVersionToSerial();
#endif
// Slight delay before activating next reader
delay(100);
}
#ifdef DEBUG
Serial.println(F("--- END SETUP ---"));
#endif
}
/**
* Main loop
*/
void loop() {
Wire.begin(); // transmit to device
x++; // Increment x
if (x > 5) x = 0; // `reset x once it gets 6
delay(500);
// Assume that the puzzle has been solved
boolean puzzleSolved = true;
// Assume that the tags have not changed since last reading
boolean changedValue = false;
// Loop through each reader
for (uint8_t i=0; i<numReaders; i++) {
// Initialise the sensor
mfrc522[i].PCD_Init();
// String to hold the ID detected by each sensor
String readRFID = "";
// If the sensor detects a tag and is able to read it
if(mfrc522[i].PICC_IsNewCardPresent() && mfrc522[i].PICC_ReadCardSerial()) {
// Extract the ID from the tag
readRFID = dump_byte_array(mfrc522[i].uid.uidByte, mfrc522[i].uid.size);
}
// If the current reading is different from the last known reading
if(readRFID != currentIDs[i]){
// Set the flag to show that the puzzle state has changed
changedValue = true;
// Update the stored value for this sensor
currentIDs[i] = readRFID;
}
// If the reading fails to match the correct ID for this sensor
if(currentIDs[i] != correctIDs[i]) {
// The puzzle has not been solved
puzzleSolved = false;
}
// Halt PICC
mfrc522[i].PICC_HaltA();
// Stop encryption on PCD
mfrc522[i].PCD_StopCrypto1();
}
#ifdef DEBUG
// If the changedValue flag has been set, at least one sensor has changed
if(changedValue){
// Dump to serial the current state of all sensors
for (uint8_t i=0; i<numReaders; i++) {
Serial.print(F("Reader #"));
Serial.print(String(i));
Serial.print(F(" on Pin #"));
Serial.print(String((ssPins[i])));
Serial.print(F(" detected tag: "));
Serial.println(currentIDs[i]);
}
Serial.println(F("---"));
}
#endif
// If the puzzleSolved flag is set, all sensors detected the correct ID
if(puzzleSolved){
onSolve();
}
// Add a short delay before next polling sensors
//delay(100);
delay(100);
}
/**
* Called when correct puzzle solution has been entered
*/
void onSolve(){
#ifdef DEBUG
// Print debugging message
Serial.println(F("Puzzle Solved!"));
#endif
// Send a true value to the receiver.
sendBooleanValue(true, 8);
}
/**
* Helper function to return a string ID from byte array
*/
String dump_byte_array(byte *buffer, byte bufferSize) {
String read_rfid = "";
for (byte i=0; i<bufferSize; i++) {
read_rfid = read_rfid + String(buffer[i], HEX);
}
return read_rfid;
}
BOMB-MASTER
/**
* "Cut The Wires" defuse bomb puzzle
*
* Players must "cut" the correct wires in order(via releasing alligator clips) to stop
* the ticking clock. If the clock reaches zero (after 60 minutes), the bomb "explodes"
* with sound effects. If wires are "cut" out of order, the ticking will get faster.
*/
// Define the I2C address for the receiver.
#define DEBUG
#define SD_ChipSelectPin 10
#define SENDER_ADDRESS 8
#include <SD.h>
#include <TMRpcm.h>
#include <SPI.h>
#include <Wire.h>
TMRpcm tmrpcm;
// CONSTANTS
const byte lockPin = A1;
const byte numWires = 5;
const int wirePins[numWires] = {2, 3, 4, 5, 6};
// Define the number of steps in the sequence that the player must follow
// GLOBALS
int lastState[numWires];
// What is the order in which wires need to be cut
// 0 indicates the wire should not be cut
int wiresToCut[numWires] = {0, 1, 2, 3, 4}; // (blue, red, yellow, green)
byte wiresCutCounter = 1;
// Keep track of the current state of the device
enum State {Inactive, Active, Defused, Exploded};
State state = State::Inactive;
//This is the timestamp at which the bomb will detonate
//It is calculated by adding on the specified number of minutes in the game time
// to the value of millis() when the code is initialised.
unsigned long detonationTime;
//The game length (in minutes)
int gameDuration = 60;
// Function to receive a boolean value via I2C.
bool receiveBooleanValue() {
bool puzzleSolved = false;
// Request 1 byte of data from the sender
Wire.requestFrom(SENDER_ADDRESS, 1);
// Check if data is available to be read
if (Wire.available()) {
// Read the received byte
byte receivedByte = Wire.read();
// Convert the received byte to a boolean value
puzzleSolved = (receivedByte == 1);
}
return puzzleSolved;
}
void Activate() {
state = State::Active;
// Set the detonation time to the appropriate time in the future
detonationTime = millis() + (unsigned long)gameDuration*60*1000;
Serial.println("Bomb activated!");
}
void Deactivate() {
state = State::Inactive;
Serial.println("Bomb inactive");
}
void Detonate() {
state = State::Exploded;
}
void setup() {
pinMode(lockPin, OUTPUT);
digitalWrite(lockPin, HIGH);
tmrpcm.speakerPin = 9;
Serial.begin(9600);
// Begin I2C communication as a master.
Wire.begin ();
if (!SD.begin(SD_ChipSelectPin)) {
Serial.println("SD fail");
}
// Initialise wire pins
for(int i=0; i<numWires; i++) {
pinMode(wirePins[i], INPUT_PULLUP);
lastState[i] = digitalRead(wirePins[i]);
}
// Set the detonation time to the appropriate time in the future
detonationTime = millis() + (unsigned long)gameDuration*60*1000;
// Print the initial state of the wires
for(int i=0; i<numWires; i++) {
Serial.println(F("Wire "));
Serial.print(i);
Serial.println(digitalRead(wirePins[i])? " Unconnected" : " Connected");
}
// Call the receiveBooleanValue function to receive the puzzleSolved value
bool puzzleSolved = receiveBooleanValue();
}
void loop() {
// "poor man's" debouncing
delay(25);
Wire.begin ();
Serial.begin(9600);
// Do something with the received puzzleSolved value
if (bool puzzleSolved = false) {
Deactivate();
digitalWrite(lockPin, HIGH);
}
else {
Activate(); // Arm the bomb!
tmrpcm.play("slow.wav");
tmrpcm.volume(5);
// First, see if any of the wires have been recently cut
for(int i=0; i<numWires; i++) {
// If the previous reading was LOW, but now it's HIGH, that means this wire must have been cut
if(digitalRead(wirePins[i]) == HIGH && lastState[i] == LOW) {
Serial.println("Wire ");
Serial.print(i);
Serial.println(" cut");
lastState[i] = HIGH;
// Was this the correct wire to cut?
if(wiresCutCounter == wiresToCut[i]) {
// Go on to the next counter
wiresCutCounter++;
}
// Incorrect wire cut
else {
// Play faster ticking sound effect
tmrpcm.setVolume(5);
tmrpcm.play("fast.wav");
Serial.println(" WRONG WIRE");
break;
}
}
// If the previous reading was LOW, but now it's HIGH, that means this wire must just have been cut
else if(digitalRead(wirePins[i]) == LOW && lastState[i] == HIGH) {
Serial.println(" Wire ");
Serial.println(i);
Serial.println( " reconnected");
lastState[i] = LOW;
break;
}
}
// Now, test the current state of all wires against the solution state
//First, assume that the correct wires have all been cut
bool allWiresCut = true;
// Then, loop over the wires array
for(int i=0; i<numWires; i++) {
// Every wire that has a number > 0 in the wiresToCut array should be cut (at some point), after which they will read HIGH,
// So if any of them still read LOW, that means that there is at least one wire still to be cut
if (wiresToCut[i] !=0 && lastState[i] == LOW) {
allWiresCut = false;
break;
}
}
// What to do next depends on the current state of the device
if(state == State::Active) {
// Retrieve the current timestamp
unsigned long currentTime = millis();
if(currentTime > detonationTime) {
Detonate();
Serial.println("BOOM!");
tmrpcm.setVolume(5); // play explosion sound effect
tmrpcm.play("expl.wav");
}
else if(numWires <= wiresCutCounter) {
Deactivate();
Serial.println("Bomb defused!");
tmrpcm.setVolume(5); // Play Sousa music
tmrpcm.play("Sous.wav");
}
}
}
}