Created
October 3, 2017 15:18
-
-
Save ElectricImpSampleCode/53f88292b8da09670e5f3e4c724b4adf to your computer and use it in GitHub Desktop.
Factory firmware with agent-hosted WiFi credentials UI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Import Electric Imp's REST API management library | |
#require "Rocky.class.nut:2.0.0" | |
// Replace the following two strings with your server logging endpoint | |
const RESULTS_URL = "<YOUR_RESULT_LOGGING_URL>"; | |
// Define the web page HTML as a constant | |
const HTML_STRING = @"<!DOCTYPE html><html lang='en-US'><meta charset='UTF-8'> | |
<html> | |
<head> | |
<title>Factory BlinkUp Fixture</title> | |
<link rel='stylesheet' href='https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css'> | |
<link href='https://fonts.googleapis.com/css?family=Abel' rel='stylesheet'> | |
<meta name='viewport' content='width=device-width, initial-scale=1.0'> | |
<style> | |
.center { margin-left: auto; margin-right: auto; margin-bottom: auto; margin-top: auto; } | |
body {background-color: black} | |
p {color: white; font-family: Abel, sans-serif; font-size: 16px} | |
p.colophon {font-family: Abel, sans-serif; font-size: 13px} | |
p.input {color: black} | |
h2 {color: white; font-family: Abel, sans-serif; font-weight:bold} | |
td {color: white; font-family: Abel, sans-serif} | |
</style> | |
</head> | |
<body> | |
<div class='container' style='padding: 40px'> | |
<div style='border:3px solid white;background-color:#888888'> | |
<h2 class='name-status' align='center'>Electric Imp Factory BlinkUp™ Fixture</h2> | |
<p class='error-message' align='center'> <i><span></span></i> </p> | |
<div class='controls-area' align='center'> | |
<p align='center'>Enter the factory WiFi settings in the SSID and password fields below<br> | |
These settings will be used to configure Devices Under Test (DUTs) for Internet access<br> </p> | |
<div class='update-button'> | |
<p>Factory WiFi SSID <input id='issid' style='color:black' type='text'></input></p> | |
<p>Factory WiFi Password <input id='ipwd' style='color:black' type='password'></input><br> | |
<input type='checkbox' name='showpw' id='showpw'> Show Password<br> </p> | |
<button style='color:dimGrey;font-family:Abel,sans-serif;font-size: 16pt;width:280px;height:36px' id='fixture-button'>Set Factory WiFi Settings</button><br> | |
</div> | |
</div> | |
<p class='colophon' align='center'> <br>Copyright © Electric Imp, 2017</p> | |
</div> | |
</div> | |
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script> | |
<script> | |
var agenturl = '%s'; | |
getState(updateReadout); | |
$('.update-button button').click(setFixtureWiFi); | |
$('#showpw').click(togglePasswordField); | |
function updateReadout(undata) { | |
data = JSON.parse(undata); | |
document.getElementById('issid').value = (data.ssid == '' ? 'Unknown' : data.ssid); | |
document.getElementById('ipwd').value = (data.pwd == '' ? 'Unknown' : data.pwd); | |
} | |
function togglePasswordField() { | |
try { | |
var pwf = document.getElementById('ipwd'); | |
pwf.type = ((document.getElementById('showpw').checked == true) ? 'text' : 'password'); | |
} catch (err) { | |
// Doesn't work? Disable the button for old browsers | |
$('#showpw').click(null); | |
$('#showpw').prop('disabled', true); | |
} | |
} | |
function getState(callback) { | |
$.ajax({ | |
url : agenturl + '/state', | |
type: 'GET', | |
success : function(response) { | |
if (callback) { | |
callback(response); | |
} | |
} | |
}); | |
} | |
function setFixtureWiFi() { | |
var s = document.getElementById('issid').value; | |
var p = document.getElementById('ipwd').value; | |
if (s == '' || s == 'Unknown' || p == 'Unknown') { | |
$('.error-message span').text('Please enter a valid SSID/password combintion'); | |
return; | |
} | |
$.ajax({ | |
url : agenturl + '/fixture', | |
type: 'POST', | |
data: JSON.stringify({ 'ssid' : s, | |
'pwd' : p }), | |
success : function(response) { | |
$('.error-message span').text('WiFi settings applied'); | |
getState(updateReadout); | |
} | |
}); | |
} | |
</script> | |
</body> | |
</html>"; | |
// Declare global variables | |
blinkupCount <- 0; | |
assid <- ""; | |
apwd <- ""; | |
api <- Rocky(); | |
// Set up an receiver for "testresult" messages from the device under test | |
// This will not be called by a factory BlinkUp fixture, because our device-side | |
// firmare only sends this message from production devices | |
device.on("testresult", function(data) { | |
local headers = { "Content-Type": "application/json" }; | |
local body = http.jsonencode(data); | |
local deviceid = imp.configparams.deviceid; | |
// The following line is helpful for debugging factory firmware during development. | |
server.log(format("Posting 'test results' for device %s: %s", deviceid, body)); | |
// Send the test result data to your server | |
http.post(RESULTS_URL, headers, body).sendasync(function(response) { | |
if (response.statuscode >= 300) { | |
// Again, the following line will not be called in a production environment | |
server.error(format("Failed posting test results for device %s with response %d:%s", deviceid, response.statuscode, response.body)); | |
} | |
}); | |
}); | |
// Set up a receiver for "blinkupsent" messages from the factory fixture | |
// This will not be called by a production device under test, because our device-side | |
// firmare only sends this message from factory fixtures | |
device.on("blinkupsent", function(value) { | |
// Increment the BlinkUp tally | |
++blinkupCount; | |
// Prepare the notification | |
local headers = { "Content-Type": "application/json" }; | |
local data = {}; | |
data.fixture_url <- FactoryTools.getFactoryFixtureURL(); | |
data.blinkup_count <- blinkupCount; | |
data.timestamp <- time(); | |
data.message <- format("BlinkUp %i triggered by this fixture", blinkupCount); | |
local body = http.jsonencode(data); | |
// Send the notification to your server | |
http.post(RESULTS_URL, headers, body).sendasync(function(response) { | |
if (response.statuscode >= 300) { | |
// Again, the following line will not be called in a production environment | |
server.error(format("Failed posting blinkup notification for device %s with response %d:%s", FactoryTools.getFactoryFixtureURL(), response.statuscode, response.body)); | |
} | |
}); | |
}); | |
// Register handler for WiFi settings requests | |
// NOTE DUT will never call this, only the fixture | |
device.on("fixture.get.wifi.credentials", function(dummy) { | |
if (assid != "") { | |
local data = {}; | |
data.ssid <- assid; | |
data.pwd <- apwd; | |
device.send("fixture.set.wifi.credentials", data); | |
} | |
}); | |
// Establish the agent REST API | |
api.get("/", function(context) { | |
// Root request: just return the UI HTML | |
context.send(200, format(HTML_STRING, http.agenturl())); | |
}); | |
api.get("/state", function(context) { | |
// Return the current ssid, password combination | |
context.send(200, http.jsonencode({ "ssid": assid, "pwd": apwd })); | |
}); | |
api.post("/fixture", function(context) { | |
// Set the WiFi data from the web page | |
local data | |
try { | |
data = http.jsondecode(context.req.rawbody); | |
} catch (err) { | |
server.error(err); | |
context.send(400, "Bad data posted"); | |
return; | |
} | |
if ("ssid" in data) assid = data.ssid; | |
if ("pwd" in data) apwd = data.pwd; | |
// Send the new credentials to the fixture | |
device.send("fixture.set.wifi.credentials", { "ssid": assid, "pwd": apwd }); | |
context.send(202, "OK"); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) 2015-17 Electric Imp | |
// This program is licensed under the MIT License (http://opensource.org/licenses/MIT) | |
// Load the Factory Tools Library, which simplifies detecting Fixture or DUT | |
#require "FactoryTools.class.nut:2.1.0" | |
// Load the CFAx33KL Library, which abstracts the LCD/Keypad used on the impFactory | |
#require "CFAx33KL.class.nut:1.1.0" | |
// CONSTANTS | |
// This sets how long to wait (seconds) after triggering BlinkUp before allowing another | |
const BLINKUP_TIME = 10; | |
// Flag used to prevent new BlinkUp triggers while BlinkUp is running | |
sendingBlinkUp <- false; | |
// WiFi credentials to send to the DUT | |
ssid <- ""; | |
pwd <- ""; | |
// FACTORY FIXTURE FUNCTIONS | |
function configureBlinkUpTrigger(pin) { | |
// Register a state-change callback for BlinkUp Trigger Pins | |
pin.configure(DIGITAL_IN, function() { | |
// Trigger only on rising edges, when BlinkUp is not already running | |
// and we have an SSID | |
if (pin.read() && !sendingBlinkUp && ssid != "") { | |
sendingBlinkUp = true; | |
imp.wakeup(BLINKUP_TIME, function() { | |
sendingBlinkUp = false; | |
}); | |
// Send factory BlinkUp | |
server.factoryblinkup(ssid, pwd, blinkupPin, BLINKUP_FAST); | |
// Signal the agent that BlinkUp has been performed | |
agent.send("blinkupsent", true); | |
} | |
}); | |
} | |
function configureFactoryFixture() { | |
// Assign pins | |
greenBtn <- hardware.pinC; | |
ledGreen <- hardware.pinE; | |
ledRed <- hardware.pinF; | |
footSwitch <- hardware.pinH; | |
blinkupPin <- hardware.pinM; | |
// Initialize front panel LEDs to Off | |
ledRed.configure(DIGITAL_OUT, 0); | |
ledGreen.configure(DIGITAL_OUT, 0); | |
// Initiate factory BlinkUp on either a front-panel button or footswitch press | |
configureBlinkUpTrigger(greenBtn); | |
configureBlinkUpTrigger(footSwitch); | |
// Print a message on the LCD and store it as default | |
lcd <- CFAx33KL(hardware.uart2); | |
lcd.clearAll(); | |
lcd.setLine1("Electric Imp"); | |
lcd.setLine2("impFactory"); | |
lcd.setBrightness(100); | |
lcd.storeCurrentStateAsBootState(); | |
} | |
// DEVICE UNDER TEST FUNCTIONS | |
function blessDeviceUnderTest() { | |
// Run any tests you require for you DUT | |
local testSuccess = myTestFunction(); | |
// Attempt to bless this device, and send the result to the Factory Test Results Webhook | |
server.bless(testSuccess, function(blessSuccess) { | |
// Send identifying info and result to the test results webhook | |
agent.send("testresult", { "device_id": hardware.getdeviceid(), | |
"mac": imp.getmacaddress(), | |
"success": blessSuccess }); | |
// Clear the WiFi credentials and reboot if blessing completes successfully | |
if (blessSuccess) { | |
imp.clearconfiguration(); | |
imp.onidle(function() { | |
// Deep sleep for five seconds; triggers Squirrel reload | |
server.sleepfor(5); | |
}); | |
} | |
}); | |
} | |
// YOUR TEST CODE | |
function myTestFunction() { | |
// Run your tests here | |
// Returning false will prevent blessing and pass a "FAIL" to the test results webhook | |
return true; | |
} | |
// RUNTIME | |
// Use the Factory Tools library's isFactoryImp() call asynchronously in order to be sure | |
// the impFactory is online and has received device status data from the server | |
FactoryTools.isFactoryImp(function(isImpFactory) { | |
if (isImpFactory) { | |
// Device is an impFactory unit | |
// Register handler for receiving WiFi settings from agent | |
agent.on("fixture.set.wifi.credentials", function(data) { | |
// Record the WiFi settings we'll transmit to the DUT | |
ssid = data.ssid; | |
pwd = data.pwd; | |
// Now have the WiFi credentials, trigger the fixture flow | |
configureFactoryFixture(); | |
}.bindenv(this)); | |
// Request WiFi credentials from agent | |
agent.send("fixture.get.wifi.credentials", true); | |
} else { | |
// The next call need not be asynchronous since we can be sure | |
// at this point (we're executing in a callback) that the status | |
// data has now been received by the device (whatever it is) | |
if (FactoryTools.isDeviceUnderTest()) { | |
// Device is a production unit - test it | |
blessDeviceUnderTest(); | |
} else { | |
server.log("This firmware is not running in the factory environment"); | |
} | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment