Project tutorial

Connected Rollout Banner and Firework © CC BY-NC-SA

Build an amazing Connected Rollout Banner with Arduino and control it with your mobile phone thanks to an AngularJS application.

  • 822 views
  • 0 comments
  • 3 respects

Components and supplies

Apps and online services

About this project

Visit us: https://handson.io

The original article: https://handson.io/prototyping-at-google/

We like to think outside of the box, and when "La Feweb" asked us to give a technical talk about AngularJS, we immediately went crazy !

AngularJS's dependency injection architecture

We proposed to give a presentation about the dependency injection system of AngularJS and the given opportunity to mock REST communications in order to master the behaviour of a web application before wiring it to a production environment. This software design pattern allows anyone to leverage the power and flexibility of a decoupled architecture, especially when it comes to be able to test at different levels (Unit testing, UI testing and E2E testing).

Why is it an important matter ?

If you want to understand more in depth this pattern, we suggest you to read Dependency Injection at Wikipedia. The short answer is : it helps developers to produce maintainable code and greatly increase the quality of an application while reducing the chances to have to fire-fight an incident in production.

Let's go crazy!

Maurizio Pedriale,Benoit Toussaint and I all participated to this project. Benoit and I decided to go crazy and started to think about the creative way to demonstrate why it is important to test properly an application before going into mission critical production environment where everything can blow-up if not properly controlled.

As always it started with a discussion and I sketched the early ideas !

"What about a connected banner and a firework (electric air compressed confetti canon), the demo application will be coded live and will act as a remote controller to fire all the things !"

For sure the picture looked amazing in our imagination and we thought that was a fun way to entertain people during an nice event afterwork while still remain serious about software engineering practices. The tone was set, we had to demontrate the proper way to test and connect a demo application to a critical component (the banner and the firework must be triggered the right way at the right moment and give back the proper result to the remote in order to update the UI).

To be honest it was easier said (or sketched) than done and the road the make it concrete and working in the real world was a bit of a challenge !

To validate your most critical hypothesis first, you have. Yes, hmmm.

It is true that it was the very first time we decided to build such a big banner that should be WiFi enabled, exposing a REST api and being able to be deployed in an unknown environement also driving the circuitry to fire up an electric air compressed confetti canon.

And such a goal comes with a lot of uncertainty. Our immediate response in such situation is to quickly build a prototype to validate the first hypothesis about how everything should integrate together, let's call it our first integration test !

Before spending too much money for a colourful rollup tailored for the best propaganda, it was absolutely necessary to try out something a little bit less fancy, a rollup window store, some pine timber, recycled cardboard and a bit of super-glue.

Re-engineering and new hypotheses validation

It was time to consolidate the learning and start building a much stronger and better designed version (with the help of the kids :) including the wiring to the air compressed canon.

The second version of the structure was built, the aREST arduino library used to publish a REST api for the Arduino UNO R3 fully loaded with a new super power thanks to the official Arduino WiFi shield.

The arduino exposed one endpoint through a dedicated Wifi network in order to trigger a servo motor used the control the opening of the banner's lid.

If you want to light-up the room with sparkling paper confetti, you should probably try out first with spare canon in order to understand its inner mechanic and make sure another less critical hypothesis (but important) would not go wild without any validation red-hot stamp.

An air compressed confetti canon is quite simple, the steel cap maintaining the pressure under a rubber joint is maintained by a single "synthetic" string. When the proper voltage is applied, the string melts and the air-blow create the magic moment.

We had to rethink and simulate again the schematic to make sure everything was working properly under 18v instead of 9v.

The AngularJS demo application

The UI of the demo application is quite simple (and mobile responsive), the app display the status online/offline of the connected banner (the server) and offers a button to fire the all things after which the user gets a notification that everything went as expected (the banner could be out of sight, who knows ;).

The structure of the app

The code of the application is hosted on GitHub and the structure it follows is called a Single-page application (SPA).

The file index.html is bound to the main module fireworksRC defined in the script app.js and declares an element <div ng-view></div> hosting the rendered template associated with routes managed by the $route service.

<!DOCTYPE html>
<html lang="en" ng-app="fireworksRC" class="no-js">
...
<body>
   <div ng-view></div>
   ...
   <script src="app.js"></script>
   <script src="home/home.js"></script>
</body>
</html>

There is only one route, the home route is activated when the user navigates to http://webserver.local/ or http://webserver.local/#/home which is rendered dynamically within the ngView directive.

Replace webserver.local by the address of the machine serving the files (run npm start). For the production version, checkout the final-version branch and dev-version for the development one.

The top level module fireworksRC contains only a configuration for the default route to be forwarded to the home route. The module definition include the required dependencies (ngRoute, fireworksRC.home) injected at run time by the dependency injector.

angular.module('fireworksRC', [
 'ngRoute',
 'fireworksRC.home'
]).
config(['$routeProvider', function($routeProvider) {
 $routeProvider.otherwise({redirectTo: '/home'});
}]); 

This is the only responsability of the main module, everything else is delegated to another module fireworksRC.home defined in the script home/home.js

