Md. Khairul Alam
Published © GPL3+

Solar Powered Environmental Monitoring Kit

A solar-powered open source kit for monitoring air quality, sound level, humidity, and temperature.

IntermediateFull instructions provided2 hours5,370
Solar Powered Environmental Monitoring Kit

Things used in this project

Hardware components

Seeeduino GPRS
Seeeduino GPRS is an Arduino board with integrated GPRS module (SIM800) and support GSM, Bluetooth and FM. Instead of using it you may use Seeeduino V4 and Seeed Studio GPRS Shield V3.0.
×1
Base Shield V2
Seeed Base Shield V2
Base Shield is very useful for connecting Grove Sensors to Arduino without any soldering.
×1
Seeed 1W Solar Panel 80X100
1W Solar Panel is required for charging the Battery and providing the required power for the kit. This panel is enough for delivering the power required to run the kit at least 48 hours with 4 hours sunlight. The size is important to adjust it in the 3D printed case.
×1
Seeed LiPo Rider Pro
The LiPo Rider Pro board allows you ride the solar wave to run your favorite 5V device. The LiPo Rider Pro board is the ideal green power solution for your outdoor sensor design. Attach the LiPo Rider Pro board to your sensor board and it can run on solar power forever! Instead of LiPo Rider Pro you may also use Solar Charger Shield v2.2.
×1
Grove - Air quality sensor v1.3
Seeed Grove - Air quality sensor v1.3
The sensor can detect carbon monoxide, alcohol, acetone, thinner, formaldehyde and other slightly toxic gases. It is compatible with 5V and 3.3V power supply. The sensor is used to monitor the air quality of the environment.
×1
Seeed Grove - Loudness Sensor
The Grove - Loudness Sensor is designed to detect the loudness of environmental sound. Based on amplifier LM2904 and a built-in microphone, it amplifies and filters the high frequency signal that received from the microphone, and outputs a positive envelop. This will make for Arduino's signal acquisition. The output value depends on the level of sound input. It gives an analog output. I mapped the analog value to dB with a sound level meter. The output dB is not accurate enough and for testing purpose only.
×1
Seeed Grove - Temperature & Humidity Sensor (DHT11)
Different types of Temperature and Humidity Sensors are available with different accuracy and price. I used DHT11 for the project.
×1
Li-Ion Battery 1000mAh
Li-Ion Battery 1000mAh
1000mAh is required for powering the device for 48 hours assuming the data will be updated once in a minute. Capacity depends on the frequency of the data update.
×1

Software apps and online services

Arduino IDE
Arduino IDE
ThingSpeak API
ThingSpeak API

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Main Body of the box

The original design was made by Thingiverse user deba168. I modified the box according to the size of the solar panel. Thanks, Mr. deba168 for his nice design.

Front Cover

The original file was designed by deba168.

Code

Code for Testing

Arduino
#include <SoftwareSerial.h>
#include "AirQuality.h"
#include "Arduino.h"
#include "DHT.h"

/* Create object named GPR of the class SoftwareSerial */
SoftwareSerial GPR(8, 7);

#define DHTPIN 4     // what digital pin DHT sensor connected to
#define DHTTYPE DHT11   // model of the sensor DHT11 

#define SIM800_POWER_PIN        9
#define SIM800_POWER_STATUS     12
#define DEFAULT_TIMEOUT     5

DHT dht(DHTPIN, DHTTYPE);

AirQuality airqualitysensor;
int current_quality =-1;

//replace with your own API key
String API_KEY = "api_key=B3FPE7GTVY1ISGQS";
String field = "";


const int sampleWindow = 50;                              // Sample window width in mS (50 mS = 20Hz)
unsigned int sample;
float value;
float temp, humid;
int airQuality;

 //--------------------------------------------------------------------------------------------
 //                                            SETUP
 //--------------------------------------------------------------------------------------------
