Absolute Position Encoder Disk (Gray Code)

Absolute position encoders are actually fairly rare, and I wanted to make my own for the sake of education. I decided to make an absolute position encoder with a 3D-printed disk and lasers used in tandem with photoresistors to detect position. With 5 bits of encoded data, this encoder can detect position with 360/(2^5) = 11.25 degrees of accuracy. 


The huge bonus of absolute position encoders that many people don't realize is that the common relative position encoder can't remember a position unless the embedded system remains constantly powered. This problem can be somewhat alleviated by adding memory, but this complicates the system greatly, and doesn't help much in situations where there is consistent rotation, such as an anemometer, which was the use that I originally intended to use this design for. 


This absolute position encoder I designed can be left unpowered for indiscriminate lengths of time and still provide accurate readings when polled. On this article is the code. Files and a bill of materials can be found in the description of the YouTube video I made on this topic:

https://www.youtube.com/watch?v=lwLOwk8zhh4




In this project, we have a disk that is encoded in Gray code, where readings are derived by photoresistors that have lasers shining on them. Low values are derived from material on the disk blocking the light. If a certain threshold of voltage is not met, that bit will be read as low. 


Thanks to David's Engineering Notes, I used his code for converting a gray code value to binary. 

http://engineeringnotes.blogspot.com/2015/07/convert-gray-code-to-binary-arduinos.html


Google Drive link to Code: 

https://drive.google.com/file/d/1wmpyNm6eDQJ28DJm7a0p2tG7Kzs1Xp-2/view


Text version of code:

// Declaring 5 variables to put the 5 bits of data into later on
int b1;
int b2;
int b3;
int b4;
int b5;

void setup() {
  Serial.begin(9600);
}
// the loop function runs over and over again forever
void loop() {
  // Read all the analog pins and put their values into the corresponding variables
  b1 = analogRead(A0);
  b2 = analogRead(A1);
  b3 = analogRead(A2);
  b4 = analogRead(A3);
  b5 = analogRead(A4);
//   Uncomment this section to display photoresistor readings on the Serial Plotter
//  Serial.print(b1);
//  Serial.print(" ");
//  Serial.print(b2);
//  Serial.print(" ");
//  Serial.print(b3);
//  Serial.print(" ");
//  Serial.print(b4);
//  Serial.print(" ");
//  Serial.println(b5);
// End of section to uncomment
  // Forcing the bits to either high or low values. More light equals lower resistance. 
  // You may need to change the threshold (mine is 200) based on what value of resistor you use.
  if (b1 < 200){b1 = 1;}else{b1 = 0;}
  if (b2 < 200){b2 = 1;}else{b2 = 0;}
  if (b3 < 200){b3 = 1;}else{b3 = 0;}
  if (b4 < 200){b4 = 1;}else{b4 = 0;}
  if (b5 < 200){b5 = 1;}else{b5 = 0;}
  // Creating a String by concatenating the 5 bits of data
  String data = String(b5) + String(b4) + String(b3) + String(b2) + String(b1);
  // Putting the string we just made into an unsigned int, because that's what the graytobin function requires
  unsigned int iStart=atoi(data.c_str());
  iStart = graytobin(iStart, 5);
  // The output that is given is in kind of an erroneous order. I didn't have time to figure out
  // what was going on here, so I just took note of the random order and used the following lines
  // of code to map them from the random order to the logical physical order as they are 
  // coded on the disk. 
  int keys1[32] = {0, 1, 13, 12, 11, 10, 6, 7, 8, 9, 26, 27, 28, 29, 14, 15, 16, 17, 2, 3, 4, 5, 22, 23, 24, 25, 21, 20, 19, 18, 30, 31};
  int valnum;
  for (int i=0; i<32; i++){
    if (keys1[i] == iStart){
      valnum = i+1;
    }
  }
  // This delay is arbitrary, I just didn't want to spam my serial monitor.
  delay(50);
  // Comment this out if using Serial Plotter. valnum variable is the current position, between 1-32.
  Serial.println(valnum);
}


// I got this code from David's Engineering Notes.
// http://engineeringnotes.blogspot.com/2015/07/convert-gray-code-to-binary-arduinos.html
int graytobin(int grayVal, int nbits ) { 
  int binVal = 0;
  bitWrite(binVal, nbits - 1, bitRead(grayVal, nbits - 1)); // MSB stays the same
  for (int b = nbits - 1; b > 0; b-- ) {
    // XOR bits
    if (bitRead(binVal, b) == bitRead(grayVal, b - 1)) { // binary bit and gray bit-1 the same
      bitWrite(binVal, b - 1, 0);
    }
    else {
      bitWrite(binVal, b - 1, 1);
    }
  }
  return binVal;
}

Comments