Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: Remote Pendant (Playstation 3 Dualshock Controller / SIXAXIS Controller) #103

Closed
AustinSaintAubin opened this issue Dec 4, 2016 · 24 comments
Assignees

Comments

@AustinSaintAubin
Copy link
Contributor

AustinSaintAubin commented Dec 4, 2016

Gamepad Control

Goal:Use Use Playstation 3 Controller wirelessly over bluetooth to control CNC.
Ideally I want the control to happen on the host device (raspberry pi), not just the client.

Using a wireless game controller (like a PS3 controller) seems to be one of the lowest cost & simplest solution methods.
Integrating Node.js libraries for support looks like the best option. I also looked at

Resources Options:


Playstation Controller Setup ( general guide to connect hardware )

Here is what I have figured out so far for PS3 on Raspberry PI 3 w/ integrated bluetooth.
The bellow just shows how to get PS3 controller connected.

Install

# Install & Enable Bluetooth Tools
sudo apt-get install -y bluetooth libbluetooth3 libusb-dev
sudo systemctl enable bluetooth.service

# Add pi user to bluetooth group
sudo usermod -G bluetooth -a pi

Pairing Tools

# Get and build the command line pairing tool (sixpair)
wget http://www.pabr.org/sixlinux/sixpair.c
gcc -o sixpair sixpair.c -lusb

### Connect PS3 over USB
# Get PS3 DS 
sudo ./sixpair

### Disonnect PS3 over USB
bluetoothctl
### Connect PS3 over USB
devices
agent on
trust MAC # Replace "MAC" with MAC of "Device 64:D4:BD:B3:9E:66 PLAYSTATION(R)3 Controller"
trust 64:D4:BD:B3:9E:66 
quit
### Disonnect PS3 over USB, you should now be able to connect wirelessly. To check this, first list everything in /dev/input:

Test Controller Connectivity

# List Devices
ls /dev/input

### PS3 Controller: press the PS button, the lights on the front of the controller should flash for a couple of seconds then stop, leaving a single light on. If you now look again at the contents of /dev/input you should see a new device, probably called something like ‘js0’:

# List Devices
ls /dev/input

Get Battery Level

cat "/sys/class/power_supply/sony_controller_battery_64:d4:bd:b3:9e:66/capacity"

Joystick Application

# Install
sudo apt-get install joystick

# Usage / Test
jstest /dev/input/js0
@AustinSaintAubin AustinSaintAubin changed the title Idea: Remote pendant (Playstation 3 Dualshock Controller / SIXAXIS Controller) Idea: Remote Pendant (Playstation 3 Dualshock Controller / SIXAXIS Controller) Dec 4, 2016
@AustinSaintAubin
Copy link
Contributor Author

I got it work. Its fucking bad ass. I can control my CNC with PS3 DS3 controller over bluetooth from raspberry pi... its everything i hoped it would be. Still have some minor bugs. But I have work in the morning.
Alpha version:

Node.js DS3 Controller Setup

# Install Tools
sudo apt-get install -y libudev-dev libusb-1.0-0-dev
sudo apt-get install -y build-essential git
sudo apt-get install -y gcc-4.8 g++-4.8 && export CXX=g++-4.8


# Set access to /usr/lib/node_modules
ls -ld /usr/lib/node_modules; stat --format '%a' /usr/lib/node_modules
sudo chmod a+w /usr/lib/node_modules  # chmod 777
ls -ld /usr/lib/node_modules; stat --format '%a' /usr/lib/node_modules

# Install Node-HID
#########sudo npm install -g node-hid --unsafe-perm
-- OR --
# Compile HID
cd ~/
git clone https://github.com/node-hid/node-hid.git
cd node-hid                                        # must change into node-hid directory
git submodule update --init                        # done on publish automatically
npm install -g  # npm install                      # rebuilds the module
node-pre-gyp rebuild                               # rebuilds the C code
node-pre-gyp install --fallback-to-build

# Install Node-dualshock-controller
#########sudo npm install -g dualshock-controller--unsafe-perm
-- OR --
# Compile HID
cd ~/
git clone https://github.com/rdepena/node-dualshock-controller.git
cd node-dualshock-controller                       # must change into node-hid directory
git submodule update --init                        # done on publish automatically
npm install -g  # npm install                      # rebuilds the module

# Set access to /usr/lib/node_modules
sudo chmod g-w,o-w /usr/lib/node_modules  # chmod 755
ls -ld /usr/lib/node_modules; stat --format '%a' /usr/lib/node_modules

# Test
node ~/node-dualshock-controller/examples/consolePrintDualShock3.js

Socket.io Client

sudo npm install -g socket.io-client

The Code

Usage: node test.js

test.js MIT License

var io = require('socket.io-client');
var token = 'YOUR_API_KEY'

var socket = io.connect('ws://localhost:8000', {
  'query': 'token=' + token
});

socket.on('connect', function() {
	 console.log('Socket IO Connected!');

	 // List ports
	socket.emit('list', function(data) {
		console.log('LIST: ' + data);
	});

	console.log('============================');

	// Open port
	socket.emit('open', '/dev/ttyUSB0', { baudrate: Number(115200) });

	// Close port
	//socket.send('close', '/dev/ttyUSB0');

	// Write
	//socket.send('write', '/dev/ttyUSB0', 'G0 X0 Y0 Z0\n'); // should contain a newline character
	//socket.send('write', '/dev/ttyUSB0', '?'); // No newline for Grbl realtime commands

	// Command
	//socket.emit('command', '/dev/ttyUSB0', 'homing');
	//socket.send('command', '/dev/ttyUSB0', 'cyclestart');
	//socket.send('command', '/dev/ttyUSB0', 'reset');
});


// Redirect user to the Sign In page
socket.on('error', function() {
	// Close port
	socket.send('close', '/dev/ttyUSB0');

	 // Disconnect
	 socket.destroy();
	 console.log('ERROR');
});


// =====================================================
// https://www.npmjs.com/package/dualshock-controller

var dualShock = require('dualshock-controller');

//pass options to init the controller.
var controller = dualShock(
	 {
		  //you can use a ds4 by uncommenting this line.
		  //config: "dualshock4-generic-driver",
		  //if using ds4 comment this line.
		  config : "dualShock3",
		  //smooths the output from the acelerometers (moving averages) defaults to true
		  accelerometerSmoothing : true,
		  //smooths the output from the analog sticks (moving averages) defaults to false
		  analogStickSmoothing : true
	 });

//make sure you add an error event handler
controller.on('connected', function() {
	console.log('connected');
});

controller.on('error', function(data) {
  console.log('error');
});

// ------------------------------------------

// Safty Switch
var psx = false;

controller.on('psxButton:press', function(data) {
	psx = true;
	console.log(data + '|' + psx);
});

controller.on('psxButton:release', function(data) {
	psx = false;
	console.log(data + '|' + psx);
});


// Homing
controller.on('start:press', function(data) {
	if (psx) {
		socket.emit('command', '/dev/ttyUSB0', 'homing');
		console.log('homing:' + data);
	}
});

// Reset
controller.on('select:press', function(data) {
	if (psx) {
		socket.emit('command', '/dev/ttyUSB0', 'reset');
		console.log('reset:' + data);
	}
});


// Cyclestart
controller.on('start:press', function(data) {
	if (!psx) {
		socket.emit('command', '/dev/ttyUSB0', 'cyclestart');
		console.log('cyclestart:' + data);
	}
});

// Feedhold
controller.on('select:press', function(data) {
	if (!psx) {
		socket.emit('command', '/dev/ttyUSB0', 'feedhold');
		console.log('feedhold:' + data);
	}
});

// ------------------------------------------

// Safty Switch
var r1 = false;

controller.on('r1:press', function(data) {
	r1 = true;
	console.log(data + '|' + r1);
});

controller.on('r1:release', function(data) {
	r1 = false;
	console.log(data + '|' + r1);
});

// Start
controller.on('triangle:press', function(data) {
	if (r1) {
		socket.emit('command', '/dev/ttyUSB0', 'start');
		console.log('start:' + data);
	}
});

// Stop
controller.on('square:press', function(data) {
	if (r1) {
		socket.emit('command', '/dev/ttyUSB0', 'stop');
		console.log('stop:' + data);
	}
});

// Pause
controller.on('circle:press', function(data) {
	if (r1) {
		socket.emit('command', '/dev/ttyUSB0', 'pause');
		console.log('pause:' + data);
	}
});

// Resume
controller.on('x:press', function(data) {
	if (r1) {
		socket.emit('command', '/dev/ttyUSB0', 'resume');
		console.log('resume:' + data);
	}
});

// ------------------------------------------

// Cyclestart
controller.on('triangle:press', function(data) {
	if (!r1 && !l1 && !psx) {
		socket.emit('command', '/dev/ttyUSB0', 'cyclestart');
		console.log('cyclestart:' + data);
	}
});

// Unlock
controller.on('square:press', function(data) {
	if (!r1 && !l1 && !psx) {
		socket.emit('command', '/dev/ttyUSB0', 'feedhold');
		console.log('feedhold:' + data);
	}
});


// Feed Hold
controller.on('circle:press', function(data) {
	if (!r1 && !l1 && !psx) {
		socket.emit('command', '/dev/ttyUSB0', 'feedhold');
		console.log('feedhold:' + data);
	}
});

// Unlock
controller.on('x:press', function(data) {
	if (!r1 && !l1 && !psx) {
		socket.emit('command', '/dev/ttyUSB0', 'unlock');
		console.log('unlock:' + data);
	}
});


// ------------------------------------------

// Probe
controller.on('x:press', function(data) {
	if (psx) {
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G91');
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G38.2 Z-15.001 F120');
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G90');
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G10 L20 P1 Z15.001');
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G91');
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G0 Z3');
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G90');

		console.log('probe:' + data);
	}
});

// ------------------------------------------

// [D Pad]

// Moddifter Switch
var l1 = false;

controller.on('l1:press', function(data) {
	l1 = true;
	console.log(data + '|' + l1);
});

controller.on('l1:release', function(data) {
	l1 = false;
	console.log(data + '|' + l1);
});

// Y Up
controller.on('dpadUp:hold', function(data) {
	if (!l1) {
		// Slow
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G91 G0 Y1');
	}
	else {
		// Fast
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G91 G0 Y10');
	}

	console.log('dpadUp:' + l1);
});

// Y Down
controller.on('dpadDown:hold', function(data) {
	if (!l1) {
		// Slow
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G91 G0 Y-1');
	}
	else {
		// Fast
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G91 G0 Y-10');
	}

	console.log('dpadDown:' + l1);
});

// X Right
controller.on('dpadRight:hold', function(data) {
	if (!l1) {
		// Slow
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G91 G0 X1');
	}
	else {
		// Fast
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G91 G0 X10');
	}

	console.log('dpadRight:' + l1);
});

// X Left
controller.on('dpadLeft:hold', function(data) {
	if (!l1) {
		// Slow
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G91 G0 X-1');
	}
	else {
		// Fast
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'G91 G0 X-10');
	}

	console.log('dpadLeft:' + l1);
});

// ------------------------------------------

// Spendle ON State
var spindle = false;

// R2
var r2 = false;
controller.on('r2:press', function(data) {
	r2 = true;
	console.log(data + '|' + r2);
});
controller.on('r2:release', function(data) {
	r2 = false;
	console.log(data + '|' + r2);
});

// L2
var l2 = false;
controller.on('l2:press', function(data) {
	l2 = true;
	console.log(data + '|' + l2);
});
controller.on('l2:release', function(data) {
	l2 = false;
	console.log(data + '|' + l2);
});


// Start Spindle
controller.on('r2:press', function(data) {
	if (r2 && l2 && psx) {
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'M3 S1000');
		spindle = true;
		console.log('Spindle: ' + spindle);
	}
});
controller.on('l2:press', function(data) {
	if (r2 && l2 && psx) {
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'M3 S1000');
		spindle = true;
		console.log('Spindle: ' + spindle);
	}
});

// Stop Spendle
controller.on('r2:release', function(data) {
	if (!psx && spindle) {
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'M5');
		spindle = false;
		console.log('Spindle: ' + spindle);
	}
});
controller.on('l2:release', function(data) {
	if (!psx && spindle) {
		socket.emit('command', '/dev/ttyUSB0', 'gcode', 'M5');
		spindle = false;
		console.log('Spindle: ' + spindle);
	}
});

// ------------------------------------------

//sixasis motion events:
//the object returned from each of the movement events is as follows:
//{
//	 direction : values can be: 1 for right, forward and up. 2 for left, backwards and down.
//	 value : values will be from 0 to 120 for directions right, forward and up and from 0 to -120 for left, backwards and down.
//}

//right-left movement
controller.on('rightLeft:motion', function (data) {
	 //...doStuff();
});

//forward-back movement
controller.on('forwardBackward:motion', function (data) {
	 //...doStuff();
});
//up-down movement
controller.on('upDown:motion', function (data) {
	 //...doStuff();
});

//controller status
//as of version 0.6.2 you can get the battery %, if the controller is connected and if the controller is charging
controller.on('battery:change', function (value) {
	console.log('battery:change:' + value);
});
controller.on('connection:change', function (value) {
	console.log('connection:change:' + value);
});
controller.on('charging:change', function (value) {
	console.log('connection:change:' + value);
});


//connect the controller
controller.connect();

That was a lot to get done in a day...

[ ] Still need a way to detect when controller connects, then load this script.. also need to close script on connectivity loss with controller... then it should load on connect / reconnect.

@cheton
Copy link
Collaborator

cheton commented Dec 9, 2016

Hi @AustinSaintAubin,

This is pretty cool. Would you like to continue your work in the examples directory or somewhere else? Just let me know if you'd like to become a collaborator.

@AustinSaintAubin
Copy link
Contributor Author

I would love to become a collaborator!
I think after I get this flushed out it would like a nice addition to the CNC tools set.

@cheton
Copy link
Collaborator

cheton commented Dec 10, 2016

Thank you. I just sent you an invitation.

You can push your code changes to the dev branch or create a new branch for development, then merge it to the master branch once you completed it.

@AustinSaintAubin
Copy link
Contributor Author

AustinSaintAubin commented Dec 10, 2016

Will do once have a working beta.

I need some help:

  • Getting Token
    • Having issues getting the token from another node.js application on same device.
    • Right now I have to grad it from another session... How do I handshake and then get the token?

Would also be nice for uses using authentication to be able to specify a username & password if authentication is enable.

Current test code with static token:

var io = require('socket.io-client');
var token = 'YOUR_TOKEN'

var socket = io.connect('ws://localhost:8000', {
  'query': 'token=' + token
});

socket.on('connect', function() {
	 console.log('Socket IO Connected!');

	 // List ports
	socket.emit('list', function(data) {
		console.log('LIST: ' + data);
	});

	// Open port
	socket.emit('open', '/dev/ttyUSB0', { baudrate: Number(115200) });
});

@cheton
Copy link
Collaborator

cheton commented Dec 10, 2016

You can try below example to generate an access token using a secret stored in ~/.cncrc. The access token lifetime seems to be a required field for jsonwebtoken, it can be expressed in seconds (e.g. 60) or a time span string like '7 days', '3d', '1h'.

var fs = require('fs');
var path = require('path');
var jwt = require('jsonwebtoken');
var io = require('socket.io-client');

// https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback
var generateAccessToken = function(payload, secret) {
    var token = jwt.sign(payload, secret, {
        expiresIn: '30d' // 30 days
    });

    return token;
};

var getUserHome = function () {
    return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
};
var cncrc = path.resolve(getUserHome(), '.cncrc');
var config = JSON.parse(fs.readFileSync(cncrc, 'utf8'));
var token = generateAccessToken({ id: '', user: '' }, config.secret);
var socket = io.connect('ws://localhost:8000', {
    'query': 'token=' + token
});

