DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป

Cover image for How to build AdonisJS API to store your Raspberry Pi Temperature
Bobby Iliev
Bobby Iliev

Posted on • Originally published at devdojo.com

How to build AdonisJS API to store your Raspberry Pi Temperature

Introduction

I have a few Raspberry Pi devices at home and I wanted to be able to collect the data from their temperature sensors on a regular interval and store that data in a Postgres database. Later on, I could use that data for some analytics together with Materialize.

In this tutorial we will use AdonisJS to build a simple API that will collect the data from the sensors and store it in a Postgres database as shown in the following image:

AdonisJS Simple API

This can be used to collect the temperature data of a large number of Raspberry Pi devices.

Prerequisites

Before you get started, you would need to make sure that you have the following installed:

What is AdonisJS

AdonisJS is a Node.js framework that is used to create RESTful APIs along with full-stack web applications. It is a great tool to build web applications that can be deployed to any platform. It feels a lot like Laravel, but it is based on Node.js rather than PHP.

If you are not familiar with AdonisJS, no worries, you would still be able to follow along! However, if you want to learn more about AdonisJS make sure to check out this tutorial here:

AdonisJS for Beginners

How to install AdonisJS

To install AdonisJS you would need to run the following command:

npm init adonis-ts-app@latest raspberry-pi-adonisjs-app
Enter fullscreen mode Exit fullscreen mode

Once you run that, you will be asked to select a project structure. You will be able to choose between an API, Web App, and a minimal possible AdonisJS app:

CUSTOMIZE PROJECT
โฏ Select the project structure โ€ฆ  Press <ENTER> to select
โฏ api   (Tailored for creating a REST API server)
  web   (Traditional web application with server-rendered templates)
  slim  (A smallest possible AdonisJS application)
Enter fullscreen mode Exit fullscreen mode

For this tutorial let's go with the API option! Using your arrow keys select web and hit enter.

After that you will be asked to choose a name for the project, I will leave this as raspberry-pi-adonisjs-app but feel free to choose a different name.

I will then press enter and say yes to the rest of the settings:

โฏ Enter the project name ยท raspberry-pi-adonisjs-app
โฏ Setup eslint? (y/N) ยท y
โฏ Configure webpack encore for compiling front-end assets? (y/N) โ€บ y
Enter fullscreen mode Exit fullscreen mode

Once that is done, you can switch to the new project directory:

cd raspberry-pi-adonisjs-app
Enter fullscreen mode Exit fullscreen mode

And once in there, start the webserver:

node ace serve --watch
Enter fullscreen mode Exit fullscreen mode

The ace command is very similar to the artisan command in Laravel. It is a command-line interface for running AdonisJS commands. The node ace serve command will start the webserver and watch for changes to your code.

To check all of the ace commands, you can run: node ace.

Installing Lucid

Similar to Laravel Eloquent, AdonisJS provides an ORM. The ORL is called Lucid and we will be using it today.

Lucid comes with an Active Record ORM, Query Builder, Migrations, Seeds, and Factories.

To install Lucid, run the following command:

npm i @adonisjs/lucid
Enter fullscreen mode Exit fullscreen mode

Once done, you would need to do a quick configuration.

Configuring Lucid

In order to configure Lucid, you need to run the following ace command:

node ace configure @adonisjs/lucid
Enter fullscreen mode Exit fullscreen mode

You will be asked to select the database driver that you want to use. Here, make sure to select PostgreSQL!

AdonisJS lucid configuration

Next, you will be asked to select where you want to display the configuration instructions. I chose In the terminal, which prints out the necessary environment variables that you have to add to your .envfile.

Make sure to update the DB_DATABASE and DB_USERNAME and DB_PASSWORD variables in your .env file accordingly so that you can connect to your database.

Add a mode and a migration

To add a model and a migration, run the following command:

node ace make:model Sensor -m
Enter fullscreen mode Exit fullscreen mode

This will create a new model and a migration:

CREATE: database/migrations/1639847090390_sensors.ts
CREATE: app/Models/Sensor.ts
Enter fullscreen mode Exit fullscreen mode

Open the migration file and update the file so that it looks like this:

import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class Sensors extends BaseSchema {
  protected tableName = 'sensors'

  public async up () {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id')
      table.string('device')
      table.string('temperature')
      table.string('timestamp')
      /**
       * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
       */
      table.timestamp('created_at', { useTz: true })
      table.timestamp('updated_at', { useTz: true })
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}
Enter fullscreen mode Exit fullscreen mode

We basically added 3 extra columns that will store the name of the device, the temperature, and the timestamp when the data was recorded.

To run the migration, run the following command:

node ace migration:run
Enter fullscreen mode Exit fullscreen mode

This will create the sensors table in your database with the columns we specified.

Creating a Controller

Next, we will create a controller. This is where we will add the functionality that will allow us to store the Raspberry Pi data in our Postgres database.

Again we will be using the ace command to create a new controller:

node ace make:controller SensorsController
Enter fullscreen mode Exit fullscreen mode

This will create a controller file at:

app/Controllers/Http/SensorsController.ts
Enter fullscreen mode Exit fullscreen mode

Next, let's create the routes that we would need!

Adding our methods

As we are going to use this API to store the data from our Raspberry Pi devices, we will add just a single method to our controller.

With your favorite text editor, open the SensorsController.ts file and add the following method:

import Route from '@ioc:Adonis/Core/Route'
import Database from '@ioc:Adonis/Lucid/Database'

// import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class SensorsController {
    public async store ({ request }) {

        let name = 'raspberry-1';
        if (request.qs().name != null) {
            name = request.qs().name;
        }

        let timestamp = '2021-11-21 19:52:49';
        if (request.qs().timestamp != null) {
            timestamp = request.qs().timestamp;
        }

        let temperature = '41.1';
        if (request.qs().temperature != null) {
            temperature = request.qs().temperature;
        }

        console.log(name, timestamp, temperature)

        await Database
        .insertQuery()
        .table('sensors')
        .insert({ device: name, timestamp: timestamp, temperature: temperature})

        return {
            message: 'Successfully added sensor data'
        }
    }
})
Enter fullscreen mode Exit fullscreen mode

There are a few things to note here:

  • The import statement is importing the Route and Database from the @ioc:Adonis/Core/Route and @ioc:Adonis/Lucid/Database packages.
  • The await keyword is used to wait for the database query to finish.
  • The request.qs() method is used to get the query string parameters from the request. That way we will be able to get the name, timestamp, and temperature sent by the Raspberry Pi devices.

Creating the AdonisJS routes

Your routes file is stored at start/routes.ts. In there we can specify our application URLs and map them to different controllers and methods!

We do not yet have the methods ready, but we know that we would need the following routes:

  • GET /temperature: This route will store the data sent by the Raspberry Pi devices.

Open your routes file at start/routes.ts and update it so that it has the following content:

import Route from '@ioc:Adonis/Core/Route'

Route.get('/temperature', 'SensorsController.store')
Enter fullscreen mode Exit fullscreen mode

Adding authentication

For the sake of this tutorial, I would not be implementing a full-blown authentication as the API would be running locally on my network and would not have any sensitive data.

However if you want to take this one step further, you can follow the steps from the documentation here on how to implement this:

AdonisJS Authentication Docs

Adding cron jobs to the Raspberry Pi devices

Now that we have our controller and routes, we can add a cron job to the Raspberry Pi devices which will send the data to the API and store it in our database.

Let's create a small bash script which we will run every minute:

#!/bin/bash

# AdonisJS API URL - Make sure to change this to your API URL
api_url="http://localhost:3333/temperature"

# Specify the name of the Raspberry Pi device:
name="raspberry-1"

if [[ -z ${NAME} ]] ; then
    name="raspberry"
fi

# Get the temperature from the Raspberry Pi device:
function temperature(){
    temperature=$(/opt/vc/bin/vcgencmd measure_temp | tr -d temp=\'C)
    echo ${temperature}
}

# Get the current time
function timestamp(){
    time=$(date +%s)
    echo ${time}
}

echo ${name},$(timestamp),$(temperature)

curl -X GET "${api_url}?name=${name}-${i}&timestamp=$(timestamp)&temperature=$(temperature)"
Enter fullscreen mode Exit fullscreen mode

Make sure to change the URL to your AdonisJS API. If you are running this on the same Raspbery Pi, you can leave it as localhost, if not you could use the IP of the device that you are running the API on.

Save the script as temperature.sh and make it executable:

chmod +x temperature.sh
Enter fullscreen mode Exit fullscreen mode

Then edit your crontab:

sudo crontab -e
Enter fullscreen mode Exit fullscreen mode

Add the following line to your crontab:

* * * * * /home/pi/temperature.sh
Enter fullscreen mode Exit fullscreen mode

This will run the script every minute and send the data to the API.

Conclusion

You can find the code for this tutorial here:

AdonisJS API - Raspberry Pi Temperature

As the second part of this tutorial, we will use Materialize to run streaming SQL queries on the data collected by the API. We are going to build the following setup:

Materialize - Raspberry Pi Temperature Sensors Demo

If you want to learn more about AdonisJS I could suggest checking out this tutorial here:

ะขะพ learn more about Materialize make sure to check out this tutorial here:

Learn Materialize by running streaming SQL on your nginx logs

Hope that this helps!

Top comments (2)

Collapse
 
peter279k profile image
peter279k

This temperature=$(/opt/vc/bin/vcgencmd measure_temp | tr -d temp=\'C) approach is to get the GPU temperature.

To get the CPU temperature, we can use the following command:

cpu=$(</sys/class/thermal/thermal_zone0/temp)
echo "$((cpu/1000)) c"
Enter fullscreen mode Exit fullscreen mode

And the reference is available here.

Collapse
 
bobbyiliev profile image
Bobby Iliev

Very good point! Thanks for mentioning this Peter!

Timeless DEV post...

How to write a kickass README

Arguably the single most important piece of documentation for any open source project is the README. A good README not only informs people what the project does and who it is for but also how they use and contribute to it.

If you write a README without sufficient explanation of what your project does or how people can use it then it pretty much defeats the purpose of being open source as other developers are less likely to engage with or contribute towards it.