49-713 Designing for the Internet of Things
· 26 members
A hands-on introductory course exploring the Internet of Things and connected product experiences.
Let you keep track of how your youtube channel was doing.
From the 5 universal emotions described in the Atlas of Emotions, we chose to invoke the enjoyment emotion. We wanted to design our device to invoke sensory pleasure(using nice LED lights and pleasing sounds along with an aesthetic look and feel), amusement / wonder (using the organic movement of the neck).
We got inspired from the technology snake robots use to maneuver challenging terrains. They have individual actuators that look like caps with three holes parallel to its axis on the periphery of the actuator. We can connects number of those actuators together using strings and manipulating the strings can bring different movements to the snake robot structure. This motions produced look organic and the manipulation of the neck could be done using servos. We borrowed some individual link actuators from a Robotics Institute PhD student to make our neck portion of the ostrich.
We went through brainstorming sessions to finalize on the snake robot technology to implement our ostrich neck, another idea was to use a series of strings connected to servos to manipulate the rise of fall of various points in the neck (team felt this was more complicated than the snake robot idea). For the outer casing that represents the ostrich body, we initially tried to use foam boards to cut layers to create a concave shell, but could not get the board to cut in proper shape. So for the next iteration used compressed wood board and laser cutter. We designed the body of ostrich such that the servo could be easily housed and created the shapes needed in Adobe illustrator and used Rabbit Laser cutter in ideate lab to cut the board. We resourced boards from wastage to cut our parts. For the iterations on the display modes of information, we initially decided to make the neck move up when the likes to dislikes ration increases and based on the necks position the led on the head of ostrich turns to a different color. Then we realised we can make it more interesting and can inform the user in a mild way with sounds from a piezo if there is a big jump on likes in the past hour.
After we cut our parts using the laser cutter, we assembled the outer casing of the ostrich body. Then we soldered wires to RGB LED and inserted them into the snake robot neck. At this point we got 4 wires coming from the LED and 3 threads holding the shape of the ostrich neck. For the ostrich neck movement we wanted in Z axis only, we sealed the movement of two of 3 strings and connected the remaining to the servo. The servo pulling the string will make the ostrich neck to move in Z axis. The wires from the LED was passed on through the ostrich body casing without disturbing the servo and provide it to the breadboard. Then we fastened the starting of the ostrich neck to the body. After this physical model was setup, we tested this with the code developed in parallel with an rough skeleton version of the setup. Development of code happened in stages. Testing this code with the physical prototype worked well and we could see the neck rise up with color changes when we linked it with a trending controversial super bowl 2017 commercial.
import sys
import urllib.request, urllib.response, urllib.error
from bs4 import BeautifulSoup
import re
from apiclient.discovery import build
from oauth2client.tools import argparser
import requests
flag = 0;
dLIKE = 0 ;
dDis = 0;
like = [];
dis = [];
like.append(0);
dis.append(0);
class SoupScrape:
def GetStats(self, code):
#
global originalRatio
global ratio
global factor
video_url = 'http://www.youtube.com/watch?v=' + code
startpage = urllib.request.urlopen(video_url)
soup = BeautifulSoup(startpage.read(), "lxml")
# Get Number of Views
rawview_data = soup.find('div', {'class': 'watch-view-count'})
view_number = rawview_data.contents[0]
view_number = re.sub('[^0-9]', '', view_number)
# Get Number of Likes and Dislikes
raw_like = soup.find('button', {'class': 'like-button-renderer-like-button'})
raw_dislike = soup.find('button', {'class': 'like-button-renderer-dislike-button'})
# Likes
likes = raw_like.contents[0]
for data in likes:
num_of_likes = data
# Dislikes
dislikes = raw_dislike.contents[0]
for data in dislikes:
num_of_dislikes = data
#video name
raw_name=soup.find('span', {'class': 'watch-title'})
video_name=raw_name.contents[0]
video_name=video_name.replace('\n', '')
#print(video_name,"-","VIEWS",view_number, "-", "LIKES", num_of_likes, "-","DISLIKES", num_of_dislikes,'\n')
num_of_likes = int(num_of_likes.replace(",", ""))
num_of_dislikes = int(num_of_dislikes.replace(",", ""))
like.append(num_of_likes);
dis.append(num_of_dislikes);
dLike= like[-1]- like[-2]
dDis= dis[-1]- dis[-2]
if (dLike<0):
dDis = dDis-dLike
elif (dDis<0):
dLike = dLike-dDis
print(dLike, dDis)
if (dLike == 0):
if (dDis == 0):
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/neu", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': dDis})
else:
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/dis", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': dDis})
elif (dDis == 0):
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/like", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': dLike})
else:
if (dLike >100):
originalRatio = (num_of_likes/num_of_dislikes)
factor = ((num_of_likes+1)/num_of_likes)
print("originalRatio, factor")
print(originalRatio,factor)
else:
if (dLike/dDis == originalRatio):
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/neu", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': dDis})
elif (dLike/dDis > originalRatio):
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/like", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': dLike})
elif (dLike/dDis < originalRatio):
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/dis", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': dDis})
else:
print("error")
"""
if (dLike == dDis):
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/neu", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': dDis})
elif (dLike > dDis):
if (dLike >100):
originalRatio = (num_of_likes/num_of_dislikes)
factor = ((num_of_likes+1)/num_of_likes)
print("originalRatio, factor")
print(originalRatio,factor)
else:
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/like", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': dLike})
elif (dLike < dDis):
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/dis", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': dDis})
"""
ratio = (num_of_likes/num_of_dislikes)
print('ratio, originalRatio')
print(ratio,originalRatio)
if (ratio/originalRatio >= factor):
#if the increase is more than a certain percent, call function youtube to pull the trigger to LIFT UP
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/youtube", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': ratio})
#set the baseline to this new ratio
originalRatio = ratio
print("newbaseline-higher")
print(originalRatio)
elif (ratio/originalRatio <= (2-factor)):
#if the decrease is more than a certain percent, call function youtube2 to pull the trigger to PULL DOWN
r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/youtube2", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': ratio})
#set the baseline to this new ratio
originalRatio = ratio
print("newbaseline-lower")
print(originalRatio)
#r = requests.post("https://api.particle.io/v1/devices/22002b001351353432393433/youtube2", data={'access_token': 'ada24d2045a440d9f975c0861224febe6b414065', 'args': num_of_dislikes})
#print(r.text)
#a=r.text.split(',')
#b=a[3].split(':')[1]
#b=b[:-1]
#b= int(b)
#print(b)
DEVELOPER_KEY = "AIzaSyDXvzq3fviM_WaXeuoqpLUK74ezsS9LzOc"
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"
# Youtube Search Functions
def youtube_search(search_string, max_results=25):
input = search_string.q
print(input)
outfile = open('temp.txt', 'w')
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=DEVELOPER_KEY)
search_response = youtube.search().list(q=search_string.q, part="id,snippet", maxResults=max_results).execute()
videos = []
# Add each result to the appropriate list, and then display the lists of
# matching videos
for search_result in search_response.get("items", []):
if search_result["id"]["kind"] == "youtube#video":
videos.append("%s (%s)" % (search_result["snippet"]["title"], search_result["id"]["videoId"]))
for x in range(0, len(videos)):
outfile.write(videos[x] + '\n')
outfile.close()
def search(artist):
argparser.add_argument("--q", help="Search term", default=artist)
argparser.add_argument("--max-results", help="Max results", default=5)
args = argparser.parse_args()
youtube_search(args)
# Main Program
if __name__ == "__main__":
#
# Youtube
#
while (flag != -1):
stat_fetch = SoupScrape()
stat_fetch.GetStats('HVyq6kAN6u4')
Click to Expand
Servo myservo;// create servo object using the built-in Particle Servo Library
int servoPin = D0; //declare variable for servo
int speakerPin = D3;
int command_int; //variable to keep track number of times the senser detects magnet
extern int original = 110;
int redPin = A4; // RED pin of the LED to PWM pin **A4**
int greenPin = D1; // GREEN pin of the LED to PWM pin **D1**
int bluePin = D2; // BLUE pin of the LED to PWM pin **D2**
int redValue = 255; // Full brightness for an Cathode RGB LED is 0, and off 255
int greenValue = 255; // Full brightness for an Cathode RGB LED is 0, and off 255
int blueValue = 255; // Full brightness for an Cathode RGB LED is 0, and off 255</td>
int buzzertone[] = {2273, 2000};
void setup()
{
//Sensor setup
Serial.begin(9600);
Particle.function("youtube", youtube);
Particle.function("youtube2", youtube2);
Particle.function("like", like);
Particle.function("dis", dis);
Particle.function("neu", neu);
//LED setup
// Set up our pins for output
pinMode( redPin, OUTPUT);
pinMode( greenPin, OUTPUT);
pinMode( bluePin, OUTPUT);
pinMode( speakerPin, OUTPUT );
// turn them all off...
analogWrite(redPin, redValue);
analogWrite(greenPin, greenValue);
analogWrite(bluePin, blueValue);
//Servo setup
analogWrite(greenPin, 50);
delay(500);
analogWrite(greenPin, 255);
delay(500);
myservo.attach(D0); //Initialize the servo attached to pin D0
myservo.write(0); //set servo to initial position
delay(5000); //delay to give the servo time to move to its position
analogWrite(redPin, 50);
delay(500);
analogWrite(redPin, 255);
delay(500);
//myservo.detach(); //detach the servo to prevent it from jittering
myservo.write(100); //set servo to initial position
delay(2500);
}
void loop()
{
}
// this function automagically gets called upon a matching POST request
//ratio baseline has been LIFTED
int youtube(String command){//turns in a ratio of num_like/num_dislike
/*Serial.println(command);
command_int = atoi(command);
Serial.println(command_int); //ratio number
return 10;*/
uint16_t i;
analogWrite(redPin, 50);
delay(500);
analogWrite(redPin, 255);
delay(500);
for (i=0; i< 5;i++){
if((10*i+original)<=160){
myservo.attach(servoPin);
myservo.write((10*i+original));
delay(500);
myservo.detach();
}
else{
myservo.attach(servoPin);
myservo.write(158);
delay(500);
myservo.detach();
}
}
if (i == 5){
if(original<=110){
myservo.attach(servoPin);
myservo.write((10*i+original));
delay(500);
Serial.println("baseline-raised");
Serial.println(original);
original = original+10*i;
Serial.println(original);
myservo.detach();
}
else{
myservo.attach(servoPin);
myservo.write(158);
delay(500);
myservo.detach();
original = 160;
Serial.println(original);
}
}
}
//ratio baseline has been DROPPED
int youtube2(String command){
uint16_t j;
analogWrite(bluePin, 50);
delay(500);
analogWrite(bluePin, 255);
delay(500);
for (j=0; j< 5;j++){
myservo.attach(servoPin);
myservo.write((-j*10+original));
delay(500);
myservo.detach();
}
if (j == 5){
if(original>=50){
myservo.attach(servoPin);
myservo.write((-j*10+original));
delay(500);
Serial.println("baseline-dropped");
Serial.println(original);
original = original-10*j;
Serial.println(original);
myservo.detach();
}
else{
myservo.attach(servoPin);
myservo.write(0);
delay(500);
myservo.detach();
original = 0;
Serial.println(original);
}
}
}
int like(String command){
Serial.println("like");
analogWrite(redPin, 200);
delay(500);
analogWrite(redPin, 255);
delay(200);
}
int dis(String command){
Serial.println("dislike");
analogWrite(bluePin, 200);
analogWrite(greenPin, 250);
delay(500);
analogWrite(bluePin, 255);
analogWrite(greenPin, 255);
delay(200);
}
int neu(String command){
analogWrite(bluePin, 200);
analogWrite(greenPin, 200);
analogWrite(redPin, 200);
delay(500);
analogWrite(bluePin, 255);
analogWrite(greenPin, 255);
analogWrite(redPin, 255);
delay(200);
Serial.println("neutral");
}
Click to Expand
We as a team worked on a new design to create an ambient device that looks beautiful and moves organically to display some information you care about. We had some roadblocks while creating the setup, but overall we think it was a success in terms of the technical achievement and using unique materials. Each one of us learnt something new. The next steps could be to make the ostrich neck more natural looking and also the movement more organic. Maybe we can control the neck better by manipulating all the three threads instead of just one. The size of the ostrich base was also bigger and little disproportional, we can fix that in next iteration. We also burnt few servos and we need to figure out how to avoid that situation.
A hands-on introductory course exploring the Internet of Things and connected product experiences.
Let you keep track of how your youtube channel was doing.
February 9th, 2017