socket.on('connect', function() {
    console.log('Socket IO Connected!');

    // List ports
    socket.emit('list', function(data) {
        console.log('LIST: ' + data);
    });

    // Open port
    socket.emit('open', '/dev/ttyUSB0', { baudrate: Number(115200) });
});

socket.on('error', function() {
    console.log('error');
});

@AustinSaintAubin
Copy link
Contributor Author

Looks like your code is working well. Thanks for the help.

@AustinSaintAubin
Copy link
Contributor Author

AustinSaintAubin commented Dec 12, 2016

BETA v0.1 [2016/12/11]

NOTE: Use pm2 to run, so it can auto restart the script

pm2 start cnc-pendant_ps3.js
pm2 save // save current processes for startup

Todo

  • Code Cleanup
  • Better method for auto detect controller conected, currently designed for linux only.
  • Remove debugging code
  • Make a visual button map / diagram.

cnc-pendant_ps3.js

// Node.js Playstation 3 / DS3 Controller for CNC.js
// by Austin St. Aubin <austinsaintaubin@gmail.com>
// v0.7 BETA [2016/12/14]
// https://github.com/cheton/cnc/issues/103

// [Dependacies]
var io = require('socket.io-client');  // Socket.io connection to CNC
var jwt = require('jsonwebtoken');
var fs = require('fs');
var path = require('path');
var dualShock = require('dualshock-controller');  // ttps://www.npmjs.com/package/dualshock-controller


// [Varables]
var controller_serial_port = '/dev/ttyUSB0'
var controller_serial_baud = 115200

var socket_address = 'localhost'
	socket_port = '8000'
	socket_token_expiration = '30d' // The access token lifetime is a required field for jsonwebtoken, it can be expressed in seconds (e.g. 60) or a time span string like '7 days', '3d', '1h'.

var socket, controller;

// =====================================================
// Check/Wait for Controller to Connect
setInterval(checkController, 3*1000);
var controler_started = false;

// [Function] check for controller to conect (show up in devices), then start services. Kill services on disconect.
function checkController(socket, controller) {
	//console.log('Checkign Controller Status');

	// Check if Controller Exist
	fs.stat('/dev/input/js0', function(err, stat) {
		if(err == null) {
			// Controller Conected
			//console.log('js0 exists');

			if (!controler_started) {
				console.log('Controller Conected: /dev/input/js0');
				console.log('Starting Services');

				// Start Socket Connection & Controller Conection
				run();

				// Set Startup Varable
				controler_started = true;
			}

		} else if(err.code == 'ENOENT') {
			// Controller Disconected / Not Conected
			//console.log('js0 NOT exists');

			if (controler_started) {
				console.log('Stopping Services');

				// Kill Socket Connection
				//socket.destroy();

				// Kill  Controller Conection
				//controller.destroy();

				// !!!!!!!!!!!!!!! Exit Script (to be relauched by pm2)
				console.log('Exiting Script');
				process.exit();

				// Set Startup Varable
				controler_started = false;
			}
		} else {
			// Error Message
			console.log('An unexpected error has occurred: ', err.code);
		}
	});
}


// RUN EVERYTHING
function run(socket, controller)
{
	// =====================================================
	// [Socket.io Connection]

	// [Function] Generate Access Token
	// https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback
	var generateAccessToken = function(payload, secret, expiration) {
	    var token = jwt.sign(payload, secret, {
	        expiresIn: expiration //'30d' // 30 days
	    });

	    return token;
	};

	// Get Secret & Generate Token
	var getUserHome = function () { return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']; };
	var cncrc = path.resolve(getUserHome(), '.cncrc');
	var config = JSON.parse(fs.readFileSync(cncrc, 'utf8'));
	var token = generateAccessToken({ id: '', name: 'pendent' }, config.secret, '30d');

	/*
	// Debugging Output
	console.log('config.secret: ' + config.secret);
	console.log('config.secret: ' + token);
	*/

	// Connect to Socket.io on CNC
	//var socket = io.connect('ws://' + socket_address + ':' + socket_port, {
	socket = io.connect('ws://' + socket_address + ':' + socket_port, {
	  'query': 'token=' + token
	});

	// Detect Connect
	socket.on('connect', function() {
		console.log('Socket IO [Connected]: ' + socket_address + ':' + socket_port);


		// List Serial Ports
	    socket.emit('list');
	    socket.on ('serialport:list', function (data) {
		    console.log('-------------------------');
		    console.log('Listed Serial Ports: ');

			// List Ports
			for (var i = 0; i < data.length; i++) {
				console.log(i + "] Seral Port List: " + data[i].port);
			}

			console.log('-------------------------');
		});

		// Open port
		socket.emit('open', controller_serial_port, { baudrate: Number(controller_serial_baud) });
		console.log('Opened Serial Port: ' + controller_serial_port + ':' + controller_serial_baud);

		// Close port
		//socket.send('close', controller_serial_port);

		// Write
		//socket.send('write', controller_serial_port, 'G0 X0 Y0 Z0\n'); // should contain a newline character
		//socket.send('write', controller_serial_port, '?'); // No newline for Grbl realtime commands

		// Command
		//socket.emit('command', controller_serial_port, 'homing');
		//socket.send('command', controller_serial_port, 'cyclestart');
		//socket.send('command', controller_serial_port, 'reset');
	});

	// Detect Disconnect
	socket.on('disconnect', function() {
		 console.log('Socket IO [Disconnected]: ' + socket_address + ':' + socket_port);
	});

	// Redirect user to the Sign In page
	socket.on('error', function() {
		// Close port
		console.log('Closed Serial Port: ' + controller_serial_port + ':' + controller_serial_baud);
		socket.send('close', controller_serial_port);


		 // Disconnect
		 socket.destroy();
		 console.log('Socket IO ERROR, token is likly invalid');
	});


	// =====================================================
	// Play Station 3 Controller / Game Pad
	// https://www.npmjs.com/package/dualshock-controller
	//var dualShock = require('dualshock-controller');

	//pass options to init the controller.
	//var controller = dualShock(
	controller = dualShock(
		 {
			  //you can use a ds4 by uncommenting this line.
			  //config: "dualshock4-generic-driver",
			  //if using ds4 comment this line.
			  config : "dualShock3",
			  //smooths the output from the acelerometers (moving averages) defaults to true
			  accelerometerSmoothing : true,
			  //smooths the output from the analog sticks (moving averages) defaults to false
			  analogStickSmoothing : false // DO NOT ENABLE, does not retun sticks to center when enabled. 128 x 128
		 });

	//make sure you add an error event handler
	controller.on('connection:change', data => console.log("Conection" + data));

	controller.on('connected', function(state) {
		console.log('connected: ' + state);
	});

	controller.on('error', function(data) {
		controller.destroy();
		console.log('error: ' + data);
	});

	// ------------------------------------------

	// Safety Switches & Modifyers

	// psx
	var psx = false;
	controller.on('psxButton:press', function(data) {
		psx = true;
		//console.log(data + '|' + psx);
	});
	controller.on('psxButton:release', function(data) {
		psx = false;
		//console.log(data + '|' + psx);
	});

	// L1
	var l1 = false;
	controller.on('l1:press', function(data) {
		l1 = true;
		//console.log(data + '|' + l1);
	});
	controller.on('l1:release', function(data) {
		l1 = false;
		//console.log(data + '|' + l1);
	});

	// R1
	var r1 = false;
	controller.on('r1:press', function(data) {
		r1 = true;
		//console.log(data + '|' + r1);
	});
	controller.on('r1:release', function(data) {
		r1 = false;
		//console.log(data + '|' + r1);
	});

	// L2
	var l2 = false;
	controller.on('l2:press', function(data) {
		l2 = true;
		//console.log(data + '|' + l2);
	});
	controller.on('l2:release', function(data) {
		l2 = false;
		//console.log(data + '|' + l2);
	});

	// R2
	var r2 = false;
	controller.on('r2:press', function(data) {
		r2 = true;
		//console.log(data + '|' + r2);
	});
	controller.on('r2:release', function(data) {
		r2 = false;
		//console.log(data + '|' + r2);
	});

	// ------------------------------------------

	// Homing
	controller.on('start:press', function(data) {
		if (psx) {
			socket.emit('command', controller_serial_port, 'homing');
			//console.log('homing:' + data);
		}
	});

	// Reset
	controller.on('select:press', function(data) {
		if (psx) {
			socket.emit('command', controller_serial_port, 'reset');
			//console.log('reset:' + data);
		}
	});


	// Cyclestart
	controller.on('start:press', function(data) {
		if (!psx) {
			socket.emit('command', controller_serial_port, 'cyclestart');
			//console.log('cyclestart:' + data);
		}
	});

	// Feedhold
	controller.on('select:press', function(data) {
		if (!psx) {
			socket.emit('command', controller_serial_port, 'feedhold');
			//console.log('feedhold:' + data);
		}
	});

	// ------------------------------------------

	// Raise Z
	controller.on('triangle:hold', function(data) {
		if (l1) {
			socket.emit('command', controller_serial_port, 'gcode', 'G91 G0 Z0.25'); // Switch to relative coordinates, Move one unit right in X and one unit right in Y
			socket.emit('command', controller_serial_port, 'gcode', 'G90');  // Switch back to absolute coordinates

			//console.log('Raising Z:' + data);
		}
	});

	// Probe
	controller.on('square:press', function(data) {
		if (l1) {
			socket.emit('command', controller_serial_port, 'gcode', 'G91');
			socket.emit('command', controller_serial_port, 'gcode', 'G38.2 Z-15.001 F120');
			socket.emit('command', controller_serial_port, 'gcode', 'G90');
			socket.emit('command', controller_serial_port, 'gcode', 'G10 L20 P1 Z15.001');
			socket.emit('command', controller_serial_port, 'gcode', 'G91');
			socket.emit('command', controller_serial_port, 'gcode', 'G0 Z3');
			socket.emit('command', controller_serial_port, 'gcode', 'G90');

			//console.log('probe:' + data);
		}
	});

	// Probe
	controller.on('circle:press', function(data) {
		if (l1) {
			socket.emit('command', controller_serial_port, 'gcode', 'G91');
			socket.emit('command', controller_serial_port, 'gcode', 'G38.2 Z-15.001 F120');
			socket.emit('command', controller_serial_port, 'gcode', 'G90');
			socket.emit('command', controller_serial_port, 'gcode', 'G10 L20 P1 Z15.001');
			socket.emit('command', controller_serial_port, 'gcode', 'G91');
			socket.emit('command', controller_serial_port, 'gcode', 'G0 Z3');
			socket.emit('command', controller_serial_port, 'gcode', 'G90');

			//console.log('probe:' + data);
		}
	});

	// Lower Z
	controller.on('x:hold', function(data) {
		if (l1) {
			socket.emit('command', controller_serial_port, 'gcode', 'G91 G0 Z-0.25'); // Switch to relative coordinates, Move one unit right in X and one unit right in Y
			socket.emit('command', controller_serial_port, 'gcode', 'G90');  // Switch back to absolute coordinates

			//console.log('Lowering Z:' + data);
		}
	});

	// ------------------------------------------

	// Start
	controller.on('triangle:press', function(data) {
		if (r1) {
			socket.emit('command', controller_serial_port, 'start');
			//console.log('start:' + data);
		}
	});

	// Stop
	controller.on('square:press', function(data) {
		if (r1) {
			socket.emit('command', controller_serial_port, 'stop');
			//console.log('stop:' + data);
		}
	});

	// Pause
	controller.on('circle:press', function(data) {
		if (r1) {
			socket.emit('command', controller_serial_port, 'pause');
			//console.log('pause:' + data);
		}
	});

	// Resume
	controller.on('x:press', function(data) {
		if (r1) {
			socket.emit('command', controller_serial_port, 'resume');
			//console.log('resume:' + data);
		}
	});

	// ------------------------------------------

	// Cyclestart
	controller.on('triangle:press', function(data) {
		if (!r1 && !l1 && !psx) {
			socket.emit('command', controller_serial_port, 'cyclestart');
			//console.log('cyclestart:' + data);
		}
	});

	// Feedhold
	controller.on('square:press', function(data) {
		if (!r1 && !l1 && !psx) {
			socket.emit('command', controller_serial_port, 'feedhold');
			//console.log('feedhold:' + data);
		}
	});


	// Pause
	controller.on('circle:press', function(data) {
		if (!r1 && !l1 && !psx) {
			socket.emit('command', controller_serial_port, 'pause');
			//console.log('pause:' + data);
		}
	});

	// Unlock
	controller.on('x:press', function(data) {
		if (!r1 && !l1 && !psx) {
			socket.emit('command', controller_serial_port, 'unlock');
			//console.log('unlock:' + data);
		}
	});


	// ------------------------------------------

/*
	// Raise Z
	controller.on('triangle:press', function(data) {
		if (psx) {
			socket.emit('command', controller_serial_port, 'gcode', 'G91 G0 Z0.1'); // Switch to relative coordinates, Move one unit right in X and one unit right in Y
			socket.emit('command', controller_serial_port, 'gcode', 'G90');  // Switch back to absolute coordinates

			console.log('Raising Z:' + data);
		}
	});

	//
	controller.on('square:press', function(data) {
		if (psx) {

		}
	});


	// Probe
	controller.on('circle:press', function(data) {
		if (psx) {
			socket.emit('command', controller_serial_port, 'gcode', 'G91');
			socket.emit('command', controller_serial_port, 'gcode', 'G38.2 Z-15.001 F120');
			socket.emit('command', controller_serial_port, 'gcode', 'G90');
			socket.emit('command', controller_serial_port, 'gcode', 'G10 L20 P1 Z15.001');
			socket.emit('command', controller_serial_port, 'gcode', 'G91');
			socket.emit('command', controller_serial_port, 'gcode', 'G0 Z3');
			socket.emit('command', controller_serial_port, 'gcode', 'G90');

			console.log('probe:' + data);
		}
	});

	// Lower Z
	controller.on('x:hold', function(data) {
		if (psx) {
			socket.emit('command', controller_serial_port, 'gcode', 'G91 G0 Z-0.1'); // Switch to relative coordinates, Move one unit right in X and one unit right in Y
			socket.emit('command', controller_serial_port, 'gcode', 'G90');  // Switch back to absolute coordinates

			console.log('Lowering Z:' + data);
		}
	});
*/

	// ------------------------------------------

	// ==[ D Pad ]==

	// Move Gantry X | Y
	function moveAxis(axis, direction, speed) {
		// Set Direction
		if (direction) {
			direction = '';
		} else {
			direction = '-';
		}

		// Set Spped
		switch(speed) {
		    case 1:
		        speed = 0.05;
		        break;
		    case 3:
		        speed = 3;
		        break;
		    default:
		        speed = 0.5;
		}

		// Send gCode
		socket.emit('command', controller_serial_port, 'gcode', 'G91 G0 ' + axis + direction + speed);
		socket.emit('command', controller_serial_port, 'gcode', 'G90');  // Switch back to absolute coordinates

		// Debuging
		//console.log(axis + ': ' + direction + ' | ' + speed);
	}

	// Move Gantry Based on DPad
	function dpad(axis, direction, name) {
		if (r1) {
			// Fast
			moveAxis(axis, direction, 3);
		} else if (l1) {
			// Slow
			moveAxis(axis, direction, 1);
		} else {
			// Normal
			moveAxis(axis, direction, 2);
		}


		// Debugging
		//console.log(name + ': ' + direction + ' | ' + axis + ' | ' +  + l1 + r1);
	}

	// - - - - - - - - - - - - - - - - - - - -

	// Y Up
	controller.on('dpadUp:press', function(data) {
		dpad('Y', true, data)
	});
	controller.on('dpadUp:hold', function(data) {
		dpad('Y', true, data)
	});

	// Y Down
	controller.on('dpadDown:press', function(data) {
		dpad('Y', false, data)
	});
	controller.on('dpadDown:hold', function(data) {
		dpad('Y', false, data)
	});

	// X Right
	controller.on('dpadRight:press', function(data) {
		dpad('X', true, data)
	});
	controller.on('dpadRight:hold', function(data) {
		dpad('X', true, data)
	});

	// X Left
	controller.on('dpadLeft:press', function(data) {
		dpad('X', false, data)
	});
	controller.on('dpadLeft:hold', function(data) {
		dpad('X', false, data)
	});

	// ------------------------------------------

	// Spendle ON State
	var spindle = false;

	// Start Spindle
	controller.on('r2:press', function(data) {
		if (r2 && l2 && psx) {
			socket.emit('command', controller_serial_port, 'gcode', 'M3 S1000');
			spindle = true;
			//console.log('Spindle: ' + spindle);
		}
	});
	controller.on('l2:press', function(data) {
		if (r2 && l2 && psx) {
			socket.emit('command', controller_serial_port, 'gcode', 'M3 S1000');
			spindle = true;
			//console.log('Spindle: ' + spindle);
		}
	});

	// Stop Spendle
	controller.on('r2:release', function(data) {
		if (!psx && spindle) {
			socket.emit('command', controller_serial_port, 'gcode', 'M5');
			spindle = false;
			//console.log('Spindle: ' + spindle);
		}
	});
	controller.on('l2:release', function(data) {
		if (!psx && spindle) {
			socket.emit('command', controller_serial_port, 'gcode', 'M5');
			spindle = false;
			//console.log('Spindle: ' + spindle);
		}
	});

	// ------------------------------------------

	// Analog Sticks
	var stick_sensitivity = 1; // Do not set bellow 1

	var left_x = 0;
		left_y = 0;
	var right_x = 0;
		right_y = 0;

	// Safty
	var stick_left = false;
		stick_right = false;

	// Safty = Stick Button
	controller.on('leftAnalogBump:press', function(data) {
		stick_left = true;
		//console.log(data + '|' + stick_left);
	});
	controller.on('leftAnalogBump:release', function(data) {
		stick_left = false;
		//console.log(data + '|' + stick_left);
	});
	controller.on('rightAnalogBump:press', function(data) {
		stick_right = true;
		//console.log(data + '|' + stick_right);
	});
	controller.on('rightAnalogBump:release', function(data) {
		stick_right = false;
		//console.log(data + '|' + stick_right);
	});

	// - - - - - - - - - - - - - - - - - - - -

	// Analog Sticks
	controller.on('left:move', function(data) {
		//console.log('left Moved: ' + data.x + ' | ' + Number((data.y * -1) +255));
		if (stick_left) {
			left_x = data.x - 128
			left_y = (data.y * -1) +128
		} else {
			left_x = 0;
			left_y = 0;
		}

		//console.log('stick-left: ' +  Number(data.x - 128) + ' [' + right_x + '] | ' +  Number(data.y - 128) + ' [' + right_y + '] | ' + stick_left)
	});
	controller.on('right:move', function(data) {
		//console.log('right Moved: ' + data.x + ' | ' + Number((data.y * -1) +255));
		if (stick_right) {

			right_x = data.x - 128
			right_y = (data.y * -1) +128
		} else {
			right_x = 0;
			right_y = 0;
		}

		//console.log('stick-right: ' + Number(data.x - 128) + ' [' + right_x + '] | ' +  Number(data.y - 128) + ' [' + right_y + '] | ' + stick_right)
	});


	// Move Gantry bassed on Sticks at a regualr interval
	setInterval(stickMovment, 100);

	// [Function] map(value, fromLow, fromHigh, toLow, toHigh)   https://www.arduino.cc/en/Reference/Map
	function map(x, in_min, in_max, out_min, out_max)
	{
	  return Number((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min);
	}

	// Move X & Y base on X & Y Stick Movments
	function stickMovment() {
		var sum_x = Number(left_x + right_x);
		var sum_y = Number(left_y + right_y);

		if (left_x >= stick_sensitivity | left_x <= -stick_sensitivity || left_y >= stick_sensitivity || left_y <= -stick_sensitivity || right_x >= stick_sensitivity || right_x <= -stick_sensitivity || right_y >= stick_sensitivity || right_y <= -stick_sensitivity) {
			// Additional Safty Catch
			if (!stick_left) {
					left_x = 0; left_y = 0;
			}
			if (!stick_right) {
					right_x = 0; right_y = 0;
			}

			//!!!!!!!!!!!!!!!!! need to detect if it's in inches or millimetersmm to avoid and overrun in the multiplier this can be done with agreeable status I believe.
			socket.emit('command', controller_serial_port, 'gcode', 'G21');  // set to millimeters

			// Move based on stick imput and mapping, need to add exponital curve.
			socket.emit('command', controller_serial_port, 'gcode', 'G91 G0 X' + map(sum_x, 0, 128, 0.0001, 2).toFixed(4) + ' Y' + map(sum_y, 0, 128, 0.0001, 2).toFixed(4)); // Switch to relative coordinates, Move one unit right in X and one unit right in Y
			socket.emit('command', controller_serial_port, 'gcode', 'G90');  // Switch back to absolute coordinates
			//console.log('setInterval: x' + sum_x + ' y' + sum_y + ' | ' + 'G91 G0 X' + map(sum_x, 0, 128, 0.0001, 2).toFixed(4) + ' Y' + map(sum_y, 0, 128, 0.0001, 2).toFixed(4));
		}
	}


	// ------------------------------------------

	//sixasis motion events:
	//the object returned from each of the movement events is as follows:
	//{
	//	 direction : values can be: 1 for right, forward and up. 2 for left, backwards and down.
	//	 value : values will be from 0 to 120 for directions right, forward and up and from 0 to -120 for left, backwards and down.
	//}

	//right-left movement
	controller.on('rightLeft:motion', function (data) {
		 //...doStuff();
	});

	//forward-back movement
	controller.on('forwardBackward:motion', function (data) {
		 //...doStuff();
	});
	//up-down movement
	controller.on('upDown:motion', function (data) {
		 //...doStuff();
	});

	//controller status
	//as of version 0.6.2 you can get the battery %, if the controller is connected and if the controller is charging
	controller.on('battery:change', function (value) {
		console.log('battery:change:' + value);
	});
	controller.on('connection:change', function (value) {
		console.log('connection:change:' + value);
	});
	controller.on('charging:change', function (value) {
		console.log('connection:change:' + value);
	});
}

@AustinSaintAubin
Copy link
Contributor Author

AustinSaintAubin commented Dec 15, 2016

Updated above script... still have a few small issues to resolve.
But the above beta script is very good for right now.
Hope to build example in dev soon... just wanted to get script working first.

@AustinSaintAubin
Copy link
Contributor Author

PS3 CNC Control Button Map Work in Progress.

@AustinSaintAubin
Copy link
Contributor Author

FYI, new baby today. Going to be busy for the next week.
Will pick this backup once things settle down.

@cheton
Copy link
Collaborator

cheton commented Dec 22, 2016

@AustinSaintAubin Congratulations! 👪

@cheton
Copy link
Collaborator

cheton commented Jan 9, 2017

Hi @AustinSaintAubin,

Would you like to add your file "cnc-pendant-ps3.js" as a main entry in the package.json file? For example:

"bin": {
  "cnc": "./bin/cnc",
  "cnc-pendant-ps3": "./bin/cnc-pendant-ps3"
},

You can add the file to bin, I will publish a new version once it's ready!

@AustinSaintAubin
Copy link
Contributor Author

That would be good, but I cant get past "GH006: Protected branch update failed for refs/heads/master.
Required status check "continuous-integration/travis-ci" is expected. At least one approved review is required."

AustinSaintAubin added a commit that referenced this issue Jan 11, 2017
Relates to [Idea: Remote Pendant (Playstation 3 Dualshock Controller /
SIXAXIS Controller) #103](#103)
@cheton
Copy link
Collaborator

cheton commented Jan 11, 2017

Let me check it.

@AustinSaintAubin
Copy link
Contributor Author

I think it might be best to branch plugins off into their own repository... but I am still thinking on that... I still need to work on a few things in regard to this script before it is ready for prime time... hope to work on it more this weekend. For now I have commited changes to the pendant-ps3 branch.

cheton pushed a commit that referenced this issue Jan 11, 2017
Relates to [Idea: Remote Pendant (Playstation 3 Dualshock Controller /
SIXAXIS Controller) #103](#103)
@cheton
Copy link
Collaborator

cheton commented Jan 11, 2017

Sure! The branch is back, you can push your changes to the pendant-ps3 branch. Thanks.

@AustinSaintAubin
Copy link
Contributor Author

Still working on this...

I have some food for thought and need input.
I have two options:

  1. Make a separate node js & npm named cncjs-pendant-ps3 that users can add like any other node.js application. Allows pendants to run anywhere and connect to cncjs.
  2. Work to integrate HID & pendant functionality into cnc js. Allows for configuration in cncjs settings and maintenance that occurs within the cncjs project.

What are your thoughts?

@cheton
Copy link
Collaborator

cheton commented Jan 25, 2017

I vote the first one. A separate repository will be convenient for source management and version control.

@cheton
Copy link
Collaborator

cheton commented Jan 26, 2017

I created an organization for managing multiple projects: https://github.com/cncjs. An invitation has already been sent to your mailbox. I will rename and move this project to the organization after 1.9 release, you can create a new repository for cncjs-pendant-ps3 and push your changes to the repository. 😃

@AustinSaintAubin
Copy link
Contributor Author

Going to create cncjs-pendant-ps3 this Friday or Saturday. Looks like the cnc -> cncjs moveover went well. Still have some documentation to update. Will try to help as time permits.

@f0m3
Copy link

f0m3 commented Jan 31, 2017

This feature sounds very cool. Will it be possible to use it on a mac?

@AustinSaintAubin
Copy link
Contributor Author

I have designed it to run on the raspberry pi, but it should be able to run on Mac OS w/ NodeJS and all dependencies installed.

I run CNCjs and my pendent on a Raspberry Pi 3. This is ideal for me because end user OS's sleep, auto update, etc. I use my MacBook to connect to the CNCjs web interface running on the Raspberry Pi. Basically everything runs on the Raspberry Pi. The end user devices (MacBook, Chromebook) are then free to do other things. Additionally, because I run the pendent software on the Raspberry Pi, the PS3 controller connects to the Raspberry Pi in my shop. Allowing my to only have to take my PS3 into my dirty areas, no need to bring my MacBook into the shops dust.

@AustinSaintAubin
Copy link
Contributor Author

Closing. Any new issues / discussions should be posted to cncjs/cncjs-pendant-ps3 or cncjs/cncjs-pendant-boilerplate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants