Editing Raspberry Pi Code Remotely from Visual Studio Code

I’m spoiled. I love the pretty colors of modern text IDEs. My favorite among them being Visual Studio Code.

I know it’ll engender a lot of bad rep with the old-timers, but I prefer the one on the right.

However, when working on a headless (no monitor) Raspberry Pi it felt like I was pretty much stuck with the nano.

Until! I discovered Visual Studio Code’s remote extension.

This allowed me to edit my Raspberry Pi files from within Visual Studio Code. So, I get all the joys of writing code directly on my Raspberry Pi, but with all the bells-and-whistles of Visual Studio Code (VSC).

For the most part, setup is pretty straightforward. But the Pi side can get tricky, so I’m going to walk us through the process.

1. Get Visual Studio Code

Download the version of VSC for your PC. Note, you aren’t running this from the Raspberry Pi–instead, you’ll be running it from the PC and connecting it to the Raspberry Pi.

After it’s downloaded and installed open it up.








Once open, click here




Ok, now search for the extension called

Remote VSCode

And hit the Install button. Once it finishes hit the reload button.

The extension works by creating a server which listens for incoming calls from the Raspberry Pi. Once we finished setting up the Raspberry Pi we will use a special command which sends the file on the Raspberry Pi to Visual Studio Code. However, when it’s all done, it’ll look pretty seamless.

Back to setup.

In Visual Studio Code type F1 and type Preferences: Open Workspace Settings

Find the section labeled

remote.onStartup: false

We need to change it to true by clicking on the pencil next to its name. This sets the listening server to start every time you open Visual Studio Code.

Almost there. Now to setup the Raspberry Pi. We need to install a program on the Pi which will send a file of our choosing to Visual Studio Code to be edited. RMate was my choice.

Start by SSH’ing into your Raspberry Pi as root.

Run an update

pacman -Syu

Let’s install ruby and supporting packages.

pacman -S ruby ruby-docs ruby-rdoc
sed "s|unset appendpath|appendpath \'$(ruby -e 'print Gem.user_dir')/bin'\\nunset appendpath|g" /etc/profile >> /etc/profile
source /etc/profile

If it installs, then we setup the remote correctly. If not, feel free to ask debugging questions in the comments.

Now we’ll install the needed Ruby gems.

gem install rmate
gem install rdoc

The above commands install Ruby, moves to to the user’s directory, uses the Ruby package manager to install rmate, then adds Ruby and it’s Gems (packages) executables to the environment variables. All of this is necessary to get Rmate working on Arch Linux.

Ok, let’s test it. Stop SSH’ing into your Pi by typing exit until it brings you back to your PC’s prompt. Now we are going to SSH into the Pi while listening for incoming files to be displayed in Visual Studio Code.

