MUX
In the previous milestone, we discussed how our mux was working improperly and not outputting the correct values. Turns out, we forgot to initialize the pinmode of the digital pins used for the select signal and that was causing incorrect readings. With this simple fix, our muxes work properly.
Another issue we came across while working on this milestone was an inadequate number of digital pins. In our previous design, we had decided to use two muxes, one for the wall sensors and one for the treasure sensors. This meant we needed digital pins for each select signal. Our radio already takes up digital pins 9-12 and that cannot be changed as those pins are required for SPI communication. We also needed pins for the LEDs to show which frequency of treasure we have found. Each servo takes up a digital PWM pin as well. Due to our limited amount of digital pins (only 5 left after the radio and servos are plugged in), we decided to simply use one mux for both the treasure and wall sensors. We needed 3 digital select bits for the 8 to 1 mux and the remaining 2 digital pins are used for the LEDs. The select signal and which signal they correspond with is as follows:
Select Bits | Input Number | Sensor Output |
---|---|---|
000 | Y0 | Left Wall Sensor |
001 | Y1 | Front Wall Sensor |
010 | Y2 | Right Wall Sensor |
011 | Y3 | Empty |
100 | Y4 | Left Treasure Sensor |
101 | Y5 | Front Treasure Sensor |
110 | Y6 | Right Treasure Sensor |
111 | Y7 | Empty |
Treasure Sensors
We positioned 3 treasure sensors on the left, front and right sides of our robot. Our treasure sensor code from lab 2 only needed minor changes when incorporating it into our main algorithm. We created two different functions for our main code. The first is called detect_treasure() and returns a character. This character signifies if there is no treasure (returns 0), the treasure is 7kHz (returns 1), the treasure is 12 kHz (returns 2) or the treasure is 17 kHz (returns 3). This function also iterates through each of the select bits as follows in order to check all 3 sensors.
digitalWrite(s2, HIGH);
for (int i = 0; i < 3; i++) {
digitalWrite(s0, LOW);
digitalWrite(s1, LOW);
if (i & 1) {
digitalWrite(s0, HIGH);
}
if (i & 2) {
digitalWrite(s1, HIGH);
}
The biggest change we needed to make to our treasure sensor code was the number of bins we were using. We originally defined FFT_N as 256, but quickly realized this takes up a lot of memory on the arduino and would potentially cause issues. In order to fix this, we reduced the amount of bins from 256 to 128. Because the bins were now of different widths and contained different frequencies, we analyzed the bin outputs when a treasure of each frequency was present. We knew the bin numbers that we should be checking would be around half the bin number of the original bins we checked. Below is an example of a graph of the sensor output vs. the bin number when a 12 kHz treasure is present.
int seven = fft_log_out[24] + fft_log_out[25];
int twelve = fft_log_out[40] + fft_log_out[41] + fft_log_out[42];
int seventeen = fft_log_out[57] + fft_log_out[58] + fft_log_out[59];
if (seven > 120){
treasure = 1;
}
else if (twelve > 180){
treasure = 2;
}
else if (seventeen > 180){
treasure = 3;
}
if (treasure != 0) {
ADCSRA = 0xc0; //turn off free running mode
return treasure;
}
}
ADCSRA = 0xc0; // Turn off free running mode
return treasure;
You will also notice that at the end of our code, we needed to reset the ADCSRA so that we were not in free running mode and could use analogRead for the rest of our main code. The next function we wrote was checkTreasure(). This function turns on the appropriate LED and updates the current maze square to reflect that there is a specific treasure present in that square of the maze. Each position in the maze is represented as a single byte and bits 6 and 7 represent treasures. If those bits are 00, there is no treasure, if 01, there is a 7kHz treasure, if 10, there is a 12 kHz treasure and if 11, there is a 17 kHz treasure. Below is the function checkTreasure().
#define SEVEN 64
#define TWELVE 128
#define SEVENTEEN 192
void checkTreasure(){
char treasure = detect_treasure();
if (treasure ==1){
digitalWrite(GREEN, HIGH);
digitalWrite(BLUE, HIGH);
maze[currentRow][currentColumn] |= SEVEN;
}
if(treasure==2){
digitalWrite(GREEN, HIGH);
maze[currentRow][currentColumn] |= TWELVE;
}
if (treasure ==3){
digitalWrite(BLUE, HIGH);
maze[currentRow][currentColumn] |= SEVENTEEN;
}
}
Since we only had enough digital pins for 2 LEDs, we decided to, instead of turning on a red LED for 7kHz, turn on both the blue and green LEDs. This function is called at every junction we reach.
Radio
Our Radio code was very similar to the radio code from lab 4, we just needed to change the data we were sending. We chose to send two bytes of information after every time we reach a junction and check for walls and treasures. The first byte characterizes the current square the robot is in and is as follows:
bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
---|---|---|---|---|---|---|---|
Treasure bit 1 | Treasure bit 0 | visited | unreachable | West wall present | South wall present | East wall present | North wall present |
bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
---|---|---|---|---|---|---|---|
unused | unused | unused | Row bit 2 | Row bit 1 | Row bit 0 | Column bit 1 | Column bit 0 |
bool transmit_node(char node, int row, int column){
char data_buffer[2];
row = row<<3;
char coords = row | column;
data_buffer[0] = node;
data_buffer[1] = coords;
// First, stop listening so we can talk.
radio.stopListening();
// Take the time, and send it. This will block until complete
bool ok = radio.write( data_buffer, 2 );
if (!ok){
return false;
}
// Now, continue listening
radio.startListening();
// Wait here until we get a response, or timeout (250ms)
unsigned long started_waiting_at = millis();
bool timeout = false;
while ( ! radio.available() && ! timeout )
if (millis() - started_waiting_at > 200 )
timeout = true;
// Describe the results
if ( timeout )
{
return false;
}
else
{
// Grab the response, compare, and send to debugging spew
char got_data[2];
radio.read( &got_data, 2 );
if(got_data[0]!=data_buffer[0] | got_data[1]!=data_buffer[1]){
return false;
}
}
}
In the main code, if the function returns false when called, we send the data again.
Algorithm
The main loop of our alrgorithm calls functions to check for walls, check for treasures and then move to a new node in the maze. At each junction, we send information about that square to the base station. Once the entire maze has been traversed and mapped, the blue and green LEDs alternate flashing on and off. The main loop of our algorithm is as follows:
maze[4][3] |= VISITED;
check();
checkTreasure();
transmit_node(maze[currentRow][currentColumn], currentRow, currentColumn);
possibleMove();
while(backTrackPointer != 0){
check();
checkTreasure();
transmit_node(maze[currentRow][currentColumn], currentRow, currentColumn);
possibleMove();
digitalWrite(GREEN, LOW);
digitalWrite(BLUE, LOW);
}
while(1){
digitalWrite(GREEN, HIGH);
delay(1000);
digitalWrite(GREEN, LOW);
digitalWrite(BLUE, HIGH);
delay(1000);
digitalWrite(BLUE, LOW);
}
Here's a picture of our current robot and a video of our robot's current maze traversal capabilities.