OiO - a Desk Lamp That Has a Soul
by riachi in Circuits > Gadgets
62787 Views, 820 Favorites, 0 Comments
OiO - a Desk Lamp That Has a Soul
His name is "oiO", he is a desk lamp robot, but he thinks he has a soul. Honestly, I can't deny that...
The idea to build oiO, came from my respect to simple object that lay around, that play a big, and usually unnoticed role in making our life easier and maybe more interesting. Think of a pencil, or a note book, think of the personal time you spend using such objects, expressing yourself through them, they perform a clear function, never question your intentions nor your biases, they remain faithful to you until they are consumed.. Well, maybe not any more. It is time for them to express!
I've always been fascinated with objects that can express or conduct emotions, and this is why I though of building a desk lamp that can interact with me, it changes mood, it has behavior, it responds through its senses, to sounds or me approaching it, it obeys, or disobeys, it feels times passing, it sneezes, it sleeps..."it" becomes "he", sometimes "she".
I made oiO with a mindset to make it easy to build, affordable and can be customized. Here I am sharing all the details and code so you can build your own oiO, and raise him or her the way you see fit :)
I am happy to share with you that oiO has won the first prize in the Microcontroller contest back in Nov 2014, sponsored by Instructables, so thanks for all you votes, comments and support :)
You can see below a video of oiO in action. Feel free to write me any comment or question
oiO's friend
OiO's Anathomy - List of Material
oiO needs:
- An Arduino Uno board acting as his brain/heart (~25$)
- Three micro servos, acting as joints (~5$)
- Two NeoPixel mini cells, acting as eyes (~5$)
- A sound sensor, acting as ears (~4$)
- A IR proximity sensor, acting as a spatial sense (~7$)
- A 11.1v 1300mA battery, the food source!
- A rocker switch
- A small USB desk lamp
- Some wires, soldering iron, and small box
- Some plastic or wooden sticks to make the skeleton (I printed mine on a 3D printer..more on this in next step)
The code running on the Arduino makes up his "soul", making sense of all the inputs, and translating them into expressions or reflexes, through oiO's joints and eyes. A step will be dedicated to discuss that.
Take some time to follow the link for each component, and read their specs and usage.
Variations:
Note that you can freely pick any Arduino or servo brands/types, also you can replace the NeoPixels with 2 regular RGB leds, but this will dramatically change the end result and also will require change in design and code, as you will need 6 PWM pins and complicated code to get same result given by the NeoPixels, as the latter uses only 1 pin to control both eyes, giving individual control on each eye, with 24-bit depth color for each!
For the sound and proximity sensor, I used those that gives digital output. This simplifies a lot the code, as there are ICs on board that handle the detection and triggering HIGH/LOW, giving you a ready-to-read values from your Arduino
Let's start building...
The Skeleton
I wanted oiO to be lite and flexible, at the same time to withstand some rough handling, so I opt to design very simple parts and print them on my 3D printer.
You can print them yourself. attached is .rar archive with all the parts in .STL format. I used tinkercad.com, it is a great and super easy webtool to design 3D objects, ready for printing.
From top-to-bottom, left-to-right:
- Base: 24x14mm, dimater 14mm; It attaches to the base servo (servo1)
- LegInToBase: 10x73 mm. it snugs into the base
- UpperLeg, 10x99mm. From one end, it connects to 'LegInToBase' using zip ties. on the other it grabs servo2
- Body: 79x23 mm. Connects servo2 and servo3 together
- Neck: 25x11mm, dimater 11mm hollow. It attaches to the neck servo (servo3)
- Head: I didn't print this one, I just hacked a USB lamp and took the head part from it
I printed all the above using 1.75mm PLA filament, and resolution of 100 Microns, using 'simple printrbot 1405'
Note: In case you want to improve or play around with the parts I designed, I've already shared them as public on tinkercad.com, just go there, create an account (it is free) , then search for oiO, you will find them under my name 'riachi'
Alternatives:
-Feel free to design and print your own parts, just make sure that the length ratio of leg to body doesn't exceed 3:2, and that your servos can handle lifting the skelton and the servos attached to it. Keep in mind, when you attach an arm to servo, the longer the arm, the harder the servo need to work, and there is a point where its torque won't be enough (remember from school, Torque = d x F) :) d being is the distance from far edge to centre of rotation. greater the distance, higher the torque needed.
- If you don't have access to a 3D printer, I suggest to use Balsa wood, used in modeling, very flexible and easy to handle, you can cut it with a cutter, and glue it with fast glue, and it is relatively and acceptably solid.
Lets see how to connect the skeleton parts and the servos..
Downloads
Putting the Skeleton and Components Together
Follow the illustrations to see exactly how I built the skeleton and connected it to the main electronic components. This will be useful only if you used the parts I provided. If you opted for other solutions, then I hope this will be usuful as a rough guide to help you putting the parts together.
The Electronics
Power source:
oiO uses a 11.1 volt 1300mA recharchable battery, mainly used by RC airplanes and cars. With this battery, oiO can run autonomously 13 hour.Later, I am planning to use the sleep mode of Arduino and some other tricks to cut down the power consumption of oiO.
Since I have some energy-hungry components (3 servos and 2 NeoPixels), so I've provided them an external 5v-regulated power source (max 1amp) using 7805 linear voltage regulator. For the Arduino, it is powered also externally, but through its own power regulator. This way I have a total separation between the capacitive loads and the Arduino. I also added a 100mF on the +/- power rail to servos, this will help keeping the voltage steady when the servos do sudden movements. In addition, the Adafruit team recommended a 1000mF capacitor at the NeoPixel's +/- power inputs, so I added this too.
The sound and IR proximity sensors are also powered from the stand-alone regulated 5v source.
Don't forget to connect the external source ground to one of the the Arduino's ground pins
A rocker switch is added between the battery and the reset of circuit. Put the switch on the positive lead of the battery, this to be inline with Adafruit recommendation to always connect the positive side after the ground. and vice-versa when disconnecting)
Outputs controls
Servos:
The 3 servos are controlled with one PWM signal line each, easily provided by Arduino through their Servo.h library. I connected the servos as follows:
Servo1 (leg): pin 3
Servo2 (Waste): pin 5
Servo3 (Head): pin 6
NeoPixels:
The 2 pixels are controlled using only 1 control line (running through a 470 ohm resistor as recommended by Adafruit). the control uses a digital protocol (I2C) and proprietary implementation to control each pixel separately. the end result is that you can easily control one or all pixels at a time, and set very precise and impressive color for each pixel. the power consumption per pixel at 100% brighness is 66mA. (Warning: avoid looking directly into the leds when they are on)
Control of NeoPixel is done through Arduino pin 12
Inputs
IR proximity sensor:
The sensor I used is found usually in robots for simple edge detection (avoid falling, walls...etc). It has a digital output, that is, gives HIGH when an objects is approx. 10cm away, or LOW, if no obstacles found. it doesn't provide distance. it does that using an on-board chip, also a SMD red led will light on the sensor whenever the sensor detects an obstacle.
Proximity sensor output is read by Arduino at pin 8
Sound Sensor:
This sensor I used has also a digital output, meaning it doesn't gives you how load the sound is or its frequency content, it only sets its output to HIGH when a sound is detected, otherwise, the output is set to LOW. You can increase/decrease the sensitivity by turning the on-board knob. Turning it to MAX setting, I am able to make oiO detect a clap anywhere in the room, given that it is a sharp and loud clap (I am planning to build my own amplifier so oiO can respond to more subtle sounds, and also to have a VCG (voltage controlled gain) this will allow me to dynamically increase/decrease oiO's sensitivity to sounds as part of his behavior :) )
The sound sensor output is read by Arduino at analog pin A0. But since this is a digital sensor, you can connect it to any of the Arduino's digital pins and use digitalRead(pin) instead of what I am using now analogRead(A0). Both will work just fine.
Putting all together:
It is your call whether you want to design a PCB to connect all components, simply use a breadboard, or do like what I did. I used a pre-drilled PCB, and soldered all my parts, connectors and wires according, to the connection shown in the design. Then, I used jumper wires to go from this connection board to Arduino, I finally connect the battery, the switch then a 9v barrel connector to hook the Arduino to the battery and put them all in a box (I used the box that came with my cell phone, after I've sprayed it in dark gray:) ) I made some openings for the USB, charger, servo and sensors wiring.
The Code, the Soul
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.
My Next Improvements
Since oiO is still a prototype and I am working towards putting it in production, I still have the below points to address
1) Improve by folds the physical stability of the skeleton upon movements and have a better design and finish
2) Smoother control of servos (possible by getting bigger and better servos, but this affects point 1.
3) reduce power consumption by using sleep mode, and replacing linear regulator with variable ones.
4) Allow the user to choose the standing start position of oiO, that is the position on which it will provide its original function, giving light.
5) Control servos and eyes color simultaneously
6) Connect oiO to the internet, and allow it to use Facebook, Twitter and Gmail, with the same behavioral moody approach. Also allow programming over the air.
7) Add another sound sensor to detect sound source location, and built a VCG amplifier
8) Add a camera with simple edge detection
9) all of the above to be hidden, oiO should remain to have only 1 simple switch as an interface
10) sell ready-to-use or in kits, and keep it open source, hardware and software, for non-commercial uses.
.... sure there is more to show up on my to-do list :)
Conclusions
Building oiO was a lot of fun, and great exploratory experience of what makes an object communicate to us. Also I've learned a lot directly and directly from all the work done by other people in the Arduino and the Makers open world.
I will be working to have more time on my hands, away from my daily job, to improve oiO and give him what he deserves :)
Enjoy reading , and hopefuly building and sharing. Feel free to write any comment or question
Fahed