├── doc ├── datasheets │ ├── P232CS14.pdf │ ├── P234CS24.pdf │ └── apw7145.pdf └── keepon_reverse_engineering.asciidoc ├── src ├── processing │ └── keepon_kinect_control │ │ ├── data │ │ └── FreeSans-18.vlw │ │ └── keepon_kinect_control.pde ├── control │ └── keepoff.json ├── python │ ├── keepoff.py │ └── keepoff_osc.py └── arudino │ ├── keepon_motor_status │ └── keepon_motor_status.pde │ └── keepon_i2c_comm │ └── keepon_i2c_comm.pde └── README.md /doc/datasheets/P232CS14.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qdot/keepoff/HEAD/doc/datasheets/P232CS14.pdf -------------------------------------------------------------------------------- /doc/datasheets/P234CS24.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qdot/keepoff/HEAD/doc/datasheets/P234CS24.pdf -------------------------------------------------------------------------------- /doc/datasheets/apw7145.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qdot/keepoff/HEAD/doc/datasheets/apw7145.pdf -------------------------------------------------------------------------------- /src/processing/keepon_kinect_control/data/FreeSans-18.vlw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qdot/keepoff/HEAD/src/processing/keepon_kinect_control/data/FreeSans-18.vlw -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | KEEPOFF IS DEPRECATED 2 | ===================== 3 | 4 | Use [http://www.github.com/beatbots/MyKeepon](http://www.github.com/beatbots/MyKeepon), which actually works. :) 5 | -------------------------------------------------------------------------------- /src/control/keepoff.json: -------------------------------------------------------------------------------- 1 | loadedInterfaceName = "keepoff"; 2 | interfaceOrientation = "landscape"; 3 | 4 | pages = [ [ 5 | { 6 | "name": "Rotate", 7 | "type": "Knob", 8 | "width":1, "height":1, 9 | "x":0, "y":0, 10 | "centerZero": true, 11 | "min": 0, "max": 255, 12 | "address" : "/rotate" 13 | }, 14 | { 15 | "name":"BackForth", 16 | "type":"Slider", 17 | "x":.25, "y":0, 18 | "isVertical": true, 19 | "width":.25, "height":.75, 20 | "min": 0, "max": 255, 21 | "address" : "/bf" 22 | } 23 | ], 24 | 25 | ]; -------------------------------------------------------------------------------- /src/python/keepoff.py: -------------------------------------------------------------------------------- 1 | import serial 2 | import sys 3 | 4 | def main(): 5 | s = serial.Serial('/dev/ttyACM1', 115200, timeout=2) 6 | print("Waiting for boot signal") 7 | print(s.read()) 8 | print("Writing sway command") 9 | s.write("".join(map(chr, [0x55, 0x0, 0x3, 0, 2, 72]))) 10 | print(s.read()) 11 | print("Reading motor encoders") 12 | s.write("".join(map(chr, [0x55, 0x1, 0x12]))) 13 | print(["0x%.02x " % ord(x) for x in s.read(12)]) 14 | 15 | if __name__ == '__main__': 16 | sys.exit(main()) -------------------------------------------------------------------------------- /src/arudino/keepon_motor_status/keepon_motor_status.pde: -------------------------------------------------------------------------------- 1 | // Wire Master Writer 2 | // by Nicholas Zambetti 3 | 4 | // Demonstrates use of the Wire library 5 | // Writes data to an I2C/TWI slave device 6 | // Refer to the "Wire Slave Receiver" example for use with this 7 | 8 | // Created 29 March 2006 9 | 10 | // This example code is in the public domain. 11 | 12 | 13 | #include 14 | 15 | void setup() 16 | { 17 | Serial.begin(115200); 18 | 19 | } 20 | 21 | byte x = 0; 22 | 23 | void loop() 24 | { 25 | Serial.print("Waiting for power on"); 26 | while(analogRead(0) < 512); 27 | Serial.print("Pulling to ground"); 28 | pinMode(A4, OUTPUT); 29 | digitalWrite(A4, LOW); 30 | pinMode(A5, OUTPUT); 31 | digitalWrite(A5, LOW); 32 | delay(1000); 33 | Serial.print("Floating"); 34 | pinMode(A4, OUTPUT); 35 | digitalWrite(A4, HIGH); 36 | pinMode(A5, OUTPUT); 37 | digitalWrite(A5, HIGH); 38 | Serial.print("Starting comms"); 39 | Wire.begin(); // join i2c bus (address optional for master) 40 | while(analogRead(0) > 512) 41 | { 42 | Serial.println("Running loop"); 43 | Wire.requestFrom(85, 12); 44 | while(Wire.available()) 45 | { 46 | Serial.println((int)Wire.receive()); 47 | } 48 | delay(500); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/python/keepoff_osc.py: -------------------------------------------------------------------------------- 1 | import serial 2 | import sys 3 | import math 4 | from OSC import OSCServer 5 | 6 | s = serial.Serial('/dev/ttyACM1', 115200) 7 | 8 | # this method of reporting timeouts only works by convention 9 | # that before calling handle_request() field .timed_out is 10 | # set to False 11 | def handle_timeout(self): 12 | self.timed_out = True 13 | 14 | def rotate_callback(path, tags, args, source): 15 | s.write("".join(map(chr, [0x55, 0x0, 0x3, 4, int(args[0]), 160]))) 16 | print(s.read()) 17 | print math.floor(args[0]) 18 | 19 | def bf_callback(path, tags, args, source): 20 | s.write("".join(map(chr, [0x55, 0x0, 0x3, 2, int(args[0]), 160]))) 21 | print(s.read()) 22 | print math.floor(args[0]) 23 | 24 | def quit_callback(path, tags, args, source): 25 | # don't do this at home (or it'll quit blender) 26 | global run 27 | run = False 28 | 29 | def main(): 30 | print("Waiting for boot signal") 31 | print(s.read()) 32 | print("Writing sway command") 33 | s.write("".join(map(chr, [0x55, 0x0, 0x3, 0, 2, 72]))) 34 | print(s.read()) 35 | # print("Reading motor encoders") 36 | # s.write("".join(map(chr, [0x55, 0x1, 0x12]))) 37 | # print(["0x%.02x " % ord(x) for x in s.read(12)]) 38 | server = OSCServer( ("192.168.123.75", 10000) ) 39 | server.timeout = 0 40 | # funny python's way to add a method to an instance of a class 41 | import types 42 | server.handle_timeout = types.MethodType(handle_timeout, server) 43 | 44 | server.addMsgHandler( "/rotate", rotate_callback ) 45 | server.addMsgHandler( "/bf", bf_callback ) 46 | 47 | try: 48 | while 1: 49 | server.handle_request() 50 | except KeyboardInterrupt: 51 | pass 52 | 53 | 54 | server.close() 55 | 56 | if __name__ == '__main__': 57 | sys.exit(main()) -------------------------------------------------------------------------------- /src/arudino/keepon_i2c_comm/keepon_i2c_comm.pde: -------------------------------------------------------------------------------- 1 | #include 2 | #define cbi(sfr, bit) _SFR_BYTE(sfr) &= ~_BV(bit) 3 | #define sbi(sfr, bit) _SFR_BYTE(sfr) |= _BV(bit) 4 | 5 | void setup() 6 | { 7 | Serial.begin(115200); 8 | } 9 | 10 | void twi_close() 11 | { 12 | // de-activate internal pull-up resistors 13 | cbi(PORTD, 0); 14 | cbi(PORTD, 1); 15 | sbi(TWSR, TWPS0); 16 | sbi(TWSR, TWPS1); 17 | sbi(TWCR,TWINT); 18 | sbi(TWCR,TWSTO); 19 | cbi(TWCR,TWEA); 20 | cbi(TWCR,TWSTA); 21 | cbi(TWCR,TWWC); 22 | sbi(TWCR,TWEN); 23 | cbi(TWCR,TWIE); 24 | //cbi(PRR0,PRTWI); 25 | } 26 | 27 | void bootup() 28 | { 29 | if(analogRead(0) > 512) { 30 | Wire.begin(); 31 | Serial.write((uint8_t)0); 32 | return; 33 | } 34 | pinMode(A4, OUTPUT); 35 | digitalWrite(A4, LOW); 36 | pinMode(A5, OUTPUT); 37 | digitalWrite(A5, LOW); 38 | while(analogRead(0) < 512); 39 | delay(1000); 40 | // Bringing the lines back up causes some keepons to 41 | // boot into normal mode. Commenting out for now. 42 | //pinMode(A4, OUTPUT); 43 | //digitalWrite(A4, HIGH); 44 | //pinMode(A5, OUTPUT); 45 | //digitalWrite(A5, HIGH); 46 | Wire.begin(); 47 | Serial.write((uint8_t)0); 48 | } 49 | 50 | void shutdown() { 51 | //twi_close(); 52 | } 53 | 54 | void loop() { 55 | int i, device, dir, amount_read, msg_size, msg[100]; 56 | bootup(); 57 | while(analogRead(0) > 512) { 58 | amount_read = 0; 59 | while(!Serial.available() > 0); 60 | device = Serial.read(); 61 | while(!Serial.available() > 0); 62 | dir = Serial.read(); 63 | while(!Serial.available() > 0); 64 | msg_size = Serial.read(); 65 | if(dir == 0) { 66 | while(msg_size > amount_read) { 67 | while(!Serial.available() > 0); 68 | msg[amount_read] = Serial.read(); 69 | amount_read++; 70 | } 71 | Wire.beginTransmission(device); 72 | for(i = 0; i < msg_size; ++i) { 73 | Wire.send(msg[i]); 74 | } 75 | Serial.println((int)Wire.endTransmission()); 76 | } 77 | else { 78 | Wire.requestFrom(device, msg_size); 79 | while(Wire.available() < msg_size); 80 | Serial.write((uint8_t)0); 81 | for(i = 0; i < msg_size; ++i) { 82 | Serial.write(Wire.receive()); 83 | } 84 | } 85 | } 86 | shutdown(); 87 | } 88 | -------------------------------------------------------------------------------- /doc/keepon_reverse_engineering.asciidoc: -------------------------------------------------------------------------------- 1 | = Keepon Reverse Engineering Document = 2 | 3 | == Author Info 4 | 5 | Kyle Machulis 6 | v1.1, Nov 14, 2011 7 | 8 | == Description == 9 | 10 | Keepoff is a project to reverse engineer with BeatBots/WowToys 11 | MyKeepon Dancing robot. 12 | 13 | http://mykeepon.com/ 14 | 15 | == Hardware == 16 | 17 | === Board Image === 18 | 19 | An annotated image of the circuit board is available at 20 | 21 | http://www.flickr.com/photos/qdot76367/6296272375/in/photostream 22 | 23 | === Sensors === 24 | 25 | * Microphone 26 | * Buttons 27 | * Servos (encoders?) 28 | 29 | === Processors and Components === 30 | 31 | * Padauk P234CS24 - http://www.jaolen.com/UploadFile/Content/2010091911223399.pdf 32 | * Padauk P232CS14 - http://www.padauk.com.tw/admin/includes/getfile.php?file_name=ZmlsZTEuUDIzMl9EUzAyMUUucGRm 33 | * APW7145 - http://www.lierda.com/upload/editor/20081212/1229063275.pdf 34 | 35 | Pinouts: 36 | 37 | P232CS14: 38 | * PIN1 - I2C CLK 39 | * PIN14 - I2C DATA 40 | 41 | P234CS24: 42 | * PIN11 - I2C CLK 43 | * PIN24 - I2C DATA 44 | 45 | === Buttons === 46 | 47 | * Front (Touch/Music) 48 | * Body (Head, Sides) 49 | 50 | == Line Protocols == 51 | 52 | The two processors in the Keepon robot communicate with each other via 53 | the I2C protocol. A set of pads to watch communication between the 54 | chips is available on the circuit board, in the corner near the happy 55 | face. 56 | 57 | The communication is 0V (GND) Low, +3.3V High. However, the internal 58 | microcontrollers are 5v tolerant. 59 | 60 | To be able to control the system using the commands listed below, the 61 | microcontroller that works as the master node must be put into slave 62 | mode. This is done by pulling the I2C data and clock lines to ground 63 | for 1 second while the keepon is powering up or coming out of sleep. 64 | After this, the bus can be controlled by outside systems. 65 | 66 | == Devices and Commands == 67 | 68 | The following device addresses are available: 69 | 70 | * 0x52 - Sound bank access/playing 71 | * 0x55 - Servos for turning whole unit? 72 | 73 | === Device Sleep === 74 | 75 | Assuming no interaction takes place for 3 minutes, the robot will put 76 | itself to sleep. This will cause the data/clock lines on the I2C bus 77 | to both drop to ground. 78 | 79 | === Device Wakeup === 80 | 81 | To wake up the device from sleep mode, pull the V line on the exposed 82 | I2C pads to ground. 83 | 84 | === Servos === 85 | 86 | * Device Address: 0x55 87 | 88 | Keepon has 4 servos: The main base rotation, right/left body bend, 89 | forward/back body bend, and up/down "squat". 90 | 91 | Messages written to the motors are as follows: 92 | 93 | ------ 94 | 0x55 0xAA 0xBB 0xCC 95 | ------ 96 | 97 | * 0xAA - servo index. 98 | * 0xBB - current servo position 99 | * 0xCC - unknown? Seems to be only 255 or single bit values (powers of 2)? 100 | 101 | ==== Main Rotation Base ==== 102 | 103 | * Motor Index: 0x04 104 | * Servo Position: 0xBB 105 | * Movement Speed: 0xCC 106 | 107 | ==== Forward/Back Bend ==== 108 | 109 | * Motor Index: 0x02 110 | * Servo Position: 0xBB 111 | * Movement Speed: 0xCC 112 | 113 | ==== Up/Down Squat and Right/Left Bend ==== 114 | 115 | * Motor Index: 0x00 116 | * Bend: 0xBB < 0x80 117 | * Squat: 0xBB > 0x80 118 | * Speed: 0xCC 119 | 120 | === Motor Macros === 121 | 122 | Unlike the 0x00/2/4 device addresses for motors, the 0x6 address takes 123 | a single byte. It seems to have certain motor "macros" programmed into 124 | these values. The valid values for this byte are: 125 | 126 | * Motor Index: 0x06 127 | * 0x20 128 | * 0x21 129 | * 0x22 130 | * 0x23 131 | * 0x24 132 | * 0xA0 133 | * 0xA1 134 | * 0xA2 135 | * 0xA3 136 | 137 | ==== Motor Status ==== 138 | 139 | Status about the position of motors (rotation and front/back only, the 140 | up/down and side to side swaying motors do not seem to have encoders?) 141 | is retreived by requesting 13 bytes from the 0x55 device. 142 | 143 | * Byte 0x00 - Unknown 144 | * Byte 0x01 - Unknown 145 | * Byte 0x02 - Unknown 146 | * Byte 0x03 - Unknown 147 | * Byte 0x04 - Unknown 148 | * Byte 0x05 - &'d against the following bits 149 | ** 0x0X - Up/Down Position (for Up/Down motor) - Range Unknown? (2-bit?) 150 | ** 0xE0 - Leaning Left (for Left/Right sway) - Boolean? 151 | ** 0xF0 - Leaning Right (for Left/Right sway) - Boolean? 152 | * Byte 0x06 - Unknown 153 | * Byte 0x07 - Unknown 154 | * Byte 0x08 - Unknown 155 | * Byte 0x09 - Unknown 156 | * Byte 0x0A - Unknown 157 | * Byte 0x0B - Front/Back Encoder 158 | * Byte 0x0C - Base Rotation Encoder 159 | 160 | 161 | === Sound Banks === 162 | 163 | * Device Index: 0x52 164 | * Message Format: 0x52 0x01 0xAA 165 | ** 0x91 - Up Wakeup Sound 166 | ** 0x92 - Down Wakeup Sound 167 | ** 0x95 - Yawn Down 168 | ** 0x96 - Initial Boot Sound? 169 | ** 0x97 - Sigh 170 | ** 0x9A - Yawn Up 171 | ** 0x9B - Sleep (Sound played before turning off) 172 | ** 0x9C - Chirp 173 | ** 0xAC - Whine 174 | ** 0xBC - Beep noise when head hit 175 | ** 0xBD - Up/Down noise when squatting 176 | ** 0xBF - Sneeze Up 177 | ** 0xC0 - Sneeze Down 178 | 179 | === Buttons - Front/Base === 180 | 181 | Button status is found from reading a single byte from Device 0x50. 182 | 183 | * Device Index: 0x50 184 | * Button Indexes: 185 | ** 0x01 - Music Mode Front Button 186 | ** 0x02 - Unused? 187 | ** 0x04 - Top of Head 188 | ** 0x08 - Touch Mode Front Button 189 | ** 0x10 - Left Body (Facing toward user) 190 | ** 0x20 - Front Body 191 | ** 0x40 - Right Body (Facing toward user) 192 | ** 0x80 - Back Body 193 | 194 | === Microphone === 195 | 196 | Microphone information is not sent over the I2C bus. 197 | -------------------------------------------------------------------------------- /src/processing/keepon_kinect_control/keepon_kinect_control.pde: -------------------------------------------------------------------------------- 1 | import oscP5.*; 2 | import netP5.*; 3 | import codeanticode.gsvideo.*; 4 | 5 | import SimpleOpenNI.*; 6 | GSCapture camera; 7 | 8 | OscP5 oscP5; 9 | String s = "Looking for user"; 10 | SimpleOpenNI context; 11 | int i = 0; 12 | PFont font; 13 | PVector elCenter = new PVector(); 14 | float elRadius; 15 | PVector lineStart = new PVector(); 16 | float lineLength; 17 | 18 | 19 | float zoomF =0.5f; 20 | float rotX = radians(180); // by default rotate the hole scene 180deg around the x-axis, 21 | // the data from openni comes upside down 22 | float rotY = radians(0); 23 | NetAddress keepon; 24 | void setup() 25 | { 26 | size(640,480); // strange, get drawing error in the cameraFrustum if i use P3D, in opengl there is no problem 27 | oscP5 = new OscP5(this,12000); 28 | context = new SimpleOpenNI(this); 29 | keepon = new NetAddress("127.0.0.1",10000); 30 | camera = new GSCapture(this, 320, 240); 31 | camera.start(); 32 | OscMessage rotmsg = new OscMessage("/rotate"); 33 | rotmsg.add(128); 34 | OscMessage bfmsg = new OscMessage("/bf"); 35 | bfmsg.add(128); 36 | oscP5.send(rotmsg, keepon); 37 | oscP5.send(bfmsg, keepon); 38 | // disable mirror 39 | context.setMirror(false); 40 | 41 | // enable depthMap generation 42 | context.enableRGB(); 43 | context.enableDepth(); 44 | 45 | // enable skeleton generation for all joints 46 | context.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL); 47 | smooth(); 48 | font = loadFont("FreeSans-18.vlw"); 49 | textFont(font, 18); 50 | textAlign(CENTER); 51 | 52 | elCenter.x = width/6; 53 | elCenter.y = height/3; 54 | elRadius = 75; 55 | lineStart.x = (width/3); 56 | lineStart.y = height/3 + (elRadius/2); 57 | lineLength = 75; 58 | } 59 | void captureEvent(GSCapture camera){ 60 | camera.read(); 61 | } 62 | void draw() 63 | { 64 | // update the cam 65 | context.update(); 66 | 67 | background(0,0,0); 68 | if((context.nodes() & SimpleOpenNI.NODE_DEPTH) != 0) 69 | { 70 | if((context.nodes() & SimpleOpenNI.NODE_IMAGE) != 0) 71 | { 72 | image(context.depthImage(),0,240, 320, 240); 73 | image(context.rgbImage(),320,240,320,240); 74 | } 75 | else 76 | image(context.depthImage(),0,0); 77 | } 78 | image(camera, 320, 0); 79 | ellipse(elCenter.x, elCenter.y, elRadius, elRadius); 80 | color(255, 255,255); 81 | text(s, width/4, 40); 82 | strokeWeight(4); 83 | if(context.isTrackingSkeleton(1)) 84 | { 85 | PVector nJ = new PVector(); 86 | PVector rsJ = new PVector(); 87 | PVector lhJ = new PVector(); 88 | PVector rhJ = new PVector(); 89 | float confidence; 90 | 91 | //Calculate angle of shoulders in relation to camera z-axis 92 | confidence = context.getJointPositionSkeleton(1,SimpleOpenNI.SKEL_NECK,nJ); 93 | if(confidence < 0.001f) 94 | return; 95 | confidence = context.getJointPositionSkeleton(1,SimpleOpenNI.SKEL_RIGHT_SHOULDER,rsJ); 96 | if(confidence < 0.001f) 97 | return; 98 | float rotAngle = atan2(rsJ.z - nJ.z, rsJ.x - nJ.x); 99 | 100 | //Calculate angle between hip and neck joints base 101 | 102 | stroke(0,0,0); 103 | line(elCenter.x, elCenter.y, elCenter.x + ((elRadius/2) * cos(rotAngle - PI/2)), elCenter.y + ((elRadius/2) * sin(rotAngle - PI/2))); 104 | 105 | // draw the joint position 106 | confidence = context.getJointPositionSkeleton(1,SimpleOpenNI.SKEL_LEFT_HIP,lhJ); 107 | if(confidence < 0.001f) 108 | return; 109 | confidence = context.getJointPositionSkeleton(1,SimpleOpenNI.SKEL_RIGHT_HIP,rhJ); 110 | if(confidence < 0.001f) 111 | return; 112 | // find midpoint between hips for center hip joint 113 | PVector chJ = PVector.add(lhJ, rhJ); 114 | chJ.div(2); 115 | float bendAngle = atan2(chJ.y - nJ.y, chJ.z - nJ.z); 116 | stroke(255,255,255); 117 | line(lineStart.x, lineStart.y, lineStart.x + (lineLength * cos(bendAngle)), lineStart.y + (lineLength * sin(bendAngle))); 118 | drawSkeleton(1); 119 | OscMessage rotmsg = new OscMessage("/rotate"); 120 | rotmsg.add(128 * ((PI + -rotAngle)/PI)); /* add an int to the osc message */ 121 | oscP5.send(rotmsg, keepon); 122 | OscMessage bfmsg = new OscMessage("/bf"); 123 | bfmsg.add(255- (128 * (-bendAngle/(PI/2)))); 124 | println(rotAngle); 125 | /* send the message */ 126 | oscP5.send(bfmsg, keepon); 127 | } 128 | else 129 | { 130 | i++; 131 | //Draw rotation circle 132 | stroke(255,255,255); 133 | line(lineStart.x, lineStart.y - lineLength, lineStart.x, lineStart.y); 134 | stroke(0,0,0); 135 | line(elCenter.x, elCenter.y, elCenter.x + ((elRadius/2) * cos(radians(i))), elCenter.y + ((elRadius/2) * sin(radians(i)))); 136 | 137 | } 138 | 139 | } 140 | 141 | // draw the skeleton with the selected joints 142 | void drawSkeleton(int userId) 143 | { 144 | strokeWeight(3); 145 | 146 | // to get the 3d joint data 147 | drawLimb(userId, SimpleOpenNI.SKEL_HEAD, SimpleOpenNI.SKEL_NECK); 148 | 149 | drawLimb(userId, SimpleOpenNI.SKEL_NECK, SimpleOpenNI.SKEL_LEFT_SHOULDER); 150 | drawLimb(userId, SimpleOpenNI.SKEL_LEFT_SHOULDER, SimpleOpenNI.SKEL_LEFT_ELBOW); 151 | drawLimb(userId, SimpleOpenNI.SKEL_LEFT_ELBOW, SimpleOpenNI.SKEL_LEFT_HAND); 152 | 153 | drawLimb(userId, SimpleOpenNI.SKEL_NECK, SimpleOpenNI.SKEL_RIGHT_SHOULDER); 154 | drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, SimpleOpenNI.SKEL_RIGHT_ELBOW); 155 | drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_ELBOW, SimpleOpenNI.SKEL_RIGHT_HAND); 156 | 157 | drawLimb(userId, SimpleOpenNI.SKEL_LEFT_SHOULDER, SimpleOpenNI.SKEL_TORSO); 158 | drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_SHOULDER, SimpleOpenNI.SKEL_TORSO); 159 | 160 | drawLimb(userId, SimpleOpenNI.SKEL_TORSO, SimpleOpenNI.SKEL_LEFT_HIP); 161 | drawLimb(userId, SimpleOpenNI.SKEL_LEFT_HIP, SimpleOpenNI.SKEL_LEFT_KNEE); 162 | drawLimb(userId, SimpleOpenNI.SKEL_LEFT_KNEE, SimpleOpenNI.SKEL_LEFT_FOOT); 163 | 164 | drawLimb(userId, SimpleOpenNI.SKEL_TORSO, SimpleOpenNI.SKEL_RIGHT_HIP); 165 | drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_HIP, SimpleOpenNI.SKEL_RIGHT_KNEE); 166 | drawLimb(userId, SimpleOpenNI.SKEL_RIGHT_KNEE, SimpleOpenNI.SKEL_RIGHT_FOOT); 167 | 168 | strokeWeight(1); 169 | 170 | } 171 | 172 | void drawLimb(int userId,int jointType1,int jointType2) 173 | { 174 | PVector jointPos1 = new PVector(); 175 | PVector jointPos2 = new PVector(); 176 | float confidence; 177 | 178 | // draw the joint position 179 | confidence = context.getJointPositionSkeleton(userId,jointType1,jointPos1); 180 | confidence = context.getJointPositionSkeleton(userId,jointType2,jointPos2); 181 | 182 | stroke(255,0,0,confidence * 200 + 55); 183 | float qw = width/4; 184 | float hh = 3*(height/4); 185 | // You know, sometimes, you just keep putting numbers in until things work. 186 | // Truncated out z because I don't really care if it doesn't line up. 187 | line((((1-(qw-(jointPos1.x/8))/qw))*qw)+qw,hh-(jointPos1.y/8), 188 | (((1-(qw-(jointPos2.x/8))/qw))*qw)+qw,hh-(jointPos2.y/8)); 189 | } 190 | 191 | // SimpleOpenNI user events 192 | 193 | void onNewUser(int userId) 194 | { 195 | s = "Found User"; 196 | println("onNewUser - userId: " + userId); 197 | println(" start pose detection"); 198 | 199 | context.startPoseDetection("Psi",userId); 200 | } 201 | 202 | void onLostUser(int userId) 203 | { 204 | s = "Looking for User"; 205 | println("onLostUser - userId: " + userId); 206 | } 207 | 208 | void onStartCalibration(int userId) 209 | { 210 | s = "Calibrating for Skeleton"; 211 | println("onStartCalibration - userId: " + userId); 212 | } 213 | 214 | void onEndCalibration(int userId, boolean successfull) 215 | { 216 | println("onEndCalibration - userId: " + userId + ", successfull: " + successfull); 217 | s = "Tracking Skeleton"; 218 | if (successfull) 219 | { 220 | println(" User calibrated !!!"); 221 | context.startTrackingSkeleton(userId); 222 | } 223 | else 224 | { 225 | println(" Failed to calibrate user !!!"); 226 | println(" Start pose detection"); 227 | context.startPoseDetection("Psi",userId); 228 | } 229 | } 230 | 231 | void onStartPose(String pose,int userId) 232 | { 233 | s = "Calibrating Skeleton"; 234 | println("onStartdPose - userId: " + userId + ", pose: " + pose); 235 | println(" stop pose detection"); 236 | 237 | context.stopPoseDetection(userId); 238 | context.requestCalibrationSkeleton(userId, true); 239 | 240 | } 241 | 242 | void onEndPose(String pose,int userId) 243 | { 244 | s = "Finished Calibrating skeleton"; 245 | println("onEndPose - userId: " + userId + ", pose: " + pose); 246 | } 247 | 248 | --------------------------------------------------------------------------------