

#include <Wire.h>

#include <Servo.h>



/* <Servo> lib supports 12 servos on normal boards */

#define MAX_SERVOS 12



/* Our own address */

#define SLAVE_ADDRESS 0x09



/* internal servo register */

struct servo_reg_t {

byte enabled;

byte pin;

byte angle;

};



/* array of servo registers */

volatile struct servo_reg_t servo_data[MAX_SERVOS];



/* current servo address for read/write (0 - MAX_SERVOS-1) */

volatile byte servo_addr = 0;



/* array of servos */

Servo servos[MAX_SERVOS];



/* flow control variable */

volatile byte updated = 0;



/* simple flush function */

void flush(int bytes) {

if (bytes) {

while(Wire.available())

Wire.read();

}

}



/* When we receive data the first byte is the servo address.

* If only a single byte came, it's a Write operation on

* the servo_addr register.

*

* If more bytes came, it's a Write operation on a servo

* register.

*/

void onReceiveEvent(int bytes) {

volatile struct servo_reg_t *servo_ptr;



servo_addr = Wire.read();



/* check for invalid address */

if (servo_addr >= MAX_SERVOS) {

servo_addr = 0;

flush(bytes);

return;

}



bytes--;



/* if Write operation (3 bytes) */

if (bytes == 3) {

servo_ptr = &servo_data[servo_addr];

servo_ptr->enabled = Wire.read();

servo_ptr->pin = Wire.read();

servo_ptr->angle = Wire.read();

updated = 1; /* tell the main loop that we updated a servo */

bytes -= 3;

}



/* flush any invalid data and exit*/

flush(bytes);

}



/* The request event sends a servo register to the master.

* This register is the current servo_addr already set on

* a previous receive event. See master code.

*/

void onRequestEvent() {

byte *out = (byte *)&servo_data[servo_addr];

Wire.write(out, 3);

}



void setup() {

Wire.begin(SLAVE_ADDRESS);

Wire.onReceive(onReceiveEvent);

Wire.onRequest(onRequestEvent);

}



/* the main loop just keeps the servos updated when <updated> flag is set */

void loop() {

volatile struct servo_reg_t *servo_ptr;

Servo *servo;



if (updated) {

for(int i = 0; i < MAX_SERVOS; i++) {

servo_ptr = &servo_data[i];

servo = &servos[i];

servo->detach();



if (servo_ptr->enabled) {

servo->attach(servo_ptr->pin);

servo->write(servo_ptr->angle);

}

}

updated = 0;

}

}







#include <Wire.h>



/* address of the slave controller */

#define SLAVE_ADDRESS 0x09



/* Send servo number, enabled = 1, pin and angle. */

void servo_enable(byte addr, byte pin, byte angle) {

Wire.beginTransmission(SLAVE_ADDRESS);

Wire.write(addr);

Wire.write(1);

Wire.write(pin);

Wire.write(angle);

Wire.endTransmission();

}



/* Send servo number and enabled = 0. The rest will be ignored by the slave. */

void servo_disable(byte addr) {

Wire.beginTransmission(SLAVE_ADDRESS);

Wire.write(addr);

Wire.write(0);

Wire.write(0);

Wire.write(0);

Wire.endTransmission();

}



/* Check if a servo is running.

* Send the servo number and then wait for the first byte (enabled).

*/

byte servo_running(byte addr) {

Wire.beginTransmission(SLAVE_ADDRESS);

Wire.write(addr);

Wire.endTransmission();



Wire.requestFrom(SLAVE_ADDRESS, 1);

return Wire.read();

}



void setup() {

Wire.begin();

pinMode(13, OUTPUT);

digitalWrite(13, LOW);

}



void loop() {



/* first servo, pin 13 (led), 90 degrees */

servo_enable(0, 13, 90);



if (servo_running(0)) // check the servo to drive the led

digitalWrite(13, HIGH);



delay(3000);



servo_disable(0);



if (!servo_running(0))

digitalWrite(13, LOW);



delay(3000);

}







I wanted to share a simple project I made. It turns an arduino into a stand alone i2c 12 servo controller.If you've seen products like this:- i2c ADC board ( https://www.adafruit.com/products/1085 - i2c servo board ( https://www.adafruit.com/product/815 you can do that with a spare arduino.The wiring is like this:I've kept the code simple, small and documented for learning purposes. It still can be improved a lot. I hope it helps.Here is the code for our servo controller (slave):And here the code for the remote controller (master). It only requires a few simple functions.