Improve this doc
Getting Started with and

Looking for other boards documentation? Click here.

Getting Started With Raspberry Pi 3 and Node.js

Introduction

In this guide we will build a simple Node.js web server project on a Raspberry Pi 3. At its most basic, the process for deploying code to a Raspberry Pi 3 consists of two major steps:

  • Setting up your Raspberry Pi 3 with resinOS, the host OS that manages communication with resin.io and runs the core device operations.
  • Pushing your Node.js project to the resin.io image builder, which pulls in all necessary dependencies and creates the container image for your application.

Once these steps are finished, your Raspberry Pi 3 will download the container image, kick off your application, and begin sending logs to your resin.io dashboard!

What you will need

Need help?

If you find yourself stuck or confused, help is just a click away:

  • The troubleshooting section of the forums is where our engineers address any issues you may be having with resin.io.

  • We also have a community chat for more general discussion.

  • You can read about our approach to support at resin.io/support.

Account setup

If you don't already have a resin.io account, make sure to sign up.

Adding an SSH Key

Resin.io uses git to push code from your computer to a dedicated repository. As part of the account creation process, you will be asked to add a public SSH key. The SSH key secures your connection to our server, letting us know you have the authority to make changes to the repository.

Note: You can click Skip to move past this step for now, but you will not be able to push code to your Raspberry Pi 3 until you have added a public key to your account. This can be done at any time from the Preferences page on the dashboard.

If you have a public SSH key, simply paste it into the box provided and click Save Key:

You can also import your key from GitHub. If you choose this option, you will be asked to enter your GitHub username:

Don't have an SSH key?

If you are unfamiliar with SSH keys, we recommend you take a look at GitHub's excellent documentation. This will walk you through everything you need to create a key pair. Window's user? Be sure to check out these instructions.

Create an application

An application is a group of devices of the same type that all run the same code. When you provision a device, it is added to a specific application, but can be migrated to another application at any time.

To create an application, simply select the Raspberry Pi 3 type from the drop-down list, type in a name, and click Create new application:

This will take you to the dashboard for your newly created application, where you can manage your whole fleet of Raspberry Pi 3s.

Add your first device

To connect with resin.io, your Raspberry Pi 3 will need a resinOS image that is configured for your application and your network. Start by clicking Add device in your application dashboard:

A list of available resinOS versions is shown in the dropdown. In general, the most recent version is recommend. You can also select whether you would prefer a Development or Production edition with a toggle:

Note: When you're first getting started, a Development image will be most useful, as it permits a number of testing and troubleshooting features. For production use, be sure to switch to a Production image. More details on the differences between Development and Production images can be found here.

A toggle is also used to select whether your network connection will be through Ethernet Only or with the option for WiFi + Ethernet. Selecting Wifi + Ethernet allows you to enter a WiFi SSID and WiFi Passphrase:

Clicking Advanced presents the option to select the rate at which your device checks for updates and the option to download just a configuration file (config.json) rather than an entire image:

Once you have finished your image configuration, click the Download resinOS button. When the download completes, you should have a zipped image file with a name like resin-FirstApp-2.3.0+rev1-dev-v6.1.3.img.zip, where FirstApp is the name you gave your application on the dashboard.

Create a bootable SD card

The next step is to flash the downloaded image onto your SD card using Etcher, a simple, cross platform SD card writer and validator. Once you have Etcher installed, start it up. To give Etcher access to your SD card, your system may prompt you to grant administrative privileges.

To create a bootable resinOS SD card follow these steps:

  1. Click Select image and find your application's resinOS image file.
  2. If you haven't already done so, insert your SD card into your computer. Etcher will automatically detect it. If you have more than one SD card inserted, you will need to select the appropriate one.
  3. Click the Flash! button.

Etcher will now prepare a bootable SD card and validate that it was flashed correctly. This can take roughly 3 or more minutes depending on the quality of your SD card. You'll get a little ping when it's done, and Etcher will safely eject the SD card for you.

Note: You can burn several SD cards with the same image file and all the devices will boot and provision into your application's fleet. You can also disable the auto-ejecting or validation steps from the Etcher settings panel.

Provision your device

Insert the SD card into your Raspberry Pi 3 and connect the ethernet cable if necessary. Now power up the Raspberry Pi 3 by inserting the micro USB cable.

insert SD

It will take a minute or two for the Raspberry Pi 3 to appear on your resin.io dashboard. While you wait, the resinOS is expanding the partitions on your SD card to use all available space, installing a custom Linux environment, and establishing a secure connection with the resin.io servers.

You should now be ready to deploy some code!

Note: Class 4 SD cards can take up to 3 times longer so it's well worth investing in the fastest card you can find.

Help! My device won't show up.

If your device still hasn't shown up on your dashboard after a few minutes, something is definitely wrong. First check that you entered the WiFi credentials correctly and ensure that your network meets these basic requirements. It may also be worth checking the LED error notifications

If you still can't get your device online, come on over and chat to us on our support channel.

Note: If you have an HDMI screen attached, you should see "Booted - Check your resin.io dashboard." on the screen when the device boots. If instead you see rainbow colours or a black screen with 4 raspberries on it, it could mean that the SD card was not burned correctly or is corrupted.

Deploy code

Now that we have a device or two connected to a resin.io application, lets deploy some code and actually start building something.

A nice first project to get your feet wet is a simple Express.js web server which will serve a static page on port :80. All the project source code can be found here on github.

To clone the project, run the following command in a terminal or your preferred git client:

$ git clone https://github.com/resin-io-projects/simple-server-node.git

Once the repo is cloned, change directory into the newly created simple-server-node directory and add the resin git remote endpoint by running the command git remote add shown in the top-right corner of your application page:

$ cd simple-server-node
$ git remote add resin <USERNAME>@git.resin.io:<USERNAME>/<APPNAME>.git

Note: On other git clients there may be an alternative way to add a remote repository.

So now we have set up a reference in our local git repository (the one on our development computer) to the resin.io application remote repository. So when we push new changes to this remote repository it will get compiled and built on our servers and deployed to every device in the application fleet.

Now to deploy this code to all device(s) in the application just run the command:

$ git push resin master

If you want to completely replace the source code of the application with a new source tree, you may need to force the push by running git push resin master --force, due to how git works.

Note: On your very first push, git may ask you if you would like to add this host to your list of allowed hosts. Don't worry about this, just type 'yes' and carry on your merry way.

You'll know your code has been successfully compiled and built when our friendly unicorn mascot appears in your terminal:

This means your code is safely built and stored on our image registry. It should only take about 2 minutes to build your code and subsequent builds will be quicker because of build caching.

Your application will now be downloaded and executed by all the devices you have connected in your application fleet. You may have to wait about 6 minutes for the first push... So time for more tea, but don't worry, all subsequent pushes are much, much faster due to Docker layer sharing. You can see the progress of the device code updates on the device dashboard:

You should now have a node.js web server running on your device and see some logs on your dashboard. If you go to the Actions page for your device, you can enable a public URL, this URL is accessible from anywhere in the world.

If you follow the URL, you will be served a page saying "Hello, World!". Alternatively you can point your browser to your devices IP address.

You should now have a basic idea of how to deploy a node.js application on resin.io. If you feel like you have a handle on Docker and Node.js projects, then skip over the next section and go straight to "Using the web terminal".

Let's dive into the code

So in the root directory of our project we see a number of files, the most important ones to focus on are:-

  • Dockerfile.template : This is basically a recipe file on how to build and run our application container.
  • package.json : This is a JSON file that describes how our node.js project is built, what dependencies it has and it's entry point. Read more about it on the npm docs.
  • server.js : This is the entry point to our application code and is where all the fun happens!

The most important part of a resin.io project repo is usually the Dockerfile or Dockerfile.template. The .template version allows you to define template variables like %%RESIN_MACHINE_NAME%% which enables you to push the same repository to multiple different architecture fleets.

If we look at our Dockerfile.template, the first thing we see is:

FROM resin/%%RESIN_MACHINE_NAME%%-node:slim

This line has quite a bit packed into it. The first thing that happens is that the %%RESIN_MACHINE_NAME%% place holder gets stripped and replaced with the resin device name. For example if your application type is a Raspberry Pi 3, the line will be replaced with:

FROM resin/raspberrypi3-node:slim

Which tells the resin builder that this is the Docker image we want as our base. Checkout the full list of official resin device names and the matching Docker Hub base images.

We also have a :slim tag associated to the base image which denotes that we want the stripped down version only contains the minimal packages needed to run node, so no node-gyp and other build-essentials. If you need to build some native modules, say node-i2c, you should switch to :latest tag. We also have a number of pinned version tags, which should be used for production devices. Checkout the full list of -node tags, if you want to target a specify node.js version or a fixed date build.

Next up we have 3 line which were commented out:

RUN apt-get update && apt-get install -yq \
   alsa-utils libasound2-dev && \
   apt-get clean && rm -rf /var/lib/apt/lists/*

This is just a demonstration of how you can use apt-get to install dependencies in your container. In this case we would install some useful linux sound utilities.

The next two directives are pretty straight forward and key parts of using Docker.

# Defines our working directory in container
WORKDIR /usr/src/app

# Copies the package.json first for better cache on later pushes
COPY package.json package.json

As the comments say, WORKDIR set our working directory for any RUN, COPY or CMD commands following it. So the next line would effectively COPY our package.json in the root of our directory to usr/src/app/package.json. Check out the Docker reference pages for more info on these commands.

We can now build all our node.js modules and dependencies, this is done using the RUN command. We also build with the --production flag and clear the cache in the same step in order to keep the final image size smaller.

# This install npm dependencies on the resin.io build server,
# making sure to clean up the artifacts it creates in order to reduce the image size.
RUN JOBS=MAX npm install --production --unsafe-perm && npm cache clean && rm -rf /tmp/*

# This will copy all files in our root to the working  directory in the container
COPY . ./

# Enable systemd init system in container
ENV INITSYSTEM on

# server.js will run when container starts up on the device
CMD ["npm", "start"]

After the npm install we copy the rest of our source code into the working directory, we do this after so that later builds can benefit from build caching. So we will only trigger a full npm install if we change something in package.json.

The last 2 commands are runtime directives. The ENV INITSYSTEM on is used to enable the systemd init within the container. This is useful for a number of reasons, like keeping the container open after application crash and handling /dev updates as new USB devices are plugged in. If you don't want an init system, just set it to off or remove the line for the Dockerfile.

The last command, CMD is perhaps one of the most important. This command defines what will run at container start on your Raspberry Pi 3, in our example we have told npm to start a process. It should be noted that you can only have one CMD per Dockerfile.

In our package.json the parts to focus on are our "scripts" and "dependencies":

{
  "name": "simple-server-node",
  "version": "1.0.0",
  "description": "A simple Expressjs Web server on resin.io",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "dependencies": {
    "express": "*"
  }
}

The "dependencies" section allows us to define node modules and their versions we want to use in our application. For a production application we recommend pinning the versions.

The "scripts" allow us to point to the server.js as our starting point for the whole application, so we get our awesome "Hello, World!" server when the container starts up :)

Use the web terminal

Often while developing and deploying code, it helps to run a few test commands or check some log files. For this resin has a handy built in web terminal which you can use from the comfort of your dashboard, or command line (check the later section on CLI and resin sync for this).

To fire up a terminal session on your device you need to two things:

  1. An online device.
  2. A running container.

Number .1 is usually pretty easy, but number .2 catches people pretty often. Since if the main process of the Docker container crashes or ends, the container effectively stops and there is nothing for the web terminal to SSH into :( . For this reason we normally recommend using the systemd init system during development as this will ensure your container is always up and running, even if your application code crashes.

To start a session, just navigate to the >_ Terminal page for the device and hit the "Start the terminal session" button. It will take a few seconds to establish a connection and then you are good to go.

Note: Currently if you navigate away from the >_ Terminal page, you session will be killed. This is a known issue and will be remedied very soon.

Use resin sync to develop quickly

Okay, so now we know how to provision a device and push code. There is just one glaring problem...

Do I really have to go through the whole git add, git commit, git push dance every time I forget a semicolon?

Luckily, our nifty little command line tool resin sync is here to save the day. It allows you to quickly sync source code and file changes across to one of the devices in your fleet, so you can rapidly iterate code on this test device before releasing it to the whole fleet.

Note: Resin sync will only work on ResinOS v1.1.4+ and Agent v1.8.0+.

Setting up resin sync.

Resin sync is bundled in with our handy resin CLI. The CLI allows you to basically do all your resin.io management from the comfort of the command line. Read the CLI reference more info on all the cool things it can do.

Warning: Currently resin sync is NOT supported on Windows. Support is currently being worked on, you can check the progress on this on the git repository.

To install resin CLI and sync you need at least node.js 4.0.0 on your development machine, then run:

$ npm install --global --production resin-cli

You may need to run the install with admin privileges depending on how you have installed node.js.

Note: If you already have resin CLI installed, you will need to upgrade to resin-cli v4.0.0+

Once the CLI is installed globally, login with your resin account:

$ resin login

You should then be presented with 3 options to login. The recommended method is Web authorisation which will open a dialog in your web browser and ask you to authorise the use of the CLI.

resin:simple-server-node shaun$ resin login
______          _         _
| ___ \        (_)       (_)
| |_/ /___  ___ _ _ __    _  ___
|    // _ \/ __| | '_ \  | |/ _ \
| |\ \  __/\__ \ | | | |_| | (_) |
\_| \_\___||___/_|_| |_(_)_|\___/

Logging in to resin.io
? How would you like to login? (Use arrow keys)
❯ Web authorisation (recommended)
  Credentials
  Authentication token
  I don't have a Resin account!

Setting up Node.js cross-compilation

Cross-compilation is not needed when using resin sync and Node.js as it is an interpreted language. Interpreted languages are programming languages in which programs may be run from source code form, they do not need to be compiled.

Using resin sync

You are now ready to start using resin sync, so open a terminal in the directory which we were using earlier in this guide. Make a trivial change to your source code and then run:

$ resin sync --source . --destination /usr/src/app

It should prompt you to select a device, then it will sync all the files in the root of your directory to /usr/src/app on your selected device and finally it will restart the container. You should see something similar to:

resin:simple-server-node shaun$ resin sync --source . --destination /usr/src/app
Getting information for device: 5dc2c87ea2caf79bc4e25ae638c6d5a35a75cecf22e8f914331dcb2f588f4b
Application container stopped.
Synced /usr/src/app on 5dc2c87.
Application container started.

resin sync completed successfully!

Your new code changes should be up an running in under 30 seconds, Great success!!

Some Notes and Caveats on Resin Sync
  • Resin sync directly syncs all the files in the root of your directory to the selected destination directory on your device. If you are using a compiled language you will need to set up cross-compiling on your local development machine and include a .resin-sync.yml file that tells resin sync how to compile your code and sync the resulting executable.

  • It is not possible(~easy) to install dependencies using resin sync. So if you need to do an apt-get or add something to either your Dockerfile, package.json or requirements.txt, then you will need to go through the standard git push resin master build pipeline.

  • If you know the device UUID you can just run: resin sync <uuid>. This is useful for using resin sync programmatically, in say a gulp workflow, as it does not require interactive action to confirm the destination device.

  • If no --destination / -d option is set, an interactive dialog will ask you to provide a destination directory. You can skip this by hitting 'enter' and resin sync will use the /usr/src/app device directory as the default sync destination.

  • After every resin sync the updated settings are saved in .resin-sync.yml in your projects's local directory and will be used in later invocations. You can also change any option by editing '.resin-sync.yml' directly.

  • A caveat, if you are using a DSA key, some newer openSSH clients do not allow them by default. So you may have to add the following option to ~/.ssh/config : PubkeyAcceptedKeyTypes=+ssh-dss

  • Resin sync is currently permitted to device owners only. The owner of a device is the user who provisioned it, so you will not be able to use resin sync on devices of shared applications that you did not provision.

One last Tip!

One of the many useful features of resin-cli is resin ssh, this command allows you to quickly SSH into a device's running container and run test commands or pull out some logs.

Provided you are already logged in on the CLI and you have a device online, you can use resin ssh <uuid> to access the container. Here is an example:

resin:simple-server-node shaun$ resin ssh 5dc2c87
Connecting with: 5dc2c87
[email protected]:/# uname -a
Linux Raspberry Pi 3-5dc2c8 3.10.93 #1 SMP PREEMPT Wed Apr 20 10:25:12 CEST 2016 armv7l GNU/Linux

Example projects

There are even more hidden treasures in the resin.io platform and tools, but we will get into those a bit later. For now why not fork one or two of our example projects and build something grand.

Basic GPIO Control in Node.js

Repository

This sample project will get you started blinking LEDs on the Raspberry Pi 3 using only javascript. For this project you will need some additional hardware, namely a basic LED, a breadboard and a 220 ohm resistor.

Audio stock ticker in Node.js

Repository

The audio stock ticker will verbally announce a list of your favourite stocks every couple of minutes or hours, depending on how you configure it. For this project you will need some head phones or speakers to connect to the Raspberry Pi 3's audio jack.

Servo motor control in Node.js

Repository

A simple application to issue commands to a servo motor using pi-blaster.

SMS 2 Speech

Repository

Send SMSes with Twilio and convert them to speech on your Raspberry Pi. For this project you will need some head phones or speakers to connect to the 's audio jack.

I2C proximity sensor

Repository

This is a simple node.js project that uses i2c-bus to get data from a VLNC4000 proximity & light sensor. It is made to be generic and act as base for any i2c sensor integration. It should work on any of the resin.io supported devices, you just need to make sure i2c is enabled in the kernel and know the i2c bus number for you device.

RPI camera module example in Node.js

Repository

Enjoy Resinifying All the Things!