Kawaii QT

Made by Tongtong Lu, Reggie Raye and N Stone

Found in Ambient Affect

QT the cutie is here to cheer you up!

0
Kawaii QT Bunny
Reginald Raye - https://youtu.be/DnQyP2orqfw
0

QT is an ambient device whose specialty is emoting super cuteness! Like many kawaii characters, QT has the ability to turn a frown upside down, whatever your mood. The device is loosely modeled after a young, hungry bunny. As soon as QT finds out that you're offering her vegetables, she gets very excited - buzzing back and forth in an arc, turning her head, coyly running away and hiding, and glowing her eyes.

Building QT required an immense coordinated effort. Tongtong focused on high level behavior mapping, storyboarding, and vegetable bait design. Nick built out the electronic end of our project, with a special emphasis on ensuring that the servos would run simultaneously at the proper RPMs. Reggie dedicated himself to designing, printing, assembling, and painting QT. 

0
Assembly
Whatsapp image 2017 02 09 at 11.10.06 am
0
Main Code, V1
/*
 * QT
 * Description:
 * Author: Nicholas Stone
 * Date: 1/29/2017
 * Version: 1
 */
const int motor1Pin = D3;    // H-bridge leg 1
const int motor2Pin = D4;    // H-bridge leg 2
const int enablePin = D5;    // H-bridge enable pin
const int phototransPin = A0;
int photoSampleRate = 100;
int light = 0;
int dcMotorStatus = 0;
const int photoLightThres = 2000;
const int bodyLoopCounter = 10;
void setup() {
    Serial.begin(9600);
    // set all the other pins you're using as outputs:
    pinMode(motor1Pin, OUTPUT);
    pinMode(motor2Pin, OUTPUT);
    pinMode(enablePin, OUTPUT);
    //pinMode(phototransPin,INPUT);
    // set enablePin high so that motor can turn on:
    digitalWrite(enablePin, HIGH);
  }
//If switch is high, turn the motor one way by taking one H-bridge pin high and the other low.
//If the switch is low, reverse the direction by reversing the states of the two H-bridge pins.
void loop() {
    int i = 0;
    light = analogRead(phototransPin);
    while(light<photoLightThres) {
      if(dcMotorStatus==0) {
        digitalWrite(enablePin, HIGH);
        dcMotorStatus = 1;
      }
    while(i<bodyLoopCounter && light<photoLightThres) {
      if(i==0){
        digitalWrite(motor1Pin, LOW);   // set leg 1 of the H-bridge low
        digitalWrite(motor2Pin, HIGH);  // set leg 2 of the H-bridge high
      }
      i++;
      light = analogRead(phototransPin);
      delay(photoSampleRate);
      Serial.print("CW Counter Value: ");
      Serial.println(i);
    }
    while(i<2*bodyLoopCounter && light<photoLightThres) {
      if(i==bodyLoopCounter){
        digitalWrite(motor1Pin, HIGH);  // set leg 1 of the H-bridge high
        digitalWrite(motor2Pin, LOW);   // set leg 2 of the H-bridge low
      }
    i++;
    light = analogRead(phototransPin);
    delay(photoSampleRate);
    Serial.print("CCW Counter Value: ");
    Serial.println(i);
  }
      if(i==2*bodyLoopCounter && light<photoLightThres){
          i = 0;
        }
}
if(dcMotorStatus==1)
{
  dcMotorStatus = 0;
  digitalWrite(enablePin, LOW);
}
Serial.print("Light Value: ");
Serial.println(analogRead(phototransPin));
delay(photoSampleRate);
  }
Click to Expand
0

Code V1: Setting the general framework for the motors; monitoring photo resistor; activates motor which changes direction each 10 iterations until the light threshold is acceded

0
Main Code, V2
/*
 * Project: QT
 * Description: A bunny that responds to its master
 * Author: Nicholas Stone
 * Date: 1/29/2017
 * Version: 2
 */
// H-bridge
const int motor1Pin = D3;    // leg 1
const int motor2Pin = D4;    // leg 2
const int enablePin = D5;    // enable pin
// Photoresistor
const int photoPin = A0;
int photoSampleRate = 100;
int light = 0;
// Servo Motor for the head
Servo headServo;
const int headServoPin = D0;
int headServoPos = 10; // avoid the 0/180 poles
//Number of DC cycles per 180 degrees
const int bodyLoopCounter = 10;
const int photoLightThres = 2000;
void setup() {
    Serial.begin(9600);
    //H-bridge
    pinMode(motor1Pin, OUTPUT);
    pinMode(motor2Pin, OUTPUT);
    pinMode(enablePin, OUTPUT);
    //set headServo initial angle
    headServo.attach(headServoPin);
    headServo.write(10);
    // allow motor to turn on if needed:
    digitalWrite(enablePin, HIGH);
  }
void loop() {
    int i = 0;
    light = analogRead(photoPin);
    //loop when no light, ie veggie present
    while(light<photoLightThres) {
      //change code state of motor
      if(digitalRead(enablePin)==LOW)) {
        digitalWrite(enablePin, HIGH);
        //dcMotorStatus = 1;
      }
    while(i<bodyLoopCounter && light<photoLightThres) {
      if(i==0){
        digitalWrite(motor1Pin, LOW);   // set leg 1 of the H-bridge low
        digitalWrite(motor2Pin, HIGH);  // set leg 2 of the H-bridge high
      }
      i++;
      light = analogRead(photoPin);
      delay(photoSampleRate);
      Serial.print("CW Counter Value: ");
      Serial.println(i);
    }
    while(i<2*bodyLoopCounter && light<photoLightThres) {
      if(i==bodyLoopCounter){
        digitalWrite(motor1Pin, HIGH);  // set leg 1 of the H-bridge high
        digitalWrite(motor2Pin, LOW);   // set leg 2 of the H-bridge low
      }
      // turn head aroudn
      if(i>=bodyLoopCounter*1.5 && i>=(bodyLoopCounter*1.5)+1) {
        headServoPos = 170;
        headServo.write(headServoPos);
      }
    i++;
    light = analogRead(photoPin);
    delay(photoSampleRate);
    Serial.print("CCW Counter Value: ");
    Serial.println(i);
  }
      if(i==2*bodyLoopCounter && light<photoLightThres){
          i = 0;
          headServoPos = 25;
          headServo.write(headServoPos);
          delay(1000);
          headServoPos = 10;
          headServo.write(headServoPos);
        }
}
if(digitalRead(enablePin)==HIGH)
{
  digitalWrite(enablePin, LOW);
}
Serial.print("Light Value: ");
Serial.println(analogRead(photoPin));
delay(photoSampleRate);
  }
Click to Expand
0

Code V2: Adds a servo motor that moves independently -- "head" of the device

0
Main Code, V3
/*
 * Project: QT
 * Description: A bunny that responds to its master
 * Author: Nicholas Stone
 * Date: 1/29/2017
 * Version: 3
 */
int veggiePresent = 0;
// H-bridge
// const int motor1Pin = D3;    // leg 1
// const int motor2Pin = D4;    // leg 2
// const int enablePin = D5;    // enable pin
// Servo Motor for the head
int servoDelay = 100;
Servo headServo;
const int headServoPin = D0;
int headServoPos = 10; // avoid the 0/180 poles
// Servo body for driving the body
Servo bodyServo;
const int bodyServoPin = D1;
//value of continuous servo that causes it to remain static
int bodyServoStatic = 92;
//Number of DC cycles per 180 degrees
const int bodyLoopCounter = 20;
// const int photoLightThres = 2000;
static const int bodyForwardSpeedArray[] = {
180,180,180,180,
160,
140,
120,
110,110,110,
120,120,
130,
140,140,140,140,140,140,140
};
void setup() {
    Serial.begin(9600);
    // Thermo Subscription
    Particle.subscribe("diotQTcarrot", foodStatus);
    //H-bridge
    // pinMode(motor1Pin, OUTPUT);
    // pinMode(motor2Pin, OUTPUT);
    // pinMode(enablePin, OUTPUT);
    // allow motor to turn on if needed:
    // digitalWrite(enablePin, HIGH);
    //set Servos initial angle
    headServo.attach(headServoPin);
    headServo.write(10);
    bodyServo.attach(bodyServoPin);
  }
void loop() {
    int i = 0;
    //loop when veggie present
    while(veggiePresent==1) {
      //change code state of motor
      if(digitalRead(enablePin)==LOW) {
        digitalWrite(enablePin, HIGH);
      }
    while(i<bodyLoopCounter && veggiePresent==1) {
      /*if(i==0){
        digitalWrite(motor1Pin, LOW);   // set leg 1 of the H-bridge low
        digitalWrite(motor2Pin, HIGH);  // set leg 2 of the H-bridge high
      }*/
      bodyServo.write(bodyForwardSpeedArray[i])
      delay(servoDelay);
      Serial.print("CW Counter Value: ");
      Serial.println(i);
      i++;
    }
    while(i<2*bodyLoopCounter && veggiePresent == 1) {
      /*if(i==bodyLoopCounter){
        digitalWrite(motor1Pin, HIGH);  // set leg 1 of the H-bridge high
        digitalWrite(motor2Pin, LOW);   // set leg 2 of the H-bridge low
        //bodyServo.write(0);
      }*/
      // turn head around
      if(i>=bodyLoopCounter*1.5 && i>=(bodyLoopCounter*1.5)+1) {
        headServoPos = 170;
        headServo.write(headServoPos);
      }
    bodyServo.write(0);
    delay(servoDelay);
    Serial.print("CCW Counter Value: ");
    Serial.println(i);
    i++;
    }
      if(i==2*bodyLoopCounter && veggiePresent==1){
          i = 0;
          headServoPos = 25;
          headServo.write(headServoPos);
          delay(servoDelay*10);
          headServoPos = 10;
          headServo.write(headServoPos);
        }
    }
  if(digitalRead(enablePin)==HIGH)
  {
    digitalWrite(enablePin, LOW);
    bodyServo.write(bodyServoStatic);
  }
}
//trigger presence of veggie from event publish by other photon
void foodStatus(const char *event,const char *data) {
  if(strcmp(data, "foundVegetable")){
    Serial.println("foundVegetable");
    veggiePresent = 1;
    return;
  }
  if(strcmp(data, "removeVegetable")) {
      Serial.println("removeVegetable");
      veggiePresent = 0;
      return;
      }
      return;
        }
Click to Expand
0

Code V3: Adds smoothing to the motor; replace the brush motor with another servo motor; adds subscription to event published by another photon.

0
Main Code, V4
/*
 * Project: QT
 * Description: A bunny that responds to its master
 * Author: Nicholas Stone
 * Date: 1/29/2017
 * Version: 4
 */
int veggiePresent = 0;
//LED
const int ledOnePin = D2;
const int ledTwoPin = D3;
int brightness = 0;
int fadeAmount = 20;
// H-bridge
// const int motor1Pin = D3;    // leg 1
// const int motor2Pin = D4;    // leg 2
//const int enablePin = D5;    // enable pin
int motionStatus = 0;
// Servo Motor for the head
int servoDelay = 100;
Servo headServo;
const int headServoPin = D0;
int headServoPos = 10; // avoid the 0/180 poles
// Servo body for driving the body
Servo bodyServo;
const int bodyServoPin = D1;
//value of continuous servo that causes it to remain static
int bodyServoStatic = 93;
//Number of DC cycles per 180 degrees
const int bodyLoopCounter = 50;
int i = 0;
static const int bodyForwardSpeedArray[] = {
180,180,180,180,180,180,180,180,180,180,180,172,164,157,149,141,134,126,118,110,103,95,95,95,95,95,
95,95,96,96,97,98,103,107,116,130,130,128,125,121,113,102,95,95,95,45,45,45,135,135,135
};
static const int bodyBackwardSpeedArray[] = {
0,0,0,0,0,0,0,0,0,0,0,8,16,23,31,39,46,54,62,70,77,85,85,85,85,85,85,85,84,84,83,82,77,73,64,50,50,
52,55,59,67,78,85,85,85,135,135,135,45,45,45
};
void setup() {
    Serial.begin(9600);
    // Thermo Subscription
    Particle.subscribe("diotQTcarrot", foodStatus);
    //H-bridge
    // pinMode(motor1Pin, OUTPUT);
    // pinMode(motor2Pin, OUTPUT);
    //pinMode(enablePin, OUTPUT);
    // allow motor to turn on if needed:
    // digitalWrite(enablePin, HIGH);
    //LED
    pinMode(ledOnePin,OUTPUT);
    pinMode(ledTwoPin,OUTPUT);
    //set Servos initial angle
    headServo.attach(headServoPin);
    headServo.write(10);
    bodyServo.attach(bodyServoPin);
  }
void loop() {
    //int i = 0;
    //loop when veggie present
    while(veggiePresent==1) {
      //change code state of motor
      if(motionStatus==0) {
        motionStatus = 1;
      }
    while(i<bodyLoopCounter && veggiePresent==1) {
      /*if(i==0){
        digitalWrite(motor1Pin, LOW);   // set leg 1 of the H-bridge low
        digitalWrite(motor2Pin, HIGH);  // set leg 2 of the H-bridge high
      }*/
      bodyServo.write(bodyForwardSpeedArray[i]);
      delay(servoDelay);
      Serial.print("CW Counter Value: ");
      Serial.println(i);
      glowLed();
      i++;
    }
    while(i<2*bodyLoopCounter && veggiePresent == 1) {
      /*if(i==bodyLoopCounter){
        digitalWrite(motor1Pin, HIGH);  // set leg 1 of the H-bridge high
        digitalWrite(motor2Pin, LOW);   // set leg 2 of the H-bridge low
        //bodyServo.write(0);
      }*/
      // turn head around
      if(i>=bodyLoopCounter*1.5 && i>=(bodyLoopCounter*1.5)+1) {
        headServoPos = 170;
        headServo.write(headServoPos);
      }
    bodyServo.write(bodyBackwardSpeedArray[i-bodyLoopCounter]);
    delay(servoDelay);
    Serial.print("CCW Counter Value: ");
    Serial.println(i);
    glowLed();
    i++;
    }
      if(i==2*bodyLoopCounter && veggiePresent==1){
          //end of loop
          i = 0;
          headServoPos = 25;
          headServo.write(headServoPos);
          delay(servoDelay*10);
          headServoPos = 10;
          headServo.write(headServoPos);
        }
    }
  if(motionStatus == 1)
  {
    motionStatus == 0;
    bodyServo.write(bodyServoStatic);
    //turn off LEDs
    brightness = 0;
    analogWrite(ledOnePin, brightness);
    analogWrite(ledTwoPin, brightness);
  }
}
//trigger presence of veggie from event publish by other photon
void foodStatus(const char *event,const char *data) {
  if(strcmp(data, "foundVegetable")){
    Serial.println("foundVegetable");
    veggiePresent = 1;
    return;
  }
  if(strcmp(data, "removeVegetable")) {
      Serial.println("removeVegetable");
      veggiePresent = 0;
      return;
      }
      return;
        }
void glowLed() {
  analogWrite(ledOnePin, brightness);
  analogWrite(ledTwoPin, brightness);
  brightness = brightness + fadeAmount;
  if (brightness <= 0 || brightness >= 255) {
    fadeAmount = -fadeAmount;
    }
}
Click to Expand
0

Code V4 (Final version): Adds glowing LEDs, improves smoothing; quicken the procedure of rotating; 

0
Detector Code
const int photoResistor = A0;
const int photoSampleRate = 100;
const int photoDiff = 200;
int light = 0;
int firstLight = 0;
int lightStatus = 0; // 0 = no vegetable, 1 = vegetable

void setup () {
  Serial.begin(9600);
  Particle.variable("light", &light, INT);

}

void loop () {
  light = analogRead(photoResistor);
  if(firstLight==0) {
    firstLight = light;
  }

  if(light < firstLight-photoDiff && lightStatus==0){
      Particle.publish("diotQTcarrot", "removeVegetable",1,PUBLIC);
      lightStatus=1;
  }

  if(light >=  firstLight-photoDiff && lightStatus==1){
      Particle.publish("diotQTcarrot", "foundVegetable",1,PUBLIC);
      lightStatus=0;
  }

  delay(photoSampleRate);
}
Click to Expand
0

Detector Code: Publish an event to indicate the light (a.k.a. vegetable offering ) status.

0

Storyboard

  1. Saito was fired by his former boss a month ago, and he failed to find a new job since then.
  2. Saito opened his fridge and took out the only thing left -- A Chinese cabbage.
  3. Saito walked on the street despairingly with his cabbage, trying to find some hope.
  4. Saito accidentally ran into a mysterious magical pet shop which didn't "sell" pets.
  5. The elder monk asked Saito to offer something to attract a little creature.
  6. Saito hesitated for a while and finally decided to offer his cabbage.
  7. QT was appreciated, and started to move around.
  8. Saito felt the cuteness of QT, and got cheered up.
0

Parts

QT Setup

1 Particle Photon

1 5V 180 deg Servo motor

1 5V Continuous Servo motor

2 LEDs (White)

2 220 Ohm Resistors

Wires

Breadboard

QT Outer Enclosure Upper Doom

QT Outer Enclosure Lower Ring

3 Fasteners

3 Wheels

4 Bevel Gears

2 Regular Gears

Inner Chasis

Fork and Steering Column


(Optional)

5V DC Brush Motor with wired H-bridge, replacing Continuous Servo


The Table

1 Particle Photon

1 Plate

1 Stuffed Vegetable (or object)

Box with Top Hole

1 Photoresistor

1 10k Ohm Resistor

Wires

1 Breadboard

x
Share this Project

Courses

49-713 Designing for the Internet of Things

· 26 members

A hands-on introductory course exploring the Internet of Things and connected product experiences.


Focused on
About

QT the cutie is here to cheer you up!

Created

February 6th, 2017