void setup() 
{
   GPR.begin(9600);  /* Define baud rate for software serial communication */
   Serial.begin(9600);                                    //Serial comms for debugging
   airqualitysensor.init(14);
   dht.begin();
   //start sim800
   pinMode(SIM800_POWER_PIN, OUTPUT);
   digitalWrite(SIM800_POWER_PIN,HIGH);
   delay(200);
   digitalWrite(SIM800_POWER_PIN,LOW);
   delay(2000);
   digitalWrite(SIM800_POWER_PIN,HIGH);
   delay(3000);  

}
 
//--------------------------------------------------------------------------------------------
 //                                         MAIN LOOP
 //--------------------------------------------------------------------------------------------
  
void loop() 
{
 
 
 value = calculate_sound_in_db();
 API_KEY = "api_key=B3FPE7GTVY1ISGQS";
 API_KEY += "&field1=";
 API_KEY += value;
 send_data();


 airQuality = calculate_air_quality();
 API_KEY = "api_key=B3FPE7GTVY1ISGQS";
 API_KEY += "&field4=";
 API_KEY += airQuality;
 send_data();
 
 calculate_temp_humid();
 API_KEY = "api_key=B3FPE7GTVY1ISGQS";
 API_KEY += "&field2=";
 API_KEY += temp;
 send_data();
 
 API_KEY = "api_key=B3FPE7GTVY1ISGQS";
 API_KEY += "&field3=";
 API_KEY += humid;
 send_data();
 
 delay(5000);
}

//this function calculate sound level in dB
float calculate_sound_in_db(){
   unsigned long startMillis= millis();                   // Start of sample window
   float peakToPeak = 0;                                  // peak-to-peak level
 
   unsigned int signalMax = 0;                            //minimum value
   unsigned int signalMin = 1024;                         //maximum value
 
                                                          // collect data for 50 mS
   while (millis() - startMillis < sampleWindow)
   {
      sample = analogRead(A1);                             //get reading from microphone
      if (sample < 1024)                                  // toss out spurious readings
      {
         if (sample > signalMax)
         {
            signalMax = sample;                           // save just the max levels
         }
         else if (sample < signalMin)
         {
            signalMin = sample;                           // save just the min levels
         }
      }
   }
   peakToPeak = signalMax - signalMin;                    // max - min = peak-peak amplitude
   //Serial.println(peakToPeak);                                     //write calibrated deciBels
   float db = map(peakToPeak,0,1000,48,120);             //calibrate for deciBels
   Serial.print(db);                                     //write calibrated deciBels
   Serial.println(" dB");                                  //write units
   return db;
}

int calculate_air_quality(){
  current_quality=airqualitysensor.slope();
    if (current_quality >= 0)// if a valid data returned.
    {
        if (current_quality==0)
            Serial.println("High pollution! Force signal active");
        else if (current_quality==1)
            Serial.println("High pollution!");
        else if (current_quality==2)
            Serial.println("Low pollution!");
        else if (current_quality ==3)
            Serial.println("Fresh air");
    }
  return current_quality;
}