The fireworksRC.home module

angular.module('fireworksRC.home', ['ngRoute'])
.constant('FireworksBE_URL', 'http://192.168.1.100')
.config(['$routeProvider', function($routeProvider) {
 $routeProvider.when('/home', {
   templateUrl: 'home/home.html',
   controllerAs : 'home',
   controller: 'HomeCtrl'
 });
}])
.controller('HomeCtrl', ['FireworksBE_URL','$http', function(FireworksBE_URL, $http) {
  ...
}]);

The module defines a constant, a config and a controller. The constant FireworksBE_URL is used to locate the address of the connected banner exposing REST endpoints a.k.a. the server.

The config instructs the provider $routeProvider to render the html template located at home/home.html and associate the controller HomeCtrl to the view as home.

Finally, the declaration of the controller named HomeCtrl contains the required dependencies, the constant FireworksBE_URL and the core AngularJS service $httpwhich again will injected at run time by the dependency injector.

The $http service

This is the service we are going to use at the controller level to interract with the connected banner exposing a REST api.

The $http service is a core AngularJS service that facilitates communication with the remote HTTP servers via the browser's XMLHttpRequest object or via JSONP.

When a system is designed to be tested...

Then obviously it is easier to test it ! The very first reflex when it comes to build a system is cooking some unit tests ! We use Karma to test our main controller HomeCtrl.

In this case we use Jasmine to describe the unit test suite of the fireworksRC.homemodule which mainly target the HomeCtrl controller.

Here are the main behaviours of the HomeCtrl :

  • It should detect if the remote system is unreachable
  • It should detect if the remote system is available
  • It should not fire if already launched
  • It should fire if not already launched

Here is the main structure of the test suite located in the file home/home_test.js

 describe('fireworksRC.home module', function() {
 describe('An application controller', function(){
   it('should detect if the remote system is unreachable', function() {
   });
   it('should detect if the remote system is available', function() {
   });
   it('should not fire if already launched', function () {
   });
   it('should fire if not already launched', function() {
   });
 });
});

$httpBackend, the real, the fake and the supercharged fake!

As mentionned earlier in this article, we chose $http service provided in the AngularJS's module ng as a way to communicate easily with the connected banner exposing the REST api.

The real one

This service internally depends on ng.$httpBackend that delegates to XMLHttpRequest object or JSONP and deals with browser incompatibilities.

The fake one

When it comes to unit test the controller in order to asses its behaviour, the dependency injector is essential. We are going to use it to inject a fake version of the $httpBackend service provided by AngularJS itself within the module ngMock.

When an AngularJS application needs some data from a server, it calls the $http service, which sends the request to a real server using $httpBackend service. With dependency injection, it is easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify the requests and respond with some testing data without sending a request to a real server.

The module ngMock is included in angular-mocks.js. This dependency is managed via Bower which install the required script in app\bower_components\angular-mocks as configured in the bower configuration file .bowerrc along with bower.json.

The module ngMock must be provided to the test suite runner in order to be properly used. This is done by referencing this dependency app\bower_components\angular-mocks\angular-mocks.js in the runner configuration file karma.conf.js.

 module.exports = function(config){
 config.set({
   basePath : './',
   files : [
     'app/bower_components/angular/angular.js',
     'app/bower_components/angular-route/angular-route.js',
     'app/bower_components/angular-mocks/angular-mocks.js',
     'app/home/**/*.js'
   ],
...

The supercharged fake

There is a second mock version of the $httpBackend service located under the module ngMockE2E. This one is particulary useful for integration tests where you want to simulate only certain responses.

This implementation can be used to respond with static or dynamic responses via the when api and its shortcuts (whenGET, whenPOST, etc) and optionally pass through requests to the real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch templates from a webserver).

This strategy proove to be useful when combined with an automated continous integration pipeline where you inject a new module that overrides the main module for example. This injection has nothing to do with a dependency injection but is more a text replacement within the files themselves.

You can find an example of this one in the dev-version branch. The index.html now use fireworksRC_DEV as the main module.

 ...
<html lang="en" ng-app="fireworksRC_DEV" class="no-js">
...
<body>
   ...
   <script src="bower_components/angular-mocks/angular-mocks.js"></script>
   ...
</body>
</html>

This one overrides the fireworksRC module in app.js and this time uses properly the module ngMockE2E located in angular-mocks.js properly referenced in index.html.

...
angular.module('fireworksRC_DEV', [
 'fireworksRC',
 'ngMockE2E'
]).
run(['$httpBackend', function($httpBackend) {
 $httpBackend.whenGET('http://192.168.1.100').respond({
   "id": "001",
   "name": "fireworks_system",
   "connected": true
 });
 $httpBackend.whenGET('http://192.168.1.100/fire?params=0').respond({
   "return_value" : 1});
 $httpBackend.whenGET('home/home.html').passThrough();
}]);

In this case, the connected banner will always seem available to the application and the firing will always result in a correct return value.

Deliberately using the injector

Let's flesh out the first unit test in home\home_test.js. We first define some variables that will host what we need to properly test our controller HomeCtrl within its module fireworksRC.home.

describe('fireworksRC.home module', function() {
 var SIMULATOR_URL = 'http://simulator.local';
 var VALID_HTTP_GET = {
   "id": "001",
   "name": "fireworks_system",
   "connected": true
 };
...

We will use SIMULATOR_URL to force the value of the constant FireworksBE_URL defined in the main module fireworksRC in app.js. We deliberately exluded the file app.jsfrom our test runner configuration (karma.conf.js) but we need this constant to be available in the scope of the controller HomeCtrl that we are going to instantiate in the test suite.

VALID_HTTP_GET will be used as a response when the controller HomeCtrl will call the connected banner (the server) to check its availability by issuing a GET request.

Jasmine provides the function beforeEach(function) that is executed before each spec in the describein which it is called. So we use it to set the value of the constant FireworksBE_URL to the one of SIMULATOR_URL.

 describe('fireworksRC.home module', function() {
     var SIMULATOR_URL = 'http://simulator.local';
     var VALID_HTTP_GET = {
       "id": "001",
       "name": "fireworks_system",
       "connected": true
 };
 beforeEach(module('fireworksRC.home',function($provide) {
   $provide.constant('FireworksBE_URL', SIMULATOR_URL);
 }));
...

We describe a new spec for the controller. This time we use the inject() function defined in the ngMock module to deliberately inject the required elements, the $httpBackend mock and the $controller service in order to instanciate our controller.

...
 describe('An application controller', function(){
   var ctrl, $httpBackend;
   beforeEach(inject(function(_$httpBackend_, $controller) {
     // init $httpBackend
      $httpBackend = _$httpBackend_;
     // init whenGET normal  VALID_HTTP_GET
     $httpBackend.whenGET(SIMULATOR_URL).respond(VALID_HTTP_GET);
     $httpBackend.whenGET(SIMULATOR_URL + '/fire/params=0').respond(VALID_FIRE_RESPONSE);
     ctrl = $controller('HomeCtrl');
   }));
...

The dependency injector is able to wrap the underscore around the required dependency in order to let us properly define the variable $httpBackend that is that way properly declared in the accessible scope of the controller and will host the ngMock.$httpBackend service. Remember $http uses $httpBackend as a dependency.

We hook up the GET requests to simulate the responses and we instanciate the controller.

The test setup is done, we can now write the first unit test that will evaluate the controller's behaviour.

   it('should detect if the remote system is available', function() {
     $httpBackend.expectGET(SIMULATOR_URL);
     $httpBackend.flush();
     expect(ctrl.connected).toBe(true);
   });

We expect the controller to initiate a request when it is instanciated in order to retreive the status of the server and update accordingly its property connected.

If you execute that test (npm test) it will fail until you provide the right code within the controller in home/home.js.

...
.controller('HomeCtrl', ['FireworksBE_URL','$http', function(FireworksBE_URL, $http) {
 var that = this;
 that.connected = false;
 var updateSystemData = function() {
 $http.get(FireworksBE_URL).
     success(function(data, status, headers, config) {
         if(status === 200 && data.connected === true) {
           that.connected = true;
         }
     }).
     error(function(data, status, headers, config) {
     });
 };
 updateSystemData();  
}]);

Conclusion

AngularJS is a very nice and well designed framework, it helps to create production ready application. On the other hand, the broad spectrum of IoT solutions allows you to create almost anything without being an expert in hardware.

Rapid prototyping implies to be able to see the end-result and design the right tests to validate your most critical hypotheses first.

We hope you enjoyed the show !

Alan

Code

Connected Banner - AngularJS/Arduino

Custom parts and enclosures

Banner
Plan - Part 1/5
Handson banner iot 1 qekszhsu6z
Banner
Plan - Part 2/5
Handson banner iot 2 dmjwkqzofm
Banner
Plan - Part 3/5
Handson banner iot 3 v1jilh6oye
Banner
Plan - Part 4/5
Handson banner iot 4 okxogbmaks
Banner
Plan - Part 5/5
Handson banner iot 5 aojnqez1wk

Schematics

Banner door control unit
Screen shot 2018 05 11 at 11 03 52 86iabzzham

Comments

Similar projects you might like

Joy Robot (Robô Da Alegria)

Project tutorial by Igor Fonseca Albuquerque

  • 2,319 views
  • 1 comment
  • 23 respects

IFTTT: The Phone Connected

by Uladzislau Bayouski

  • 3,434 views
  • 3 comments
  • 22 respects

Android App-Based Home Automation System Using IOT

Project tutorial by Team Autoshack

  • 24,849 views
  • 17 comments
  • 75 respects

Hacking Qualcomm (Quick Charge) QC 2.0/3.0 With ATtiny85

Project tutorial by Shahariar

  • 5,897 views
  • 11 comments
  • 40 respects

How to Use Modbus with Arduino

Project tutorial by hwhardsoft

  • 5,341 views
  • 2 comments
  • 14 respects

EnLight (Sunset) v1

Project tutorial by MicroBob

  • 1,882 views
  • 0 comments
  • 6 respects
Add projectSign up / Login