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
// On Leonardo/Micro or others with hardware serial, use those! | |
// uncomment this line: | |
// #define pmsSerial Serial1 | |
// For UNO and others without hardware serial, we must use software serial... | |
// pin #2 is IN from sensor (TX pin on sensor), leave pin #3 disconnected | |
// comment these two lines if using hardware serial | |
#include <SoftwareSerial.h> | |
SoftwareSerial pmsSerial(2, 3); | |
int redPin = 4; // d4 | |
int greenPin = 5; // d5 | |
int bluePin = 7; // d7 | |
#include <SoftModem.h> | |
SoftModem modem = SoftModem(); | |
void setup() { | |
// our debugging output | |
Serial.begin(115200); | |
// sensor baud rate is 9600 | |
pmsSerial.begin(9600); | |
pinMode(redPin, OUTPUT); | |
pinMode(greenPin, OUTPUT); | |
pinMode(bluePin, OUTPUT); | |
// start softmodem | |
delay(100); | |
modem.begin(); | |
} | |
struct pms5003data { | |
uint16_t framelen; | |
uint16_t pm10_standard, pm25_standard, pm100_standard; | |
uint16_t pm10_env, pm25_env, pm100_env; | |
uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; | |
uint16_t unused; | |
uint16_t checksum; | |
}; | |
struct pms5003data data; | |
void loop() { | |
if (readPMSdata(&pmsSerial)) { | |
// reading data was successful! | |
Serial.println(); | |
Serial.println("---------------------------------------"); | |
Serial.println("Concentration Units (standard)"); | |
Serial.print("PM 1.0: "); Serial.print(data.pm10_standard); | |
Serial.print("\t\tPM 2.5: "); Serial.print(data.pm25_standard); | |
Serial.print("\t\tPM 10: "); Serial.println(data.pm100_standard); | |
Serial.println("---------------------------------------"); | |
Serial.println("Concentration Units (environmental)"); | |
Serial.print("PM 1.0: "); Serial.print(data.pm10_env); | |
Serial.print("\t\tPM 2.5: "); Serial.print(data.pm25_env); | |
Serial.print("\t\tPM 10: "); Serial.println(data.pm100_env); | |
Serial.println("---------------------------------------"); | |
Serial.print("Particles > 0.3um / 0.1L air:"); Serial.println(data.particles_03um); | |
Serial.print("Particles > 0.5um / 0.1L air:"); Serial.println(data.particles_05um); | |
Serial.print("Particles > 1.0um / 0.1L air:"); Serial.println(data.particles_10um); | |
Serial.print("Particles > 2.5um / 0.1L air:"); Serial.println(data.particles_25um); | |
Serial.print("Particles > 5.0um / 0.1L air:"); Serial.println(data.particles_50um); | |
Serial.print("Particles > 10.0 um / 0.1L air:"); Serial.println(data.particles_100um); | |
Serial.println("---------------------------------------"); | |
modem.print((String) data.pm10_standard + "," + data.pm25_standard + "," + data.pm100_standard); | |
int minVal = 0; | |
int maxVal = 20; // arbitrary | |
// map values to a portion of the 360 degree color wheel | |
int hue = max(0, min(359, map(data.pm25_standard, minVal, maxVal, 140, 0))); | |
setLedColorHSV(hue, 1, 1); | |
} | |
} | |
boolean readPMSdata(Stream *s) { | |
if (! s->available()) { | |
return false; | |
} | |
// Read a byte at a time until we get to the special '0x42' start-byte | |
if (s->peek() != 0x42) { | |
s->read(); | |
return false; | |
} | |
// Now read all 32 bytes | |
if (s->available() < 32) { | |
return false; | |
} | |
uint8_t buffer[32]; | |
uint16_t sum = 0; | |
s->readBytes(buffer, 32); | |
// get checksum ready | |
for (uint8_t i=0; i<30; i++) { | |
sum += buffer[i]; | |
} | |
/* debugging | |
for (uint8_t i=2; i<32; i++) { | |
Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", "); | |
} | |
Serial.println(); | |
*/ | |
// The data comes in endian'd, this solves it so it works on all platforms | |
uint16_t buffer_u16[15]; | |
for (uint8_t i=0; i<15; i++) { | |
buffer_u16[i] = buffer[2 + i*2 + 1]; | |
buffer_u16[i] += (buffer[2 + i*2] << 8); | |
} | |
// put it into a nice struct :) | |
memcpy((void *)&data, (void *)buffer_u16, 30); | |
if (sum != data.checksum) { | |
Serial.println("Checksum failure"); | |
return false; | |
} | |
// success! | |
return true; | |
} | |
// http://www.hobbitsandhobos.com/wp-content/uploads/2011/06/colorWheel.png | |
// Convert a given HSV (Hue Saturation Value) to RGB (Red Green Blue) | |
// and set the led to the color | |
// h is hue value, integer between 0 and 360 | |
// s is saturation value, double between 0 and 1 | |
// v is value, double between 0 and 1 | |
// http://splinter.com.au/blog/?p=29 | |
void setLedColorHSV(int h, double s, double v) { | |
double r=0; | |
double g=0; | |
double b=0; | |
double hf=h/60.0; | |
int i=(int)floor(h/60.0); | |
double f = h/60.0 - i; | |
double pv = v * (1 - s); | |
double qv = v * (1 - s*f); | |
double tv = v * (1 - s * (1 - f)); | |
switch (i) | |
{ | |
case 0: // rojo dominante | |
r = v; | |
g = tv; | |
b = pv; | |
break; | |
case 1: // verde | |
r = qv; | |
g = v; | |
b = pv; | |
break; | |
case 2: | |
r = pv; | |
g = v; | |
b = tv; | |
break; | |
case 3: // azul | |
r = pv; | |
g = qv; | |
b = v; | |
break; | |
case 4: | |
r = tv; | |
g = pv; | |
b = v; | |
break; | |
case 5: // rojo | |
r = v; | |
g = pv; | |
b = qv; | |
break; | |
} | |
// set each component to a integer value between 0 and 255 | |
int red = constrain(255-(int)255*r,0,255); | |
int green = constrain(255-(int)255*g,0,255); | |
int blue = constrain(255-(int)255*b,0,255); | |
setLedColor(red,green,blue); | |
} | |
void setLedColor(int red, int green, int blue) { | |
analogWrite(redPin, red); | |
analogWrite(greenPin, green); | |
analogWrite(bluePin, blue); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment