Have ideas to improve npm?Join in the discussion! »

    nobel

    0.3.0 • Public • Published

    nobel

    NPM version NPM Downloads

    Nobel (code-generator) creates a REST API for your Arduino board, based on a SWAGGER or RAML definition.

    Description

    Nobel scaffolds an Arduino application that exposes a REST API. Then, you can write the logic for interacting with your physical devices inside methods that will be executed when the corresponding URL is invoked.

    Example

    Considering the following Swagger code:

    swagger: "2.0"
    info:
      version: "0.0.1"
      title: NobelTestingAPI
    paths:
      /servo:
        post:
          description: |
            Moves the servo to the specified angle.
          parameters:
            name: angle 
              in: body
              description: angle object
              required: true
              schema:
                $ref: '#/definitions/Angle'
          responses:
            200:
              description: Successful response
              schema:
                $ref: '#/definitions/Angle'
        put:
          description: |
            Moves the servo buy Adding the specified angle (could be negative)
          parameters:
            name: angle 
              in: body
              description: angle object
              required: true
              schema:
                $ref: '#/definitions/Angle'
          responses:
            200:
              description: Successful response
              schema:
                $ref: '#/definitions/Angle'
        get:
          description: |
           Returns the current servo angle
          responses:
            200:
              description: Successful response
              schema:
                $ref: '#/definitions/Angle'
    definitions:
      Angle:
        description: Task object
        properties:
          angle:
            type: integer
            description: task object name
        required:
          - angle

    or the following RAML code

    #%RAML 0.8 
    title: NobelTestingAPI
    /servo:
      post:
        description: |
          Moves the servo to the specified angle.
        body:
          application/json:
            example: |
              {"angle": 71}
      put:
        description: |
          Moves the servo buy Adding the specified angle (could be negative)
        body:
          application/json:
            example: |
              {"angle": -10}
      get:
        description: |
          Returns the current servo angle
        responses:
          200:
            body:
              application/json:
                example: |
                  { "angle": 71 }

    Nobel generates a project with several files (following the Arduino specs for splitting a program). One of the files contains the Handlers, where you can write your own code. Associated to the RAML example:

    // Handlers 
     
    void servoHandler(WebServer &server, WebServer::ConnectionType verb, String uriParams, String queryParams) {
      switch (verb)
        {
     
        case WebServer::POST:
            server.httpSuccess();
            break;
     
        case WebServer::PUT:
            server.httpSuccess();
            break;
     
        case WebServer::GET:
            server.httpSuccess();
            break;
     
        default:
            server.httpFail();
        }
    }
     

    Installation

    Pre-requisites

    • NodeJS
    • NPM
    • Arduino development environment (This is not needed to run Nobel, but will be needed to make something useful with it).

    Install Nobel

    Installing Nobel is really simple.

    • Open a terminal/command line
    • type npm install -g nobel

    ... That's all you need.

    Usage

    Nobel is a Command Line Interface (CLI), which means that you will be executing it from a terminal/command line (I don't see a clear value on building a GUI for this).

    nobel -s [your_swagger_or_raml_file] -n [your_project_name]

    The line above shows the minimum parameters set you need to specify in order to scaffold an Arduino Application using Nobel. The result is:

    .
    └── myArduinoRobot
        ├── A_functions.ino
        ├── B_definitions.ino
        ├── C_handlers.ino
        ├── D_initialization.ino
        └── myArduinoRobot.ino

    You can find a description of each file in the following sections.

    Arguments

    Argument Required Description
    -s --source YES The Swagger or RAML file describing the API to implement on the Arduino program.
    -n --name YES The name of your project. Nobel will create a folder with this name, and one main project file inside named like this.
    -t --target NO The target directory where the project will be placed. If not specified, the project will be created on the folder where you are running Nobel.
    -il --installLibraries NO If specified, it installs the required Arduino libraries on the folder you specify. Click here to figure out where this folder is.
    -h --help NO Really? Yes, the user manual will be printed in the terminal/command line

    Once your app is there

    • You just need to open the project with the Arduino IDE (you can use your preferred one, but all this project has been made using the Official Arduino IDE). Important: If you are installing libraries, you will need to restart the Arduino IDE for those to be recognized (I can't do anything about it. Arduino IDE has some tricky parts. Get over it ;)).
    • Compile: If it's not working, something is wrong. Nobel is thought to scaffold a right-away working application (that won't do anything until you put your code on it). If anything goes wrong at anytime, create an issue
    • Put your own code!

    Files

    Coding in Arduino is really a lot of fun (despite of/becouse of/but/your choice) it presents some challenges. The main one here: Memory. There could be better ways of implementing the code Nobel creates, but I haven't found it yet (feel free of making your own suggestions. That won't hurt my feelings at all). Because of this, I needed to hardcode some values (yes, shame on me). You only need to worry about it if you need to manually add new resources. If not:

    • [your_project_name].ino: Main file. It only includes the libraries required by all of the other files. No need to touch at all.
    • A_functions.ino: Contains functions that will be used by the application to parse and route the URLs to the proper handlers (It is not that simple to put these into a library, thanks for asking).
    • B_definitions.ino: You will probably need to define some variables that you will use later in a global way. Even though you will probably initialize these on D_initialization.ino, you will use these in C_Handlers.ino, and, since Arduino concatenates in alphabetical order (excepting for the main file), not declaring the variables here, will result in errors.
    • C_Handlers.ino: You will write your logic here!!! A function will be generated per each result, and a SWITCH CASE inside of it, to determine the HTTP verb being invoked (this could be changing in the future, supporting other code styles. For Example: One function per Resource-Verb combination)
    • D_Initialization.ino: You need a place where everything starts. The setup and loop functions (which Arduino calls when booting) are placed here for you to complete as needed.

    Seeing the example is the best way of understanding how to code. Note: The scaffolded application will use DHCP. You can find the code to change this in the D_Initialization.ino file (setup function)

    Manually Adding new resources

    Sorry! I'll try to improve this, but this far, for an MVP, it is what it is!

    Don't bother reading this if you are NOT manually adding new resources.

    A_Functions.ino

    • Since sizeof function is not properly working, I needed to hardcode the resources array length on the very first lin: const byte SIZE_RESOURCES = 5; // constant until resolve what's going on with sizeof. Change it if you manually add or remove resources.
    • Since I'm using a FLASH_ARRAY (basically for placing big amounts of data in the program memory instead of the variables space), I can't freely use an array and pass pointers as parameters or return these. So, the array needs to be defined inside the function that will use it. ugh indeed.
     FLASH_STRING_ARRAY(resources,
          PSTR("/resourceA/"),
          PSTR("/resourceB/"),
          PSTR("/resourceC/"),
          PSTR("/resourceD/"),
          PSTR("/resourceE/")
          // add more if needed 
      );

    When adding one, you will also need to add the proper Handler in D_Initialization.ino

    D_Initialization.ino

    • Add a new handler:
    void registerHandlers() {
    handlers[0].method = &resourceAHandler;
    handlers[1].method = &resourceBlHandler;
    handlers[2].method = &resourceCHandler;
    handlers[3].method = &resourceDHandler;
    handlers[4].method = &resourceEHandler;
    }

    Important: The order is the way of matching a resource with it handler!!! It's much more efficient and doable than trying to do some kind of "reflection". So, make sure of adding the handlers in the same order than the resources on the previous step. Now, a handler, is basically a pointer to a function (Nobel places these functions on C_Handlers.ino). So, if you have &resourceAHandler, you will need:

    C_Handlers.ino

    void resourceAHandler(WebServer &server, WebServer::ConnectionType verb, String uriParams, String queryParams) {
      // Your code here (generally, start with a switch) 
    }
     

    Hands On Example

    Create the API definition file.

    You can choose to define your API by using Swagger or RAML. It's up to you ;)

    Swagger

    swagger: "2.0"
    info:
      version: "0.0.1"
      title: NobelExampleAPI
    paths:
      /led:
        post:
          description: |
            Turns the light on
          responses:
            200:
              description: Successful response
        delete:
          description: |
            Turns the light off
          responses:
            200:
              description: Successful response

    RAML

    #%RAML 0.8 
    title: NobelExampleAPI
    /led:
      post:
        description: Turns the light on
      delete:
        description: Turns the light off

    and save it (the example asumes that it will be called swaggerExample.yaml or ramlExample.raml)

    Scaffold the application

    For Swagger

    nobel -s swaggerExample.yaml -n ledController
    

    For RAML

    nobel -s ramlExample.raml -n ledController
    

    or

    For Swagger

    nobel -s nobelExample.raml -n ledController -il [path_to_your_arduino_libraries_directory]
    

    For RAML

    nobel -s nobelExample.raml -n ledController -il [path_to_your_arduino_libraries_directory]
    

    if you haven't ever installed the required libraries.

    Put your own code

    • Open the IDE.

    B_Definitions.ino

    Add this line:

    byte ledPin = 9// It's a good practice to use a variable to semantically represent your Arduino output. 

    D_Initialization.ino

    Add this line in the void setup() function

    pinMode(ledPin, OUTPUT); // We are specifying that the pin 9 will be used as an output. 

    The function code should look like this:

    void setup() {
      Serial.begin(9600);
      pinMode(ledPin, OUTPUT);
      ethStart();
      registerHandlers();
      webserver.setFailureCommand(&dispatch);
      webserver.begin();
    }

    C_Handlers

    digitalWrite(ledPin, HIGH); // Turns the led on 
    digitalWrite(ledPin, LOW); // Turns the led off 

    Let's add both calls to the POST and DELETE methods, and the final code should look like this:

    void ledHandler(WebServer &server, WebServer::ConnectionType verb, String uriParams, String queryParams) {
      switch (verb)
        {
     
        case WebServer::POST:
            digitalWrite(ledPin, HIGH);
            server.httpSuccess();
            break;
     
        case WebServer::DELETE:
            digitalWrite(ledPin, LOW);
            server.httpSuccess();
            break;
     
        default:
            server.httpFail();
        }
    }

    Compile and deploy

    By default, the generated code will make Arduino take an IP by using DHCP. Open a Serial Monitor to see the assigned IP. In order to test the app, use any program capable of sending HTTP messages via network. Postman extension for chrome is my favorite.

    • POST http://[the_ip_DHCP_assigned]/led (should turn the led ON).
    • DELETE http://[the_ip_DHCP_assigned]/led (should turn the led OFF).

    Contributing to this code

    • If you are using Atom, please make sure that the following configuration is correct. If not, it will add or remove "\n" and spaces characters from your templates making the unit tests to fail alt text

    Credits

    • Without any doubt, I would have quit the idea of creating this project if I didn't find a good HTTP server for Arduino. So, it wouldn't be possible at all without Webduino by sirleech. Thanks to him and his amazing job.
    • 2 KB for all my variables? Where to put the resources array? In the Program Memory!!! That is possible thanks to the Flash module by mikalhart.

    Install

    npm i nobel

    DownloadsWeekly Downloads

    4

    Version

    0.3.0

    License

    none

    Last publish

    Collaborators

    • avatar