There are a lot of great options for building mobile apps to control connected microcontrollers, like the Particle Photon. If you need an app but you want to avoid writing code, you can use Blynk to create apps in a building block style. You can also create native apps for iOS and Android if you’re familiar with the languages they support. As someone with a lot of experience in the front-end world, I wanted to build my app with JavaScript and Vue.js, so I chose NativeScript, which has Vue.js support via a community extension.
This project was part of a couple of demos I built to show-off the power of the RGB LED built into every Particle device. Check out this blog post to see both demos in action, or you can watch this video.
In this post, I’ll cover how I programmed my Photon to make the RGB LED drivable by my app, and how I used NativeScript, Vue.js and the Particle Device Cloud API to drive the LED via an iOS app.
Programming my Particle PhotonThere are two ways to control the onboard RGB LED: the RGB
class and the LEDStatus
class. The latter is the preferred approach. I cover both in my blog post at more length, and will explain the LEDStatus
class here. The LEDStatus
class allows you to specify a signal pattern, speed and color, and has plenty of overloads and enums to help you. Check out the docs for more info on everything you can do with the LEDStatus
class.
I started with declaring an instance of the LEDStatus
class as a global object, which ensures that it’s only activated once:
LEDStatus blinkLED;
Then, I created a function that takes red, green, blue and blink rate values and calls a few handy methods on my class instance.
void useLEDStatusClass(int red, int green, int blue, int blinkRate) {
// Set to false in case we're switching between modes
RGB.control(false);
LEDSpeed blinkSpeed = LED_SPEED_SLOW;
// Convert the R, G, B values to hex with some fancy bit shifting
long RGBHex = (red << 16) | (green << 8) | blue;
// Set the color, pattern and speed and activate our LED
blinkLED.setColor(RGBHex);
blinkLED.setPattern(LED_PATTERN_BLINK);
blinkLED.setSpeed((LEDSpeed)blinkRate);
if (!blinkLED.isActive()) blinkLED.setActive(true);
}
There are a few notable bits worth explaining here. First, the setColor method needs a single hexadecimal value that represents the RGB color, and the mobile app provides integers. Thankfully, it’s easy to convert those three values to hex with a bit of bit-shifting.
unsigned long RGBHex = (red << 16) | (green << 8) | blue;
Basically, I’m taking each int and shifting (<<
) its binary value by a certain number of bits, 16 in the case of red and 8, for green. Then using a bitwise OR (|
) operation, I can combine all three into a single value through masking. Once I have that value, I can call the setColor
, setPattern
and setSpeed
methods. Finally, I’ll set the LEDStatus
class to active if its not already.
With that function in hand, I need to expose the red, green, blue and blink rate values to the outside world so that my mobile app can control them. With Particle, this is as simple as creating cloud functions that allow an external app to provide a value for each. I created functions to control each separately that look like this:
int setRed(String val) {
redValue = val.toInt();
return 1;
}
The other functions follow a similar approach. You can see them all in the source below.
Once I flash the code to my Photon, I’m ready to build my mobile app!
Building the Mobile AppNativeScript is a popular open source framework for building truly native apps (meaning not hybrid) using JavaScript. It’s similar to React Native, but is unique in that it works with vanilla JavaScript, Angular, Typescript and — through community support — Vue.js.
To get started with NativeScript, check out the website, where you can explore the tool using an online interactive playground, or install the CLI tools locally. Once you have NativeScript up and running, you can head over to the NativeScript Vue website to install Vue and use the template for creating NativeScript Vue apps to scaffold out a new app.
With Vue, I can create components with a .vue
extension to represent screens in my mobile app UI. I’ll create one named RGBController.vue
. Since this isn’t a web app, I won’t be writing HTML markup to represent the UI. Instead, NativeScript provides an easy-to-understand, HTML-like XML syntax to represent things like Sliders, labels and buttons.
<template>
<Page class="page">
<ActionBar class="action-bar" title="RGB Commander">
</ActionBar>
<StackLayout class="rgb-main">
<Label class="body"
textWrap=true
text="Adjust the sliders below to control the RGB LED of your device"/>
<Slider id="redSlider" minValue="0" maxValue="255"
v-model="redAmount" @valueChange="onRedChanged" />
<Slider id="greenSlider" minValue="0" maxValue="255"
v-model="greenAmount" @valueChange="onGreenChanged" />
<Slider id="blueSlider" minValue="0" maxValue="255"
v-model="blueAmount" @valueChange="onBlueChanged" />
<GridLayout columns="100, 115" rows="80">
<Label row="0" col="0" text="Blink Speed" />
<ListPicker row="0" col="1" :items="blinkSpeeds"
v-model="currentBlinkSpeed" @selectedIndexChange="onSpeedChanged" />
</GridLayout>
<GridLayout columns="100, 115" rows="80">
<Label row="0" col="0" text="Take Control" />
<Switch row="0" col="1" id="controlRGBLED"
v-model="isControlling" @checkedChange="onControlChanged" />
</GridLayout>
<GridLayout columns="100, 115" rows="80">
<Label row="0" col="0" text="RAINBOWS!" />
<Switch row="0" col="1" id="signaldevice"
v-model="isSignaling" @checkedChange="onSignalChanged" />
</GridLayout>
</StackLayout>
</Page>
</template>
In addition to using a clean, HTML-like syntax for UI in NativeScript apps, you can use CSS to style your components. You can do this by adding a style block to your component file, referencing components by name, class, or type and using standard CSS rules to style your UI.
<style scoped>
.rgb-main {
margin: 20;
}
Slider {
margin: 10;
}
#redSlider {
background-color: red;
}
#greenSlider {
background-color: green;
}
</style>
Assuming you created your NativeScript Vue app using the template, the UI is all you need to see something working in the simulator or on your device. For example, I can build and run my app with the command:
npm run watch:ios
And here’s what I see in the simulator or on my phone.
In addition to the XML layout, you probably noticed a few interesting pieces of syntax in the snippet above. The references to v-model
create a two-way binding so that updates to the underlying data are tracked and always up-to-date in the UI, and vice versa. The other piece of syntax (the attributes that start with @
) tells NativeScript to call a function when the UI component value changes. Both reference JavaScript code I created via a script block in my component.
<script>
export default {
data() {
return {
blinkSpeeds: ['Slow', 'Normal', 'Fast'],
currentBlinkSpeed: 0,
redAmount: 80,
blueAmount: 65,
greenAmount: 44,
isControlling: false,
isSignaling: false
};
},
methods: {
onRedChanged: function(args) {
callParticleAPI('setRed', this.redAmount);
},
onGreenChanged: function(args) {
callParticleAPI('setGreen', this.greenAmount);
},
onBlueChanged: function(args) {
callParticleAPI('setBlue', this.blueAmount);
},
onSpeedChanged: function(args) {
callParticleAPI('setBlinkRate', this.currentBlinkSpeed);
},
onControlChanged: function(args) {
callParticleAPI('toggleCtrl', this.isControlling);
},
onSignalChanged: function(args) {
callParticleAPI('', this.isSignaling, 'PUT');
}
}
};
</script>
Each method responds to a change in a slider value, or the toggling of a switch. It does so by calling an endpoint on the Particle Device Cloud API through a shared function I created to minimize some of the duplication in my app.
const callParticleAPI = (endpoint, value, type = 'POST') => {
const baseUrl = 'https://api.particle.io/v1/devices/<device id>';
const token = ‘<your token here>’;
axios({
method: type,
url: `${baseUrl}/${endpoint}?access_token=${token}`,
data: `{ "arg": "${value}" }`,
headers: { 'content-type': 'application/json' }
}).catch(error => {
console.error(error);
});
};
I’m using the Axios library to call each function via its unique endpoint, also passing along the respective value as an argument. With everything built, I can run npm run watch:ios
, see my app in action and control the RGB LED on my device.
There are lots of ways to build mobile apps that interact with Particle and other IoT devices. If you’re a JavaScript aficionado, I recommend giving NativeScript a try. With support for Angular, TypeScript and Vue.js, it's a great option for jumping into cross-platform mobile app development.
Comments