The most interesting part, it is where all your hard work so far will pay off. We've been working to take care of all physical details, now, what about the emotional ones? :p

As a start, you will need to have the Arduino IDE installed for your OS, then you need to manually install the Adafruit library to be able to control the NeoPixel in case you've decided to use them.

Attached is the full code of oiO as of the time I am writing this. He will definitively receive more code and education as I go :) Note that the code is calibrated to work with the sensors and servos I used, so maybe it wont make sense for you when you try it. You will need to go through many trials until you find the good values (more on this in the next section)

Introduction

For me, the whole point behind this project is to make oiO interactive and autonomous, acting on his own. he should continue to provide his main function, give you light, but also he has his mood, and mood swings, it changes posture and interacts with its user.

But there was a challenge for me, summarized in the below points

1) How can I move 3 servos at the same time, or separately if needed

2) How can I ease the movements, and introduce some easing to simulate a fluid motion. Also have some control on the speed of servos to simulate different behaviors.

3) How can I read and control all sensors and servos in parallel knowing that Arduino doesn't natively support multitasking.

4) How can I make oiO's behavior believable, and avoid robotic responses to similar inputs, as I want him to respond differently for the same input (sound and proximity), all this knowing that Arduino just reads the code from top-to-bottom and then loops

5) oiO needs to have a sense of time passing by, an act according to it.

All this to be done with 32KB of dynamic memory available on the Arduino for the code, and while keeping it simple!

After 2 days of writing code, I think I achieved my targets and answered the five question in an acceptable way with few compromises.

We might argue on how well I did this and how believable it is, but end of day, the video you saw, is using only 17% of code memory, and there is a lot of room for improvements, that I will continue to pursue. but since you have the code, please write me down any suggestion, or just share your improvements.

In next sections I will share some snippets of code I felt i need to discuss, I leave to you reading the full code and making sense of it, also it is heavily commented to help better understanding, and for me to remember the details 3 months from now :). I hope I didn't over do it!

Points 1 & 2

Moving 3 servos simultaneously on Arduino is not possible natively. so I had to write a function that takes 3 servos and 3 target positions for each, in addition to an easing factor to control the speed and ease of servos. The function controls the servos using writeMicroseconds() instead of write(), as the former gives more precise control due to its wider range of values, controlling the servos by setting the exact PWM pulse width in milliseconds, instead of the latter that users angular degrees.

The function is:

void <strong>move3Servos</strong>(Servo <strong>servo1Name</strong>, float<strong> target_pos1</strong>,Servo <strong>servo2Name</strong>, float <strong>target_pos2</strong>,Servo <strong>servo3Name</strong>, float <strong>target_pos3</strong>,float <strong>easing</strong>)<br>

- target_posX: has limits on start and end positions, between 600 and 2400 mS, Some servos work between 1000 and 2000...Please check your servos limits by testing or datasheet before setting the values

- easing: is a value between 0.0 and 5.0, where:

- values between 0.0 to 1.0 cause the servos to start movement fast but end slowely, values around zero (ex: 0.05) gives much slower/desired response of smoothing, value of 0.1 is fast enough

- values between 1.0 and 5.0, are not considered easing factors, as they cause a change in behavior, causing servos to move linearly start to stop with x increments depending on the value provided. Value of 2.0 is turtle speed, while a value of 5.0 is relatively fast.

To simulate simultaneous control of the 3 servos, the function control each servo one step at a time, but in a row, that is, moves servo1 a tiny step, then moves servo 2 a tiny step, then servo3 similarly, then it does this again and agin in a loop until all servos had reached their target positions. The end result, all servos seem to have moved at the same time to their target positions.

Points 3 & 4

To tackle this, the trick was to separate reading the sensors value from actually acting on the servos and eyes. This allows me to create counters that count sound and proximity consequent detections, and also measure timing between those occurrences. These counters and time stamps can be later independently used in the part that act on the servos and eyes. I test for counters values and see how long it took the user between each detection. For example, this is how oiO can differentiate between a user approaching him 3 times in quick manner, thus making oiO angry with red eyes and jerky movements, or if the user approached him consecutively in a slow manner, thus making oiO calmer with warmer eyes color and graceful movements...

In a nutshell, get sensors value first, then act later. but since the Arduino loop() is running fast enough, the user will not notice the delay.

Point 5

Since the Arduino is not connected to an external timing source, thus it can't tell the absolute time, so oiO is using the internal timing of Arduino using the function millis() which counts the number of milliseconds since the start/reset of the Arduino. The counter rolls over after aprox. 50 days, more than enough for the life cycle of oiO. This should be enough to calculate for example: 15 min has passed so do this and that, or it has been 60 seconds the user didn't interact with oiO.

For example, this is how oiO will detect that the user didn't interact with him since 1 hr, and accordingly starts dozing off, followed by deep sleep!

...

//oiO will go to sleep if no action by user for more than 'sleepIfNoActionTimer' seconds

if((millis() - lastAction) > sleepIfNoActionTimer) { doseOff(); }

<p>//oiO will sneeze each 'sneezeTimer' min (will not be recorded as a user action)</p>if(millis() % sneezeTimer == 0) { sneeze(); } ...

Controlling the eyes

I've used the Adafruit library as they provide interesting functions to control the NeoPixels, I've added few functions like setEyesColor() and dimmer() to have easy access to setting both eyes colors and also control the dimming on a given color with a given speed and direction.

void dimmer(int a, int b, int red, int green, int blue,int fadeDirection, int stepSpeed)

a : min brightness

b : max brightness

r,g,b : are the RGB color components, each can have a value between 0-255

fadeDirection : can be 1=up,2=down,3=updown

stepSpeed , delay in milliseconds between color fade steps

Conclusion

I highly recommend reading the code and understanding it and modify it as needed. All the variables in the initialization phase of the code (i.e before and inside the setup()) should be set according to your needs.