“What you learn today can change the future”
Nowadays more and more devices can be controlled by voice using services like Google Home and Amazon Alexa. I’ve decided to bring myself up to speed by taking Alexa course at codecademy and adding voice control to my 2006 Infiniti G35 seemed like a good way to test out what I’ve learned. In this tutorial we’ll solder onto your car’s remote so the things you can control are going to be limited to the remote’s capabilities(for now). In order to complete this project you’ll need to have some experience with soldering iron, Raspberry Pi(RPi), Amazon Alexa, AWS Lambda, Javascript/NodeJS/NodePackageManager and basic understanding of electrical circuits or willingness to learn as you go. I’m still a beginner myself and I’m sure there is a better/cleaner way to do this so any feedback is welcomed.
Here is what you can expect once done
Now let’s dive in :)
Part 0: Programming Extra Car RemoteBefore we get too far make sure you are able to program the spare remote control to your car. If your vehicle is self programmable (as is mine) you can follow this link or for so called key programming this link. If your vehicle is NOT self programmable you can find a locksmith or contact your dealer.
Part 1: Setting Up Alexa on Raspberry PIGo ahead and connect your speaker to Raspberry PI and make sure it works (play random video on YouTube, for example). Once done, follow this or this tutorial to install Alexa client on your Pi. You will most likely run into a known Bad Request issue along the way and here is how you can fix it. If you’ve done everything properly you should hear Alexa greet you. At this point pat yourself on a shoulder and move on to part 2 :)
Part 2: Setting Up the MicrophoneIn case you went with PS Eye you’ll first need to install the motion package by typing
sudo apt-get install motion
in terminal as described in this excellent article.
When done type in
arecord -l
You should see your device listed under CAPTURE Hardware Devices
This means the USB mic/camera is on “card 1”, “device 0”. Now go ahead and try to record something by typing
arecord --device=hw:1,0 --format S16_LE --rate 44100 -c1 test.wav
and then play it back by typing
aplay --device=plughw:1,0 test.wav
If you were able to hear your recording, play around with Alexa for a bit and move on to the part 3, otherwise check out this and/or this to troubleshoot.
Part 3: Putting Together the CircuitAt this point you should already have a working Alexa service on your Raspberry Pi and ready to talk some circuitry. This is probably the hardest and most time consuming part so patience is the key :)
Disclaimer: I am by no means an expert when it comes to building circuits. Most of the stuff needed for this project I learned along the way so if you are like me we are in the same boat :) I have to admit during the learning process I burned 2 LEDs and 1 transistor. May the current flow with them.
In order to send commands to the car we’ll need to imitate buttons being pressed on a remote. This is where transistors come into play. How NPN transistors work is out of scoop of this tutorial but if you are interested to learn I highly recommend this video. In short, transistor will act as a switch that opens to let the current flow thru (as if the button was pressed down) and then closes (button released).
Step 3.1: TestingBefore we start soldering let’s test the transistor on a breadboard
Here 3.3V coming from RPi goes to transistor collector leg(short red wire) and thru resistor to base(middle) leg. LED’s Anode(longer leg) is connected to emitter leg and Cathode(shorter leg) connected to GND(blue wire). If you take the resistor out LED should turn off because transistor base doesn’t have any current therefore it’s closed — no current travels from collector to emitter. Here is GPIO mapping for reference (3.3V = 3V3 on the diagram)
Step 3.2: Soldering the Printed Circuit Board (PCB)Cut and strip the wires. I picked red for VCC, black for GND and green for control signals.
Solder breakaway headers to the PCB. They will be used to communicate with RPi. Also, solder the resistors.
Solder the wires
Solder together headers with resistors and then resistors with wires on a back of PCB
I also decided to add a LED light to see if the PCB is on. This step is optional
Using jumper wires connect VCC header to 3.3V GPIO on RPi and Ground to GND. If you soldered a LED like I did it should come on. At this point PCB soldering is done, let’s move onto the remote.
Step 3.3: Car RemoteTake apart the spare remote control. Note which button does what (lock, unlock, trunk, etc.)
Now desolder the actual buttons
Solder red(VCC) and black(GND) wires coming from PCB onto remote where battery used to be. We should now have constant power coming from RPi. Solder transistors into places where buttons used to be by connecting transistor collector leg to VCC and emitter leg to Ground (I used multi meter to determine VCC was). Please note this is most likely a bad practice since transistors should be soldered onto PCB as well but for simplicity sake I did it this way
Next, solder green wires onto resistors’ base legs and use jumper wires to connect headers to GPIO pins that will be responsible for sending signals to the remote. I picked GPIOs 16, 20 and 21 for locking, unlocking and trunk respectively. Here is mapping again
Don’t forget to do some cleanup with a cotton swab and rubbing alcohol. 50% is probably not very good since it contains more water than higher percentage one but for the time being will do the trick
We will be running a local NodeJS/Express server that exposes endpoints for Alexa to communicate with.
First of all if you don’t have NodeJS installed on your Raspberry PI yet go ahead and do so . Then run these commands in the terminal on your Raspberry PI:
mkdir my-car-server && cd my-car-server
npm init
touch index.js
mkdir routes && cd routes
touch rest.js
cd ..
From now on you can either edit code with nano by typing in the terminal
nano index.js
or use Geany IDE from Applications Menu -> Programming -> Geany
npm init command should have created a package.json file inside my-car-server directory. Open it and paste the following dependencies:
"dependencies": {
"express": "^4.16.2",
"nodemon": "^1.17.1",
"onoff": "^2.0.0",
}
then run
npm install
this will install the above dependencies (ahoy captain obvious!)
Here is my git repo if you’d like to take a look/clone. Otherwise, open index.js and paste:
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');
const port = 4200;
const app = express();
const server = http.createServer(app);
app.locals.title = "Your title";
const rest = require('./routes/rest');
app.use('/rest', rest);
app.set('view engine', 'ejs');
server.listen(port, () => console.log(`listening on ${port}`));
module.exports = app;
index.js file now contains everything necessary to run local server. Now we need to create the actual endpoints that Alexa can talk to.
Navigate to routes directory and open rest.js. Paste the following:
const express = require('express');
const router = express.Router();
const path = require('path');
const GPIO = require('onoff').Gpio;
const carLock = new GPIO(16, 'out');
const carUnlock = new GPIO(20, 'out');
const carTrunk = new GPIO(21, 'out');
carLock.writeSync(0);
carUnlock.writeSync(0);
carTrunk.writeSync(0);
router.get('/lock', (req, res, next) => {
carLock.writeSync(1);
setTimeout(() => {
carLock.writeSync(0);
}, 1000);
console.log("lock car received");
res.send(200);
});
router.get('/unlock', (req, res, next) => {
carUnlock.writeSync(1);
setTimeout(() => {
carUnlock.writeSync(0);
}, 1000);
console.log("unlock car received");
res.sendStatus(200);
});
router.get('/trunk', (req, res, next) => {
carTrunk.writeSync(1);
setTimeout(() => {
carTrunk.writeSync(0);
}, 1000);
console.log("trunk received");
res.sendStatus(200);
});
router.get('/windows', (req, res, next) => {
console.log("windows received");
carUnlock.writeSync(1);
setTimeout(() => {
carUnlock.writeSync(0);
res.sendStatus(200);
}, 6000);
});
module.exports = router;
My car’s remote supports
- Lock
- Unlock
- Trunk
- Alarm
- Windows down (hold unlock for a few seconds)
Adjust the code to your remote capabilities. In order to imitate the button being pressed I set GPIO output to high(writeSync(1)) for a certain amount of time (1000, 2000, 6000ms) and then to low (writeSync(0)).
This is a very basic example and doesn’t include any security measures. Ideally you should have Alexa send some sort of token with each request to the endpoint. Server then verifies it and either executes the code or sends back HTTP 401 Unauthorized status code.
Now navigate back to my-car-server from terminal window and run the server by typing
node index.js
You should see the message in the terminal window:
listening on 4200
This is great BUT right now server runs locally meaning it’s only visible inside your local network. You have 2 options (that I’m aware of) to expose it:
These services tunnel data from “outside” to the local server you created.
Pick one and follow the documentation to install and run it. I personally prefer ngrok because I’ve been having problems with localtunnel dropping connection. On the other hand localtunnel allows you to pick domain names for free while ngrok generates random ones unless you pay for a subscription (5$/month gives you 3 reserved domains)
Part 5: Setting Up AWS Lambda FunctionI’m going to assume you took the Amazon Alexa course at codecademy that I referenced earlier in the article and already somewhat familiar with Lambda. Login to your AWS console, go to Lambda and create new function. Pick Blueprints -> alexa-skill-kit-sdk-factskill -> hit Configure at the bottom of the page
Fill out basic information and pick existing role called lambda_basic_execution. Hit Create function. You should be taken to the function configuration screen. Add trigger, make note of the ARN and make sure region is set to N. Virginia (top right corner).
Scroll down to code editor, erase all code and paste following:
"use strict";
var Alexa = require("alexa-sdk");
var https = require('https');
const myCar = "Infinit G35";
const api = "https://yourdomain.ngrok.io/rest";
var handlers = {
"LockIntent": function () {
let alexa_this = this;
https.get(`${api}/lock`, function(res) {
res.on('data', (d) => {
});
res.on('end', function(res) {
alexa_this.response.speak("Your car is locked");
alexa_this.emit(':responseReady');
});
});
},
"UnlockIntent": function () {
let alexa_this = this;
https.get(`${api}/unlock`, res => {
res.on('data', (d) => {
});
res.on('end', function(res) {
alexa_this.response.speak("Your car is unlocked");
alexa_this.emit(':responseReady');
});
})
},
"TrunkIntent": function () {
let alexa_this = this;
https.get(`${api}/trunk`, res => {
res.on('data', (d) => {
});
res.on('end', function(res) {
alexa_this.response.speak("Trunk is open");
alexa_this.emit(':responseReady');
});
})
},
"WindowsIntent": function () {
let alexa_this = this;
https.get(`${api}/windows`, res => {
res.on('data', (d) => {
});
res.on('end', function(res) {
alexa_this.response.speak("Windows down");
alexa_this.emit(':responseReady');
});
})
},
"LaunchRequest": function () {
this.response.speak("Hello. You can say lock, unlock or trunk to interact with " + myCar);
this.emit(':responseReady');
}
};
exports.handler = function(event, context, callback){
var alexa = Alexa.handler(event, context);
alexa.registerHandlers(handlers);
alexa.execute();
};
Again, code is not ideal and just serves as a basic example. Tweak it accordingly to your needs and save.
Part 6: Setting Up the Alexa SkillLet’s login to the Alexa skills kit developer console and create a new skill. Once you filled out the basic skill information you should see the skill builder screen. Go ahead and add desired intents. Here is what mine looks like:
Once done, hit Build Model and proceed to the endpoint sub-menu. Set radio button to AWS Lambda ARN and paste the ARN you made note of in the previous step into the Default Region field:
At this point skill should be all set, but it’s a good practice to test it out first so if the build is ready, head over to Test menu and type/say some of the utterances you added to intents earlier:
This is it! You should now be all set! Well done comrade!
Part 7: Testing with Your CarYou made it to the last chapter, congrats! Pat yourself on a shoulder with both hands this time and let’s wrap this up shall we?
You can either head over to your car and test it there or do some tests on a bread board first by connecting LEDs to the GPIO pins (don’t forget to put resistors in between!) we had set up earlier and checking if they light up based on your voice commands. Choice is yours :)
Before headed to the car, though, make sure to configure the WIFI hotspot on your phone and connect Raspberry Pi to it so that it automatically connects later. This will do for now but if you plan on keeping Alexa in your car you might want to invest in something like AT&T Velocity. The cheapest plan comes with 2GB of data for 90 days for 25$ which should be enough for interacting with Alexa.
I used the power bank to feed the Raspberry PI but you can also power it up from the 12V socket inside your car if you have an adapter.
Further DevelopmentI plan to keep working on this project by adding face recognition and control other things like AC, radio, lights etc. Ideally I’d like to be able to start/stop the car but manual transmission, immobilizer and steering wheel locking make it very hard to achieve :( If you have any ideas please feel free to share! Also, if you have any videos/pictures of what you’ve created based on this article I would definitely love to see it! Cheers!
Comments