-
Mar 31, 2017
In this tutorial I show you how to package and deploy a simple Scala project to AWS Lambda. Then I show you how to add the lambda to API Gateway to run in response to GET requests.
Build the Scala Project
- Create the
build.sbt
file
- Write the main scala program,
src/main/scala/example/Main.scala
- In the terminal run
sbt
then in the sbt prompt run assembly
.
- This creates the jar which will be located at
target/scala-2.11/lambda-demo-assembly-1.0.jar
build.sbt
javacOptions ++= Seq("-source", "1.8", "-target", "1.8", "-Xlint")
lazy val root = (project in file(".")).
settings(
name := "lambda-demo",
version := "1.0",
scalaVersion := "2.11.4",
retrieveManaged := true,
libraryDependencies += "com.amazonaws" % "aws-lambda-java-core" % "1.0.0",
libraryDependencies += "com.amazonaws" % "aws-lambda-java-events" % "1.0.0"
)
mergeStrategy in assembly :=
{
case PathList("META-INF", xs @ _*) => MergeStrategy.discard
case x => MergeStrategy.first
}
src/main/scala/example/Main.scala
package example;
class Main {
import java.io.{InputStream, OutputStream, PrintStream}
def greeting(input: InputStream, output: OutputStream): Unit = {
val result = s"Yo!"
output.write(result.getBytes("UTF-8"))
}
}
Deploy the Scala Project to AWS Lambda
- Select Java 8
- Upload the jar which will be located at
/target/scala-2.11/lambda-demo-assembly-1.0.jar
- Test the Lambda Function from the AWS Lambda UI
At this point you could setup API Gateway to use the Scala Lambda in a similar way that I set it up for the node.js Lambda Function.
-
Mar 10, 2017
AWS has a great blog post about enabling CORS on API Gateway. I followed the AWS blog and CORS works well for most requests. The one thing it didn’t work for was secure request made from JavaScript.
JavaScript GET request
This code worked, but would not send cookies that were set for the example.com domain.
xhr.open('GET', 'https://subdomain.example.com/ab.json’, true);
xhr.withCredentials = true;
xhr.onreadystatechange= function() {
if (this.readyState!==4) return;
if (this.status!==200) return; // or whatever error handling you want
document.getElementById('ab').innerHTML= this.responseText;
};
xhr.send(null);
Chrome Browser Error
XMLHttpRequest cannot load https://subdomain.example.com/ab.json. The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'https://example.com' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
The method in the AWS blog works well but sets Access-Control-Allow-Origin to ‘*’ which is not compatible with secure requests. In the case that a cross origin request is made for an HTTPS resource the browser will throw an error if the Access-Control-Allow-Origin is not EXACTLY the same as the Origin, this includes requests from subdomains.
The solution is to pass the request Origin to Access-Control-Allow-Origin. Since I am using AWS Lambda functions with my API Gateway, I use the Lambda function to read the Origin, filter it and return it to API Gateway.
First I setup API Gateway to respond with the required headers.
Create the OPTIONS Method
Under Method Response add a 200 response.
Add the CORS headers to the 200 response.
Access-Control-Allow-Headers
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Allow-Methods
Under Integration Response add the values to each of the CORS headers
Access-Control-Allow-Headers 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with'
Access-Control-Allow-Origin '*'
Access-Control-Allow-Credentials 'true'
Access-Control-Allow-Methods 'GET'
Create or modify the GET Method
The GET method may already be present if you are adding CORS to an existing API
Under Method Response add a 200 response.
Add the CORS headers to the 200 response.
Access-Control-Allow-Headers
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Allow-Methods
Under Integration Response add the values to each of the CORS headers
Access-Control-Allow-Headers 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with'
Access-Control-Allow-Origin integration.response.body.Origin
Access-Control-Allow-Credentials 'true'
Access-Control-Allow-Methods 'GET'
Then in the Lambda Function associated with the GET method I send back the Origin. I do some simple filtering on the Origin and only pass it through if it is one of my subdomains.
index.js
‘use strict’
exports.handler = function (event, context, callback) {
...
const origin = filter(event.params.header.Origin);
const someHtml = “<h1>Title</h1>”;
…
callback(null, { html: someHtml, Origin:origin });
}
function filter(origin) {
var result = origin ? origin : '*';
if( result === “https://subdomain.example.com” ) {
// just a simple filter example, not to be used in production
return “https://subdomain.example.com”;
}
return '*';
}
Now when I visit example.com and make secure requests to subdomain.example.com cookies are sent without error or warning.
-
Feb 28, 2017
This is the fifth iteration of my tracking pixel experiment:
AWS Lambda function to return a tracking pixel
AWS Lambda Tracking Pixel with Elasticsearch
AWS Lambda, Kinesis Firehose and Elasticsearch
AWS Lambda Detect Android or iOS device node.js
In this tutorial I show you how to create a node.js project, include node_modules, zip the project and deploy the project to AWS Lambda.
I’m going to take the code from the first tutorial and use the node-cuid module to create a unique cookie id.
First I create an empty directory mkdir lambda-test
, then cd lambda-test
and install the aws-sdk and node-cuid modules.
mkdir lambda-test
cd lambda-test
npm init
npm install --save aws-sdk
npm install --save cuid
Once the modules are installed I can create index.js and copy this code to it
index.js
'use strict';
var cuid = require('cuid');
exports.handler = function(event, context, callback) {
const cookie_name = "_cl";
var date = new Date();
var cookieVal = cuid(); // Generate a random cookie string
var cookieString = cookie_name+"="+cookieVal;
var cookie = '';
if (event.hasOwnProperty('params') && event.params.hasOwnProperty('header') && event.params.header.hasOwnProperty('Cookie')) {
cookie = getCookie(cookie_name, event.params.header.Cookie);
if (!cookie) {
cookieString = cookie_name + "=" + cookieVal;
} else {
cookieString = cookie_name + "=" + cookie;
}
}
// Get Unix milliseconds at current time plus 365 days
date.setTime(+ date + (365 * 86400000)); //24 \* 60 \* 60 \* 100
cookieString = cookieString+"; "+"expires="+date.toGMTString()+";";
var imageHex = "\x42\x4d\x3c\x00\x00\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00"+
"\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00"+
"\x00\x00\x06\x00\x00\x00\x27\x00\x00\x00\x27\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00";
callback(null, { "body":imageHex, "Cookie":cookieString });
};
function getCookie(name, cookies) {
var value = "; " + cookies;
var parts = value.split("; " + name + "=");
if (parts.length >= 2) return parts.pop().split(";").shift();
}
At this point I could zip the node.js project and use the AWS GUI to upload and deploy. Instead of testing on AWS, I like to do a quick test on my local machine.
Install node-lambda globally. This module makes it easy to test AWS Lambda functions locally.
npm install -g node-lambda
Then run $ node-lambda setup
in the lambda-test directory
This will create 3 files: deploy.env, event.json, and context.json
Open and edit event.json
to match the test events you may already have defined for your AWS Lambda functions
event.json
{
"params": {
"path": "",
"querystring": "",
"header": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, sdch, br",
"Accept-Language": "en-US,en;q=0.8",
"Cache-Control": "no-cache"
}
},
"context": {
"http-method": "GET",
"resource-path": "/test.gif"
}
}
Save event.json and run $ node-lambda run
on the console. This will simulate a lambda test and use event.json as the input. If everything works you will see a success in the console and a print out of the pixel and cookie values.
example response from node-lambda run
Success:
{"body":"BM<\u0000\u0000\u0000\u0000\u0000\u0000\u00006\u0000\u0000\u0000(\u0000\u0000\u0000\u0001\u0000\u0000\u0000\u0001\u0000\u0000\u0000\u0001\u0000\u0018\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000'\u0000\u0000\u0000'\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000ÿÿÿÿ\u0000\u0000","Cookie":"_cl=cizqc3be800008ouxn5l6vdw6; expires=Thu, 01 Mar 2018 02:19:26 GMT;"}
Now to create a zip: run $ node-lambda package
this will create a zip file that will be located in the build folder. In the AWS Lambda Console upload this zip file in the function package. If the main file is named index.js (as I show above) then the default Lambda values are fine. Then save and test. For more details checkout the AWS Tutorial - Deployment Package (Node.js).
API Gateway
At this point the Lambda function should be successfully deployed and tested. Now I integrate it with AWS API Gateway. I setup the API Gateway as I did in the other tutorials. In addition, I added Set-Cookie to the Response Header in the Method Response.
Once Set-Cookie is added to the Method Response it is available to edit in the Integration Response. In the Integration Response I expand the table, then expand the Header Mappings table. In the Header Mappings table I edit the Set-Cookie and add the value integration.response.body.Cookie
, click the tiny checkmark and click save.
Also in the Integration Response, I expand the Body Mappings Templates and verify I only have image/gif
set in the Content-Type table.
Integration Response -> Body Mappings Templates -> Content-Type -> ‘image/gif’
#set($inputRoot = $input.path('$'))
$inputRoot.body
Now I deploy the API Gateway to my dev stage and visit the endpoint with Chrome. The response should be an invisible GIF and a cookie should be set. If I visit the endpoint again the cookie should remain the same. If I clear the cookie then visit the endpoint I should have a new cookie value set.
-
Feb 6, 2017
This is the fourth iteration of my AWS Lambda experiments:
AWS Lambda function to return a tracking pixel
AWS Lambda Tracking Pixel with Elasticsearch
AWS Lambda, Kinesis Firehose and Elasticsearch
In this tutorial I demonstrate how to serve different HTML based on the device type. Detecting the device type allows me to serve iTunes links to iPhone and Google Play links to Android. In the previous posts I demonstrated how to set a cookie with a web token, log requests to Elasticsearch, and scale using Kinesis Firehose.
Here’s the AWS Lambda function in node.js to read the User-Agent string and return different HTML for iPhone or Android devices. If the device cannot be determined I return HTML with links for each type of device.
AWS Lambda Function to detect device type and return HTML
'use strict';
exports.handler = function (event, context) {
var userAgent = '';
var htmlLink = '';
var oss = 'unknown';
if (event.hasOwnProperty('params') && event.params.hasOwnProperty('header') && event.params.header.hasOwnProperty('User-Agent')) {
userAgent = event.params.header["User-Agent"];
}
if (userAgent.match("iPhone")) {
oss = "ios";
} else if (userAgent.match("Android")) {
oss = "android";
}
switch(oss) {
case "ios":
htmlLink = "<h1>Click the link below to subscribe to the Podcast</h1>"+
"<a href='https://itunes.apple.com/us/podcast/internet-ballers-michael-pasha/id1108577427'>Subscribe on iTunes</a>"+
""+
"";
break;
case "android":
htmlLink = "<h1>Click the link below to subscribe to the Podcast</h1>"+
"<a href='http://subscribeonandroid.com/www.internetballers.com/feed/podcast/'>Subscribe on Google Play</a>"+
""+
"";
break;
default:
htmlLink = "<h1>Click the link below to subscribe to the Podcast</h1>"+
"<a href='https://itunes.apple.com/us/podcast/internet-ballers-michael-pasha/id1108577427'>Subscribe on iTunes</a>"+
"<br >" +
"<a href='http://subscribeonandroid.com/www.internetballers.com/feed/podcast/'>Subscribe on Google Play</a>"+
"<br >" +
"<a href='http://www.internetballers.com/feed/podcast/'>Subscribe to RSS</a>"+
""+
"";
}
context.done(null, { "body": htmlLink });
};
The setup is the same as it is in the previous tutorials.
I setup an AWS API Gateway in front of this Lambda function. To get the header data in the Lambda function I had to modify the Integration Method.
I setup a GET method for my API Resource. For the GET method I modified the Integration Request by setting the Body Mapping Templates to the third option, Never.
Then I added a Content-Type of application/json and for the template I selected Generate template: Method request passthrough. This creates a mapping that passes all the header data to the Lambda function.
Integration Request, Body Mapping Templates, application/json, Method request passthrough mapping template
## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
## This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"params" : {
#foreach($type in $allParams.keySet())
#set($params = $allParams.get($type))
"$type" : {
#foreach($paramName in $params.keySet())
"$paramName" : "$util.escapeJavaScript($params.get($paramName))"
#if($foreach.hasNext),#end
#end
}
#if($foreach.hasNext),#end
#end
},
"context" : {
"account-id" : "$context.identity.accountId",
"api-id" : "$context.apiId",
"api-key" : "$context.identity.apiKey",
"authorizer-principal-id" : "$context.authorizer.principalId",
"caller" : "$context.identity.caller",
"cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
"cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
"cognito-identity-id" : "$context.identity.cognitoIdentityId",
"cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
"http-method" : "$context.httpMethod",
"stage" : "$context.stage",
"source-ip" : "$context.identity.sourceIp",
"user" : "$context.identity.user",
"user-agent" : "$context.identity.userAgent",
"user-arn" : "$context.identity.userArn",
"request-id" : "$context.requestId",
"resource-id" : "$context.resourceId",
"resource-path" : "$context.resourcePath"
}
}
-
Jan 5, 2017
For Christmas I received a Photon Maker Kit from Particle
This is the kit I will be using for the IIoT tutorials and blog posts. The kit includes just about everything to build IIoT devices. The one thing it does not include is a 24 VDC power converter. Most Industrial power is 24 VDC. I can make due with the USB power connector or now.
The Travel Case
The Photon and Breadboard
The Parts and Jumper Wires
Here’s everything that comes in the kit:
- Photon with Headers (1)
- USB Micro B Cable (1)
- Flex antenna (1)
- Mini Breadboard (1)
- Proto-board (1)
- Deluxe Jumper Wire Pack (1)
- Male to Female Jumper Wires (10)
- Battery holder - 4xAA (1)
- Headers (7)
- Ceramic Capacitors (10 each)
- Electrolytic Capacitor 100uF (5)
- LEDs (10)
- RGB LED (1)
- Diode (6)
- IR LED (1)
- Resistors (30)
- Photoresistors (2)
- 10K Rotary Potentiometer (1)
- Temperature Sensor (1)
- Temperature Sensor - Sealed (1)
- Piezo Buzzer (1)
- Mini Pushbuttons (3)
- SPDT Switch (2)
- SPDT Relay (1)
- NPN Transistor (1)
- PIR sensor (1)
- Pancake Vibration Motor (1)
- Micro Servo (1)
- Serial OLED Screen,0.96”(1)
-
Jan 4, 2017
This is the third iteration of my tracking pixel experiment:
AWS Lambda function to return a tracking pixel
AWS Lambda Tracking Pixel with Elasticsearch
The Lambda to Elasticsearch function works well, but what happens when there is a traffic spike?
In this tutorial I add Kinesis Firehose so my app scales quickly. The incoming requests are written to Kinesis Firehose and later sent to Elasticsearch and also backed up in S3.
First, I used AWS Elasticsearch service to setup a t2.micro instance running Kibana and Elasticsearch.
Second, I setup Kinesis Firehose to publish to S3 and the Elasticsearch cluster I just created.
Third, I setup API Gateway as I did in the previous blog posts.
Finally, I updated the lambda function to publish to Kinesis Firehose instead of Elasticsearch.
Lambda Function to log events to Kinesis Firehose
'use strict';
var AWS = require('aws-sdk');
var setRegion = process.env['us-east-1'];
AWS.config.update({ region: 'us-east-1' });
var path = require('path');
var fh = new AWS.Firehose({
apiVersion : '2015-08-04',
region : setRegion
});
var imageHex = "\x42\x4d\x3c\x00\x00\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00" +
"\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00" +
"\x00\x00\x06\x00\x00\x00\x27\x00\x00\x00\x27\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00";
var es_data = {};
exports.handler = function (event, context) {
// Add date for Elasticsearch index
var d = new Date();
es_data.date = d.toISOString();
es_data.context = event.context;
es_data.params = event.params;
var jsonDoc = JSON.stringify(es_data);
fh.putRecord(
{
DeliveryStreamName: 'kinesis-firehose-stream-name',
Record: { Data: jsonDoc }
},
function (err, data) {
if (err) {
console.log(err); // an error occurred
} else {
console.log(data); // successful response
}
context.done(err, { "body": imageHex });
});
};
-
Dec 19, 2016
Previously I wrote a small AWS Lambda function to return a tracking pixel. In this post I’m logging the request for the pixel to Elasticsearch. Elasticsearch is a search and analytics engine. Elasticsearch is paired with Kibana for creating graphs and visuals from the log data.
I used AWS Elasticsearch service to setup a t2.micro instance running Kibana and Elasticsearch.
Once AWS finished launching the Elasticsearch instance I copied the Elasticsearch URL and pasted in my Lambda function below.
The Lambda function gathers the header data from the request along with the timestamp, stores it in Elasticsearch and returns a 1x1 pixel.
The data is in now searchable and viewable with Kibana.
The Lambda function is adapted from this Sample Code from AWS
Lambda Function to log event to Elasticsearch and return a 1x1 pixel
'use strict';
var AWS = require('aws-sdk');
var path = require('path');
var esDomain = {
region: 'us-east-1',
endpoint: 'my-elasticsearch-url.us-east-1.es.amazonaws.com',
index: 'events',
doctype: 'event'
};
var endpoint = new AWS.Endpoint(esDomain.endpoint);
var creds = new AWS.EnvironmentCredentials('AWS');
var imageHex = "\x42\x4d\x3c\x00\x00\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00"+
"\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00"+
"\x00\x00\x06\x00\x00\x00\x27\x00\x00\x00\x27\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00";
exports.handler = function(event, context) {
console.log('Received event:', JSON.stringify(event, null, 2));
var date = new Date();
event.date = Date.now();
var doc = JSON.stringify(event, null, 2);
var req = new AWS.HttpRequest(endpoint);
req.method = 'POST';
req.path = path.join('/', esDomain.index, esDomain.doctype);
req.region = esDomain.region;
req.headers['presigned-expires'] = false;
req.headers['Host'] = endpoint.host;
req.body = doc;
var signer = new AWS.Signers.V4(req , 'es'); // es: service code
signer.addAuthorization(creds, new Date());
var send = new AWS.NodeHttpClient();
send.handleRequest(req, null, function(httpResp) {
var respBody = '';
httpResp.on('data', function (chunk) {
respBody += chunk;
});
httpResp.on('end', function (chunk) {
console.log('Response: ' + respBody);
//return pixel
context.succeed({ "body":imageHex });
});
}, function(err) {
console.log('Error: ' + err);
//return pixel
context.fail({ "body":imageHex });
});
};
I setup an AWS API Gateway in front of this Lambda function. To get the header data in the Lambda function I had to modify the Integration Method.
I setup a GET method for my API Resource. For the GET method I modified the Integration Request by setting the Body Mapping Templates to the first option, When no template matches the request Content-Type header.
Then I added a Content-Type of application/json and for the template I selected Generate template: Method request passthrough. This creates a mapping that passes all the header data to the Lambda function.
Integration Request, Body Mapping Templates, application/json, Method request passthrough mapping template
## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
## This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"body-json" : $input.json('$'),
"params" : {
#foreach($type in $allParams.keySet())
#set($params = $allParams.get($type))
"$type" : {
#foreach($paramName in $params.keySet())
"$paramName" : "$util.escapeJavaScript($params.get($paramName))"
#if($foreach.hasNext),#end
#end
}
#if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
#if($foreach.hasNext),#end
#end
},
"context" : {
"account-id" : "$context.identity.accountId",
"api-id" : "$context.apiId",
"api-key" : "$context.identity.apiKey",
"authorizer-principal-id" : "$context.authorizer.principalId",
"caller" : "$context.identity.caller",
"cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
"cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
"cognito-identity-id" : "$context.identity.cognitoIdentityId",
"cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
"http-method" : "$context.httpMethod",
"stage" : "$context.stage",
"source-ip" : "$context.identity.sourceIp",
"user" : "$context.identity.user",
"user-agent" : "$context.identity.userAgent",
"user-arn" : "$context.identity.userArn",
"request-id" : "$context.requestId",
"resource-id" : "$context.resourceId",
"resource-path" : "$context.resourcePath"
}
}
-
Dec 16, 2016
Lambda Function to return a 1x1 Pixel. It took me a while to figure out how to return binary data from Lambda through the AWS API Gateway. Here’s my Lambda function for returning a GIF image pixel.
Lambda Function to return 1x1 pixel
'use strict';
exports.handler = function(event, context) {
var imageHex = "\x42\x4d\x3c\x00\x00\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00"+
"\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00"+
"\x00\x00\x06\x00\x00\x00\x27\x00\x00\x00\x27\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00";
context.done(null, { "body":imageHex });
};
Then in the Method Response for the API Gateway I added Content-Type to the Response Header.
Then in the Integration Response I map Content-Type to the constant string, ‘image/gif’ (single quotes).
In the Integration Response -> Body Mapping Templates I added a template for image/gif.
image/gif Body Mapping Template
#set($inputRoot = $input.path('$'))
$inputRoot.body
Now I can call the pixel from my webapges.
<img src="https://api-gateway-url/stage/pixel.gif" />
-
Oct 22, 2016
The 2016 IoT hackday was so much fun! To give you an idea of what a hackday is and how it is structured I outlined The Team Random 2016 Hackday.
Enjoy!
Chris
Timeline
8:00 AM - Meet new team members
Team Random was put together so anyone who came to IoT Hackday would have a team to join. Most of us met for the first time today.
We started from scratch.
No plan.
No idea.
8:10 AM - Brainstorm Ideas and plan an MVP (minimum viable project)
After coffee and breakfast (Yum, I love muffins for breakfast) we worked together to plan our project. We settled on bell training puppies.
9:00 AM - Each team pitches their idea
All the teams gave a 2 minute summary of what they planned to create today. This was the first time we presented our idea to the event.
11:21 AM - MVP complete!
Wow! We finished our MVP before lunchtime. I am so excited by our progress. We have an amazingly talented team.
11:30 AM - Eat Lunch
Delicious lunch! I went back for seconds (maybe thirds :-)
12:30 PM - Go for a walk
Even during an intensely focused hackathon we took time to take care of our minds and bodies. It is so important to prevent burnout.
12:51 PM - Expand our project scope
We finished our MVP and now we expand our scope. The unique abilities of each team member shined here. I love watching everyone working in their flow.
2:15 PM - Help Out Another Team
We are all hacking together. Always glad to help out another team.
3:15 PM - Add light indicator
The light indicator for behind the entertainment center is complete.
5:00 PM - Add mobile app
Yeah! A branded mobile app built custom for our project.
7:45 PM - Add fan indicator
Solving edge case problems with this addition. The creativity of our team is inspiring.
8:00 PM - Present our project
Show time! This is my favorite part. We presented our project to the friends and family that came to vote and support the event.
9:30 PM - Winners announced
Amazing projects were completed today. We didn’t win a prize today. We did accomplish more than any of us at first thought possible. I’m so proud of Team Random.
-
Mar 4, 2014
Android emulators are great for developing BluetoothLE applications. The trick is getting the Android emulator to recognize the BluetoothLE adapter.
What you’ll need:
- Androidx86 iso from android-x86.org I used the 4.4 release candidate
- Virtual Machine software: I used Oracle VirtualBox
- A BluetoothLE USB adapter: I used the Cirago Bluetooth 4.0 USB Mini Adapter (BTA8000)
- Android SDK for debugging
- Install VirtualBox
- Download Androidx86
- Open VirtualBox and create a new machine. Set type to linux/other(32bit)
- Set the virtual machine’s memory and harddrive space to whatever you need (but at least the minimum specs for Android).
- When asked for the OS image, select the Androidx86 image you download from Androidx86.org
- When the virtual machine boots, choose to install Android.
- When the installation completes, shutdown the Android virtual machine and unmount the iso image
- Plug in the Bluetooth USB adapter and add it to the Android Virtual Machine’s settings
- Start the Android Virtual Machine and go through the start-up screens to configure Android for use
- In the Android VM go to the settings and enable BluetoothLE (if this fails reboot the VM and try to enable again)
-
Mar 4, 2014
Building native HTML and JavaScript apps for Android can be done with Apache Cordova on Ubuntu.
- Download the Android SDK from developer.android.com
- Unzip the Android SDK and place the contents of the ‘sdk’ folder in ~/Development/adt-bundle/sdk
- Add the SDK to your path
PATH=$PATH:~/Development/adt-bundle/sdk/platform-tools:~/Development/adt-bundle/sdk/tools
- To permanently add the Android SDK to your Ubuntu path follow this Ubuntu.com article
- install Apache Cordova with npm
npm install -g cordova
(Install nodejs if you don’t have it.)
- cd to your Documents directory and create a helloWorld folder, then cd into that folder
cd ~/Documents
mkdir helloWorld
cd helloWorld
- Create a Cordova app and cd to the new folder
cordova create hello com.example.hello "Hello World"
cd hello/
- Add Android as a cordova platform
cordova platform add android
- Create an Andorid emulator
android create avd -n hello -t 1
- Run the cordova app on the emulator
- Build your web app and place it in the www folder, making sure to include config.xml and cordova.js
-
Feb 18, 2013
Using HTTPS with express is almost as simple as replacing require('http')
with require('https')
. For my application I am using a self-signed certificate that I created after following the directions on Nate Good’s blog. I placed the certs in a folder called cert.
I initialized the variables in preparation for two servers following this Stackoverflow question. One server for https listening on 443 and another for http listening on port 80. Then I wrote the http server to forward all requests to the https server. So that node doesn’t run as root I edited the iptables to map port 80 to 8081 and port 443 to 8080.
var app = express()
, httpapp = express()
, fs = require('fs')
, options = {
key: fs.readFileSync('./cert/client.key'),
cert: fs.readFileSync('./cert/client.crt'),
requestCert: true
}
, http = require('http').createServer(httpapp)
, server = require('https').createServer(options, app)
, io = require('socket.io').listen(server);
The following code forwards all requests for HTTP to HTTPS.
httpapp.get('*',function(req,res){
res.redirect('https://127.0.0.1:8080'+req.url)
})
server.listen(8080);
http.listen(8081);
Then on the client side I had to setup socket.io to use the secured port as well.
var socket = io.connect('https://127.0.0.1:8080', {secure: true});
The process was first to obtain or create an SSL Certificate. Then create an express server that requires ‘HTTPS’. Finally, setup the client JavaScript to reply on the HTTPS secure port.
EDIT 7/24/13: Also check out the documentation for HTTPS.