Read from EEPROM
By: Matt Shellhammer (Electronics & Control Engineer)
Approved by: Lucas Gutierrez (Project Manager)
Table of Contents
Introduction
When in play back mode the robots will be reading data that was previously stored in EEPROM while being controlled by the Arxterra App. What is being read from EEPROM is the direction that the robot was facing and the turn that the robot took at every decision point. This will allow the robot autonomously navigate throughout the maze since it will have the decision stored for every decision point throughout the robots path. For rooms such as hallways, corners, and a dead end the robot will always choose the same turn value so that does not require reading from EEPROM.
Methodology
This software is developed to read data about direction and turn value every time a decision is made (room types 1, 2, and 4). It stores reads the data from EEPROM starting again at the address 0x0000. Every time the robot reaches a decision point, it will read the turn and direction value for that decision point and then use that to make its decision. The software in subroutines.ino file is the same as the subroutines.ino file in the blog post “Write to EEPROM”. The software specific to read EEPROM is in orange text within the EEPROM_Write.ino file below.
The roomType subroutine determines what room you’re in using “hitWall”, “rightHit”, and “leftHit”. “hitWall” ands the room value with a byte value from hit_table that is determined by the direction of the robot. The result will either be true or false depending if the robot is facing a wall or not. Then “rightHit” and “leftHit” just turns the robot and then implements hitWall again within the subroutines and determines if there’s a wall on the right and the left of the robot. This is then used to determine when a decision is required and when direction and turn must be read from EEPROM.
Data is read as follows:
Address | Value |
0x0000 | Direction (decision 1) |
0x0001 | Turn (decision 1) |
0x0002 | Direction (decision 2) |
0x0003 | Turn (decision 2) |
0x0004 | Direction (decision 3) |
… | … |
Software
EEPROM_Write.ino (MAIN SETUP & LOOP)
//////////////////////////////////////////////////////////////// // Name : EEPROM Read maze data // // Author : Matt Shellhammer // // Date : 2 December, 2017 // // Version : 1.0 // //////////////////////////////////////////////////////////////// #define __PROG_TYPES_COMPAT__ // __PROG_TYPES_COMPAT__ #include <avr/pgmspace.h> #include <Robot3DoTBoard.h> #include <EEPROM.h> #include <Wire.h> #include <Servo.h> #include "maze.h" void setup() { Serial.begin(9600); delay(5000); } void loop() { static uint16_t EEPROM_Idx = 0x0000; // first EEPROM index is 0x0000 static uint8_t type = 0; // initially outside of the maze static bool decision; // create a variable called decision static myRobot_t robot_inst; // create an instance of myRobot_t called robot_inst // No decision to be made when in a Hallway or outside of the maze (go straight) if ((type == 0)||(type == 5)){decision = false;robot_inst.turn = 0x00;} // No decision to be made when in a left corner (turn left) if (type == 3){decision = false;robot_inst.turn = 0x10;} // No decision to be made when in a right corner (turn right) if (type == 6){decision = false;robot_inst.turn = 0x01;} // No decision to be made when at a dead end (turn around) if (type == 7){decision = false;robot_inst.turn = 0x11;} else{decision = true;} // Call read data to EEPROM when at a decision point if ((decision == true)&&(EEPROM_Idx < 0x400)){ // Call Arxterra custom command to request a turn value // Store dir facing and turn value uint8_t temp_dir = EEPROM_read(EEPROM_Idx);EEPROM_Idx++; // if (temp_dir != robot_inst.dir){//ERROR: Do something} robot_inst.turn = EEPROM_read(EEPROM_Idx);EEPROM_Idx++; } robot_inst = enterRoom(robot_inst); // Update robot_inst type = roomType(robot_inst); // Determine room type }
maze.h (Structure, array, and variable definitions)
struct coord_t{
uint8_t row = 0x13; // Robot is initially outside of the maze
uint8_t col = 0x00; // Robot is initially outside of the maze
};
struct myRobot_t{
uint8_t dir = 0x03; // Robot is initially facing north
uint8_t turn = 0x00; // First action is no turn
coord_t maze;
uint8_t room = 0x00; // Initial room is empty
uint8_t bees = 0x00; // No bees present
};
const uint8_t hit_table[] PROGMEM =
{0x08, // South (dir == 0b00)
0x02, // East (dir == 0b01)
0x04, // West (dir == 0b10)
0x01}; // North (dir == 0b11)
//Compass S E W N
//dir 00 01 10 11
const uint8_t turn_table[] PROGMEM =
{0b00, 0b01, 0b10, 0b11, // 00 no turn
0b10, 0b00, 0b11, 0b01, // 01 turn right
0b01, 0b11, 0b00, 0b10, // 10 turn left
0b11, 0b10, 0b01, 0b00 // 11 turn around
};
// row col dir
const int8_t map_table[] PROGMEM =
{1 , 0, // 00
0 , 1, // 01
0 , -1, // 10
-1 , 0 // 11
};
const int maze_length = 399;
const uint8_t theMaze[] PROGMEM =
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14
{0x05,0x09,0x09,0x09,0x09,0x09,0x01,0x03,0x05,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x29,0x09,0x09,0x09,0x02, // 00
0x0C,0x09,0x09,0x03,0x05,0x09,0x0A,0x06,0x06,0x05,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x03,0x05,0x03,0x06, // 01
0x05,0x09,0x0B,0x06,0x06,0x05,0x09,0x0A,0x06,0x0C,0x09,0x09,0x09,0x09,0x09,0x01,0x0B,0x0C,0x0A,0x06,0x06, // 02
0x06,0x0D,0x09,0x0A,0x06,0x06,0x05,0x03,0x0C,0x09,0x09,0x03,0x05,0x09,0x09,0x0A,0x05,0x09,0x09,0x08,0x02, // 03
0x06,0x05,0x09,0x09,0x0A,0x06,0x06,0x0C,0x09,0x09,0x09,0x0A,0x0C,0x09,0x09,0x03,0x06,0x05,0x09,0x09,0x0A, // 04
0x06,0x0C,0x03,0x05,0x09,0x02,0x06,0x05,0x09,0x09,0x09,0x09,0x09,0x09,0x03,0x06,0x06,0x0C,0x03,0x05,0x03, // 05
0x06,0x05,0x0A,0x0C,0x03,0x06,0x06,0x06,0x05,0x01,0x03,0x07,0x05,0x03,0x06,0x06,0x06,0x05,0x0A,0x06,0x06, // 06
0x06,0x0C,0x09,0x03,0x0E,0x0C,0x08,0x02,0x06,0x06,0x06,0x06,0x06,0x06,0x0C,0x02,0x06,0x0C,0x09,0x02,0x06, // 07
0x06,0x05,0x0B,0x0C,0x09,0x09,0x09,0x08,0x02,0x06,0x06,0x06,0x06,0x0C,0x09,0x0A,0x04,0x09,0x0B,0x06,0x06, // 08
0x0C,0x08,0x09,0x09,0x09,0x09,0x01,0x01,0x02,0x06,0x0C,0x08,0x08,0x09,0x01,0x09,0x08,0x09,0x03,0x06,0x06, // 09
0x05,0x01,0x09,0x09,0x0B,0x07,0x06,0x04,0x02,0x0C,0x09,0x09,0x09,0x03,0x04,0x09,0x03,0x07,0x06,0x06,0x06, // 0A
0x06,0x0C,0x09,0x09,0x09,0x02,0x06,0x04,0x02,0x0D,0x09,0x09,0x09,0x0A,0x0C,0x03,0x06,0x06,0x06,0x06,0x06, // 0B
0x06,0x05,0x09,0x09,0x09,0x0A,0x06,0x0C,0x0A,0x05,0x09,0x09,0x09,0x09,0x03,0x06,0x06,0x06,0x06,0x06,0x06, // 0C
0x06,0x0C,0x09,0x09,0x09,0x03,0x04,0x09,0x09,0x08,0x0B,0x05,0x03,0x05,0x0A,0x06,0x06,0x06,0x06,0x06,0x06, // 0D
0x04,0x09,0x09,0x09,0x09,0x08,0x02,0x05,0x01,0x09,0x03,0x06,0x06,0x06,0x05,0x0A,0x0E,0x06,0x06,0x06,0x06, // 0E
0x06,0x05,0x09,0x09,0x09,0x09,0x0A,0x0E,0x06,0x07,0x06,0x06,0x06,0x06,0x06,0x05,0x09,0x0A,0x06,0x06,0x06, // 0F
0x06,0x0C,0x09,0x09,0x09,0x09,0x09,0x09,0x0A,0x06,0x06,0x06,0x06,0x0E,0x0E,0x06,0x05,0x09,0x0A,0x06,0x06, // 10
0x04,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x0A,0x0C,0x0A,0x06,0x05,0x09,0x0A,0x06,0x0D,0x09,0x0A,0x06, // 11
0x04,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x08,0x08,0x09,0x09,0x08,0x09,0x09,0x09,0x0A, // 12
};
subroutines.ino
/* * Write data to EEPROM, NOTE: interrupts are disabled while writing * @param uiAddress 16 bit interger pointing to the address of the data to write * @param ucData 8 bit value signifying the data being written */ void EEPROM_write(uint16_t uiAddress, uint8_t ucData) { /*Store SREG value before we disable Interrupts*/ char SREG_save = SREG; noInterrupts(); /* Wait for completion of any Flash Write Note:Only necessary if Flash Memory Manipulation is taking place */ while(SPMCSR &(1<<SPMEN)); /* Wait for completion of previous write */ while(EECR & (1<<EEPE)); /* Set up address and Data Registers */ EEAR = uiAddress; EEDR = ucData; /* Write logical one to EEMPE */ EECR |= (1<<EEMPE); /* Start eeprom write by setting EEPE */ EECR |= (1<<EEPE); /*Restore the SREG value*/ SREG = SREG_save; } /* * Read data from EEPROM, NOTE: interrupts are disabled while writing * @param uiAddress 16 bit interger pointing to the address of the data to read * @return 8 bit value signifying the data that was read */ uint8_t EEPROM_read(uint16_t uiAddress) { /*Store SREG value before we disable Interrupts*/ char SREG_save = SREG; noInterrupts(); /* Wait for completion of any Flash Write Note:Only necessary if Flash Memory Manipulation is taking place */ while(SPMCSR &(1<<SPMEN)); /* Wait for completion of previous write */ while(EECR & (1<<EEPE)); /* Set up address register */ EEAR = uiAddress; /* Start eeprom read by writing EERE */ EECR |= (1<<EERE); /*Restore the SREG value*/ SREG = SREG_save; /* Return data from Data Register */ return EEDR; } myRobot_t enterRoom(myRobot_t robot){ robot = turnInMaze(robot); robot = stepInMaze(robot); robot = roomInMaze(robot); return robot; } // Returns updated direction based on current direction and turn value // values returned in robot structure myRobot_t turnInMaze(myRobot_t robot){ // index = 4*turn_val + dir_val uint8_t index = (robot.turn << 2) + robot.dir; robot.dir = pgm_read_byte_near(turn_table + index); return robot; } // Returns updated row and column values after taking a step in current direction // values returned in robot structure myRobot_t stepInMaze(myRobot_t robot){ // index = 2*robot.dir uint8_t index = (robot.dir << 1); robot.maze.row += pgm_read_byte_near(map_table + index); // Add either -1, 0, or 1 to current row value robot.maze.col += pgm_read_byte_near(map_table + index + 1); // Add either -1, 0, or 1 to current column value return robot; } // Returns updated room and bees values using current row and column values // values returned in robot structure myRobot_t roomInMaze(myRobot_t robot){ // index = 21*robot.maze.row + robot.maze.col uint16_t index = (21*robot.maze.row) + robot.maze.col; uint8_t maze_val = pgm_read_byte_near(theMaze + index); robot.room = maze_val & 0x0F; // clear upper nibble and store as the room value uint8_t temp_bees = (maze_val & 0xF0) >> 4; // clear lower nibble and store as the temp bees value robot.bees += temp_bees; // add temp_bees to curret bees value return robot; } // Room Type subroutine uint8_t roomType(myRobot_t robot){ bool leftWall = leftHit(robot); // Test if hiting left wall bool hit = hitWall(robot); // Test if facing wall bool rightWall = rightHit(robot); // Test if hiting right wall uint8_t room = (uint8_t(leftWall) << 2)|(uint8_t(hit) << 1)|uint8_t(rightWall); // Convert to room type return room; } // Returns true if there is a wall and false if there is no wall bool hitWall(myRobot_t robot){ // index = dir_val robot = roomInMaze(robot); // Determine room value uint8_t wallVal = pgm_read_byte_near(hit_table + robot.dir); // Determine wall bit based on direction uint8_t outVal = bool(wallVal & robot.room); // Clear all bits other than the wall robot is facing if (outVal == 0){return false;} // If the robot is not hiting a wall outVal will equal zero else {return true;} // and the subroutine will return false, else it returns true. } // Returns true if there is a wall and false if there is no wall // on the right side of the robot bool rightHit(myRobot_t robot){ robot.turn = 0x01; // Modify turn value to turn right robot = turnInMaze(robot); // Call turnInMaze to turn the robot (virtually) bool hit = hitWall(robot); // Test hit wall return hit; } // Returns true if there is a wall and false if there is no wall // on the left side of the robot bool leftHit(myRobot_t robot){ robot.turn = 0x02; // Modify turn value to turn left robot = turnInMaze(robot); // Call turnInMaze to turn the robot (virtually) bool hit = hitWall(robot); // Test hit wall return hit; }