void calculate_temp_humid(){
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }

  temp = t;
  humid = h;
  
  Serial.print("Humidity: ");
  Serial.print(h);
  Serial.print(" %\t");
  Serial.print("Temperature: ");
  Serial.print(t);
  Serial.print(" *C ");
}
//this function sends data to ThingSpeak server
//it takes approximately 13 seconds to send data to server using GPRS
void send_data(){ 
  Serial.print("Checking Communication...");
  GPR.println("AT"); /* Check Communication */
  delay(200);
  ShowSerialData(); /* Print response on the serial monitor */
  delay(100);
  /* Configure bearer profile 1 */
  Serial.print("Set connection type as GPRS: ");
  GPR.println("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"");  /* Connection type GPRS */
  delay(1500);
  ShowSerialData();
  delay(100);
  Serial.print("Set APN as gpinternet: ");
  GPR.println("AT+SAPBR=3,1,\"APN\",\"gpinternet\"");  /* APN of the provider */
  delay(1500);
  ShowSerialData();
  delay(100);
  Serial.print("Open GPRS context: ");
  GPR.println("AT+SAPBR=1,1"); /* Open GPRS context */
  delay(1500);
  ShowSerialData();
  delay(100);
  Serial.print("Query the GPRS context: ");
  GPR.println("AT+SAPBR=2,1"); /* Query the GPRS context */
  delay(1500);
  ShowSerialData();
  delay(100);
  Serial.print("Initilize HTTP service: ");
  GPR.println("AT+HTTPINIT");  /* Initialize HTTP service */
  delay(1500); 
  ShowSerialData();
  delay(100);
  Serial.print("HTTP parameter set: ");
  GPR.println("AT+HTTPPARA=\"CID\",1");  /* Set parameters for HTTP session */
  delay(1500);
  ShowSerialData();
  delay(100);
  
  Serial.print("HTTP URL set: ");
  GPR.println("AT+HTTPPARA=\"URL\",\"api.thingspeak.com/update\"");  
  delay(2000);
  ShowSerialData();
  delay(100);
  Serial.print("HTTP Post data: ");
  GPR.println("AT+HTTPDATA=33,10000"); 
  delay(2000);
  ShowSerialData();
  delay(100);
  Serial.print("HTTP data set: ");  
  //GPR.println("api_key=B3FPE7GTVY1ISGQS&field1=2");
  GPR.println(API_KEY);
  delay(2000);
  ShowSerialData();
  delay(100);

  Serial.print("Start post session: ");
  GPR.println("AT+HTTPACTION=1");  /* Start POST session */
  delay(2000);
  ShowSerialData();
  delay(100);
  Serial.print("Terminate HTTP service: ");  
  GPR.println("AT+HTTPTERM");  /* Terminate HTTP service */
  delay(2000);
  ShowSerialData();
  delay(100);
  Serial.print("Close GPRS context: ");
  GPR.println("AT+SAPBR=0,1"); /* Close GPRS context */
  delay(2000);
  ShowSerialData();
  delay(100);
}

//this function is used for debugging purpose
//print the response back from gprs module
void ShowSerialData()
{
  while(GPR.available()!=0)  /* If data is available on serial port */
  Serial.write(char (GPR.read())); /* Print character received on to the serial monitor */
}
//interrupt service routine for air quality sensor
ISR(TIMER1_OVF_vect)
{
  if(airqualitysensor.counter==61)//set 2 seconds as a detected duty
  {

      airqualitysensor.last_vol=airqualitysensor.first_vol;
      airqualitysensor.first_vol=analogRead(A0);
      airqualitysensor.counter=0;
      airqualitysensor.timer_index=1;
      PORTB=PORTB^0x20;
  }
  else
  {
    airqualitysensor.counter++;
  }
}

Final Code

Arduino
#include <SoftwareSerial.h>
#include "AirQuality.h"
#include "Arduino.h"
#include "DHT.h"

/* Create object named GPRS of the class SoftwareSerial */
SoftwareSerial GPRS(8, 7);

#define DHTPIN 4     // what digital pin DHT sensor connected to
#define DHTTYPE DHT11   // model of the sensor DHT11 

#define SIM800_POWER_PIN        9
#define SIM800_POWER_STATUS     12
#define DEFAULT_TIMEOUT     5

DHT dht(DHTPIN, DHTTYPE);

AirQuality airqualitysensor;
int current_quality =-1;

//replace with your own API key
String API_KEY = "api_key=B3FPE7GTVY1ISGQS";
String field = "";


const int sampleWindow = 50;                              // Sample window width in mS (50 mS = 20Hz)
unsigned int sample;
float value;
float temp, humid;
int airQuality;

 //--------------------------------------------------------------------------------------------
 //                                            SETUP
 //--------------------------------------------------------------------------------------------
void setup() 
{
   GPRS.begin(9600);  /* Define baud rate for software serial communication */
   airqualitysensor.init(14);
   dht.begin();
   //start sim800
   pinMode(SIM800_POWER_PIN, OUTPUT);
   digitalWrite(SIM800_POWER_PIN,HIGH);
   delay(200);
   digitalWrite(SIM800_POWER_PIN,LOW);
   delay(2000);
   digitalWrite(SIM800_POWER_PIN,HIGH);
   delay(3000);  

}
 
//--------------------------------------------------------------------------------------------
 //                                         MAIN LOOP
 //--------------------------------------------------------------------------------------------
  
void loop() 
{
 
 
 value = calculate_sound_in_db();
 airQuality = calculate_air_quality();
 calculate_temp_humid();
 
 API_KEY = "api_key=B3FPE7GTVY1ISGQS";
 API_KEY += "&field1=";
 API_KEY += value;
 API_KEY += "&field2=";
 API_KEY += temp;
 API_KEY += "&field3=";
 API_KEY += humid;
 API_KEY += "&field4=";
 API_KEY += airQuality;
 send_data();
 
 delay(120000);
}

//this function calculate sound level in dB
float calculate_sound_in_db(){
   unsigned long startMillis= millis();                   // Start of sample window
   float peakToPeak = 0;                                  // peak-to-peak level
 
   unsigned int signalMax = 0;                            //minimum value
   unsigned int signalMin = 1024;                         //maximum value
 
                                                          // collect data for 50 mS
   while (millis() - startMillis < sampleWindow)
   {
      sample = analogRead(A1);                             //get reading from microphone
      if (sample < 1024)                                  // toss out spurious readings
      {
         if (sample > signalMax)
         {
            signalMax = sample;                           // save just the max levels
         }
         else if (sample < signalMin)
         {
            signalMin = sample;                           // save just the min levels
         }
      }
   }
   peakToPeak = signalMax - signalMin;                    // max - min = peak-peak amplitude
   //Serial.println(peakToPeak);                                     //write calibrated deciBels
   float db = map(peakToPeak,0,1000,48,120);             //calibrate for deciBels
   return db;
}

int calculate_air_quality(){
  current_quality=airqualitysensor.slope();
  return current_quality;
}

void calculate_temp_humid(){
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    return;
  }

  temp = t;
  humid = h;
}
//this function sends data to ThingSpeak server
//it takes approximately 13 seconds to send data to server using GPRSS
void send_data(){  
  GPRS.println("AT"); /* Check Communication */
  delay(200);
  /* Configure bearer profile 1 */
  GPRS.println("AT+SAPBR=3,1,\"CONTYPE\",\"GPRSS\"");  /* Connection type GPRSS */
  delay(500);
  GPRS.println("AT+SAPBR=3,1,\"APN\",\"gpinternet\"");  /* APN of the provider */
  delay(1500);
  GPRS.println("AT+SAPBR=1,1"); /* Open GPRSS context */
  delay(1500);
  GPRS.println("AT+SAPBR=2,1"); /* Query the GPRSS context */
  delay(1500);
  GPRS.println("AT+HTTPINIT");  /* Initialize HTTP service */
  delay(1500); 
  GPRS.println("AT+HTTPPARA=\"CID\",1");  /* Set parameters for HTTP session */
  delay(1500);
  GPRS.println("AT+HTTPPARA=\"URL\",\"api.thingspeak.com/update\"");  
  delay(2000);
  GPRS.println("AT+HTTPDATA=33,10000"); 
  delay(2000);
  //GPRS.println("api_key=B3FPE7GTVY1ISGQS&field1=2");
  GPRS.println(API_KEY);
  delay(2000);
  GPRS.println("AT+HTTPACTION=1");  /* Start POST session */
  delay(2000); 
  GPRS.println("AT+HTTPTERM");  /* Terminate HTTP service */
  delay(2000);
  GPRS.println("AT+SAPBR=0,1"); /* Close GPRSS context */
  delay(2000);
}

//interrupt service routine for air quality sensor
ISR(TIMER1_OVF_vect)
{
  if(airqualitysensor.counter==61)//set 2 seconds as a detected duty
  {

      airqualitysensor.last_vol=airqualitysensor.first_vol;
      airqualitysensor.first_vol=analogRead(A0);
      airqualitysensor.counter=0;
      airqualitysensor.timer_index=1;
      PORTB=PORTB^0x20;
  }
  else
  {
    airqualitysensor.counter++;
  }
}

Final Sketch with Power Saving

Arduino
Code with Sleep Mode Enabled for both Arduino & SIM800
#include <SoftwareSerial.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include "AirQuality.h"
#include "Arduino.h"
#include "DHT.h"

/* Create object named GPRS of the class SoftwareSerial */
SoftwareSerial GPRS(8, 7);

#define DHTPIN 4     // what digital pin DHT sensor connected to
#define DHTTYPE DHT11   // model of the sensor DHT11 

#define SIM800_POWER_PIN        9
#define SIM800_POWER_STATUS     12
#define DEFAULT_TIMEOUT     5
#define DTR_PIN 11

DHT dht(DHTPIN, DHTTYPE);

AirQuality airqualitysensor;
int current_quality =-1;

//replace with your own API key
String API_KEY = "api_key=B3FPE7GTVY1ISGQS";
String field = "";

