-
Woodworks
06/05/2017 at 18:20 • 0 commentsBalambér's body was a plastic sour cream box. But working with other projects I have began to learn woodwoking. So I renewed his body.
Here is the original body:
Then I worked with my DIY lathe:
And here the new body of Balambér:
If you were not read this post you you would never recognize the internal changes:
-
From RaspberryPi (Python) to NodeMCU (Arduino)
12/20/2016 at 13:45 • 1 commentBalambér has worked well with the Raspberry Pi, but I needed a lightweight solution as well so as to be able to run Balambér on battery only. So I bought a NodeMcu ESP8266 V3 WIFI Internet Development Board. It is a nice and small arduino compatible board with wifi module so I can continue to give orders to Balambér through a webpage.
Hardware
I followed the simple steps of this guide on instructables.com, and my LED began to blink easily. So I could begin to work with PCA9685 16 Channel PWM Servo motor Driver. Adafruit has an easy to follow step by step guide to use this driver with any arduinoish boards.
I connected the NodeMCU's D4 pin to the drives's SDA, D3 to SCL, and 3V to VCC. This last one is very important as my servos should not consume current from the NodeMCU but from the power supply. And where is GND? I use the same power source (a homemade DC 5V voltage regulator) to power the NodeMCU and the servos. In this way the NodeMCU's GND and the motor driver's GND has already connected.
The Code
As the hardware was ready the time to rewrite the code has come. I installed the ESP8266Wifi module and the Adafruit_PWMServoDrive as I learnt from the guides above.The script after connect2wifi() waits for an user input from the browser. Looking at the serial monitor you can find out the IP address of the NodeMCU. When getHttp() returns with a code of a command, we run the proper command like yes, now, bow, or clap.
Here you can check out the new code. If you have any problem understanding or adapting it, please give me a comment and I am ready to try to help you.
#include <Wire.h> #include <ESP8266WiFi.h> #include <Adafruit_PWMServoDriver.h> const char* ssid = "ssid"; const char* password = "passwordForSsid"; WiFiServer server(80); #define pwmSda D4 #define pwmScl D3 #define servoHeadVertical 0 #define servoHeadHorizontal 2 #define servoLeftArm 3 #define servoRightArm 1 void moveServo(int servo, int servoFrom, int servoTo, int servoDelay) { int servoStep = 5; if(servoTo > servoFrom) { for (uint16_t pulselen = servoFrom; pulselen <= servoTo; pulselen = pulselen + servoStep) { pwm.setPWM(servo, 0, pulselen); delay(servoDelay); } } else { for (uint16_t pulselen = servoFrom; pulselen >= servoTo; pulselen = pulselen - servoStep) { pwm.setPWM(servo, 0, pulselen); delay(servoDelay); } } } void commandYes() { moveServo(servoHeadVertical,440,370,15); moveServo(servoHeadVertical,370,480,15); moveServo(servoHeadVertical,480,440,15); } void commandBow() { moveServo(servoHeadVertical,440,280,80); moveServo(servoHeadVertical,280,440,60); } void commandNo() { moveServo(servoHeadHorizontal,300,450,10); moveServo(servoHeadHorizontal,450,150,10); moveServo(servoHeadHorizontal,150,300,10); } void commandNod() { pwm.setPWM(servoHeadVertical, 0, 410); delay(300); pwm.setPWM(servoHeadVertical, 0, 450); } void commandClap() { int servoStep = 3; int stepCount = 75; for (uint16_t stepstep = 0; stepstep < stepCount; stepstep++) { pwm.setPWM(servoRightArm, 0, 210 + ( stepstep * servoStep) ); pwm.setPWM(servoLeftArm, 0, 170 + (servoStep * stepCount) - (stepstep * servoStep)); delay(2); } for (uint16_t stepstep = stepCount; stepstep > 0; stepstep--) { pwm.setPWM(servoRightArm, 0, 210 + ( stepstep * servoStep) ); pwm.setPWM(servoLeftArm, 0, 170 + (servoStep * stepCount) - (stepstep * servoStep)); delay(2); } } void connect2wifi(int timeout) { WiFi.mode(WIFI_OFF); delay(100); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.println(ssid); Serial.println(password); int endTime = millis() + timeout; while (WiFi.status() != WL_CONNECTED && millis() < endTime ) { delay(500); Serial.print("."); } if(WiFi.status() != WL_CONNECTED) { Serial.println("Could not connect to " + String(ssid) ); commandNo(); return; } else { Serial.println(""); Serial.println("WiFi connected"); // Start the server server.begin(); Serial.println("Server started"); // Print the IP address Serial.println(WiFi.localIP()); commandYes(); } } int getHttp() { WiFiClient client = server.available(); if (!client) { //Serial.println("Server?"); delay(2); return 0; } // Wait until the client sends some data Serial.println("new client"); while(!client.available()){ Serial.println("Waiting the client sends some data."); delay(1); } // Read the first line of the request String req = client.readStringUntil('\r'); Serial.println(req); client.flush(); // Prepare the response String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\nEnjoy:<br/>"; s += "<a href=\"/c/1\">/c/1</a> clap<br/>\r\n"; s += "<a href=\"/c/2\">/c/2</a> bow<br/>\r\n"; s += "<a href=\"/c/4\">/c/4</a> no<br/>\r\n"; s += "<a href=\"/c/8\">/c/8</a> yes<br/>\r\n"; //s += (val)?"high":"low"; s += "</html>\n"; // Match the request int val; if (req.indexOf("/c/1") != -1) val = 1; else if (req.indexOf("/c/2") != -1) val = 2; else if (req.indexOf("/c/4") != -1) val = 4; else if (req.indexOf("/c/8") != -1) val = 8; else { Serial.println("invalid request"); client.print(s); client.stop(); return 0; } client.flush(); // Send the response to the client client.print(s); delay(1); Serial.println("Client disonnected"); return val; // The client will actually be disconnected // when the function returns and 'client' object is detroyed } void servoOff() { pwm.setPWM(servoHeadVertical, 0, 0); pwm.setPWM(servoHeadHorizontal, 0, 0); pwm.setPWM(servoRightArm, 0, 0); pwm.setPWM(servoLeftArm, 0, 0); } void setup() { Wire.begin(pwmSda, pwmScl); //sda scl pwm.begin(); pwm.setPWMFreq(60); Serial.begin(9600); //commandClap(); commandNod(); connect2wifi(5000); servoOff(); } void loop() { int code = 0; code = getHttp(); if(code > 0) { switch (code) { case 8: Serial.println("Yes"); commandYes(); break; case 4: Serial.println("No"); commandNo(); break; case 2: Serial.println("Bow"); commandBow(); break; case 1: Serial.println("Clap"); commandClap(); commandClap(); break; case 6: Serial.println("Connect to " + String(ssid) + "..."); commandNod(); connect2wifi(20000); break; default: Serial.print("\nThere is no command to inputCode " + String(inputCode) + "."); break; } servoOff(); } }
-
From soft(ware) PWM to hard(ware) PWM
11/03/2016 at 15:25 • 0 commentsIn the beginning I had no external PWM module and my Raspberry Pi3 has only 2 native PWM output. So I have used PigPio module as a software solution. It was a good temporary solution even though the PWM had an interference with my room's lights.
So I've ordered a PCA9685 16 Channel PWM Servo motor Driver. The Raspberry Pi can communicate with this cheap module via I2C protocol. So now I have 16 channels. :D
The new module needs new wiring and new code. I followed this post: Adafruit 16 Channel Servo Driver with Raspberry Pi. It was fine and easy. So I moved on to write the new code with extra features like clapping and bowing. (Do not forget to install the Adafruit_PCA9685 as it was written in that adafruit post.) So here my new code:
import time, sys import Adafruit_PCA9685 class Servo: min = 100 max = 480 pwm = Adafruit_PCA9685.PCA9685() pwm = Adafruit_PCA9685.PCA9685(address=0x70, busnum=1) pwm.set_pwm_freq(50) def __init__(self, channel): self.channel = channel pass def setLimits(min, max): self.min = min self.max = min pass def set_pwm(self, on, off): self.pwm.set_pwm(self.channel, on, off) pass def setDegree(self, degree): oneDegree = (self.max - self.min) / 180 self.set_pwm(0, self.min + int(oneDegree * degree)) pass def move(self, degreeFrom, degreeTo, speed = 0.01): if degreeFrom < degreeTo: step = 1 else: step = -1 for degree in range(degreeFrom, degreeTo, step): self.setDegree(degree) time.sleep(speed) pass class Arm(Servo): def __init__(self, channel, limits): self.channel = channel self.limits = limits pass def wave(self): self.move(self.limits[1],self.limits[3], 0.002) self.move(self.limits[3],self.limits[1], 0.004) pass def reset(self): self.setDegree(self.limits[1]) pass class Arms(): def __init__(self): self.rightLimits = [20, 50, 155, 160] # -, normal, clap, full self.leftLimits = [140, 110, 35, 10] self.Left = Arm(3,self.leftLimits) self.Right = Arm(1,self.rightLimits) pass def clap(self, speed = 0): leftDistance = self.leftLimits[1] - self.leftLimits[2] rightDistance = self.rightLimits[2] - self.rightLimits[1] if leftDistance > rightDistance: distance = leftDistance else: distance = rightDistance for count in range(0, distance): self.Right.setDegree(self.rightLimits[1] + count) self.Left.setDegree(self.leftLimits[1] - count) time.sleep(speed) for count in range(distance, 0, -1): self.Right.setDegree(self.rightLimits[1] + count) self.Left.setDegree(self.leftLimits[1] - count) time.sleep(speed) pass def claps(self, quantity): for c in range(0,quantity): self.clap() pass class Head(): def __init__(self): self.Horizontal = Servo(2) self.Vertical = Servo(0) self.horizontalBase = 90 self.verticalBase = 160 pass def shake(self): limits = [40,self.horizontalBase,140] speed = 0.002 self.Horizontal.move(limits[0],limits[2],speed) self.Horizontal.move(limits[2],limits[0],speed) self.Horizontal.move(limits[0],limits[1],speed) pass def nod(self): limits = [self.verticalBase,130] self.Vertical.move(limits[0],limits[1], 0.01) self.Vertical.move(limits[1],limits[0], 0.01) pass def bow(self): limits = [self.verticalBase,100] self.Vertical.move(limits[0],limits[1],0.05) self.Vertical.move(limits[1],limits[0],0.01) pass def reset(self): self.Horizontal.setDegree(self.horizontalBase) self.Vertical.setDegree(self.verticalBase) pass Arms = Arms() Head = Head() if len(sys.argv) > 1: command = sys.argv[1] if command == "no": Head.shake() elif command == "reset": Head.reset() Arms.Left.reset() Arms.Right.reset() elif command == "yes": Head.nod() Head.nod() elif command == "bow": Head.bow() elif command == "right": Arms.Left.wave() elif command == "left": Arms.Right.wave() elif command == "clap": Arms.claps(3) else: print "Invalid command" else: print "Arguments needed"
You can give commands to Balambér through arguments of the command. So he is happy again. :D -
Arms - the easiest is the best
10/12/2016 at 20:42 • 0 commentsNext step to make Balambér's arms to move. First I planned an arm with moving shoulder and elbow. I was thinking about a parallel (but crossed) mechanism. (Thank you @Radomir Dopieralski.) Finally I made a design with two axis but one servo only.
Then I realized that the more simple is the better. So I designed the arms:
So Balambér has two arms now, even though he has a chest missing:
Because of the four servos the new code a little bit more complex. Only a little bit:
import time import pigpio import sys pi = pigpio.pi() GPIONeckHor= 24 NeckHorMin = 1100 NeckHorMid = 1500 NeckHorMax = 2100 GPIONeckVer = 27 NeckVerMin= 1900 NeckVerMid = 2200 NeckVerMax = 2300 GPIORightHand = 22 RightHandMin = 1000 RightHandMid = 1050 RightHandMax = 2000 GPIOLeftHand = 23 LeftHandMin = 1000 LeftHandMid = 2000 LeftHandMax = 2100 speed = 0.005 def shake(gpio,min,mid,max): for x in xrange(mid, min, -10): pi.set_servo_pulsewidth(gpio,x) time.sleep(speed) pass for x in xrange(min,max,10): pi.set_servo_pulsewidth(gpio, x) time.sleep(speed) pass for x in xrange(max,mid,-10): pi.set_servo_pulsewidth(gpio, x) time.sleep(speed) pass pass if len(sys.argv) > 1: command = sys.argv[1] if command == "-h" or command == "--help": print "Example: no, yes, right, left" sys.exit() #SET command elif command == "no": shake(GPIONeckHor,NeckHorMin,NeckHorMid,NeckHorMax) elif command == "yes": shake(GPIONeckVer,NeckVerMin,NeckVerMid,NeckVerMax) elif command == "right": shake(GPIORightHand,RightHandMin,RightHandMid,RightHandMax) elif command == "left": pi.set_servo_pulsewidth(GPIONeckVer, NeckVerMid) shake(GPIOLeftHand,LeftHandMin,LeftHandMid,LeftHandMax) else: error("Invalid command") else: print "Arguments needed" pi.set_servo_pulsewidth(GPIONeckVer, 0) pi.set_servo_pulsewidth(GPIONeckHor, 0) pi.set_servo_pulsewidth(GPIOLeftHand, 0) pi.set_servo_pulsewidth(GPIORightHand, 0)
And a short video about the movement of Balambér:
-
Headbang - The second servo
10/04/2016 at 21:46 • 0 commentsWith more 3D designing and printing, I put in work the second servo as well. Balambér is now able to say yes (shaking his head) or no (headbanging).
This is the not-elaborated python code:
import time import pigpio pi = pigpio.pi() GPIOHor= 23 HorMin = 1000 HorMid = 1500 HorMax = 2000 GPIOVer = 24 VerMin= 1800 VerMid = 2200 VerMax = 2300 speed = 0.01 pi.set_PWM_range(GPIOHor, 2500) pi.set_PWM_range(GPIOVer, 2500) pi.set_servo_pulsewidth(GPIOHor, 0) pi.set_servo_pulsewidth(GPIOVer, 0) def shake(gpio,min,mid,max): for x in xrange(mid, min, -10): pi.set_servo_pulsewidth(gpio,x) time.sleep(speed) pass for x in xrange(min,max,10): pi.set_servo_pulsewidth(gpio, x) time.sleep(speed) pass for x in xrange(max,mid,-10): pi.set_servo_pulsewidth(gpio, x) time.sleep(speed) pass pass shake(GPIOHor,HorMin,HorMid,HorMax) shake(GPIOHor,HorMin,HorMid,HorMax) shake(GPIOHor,HorMin,HorMid,HorMax) time.sleep(0.5) shake(GPIOVer,VerMin,VerMid,VerMax) shake(GPIOVer,VerMin,VerMid,VerMax) shake(GPIOVer,VerMin,VerMid,VerMax) time.sleep(1) pi.set_servo_pulsewidth(GPIOVer, 0) pi.set_servo_pulsewidth(GPIOHor, 0)
And the video for those who has read this log:
-
Balambér's Neck and My First Servo Motors
10/04/2016 at 12:15 • 0 commentsMy new SG90 Micro Servo motors have arrived, so I can begin to work.
1. Mechanics
I would like to make Balambér able to turn around his had. So I designed a small attachment that connects the SG90's "shaft' with the empty neck of Balambér. The small 3D printed part makes its job pretty well. (I will share the STL files later on.)
2. Electronics
The servos ask for PWM but the Raspberry Pi has only one (or two) of these outputs. So I use the "poor man's PWM": the PigPio.
2 .1. Cables and connections
The outputs of the Raspberry Pi is very week, so I feed the servo with 5V from external source. (Do not forget to connect your external source's ground to one of the Raspberry Pi's ground.) Then I connected the servo's PWM line (orange) to the RPi's GPIO 23 (pin 16). (Yes, you can choose any of the normal GPIO-s.)
2. 2. Software
I downloaded and installed the PigPio library as it was told on its website. Then I had to begin with:
sudo pigpiod
I made a very simple but working python example how to turn his head around.
import time import pigpio GPIO = 23 pi = pigpio.pi() for x in xrange(50,250): pi.set_servo_pulsewidth(GPIO, x * 10) time.sleep(0.2) pass pi.set_servo_pulsewidth(GPIO, 0)
Please send me a message if you find problems with this code.
And now, let's work on the head-bang servo. :D