├── .gitignore ├── scripts ├── screenon.sh └── screenoff.sh ├── IMG_0747.jpg ├── Gesture_sensor_3.png ├── MMM-GroveGestures.css ├── package.json ├── LICENSE.md ├── py ├── gesture_print.py ├── grove_gesture_sensor.py.RPI └── grove_gesture_sensor.py.ATB ├── node_helper.js ├── MMM-GroveGestures.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /scripts/screenon.sh: -------------------------------------------------------------------------------- 1 | vcgencmd display_power 1 2 | -------------------------------------------------------------------------------- /scripts/screenoff.sh: -------------------------------------------------------------------------------- 1 | vcgencmd display_power 0 2 | -------------------------------------------------------------------------------- /IMG_0747.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MMRIZE/MMM-GroveGestures/HEAD/IMG_0747.jpg -------------------------------------------------------------------------------- /Gesture_sensor_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MMRIZE/MMM-GroveGestures/HEAD/Gesture_sensor_3.png -------------------------------------------------------------------------------- /MMM-GroveGestures.css: -------------------------------------------------------------------------------- 1 | #GROVEGESTURE { 2 | 3 | } 4 | 5 | #GROVEGESTURE.visible { 6 | display:block; 7 | } 8 | 9 | #GROVEGESTURE.non_visible { 10 | display:none; 11 | } 12 | 13 | #GESTURE_DISP { 14 | display:none; 15 | box-sizing: border-box; 16 | border-radius:20px; 17 | height:40px; 18 | background-color:#FFF; 19 | padding:10px 20px; 20 | line-height: 100%; 21 | vertical-align: top; 22 | font-size:20px; 23 | color:#FFF; 24 | background-color:rgba(64,255,64,0.8); 25 | } 26 | 27 | #GESTURE_DISP.visible { 28 | display:block; 29 | } 30 | 31 | #GESTURE_DISP.finish { 32 | display:block; 33 | background-color:rgba(64,64,255,0.8); 34 | } 35 | 36 | #GESTURE_DISP { 37 | display:none; 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mmm-grovegesture", 3 | "version": "1.1.2", 4 | "description": "MagicMirror module - detect gesture with GroveGesture sensor(PAJ7620u2)", 5 | "main": "MMM-GroveGestures.js", 6 | "keywords": [ 7 | "magic mirror", 8 | "module", 9 | "Grove Gesture", 10 | "gesture", 11 | "PAJ7620u2" 12 | ], 13 | "author": "eouia", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/MMRIZE/MMM-GroveGestures/issues" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/MMRIZE/MMM-GroveGestures.git" 21 | }, 22 | "dependencies": { 23 | "python-shell": "^1.0.6" 24 | }, 25 | "homepage": "https://github.com/MMRIZE/MMM-GroveGestures#readme", 26 | "scripts": { 27 | "test": "echo \"Error: no test specified\" && exit 1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright © 2018 Seongnoh Sean Yi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /py/gesture_print.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # GrovePi Example for using the Grove - Gesture Sensor v1.0(http://www.seeedstudio.com/depot/Grove-Gesture-p-2463.html) 4 | # 5 | # This example prints the gesture on the screen when a user does an action over the sensor 6 | # 7 | # The GrovePi connects the Raspberry Pi and Grove sensors. You can learn more about GrovePi here: http://www.dexterindustries.com/GrovePi 8 | # 9 | # Have a question about this example? Ask on the forums here: http://forum.dexterindustries.com/c/grovepi 10 | # 11 | ''' 12 | ## License 13 | 14 | The MIT License (MIT) 15 | 16 | GrovePi for the Raspberry Pi: an open source platform for connecting Grove Sensors to the Raspberry Pi. 17 | Copyright (C) 2017 Dexter Industries 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | ''' 37 | import grove_gesture_sensor 38 | import time 39 | 40 | g=grove_gesture_sensor.gesture() 41 | g.init() 42 | while True: 43 | g.print_gesture() 44 | time.sleep(.05) 45 | -------------------------------------------------------------------------------- /node_helper.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | const exec = require("child_process").exec 3 | const {PythonShell} = require("python-shell") 4 | 5 | var NodeHelper = require('node_helper') 6 | 7 | module.exports = NodeHelper.create({ 8 | 9 | start: function() { 10 | this.config = {} 11 | this.gestureSequence = "" 12 | this.commandTimer = null 13 | this.shell = null 14 | }, 15 | 16 | stop: function() { 17 | this.log("[GESTURE] Finishing...") 18 | if (this.shell) { 19 | this.shell.end() 20 | } 21 | }, 22 | 23 | socketNotificationReceived: function (noti, payload) { 24 | switch (noti) { 25 | case "INIT": 26 | this.job(payload) 27 | break 28 | case "SHELLEXEC": 29 | exec(payload, (e,so,se)=>{ 30 | this.log("[GESTURE] Shell command: " + payload) 31 | if (e) console.log(e) 32 | }) 33 | break 34 | } 35 | }, 36 | 37 | log : function(obj) { 38 | if (this.config.verbose) { 39 | console.log(obj) 40 | } 41 | }, 42 | 43 | job: function(config) { 44 | this.config = config 45 | var map = this.config.gestureMapFromTo 46 | var py = path.resolve(__dirname, "py", "gesture_print.py") 47 | var option = { 48 | mode: "text", 49 | pythonPath:this.config.pythonPath, 50 | pythonOptions: ['-u'], 51 | } 52 | this.shell = new PythonShell(py, option) 53 | this.shell.on("message", (message)=>{ 54 | this.log("[GESTURE] ORIGIN:" + message) 55 | var gesture = null 56 | gesture = (map.hasOwnProperty(message)) ? map[message] : null 57 | if (gesture) { 58 | this.gestureProcess(gesture) 59 | } 60 | }) 61 | this.shell.on("error", (message)=>{ 62 | this.shell.end() 63 | if (!message.traceback.search("KeyboardInterrupt")) { 64 | this.log(message) 65 | } else { 66 | this.log("Keyboard Interrupted") 67 | } 68 | this.log("[GESTURE] Gesture script is finished.") 69 | }) 70 | this.shell.on("close", ()=>{ 71 | setTimeout(()=>{ 72 | this.log("[GESTURE] Python script is terminated. It will restart soon.") 73 | this.job(config) 74 | }, 500) 75 | }) 76 | }, 77 | 78 | gestureProcess: function(gesture) { 79 | clearTimeout(this.commandTimer) 80 | this.commandTimer = null 81 | if (gesture == this.config.cancelGesture) { 82 | this.log("[GESTURE] Cancel: " + this.gestureSequence) 83 | this.sendSocketNotification("CANCEL", { 84 | last: gesture, 85 | sequence: this.gestureSequence, 86 | }) 87 | this.gestureSequence = "" 88 | clearTimeout(this.commandTimer) 89 | } else { 90 | this.gestureSequence += (this.gestureSequence) ? ("-" + gesture) : gesture 91 | this.log("[GESTURE] Ongoing: " + this.gestureSequence) 92 | this.sendSocketNotification("ONGOING", { 93 | last:gesture, 94 | sequence: this.gestureSequence, 95 | }) 96 | this.log("[GESTURE] Timer reset") 97 | this.commandTimer = setTimeout(()=>{ 98 | this.log("[GESTURE] Finish: " + this.gestureSequence) 99 | this.sendSocketNotification("FINISH", { 100 | last:gesture, 101 | sequence: this.gestureSequence, 102 | }) 103 | this.gestureSequence = "" 104 | }, this.config.recognitionTimeout) 105 | 106 | } 107 | }, 108 | }) 109 | -------------------------------------------------------------------------------- /MMM-GroveGestures.js: -------------------------------------------------------------------------------- 1 | //TODO : notificationExec, shellExec 2 | Module.register("MMM-GroveGestures", { 3 | defaults: { 4 | verbose:false, 5 | recognitionTimeout: 1500, 6 | cancelGesture: "WAVE", 7 | visible: true, 8 | idleTimer: 1000*60*30, // `0` for disable 9 | onIdle: { 10 | notificationExec: { 11 | notification: "GESTURE_IDLE", 12 | } 13 | }, 14 | onDetected: { 15 | notificationExec: { 16 | notification: "GESTURE_DETECTED", 17 | }, 18 | }, 19 | defaultCommandSet: "default", 20 | commandSet: { 21 | "default": { 22 | "FORWARD-BACKWARD": { 23 | notificationExec: { 24 | notification: "ASSISTANT_ACTIVATE", 25 | payload: null 26 | } 27 | }, 28 | "LEFT-RIGHT": { 29 | notificationExec: { 30 | notification: "ASSISTANT_CLEAR", 31 | payload:null, 32 | } 33 | }, 34 | "CLOCKWISE": { 35 | moduleExec: { 36 | module: [], 37 | exec: (module, gestures) => { 38 | module.hide(1000, () => { }, { lockString: "GESTURE" }) 39 | } 40 | } 41 | }, 42 | "ANTICLOCKWISE": { 43 | moduleExec: { 44 | module: [], 45 | exec: (module, gestures) => { 46 | module.show(1000, null, {lockstring:"GESTURE"}) 47 | } 48 | } 49 | }, 50 | "LEFT": { 51 | notificationExec: { 52 | notification: "ARTICLE_PREVIOUS", 53 | payload: null, 54 | } 55 | }, 56 | "RIGHT": { 57 | notificationExec: { 58 | notification: "ARTICLE_NEXT", 59 | payload: null, 60 | } 61 | }, 62 | }, 63 | }, 64 | 65 | command: {}, 66 | commandSetTrigger: { 67 | }, 68 | 69 | 70 | gestureMapFromTo: { 71 | "Up": "UP", 72 | "Down": "DOWN", 73 | "Left": "LEFT", 74 | "Right": "RIGHT", 75 | "Forward": "FORWARD", 76 | "Backward": "BACKWARD", 77 | "Clockwise": "CLOCKWISE", 78 | "anti-clockwise": "ANTICLOCKWISE", 79 | "wave": "WAVE" 80 | }, 81 | 82 | defaultNotification: "GESTURE", 83 | pythonPath: "/usr/bin/python", 84 | 85 | }, 86 | 87 | getStyles: function() { 88 | return ["MMM-GroveGestures.css"] 89 | }, 90 | 91 | start: function(){ 92 | this.idleTimer = null 93 | }, 94 | 95 | getDom: function() { 96 | var wrapper = document.createElement("div") 97 | wrapper.id = "GROVEGESTURE" 98 | wrapper.className = (this.config.visible) ? "visible" : "non_visible" 99 | 100 | var win = document.createElement("div") 101 | win.id = "GESTURE_DISP" 102 | wrapper.appendChild(win) 103 | 104 | return wrapper 105 | }, 106 | 107 | notificationReceived: function(noti, payload, sender) { 108 | if (noti == "DOM_OBJECTS_CREATED") { 109 | this.prepare() 110 | } 111 | 112 | var commandSetName = "" 113 | if (this.config.commandSetTrigger.hasOwnProperty(noti)) { 114 | if (typeof this.config.commandSetTrigger[noti] == "function") { 115 | commandSetName = this.config.commandSetTrigger[noti](payload) 116 | } else { 117 | commandSetName = this.config.commandSetTrigger[noti] 118 | } 119 | this.setCommandSet(commandSetName) 120 | } 121 | }, 122 | 123 | socketNotificationReceived: function(noti, payload, sender) { 124 | var gestures = { 125 | status: noti, 126 | last: payload.last, 127 | sequence: payload.sequence 128 | } 129 | if (this.config.onDetected) { 130 | this.doCommand(this.config.onDetected, gestures) 131 | } 132 | switch(noti) { 133 | case "FINISH": 134 | if (gestures.sequence in this.config.command) { 135 | this.doCommand(this.config.command[gestures.sequence], gestures) 136 | } 137 | //this.cancelCommand() 138 | break 139 | case "ONGOING": 140 | this.showCommand(gestures) 141 | break 142 | case "CANCEL": 143 | this.cancelCommand() 144 | break 145 | } 146 | 147 | if (this.config.idleTimer > 0) { 148 | clearTimeout(this.idleTimer) 149 | this.idleTimer = setTimeout(()=>{ 150 | this.doCommand(this.config.onIdle, gestures) 151 | }, this.config.idleTimer) 152 | } 153 | }, 154 | 155 | showCommand: function(gestures) { 156 | var sequence = gestures.sequence 157 | if (this.config.visible) { 158 | var seq = document.getElementById("GESTURE_DISP") 159 | seq.innerHTML = sequence 160 | seq.className = "visible" 161 | } 162 | }, 163 | 164 | cancelCommand: function() { 165 | if (this.config.visible) { 166 | var seq = document.getElementById("GESTURE_DISP") 167 | seq.className = "" 168 | seq.innerHTML = "" 169 | } 170 | }, 171 | 172 | doCommand: function(command, gestures=null) { 173 | var sequence = gestures.sequence 174 | 175 | if (command.hasOwnProperty("shellExec")) { 176 | this.sendSocketNotification("SHELLEXEC", command.shellExec) 177 | } 178 | if (command.hasOwnProperty("moduleExec")) { 179 | var tm = command.moduleExec.module 180 | if (Array.isArray(tm)) { 181 | // do nothing 182 | } else if (tm){ 183 | var ttm = tm.toString() 184 | tm = [] 185 | tm.push(ttm) 186 | } else { 187 | tm = [] 188 | } 189 | var modules = MM.getModules().enumerate((module)=>{ 190 | if (tm.length == 0 || tm.includes(module.name)) { 191 | //console.log("EXEC", command.moduleExec.exec) 192 | command.moduleExec.exec(module, gestures) 193 | } 194 | }) 195 | } 196 | if (command.hasOwnProperty("notificationExec")) { 197 | var noti = (command.notificationExec.notification) ? command.notificationExec.notification : this.config.defaultNotification 198 | var payload = (typeof command.notificationExec.payload == "undefined") ? gestures : command.notificationExec.payload 199 | this.sendNotification(noti, payload) 200 | } 201 | this.cancelCommand() 202 | }, 203 | 204 | prepare: function() { 205 | this.setCommandSet(this.config.defaultCommandSet) 206 | this.sendSocketNotification("INIT", this.config) 207 | }, 208 | 209 | setCommandSet: function(commandSetName) { 210 | if (this.config.commandSet.hasOwnProperty(commandSetName)) { 211 | this.config.command = Object.assign({}, this.config.commandSet[commandSetName]) 212 | } 213 | } 214 | }) 215 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MMM-GroveGestures 2 | 3 | MagicMirror Module - detecting 3D gesture with GroveGesture Sensor(PAJ7620u2) 4 | 5 | ## Screenshot 6 | 7 | [![2.1.0 demo](https://img.youtube.com/vi/BcpZvkcrfHU/0.jpg)](https://youtu.be/BcpZvkcrfHU) 8 | 9 | ## NEW UPDATES 10 | 11 | **1.1.1** 12 | 13 | - Fix: bug of `moduleExec` 14 | 15 | **1.1.0** 16 | 17 | - `commandSet` is added. 18 | - You can change set of your commands on runtime by notification. 19 | - See the `commandSet` section. 20 | 21 | ## Hardware 22 | 23 | ![image](/Gesture_sensor_3.png) 24 | 25 | - Grove Gesture Sensor (PAJ7620u2) 26 | - The sensor on Grove - Gesture is PAJ7620U2 that integrates gesture recognition function with general I2C interface into a single chip. It can recognize 9 basic gestures, and these gestures information can be simply accessed via the I2C bus. 27 | - price: $10~20USD 28 | - gestures : Up / Down / Left / Right / Forward / Backward / Clockwise / Count Clockwise / Wave 29 | - detection range : 5 ~ 10cm / non-touch 30 | - size : 20mm X 20mm 31 | 32 | Unfortunately, due to it's short range and theory of electro-magnetic field, this sensor will not be used beneath Spymirror. But under the thin wooden, plastic or normal glass, it will work. 33 | It works as 3D gesture sensor similar with `Skywriter` or `Flick!`, but definitely small, flat, and non-HAT. It could fit for bezel-frame of MagicMirror. Soldering is not needed. 34 | 35 | ### Comparison 36 | 37 | - vs. PIR sensor, ultrasonic sensor, CAM motion detection 38 | - **PROS**: more gestures 39 | - **CONS**: short range 40 | - vs. Skywriter, Flick! 41 | - **PROS**: small 42 | - **CONS**: touch, tap gestures are not supported 43 | - vs. CAM gesture recognition 44 | - **PROS**: light, easy, low CPU 45 | - **CONS**: short range, not so COOL 46 | 47 | ### REQUIREMENT 48 | 49 | - 1 x Grove Gesture Sensor (or PAJ7620u2 compatible) 50 | - 1 x 4pin Female jumper cable 51 | 52 | ### Pin Connect (I2C 1) 53 | 54 | - 5V (GPIO PIN 4) : VCC 55 | - GND (GPIO PIN 6) : GND 56 | - SDA (GPIO PIN 3) : SDA 57 | - SCL (GPIO PIN 5) : SCL 58 | ![](/IMG_0747.jpg) 59 | 60 | ### Install H/W 61 | 62 | - Go to Raspberry config program, set I2C (I2C_1) as enabled. Then shutdown. 63 | - Connect Sensor and RPI with cable. Then power on. 64 | - Check `sudo i2cdetect -y 1` or install it. 65 | 66 | ``` 67 | sudo apt-get update 68 | sudo apt-get install i2c-tools 69 | ``` 70 | 71 | - After installation rebooting might be needed. 72 | - Try again `sudo i2cdetect -y 1`, if you can see `73` on the matrix of result, H/W is installed properly. 73 | 74 | ## Installation of Module 75 | 76 | ```bash 77 | cd ~/MagicMirror/modules 78 | git clone https://github.com/MMRIZE/MMM-GroveGestures 79 | cd MMM-GroveGestures 80 | npm install 81 | cd scripts 82 | chmod +x *.sh 83 | ``` 84 | 85 | You might need to modify `/scripts/*.sh` files for your environment. 86 | 87 | After installation of module, 88 | 89 | ```bash 90 | cd ~/MagicMirror/modules/MMM-GroveGestures/py 91 | cp grove_gesture_sensor.py.RPI grove_gesture_sensor.py 92 | ``` 93 | 94 | You can test your sensor with this; 95 | 96 | ```bash 97 | cd ~/MagicMirror/modules/MMM-GroveGestures/py 98 | python gesture_print.py 99 | ``` 100 | 101 | ## Configuration 102 | 103 | ### Simple Version 104 | 105 | ```js 106 | { 107 | module: "MMM-GroveGestures", 108 | position: "top_right", 109 | config: {} 110 | }, 111 | ``` 112 | 113 | ### Details and default 114 | 115 | ```js 116 | { 117 | module: "MMM-GroveGestures", 118 | position: "top_right", 119 | config: { 120 | autoStart: true, //When Mirror starts, recognition will start. 121 | verbose:false, // If set as `true`, useful messages will be logged. 122 | recognitionTimeout: 1000, //Gesture sequence will be ended after this time from last recognized gesture. 123 | cancelGesture: "WAVE", //If set, You can cancel gesture sequence with this gesture. 124 | visible: true, //Recognized gesture sequence will be displayed on position 125 | 126 | idleTimer: 1000*60*30, // `0` for disable, After this time from last gesture, onIdle will be executed. 127 | onIdle: { // See command section 128 | moduleExec: { 129 | module: [], 130 | exec: (module, gestures) => { 131 | module.hide(1000, () => { }, { lockString: "GESTURE" }) 132 | } 133 | } 134 | }, 135 | onDetected: { 136 | notificationExec: { 137 | notification: "GESTURE_DETECTED", 138 | }, 139 | /* You can make Mirror to wake up the modules which were hidden by onIdle with any gestures. 140 | moduleExec: { 141 | module: [], 142 | exec: (module) => { 143 | module.show(1000, null, {lockstring:"GESTURE"}) 144 | } 145 | } 146 | */ 147 | }, 148 | 149 | gestureMapFromTo: { //When your sensor is installed with rotated direction, you can calibrate with this. 150 | "Up": "UP", 151 | "Down": "DOWN", 152 | "Left": "LEFT", 153 | "Right": "RIGHT", 154 | "Forward": "FORWARD", 155 | "Backward": "BACKWARD", 156 | "Clockwise": "CLOCKWISE", 157 | "anti-clockwise": "ANTICLOCKWISE", 158 | "wave": "WAVE" 159 | }, 160 | 161 | defaultNotification: "GESTURE", 162 | pythonPath: "/usr/bin/python", // your python path 163 | 164 | defaultCommandSet: "default", 165 | commandSet: { 166 | "default": { 167 | "FORWARD-BACKWARD": { 168 | notificationExec: { 169 | notification: "ASSISTANT_ACTIVATE", 170 | payload: null 171 | } 172 | }, 173 | "LEFT-RIGHT": { 174 | notificationExec: { 175 | notification: "ASSISTANT_CLEAR", 176 | payload:null, 177 | } 178 | }, 179 | "CLOCKWISE": { 180 | moduleExec: { 181 | module: [], 182 | exec: (module, gestures) => { 183 | module.hide(1000, () => { }, { lockString: "GESTURE" }) 184 | } 185 | } 186 | }, 187 | "ANTICLOCKWISE": { 188 | moduleExec: { 189 | module: [], 190 | exec: (module, gestures) => { 191 | module.show(1000, null, {lockstring:"GESTURE"}) 192 | } 193 | } 194 | }, 195 | "LEFT": { 196 | notificationExec: { 197 | notification: "ARTICLE_PREVIOUS", 198 | payload: null, 199 | } 200 | }, 201 | "RIGHT": { 202 | notificationExec: { 203 | notification: "ARTICLE_NEXT", 204 | payload: null, 205 | } 206 | }, 207 | }, 208 | }, 209 | } 210 | }, 211 | ``` 212 | 213 | ### Command 214 | 215 | ### Prepared Gestures 216 | 217 | You can modify, remove or create these gestures commands in `config.js` 218 | 219 | - **LEFT** : show previous article of default `newsfeed` 220 | - **RIGHT** : show next article of default `newsfeed` 221 | - **CLOCKWISE** : hide all modules on screen 222 | - **ANTICLOCKWISE** : show all modules(previously hidden by **CLOCKWISE** or **onIdle**) 223 | - **FORWARD-BACKWARD** : activate `MMM-AssistantMk2` instead Hotword 224 | - **LEFT-RIGHT** : stop youtube video(result of `MMM-AssistantMk2`) playing. 225 | 226 | #### Structure of `command` in Configuration 227 | 228 | ```js 229 | "GESTURE-SEQUENCE" : { 230 | shellExec: "...", 231 | notificationExec: { ... }, 232 | moduleExec: { ... } 233 | } 234 | ``` 235 | 236 | - You can define command with gesture(**LEFT**) or gesture sequence (**LEFT-LEFT-UP**) 237 | - 3 kinds of execution are available (be usable lonely or together) 238 | - `shellExec` : You can execute this `String` as shell command 239 | ```js 240 | shellExec: "sudo reboot now"; 241 | ``` 242 | 243 | - `notificationExec` : You can send notification with this. If payload is not defined, `gestures` Object will be used by default. If you want null value as payload, just set `payload:null`. 244 | ```js 245 | notificationExec: { 246 | notification: "SHOW_ALERT", 247 | payload: { 248 | message: "Hello, World", 249 | timer: 5000 250 | } 251 | } 252 | ``` 253 | 254 | - `moduleExec` : You can control Module and it's method. `exec` is function which has `module` and `gestures` objects as arguments. 255 | ```js 256 | moduleExec: { 257 | module: ["clock", "newsfeed"], // `[]` will be all modules. 258 | exec: (module, gestures) => { 259 | module.hide() 260 | } 261 | } 262 | ``` 263 | 264 | - `gestures` Object 265 | ```js 266 | gestures: { 267 | status: "FINISH", // "FINISH", "ONGOING", "CANCEL" 268 | last: "LEFT", // Last detected gesture 269 | sequence: "UP-DOWN-LEFT", // detected gesture sequence until now 270 | } 271 | ``` 272 | 273 | ### CommandSet 274 | 275 | You can define set of command for your purpose. The set would be changed by notification on runtime 276 | 277 | - Example 278 | 279 | ```js 280 | defaultCommandSet: "DEFAULT_MODE", 281 | commandSet: { 282 | "DEFAULT_MODE": { 283 | "LEFT": { 284 | notificationExec: { 285 | notification: "PAGE_INCREMENT", 286 | payload: null 287 | } 288 | }, 289 | }, 290 | "NEWS_MODE": { 291 | "LEFT": { 292 | notificationExec: { 293 | notification: "ARTICLE_NEXT", 294 | payload: null 295 | } 296 | }, 297 | } 298 | }, 299 | commandSetTrigger: { 300 | "GG_CHANGE_COMMANDSET_NEWSMODE": "NEWS_MODE", 301 | "GG_CHANGE_COMMANDSET_BY_PAYLOAD": (payload) => { // You can use callback function to change set with conditional payload values. 302 | return payload.commandSetName 303 | } 304 | }, 305 | ``` 306 | 307 | There are two sets of command - `DEFAULT_MODE` and `NEWS_MODE`. The "LEFT" command of each set could be defined differently. 308 | You can change the set by notification `GG_CHANGE_COMMANDSET_NEWSMODE` and `GG_CHANGE_COMMANDSET` which are defined in `commandSetTrigger`. If `GG_CHANGE_COMMANDSET_NEWSMODE` notification is arrived, the current set of commands will be changed to `NEWS_MODE`. 309 | Of course, you can redefine the names of trigger notifications or commandSets. 310 | 311 | ## Issues 312 | 313 | - `[GESTURE] Python script is terminated. It will restart soon.` : If you too often meet this error, adjust your i2c bus speed. (Default would be 100000, try 10000 or 32000.) 314 | https://www.raspberrypi-spy.co.uk/2018/02/change-raspberry-pi-i2c-bus-speed/ 315 | 316 | ## License 317 | 318 | This project is licensed under the MIT License - see the [LICENSE](LICENSE.md) file for details. 319 | -------------------------------------------------------------------------------- /py/grove_gesture_sensor.py.RPI: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # GrovePi Library for using the Grove - Gesture Sensor v1.0(http://www.seeedstudio.com/depot/Grove-Gesture-p-2463.html) 4 | # 5 | # The GrovePi connects the Raspberry Pi and Grove sensors. You can learn more about GrovePi here: http://www.dexterindustries.com/GrovePi 6 | # 7 | # Have a question about this library? Ask on the forums here: http://forum.dexterindustries.com/c/grovepi 8 | # 9 | # History 10 | # ------------------------------------------------ 11 | # Author Date Comments 12 | # Karan 31 Dec 15 Initial Authoring 13 | # 14 | # Code derived from the basic Arduino library for the Gesture Sensor by Seeed: https://github.com/Seeed-Studio/Gesture_PAJ7620 15 | 16 | # Released under the MIT license (http://choosealicense.com/licenses/mit/). 17 | # For more information see https://github.com/DexterInd/GrovePi/blob/master/LICENSE 18 | 19 | import time,sys 20 | import RPi.GPIO as GPIO 21 | import smbus 22 | 23 | # use the bus that matches your raspi version 24 | rev = GPIO.RPI_REVISION 25 | if rev == 2 or rev == 3: 26 | bus = smbus.SMBus(1) 27 | else: 28 | bus = smbus.SMBus(0) 29 | 30 | class gesture: 31 | #Registers and variables for the gesture sensor 32 | GES_REACTION_TIME =.200 # You can adjust the reaction time according to the actual circumstance. 33 | GES_ENTRY_TIME =.400 # When you want to recognize the Forward/Backward gestures, your gestures' reaction time must less than GES_ENTRY_TIME(0.8s). 34 | GES_QUIT_TIME =.800 35 | 36 | BANK0 = 0 37 | BANK1 = 1 38 | 39 | PAJ7620_ADDR_BASE =0x00 40 | 41 | #REGISTER BANK SELECT 42 | PAJ7620_REGITER_BANK_SEL =(PAJ7620_ADDR_BASE + 0xEF) #W 43 | 44 | #DEVICE ID 45 | PAJ7620_ID =0x73 46 | 47 | #REGISTER BANK 0 48 | PAJ7620_ADDR_SUSPEND_CMD =(PAJ7620_ADDR_BASE + 0x3) #W 49 | PAJ7620_ADDR_GES_PS_DET_MASK_0 =(PAJ7620_ADDR_BASE + 0x41) #RW 50 | PAJ7620_ADDR_GES_PS_DET_MASK_1 =(PAJ7620_ADDR_BASE + 0x42) #RW 51 | PAJ7620_ADDR_GES_PS_DET_FLAG_0 =(PAJ7620_ADDR_BASE + 0x43) #R 52 | PAJ7620_ADDR_GES_PS_DET_FLAG_1 =(PAJ7620_ADDR_BASE + 0x44) #R 53 | PAJ7620_ADDR_STATE_INDICATOR =(PAJ7620_ADDR_BASE + 0x45) #R 54 | PAJ7620_ADDR_PS_HIGH_THRESHOLD =(PAJ7620_ADDR_BASE + 0x69) #RW 55 | PAJ7620_ADDR_PS_LOW_THRESHOLD =(PAJ7620_ADDR_BASE + 0x6A) #RW 56 | PAJ7620_ADDR_PS_APPROACH_STATE =(PAJ7620_ADDR_BASE + 0x6B) #R 57 | PAJ7620_ADDR_PS_RAW_DATA =(PAJ7620_ADDR_BASE + 0x6C) #R 58 | 59 | #REGISTER BANK 1 60 | PAJ7620_ADDR_PS_GAIN =(PAJ7620_ADDR_BASE + 0x44) #RW 61 | PAJ7620_ADDR_IDLE_S1_STEP_0 =(PAJ7620_ADDR_BASE + 0x67) #RW 62 | PAJ7620_ADDR_IDLE_S1_STEP_1 =(PAJ7620_ADDR_BASE + 0x68) #RW 63 | PAJ7620_ADDR_IDLE_S2_STEP_0 =(PAJ7620_ADDR_BASE + 0x69) #RW 64 | PAJ7620_ADDR_IDLE_S2_STEP_1 =(PAJ7620_ADDR_BASE + 0x6A) #RW 65 | PAJ7620_ADDR_OP_TO_S1_STEP_0 =(PAJ7620_ADDR_BASE + 0x6B) #RW 66 | PAJ7620_ADDR_OP_TO_S1_STEP_1 =(PAJ7620_ADDR_BASE + 0x6C) #RW 67 | PAJ7620_ADDR_OP_TO_S2_STEP_0 =(PAJ7620_ADDR_BASE + 0x6D) #RW 68 | PAJ7620_ADDR_OP_TO_S2_STEP_1 =(PAJ7620_ADDR_BASE + 0x6E) #RW 69 | PAJ7620_ADDR_OPERATION_ENABLE =(PAJ7620_ADDR_BASE + 0x72) #RW 70 | 71 | #PAJ7620_REGITER_BANK_SEL 72 | PAJ7620_BANK0=0 73 | PAJ7620_BANK1=1 74 | 75 | #PAJ7620_ADDR_SUSPEND_CMD 76 | PAJ7620_I2C_WAKEUP =1 77 | PAJ7620_I2C_SUSPEND =0 78 | 79 | #PAJ7620_ADDR_OPERATION_ENABLE 80 | PAJ7620_ENABLE=1 81 | PAJ7620_DISABLE=0 82 | 83 | #ADC, delete 84 | REG_ADDR_RESULT = 0x00 85 | REG_ADDR_ALERT = 0x01 86 | REG_ADDR_CONFIG = 0x02 87 | REG_ADDR_LIMITL = 0x03 88 | REG_ADDR_LIMITH = 0x04 89 | REG_ADDR_HYST = 0x05 90 | REG_ADDR_CONVL = 0x06 91 | REG_ADDR_CONVH = 0x07 92 | 93 | GES_RIGHT_FLAG =1<<0 94 | GES_LEFT_FLAG =1<<1 95 | GES_UP_FLAG =1<<2 96 | GES_DOWN_FLAG =1<<3 97 | GES_FORWARD_FLAG =1<<4 98 | GES_BACKWARD_FLAG =1<<5 99 | GES_CLOCKWISE_FLAG =1<<6 100 | GES_COUNT_CLOCKWISE_FLAG =1<<7 101 | GES_WAVE_FLAG =1<<0 102 | 103 | #Gesture output 104 | FORWARD = 1 105 | BACKWARD = 2 106 | RIGHT = 3 107 | LEFT = 4 108 | UP = 5 109 | DOWN = 6 110 | CLOCKWISE = 7 111 | ANTI_CLOCKWISE = 8 112 | WAVE = 9 113 | 114 | #Initial register state 115 | initRegisterArray=( [0xEF,0x00], 116 | [0x32,0x29], 117 | [0x33,0x01], 118 | [0x34,0x00], 119 | [0x35,0x01], 120 | [0x36,0x00], 121 | [0x37,0x07], 122 | [0x38,0x17], 123 | [0x39,0x06], 124 | [0x3A,0x12], 125 | [0x3F,0x00], 126 | [0x40,0x02], 127 | [0x41,0xFF], 128 | [0x42,0x01], 129 | [0x46,0x2D], 130 | [0x47,0x0F], 131 | [0x48,0x3C], 132 | [0x49,0x00], 133 | [0x4A,0x1E], 134 | [0x4B,0x00], 135 | [0x4C,0x20], 136 | [0x4D,0x00], 137 | [0x4E,0x1A], 138 | [0x4F,0x14], 139 | [0x50,0x00], 140 | [0x51,0x10], 141 | [0x52,0x00], 142 | [0x5C,0x02], 143 | [0x5D,0x00], 144 | [0x5E,0x10], 145 | [0x5F,0x3F], 146 | [0x60,0x27], 147 | [0x61,0x28], 148 | [0x62,0x00], 149 | [0x63,0x03], 150 | [0x64,0xF7], 151 | [0x65,0x03], 152 | [0x66,0xD9], 153 | [0x67,0x03], 154 | [0x68,0x01], 155 | [0x69,0xC8], 156 | [0x6A,0x40], 157 | [0x6D,0x04], 158 | [0x6E,0x00], 159 | [0x6F,0x00], 160 | [0x70,0x80], 161 | [0x71,0x00], 162 | [0x72,0x00], 163 | [0x73,0x00], 164 | [0x74,0xF0], 165 | [0x75,0x00], 166 | [0x80,0x42], 167 | [0x81,0x44], 168 | [0x82,0x04], 169 | [0x83,0x20], 170 | [0x84,0x20], 171 | [0x85,0x00], 172 | [0x86,0x10], 173 | [0x87,0x00], 174 | [0x88,0x05], 175 | [0x89,0x18], 176 | [0x8A,0x10], 177 | [0x8B,0x01], 178 | [0x8C,0x37], 179 | [0x8D,0x00], 180 | [0x8E,0xF0], 181 | [0x8F,0x81], 182 | [0x90,0x06], 183 | [0x91,0x06], 184 | [0x92,0x1E], 185 | [0x93,0x0D], 186 | [0x94,0x0A], 187 | [0x95,0x0A], 188 | [0x96,0x0C], 189 | [0x97,0x05], 190 | [0x98,0x0A], 191 | [0x99,0x41], 192 | [0x9A,0x14], 193 | [0x9B,0x0A], 194 | [0x9C,0x3F], 195 | [0x9D,0x33], 196 | [0x9E,0xAE], 197 | [0x9F,0xF9], 198 | [0xA0,0x48], 199 | [0xA1,0x13], 200 | [0xA2,0x10], 201 | [0xA3,0x08], 202 | [0xA4,0x30], 203 | [0xA5,0x19], 204 | [0xA6,0x10], 205 | [0xA7,0x08], 206 | [0xA8,0x24], 207 | [0xA9,0x04], 208 | [0xAA,0x1E], 209 | [0xAB,0x1E], 210 | [0xCC,0x19], 211 | [0xCD,0x0B], 212 | [0xCE,0x13], 213 | [0xCF,0x64], 214 | [0xD0,0x21], 215 | [0xD1,0x0F], 216 | [0xD2,0x88], 217 | [0xE0,0x01], 218 | [0xE1,0x04], 219 | [0xE2,0x41], 220 | [0xE3,0xD6], 221 | [0xE4,0x00], 222 | [0xE5,0x0C], 223 | [0xE6,0x0A], 224 | [0xE7,0x00], 225 | [0xE8,0x00], 226 | [0xE9,0x00], 227 | [0xEE,0x07], 228 | [0xEF,0x01], 229 | [0x00,0x1E], 230 | [0x01,0x1E], 231 | [0x02,0x0F], 232 | [0x03,0x10], 233 | [0x04,0x02], 234 | [0x05,0x00], 235 | [0x06,0xB0], 236 | [0x07,0x04], 237 | [0x08,0x0D], 238 | [0x09,0x0E], 239 | [0x0A,0x9C], 240 | [0x0B,0x04], 241 | [0x0C,0x05], 242 | [0x0D,0x0F], 243 | [0x0E,0x02], 244 | [0x0F,0x12], 245 | [0x10,0x02], 246 | [0x11,0x02], 247 | [0x12,0x00], 248 | [0x13,0x01], 249 | [0x14,0x05], 250 | [0x15,0x07], 251 | [0x16,0x05], 252 | [0x17,0x07], 253 | [0x18,0x01], 254 | [0x19,0x04], 255 | [0x1A,0x05], 256 | [0x1B,0x0C], 257 | [0x1C,0x2A], 258 | [0x1D,0x01], 259 | [0x1E,0x00], 260 | [0x21,0x00], 261 | [0x22,0x00], 262 | [0x23,0x00], 263 | [0x25,0x01], 264 | [0x26,0x00], 265 | [0x27,0x39], 266 | [0x28,0x7F], 267 | [0x29,0x08], 268 | [0x30,0x03], 269 | [0x31,0x00], 270 | [0x32,0x1A], 271 | [0x33,0x1A], 272 | [0x34,0x07], 273 | [0x35,0x07], 274 | [0x36,0x01], 275 | [0x37,0xFF], 276 | [0x38,0x36], 277 | [0x39,0x07], 278 | [0x3A,0x00], 279 | [0x3E,0xFF], 280 | [0x3F,0x00], 281 | [0x40,0x77], 282 | [0x41,0x40], 283 | [0x42,0x00], 284 | [0x43,0x30], 285 | [0x44,0xA0], 286 | [0x45,0x5C], 287 | [0x46,0x00], 288 | [0x47,0x00], 289 | [0x48,0x58], 290 | [0x4A,0x1E], 291 | [0x4B,0x1E], 292 | [0x4C,0x00], 293 | [0x4D,0x00], 294 | [0x4E,0xA0], 295 | [0x4F,0x80], 296 | [0x50,0x00], 297 | [0x51,0x00], 298 | [0x52,0x00], 299 | [0x53,0x00], 300 | [0x54,0x00], 301 | [0x57,0x80], 302 | [0x59,0x10], 303 | [0x5A,0x08], 304 | [0x5B,0x94], 305 | [0x5C,0xE8], 306 | [0x5D,0x08], 307 | [0x5E,0x3D], 308 | [0x5F,0x99], 309 | [0x60,0x45], 310 | [0x61,0x40], 311 | [0x63,0x2D], 312 | [0x64,0x02], 313 | [0x65,0x96], 314 | [0x66,0x00], 315 | [0x67,0x97], 316 | [0x68,0x01], 317 | [0x69,0xCD], 318 | [0x6A,0x01], 319 | [0x6B,0xB0], 320 | [0x6C,0x04], 321 | [0x6D,0x2C], 322 | [0x6E,0x01], 323 | [0x6F,0x32], 324 | [0x71,0x00], 325 | [0x72,0x01], 326 | [0x73,0x35], 327 | [0x74,0x00], 328 | [0x75,0x33], 329 | [0x76,0x31], 330 | [0x77,0x01], 331 | [0x7C,0x84], 332 | [0x7D,0x03], 333 | [0x7E,0x01]) 334 | 335 | #Enable debug message 336 | debug=0 337 | 338 | #Initialize the sensors 339 | def init(self): 340 | time.sleep(.001) 341 | self.paj7620SelectBank(self.BANK0) 342 | self.paj7620SelectBank(self.BANK0) 343 | 344 | data0 = self.paj7620ReadReg(0, 1)[0] 345 | data1 = self.paj7620ReadReg(1, 1)[0] 346 | if self.debug: 347 | print("data0:",data0,"data1:",data1) 348 | if data0 != 0x20 :#or data1 <> 0x76: 349 | print("Error with sensor") 350 | #return 0xff 351 | if data0 == 0x20: 352 | print("wake-up finish.") 353 | 354 | for i in range(len(self.initRegisterArray)): 355 | self.paj7620WriteReg(self.initRegisterArray[i][0],self.initRegisterArray[i][1]) 356 | 357 | self.paj7620SelectBank(self.BANK0) 358 | 359 | print("Paj7620 initialize register finished.") 360 | 361 | #Write a byte to a register on the Gesture sensor 362 | def paj7620WriteReg(self,addr,cmd): 363 | bus.write_word_data(self.PAJ7620_ID, addr, cmd) 364 | 365 | #Select a register bank on the Gesture Sensor 366 | def paj7620SelectBank(self,bank): 367 | if bank==self.BANK0: 368 | self.paj7620WriteReg(self.PAJ7620_REGITER_BANK_SEL, self.PAJ7620_BANK0) 369 | 370 | #Read a block of bytes of length "qty" starting at address "addr" from the Gesture sensor 371 | def paj7620ReadReg(self,addr,qty): 372 | return bus.read_i2c_block_data(self.PAJ7620_ID, addr,qty) 373 | 374 | #Print the values from the gesture sensor 375 | def print_gesture(self): 376 | data=self.paj7620ReadReg(0x43,1)[0] 377 | if data==self.GES_RIGHT_FLAG: 378 | time.sleep(self.GES_ENTRY_TIME) 379 | data=self.paj7620ReadReg(0x43, 1)[0] 380 | if data == self.GES_FORWARD_FLAG: 381 | print("Forward") 382 | time.sleep(self.GES_QUIT_TIME) 383 | elif data == self.GES_BACKWARD_FLAG: 384 | print("Backward") 385 | time.sleep(self.GES_QUIT_TIME) 386 | else: 387 | print("Right") 388 | 389 | elif data==self.GES_LEFT_FLAG: 390 | time.sleep(self.GES_ENTRY_TIME) 391 | data=self.paj7620ReadReg(0x43, 1)[0] 392 | if data == self.GES_FORWARD_FLAG: 393 | print("Forward") 394 | time.sleep(self.GES_QUIT_TIME) 395 | elif data == self.GES_BACKWARD_FLAG: 396 | print("Backward") 397 | time.sleep(self.GES_QUIT_TIME) 398 | else: 399 | print("Left") 400 | 401 | elif data==self.GES_UP_FLAG: 402 | time.sleep(self.GES_ENTRY_TIME) 403 | data=self.paj7620ReadReg(0x43, 1)[0] 404 | if data == self.GES_FORWARD_FLAG: 405 | print("Forward") 406 | time.sleep(self.GES_QUIT_TIME) 407 | elif data == self.GES_BACKWARD_FLAG: 408 | print("Backward") 409 | time.sleep(self.GES_QUIT_TIME) 410 | else: 411 | print("Up") 412 | 413 | elif data==self.GES_DOWN_FLAG: 414 | time.sleep(self.GES_ENTRY_TIME) 415 | data=self.paj7620ReadReg(0x43, 1)[0] 416 | if data == self.GES_FORWARD_FLAG: 417 | print("Forward") 418 | time.sleep(self.GES_QUIT_TIME) 419 | elif data == self.GES_BACKWARD_FLAG: 420 | print("Backward") 421 | time.sleep(self.GES_QUIT_TIME) 422 | else: 423 | print("Down") 424 | 425 | elif data==self.GES_FORWARD_FLAG: 426 | print("Forward") 427 | time.sleep(self.GES_QUIT_TIME) 428 | 429 | elif data==self.GES_BACKWARD_FLAG: 430 | print("Backward") 431 | time.sleep(self.GES_QUIT_TIME) 432 | 433 | elif data==self.GES_CLOCKWISE_FLAG: 434 | print("Clockwise") 435 | 436 | elif data==self.GES_COUNT_CLOCKWISE_FLAG: 437 | print("anti-clockwise") 438 | 439 | else: 440 | data1=self.paj7620ReadReg(0x44, 1)[0] 441 | if (data1 == self.GES_WAVE_FLAG): 442 | print("wave") 443 | 444 | #Return a vlaue from the gestire sensor which can be used in a program 445 | # 0:nothing 446 | # 1:Forward 447 | # 2:Backward 448 | # 3:Right 449 | # 4:Left 450 | # 5:Up 451 | # 6:Down 452 | # 7:Clockwise 453 | # 8:anti-clockwise 454 | # 9:wave 455 | def return_gesture(self): 456 | 457 | data=self.paj7620ReadReg(0x43,1)[0] 458 | if data==self.GES_RIGHT_FLAG: 459 | time.sleep(self.GES_ENTRY_TIME) 460 | data=self.paj7620ReadReg(0x43, 1)[0] 461 | if data == self.GES_FORWARD_FLAG: 462 | return 1 463 | time.sleep(self.GES_QUIT_TIME) 464 | elif data == self.GES_BACKWARD_FLAG: 465 | return 2 466 | time.sleep(self.GES_QUIT_TIME) 467 | else: 468 | return 3 469 | 470 | elif data==self.GES_LEFT_FLAG: 471 | time.sleep(self.GES_ENTRY_TIME) 472 | data=self.paj7620ReadReg(0x43, 1)[0] 473 | if data == self.GES_FORWARD_FLAG: 474 | return 1 475 | time.sleep(self.GES_QUIT_TIME) 476 | elif data == self.GES_BACKWARD_FLAG: 477 | return 2 478 | time.sleep(self.GES_QUIT_TIME) 479 | else: 480 | return 4 481 | 482 | elif data==self.GES_UP_FLAG: 483 | time.sleep(self.GES_ENTRY_TIME) 484 | data=self.paj7620ReadReg(0x43, 1)[0] 485 | if data == self.GES_FORWARD_FLAG: 486 | return 1 487 | time.sleep(self.GES_QUIT_TIME) 488 | elif data == self.GES_BACKWARD_FLAG: 489 | return 2 490 | time.sleep(self.GES_QUIT_TIME) 491 | else: 492 | return 5 493 | 494 | elif data==self.GES_DOWN_FLAG: 495 | time.sleep(self.GES_ENTRY_TIME) 496 | data=self.paj7620ReadReg(0x43, 1)[0] 497 | if data == self.GES_FORWARD_FLAG: 498 | return 1 499 | time.sleep(self.GES_QUIT_TIME) 500 | elif data == self.GES_BACKWARD_FLAG: 501 | return 2 502 | time.sleep(self.GES_QUIT_TIME) 503 | else: 504 | return 6 505 | 506 | elif data==self.GES_FORWARD_FLAG: 507 | return 1 508 | time.sleep(self.GES_QUIT_TIME) 509 | 510 | elif data==self.GES_BACKWARD_FLAG: 511 | return 2 512 | time.sleep(self.GES_QUIT_TIME) 513 | 514 | elif data==self.GES_CLOCKWISE_FLAG: 515 | return 7 516 | 517 | elif data==self.GES_COUNT_CLOCKWISE_FLAG: 518 | return 8 519 | 520 | else: 521 | data1=self.paj7620ReadReg(0x44, 1)[0] 522 | if (data1 == self.GES_WAVE_FLAG): 523 | return 9 524 | return 0 525 | 526 | if __name__ == "__main__": 527 | g=gesture() 528 | g.init() 529 | while True: 530 | g.print_gesture() 531 | time.sleep(.1) 532 | # print g.return_gesture() 533 | # time.sleep(.1) 534 | -------------------------------------------------------------------------------- /py/grove_gesture_sensor.py.ATB: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # GrovePi Library for using the Grove - Gesture Sensor v1.0(http://www.seeedstudio.com/depot/Grove-Gesture-p-2463.html) 4 | # 5 | # The GrovePi connects the Raspberry Pi and Grove sensors. You can learn more about GrovePi here: http://www.dexterindustries.com/GrovePi 6 | # 7 | # Have a question about this library? Ask on the forums here: http://forum.dexterindustries.com/c/grovepi 8 | # 9 | # History 10 | # ------------------------------------------------ 11 | # Author Date Comments 12 | # Karan 31 Dec 15 Initial Authoring 13 | # 14 | # Code derived from the basic Arduino library for the Gesture Sensor by Seeed: https://github.com/Seeed-Studio/Gesture_PAJ7620 15 | 16 | # Released under the MIT license (http://choosealicense.com/licenses/mit/). 17 | # For more information see https://github.com/DexterInd/GrovePi/blob/master/LICENSE 18 | 19 | import time,sys 20 | #import RPi.GPIO as GPIO 21 | import ASUS.GPIO as GPIO 22 | import smbus 23 | 24 | # use the bus that matches your raspi version 25 | #rev = GPIO.RPI_REVISION 26 | rev = 2 27 | if rev == 2 or rev == 3: 28 | bus = smbus.SMBus(1) 29 | else: 30 | bus = smbus.SMBus(0) 31 | 32 | class gesture: 33 | #Registers and variables for the gesture sensor 34 | GES_REACTION_TIME =.200 # You can adjust the reaction time according to the actual circumstance. 35 | GES_ENTRY_TIME =.400 # When you want to recognize the Forward/Backward gestures, your gestures' reaction time must less than GES_ENTRY_TIME(0.8s). 36 | GES_QUIT_TIME =.800 37 | 38 | BANK0 = 0 39 | BANK1 = 1 40 | 41 | PAJ7620_ADDR_BASE =0x00 42 | 43 | #REGISTER BANK SELECT 44 | PAJ7620_REGITER_BANK_SEL =(PAJ7620_ADDR_BASE + 0xEF) #W 45 | 46 | #DEVICE ID 47 | PAJ7620_ID =0x73 48 | 49 | #REGISTER BANK 0 50 | PAJ7620_ADDR_SUSPEND_CMD =(PAJ7620_ADDR_BASE + 0x3) #W 51 | PAJ7620_ADDR_GES_PS_DET_MASK_0 =(PAJ7620_ADDR_BASE + 0x41) #RW 52 | PAJ7620_ADDR_GES_PS_DET_MASK_1 =(PAJ7620_ADDR_BASE + 0x42) #RW 53 | PAJ7620_ADDR_GES_PS_DET_FLAG_0 =(PAJ7620_ADDR_BASE + 0x43) #R 54 | PAJ7620_ADDR_GES_PS_DET_FLAG_1 =(PAJ7620_ADDR_BASE + 0x44) #R 55 | PAJ7620_ADDR_STATE_INDICATOR =(PAJ7620_ADDR_BASE + 0x45) #R 56 | PAJ7620_ADDR_PS_HIGH_THRESHOLD =(PAJ7620_ADDR_BASE + 0x69) #RW 57 | PAJ7620_ADDR_PS_LOW_THRESHOLD =(PAJ7620_ADDR_BASE + 0x6A) #RW 58 | PAJ7620_ADDR_PS_APPROACH_STATE =(PAJ7620_ADDR_BASE + 0x6B) #R 59 | PAJ7620_ADDR_PS_RAW_DATA =(PAJ7620_ADDR_BASE + 0x6C) #R 60 | 61 | #REGISTER BANK 1 62 | PAJ7620_ADDR_PS_GAIN =(PAJ7620_ADDR_BASE + 0x44) #RW 63 | PAJ7620_ADDR_IDLE_S1_STEP_0 =(PAJ7620_ADDR_BASE + 0x67) #RW 64 | PAJ7620_ADDR_IDLE_S1_STEP_1 =(PAJ7620_ADDR_BASE + 0x68) #RW 65 | PAJ7620_ADDR_IDLE_S2_STEP_0 =(PAJ7620_ADDR_BASE + 0x69) #RW 66 | PAJ7620_ADDR_IDLE_S2_STEP_1 =(PAJ7620_ADDR_BASE + 0x6A) #RW 67 | PAJ7620_ADDR_OP_TO_S1_STEP_0 =(PAJ7620_ADDR_BASE + 0x6B) #RW 68 | PAJ7620_ADDR_OP_TO_S1_STEP_1 =(PAJ7620_ADDR_BASE + 0x6C) #RW 69 | PAJ7620_ADDR_OP_TO_S2_STEP_0 =(PAJ7620_ADDR_BASE + 0x6D) #RW 70 | PAJ7620_ADDR_OP_TO_S2_STEP_1 =(PAJ7620_ADDR_BASE + 0x6E) #RW 71 | PAJ7620_ADDR_OPERATION_ENABLE =(PAJ7620_ADDR_BASE + 0x72) #RW 72 | 73 | #PAJ7620_REGITER_BANK_SEL 74 | PAJ7620_BANK0=0 75 | PAJ7620_BANK1=1 76 | 77 | #PAJ7620_ADDR_SUSPEND_CMD 78 | PAJ7620_I2C_WAKEUP =1 79 | PAJ7620_I2C_SUSPEND =0 80 | 81 | #PAJ7620_ADDR_OPERATION_ENABLE 82 | PAJ7620_ENABLE=1 83 | PAJ7620_DISABLE=0 84 | 85 | #ADC, delete 86 | REG_ADDR_RESULT = 0x00 87 | REG_ADDR_ALERT = 0x01 88 | REG_ADDR_CONFIG = 0x02 89 | REG_ADDR_LIMITL = 0x03 90 | REG_ADDR_LIMITH = 0x04 91 | REG_ADDR_HYST = 0x05 92 | REG_ADDR_CONVL = 0x06 93 | REG_ADDR_CONVH = 0x07 94 | 95 | GES_RIGHT_FLAG =1<<0 96 | GES_LEFT_FLAG =1<<1 97 | GES_UP_FLAG =1<<2 98 | GES_DOWN_FLAG =1<<3 99 | GES_FORWARD_FLAG =1<<4 100 | GES_BACKWARD_FLAG =1<<5 101 | GES_CLOCKWISE_FLAG =1<<6 102 | GES_COUNT_CLOCKWISE_FLAG =1<<7 103 | GES_WAVE_FLAG =1<<0 104 | 105 | #Gesture output 106 | FORWARD = 1 107 | BACKWARD = 2 108 | RIGHT = 3 109 | LEFT = 4 110 | UP = 5 111 | DOWN = 6 112 | CLOCKWISE = 7 113 | ANTI_CLOCKWISE = 8 114 | WAVE = 9 115 | 116 | #Initial register state 117 | initRegisterArray=( [0xEF,0x00], 118 | [0x32,0x29], 119 | [0x33,0x01], 120 | [0x34,0x00], 121 | [0x35,0x01], 122 | [0x36,0x00], 123 | [0x37,0x07], 124 | [0x38,0x17], 125 | [0x39,0x06], 126 | [0x3A,0x12], 127 | [0x3F,0x00], 128 | [0x40,0x02], 129 | [0x41,0xFF], 130 | [0x42,0x01], 131 | [0x46,0x2D], 132 | [0x47,0x0F], 133 | [0x48,0x3C], 134 | [0x49,0x00], 135 | [0x4A,0x1E], 136 | [0x4B,0x00], 137 | [0x4C,0x20], 138 | [0x4D,0x00], 139 | [0x4E,0x1A], 140 | [0x4F,0x14], 141 | [0x50,0x00], 142 | [0x51,0x10], 143 | [0x52,0x00], 144 | [0x5C,0x02], 145 | [0x5D,0x00], 146 | [0x5E,0x10], 147 | [0x5F,0x3F], 148 | [0x60,0x27], 149 | [0x61,0x28], 150 | [0x62,0x00], 151 | [0x63,0x03], 152 | [0x64,0xF7], 153 | [0x65,0x03], 154 | [0x66,0xD9], 155 | [0x67,0x03], 156 | [0x68,0x01], 157 | [0x69,0xC8], 158 | [0x6A,0x40], 159 | [0x6D,0x04], 160 | [0x6E,0x00], 161 | [0x6F,0x00], 162 | [0x70,0x80], 163 | [0x71,0x00], 164 | [0x72,0x00], 165 | [0x73,0x00], 166 | [0x74,0xF0], 167 | [0x75,0x00], 168 | [0x80,0x42], 169 | [0x81,0x44], 170 | [0x82,0x04], 171 | [0x83,0x20], 172 | [0x84,0x20], 173 | [0x85,0x00], 174 | [0x86,0x10], 175 | [0x87,0x00], 176 | [0x88,0x05], 177 | [0x89,0x18], 178 | [0x8A,0x10], 179 | [0x8B,0x01], 180 | [0x8C,0x37], 181 | [0x8D,0x00], 182 | [0x8E,0xF0], 183 | [0x8F,0x81], 184 | [0x90,0x06], 185 | [0x91,0x06], 186 | [0x92,0x1E], 187 | [0x93,0x0D], 188 | [0x94,0x0A], 189 | [0x95,0x0A], 190 | [0x96,0x0C], 191 | [0x97,0x05], 192 | [0x98,0x0A], 193 | [0x99,0x41], 194 | [0x9A,0x14], 195 | [0x9B,0x0A], 196 | [0x9C,0x3F], 197 | [0x9D,0x33], 198 | [0x9E,0xAE], 199 | [0x9F,0xF9], 200 | [0xA0,0x48], 201 | [0xA1,0x13], 202 | [0xA2,0x10], 203 | [0xA3,0x08], 204 | [0xA4,0x30], 205 | [0xA5,0x19], 206 | [0xA6,0x10], 207 | [0xA7,0x08], 208 | [0xA8,0x24], 209 | [0xA9,0x04], 210 | [0xAA,0x1E], 211 | [0xAB,0x1E], 212 | [0xCC,0x19], 213 | [0xCD,0x0B], 214 | [0xCE,0x13], 215 | [0xCF,0x64], 216 | [0xD0,0x21], 217 | [0xD1,0x0F], 218 | [0xD2,0x88], 219 | [0xE0,0x01], 220 | [0xE1,0x04], 221 | [0xE2,0x41], 222 | [0xE3,0xD6], 223 | [0xE4,0x00], 224 | [0xE5,0x0C], 225 | [0xE6,0x0A], 226 | [0xE7,0x00], 227 | [0xE8,0x00], 228 | [0xE9,0x00], 229 | [0xEE,0x07], 230 | [0xEF,0x01], 231 | [0x00,0x1E], 232 | [0x01,0x1E], 233 | [0x02,0x0F], 234 | [0x03,0x10], 235 | [0x04,0x02], 236 | [0x05,0x00], 237 | [0x06,0xB0], 238 | [0x07,0x04], 239 | [0x08,0x0D], 240 | [0x09,0x0E], 241 | [0x0A,0x9C], 242 | [0x0B,0x04], 243 | [0x0C,0x05], 244 | [0x0D,0x0F], 245 | [0x0E,0x02], 246 | [0x0F,0x12], 247 | [0x10,0x02], 248 | [0x11,0x02], 249 | [0x12,0x00], 250 | [0x13,0x01], 251 | [0x14,0x05], 252 | [0x15,0x07], 253 | [0x16,0x05], 254 | [0x17,0x07], 255 | [0x18,0x01], 256 | [0x19,0x04], 257 | [0x1A,0x05], 258 | [0x1B,0x0C], 259 | [0x1C,0x2A], 260 | [0x1D,0x01], 261 | [0x1E,0x00], 262 | [0x21,0x00], 263 | [0x22,0x00], 264 | [0x23,0x00], 265 | [0x25,0x01], 266 | [0x26,0x00], 267 | [0x27,0x39], 268 | [0x28,0x7F], 269 | [0x29,0x08], 270 | [0x30,0x03], 271 | [0x31,0x00], 272 | [0x32,0x1A], 273 | [0x33,0x1A], 274 | [0x34,0x07], 275 | [0x35,0x07], 276 | [0x36,0x01], 277 | [0x37,0xFF], 278 | [0x38,0x36], 279 | [0x39,0x07], 280 | [0x3A,0x00], 281 | [0x3E,0xFF], 282 | [0x3F,0x00], 283 | [0x40,0x77], 284 | [0x41,0x40], 285 | [0x42,0x00], 286 | [0x43,0x30], 287 | [0x44,0xA0], 288 | [0x45,0x5C], 289 | [0x46,0x00], 290 | [0x47,0x00], 291 | [0x48,0x58], 292 | [0x4A,0x1E], 293 | [0x4B,0x1E], 294 | [0x4C,0x00], 295 | [0x4D,0x00], 296 | [0x4E,0xA0], 297 | [0x4F,0x80], 298 | [0x50,0x00], 299 | [0x51,0x00], 300 | [0x52,0x00], 301 | [0x53,0x00], 302 | [0x54,0x00], 303 | [0x57,0x80], 304 | [0x59,0x10], 305 | [0x5A,0x08], 306 | [0x5B,0x94], 307 | [0x5C,0xE8], 308 | [0x5D,0x08], 309 | [0x5E,0x3D], 310 | [0x5F,0x99], 311 | [0x60,0x45], 312 | [0x61,0x40], 313 | [0x63,0x2D], 314 | [0x64,0x02], 315 | [0x65,0x96], 316 | [0x66,0x00], 317 | [0x67,0x97], 318 | [0x68,0x01], 319 | [0x69,0xCD], 320 | [0x6A,0x01], 321 | [0x6B,0xB0], 322 | [0x6C,0x04], 323 | [0x6D,0x2C], 324 | [0x6E,0x01], 325 | [0x6F,0x32], 326 | [0x71,0x00], 327 | [0x72,0x01], 328 | [0x73,0x35], 329 | [0x74,0x00], 330 | [0x75,0x33], 331 | [0x76,0x31], 332 | [0x77,0x01], 333 | [0x7C,0x84], 334 | [0x7D,0x03], 335 | [0x7E,0x01]) 336 | 337 | #Enable debug message 338 | debug=0 339 | 340 | #Initialize the sensors 341 | def init(self): 342 | time.sleep(.001) 343 | self.paj7620SelectBank(self.BANK0) 344 | self.paj7620SelectBank(self.BANK0) 345 | 346 | data0 = self.paj7620ReadReg(0, 1)[0] 347 | data1 = self.paj7620ReadReg(1, 1)[0] 348 | if self.debug: 349 | print("data0:",data0,"data1:",data1) 350 | if data0 != 0x20 :#or data1 <> 0x76: 351 | print("Error with sensor") 352 | #return 0xff 353 | if data0 == 0x20: 354 | print("wake-up finish.") 355 | 356 | for i in range(len(self.initRegisterArray)): 357 | self.paj7620WriteReg(self.initRegisterArray[i][0],self.initRegisterArray[i][1]) 358 | 359 | self.paj7620SelectBank(self.BANK0) 360 | 361 | print("Paj7620 initialize register finished.") 362 | 363 | #Write a byte to a register on the Gesture sensor 364 | def paj7620WriteReg(self,addr,cmd): 365 | bus.write_word_data(self.PAJ7620_ID, addr, cmd) 366 | 367 | #Select a register bank on the Gesture Sensor 368 | def paj7620SelectBank(self,bank): 369 | if bank==self.BANK0: 370 | self.paj7620WriteReg(self.PAJ7620_REGITER_BANK_SEL, self.PAJ7620_BANK0) 371 | 372 | #Read a block of bytes of length "qty" starting at address "addr" from the Gesture sensor 373 | def paj7620ReadReg(self,addr,qty): 374 | return bus.read_i2c_block_data(self.PAJ7620_ID, addr,qty) 375 | 376 | #Print the values from the gesture sensor 377 | def print_gesture(self): 378 | data=self.paj7620ReadReg(0x43,1)[0] 379 | if data==self.GES_RIGHT_FLAG: 380 | time.sleep(self.GES_ENTRY_TIME) 381 | data=self.paj7620ReadReg(0x43, 1)[0] 382 | if data == self.GES_FORWARD_FLAG: 383 | print("Forward") 384 | time.sleep(self.GES_QUIT_TIME) 385 | elif data == self.GES_BACKWARD_FLAG: 386 | print("Backward") 387 | time.sleep(self.GES_QUIT_TIME) 388 | else: 389 | print("Right") 390 | 391 | elif data==self.GES_LEFT_FLAG: 392 | time.sleep(self.GES_ENTRY_TIME) 393 | data=self.paj7620ReadReg(0x43, 1)[0] 394 | if data == self.GES_FORWARD_FLAG: 395 | print("Forward") 396 | time.sleep(self.GES_QUIT_TIME) 397 | elif data == self.GES_BACKWARD_FLAG: 398 | print("Backward") 399 | time.sleep(self.GES_QUIT_TIME) 400 | else: 401 | print("Left") 402 | 403 | elif data==self.GES_UP_FLAG: 404 | time.sleep(self.GES_ENTRY_TIME) 405 | data=self.paj7620ReadReg(0x43, 1)[0] 406 | if data == self.GES_FORWARD_FLAG: 407 | print("Forward") 408 | time.sleep(self.GES_QUIT_TIME) 409 | elif data == self.GES_BACKWARD_FLAG: 410 | print("Backward") 411 | time.sleep(self.GES_QUIT_TIME) 412 | else: 413 | print("Up") 414 | 415 | elif data==self.GES_DOWN_FLAG: 416 | time.sleep(self.GES_ENTRY_TIME) 417 | data=self.paj7620ReadReg(0x43, 1)[0] 418 | if data == self.GES_FORWARD_FLAG: 419 | print("Forward") 420 | time.sleep(self.GES_QUIT_TIME) 421 | elif data == self.GES_BACKWARD_FLAG: 422 | print("Backward") 423 | time.sleep(self.GES_QUIT_TIME) 424 | else: 425 | print("Down") 426 | 427 | elif data==self.GES_FORWARD_FLAG: 428 | print("Forward") 429 | time.sleep(self.GES_QUIT_TIME) 430 | 431 | elif data==self.GES_BACKWARD_FLAG: 432 | print("Backward") 433 | time.sleep(self.GES_QUIT_TIME) 434 | 435 | elif data==self.GES_CLOCKWISE_FLAG: 436 | print("Clockwise") 437 | 438 | elif data==self.GES_COUNT_CLOCKWISE_FLAG: 439 | print("anti-clockwise") 440 | 441 | else: 442 | data1=self.paj7620ReadReg(0x44, 1)[0] 443 | if (data1 == self.GES_WAVE_FLAG): 444 | print("wave") 445 | 446 | #Return a vlaue from the gestire sensor which can be used in a program 447 | # 0:nothing 448 | # 1:Forward 449 | # 2:Backward 450 | # 3:Right 451 | # 4:Left 452 | # 5:Up 453 | # 6:Down 454 | # 7:Clockwise 455 | # 8:anti-clockwise 456 | # 9:wave 457 | def return_gesture(self): 458 | 459 | data=self.paj7620ReadReg(0x43,1)[0] 460 | if data==self.GES_RIGHT_FLAG: 461 | time.sleep(self.GES_ENTRY_TIME) 462 | data=self.paj7620ReadReg(0x43, 1)[0] 463 | if data == self.GES_FORWARD_FLAG: 464 | return 1 465 | time.sleep(self.GES_QUIT_TIME) 466 | elif data == self.GES_BACKWARD_FLAG: 467 | return 2 468 | time.sleep(self.GES_QUIT_TIME) 469 | else: 470 | return 3 471 | 472 | elif data==self.GES_LEFT_FLAG: 473 | time.sleep(self.GES_ENTRY_TIME) 474 | data=self.paj7620ReadReg(0x43, 1)[0] 475 | if data == self.GES_FORWARD_FLAG: 476 | return 1 477 | time.sleep(self.GES_QUIT_TIME) 478 | elif data == self.GES_BACKWARD_FLAG: 479 | return 2 480 | time.sleep(self.GES_QUIT_TIME) 481 | else: 482 | return 4 483 | 484 | elif data==self.GES_UP_FLAG: 485 | time.sleep(self.GES_ENTRY_TIME) 486 | data=self.paj7620ReadReg(0x43, 1)[0] 487 | if data == self.GES_FORWARD_FLAG: 488 | return 1 489 | time.sleep(self.GES_QUIT_TIME) 490 | elif data == self.GES_BACKWARD_FLAG: 491 | return 2 492 | time.sleep(self.GES_QUIT_TIME) 493 | else: 494 | return 5 495 | 496 | elif data==self.GES_DOWN_FLAG: 497 | time.sleep(self.GES_ENTRY_TIME) 498 | data=self.paj7620ReadReg(0x43, 1)[0] 499 | if data == self.GES_FORWARD_FLAG: 500 | return 1 501 | time.sleep(self.GES_QUIT_TIME) 502 | elif data == self.GES_BACKWARD_FLAG: 503 | return 2 504 | time.sleep(self.GES_QUIT_TIME) 505 | else: 506 | return 6 507 | 508 | elif data==self.GES_FORWARD_FLAG: 509 | return 1 510 | time.sleep(self.GES_QUIT_TIME) 511 | 512 | elif data==self.GES_BACKWARD_FLAG: 513 | return 2 514 | time.sleep(self.GES_QUIT_TIME) 515 | 516 | elif data==self.GES_CLOCKWISE_FLAG: 517 | return 7 518 | 519 | elif data==self.GES_COUNT_CLOCKWISE_FLAG: 520 | return 8 521 | 522 | else: 523 | data1=self.paj7620ReadReg(0x44, 1)[0] 524 | if (data1 == self.GES_WAVE_FLAG): 525 | return 9 526 | return 0 527 | 528 | if __name__ == "__main__": 529 | g=gesture() 530 | g.init() 531 | while True: 532 | g.print_gesture() 533 | time.sleep(.1) 534 | # print g.return_gesture() 535 | # time.sleep(.1) 536 | --------------------------------------------------------------------------------