//Value for watchdog timer interrupt.
volatile int f_wdt = 1;
int seconds = 0;
int minutes = 2;
int hours = 0;
int interval = ((hours*60*60) + (minutes*60) + (seconds))/8;
int timerCounter = 0;

const int sampleWindow = 50;                              // Sample window width in mS (50 mS = 20Hz)
unsigned int sample;
float value;
float temp, humid;
int airQuality;


//ISR for watchdog timer.
ISR(WDT_vect)
{
  if(f_wdt == 0)
  {
    f_wdt=1;
    timerCounter++;
  }
  else
  {
    Serial.println("WDT Overrun!!!");
  }
}

 //--------------------------------------------------------------------------------------------
 //                                            SETUP
 //--------------------------------------------------------------------------------------------
void setup() 
{
   GPRS.begin(9600);  /* Define baud rate for software serial communication */
   airqualitysensor.init(14);
   dht.begin();
   setupWdt();
   //start sim800
   pinMode(SIM800_POWER_PIN, OUTPUT);
   digitalWrite(SIM800_POWER_PIN,HIGH);
   delay(200);
   digitalWrite(SIM800_POWER_PIN,LOW);
   delay(2000);
   digitalWrite(SIM800_POWER_PIN,HIGH);
   delay(3000);  
   pinMode(DTR_PIN, OUTPUT);
   digitalWrite(DTR_PIN, HIGH); //this pin should be high to enable sleep mode of sim800

}
 
//--------------------------------------------------------------------------------------------
 //                                         MAIN LOOP
 //--------------------------------------------------------------------------------------------
  
void loop() 
{
 if(f_wdt == 1)
  {
    if (timerCounter == interval)
    {
      //wdt_disable();
      //Serial.println(F("Tried to stop the watchdog."));
      //wdt_reset();

      value = calculate_sound_in_db();
      airQuality = calculate_air_quality();
      calculate_temp_humid();
      //field should be according to ThingSpeak chennel seting
      API_KEY = "api_key=B3FPE7GTVY1ISGQS";
      API_KEY += "&field1=";
      API_KEY += value;
      API_KEY += "&field2=";
      API_KEY += temp;
      API_KEY += "&field3=";
      API_KEY += humid;
      API_KEY += "&field4=";
      API_KEY += airQuality;
      send_data();

      //Reset timer.
      timerCounter = 0;
      //wdt_enable();
      //Serial.println(F("Tried to re-enable watchdog."));
    }

    /* Don't forget to clear the flag. */
    f_wdt = 0;

    /* Re-enter sleep mode. */
    enterSleep();
  }
  else
  {
    /* Do nothing. */
  }

}

//this function calculate sound level in dB
float calculate_sound_in_db(){
   unsigned long startMillis= millis();                   // Start of sample window
   float peakToPeak = 0;                                  // peak-to-peak level
 
   unsigned int signalMax = 0;                            //minimum value
   unsigned int signalMin = 1024;                         //maximum value
 
                                                          // collect data for 50 mS
   while (millis() - startMillis < sampleWindow)
   {
      sample = analogRead(A1);                             //get reading from microphone
      if (sample < 1024)                                  // toss out spurious readings
      {
         if (sample > signalMax)
         {
            signalMax = sample;                           // save just the max levels
         }
         else if (sample < signalMin)
         {
            signalMin = sample;                           // save just the min levels
         }
      }
   }
   peakToPeak = signalMax - signalMin;                    // max - min = peak-peak amplitude
   //Serial.println(peakToPeak);                                     //write calibrated deciBels
   float db = map(peakToPeak,0,1000,48,120);             //calibrate for deciBels
   return db;
}

int calculate_air_quality(){
  current_quality=airqualitysensor.slope();
  return current_quality;
}

void calculate_temp_humid(){
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    return;
  }

  temp = t;
  humid = h;
}
//this function sends data to ThingSpeak server
//it takes approximately 13 seconds to send data to server using GPRSS
void send_data(){  
  GPRS.println("AT"); /* Check Communication */
  delay(200);
  GPRS.println("AT"); /* Check Communication */
  delay(200);
  GPRS.println("AT+CSCLK=0"); /* disable sleep mode */
  delay(500);
  /* Configure bearer profile 1 */
  GPRS.println("AT+SAPBR=3,1,\"CONTYPE\",\"GPRSS\"");  /* Connection type GPRSS */
  delay(500);
  GPRS.println("AT+SAPBR=3,1,\"APN\",\"gpinternet\"");  /* APN of the provider */
  delay(1500);
  GPRS.println("AT+SAPBR=1,1"); /* Open GPRSS context */
  delay(1500);
  wdt_reset(); //manually reset wdt timer to prevent interrupt
  GPRS.println("AT+SAPBR=2,1"); /* Query the GPRSS context */
  delay(1500);
  GPRS.println("AT+HTTPINIT");  /* Initialize HTTP service */
  delay(1500); 
  GPRS.println("AT+HTTPPARA=\"CID\",1");  /* Set parameters for HTTP session */
  delay(1500);
  wdt_reset();
  GPRS.println("AT+HTTPPARA=\"URL\",\"api.thingspeak.com/update\"");  
  delay(2000);
  GPRS.println("AT+HTTPDATA=33,10000"); 
  delay(2000);
  wdt_reset();
  //GPRS.println("api_key=B3FPE7GTVY1ISGQS&field1=2");
  GPRS.println(API_KEY);
  delay(2000);
  GPRS.println("AT+HTTPACTION=1");  /* Start POST session */
  delay(2000); 
  wdt_reset();
  GPRS.println("AT+HTTPTERM");  /* Terminate HTTP service */
  delay(2000);
  GPRS.println("AT+SAPBR=0,1"); /* Close GPRSS context */
  delay(2000);
  wdt_reset();
  GPRS.println("AT+CSCLK=2"); /* enable sleep mode */
  delay(500);
}

void enterSleep(void)
{
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();

  /* Now enter sleep mode. */
  sleep_mode();

  /* The program will continue from here after the WDT timeout*/
  sleep_disable(); /* First thing to do is disable sleep. */

  /* Re-enable the peripherals. */
  power_all_enable();
}

void setupWdt()
{
  /*** Setup the WDT ***/

  /* Clear the reset flag. */
  MCUSR &= ~(1<<WDRF);

  /* In order to change WDE or the prescaler, we need to
   * set WDCE (This will allow updates for 4 clock cycles).
   */
  WDTCSR |= (1<<WDCE) | (1<<WDE);

  /* set new watchdog timeout prescaler value */
  WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */

  /* Enable the WD interrupt (note no reset). */
  WDTCSR |= _BV(WDIE);
}

//interrupt service routine for air quality sensor
ISR(TIMER1_OVF_vect)
{
  if(airqualitysensor.counter==61)//set 2 seconds as a detected duty
  {

      airqualitysensor.last_vol=airqualitysensor.first_vol;
      airqualitysensor.first_vol=analogRead(A0);
      airqualitysensor.counter=0;
      airqualitysensor.timer_index=1;
      PORTB=PORTB^0x20;
  }
  else
  {
    airqualitysensor.counter++;
  }
}

Credits

Md. Khairul Alam

Md. Khairul Alam

24 projects • 214 followers
Engineer, developer, maker, and hacker.
Contact

Comments

Add projectSign up / Login