OpenMV to Arduino

I’ve hooked up an OpenMV Cam to an Arduino Pro Mini (3.3V 8MHz) using I2C. The OpenMV is defined as a SLAVE:

# Edge Detection Example:
#
# This example demonstrates using the morph function on an image to do edge
# detection and then thresholding and filtering that image afterwards.

import sensor, image, time
from pyb import Pin, I2C

kernel_size = 1 # kernel width = (size*2)+1, kernel height = (size*2)+1
kernel = [-1, -1, -1,\
          -1, +8, -1,\
          -1, -1, -1]
# This is a high pass filter kernel. see here for more kernels:
# http://www.fmwconcepts.com/imagemagick/digital_image_filtering.pdf
thresholds = [(150, 255)] # grayscale thresholds
TestData = 123

bus = I2C(2, I2C.SLAVE, addr=0x12) # P4=SCL, P5=SDA, set the slave address to whatever...

sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.RGB565
sensor.set_framesize(sensor.QVGA) # or sensor.QVGA (or others)
sensor.skip_frames(10) # Let new settings take affect.
clock = time.clock() # Tracks FPS.

# On the OV7725 sensor, edge detection can be enhanced
# significantly by setting the sharpness/edge registers.
# Note: This will be implemented as a function later.
if (sensor.get_id() == sensor.OV7725):
    sensor.__write_reg(0xAC, 0xDF)
    sensor.__write_reg(0x8F, 0xFF)

while(True):
    clock.tick() # Track elapsed milliseconds between snapshots().
    img = sensor.snapshot() # Take a picture and return the image.

    img.morph(kernel_size, kernel)
    img.binary(thresholds)

    # Erode pixels with less than 2 neighbors using a 3x3 image kernel
    img.erode(1, threshold = 2)

    # Send a test byte
    try:
        bus.send(TestData, timeout=1000)
    except OSError as err:
        if err.args[0] == 116:
            print("Check I2C Circuit")
        elif err.args[0] == 5:
            print("ACK missing on Data")
        elif err.args[0] == 16:
            print("Reset the board")
        else:
            print(err)
    print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
    # connected to your computer. The FPS should increase once disconnected.

The Arduino has an 23LC512 SRAM chip on SPI (32K bytes):

   
#define SlaveAddress 0x12

#include <SPI.h>
#include <Wire.h>

//---------------------------------------------------
byte GetByte(unsigned int Offset, unsigned int Index)
//---------------------------------------------------
{
unsigned int address;
byte data;
  address = Index + Offset;
  digitalWrite(10, LOW);
  SPI.transfer(3);             // SRAM Read instruction
  SPI.transfer((byte) (address >> 8));     
  SPI.transfer((byte) (address & 0xFF)); 
  data = SPI.transfer(0);
  digitalWrite(10, HIGH);
  return data;
}

//---------------------------------------------------------------
void PutByte(unsigned int Offset, unsigned int Index, byte value)
//---------------------------------------------------------------
{
unsigned int address;
  address = Index + Offset;
  digitalWrite(10, LOW);
  SPI.transfer(2);  // Write instruction
  SPI.transfer((byte) (address >> 8));     
  SPI.transfer((byte) (address & 0xFF)); 
  SPI.transfer(value);
  digitalWrite(10, HIGH);
}

//----------
void setup()
//----------
{
byte Mode;

  Serial.begin(115200);           // start serial for output  
  Serial.print(F("ARDUINO ")); 

  pinMode(10, OUTPUT); 
  digitalWrite(10, HIGH);         //Set SPI chip select pins HIGH.
  pinMode(11, OUTPUT);
  pinMode(12, INPUT);
  pinMode(13, OUTPUT);

  SPI.beginTransaction(SPISettings(20000000, MSBFIRST, SPI_MODE0));  // Begin SPI
 // In SPI_MODE0 (default the clock is normally low (CPOL = 0), and data
 // is sampled on the transition from low to high (leading edge) (CPHA = 0) 
  delay(10);

  // Read the SRAM Mode
  digitalWrite(10, LOW);
  SPI.transfer(5);  // read mode instruction
  Mode = SPI.transfer(0);
  digitalWrite(10, HIGH);
  
  //Default Mode = 64 or Sequential 

  Serial.print(F("SRAM Mode = ")); 
  Serial.println(Mode);
  
  Wire.begin();                   // join i2c bus as master 
}

//---------
void loop()
//---------
{
/*
Arduino 1 will write to data1 and read from data1
Arduino 2 will write to data2 and read from data2
*/
// Define SRAM Allocation:

//      Name   Offset      SRAM Bytes
//-----------------------------------
unsigned int Map = 0;  
unsigned int i,j,k,l;
unsigned int ReadError, WriteError;
unsigned long ExecTime;
byte x = 10;

  ReadError = 0;
  WriteError = 0;
  ExecTime = micros();
   
  for (i = 1; i <= 100; i++)
  {
    for (j = 0; j < 100; j++) PutByte(Map,j,i); // test write
    for (j = 0; j < 100; j++)                    // test read
    {
      k = GetByte(Map,j);
      if (k != i)
      {
        Serial.print(k);
        Serial.print("  ");
        Serial.println(i);
        l = GetByte(Map,j);
        if (k == l) WriteError++;
        else ReadError++;
      }
    }
  }
  ExecTime = micros() - ExecTime;
  Serial.print("ExecTime = ");
  Serial.print(ExecTime);
  Serial.print(" ReadError = ");
  Serial.print(ReadError);
  Serial.print(" WriteError = ");
  Serial.println(WriteError);
 
  //Wire.beginTransmission(SlaveAddress);  // transmit to Slave
  //Wire.write(ePointer >> 8);      // MSB
  //Wire.write(ePointer & 0xFF);    // LSB
  //Wire.write(x);        // write byte to EEPROM
  //Wire.endTransmission();     // stop transmitting  


  Wire.requestFrom(SlaveAddress, 1);  // request 1 bytes from slave
  i = Wire.available();
  if (i == 0)
  {
     Serial.println("Check I2C circuit");
  }
  else
  {
    while(Wire.available())       // slave may send less than requested
    { 
      x = Wire.read();      // receive a byte
      Serial.println(x);      // print byte
    }
  }
}

10K pull ups on both clock and data lines on the I2C bus. It works sometimes, but other times I get a “Reset the board” error from the OpenMV. I’m guessing there’s a synchronization problem :astonished:. Both OpenMV Cam and the Arduino are doing their own thing, I think I may need a synchronization pulse so both chips can always talk on I2C. Any ideas appreciated.

Moe

Hi, you saw this right: Camera interfacing using I2C - Project Discussion - OpenMV Forums

I wrote an I2C Master and slave script for the OpenMV Cam. The scripts work really well.

Let me know a more particular problem once you look at the scripts I wrote.

Hi Nyamekye:

Tx for reply. I based the OpenMV cam program on your post. Can OpernMV buffer bytes as a slave?

Yes, please see the slave script. The slave just needs to know what to send when it gets a request. So, you have to design some type of protocol.

I keep getting an OSError 16 with the Arduino as master and OpenMV as slave, so I switched things around. The OpenMV is now master and the Arduino is slave. My circuit is:

Arduino A5 to OpenMV P4 (SCL)
Arduino A4 to OpenMV P5 (SDA)
10K pull ups on SCL and SDA.
Ground to Ground
Power from USB cables on Arduino and OpenMV

This configuration has the advantage of using Arduino’s ISR routines Wire.onRequest() and Wire.onReceive().

The Arduino code:

#define SlaveAddress 0x12

#include <SPI.h>
#include <Wire.h>

volatile byte Register[15];  // Internal Registers for Slave
// Register[0] = last byte recieved from OpenMV Cam
// Register[1] = Data byte to send to OpenMV Cam
// Register[2] = Slave status.  0 is not bussy.  >0 is bussy

volatile boolean TrasmitStatus = false;

//---------------------------------------------------
byte GetByte(unsigned int Offset, unsigned int Index)
//---------------------------------------------------
{
unsigned int address;
byte data;
  address = Index + Offset;
  digitalWrite(10, LOW);
  SPI.transfer(3);             // SRAM Read instruction
  SPI.transfer((byte) (address >> 8));     
  SPI.transfer((byte) (address & 0xFF)); 
  data = SPI.transfer(0);
  digitalWrite(10, HIGH);
  return data;
}

//---------------------------------------------------------------
void PutByte(unsigned int Offset, unsigned int Index, byte value)
//---------------------------------------------------------------
{
unsigned int address;
  address = Index + Offset;
  digitalWrite(10, LOW);
  SPI.transfer(2);  // Write instruction
  SPI.transfer((byte) (address >> 8));     
  SPI.transfer((byte) (address & 0xFF)); 
  SPI.transfer(value);
  digitalWrite(10, HIGH);
}

//----------
void setup()
//----------
{
byte Mode;

  Serial.begin(115200);           // start serial for output  
  Serial.print(F("ARDUINO ")); 

  pinMode(9, OUTPUT); 
  digitalWrite(9, HIGH);         //Set SPI chip select pins HIGH.

  pinMode(10, OUTPUT); 
  digitalWrite(10, HIGH);         //Set SPI chip select pins HIGH.
  pinMode(11, OUTPUT);
  pinMode(12, INPUT);
  pinMode(13, OUTPUT);

  SPI.beginTransaction(SPISettings(20000000, MSBFIRST, SPI_MODE0));  // Begin SPI

  delay(10);

  // Read the SRAM Mode
  digitalWrite(10, LOW);
  SPI.transfer(5);  // read mode instruction
  Mode = SPI.transfer(0);
  digitalWrite(10, HIGH);
  
  //Default Mode = 64 or Sequential 

  Serial.print(F("SRAM Mode = ")); 
  Serial.println(Mode);
  
  // Wire.begin();    // join i2c bus as master 
  Wire.begin(SlaveAddress);       // join i2c bus with address SlaveAddress
  Wire.onRequest(requestEvent);   // register event
  Wire.onReceive(receiveEvent);   // register event
  delay(10);
}

//---------
void loop()
//---------
{
unsigned int Map = 0;  
unsigned int i,j,k,l;
unsigned int ReadError, WriteError;
unsigned long ExecTime;

  Register[1] = 0;
  
  while(true)
  {
    Register[2] = 1;
    ReadError = 0;
    WriteError = 0;
    ExecTime = micros();
   
    for (i = 1; i <= 100; i++)
    {
      for (j = 0; j < 100; j++) PutByte(Map,j,i); // test write
      for (j = 0; j < 100; j++)                    // test read
      {
        k = GetByte(Map,j);
        if (k != i)
        {
          Serial.print(k);
          Serial.print("  ");
          Serial.println(i);
          l = GetByte(Map,j);
          if (k == l) WriteError++;
          else ReadError++;
        }
      }
    }
    ExecTime = micros() - ExecTime;
    Serial.print("ExecTime = ");
    Serial.print(ExecTime);
    Serial.print(" ReadError = ");
    Serial.print(ReadError);
    Serial.print(" WriteError = ");
    Serial.println(WriteError);
  
    Register[1]++;
    if (Register[1] > 3) Register[1] = 0;
    if (Register[0] > 0)
    {
      Serial.println(Register[0]);      // print byte
      Register[0] = 0;
    }
  }
}

//-----------------
void requestEvent()  
//-----------------
// ISR that executes whenever data is requested by master
{
  if (TrasmitStatus == true)
  {
    Wire.write(Register[2]);
    TrasmitStatus = false;
  }
  else
  {
    Wire.write(Register[1]);
  }
}

//----------------------------
void receiveEvent(int howMany)
//----------------------------
// ISR that executes whenever data is received from master
{
  byte Command = Wire.read(); 
  if (Command == 1)
  {
    TrasmitStatus = true;
  }
  else
  {
    Register[0] = Command;
  }
}

The OpenMV code:

import sensor, image, time, pyb
from pyb import Pin, I2C

kernel_size = 1 # kernel width = (size*2)+1, kernel height = (size*2)+1
kernel = [-1, -1, -1,\
          -1, +8, -1,\
          -1, -1, -1]
# This is a high pass filter kernel. see here for more kernels:
# http://www.fmwconcepts.com/imagemagick/digital_image_filtering.pdf
thresholds = [(150, 255)] # grayscale thresholds
TestData = 123
SlaveAddress = 0x12

bus = I2C(2, I2C.MASTER, baudrate=100000, gencall=False) # P4=SCL, P5=SDA, set the slave address to whatever...

sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.RGB565
sensor.set_framesize(sensor.QVGA) # or sensor.QVGA (or others)
sensor.skip_frames(10) # Let new settings take affect.
clock = time.clock() # Tracks FPS.


# On the OV7725 sensor, edge detection can be enhanced
# significantly by setting the sharpness/edge registers.
# Note: This will be implemented as a function later.
if (sensor.get_id() == sensor.OV7725):
    sensor.__write_reg(0xAC, 0xDF)
    sensor.__write_reg(0x8F, 0xFF)


#OnLine = bus.scan()

#print(OnLine)

while(True):
    clock.tick() # Track elapsed milliseconds between snapshots().
    img = sensor.snapshot() # Take a picture and return the image.

    #img.morph(kernel_size, kernel)
    #img.binary(thresholds)

    # Erode pixels with less than 2 neighbors using a 3x3 image kernel
    #img.erode(1, threshold = 2)

    TestData = 111
    # Get a test byte
    try:
        TestData = bus.recv(1, SlaveAddress, timeout=1000)
    except OSError as err:
        if err.args[0] == 116:
            print("Check I2C Circuit")
        elif err.args[0] == 5:
            print("ACK missing on Data")
        elif err.args[0] == 16:
            print("Reset the pyboard")
        else:
            print(err)

    print(TestData)

    try:
        bus.send(TestData, SlaveAddress, timeout=1000)
    except OSError as err:
        if err.args[0] == 116:
            print("Check I2C Circuit")
        elif err.args[0] == 5:
            print("ACK missing on Data")
        elif err.args[0] == 16:
            print("Reset the pyboard")
        else:
            print(err)
    print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
    # connected to your computer. The FPS should increase once disconnected.

I’ve tried scanning the I2C bus with bus.scan() but nothing shows up.

Hi, I have an Arduino at home. I can debug this code for you tonight. But, can you do this for me in the mean time, just strip out everything from both scripts except the I2C commands. Since I think you’d prefer the Arduino as the master I’ll try to get that working.

I’ll try to make the code as your protocol.

Nyamekye:

Thanks for looking at this. This is the Arduino code with only I2C:

#define SlaveAddress 0x12

#include <Wire.h>

volatile byte Register[15];  // Internal Registers for Slave
// Register[0] = last byte recieved from OpenMV Cam
// Register[1] = Data byte to send to OpenMV Cam
// Register[2] = Slave status.  0 is not bussy.  >0 is bussy

volatile boolean TrasmitStatus = false;

//----------
void setup()
//----------
{
byte Mode;

  Serial.begin(115200);           // start serial for output  
  Serial.println(F("OpenMV to Arduino")); 
  Wire.begin(SlaveAddress);       // join i2c bus with address SlaveAddress
  Wire.onRequest(requestEvent);   // register event
  Wire.onReceive(receiveEvent);   // register event
  delay(10);
}

//---------
void loop()
//---------
{
  Register[1] = 0;  // byte to send to OpenMV
  
  while(true)
  {
    Register[2] = 1;  //Set bussy byte
    delay(100);       // delay a bit instead of doing SPI to SRAM
    Register[2] = 1;
  
    Register[1]++;  // change byte to send to OpenMV
    if (Register[1] > 3) Register[1] = 0;
    if (Register[0] > 0)
    {
      Serial.println(Register[0]);      // print byte from OpenMV
      Register[0] = 0;
    }
  }
}

//-----------------
void requestEvent()  
//-----------------
// ISR that executes whenever data is requested by master
{
  if (TrasmitStatus == true)
  {
    Wire.write(Register[2]);
    TrasmitStatus = false;
  }
  else
  {
    Wire.write(Register[1]);
  }
}

//----------------------------
void receiveEvent(int howMany)
//----------------------------
// ISR that executes whenever data is received from master
{
  byte Command = Wire.read(); 
  if (Command == 1)
  {
    TrasmitStatus = true;
  }
  else
  {
    Register[0] = Command;
  }
}

Moe

Hi, do you think you can wait till the weekend? I need to do a video update for the company blog and I don’t have the cycles tonight. Ping me if I forget about this on the weekend.

I’ll try to do a SPI/ Serial / and I2C example and I’ll make them part of the examples that come with the IDE.

Hi Nyamekye:

My bad :astonished:. I was going to replace the 10K pull ups and notice a circuit error. Works now but a bit sloppy. I’ll do a bit more work on it and post results.

Moe

This works. OpenMV code:

import sensor, image, time
from pyb import I2C

def Com123():
    print("Command 123")

def Com124():
    print("Command 124")
    
def Com125():
    print("Command 125")
    
def Com126():
    print("Command 126")  
    
kernel_size = 1 # kernel width = (size*2)+1, kernel height = (size*2)+1
kernel = [-1, -1, -1,\
          -1, +8, -1,\
          -1, -1, -1]
# This is a high pass filter kernel. see here for more kernels:
# http://www.fmwconcepts.com/imagemagick/digital_image_filtering.pdf
thresholds = [(150, 255)] # grayscale thresholds
TestData = 123
SlaveAddress = 0x12

bus = I2C(2, I2C.MASTER, baudrate=100000, gencall=False) # P4=SCL, P5=SDA, set the slave address to whatever...

sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.RGB565
sensor.set_framesize(sensor.QVGA) # or sensor.QVGA (or others)
sensor.skip_frames(10) # Let new settings take affect.
clock = time.clock() # Tracks FPS.


# On the OV7725 sensor, edge detection can be enhanced
# significantly by setting the sharpness/edge registers.
# Note: This will be implemented as a function later.
if (sensor.get_id() == sensor.OV7725):
    sensor.__write_reg(0xAC, 0xDF)
    sensor.__write_reg(0x8F, 0xFF)

#Look for Arduino
OnLine = bus.scan()
if(OnLine[0] == SlaveAddress):
    print("Arduino Online")
    MainLoop = True
else:
    print("Arduino not found")
    print(OnLine)
    MainLoop = False

while(MainLoop):  #Start Main loop
    clock.tick()  #Track elapsed milliseconds between snapshots().
    TestData = 111
    # Get a test byte from Arduino
    TestData = bus.recv(1, SlaveAddress, timeout=1000)
    # Confirm test byte received
    bus.send(TestData, SlaveAddress, timeout=1000)
    if (TestData == b'{'):
        Com123()
    elif (TestData == b'|'):
        Com124()
    elif (TestData == b'}'):
        Com125()  
    elif (TestData == b'~'):
        Com126()             
    else:
        print(TestData)
    img = sensor.snapshot() # Take a picture and return the image.
    print(clock.fps())

Arduino code:

#define SlaveAddress 0x12

#include <Wire.h>

volatile byte Register[15];  // Internal Registers for Slave
// Register[0] = last byte recieved from OpenMV Cam
// Register[1] = Data byte to send to OpenMV Cam
// Register[2] = Slave status.  0 is not bussy.  >0 is bussy

volatile boolean TrasmitStatus = false;

//----------
void setup()
//----------
{
byte Mode;

  Serial.begin(115200);           // start serial for output  
  Serial.println(F("OpenMV to Arduino")); 
  pinMode(13, OUTPUT);
  Wire.begin(SlaveAddress);       // join i2c bus with address SlaveAddress
  Wire.onRequest(requestEvent);   // register event
  Wire.onReceive(receiveEvent);   // register event
  delay(10);
}

//---------
void loop()
//---------
{
  Register[1] = 123;  // byte to send to OpenMV
  
  while(true)
  {
    Register[2] = 1;
    digitalWrite(13, HIGH);
    delay(50);       // delay a bit instead of doing SPI to SRAM
    digitalWrite(13, LOW);
    delay(50);       // delay a bit instead of doing SPI to SRAM   
    Register[2] = 1;
  
    Register[1]++;  // change byte to send to OpenMV
    if (Register[1] == 255) Serial.println(F("Arduino 255"));    
    if (Register[1] > 126) Register[1] = 123;
    if (Register[0] > 0)
    {
      Serial.println(Register[0]);      // print byte from OpenMV
      Register[0] = 0;
    }
  }
}

//-----------------
void requestEvent()  
//-----------------
// ISR that executes whenever data is requested by master
{
  if (TrasmitStatus == true)
  {
    Wire.write(Register[2]);
    TrasmitStatus = false;
  }
  else
  {
    Wire.write(Register[1]);
  }
}

//----------------------------
void receiveEvent(int howMany)
//----------------------------
// ISR that executes whenever data is received from master
{
  byte Command = Wire.read(); 
  if (Command == 1)
  {
    TrasmitStatus = true;
  }
  else
  {
    Register[0] = Command;
  }
}

Moe

Great for you!

So, I spent all day on these two scripts. Learned a lot. Wrote notes in the code.

In general, interfacing the OpenMV Cam as a slave device is not impossible and works fairly robust. However, it’s important to make sure both are synced up correctly. Otherwise things don’t work at all. In general, I’d avoid using either the Arduino or the OpenMV Cam as a slave device. Async serial is much, much, much more robust to use. But, I know the Arduino only has one serial port.

I read through this and your other linked thread. Do you still think these scripts are the best possible methods for sending images through an arduino device? Do you think SPI is better than I2C?

You definitely want to use SPI to send an image to the Arduino. So, make the OpenMV Cam into a SPI slave and read the image out. You just have to do the sync byte things I did in the script and then it’s pretty stable.

That said, why do you want the Arduino in the equation at all? What can it do that the OpenMV Cam can’t?

That’s a good question. I want to transmit every 10 images to an ftp or http portal using cellular communication. I have previously used the konekt dash and some other options to go to cellular such as the adafruit FONA. I also know that the rasp pi and beaglebone have been connected via cellular but I don’t know if this has been done with a micro python system. Is there a library to use a particular module as a cellular modem? I see there is a library for a wifi chip but would like to avoid daisy chaining radio comm by using a VZ jetpack or something like that. I just wanted something that would work now for a location with no wifi or other non cellular radio comm present and this seemed like it could although it may not be the prettiest.

Mmm, well, if you need a pre-canned library then use the Arduino. It honestly shouldn’t be to hard to make the OpenMV Cam do that however. I’m assuming the coms work via serial using some type of simple protocol right? Anyway, if writing the com interface in python doesn’t work for you then I’d use the SPI interface for the OpenMV Cam. It should be pretty reliable assuming the Cam is up and running first before the Arduino. Maybe you might want to use some extra wires for sync purposes.

As for image readout… do you want a jpeg compressed image or a higher quality non-compressed image? You can just do img[index] to get each pixel of an image one at a time like from a linear array. And… if you want a jpeg image call img.compressed() first to get a jpeg compressed copy. The indexing also works on the jpeg compressed image.

The next firmware update we have coming out fixes the compress() method so you’ll be able to jpeg compress images in place.

I did a bit more work on I2C OpenMV to Arduiono
The Arduino ISR are realy simple.
WARNING: Don’t try to downlond more than one byte from OpenMV to Arduino. No image download.
If you need to do this, then the Arduino recieve ISR will need a buffer.

#define SlaveAddress 0x12
#include <Wire.h>

volatile byte SendByte;
volatile byte RecByte;

