Code for sketches included in the book – see the referenced figure numbers. This code can be copied and pasted into the Arduino IDE. Chapter 3. Figure 3.2 /* Blink Turns an LED on for one second, then off for one second, repeatedly. Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to the correct LED pin independent of which board is used. If you want to know what pin the on-board LED is connected to on your Arduino model, check the Technical Specs of your board at: https://www.arduino.cc/en/Main/Products modified 8 May 2014 by Scott Fitzgerald modified 2 Sep 2016 by Arturo Guadalupi modified 8 Sep 2016 by Colby Newman This example code is in the public domain. http://www.arduino.cc/en/Tutorial/Blink */ // the setup function runs once when you press reset or power the board void setup() { // initialize digital pin LED_BUILTIN as an output. pinMode(LED_BUILTIN, OUTPUT); } // the loop function runs over and over again forever void loop() { digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) delay(1000); // wait for a second digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW delay(1000); // wait for a second } Chapter 4. Figure 4.6 // Blink2LEDs.ino, D. Brooks, July 2016 // alternates on/off between 2 LEDS const int greenPin=7,redPin=8,delayTime=2000; void setup() { Serial.begin(9600); pinMode(greenPin,OUTPUT); pinMode(redPin,OUTPUT); // Start with both LEDs on, just an arbitrary choice. digitalWrite(greenPin,HIGH); digitalWrite(redPin,HIGH); delay(delayTime); } void loop() { digitalWrite(greenPin,LOW); digitalWrite(redPin,HIGH); Serial.println("Red LED on."); delay(delayTime); digitalWrite(greenPin,HIGH); digitalWrite(redPin,LOW); Serial.println("Green LED on."); delay(delayTime); } Chapter 5. Figure 5.6 /* TMP_basic.ino, D. Brooks, October 2015 Assumes TMP36 or compatible analog temperature sensor. */ const int TMPpin=A0; int V_digitized, delayTime=5000; // 5 sec delay between readings float V_analog,T; // temperature in deg C void setup() { pinMode(TMPpin,INPUT); // optional pin mode setting (default) Serial.begin(9600); } void loop() { V_digitized=analogRead(TMPpin); // integer between 0 and 1023 // NOTE: 5*V_digitized/1023; will give a value of 0! 5.0 or 5. forces // floating point arithmetic rather than integer arithmetic! V_analog=5.*V_digitized/1023; // convert back to analog value //T=100*(V_analog - 0.5); // convert analog value to T, deg C T=100*V_analog; // if using TMP35 (or LM35) sensor instead of TMP36 Serial.print(V_digitized); Serial.print(", "); Serial.println(T,1); // displays temperature to nearest 0.1 deg C delay(delayTime); } Chapter 6. Figure 6.3 /* CdS.ino, D. Brooks, December 2016 Using a CdS resistor in series with 10K resistor. */ const int CdS_pin = A0; int V; float V_CdS; void setup () { Serial.begin(9600); pinMode (CdS_pin, INPUT); } void loop () { V = analogRead(CdS_pin); // digitized voltage across 10K resistor V_CdS = 5.*V/1023.; // analog voltage across 10K resistor Serial.print(V); Serial.print(' '); Serial.print(V_CdS); Serial.println(); delay(200); } Chapter 7. Figure 7.3 /* TMP36_with_LEDs.ino, D. Brooks, December, 2018 Read air temperature using a TMP36 or TMP35 sensor. Control LEDs based on temperature */ const int greenPin=7,redPin=8; const int TMP36pin=A0; // connect center pin of TMP36 to A0 int V_digitized; // digitized input voltage, integer from 0-1023 float V; // V_digitized converted to a voltage float T; // the voltage converted to temperature const float T_set=25.0; // temperature at which red/green LEDs switched on/off const int delayTime=1000; // delay time in milliseconds void setup() { pinMode(TMP36pin,INPUT); // specify pin status pinMode(redPin,OUTPUT); pinMode(greenPin,OUTPUT) // initially both LEDs off... digitalWrite(redPin,LOW); digitalWrite(greenPin,LOW); Serial.begin(9600); // activate serial port } void loop() { V_digitized=analogRead(TMP36pin); // read the voltage Serial.print(V_digitized); V=5.0*V_digitized/1023; T=100*(V - 0.5); // T=100*V if using TMP35 (or LM35) sensor instead of TMP36 Serial.print("Temperature (deg C) = "); Serial.println(T,1); if (T>=T_set) { digitalWrite(redPin,HIGH); digitalWrite(greenPin,LOW); } else { digitalWrite(redPin,LOW); digitalWrite(greenPin,HIGH); } delay(delayTime); } Chapter 8. Figure 8.2 /* LCD1602_TMP36, D. Brooks, December 2018 Display temperature from TMP36 or similar. */ #include #include // set the LCD address to 0x27 for a 16 chars 2 line display LiquidCrystal_I2C lcd(0x27,16,2); char line1[]="Air Temp. (C)"; const int TMP36pin=A0; int V_digitized, delayTime=5000; float V,T; void setup() { //Serial.begin(9600); pinMode(TMP36pin,INPUT); lcd.init(); //initialize the lcd lcd.backlight(); //turn on the backlight } void loop() { lcd.clear(); lcd.setCursor(0,0); lcd.print(line1); V_digitized=analogRead(TMP36pin); Serial.print(V_digitized); V=5.0*V_digitized/1023; //T=100*(V - 0.5); T=100*V; // if using TMP35 (or LM35) sensor instead of TMP36 lcd.setCursor(0,1); lcd.print(T,1); delay(delayTime); } Chapter 9. Figure 9.2 /* BME280_Adafruit.ino, D. Brooks, January 2019 Logs data from Adafruit's BME280 T/RH/P breakout board, using the I2C interface and Adafruit data logging shield. */ #include #include #include #include #include Adafruit_BME280 bme; // I2C // Setup data logging RTC_DS1307 rtc; // real time clock module, old datalogger shield //RTC_PCF8523 rtc; // new datalogger shield const int SDpin=10; const long int DelayTime=120000L; File logfile; char filename[]="BME280.CSV"; int year,month,day,hour,minute,second; float T,P,RH,P_sealevel; const int delayTime=5000; void setup() { Serial.begin(9600); // setup sensors and data logging, assuming everything will work bme.begin(); SD.begin(SDpin); logfile=SD.open(filename,FILE_WRITE); Serial.println("Ready to go!"); Serial.println("YY/MM/DD,HH:MM:SS,day_frac,T(C),P(mbar),P_sealevel(inHg),RH(%)"); logfile.println("YY/MM/DD,HH:MM:SS,day_frac,T(C),P(mbar),P_sealevel(inHg),RH(%)"); logfile.flush(); } void loop() { DateTime now=rtc.now(); year=now.year(); month=now.month(); day=now.day(); hour=now.hour(); minute=now.minute(); second=now.second(); Serial.print(year); Serial.print('/'); Serial.print(month); Serial.print('/'); Serial.print(day); Serial.print(','); Serial.print(hour);Serial.print(':'); Serial.print(minute); Serial.print(':'); Serial.print(second); Serial.print(','); logfile.print(year); logfile.print('/'); logfile.print(month); logfile.print('/'); logfile.print(day); logfile.print(','); logfile.print(hour); logfile.print(':'); logfile.print(minute); logfile.print(':'); logfile.print(second); logfile.print(','); // Write day expressed as decimal fraction. Serial.print(day+hour/24.+minute/1440.+second/86400.,6); Serial.print(','); logfile.print(day+hour/24.+minute/1440.+second/86400.,6); logfile.print(','); // Read sensor data. T=bme.readTemperature(); delay(10); // just to make sure read is done... P=bme.readPressure(); delay(10); RH=bme.readHumidity(); delay(10); // Write to serial port and SD card file. Serial.print(T,1); Serial.print(','); Serial.print(P/100.0,1); Serial.print(','); 49 // for a site elevation of 131 m... P_sealevel=seaLevel(P/100.,0.131); Serial.print(P_sealevel,2); Serial.print(','); Serial.print(RH,1); Serial.println(); logfile.print(T,1); logfile.print(','); logfile.print(P/100.0,1); logfile.print(','); logfile.print(P_sealevel,2); logfile.print(','); logfile.print(RH,1); logfile.println(); logfile.flush(); delay(delayTime); } float seaLevel(float P,float elev) { // conversion from station pressure in mbar to sea level "Hg, elevation in km. return 29.921/1013.25*P/exp(-0.119*elev-0.0013*elev*elev); } Chapter 10. Figure 10.2 /* DataLogTemplate.ino, D. Brooks, May 2018 Template code for using microSD and DS1307 board for data logging with date/time stamp. NOTE: new Adafruit datalogging shield uses PCF8523 RTC module rather than DS1307 on old shields. */ #include // if you will attach an I2C sensor #include #include // RTC_DS1307 rtc; // real time clock module, old datalogger shield RTC_PCF8523 rtc; // new datalogger shield const int SDpin=10,DelayTime=5000; File logfile; char filename[]="LOG_TEST.CSV"; int year,month,day,hour,minute,second; void setup(void) { Serial.begin(9600); rtc.begin(); Wire.begin(); //RTC.adjust(DateTime(__DATE__,__TIME__)); // needed only for new RTC not yet set. if (!SD.begin(SDpin)) {Serial.println("Card failed."); delay(50);exit(0); } Serial.println("card initialized."); logfile = SD.open(filename, FILE_WRITE); if (!logfile) { Serial.println("Couldn't create file."); delay(50); exit(0); } Serial.print("Logging to file "); Serial.println(filename); // Optionally, write header line here, with logfile.flush() uncommented. // logfile.flush(); } void loop(void) { // Get date and time. DateTime now=rtc.now(); year=now.year(); month=now.month(); day=now.day(); hour=now.hour(); minute=now.minute(); second=now.second(); logfile.print(year); logfile.print('/'); logfile.print(month); logfile.print('/'); logfile.print(day); logfile.print(','); logfile.print(hour); logfile.print(':'); logfile.print(minute); logfile.print(':'); logfile.print(second); logfile.print(','); // Write day expressed as decimal fraction. logfile.print(day+hour/24.+minute/1440.+second/86400.,6); // Write other data here... logfile.println(); logfile.flush(); delay(DelayTime); } Figure 10.3 /* RCT_2.ino, D. Brooks, August 2018 This code has been modified to work with both old and new datalogging shields. The clock chip on the shields is different. */ #include #include "RTClib.h" RTC_DS1307 RTC; // old data logger shield //RTC_PCF8523 RTC; // new data logger shield void setup() { Serial.begin(9600); Wire.begin(); if (!RTC.begin()) { Serial.println("RTC not running."); exit; } RTC.adjust(DateTime(__DATE__,__TIME__)); DateTime now=RTC.now(); Serial.print(now.year()); Serial.print('/'); Serial.print(now.month()); Serial.print('/'); Serial.print(now.day()); Serial.print(' '); Serial.print(now.hour()); Serial.print(':'); Serial.print(now.minute()); Serial.print(':'); Serial.println(now.second()); } void loop() { } Figure 10.4 /* BME280_Adafruit.ino, D. Brooks, January 2019 Logs data from Adafruit's BME280 T/RH/P breakout board, using the I2C interface and Adafruit data logging shield. */ #include #include #include #include #include Adafruit_BME280 bme; // I2C // Setup data logging RTC_DS1307 rtc; // real time clock module, old datalogger shield //RTC_PCF8523 rtc; // new datalogger shield const int SDpin=10; const long int DelayTime=120000L; File logfile; char filename[]="BME280.CSV"; int year,month,day,hour,minute,second; float T,P,RH,P_sealevel; const int delayTime=5000; void setup() { Serial.begin(9600); // setup sensors and data logging, assuming everything will work bme.begin(); SD.begin(SDpin); logfile=SD.open(filename,FILE_WRITE); Serial.println("Ready to go!"); Serial.println("YY/MM/DD,HH:MM:SS,day_frac,T(C),P(mbar),P_sealevel(inHg),RH(%)"); logfile.println("YY/MM/DD,HH:MM:SS,day_frac,T(C),P(mbar),P_sealevel(inHg),RH(%)"); logfile.flush(); } void loop() { DateTime now=rtc.now(); year=now.year(); month=now.month(); day=now.day(); hour=now.hour(); minute=now.minute(); second=now.second(); Serial.print(year); Serial.print('/'); Serial.print(month); Serial.print('/'); Serial.print(day); Serial.print(','); Serial.print(hour);Serial.print(':'); Serial.print(minute); Serial.print(':'); Serial.print(second); Serial.print(','); logfile.print(year); logfile.print('/'); logfile.print(month); logfile.print('/'); logfile.print(day); logfile.print(','); logfile.print(hour); logfile.print(':'); logfile.print(minute); logfile.print(':'); logfile.print(second); logfile.print(','); // Write day expressed as decimal fraction. Serial.print(day+hour/24.+minute/1440.+second/86400.,6); Serial.print(','); logfile.print(day+hour/24.+minute/1440.+second/86400.,6); logfile.print(','); // Read sensor data. T=bme.readTemperature(); delay(10); // just to make sure read is done... P=bme.readPressure(); delay(10); RH=bme.readHumidity(); delay(10); // Write to serial port and SD card file. Serial.print(T,1); Serial.print(','); Serial.print(P/100.0,1); Serial.print(','); // for a site elevation of 131 m... P_sealevel=seaLevel(P/100.,0.131); Serial.print(P_sealevel,2); Serial.print(','); Serial.print(RH,1); Serial.println(); logfile.print(T,1); logfile.print(','); logfile.print(P/100.0,1); logfile.print(','); logfile.print(P_sealevel,2); logfile.print(','); logfile.print(RH,1); logfile.println(); logfile.flush(); delay(delayTime); } float seaLevel(float P,float elev) { // conversion from station pressure in mbar to sea level "Hg, elevation in km. return 29.921/1013.25*P/exp(-0.119*elev-0.0013*elev*elev); } Chapter 11. Figure 11.3 /* DHT22_LCD.ino, D. Brooks, December 2018 Display air temperature and RH on LCD, using a DHT22 sensor. (Serial port output is optional.) */ #include #include LiquidCrystal_I2C lcd(0x27,16,2); // for a 16x2 LCD #include #define DHTPIN 8 #define DHTTYPE DHT22 // specify DHT11 or DHT22 DHT dht(DHTPIN,DHTTYPE); // create "instance" of the DHT object float DHT_T,DHT_TF,DHT_RH,DHT_HIC, DewPt; // DHT values const int delayTime=2000; // delay time between readings void setup() { pinMode(DHTPIN,INPUT); dht.begin(); Serial.begin(9600); lcd.init(); lcd.backlight(); lcd.clear(); lcd.setCursor(0,0); lcd.print("Initializing..."); } void loop() { DHT_T=dht.readTemperature(); DHT_RH=dht.readHumidity(); DHT_TF=dht.readTemperature(true); // get temperature in deg F DHT_HIC = dht.computeHeatIndex(DHT_T, DHT_RH, false); // optional output to serial port window Serial.print("DHT22 temperature C and F, heat index, relative humidity, dewpoint "); Serial.print(DHT_T,1); Serial.print(", "); Serial.print(DHT_TF,1); Serial.print(", "); Serial.print(DHT_RH,0); Serial.print(", "); Serial.print("DHT_HIC,1"); Serial.print(", "); DewPt=dewpoint(DHT_T,DHT_RH); Serial.println(DewPt,1); // LCD output lcd.clear(); lcd.setCursor(0,0); // (column,row) row=0 is top row lcd.print("T C & F, RH %"); lcd.setCursor(0,1); lcd.print(DHT_T,1); lcd.print(", "); lcd.print(DHT_TF,1); lcd.print(", "); lcd.print(DHT_RH,0); delay(delayTime); } float dewpoint(float T,float RH) { // See, for example, http://www.paroscientific.com/dewpoint.htm // input: T in deg C and RH 0-100 % float a=17.27,b=237.7, alpha=a*T/(b+T)+log(RH/100.); return b*alpha/(a-alpha); } Chapter 12. Figure 12.4 /* RGB_test, D. Brooks, December 2018 produces blended colors by turning on/off all possible combinations of the three chips on a common-anode RGB LED. */ // blue LED not used in this code. int Red=9,Grn=10,Blu=11; void setup() { Serial.begin(9600); pinMode(Blu,OUTPUT);pinMode(Grn,OUTPUT);pinMode(Red,OUTPUT); // 255 is "OFF" for common anode LEDs analogWrite(Blu,255);analogWrite(Grn,255);analogWrite(Red,255); } void loop() { analogWrite(Red,0); analogWrite(Grn,255); analogWrite(Blu,255); delay(1000); analogWrite(Red,255); analogWrite(Grn,0); analogWrite(Blu,255); delay(1000); analogWrite(Red,255); analogWrite(Grn,255); analogWrite(Blu,0); delay(1000); analogWrite(Red,0); analogWrite(Grn,255); analogWrite(Blu,0); delay(1000); analogWrite(Red,0); analogWrite(Grn,0); analogWrite(Blu,255); delay(1000); analogWrite(Red,255); analogWrite(Grn,0); analogWrite(Blu,0); delay(1000); analogWrite(Red,0); analogWrite(Grn,0); analogWrite(Blu,0); delay(1000); } Figure 12.5 /* RGB_cycle.ino, D. Brooks, December 2018 Use PWM to cycle RGB LED through the color spectrum. See stackoverflow referenced in line 27. */ const int redPin=9,greenPin=10,bluePin=11; int dt=100; float lambda; float R,G,B; void setup() { Serial.begin(9600); } void loop() { delay(250); for (lambda=400; lambda<=700; lambda+=5) { //makeColor(lambda); spectral_color(lambda); analogWrite(redPin,255-round(255.*R)); analogWrite(greenPin,255-round(255.*G)); analogWrite(bluePin,255-round(255.*B)); Serial.print(lambda);Serial.print(','); Serial.print(round(255.*R));Serial.print(','); Serial.print(round(255.*G));Serial.print(','); Serial.print(round(255.*B));Serial.println(); delay(dt); } } void spectral_color(int lambda) { // see http://stackoverflow.com/questions/3407942/rgb-values-of-visible-spectrum float t; R=0.; G=0.; B=0.; if ((lambda>=400)&&(lambda<410)) { t=(lambda-400.0)/(410.0-400.0); R= +(0.33*t)-(0.20*t*t); } else if ((lambda>=410)&&(lambda<475)) { t=(lambda-410.0)/(475.0-410.0); R=0.14 -(0.13*t*t); } else if ((lambda>=545)&&(lambda<595)) { t=(lambda-545.0)/(595.0-545.0); R= +(1.98*t)-( t*t); } else if ((lambda>=595)&&(lambda<650)) { t=(lambda-595.0)/(650.0-595.0); R=0.98+(0.06*t)-(0.40*t*t); } else if ((lambda>=650)&&(lambda<700)) { t=(lambda-650.0)/(700.0-650.0); R=0.65-(0.84*t)+(0.20*t*t); } else R=0; if ((lambda>=415)&&(lambda<475)) { t=(lambda-415.0)/(475.0-415.0); G= +(0.80*t*t); } else if ((lambda>=475)&&(lambda<590)) { t=(lambda-475.0)/(590.0-475.0); G=0.8 +(0.76*t)-(0.80*t*t); } else if ((lambda>=585)&&(lambda<639)) { t=(lambda-585.0)/(639.0-585.0); G=0.84-(0.84*t) ; } else G=0.; if ((lambda>=400)&&(lambda<475)) { t=(lambda-400.0)/(475.0-400.0); B= +(2.20*t)-(1.50*t*t); } else if ((lambda>=475)&&(lambda<560)) { t=(lambda-475.0)/(560.0-475.0); B=0.7 -( t)+(0.30*t*t); } else B=0.; } Chapter 13. Figure 13.2 /* SoilMoisture_LED.ino, D. Brooks, December 2018 Read and display soil moisture from dfrobot capacitive probe. Use values to turn red or green LED on/off. */ const int redPin=5,greenPin=4; const int SoilPin=A0; int V; // digitized input voltage, integer from 0-1023 const int V_set=500; // digitized voltage at which red/green LEDs switch on/off const int delayTime=1000;// delay time in milliseconds void setup() { pinMode(SoilPin,INPUT); // specify pin status pinMode(redPin,OUTPUT); pinMode(greenPin,OUTPUT); // initially both LEDs off... digitalWrite(redPin,LOW); digitalWrite(greenPin,LOW); Serial.begin(9600); // activate serial port } void loop() { V=analogRead(SoilPin); // read the voltage Serial.print("Probe voltage: "); Serial.println(V); if (V>=V_set) { digitalWrite(redPin,HIGH); digitalWrite(greenPin,LOW); } else { digitalWrite(redPin,LOW); digitalWrite(greenPin,HIGH); } delay(delayTime); } Chapter 14. Figure 14.3 /* relayController.ino, D. Brooks, March 2017 Test a solid-state relay. Use digital pin 8 to control the relay. */ const int relay=8; void setup() { Serial.begin(9600); pinMode(relay,OUTPUT); digitalWrite(relay,HIGH); // start with LED off } void loop() { digitalWrite(relay,HIGH); Serial.println("Turn relay off."); delay(3000); digitalWrite(relay,LOW); Serial.println("Turn relay on."); delay(3000); } Chapter 15. Figure 15.3 /* plantower_DHT22_log_2, D. Brooks, September 2018 Logs data from a Plantower PMS5003 sensor and a DHT22 T/RH sensor. A data stream is received about once per second and KNT_MAX values are averaged (200 samples = about 3 minutes). See also example code from Adafruit. Includes particle counts and conversion to PM2.5 AQI values. */ #define ECHO_TO_SERIAL 1 #define KNT_MAX 200 #include SoftwareSerial pmsSerial(2, 3); #include #define DHTPIN 8 #define DHTTYPE DHT22 DHT dht(DHTPIN,DHTTYPE); #include #define SDpin 10 #include #include RTC_DS1307 rtc; int knt=0; int yr,mon,dy,hr,mn,sec; float pm1=0,pm25=0,pm10=0,AQI25,AQI10; float p03=0,p05=0,p10=0,p25=0,p50=0,p100=0; File logfile; void setup() { rtc.begin(); Serial.begin(9600); pmsSerial.begin(9600); Serial.print(F("Initializing SD card...")); pinMode(SDpin,OUTPUT); if (!SD.begin(SDpin)) {Serial.println("Card failed."); delay(50);exit(0);} Serial.println("card initialized."); logfile=SD.open("PM25_DHT.CSV",FILE_WRITE); logfile.println("yr,mon,day,hr,min,sec,day_frac,DHT_T,DHT_RH,PM1,PM2.5,PM10,AQI2.5,AQI10,p0.3,p0.5,p1.0,p2.5,p5,p10"); logfile.flush(); } 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! pm1+=data.pm10_env; pm25+=data.pm25_env; pm10+=data.pm100_env; p03+=data.particles_03um; p05+=data.particles_05um; p10+=data.particles_10um; p25+=data.particles_25um; p50+=data.particles_50um; p100+=data.particles_100um; knt++; if (knt==KNT_MAX) { DateTime now=rtc.now(); yr=now.year(); mon=now.month(); dy=now.day(); hr=now.hour(); mn=now.minute(); sec=now.second(); pm1/=knt; pm25/=knt; pm10/=knt; p03/=knt; p05/=knt; p10/=knt; p25/=knt; p50/=knt; p100/=knt; // Convert to AQI value. if (pm25<=12.0) { AQI25=(50.-0.)/(12.0-0.)*(pm25-0.0)+0.; } else if (pm25<=35.4) { AQI25=(100.-50.)/(35.4-12.0)*(pm25-12.0)+50.; } else if (pm25<=55.4) { AQI25=(150.-100.)/(55.4-35.4)*(pm25-35.4)+100.; } else if (pm25<=150.4) { AQI25=(200.-150.)/(150.4-55.4)*(pm25-55.4)+150.; } else if (pm25<=250.4) { AQI25=(300.-200.)/(250.4-150.4)*(pm25-150.4)+200.; } else if (pm25<=350.4) { AQI25=(400.-300.)/(350.4-250.4)*(pm25-250.4)+300.; } else if (pm25<=500.4) { AQI25=(500.-400.)/(500.4-350.4)*(pm25-350.4) +400.; } else AQI25=501.; if (pm10<=54.) { AQI10=(50.-0.)/(54.-0.)*(pm10-0.)+0.; } else if (pm10<=154.) { AQI10=(100.-50.)/(154.-54.)*(pm10-54.)+50.; } else if (pm10<=254.) { AQI10=(150.-100.)/(254.-154.)*(pm10-154.)+100.; } else if (pm10<=354.) { AQI10=(200.-150.)/(354.-254.)*(pm10-254.)+150.; } else if (pm10<=424.) { AQI10=(300.-200.)/(424.-354.)*(pm10-354.)+200.; } else if (pm10<=504) { AQI10=(400-300)/(504.-424.)*(pm10-424.); } else if (pm10<=604.) { AQI10=(500.-400.)/(604.-504.)*(pm10-504.)*400.; } else AQI10=501.; logfile.print(yr); logfile.print(','); logfile.print(mon); logfile.print(','); logfile.print(dy); logfile.print(','); logfile.print(hr); logfile.print(','); logfile.print(mn); logfile.print(','); logfile.print(sec); logfile.print(','); logfile.print(dy+hr/24.+mn/1440.+sec/86400.,5); logfile.print(','); logfile.print(dht.readTemperature(),1); logfile.print(','); logfile.print(dht.readHumidity(),0); logfile.print(','); logfile.print(pm1,1); logfile.print(','); logfile.print(pm25,1); logfile.print(','); logfile.print(pm10,1); logfile.print(','); logfile.print(AQI25,0); logfile.print(','); logfile.print(AQI10,0); logfile.print(','); logfile.print(p03); logfile.print(','); logfile.print(p05); logfile.print(','); logfile.print(p10); logfile.print(','); logfile.print(p25); logfile.print(','); logfile.print(p50); logfile.print(','); logfile.println(p100); logfile.flush(); #if ECHO_TO_SERIAL Serial.print(dy);Serial.print(' ');Serial.print(hr);Serial.print(':');Serial.print(mn); Serial.print(':');Serial.print(sec);Serial.print(' '); Serial.print(pm1,1);Serial.print(", ");Serial.print(pm25,1);Serial.print(", "); Serial.print(pm10,1);Serial.println(); #endif // ECHO_TO_SERIAL pm1=0; pm25=0; pm10=0; // reset PM totals p03=0; p05=0; p10=0; p25=0; p50=0; p100=0; knt=0; } } } 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]; } // 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; } return true; // success! } Chapter 16. Figure 16.3 /*************************************************** This is a library example for the MLX90614 Temp Sensor Designed specifically to work with the MLX90614 sensors in the adafruit shop ----> https://www.adafruit.com/products/1748 ----> https://www.adafruit.com/products/1749 These sensors use I2C to communicate, 2 pins are required to interface Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, all text above must be included in any redistribution ****************************************************/ #include #include Adafruit_MLX90614 mlx = Adafruit_MLX90614(); void setup() { Serial.begin(9600); Serial.println("Adafruit MLX90614 test"); mlx.begin(); } void loop() { Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempC()); Serial.print("*C\tObject = "); Serial.print(mlx.readObjectTempC()); Serial.println("*C"); Serial.print("Ambient = "); Serial.print(mlx.readAmbientTempF()); Serial.print("*F\tObject = "); Serial.print(mlx.readObjectTempF()); Serial.println("*F"); Serial.println(); delay(1000); } Chapter 17. Figure 17.3 /* ADS1115_TMP36, D. Brooks, January 2019 Shows how to read 1 channel from ADS1115 breakout. */ #include #include // This library works for both 1015 and 1115 Adafruit_ADS1115 ads(0x48); // default address is 0x48 float V0,V1,V2,V3,DtoA,T; int16_t adc0,adc1,adc2,adc3; // ADC reading produces 16-bit integer const int delayTime=5000; void setup(void) { Serial.begin(9600); Wire.begin(); ads.begin(); //ads.setGain(GAIN_ONE); DtoA=0.125/1000; // 4.096 V ads.setGain(GAIN_TWO); DtoA=0.0625/1000; // 2.048 V //ads1115.setGain(GAIN_TWOTHIRDS); DtoA=0.187506/1000; // 6.144 V (default gain) //ads1115.setGain(GAIN_FOUR); DtoA=0.03125/1000; // 1.024 V //ads1115.setGain(GAIN_EIGHT); DtoA=0.015625/1000; // 0.512 V //ads1115.setGain(GAIN_SIXTEEN); DtoA=0.007813/1000; // 0.256 V } void loop(void) { adc0 = ads.readADC_SingleEnded(0); //adc1 = ads.readADC_SingleEnded(1); //adc2 = ads.readADC_SingleEnded(2); //adc3 = ads.readADC_SingleEnded(3); V0 = adc0 * DtoA; //V1 = adc1 * DtoA; //V2 = adc2 * DtoA; //V3 = adc3 * DtoA; //T=(V0-0.5)*100.; T=V0*100.; Serial.print("Temperature, *C: "); Serial.println(T,2); delay(delayTime); } Chapter 18. Figure 18.3 /* Vaisala_SHT1x_AM2315_log.ino, [C] D. Brooks, October 2018 Compares analog-output Vaisala HMP260 with three other T/RH sensors: DHT22, Adafruit ID 385, $10 (10K pull-up resistor included, see www.adafruit.com) AM2315, Adafruit ID 1293, $30 (needs 2 10K pull-up resistors, not included) SHT10, Adafruit ID 1298, $40 (10K pull-up resistor included) Uses any Arduino UNO or compatible plus: Data logging shield, Adafruit ID 1141, $14 (required coin cell battery not included) ADS1115 16-bit ADC, programmable gain amplifier, Adafruit 1085, $15 HMP60 outputs 0->2.5 VDC (one possible choice), Ch1=humidity, Ch2=temperature, calibration linear over 0-2.5 V, 0->100% for RH, -40->60C for T. */ #define ECHO_TO_FILE 1 // 0/1 = Write to file turned off/on #define ECHO_TO_SERIAL 1 // 0/1 = Write to serial port turned off/on // setup for AM2315 #include #include Adafruit_AM2315 am2315; float am2315_T,am2315_H; // setup for SHT1x #include #define dataPin 6 #define clockPin 7 float temp_c, humidity; SHT1x sht1x(dataPin,clockPin); // setup for DHT22 (optional extra sensor for comparison) #include #define DHTPIN 8 #define DHTTYPE DHT22 DHT dht(DHTPIN,DHTTYPE); float DHT_T,DHT_RH; // DHT22 output // setup for SD card and clock #include #define SDpin 10 #include RTC_DS1307 rtc; // old Adafruit datalogger shield clock //RTC_PCF8523 rtc; // new datalogger shield clock #include // same library for 1015 or 1115 Adafruit_ADS1115 ads(0x48); // default address is 0x48 float DtoA; // Convert ADS digital counts to analog voltage. float T,RH,T_V,RH_V; // variables for HMP60 output float dayFrac; // day, hour, minute, second converted to fractional day const long delayTime=120000L; // delay time in milliseconds (2 m) int16_t adc0,adc1; // ADC reading produces 16-bit integer int Y,D,MM,H,M,S; File logfile; char filename[]="VAISALA.CSV"; void setup() { am2315.begin(); ads.begin(); ads.setGain(GAIN_ONE); DtoA=0.125/1000; // 0-4.096 V Serial.begin(9600); pinMode(SDpin,OUTPUT); #if ECHO_TO_FILE if (!SD.begin(SDpin)) {Serial.println("Card failed."); delay(50);exit(0);} Serial.println("card initialized."); Serial.print("Logging to: "); Serial.println(filename); logfile=SD.open(filename,FILE_WRITE); logfile.print("yr,mon,day,hr,min,sec,day_frac,HMP60_T,HMP60_RH,DHT_T,DHT_RH,"); logfile.println("nSHT1x_T,SHT1x_RH,AM2315_T,AM2315_RH"); logfile.flush(); #endif // ECHO_TO_FILE rtc.begin(); if (!rtc.isrunning()) { Serial.println("RTC not running."); exit; } else Serial.println("RTC is running."); // Uncomment this line with new logging shield or if date/time is off. // Of course, will work only when code uploaded and run from computer USB. //rtc.adjust(DateTime(__DATE__, __TIME__)); } void loop() { DateTime now=rtc.now(); Y=now.year(); MM=now.month(); D=now.day(); H=now.hour(); M=now.minute(); S=now.second(); dayFrac=D+H/24.+M/1440.+S/86400; // read HMP60 adc0 = ads.readADC_SingleEnded(0); adc1 = ads.readADC_SingleEnded(1); RH_V=adc0*DtoA; T_V=adc1*DtoA; RH=100.*RH_V/2.5; T=100.*T_V/2.5-40.; // read DHT22 DHT_T=dht.readTemperature(); DHT_RH=dht.readHumidity(); // read SHT1x temp_c=sht1x.readTemperatureC(); humidity=sht1x.readHumidity(); // read AM2315 am2315_T=am2315.readTemperature(); am2315_H=am2315.readHumidity(); #if ECHO_TO_SERIAL Serial.print(" V, Temperature (deg C) = "); Serial.print(T_V,4); Serial.print(", "); Serial.println(T,2); Serial.print(" V, Relative humidity (%) = "); Serial.print(RH_V,4); Serial.print(", "); Serial.println(RH,1); Serial.print("SHT1x T, RH = "); Serial.print(temp_c,2); Serial.print(", "); Serial.println(humidity,1); Serial.println("----------------------"); #endif // ECHO_TO_SERIAL #if ECHO_TO_FILE logfile.print(Y); logfile.print(','); logfile.print(MM); logfile.print(','); logfile.print(D); logfile.print(','); logfile.print(H); logfile.print(','); logfile.print(M); logfile.print(','); logfile.print(S); logfile.print(','); logfile.print(dayFrac,6); logfile.print(','); logfile.print(T,2); logfile.print(','); // Vaisala HMP60 data logfile.print(RH,1); logfile.print(','); logfile.print(DHT_T,2); logfile.print(','); // Adafruit DHT22 data logfile.print(DHT_RH,1); logfile.print(','); logfile.print(temp_c,2); logfile.print(','); // Adafruit SHT1x data logfile.print(humidity,1); logfile.print(','); logfile.print(am2315_T,2); logfile.print(','); // Adafruit AM2315 data logfile.print(am2315_H,1); logfile.println(); logfile.flush(); // dump buffer onto SD card #endif // ECHO_TO_FILE delay(delayTime); } Chapter 19. Figure 19.2 /* VEML7700_GA1A.ino, D. Brooks, April 2019 Tests Adafruit VEML7700 and GA1A12S202 breakout boards, collecting data with Adafruit data logging shield at ~2 minute intervals. */ #include #include //RTC_DS1307 rtc; // old data logging shield clock RTC_PCF8523 rtc; // new datalogger shield clock #include #include Adafruit_VEML7700 veml = Adafruit_VEML7700(); File logfile; char filename[]="VEML7700.CSV"; float VEML_lux,VEML_raw,VEML_white,lux_corrected,dayFrac,GA1A_V,GA1A_lux; // raw should be long int float range=5.05; // tested for the Arduino board I'm using. float white_corrected; int YR,MON,DAY,HR,MIN,SEC,GA1A_raw; const long int delayTime=120000L; const int SDpin=10, GA1Apin=A0; void setup() { Serial.begin(9600); Serial.println(F("Adafruit VEML7700 Test")); if (!veml.begin()){ Serial.println(F("Sensor not found")); while (1); } Serial.println(F("Sensor found")); veml.setGain(VEML7700_GAIN_1_8); // settings for bright light veml.setIntegrationTime(VEML7700_IT_25MS); Serial.print(F("Gain: ")); // optional serial port output switch (veml.getGain()) { case VEML7700_GAIN_1: Serial.println("1"); break; case VEML7700_GAIN_2: Serial.println("2"); break; case VEML7700_GAIN_1_4: Serial.println("1/4"); break; case VEML7700_GAIN_1_8: Serial.println("1/8"); break; } Serial.print(F("Integration Time (ms): ")); switch (veml.getIntegrationTime()) { // optional serial port output case VEML7700_IT_25MS: Serial.println("25"); break; case VEML7700_IT_50MS: Serial.println("50"); break; case VEML7700_IT_100MS: Serial.println("100"); break; case VEML7700_IT_200MS: Serial.println("200"); break; case VEML7700_IT_400MS: Serial.println("400"); break; case VEML7700_IT_800MS: Serial.println("800"); break; } if (!SD.begin(SDpin)) {Serial.println("SD card failed."); delay(50);exit(0); } Serial.println(F("SD card initialized.")); logfile=SD.open(filename,FILE_WRITE); if (!logfile) { Serial.println(F("Couldn't create file.")); delay(50); exit(0); } logfile.println(F("Date,Time,DayFrac,GA1A_raw,GA1A_V,GA1A_lux,lux,white,raw,lux_corrected")); logfile.flush(); } void loop() { DateTime now=rtc.now(); // Get date and time. YR=now.year(); MON=now.month(); DAY=now.day(); HR=now.hour(); MIN=now.minute(); SEC=now.second(); dayFrac=DAY+HR/24.+MIN/1440.+SEC/86400.; GA1A_raw=analogRead(GA1Apin); GA1A_V=range*GA1A_raw/1023; GA1A_lux=pow(10,GA1A_V); VEML_lux=veml.readLux(); VEML_white=veml.readWhite(); VEML_raw=veml.readALS(); lux_corrected=6.0135e-13*pow(VEML_lux,4)-9.3924e-9*pow(VEML_lux,3)+8.1488e-5*pow(VEML_lux,2)+1.0023*VEML_lux; white_corrected= 2E-15*pow(VEML_white,4) + 4E-12*pow(VEML_white,3) + 9E-06*pow(VEML_white,2) + 1.0179*VEML_white - 11.052; logfile.print(YR); logfile.print('/'); logfile.print(MON); logfile.print('/'); logfile.print(DAY); logfile.print(','); logfile.print(HR); logfile.print(':'); logfile.print(MIN); logfile.print(':'); logfile.print(SEC); logfile.print(','); logfile.print(dayFrac,6); logfile.print(','); logfile.print(GA1A_raw); logfile.print(','); logfile.print(GA1A_V,3); logfile.print(','); logfile.print(GA1A_lux,1); logfile.print(','); logfile.print(VEML_lux); logfile.print(','); logfile.print(VEML_white,0); logfile.print(','); logfile.print(VEML_raw); logfile.print(','); logfile.print(lux_corrected); logfile.print(','); logfile.println(white_corrected,0); logfile.flush(); delay(delayTime); } Chapter 20. Figure 20.5 /* UV_5.ino, D. Brooks, May 2019 Compares analog output from Adafruit GUVA_S12SD UVA/B analog aensor board (ID 1918). with Adafruit VEML6075 UVA/B/Index I2C senesor (ID 3964). */ #define ECHO_TO_SERIAL 1 #define ECHO_TO_FILE 1 #include #include #include // Includes files for both old and new datalogging shield clocks. #include "Adafruit_VEML6075.h" Adafruit_VEML6075 uv = Adafruit_VEML6075(); RTC_DS1307 rtc; // real time clock module, old datalogger shield // RTC_PCF8523 rtc; // new datalogger shield const int SDpin=10, UVpin=A1; const long int delayTime=60000L; float GUVA,VEML_UVA,VEML_UVB,VEML_UVIndex,dayFrac; int GUVA_digitized; File logfile; char filename[]="UV_TEST.CSV"; int YR,MON,DAY,HR,MIN,SEC; void setup() { pinMode(UVpin,INPUT); // for reading GUVA-S12SD UV sensor output Serial.begin(9600); Wire.begin(); rtc.begin(); //RTC.adjust(DateTime(__DATE__,__TIME__)); #if ECHO_TO_FILE if (!SD.begin(SDpin)) {Serial.println(F("Card failed.")); delay(50); exit(0); } Serial.println(F("SD card initialized.")); logfile = SD.open(filename, FILE_WRITE); if (!logfile) { Serial.println(F("Couldn't create file.")); delay(50); exit(0); } logfile=SD.open(filename,FILE_WRITE); Serial.println(F("Log file opened.")); #endif if (! uv.begin()) { Serial.println(F("Failed to communicate with VEML6075 sensor, check wiring?")); } Serial.println(F("Found VEML6075 sensor")); uv.setIntegrationTime(VEML6075_50MS); // Get the integration constant and print it! Serial.print(F("Integration time set to ")); switch (uv.getIntegrationTime()) { // optional to display this value... case VEML6075_50MS: Serial.print("50"); break; case VEML6075_100MS: Serial.print("100"); break; case VEML6075_200MS: Serial.print("200"); break; case VEML6075_400MS: Serial.print("400"); break; case VEML6075_800MS: Serial.print("800"); break; } Serial.println("ms"); uv.setHighDynamic(false); // Set the dynamic mode if (uv.getHighDynamic()) { // Get the mode Serial.println(F("High dynamic reading mode")); } else { Serial.println(F("Normal dynamic reading mode")); } uv.setForcedMode(false); //Set the mode if (uv.getForcedMode()) { //Get the mode, optional to display it... Serial.println(F("Forced reading mode")); } else { Serial.println(F("Continuous reading mode")); } // Set the calibration coefficients. For more ino about calculating UV index. // See Adafruit documentation and .cpp file. // Don't change these coefficients for "bare" sensor. uv.setCoefficients(2.22, 1.33, // UVA_A and UVA_B coefficients 2.95, 1.74, // UVB_C and UVB_D coefficients 0.001461, 0.002591); // UVA and UVB responses #if ECHO_TO_SERIAL Serial.println(F("yr,mon,dy,hr,min,sec,day_frac,GUVA,VEML_UVA,VEML_UVB,VEML_UVIndex")); #endif #if ECHO_TO_FILE logfile.println(F("yr,mon,dy,hr,min,sec,day_frac,GUVA,VEML_UVA,VEML_UVB,VEML_UVIndex")); logfile.flush(); #endif } void loop() { // Adafruit's Roithner GUVA sensor board GUVA_digitized=analogRead(UVpin); delay(10); GUVA=5.*GUVA_digitized/1023.; VEML_UVA=uv.readUVA(); VEML_UVB=uv.readUVB(); VEML_UVIndex=uv.readUVI(); DateTime now=rtc.now(); // Get date and time. YR=now.year(); MON=now.month(); DAY=now.day(); HR=now.hour(); MIN=now.minute(); SEC=now.second(); dayFrac=DAY+HR/24.+MIN/1440.+SEC/86400.; #if ECHO_TO_FILE logfile.print(YR); logfile.print(','); logfile.print(MON); logfile.print(','); logfile.print(DAY); logfile.print(','); logfile.print(HR); logfile.print(','); logfile.print(MIN); logfile.print(','); logfile.print(SEC); logfile.print(','); logfile.print(dayFrac,6); logfile.print(','); logfile.print(GUVA,5); logfile.print(','); logfile.print(VEML_UVA); logfile.print(','); logfile.print(VEML_UVB); logfile.print(','); logfile.println(VEML_UVIndex,2); logfile.flush(); #endif #if ECHO_TO_SERIAL Serial.print(YR); Serial.print(','); Serial.print(MON); Serial.print(','); Serial.print(DAY); Serial.print(','); Serial.print(HR); Serial.print(','); Serial.print(MIN); Serial.print(','); Serial.print(SEC); Serial.print(','); Serial.print(dayFrac,6); Serial.print(','); Serial.print(GUVA,5); Serial.print(','); Serial.print(VEML_UVA); Serial.print(','); Serial.print(VEML_UVB); Serial.print(','); Serial.println(VEML_UVIndex,2); #endif delay(delayTime); } Figure 20.9 // VEML6075_TFT.ino, D. Brooks, May 2019 // Displays UV Index on an ID 2088 Adafruit TFT screem. #include #include // Core graphics library #include // Hardware-specific library #include #include Adafruit_VEML6075 uv = Adafruit_VEML6075(); #define TFT_CS 10 #define TFT_RST 9 #define TFT_DC 8 #define BLACK 0x0000 #define WHITE 0xFFFF Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,TFT_DC,TFT_RST); const long int delayTime=2000L; void setup(void) { Serial.begin(9600); if (! uv.begin()) { Serial.println(F("Failed to communicate with VEML6075 sensor, check wiring?")); } uv.setHighDynamic(false); uv.setIntegrationTime(VEML6075_50MS); uv.setForcedMode(false); uv.setCoefficients(2.22, 1.33, // UVA_A and UVA_B coefficients // no Teflon sensor cover 2.95, 1.74, // UVB_C and UVB_D coefficients 0.001461, 0.002591); // UVA and UVB responses Serial.println(F("Found VEML6075 sensor")); tft.initR(INITR_144GREENTAB); // initialize a ST7735S chip, green tab Serial.println("TFT Initialized"); tft.fillScreen(BLACK); tft.setTextColor(WHITE); tft.setTextSize(2); } void loop() { tft.fillScreen(BLACK); tft.setCursor(2,2); tft.println("UV Index:"); tft.setCursor(2,30); tft.println(uv.readUVI()); delay(delayTime); } Chapter 21. Figure 21.6 // StringToNumber.ino, D. Brooks, August 2019 char s1[5],s2[4],s3[6]; float x=12.5,y=6.6,z=15.89; int i; char S[14],S1[5],S2[4],S3[6]; void setup() { Serial.begin(9600); Serial.println("Original data values: "); Serial.print(x,1); Serial.print(','); Serial.print(y,1); Serial.print(','); Serial.print(z,2); Serial.println(); // Convert numbers to packet string. // 5+1+4+1+6+1 char S[14],S1[5],S2[4],S3[6]; dtostrf(x,4,1,S1); dtostrf(y,3,1,S2); dtostrf(z,5,2,S3); for (i=0; i<=3; i++) { S[i]=S1[i]; } S[4]=','; for (i=5; i<=7; i++) { S[i]=S2[i-5]; } S[8]=','; for (i=9; i<=13; i++) { S[i]=S3[i-9]; } S[14]='\0'; Serial.print("packet string: "); Serial.println(S); for (i=0; i<=3; i++) { s1[i]=S[i]; } s1[i]='\0'; for (i=5; i<=7; i++) { s2[i-5]=S[i]; } s2[i]='\0'; for (i=9; i<=13; i++) { s3[i-9]=S[i]; } s1[3]='\0'; x=atof(s1); y=atof(s2); z=atof(s3); Serial.println("values extracted from packet string: "); Serial.print(x,1); Serial.print(','); Serial.print(y,1); Serial.print(','); Serial.println(z,2); } void loop() { } Figure 21.8 /* PacketRadio915_AM2315_BME280_TX.ino, D. Brooks, January 2018 Sends data from AM2315 T/RH and BME280 T/RH/P sensors. Puts all the TX code in the setup() function. Turns system on to collect data and send packet, then turns system off upon receipt of "done" signal from Pro Mini Board, using the TPL5110 low power module from adafruit.com. */ // setup for AM2315 #include #include Adafruit_AM2315 am2315; float am2315_T,am2315_H; #include #include #include #include Adafruit_BME280 bme; float T_BME,RH_BME,P_BME,P_inHg; // TX Radio Setup #define RF69_FREQ 900.0 // RX must match this frequency. #define RFM69_INT 3 // for UNO #define RFM69_CS 4 // #define RFM69_RST 2 // // The encryption key has to be the same as the one in the receiver. uint8_t key[] = { 0x11, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; RH_RF69 rf69(RFM69_CS, RFM69_INT); // Instantiate radio driver. int i; const int donePIN=5; char AMTString[7],AMRHString[7],T_BMEString[7],RH_BMEString[7],P_BMEString[7]; char RadioPacket[40],P_inHgString[6]; void setup() { pinMode(donePIN,OUTPUT); digitalWrite(donePIN,LOW); Serial.begin(9600); bme.begin(); am2315.begin(); Serial.println("sensors initiated"); pinMode(RFM69_RST, OUTPUT); digitalWrite(RFM69_RST, LOW); // manual reset TX digitalWrite(RFM69_RST, HIGH); delay(10); digitalWrite(RFM69_RST, LOW); delay(10); if (!rf69.init()) { Serial.println("RFM69 radio init failed"); while (1); } Serial.println("RFM69 radio init OK!"); // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module) // No encryption if (!rf69.setFrequency(RF69_FREQ)) { Serial.println("setFrequency failed"); } rf69.setTxPower(20, true); // range from 14-20 for power, 2nd arg must be true for 69HCW rf69.setEncryptionKey(key); Serial.print("RFM69 radio @"); Serial.print((int)RF69_FREQ); Serial.println(" MHz"); // read AM2315 am2315_T=am2315.readTemperature(); am2315_H=am2315.readHumidity(); delay(10); T_BME=bme.readTemperature(); delay(10); RH_BME=bme.readHumidity(); delay(10); P_BME=bme.readPressure(); delay(10); // Add pressure conversion to inches Hg. Mysite elevation is 131 m. // The formula needs elevation in km. P_inHg=(P_BME/100.)/exp(-0.119*0.131-0.0013*0.131*0.131); // mbar to sea level P_inHg*=29.921/1013.25; // to in Hg Serial.println("Values..."); Serial.print(P_BME/100.);Serial.print(','); Serial.println(P_inHg,2); Serial.print(am2315_T,2); Serial.print(',');Serial.print(am2315_H,2); Serial.print(',');Serial.print(T_BME,2); Serial.print(',');Serial.print(RH_BME,2);Serial.print(','); Serial.println(P_BME/100.,1); dtostrf(P_inHg,5,2,P_inHgString); dtostrf(am2315_H,6,2,AMRHString); dtostrf(am2315_T,6,2,AMTString); dtostrf(RH_BME,6,2,RH_BMEString); dtostrf(T_BME,6,2,T_BMEString); dtostrf(P_BME/100.,6,1,P_BMEString); Serial.println("Strings..."); Serial.print(AMTString);Serial.print(',');Serial.print(AMRHString); Serial.print(','); Serial.print(T_BMEString);Serial.print(',');Serial.print(RH_BMEString);Serial.print(','); Serial.print(P_BMEString);Serial.print(','); Serial.println(P_inHgString); for (i = 0; i <= 5; i++) { RadioPacket[i] = AMTString[i]; } RadioPacket[6] = ','; for (i = 7; i <= 12; i++) { RadioPacket[i] = AMRHString[i - 7];} RadioPacket[13]=','; for (i=14; i<=19; i++) { RadioPacket[i]=T_BMEString[i-14];} RadioPacket[20]=','; for (i=21; i<=26; i++) { RadioPacket[i]=RH_BMEString[i-21]; } RadioPacket[27]=','; for (i=28; i<=33; i++) { RadioPacket[i]=P_BMEString[i-28]; } RadioPacket[34]=','; for (i=35; i<=39; i++) {RadioPacket[i]=P_inHgString[i-35];} RadioPacket[40] = '\0'; rf69.send((uint8_t *)RadioPacket, strlen(RadioPacket)); rf69.waitPacketSent(); // Now wait for a reply uint8_t buf[RH_RF69_MAX_MESSAGE_LEN]; uint8_t len = sizeof(buf); if (rf69.waitAvailableTimeout(500)) { // Should be a reply message for us now if (rf69.recv(buf, &len)) { Serial.print("Got a reply: "); Serial.println((char*)buf); } else { Serial.println("Receive failed"); } } else { Serial.println("No reply, is another RFM69 listening?"); } delay(50); digitalWrite(donePIN,HIGH); // turn off power } void loop() { } Figure 21.9 /* PacketRadio915_AM2315_BME280_RX.ino, D. Brooks, August 2018 A modification of the example RX code for the RH-RF69 packet radio module. Receives a text string from the TX module (see TX_LowPower_TRHP.ino). Formats it for display on a SunFounder 20x4 LCD and saves with date/time stamp on an SD card, using Adafruit datalogging shield. */ #include #include #include #include #include // from sunfounder.com #include #define RF69_FREQ 900.0 #define SDpin 10 #define RFM69_INT 3 #define RFM69_CS 4 #define RFM69_RST 2 RTC_DS1307 rtc; LiquidCrystal_I2C lcd(0x27,20,4); RH_RF69 rf69(RFM69_CS, RFM69_INT); int i; File logfile; char filename[]="LOG00000.CSV"; //char P_inHg[]="xx.xx"; void setup() { Serial.begin(9600); SD.begin(); Wire.begin(); rtc.begin(); rtc.adjust(DateTime(__DATE__,__TIME__)); // only when connected to computer! Serial.print("Current time: "); DateTime now=rtc.now(); Serial.print(now.year()); Serial.print('/'); Serial.print(now.month()); Serial.print('/'); Serial.print(now.day()); Serial.print(' '); Serial.print(now.hour()); Serial.print(':'); Serial.print(now.minute()); Serial.print(':'); Serial.print(now.second()); Serial.println(); logfile=SD.open(filename,FILE_WRITE); // Setup for 20x4 SunFounder LCD lcd.init(); lcd.backlight(); lcd.clear(); lcd.setCursor(0,0); lcd.print("LCD setup..."); pinMode(RFM69_RST, OUTPUT); digitalWrite(RFM69_RST, LOW); // manual reset digitalWrite(RFM69_RST, HIGH); delay(10); digitalWrite(RFM69_RST, LOW); delay(10); if (!rf69.init()) { Serial.println("RFM69 radio init failed"); while (1); } Serial.println("RFM69 radio init OK!"); if (!rf69.setFrequency(RF69_FREQ)) { Serial.println("setFrequency failed"); } rf69.setTxPower(20, true); // range from 14-20 for power, // The encryption key has to be the same as the one in the TX. uint8_t key[] = { 0x11, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x11, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; rf69.setEncryptionKey(key); Serial.print("RFM69 radio @"); Serial.print((int)RF69_FREQ); Serial.println(" MHz"); } void loop() { if (rf69.available()) { DateTime now=rtc.now(); logfile.print(now.day()+now.hour()/24.+now.minute()/1440.+now.second()/86400.,6); logfile.print(','); // Should be a message for us now uint8_t buf[RH_RF69_MAX_MESSAGE_LEN]; uint8_t len = sizeof(buf); if (rf69.recv(buf, &len)) { if (!len) return; buf[len] = 0; Serial.println(); lcd.clear(); lcd.setCursor(0,0); lcd.print("AM2 T/RH "); for (i=0; i<=5; i++) { Serial.print((char)buf[i]); lcd.print((char)buf[i]); logfile.print((char)buf[i]); } Serial.print(','); logfile.print(','); for (i=7; i<=12; i++) { Serial.print((char)buf[i]); lcd.print((char)buf[i]); logfile.print((char)buf[i]); } Serial.print(','); logfile.print(','); lcd.setCursor(0,1); lcd.print("BME T/RH "); for (i=14; i<=19; i++) { Serial.print((char)buf[i]); lcd.print((char)buf[i]); logfile.print((char)buf[i]); } Serial.print(','); logfile.print(','); for (i=21; i<=26; i++) { Serial.print((char)buf[i]); lcd.print((char)buf[i]); logfile.print((char)buf[i]); } Serial.print(','); logfile.print(','); lcd.setCursor(0,2); lcd.print("BME P "); //char Pstring[7]; for (i=28; i<=33; i++) { Serial.print((char)buf[i]); //Pstring[i-28]=(char)buf[i]; lcd.print((char)buf[i]); logfile.print((char)buf[i]); } lcd.print(' '); lcd.print(' '); for (i=35; i<=39; i++) { lcd.print((char)buf[i]); //lcd.print(P_inHg[i-35]); } Serial.println(); logfile.print(','); logfile.print(now.year()); logfile.print(','); logfile.print(now.month()); logfile.print(','); logfile.print(now.day()); logfile.print(','); logfile.print(now.hour()); logfile.print(','); logfile.print(now.minute()); logfile.print(','); logfile.print(now.second()); logfile.print(','); logfile.println(); logfile.flush(); lcd.setCursor(0,1); lcd.setCursor(0,3); lcd.print(now.year()); lcd.print('/'); lcd.print(now.month()); lcd.print('/'); lcd.print(now.day()); lcd.print(' '); lcd.print(now.hour()); lcd.print(':'); lcd.print(now.minute()); lcd.print(':'); lcd.print(now.second()); } else { lcd.print("Receive failed"); } } } Figure 21.13 /* 433_DHT_TX.ino, D. Brooks, December 2019 TX with Pro Mini. Reads DHT22 data from pin D8. */ #include #include RH_ASK rf_driver; #include #define DHTPIN 8 #define DHTTYPE DHT22 DHT dht(DHTPIN,DHTTYPE); float h,t; char TS[6],HS[6],S[12]; const int j; int i; void setup() { rf_driver.init(); Serial.begin(9600); dht.begin(); } void loop() { t=dht.readTemperature(); h=dht.readHumidity(); Serial.print(t,1); Serial.print(','); Serial.println(h,1); dtostrf(t,5,1,TS); dtostrf(h,5,1,HS); for (i=0; i<=4; i++) { S[i]=TS[i]; } S[5]=','; for (i=6; i<=10; i++) { S[i]=HS[i-6]; } S[11]='\0'; const char *packet=S; rf_driver.send((uint8_t *)packet,strlen(packet)); rf_driver.waitPacketSent(); Serial.print("Packet sent: "); Serial.println(S); delay(2000); } Chapter 22. Figure 22.3 /* Feather_SD.ino, D. Brooks, May 2019 Save date/time and TMP36 data to SD card. */ #include #include #include #include #define cardSelect 4 RTC_PCF8523 rtc; File logfile; char filename[]="SDLOGGER.CSV"; int YR,MON,DAY,HR,MIN,SEC; const int TMP36pin=A0; const long int delayTime=60000L; float T; void setup () { Serial.begin(9600); if (! rtc.begin()) { Serial.println("Couldn't find RTC"); while (1); } if (! rtc.initialized()) { Serial.println("RTC is NOT running!"); // following line sets the RTC to the date & time this sketch was compiled rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // This line sets the RTC with an explicit date & time, for example to set // January 21, 2014 at 3am you would call: // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); } if (!SD.begin(cardSelect)) { Serial.println("Card init. failed!"); } logfile=SD.open(filename,FILE_WRITE); Serial.print("Writing to "); Serial.println(filename); if (! rtc.initialized()) { Serial.println("RTC is NOT running!"); // following line sets the RTC to the date & time this sketch was compiled rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // This line sets the RTC with an explicit date & time, for example to set // January 21, 2014 at 3am you would call: // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); } } void loop () { T=3.3*analogRead(TMP36pin)/1023.; T=100.*(T-0.5); DateTime now = rtc.now(); YR=now.year(); MON=now.month(); DAY=now.day(); HR=now.hour(); MIN=now.minute(); SEC=now.second(); Serial.print(YR); Serial.print('/'); Serial.print(MON); Serial.print('/'); Serial.print(DAY); Serial.print(' '); Serial.print(HR); Serial.print(':'); Serial.print(MIN); Serial.print(':'); Serial.print(SEC); Serial.print(' '); Serial.print(DAY+HR/24.+MIN/1440.+SEC/86400.,6); Serial.print(' '); Serial.println(T,2); logfile.print(YR); logfile.print(','); logfile.print(MON); logfile.print(','); logfile.print(DAY); logfile.print(','); logfile.print(HR); logfile.print(','); logfile.print(MIN); logfile.print(','); logfile.print(SEC); logfile.print(','); logfile.print(DAY+HR/24.+MIN/1440.+SEC/86400.,6); logfile.print(','); logfile.println(T,2); logfile.flush(); delay(delayTime); } Appendix 1. Figure A1.1 /* float_math.ino. D. Brooks, March 2019 Demonstrates time required for integer and real number calculations. */ int i,y; unsigned long int time1,time2; float Y; void setup() { Serial.begin(9600); float x=3.3, y=7.0; Serial.println(x*y,8); if (x*y == 23.1) Serial.println("equality!"); else Serial.println("not euite equal"); Serial.println(25./3.,8); if ((abs(x*y)-23.1)<1e-6) Serial.println("equal"); time1=micros(); Serial.print("time 1 ");Serial.println(time1); for (i=1; i<=5000; i++) { y=5*5; } time2=micros(); Serial.print("time 2 ");Serial.println(time2); Serial.println(time2-time1); time1=micros(); Serial.print("time 1 ");Serial.println(time1); for (i=1; i<=5000; i++) { Y=5.*5.; } time2=micros(); Serial.print("time 2 ");Serial.println(time2); Serial.println(time2-time1); } void loop() { } Appendix 4. Figure A4.1 /* NormalDistribution.ino, Feb. 2019, D. Brooks Generates some normally distributed numbers and calculates min, max, mean, and standard deviation, assuming the range of values is never more than +/-1000. */ long u1,u2; float U1,U2,Theta,N1,N2; float sumX=0.,sumXX=0.,mean,stdDev; float minX=1000.,maxX=-1000.; int N=40; void setup() { Serial.begin(9600); randomSeed(analogRead(0)); for (int i=1; i<=N; i++) { // Generate 2N normally distributed numbers // using the Box-Muller transform. u1=random(0,1000); u2=random(0,1000); U1=u1/1000.; U2=u2/1000.; // It's possible that U1 or U2 could be 0, for // which the log doesn't exist. Fix it... if (U1==0) U1=.001; if (U2==0) U2=.001; Theta=2*M_PI*U2; N1=sqrt(-2*log(U1))*cos(Theta); N2=sqrt(-2*log(U1))*sin(Theta); sumX=sumX+N1+N2; sumXX=sumXX+N1*N1+N2*N2; if (N1>maxX) maxX=N1; if (N2>maxX) maxX=N2; if (N1