Open Visual Studio Code and open the integrated terminal (if it’s not showing hit CTRL + `).

At the terminal type

ssh -R 52698:localhost:52698 alarm@192.168.1.x

Replace the x with your Pi’s ip address.

This should SSH into the Pi while listening for files.

At the pi command prompt, type

rmate test.js

This should open a new file called test.js in your Visual Studio Code.

Now you get all the goodness of the VSC IDE, such as syntax highlighting, linting, etc!

A few notes. File permissions still apply, so if you want to be able to save a file the user you logged into on the Raspberry Pi and rmated the file must have write permission on the file.

However, if you do have write permissions, then the “File Save” function in the VSC editor will update the Raspberry Pi file with your modifications. Booyah!




One last annoyance to address. Whenever you want to use VSC to edit your file you have to log into the Pi using

ssh -R 52698:localhost:52698 alarm@192.168.1.x

This annoyed me a bit. I could never remember all that. Instead, I created a small bash script to help.

On my PC (for Mac and Linux, Windows, you’re on your own) I created in my home user directory called

vs

And added the following to the file.

echo $1
ssh -R 52698:localhost:52698 "$1"

Essentially, this script takes your Pi’s login information and logs in to your Pi using the VSC Remote Extension listening.

To get it to work you’ve got to make the file executable

sudo +x chmod vs

Then login in your Pi like this

./vs alarm@192.168.1.x

Hope you enjoy.

Oh, and for you web-devs, this also works for remote servers. Just replace the Pi with the server.

Porting DRV8830 I2C Motor Driver Code to NodeJS

Earlier in this article series I showed how to install NodeJS – it was pretty simple with an install script. However, I thought I better show how I actually worked with NodeJS to create my little 1b1 driver code.

Again, simple, I used others hard work. Specifically, Michael Hord with Sparkfun’s MiniMoto library.

Really, all I did was tweak the code a little bit to fit JavaScript syntax.

The result

'use strict';
var i2c = require('i2c-bus');
var sleep = require('sleep');

// Commands

const FAULT_CMD         = 0x01;

// Fault constants

const CLEAR_FAULT       = 0x80;
const FAULT             = 0x01;
const ILIMIT            = 0x10;
const OTS               = 0x08;
const UVLO              = 0x04;
const OCP               = 0x02;

// Direction bits

const FORWARD           = 0b00000010;
const REVERSE           = 0b00000001;
const HI_Z              = 0b00000000;
const BRAKE             = 0b00000011;

module.exports = class Motor {

    /** 1. Add "inverse" motor option
     *  2. Add option to clear fault on each motor call.
     *  
     */

    constructor(address, i2cbus, options = undefined) {        
        this.address = address
        this.i2cbus = i2cbus
        this.options = options
    }

    getFault() {

        var fault = {
            message: '',
            code: 0
        }
    
        var faultCode;
        try {
            this.i2cbus.readByteSync(this.address, FAULT_CMD);
        } catch (e) {
            console.log(`Read fault failed: ${e}`)
        }
        
        fault.code = faultCode;
    
        if (faultCode !== undefined) {
            console.log(faultCode);
            fault.message = 'Unknown fault.';
            switch (faultCode){
                case FAULT:
                    fault.message = 'Unknown fault.'
                    break;
                case ILIMIT:
                    fault.message = 'Extended current limit event'
                    break;
                case OTS:
                    fault.message = 'Over temperature.'
                    break;
                case UVLO:
                    fault.message = 'Undervoltage lockout.'
                    break;
                case OCP:
                    fault.message = 'Overcurrent lockout.'
                    break;
                default:
                    fault.message = 'Unknown fault.'
                    break;
            }
            return fault;
        } else {
            fault.message = 'No fault';
            return fault;
        }
    }
    
    clearFault() {
        var fault = this.getFault(this.address);
        if (fault.code) {
            try {
                var success = this.i2cbus.writeByteSync(this.address, FAULT_CMD, CLEAR_FAULT);
                if (success) { return true; }
            } catch (e) {
                console.log(`Failed to clear faults: ${e}`)
            }
        }
        return false;
    }
    
    drive(speed = 0, direction = undefined, checkFault = false) {
        // The speed should be 0-63.

        if (checkFault) { this.clearFault();}
        if (direction === undefined) {        
            direction = speed < 0;
            speed = Math.abs(speed);
            if (speed > 63) { speed = 63; }
            speed = speed << 2 ;
            if (direction) { speed |= FORWARD; }
            else           { speed |= REVERSE; }
        } else {
            speed = speed << 2 ;
            speed |= direction;
        }
        try {
            this.i2cbus.writeByteSync(this.address, 0x00, speed);
        } catch (e){
            console.log('Drive command failed.')
        }
    }
    
    brake() {
        try {
            this.drive(0, HI_Z);
        } catch (e) {
            console.log('Brake command failed.')
        }
    }
    
    
    stop() {
        try {
            this.drive(0, BRAKE);
        } catch (e) {
            console.log('Brake command failed.')
        }
    }
}

There’s a lot left to do, but it works.

Todo List:

  1. Have the constructor accept an options object
  2. Add read() to get the current speed which a motor is set.
  3. Refactor option to clear faults on write to be determined during construction
  4. Add acceleration and deceleration algorithms add functions.
  5. Create an async polling of fault codes.

But! For now it works.

Also, or those who are like, “You stole code, dewd! Not cool.” Mhord’s code has a beerware license. I sent this email to Sparkfun in regards to the license and how I might pay Sparkfun back for their work.

Hey Mr. Hord,

I’m in the process of porting your DRV8830 library to Node–I wanted to make sure I give appropriate credit.

https://github.com/Ladvien/drv8830

Also, was going to ship some beer to Sparkfun–in respect of the beerware license. Just let me know what kind.

Lastly, I wanted to make sure Sparkfun benefits. It looks like the DRV8830 TinyMoto board has been discontinued. > Should I recommend people roll their own…or gasp get something off a slow ship from China? —Thomas aka, Ladvien

But I didn’t hear back. C’est la vie

Setup NodeJS Project Space on Raspberry Pi Zero W

Setup the Arch Linux SD Card

This article will be building off a previous article, where I walked through a headless setup of Arch Linux on the Raspberry Pi Zero W (rp0w). And if you aren’t familiar with the term “headless setup,” essentially, we are talking about setting up the SD card so you don’t have to plug it into a monitor. You can plug it in to your rp0w, boot it, and SSH in.

Now you’ve setup the Arch Linux card and SSH’ed lets go through setting up a NodeJS environment on the rp0w. Luckily, there have been people smarter than me who’ve already done some heavy lifting for us.

Alright, start by SSH’ing into your Raspberry Pi.

Running a NodeJS Install Script

Now we are at the Raspberry Pi command prompt we are going to run a script which will pull down the latest version of NodeJS built for ARM and install it to the Raspberry Pi.

But before we can do that we need to install a few helper programs

At the command prompt type and say “yes” when prompted.

sudo pacman -S wget

Wget is a package which allows direct download of Internet content from the command prompt.

Now, we will run a command which pulls a NodeJS installation script of the Internet and run it. This script was written by audstanley and can be found at

If you like the script, you should go buy audstanley a coffee – the link to do so is the Github page.

As of this writing, the script downloads the latest version of NodeJS for your architecture (that’s the tricky part), installs it, then creates the appropriate symbolic links for NodeJS and npm to work correctly.

Ok, enough preamble.

To install NodeJS type

sudo wget -O - https://raw.githubusercontent.com/audstanley/NodeJs-Raspberry-Pi/master/Install-Node.sh | sudo bash
node -v

That’s it!

We can now create a new node project by typing

mkdir my_node_project
cd my_node_project
npm init

If you want to learn more about NodeJS, I recommend the Udemy course:

I’m not getting kick backs; it’s the course I used to get and liked it. I actually used the Raspberry Pi for the code he walks through building and didn’t have a problem.

Lag() before MySQL 10.2

Recently I was required to work with MySQL 10.0. I was surprised to find MySQL 10.2 and less does not support some common Windowing Functions, specifically, Value Functions and Rank Functions.

Well, bummer. I really needed them.

On top of it, I only had read access to the database without the ability to create a stored procedure. Out of desperation, I found myself researching the possibility of creating my own functions using MySQL User Variables.

Slightly tweaking Dante, “Abandon all normal, ye who enter here.” User variables are weird.

I’ve spent some time researching on the interweb and about the best article I’ve found on the subject is:

Advanced MySQL User Variable Techniques

Which focuses on getting the desired behavior from user variables rather than understanding them. This article is going to stick with the same philosophy–I don’t need to necessarily understand them, however, I want to be able to predict their behavior.

One word of warning, the above article is not really a suggessted reading before this article–it’s more of required reading. If you don’t know how to force the user variables to be evaluated when you need them, then the results are unpredictable.

The TL;DR version: Order of operations matter a lot in user variables and wrap the user variable in a subquery or function to force evaluation.

At this bottom of the article I’ve included the data used in this article. You can insert it into a MySQL or MariaDB database and follow along. The goal is to convert these data into a start_date and stop_date which would greatly reduce the storage needs.

For id 1 the start_date and stop_date equivalents would look like:

id date  
1 09/10/12 start_1
1 09/11/12  
1 09/12/12  
1 09/13/12  
1 09/14/12 stop_1
1 10/11/12 start_2
1 10/12/12  
1 10/13/12 stop_2

We want to end up with a table like below.

id start_date end_date
1 09/10/12 09/14/2012
1 10/11/22 10/13/2012

To transform the data into this table it’s important to know user variables can hold a value from one row to the next.

SELECT
    id, date, @row_number:=@row_number + 1 row_num
FROM
    attendance
        CROSS JOIN
    (SELECT @row_number:=0) r;

This should produce the following table:

id date row_num
1 2012-09-10 1
1 2012-09-10 2
1 2012-09-11 3
5 2013-02-07 4
5 2013-02-07 5
5 2013-02-07 6
5 2013-02-07 7
5 2013-02-07 8
5 2013-02-07 9

Pretty cool, right? Now, if only we could get the row_number to reset whenever the id changes. No worries, let’s use another variable to store the id from the previous row so we can compare it to the current.

SELECT 
    id,
    date,
    @row_number:=@row_number + 1 row_number,
    IF(@previous_id = id,
        @row_number,
        @row_number:=0) calc1,
    @previous_id:=id cacl2
FROM
    attendance
        CROSS JOIN
    (SELECT @row_number:=0, @previous_id:=0) r;

This should give us the following output:

id date row_number calc1 cacl2
1 2012-09-10 1 0 1
1 2012-09-10 1 1 1
1 2012-09-11 2 2 1
5 2013-02-07 3 0 5
5 2013-02-07 1 1 5

Notice, the calc1 and calc2 are not values you need. They are merely calculations used to reset the row_number whenever the id changes. Hmm, this is interesting–and, hopefully, you can see it has many possibilities.

Now, let’s go back and think about our problem a bit more.

id date
1 09/10/12
1 09/11/12
1 09/12/12
1 09/13/12
1 09/14/12
1 10/11/12
1 10/12/12
1 10/13/12

We can save a value from one row to the next. Therefore, detecting the breaks in a range of attendance dates can be obtained by comparing the current row’s date value to the previous row’s date value. If the previous row is greater than the current row minus one, then we know there was a break in the range.

SELECT 
    id, date, range_selector
FROM
    (SELECT DISTINCT
        id,
            date,
            IF(@previous_id = id, @range_selector, @range_selector:=0) calc1,
            IF(DATEDIFF(@previous_date, date) = 1, @range_selector, @range_selector:=@range_selector + 1) range_selector,
            @previous_id:=id calc2,
            @previous_date:=DATE(date) calc3
    FROM
        (SELECT DISTINCT
        *
    FROM
        attendance
    ORDER BY id DESC , date DESC) order_sub
    CROSS JOIN (SELECT 
        @id_selector:=0,
            @previous_date:=0,
            @range_selector:=0,
            @previous_id:=0
    ) variable_initialization
    ORDER BY id , date DESC) date_ranges;

This should give the following table:

id date range_index
1 2012-10-13 1
1 2012-10-12 1
1 2012-10-11 1
1 2012-09-14 2
1 2012-09-13 2
1 2012-09-12 2
1 2012-09-11 2
1 2012-09-10 2
2 2012-09-23 1
2 2012-08-22 2
2 2012-08-17 3
2 2012-08-12 4
2 2012-08-11 4
2 2012-08-10 4
2 2012-08-09 4
4 2012-11-22 1
4 2012-11-03 2
4 2012-11-02 2
4 2012-11-01 2
4 2012-10-04 3
4 2012-10-03 3
4 2012-10-02 3
4 2012-10-01 3
5 2013-02-07 1
5 2013-02-06 1
5 2013-02-05 1
5 2013-02-04 1
5 2013-02-03 1
5 2013-02-02 1
5 2013-01-28 2
5 2013-01-24 3
5 2013-01-23 3
5 2012-12-07 4
5 2012-12-06 4
5 2012-12-05 4
5 2012-12-04 4
5 2012-12-03 4
5 2012-12-02 4
5 2012-12-01 4
5 2012-11-01 5

The reason I state “should”, if you modify the order of the user variables, it’ll break. If you change the ORDER BY, it’ll break. If you add a WHERE or HAVING clause, it’ll break. Pretty much, it’s as fragile a query as they come.

However, the clever bunch probably see where we are going with this. Now, it’s simply a matter of taking the MIN() and MAX() of of date and group by the id and range_index.

SELECT 
    id, min(date) start_date, max(date) end_date
FROM
    (SELECT DISTINCT
        id,
            date,
            IF(@previous_id = id, @range_selector, @range_selector:=0) calc1,
            IF(DATEDIFF(@previous_date, date) = 1, @range_selector, @range_selector:=@range_selector + 1) range_selector,
            @previous_id:=id calc2,
            @previous_date:=DATE(date) calc3
    FROM
        (SELECT DISTINCT
        *
    FROM
        attendance
    ORDER BY id DESC , date DESC) order_sub
    CROSS JOIN (SELECT 
        @id_selector:=0,
            @previous_date:=0,
            @range_selector:=0,
            @previous_id:=0
    ) r
    ORDER BY id , date DESC) date_ranges
    GROUP BY id, range_selector;

Which should provide us with output like:

id start_date end_date
1 2012-10-11 2012-10-13
1 2012-09-10 2012-09-14
2 2012-09-23 2012-09-23
2 2012-08-22 2012-08-22
2 2012-08-17 2012-08-17
2 2012-08-09 2012-08-12
4 2012-11-22 2012-11-22
4 2012-11-01 2012-11-03
4 2012-10-01 2012-10-04
5 2013-02-02 2013-02-07
5 2013-01-28 2013-01-28
5 2013-01-23 2013-01-24
5 2012-12-01 2012-12-07
5 2012-11-01 2012-11-01

And there we go. Not too amazing, but I couldn’t find this answer by Googling, so I figure I’d add it to the great Wiki in the Sky.

CREATE TABLE attendance(
   id   INTEGER  NOT NULL
  ,date DATE  NOT NULL
);
INSERT INTO attendance(id,date) VALUES (1,'2012-09-10');
INSERT INTO attendance(id,date) VALUES (1,'2012-09-11');
INSERT INTO attendance(id,date) VALUES (1,'2012-09-12');
INSERT INTO attendance(id,date) VALUES (1,'2012-09-13');
INSERT INTO attendance(id,date) VALUES (1,'2012-09-14');
INSERT INTO attendance(id,date) VALUES (1,'2012-10-11');
INSERT INTO attendance(id,date) VALUES (1,'2012-10-12');
INSERT INTO attendance(id,date) VALUES (1,'2012-10-13');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-09');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-10');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-11');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-12');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-17');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-22');
INSERT INTO attendance(id,date) VALUES (2,'2012-09-23');
INSERT INTO attendance(id,date) VALUES (4,'2012-10-01');
INSERT INTO attendance(id,date) VALUES (4,'2012-10-02');
INSERT INTO attendance(id,date) VALUES (4,'2012-10-03');
INSERT INTO attendance(id,date) VALUES (4,'2012-10-04');
INSERT INTO attendance(id,date) VALUES (4,'2012-11-01');
INSERT INTO attendance(id,date) VALUES (4,'2012-11-02');
INSERT INTO attendance(id,date) VALUES (4,'2012-11-03');
INSERT INTO attendance(id,date) VALUES (4,'2012-11-22');
INSERT INTO attendance(id,date) VALUES (5,'2012-11-01');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-01');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-02');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-03');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-04');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-05');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-06');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-07');
INSERT INTO attendance(id,date) VALUES (5,'2013-01-23');
INSERT INTO attendance(id,date) VALUES (5,'2013-01-24');
INSERT INTO attendance(id,date) VALUES (5,'2013-01-28');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-02');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-03');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-04');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-05');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-06');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-07');
Setup i2c on Raspberry Pi Zero W using Arch Linux

This article builds on the previous, where I ran us through setting up Arch Linux for the Raspberry Pi Zero W.

Let’s not stop, let’s get I2C going so we can interact with some cool hardware.

1. Installing sudo

If you’ve followed my previous guide on installing Arch Linux on a Raspberry Pi then you’ll have ended up with a bare bones system, which is good. No unneeded fat. But sometimes fat is needed, it’s what gives us curves, and curves are beautiful….I feel this metaphor is breaking down. In short, we need extra packages to get work done.

The first package we need is sudo

It will allow us to easily manage file permissions.

First, make sure your system is up to date. To do this we are going to login as the root user. You can do this by typing su followed by the root user’s password, which for a barebone Arch Linux installation is root.

$ su
Password: root

Oh, and if you haven’t figured out yet, alarm stands for Arch Linux ARM.

Next we need to update the package libraries and system.

pacman -Syu

After hitting enter, it should look like this:

root@alarmpi alarm]# pacman -Syu
:: Synchronizing package databases...
 core                                                        179.0 KiB   448K/s 00:00 [#################################################] 100%
 extra                                                      1982.8 KiB  1279K/s 00:02 [#################################################] 100%
 community                                                     4.0 MiB  1689K/s 00:02 [#################################################] 100%
 alarm                                                        35.0 KiB   583K/s 00:00 [#################################################] 100%
 aur                                                           6.0 KiB  0.00B/s 00:00 [#################################################] 100%
:: Starting full system upgrade...
resolving dependencies...
looking for conflicting packages...

It should give you a list of packages with update and upgrade candidates and prompting you to confirm the updates. Go ahead and say yes.

Now we should be good to install sudo

$ pacman -S sudo

Even after sudo is installed, we still need to add the main user, which is alarm to the sudo’er group. This in effect gives the alarm user the superpowers of the root user.

Now, the way sudo works is by adding a user to a special Linux group. Anyone added to this group will be given root superpowers. To get a list of those currently in the sudo group:

sudo -ll

You should get something like

User root may run the following commands on alarmpi:

Sudoers entry:
    RunAsUsers: ALL
    Commands:

Ok, let’s get the alarm user added to the sudoer group.

Type

EDITOR=nano visudo

This should allow you to edit the visudo file and add alarmpi to sudoers. Oh, the write permissions for the visudo file are limited to root, so if you have switched back from the root user to alarmpi you will need to run su again and log back in as root before editing this file.

Let’s find the entry for adding users to the sudo’er group.

Find the part which looks like this:

##
## User privilege specification
##
root ALL=(ALL) ALL

And add alarm ALL=(ALL) ALL right below the root entry. It should look like this after editing.

##
## User privilege specification
##
root ALL=(ALL) ALL
alarm ALL=(ALL) ALL

Then hit CTRL+O to write the changes and CTRL+X to exit.

Before we can check the changes took, we will need to exit our root session.

exit

This should land you back at your alarm session. To see you the alarm user is now added to the sudoer group type

sudo -ll

And if all went well, you’ll get this output

User alarm may run the following commands on alarmpi:

Sudoers entry:
    RunAsUsers: ALL
    Commands:
        ALL

Notice, we now have access to ALL commands. Booyah!

We can do a hard test by typing:

sudo ls

We should get

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for alarm:

Type the alarm user password (which is alarm, if you haven’t changed it).

2. Install needed packages

pacman -S git python2 i2c-tools base-devel python2-distribute python2-pip

Use Python’s Package Index (pip) to install Raspberry Pi GPIO support

sudo pip2 install RPi.GPIO

3. Install raspi-config

sudo pacman -S xorg-xrandr libnewt
git clone https://aur.archlinux.org/raspi-config.git
cd raspi-config
makepkg -i

Use the Raspi-Config tool to enable I2C

sudo raspi-config

Select “Interfacing Options” and enable I2C.

Note: Going back through these instructions I did notice when I started raspi-config I received this warning:

/usr/bin/raspi-config: line 997: warning: command substitution: ignored null byte in input

And when I attempted to enable I2C it gave this error.

* Failed to read active DTB

But it still seemed to do the job. I’ll investigate more when I’ve time.

Now, we need to reboot the system for everything to register.

4. Test the I2C Setup

We should be all setup. Try running

sudo i2cdetect -y 1

If all has went well then you should get

[alarm@alarmpi ~]$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Now, we just need to connect an I2C device to the bus and we should the hex address of where the device may be found.