Game controller control

Has anyone developed a way to control the Farmbot via a USB gamepad type controller? I see this being useful to move the Farmbot to specific areas to get coordinates, manual watering, etc. I know it’s easy to control via the app, but I am one that likes tactile control with physical buttons vs the app.

I’m looking through the API documentation and I don’t know if will even be possible but I figured I’d ask if someone has gone down this rabbit hole yet.

Thanks!

I think not. Would be a fun experiment. I find a tablet and Farm Designer controls are a pretty good work around for what you’re looking for.

1 Like

I think this sounds like a good idea. I have also missed this functionality. I thought more about a PS4 Bluetooth controller to be able to walk around the bot. The raspi has BT integrated.

1 Like

@jensGeorgsen A former FarmBot employee did this once for fun (not as an official FarmBot feature). His experiment did not use Bluetooth, however. Unfortunately, FarmBot does not have resources to do this on an official basis right now (our big focus for the next 6-12 months is fixing outstanding bugs and weed detector improvements).

Here are some resources that are helpful to a third party developer wishing to build such a feature:

Thanks @RickCarlino,

I will take look at this and see what I can come up with!

BTW, I haven’t forgotten about the SSL on the server. I just have everything working fine with HTTP and VPN so it’s now lower on my priority list :slight_smile:

1 Like

I ended up getting this working very crudely with a node module called “node-gamepad” and a USB game controller I purchased on Amazon some time ago for RetroPie. It’s heavily based upon the tutorial that @RickCarlino posted below. I am definitely not a career programmer so I’m sure there are more efficient/proper ways to code this so I’m definitely open to constructive criticism.

EDIT I was not able to upload the file so I will copy and paste it below

// FarmBotJS requires a global atob() function for
// decoding tokens. In NodeJS, we must add a polyfill.
// To learn more about the issue, see:
// https://github.com/FarmBot/farmbot-js/issues/33
var GamePad = require( ‘node-gamepad’ );
var controller = new GamePad( ‘snes/retrolink’, {
vendorID: 2064,
productID: 58625
});
controller.connect();
global.atob = require(“atob”);

// Set default move amount to 1 mm
var moveAmount = 1;

// Now that we have access to atob(), we can load
// FarmbotJS, a Farmbot client.
// NodeJS uses CommonJS modules. You can learn more
// about CommonJS here:
// https://flaviocopes.com/commonjs/

// The first library we will load is FarmBotJS.
// Using FarmBotJS is a smart idea because it is
// the same wrapper library used by FarmBot, Inc.
// to control FarmBot. Using it ensures that your
// application will remain compatibile with future
// FarmBot OS versions.
//
// Learn more about it here:
// https://github.com/FarmBot/farmbot-js
const Farmbot = require(“farmbot”).Farmbot;

// We will need an HTTP client in order to create
// a FarmBot authorization token.
// Learn more about tokens here:
// https://developer.farm.bot/docs/rest-api#section-generating-an-api-token
// Learn more about Axios the HTTP client here:
// https://github.com/axios/axios
const post = require(“axios”).post;

// Now that we have loaded all the third party libraries,
// Let’s store some application config as constants.
// We need this information to create an auth token
// and also to log in to the FarmBot server so that
// we can remote control the device.
const PASSWORD =
const EMAIL =
const SERVER =

// We will also store some application state in an
// object known as “APPLICATION_STATE”.
const APPLICATION_STATE = {
// This object will be populated later,
// after we connect to the server.
// Please see the FarmBotJS documentation for
// more information about what the FarmBot object
// can do.
farmbot: undefined,
// This application is trivial- it just moves the
// Z-axis up and down. We will keep track of that
// here.
direction: “up”,
// The correct way to track FarmBot’s busy state
// is via Javascript “Promises”. Promises are beyond
// the scope of this example, so we will just use
// a simple boolean flag.
// If you don’t know what promises are, YouTube
// tutorials are a good place to start.
// Promises are tricky to learn at first, but
// will make your code very clean once you understand
// them.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
busy: false
};

// The function below will be used to extract a JSON
// Web Token (JWT) from the Farmbot server. It is
// a callback that will be used by an HTTP POST
// request by Axios later. Continue reading to learn more.
const tokenOK = (response) => {
console.log("GOT TOKEN: " + response.data.token.encoded);
return response.data.token.encoded;
};

// This is a generic error handler. We will use it
// to catch miscellaneous “promise rejections”.
// Check out egghead.io and YouTube to learn more
// about Javascript promises.
const errorHandler = (error) => {
console.log("=== ERROR ===");
console.dir(error);
};

// This function will perform an HTTP post and
// resolve a promise that contains a FarmBot auth
// token (string).
// Call this function with an email and password
const createToken = (email, password) => {
const payload = { user: { email, password } };
return post(SERVER + “/api/tokens”, payload).then(tokenOK);
};

// This function is called exactly once at the start
// of the application lifecycle.
const start = () => {
// Perform an HTTP reqeust to create a token:
return createToken(EMAIL, PASSWORD)
// Pass the token to FarmBotJS so that we
// can connect ot the server:
.then((token) => {
// Set the global FarmBot object instance for the entire app.
APPLICATION_STATE.farmbot = new Farmbot({ token: token });
// Were ready to connect!
return APPLICATION_STATE.farmbot.connect();
})
// Once we are connected to the server,
// we can display a helpful message.
.then(() => { console.log(“CONNECTED TO FARMBOT!”); })
// If anything goes wrong, throw the error to
// our error handler function.
.catch(errorHandler);
};

if (PASSWORD && EMAIL) {
// It is important to use promises here-
// The run loop won’t start until we are finished
// connecting t the server. If we don’t do this,
// the app might try to send commands before we
// are connected to the server
start()

// Set move amount to 1 mm
controller.on( 'y:press', function () {
   moveAmount = 1;
   console.log("Move amount set to 1 mm");
} );

// Set move amount to 10 mm
controller.on( 'x:press', function () {
   moveAmount = 10;
   console.log("Move amount set to 10 mm");
} );

// Set move amount to 100 mm
controller.on( 'b:press', function () {
   moveAmount = 100;
   console.log("Move amount set to 100 mm");
} );

// Set move amount to 1000 mm
controller.on( 'a:press', function () {
	moveAmount = 1000;
	console.log("Move amount set to 1000 mm");
} );

// Move FarmBot x-axis up
controller.on( 'up:press', function() {
if (APPLICATION_STATE.busy) {
	console.log("FarmBot is currently busy.");
return;
} else {
		APPLICATION_STATE.busy = true;
		APPLICATION_STATE
		.farmbot
		.moveRelative({ x: -moveAmount, y: 0, z: 0 })
		.then(() => {
		APPLICATION_STATE.direction = "up";
		APPLICATION_STATE.busy = false;
	})
		.catch(errorHandler);
	}
	
});

// Move FarmBot x-axis down	
controller.on( 'down:press', function() {
if (APPLICATION_STATE.busy) {
	console.log("FarmBot is currently busy.");
return;
} else {
	
		APPLICATION_STATE.busy = true;
		APPLICATION_STATE
		.farmbot
		.moveRelative({ x: moveAmount, y: 0, z: 0 })
		.then(() => {
		APPLICATION_STATE.direction = "down";
		APPLICATION_STATE.busy = false;
	})
		.catch(errorHandler);
	}
});

// Move FarmBot y-axis right	
controller.on( 'right:press', function() {
if (APPLICATION_STATE.busy) {
	console.log("FarmBot is currently busy.");
return;
} else {
	
		APPLICATION_STATE.busy = true;
		APPLICATION_STATE
		.farmbot
		.moveRelative({ x: 0, y: moveAmount, z: 0 })
		.then(() => {
		APPLICATION_STATE.direction = "right";
		APPLICATION_STATE.busy = false;
	})
		.catch(errorHandler);
	}
});

// Move FarmBot y-axis left	
controller.on( 'left:press', function() {
if (APPLICATION_STATE.busy) {
	console.log("FarmBot is currently busy.");
return;
} else {
	
		APPLICATION_STATE.busy = true;
		APPLICATION_STATE
		.farmbot
		.moveRelative({ x: 0, y: -moveAmount, z: 0 })
		.then(() => {
		APPLICATION_STATE.direction = "left";
		APPLICATION_STATE.busy = false;
	})
		.catch(errorHandler);
	}
});

// Move FarmBot z-axis down	
controller.on( 'r:press', function() {
if (APPLICATION_STATE.busy) {
	console.log("FarmBot is currently busy.");
return;
} else {
	
		APPLICATION_STATE.busy = true;
		APPLICATION_STATE
		.farmbot
		.moveRelative({ x: 0, y: 0, z: -moveAmount })
		.then(() => {
		APPLICATION_STATE.direction = "zdown";
		APPLICATION_STATE.busy = false;
	})
		.catch(errorHandler);
	}
});

// Move FarmBot z-axis up	
controller.on( 'r:press', function() {
if (APPLICATION_STATE.busy) {
	console.log("FarmBot is currently busy.");
return;
} else {
	
		APPLICATION_STATE.busy = true;
		APPLICATION_STATE
		.farmbot
		.moveRelative({ x: 0, y: 0, z: moveAmount })
		.then(() => {
		APPLICATION_STATE.direction = "zup";
		APPLICATION_STATE.busy = false;
	})
		.catch(errorHandler);
	}
});

} else {
// You should not see this message if your .env file is correct:
throw new Error(“You did not set FARMBOT_EMAIL or FARMBOT_PASSWORD in the .env file.”);
}

2 Likes

That’s great to hear @stre1026 I will give it a look in the coming days. :tada:

1 Like