Issues communicating with Parallax OFN over I2C

Hello, I’m trying to connect an OFN module from Parallax to my Arduino Uno. It should just be a matter of talking to the OFN module over I2C by checking the Motion register, seeing if motion has occurred, then reading Delta_X and Delta_Y if it has.

However, when I run the following code, it hangs at the Wire.endTransmission() call. I get no result and the program stalls. I’ve been reading up, and it looks like wire.h might not be working correctly right now, but I’ve never done this before, so I’m assuming it’s an error on my end. The following code is just trying to get a single byte of data from the Motion register to see if the OFN is even talking, and so far, it’s not.

Product Info Here: http://www.parallax.com/Store/Microcontrollers/BASICStampModules/tabid/134/ProductID/715/List/1/Default.aspx?SortField=UnitCost,ProductName
Datasheet Here: http://www.parallax.com/Portals/0/Downloads/docs/prod/acc/27903-OFNModule-v1.1.pdf

/* Attempting to read motion data from a Parallax OFN board
   Device Info: http://www.parallax.com/Store/Microcontrollers/BASICStampModules/tabid/134/ProductID/715/List/1/Default.aspx?SortField=UnitCost,ProductName
   Datasheet: http://www.parallax.com/Portals/0/Downloads/docs/prod/acc/27903-OFNModule-v1.1.pdf
   

   Setup:
     Arduino A4 connected to SDA
             A5 connected to SCL
             5V connected to VCC
             GND Connected to GND
     10K Resitor connected from SDA to VCC and from SCL to VCC
     
     Parallax OFN Vdd connected to VCC
                  GND Connected to GND
                  CLK connected to SCL
                  SDA connected to SDA
                  
     Parallax OFN Register Table
     
     Addr    Register    Read/Write    Default Value
     0x00    Product_ID    R                0x83
     0x01    Revision_ID   R                0x01
     0x02    Motion        R/W              0x00
     0x03    Delta_X       R                Any
     0x04    Delta_Y       R                Any
     etc ...
     
  This is based on my limited understanding of Nicholas Zambetti's Wire.h example code
  
*/


#include <Wire.h>

//each device has a custom ID (parallax Unit is ID# 0x83)
char deviceID = 0x83; //default according to device PDF

void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop()
{
  //Begin Transmission
  Serial.println("Wire.beginTransmission");
  Wire.beginTransmission(deviceID);
  
  //send address of register 0x02 which is the motion register
  Serial.println("Wire.write(0x02)");
  Wire.write(0x02); //register of Motion data
  
  Wire.endTransmission(); // <----- program gets stuck here, doesn't ever get to following serlial.println 
  /*
  I thought that this code would show me why the program hangs, but this doesn't seem to work at all
  char result = Wire.endTransmission();
  Serial.print("Wire.endTransmission() = "); 
  Serial.println(result);
  */
  
  //Request 1 byte
  Serial.println("Wire.requestFrom");
  Wire.requestFrom(deviceID, 1);    // request 1 bytes from slave deviceID

  //wait for response
  while(Wire.available() == 0) {
    Serial.println("Wire Not available loop."); //here b/c I wasn't getting any feedback
  }
  
  //Read one byte returned from the Motion register 
  char c = Wire.read(); // receive a byte as character
  Serial.print(c);         // print the character

  delay(500);
  
}

According to the sample code in the datasheet (top of page 4), the I2C device address is 0x33. Try with this address and report if you have success.

I’m very new to this process, and it took me ages to figure out to what you were referring. I assumed that the Product_ID mentioned was the device address, but in their example code, they show an address of B01100110, which translates to an 8bit adress of 0x67, but keeping in mind the 7-bit address structure, we remove the last (first?) bit, and it becomes 0110011 or 0x33. I updated the code with this new address, and it’s still hanging on Wire.endTransmission();

After looking around, I found some example code from Nick Gammon to scan the I2C bus for devices, and tried to implement it. The code is half way down on this page: Gammon Forum : Electronics : Microprocessors : I2C - Two-Wire Peripheral Interface - for Arduino

// I2C Scanner
// Written by Nick Gammon
// Date: 20th April 2011

#include <Wire.h>

void setup() {
  Serial.begin (115200);

  // Leonardo: wait for serial port to connect
  while (!Serial) 
    {
    }

  Serial.println ();
  Serial.println ("I2C scanner. Scanning ...");
  byte count = 0;
  
  Wire.begin();
  for (byte i = 1; i < 120; i++)
  {
    Wire.beginTransmission (i);
    if (Wire.endTransmission () == 0)
      {
      Serial.print ("Found address: ");
      Serial.print (i, DEC);
      Serial.print (" (0x");
      Serial.print (i, HEX);
      Serial.println (")");
      count++;
      delay (1);  // maybe unneeded?
      } // end of good response
  } // end of for loop
  Serial.println ("Done.");
  Serial.print ("Found ");
  Serial.print (count, DEC);
  Serial.println (" device(s).");
}  // end of setup

void loop() {}

I had to adjust the baud rate back to 9600 to get it working properly, but it too is hanging on the first address Wire.endTransmission() call in the loop. When I added a Serial.print to debug inside the loop before the Wire.endTrasnmission() call is made (and after the beginTransmission()), it reports hanging on the forst address tried (8 in my code below). Even if the prallax sensor was setup incorrectly, it should still be able to try all of the address right? I think I’m over my head on this one :slight_smile:

OK, after trying a different (maybe even simpler) I2C library (http://dsscircuits.com/articles/arduino-i2c-master-library.html), which has a scanning function and enables a timeout value, I was able to see that it was MY fault all along. I had incorrrectly bridged the SCL Line to the GROUND, not the 5V! The I2c.scan() function in this new library reported that the bus was not working correctly, which was very helpful. After removing the Parallax OFN, I got the same error, which means the basic wiring was failing.

The scanning code:

/*
Trying a new I2C library b/c Wire.cpp apparanetly i sbroekn in the current Arduino Rev
https://github.com/DSSCircuits/I2C-Master-Library
*/

#include <I2C.h>

void setup()
{
  I2c.begin();   
  I2c.timeOut(1000);
  
  Serial.begin(9600);           // start serial for output
}

void loop()
{
    I2c.scan();
}

This is pretty embarrassing, but I’ll revise my previous code now and see what I get back :slight_smile: Thanks for the help guys, and you were correct, the device reports an ID of 0x33.

After a couple of days at it, and some help on the Parallax forum, I’ve finally got the arduino Uno reading positive and negative delta values from the OFN module. I had to remove the 10K pull UP resistors from the circuit to get the OFN to behave, and then ran into a data issue when reading the delta x and y registers. Essentially, the data was encoded as a signed byte, but I wasn’t able to see that, so it looked to be reporting a 0-255 value, but it actually wanted to treat the byte as a value of -127 to 127. There is an if statement that checks for this and corrects it now. After increases the CPI of the sensor to 1000CPI, I’m getting pretty useful readings. I’m pretty stoked. Here’s the code that worked for me:

/*
This code connects to and reads data over I2C from a Parallax OFN module.  

   Device Info: http://www.parallax.com/Store/Microcontrollers/BASICStampModules/tabid/134/ProductID/715/List/1/Default.aspx?SortField=UnitCost,ProductName
   Datasheet: http://www.parallax.com/Portals/0/Downloads/docs/prod/acc/27903-OFNModule-v1.1.pdf
   

   Setup:
     Arduino A4 connected to SDA
             A5 connected to SCL
             5V connected to VCC
             GND Connected to GND
     Removed these b/c they were causing errors with OFN (add 'em back if you can't get it working) 
     --> 10K Resitor connected from SDA to VCC and from SCL to VCC
     
     Parallax OFN Vdd connected to VCC
                  GND Connected to GND
                  CLK connected to SCL
                  SDA connected to SDA
                  
     Parallax OFN Register Table    
     Addr    Register    Read/Write    Default Value
     0x00    Product_ID    R                0x83
     0x01    Revision_ID   R                0x01
     0x02    Motion        R/W              0x00
     0x03    Delta_X       R                Any
     0x04    Delta_Y       R                Any
     0x11    Res Config    R/W              00000000=500cpi  10000000=1000cpi
     etc ...
     
     
     The values returned when set to 1000CPI resolution will cover the range from -127 to -127
     *see below on how to change resolution by setting register 0x11
     
Trying a new I2C library that simplifies communication compared to wire.h
https://github.com/DSSCircuits/I2C-Master-Library
more info: http://dsscircuits.com/articles/arduino-i2c-master-library.html
*/

#include <I2C.h>

//each device has a custom ID (parallax Unit is 0x33)
char deviceID = 0x33;
int result = 0;
boolean MOT = false;    //is motion bit state
boolean OVF = false;    //is OVF bit state
char deltaX = 0;        //holds Delta_X reading from OFN
char deltaY = 0;        //holds Delta_Y reading from OFN
int deltaXI = 0;        //holds corrected (signed) Delta_X reading
int deltaYI = 0;        //holds corrected (signed) Delta_Y reading


void setup()
{

  I2c.begin();          // join i2c bus 
  Serial.begin(9600);   // start serial for output
  
  //if you need to chage the resoution of teh sensor uncomment the corresponding line below:
  //I2c.write(deviceID, 0x11, B10000000);        //Set the Resolution to 1000CPI
  //I2c.write(deviceID, 0x11, B00000000);        //Set the Resolution to 500CPI
}

void loop()
{
  //read the Motion register to evaluate if MOT is set
  I2c.write(deviceID, 0x02);    //send Register 2 address which is what we'd like to read I think
  I2c.read(deviceID, 1); 
  result = I2c.receive();   //read the byte from the motion register
  
  //check if MOT bit is set
  //this works by shifting the bits over 7 spaces and checking if the result is 1 or 0
  if ((result & (1 << 7)))
  {
   //Debug to see if MOT bit test is working
   //Serial.println("The Motion Bit is set.");
   MOT = true;
  } else MOT = false;
  
  //check if OVF bit is set
  //this works by shifting the bits over 4 spaces and checking if the result is 1 or 0
  if ((result & (1 << 4)))
  {
   //Serial.println("The OVF Bit is set.");
   OVF = true;
   
   //since I don't really understand the data, I'll just reset the OFN if OVF is set
   //send any data to register 0x02 should reset MOT, OVF, and Delta_X and Delta_Y according to PDF
   I2c.write(deviceID, 0x02, 0);
   MOT = false;
   OVF = false;
   //Serial.println("OFN data reset");
    
  }
  
  //If Motion has occurred, then read the Delta_X (0x03) and Delta_Y (0x04) registers 
  if (MOT) {
    //set the Delta_X register
    I2c.write(deviceID, 0x03);
    //read one byte of data
    I2c.read(deviceID, 1); 
    deltaX = I2c.receive();    
    
     //set the Delta_Y register
    I2c.write(deviceID, 0x04);
    //read one byte of data
    I2c.read(deviceID, 1); 
    deltaY = I2c.receive(); 
  
    // Simple check to overcome the problem of reading the values from the OFN.  By default, the byte returned by
    // the OFN module is signed, meaning it holds a value from -127 to 127 in the 8bits, but the byte in Arduino is unsigned, 
    // and holds a value from 0-255.  To correct for this, you can simply check to see if the value is over 127, and subtract the maximum byte value (256)
    // after testing, this seems to work great.
    if (deltaX > 127) deltaXI = deltaX - 255; //working 256
       else deltaXI = deltaX;                 //try 255 to get more sensitiveity?
    
    if (deltaY > 127) deltaYI = deltaY - 255;
       else deltaYI = deltaY;
    
    
    Serial.print("delataX, DeltaY: ");
    //Serial.print(deltaX, BIN);
    Serial.print(deltaXI, DEC);
    Serial.print(",");
    //Serial.println(deltaY, BIN);
    Serial.println(deltaYI, DEC);

  } //end if
  

  delay(50);  //delay for a moment to reduce the amount of readings
  
}

Hello, just wanted to thank you very much for this . This made my project SO much easier to complete. Bless you sir and all the best to your future work.

H