//-----------------------------
void CommandToOpenMV(byte Com)
//-----------------------------
{
boolean Checking = true;
  while(Checking)
  {  
    Serial.print(F("Sending Com "));
    Serial.print(Com);
    RecByte = 111;  // OpenMV wait command
    SendByte = Com;
    while(RecByte == 111) delay(1);
    if(RecByte == Com)
    {
      Serial.println(F(" Confirmed"));
      Checking = false;
    }
    else
    {
      Serial.print(F(" RecByte Error = "));
      Serial.println(RecByte);
    }
  } 
}

//----------
void setup()
//----------
{
  Serial.begin(115200);           // start serial for output  
  Serial.println(F("OpenMV to Arduino")); 
  pinMode(13, OUTPUT);
  Wire.begin(SlaveAddress);       // join i2c bus with address SlaveAddress
  Wire.onRequest(requestEvent);   // register event
  Wire.onReceive(receiveEvent);   // register event
  delay(10);
}

//---------
void loop()
//---------
{
  while(true)
  {
    digitalWrite(13, HIGH);
    delay(50);       // delay a bit instead of doing SPI to SRAM
    digitalWrite(13, LOW);
    delay(50);       // delay a bit instead of doing SPI to SRAM   
    CommandToOpenMV(123);
    CommandToOpenMV(124);
    CommandToOpenMV(125);
    CommandToOpenMV(126);
  } 
}

//-----------------
void requestEvent()  
//-----------------
// ISR that executes whenever data is requested by master
{
  Wire.write(SendByte);
  SendByte = 111;
}

//----------------------------
void receiveEvent(int howMany)
//----------------------------
// ISR that executes whenever data is received from master
{
  RecByte = Wire.read(); 
}

The OpenMV code:

import sensor, image, time
from pyb import I2C

def Com123():
    print("Command 123")

def Com124():
    print("Command 124")

def Com125():
    print("Command 125")

def Com126():
    print("Command 126")

kernel_size = 1 # kernel width = (size*2)+1, kernel height = (size*2)+1
kernel = [-1, -1, -1,\
          -1, +8, -1,\
          -1, -1, -1]
thresholds = [(150, 255)] # grayscale thresholds
TestData = 123
SlaveAddress = 0x12

bus = I2C(2, I2C.MASTER, baudrate=100000, gencall=False) # P4=SCL, P5=SDA, set the slave address to whatever...

sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.GRAYSCALE) # or sensor.RGB565
sensor.set_framesize(sensor.QVGA) # or sensor.QVGA (or others)
sensor.skip_frames(10) # Let new settings take affect.
clock = time.clock() # Tracks FPS.

if (sensor.get_id() == sensor.OV7725):
    sensor.__write_reg(0xAC, 0xDF)
    sensor.__write_reg(0x8F, 0xFF)

#----------------
#Look for Arduino
#----------------
OnLine = bus.scan()
if(OnLine == []):
    print("Arduino not found")
    MainLoop = False
else:
    if(OnLine[0] == SlaveAddress):
        print("Arduino Online")
        MainLoop = True
    else:
        print("Arduino not found")
        print(OnLine)
        MainLoop = False

#---------------
#start main loop
#---------------
while(MainLoop):  #Start Main loop
    clock.tick()  #Track elapsed milliseconds between snapshots().

    #---------------
    #Talk to Arduino
    #---------------
    ArduionWait = True
    TestData = 100
    while(ArduionWait):
        # Get a test byte from Arduino
        try:
            TestData = ord(bus.recv(1, SlaveAddress, timeout=1000))
        except OSError:
            pass
        # Confirm test byte received
        try:
            bus.send(TestData, SlaveAddress, timeout=1000)
        except OSError:
            pass
        if(TestData != 111):
            ArduionWait = False

    #----------------------
    #What did Arduino say?
    #----------------------
    if (TestData == 123):
        Com123()
    elif (TestData == 124):
        Com124()
    elif (TestData == 125):
        Com125()
    elif (TestData == 126):
        Com126()
    else:
        print(TestData)
    img = sensor.snapshot() # Take a picture and return the image.
    print(clock.fps())

Moe