Skip to content

Instantly share code, notes, and snippets.

@androng
Forked from ElectricImpSampleCode/webserver.agent.nut
Last active August 25, 2020 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save androng/6306a5cfb608ef4982d6f570b4a538b3 to your computer and use it in GitHub Desktop.
Save androng/6306a5cfb608ef4982d6f570b4a538b3 to your computer and use it in GitHub Desktop.
Example code showing how an agent can serve a dynamic web page
// Updated: 14 July 2020
// IMPORTS
#require "rocky.agent.lib.nut:3.0.0"
// CONSTANTS
const HTML_STRING = @"<!DOCTYPE html><html lang='en-US'><meta charset='UTF-8'>
<html>
<head>
<title>Environment Data</title>
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css' integrity='sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk' crossorigin='anonymous'>
<link href='//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: #3366cc}
p {color: white; font-family: Abel}
h2 {color: #99ccff; font-family: Abel; font-weight:bold; padding: 20px;}
h4 {color: white; font-family: Abel}
a:link {color: white; font-family: Abel}
a:visited {color: #cccccc; font-family: Abel}
a:hover {color: black; font-family: Abel}
a:active {color: black; font-family: Abel}
</style>
</head>
<body>
<div class='container' style='padding: 20px'>
<div style='border: 2px solid white'>
<h2 class='text-center'>Environment Data</h2>
<div>
<h4 class='temp-status text-center'>Current Temperature: <span></span>&deg;C</h4>
<h4 class='humid-status text-center'>Current Humidity: <span></span> per cent</h4>
<h4 class='locale-status text-center'>Sensor Location: <span></span></h4>
<p class='timestamp text-center'>Last reading: <span></span></p>
</div>
<br />
<div class='update-button'>
<p class='text-center'>Update Location Name<br /><input id='location' style='width:200px;color:CornflowerBlue'></input><br />
<button style='font-family:Abel' type='submit' class='btn btn-light btn-sm'>Set Location</button></p>
</div>
<br />
<p class='text-center'><a href='https://developer.electricimp.com/examples/webserver'><small>Powered by Electric Imp</small></a></p>
</div>
</div>
<script src='https://code.jquery.com/jquery-3.5.1.min.js'></script>
<script>
// Store the agent URL
var agenturl = '%s';
// Set the 'Set Location' button's action
$('.update-button button').click(getStateInput);
// Get the current state of the device, calling 'updateReadout()' when we have it
getState(updateReadout);
// Get State entry
function getStateInput(e){
e.preventDefault();
var place = $('#location').val();
setLocation(place);
$('#name-form').trigger('reset');
}
// Update the display when we receive device state information
function updateReadout(data) {
// Update the readouts
$('.temp-status span').text(data.temp);
$('.humid-status span').text(data.humid);
$('.locale-status span').text(data.locale);
// Display the current time and date (when the thermal data was received)
var date = new Date();
$('.timestamp span').text(date.toTimeString());
// Auto-update in 2 minutes' time
setTimeout(function() {
getState(updateReadout);
}, 120000);
}
// Send the content of the location field - ie. where the user has said the device
// is placed - to the agent using Ajax
function setLocation(place){
$.ajax({
url : agenturl + '/location',
type: 'POST',
data: JSON.stringify({ 'location' : place }),
success : function(response) {
if ('locale' in response) {
$('.locale-status span').text(response.locale);
}
}
});
}
// Get the device state from the agent
function getState(callback) {
$.ajax({
url : agenturl + '/state',
type: 'GET',
success : function(response) {
if (callback) {
callback(response);
}
}
});
}
</script>
</body>
</html>";
// GLOBAL VARIABLES
api <- null;
savedData <- null;
debug <- true;
// FUNCTIONS
function postReading(reading) {
// Format and save the data
savedData.temp = "temp" in reading ? format("%.2f", reading.temp) : "0.0";
savedData.humid = "humid" in reading ? format("%.2f", reading.humid) : "0.0";
local result = server.save(savedData);
if (result != 0) server.error("Could not back up data");
}
// RUNTIME START
// Instantiate Rocky
api = Rocky.init();
// Set up the app's API
api.get("/", function(context) {
// Root request: just return standard HTML string
local url = http.agenturl();
context.send(200, format(HTML_STRING, url));
});
api.get("/state", function(context) {
// Request for data made to the /state endpoint
context.send(200, savedData);
});
api.post("/location", function(context) {
// Sensor location string submission at the /location endpoint
local data = http.jsondecode(context.req.rawbody);
if ("location" in data) {
if (data.location != "") {
savedData.locale = data.location;
context.send(200, { "locale" : savedData.locale });
local result = server.save(savedData);
if (result != 0) server.error("Could not back up data");
return;
}
}
context.send(200, "OK");
});
// Set up the backup data
savedData = {};
savedData.temp <- "TBD";
savedData.humid <- "TBD";
savedData.locale <- "Unknown";
local backup = server.load();
if (backup.len() != 0) {
savedData = backup;
} else {
local result = server.save(savedData);
if (result != 0) server.error("Could not back up data");
}
// Register the function to handle data messages from the device
device.on("reading", postReading);
// Updated: 14 July 2020
// IMPORTS
#require "HTS221.device.lib.nut:2.0.2"
// CONSTANTS
const SLEEP_TIME = 120;
// GLOBAL VARIABLES
sensor <- null;
// FUNCTIONS
function getData() {
// Create a Squirrel table to hold the data - handy if we
// later want to package up other data from other sensors
local sendData = {};
// Get the sensor data
local reading = sensor.read();
// Add the temperature using Squirrel’s 'new key' operator
sendData.temp <- reading.temperature;
sendData.humid <- reading.humidity;
// Send the packaged data to the agent
agent.send("reading", sendData);
server.log("Temperature: " + reading.temperature);
server.log(" Humidity: " + reading.humidity);
// All done, we can put the device to sleep for 'SLEEP_TIME' minutes,
imp.onidle(function() {
server.sleepfor(SLEEP_TIME);
});
}
// RUNTIME START
// Configure I2C bus and the sensor connected to it
// NOTE Updated for the imp006 Breakout Kit
// https://developer.electricimp.com/hardware/resources/reference-designs/imp006breakout
// Change the value of 'i2c' for other imps
local i2c = hardware.i2cLM;
i2c.configure(CLOCK_SPEED_400_KHZ);
sensor = HTS221(i2c);
sensor.setMode(HTS221_MODE.ONE_SHOT);
// Take a temperature reading as soon as the device starts up
// NOTE When the device wakes from sleep (caused by line 28)
// it runs its device code afresh - ie. it does a warm boot
getData();
@androng
Copy link
Author

androng commented Aug 25, 2020

I revised the Agent code to make it easier to add more outputs: https://gist.github.com/androng/6306a5cfb608ef4982d6f570b4a538b3/revisions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment