MuleSoft IoT - Make Max Dance!

darren_ingram

Darren Ingram

Posted on November 26, 2020

MuleSoft IoT - Make Max Dance!

IoT and MuleSoft

Alt Text

Introduction

I have just started learning how to use MuleSoft at work, where we will be using it for integration between our various systems. I completed the Anypoint Platform Development: Fundamentals (Mule 4) self-paced training and then started on this project to help get some hands-on-experience.
So, disclaimer: I don't really know what I'm doing, so don't rely on this for anything :)

Muley (Max) the Mule

After attending a MuleSoft user group event, the organizer sent me a little Max the Mule squishy. My colleagues and I renamed him to Muley, and during our project planning, whenever we noted a potential use for MuleSoft Anypoint, I'd make Muley do a little dance. I found myself doing this a lot, and wondered "could I automate this?"

Project Goal

  • Create a IoT device that controls a motor attached to Muley to make him "dance"
  • Control the IoT device from a Web API
  • Have some sort of trigger to make Muley dance

Implementation Overview

IoT

A few years ago I started tinkering with Arduino and other hobby electronics boards. I haven't touched any of that for a couple of years, but I went out to the garage and found my old box of goodies. In there was an unopened Particle Photon WiFi Development Board. This allows you to build a little device and then expose functions over HTTP.

  • I connected a servo motor to the Photon board and attached it to the bottom of Muley (using some very professional sticks and tape from my 8-year-old's art supplies!)
    Muley attached to servo

  • I also found a LED light ring in my box of tricks, so connected that, just because I could. I later made up a reason to use it...
    Muley and lights connected to Photon

  • I wrote the code to make the servo move around and to flash the lights. These were exposed as functions on the Photon. I used the Particle Web IDE to do this (as described in the getting started information for the Photon).

  • The full code is available on GitHub:

    but here are the important parts that register the dance and setMood functions as cloud functions:
    bool setMoodSuccess = Particle.function("setMood", setMood); 
    bool danceSuccess = Particle.function("dance", dance);
Enter fullscreen mode Exit fullscreen mode

Particle cloud functions

Particle Cloud Functions API

Using the documentation, I was able to figure out how to call the Particle API to trigger my functions using Postman. The screenshot below shows how I just had to POST to the function name and include my device's unique ID and an access token (both from my Particle account):
Postman Particle API calls
That call resulted in Muley doing a little dance (and was a very exciting moment for me :))

MuleSoft Anypoint

My next step was to use Anypoint to design and implement a System API to make calls to my cloud functions.

System API Design

I used the Anypoint Platform API Designer to build up my RAML API specification.

While designing the System API, I quickly noticed that both of the cloud functions had the same request and response types, so I created API Fragments for these to allow me to reuse them. I also had a ton of fun working on this project, so I am very likely to want to use these data types again in future tinkerings!

Alt Text

The screenshot below shows how I was able to reference the reusable API Fragments that I published to my own Exchange:
Alt Text

Muley IoT System API

#%RAML 1.0
baseUri: https://anypoint.mulesoft.com/mocking/api/v1/links/3bd0afd8-1b41-43b1-a034-2842d75a2211/ # 
title: iot-muley-sapi
description: System API to call Particle Photon IoT Muley project

protocols:
  - HTTP
  - HTTPS
/mood:
  post:
    description: Set the lighting based on mood
    body:
      application/json:
        type: !include ../exchange_modules/f355980a-ec00-4935-8944-f5f13d8b08ba/particle-iot-function-request/1.0.2/particle-iot-function-request-data-type.raml
        example: !include examples/iot-muley-function-request-example.json

    responses:
      200:
        body:
          application/json:
            type: !include ../exchange_modules/f355980a-ec00-4935-8944-f5f13d8b08ba/particle-iot-function-response-data-type/1.0.2/particle-iot-function-response-data-type.raml
            example: !include examples/iot-muley-function-response-example.json

/dance:
  post:
    description: Make Muley dance
    body:
      application/json:
        type: !include ../exchange_modules/f355980a-ec00-4935-8944-f5f13d8b08ba/particle-iot-function-request/1.0.2/particle-iot-function-request-data-type.raml
        example: !include examples/iot-muley-function-request-example.json
    responses:
      200:
        body:
          application/json:
            type: !include ../exchange_modules/f355980a-ec00-4935-8944-f5f13d8b08ba/particle-iot-function-response-data-type/1.0.2/particle-iot-function-response-data-type.raml
            example: !include examples/iot-muley-function-response-example.json


Enter fullscreen mode Exit fullscreen mode

Particle IoT Function Request

#%RAML 1.0 DataType
displayName: Particle IoT Function Request
description: Representation of the information required when making calls to particle.io functions
properties:
  accessToken:
    type: string
    required: true
    displayName: Access Token
    example: 123456ABC

  arg:
    type: string
    required: false
    displayName: Argument
    example: "extra data"
example: !include particle-iot-function-request-example.json
Enter fullscreen mode Exit fullscreen mode

Particle IoT Function Response

#%RAML 1.0 DataType
displayName: Particle IoT Function Response
description: Representation of the information returned from a particle.io function call
properties:
  id:
    type: string
    displayName: Id
    example: "30002e000347353137323334"

  connected:
    type: boolean
    displayName: Connected
    example: true

  return-value:
    type: integer
    displayName: "Return value"
    example: 12
example: !include particle-iot-reponse-example.json

Enter fullscreen mode Exit fullscreen mode

System API Implementation

In Anypoint Studio I pulled in my System API RAML from Exchange. It kindly scaffolded it all out for me and I went to work implementing my flows to make HTTP POST requests over to the Particle REST API.
I included a config.yaml file and used the Configuration Properties to connect to this:

config.yaml Text

This allowed me to access these configuration properties within my implementation flows:
System API Implementation Flow

Since I created the particle API request and response types, I didn't need to do any additional DataWeave mapping before making the http request.

Integration Process Flow Implementation

So now I had a Muley IoT System API to handle communications to my IoT device.
Next I needed to create a Flow that handled some trigger and made calls the System API.
I decided to use an email as the trigger. This would allow me to send emails to muley.max.mule@gmail.com and have Muley dance.

I was able to pull in the Muley IoT System API from Exchange and get the generated connectors (which was pretty cool!)

Because I wanted to use this experience to learn, I decided to add another integration in at this point. I wanted to make Muley react to the content of the email. For this I found the Monkey Learn Sentiment Analysis API. I setup a developer account which gave me an authorization token and a model Id. I used these in an HTTP Request connector to pass through the body of the email message and get back a sentiment (of "Positive", "Negative", "Neutral" - along with a bunch of other stuff like the certainty rating).

So my flow needed to do something like this:

  • Trigger on a new email to muley.max.mule@gmail.com
  • Pass the content of the email to the Sentiment Analysis API
  • Map the result into the request required for the Muley IoT System API Set Mood call
  • Call the Muley IoT System API Set Mood method
  • Call the Muley IoT System API Dance method

Here is how the flow looks:
Muley flow

As you can see, I went a bit extra and made use of the JMS Queue connector. When a new email arrives, I simply published to a JMS queue. I then made the rest of my integration flow trigger on a new message to the queue. I really did this just to practice using JMS queues, but it also sets me up to make "other things" happen when Muley gets an email, without having to keep updating this flow.

Here is how I mapped from the email message payload into the request expected by the sentiment analysis API:
Dataweave mapping to api call

And another example of Dataweave, this time mapping from the sentiment analysis response into request format for our System API:

%dw 2.0
output application/json

---
{
    "mood": lower(payload[0].classifications[0].tag_name)
}
Enter fullscreen mode Exit fullscreen mode

The Result

Here is the end result of this Hackathon project. If we send a positive email to Muley, then the lights flash green while he dances:
Alt Text

But if we send a negative email then the lights flash red:
Alt Text

Source Code

The IoT Particle source code and Anypoint .jar files are available on GitHub:

💖 💪 🙅 🚩
darren_ingram
Darren Ingram

Posted on November 26, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

MuleSoft IoT - Make Max Dance!
mulesofthackathon MuleSoft IoT - Make Max Dance!

November 26, 2020