Skip to content

Instantly share code, notes, and snippets.

@hfiennes
Last active November 29, 2020 23:24
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hfiennes/8a812b9623ae5d2f32a6fbb7f1eb5153 to your computer and use it in GitHub Desktop.
Save hfiennes/8a812b9623ae5d2f32a6fbb7f1eb5153 to your computer and use it in GitHub Desktop.
imp001 air quality monitoring (with AQI index)
// Ensure we have a default reading
lastreading <- { "pm10":0, "pm25":0, "pm100":0 };
// Code to convert particlate density to AQI index
// based on https://gist.github.com/kfury/822bbba2cb0f946abb73baa156722ab1
function Linear(AQIhigh, AQIlow, Conchigh, Conclow, Conc) {
local a=((Conc-Conclow)/(Conchigh-Conclow))*(AQIhigh-AQIlow)+AQIlow;
return math.floor(a+0.5);
}
function PM25toAQI(Conc) {
local c=(math.floor(10*Conc))/10;
local AQI = 9999;
if (c>=0 && c<12.1) {
AQI=Linear(50,0,12,0,c);
} else if (c>=12.1 && c<35.5) {
AQI=Linear(100,51,35.4,12.1,c);
} else if (c>=35.5 && c<55.5) {
AQI=Linear(150,101,55.4,35.5,c);
} else if (c>=55.5 && c<150.5) {
AQI=Linear(200,151,150.4,55.5,c);
} else if (c>=150.5 && c<250.5) {
AQI=Linear(300,201,250.4,150.5,c);
} else if (c>=250.5 && c<350.5) {
AQI=Linear(400,301,350.4,250.5,c);
} else if (c>=350.5 && c<500.5) {
AQI=Linear(500,401,500.4,350.5,c);
}
return AQI;
}
// When we get data from the device, log it and keep a copy
device.on("aq", function(v) {
lastreading = v;
server.log(http.jsonencode(lastreading));
});
// Handle slack calls
http.onrequest(function(req, res) {
local status = format("Current air quality: PM1.0: %dug/m3, PM2.5: %dug/m3, PM10: %dug/m3, ",
lastreading.pm10, lastreading.pm25, lastreading.pm100);
local aqi = PM25toAQI(lastreading.pm25);
status += format("PM2.5 AQI index %d ", aqi);
if (aqi < 51) status += "(good)";
else if (aqi < 101) status += "(moderate)";
else if (aqi < 151) status += "(unhealthy for sensitive groups)";
else if (aqi < 201) status += "(unhealthy)";
else if (aqi < 301) status += "(very unhealthy)";
else if (aqi < 501) status += "(hazardous)";
else status += "(get out of there)";
// Check for slack
if (req.path == "/slack") {
res.send(200, http.jsonencode({"text": status}));
} else {
// Otherwise, if someone hits the agent, just send status as plain text
res.send(200, status+"\n");
}
});
// This part runs on the device
// Cut and paste this into the "device" side of impcentral
// DFrobot PM2.5 laser dust sensor connected to pins 1 & 2
uart <- hardware.uart12;
// Current serial state and packet
state <- -1;
packet <- blob(31);
// Deal with receiving a single byte
function rx() {
local b = uart.read();
// Waiting for start of a packet?
if (state == -1) {
// Packets always start with 0x42
if (b == 0x42) state++;
return;
}
// Collect the byte
packet[state++] = b;
// Got the whole packet?
if (state == 31) {
// Process it
rxpacket();
// Reset state for next one
state = -1;
}
}
// We've got a whole packet, check that it's valid
function rxpacket() {
// Calculate checksum
local chk = 0x42;
for(local a=0; a<29;a++) chk += packet[a];
// Does it match what we were sent?
if (packet[29] == (chk >> 8) && packet[30] == (chk & 0xff)) {
// Extract reading & send to server
local reading = {};
reading.pm10 <- (packet[3]<<8)|packet[4];
reading.pm25 <- (packet[5]<<8)|packet[6];
reading.pm100 <- (packet[7]<<8)|packet[8];
agent.send("aq", reading);
}
}
// Configure UART and attach receive handler
uart.configure(9600, 8, PARITY_NONE, 1, NO_CTSRTS, rx);
server.log("AQ sensor online");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment