├── .gitignore ├── README.md ├── assets ├── LED_RGB.png ├── boton.png ├── burnboard.png ├── dc_motor.png ├── fotorresistencia.png ├── i2c_backpacking.png ├── led.png ├── lsusb_device_id.png ├── movement.png ├── multi-DHT11_I2C_NANO_BACKPACK.png ├── piezo.png ├── potentiometer.png ├── proximity-hcsr04.png ├── repl.png ├── servo.png ├── stepper-driver.png ├── temperature-ds18b20.png ├── thermoresistor.png ├── two_color_led_bb.png └── wiichuck.png ├── examples ├── button │ ├── README.md │ └── button.js ├── dc_motors │ ├── README.md │ └── dc_motor.js ├── hygrometer │ ├── README.md │ └── dht11.js ├── i2c_backpack │ ├── README.md │ └── i2c.js ├── joystick │ ├── README.md │ └── joystick.js ├── keypress │ ├── README.md │ └── keypress.js ├── led │ ├── README.md │ └── led.js ├── movement │ ├── README.md │ └── movement.js ├── photoresistor │ ├── README.md │ └── photoresistor.js ├── piezo │ ├── README.md │ └── piezo.js ├── potentiometer │ ├── README.md │ └── potentiometer.js ├── proximity │ ├── README.md │ └── proximity.js ├── repl │ ├── README.md │ └── repl.js ├── rgb_led │ ├── README.md │ └── rgb_led.js ├── sensors │ ├── README.md │ ├── analog.js │ └── digital.js ├── servo_motors │ ├── README.md │ └── servos.js ├── steppers │ ├── README.md │ └── steppers.js ├── temperature │ ├── README.md │ └── temp.js ├── thermistor │ ├── README.md │ └── thermistor.js └── two_color_leds │ ├── README.md │ └── two_colors_led.js ├── firmwares ├── README.md └── standardFirmataWiFi │ ├── README.md │ ├── StandardFirmataWiFi.ino │ └── wifiConfig.h ├── hexapod ├── README.md ├── index.js ├── nunchuk.js ├── robot.js └── test.js ├── kit1 └── README.md ├── kit2 └── README.md ├── kit3 └── README.md ├── kit4 └── README.md ├── kit5 └── README.md ├── package-lock.json ├── package.json ├── quadpod ├── README.md ├── index.js ├── nunchuk.js ├── robot.js └── test.js ├── robot-arm └── README.md ├── simon_x3 └── README.md ├── simon_x4 └── README.md └── spacebucket └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | Desarrollaremos los conceptos básicos de robótica y domótica utilizando tecnologías conocidas y componentes de muy bajo presupuesto y al alcance de todos. 3 | 4 | Utilizaremos JavaScript como lenguaje base de progarmación para los dispositivos. 5 | 6 | _Author [Marcos Tomatti](mailto:mtomatti@elementum.com)_ 7 | 8 | ### Prerequisitos 9 | 10 | - [Git](https://git-scm.com) 11 | - [Node.js and npm](nodejs.org) Node ^6.9.11 **IMPORTANTE** 12 | 13 | ### Instalación 14 | 15 | 1. Colnar este repositorio: 16 | `$ git clone git@github.com:elementumscm/workshop-roboticsjs.git` 17 | 18 | 2. Para instalar las dependencias del proyecto ejecutar: 19 | 20 | `$ npm install` 21 | 22 | 3. Según los diferentes sistemas operativos, podríamos encontrarnos con alguno de los siguientes problemas comunes: 23 | - Linux, sin permisos para leer/ecribir el puerto USB: 24 | - `$ sudo usermod -a -G dialout ` 25 | - `$ sudo chmod a+rw /dev/ttyACM0` 26 | 27 | Donde <username> es el nombre de nuestro usuario en linux, /dev/ttyACM0 es el puerto de nuestro Arduino, el ID del dispositivo puede cambiar según la marca y modelo del board. 28 | 29 | - Windows, es recomendable correr la consola como administrador. 30 | - Mac OSX, suele no reconocer los USB de los arduinos. Para idenitifcar el modelo y bajar los drivers hay que correr: 31 | - `$ brew update && brew tap jlhonora/lsusb && brew install lsusb` 32 | - Ejecutandp `$ lsusb` con y sin el arduino conectado podremos identificar que ID de fabricante y de dispositivo tiene el microcontrolador. Ese id nos permitirá buscar y encontrar el driver adecuado para nuestro dispositivo. 33 | 34 | ![lsusb](./assets/lsusb_device_id.png) 35 | 36 | 4. Si todo salió bien, ya podemos probar nuestro arduino con un simple "hola mundo": 37 | `$ npm start` 38 | 39 | ### Firmware 40 | Cuando tenemos un arduino nuevo, o debemos usar una versión especial de firmata para conectar a nuestros dispositivos, deberemos _flashearlo_. 41 | 42 | En la sección de [firmwares](./firmwares) encontrarán detalles de como hacerlo. 43 | 44 | ### Kits 45 | Para el workshop tenemos preparados algunos kits con diferentes desafíos: 46 | - [Kit 1](./kit1) 47 | - [Kit 2](./kit2) 48 | - [Kit 3](./kit3) 49 | - [Kit 4](./kit4) 50 | - [Kit 5](./kit5) 51 | - [Simon x3](./simon_x3) 52 | - [Simon x4](./simon_x4) 53 | - [Robot Arm](./robot-arm) 54 | - ~~[Step by Step](./step-by-step)~~ 55 | - [SpaceBucket](./spacebucket) 56 | - [Quadpod](./quadpod) 57 | - [Hexapod](./hexapod) 58 | 59 | 60 | ### Ejemplos de Codigo 61 | - [Botones](./examples/button) 62 | - [Fotoresistencia](./examples/photoresistor) 63 | - [Higrómetro](./examples/hygrometer) 64 | - [Leds](./examples/led) 65 | - [Leds RGB](./examples/rgb_led) 66 | - [Leds de 2 colores](./examples/two_color_leds) 67 | - [Motores continuos](examples/dc_motors/) 68 | - [Servomotores](./examples/servo_motors) 69 | - *[Motores paso a paso](./examples/steppers) 70 | - [Piezo / Sonido](./examples/piezo) 71 | - [Sensores análgos y digitales](./examples/sensors) 72 | - [Sensor de movimiento](./examples/movement) 73 | - [Potenciómetro](./examples/potentiometer) 74 | - [Termistor](./examples/thermistor) 75 | - [Termómetro](./examples/temperature) 76 | 77 | Otros ejemplos: 78 | - [Backpack I2C](./examples/i2c_backpack) 79 | - [Keypress](./examples/keypress) 80 | - [REPL](./examples/repl) 81 | 82 | ## Presentación 83 | Los slides de la presentación del workshop pueden encontrarlos [aqui](https://docs.google.com/presentation/d/1eZ7Vt9LG6fp64gvVSviCgytMsvBdvqT8KcOFrs97kFM/edit?usp=sharing). 84 | 85 | ## License 86 | Licensed under the MIT license. 87 | -------------------------------------------------------------------------------- /assets/LED_RGB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/LED_RGB.png -------------------------------------------------------------------------------- /assets/boton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/boton.png -------------------------------------------------------------------------------- /assets/burnboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/burnboard.png -------------------------------------------------------------------------------- /assets/dc_motor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/dc_motor.png -------------------------------------------------------------------------------- /assets/fotorresistencia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/fotorresistencia.png -------------------------------------------------------------------------------- /assets/i2c_backpacking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/i2c_backpacking.png -------------------------------------------------------------------------------- /assets/led.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/led.png -------------------------------------------------------------------------------- /assets/lsusb_device_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/lsusb_device_id.png -------------------------------------------------------------------------------- /assets/movement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/movement.png -------------------------------------------------------------------------------- /assets/multi-DHT11_I2C_NANO_BACKPACK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/multi-DHT11_I2C_NANO_BACKPACK.png -------------------------------------------------------------------------------- /assets/piezo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/piezo.png -------------------------------------------------------------------------------- /assets/potentiometer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/potentiometer.png -------------------------------------------------------------------------------- /assets/proximity-hcsr04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/proximity-hcsr04.png -------------------------------------------------------------------------------- /assets/repl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/repl.png -------------------------------------------------------------------------------- /assets/servo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/servo.png -------------------------------------------------------------------------------- /assets/stepper-driver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/stepper-driver.png -------------------------------------------------------------------------------- /assets/temperature-ds18b20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/temperature-ds18b20.png -------------------------------------------------------------------------------- /assets/thermoresistor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/thermoresistor.png -------------------------------------------------------------------------------- /assets/two_color_led_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/two_color_led_bb.png -------------------------------------------------------------------------------- /assets/wiichuck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elementumscm/workshop-roboticsjs/8d51ba736dfc865b56c9ff4030022aca3d80574b/assets/wiichuck.png -------------------------------------------------------------------------------- /examples/button/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Boton 2 | 3 | ### Cableado 4 | ![cableado boton](../../assets/boton.png) 5 | 6 | ### Código 7 | ```javascript 8 | const five = require('johnny-five'); 9 | 10 | let board = new five.Board(); 11 | 12 | board.on('ready', () => { 13 | const button = new five.Button({ 14 | pin: 2, 15 | invert: true // algunos botones vienen con el modo invertido. 16 | }); 17 | 18 | button.on('hold', function onHold() { 19 | console.log('Button held'); 20 | }); 21 | 22 | button.on('press', function onPress() { 23 | console.log('Button pressed'); 24 | }); 25 | 26 | button.on('release', function onRelease() { 27 | console.log('Button released'); 28 | }); 29 | }); 30 | ``` 31 | 32 | ### Referencia de la API 33 | [Botones](http://johnny-five.io/api/button/) -------------------------------------------------------------------------------- /examples/button/button.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | let board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | const button = new five.Button({ 7 | pin: 8 8 | }); 9 | 10 | button.on('hold', function onHold() { 11 | console.log('Button held'); 12 | }); 13 | 14 | button.on('press', function onPress() { 15 | console.log('Button pressed'); 16 | }); 17 | 18 | button.on('release', function onRelease() { 19 | console.log('Button released'); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /examples/dc_motors/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Motores Continuos 2 | 3 | ### Cableado 4 | ![Cableado motores continuos](../../assets/dc_motor.png) 5 | 6 | ### Código 7 | ```javascript 8 | const five = require('johnny-five'); 9 | 10 | const board = new five.Board(); 11 | 12 | board.on('ready', function onReady() { 13 | const motorA = new five.Motor({ 14 | pins: { 15 | pwm: 6, 16 | dir: 7, 17 | cdir: 8 18 | } 19 | }), 20 | motorB = new five.Motor({ 21 | pins: { 22 | pwm: 11, 23 | dir: 10, 24 | cdir: 9 25 | } 26 | }); 27 | 28 | // 100% velocidad al frente en los dos motores 29 | motorA.forward(255); 30 | motorB.forward(255); 31 | 32 | setTimeout(() => { 33 | // 100% velocidad marcha atras en los dos motores 34 | motorA.reverse(255); 35 | motorB.reverse(255); 36 | }, 5000); 37 | 38 | setTimeout(() => { 39 | // freno total 40 | motorA.stop(); 41 | motorB.stop(); 42 | }, 10000); 43 | }); 44 | ``` 45 | 46 | ### Referencia de la API 47 | [motor](http://johnny-five.io/api/motor/) -------------------------------------------------------------------------------- /examples/dc_motors/dc_motor.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | const board = new five.Board(); 4 | 5 | board.on('ready', function onReady() { 6 | const motorA = new five.Motor({ 7 | pins: { 8 | pwm: 6, 9 | dir: 7, 10 | cdir: 8 11 | } 12 | }), 13 | motorB = new five.Motor({ 14 | pins: { 15 | pwm: 11, 16 | dir: 10, 17 | cdir: 9 18 | } 19 | }); 20 | 21 | // 100% velocidad al frente en los dos motores 22 | motorA.forward(255); 23 | motorB.forward(255); 24 | 25 | setTimeout(() => { 26 | // 100% velocidad marcha atras en los dos motores 27 | motorA.reverse(255); 28 | motorB.reverse(255); 29 | }, 5000); 30 | 31 | setTimeout(() => { 32 | // freno total 33 | motorA.stop(); 34 | motorB.stop(); 35 | }, 10000); 36 | }); 37 | -------------------------------------------------------------------------------- /examples/hygrometer/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Higrometro DHT11 2 | 3 | Este ejemplo usa [backpacking](../i2c_backpack) para poder acceder a un dispositivo no soportado nativamente por **Johnny Five**. 4 | 5 | ### Cableado 6 | ![cableado boton](../../assets/multi-DHT11_I2C_NANO_BACKPACK.png) 7 | 8 | ### Código JS 9 | ```javascript 10 | const five = require('johnny-five'); 11 | 12 | let board = new five.Board(); 13 | 14 | board.on('ready', () => { 15 | 16 | let dht11 = new five.Thermometer({ 17 | controller: 'DHT11_I2C_NANO_BACKPACK' 18 | }); 19 | 20 | dht11.on('change', () => { 21 | console.log(`${dht11.celsius}°C`); 22 | console.log(`${dht11.humidity}%`); 23 | }); 24 | }); 25 | ``` 26 | 27 | ### Código Backpack (Arduino) 28 | ```C 29 | #include 30 | #include "DHT.h" 31 | 32 | #define DEBUG_MODE 0 33 | 34 | // Address Pins 35 | #define AD0 11 36 | #define AD1 12 37 | 38 | // I2C Defaults 39 | #define I2C_DEFAULT_ADDRESS 0x0A 40 | #define I2C_BUFFER_SIZE 4 41 | // 42 | // 0 H LSB 43 | // 1 H MSB 44 | // 2 T LSB 45 | // 3 T MSB 46 | // 47 | byte buffer[I2C_BUFFER_SIZE]; 48 | 49 | int addressPins[] = { AD0, AD1 }; 50 | int address = I2C_DEFAULT_ADDRESS; 51 | 52 | int dhtPin = -1; 53 | int dhtType = -1; 54 | 55 | void resetState() { 56 | digitalWrite(dhtPin, LOW); 57 | pinMode(dhtPin, INPUT); 58 | } 59 | 60 | void setup() { 61 | 62 | int offset = 0; 63 | 64 | for (int i = 0; i < 2; i++) { 65 | pinMode(addressPins[i], INPUT); 66 | if (digitalRead(addressPins[i])) { 67 | offset |= 1 << i; 68 | } 69 | } 70 | 71 | address += offset; 72 | 73 | #if DEBUG_MODE 74 | Serial.begin(9600); 75 | #endif 76 | 77 | resetState(); 78 | 79 | Wire.begin(address); 80 | Wire.onRequest(onRequest); 81 | Wire.onReceive(onReceive); 82 | } 83 | 84 | void loop() { 85 | if (dhtPin != -1 && dhtType != -1) { 86 | DHT dht(dhtPin, dhtType); 87 | dht.begin(); 88 | 89 | int h = (int)((float)dht.readHumidity() * 100); 90 | int c = (int)((float)dht.readTemperature() * 100); 91 | 92 | buffer[0] = h >> 8; 93 | buffer[1] = h & 0xFF; 94 | buffer[2] = c >> 8; 95 | buffer[3] = c & 0xFF; 96 | 97 | #if DEBUG_MODE 98 | Serial.print("h: "); 99 | Serial.println(h); 100 | Serial.print("c: "); 101 | Serial.println(c); 102 | #endif 103 | 104 | delay(250); 105 | 106 | #if DEBUG_MODE 107 | Serial.print("free ram: "); 108 | Serial.println(freeRam()); 109 | #endif 110 | 111 | } 112 | } 113 | 114 | #if DEBUG_MODE 115 | int freeRam() { 116 | extern int __heap_start, *__brkval; 117 | int v; 118 | return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 119 | } 120 | #endif 121 | 122 | void onRequest() { 123 | Wire.write(buffer, I2C_BUFFER_SIZE); 124 | } 125 | 126 | void onReceive(int howMany) { 127 | // Order: [ pin, type ] 128 | // Default: [ 2, 11 ] 129 | dhtPin = (byte)Wire.read(); 130 | dhtType = (byte)Wire.read(); 131 | 132 | #if DEBUG_MODE 133 | Serial.print("dhtPin: "); 134 | Serial.println(dhtPin); 135 | Serial.print("dhtType: "); 136 | Serial.println(dhtType); 137 | #endif 138 | } 139 | ``` 140 | 141 | ### Referencia de la API 142 | [Termometro](http://johnny-five.io/api/thermometer) 143 | [Higrometro](http://johnny-five.io/api/hygrometer) -------------------------------------------------------------------------------- /examples/hygrometer/dht11.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | let board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | 7 | let dht11 = new five.Thermometer({ 8 | controller: 'DHT11_I2C_NANO_BACKPACK' 9 | }); 10 | 11 | dht11.on('change', () => { 12 | console.log(`${dht11.celsius}°C`); 13 | console.log(`${dht11.humidity}%`); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /examples/i2c_backpack/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Backpacking con I2C 2 | 3 | ### Cableado 4 | ![cableado i2c backpack](../../assets/i2c_backpacking.png) 5 | 6 | 7 | ### Código JS 8 | ```javascript 9 | const five = require('johnny-five'); 10 | 11 | const board = new five.Board(); 12 | 13 | const int16 = function(msb, lsb) { 14 | const result = (msb << 8) | lsb; 15 | return result >> 15 ? ((result ^ 0xFFFF) + 1) * -1 : result; 16 | }; 17 | 18 | board.on("ready", () => { 19 | board.i2cConfig(); 20 | 21 | board.i2cRead(0x0A, 2, (bytes) => { 22 | console.log("data ", int16(bytes[0], bytes[1])); 23 | }); 24 | 25 | const blink = () => { 26 | board.i2cWrite(0x0A, [0x01]); 27 | }; 28 | 29 | }); 30 | ``` 31 | 32 | ### Código Arduino 33 | ```c 34 | #include 35 | 36 | // Address Pins 37 | #define LED_PIN 13 38 | 39 | // I2C Defaults 40 | #define I2C_DEFAULT_ADDRESS 0x0A // esta es la direccion con la que identificamos cada dispositivo. 41 | #define I2C_BUFFER_SIZE 2 // tamaño del buffer de datos que vamos a enviar. 42 | 43 | byte buffer[I2C_BUFFER_SIZE]; 44 | 45 | int myAddress = I2C_DEFAULT_ADDRESS; 46 | int data; 47 | 48 | void setup() 49 | { 50 | pinMode(LED_PIN, OUTPUT); 51 | Wire.begin (myAddress); 52 | 53 | 54 | Wire.onRequest(requestEvents); 55 | Wire.onReceive(receiveEvent); 56 | } 57 | 58 | 59 | void loop() { 60 | 61 | // Definimos el estado del led dentro de receiveEvent, aqui lo usamos 62 | digitalWrite(LED_PIN, LED_STATUS); 63 | 64 | 65 | // Seteamos el valor de DATA aca 66 | data = 10; 67 | } 68 | 69 | void requestEvents () { 70 | // Ponemos nuestro codigo aca, que va a formatear la DATA le pasamos al dispositivo MAESTRO. 71 | buffer[0] = data >> 8; 72 | buffer[1] = data & 0xFF; 73 | 74 | Wire.write(buffer, I2C_BUFFER_SIZE); 75 | } 76 | 77 | void receiveEvent (int received) 78 | { 79 | LED_STATUS = LOW; 80 | if(received > 0) { 81 | // Recibimos algo del MAESTRO, realizamos una accion acorde dentro de este bloque 82 | LED_STATUS = HIGH; 83 | 84 | // Si recibimos algo, tenemos que leer toda la data 85 | while(Wire.available()){ 86 | Wire.read(); 87 | } 88 | } 89 | } 90 | ``` 91 | 92 | 93 | ### Referencia de la API 94 | [Board](http://johnny-five.io/api/board) -------------------------------------------------------------------------------- /examples/i2c_backpack/i2c.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | const board = new five.Board(); 4 | 5 | const int16 = function(msb, lsb) { 6 | const result = (msb << 8) | lsb; 7 | return result >> 15 ? ((result ^ 0xFFFF) + 1) * -1 : result; 8 | }; 9 | 10 | board.on("ready", () => { 11 | board.i2cConfig(); 12 | 13 | board.i2cRead(0x0A, 6, (bytes) => { 14 | console.log(bytes); 15 | console.log("Left ", int16(bytes[0], bytes[1])); 16 | console.log("Right ", int16(bytes[2], bytes[3])); 17 | console.log("Back ", int16(bytes[4], bytes[5])); 18 | }); 19 | 20 | const blink = () => { 21 | board.i2cWrite(0x0A, [0x01]); 22 | }; 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /examples/joystick/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo WiiChuck 2 | Este ejemplo usa un componente de muy bajo costo (WiiChuck) para conectar un Nunchuck de Wii a un Arduino. 3 | 4 | ### Cableado 5 | ![cableado wii nunchuck](../../assets/wiichuck.png) 6 | 7 | 8 | ### Código 9 | ```javascript 10 | const five = require('johnny-five'); 11 | 12 | const board = new five.Board(); 13 | 14 | board.on('ready', function() { 15 | 16 | // Create a new `nunchuk` hardware instance. 17 | const nunchuk = new five.Wii.Nunchuk({ 18 | freq: 50 19 | }); 20 | 21 | new five.Pin("A2").low(); // Ground 22 | new five.Pin("A3").high(); // 5v 23 | 24 | nunchuk.joystick.on('change', function(event) { 25 | console.log( 26 | 'joystick ' + event.axis, 27 | event.target[event.axis], 28 | event.axis, event.direction 29 | ); 30 | }); 31 | 32 | nunchuk.accelerometer.on('change', function(event) { 33 | console.log( 34 | 'accelerometer ' + event.axis, 35 | event.target[event.axis], 36 | event.axis, event.direction 37 | ); 38 | }); 39 | 40 | ['down', 'up', 'hold'].forEach(function(type) { 41 | nunchuk.on(type, function(event) { 42 | console.log( 43 | event.target.which + ' is ' + type, 44 | 45 | { 46 | isUp: event.target.isUp, 47 | isDown: event.target.isDown 48 | } 49 | ); 50 | }); 51 | }); 52 | }); 53 | 54 | ``` 55 | 56 | ### Referencia de la API 57 | -------------------------------------------------------------------------------- /examples/joystick/joystick.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | const board = new five.Board(); 4 | 5 | board.on('ready', function() { 6 | 7 | // Create a new `nunchuk` hardware instance. 8 | const nunchuk = new five.Wii.Nunchuk({ 9 | freq: 50 10 | }); 11 | 12 | new five.Pin("A2").low(); 13 | new five.Pin("A3").high(); 14 | 15 | nunchuk.joystick.on('change', function(event) { 16 | console.log( 17 | 'joystick ' + event.axis, 18 | event.target[event.axis], 19 | event.axis, event.direction 20 | ); 21 | }); 22 | 23 | nunchuk.accelerometer.on('change', function(event) { 24 | console.log( 25 | 'accelerometer ' + event.axis, 26 | event.target[event.axis], 27 | event.axis, event.direction 28 | ); 29 | }); 30 | 31 | ['down', 'up', 'hold'].forEach(function(type) { 32 | nunchuk.on(type, function(event) { 33 | console.log( 34 | event.target.which + ' is ' + type, 35 | 36 | { 37 | isUp: event.target.isUp, 38 | isDown: event.target.isDown 39 | } 40 | ); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /examples/keypress/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Keypress 2 | 3 | Nos permite interceptar las acciones del teclado y definirle callbacks para los distintos eventos de una tecla. El siguiente es un ejemplo clásico del uso de keypress, se puede utilizar para unir las flechas del teclado con las acciones de adelante, atras y giro de un robot. 4 | 5 | ### Código 6 | ```javascript 7 | const keypress = require('keypress'); 8 | 9 | // make `process.stdin` begin emitting "keypress" events 10 | keypress(process.stdin); 11 | 12 | process.stdin.on('keypress', (ch, key) => { 13 | let combo = ''; 14 | if (key) { 15 | if (key.ctrl) { 16 | combo += 'ctrl + '; 17 | } 18 | 19 | if (key.shift) { 20 | combo += 'shift + '; 21 | } 22 | 23 | switch (key.name) { 24 | case 'up': 25 | case 'right': 26 | case 'down': 27 | case 'left': 28 | default: 29 | console.log(`${combo}${key.name}`); 30 | } 31 | } 32 | // keep escaping if ctrl+c is pressed 33 | if (key && key.ctrl && key.name === 'c') { 34 | process.stdin.pause(); 35 | } 36 | }); 37 | 38 | process.stdin.setRawMode(true); 39 | process.stdin.resume(); 40 | 41 | 42 | ``` 43 | -------------------------------------------------------------------------------- /examples/keypress/keypress.js: -------------------------------------------------------------------------------- 1 | const keypress = require('keypress'); 2 | 3 | // make `process.stdin` begin emitting "keypress" events 4 | keypress(process.stdin); 5 | 6 | process.stdin.on('keypress', (ch, key) => { 7 | let combo = ''; 8 | if (key) { 9 | if (key.ctrl) { 10 | combo += 'ctrl + '; 11 | } 12 | 13 | if (key.shift) { 14 | combo += 'shift + '; 15 | } 16 | 17 | switch (key.name) { 18 | case 'up': 19 | case 'right': 20 | case 'down': 21 | case 'left': 22 | default: 23 | console.log(`${combo}${key.name}`); 24 | } 25 | } 26 | if (key && key.ctrl && key.name === 'c') { 27 | process.stdin.pause(); 28 | } 29 | }); 30 | 31 | process.stdin.setRawMode(true); 32 | process.stdin.resume(); 33 | -------------------------------------------------------------------------------- /examples/led/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo LED 2 | 3 | ### Cableado 4 | ![Cableado Led](../../assets/led.png) 5 | 6 | NOTA: Para el ejemplo inicial de "hola mundo" no se necesita cableado. La gran mayoria, si no todos, los Arduinos vienen con un led integrado en el pin D13. 7 | 8 | ### Código 9 | ```javascript 10 | const five = require('johnny-five'); 11 | 12 | const board = new five.Board(); 13 | 14 | board.on('ready', () => { 15 | const led = new five.Led(13); 16 | led.strobe(500); 17 | }); 18 | 19 | ``` 20 | 21 | ### Referencia de la API 22 | [Leds](http://johnny-five.io/api/led) 23 | -------------------------------------------------------------------------------- /examples/led/led.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | const board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | const led = new five.Led(13); 7 | led.strobe(500); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/movement/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Sensor de Movimiento 2 | 3 | ### Cableado 4 | ![cableado Sensor de movimiento](../../assets/movement.png) 5 | 6 | ### Código 7 | ```javascript 8 | const five = require('johnny-five'); 9 | const board = new five.Board(); 10 | 11 | board.on('ready', function onReady() { 12 | // Create a new `motion` hardware instance. 13 | const motion = new five.Motion(7); 14 | 15 | // 'calibrated' occurs once, at the beginning of a session, 16 | motion.on('calibrated', function calibrated() { 17 | console.log('calibrated'); 18 | }); 19 | 20 | // 'motionstart' events are fired when the 'calibrated' 21 | // proximal area is disrupted, generally by some form of movement 22 | motion.on('motionstart', function motionStart() { 23 | console.log('motionstart'); 24 | }); 25 | 26 | // 'motionend' events are fired following a 'motionstart' event 27 | // when no movement has occurred in X ms 28 | motion.on('motionend', function motionEnd() { 29 | console.log('motionend'); 30 | }); 31 | 32 | // 'data' events are fired at the interval set in opts.freq 33 | // or every 25ms. Uncomment the following to see all 34 | // motion detection readings. 35 | motion.on('data', function onData(data) { 36 | console.log(data.detectedMotion); 37 | }); 38 | }); 39 | 40 | ``` 41 | 42 | ### Referencia de la API 43 | [Motion](http://johnny-five.io/api/motion) -------------------------------------------------------------------------------- /examples/movement/movement.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | const board = new five.Board(); 3 | 4 | board.on('ready', function onReady() { 5 | // Create a new `motion` hardware instance. 6 | const motion = new five.Motion(7); 7 | 8 | // 'calibrated' occurs once, at the beginning of a session, 9 | motion.on('calibrated', function calibrated() { 10 | console.log('calibrated'); 11 | }); 12 | 13 | // 'motionstart' events are fired when the 'calibrated' 14 | // proximal area is disrupted, generally by some form of movement 15 | motion.on('motionstart', function motionStart() { 16 | console.log('motionstart'); 17 | }); 18 | 19 | // 'motionend' events are fired following a 'motionstart' event 20 | // when no movement has occurred in X ms 21 | motion.on('motionend', function motionEnd() { 22 | console.log('motionend'); 23 | }); 24 | 25 | // 'data' events are fired at the interval set in opts.freq 26 | // or every 25ms. Uncomment the following to see all 27 | // motion detection readings. 28 | motion.on('data', function onData(data) { 29 | console.log(data.detectedMotion); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /examples/photoresistor/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Fotorresistencia 2 | 3 | ### Cableado 4 | ![cableado fotorresitencia](../../assets/fotorresistencia.png) 5 | 6 | ### Código 7 | ```javascript 8 | const five = require('johnny-five'); 9 | 10 | const board = new five.Board(); 11 | 12 | board.on('ready', () => { 13 | const photoresistor = new five.Sensor({ 14 | pin: 'A2', 15 | freq: 250 16 | }); 17 | 18 | photoresistor.on('change', function onChange() { 19 | console.log(this.value); 20 | }); 21 | 22 | photoresistor.on('data', function onData() { 23 | console.log(this.value); 24 | }); 25 | }); 26 | ``` 27 | 28 | ### Referencia de la API 29 | [Sensores](http://johnny-five.io/api/sensor/) -------------------------------------------------------------------------------- /examples/photoresistor/photoresistor.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | const board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | const photoresistor = new five.Sensor({ 7 | pin: 'A2', 8 | freq: 250 9 | }); 10 | 11 | photoresistor.on('change', function onChange() { 12 | console.log(this.value); 13 | }); 14 | 15 | photoresistor.on('data', function onData() { 16 | console.log(this.value); 17 | }); 18 | 19 | board.repl.inject({ 20 | pot: photoresistor 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /examples/piezo/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Piezo / Sonido 2 | 3 | ### Cableado 4 | ![cableado Sensor de movimiento](../../assets/piezo.png) 5 | 6 | ### Código 7 | ```javascript 8 | const five = require('johnny-five'); 9 | const board = new five.Board(); 10 | 11 | board.on('ready', function onReady() { 12 | 13 | // Create a standard `piezo` instance on pin 3 14 | const piezo = new five.Piezo(3); 15 | 16 | // Plays a song 17 | piezo.play({ 18 | // song is composed by an array of pairs of notes and beats 19 | // The first argument is the note (null means 'no note') 20 | // The second argument is the length of time (beat) of the note (or non-note) 21 | song: [ 22 | ['C4', 1 / 4], 23 | ['D4', 1 / 4], 24 | ['F4', 1 / 4], 25 | ['D4', 1 / 4], 26 | ['A4', 1 / 4], 27 | [null, 1 / 4], 28 | ['A4', 1], 29 | ['G4', 1], 30 | [null, 1 / 2], 31 | ['C4', 1 / 4], 32 | ['D4', 1 / 4], 33 | ['F4', 1 / 4], 34 | ['D4', 1 / 4], 35 | ['G4', 1 / 4], 36 | [null, 1 / 4], 37 | ['G4', 1], 38 | ['F4', 1], 39 | [null, 1 / 2] 40 | ], 41 | tempo: 100 42 | }); 43 | }); 44 | 45 | 46 | ``` 47 | 48 | ### Referencia de la API 49 | [Piezo](http://johnny-five.io/api/piezo) -------------------------------------------------------------------------------- /examples/piezo/piezo.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | const board = new five.Board(); 3 | 4 | board.on('ready', function onReady() { 5 | 6 | // Create a standard `piezo` instance on pin 3 7 | const piezo = new five.Piezo(3); 8 | 9 | // Plays a song 10 | piezo.play({ 11 | // song is composed by an array of pairs of notes and beats 12 | // The first argument is the note (null means 'no note') 13 | // The second argument is the length of time (beat) of the note (or non-note) 14 | song: [ 15 | ['C4', 1 / 4], 16 | ['D4', 1 / 4], 17 | ['F4', 1 / 4], 18 | ['D4', 1 / 4], 19 | ['A4', 1 / 4], 20 | [null, 1 / 4], 21 | ['A4', 1], 22 | ['G4', 1], 23 | [null, 1 / 2], 24 | ['C4', 1 / 4], 25 | ['D4', 1 / 4], 26 | ['F4', 1 / 4], 27 | ['D4', 1 / 4], 28 | ['G4', 1 / 4], 29 | [null, 1 / 4], 30 | ['G4', 1], 31 | ['F4', 1], 32 | [null, 1 / 2] 33 | ], 34 | tempo: 100 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /examples/potentiometer/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Potenciometro 2 | 3 | ### Cableado 4 | ![cableado potenciometro](../../assets/potentiometer.png) 5 | 6 | ### Código 7 | ```javascript 8 | const five = require('johnny-five'); 9 | 10 | const board = new five.Board(); 11 | 12 | board.on('ready', () => { 13 | const potentiometer = new five.Sensor({ 14 | pin: 'A3', 15 | freq: 250 16 | }); 17 | 18 | potentiometer.on('data', () => { 19 | potentiometer.value; 20 | }); 21 | 22 | }); 23 | ``` 24 | 25 | ### Referencia de la API 26 | [Sensor](http://johnny-five.io/api/sensor) -------------------------------------------------------------------------------- /examples/potentiometer/potentiometer.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | const board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | const potentiometer = new five.Sensor({ 7 | pin: 'A3', 8 | freq: 250 9 | }); 10 | 11 | potentiometer.on('data', () => { 12 | potentiometer.value; 13 | }); 14 | 15 | }); 16 | -------------------------------------------------------------------------------- /examples/proximity/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Proximidad 2 | 3 | ### Cableado 4 | ![cableado Proximidad](../../assets/proximity-hcsr04.png) 5 | 6 | ### Firmware 7 | Este ejemplo requiere de una versión especial de Firmata llamada PingFirmata, y debe ser cargada en el Arduino para que el código funcione. 8 | 9 | Siguiendo los pasos especificados en la sección de [firmwares](../../firmwares#flasheando-un-arduino) se puede seleccionar la versión de firmata específica para HC-SR04. 10 | 11 | ### Código 12 | ```javascript 13 | const five = require('johnny-five'); 14 | const board = new five.Board(); 15 | 16 | board.on('ready', function onReady() { 17 | const proximity = new five.Proximity({ 18 | controller: 'HCSR04', 19 | pin: 7 20 | }); 21 | 22 | proximity.on('data', function() { 23 | console.log('Proximity:'); 24 | console.log(`cm : ${proximity.cm}`); 25 | console.log(`in : ${proximity.in}`); 26 | console.log('-----------------'); 27 | }); 28 | 29 | proximity.on('change', function onChange() { 30 | console.log('The obstruction has moved.'); 31 | }); 32 | }); 33 | ``` 34 | 35 | ### Referencia de la API 36 | [Proximidad](http://johnny-five.io/api/proximity) -------------------------------------------------------------------------------- /examples/proximity/proximity.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | const board = new five.Board(); 3 | 4 | board.on('ready', function onReady() { 5 | const proximity = new five.Proximity({ 6 | controller: 'HCSR04', 7 | pin: 7 8 | }); 9 | 10 | proximity.on('data', function() { 11 | console.log('Proximity:'); 12 | console.log(`cm : ${proximity.cm}`); 13 | console.log(`in : ${proximity.in}`); 14 | console.log('-----------------'); 15 | }); 16 | 17 | proximity.on('change', function onChange() { 18 | console.log('The obstruction has moved.'); 19 | }); 20 | }); -------------------------------------------------------------------------------- /examples/repl/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo REPL 2 | 3 | ### Que es? 4 | Del Inglés **R**ead**E**valuate**P**rint**L**oop, esta funcionalidad nos provee de una consola interactiva para poder acceder a los atributos, objetos y/o funciones que nosotros querramos exponer en tiempo de ejecución. Es ideal para procesos de debug y configuración. 5 | 6 | ### Código 7 | ```javascript 8 | const five = require('johnny-five'); 9 | 10 | const board = new five.Board(); 11 | 12 | board.on('ready', () => { 13 | const led = new five.Led(13); 14 | led.strobe(500); 15 | board.repl.inject({ 16 | // Allow limited on/off control access to the 17 | // Led instance from the REPL. 18 | on: function() { 19 | led.on(); 20 | }, 21 | off: function() { 22 | led.off(); 23 | } 24 | }); 25 | }); 26 | 27 | ``` 28 | 29 | ![ejemplo repl](../../assets/repl.png) -------------------------------------------------------------------------------- /examples/repl/repl.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | const board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | const led = new five.Led(13); 7 | led.strobe(500); 8 | board.repl.inject({ 9 | // Allow limited on/off control access to the 10 | // Led instance from the REPL. 11 | on: function() { 12 | led.on(); 13 | }, 14 | off: function() { 15 | led.off(); 16 | } 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /examples/rgb_led/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo LED RGB 2 | 3 | ### Cableado 4 | ![Led RGB](../../assets/LED_RGB.png) 5 | ### Código 6 | ```javascript 7 | const five = require('johnny-five'); 8 | 9 | const board = new five.Board(); 10 | 11 | board.on('ready', () => { 12 | const rgbLed = new five.Led.RGB([9, 10, 11]); 13 | const rainbow = ['FF0000', 'FF7F00', 'FFFF00', '00FF00', '0000FF', '4B0082', '8F00FF']; 14 | let i = 0; 15 | 16 | rgbLed.on(); 17 | rgbLed.color('#ff0000'); 18 | board.loop(1000, () => { 19 | if (i + 1 === rainbow.length) { 20 | i = 0; 21 | } 22 | rgbLed.color(rainbow[i++]); 23 | }); 24 | }); 25 | ``` 26 | 27 | ### Referencia de la API 28 | [Leds](http://johnny-five.io/api/led) 29 | -------------------------------------------------------------------------------- /examples/rgb_led/rgb_led.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | const board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | const rgbLed = new five.Led.RGB([9, 10, 11]); 7 | const rainbow = ['FF0000', 'FF7F00', 'FFFF00', '00FF00', '0000FF', '4B0082', '8F00FF']; 8 | let i = 0; 9 | 10 | rgbLed.on(); 11 | rgbLed.color('#ff0000'); 12 | board.loop(1000, () => { 13 | if (i + 1 === rainbow.length) { 14 | i = 0; 15 | } 16 | rgbLed.color(rainbow[i++]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /examples/sensors/README.md: -------------------------------------------------------------------------------- 1 | ## Sensores 2 | 3 | ### Cableado 4 | El cableado de sensores _genéricos_ varía según el dispositivo en si. Algunos operan de forma similar a una foto o termorresistencia, otros tienen entradas de 5v, GND y una salida de señal digital o análoga. La mayoría, a su vez, pueden requerir de resistencias de valores especificos para operar correctamente o proteger el arduino de potenciales cortocircuitos. Lo único standard en este caso es el código que utilizaremos que es prácticamente el mismo para todos los casos. 5 | 6 | ### Código para sensor *digital* 7 | ```javascript 8 | const five = require('johnny-five'); 9 | 10 | let board = new five.Board(); 11 | 12 | board.on('ready', () => { 13 | let sensor = new five.Sensor.Digital(3); 14 | 15 | sensor.on('change', () => { 16 | console.log(`my state is ${ sensor.value ? 'on' : 'off' }`); 17 | }); 18 | }); 19 | ``` 20 | 21 | ### Código para sensor *analógico* 22 | ```Javascript 23 | const five = require('johnny-five'); 24 | 25 | let board = new five.Board(); 26 | 27 | board.on('ready', () => { 28 | 29 | let sensor = new five.Sensor({ 30 | pin: 'A0', 31 | freq: 250, // en milisegundos, cada cuanto lee data del sensor. Afecta la frecuencia del evento on('data', ...) 32 | threshold: 5 // diferencia entre lecturas que sirve para disparar el evento on('change', ...) 33 | }); 34 | 35 | sensor.on('change', function onChange() { 36 | console.log(this.value); 37 | }); 38 | }); 39 | ``` 40 | 41 | ### Referencia de la API 42 | 43 | [Sensors](http://johnny-five.io/api/sensor) -------------------------------------------------------------------------------- /examples/sensors/analog.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | let board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | 7 | let sensor = new five.Sensor({ 8 | pin: 'A0' 9 | //freq: 250, 10 | //threshold: 5 11 | }); 12 | 13 | sensor.on('change', function onChange() { 14 | console.log(this.value); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /examples/sensors/digital.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | let board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | let sensor = new five.Sensor.Digital(3); 7 | 8 | sensor.on('change', () => { 9 | console.log(`my state is ${ sensor.value ? 'on' : 'off' }`); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/servo_motors/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Servomotores 2 | 3 | ### Cableado 4 | ![Cableado Servomotor](../../assets/servo.png) 5 | 6 | ### Código 7 | ```javascript 8 | const five = require('johnny-five'); 9 | 10 | const board = new five.Board(); 11 | 12 | board.on('ready', function onReady() { 13 | const servo = new five.Servo({ 14 | pin: 3, 15 | range: [0, 180], 16 | fps: 100, 17 | startAt: 90 18 | }); 19 | 20 | servo.center(); 21 | 22 | setTimeout(() => { 23 | servo.sweep(); 24 | }, 1000); 25 | 26 | setTimeout(() => { 27 | servo.center(); 28 | }, 5000); 29 | 30 | }); 31 | ``` 32 | 33 | ### Referencia de la API 34 | [Servo](http://johnny-five.io/api/servo) -------------------------------------------------------------------------------- /examples/servo_motors/servos.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | const board = new five.Board(); 4 | 5 | board.on('ready', function onReady() { 6 | const servo = new five.Servo({ 7 | pin: 3, 8 | range: [0, 180], 9 | fps: 100, 10 | startAt: 90 11 | }); 12 | 13 | servo.center(); 14 | 15 | setTimeout(() => { 16 | servo.sweep(); 17 | }, 1000); 18 | 19 | setTimeout(() => { 20 | servo.center(); 21 | }, 5000); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /examples/steppers/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Motores Paso a Paso 2 | 3 | ### Cableado 4 | ![Cableado Motor paso a paso](../../assets/stepper-driver.png) 5 | 6 | ### Código 7 | ```javascript 8 | const five = require('johnny-five'); 9 | const board = new five.Board(); 10 | 11 | board.on('ready', function onReady() { 12 | 13 | /** 14 | * In order to use the Stepper class, your board must be flashed with 15 | * either of the following: 16 | * 17 | * - AdvancedFirmata https://github.com/soundanalogous/AdvancedFirmata 18 | * - ConfigurableFirmata https://github.com/firmata/arduino/releases/tag/v2.6.2 19 | * 20 | */ 21 | 22 | const stepper = new five.Stepper({ 23 | type: five.Stepper.TYPE.DRIVER, 24 | stepsPerRev: 200, 25 | pins: { 26 | step: 11, 27 | dir: 13 28 | } 29 | }); 30 | 31 | // Make 10 full revolutions counter-clockwise at 180 rpm with acceleration and deceleration 32 | stepper.rpm(180).ccw().accel(1600).decel(1600).step(2000, function step() { 33 | 34 | console.log('Done moving CCW'); 35 | 36 | // once first movement is done, make 10 revolutions clockwise at previously 37 | // defined speed, accel, and decel by passing an object into stepper.step 38 | 39 | stepper.step({ 40 | steps: 2000, 41 | direction: five.Stepper.DIRECTION.CW 42 | }, function cb() { 43 | console.log('Done moving CW'); 44 | }); 45 | }); 46 | }); 47 | ``` 48 | 49 | ### Referencia de la API 50 | [Motor](http://johnny-five.io/api/motor) 51 | [Stepper](http://johnny-five.io/api/stepper) -------------------------------------------------------------------------------- /examples/steppers/steppers.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | const board = new five.Board(); 3 | 4 | board.on('ready', function onReady() { 5 | 6 | /** 7 | * In order to use the Stepper class, your board must be flashed with 8 | * either of the following: 9 | * 10 | * - AdvancedFirmata https://github.com/soundanalogous/AdvancedFirmata 11 | * - ConfigurableFirmata https://github.com/firmata/arduino/releases/tag/v2.6.2 12 | * 13 | */ 14 | 15 | const stepper = new five.Stepper({ 16 | type: five.Stepper.TYPE.DRIVER, 17 | stepsPerRev: 200, 18 | pins: { 19 | step: 11, 20 | dir: 13 21 | } 22 | }); 23 | 24 | // Make 10 full revolutions counter-clockwise at 180 rpm with acceleration and deceleration 25 | stepper.rpm(180).ccw().accel(1600).decel(1600).step(2000, function step() { 26 | 27 | console.log('Done moving CCW'); 28 | 29 | // once first movement is done, make 10 revolutions clockwise at previously 30 | // defined speed, accel, and decel by passing an object into stepper.step 31 | 32 | stepper.step({ 33 | steps: 2000, 34 | direction: five.Stepper.DIRECTION.CW 35 | }, function cb() { 36 | console.log('Done moving CW'); 37 | }); 38 | }); 39 | }); -------------------------------------------------------------------------------- /examples/temperature/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Termometro 2 | Este ejemplo usa la version ConfigurableFirmata dentro del Arduino. 3 | 4 | ### Cableado 5 | ![cableado Termometro](../../assets/temperature-ds18b20.png) 6 | 7 | ### Código 8 | ```javascript 9 | const five = require('johnny-five'); 10 | 11 | let board = new five.Board(); 12 | 13 | board.on('ready', () => { 14 | 15 | let temperature = new five.Thermometer({ 16 | controller: 'DS18B20', 17 | pin: 3, 18 | freq: 1000 19 | }); 20 | 21 | temperature.on('data', () => { 22 | // La ventaja del ds18b20 es que pueden conectarse multiples sensores en el mismo cable 23 | // y accederlos individualmente por medio del address. 24 | console.log(`${temperature.celsius}°C on 0x${temperature.address.toString(16)}`); 25 | }); 26 | 27 | }); 28 | 29 | ``` 30 | 31 | ### Referencia de la API 32 | [Sensor](http://johnny-five.io/api/sensor) 33 | [Thermometer](http://johnny-five.io/api/thermometer) -------------------------------------------------------------------------------- /examples/temperature/temp.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | let board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | 7 | let temperature = new five.Thermometer({ 8 | controller: 'DS18B20', 9 | pin: 3, 10 | freq: 1000 11 | }); 12 | 13 | temperature.on('data', () => { 14 | console.log(`${temperature.celsius}°C on 0x${temperature.address.toString(16)}`); 15 | }); 16 | 17 | }); 18 | -------------------------------------------------------------------------------- /examples/thermistor/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo Termistor 2 | 3 | ### Cableado 4 | ![cableado termistor](../../assets/thermoresistor.png) 5 | 6 | Nota: como lo explicado en los ejemplos de [sensores](../sensors), un termistor puede tener un cableado distinto o requerir de una resistencia adicional. 7 | 8 | 9 | ### Código 10 | ```javascript 11 | const five = require('johnny-five'); 12 | 13 | let board = new five.Board(); 14 | 15 | // Los termistores miden una resistencia especifica segun la temperatura, similar a como funcionan las fotoresistencias. Ese valor en ohms, deberemos convertilo a celcius o farenheit segun sea necesario. 16 | function voltToTemp(resistance) { 17 | let tempC; 18 | const thermistorOhm = 10000, 19 | nominalTemp = 25, 20 | betaCoef = 3435, // este valor suele ser especifico para el sensor, usualmente relacionado con la marca o fabricante. 21 | resistorOhm = 10000; 22 | 23 | resistance = 1023 / resistance - 1; 24 | resistance = resistorOhm / resistance; 25 | console.log(`Resistance: ${resistance}`); 26 | 27 | 28 | 29 | tempC = resistance / thermistorOhm; // (R/Ro) 30 | tempC = parseFloat(Math.log(tempC).toFixed(2)); // ln(R/Ro) 31 | tempC /= betaCoef; // 1/B * ln(R/Ro) 32 | tempC += 1 / (nominalTemp + 273.15); // + (1/To) 33 | tempC = 1 / tempC; // Invert 34 | tempC -= 273.15; // convert to C 35 | 36 | console.log(`Temperature: ${parseFloat(tempC.toFixed(2))}`); 37 | } 38 | 39 | board.on('ready', () => { 40 | 41 | let thermistor = new five.Sensor({ 42 | pin: 'A0', 43 | freq: 1000 44 | }), 45 | average = 0, 46 | count = 0; 47 | 48 | thermistor.on('data', (data) => { 49 | if (count < 5) { 50 | average += data; 51 | count++; 52 | } else { 53 | voltToTemp(average/5); 54 | count = 0; 55 | average = 0; 56 | } 57 | 58 | }); 59 | 60 | }); 61 | ``` 62 | 63 | ### Referencia de la API 64 | [Sensor](http://johnny-five.io/api/sensor) -------------------------------------------------------------------------------- /examples/thermistor/thermistor.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | let board = new five.Board(); 4 | 5 | function voltToTemp(resistance) { 6 | let tempC; 7 | const thermistorOhm = 10000, 8 | nominalTemp = 25, 9 | betaCoef = 3435, 10 | resistorOhm = 10000; 11 | 12 | resistance = 1023 / resistance - 1; 13 | resistance = resistorOhm / resistance; 14 | console.log(`Resistance: ${resistance}`); 15 | 16 | 17 | 18 | tempC = resistance / thermistorOhm; // (R/Ro) 19 | tempC = parseFloat(Math.log(tempC).toFixed(2)); // ln(R/Ro) 20 | tempC /= betaCoef; // 1/B * ln(R/Ro) 21 | tempC += 1 / (nominalTemp + 273.15); // + (1/To) 22 | tempC = 1 / tempC; // Invert 23 | tempC -= 273.15; // convert to C 24 | 25 | console.log(`Temperature: ${parseFloat(tempC.toFixed(2))}`); 26 | } 27 | 28 | board.on('ready', () => { 29 | 30 | let thermistor = new five.Sensor({ 31 | pin: 'A0', 32 | freq: 1000 33 | }), 34 | average = 0, 35 | count = 0; 36 | 37 | thermistor.on('data', (data) => { 38 | if (count < 5) { 39 | average += data; 40 | count++; 41 | } else { 42 | voltToTemp(average/5); 43 | count = 0; 44 | average = 0; 45 | } 46 | 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /examples/two_color_leds/README.md: -------------------------------------------------------------------------------- 1 | ## Ejemplo LED de 2 colores 2 | 3 | ### Cableado 4 | ![Led de 2 colores](../../assets/two_color_led_bb.png) 5 | ### Código 6 | ```javascript 7 | const five = require('johnny-five'); 8 | 9 | let board = new five.Board(); 10 | 11 | board.on('ready', () => { 12 | 13 | let greenLed = new five.Led(10), 14 | redLed = new five.Led(11), 15 | direction = 1; 16 | greenLed.brightness(255); 17 | redLed.brightness(0); 18 | board.loop(5000, () => { 19 | 20 | if (direction > 0) { 21 | greenLed.fade(0, 4000); 22 | redLed.fade(255, 4000); 23 | direction = -1; 24 | } else { 25 | greenLed.fade(255, 4000); 26 | redLed.fade(0, 4000); 27 | direction = 1; 28 | } 29 | }); 30 | }); 31 | ``` 32 | 33 | ### Referencia de la API 34 | [Leds](http://johnny-five.io/api/led) 35 | -------------------------------------------------------------------------------- /examples/two_color_leds/two_colors_led.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | let board = new five.Board(); 4 | 5 | board.on('ready', () => { 6 | 7 | let greenLed = new five.Led(10), 8 | redLed = new five.Led(11), 9 | direction = 1; 10 | greenLed.brightness(255); 11 | redLed.brightness(0); 12 | board.loop(5000, () => { 13 | 14 | if (direction > 0) { 15 | greenLed.fade(0, 4000); 16 | redLed.fade(255, 4000); 17 | direction = -1; 18 | } else { 19 | greenLed.fade(255, 4000); 20 | redLed.fade(0, 4000); 21 | direction = 1; 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /firmwares/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Firmata 4 | 5 | ### Flasheando un Arduino 6 | Instalar firmata en un arduino es extremadamente simple usando [nodebots-interchage](https://github.com/johnny-five-io/nodebots-interchange/). 7 | 8 | ##### desde nuestro proyecto: 9 | $ npm run flash 10 | 11 | ##### desde otro lugar: 12 | $ npm install nodebots-interchange -g 13 | $ interchange install --interactive 14 | 15 | En ambos casos se deben seguir los pasos y seleccionar las opciones correctas para cada dispositivo. 16 | 17 | 18 | ### Flasheando un NodeMCU, ESP8266 o similar. 19 | Para esto deberemos tener instalada la IDE de Arduino (1.6.4 o posterior). 20 | 21 | 1. Abrir la IDE de Arduino e ir a **File > Preferences** 22 | 2. Cerca del final encontraremos una caja de texto llamada "Additional Board Manager URLs". Allí deberemos pegar lo siguiente: 23 | `http://arduino.esp8266.com/stable/package_esp8266com_index.json` 24 | 3. Luego iremos al administrador de dispositivos en **Tools > Boards > Boards Manager**. Alli encontraremos varias opciones ademas de las estandares de Arduino. Deberemos buscar por **esp8266**. Seleccionar esa entrada e instalar. 25 | 4. Por ultimo seleccionar la version adecuada de ESP8266 desde el menu **Tools > Boards**. 26 | 27 | [Archivos de ejemplo](./standardFirmataWiFi) de firmata Wi Fi. 28 | 29 | 30 | ## License 31 | Licensed under the MIT license. 32 | -------------------------------------------------------------------------------- /firmwares/standardFirmataWiFi/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Standard Firmata WiFi 4 | Estos serían los cambios minimos necesarios para poder conectar un dispositivo con johnny five por medio de una red WiFi. 5 | 6 | ### Cambios en los archivos 7 | Los dispositivos pueden usar la mayoría de los tipos de concción para establecer un link con un host y proveer de transporte para la comunicación serial requerida por firmata. 8 | En este ejemplo nosotros vamos a utilizar la version WIFI de StandardFirmata, en combinación con un controlador de wifi ESP8266. 9 | 10 | Para establecer el link necesitamos definir nuestros parámetros de configuración de la red [wifiConf](./wifiConf.h). Esta versión de firmata tiene soporte principalmente dispositivos Arduino, y dado que el ESP8266 tiene un rango diferente en la señal PWM, deberemos hacer unos ajustes al sketch principal [StandardFirmataWiFi](./StandardFirmataWiFi.ino) para resolver este inconveniente. 11 | 12 | - wifiConfig.h: 13 | - [Linea 114](./wifiConfig.h#L114): Colocamos nuesta IP separada por comas. 14 | - [Linea 119](./wifiConfig.h#L119): Definimos nuestro SSID (nombre de la WiFi). 15 | - [Linea 133](./wifiConfig.h#L133): Aseguremosnos de que en este puerto es donde el script de Johnny Five está escuchando por nuestro dispositivo. 16 | - [Linea 151](./wifiConfig.h#L151): Aqui colocamos la contraseña de nuestra WiFi. 17 | - StandardFirmataWiFi.ino: 18 | - [Linea 998](./StandardFirmataWiFi.ino#L998): Aqui agregaremos una linea para definir el rango analogo a 255. 19 | 20 | **Según la versión del StandardFirmataWiFi el número de linea en cada archivo puede variar, solo se deben utilizar los ejemplos dados como referencia**. 21 | 22 | ## License 23 | Licensed under the MIT license. 24 | -------------------------------------------------------------------------------- /firmwares/standardFirmataWiFi/StandardFirmataWiFi.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Firmata is a generic protocol for communicating with microcontrollers 3 | from software on a host computer. It is intended to work with 4 | any host computer software package. 5 | 6 | To download a host software package, please click on the following link 7 | to open the list of Firmata client libraries in your default browser. 8 | 9 | https://github.com/firmata/arduino#firmata-client-libraries 10 | 11 | Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. 12 | Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved. 13 | Copyright (C) 2009 Shigeru Kobayashi. All rights reserved. 14 | Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved. 15 | Copyright (C) 2015-2016 Jesse Frush. All rights reserved. 16 | Copyright (C) 2016 Jens B. All rights reserved. 17 | 18 | This library is free software; you can redistribute it and/or 19 | modify it under the terms of the GNU Lesser General Public 20 | License as published by the Free Software Foundation; either 21 | version 2.1 of the License, or (at your option) any later version. 22 | 23 | See file LICENSE.txt for further informations on licensing terms. 24 | 25 | Last updated October 16th, 2016 26 | */ 27 | 28 | /* 29 | README 30 | 31 | StandardFirmataWiFi enables the use of Firmata over a TCP connection. It can be configured as 32 | either a TCP server or TCP client. 33 | 34 | To use StandardFirmataWiFi you will need to have one of the following 35 | boards or shields: 36 | 37 | - Arduino WiFi Shield (or clone) 38 | - Arduino WiFi Shield 101 39 | - Arduino MKR1000 board 40 | - ESP8266 WiFi board compatible with ESP8266 Arduino core 41 | 42 | Follow the instructions in the wifiConfig.h file (wifiConfig.h tab in Arduino IDE) to 43 | configure your particular hardware. 44 | 45 | Dependencies: 46 | - WiFi Shield 101 requires version 0.7.0 or higher of the WiFi101 library (available in Arduino 47 | 1.6.8 or higher, or update the library via the Arduino Library Manager or clone from source: 48 | https://github.com/arduino-libraries/WiFi101) 49 | - ESP8266 requires the Arduino ESP8266 core v2.1.0 or higher which can be obtained here: 50 | https://github.com/esp8266/Arduino 51 | 52 | In order to use the WiFi Shield 101 with Firmata you will need a board with at least 35k of Flash 53 | memory. This means you cannot use the WiFi Shield 101 with an Arduino Uno or any other 54 | ATmega328p-based microcontroller or with an Arduino Leonardo or other ATmega32u4-based 55 | microcontroller. Some boards that will work are: 56 | 57 | - Arduino Zero 58 | - Arduino Due 59 | - Arduino 101 60 | - Arduino Mega 61 | 62 | NOTE: If you are using an Arduino WiFi (legacy) shield you cannot use the following pins on 63 | the following boards. Firmata will ignore any requests to use these pins: 64 | 65 | - Arduino Uno or other ATMega328 boards: (D4, D7, D10, D11, D12, D13) 66 | - Arduino Mega: (D4, D7, D10, D50, D51, D52, D53) 67 | - Arduino Due, Zero or Leonardo: (D4, D7, D10) 68 | 69 | If you are using an Arduino WiFi 101 shield you cannot use the following pins on the following 70 | boards: 71 | 72 | - Arduino Due or Zero: (D5, D7, D10) 73 | - Arduino Mega: (D5, D7, D10, D50, D52, D53) 74 | */ 75 | 76 | #include 77 | #include 78 | #include 79 | 80 | /* 81 | * Uncomment the #define SERIAL_DEBUG line below to receive serial output messages relating to your 82 | * connection that may help in the event of connection issues. If defined, some boards may not begin 83 | * executing this sketch until the Serial console is opened. 84 | */ 85 | //#define SERIAL_DEBUG 86 | #include "utility/firmataDebug.h" 87 | 88 | /* 89 | * Uncomment the following include to enable interfacing with Serial devices via hardware or 90 | * software serial. 91 | */ 92 | // In order to use software serial, you will need to compile this sketch with 93 | // Arduino IDE v1.6.6 or higher. Hardware serial should work back to Arduino 1.0. 94 | //#include "utility/SerialFirmata.h" 95 | 96 | // follow the instructions in wifiConfig.h to configure your particular hardware 97 | #include "wifiConfig.h" 98 | 99 | #define I2C_WRITE B00000000 100 | #define I2C_READ B00001000 101 | #define I2C_READ_CONTINUOUSLY B00010000 102 | #define I2C_STOP_READING B00011000 103 | #define I2C_READ_WRITE_MODE_MASK B00011000 104 | #define I2C_10BIT_ADDRESS_MODE_MASK B00100000 105 | #define I2C_END_TX_MASK B01000000 106 | #define I2C_STOP_TX 1 107 | #define I2C_RESTART_TX 0 108 | #define I2C_MAX_QUERIES 8 109 | #define I2C_REGISTER_NOT_SPECIFIED -1 110 | 111 | // the minimum interval for sampling analog input 112 | #define MINIMUM_SAMPLING_INTERVAL 1 113 | 114 | #define MAX_CONN_ATTEMPTS 20 // [500 ms] -> 10 s 115 | 116 | /*============================================================================== 117 | * GLOBAL VARIABLES 118 | *============================================================================*/ 119 | 120 | #ifdef FIRMATA_SERIAL_FEATURE 121 | SerialFirmata serialFeature; 122 | #endif 123 | 124 | #ifdef STATIC_IP_ADDRESS 125 | IPAddress local_ip(STATIC_IP_ADDRESS); 126 | #endif 127 | #ifdef SUBNET_MASK 128 | IPAddress subnet(SUBNET_MASK); 129 | #endif 130 | #ifdef GATEWAY_IP_ADDRESS 131 | IPAddress gateway(GATEWAY_IP_ADDRESS); 132 | #endif 133 | 134 | int connectionAttempts = 0; 135 | bool streamConnected = false; 136 | 137 | /* analog inputs */ 138 | int analogInputsToReport = 0; // bitwise array to store pin reporting 139 | 140 | /* digital input ports */ 141 | byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence 142 | byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent 143 | 144 | /* pins configuration */ 145 | byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else 146 | 147 | /* timer variables */ 148 | unsigned long currentMillis; // store the current value from millis() 149 | unsigned long previousMillis; // for comparison with currentMillis 150 | unsigned int samplingInterval = 19; // how often to sample analog inputs (in ms) 151 | 152 | /* i2c data */ 153 | struct i2c_device_info { 154 | byte addr; 155 | int reg; 156 | byte bytes; 157 | byte stopTX; 158 | }; 159 | 160 | /* for i2c read continuous mode */ 161 | i2c_device_info query[I2C_MAX_QUERIES]; 162 | 163 | byte i2cRxData[64]; 164 | boolean isI2CEnabled = false; 165 | signed char queryIndex = -1; 166 | // default delay time between i2c read request and Wire.requestFrom() 167 | unsigned int i2cReadDelayTime = 0; 168 | 169 | Servo servos[MAX_SERVOS]; 170 | byte servoPinMap[TOTAL_PINS]; 171 | byte detachedServos[MAX_SERVOS]; 172 | byte detachedServoCount = 0; 173 | byte servoCount = 0; 174 | 175 | boolean isResetting = false; 176 | 177 | // Forward declare a few functions to avoid compiler errors with older versions 178 | // of the Arduino IDE. 179 | void setPinModeCallback(byte, int); 180 | void reportAnalogCallback(byte analogPin, int value); 181 | void sysexCallback(byte, byte, byte*); 182 | 183 | /* utility functions */ 184 | void wireWrite(byte data) 185 | { 186 | #if ARDUINO >= 100 187 | Wire.write((byte)data); 188 | #else 189 | Wire.send(data); 190 | #endif 191 | } 192 | 193 | byte wireRead(void) 194 | { 195 | #if ARDUINO >= 100 196 | return Wire.read(); 197 | #else 198 | return Wire.receive(); 199 | #endif 200 | } 201 | 202 | /*============================================================================== 203 | * FUNCTIONS 204 | *============================================================================*/ 205 | 206 | void attachServo(byte pin, int minPulse, int maxPulse) 207 | { 208 | if (servoCount < MAX_SERVOS) { 209 | // reuse indexes of detached servos until all have been reallocated 210 | if (detachedServoCount > 0) { 211 | servoPinMap[pin] = detachedServos[detachedServoCount - 1]; 212 | if (detachedServoCount > 0) detachedServoCount--; 213 | } else { 214 | servoPinMap[pin] = servoCount; 215 | servoCount++; 216 | } 217 | if (minPulse > 0 && maxPulse > 0) { 218 | servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); 219 | } else { 220 | servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin)); 221 | } 222 | } else { 223 | Firmata.sendString("Max servos attached"); 224 | } 225 | } 226 | 227 | void detachServo(byte pin) 228 | { 229 | servos[servoPinMap[pin]].detach(); 230 | // if we're detaching the last servo, decrement the count 231 | // otherwise store the index of the detached servo 232 | if (servoPinMap[pin] == servoCount && servoCount > 0) { 233 | servoCount--; 234 | } else if (servoCount > 0) { 235 | // keep track of detached servos because we want to reuse their indexes 236 | // before incrementing the count of attached servos 237 | detachedServoCount++; 238 | detachedServos[detachedServoCount - 1] = servoPinMap[pin]; 239 | } 240 | 241 | servoPinMap[pin] = 255; 242 | } 243 | 244 | void enableI2CPins() 245 | { 246 | byte i; 247 | // is there a faster way to do this? would probaby require importing 248 | // Arduino.h to get SCL and SDA pins 249 | for (i = 0; i < TOTAL_PINS; i++) { 250 | if (IS_PIN_I2C(i)) { 251 | // mark pins as i2c so they are ignore in non i2c data requests 252 | setPinModeCallback(i, PIN_MODE_I2C); 253 | } 254 | } 255 | 256 | isI2CEnabled = true; 257 | 258 | Wire.begin(); 259 | } 260 | 261 | /* disable the i2c pins so they can be used for other functions */ 262 | void disableI2CPins() { 263 | isI2CEnabled = false; 264 | // disable read continuous mode for all devices 265 | queryIndex = -1; 266 | } 267 | 268 | void readAndReportData(byte address, int theRegister, byte numBytes, byte stopTX) { 269 | // allow I2C requests that don't require a register read 270 | // for example, some devices using an interrupt pin to signify new data available 271 | // do not always require the register read so upon interrupt you call Wire.requestFrom() 272 | if (theRegister != I2C_REGISTER_NOT_SPECIFIED) { 273 | Wire.beginTransmission(address); 274 | wireWrite((byte)theRegister); 275 | Wire.endTransmission(stopTX); // default = true 276 | // do not set a value of 0 277 | if (i2cReadDelayTime > 0) { 278 | // delay is necessary for some devices such as WiiNunchuck 279 | delayMicroseconds(i2cReadDelayTime); 280 | } 281 | } else { 282 | theRegister = 0; // fill the register with a dummy value 283 | } 284 | 285 | Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom 286 | 287 | // check to be sure correct number of bytes were returned by slave 288 | if (numBytes < Wire.available()) { 289 | Firmata.sendString("I2C: Too many bytes received"); 290 | } else if (numBytes > Wire.available()) { 291 | Firmata.sendString("I2C: Too few bytes received"); 292 | } 293 | 294 | i2cRxData[0] = address; 295 | i2cRxData[1] = theRegister; 296 | 297 | for (int i = 0; i < numBytes && Wire.available(); i++) { 298 | i2cRxData[2 + i] = wireRead(); 299 | } 300 | 301 | // send slave address, register and received bytes 302 | Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData); 303 | } 304 | 305 | void outputPort(byte portNumber, byte portValue, byte forceSend) 306 | { 307 | // pins not configured as INPUT are cleared to zeros 308 | portValue = portValue & portConfigInputs[portNumber]; 309 | // only send if the value is different than previously sent 310 | if (forceSend || previousPINs[portNumber] != portValue) { 311 | Firmata.sendDigitalPort(portNumber, portValue); 312 | previousPINs[portNumber] = portValue; 313 | } 314 | } 315 | 316 | /* ----------------------------------------------------------------------------- 317 | * check all the active digital inputs for change of state, then add any events 318 | * to the Stream output queue using Stream.write() */ 319 | void checkDigitalInputs(void) 320 | { 321 | /* Using non-looping code allows constants to be given to readPort(). 322 | * The compiler will apply substantial optimizations if the inputs 323 | * to readPort() are compile-time constants. */ 324 | if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false); 325 | if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false); 326 | if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false); 327 | if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false); 328 | if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false); 329 | if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false); 330 | if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false); 331 | if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false); 332 | if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false); 333 | if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false); 334 | if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false); 335 | if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false); 336 | if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false); 337 | if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false); 338 | if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false); 339 | if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false); 340 | } 341 | 342 | // ----------------------------------------------------------------------------- 343 | // function forward declarations for xtensa compiler (ESP8266) 344 | void enableI2CPins(); 345 | void disableI2CPins(); 346 | void reportAnalogCallback(byte analogPin, int value); 347 | 348 | // ----------------------------------------------------------------------------- 349 | /* sets the pin mode to the correct state and sets the relevant bits in the 350 | * two bit-arrays that track Digital I/O and PWM status 351 | */ 352 | void setPinModeCallback(byte pin, int mode) 353 | { 354 | if (Firmata.getPinMode(pin) == PIN_MODE_IGNORE) 355 | return; 356 | 357 | if (Firmata.getPinMode(pin) == PIN_MODE_I2C && isI2CEnabled && mode != PIN_MODE_I2C) { 358 | // disable i2c so pins can be used for other functions 359 | // the following if statements should reconfigure the pins properly 360 | disableI2CPins(); 361 | } 362 | if (IS_PIN_DIGITAL(pin) && mode != PIN_MODE_SERVO) { 363 | if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { 364 | detachServo(pin); 365 | } 366 | } 367 | if (IS_PIN_ANALOG(pin)) { 368 | reportAnalogCallback(PIN_TO_ANALOG(pin), mode == PIN_MODE_ANALOG ? 1 : 0); // turn on/off reporting 369 | } 370 | if (IS_PIN_DIGITAL(pin)) { 371 | if (mode == INPUT || mode == PIN_MODE_PULLUP) { 372 | portConfigInputs[pin / 8] |= (1 << (pin & 7)); 373 | } else { 374 | portConfigInputs[pin / 8] &= ~(1 << (pin & 7)); 375 | } 376 | } 377 | Firmata.setPinState(pin, 0); 378 | switch (mode) { 379 | case PIN_MODE_ANALOG: 380 | if (IS_PIN_ANALOG(pin)) { 381 | if (IS_PIN_DIGITAL(pin)) { 382 | pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver 383 | #if ARDUINO <= 100 384 | // deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6 385 | digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups 386 | #endif 387 | } 388 | Firmata.setPinMode(pin, PIN_MODE_ANALOG); 389 | } 390 | break; 391 | case INPUT: 392 | if (IS_PIN_DIGITAL(pin)) { 393 | pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver 394 | #if ARDUINO <= 100 395 | // deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6 396 | digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups 397 | #endif 398 | Firmata.setPinMode(pin, INPUT); 399 | } 400 | break; 401 | case PIN_MODE_PULLUP: 402 | if (IS_PIN_DIGITAL(pin)) { 403 | pinMode(PIN_TO_DIGITAL(pin), INPUT_PULLUP); 404 | Firmata.setPinMode(pin, PIN_MODE_PULLUP); 405 | Firmata.setPinState(pin, 1); 406 | } 407 | break; 408 | case OUTPUT: 409 | if (IS_PIN_DIGITAL(pin)) { 410 | if (Firmata.getPinMode(pin) == PIN_MODE_PWM) { 411 | // Disable PWM if pin mode was previously set to PWM. 412 | digitalWrite(PIN_TO_DIGITAL(pin), LOW); 413 | } 414 | pinMode(PIN_TO_DIGITAL(pin), OUTPUT); 415 | Firmata.setPinMode(pin, OUTPUT); 416 | } 417 | break; 418 | case PIN_MODE_PWM: 419 | if (IS_PIN_PWM(pin)) { 420 | pinMode(PIN_TO_PWM(pin), OUTPUT); 421 | analogWrite(PIN_TO_PWM(pin), 0); 422 | Firmata.setPinMode(pin, PIN_MODE_PWM); 423 | } 424 | break; 425 | case PIN_MODE_SERVO: 426 | if (IS_PIN_DIGITAL(pin)) { 427 | Firmata.setPinMode(pin, PIN_MODE_SERVO); 428 | if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) { 429 | // pass -1 for min and max pulse values to use default values set 430 | // by Servo library 431 | attachServo(pin, -1, -1); 432 | } 433 | } 434 | break; 435 | case PIN_MODE_I2C: 436 | if (IS_PIN_I2C(pin)) { 437 | // mark the pin as i2c 438 | // the user must call I2C_CONFIG to enable I2C for a device 439 | Firmata.setPinMode(pin, PIN_MODE_I2C); 440 | } 441 | break; 442 | case PIN_MODE_SERIAL: 443 | #ifdef FIRMATA_SERIAL_FEATURE 444 | serialFeature.handlePinMode(pin, PIN_MODE_SERIAL); 445 | #endif 446 | break; 447 | default: 448 | Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM 449 | } 450 | // TODO: save status to EEPROM here, if changed 451 | } 452 | 453 | /* 454 | * Sets the value of an individual pin. Useful if you want to set a pin value but 455 | * are not tracking the digital port state. 456 | * Can only be used on pins configured as OUTPUT. 457 | * Cannot be used to enable pull-ups on Digital INPUT pins. 458 | */ 459 | void setPinValueCallback(byte pin, int value) 460 | { 461 | if (pin < TOTAL_PINS && IS_PIN_DIGITAL(pin)) { 462 | if (Firmata.getPinMode(pin) == OUTPUT) { 463 | Firmata.setPinState(pin, value); 464 | digitalWrite(PIN_TO_DIGITAL(pin), value); 465 | } 466 | } 467 | } 468 | 469 | void analogWriteCallback(byte pin, int value) 470 | { 471 | if (pin < TOTAL_PINS) { 472 | switch (Firmata.getPinMode(pin)) { 473 | case PIN_MODE_SERVO: 474 | if (IS_PIN_DIGITAL(pin)) 475 | servos[servoPinMap[pin]].write(value); 476 | Firmata.setPinState(pin, value); 477 | break; 478 | case PIN_MODE_PWM: 479 | if (IS_PIN_PWM(pin)) 480 | analogWrite(PIN_TO_PWM(pin), value); 481 | Firmata.setPinState(pin, value); 482 | break; 483 | } 484 | } 485 | } 486 | 487 | void digitalWriteCallback(byte port, int value) 488 | { 489 | byte pin, lastPin, pinValue, mask = 1, pinWriteMask = 0; 490 | 491 | if (port < TOTAL_PORTS) { 492 | // create a mask of the pins on this port that are writable. 493 | lastPin = port * 8 + 8; 494 | if (lastPin > TOTAL_PINS) lastPin = TOTAL_PINS; 495 | for (pin = port * 8; pin < lastPin; pin++) { 496 | // do not disturb non-digital pins (eg, Rx & Tx) 497 | if (IS_PIN_DIGITAL(pin)) { 498 | // do not touch pins in PWM, ANALOG, SERVO or other modes 499 | if (Firmata.getPinMode(pin) == OUTPUT || Firmata.getPinMode(pin) == INPUT) { 500 | pinValue = ((byte)value & mask) ? 1 : 0; 501 | if (Firmata.getPinMode(pin) == OUTPUT) { 502 | pinWriteMask |= mask; 503 | } else if (Firmata.getPinMode(pin) == INPUT && pinValue == 1 && Firmata.getPinState(pin) != 1) { 504 | // only handle INPUT here for backwards compatibility 505 | #if ARDUINO > 100 506 | pinMode(pin, INPUT_PULLUP); 507 | #else 508 | // only write to the INPUT pin to enable pullups if Arduino v1.0.0 or earlier 509 | pinWriteMask |= mask; 510 | #endif 511 | } 512 | Firmata.setPinState(pin, pinValue); 513 | } 514 | } 515 | mask = mask << 1; 516 | } 517 | writePort(port, (byte)value, pinWriteMask); 518 | } 519 | } 520 | 521 | 522 | // ----------------------------------------------------------------------------- 523 | /* sets bits in a bit array (int) to toggle the reporting of the analogIns 524 | */ 525 | //void FirmataClass::setAnalogPinReporting(byte pin, byte state) { 526 | //} 527 | void reportAnalogCallback(byte analogPin, int value) 528 | { 529 | if (analogPin < TOTAL_ANALOG_PINS) { 530 | if (value == 0) { 531 | analogInputsToReport = analogInputsToReport & ~ (1 << analogPin); 532 | } else { 533 | analogInputsToReport = analogInputsToReport | (1 << analogPin); 534 | // prevent during system reset or all analog pin values will be reported 535 | // which may report noise for unconnected analog pins 536 | if (!isResetting) { 537 | // Send pin value immediately. This is helpful when connected via 538 | // ethernet, wi-fi or bluetooth so pin states can be known upon 539 | // reconnecting. 540 | Firmata.sendAnalog(analogPin, analogRead(analogPin)); 541 | } 542 | } 543 | } 544 | // TODO: save status to EEPROM here, if changed 545 | } 546 | 547 | void reportDigitalCallback(byte port, int value) 548 | { 549 | if (port < TOTAL_PORTS) { 550 | reportPINs[port] = (byte)value; 551 | // Send port value immediately. This is helpful when connected via 552 | // ethernet, wi-fi or bluetooth so pin states can be known upon 553 | // reconnecting. 554 | if (value) outputPort(port, readPort(port, portConfigInputs[port]), true); 555 | } 556 | // do not disable analog reporting on these 8 pins, to allow some 557 | // pins used for digital, others analog. Instead, allow both types 558 | // of reporting to be enabled, but check if the pin is configured 559 | // as analog when sampling the analog inputs. Likewise, while 560 | // scanning digital pins, portConfigInputs will mask off values from any 561 | // pins configured as analog 562 | } 563 | 564 | /*============================================================================== 565 | * SYSEX-BASED commands 566 | *============================================================================*/ 567 | 568 | void sysexCallback(byte command, byte argc, byte *argv) 569 | { 570 | byte mode; 571 | byte stopTX; 572 | byte slaveAddress; 573 | byte data; 574 | int slaveRegister; 575 | unsigned int delayTime; 576 | 577 | switch (command) { 578 | case I2C_REQUEST: 579 | mode = argv[1] & I2C_READ_WRITE_MODE_MASK; 580 | if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) { 581 | Firmata.sendString("10-bit addressing not supported"); 582 | return; 583 | } 584 | else { 585 | slaveAddress = argv[0]; 586 | } 587 | 588 | // need to invert the logic here since 0 will be default for client 589 | // libraries that have not updated to add support for restart tx 590 | if (argv[1] & I2C_END_TX_MASK) { 591 | stopTX = I2C_RESTART_TX; 592 | } 593 | else { 594 | stopTX = I2C_STOP_TX; // default 595 | } 596 | 597 | switch (mode) { 598 | case I2C_WRITE: 599 | Wire.beginTransmission(slaveAddress); 600 | for (byte i = 2; i < argc; i += 2) { 601 | data = argv[i] + (argv[i + 1] << 7); 602 | wireWrite(data); 603 | } 604 | Wire.endTransmission(); 605 | delayMicroseconds(70); 606 | break; 607 | case I2C_READ: 608 | if (argc == 6) { 609 | // a slave register is specified 610 | slaveRegister = argv[2] + (argv[3] << 7); 611 | data = argv[4] + (argv[5] << 7); // bytes to read 612 | } 613 | else { 614 | // a slave register is NOT specified 615 | slaveRegister = I2C_REGISTER_NOT_SPECIFIED; 616 | data = argv[2] + (argv[3] << 7); // bytes to read 617 | } 618 | readAndReportData(slaveAddress, (int)slaveRegister, data, stopTX); 619 | break; 620 | case I2C_READ_CONTINUOUSLY: 621 | if ((queryIndex + 1) >= I2C_MAX_QUERIES) { 622 | // too many queries, just ignore 623 | Firmata.sendString("too many queries"); 624 | break; 625 | } 626 | if (argc == 6) { 627 | // a slave register is specified 628 | slaveRegister = argv[2] + (argv[3] << 7); 629 | data = argv[4] + (argv[5] << 7); // bytes to read 630 | } 631 | else { 632 | // a slave register is NOT specified 633 | slaveRegister = (int)I2C_REGISTER_NOT_SPECIFIED; 634 | data = argv[2] + (argv[3] << 7); // bytes to read 635 | } 636 | queryIndex++; 637 | query[queryIndex].addr = slaveAddress; 638 | query[queryIndex].reg = slaveRegister; 639 | query[queryIndex].bytes = data; 640 | query[queryIndex].stopTX = stopTX; 641 | break; 642 | case I2C_STOP_READING: 643 | byte queryIndexToSkip; 644 | // if read continuous mode is enabled for only 1 i2c device, disable 645 | // read continuous reporting for that device 646 | if (queryIndex <= 0) { 647 | queryIndex = -1; 648 | } else { 649 | queryIndexToSkip = 0; 650 | // if read continuous mode is enabled for multiple devices, 651 | // determine which device to stop reading and remove it's data from 652 | // the array, shifiting other array data to fill the space 653 | for (byte i = 0; i < queryIndex + 1; i++) { 654 | if (query[i].addr == slaveAddress) { 655 | queryIndexToSkip = i; 656 | break; 657 | } 658 | } 659 | 660 | for (byte i = queryIndexToSkip; i < queryIndex + 1; i++) { 661 | if (i < I2C_MAX_QUERIES) { 662 | query[i].addr = query[i + 1].addr; 663 | query[i].reg = query[i + 1].reg; 664 | query[i].bytes = query[i + 1].bytes; 665 | query[i].stopTX = query[i + 1].stopTX; 666 | } 667 | } 668 | queryIndex--; 669 | } 670 | break; 671 | default: 672 | break; 673 | } 674 | break; 675 | case I2C_CONFIG: 676 | delayTime = (argv[0] + (argv[1] << 7)); 677 | 678 | if (delayTime > 0) { 679 | i2cReadDelayTime = delayTime; 680 | } 681 | 682 | if (!isI2CEnabled) { 683 | enableI2CPins(); 684 | } 685 | 686 | break; 687 | case SERVO_CONFIG: 688 | if (argc > 4) { 689 | // these vars are here for clarity, they'll optimized away by the compiler 690 | byte pin = argv[0]; 691 | int minPulse = argv[1] + (argv[2] << 7); 692 | int maxPulse = argv[3] + (argv[4] << 7); 693 | 694 | if (IS_PIN_DIGITAL(pin)) { 695 | if (servoPinMap[pin] < MAX_SERVOS && servos[servoPinMap[pin]].attached()) { 696 | detachServo(pin); 697 | } 698 | attachServo(pin, minPulse, maxPulse); 699 | setPinModeCallback(pin, PIN_MODE_SERVO); 700 | } 701 | } 702 | break; 703 | case SAMPLING_INTERVAL: 704 | if (argc > 1) { 705 | samplingInterval = argv[0] + (argv[1] << 7); 706 | if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) { 707 | samplingInterval = MINIMUM_SAMPLING_INTERVAL; 708 | } 709 | } else { 710 | //Firmata.sendString("Not enough data"); 711 | } 712 | break; 713 | case EXTENDED_ANALOG: 714 | if (argc > 1) { 715 | int val = argv[1]; 716 | if (argc > 2) val |= (argv[2] << 7); 717 | if (argc > 3) val |= (argv[3] << 14); 718 | analogWriteCallback(argv[0], val); 719 | } 720 | break; 721 | case CAPABILITY_QUERY: 722 | Firmata.write(START_SYSEX); 723 | Firmata.write(CAPABILITY_RESPONSE); 724 | for (byte pin = 0; pin < TOTAL_PINS; pin++) { 725 | if (IS_PIN_DIGITAL(pin)) { 726 | Firmata.write((byte)INPUT); 727 | Firmata.write(1); 728 | Firmata.write((byte)PIN_MODE_PULLUP); 729 | Firmata.write(1); 730 | Firmata.write((byte)OUTPUT); 731 | Firmata.write(1); 732 | } 733 | if (IS_PIN_ANALOG(pin)) { 734 | Firmata.write(PIN_MODE_ANALOG); 735 | Firmata.write(10); // 10 = 10-bit resolution 736 | } 737 | if (IS_PIN_PWM(pin)) { 738 | Firmata.write(PIN_MODE_PWM); 739 | Firmata.write(DEFAULT_PWM_RESOLUTION); 740 | } 741 | if (IS_PIN_DIGITAL(pin)) { 742 | Firmata.write(PIN_MODE_SERVO); 743 | Firmata.write(14); 744 | } 745 | if (IS_PIN_I2C(pin)) { 746 | Firmata.write(PIN_MODE_I2C); 747 | Firmata.write(1); // TODO: could assign a number to map to SCL or SDA 748 | } 749 | #ifdef FIRMATA_SERIAL_FEATURE 750 | serialFeature.handleCapability(pin); 751 | #endif 752 | Firmata.write(127); 753 | } 754 | Firmata.write(END_SYSEX); 755 | break; 756 | case PIN_STATE_QUERY: 757 | if (argc > 0) { 758 | byte pin = argv[0]; 759 | Firmata.write(START_SYSEX); 760 | Firmata.write(PIN_STATE_RESPONSE); 761 | Firmata.write(pin); 762 | if (pin < TOTAL_PINS) { 763 | Firmata.write(Firmata.getPinMode(pin)); 764 | Firmata.write((byte)Firmata.getPinState(pin) & 0x7F); 765 | if (Firmata.getPinState(pin) & 0xFF80) Firmata.write((byte)(Firmata.getPinState(pin) >> 7) & 0x7F); 766 | if (Firmata.getPinState(pin) & 0xC000) Firmata.write((byte)(Firmata.getPinState(pin) >> 14) & 0x7F); 767 | } 768 | Firmata.write(END_SYSEX); 769 | } 770 | break; 771 | case ANALOG_MAPPING_QUERY: 772 | Firmata.write(START_SYSEX); 773 | Firmata.write(ANALOG_MAPPING_RESPONSE); 774 | for (byte pin = 0; pin < TOTAL_PINS; pin++) { 775 | Firmata.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127); 776 | } 777 | Firmata.write(END_SYSEX); 778 | break; 779 | 780 | case SERIAL_MESSAGE: 781 | #ifdef FIRMATA_SERIAL_FEATURE 782 | serialFeature.handleSysex(command, argc, argv); 783 | #endif 784 | break; 785 | } 786 | } 787 | 788 | /*============================================================================== 789 | * SETUP() 790 | *============================================================================*/ 791 | 792 | void systemResetCallback() 793 | { 794 | isResetting = true; 795 | 796 | // initialize a defalt state 797 | // TODO: option to load config from EEPROM instead of default 798 | 799 | #ifdef FIRMATA_SERIAL_FEATURE 800 | serialFeature.reset(); 801 | #endif 802 | 803 | if (isI2CEnabled) { 804 | disableI2CPins(); 805 | } 806 | 807 | for (byte i = 0; i < TOTAL_PORTS; i++) { 808 | reportPINs[i] = false; // by default, reporting off 809 | portConfigInputs[i] = 0; // until activated 810 | previousPINs[i] = 0; 811 | } 812 | 813 | for (byte i = 0; i < TOTAL_PINS; i++) { 814 | // pins with analog capability default to analog input 815 | // otherwise, pins default to digital output 816 | if (IS_PIN_ANALOG(i)) { 817 | // turns off pullup, configures everything 818 | setPinModeCallback(i, PIN_MODE_ANALOG); 819 | } else if (IS_PIN_DIGITAL(i)) { 820 | // sets the output to 0, configures portConfigInputs 821 | setPinModeCallback(i, OUTPUT); 822 | } 823 | 824 | servoPinMap[i] = 255; 825 | } 826 | // by default, do not report any analog inputs 827 | analogInputsToReport = 0; 828 | 829 | detachedServoCount = 0; 830 | servoCount = 0; 831 | 832 | /* send digital inputs to set the initial state on the host computer, 833 | * since once in the loop(), this firmware will only send on change */ 834 | /* 835 | TODO: this can never execute, since no pins default to digital input 836 | but it will be needed when/if we support EEPROM stored config 837 | for (byte i=0; i < TOTAL_PORTS; i++) { 838 | outputPort(i, readPort(i, portConfigInputs[i]), true); 839 | } 840 | */ 841 | isResetting = false; 842 | } 843 | 844 | /* 845 | * Called when a TCP connection is either connected or disconnected. 846 | * TODO: 847 | * - report connected or reconnected state to host (to be added to protocol) 848 | * - report current state to host (to be added to protocol) 849 | */ 850 | void hostConnectionCallback(byte state) 851 | { 852 | switch (state) { 853 | case HOST_CONNECTION_CONNECTED: 854 | DEBUG_PRINTLN( "TCP connection established" ); 855 | break; 856 | case HOST_CONNECTION_DISCONNECTED: 857 | DEBUG_PRINTLN( "TCP connection disconnected" ); 858 | break; 859 | } 860 | } 861 | 862 | /* 863 | * Print the status of the WiFi connection. This is the connection to the access point rather 864 | * than the TCP connection. 865 | */ 866 | void printWifiStatus() { 867 | if ( WiFi.status() != WL_CONNECTED ) 868 | { 869 | DEBUG_PRINT( "WiFi connection failed. Status value: " ); 870 | DEBUG_PRINTLN( WiFi.status() ); 871 | } 872 | else 873 | { 874 | // print the SSID of the network you're attached to: 875 | DEBUG_PRINT( "SSID: " ); 876 | DEBUG_PRINTLN( WiFi.SSID() ); 877 | 878 | // print your WiFi shield's IP address: 879 | DEBUG_PRINT( "IP Address: " ); 880 | IPAddress ip = WiFi.localIP(); 881 | DEBUG_PRINTLN( ip ); 882 | 883 | // print the received signal strength: 884 | DEBUG_PRINT( "signal strength (RSSI): " ); 885 | long rssi = WiFi.RSSI(); 886 | DEBUG_PRINT( rssi ); 887 | DEBUG_PRINTLN( " dBm" ); 888 | } 889 | } 890 | 891 | /* 892 | * StandardFirmataWiFi communicates with WiFi shields over SPI. Therefore all 893 | * SPI pins must be set to IGNORE. Otherwise Firmata would break SPI communication. 894 | * Additional pins may also need to be ignored depending on the particular board or 895 | * shield in use. 896 | */ 897 | void ignorePins() 898 | { 899 | #ifdef IS_IGNORE_PIN 900 | for (byte i = 0; i < TOTAL_PINS; i++) { 901 | if (IS_IGNORE_PIN(i)) { 902 | Firmata.setPinMode(i, PIN_MODE_IGNORE); 903 | } 904 | } 905 | #endif 906 | 907 | //Set up controls for the Arduino WiFi Shield SS for the SD Card 908 | #ifdef ARDUINO_WIFI_SHIELD 909 | // Arduino WiFi Shield has SD SS wired to D4 910 | pinMode(PIN_TO_DIGITAL(4), OUTPUT); // switch off SD card bypassing Firmata 911 | digitalWrite(PIN_TO_DIGITAL(4), HIGH); // SS is active low; 912 | 913 | #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 914 | pinMode(PIN_TO_DIGITAL(53), OUTPUT); // configure hardware SS as output on MEGA 915 | #endif //defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 916 | 917 | #endif //ARDUINO_WIFI_SHIELD 918 | } 919 | 920 | void initTransport() 921 | { 922 | // This statement will clarify how a connection is being made 923 | DEBUG_PRINT( "StandardFirmataWiFi will attempt a WiFi connection " ); 924 | #if defined(WIFI_101) 925 | DEBUG_PRINTLN( "using the WiFi 101 library." ); 926 | #elif defined(ARDUINO_WIFI_SHIELD) 927 | DEBUG_PRINTLN( "using the legacy WiFi library." ); 928 | #elif defined(ESP8266_WIFI) 929 | DEBUG_PRINTLN( "using the ESP8266 WiFi library." ); 930 | #elif defined(HUZZAH_WIFI) 931 | DEBUG_PRINTLN( "using the HUZZAH WiFi library." ); 932 | //else should never happen here as error-checking in wifiConfig.h will catch this 933 | #endif //defined(WIFI_101) 934 | 935 | // Configure WiFi IP Address 936 | #ifdef STATIC_IP_ADDRESS 937 | DEBUG_PRINT( "Using static IP: " ); 938 | DEBUG_PRINTLN( local_ip ); 939 | #if defined(ESP8266_WIFI) || (defined(SUBNET_MASK) && defined(GATEWAY_IP_ADDRESS)) 940 | stream.config( local_ip , gateway, subnet ); 941 | #else 942 | // you can also provide a static IP in the begin() functions, but this simplifies 943 | // ifdef logic in this sketch due to support for all different encryption types. 944 | stream.config( local_ip ); 945 | #endif 946 | #else 947 | DEBUG_PRINTLN( "IP will be requested from DHCP ..." ); 948 | #endif 949 | 950 | stream.attach(hostConnectionCallback); 951 | 952 | // Configure WiFi security and initiate WiFi connection 953 | #if defined(WIFI_WEP_SECURITY) 954 | DEBUG_PRINT( "Attempting to connect to WEP SSID: " ); 955 | DEBUG_PRINTLN(ssid); 956 | stream.begin(ssid, wep_index, wep_key); 957 | #elif defined(WIFI_WPA_SECURITY) 958 | DEBUG_PRINT( "Attempting to connect to WPA SSID: " ); 959 | DEBUG_PRINTLN(ssid); 960 | stream.begin(ssid, wpa_passphrase); 961 | #else //OPEN network 962 | DEBUG_PRINTLN( "Attempting to connect to open SSID: " ); 963 | DEBUG_PRINTLN(ssid); 964 | stream.begin(ssid); 965 | #endif //defined(WIFI_WEP_SECURITY) 966 | DEBUG_PRINTLN( "WiFi setup done" ); 967 | 968 | // Wait for connection to access point to be established. 969 | while (WiFi.status() != WL_CONNECTED && ++connectionAttempts <= MAX_CONN_ATTEMPTS) { 970 | delay(500); 971 | DEBUG_PRINT("."); 972 | } 973 | printWifiStatus(); 974 | } 975 | 976 | void initFirmata() 977 | { 978 | Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION); 979 | Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); 980 | Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback); 981 | Firmata.attach(REPORT_ANALOG, reportAnalogCallback); 982 | Firmata.attach(REPORT_DIGITAL, reportDigitalCallback); 983 | Firmata.attach(SET_PIN_MODE, setPinModeCallback); 984 | Firmata.attach(SET_DIGITAL_PIN_VALUE, setPinValueCallback); 985 | Firmata.attach(START_SYSEX, sysexCallback); 986 | Firmata.attach(SYSTEM_RESET, systemResetCallback); 987 | 988 | ignorePins(); 989 | 990 | // Initialize Firmata to use the WiFi stream object as the transport. 991 | Firmata.begin(stream); 992 | systemResetCallback(); // reset to default config 993 | } 994 | 995 | void setup() 996 | { 997 | DEBUG_BEGIN(9600); 998 | analogWriteRange(255); 999 | initTransport(); 1000 | 1001 | initFirmata(); 1002 | } 1003 | 1004 | /*============================================================================== 1005 | * LOOP() 1006 | *============================================================================*/ 1007 | void loop() 1008 | { 1009 | byte pin, analogPin; 1010 | 1011 | /* DIGITALREAD - as fast as possible, check for changes and output them to the 1012 | * Stream buffer using Stream.write() */ 1013 | checkDigitalInputs(); 1014 | 1015 | /* STREAMREAD - processing incoming messagse as soon as possible, while still 1016 | * checking digital inputs. */ 1017 | while (Firmata.available()) { 1018 | Firmata.processInput(); 1019 | } 1020 | 1021 | // TODO - ensure that Stream buffer doesn't go over 60 bytes 1022 | 1023 | currentMillis = millis(); 1024 | if (currentMillis - previousMillis > samplingInterval) { 1025 | previousMillis += samplingInterval; 1026 | /* ANALOGREAD - do all analogReads() at the configured sampling interval */ 1027 | for (pin = 0; pin < TOTAL_PINS; pin++) { 1028 | if (IS_PIN_ANALOG(pin) && Firmata.getPinMode(pin) == PIN_MODE_ANALOG) { 1029 | analogPin = PIN_TO_ANALOG(pin); 1030 | if (analogInputsToReport & (1 << analogPin)) { 1031 | Firmata.sendAnalog(analogPin, analogRead(analogPin)); 1032 | } 1033 | } 1034 | } 1035 | // report i2c data for all device with read continuous mode enabled 1036 | if (queryIndex > -1) { 1037 | for (byte i = 0; i < queryIndex + 1; i++) { 1038 | readAndReportData(query[i].addr, query[i].reg, query[i].bytes, query[i].stopTX); 1039 | } 1040 | } 1041 | } 1042 | 1043 | #ifdef FIRMATA_SERIAL_FEATURE 1044 | serialFeature.update(); 1045 | #endif 1046 | 1047 | stream.maintain(); 1048 | } 1049 | -------------------------------------------------------------------------------- /firmwares/standardFirmataWiFi/wifiConfig.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | * WIFI CONFIGURATION 3 | * 4 | * You must configure your particular hardware. Follow the steps below. 5 | * 6 | * By default, StandardFirmataWiFi is configured as a TCP server, to configure 7 | * as a TCP client, see STEP 2. 8 | *============================================================================*/ 9 | 10 | // STEP 1 [REQUIRED] 11 | // Uncomment / comment the appropriate set of includes for your hardware (OPTION A, B or C) 12 | // Arduino MKR1000 or ESP8266 are enabled by default if compiling for either of those boards. 13 | 14 | /* 15 | * OPTION A: Configure for Arduino MKR1000 or Arduino WiFi Shield 101 16 | * 17 | * This will configure StandardFirmataWiFi to use the WiFi101 library, which works with the 18 | * Arduino WiFi101 shield and devices that have the WiFi101 chip built in (such as the MKR1000). 19 | * It is compatible with 802.11 B/G/N networks. 20 | * 21 | * If you are using the MKR1000 board, continue on to STEP 2. If you are using the WiFi 101 shield, 22 | * follow the instructions below. 23 | * 24 | * To enable for the WiFi 101 shield, uncomment the #define WIFI_101 below and verify the 25 | * #define ARDUINO_WIFI_SHIELD is commented out for OPTION B. 26 | * 27 | * IMPORTANT: You must have the WiFI 101 library installed. To easily install this library, open 28 | * the library manager via: Arduino IDE Menus: Sketch > Include Library > Manage Libraries > filter 29 | * search for "WiFi101" > Select the result and click 'install' 30 | */ 31 | //#define WIFI_101 32 | 33 | //do not modify the following 11 lines 34 | #if defined(ARDUINO_SAMD_MKR1000) && !defined(WIFI_101) 35 | // automatically include if compiling for MRK1000 36 | #define WIFI_101 37 | #endif 38 | #ifdef WIFI_101 39 | #include 40 | #include "utility/WiFiClientStream.h" 41 | #include "utility/WiFiServerStream.h" 42 | #define WIFI_LIB_INCLUDED 43 | #endif 44 | 45 | /* 46 | * OPTION B: Configure for legacy Arduino WiFi shield 47 | * 48 | * This will configure StandardFirmataWiFi to use the original WiFi library (deprecated) provided 49 | * with the Arduino IDE. It is supported by the Arduino WiFi shield (a discontinued product) and 50 | * is compatible with 802.11 B/G networks. 51 | * 52 | * To configure StandardFirmataWiFi to use the legacy Arduino WiFi shield 53 | * leave the #define below uncommented and ensure #define WIFI_101 is commented out for OPTION A. 54 | */ 55 | //#define ARDUINO_WIFI_SHIELD 56 | 57 | //do not modify the following 10 lines 58 | #ifdef ARDUINO_WIFI_SHIELD 59 | #include 60 | #include "utility/WiFiClientStream.h" 61 | #include "utility/WiFiServerStream.h" 62 | #ifdef WIFI_LIB_INCLUDED 63 | #define MULTIPLE_WIFI_LIB_INCLUDES 64 | #else 65 | #define WIFI_LIB_INCLUDED 66 | #endif 67 | #endif 68 | 69 | /* 70 | * OPTION C: Configure for ESP8266 71 | * 72 | * This will configure StandardFirmataWiFi to use the ESP8266WiFi library for boards 73 | * with an ESP8266 chip. It is compatible with 802.11 B/G/N networks. 74 | * 75 | * The appropriate libraries are included automatically when compiling for the ESP8266 so 76 | * continue on to STEP 2. 77 | * 78 | * IMPORTANT: You must have the esp8266 board support installed. To easily install this board see 79 | * the instructions here: https://github.com/esp8266/Arduino#installing-with-boards-manager. 80 | */ 81 | //do not modify the following 14 lines 82 | #ifdef ESP8266 83 | // automatically include if compiling for ESP8266 84 | #define ESP8266_WIFI 85 | #endif 86 | #ifdef ESP8266_WIFI 87 | #include 88 | #include "utility/WiFiClientStream.h" 89 | #include "utility/WiFiServerStream.h" 90 | #ifdef WIFI_LIB_INCLUDED 91 | #define MULTIPLE_WIFI_LIB_INCLUDES 92 | #else 93 | #define WIFI_LIB_INCLUDED 94 | #endif 95 | #endif 96 | 97 | /* 98 | * OPTION D: Configure for HUZZAH 99 | * 100 | * HUZZAH with CC3000 is not yet supported, this will be added in a later revision to 101 | * StandardFirmataWiFi. 102 | * For HUZZAH with ESP8266 use ESP8266_WIFI. 103 | */ 104 | 105 | //------------------------------ 106 | // TODO 107 | //------------------------------ 108 | //#define HUZZAH_WIFI 109 | 110 | 111 | // STEP 2 [OPTIONAL for all boards and shields] 112 | // If you want to setup you board as a TCP client, uncomment the following define and replace 113 | // the IP address with the IP address of your server. 114 | #define SERVER_IP 10, 2, 1, 300 115 | 116 | 117 | // STEP 3 [REQUIRED for all boards and shields] 118 | // replace this with your wireless network SSID 119 | char ssid[] = "My WiFi"; 120 | 121 | 122 | // STEP 4 [OPTIONAL for all boards and shields] 123 | // If you want to use a static IP (v4) address, uncomment the line below. You can also change the IP. 124 | // If the first line is commented out, the WiFi shield will attempt to get an IP from the DHCP server. 125 | // If you are using a static IP with the ESP8266 then you must also uncomment the SUBNET and GATEWAY. 126 | //#define STATIC_IP_ADDRESS 192,168,1,113 127 | //#define SUBNET_MASK 255,255,255,0 // REQUIRED for ESP8266_WIFI, optional for others 128 | //#define GATEWAY_IP_ADDRESS 0,0,0,0 // REQUIRED for ESP8266_WIFI, optional for others 129 | 130 | 131 | // STEP 5 [REQUIRED for all boards and shields] 132 | // define your port number here, you will need this to open a TCP connection to your Arduino 133 | #define SERVER_PORT 3030 134 | 135 | 136 | // STEP 6 [REQUIRED for all boards and shields] 137 | // determine your network security type (OPTION A, B, or C). Option A is the most common, and the 138 | // default. 139 | 140 | /* 141 | * OPTION A: WPA / WPA2 142 | * 143 | * WPA is the most common network security type. A passphrase is required to connect to this type. 144 | * 145 | * To enable, leave #define WIFI_WPA_SECURITY uncommented below, set your wpa_passphrase value 146 | * appropriately, and do not uncomment the #define values under options B and C 147 | */ 148 | #define WIFI_WPA_SECURITY 149 | 150 | #ifdef WIFI_WPA_SECURITY 151 | char wpa_passphrase[] = "ThisIsThePassword"; 152 | #endif //WIFI_WPA_SECURITY 153 | 154 | 155 | /* 156 | * OPTION B: WEP 157 | * 158 | * WEP is a less common (and regarded as less safe) security type. A WEP key and its associated 159 | * index are required to connect to this type. 160 | * 161 | * To enable, Uncomment the #define below, set your wep_index and wep_key values appropriately, 162 | * and verify the #define values under options A and C are commented out. 163 | */ 164 | //#define WIFI_WEP_SECURITY 165 | 166 | #ifdef WIFI_WEP_SECURITY 167 | //The wep_index below is a zero-indexed value. 168 | //Valid indices are [0-3], even if your router/gateway numbers your keys [1-4]. 169 | byte wep_index = 0; 170 | char wep_key[] = "your_wep_key"; 171 | #endif //WIFI_WEP_SECURITY 172 | 173 | 174 | /* 175 | * OPTION C: Open network (no security) 176 | * 177 | * Open networks have no security, can be connected to by any device that knows the ssid, and are 178 | * unsafe. 179 | * 180 | * To enable, uncomment #define WIFI_NO_SECURITY below and verify the #define values 181 | * under options A and B are commented out. 182 | */ 183 | //#define WIFI_NO_SECURITY 184 | 185 | /*============================================================================== 186 | * CONFIGURATION ERROR CHECK (don't change anything here) 187 | *============================================================================*/ 188 | 189 | #ifdef MULTIPLE_WIFI_LIB_INCLUDES 190 | #error "you may not define more than one wifi device type in wifiConfig.h." 191 | #endif 192 | 193 | #ifndef WIFI_LIB_INCLUDED 194 | #error "you must define a wifi device type in wifiConfig.h." 195 | #endif 196 | 197 | #if ((defined(WIFI_NO_SECURITY) && (defined(WIFI_WEP_SECURITY) || defined(WIFI_WPA_SECURITY))) || (defined(WIFI_WEP_SECURITY) && defined(WIFI_WPA_SECURITY))) 198 | #error "you may not define more than one security type at the same time in wifiConfig.h." 199 | #endif //WIFI_* security define check 200 | 201 | #if !(defined(WIFI_NO_SECURITY) || defined(WIFI_WEP_SECURITY) || defined(WIFI_WPA_SECURITY)) 202 | #error "you must define a wifi security type in wifiConfig.h." 203 | #endif //WIFI_* security define check 204 | 205 | #if (defined(ESP8266_WIFI) && !(defined(WIFI_NO_SECURITY) || (defined(WIFI_WPA_SECURITY)))) 206 | #error "you must choose between WIFI_NO_SECURITY and WIFI_WPA_SECURITY" 207 | #endif 208 | 209 | /*============================================================================== 210 | * WIFI STREAM (don't change anything here) 211 | *============================================================================*/ 212 | 213 | #ifdef SERVER_IP 214 | WiFiClientStream stream(IPAddress(SERVER_IP), SERVER_PORT); 215 | #else 216 | WiFiServerStream stream(SERVER_PORT); 217 | #endif 218 | 219 | /*============================================================================== 220 | * PIN IGNORE MACROS (don't change anything here) 221 | *============================================================================*/ 222 | 223 | #if defined(WIFI_101) && !defined(ARDUINO_SAMD_MKR1000) 224 | // ignore SPI pins, pin 5 (reset WiFi101 shield), pin 7 (WiFi handshake) and pin 10 (WiFi SS) 225 | // also don't ignore SS pin if it's not pin 10. Not needed for Arduino MKR1000. 226 | #define IS_IGNORE_PIN(p) ((p) == 10 || (IS_PIN_SPI(p) && (p) != SS) || (p) == 5 || (p) == 7) 227 | 228 | #elif defined(ARDUINO_WIFI_SHIELD) && defined(__AVR_ATmega32U4__) 229 | // ignore SPI pins, pin 4 (SS for SD-Card on WiFi-shield), pin 7 (WiFi handshake) and pin 10 (WiFi SS) 230 | // On Leonardo, pin 24 maps to D4 and pin 28 maps to D10 231 | #define IS_IGNORE_PIN(p) ((IS_PIN_SPI(p) || (p) == 4) || (p) == 7 || (p) == 10 || (p) == 24 || (p) == 28) 232 | 233 | #elif defined(ARDUINO_WIFI_SHIELD) 234 | // ignore SPI pins, pin 4 (SS for SD-Card on WiFi-shield), pin 7 (WiFi handshake) and pin 10 (WiFi SS) 235 | #define IS_IGNORE_PIN(p) ((IS_PIN_SPI(p) || (p) == 4) || (p) == 7 || (p) == 10) 236 | 237 | #elif defined(ESP8266_WIFI) && defined(SERIAL_DEBUG) 238 | #define IS_IGNORE_PIN(p) ((p) == 1) 239 | 240 | #endif 241 | -------------------------------------------------------------------------------- /hexapod/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## hexapod 4 | 5 | ### Configuración del Robot 6 | 7 | - Editando y ejecutando `$ node hexapod/test` se puede establecer que servo esta conectado a que pin, y si apuntan o no en la dirección correcta. 8 | - El paso anterior es el mas importante, hay que llevar bien las anotaciones. 9 | - Con la información recaudada se debe completar la configuracion correcta en el archivo `robot.js`. 10 | - Para la animación de los movimientos, se debe encontrar los puntos mas altos y bajos de movimiento en cada articulación (sin colisiones), esto nos dará los ángulos para las posiciones de inicio, medio y fin de cada extremo. **IMPORTANTE: Asegurarse que que las piernas no se golpeen en ningún punto de la animación** 11 | - Una vez establecido el "grueso" de la animación se pueden realizar ajustes pequeños en cada paso para lograr movimientos mas naturales y fluidos. 12 | 13 | 14 | ### Ejecutando y debugueando 15 | 16 | 1. Ejecutando `node hexapod` se puede probar el robots con las animaciones. 17 | 18 | 2. El script nos expone un objeto `hexa` via [REPL](../examples/repl) para interactuar con robot, solo necesitamos tipear algo como: 19 | - `$ hexa.walk(''|'rev')` 20 | - `$ hexa.turn(''|'right')` 21 | - `$ hexa.stand()` 22 | - `$ hexa.stop()` 23 | 3. Si todo está funcionando bien, se pueden probar las animaciones/acciones creadas con el joystick. 24 | 25 | ## License 26 | Licensed under the MIT license. 27 | -------------------------------------------------------------------------------- /hexapod/index.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | const printii = require('printii')(__dirname); 3 | const clear = require('clear'); 4 | const cliCursor = require('cli-cursor'); 5 | const charm = require('charm')(); 6 | const robot = require('./robot'); 7 | const nunchuk = require('./nunchuk'); 8 | const EtherPort = require('etherport'); 9 | 10 | clear(); 11 | printii(); 12 | cliCursor.hide(); 13 | charm.pipe(process.stdout); 14 | 15 | // Set boards. 16 | const boards = new five.Boards([ 17 | { id: 'bot', port: new EtherPort(3030), timeout: 30000 }, // Make sure to have flashed the robot with your IP address and WIFI access data. 18 | { id: 'nunchuk', port: '/dev/cu.usbserial-AH01CDU1' }, //replace with your usb port id 19 | ]); 20 | 21 | function log(message) { 22 | if (typeof message === 'object') { 23 | message = JSON.stringify(message); 24 | } 25 | 26 | charm 27 | .position(0, 16) 28 | .move(0, 1) 29 | .erase('line') 30 | .foreground('green') 31 | .write(message); 32 | } 33 | 34 | const botState = { 35 | stand: false, 36 | sleep: false, 37 | }; 38 | 39 | boards.on('ready', () => { 40 | console.log('BOARDS READY!'); 41 | 42 | const bot = robot.init(five, boards[0]); 43 | nunchuk.init(five, boards[1]).onChange(state => { 44 | log(state); 45 | }); 46 | boards.repl.inject({ bot }); 47 | }); 48 | -------------------------------------------------------------------------------- /hexapod/nunchuk.js: -------------------------------------------------------------------------------- 1 | const hash = require('object-hash'); 2 | const charm = require('charm')(); 3 | 4 | charm.pipe(process.stdout); 5 | 6 | class Nunchuk { 7 | constructor( 8 | { joystick = true, buttons = true, accelerometer = true, freq = 100 } = {}, 9 | ) { 10 | this.onChangeCallback = null; 11 | this.state = { 12 | // This is our source of truth. 13 | x: 0, // x-axis [-1, 0, 1] 14 | y: 0, // y-axis [-1, 0, 1] 15 | a: 0, // button a [0, 1] 16 | b: 0, // button b [0, 1] 17 | }; 18 | this.lastState = { 19 | // Set from last sensors read. 20 | x: 0, // x-axis [-1, 0, 1] 21 | y: 0, // y-axis [-1, 0, 1] 22 | a: 0, // button a [0, 1] 23 | b: 0, // button b [0, 1] 24 | }; 25 | 26 | this.scale = { 27 | x: { 28 | max: 1112, 29 | min: 112, 30 | center: 480, 31 | }, 32 | y: { 33 | max: 600, 34 | min: 40, 35 | center: 512, 36 | }, 37 | }; 38 | 39 | this.joystick = joystick; 40 | this.buttons = buttons; 41 | this.accelerometer = accelerometer; 42 | this.freq = freq; 43 | this.debug = !!process.env.DEBUG || false; 44 | } 45 | 46 | init(five, board) { 47 | // Create a new `nunchuk` hardware instance. 48 | this.controller = new five.Wii.Nunchuk({ 49 | freq: 50, 50 | board, 51 | }); 52 | 53 | this.setEvents(); 54 | this.loop(); 55 | this.hello(); 56 | 57 | return this; 58 | } 59 | 60 | hello() { 61 | charm.move(0, 1).foreground('blue').write('I know kung fu!'); 62 | } 63 | 64 | loop() { 65 | this.timer = setInterval(() => { 66 | if (!this.isStateChange(this.lastState)) return; 67 | 68 | this.state = this.lastState; 69 | if (typeof this.onChangeCallback !== 'function') return; 70 | this.onChangeCallback(this.state); 71 | }, this.freq); 72 | } 73 | 74 | updateState(event, type) { 75 | if (this.debug) this.logEvent(event, type); 76 | this.lastState = this.getStateFromEvent(event, type); 77 | } 78 | 79 | getStateHash(state) { 80 | return hash(state); 81 | } 82 | 83 | isStateChange(state) { 84 | // Get hashes. 85 | const hash = this.getStateHash(this.state); 86 | const newHash = this.getStateHash(state); 87 | 88 | return hash !== newHash; 89 | } 90 | 91 | setState(state) { 92 | this.state = state; 93 | } 94 | 95 | isButtonChange(state) { 96 | return this.state.a !== state.a || this.state.b !== state.b; 97 | } 98 | 99 | getButtonName(target) { 100 | const buttonMap = { 101 | c: 'a', 102 | z: 'b', 103 | }; 104 | 105 | const buttonName = buttonMap[target.which] || null; 106 | return buttonName; 107 | } 108 | 109 | getDirection(axis, value) { 110 | if (!axis || !value || axis === 'z') return 0; 111 | 112 | const center = this.scale[axis].center; 113 | const threshold = 100; 114 | if (value > center + threshold) return 1; 115 | if (value < center - threshold) return -1; 116 | return 0; 117 | } 118 | 119 | getStateFromEvent(event, type = 'MOVEMENT') { 120 | const { axis, target } = event; 121 | const button = this.getButtonName(target); 122 | const value = parseInt(target[event.axis], 10) || 0; 123 | const { isUp, isDown } = target; 124 | const direction = this.getDirection(axis, value); 125 | 126 | let x = this.state.x; 127 | let y = this.state.y; 128 | let a = this.state.a; 129 | let b = this.state.b; 130 | let newState = { x, y, a, b }; 131 | 132 | // There's a bug in johnny-five where some peaks 133 | // moving on -x throws incorrect values. 134 | if (axis === 'x') { 135 | if (value > 1000) return this.lastState; 136 | } 137 | 138 | // Update button state. 139 | if (button) { 140 | newState[button] = type !== 'up' ? 1 : 0; 141 | 142 | // Update button state and return. 143 | if (this.isButtonChange(newState)) { 144 | return newState; 145 | } 146 | } 147 | 148 | // Update axis and return. 149 | if (axis) newState[axis] = direction; 150 | return newState; 151 | } 152 | 153 | setEvents() { 154 | // Joystick events. 155 | if (this.joystick) { 156 | this.controller.joystick.on('change', event => { 157 | this.updateState(event); 158 | }); 159 | } 160 | 161 | // Button events. 162 | if (this.buttons) { 163 | const buttons = ['down', 'up', 'hold']; 164 | buttons.forEach(type => { 165 | this.controller.on(type, event => { 166 | this.updateState(event, type); 167 | }); 168 | }); 169 | } 170 | 171 | // Acceleromoter events. 172 | if (this.accelerometer) { 173 | this.controller.accelerometer.on('change', event => { 174 | this.updateState(event); 175 | }); 176 | } 177 | } 178 | 179 | logEvent(event, type = 'MOVEMENT') { 180 | const { axis, direction, target } = event; 181 | const button = this.getButtonName(target); 182 | const value = target[event.axis]; 183 | const { isUp, isDown } = target; 184 | 185 | if (process.env.X) if (axis !== 'x') return; 186 | 187 | console.log(` 188 | JOYSTICK EVENT: 189 | - value: ${value} 190 | - axis: ${axis} 191 | - direction: ${direction} 192 | - type: ${type} 193 | - button: ${button} 194 | - up: ${isUp} 195 | - down: ${isDown} 196 | `); 197 | } 198 | 199 | onChange(callback) { 200 | this.onChangeCallback = callback; 201 | } 202 | } 203 | 204 | const nunchuk = new Nunchuk(); 205 | module.exports = nunchuk; 206 | -------------------------------------------------------------------------------- /hexapod/robot.js: -------------------------------------------------------------------------------- 1 | const temporal = require('temporal'); 2 | 3 | function init(five, board) { 4 | const quad = { 5 | status: 'sleep', 6 | }; 7 | 8 | const lift = { femur: 20 }; 9 | const easeIn = 'inQuad'; 10 | const easeOut = 'outQuad'; 11 | const easeInOut = 'inOutQuad'; 12 | const gait = 1; 13 | const s = { 14 | front: { 15 | coxa: [ 100 - 20 * gait, 100, 100 + 20 * gait ], 16 | femur: [ 100, 80, 60 ], 17 | tibia: [60, 45, 30] 18 | }, 19 | 20 | mid: { 21 | coxa: [ 90 + 15 * gait, 90, 90 - 15 * gait ], 22 | femur: [ 60, 80, 100 ], 23 | tibia: [30, 45, 60] 24 | }, 25 | 26 | rear: { 27 | coxa: [ 100 + 20 * gait, 100, 100 - 20 * gait ], 28 | femur: [ 100, 80, 60 ], 29 | tibia: [60, 45, 30] 30 | } 31 | }; 32 | const hexa = { 33 | status: 'sleep' 34 | }; 35 | 36 | hexa.r1c = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 15}); 37 | hexa.r1f = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 14}); 38 | hexa.r1t = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 13}); 39 | hexa.r1 = new five.Servos([hexa.r1c, hexa.r1f, hexa.r1t]); 40 | 41 | hexa.r2c = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 11}); 42 | hexa.r2f = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 10}); 43 | hexa.r2t = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 9}); 44 | hexa.r2 = new five.Servos([hexa.r2c, hexa.r2f, hexa.r2t]); 45 | 46 | hexa.r3c = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 7}); 47 | hexa.r3f = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 6}); 48 | hexa.r3t = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 5}); 49 | hexa.r3 = new five.Servos([hexa.r3c, hexa.r3f, hexa.r3t]); 50 | 51 | hexa.l1c = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 0, invert: true}); 52 | hexa.l1f = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 1, invert: true}); 53 | hexa.l1t = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 2, invert: true}); 54 | hexa.l1 = new five.Servos([hexa.l1c, hexa.l1f, hexa.l1t]); 55 | 56 | hexa.l2c = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 4, invert: true}); 57 | hexa.l2f = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 5, invert: true}); 58 | hexa.l2t = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 6, invert: true}); 59 | hexa.l2 = new five.Servos([hexa.l2c, hexa.l2f, hexa.l2t]); 60 | 61 | hexa.l3c = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 8, invert: true}); 62 | hexa.l3f = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 9, invert: true}); 63 | hexa.l3t = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 10, invert: true}); 64 | hexa.l3 = new five.Servos([hexa.l3c, hexa.l3f, hexa.l3t]); 65 | 66 | hexa.coxa = new five.Servos([hexa.r1c, hexa.l1c, hexa.r2c, hexa.l2c, hexa.r3c, hexa.l3c]); 67 | hexa.femur = new five.Servos([hexa.r1f, hexa.l1f, hexa.r2f, hexa.l2f, hexa.r3f, hexa.l3f]); 68 | hexa.tibia = new five.Servos([hexa.r1t, hexa.l1t, hexa.r2t, hexa.l2t, hexa.r3t, hexa.l3t]); 69 | 70 | hexa.joints = new five.Servos([hexa.coxa, hexa.femur, hexa.tibia]); 71 | 72 | hexa.legs = new five.Servos([ 73 | hexa.r1c, hexa.r1f, hexa.r1t, 74 | hexa.l1c, hexa.l1f, hexa.l1t, 75 | hexa.r2c, hexa.r2f, hexa.r2t, 76 | hexa.l2c, hexa.l2f, hexa.l2t, 77 | hexa.r3c, hexa.r3f, hexa.r3t, 78 | hexa.l3c, hexa.l3f, hexa.l3t 79 | ]); 80 | 81 | const legsAnimation = new five.Animation(hexa.legs); 82 | 83 | const stand = { 84 | target: hexa.joints, 85 | duration: 500, 86 | loop: false, 87 | fps: 100, 88 | cuePoints: [0, 0.1, 0.3, 0.7, 1.0], 89 | oncomplete: function oncomplete() { 90 | hexa.state = 'stand'; 91 | }, 92 | keyFrames: [ 93 | [ 94 | null, 95 | { degrees: s.front.coxa[1] } 96 | ], 97 | [ 98 | null, 99 | false, 100 | false, 101 | { 102 | degrees: s.front.femur[1], 103 | easing: easeOut 104 | }, 105 | { 106 | degrees: s.front.femur[1], 107 | easing: easeIn 108 | } 109 | ], 110 | [ 111 | null, 112 | false, { 113 | degrees: s.front.tibia[1] + 13 114 | }, 115 | false, { 116 | degrees: s.front.tibia[1] 117 | } 118 | ] 119 | ] 120 | }; 121 | 122 | const sleep = { 123 | duration: 500, 124 | cuePoints: [0, 0.5, 1.0], 125 | fps: 100, 126 | target: hexa.joints, 127 | oncomplete: function oncomplete() { 128 | hexa.state = 'sleep'; 129 | }, 130 | keyFrames: [ 131 | [null, false, { degrees: 100, easing: easeOut }], 132 | [null, { degrees: 60, easing: easeInOut }, { degrees: 120, easing: easeInOut }], 133 | [null, { degrees: 30, easing: easeInOut }, { step: 0, easing: easeInOut }] 134 | ] 135 | }; 136 | 137 | const waveRight = { 138 | duration: 1500, 139 | cuePoints: [0, 0.1, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], 140 | target: hexa.r1, 141 | oncomplete: function oncomplete() { 142 | hexa.state = 'stand'; 143 | }, 144 | keyFrames: [ 145 | [null, false, { degrees: 120, easing: easeInOut }, false, false, false, false, false, { degrees: 52, easing: easeInOut }, { copyDegrees: 0, easing: easeInOut } ], // r1c 146 | [null, { step: 55, easing: easeInOut }, false, false, false, false, false, false, { step: -55, easing: easeInOut }, { copyDegrees: 0, easing: easeInOut } ], // r1f 147 | [null, { degrees: 85, easing: easeInOut }, { degrees: 45, easing: easeInOut }, { step: -15, easing: easeInOut }, { step: 30, easing: easeInOut}, { copyDegrees: 3, easing: easeInOut}, { copyFrame: 4 }, { copyDegrees: 2, easing: easeInOut}, { copyFrame: 1 }, {copyDegrees: 0, easing: easeInOut} ] 148 | ] 149 | }; 150 | 151 | const waveLeft = { 152 | duration: 1500, 153 | cuePoints: [0, 0.1, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], 154 | target: hexa.l1, 155 | oncomplete: function oncomplete() { 156 | hexa.state = 'stand'; 157 | }, 158 | keyFrames: [ 159 | [null, false, { degrees: 120, easing: easeInOut }, false, false, false, false, false, { degrees: 52, easing: easeInOut }, {copyDegrees: 0, easing: easeInOut} ], // l1c 160 | [null, { step: 55, easing: easeInOut }, false, false, false, false, false, false, { step: -55, easing: easeInOut }, {copyDegrees: 0, easing: easeInOut} ], // l1f 161 | [null, { degrees: 85, easing: easeInOut }, { degrees: 45, easing: easeInOut }, { step: -15, easing: easeInOut}, { step: 30, easing: easeInOut}, { copyDegrees: 3, easing: easeInOut}, { copyFrame: 4 }, { copyDegrees: 2, easing: easeInOut}, { copyFrame: 1 }, {copyDegrees: 0, easing: easeInOut} ] 162 | ] 163 | }; 164 | 165 | hexa.walk = function hexaWalk(dir) { 166 | let a = dir === 'rev' ? 0 : 2, 167 | b = dir === 'rev' ? 2 : 0; 168 | 169 | legsAnimation.enqueue({ 170 | duration: 1500, 171 | target: hexa.legs, 172 | cuePoints: [0, 0.25, 0.5, 0.625, 0.75, 0.875, 1.0], 173 | loop: true, 174 | loopback: 0.5, 175 | fps: 100, 176 | onstop: function onstop() { 177 | hexa.stand(); 178 | }, 179 | oncomplete: function oncomplete() {}, 180 | keyFrames: [ 181 | /* r1c, r1f, r1t */ 182 | [ null, null, { degrees: s.front.coxa[a] }, { degrees: s.front.coxa[1]}, {degrees: s.front.coxa[b]}, null, {degrees: s.front.coxa[a]}], 183 | [ null, { step: lift.femur, easing: easeOut }, { degrees: s.front.femur[a], easing: easeIn}, {degrees: s.front.femur[1]}, {degrees: s.front.femur[b]}, { step: lift.femur, easing: easeOut }, {degrees: s.front.femur[a], easing: easeIn}], 184 | [ null, { step: lift.tibia, easing: easeOut }, { degrees: s.front.tibia[a], easing: easeIn}, {degrees: s.front.tibia[1]}, {degrees: s.front.tibia[b]}, { step: lift.tibia, easing: easeOut }, {degrees: s.front.tibia[a], easing: easeIn}], 185 | 186 | /* l1c, l1f, l1t */ 187 | [ null, null, { degrees: s.front.coxa[b] }, null, { degrees: s.front.coxa[a]}, {degrees: s.front.coxa[1]}, {degrees: s.front.coxa[b]}], 188 | [ null, null, { degrees: s.front.femur[b] }, { step: lift.femur, easing: easeOut }, {degrees: s.front.femur[a], easing: easeIn}, {degrees: s.front.femur[1]}, {degrees: s.front.femur[b]}], 189 | [ null, null, { degrees: s.front.tibia[b] }, { step: lift.tibia, easing: easeOut }, {degrees: s.front.tibia[a], easing: easeIn}, {degrees: s.front.tibia[1]}, {degrees: s.front.tibia[b]}], 190 | 191 | /* r2c, r2f, r2t */ 192 | [ null, null, { degrees: s.rear.coxa[b] }, null, { degrees: s.rear.coxa[a]}, {degrees: s.rear.coxa[1]}, {degrees: s.rear.coxa[b]}], 193 | [ null, null, { degrees: s.rear.femur[b] }, { step: lift.femur, easing: easeOut }, {degrees: s.rear.femur[a], easing: easeIn}, {degrees: s.rear.femur[1]}, {degrees: s.rear.femur[b]}], 194 | [ null, null, { degrees: s.mid.tibia[b] }, { step: lift.tibia, easing: easeOut }, {degrees: s.mid.tibia[a], easing: easeIn}, {degrees: s.mid.tibia[1]}, {degrees: s.mid.tibia[b]}], 195 | 196 | /* l2c, l2f, l2t */ 197 | [ null, null, { degrees: s.rear.coxa[a] }, { degrees: s.rear.coxa[1]}, {degrees: s.rear.coxa[b]}, null, {degrees: s.rear.coxa[a]}], 198 | [ null, { step: lift.femur, easing: easeOut }, { degrees: s.rear.femur[a], easing: easeIn}, {degrees: s.rear.femur[1]}, {degrees: s.rear.femur[b]}, { step: lift.femur, easing: easeOut }, {degrees: s.rear.femur[a], easing: easeIn}], 199 | [ null, { step: lift.tibia, easing: easeOut }, { degrees: s.mid.tibia[a], easing: easeIn}, {degrees: s.mid.tibia[1]}, {degrees: s.mid.tibia[b]}, { step: lift.tibia, easing: easeOut }, {degrees: s.mid.tibia[a], easing: easeIn}], 200 | 201 | /* r3c, r3f, r3t */ 202 | [ null, null, { degrees: s.front.coxa[a] }, { degrees: s.front.coxa[1]}, {degrees: s.front.coxa[b] }, null, { degrees: s.front.coxa[a] }], 203 | [ null, { step: lift.femur, easing: easeOut }, { degrees: s.front.femur[a], easing: easeIn }, { degrees: s.front.femur[1] }, { degrees: s.front.femur[b] }, { step: lift.femur, easing: easeOut }, { degrees: s.front.femur[a], easing: easeIn }], 204 | [ null, { step: lift.tibia, easing: easeOut }, { degrees: s.rear.tibia[a], easing: easeIn }, { degrees: s.rear.tibia[1] }, { degrees: s.rear.tibia[b] }, { step: lift.tibia, easing: easeOut }, { degrees: s.rear.tibia[a], easing: easeIn }], 205 | 206 | /* l3c, l3f, r3t */ 207 | [ null, null, { degrees: s.front.coxa[b] }, null, { degrees: s.front.coxa[a] }, { degrees: s.front.coxa[1] }, { degrees: s.front.coxa[b] }], 208 | [ null, null, { degrees: s.front.femur[b] }, { step: lift.femur, easing: easeOut }, { degrees: s.front.femur[a], easing: easeIn }, { degrees: s.front.femur[1] }, { degrees: s.front.femur[b] }], 209 | [ null, null, { degrees: s.rear.tibia[b] }, { step: lift.tibia, easing: easeOut }, { degrees: s.rear.tibia[a], easing: easeIn }, { degrees: s.rear.tibia[1] }, { degrees: s.rear.tibia[b] }] 210 | 211 | ] 212 | }); 213 | return this; 214 | }; 215 | 216 | hexa.turn = function hexaTurn(dir) { 217 | let a = dir === 'left' ? 0 : 2, 218 | b = dir === 'left' ? 2 : 0; 219 | 220 | legsAnimation.enqueue({ 221 | duration: 1500, 222 | fps: 100, 223 | cuePoints: [0, 0.25, 0.5, 0.625, 0.75, 0.875, 1.0], 224 | loop: true, 225 | loopback: 0.5, 226 | onstop: function onstop() { 227 | hexa.att(); 228 | }, 229 | keyFrames: [ 230 | [ null, null, { degrees: s.front.coxa[a] }, null, { degrees: s.front.coxa[b] }, null, { degrees: s.front.coxa[a] }], 231 | [ null, null, { degrees: s.front.femur[a] }, { step: lift.femur, easing: easeOut }, { degrees: s.front.femur[b] }, null, { degrees: s.front.femur[a] }], 232 | 233 | [ null, null, { degrees: s.front.coxa[a] }, null, { degrees: s.front.coxa[b] }, null, { degrees: s.front.coxa[a] }], 234 | [ null, { step: lift.femur, easing: easeOut }, { degrees: s.front.femur[a], easing: easeIn}, null, { degrees: s.front.femur[b], easing: easeIn }, { step: lift.femur, easing: easeOut }, { degrees: s.front.femur[a], easing: easeIn }], 235 | 236 | [ null, null, { degrees: s.rear.coxa[b] }, null, { degrees: s.rear.coxa[a] }, null, { degrees: s.rear.coxa[b] }], 237 | [ null, { step: lift.femur, easing: easeOut }, { degrees: s.rear.femur[b], easing: easeIn}, null, { degrees: s.rear.femur[a], easing: easeIn }, { step: lift.femur, easing: easeOut }, { degrees: s.rear.femur[b], easing: easeIn }], 238 | 239 | [ null, null, { degrees: s.rear.coxa[b] }, null, { degrees: s.rear.coxa[a] }, null, { degrees: s.rear.coxa[b] }], 240 | [ null, null, { degrees: s.rear.femur[b] }, { step: lift.femur, easing: easeOut }, { degrees: s.rear.femur[a] }, null, {degrees: s.rear.femur[b] }] 241 | ] 242 | }); 243 | return this; 244 | }; 245 | 246 | hexa.att = function hexaStand() { 247 | let grouped, 248 | work = [ 249 | { name: 'r1', offset: 0, home: s.front.femur[1], thome: s.front.tibia[1], chome: s.front.coxa[1]}, 250 | { name: 'r2', offset: 0, home: s.mid.femur[1], thome: s.mid.tibia[1], chome: s.front.coxa[1] }, 251 | { name: 'r3', offset: 0, home: s.rear.femur[1], thome: s.rear.tibia[1], chome: s.front.coxa[1] }, 252 | { name: 'l1', offset: 0, home: s.front.femur[1], thome: s.front.tibia[1], chome: s.front.coxa[1] }, 253 | { name: 'l2', offset: 0, home: s.mid.femur[1], thome: s.mid.tibia[1], chome: s.front.coxa[1] }, 254 | { name: 'l3', offset: 0, home: s.rear.femur[1], thome: s.rear.tibia[1], chome: s.front.coxa[1] } 255 | ]; 256 | 257 | work.forEach((leg, i) => { 258 | work[i].offset = Math.abs(hexa[leg.name + 'f'].last.reqDegrees - leg.home); 259 | }); 260 | 261 | if (work[1].offset > work[4].offset) { 262 | grouped = [ 263 | [0, 2, 4], 264 | [1, 3, 5] 265 | ]; 266 | } else { 267 | grouped = [ 268 | [1, 3, 5], 269 | [0, 2, 4] 270 | ]; 271 | } 272 | 273 | grouped.forEach((group, i) => { 274 | group.forEach((leg) => { 275 | temporal.queue([{ 276 | delay: 250 * i, 277 | task: () => { 278 | hexa[work[leg].name + 'f'].to(work[leg].home + lift.femur); 279 | hexa[work[leg].name + 't'].to(work[leg].thome + lift.tibia); 280 | } 281 | }, { 282 | delay: 50, 283 | task: () => { 284 | hexa[work[leg].name + 'c'].to(work[leg].chome); 285 | } 286 | }, { 287 | delay: 50, 288 | task: () => { 289 | hexa[work[leg].name + 'f'].to(work[leg].home); 290 | hexa[work[leg].name + 't'].to(work[leg].thome); 291 | } 292 | }]); 293 | }); 294 | }); 295 | hexa.state = 'stand'; 296 | }; 297 | 298 | hexa.sleep = () => { 299 | legsAnimation.enqueue(sleep); 300 | }; 301 | 302 | hexa.stand = () => { 303 | legsAnimation.enqueue(stand); 304 | }; 305 | 306 | hexa.waveLeft = () => { 307 | legsAnimation.enqueue(waveLeft); 308 | }; 309 | 310 | hexa.waveRight = () => { 311 | legsAnimation.enqueue(waveRight); 312 | }; 313 | 314 | hexa.stop = () => { 315 | legsAnimation.stop(); 316 | }; 317 | 318 | return hexa; 319 | } 320 | 321 | module.exports = { init }; 322 | -------------------------------------------------------------------------------- /hexapod/test.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | const EtherPort = require('etherport'); 3 | const board = new five.Board( 4 | {port: new EtherPort(3030), timeout: 30000 } // Make sure to have flashed the robot with your IP address and WIFI access data. 5 | ); 6 | 7 | board.on('ready', () => { 8 | const a = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 5}); 9 | const b = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 6}); 10 | const c = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 7}); 11 | 12 | const d = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 9}); 13 | const e = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 10}); 14 | const f = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 11}); 15 | 16 | const g = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 13}); 17 | const h = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 14}); 18 | const i = new five.Servo({address: 0x41, controller: 'PCA9685', pin: 15}); 19 | 20 | const j = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 0}); 21 | const k = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 1}); 22 | const l = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 2}); 23 | 24 | const m = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 4}); 25 | const n = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 5}); 26 | const o = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 6}); 27 | 28 | const p = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 8}); 29 | const q = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 9}); 30 | const r = new five.Servo({address: 0x40, controller: 'PCA9685', pin: 10}); 31 | 32 | const servos = new five.Servos([ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r]); 33 | board.repl.inject({ 34 | a, b, c, d, e, f, g, h, servos 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /kit1/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Kit 1 4 | 5 | ### Componentes 6 | - 1 sensor HC-SR04 7 | - 1 Botón con resistencia 8 | - 3 Leds con resistencias 9 | 10 | ### Instrucciones 11 | Usando como base los ejemplos de [leds](../examples/led), [proximidad](../examples/proximity) y [botones](../examples/button) escribir un script que vaya indicando la proximdad de un objeto encendiendo los 3 leds en secuencia. El botón es para proveernos de otro tipo de entrada, usemoslo para agregarle una funcionalidad adicional a nuestra invención. 12 | 13 | Recuerden que tal y como se explica en [proximidad](../examples/proximity) este kit requiere de una version especial de firmata (PingFirmata). 14 | ## License 15 | Licensed under the MIT license. 16 | -------------------------------------------------------------------------------- /kit2/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Kit 2 4 | 5 | ### Componentes 6 | - 1 Potenciometro 7 | - 1 Botón con resistencia 8 | - 1 Piezo 9 | 10 | ### Instrucciones 11 | Usando como base los ejemplos de [piezo](../examples/piezo), [potenciometro](../examples/potentiometer) y [botones](../examples/button) escribir un script que al presionar el botón emita un sonido. El potenciometro puede ayudarnos a definir la frecuencia y/o duracion del sonido. Recuerden que el botón tiene un evento `onHold` que se puede utilizar para darle mas funcionalidad a nuestra invención. 12 | 13 | ## License 14 | Licensed under the MIT license. 15 | -------------------------------------------------------------------------------- /kit3/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Kit 3 4 | 5 | ### Componentes 6 | - 3 Botones con resistencias 7 | - 1 LED RGB con resistencias 8 | 9 | ### Instrucciones 10 | Usando como base los ejemplos de [LED RGB](../examples/rgb_led) y [botones](../examples/button) escribir un script que nos permita cambiar de color o aplicar distintas transiciones y efectos al led al presionar los botones. 11 | 12 | ## License 13 | Licensed under the MIT license. 14 | -------------------------------------------------------------------------------- /kit4/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Kit 4 4 | 5 | ### Componentes 6 | - 1 botónes con resistencias 7 | - 1 LED RGB con resistencias 8 | - 1 potenciometro 9 | 10 | ### Instrucciones 11 | Usando como base los ejemplos de [LED RGB](../examples/rgb_led), [potenciometro](../examples/potentiometer) y [botónes](../examples/button) escribir un script que nos permita cambiar de color o aplicar distintas transiciones y efectos al led al presionar el botón o mover el potenciometro. Recuerden que el botón tiene un evento `onHold` que se puede utilizar para darle mas funcionalidad a nuestra invención. 12 | 13 | ## License 14 | Licensed under the MIT license. 15 | -------------------------------------------------------------------------------- /kit5/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Kit 5 4 | 5 | ### Componentes 6 | - 1 Sensor de movimiento 7 | - 1 Piezo o 1 Led 8 | 9 | ### Instrucciones 10 | Usando como base los ejemplos de [movmiento](../examples/movement) y [piezo](../examples/piezo) o [led](../examples/led) escribir un script que detecte el movimiento y emita un sonido o encienda un led. Se recomienda utilizar los distintos modos del sensor (continuo o repetición), y hacer uso de los estados de inicio y fin de movimiento. También se puede reconocer la frecuencia con la que se detectan los movimientos e ir cambiando el sonido o la intencidad del parpadeo acorde a ello. 11 | 12 | ## License 13 | Licensed under the MIT license. 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workshop-roboticajs", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "description": "Workshop orientado a la introduccion de los participantes a la robotica y domotica utilizando componentes simples de bajo presupuesto y javascript como lenguaje de codeo.", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "flash": "interchange install --interactive", 9 | "start": "node examples/led/led" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "ssh://git@bitbucket.escm.co:7999/~apivetta/workshop-robotica.git" 14 | }, 15 | "author": "Marcos Tomatti ", 16 | "contributors": [ 17 | "Agustin Pivetta " 18 | ], 19 | "license": "MIT", 20 | "dependencies": { 21 | "etherport": "^0.2.0", 22 | "johnny-five": "^0.11.6", 23 | "keypress": "^0.2.1", 24 | "nodebots-interchange": "^1.1.6", 25 | "temporal": "^0.6.0" 26 | }, 27 | "devDependencies": { 28 | "charm": "^1.0.2", 29 | "clear": "0.0.1", 30 | "object-hash": "^1.2.0", 31 | "printii": "^1.0.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /quadpod/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Quadpod 4 | 5 | ### Configuración del Robot 6 | 7 | - Editando y ejecutando `$ node quadbpod/test` se puede establecer que servo esta conectado a que pin, y si apuntan o no en la dirección correcta. 8 | - El paso anterior es el mas importante, hay que llevar bien las anotaciones. 9 | - Con la información recaudada se debe completar la configuracion correcta en el archivo `robot.js`. 10 | - Para la animación de los movimientos, se debe encontrar los puntos mas altos y bajos de movimiento en cada articulación (sin colisiones), esto nos dará los ángulos para las posiciones de inicio, medio y fin de cada extremo. **IMPORTANTE: Asegurarse que que las piernas no se golpeen en ningún punto de la animación** 11 | - Una vez establecido el "grueso" de la animación se pueden realizar ajustes pequeños en cada paso para lograr movimientos mas naturales y fluidos. 12 | 13 | 14 | ### Ejecutando y debugueando 15 | 16 | 1. Ejecutando `node quadpod` se puede probar el robots con las animaciones. 17 | 18 | 2. El script nos expone un objeto `quad` via [REPL](../examples/repl) para interactuar con robot, solo necesitamos tipear algo como: 19 | - `$ quad.walk(''|'rev')` 20 | - `$ quad.turn(''|'right')` 21 | - `$ quad.stand()` 22 | - `$ quad.stop()` 23 | 3. Si todo está funcionando bien, se pueden probar las animaciones/acciones creadas con el joystick. 24 | 25 | ## License 26 | Licensed under the MIT license. 27 | -------------------------------------------------------------------------------- /quadpod/index.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | const printii = require('printii')(__dirname); 3 | const clear = require('clear'); 4 | const cliCursor = require('cli-cursor'); 5 | const charm = require('charm')(); 6 | const robot = require('./robot'); 7 | const nunchuk = require('./nunchuk'); 8 | const EtherPort = require('etherport'); 9 | 10 | clear(); 11 | //printii(); 12 | cliCursor.hide(); 13 | charm.pipe(process.stdout); 14 | 15 | // Set boards. 16 | const boards = new five.Boards([ 17 | { id: 'bot', port: new EtherPort(3030) }, //replace with your bluetooth port id 18 | { id: 'nunchuk', port: '/dev/cu.wchusbserial1420' }, //replace with your usb port id 19 | ]); 20 | 21 | function log(message) { 22 | if (typeof message === 'object') { 23 | message = JSON.stringify(message); 24 | } 25 | 26 | charm 27 | .position(0, 16) 28 | .move(0, 1) 29 | .erase('line') 30 | .foreground('green') 31 | .write(message); 32 | } 33 | 34 | const botState = { 35 | stand: false, 36 | sleep: false, 37 | }; 38 | 39 | boards.on('ready', () => { 40 | console.log('BOARDS READY!'); 41 | 42 | const bot = robot.init(five, boards[0]); 43 | nunchuk.init(five, boards[1]).onChange(state => { 44 | log(state); 45 | 46 | if (state.a) { 47 | if (botState.stand) { 48 | bot.sleep(); 49 | botState.stand = false; 50 | log('BYE!'); 51 | } else { 52 | bot.joints.to(60); 53 | botState.stand = true; 54 | log('TATAKAU!'); 55 | } 56 | return; 57 | } 58 | 59 | if (state.b) { 60 | log('STOP'); 61 | bot.stop(); 62 | return; 63 | } 64 | 65 | if (state.x === 0 && state.y === 0) { 66 | log('QUIET'); 67 | bot.stop(); 68 | return; 69 | } 70 | 71 | if (state.x === 1) { 72 | log('TURN FORWARD'); 73 | bot.turn(); 74 | return; 75 | } 76 | 77 | if (state.x === -1) { 78 | log('TURN BACKWARDS'); 79 | bot.turn('left'); 80 | return; 81 | } 82 | 83 | if (state.y === 1) { 84 | log('WALK RIGHT'); 85 | bot.walk(); 86 | return; 87 | } 88 | 89 | if (state.y === -1) { 90 | log('WALK LEFT'); 91 | bot.walk('rev'); 92 | return; 93 | } 94 | }); 95 | 96 | boards.repl.inject({ bot }); 97 | }); 98 | -------------------------------------------------------------------------------- /quadpod/nunchuk.js: -------------------------------------------------------------------------------- 1 | const hash = require('object-hash'); 2 | const charm = require('charm')(); 3 | 4 | charm.pipe(process.stdout); 5 | 6 | class Nunchuk { 7 | constructor( 8 | { joystick = true, buttons = true, accelerometer = true, freq = 100 } = {} 9 | ) { 10 | this.onChangeCallback = null; 11 | this.state = { 12 | // This is our source of truth. 13 | x: 0, // x-axis [-1, 0, 1] 14 | y: 0, // y-axis [-1, 0, 1] 15 | a: 0, // button a [0, 1] 16 | b: 0, // button b [0, 1] 17 | }; 18 | this.lastState = { 19 | // Set from last sensors read. 20 | x: 0, // x-axis [-1, 0, 1] 21 | y: 0, // y-axis [-1, 0, 1] 22 | a: 0, // button a [0, 1] 23 | b: 0, // button b [0, 1] 24 | }; 25 | 26 | this.scale = { 27 | x: { 28 | max: 1112, 29 | min: 112, 30 | center: 480, 31 | }, 32 | y: { 33 | max: 600, 34 | min: 40, 35 | center: 512, 36 | }, 37 | }; 38 | 39 | this.joystick = joystick; 40 | this.buttons = buttons; 41 | this.accelerometer = accelerometer; 42 | this.freq = freq; 43 | this.debug = !!process.env.DEBUG || false; 44 | } 45 | 46 | init(five, board) { 47 | // Create a new `nunchuk` hardware instance. 48 | this.controller = new five.Wii.Nunchuk({ 49 | freq: 50, 50 | board, 51 | }); 52 | 53 | this.setEvents(); 54 | this.loop(); 55 | this.hello(); 56 | 57 | return this; 58 | } 59 | 60 | hello() { 61 | charm.move(0, 1).foreground('blue').write('I know kung fu!'); 62 | } 63 | 64 | loop() { 65 | this.timer = setInterval(() => { 66 | if (!this.isStateChange(this.lastState)) return; 67 | 68 | this.state = this.lastState; 69 | if (typeof this.onChangeCallback !== 'function') return; 70 | this.onChangeCallback(this.state); 71 | }, this.freq); 72 | } 73 | 74 | updateState(event, type) { 75 | if (this.debug) this.logEvent(event, type); 76 | this.lastState = this.getStateFromEvent(event, type); 77 | } 78 | 79 | getStateHash(state) { 80 | return hash(state); 81 | } 82 | 83 | isStateChange(state) { 84 | // Get hashes. 85 | const hash = this.getStateHash(this.state); 86 | const newHash = this.getStateHash(state); 87 | 88 | return hash !== newHash; 89 | } 90 | 91 | setState(state) { 92 | this.state = state; 93 | } 94 | 95 | isButtonChange(state) { 96 | return this.state.a !== state.a || this.state.b !== state.b; 97 | } 98 | 99 | getButtonName(target) { 100 | const buttonMap = { 101 | c: 'a', 102 | z: 'b', 103 | }; 104 | 105 | const buttonName = buttonMap[target.which] || null; 106 | return buttonName; 107 | } 108 | 109 | getDirection(axis, value) { 110 | if (!axis || !value || axis === 'z') return 0; 111 | 112 | const center = this.scale[axis].center; 113 | const threshold = 100; 114 | if (value > center + threshold) return 1; 115 | if (value < center - threshold) return -1; 116 | return 0; 117 | } 118 | 119 | getStateFromEvent(event, type = 'MOVEMENT') { 120 | const { axis, target } = event; 121 | const button = this.getButtonName(target); 122 | const value = parseInt(target[event.axis], 10) || 0; 123 | const { isUp, isDown } = target; 124 | const direction = this.getDirection(axis, value); 125 | 126 | let x = this.state.x; 127 | let y = this.state.y; 128 | let a = this.state.a; 129 | let b = this.state.b; 130 | let newState = { x, y, a, b }; 131 | 132 | // There's a bug in johnny-five where some peaks 133 | // moving on -x throws incorrect values. 134 | if (axis === 'x') { 135 | if (value > 1000) return this.lastState; 136 | } 137 | 138 | // Update button state. 139 | if (button) { 140 | newState[button] = type !== 'up' ? 1 : 0; 141 | 142 | // Update button state and return. 143 | if (this.isButtonChange(newState)) { 144 | return newState; 145 | } 146 | } 147 | 148 | // Update axis and return. 149 | if (axis) newState[axis] = direction; 150 | return newState; 151 | } 152 | 153 | setEvents() { 154 | // Joystick events. 155 | if (this.joystick) { 156 | this.controller.joystick.on('change', event => { 157 | this.updateState(event); 158 | }); 159 | } 160 | 161 | // Button events. 162 | if (this.buttons) { 163 | const buttons = ['down', 'up', 'hold']; 164 | buttons.forEach(type => { 165 | this.controller.on(type, event => { 166 | this.updateState(event, type); 167 | }); 168 | }); 169 | } 170 | 171 | // Acceleromoter events. 172 | if (this.accelerometer) { 173 | this.controller.accelerometer.on('change', event => { 174 | this.updateState(event); 175 | }); 176 | } 177 | } 178 | 179 | logEvent(event, type = 'MOVEMENT') { 180 | const { axis, direction, target } = event; 181 | const button = this.getButtonName(target); 182 | const value = target[event.axis]; 183 | const { isUp, isDown } = target; 184 | 185 | if (process.env.X) if (axis !== 'x') return; 186 | 187 | console.log(` 188 | JOYSTICK EVENT: 189 | - value: ${value} 190 | - axis: ${axis} 191 | - direction: ${direction} 192 | - type: ${type} 193 | - button: ${button} 194 | - up: ${isUp} 195 | - down: ${isDown} 196 | `); 197 | } 198 | 199 | onChange(callback) { 200 | this.onChangeCallback = callback; 201 | } 202 | } 203 | 204 | const nunchuk = new Nunchuk(); 205 | module.exports = nunchuk; 206 | -------------------------------------------------------------------------------- /quadpod/robot.js: -------------------------------------------------------------------------------- 1 | const temporal = require('temporal'); 2 | 3 | function init(five, board) { 4 | const quad = { 5 | status: 'sleep' 6 | }; 7 | 8 | const lift = { femur: 0 }; 9 | const easeIn = 'inQuad'; 10 | const easeOut = 'outQuad'; 11 | const gait = 1; 12 | const s = { 13 | front: { 14 | coxa: [ 90 - 30 * gait, 90, 90 + 30 * gait ], 15 | femur: [ 30, 0, 50 ] 16 | }, 17 | 18 | rear: { 19 | coxa: [ 90 + 30 * gait, 90, 90 - 30 * gait ], 20 | femur: [ 50, 0, 30 ] 21 | } 22 | }; 23 | 24 | // Front Right Leg 25 | quad.r1c = new five.Servo({ address: 0x40, controller: 'PCA9685', pin: 0, board }); 26 | quad.r1f = new five.Servo({ address: 0x40, controller: 'PCA9685', pin: 1, board, invert:true }); 27 | quad.r1 = new five.Servos([quad.r1c, quad.r1f]); 28 | 29 | // Front Left Leg 30 | quad.l1c = new five.Servo({ address: 0x40, controller: 'PCA9685', pin: 2, board, invert:true }); 31 | quad.l1f = new five.Servo({ address: 0x40, controller: 'PCA9685', pin: 3, board }); 32 | quad.l1 = new five.Servos([quad.l1c, quad.l1f]); 33 | 34 | // Rear Right Leg 35 | quad.r2c = new five.Servo({ address: 0x40, controller: 'PCA9685', pin: 4, board, invert: true }); 36 | quad.r2f = new five.Servo({ address: 0x40, controller: 'PCA9685', pin: 5, board, invert: true }); 37 | quad.r2 = new five.Servos([quad.r2c, quad.r2f]); 38 | 39 | //Rear Left Leg 40 | quad.l2c = new five.Servo({ address: 0x40, controller: 'PCA9685', pin: 6, board }); 41 | quad.l2f = new five.Servo({ address: 0x40, controller: 'PCA9685', pin: 7, board }); 42 | quad.l2 = new five.Servos([quad.l2c, quad.l2f]); 43 | 44 | quad.coxa = new five.Servos([quad.r1c, quad.l1c, quad.r2c, quad.l2c]); 45 | quad.femurs = new five.Servos([quad.r1f, quad.l1f, quad.r2f, quad.l2f]); 46 | 47 | quad.joints = new five.Servos([quad.coxa, quad.femurs]); 48 | 49 | quad.legs = new five.Servos([ 50 | quad.r1c, quad.r1f, 51 | quad.l1c, quad.l1f, 52 | quad.r2c, quad.r2f, 53 | quad.l2c, quad.l2f 54 | ]); 55 | 56 | const legsAnimation = new five.Animation(quad.legs); 57 | const femursAnimation = new five.Animation(quad.femurs); 58 | 59 | const stand = { 60 | target: quad.joints, 61 | duration: 500, 62 | loop: false, 63 | fps: 100, 64 | cuePoints: [0, 0.1, 0.3, 0.7, 1.0], 65 | oncomplete: function oncomplete() { 66 | quad.state = 'stand'; 67 | }, 68 | keyFrames: [ 69 | [null, { degrees: s.front.coxa[1] }], 70 | [ 71 | null, 72 | false, 73 | false, 74 | { 75 | degrees: s.front.femur[1], 76 | easing: easeOut 77 | }, 78 | { 79 | degrees: s.front.femur[1], 80 | easing: easeIn 81 | } 82 | ] 83 | ] 84 | }; 85 | 86 | quad.walk = function quadWalk(dir) { 87 | let a = dir === 'rev' ? 0 : 2, 88 | b = dir === 'rev' ? 2 : 0; 89 | 90 | legsAnimation.enqueue({ 91 | duration: 1500, 92 | cuePoints: [0, 0.25, 0.5, 0.625, 0.75, 0.875, 1.0], 93 | loop: true, 94 | loopback: 0.5, 95 | fps: 100, 96 | onstop: function onstop() { 97 | quad.att(); 98 | }, 99 | oncomplete: function oncomplete() {}, 100 | keyFrames: [ 101 | /* r1c, r1f */ 102 | [ 103 | null, 104 | null, 105 | { degrees: s.front.coxa[a] }, 106 | { degrees: s.front.coxa[1] }, 107 | { degrees: s.front.coxa[b] }, 108 | null, 109 | { degrees: s.front.coxa[a] } 110 | ], 111 | [ 112 | null, 113 | { step: lift.femur, easing: easeOut }, 114 | { degrees: s.front.femur[a], easing: easeIn }, 115 | { degrees: s.front.femur[1] }, 116 | { degrees: s.front.femur[b] }, 117 | { step: lift.femur, easing: easeOut }, 118 | { degrees: s.front.femur[a], easing: easeIn }, 119 | ], 120 | 121 | /* l1c, l1f */ 122 | [ 123 | null, 124 | null, 125 | { degrees: s.front.coxa[b] }, 126 | null, 127 | { degrees: s.front.coxa[a] }, 128 | { degrees: s.front.coxa[1] }, 129 | { degrees: s.front.coxa[b] }, 130 | ], 131 | [ 132 | null, 133 | null, 134 | { degrees: s.front.femur[b] }, 135 | { step: lift.femur, easing: easeOut }, 136 | { degrees: s.front.femur[a], easing: easeIn }, 137 | { degrees: s.front.femur[1] }, 138 | { degrees: s.front.femur[b] }, 139 | ], 140 | 141 | /* r2c, r2f */ 142 | [ 143 | null, 144 | null, 145 | { degrees: s.rear.coxa[b] }, 146 | null, 147 | { degrees: s.rear.coxa[a] }, 148 | { degrees: s.rear.coxa[1] }, 149 | { degrees: s.rear.coxa[b] }, 150 | ], 151 | [ 152 | null, 153 | null, 154 | { degrees: s.rear.femur[b] }, 155 | { step: lift.femur, easing: easeOut }, 156 | { degrees: s.rear.femur[a], easing: easeIn }, 157 | { degrees: s.rear.femur[1] }, 158 | { degrees: s.rear.femur[b] }, 159 | ], 160 | 161 | /* l2c, l2f */ 162 | [ 163 | null, 164 | null, 165 | { degrees: s.rear.coxa[a] }, 166 | { degrees: s.rear.coxa[1] }, 167 | { degrees: s.rear.coxa[b] }, 168 | null, 169 | { degrees: s.rear.coxa[a] }, 170 | ], 171 | [ 172 | null, 173 | { step: lift.femur, easing: easeOut }, 174 | { degrees: s.rear.femur[a], easing: easeIn }, 175 | { degrees: s.rear.femur[1] }, 176 | { degrees: s.rear.femur[b] }, 177 | { step: lift.femur, easing: easeOut }, 178 | { degrees: s.rear.femur[a], easing: easeIn }, 179 | ], 180 | ], 181 | }); 182 | }; 183 | 184 | quad.turn = function quadTurn(dir) { 185 | let a = dir === 'left' ? 0 : 2, 186 | b = dir === 'left' ? 2 : 0; 187 | 188 | legsAnimation.enqueue({ 189 | duration: 1500, 190 | fps: 100, 191 | cuePoints: [0, 0.25, 0.5, 0.625, 0.75, 0.875, 1.0], 192 | loop: true, 193 | loopback: 0.5, 194 | onstop: function onstop() { 195 | quad.att(); 196 | }, 197 | keyFrames: [ 198 | [ 199 | null, 200 | null, 201 | { degrees: s.front.coxa[a] }, 202 | null, 203 | { degrees: s.front.coxa[b] }, 204 | null, 205 | { degrees: s.front.coxa[a] }, 206 | ], 207 | [ 208 | null, 209 | null, 210 | { degrees: s.front.femur[a] }, 211 | { step: lift.femur, easing: easeOut }, 212 | { degrees: s.front.femur[b] }, 213 | null, 214 | { degrees: s.front.femur[a] }, 215 | ], 216 | 217 | [ 218 | null, 219 | null, 220 | { degrees: s.front.coxa[a] }, 221 | null, 222 | { degrees: s.front.coxa[b] }, 223 | null, 224 | { degrees: s.front.coxa[a] }, 225 | ], 226 | [ 227 | null, 228 | { step: lift.femur, easing: easeOut }, 229 | { degrees: s.front.femur[a], easing: easeIn }, 230 | null, 231 | { degrees: s.front.femur[b], easing: easeIn }, 232 | { step: lift.femur, easing: easeOut }, 233 | { degrees: s.front.femur[a], easing: easeIn }, 234 | ], 235 | 236 | [ 237 | null, 238 | null, 239 | { degrees: s.rear.coxa[b] }, 240 | null, 241 | { degrees: s.rear.coxa[a] }, 242 | null, 243 | { degrees: s.rear.coxa[b] }, 244 | ], 245 | [ 246 | null, 247 | { step: lift.femur, easing: easeOut }, 248 | { degrees: s.rear.femur[b], easing: easeIn }, 249 | null, 250 | { degrees: s.rear.femur[a], easing: easeIn }, 251 | { step: lift.femur, easing: easeOut }, 252 | { degrees: s.rear.femur[b], easing: easeIn }, 253 | ], 254 | 255 | [ 256 | null, 257 | null, 258 | { degrees: s.rear.coxa[b] }, 259 | null, 260 | { degrees: s.rear.coxa[a] }, 261 | null, 262 | { degrees: s.rear.coxa[b] }, 263 | ], 264 | [ 265 | null, 266 | null, 267 | { degrees: s.rear.femur[b] }, 268 | { step: lift.femur, easing: easeOut }, 269 | { degrees: s.rear.femur[a] }, 270 | null, 271 | { degrees: s.rear.femur[b] }, 272 | ], 273 | ], 274 | }); 275 | }; 276 | 277 | quad.att = function quadStand() { 278 | let grouped, 279 | work = [ 280 | { 281 | name: 'r1', 282 | offset: 0, 283 | home: s.front.femur[1], 284 | chome: s.front.coxa[1], 285 | }, 286 | { 287 | name: 'r2', 288 | offset: 0, 289 | home: s.rear.femur[1], 290 | chome: s.front.coxa[1], 291 | }, 292 | { 293 | name: 'l1', 294 | offset: 0, 295 | home: s.front.femur[1], 296 | chome: s.front.coxa[1], 297 | }, 298 | { 299 | name: 'l2', 300 | offset: 0, 301 | home: s.rear.femur[1], 302 | chome: s.front.coxa[1], 303 | }, 304 | ]; 305 | 306 | work.forEach((leg, i) => { 307 | work[i].offset = Math.abs( 308 | quad[leg.name + 'f'].last.reqDegrees - leg.home 309 | ); 310 | }); 311 | 312 | if (work[1].offset > work[3].offset) { 313 | grouped = [[0, 2], [1, 3]]; 314 | } else { 315 | grouped = [[1, 3], [0, 2]]; 316 | } 317 | 318 | grouped.forEach((group, i) => { 319 | group.forEach(leg => { 320 | temporal.queue([ 321 | { 322 | delay: 250 * i, 323 | task: () => { 324 | quad[work[leg].name + 'f'].to(work[leg].home + lift.femur); 325 | }, 326 | }, 327 | { 328 | delay: 50, 329 | task: () => { 330 | quad[work[leg].name + 'c'].to(work[leg].chome); 331 | }, 332 | }, 333 | { 334 | delay: 50, 335 | task: () => { 336 | quad[work[leg].name + 'f'].to(work[leg].home); 337 | }, 338 | }, 339 | ]); 340 | }); 341 | }); 342 | quad.state = 'stand'; 343 | }; 344 | 345 | quad.sleep = function() { 346 | femursAnimation.enqueue({ 347 | duration: 1000, 348 | fps: 100, 349 | cuePoints: [0, 0.5, 1.0], 350 | loop: false, 351 | oncomplete: function onstop() { 352 | // quad.stop() 353 | }, 354 | keyFrames: [ 355 | [{ degrees: 0 }, { degrees: 45 }, { degrees: 180 }], 356 | [{ degrees: 0 }, { degrees: 45 }, { degrees: 180 }], 357 | [{ degrees: 0 }, { degrees: 45 }, { degrees: 180 }], 358 | [{ degrees: 0 }, { degrees: 45 }, { degrees: 180 }], 359 | ], 360 | }); 361 | }; 362 | 363 | quad.stand = () => { 364 | legsAnimation.enqueue(stand); 365 | }; 366 | 367 | quad.stop = () => { 368 | legsAnimation.stop(); 369 | }; 370 | 371 | quad.joints.to(90); 372 | 373 | // Return the usable robot. 374 | return quad; 375 | } 376 | 377 | module.exports = { init }; 378 | -------------------------------------------------------------------------------- /quadpod/test.js: -------------------------------------------------------------------------------- 1 | const five = require('johnny-five'); 2 | 3 | const board = new five.Board( 4 | {port: '/dev/cu.NODEBOT_BETA-DevB', timeout: 30000 } // replace this with the right port on your machine 5 | ); 6 | 7 | board.on('ready', () => { 8 | 9 | const a = new five.Servo({ pin: 6 }); 10 | const b = new five.Servo({ pin: 7 }); 11 | const c = new five.Servo({ pin: 8 }); 12 | const d = new five.Servo({ pin: 9 }); 13 | const e = new five.Servo({ pin: 10 }); 14 | const f = new five.Servo({ pin: 11 }); 15 | const g = new five.Servo({ pin: 12 }); 16 | const h = new five.Servo({ pin: 13 }); 17 | 18 | const servos = new five.Servos([a, b, c, d, e, f, g, h]); 19 | 20 | board.repl.inject({ 21 | a, b, c, d, e, f, g, h, servos 22 | }); 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /robot-arm/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Robot Arm 4 | 5 | ### Componentes 6 | - 4 servos Montados en un brazo mecanico. 7 | - 3 Potenciometro 8 | - 1 Botón 9 | 10 | ### Instrucciones 11 | Usando como base los ejemplos de [potenciometro](../examples/potentiometer), [botones](../examples/button) y [servos](../examples/servo_motors) escribir un script que use los tres potenciometros para guiar el movimiento del brazo mecanico, y el boton para abrir y cerrar las pinzas. Una alternativa puede ser aplicar [animaciones](../quadpod) desde el lado de la lógica para realizar acciones predefinidas. 12 | 13 | ## License 14 | Licensed under the MIT license. 15 | -------------------------------------------------------------------------------- /simon_x3/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Simon de 3 notas. 4 | 5 | ### Componentes 6 | - 1 buzzer Piezo 7 | - 3 Botones con resistencias 8 | - 3 Leds con resistencias 9 | 10 | ### Instrucciones 11 | Usando como base los ejemplos de [leds](../examples/led), [piezo](../examples/piezo) y [botones](../examples/button) escribir un script que simule el tradicional juego Simon. Lógica basica: se elije uno de 3 sonidos y se reproduce junto con un indicador visual (led), en cada iteración se mantiene la secuencia original pero se agrega un sonido mas al final. El jugador debe siempre repetir la secuencia correctamente, al perder se inicia una sequencia nueva. 12 | 13 | ## License 14 | Licensed under the MIT license. 15 | -------------------------------------------------------------------------------- /simon_x4/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## Simon de 4 notas. 4 | 5 | ### Componentes 6 | - 1 buzzer Piezo 7 | - 4 Botones con resistencias 8 | - 4 Leds con resistencias 9 | 10 | ### Instrucciones 11 | Usando como base los ejemplos de [leds](../examples/led), [piezo](../examples/piezo) y [botones](../examples/button) escribir un script que simule el tradicional juego Simon. Lógica basica: se elije uno de 4 sonidos y se reproduce junto con un indicador visual (led), en cada iteración se mantiene la secuencia original pero se agrega un sonido mas al final. El jugador debe siempre repetir la secuencia correctamente, al perder se inicia una sequencia nueva. 12 | 13 | ## License 14 | Licensed under the MIT license. -------------------------------------------------------------------------------- /spacebucket/README.md: -------------------------------------------------------------------------------- 1 | # RoboticaJS 2 | 3 | ## SpaceBuckets 4 | 5 | ### Componentes 6 | - 1 Relee de 4 puertos. 7 | - 1 Sensor de humedad de suelo analogo. 8 | - 1 Sensor de humedad de suelo digital. 9 | - 1 higrometro/termometro. 10 | - 1 fotorresistencia. 11 | - 2 ventiladores. 12 | - 1 led strip. 13 | - 1 bomba de agua. 14 | 15 | 16 | ### Instrucciones 17 | Este proyecto es el mas complejo y extenso, consta de una variedad de sensores que nos permiten analizar el estado de un ambiente cerrado y controlarlo para mantenerlo dentro de los parametros establecidos. 18 | 19 | ## License 20 | Licensed under the MIT license. 21 | --------------------------------------------------------------------------------