├── .conf-mysensors ├── AirQuality-MICS2614.ino ├── LeafWetnessSensor.ino ├── README.md ├── 1.3 ├── UVsensor.ino ├── VibrationSensor.ino ├── MotionSensor2.ino ├── MQ135.ino ├── DustSensor.ino ├── MQ135dgi.ino ├── SoundSensor2.ino ├── WaterMeterPulseSensor2.ino ├── PressureSensor.ino ├── MQ2.ino └── MQv0.ino ├── ThingSpeak.pl ├── .travis.yml ├── TSL1401 ├── SoilMoistSensorSHT1x.ino ├── MutichannelGasSensor.h ├── DustSensor.ino ├── VibrationSensor.ino ├── FloodSensor.ino ├── AirModule.h ├── SoundSensor2.ino ├── AirQuality-CO2-MH-Z14.ino ├── AirQuality-CO-MH-Z14A.ino ├── DustSensor_Sharp_GP2Y1010AU.ino ├── AirQuality-MQ135.ino ├── AirQuality-HCHO.ino ├── WaterMeterPulseSensor2-gs.ino ├── EnergyMeterPulseSensorTCRT5000.ino ├── LuxSensor-c.ino ├── WaterMeterPulseSensor2.ino ├── WaterMeterPulseSensor3-gs.ino ├── DustSensor_Shinyei_PPD42NS.ino ├── LuxUVSensor-c.ino ├── SoilMoistSensor.ino ├── DustSensor-BJHIKE-HK-A5.ino ├── AirQuality-CO-NO2-NH3.ino ├── PressureSensor.ino ├── MutichannelGasSensor.cpp ├── mysensors-gw1.4.pl └── PressureSensor-c.ino /.conf-mysensors: -------------------------------------------------------------------------------- 1 | [Internal] 2 | hardware_domo=29 3 | domo_ip=192.168.0.28 4 | domo_port=8080 5 | domo_login= 6 | domo_pass= 7 | usb_port=/dev/ttyUSB0 8 | gw_ip=192.168.0.28 9 | gw_port=5001 10 | -------------------------------------------------------------------------------- /AirQuality-MICS2614.ino: -------------------------------------------------------------------------------- 1 | /* 2 | O3 sensor - MiCS-2614 3 | 4 | .......C 5 | .......| 6 | A___|___B [SENSOR] 7 | .......| 8 | .......| 9 | .......D 10 | 11 | A-B -> Heating resistance 12 | C-D -> Measuring point 13 | +5 / GND ? 14 | 15 | datasheet: http://www.sgxsensortech.com/content/uploads/2014/07/MICS-%E2%80%93-2614-Ozone.pdf 16 | http://www.mymectronic.com/datasheet/13059_4173144_mics-2614.pdf 17 | http://www.cdiweb.com/datasheets/e2v/MiCsQuickStartEvaluationBoard-CDI.pdf 18 | 19 | (1000,8) (100,1) (10,0.6) 20 | 21 | */ 22 | -------------------------------------------------------------------------------- /LeafWetnessSensor.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define ANALOG_INPUT_LEAFWETNESS_SENSOR 0 // Digital input did you attach your soil sensor. 5 | #define CHILD_ID 0 // Id of the sensor child 6 | 7 | MySensor gw; 8 | unsigned long SLEEP_TIME = 30*1000; // sleep time between reads (seconds * 1000 milliseconds) 9 | MyMessage msg(CHILD_ID, V_TRIPPED); 10 | int lastSoilValue = -1; 11 | 12 | void setup() 13 | { 14 | 15 | gw.begin(); 16 | 17 | // Send the sketch version information to the gateway and Controller 18 | gw.sendSketchInfo("Leaf Wetness Sensor", "1.0"); 19 | // Register all sensors to gw (they will be created as child devices) 20 | gw.present(CHILD_ID, S_MOTION); 21 | } 22 | 23 | void loop() 24 | { 25 | 26 | 27 | // Read digital soil value 28 | int soilValue = ((float)analogRead(ANALOG_INPUT_LEAFWETNESS_SENSOR)*100/1023); 29 | if (soilValue != lastSoilValue) { 30 | Serial.println(soilValue); 31 | gw.send(msg.set(soilValue)); // Send the inverse to gw as tripped should be when no water in soil 32 | lastSoilValue = soilValue; 33 | } 34 | // Power down the radio 35 | gw.sleep(SLEEP_TIME); 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | arduino 2 | ======= 3 | MySensors scripts 4 | 5 | Air Quality: 6 | * AirQuality-MQ135.ino : for CO2/COV ... validated 7 | * AirQuality-CO2-MH-Z14.ino: CO2 through calibrated MH-Z14, validated 8 | * AirQuality-Multiple_Gas_Sensor.ino : MQ2 MQ6 MQ131 MQ135 TGS2600 TGS2602 sensors 9 | * DustSensor-*.ino : Dust sensors from several providers, validated for DSM 10 | * AirQuality-MQ2.ino : for ethanol... ongoing 11 | * AirQuality-MICS2614.ino: on going 12 | * AirQuality-CO-NO2-NH3.ino: for Mics-6814 validated 13 | * AirQuality-HCHO.ino: for HSHO, validated 14 | 15 | Environmental sensors: 16 | * PressureSensor.ino : validated, works well, too much temp reading given back 17 | * SoundSensor2.ino: tested ok (not SPL) 18 | * UVsensor.ino : validated 19 | * MotionSensor2.ino : motion sensor validated 20 | * VibrationSensor.ino : simple vibration sensor, tested 21 | * FloodSensor: tested 22 | * LeafWetnessSensor.ino: validated, need an immersion gold sensor 23 | * SoilMoistSensor.ino: validated 24 | * SoilMoistSensorSHT1x.ino: validated, sensor cannot be burried 25 | 26 | Variants for ceech board (Solar pannel + LiOn/LiPo NRF24L compatible board): 27 | * PressureSensor-c.ino: validated 28 | * LuxUVSensor-c.ino : validated 29 | * LuxSensor-c.ino : validated 30 | 31 | Energy sensors 32 | * WaterMeterPulseSensor2.ino : for use with water meter that have a reed switch, validated 33 | * WaterMeterPulseSensor2-gs.ino : water meter with GreyScale Dfrobot sensor for Residia Jet water meter 34 | -------------------------------------------------------------------------------- /1.3/UVsensor.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Vera Arduino UVM-30A 3 | 4 | connect the sensor as follows : 5 | 6 | + >>> 5V 7 | - >>> GND 8 | out >>> A0 9 | 10 | Contribution: epierre, bulldoglowell 11 | 12 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 13 | 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define CHILD_ID_UV 0 24 | #define UV_SENSOR_ANALOG_PIN 0 25 | unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) 26 | 27 | Sensor gw; 28 | Sleep sleep; 29 | int lastUV = -1; 30 | int uvIndexValue [12] = { 50, 227, 318, 408, 503, 606, 696, 795, 881, 976, 1079, 1170, 3000}; 31 | int uvIndex; 32 | 33 | void setup() 34 | { 35 | gw.begin(); 36 | 37 | // Send the sketch version information to the gateway and Controller 38 | gw.sendSketchInfo("UV Sensor", "1.0"); 39 | 40 | // Register all sensors to gateway (they will be created as child devices) 41 | gw.sendSensorPresentation(CHILD_ID_UV, S_UV); 42 | 43 | } 44 | 45 | void loop() 46 | { 47 | uint16_t uv = analogRead(0);// Get UV value 48 | Serial.println(uv); 49 | for (int i = 0; i < 13; i++) 50 | { 51 | if (uv <= uvIndexValue[i]) 52 | { 53 | uvIndex = i; 54 | break; 55 | } 56 | } 57 | if (uvIndex != lastUV) { 58 | gw.sendVariable(CHILD_ID_UV, V_UV, uvIndex); 59 | lastUV = uvIndex; 60 | } 61 | 62 | // Power down the radio. Note that the radio will get powered back up 63 | // on the next write() call. 64 | delay(1000); //delay to allow serial to fully print before sleep 65 | gw.powerDown(); 66 | sleep.pwrDownMode(); //set sleep mode 67 | sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime 68 | } 69 | -------------------------------------------------------------------------------- /ThingSpeak.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use v5.14; 3 | use LWP::Simple; # From CPAN 4 | use JSON ; # From CPAN 5 | use File::Slurp; 6 | use LWP::UserAgent; 7 | use Crypt::SSLeay; 8 | use Data::Dumper; # Perl core module 9 | use strict; # Good practice 10 | use warnings; # Good practice 11 | use utf8; 12 | use Time::Piece; 13 | use feature qw< unicode_strings >; 14 | 15 | my $API_KEY = ''; 16 | my $FEED = "88871"; 17 | my $feed = { 'version' => '1.0.0', 'datastreams' => [] }; 18 | 19 | # Create an HTTP client 20 | my $ua = LWP::UserAgent->new; 21 | $ua->agent('RasperryPiInMyHome/1.4 '); 22 | 23 | my $trendsurl = "http://192.168.0.28:8080/json.htm?type=devices&filter=all&used=true&order=Name"; 24 | 25 | my $json = $ua->get( $trendsurl ); 26 | die "Could not get $trendsurl!" unless defined $json; 27 | # Decode the entire JSON 28 | my $decoded = JSON->new->utf8(0)->decode( $json->decoded_content ); 29 | my $url=""; 30 | 31 | my @results = @{ $decoded->{'result'} }; 32 | foreach my $f ( @results ) { 33 | #baro 34 | if ($f->{"idx"}==198) { 35 | $url.="&field1=".$f->{Pressure}; 36 | } 37 | # Ext T 38 | # Ext H 39 | if ($f->{"idx"}==422) { 40 | $url.="&field2=".$f->{Temp}; 41 | $url.="&field3=".$f->{Humidity}; 42 | } 43 | # Rain 44 | if ($f->{"idx"}==448) { 45 | $url.="&field4=".$f->{Rain}; 46 | } 47 | # UV 48 | if ($f->{"idx"}==218) { 49 | $url.="&field5=".$f->{UVI}; 50 | } 51 | # lux 52 | if ($f->{"idx"}==361) { 53 | my ($data)=($f->{Data}=~/(\d+) Lux/); 54 | $url.="&field6=".$data; 55 | } 56 | # CO2 57 | if ($f->{"idx"}==124) { 58 | my ($data)=($f->{Data}=~/(\d+) ppm/); 59 | $url.="&field7=".$data; 60 | } 61 | # PM10 62 | if ($f->{"idx"}==244) { 63 | my ($data)=($f->{Data}=~/(\d+) ppm/); 64 | $url.="&field8=".$data; 65 | } 66 | 67 | } 68 | 69 | # Create a HTTP request 70 | my $req = HTTP::Request->new(POST => "http://api.thingspeak.com/update?key=$API_KEY".$url); 71 | 72 | # Make the request 73 | my $res = $ua->request($req); 74 | unless ($res->is_success) { 75 | print STDERR $res->status_line, "\n"; 76 | print STDERR $res->content, "\n"; 77 | } 78 | 79 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | before_install: 4 | - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh) 5 | - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" 6 | - sleep 3 7 | - export DISPLAY=:1.0 8 | - wget http://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz 9 | - tar xf arduino-1.6.5-linux64.tar.xz 10 | - sudo mv arduino-1.6.5 /usr/local/share/arduino 11 | - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino 12 | - git clone https://github.com/mysensors/Arduino.git 13 | 14 | install: 15 | - ln -s $PWD/libraries/MySensors /usr/local/share/arduino/libraries/MySensors 16 | #- arduino --install-library "MySensors" 17 | 18 | script: 19 | - arduino --verify --board ${BOARD} $PWD/AirQuality-CO-NO2-NH3.ino 20 | - arduino --verify --board ${BOARD} $PWD/AirQuality-CO2-MH-Z14.ino 21 | - arduino --verify --board ${BOARD} $PWD/WaterMeterPulseSensor2.ino 22 | - arduino --verify --board ${BOARD} $PWD/DustSensor_Shinyei_PPD42NS.ino 23 | - arduino --verify --board ${BOARD} $PWD/AirQuality-MQ135.ino 24 | 25 | notifications: 26 | email: 27 | on_success: change 28 | on_failure: change 29 | 30 | env: 31 | - BOARD=arduino:avr:uno 32 | # - BOARD=arduino:avr:yun 33 | # - BOARD=arduino:avr:diecimila:cpu=atmega168 34 | # - BOARD=arduino:avr:diecimila:cpu=atmega328 35 | - BOARD=arduino:avr:nano:cpu=atmega168 36 | - BOARD=arduino:avr:nano:cpu=atmega328 37 | # - BOARD=arduino:avr:mega:cpu=atmega1280 38 | # - BOARD=arduino:avr:mega:cpu=atmega2560 39 | # - BOARD=arduino:avr:megaADK 40 | # - BOARD=arduino:avr:leonardo 41 | - BOARD=arduino:avr:micro 42 | # - BOARD=arduino:avr:esplora 43 | # - BOARD=arduino:avr:mini:cpu=atmega168 44 | - BOARD=arduino:avr:mini:cpu=atmega328 45 | # - BOARD=arduino:avr:ethernet 46 | # - BOARD=arduino:avr:bt:cpu=atmega168 47 | # - BOARD=arduino:avr:bt:cpu=atmega328 48 | # - BOARD=arduino:avr:lilypad:cpu=atmega168 49 | # - BOARD=arduino:avr:lilypad:cpu=atmega328 50 | # - BOARD=arduino:avr:pro:cpu=atmega168 51 | - BOARD=arduino:avr:pro:cpu=atmega328 52 | # - BOARD=arduino:sam:arduino_due_x 53 | -------------------------------------------------------------------------------- /1.3/VibrationSensor.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Vera Arduino Vibration Sensor 3 | 4 | connect the sensor as follows : 5 | 6 | VCC >>> 5V 7 | S >>> D3 8 | GND >>> GND 9 | 10 | Based on: http://www.dfrobot.com/wiki/index.php/DFRobot_Digital_Vibration_Sensor_V2_SKU:DFR0027 11 | Contribution: epierre 12 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 13 | 14 | 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define CHILD_ID_VIBRATION 0 25 | #define VIBRATION_SENSOR_DIGITAL_PIN 3 26 | #define SensorLED 13 27 | 28 | unsigned long SLEEP_TIME = 10; // Sleep time between reads (in seconds) 29 | //VARIABLES 30 | int val = 0; // variable to store the value coming from the sensor 31 | float valVIBRATION =0.0; 32 | float lastVIBRATION =0.0; 33 | unsigned char state = 0; 34 | 35 | Sensor gw; 36 | Sleep sleep; 37 | 38 | void setup() 39 | { 40 | gw.begin(); 41 | 42 | // Send the sketch version information to the gateway and Controller 43 | gw.sendSketchInfo("VIBRATION Sensor", "1.0"); 44 | 45 | // Register all sensors to gateway (they will be created as child devices) 46 | gw.sendSensorPresentation(CHILD_ID_VIBRATION, 23); 47 | 48 | pinMode(VIBRATION_SENSOR_DIGITAL_PIN, INPUT); 49 | attachInterrupt(1, blink, FALLING);// Trigger the blink function when the falling edge is detected 50 | pinMode(SensorLED, OUTPUT); 51 | } 52 | 53 | void loop() 54 | { 55 | 56 | if(state>=40){ // basically below 40 so ignire basic level 57 | gw.sendVariable(CHILD_ID_VIBRATION, V_VAR1, (int(state))); 58 | state = 0; 59 | digitalWrite(SensorLED,HIGH); 60 | } else { 61 | state = 0; 62 | digitalWrite(SensorLED,LOW); 63 | } 64 | 65 | 66 | // Power down the radio. Note that the radio will get powered back up 67 | // on the next write() call. 68 | delay(1000); //delay to allow serial to fully print before sleep 69 | gw.powerDown(); 70 | sleep.pwrDownMode(); //set sleep mode 71 | sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime 72 | } 73 | 74 | void blink()//Interrupts function 75 | { 76 | state++; 77 | } 78 | -------------------------------------------------------------------------------- /1.3/MotionSensor2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | // License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 7 | 8 | 9 | #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your motion sensor. (Only 2 and 3 generates interrupt!) 10 | #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) 11 | #define CHILD_ID 0 // Id of the sensor child 12 | unsigned long SEND_FREQUENCY = 2; // Minimum time between send (in seconds). We don't want to spam the gateway. 13 | long lastDebounce = 0; 14 | long debounceDelay = 500; 15 | unsigned long lastSend; 16 | unsigned long currentTime; 17 | unsigned int isTripped =0; 18 | 19 | Sensor gw; 20 | Sleep sleep; 21 | 22 | void setup() 23 | { 24 | gw.begin(); 25 | 26 | // Send the sketch version information to the gateway and Controller 27 | gw.sendSketchInfo("Motion Sensor", "1.0"); 28 | 29 | pinMode(DIGITAL_INPUT_SENSOR, INPUT); // sets the motion sensor digital pin as input 30 | 31 | // Register all sensors to gw (they will be created as child devices) 32 | gw.sendSensorPresentation(CHILD_ID, S_MOTION); 33 | 34 | lastSend = -1; 35 | } 36 | 37 | void loop() 38 | { 39 | currentTime = millis(); 40 | 41 | // Read digital motion value 42 | boolean tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH; 43 | 44 | Serial.println(tripped); 45 | Serial.println(currentTime-lastSend); 46 | if ((isTripped==0) &&(tripped==1)) { 47 | Serial.println("tripped"); 48 | gw.sendVariable(CHILD_ID, V_TRIPPED, tripped?"1":"0"); // Send tripped value to gw 49 | lastSend=currentTime; 50 | isTripped=1; 51 | } 52 | 53 | if ((tripped==0) && (isTripped==1) && (currentTime - lastSend > 500*SEND_FREQUENCY)){ 54 | Serial.println("un-tripped"); 55 | gw.sendVariable(CHILD_ID, V_TRIPPED, tripped?"1":"0"); // Send tripped value to gw 56 | lastSend=currentTime; 57 | isTripped=0; 58 | } 59 | 60 | // Power down the radio. Note that the radio will get powered back up 61 | // on the next write() call. 62 | delay(2000); //delay to allow serial to fully print before sleep 63 | gw.powerDown(); 64 | sleep.pwrDownMode(); //set sleep mode 65 | sleep.sleepInterrupt(INTERRUPT,CHANGE); 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /1.3/MQ135.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Vera Arduino MQ135 3 | 4 | connect the sensor as follows : 5 | 6 | A H A >>> 5V 7 | B >>> A0 8 | H >>> GND 9 | B >>> 10K ohm >>> GND 10 | 11 | Contribution: epierre 12 | 13 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 14 | 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define CHILD_ID_AIQ 0 25 | #define AIQ_SENSOR_ANALOG_PIN 0 26 | unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) 27 | //VARIABLES 28 | //float Ro = 10000.0; // this has to be tuned 10K Ohm 29 | float Ro = 0000.0; // this has to be tuned 10K Ohm 30 | int val = 0; // variable to store the value coming from the sensor 31 | float valAIQ =0.0; 32 | float lastAIQ =0.0; 33 | 34 | Sensor gw; 35 | Sleep sleep; 36 | 37 | void setup() 38 | { 39 | gw.begin(); 40 | 41 | // Send the sketch version information to the gateway and Controller 42 | gw.sendSketchInfo("AIQ Sensor", "1.0"); 43 | 44 | // Register all sensors to gateway (they will be created as child devices) 45 | gw.sendSensorPresentation(CHILD_ID_AIQ, 22); 46 | 47 | } 48 | 49 | // get CO ppm 50 | float get_CO (float ratio){ 51 | float ppm = 0.0; 52 | ppm = 37143 * pow (ratio, -3.178); 53 | return ppm; 54 | } 55 | 56 | void loop() 57 | { 58 | uint16_t val = analogRead(AIQ_SENSOR_ANALOG_PIN);// Get AIQ value 59 | Serial.println(val); 60 | 61 | float Vrl = val * ( 5.00 / 1024.0 ); // V 62 | float Rs = 20000 * ( 5.00 - Vrl) / Vrl ; // Ohm 63 | int ratio = Rs/Ro; 64 | 65 | Serial.print ( "Vrl / Rs / ratio:"); 66 | Serial.print (Vrl); 67 | Serial.print(" "); 68 | Serial.print (Rs); 69 | Serial.print(" "); 70 | Serial.println(ratio); 71 | Serial.print ( "CO ppm :"); 72 | Serial.println(get_CO(ratio)); 73 | 74 | valAIQ=get_CO(ratio); 75 | 76 | if (valAIQ != lastAIQ) { 77 | gw.sendVariable(CHILD_ID_AIQ, V_VAR1, (int)ceil(valAIQ)); 78 | lastAIQ = ceil(valAIQ); 79 | } 80 | 81 | // Power down the radio. Note that the radio will get powered back up 82 | // on the next write() call. 83 | delay(1000); //delay to allow serial to fully print before sleep 84 | gw.powerDown(); 85 | sleep.pwrDownMode(); //set sleep mode 86 | sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime 87 | } 88 | 89 | -------------------------------------------------------------------------------- /TSL1401: -------------------------------------------------------------------------------- 1 | /* 2 | TSL1401test --- Taos TSL1401 image sensor chip 2010-07-24 3 | datasheet: http://www.ams.com/eng/content/download/250163/975677/file/TSL1401CL.pdf 4 | 5 | trace: http://ap.urpi.fei.stuba.sk/sensorwiki/index.php/TSL1401_Line_Sensor 6 | other inos: 7 | - http://forums.parallax.com/showthread.php/125594-TSL1401-and-Arduino 8 | - https://github.com/ap-tech/Getting-started-with-the-TSL1401CL-linescan-camera-with-arduino-and-processing.-/blob/master/TSL1401CL%20linescan%20camera%20code./Linescane_camera_code/Linescane_camera_code.ino 9 | 10 | 11 | */ 12 | 13 | 14 | // Sensor interface: 15 | #define AOpin 0 // Analog output - yellow 16 | #define SIpin 3 // Start Integration - orange 17 | #define CLKpin 2 // Clock - red 18 | // Vcc - brown 19 | // GND - black 20 | 21 | #define NPIXELS 128 // No. of pixels in array 22 | 23 | byte Pixel[NPIXELS]; // Field for measured values <0-255> 24 | 25 | 26 | #define FASTADC 1 27 | // defines for setting and clearing register bits 28 | #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) 29 | #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) 30 | 31 | 32 | 33 | void setup(void) 34 | { 35 | pinMode(SIpin, OUTPUT); 36 | pinMode(CLKpin, OUTPUT); 37 | //pinMode (AOpin, INPUT); 38 | 39 | digitalWrite(SIpin, LOW); // IDLE state 40 | digitalWrite(CLKpin, LOW); // IDLE state 41 | 42 | #if FASTADC 43 | // set prescale to 16 44 | sbi(ADCSRA,ADPS2); 45 | cbi(ADCSRA,ADPS1); 46 | cbi(ADCSRA,ADPS0); 47 | #endif 48 | 49 | Serial.begin (115200); 50 | } 51 | 52 | 53 | 54 | void loop (void) 55 | { 56 | int i; 57 | int expTime; 58 | 59 | 60 | delayMicroseconds (1); /* Integration time in microseconds */ 61 | delay(10); /* Integration time in miliseconds */ 62 | 63 | 64 | digitalWrite (CLKpin, LOW); 65 | digitalWrite (SIpin, HIGH); 66 | digitalWrite (CLKpin, HIGH); 67 | digitalWrite (SIpin, LOW); 68 | 69 | delayMicroseconds (1); 70 | 71 | /* and now read the real image */ 72 | 73 | for (i = 0; i < NPIXELS; i++) { 74 | Pixel[i] = analogRead (AOpin)/4 ; // 8-bit is enough 75 | digitalWrite (CLKpin, LOW); 76 | delayMicroseconds (1); 77 | digitalWrite (CLKpin, HIGH); 78 | } 79 | 80 | 81 | Serial.write ((byte)0); // sync byte = 0 82 | for (i = 0; i < NPIXELS; i++) { 83 | Serial.write ((byte)Pixel[i]+1); 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /SoilMoistSensorSHT1x.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Soil Moisture sensor with SHT1x 3 | 4 | connect the sensor as follows : 5 | 6 | VCC -- Red = VCC (3-5VDC) 7 | GND -- Black or Green = Ground 8 | Pin 8 -- Yellow = Clock 9 | Pin 3 -- Blue = Data. 10 | 11 | Contribution: epierre 12 | SHT1x library V2.0 10Dec2010 : follow instructions http://playground.arduino.cc/code/Sensirion#SHT1x 13 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 14 | */ 15 | 16 | #include 17 | #include 18 | #include "Sensirion.h" 19 | 20 | #define DIGITAL_INPUT_SOIL_SENSOR 3 // Digital input did you attach your soil sensor. 21 | #define CHILD_ID_TEMP 0 // Id of the sensor child 22 | #define CHILD_ID_HUM 1 // Id of the sensor child 23 | #define sckPin 8 //serial clock 24 | 25 | 26 | // We are using the SHT10 27 | // Humid accuracy +/- 5% 28 | // Steady accuracy between 10-80 29 | // example at 10/90 +/- 6%, 0/100 +/- 7.5% 30 | 31 | // Temp accuracy +/- .5 degrees celcius 32 | // Temp error increases more as we get farther from 25 celc. 33 | // example: @ 0/50 degrees, +/- 1.2 degrees 34 | 35 | 36 | Sensirion th_sensor=Sensirion(DIGITAL_INPUT_SOIL_SENSOR, sckPin); 37 | 38 | MySensor gw; 39 | MyMessage msgtemp(CHILD_ID_TEMP, V_TEMP); 40 | MyMessage msghum(CHILD_ID_HUM, V_HUM); 41 | int lastTempValue = -1; 42 | int lastHumValue = -1; 43 | unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds) 44 | 45 | void setup() 46 | { 47 | gw.begin(); 48 | 49 | // Send the sketch version information to the gateway and Controller 50 | gw.sendSketchInfo("Soil Moisture Sensor SHT1x", "1.0"); 51 | 52 | // Register all sensors to gw (they will be created as child devices) 53 | gw.present(CHILD_ID_TEMP, S_TEMP); 54 | gw.present(CHILD_ID_HUM, S_HUM); 55 | } 56 | 57 | void loop() 58 | { 59 | float temp_c; 60 | float humid; 61 | float dewpoint; 62 | // Read values from the sensor 63 | th_sensor.measure(&temp_c, &humid, &dewpoint); 64 | 65 | 66 | if (temp_c != lastTempValue) { 67 | Serial.println(temp_c); 68 | gw.send(msgtemp.set(temp_c, 1)); // Send the inverse to gw as tripped should be when no water in soil 69 | lastTempValue = temp_c; 70 | } 71 | if (humid != lastHumValue) { 72 | Serial.println(humid); 73 | gw.send(msghum.set(humid, 1)); // Send the inverse to gw as tripped should be when no water in soil 74 | lastHumValue = humid; 75 | } 76 | // Power down the radio and arduino until digital input changes. 77 | gw.sleep(SLEEP_TIME); //sleep a bit 78 | } 79 | -------------------------------------------------------------------------------- /1.3/DustSensor.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Vera Arduino Dust Sensort 3 | 4 | connect the sensor as follows : 5 | 6 | VCC >>> 5V 7 | A >>> A0 8 | GND >>> GND 9 | 10 | Based on: http://www.dfrobot.com/wiki/index.php/Sharp_GP2Y1010AU 11 | Authors: Cyrille Médard de Chardon (serialC), Christophe Trefois (Trefex) 12 | Contribution: epierre 13 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 14 | 15 | 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define CHILD_ID_DUST 0 26 | #define DUST_SENSOR_ANALOG_PIN 1 27 | 28 | unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) 29 | //VARIABLES 30 | int val = 0; // variable to store the value coming from the sensor 31 | float valDUST =0.0; 32 | float lastDUST =0.0; 33 | int samplingTime = 280; 34 | int deltaTime = 40; 35 | int sleepTime = 9680; 36 | float voMeasured = 0; 37 | float calcVoltage = 0; 38 | float dustDensity = 0; 39 | 40 | Sensor gw; 41 | Sleep sleep; 42 | 43 | void setup() 44 | { 45 | gw.begin(); 46 | 47 | // Send the sketch version information to the gateway and Controller 48 | gw.sendSketchInfo("Dust Sensor", "1.0"); 49 | 50 | // Register all sensors to gateway (they will be created as child devices) 51 | gw.sendSensorPresentation(CHILD_ID_DUST, 23); 52 | 53 | } 54 | 55 | void loop() 56 | { 57 | uint16_t voMeasured = analogRead(DUST_SENSOR_ANALOG_PIN);// Get DUST value 58 | 59 | // 0 - 5V mapped to 0 - 1023 integer values 60 | // recover voltage 61 | calcVoltage = voMeasured * (5.0 / 1024.0); 62 | 63 | // linear eqaution taken from http://www.howmuchsnow.com/arduino/airquality/ 64 | // Chris Nafis (c) 2012 65 | dustDensity = (0.17 * calcVoltage - 0.1)*1000; 66 | 67 | Serial.print("Raw Signal Value (0-1023): "); 68 | Serial.print(voMeasured); 69 | 70 | Serial.print(" - Voltage: "); 71 | Serial.print(calcVoltage); 72 | 73 | Serial.print(" - Dust Density: "); 74 | Serial.println(dustDensity); // unit: ug/m3 75 | 76 | if (ceil(dustDensity) != lastDUST) { 77 | gw.sendVariable(CHILD_ID_DUST, V_VAR1, (int)ceil(dustDensity)); 78 | lastDUST = ceil(dustDensity); 79 | } 80 | 81 | // Power down the radio. Note that the radio will get powered back up 82 | // on the next write() call. 83 | delay(1000); //delay to allow serial to fully print before sleep 84 | gw.powerDown(); 85 | sleep.pwrDownMode(); //set sleep mode 86 | sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime 87 | } 88 | -------------------------------------------------------------------------------- /MutichannelGasSensor.h: -------------------------------------------------------------------------------- 1 | /* 2 | MutichannelGasSensor.h 3 | 2015 Copyright (c) Seeed Technology Inc. All right reserved. 4 | 5 | Author: Jacky Zhang 6 | 2015-3-17 7 | http://www.seeed.cc/ 8 | modi by Jack, 2015-8 9 | 10 | The MIT License (MIT) 11 | 12 | Copyright (c) 2015 Seeed Technology Inc. 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in 22 | all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | THE SOFTWARE. 31 | */ 32 | 33 | #ifndef __MUTICHANNELGASSENSOR_H__ 34 | #define __MUTICHANNELGASSENSOR_H__ 35 | 36 | #define DEFAULT_I2C_ADDR 0x04 37 | enum{CO, NO2, NH3, C3H8, C4H10, CH4, H2, C2H5OH}; 38 | 39 | class MutichannelGasSensor{ 40 | 41 | private: 42 | 43 | uint8_t i2cAddress;//I2C address of this MCU 44 | uint16_t res0[3];//sensors res0 45 | uint16_t res[3];//sensors res 46 | uint8_t is_connected; 47 | 48 | void sendI2C(unsigned char dta); 49 | int16_t readData(uint8_t cmd); 50 | int16_t readR0(void); 51 | int16_t readR(void); 52 | float calcGas(int gas); 53 | 54 | public: 55 | 56 | void begin(int address); 57 | void begin(); 58 | void changeI2cAddr(uint8_t newAddr); 59 | void powerOn(void); 60 | void powerOff(void); 61 | void doCalibrate(void); 62 | //get gas concentration, unit: ppm 63 | float measure_CO(); 64 | float measure_NO2(); 65 | float measure_NH3(); 66 | float measure_C3H8(); 67 | float measure_C4H10(); 68 | float measure_CH4(); 69 | float measure_H2(); 70 | float measure_C2H5OH(); 71 | 72 | }; 73 | 74 | extern MutichannelGasSensor mutichannelGasSensor; 75 | 76 | #endif 77 | 78 | /********************************************************************************************************* 79 | END FILE 80 | *********************************************************************************************************/ -------------------------------------------------------------------------------- /DustSensor.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Dust Sensor for Sharp_GP2Y1010AU 3 | 4 | connect the sensor as follows : 5 | 6 | *Please note both analogic and digital must be wired at the same time* 7 | 8 | Analogic connector: 9 | VCC >>> 5V 10 | A >>> A1 11 | GND >>> GND 12 | 13 | Digital connector: 14 | VCC >>> 5V 15 | D >>> D1 16 | GND >>> GND 17 | 18 | Based on: http://www.dfrobot.com/wiki/index.php/Sharp_GP2Y1010AU 19 | Authors: Cyrille Médard de Chardon (serialC), Christophe Trefois (Trefex) 20 | Contribution: epierre 21 | COnverted to 1.4 by Henrik Ekblad 22 | 23 | The dust sensor used (see purchase guide for latest link): 24 | http://rover.ebay.com/rover/1/711-53200-19255-0/1?icep_ff3=2&pub=5575069610&toolid=10001&campid=5337433187&customid=&icep_item=171259125886&ipn=psmain&icep_vectorid=229466&kwid=902099&mtid=824&kw=lg 25 | 26 | */ 27 | 28 | #include 29 | #include 30 | 31 | #define CHILD_ID_DUST 0 32 | #define DUST_SENSOR_ANALOG_PIN 1 33 | #define DUST_SENSOR_DIGITAL_PIN 1 34 | 35 | unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds) 36 | //VARIABLES 37 | int val = 0; // variable to store the value coming from the sensor 38 | float valDUST =0.0; 39 | float lastDUST =0.0; 40 | int samplingTime = 280; 41 | int deltaTime = 40; 42 | int sleepTime = 9680; 43 | float voMeasured = 0; 44 | float calcVoltage = 0; 45 | float dustDensity = 0; 46 | 47 | MySensor gw; 48 | MyMessage dustMsg(CHILD_ID_DUST, V_LEVEL); 49 | 50 | void setup() 51 | { 52 | gw.begin(); 53 | 54 | // Send the sketch version information to the gateway and Controller 55 | gw.sendSketchInfo("Dust Sensor", "1.2"); 56 | 57 | // Register all sensors to gateway (they will be created as child devices) 58 | gw.present(CHILD_ID_DUST, S_DUST); 59 | 60 | pinMode(DUST_SENSOR_DIGITAL_PIN,OUTPUT); //light on dust sensor led 61 | 62 | } 63 | 64 | void loop() 65 | { 66 | digitalWrite(DUST_SENSOR_DIGITAL_PIN,LOW); // power on the LED 67 | delayMicroseconds(280); 68 | uint16_t voMeasured = analogRead(DUST_SENSOR_ANALOG_PIN);// Get DUST value 69 | delayMicroseconds(40); 70 | digitalWrite(DUST_SENSOR_DIGITAL_PIN,HIGH); // turn the LED off 71 | 72 | // 0 - 5V mapped to 0 - 1023 integer values 73 | // recover voltage 74 | calcVoltage = voMeasured * (5.0 / 1024.0); 75 | 76 | // linear eqaution taken from http://www.howmuchsnow.com/arduino/airquality/ 77 | // Chris Nafis (c) 2012 78 | dustDensity = (0.17 * calcVoltage - 0.1)*1000; 79 | 80 | Serial.print("Raw Signal Value (0-1023): "); 81 | Serial.print(voMeasured); 82 | 83 | Serial.print(" - Voltage: "); 84 | Serial.print(calcVoltage); 85 | 86 | Serial.print(" - Dust Density: "); 87 | Serial.println(dustDensity); // unit: ug/m3 88 | 89 | if (ceil(dustDensity) != lastDUST) { 90 | gw.send(dustMsg.set((int)ceil(dustDensity))); 91 | lastDUST = ceil(dustDensity); 92 | } 93 | 94 | gw.sleep(SLEEP_TIME); 95 | } 96 | -------------------------------------------------------------------------------- /VibrationSensor.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MySensors Arduino library handles the wireless radio link and protocol 3 | * between your home built sensors/actuators and HA controller of choice. 4 | * The sensors forms a self healing radio network with optional repeaters. Each 5 | * repeater and gateway builds a routing tables in EEPROM which keeps track of the 6 | * network topology allowing messages to be routed to nodes. 7 | * 8 | * Created by Henrik Ekblad 9 | * Copyright (C) 2013-2015 Sensnology AB 10 | * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 11 | * 12 | * Documentation: http://www.mysensors.org 13 | * Support Forum: http://forum.mysensors.org 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License 17 | * version 2 as published by the Free Software Foundation. 18 | * 19 | ******************************* 20 | * 21 | * DESCRIPTION 22 | * 23 | * Vibration Sensor 24 | * 25 | * connect the sensor as follows : 26 | * 27 | * VCC >>> 5V 28 | * S >>> D3 29 | * GND >>> GND 30 | * 31 | * Based on: http://www.dfrobot.com/wiki/index.php/DFRobot_Digital_Vibration_Sensor_V2_SKU:DFR0027 32 | * Contributor: epierre 33 | **/ 34 | 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | #define CHILD_ID_VIBRATION 0 41 | #define VIBRATION_SENSOR_DIGITAL_PIN 3 42 | #define SensorLED 13 43 | 44 | unsigned long SLEEP_TIME = 10*1000; // Sleep time between reads (in seconds) 45 | 46 | //VARIABLES 47 | int val = 0; // variable to store the value coming from the sensor 48 | float valVIBRATION =0.0; 49 | float lastVIBRATION =0.0; 50 | unsigned char state = 0; 51 | 52 | MySensor gw; 53 | MyMessage vibrationMsg(CHILD_ID_VIBRATION, V_LEVEL); 54 | 55 | void setup() 56 | { 57 | gw.begin(); 58 | 59 | // Send the sketch version information to the gateway and Controller 60 | gw.sendSketchInfo("VIBRATION Sensor", "1.0"); 61 | 62 | // Register all sensors to gateway (they will be created as child devices) 63 | gw.present(CHILD_ID_VIBRATION, S_VIBRATION); 64 | 65 | 66 | pinMode(VIBRATION_SENSOR_DIGITAL_PIN, INPUT); 67 | attachInterrupt(1, blink, FALLING);// Trigger the blink function when the falling edge is detected 68 | pinMode(SensorLED, OUTPUT); 69 | } 70 | 71 | void loop() 72 | { 73 | 74 | if(state>=40){ // basically below 40 so ignire basic level 75 | gw.send(vibrationMsg.set(int(state))); 76 | state = 0; 77 | digitalWrite(SensorLED,HIGH); 78 | } else { 79 | state = 0; 80 | digitalWrite(SensorLED,LOW); 81 | } 82 | 83 | 84 | // Power down the radio. Note that the radio will get powered back up 85 | // on the next write() call. 86 | delay(1000); //delay to allow serial to fully print before sleep 87 | 88 | gw.sleep(SLEEP_TIME); //sleep for: sleepTime 89 | } 90 | 91 | void blink()//Interrupts function 92 | { 93 | state++; 94 | } 95 | -------------------------------------------------------------------------------- /FloodSensor.ino: -------------------------------------------------------------------------------- 1 | /* Flood Sensor 2 | 3 | This sketch will light up the LED on Pin 13, when water (anything conductive) bridges the gap in the sensor. 4 | 5 | created 05-18-2012 by PvE 6 | Contributor: epierre 7 | 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #define CHILD_ID_FLOOD 0 14 | #define FLOOD_SENSOR_ANALOG_PIN 4 // the number of the Flood Sensor pin 15 | #define ledPin = 13; // the number of the LED pin 16 | 17 | MySensor gw; 18 | MyMessage msg(CHILD_ID_FLOOD, V_TRIPPED); 19 | 20 | // Variables will change: 21 | int ledState = HIGH; // the current state of the output pin 22 | int buttonState; // the current reading from the input pin 23 | int lastButtonState = LOW; // the previous reading from the input pin 24 | int floodSensorState = 0; // variable for reading the floodSensors status 25 | 26 | // the following variables are long's because the time, measured in miliseconds, 27 | // will quickly become a bigger number than can be stored in an int. 28 | long lastDebounceTime = 0; // the last time the output pin was toggled 29 | long debounceDelay = 50; // the debounce time; increase if the output flickers 30 | 31 | void setup() { 32 | 33 | gw.begin(); 34 | 35 | // Send the sketch version information to the gateway and Controller 36 | gw.sendSketchInfo("Flood Sensor", "1.0"); 37 | 38 | // Register all sensors to gateway (they will be created as child devices) 39 | gw.present(FLOOD_SENSOR_ANALOG_PIN, S_WATER_LEAK); 40 | 41 | // initialize the flood Sensor pin as an input: 42 | pinMode(FLOOD_SENSOR_ANALOG_PIN, INPUT); 43 | pinMode(ledPin, OUTPUT); 44 | } 45 | 46 | void loop(){ 47 | // read the state of the flood Sensor value: 48 | floodSensorState = digitalRead(floodSensors); 49 | 50 | // check to see if you just pressed the button 51 | // (i.e. the input went from LOW to HIGH), and you've waited 52 | // long enough since the last press to ignore any noise: 53 | 54 | // If the switch changed, due to noise or pressing: 55 | if (floodSensorState != lastButtonState) { 56 | // reset the debouncing timer 57 | lastDebounceTime = millis(); 58 | } 59 | 60 | if ((millis() - lastDebounceTime) > debounceDelay) { 61 | // whatever the reading is at, it's been there for longer 62 | // than the debounce delay, so take it as the actual current state: 63 | buttonState = floodSensorState; 64 | } 65 | 66 | // check if the flood Sensor is wet. 67 | // if it is, the floodSensorState is HIGH: 68 | if (buttonState == HIGH) { 69 | // turn LED on: 70 | digitalWrite(ledPin, LOW); 71 | gw.sendVariable(CHILD_ID, V_TRIPPED, "1"); // Send tripped value to gw 72 | 73 | } 74 | else { 75 | // turn LED off: 76 | digitalWrite(ledPin, HIGH); 77 | gw.sendVariable(CHILD_ID, V_TRIPPED, "0"); // Send un-tripped value to gw 78 | } 79 | // save the reading. Next time through the loop, 80 | // it'll be the lastButtonState: 81 | lastButtonState = floodSensorState; 82 | 83 | } 84 | -------------------------------------------------------------------------------- /AirModule.h: -------------------------------------------------------------------------------- 1 | /* 2 | Air Quality Module DS-HCHO 3 | 4 | author: terryoy 5 | email: terry.ouyang@gmail.com 6 | */ 7 | #include 8 | 9 | /* Request data definition */ 10 | enum Command { 11 | CMD_QUERY = 0x01, 12 | CMD_CLEAR = 0x02, 13 | CMD_SPAN = 0x03, 14 | CMD_FACTORY = 0x04, 15 | }; 16 | 17 | typedef struct { 18 | char begin_1 = 0x42; 19 | char begin_2 = 0x4d; 20 | char cmd = CMD_QUERY; 21 | char dhh = 0; 22 | char dll = 0; 23 | char lrch = 0; 24 | char lrcl = 0; 25 | 26 | } AirCommand; 27 | 28 | /* Response data definition */ 29 | 30 | enum AirType { 31 | AIRTYPE_NO_SENSOR = 0x00, 32 | AIRTYPE_CO = 0x01, 33 | AIRTYPE_H2S = 0x02, 34 | AIRTYPE_CH4 = 0x03, 35 | AIRTYPE_CL2 = 0x04, 36 | AIRTYPE_HCL = 0x05, 37 | AIRTYPE_F2 = 0x06, 38 | AIRTYPE_HF = 0x07, 39 | AIRTYPE_NH3 = 0x08, 40 | AIRTYPE_HCN = 0x09, 41 | AIRTYPE_PH3 = 0x0a, 42 | AIRTYPE_NO = 0x0b, 43 | AIRTYPE_NO2 = 0x0c, 44 | AIRTYPE_O3 = 0x0d, 45 | AIRTYPE_O2 = 0x0e, 46 | AIRTYPE_SO2 = 0x0f, 47 | AIRTYPE_CLO2 = 0x10, 48 | AIRTYPE_COCL2 = 0x11, 49 | AIRTYPE_PH3_ = 0x12, // duplicated with 0x0a 50 | AIRTYPE_SIH4 = 0x13, 51 | AIRTYPE_HCHO = 0x14, 52 | AIRTYPE_CO2 = 0x15, 53 | AIRTYPE_VOC = 0x16, 54 | AIRTYPE_ETO = 0x17, 55 | AIRTYPE_C2H4 = 0x18, 56 | AIRTYPE_C2H2 = 0x19, 57 | AIRTYPE_SF6 = 0x1a, 58 | AIRTYPE_ASH3 = 0x1b, 59 | AIRTYPE_H2 = 0x1c, 60 | AIRTYPE_TOX1 = 0x1d, 61 | AIRTYPE_TOX2 = 0x1e, 62 | AIRTYPE_GAS_FLOW = 0x1f, // L/M 63 | AIRTYPE_BATTERY = 0x20, 64 | }; 65 | 66 | enum MeasureUnit { 67 | MU_PPM = 0x01, 68 | MU_VOL = 0x02, 69 | MU_LEL = 0x03, 70 | MU_PPB = 0x04, 71 | MU_MG_M3 = 0x05, 72 | }; 73 | 74 | enum Equivalent { 75 | EQV_ONE = 0x01, 76 | EQV_TEN = 0x02, 77 | EQV_HUNDRED = 0x03, 78 | EQV_THOUSAND = 0x04, 79 | }; 80 | 81 | typedef struct { 82 | char begin_1 = 0x42; 83 | char begin_2 = 0x4d; 84 | char length = 0x08; 85 | char air_type = 0x00; 86 | char unit = 0x00; 87 | char vh = 0x00; // 88 | char dhh = 0x00; 89 | char dll = 0x00; 90 | char lrch = 0x00; 91 | char lrcl = 0x00; 92 | } AirResponse; // length 10 93 | 94 | void parse_air_response(AirResponse *response, char *raw) { 95 | response->begin_1 = raw[0]; 96 | response->begin_2 = raw[1]; 97 | response->length = raw[2]; 98 | response->air_type = raw[3]; 99 | response->unit = raw[4]; 100 | response->vh = raw[5]; 101 | response->dhh = raw[6]; 102 | response->dll = raw[7]; 103 | response->lrch = raw[8]; 104 | response->lrcl = raw[9]; 105 | } 106 | 107 | char* get_unit_display(AirResponse response) { 108 | switch(response.unit) { 109 | case MU_PPM: 110 | return "ppm"; 111 | case MU_VOL: 112 | return "VOL"; 113 | case MU_LEL: 114 | return "LEL"; 115 | case MU_PPB: 116 | return "Ppb"; 117 | case MU_MG_M3: 118 | return "Mg/m3"; 119 | default: 120 | return ""; 121 | } 122 | } 123 | 124 | float get_read_value(AirResponse response) { 125 | float equivalent = pow(10, response.vh) / 10; 126 | int read_value = (int)response.dhh << 8 | (int)response.dll; 127 | return read_value / equivalent; 128 | } 129 | 130 | -------------------------------------------------------------------------------- /1.3/MQ135dgi.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Vera Arduino MQ135 3 | 4 | connect the sensor as follows : 5 | 6 | A H A >>> 5V 7 | B >>> A0 8 | H >>> GND 9 | B >>> 10K ohm >>> GND 10 | 11 | Contribution: epierre 12 | Based on David Gironi http://davidegironi.blogspot.fr/2014/01/cheap-co2-meter-using-mq135-sensor-with.html 13 | http://skylink.dl.sourceforge.net/project/davidegironi/avr-lib/avr_lib_mq135_01.zip 14 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 15 | 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define CHILD_ID_AIQ 0 27 | #define AIQ_SENSOR_ANALOG_PIN 0 28 | 29 | #define MQ135_DEFAULTPPM 392 //default ppm of CO2 for calibration 30 | #define MQ135_DEFAULTRO 41763 //default Ro for MQ135_DEFAULTPPM ppm of CO2 31 | #define MQ135_SCALINGFACTOR 116.6020682 //CO2 gas value 32 | #define MQ135_EXPONENT -2.769034857 //CO2 gas value 33 | #define MQ135_MAXRSRO 2.428 //for CO2 34 | #define MQ135_MINRSRO 0.358 //for CO2 35 | 36 | unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) 37 | //VARIABLES 38 | //float Ro = 10000.0; // this has to be tuned 10K Ohm 39 | float mq135_ro = 10000.0; // this has to be tuned 10K Ohm 40 | int val = 0; // variable to store the value coming from the sensor 41 | float valAIQ =0.0; 42 | float lastAIQ =0.0; 43 | 44 | Sensor gw; 45 | Sleep sleep; 46 | 47 | void setup() 48 | { 49 | gw.begin(); 50 | 51 | // Send the sketch version information to the gateway and Controller 52 | gw.sendSketchInfo("AIQ Sensor", "1.0"); 53 | 54 | // Register all sensors to gateway (they will be created as child devices) 55 | gw.sendSensorPresentation(CHILD_ID_AIQ, 22); 56 | 57 | } 58 | 59 | /* 60 | * get the calibrated ro based upon read resistance, and a know ppm 61 | */ 62 | long mq135_getro(long resvalue, double ppm) { 63 | return (long)(resvalue * exp( log(MQ135_SCALINGFACTOR/ppm) / MQ135_EXPONENT )); 64 | } 65 | 66 | /* 67 | * get the ppm concentration 68 | */ 69 | double mq135_getppm(long resvalue, long ro) { 70 | double ret = 0; 71 | double validinterval = 0; 72 | validinterval = resvalue/(double)ro; 73 | if(validintervalMQ135_MINRSRO) { 74 | ret = (double)MQ135_SCALINGFACTOR * pow( ((double)resvalue/ro), MQ135_EXPONENT); 75 | } 76 | return ret; 77 | } 78 | 79 | void loop() 80 | { 81 | uint16_t val = analogRead(AIQ_SENSOR_ANALOG_PIN);// Get AIQ value 82 | Serial.println(val); 83 | 84 | mq135_ro = mq135_getro(val, MQ135_DEFAULTPPM); 85 | //convert to ppm (using default ro) 86 | valAIQ = mq135_getppm(val, MQ135_DEFAULTRO); 87 | 88 | Serial.print ( "Vrl / Rs / ratio:"); 89 | Serial.print ( val); 90 | Serial.print ( " / "); 91 | Serial.print ( mq135_ro); 92 | Serial.print ( " / "); 93 | Serial.print ( valAIQ); 94 | 95 | 96 | if (valAIQ != lastAIQ) { 97 | gw.sendVariable(CHILD_ID_AIQ, V_VAR1, (int)ceil(valAIQ)); 98 | lastAIQ = ceil(valAIQ); 99 | } 100 | 101 | // Power down the radio. Note that the radio will get powered back up 102 | // on the next write() call. 103 | delay(1000); //delay to allow serial to fully print before sleep 104 | gw.powerDown(); 105 | sleep.pwrDownMode(); //set sleep mode 106 | sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime 107 | } 108 | -------------------------------------------------------------------------------- /1.3/SoundSensor2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Vera Arduino Sound 3 | 4 | connect the sensor as follows : 5 | 6 | + >>> 5V 7 | - >>> GND 8 | out >>> A0 9 | 10 | Contribution: epierre 11 | based on :https://www.inkling.com/read/arduino-cookbook-michael-margolis-2nd/chapter-6/recipe-6-7 12 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 13 | 14 | 15 | */ 16 | TBD: http://davidegironi.blogspot.fr/2014/02/a-simple-sound-pressure-level-meter-spl.html 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define CHILD_ID_SND 0 25 | #define SND_SENSOR_ANALOG_PIN 0 26 | unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) 27 | 28 | const int ledPin = 13; //the code will flash the LED in pin 13 29 | const int middleValue = 512; //the middle of the range of analog values 30 | const int numberOfSamples = 128; //how many readings will be taken each time 31 | int sample; //the value read from microphone each time 32 | long signal; //the reading once you have removed DC offset 33 | long averageReading; //the average of that loop of readings 34 | 35 | long runningAverage=0; //the running average of calculated values 36 | const int averagedOver= 16; //how quickly new values affect running average 37 | //bigger numbers mean slower 38 | 39 | const int threshold=400; //at what level the light turns on 40 | 41 | Sensor gw; 42 | Sleep sleep; 43 | int lastSND; 44 | 45 | void setup() 46 | { 47 | gw.begin(); 48 | 49 | // Send the sketch version information to the gateway and Controller 50 | gw.sendSketchInfo("Sound Sensor", "1.0"); 51 | 52 | // Register all sensors to gateway (they will be created as child devices) 53 | gw.sendSensorPresentation(CHILD_ID_SND, 22); 54 | 55 | pinMode(SND_SENSOR_ANALOG_PIN, INPUT); 56 | pinMode(ledPin, OUTPUT); 57 | 58 | } 59 | 60 | void loop() 61 | { 62 | long sumOfSquares = 0; 63 | for (int i=0; ithreshold){ //is average more than the threshold ? 73 | digitalWrite(ledPin, HIGH); //if it is turn on the LED 74 | }else{ 75 | digitalWrite(ledPin, LOW); //if it isn't turn the LED off 76 | } 77 | Serial.println(runningAverage); //print the value so you can check it 78 | 79 | if (runningAverage != lastSND) { 80 | //gw.sendVariable(CHILD_ID_SND, 37, runningAverage); 81 | gw.sendVariable(CHILD_ID_SND, 37, sample); 82 | lastSND = runningAverage; 83 | } 84 | 85 | // Power down the radio. Note that the radio will get powered back up 86 | // on the next write() call. 87 | delay(1000); //delay to allow serial to fully print before sleep 88 | gw.powerDown(); 89 | sleep.pwrDownMode(); //set sleep mode 90 | sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime 91 | } 92 | -------------------------------------------------------------------------------- /SoundSensor2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Sound 3 | 4 | connect the sensor as follows : 5 | 6 | + >>> 5V 7 | - >>> GND 8 | out >>> A0 9 | 10 | Contribution: epierre 11 | based on :https://www.inkling.com/read/arduino-cookbook-michael-margolis-2nd/chapter-6/recipe-6-7 12 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 13 | 14 | 15 | 16 | TBD: http://davidegironi.blogspot.fr/2014/02/a-simple-sound-pressure-level-meter-spl.html 17 | 18 | # This program is free software; you can redistribute it and/or 19 | # modify it under the terms of the GNU General Public License 20 | # version 2 as published by the Free Software Foundation. 21 | 22 | */ 23 | #include 24 | #include 25 | #include 26 | 27 | #define CHILD_ID_SND 0 28 | #define SND_SENSOR_ANALOG_PIN 0 29 | unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) 30 | 31 | const int ledPin = 13; //the code will flash the LED in pin 13 32 | const int middleValue = 512; //the middle of the range of analog values 33 | const int numberOfSamples = 128; //how many readings will be taken each time 34 | int sample; //the value read from microphone each time 35 | long signal; //the reading once you have removed DC offset 36 | long averageReading; //the average of that loop of readings 37 | 38 | long runningAverage=0; //the running average of calculated values 39 | const int averagedOver= 16; //how quickly new values affect running average 40 | //bigger numbers mean slower 41 | 42 | const int threshold=400; //at what level the light turns on 43 | 44 | MySensor gw; 45 | MyMessage soundMsg(CHILD_ID_SND, V_VOLUME); 46 | int lastSND; 47 | 48 | void setup() 49 | { 50 | gw.begin(); 51 | 52 | // Send the sketch version information to the gateway and Controller 53 | gw.sendSketchInfo("Sound Sensor", "1.0"); 54 | 55 | // Register all sensors to gateway (they will be created as child devices) 56 | gw.present(CHILD_ID_SND, S_CUSTOM); 57 | 58 | pinMode(SND_SENSOR_ANALOG_PIN, INPUT); 59 | pinMode(ledPin, OUTPUT); 60 | 61 | } 62 | 63 | void loop() 64 | { 65 | long sumOfSquares = 0; 66 | for (int i=0; ithreshold){ //is average more than the threshold ? 76 | digitalWrite(ledPin, HIGH); //if it is turn on the LED 77 | }else{ 78 | digitalWrite(ledPin, LOW); //if it isn't turn the LED off 79 | } 80 | Serial.println(runningAverage); //print the value so you can check it 81 | 82 | if (runningAverage != lastSND) { 83 | gw.send(soundMsg.set(runningAverage)); 84 | lastSND = runningAverage; 85 | } 86 | 87 | // Power down the radio. Note that the radio will get powered back up 88 | // on the next write() call. 89 | delay(1000); //delay to allow serial to fully print before sleep 90 | gw.sleep(SLEEP_TIME * 1000); //sleep for: sleepTime 91 | } 92 | -------------------------------------------------------------------------------- /AirQuality-CO2-MH-Z14.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MySensors Arduino library handles the wireless radio link and protocol 3 | * between your home built sensors/actuators and HA controller of choice. 4 | * The sensors forms a self healing radio network with optional repeaters. Each 5 | * repeater and gateway builds a routing tables in EEPROM which keeps track of the 6 | * network topology allowing messages to be routed to nodes. 7 | * 8 | * Created by Henrik Ekblad 9 | * Copyright (C) 2013-2015 Sensnology AB 10 | * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 11 | * 12 | * Documentation: http://www.mysensors.org 13 | * Support Forum: http://forum.mysensors.org 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License 17 | * version 2 as published by the Free Software Foundation. 18 | * 19 | ******************************* 20 | * 21 | * DESCRIPTION 22 | * 23 | * MH-Z14 CO2 sensor 24 | * 25 | * Wiring: 26 | * Pad 1, Pad 15: Vin (Voltage input 4.5V-6V) 27 | * Pad 2, Pad 3, Pad 12: GND 28 | * Pad 6: PWM output ==> pin 6 29 | * 30 | * From: http://davidegironi.blogspot.fr/2014/01/co2-meter-using-ndir-infrared-mh-z14.html 31 | * MH-Z14 has a PWM output, with a sensitivity range of 0ppm to 2000ppm CO2, an accurancy of ±200ppm. 32 | * The cycle is 1004ms±5%, given the duty cicle Th (pulse high), Tl is 1004-Th, we can convert it to CO2 value using the formula: 33 | * CO2ppm = 2000 * (Th - 2ms) /(Th + Tl - 4ms) 34 | * From: http://airqualityegg.wikispaces.com/Sensor+Tests 35 | * - response time is less than 30 s 36 | * - 3 minute warm up time 37 | * datasheet: http://www.futurlec.com/Datasheet/Sensor/MH-Z14.pdf 38 | * Contributor: epierre 39 | **/ 40 | 41 | 42 | #include 43 | #include 44 | 45 | #define CHILD_ID_AIQ 0 46 | #define AIQ_SENSOR_ANALOG_PIN 6 47 | 48 | unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds) 49 | 50 | float valAIQ =0.0; 51 | float lastAIQ =0.0; 52 | 53 | MySensor gw; 54 | MyMessage msg(CHILD_ID_AIQ, V_LEVEL); 55 | MyMessage msg2(CHILD_ID_AIQ, V_UNIT_PREFIX); 56 | 57 | void setup() 58 | { 59 | gw.begin(); 60 | 61 | // Send the sketch version information to the gateway and Controller 62 | gw.sendSketchInfo("AIQ Sensor CO2 MH-Z14", "1.0"); 63 | 64 | // Register all sensors to gateway (they will be created as child devices) 65 | gw.present(CHILD_ID_AIQ, S_AIR_QUALITY); 66 | gw.send(msg2.set("ppm")); 67 | 68 | gw.sleep(SLEEP_TIME); 69 | 70 | pinMode(AIQ_SENSOR_ANALOG_PIN, INPUT); 71 | 72 | } 73 | 74 | void loop() { 75 | 76 | //unsigned long duration = pulseIn(AIQ_SENSOR_ANALOG_PIN, HIGH); 77 | while(digitalRead(AIQ_SENSOR_ANALOG_PIN) == HIGH) {;} 78 | //wait for the pin to go HIGH and measure HIGH time 79 | unsigned long duration = pulseIn(AIQ_SENSOR_ANALOG_PIN, HIGH); 80 | 81 | //Serial.print(duration/1000); Serial.println(" ms "); 82 | //from datasheet 83 | //CO2 ppm = 2000 * (Th - 2ms) / (Th + Tl - 4ms) 84 | // given Tl + Th = 1004 85 | // Tl = 1004 - Th 86 | // = 2000 * (Th - 2ms) / (Th + 1004 - Th -4ms) 87 | // = 2000 * (Th - 2ms) / 1000 = 2 * (Th - 2ms) 88 | long co2ppm = 2 * ((duration/1000) - 2); 89 | //Serial.print(co2ppm); 90 | if ((co2ppm != lastAIQ)&&(abs(co2ppm-lastAIQ)>=10)) { 91 | gw.send(msg.set((long)ceil(co2ppm))); 92 | lastAIQ = ceil(co2ppm); 93 | } 94 | 95 | //Serial.println(); 96 | 97 | // Power down the radio. Note that the radio will get powered back up 98 | // on the next write() call. 99 | gw.sleep(SLEEP_TIME); //sleep for: sleepTime 100 | } 101 | -------------------------------------------------------------------------------- /AirQuality-CO-MH-Z14A.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MySensors Arduino library handles the wireless radio link and protocol 3 | * between your home built sensors/actuators and HA controller of choice. 4 | * The sensors forms a self healing radio network with optional repeaters. Each 5 | * repeater and gateway builds a routing tables in EEPROM which keeps track of the 6 | * network topology allowing messages to be routed to nodes. 7 | * 8 | * Created by Henrik Ekblad 9 | * Copyright (C) 2013-2015 Sensnology AB 10 | * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 11 | * 12 | * Documentation: http://www.mysensors.org 13 | * Support Forum: http://forum.mysensors.org 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License 17 | * version 2 as published by the Free Software Foundation. 18 | * 19 | ******************************* 20 | * 21 | * DESCRIPTION 22 | * 23 | * MH-Z14 CO2 sensor 24 | * 25 | * Wiring: 26 | * PWM PIN6 27 | * TXD PAD18 28 | * RXD PAD19 29 | * VCC PAD17 VCC 30 | * GND PAD16 GND 31 | * Analog 32 | * HD 33 | * 34 | * From: http://davidegironi.blogspot.fr/2014/01/co2-meter-using-ndir-infrared-mh-z14.html 35 | * MH-Z14 has a PWM output, with a sensitivity range of 0ppm to 2000ppm CO2, an accurancy of ±200ppm. 36 | * The cycle is 1004ms±5%, given the duty cicle Th (pulse high), Tl is 1004-Th, we can convert it to CO2 value using the formula: 37 | * CO2ppm = 2000 * (Th - 2ms) /(Th + Tl - 4ms) 38 | * From: http://airqualityegg.wikispaces.com/Sensor+Tests 39 | * - response time is less than 30 s 40 | * - 3 minute warm up time 41 | * datasheet: http://www.futurlec.com/Datasheet/Sensor/MH-Z14.pdf 42 | * Contributor: epierre 43 | **/ 44 | 45 | 46 | #include 47 | #include 48 | 49 | #define CHILD_ID_AIQ 0 50 | #define AIQ_SENSOR_ANALOG_PIN 6 51 | 52 | unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds) 53 | 54 | float valAIQ =0.0; 55 | float lastAIQ =0.0; 56 | 57 | MySensor gw; 58 | MyMessage msg(CHILD_ID_AIQ, V_LEVEL); 59 | MyMessage msg2(CHILD_ID_AIQ, V_UNIT_PREFIX); 60 | 61 | void setup() 62 | { 63 | gw.begin(); 64 | 65 | // Send the sketch version information to the gateway and Controller 66 | gw.sendSketchInfo("AIQ Sensor CO2 MH-Z14A", "1.0"); 67 | 68 | // Register all sensors to gateway (they will be created as child devices) 69 | gw.present(CHILD_ID_AIQ, S_AIR_QUALITY); 70 | gw.send(msg2.set("ppm")); 71 | 72 | gw.sleep(SLEEP_TIME); 73 | 74 | pinMode(AIQ_SENSOR_ANALOG_PIN, INPUT); 75 | 76 | } 77 | 78 | void loop() { 79 | 80 | //unsigned long duration = pulseIn(AIQ_SENSOR_ANALOG_PIN, HIGH); 81 | while(digitalRead(AIQ_SENSOR_ANALOG_PIN) == HIGH) {;} 82 | //wait for the pin to go HIGH and measure HIGH time 83 | unsigned long duration = pulseIn(AIQ_SENSOR_ANALOG_PIN, HIGH); 84 | 85 | //Serial.print(duration/1000); Serial.println(" ms "); 86 | //from datasheet 87 | //CO2 ppm = 2000 * (Th - 2ms) / (Th + Tl - 4ms) 88 | // given Tl + Th = 1004 89 | // Tl = 1004 - Th 90 | // = 2000 * (Th - 2ms) / (Th + 1004 - Th -4ms) 91 | // = 2000 * (Th - 2ms) / 1000 = 2 * (Th - 2ms) 92 | long co2ppm = 2 * ((duration/1000) - 2); 93 | //Serial.print(co2ppm); 94 | if ((co2ppm != lastAIQ)&&(abs(co2ppm-lastAIQ)>=10)) { 95 | gw.send(msg.set((long)ceil(co2ppm))); 96 | lastAIQ = ceil(co2ppm); 97 | } 98 | 99 | //Serial.println(); 100 | 101 | // Power down the radio. Note that the radio will get powered back up 102 | // on the next write() call. 103 | gw.sleep(SLEEP_TIME); //sleep for: sleepTime 104 | } 105 | -------------------------------------------------------------------------------- /DustSensor_Sharp_GP2Y1010AU.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MySensors Arduino library handles the wireless radio link and protocol 3 | * between your home built sensors/actuators and HA controller of choice. 4 | * The sensors forms a self healing radio network with optional repeaters. Each 5 | * repeater and gateway builds a routing tables in EEPROM which keeps track of the 6 | * network topology allowing messages to be routed to nodes. 7 | * 8 | * Created by Henrik Ekblad 9 | * Copyright (C) 2013-2015 Sensnology AB 10 | * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 11 | * 12 | * Documentation: http://www.mysensors.org 13 | * Support Forum: http://forum.mysensors.org 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License 17 | * version 2 as published by the Free Software Foundation. 18 | * 19 | ******************************* 20 | * 21 | * REVISION HISTORY 22 | * Version 1.0 - epierre 23 | * Converted to 1.4 by Henrik Ekblad 24 | * corrections by brnwshr 25 | * 26 | * DESCRIPTION 27 | * Arduino Dust Sensort 28 | * 29 | * connect the sensor as follows : 30 | * 31 | * VCC >>> 5V 32 | * A >>> A0 33 | * GND >>> GND 34 | * LED >>> 2 35 | * 36 | * Based on: http://www.dfrobot.com/wiki/index.php/Sharp_GP2Y1010AU 37 | * Authors: Cyrille Médard de Chardon (serialC), Christophe Trefois (Trefex) 38 | * 39 | * http://www.mysensors.org/build/dust 40 | * 41 | */ 42 | 43 | #include 44 | #include 45 | #define CHILD_ID_DUST 0 46 | #define DUST_SENSOR_ANALOG_PIN 0 47 | 48 | int ledPower = 2; 49 | const int numreadings = 60; 50 | 51 | unsigned long SLEEP_TIME = 10*1000; // Sleep time between reads (in milliseconds) 52 | //VARIABLES 53 | int val = 0;// variable to store the value coming from the sensor 54 | float valDUST = 0.0; 55 | float lastDUST =0.0; 56 | int samplingTime = 280; 57 | int deltaTime = 40; 58 | int sleepTime = 9680; 59 | float voMeasured = 0; 60 | float calcVoltage = 0; 61 | float dustDensity = 0; 62 | 63 | MySensor gw; 64 | MyMessage dustMsg(CHILD_ID_DUST, V_LEVEL); 65 | 66 | void setup(){ 67 | gw.begin(); 68 | pinMode(ledPower,OUTPUT); 69 | // Send the sketch version information to the gateway and Controller 70 | gw.sendSketchInfo("Dust Sensor", "1.1"); 71 | // Register all sensors to gateway (they will be created as child devices) 72 | gw.present(CHILD_ID_DUST, S_DUST); 73 | } 74 | 75 | void loop(){ 76 | float cumsum = 0; 77 | float temp = 0; 78 | float temp1 = 0; 79 | float cum_density = 0; 80 | for (int sample = 0; sample < numreadings; sample ++){ // loop reading dust sensor 81 | digitalWrite(ledPower,LOW); // power on the LED 82 | delayMicroseconds(samplingTime); 83 | uint16_t voMeasured = analogRead(DUST_SENSOR_ANALOG_PIN);// Get DUST value 84 | delayMicroseconds(deltaTime); 85 | digitalWrite(ledPower,HIGH); // turn the LED off 86 | // 0 - 5V mapped to 0 - 1023 integer values 87 | // recover voltage 88 | temp = voMeasured * (5.0 / 1024.0); 89 | cumsum = cumsum + temp;// cumulative sum over 60 seconds 90 | delay(1000); 91 | Serial.print("reading sample: "); 92 | Serial.println(sample); 93 | Serial.print("Raw Signal Value (0-1023): "); 94 | Serial.println(voMeasured); 95 | Serial.print(" - cumulative: "); 96 | Serial.println(cumsum); 97 | } 98 | // linear eqaution taken from http://www.howmuchsnow.com/arduino/airquality/ 99 | // Chris Nafis (c) 2012 100 | dustDensity = (0.17 * cumsum - 0.1)* (1000/60); 101 | 102 | Serial.print(" - Dust Density: "); 103 | Serial.println(dustDensity); // unit: ug/m3 104 | Serial.println("#########################################"); 105 | 106 | // if (ceil(dustDensity) != lastDUST) { 107 | // gw.send(dustMsg.set((int)ceil(dustDensity))); 108 | // lastDUST = ceil(dustDensity); 109 | // } 110 | gw.send(dustMsg.set((int)ceil(dustDensity))); 111 | gw.sleep(SLEEP_TIME); 112 | } 113 | -------------------------------------------------------------------------------- /AirQuality-MQ135.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino MQ135 3 | 4 | connect the sensor as follows for raw sensor : 5 | 6 | A H A >>> 5V 7 | B >>> A0 8 | H >>> GND 9 | B >>> 10K ohm >>> GND 10 | 11 | connect the sensor as follows if on board : 12 | 13 | Vcc >>> 5V 14 | Gnd >>> Gnd 15 | Aout >>> A0 16 | 17 | Contribution: epierre 18 | Based on David Gironi http://davidegironi.blogspot.fr/2014/01/cheap-co2-meter-using-mq135-sensor-with.html 19 | http://skylink.dl.sourceforge.net/project/davidegironi/avr-lib/avr_lib_mq135_01.zip 20 | 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #define CHILD_ID_AIQ 0 28 | #define AIQ_SENSOR_ANALOG_PIN 0 29 | 30 | #define MQ135_DEFAULTPPM 399 //default ppm of CO2 for calibration 31 | #define MQ135_DEFAULTRO 68550 //default Ro for MQ135_DEFAULTPPM ppm of CO2 32 | #define MQ135_SCALINGFACTOR 116.6020682 //CO2 gas value 33 | #define MQ135_EXPONENT -2.769034857 //CO2 gas value 34 | #define MQ135_MAXRSRO 2.428 //for CO2 35 | #define MQ135_MINRSRO 0.358 //for CO2 36 | 37 | unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in seconds) 38 | //VARIABLES 39 | double mq135_ro = 10000; // this has to be tuned 10K Ohm 40 | double val = 0; // variable to store the value coming from the sensor 41 | double valAIQ =0; 42 | double lastAIQ =0; 43 | 44 | MySensor gw; 45 | MyMessage msg(CHILD_ID_AIQ, V_LEVEL); 46 | 47 | void setup() 48 | { 49 | gw.begin(); 50 | 51 | // Send the sketch version information to the gateway and Controller 52 | gw.sendSketchInfo("AIQ Sensor MQ135", "1.0"); 53 | 54 | // Register all sensors to gateway (they will be created as child devices) 55 | gw.present(CHILD_ID_AIQ, S_AIR_QUALITY); 56 | 57 | } 58 | 59 | /* 60 | * get the calibrated ro based upon read resistance, and a know ppm 61 | */ 62 | long mq135_getro(long resvalue, double ppm) { 63 | return (long)(resvalue * exp( log(MQ135_SCALINGFACTOR/ppm) / MQ135_EXPONENT )); 64 | } 65 | 66 | /* 67 | * get the ppm concentration 68 | */ 69 | double mq135_getppm(long resvalue, long ro) { 70 | double ret = 0; 71 | double validinterval = 0; 72 | validinterval = resvalue/(double)ro; 73 | if(validintervalMQ135_MINRSRO) { 74 | ret = (double)((double)MQ135_SCALINGFACTOR * pow( ((double)resvalue/ro), MQ135_EXPONENT)); 75 | } 76 | return ret; 77 | } 78 | 79 | void loop() 80 | { 81 | double valr = analogRead(AIQ_SENSOR_ANALOG_PIN);// Get AIQ value 82 | Serial.println(val); 83 | double val = ((float)22000*(1023-valr)/valr); 84 | //during clean air calibration, read the Ro value and replace MQ135_DEFAULTRO value with it, you can even deactivate following function call. 85 | mq135_ro = mq135_getro(val, MQ135_DEFAULTPPM); 86 | //convert to ppm (using default ro) 87 | valAIQ = mq135_getppm(val, MQ135_DEFAULTRO); 88 | 89 | Serial.print ( "Val / Ro / value:"); 90 | Serial.print ( val); 91 | Serial.print ( " / "); 92 | Serial.print ( mq135_ro); 93 | Serial.print ( " / "); 94 | Serial.print ( valAIQ); 95 | 96 | 97 | if (valAIQ != lastAIQ) { 98 | gw.send(msg.set(MQ135_DEFAULTPPM+(int)ceil(valAIQ))); 99 | lastAIQ = ceil(valAIQ); 100 | } 101 | 102 | // Power down the radio. Note that the radio will get powered back up 103 | // on the next write() call. 104 | gw.sleep(SLEEP_TIME); //sleep for: sleepTime 105 | } 106 | 107 | 108 | /***************************** MQGetPercentage ********************************** 109 | Input: rs_ro_ratio - Rs divided by Ro 110 | pcurve - pointer to the curve of the target gas 111 | Output: ppm of the target gas 112 | Remarks: By using the slope and a point of the line. The x(logarithmic value of ppm) 113 | of the line could be derived if y(rs_ro_ratio) is provided. As it is a 114 | logarithmic coordinate, power of 10 is used to convert the result to non-logarithmic 115 | value. 116 | ************************************************************************************/ 117 | int MQGetPercentage(float rs_ro_ratio, float ro, float *pcurve) 118 | { 119 | return (double)(pcurve[0] * pow(((double)rs_ro_ratio/ro), pcurve[1])); 120 | } 121 | -------------------------------------------------------------------------------- /AirQuality-HCHO.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MySensors Arduino library handles the wireless radio link and protocol 3 | * between your home built sensors/actuators and HA controller of choice. 4 | * The sensors forms a self healing radio network with optional repeaters. Each 5 | * repeater and gateway builds a routing tables in EEPROM which keeps track of the 6 | * network topology allowing messages to be routed to nodes. 7 | * 8 | * Created by Henrik Ekblad 9 | * Copyright (C) 2013-2015 Sensnology AB 10 | * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 11 | * 12 | * Documentation: http://www.mysensors.org 13 | * Support Forum: http://forum.mysensors.org 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License 17 | * version 2 as published by the Free Software Foundation. 18 | * 19 | ******************************* 20 | * 21 | * DESCRIPTION 22 | * 23 | * Air Quality Sensors for HCHO sensor 24 | * 25 | * Wiring: 26 | * 27 | * sensor side with 7 pin on the right, top pin is 1 28 | * 1 VCC -> (Arduino) 3V3 29 | * 2 GND -> (Arduino) GND 30 | * 6 TX -> (Arduino) Pin 8 31 | * 7 RX -> (Arduino) Pin 7 32 | * 33 | * based on: terryroy terry.ouyang@gmail.com 34 | * https://github.com/terryoy/airmonitor 35 | * Contribution: epierre 36 | * 37 | * Precaution: 38 | * The gasses detected by these gas sensors can be deadly in high concentrations. Always be careful to perform gas tests in well ventilated areas. 39 | * 40 | * Note: 41 | * THESE GAS SENSOR MODULES ARE NOT DESIGNED FOR OR APPROVED FOR ANY APPLICATION INVOLVING HEALTH OR HUMAN SAFETY. THESE GAS SENSOR MODULES ARE FOR EXPERIMENTAL PURPOSES ONLY.* 42 | /* 43 | 44 | */ 45 | #include 46 | #include 47 | 48 | /* initialize air module */ 49 | #include 50 | #include "AirModule.h" 51 | #define rxPin 8 52 | #define txPin 7 53 | #define CHILD_ID 0 54 | 55 | unsigned long SLEEP_TIME = 600; // Sleep time between reads (in seconds) 56 | 57 | //VARIABLES 58 | int val = 0; // variable to store the value coming from the sensor 59 | 60 | SoftwareSerial airSerial(8, 7); // RX, TX 61 | char testCmd[7] = {0x42, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x90}; 62 | char testResponse[20]; 63 | 64 | MySensor gw; // Arduino initialization 65 | MyMessage msg_hcho(CHILD_ID, V_UNIT_PREFIX); 66 | 67 | void setup() { 68 | 69 | 70 | 71 | Serial.begin(115200); 72 | airSerial.begin(9600); 73 | 74 | pinMode(rxPin, INPUT); 75 | pinMode(txPin, OUTPUT); 76 | 77 | gw.begin(); 78 | 79 | // Send the sketch version information to the gateway and Controller 80 | gw.sendSketchInfo("AIQ HCHO", "1.1"); 81 | 82 | gw.present(CHILD_ID, S_AIR_QUALITY); 83 | gw.send(msg_hcho.set("ppm")); 84 | } 85 | 86 | void loop() { 87 | // request hcho 88 | for (int i=0;i<7;i++) { 89 | airSerial.write(testCmd[i]); 90 | } 91 | 92 | // response handle 93 | int pos = 0; 94 | for (int i=0;i<20;i++) testResponse[i]=0x00; // clean buffer 95 | 96 | while (airSerial.available() && pos < 20) { 97 | testResponse[pos] = airSerial.read(); 98 | //Serial.print(testResponse[pos], HEX); 99 | delay(1); 100 | 101 | if (pos == 0 && testResponse[pos] != 0x42) { 102 | testResponse[pos] = 0; // clear the buffer 103 | } else if (pos == 1 && testResponse[pos] != 0x4d) { 104 | testResponse[0] = testResponse[1] = 0; 105 | pos = 0; 106 | } 107 | else { 108 | pos++; 109 | } 110 | } 111 | 112 | // parse data 113 | if (testResponse[0] == 0x42 && testResponse[1] == 0x4d) { 114 | //for (int i=0;i<20;i++) Serial.print(testResponse[i], HEX); 115 | AirResponse air; 116 | parse_air_response(&air, testResponse); 117 | //move mg/m3 to ppm, HCHO weight is 30.03 118 | long int v=get_read_value(air)*(8,31441*298,15)/(30,03*101,325); 119 | 120 | if ((v != val)&&(v>0)) { 121 | gw.send(msg_hcho.set((long int)v)); 122 | val=v; 123 | } 124 | //Serial.println(get_unit_display(air)); 125 | } 126 | 127 | // Power down the radio. Note that the radio will get powered back up 128 | // on the next write() call. 129 | gw.sleep(SLEEP_TIME*4); //sleep for: sleepTime 130 | } 131 | -------------------------------------------------------------------------------- /WaterMeterPulseSensor2-gs.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Use this sensor to measure volume and flow of your house watermeter. 3 | You need to set the correct pulsefactor of your meter (pulses per m3). 4 | The sensor starts by fetching current volume reading from gateway (VAR 1). 5 | Reports both volume and flow back to gateway. 6 | 7 | Sensor on pin analog 0 8 | 9 | DFRobot Analog Grayscale Sensor V2 10 | Watermeter sensus Residia Jet 11 | 12 | http://www.dfrobot.com/index.php?route=product/product&product_id=81 13 | 14 | Contribution: Hek, adapted by epierre to greyscale sensor water meter 15 | 16 | * This program is free software; you can redistribute it and/or 17 | * modify it under the terms of the GNU General Public License 18 | * version 2 as published by the Free Software Foundation. 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #define ANALOG_INPUT_SENSOR 0 // The analog input you attached your sensor. 25 | #define PULSE_FACTOR 1000 // Nummber of blinks per m3 of your meter (One rotation/liter) 26 | #define SLEEP_MODE false // flowvalue can only be reported when sleep mode is false. 27 | #define MAX_FLOW 40 // Max flow (l/min) value to report. This filetrs outliers. 28 | #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) 29 | #define CHILD_ID 1 // Id of the sensor child 30 | unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in seconds). We don't want to spam the gateway. 31 | 32 | MySensor gw; 33 | MyMessage flowMsg(CHILD_ID,V_FLOW); 34 | MyMessage volumeMsg(CHILD_ID,V_VOLUME); 35 | MyMessage pcMsg(CHILD_ID,V_VAR1); 36 | 37 | double ppl = ((double)PULSE_FACTOR)/1000; // Pulses per liter 38 | 39 | volatile unsigned long pulseCount = 0; 40 | volatile unsigned long lastBlink = 0; 41 | volatile double flow = 0; 42 | boolean pcReceived = false; 43 | unsigned long oldPulseCount = 0; 44 | unsigned long newBlink = 0; 45 | double oldflow = 0; 46 | double volume; 47 | double oldvolume; 48 | unsigned long lastSend; 49 | unsigned long lastPulse; 50 | unsigned long currentTime; 51 | boolean metric; 52 | long lastDebounce = 0; 53 | long debounceDelay = 500; // Ignore bounces under 1/2 second 54 | int oldval =0; 55 | int val=0; 56 | 57 | 58 | void setup() 59 | { 60 | gw.begin(incomingMessage); 61 | 62 | // Send the sketch version information to the gateway and Controller 63 | gw.sendSketchInfo("Water Meter", "1.0 greyscale"); 64 | 65 | // Register this device as Waterflow sensor 66 | gw.present(CHILD_ID, S_WATER); 67 | 68 | pulseCount = oldPulseCount = 0; 69 | 70 | // Fetch last known pulse count value from gw 71 | gw.request(CHILD_ID, V_VAR1); 72 | 73 | lastSend = millis(); 74 | 75 | //led blinking 76 | pinMode(13, OUTPUT); 77 | } 78 | 79 | 80 | void loop() 81 | { 82 | gw.process(); 83 | currentTime = millis(); 84 | 85 | val=analogRead(0); 86 | //Serial.println(val); 87 | if ((oldval <100) and (val >100)) { 88 | pulseCount++; 89 | oldval=val; 90 | } else { 91 | oldval=val; 92 | } 93 | delay(100); 94 | // Only send values at a maximum frequency or woken up from sleep 95 | bool sendTime = currentTime - lastSend > SEND_FREQUENCY; 96 | 97 | // Pulse count has changed 98 | if (pulseCount != oldPulseCount) { 99 | gw.send(pcMsg.set(pulseCount)); // Send volumevalue to gw VAR1 100 | double volume = ((double)pulseCount/((double)PULSE_FACTOR)); 101 | oldPulseCount = pulseCount; 102 | if (volume != oldvolume) { 103 | gw.send(volumeMsg.set(volume, 3)); // Send volume value to gw 104 | //Serial.print("V="); 105 | //Serial.println(volume); 106 | oldvolume = volume; 107 | } 108 | } 109 | lastSend = currentTime; 110 | if (sendTime && !pcReceived) { 111 | // No count received. Try requesting it again 112 | gw.request(CHILD_ID, V_VAR1); 113 | lastSend=currentTime; 114 | } 115 | 116 | 117 | } 118 | 119 | 120 | void incomingMessage(const MyMessage &message) { 121 | if (message.type==V_VAR1) { 122 | pulseCount = oldPulseCount = message.getLong(); 123 | Serial.print("Received last pulse count from gw:"); 124 | Serial.println(pulseCount); 125 | pcReceived = true; 126 | } 127 | } 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /EnergyMeterPulseSensorTCRT5000.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MySensors Arduino library handles the wireless radio link and protocol 3 | * between your home built sensors/actuators and HA controller of choice. 4 | * The sensors forms a self healing radio network with optional repeaters. Each 5 | * repeater and gateway builds a routing tables in EEPROM which keeps track of the 6 | * network topology allowing messages to be routed to nodes. 7 | * 8 | * Created by Henrik Ekblad 9 | * Copyright (C) 2013-2015 Sensnology AB 10 | * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 11 | * 12 | * Documentation: http://www.mysensors.org 13 | * Support Forum: http://forum.mysensors.org 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License 17 | * version 2 as published by the Free Software Foundation. 18 | * 19 | ******************************* 20 | * 21 | * REVISION HISTORY 22 | * Version 1.0 - Henrik EKblad 23 | * 24 | * DESCRIPTION 25 | * This sketch provides an example how to implement a distance sensor using HC-SR04 26 | * Use this sensor to measure KWH and Watt of your house meeter 27 | * You need to set the correct pulsefactor of your meeter (blinks per KWH). 28 | * The sensor starts by fetching current KWH value from gateway. 29 | * Reports both KWH and Watt back to gateway. 30 | * 31 | * Unfortunately millis() won't increment when the Arduino is in 32 | * sleepmode. So we cannot make this sensor sleep if we also want 33 | * to calculate/report watt-number. 34 | * http://www.mysensors.org/build/pulse_power 35 | */ 36 | 37 | #include 38 | #include 39 | 40 | #define ANALOG_INPUT_SENSOR 0 // The analog input you attached your sensor. 41 | #define PULSE_FACTOR 1000 // Nummber of blinks per KWH of your meter 42 | #define SLEEP_MODE false // Watt-value can only be reported when sleep mode is false. 43 | #define MAX_WATT 10000 // Max watt value to report. This filters outliers. 44 | #define CHILD_ID 1 // Id of the sensor child 45 | unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't wnat to spam the gateway. 46 | 47 | MySensor gw; 48 | double ppwh = ((double)PULSE_FACTOR)/1000; // Pulses per watt hour 49 | boolean pcReceived = false; 50 | volatile unsigned long pulseCount = 0; 51 | volatile unsigned long lastBlink = 0; 52 | volatile unsigned long watt = 0; 53 | unsigned long oldPulseCount = 0; 54 | unsigned long oldWatt = 0; 55 | double oldKwh; 56 | unsigned long lastSend; 57 | int val=0;int oldval=0; 58 | 59 | MyMessage wattMsg(CHILD_ID,V_WATT); 60 | MyMessage kwhMsg(CHILD_ID,V_KWH); 61 | MyMessage pcMsg(CHILD_ID,V_VAR1); 62 | 63 | 64 | void setup() 65 | { 66 | gw.begin(incomingMessage); 67 | 68 | // Send the sketch version information to the gateway and Controller 69 | gw.sendSketchInfo("Energy Meter TCRT5000", "1.0"); 70 | 71 | // Register this device as power sensor 72 | gw.present(CHILD_ID, S_POWER); 73 | 74 | // Fetch last known pulse count value from gw 75 | gw.request(CHILD_ID, V_VAR1); 76 | 77 | pulseCount = oldPulseCount = 0; 78 | 79 | lastSend=millis(); 80 | 81 | Serial.begin(115200); 82 | } 83 | 84 | 85 | void loop() 86 | { 87 | gw.process(); 88 | unsigned long now = millis(); 89 | 90 | val=analogRead(ANALOG_INPUT_SENSOR); 91 | Serial.println("-"); 92 | Serial.println(val); 93 | Serial.println("-"); 94 | if ((oldval <100) and (val >100)) { 95 | pulseCount++; 96 | oldval=val; 97 | Serial.println(val); 98 | Serial.println(" "); 99 | } else { 100 | oldval=val; 101 | } 102 | delay(100); 103 | 104 | // Only send values at a maximum frequency or woken up from sleep 105 | bool sendTime = now - lastSend > SEND_FREQUENCY; 106 | 107 | // Pulse cout has changed 108 | if (pulseCount != oldPulseCount) { 109 | gw.send(pcMsg.set(pulseCount)); // Send pulse count value to gw 110 | double kwh = ((double)pulseCount/((double)PULSE_FACTOR)); 111 | oldPulseCount = pulseCount; 112 | if (kwh != oldKwh) { 113 | gw.send(kwhMsg.set(kwh, 4)); // Send kwh value to gw 114 | oldKwh = kwh; 115 | } 116 | } 117 | lastSend = now; 118 | if (sendTime && !pcReceived) { 119 | // No count received. Try requesting it again 120 | gw.request(CHILD_ID, V_VAR1); 121 | lastSend=now; 122 | } 123 | 124 | } 125 | 126 | void incomingMessage(const MyMessage &message) { 127 | if (message.type==V_VAR1) { 128 | pulseCount = oldPulseCount = message.getLong(); 129 | Serial.print("Received last pulse count from gw:"); 130 | Serial.println(pulseCount); 131 | pcReceived = true; 132 | } 133 | } 134 | 135 | 136 | -------------------------------------------------------------------------------- /LuxSensor-c.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Vera Arduino BH1750FVI Light sensor 3 | communicate using I2C Protocol 4 | this library enable 2 slave device addresses 5 | Main address 0x23 6 | secondary address 0x5C 7 | connect the sensor as follows : 8 | 9 | VCC >>> 5V 10 | Gnd >>> Gnd 11 | ADDR >>> NC or GND 12 | SCL >>> A5 13 | SDA >>> A4 14 | 15 | Contribution: idefix/epierre for ceech 16 | 17 | */ 18 | #include 19 | #include 20 | #include // I2C 21 | #include 22 | 23 | #define LTC4067_CHRG_PIN A1 //analog input A1 on ATmega 328 is /CHRG signal from LTC4067 24 | #define batteryVoltage_PIN A0 //analog input A0 on ATmega328 is battery voltage ( /2) 25 | #define solarVoltage_PIN A2 //analog input A2 is solar cell voltage (/ 2) 26 | #define solarCurrent_PIN A6 //analog input A6 is input current ( I=V/Rclprog x 1000 ) 27 | #define batteryChargeCurrent_PIN A7 //analog input A7 is battery charge current ( I=V/Rprog x 1000 ) 28 | #define LTC4067_SUSPEND_PIN 9 //digital output D9 - drive it high to put LTC4067 in SUSPEND mode 29 | 30 | const float VccMin = 1.0*3.5; // Minimum expected Vcc level, in Volts. Example for 1 rechargeable lithium-ion. 31 | const float VccMax = 1.0*4.2; // Maximum expected Vcc level, in Volts. 32 | 33 | #define LIGHT_SENSOR_ANALOG_PIN 3 // Digital input did you attach your soil sensor. 34 | #define CHILD_ID_LIGHT 0 // Id of the sensor child 35 | #define BATT_CHILD_ID 10 36 | #define SOLAR_CHILD_ID 11 37 | 38 | // PIN Radio 39 | #define RADIO_CE_PIN 7 // radio chip enable 40 | #define RADIO_SS_PIN 8 // CS SS serial select 41 | 42 | float lastBattVoltage; 43 | float lastBattCurrent; 44 | float lastSolarVoltage; 45 | float lastSolarCurrent; 46 | int lastBattPct = 0; 47 | uint16_t lastlux; 48 | float VccReference = 3.3 ; // voltage reference for measurement, definitive init in setup 49 | 50 | BH1750 lightSensor; 51 | MySensor gw(RADIO_CE_PIN, RADIO_SS_PIN); 52 | unsigned long SLEEP_TIME = 30*1000; // sleep time between reads (seconds * 1000 milliseconds) 53 | MyMessage msg(CHILD_ID_LIGHT, V_LIGHT_LEVEL); 54 | MyMessage batteryVoltageMsg(BATT_CHILD_ID, V_VOLTAGE); // Battery voltage (V) 55 | MyMessage batteryCurrentMsg(BATT_CHILD_ID, V_CURRENT); // Battery current (A) 56 | MyMessage solarVoltageMsg(SOLAR_CHILD_ID, V_VOLTAGE); // Solar voltage (V) 57 | MyMessage solarCurrentMsg(SOLAR_CHILD_ID, V_CURRENT); // Solar current (A) 58 | 59 | void setup() 60 | { 61 | 62 | gw.begin(); 63 | 64 | // Send the sketch version information to the gateway and Controller 65 | gw.sendSketchInfo("Light Lux Sensor", "1.0"); 66 | // Register all sensors to gw (they will be created as child devices) 67 | gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL); 68 | gw.present(BATT_CHILD_ID, S_POWER); // Battery parameters 69 | gw.present(SOLAR_CHILD_ID, S_POWER); // Solar parameters 70 | 71 | // use VCC (3.3V) reference 72 | analogReference(DEFAULT); // default external reference = 3.3v for Ceech board 73 | VccReference = 3.323 ; // measured Vcc input (on board LDO) 74 | pinMode(LTC4067_SUSPEND_PIN, OUTPUT); // suspend of Lion charger set 75 | digitalWrite(LTC4067_SUSPEND_PIN,LOW); // active (non suspend) at start 76 | 77 | lightSensor.begin(); 78 | } 79 | 80 | void loop() 81 | { 82 | 83 | sendVoltage(); 84 | 85 | uint16_t lux = lightSensor.readLightLevel();// Get Lux value 86 | Serial.println(lux); 87 | if (lux != lastlux) { 88 | gw.send(msg.set(lux)); 89 | lastlux = lux; 90 | } 91 | 92 | // Power down the radio 93 | gw.sleep(SLEEP_TIME); 94 | } 95 | 96 | void sendVoltage(void) 97 | // battery and charging values 98 | { 99 | // get Battery Voltage & charge current 100 | float batteryVoltage = ((float)analogRead(batteryVoltage_PIN)* VccReference/1024) * 2; // actual voltage is double 101 | Serial.print("Batt: "); 102 | Serial.print(batteryVoltage); 103 | Serial.print("V ; "); 104 | float batteryChargeCurrent = ((float)analogRead(batteryChargeCurrent_PIN) * VccReference/1024)/ 2.5 ; // current(A) = V/Rprog(kohm) 105 | Serial.print(batteryChargeCurrent); 106 | Serial.println("A "); 107 | 108 | 109 | // get Solar Voltage & charge current 110 | float solarVoltage = ((float)analogRead(solarVoltage_PIN)/1024 * VccReference) * 2 ; // actual voltage is double 111 | Serial.print("Solar: "); 112 | Serial.print(solarVoltage); 113 | Serial.print("V ; "); 114 | // get Solar Current 115 | float solarCurrent = ((float)analogRead(solarCurrent_PIN)/1024 * VccReference)/ 2.5; // current(A) = V/Rclprog(kohm) 116 | Serial.print(solarCurrent); 117 | Serial.print(" A; charge: "); 118 | Serial.println(digitalRead(LTC4067_CHRG_PIN)?"No":"Yes"); 119 | 120 | // send battery percentage for node 121 | int battPct = 1 ; 122 | if (batteryVoltage > VccMin){ 123 | battPct = 100.0*(batteryVoltage - VccMin)/(VccMax - VccMin); 124 | } 125 | Serial.print("BattPct: "); 126 | Serial.print(battPct); 127 | Serial.println("% "); 128 | 129 | if (lastBattPct != battPct) { 130 | gw.send(batteryVoltageMsg.set(batteryVoltage, 3)); // Send (V) 131 | gw.send(batteryCurrentMsg.set(batteryChargeCurrent, 6)); // Send (Amps) 132 | gw.send(solarVoltageMsg.set(solarVoltage, 3)); // Send (V) 133 | gw.send(solarCurrentMsg.set(solarCurrent, 6)); // Send (Amps) 134 | gw.sendBatteryLevel(battPct); 135 | lastBattPct = battPct; 136 | } 137 | } 138 | 139 | -------------------------------------------------------------------------------- /WaterMeterPulseSensor2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Use this sensor to measure volume and flow of your house watermeter. 3 | You need to set the correct pulsefactor of your meter (pulses per m3). 4 | The sensor starts by fetching current volume reading from gateway (VAR 1). 5 | Reports both volume and flow back to gateway. 6 | 7 | Unfortunately millis() won't increment when the Arduino is in 8 | sleepmode. So we cannot make this sensor sleep if we also want 9 | to calculate/report flow. 10 | 11 | Sensor on pin 3 12 | 13 | Contribution: Hek, adapted by epierre to reed water meter 14 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 15 | 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!) 23 | #define PULSE_FACTOR 1000 // Nummber of blinks per m3 of your meter (One rotation/liter) 24 | #define SLEEP_MODE false // flowvalue can only be reported when sleep mode is false. 25 | #define MAX_FLOW 40 // Max flow (l/min) value to report. This filetrs outliers. 26 | #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) 27 | #define CHILD_ID 5 // Id of the sensor child 28 | unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in seconds). We don't want to spam the gateway. 29 | 30 | MySensor gw; 31 | MyMessage flowMsg(CHILD_ID,V_FLOW); 32 | MyMessage volumeMsg(CHILD_ID,V_VOLUME); 33 | MyMessage pcMsg(CHILD_ID,V_VAR1); 34 | 35 | double ppl = ((double)PULSE_FACTOR)/1000; // Pulses per liter 36 | 37 | volatile unsigned long pulseCount = 0; 38 | volatile unsigned long lastBlink = 0; 39 | volatile double flow = 0; 40 | boolean pcReceived = false; 41 | unsigned long oldPulseCount = 0; 42 | unsigned long newBlink = 0; 43 | double oldflow = 0; 44 | double volume; 45 | double oldvolume; 46 | unsigned long lastSend; 47 | unsigned long lastPulse; 48 | unsigned long currentTime; 49 | boolean metric; 50 | long lastDebounce = 0; 51 | long debounceDelay = 500; // Ignore bounces under 1/2 second 52 | 53 | 54 | void setup() 55 | { 56 | gw.begin(incomingMessage); 57 | 58 | // Send the sketch version information to the gateway and Controller 59 | gw.sendSketchInfo("Water Meter", "1.0 reed"); 60 | 61 | // Register this device as Waterflow sensor 62 | gw.present(CHILD_ID, S_WATER); 63 | 64 | // Fetch last known pulse count value from gw 65 | gw.request(CHILD_ID, V_VAR1); 66 | 67 | //Serial.print("Last pulse count from gw:"); 68 | //Serial.println(pulseCount); 69 | // attachInterrupt(INTERRUPT, onPulse, RISING); 70 | lastSend = millis(); 71 | 72 | // Setup the reed 73 | pinMode(DIGITAL_INPUT_SENSOR,INPUT); 74 | // Activate internal pull-up 75 | digitalWrite(DIGITAL_INPUT_SENSOR,HIGH); 76 | 77 | attachInterrupt(INTERRUPT, onPulse, FALLING); 78 | 79 | //led blinking 80 | pinMode(13, OUTPUT); 81 | } 82 | 83 | 84 | void loop() 85 | { 86 | gw.process(); 87 | currentTime = millis(); 88 | 89 | // Only send values at a maximum frequency or woken up from sleep 90 | bool sendTime = currentTime - lastSend > SEND_FREQUENCY; 91 | if (pcReceived && (SLEEP_MODE || sendTime)) { 92 | // New flow value has been calculated 93 | if (!SLEEP_MODE && flow != oldflow) { 94 | // Check that we dont get unresonable large flow value. 95 | // could hapen when long wraps or false interrupt triggered 96 | if (flow<((unsigned long)MAX_FLOW)) { 97 | gw.send(flowMsg.set(flow, 2)); // Send flow value to gw 98 | } 99 | //Serial.print("l/min:"); 100 | //Serial.println(flow); 101 | oldflow = flow; 102 | } 103 | 104 | // No Pulse count in 2min 105 | if(currentTime - lastPulse > 120000){ 106 | flow = 0; 107 | } 108 | 109 | 110 | // Pulse count has changed 111 | if (pulseCount != oldPulseCount) { 112 | gw.send(pcMsg.set(pulseCount)); // Send volumevalue to gw VAR1 113 | double volume = ((double)pulseCount/((double)PULSE_FACTOR)); 114 | oldPulseCount = pulseCount; 115 | if (volume != oldvolume) { 116 | gw.send(volumeMsg.set(volume, 3)); // Send volume value to gw 117 | oldvolume = volume; 118 | } 119 | } 120 | lastSend = currentTime; 121 | } else if (sendTime && !pcReceived) { 122 | // No count received. Try requesting it again 123 | gw.request(CHILD_ID, V_VAR1); 124 | lastSend=currentTime; 125 | } 126 | 127 | if (SLEEP_MODE) { 128 | gw.sleep(SEND_FREQUENCY); 129 | } 130 | } 131 | 132 | 133 | void incomingMessage(const MyMessage &message) { 134 | if (message.type==V_VAR1) { 135 | pulseCount = oldPulseCount = message.getLong(); 136 | Serial.print("Received last pulse count from gw:"); 137 | Serial.println(pulseCount); 138 | pcReceived = true; 139 | } 140 | } 141 | 142 | void onPulse() 143 | { 144 | if (!SLEEP_MODE) { 145 | unsigned long newBlink = micros(); 146 | unsigned long interval = newBlink-lastBlink; 147 | lastPulse = millis(); 148 | if (interval<500000L) { 149 | // Sometimes we get interrupt on RISING, 500000 = 0.5sek debounce ( max 120 l/min) 150 | return; 151 | } 152 | flow = (60000000.0 /interval) / ppl; 153 | lastBlink = newBlink; 154 | } 155 | if( (millis() - lastDebounce) > debounceDelay){ 156 | pulseCount++; 157 | lastDebounce = millis(); 158 | digitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level) 159 | delay(500); // wait for a second 160 | digitalWrite(13, LOW); // turn the LED off by making the voltage LOW 161 | } 162 | } 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /1.3/WaterMeterPulseSensor2.ino: -------------------------------------------------------------------------------- 1 | // 2 | // Use this sensor to measure volume and flow of your house watermeter. 3 | // You need to set the correct pulsefactor of your meter (pulses per m3). 4 | // The sensor starts by fetching current volume reading from gateway (VAR 1). 5 | // Reports both volume and flow back to gateway. 6 | // 7 | // Unfortunately millis() won't increment when the Arduino is in 8 | // sleepmode. So we cannot make this sensor sleep if we also want 9 | // to calculate/report flow. 10 | // 11 | // Sensor on pin 3 12 | // License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 13 | 14 | 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!) 25 | #define PULSE_FACTOR 1000 // Nummber of blinks per m3 of your meter (One rotation/liter) 26 | #define SLEEP_MODE false // flowvalue can only be reported when sleep mode is false. 27 | #define MAX_FLOW 40 // Max flow (l/min) value to report. This filetrs outliers. 28 | #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) 29 | #define CHILD_ID 5 // Id of the sensor child 30 | unsigned long SEND_FREQUENCY = 20; // Minimum time between send (in seconds). We don't want to spam the gateway. 31 | 32 | Sensor gw; 33 | Bounce debouncer = Bounce(); 34 | Sleep sleep; 35 | 36 | double ppl = ((double)PULSE_FACTOR)/1000; // Pulses per liter 37 | 38 | volatile unsigned long pulseCount = 0; 39 | volatile unsigned long lastBlink = 0; 40 | volatile double flow = 0; 41 | unsigned long oldPulseCount = 0; 42 | unsigned long newBlink = 0; 43 | double oldflow = 0; 44 | double volume; 45 | double oldvolume; 46 | unsigned long lastSend; 47 | unsigned long lastPulse; 48 | unsigned long currentTime; 49 | boolean metric; 50 | long lastDebounce = 0; 51 | long debounceDelay = 500; // Ignore bounces under 1/2 second 52 | 53 | 54 | void setup() 55 | { 56 | gw.begin(); 57 | 58 | // Send the sketch version information to the gateway and Controller 59 | gw.sendSketchInfo("Water Meter", "1.0b"); 60 | 61 | // Register this device as Waterflow sensor 62 | gw.sendSensorPresentation(CHILD_ID, S_WATER); 63 | 64 | // Fetch last known pulse count value from gw 65 | pulseCount = oldPulseCount = atol(gw.getStatus(CHILD_ID, V_VAR1)); 66 | //Serial.print("Last pulse count from gw:"); 67 | //Serial.println(pulseCount); 68 | // attachInterrupt(INTERRUPT, onPulse, RISING); 69 | lastSend = millis(); 70 | 71 | // Setup the button 72 | pinMode(DIGITAL_INPUT_SENSOR,INPUT); 73 | // Activate internal pull-up 74 | digitalWrite(DIGITAL_INPUT_SENSOR,HIGH); 75 | 76 | attachInterrupt(INTERRUPT, onPulse, FALLING); 77 | 78 | // After setting up the button, setup debouncer 79 | //debouncer.attach(DIGITAL_INPUT_SENSOR); 80 | //debouncer.interval(5); 81 | 82 | } 83 | 84 | 85 | void loop() 86 | { 87 | currentTime = millis(); 88 | 89 | // Only send values at a maximum frequency or woken up from sleep 90 | if (SLEEP_MODE || currentTime - lastSend > 1000*SEND_FREQUENCY) { 91 | // New flow value has been calculated 92 | if (!SLEEP_MODE && flow != oldflow) { 93 | // Check that we dont get unresonable large flow value. 94 | // could hapen when long wraps or false interrupt triggered 95 | if (flow<((unsigned long)MAX_FLOW)) { 96 | gw.sendVariable(CHILD_ID, V_FLOW, flow, 2); // Send flow value to gw 97 | } 98 | //Serial.print("l/min:"); 99 | //Serial.println(flow); 100 | oldflow = flow; 101 | } 102 | 103 | // No Pulse count in 2min 104 | 105 | //Serial.print("currentTime"); 106 | //Serial.println(currentTime); 107 | //Serial.print("lastPulse"); 108 | //Serial.println(lastPulse); 109 | 110 | if(currentTime - lastPulse > 120000){ 111 | flow = 0; 112 | } 113 | 114 | 115 | // Pulse count has changed 116 | if (pulseCount != oldPulseCount) { 117 | gw.sendVariable(CHILD_ID, V_VAR1, pulseCount); // Send volumevalue to gw VAR1 118 | double volume = ((double)pulseCount/((double)PULSE_FACTOR)); 119 | oldPulseCount = pulseCount; 120 | //Serial.print("Pulse count:"); 121 | //Serial.println(pulseCount); 122 | if (volume != oldvolume) { 123 | gw.sendVariable(CHILD_ID, V_VOLUME, volume, 3); // Send volume value to gw 124 | //Serial.print("m3:"); 125 | //Serial.println(volume, 3); 126 | oldvolume = volume; 127 | } 128 | } 129 | lastSend = currentTime; 130 | } 131 | 132 | if (SLEEP_MODE) { 133 | delay(300); //delay to allow serial to fully print before sleep 134 | gw.powerDown(); 135 | sleep.pwrDownMode(); //set sleep mode 136 | sleep.sleepDelay(SEND_FREQUENCY * 1000); //sleep for: sleepTime 137 | } 138 | } 139 | 140 | 141 | void onPulse() 142 | { 143 | if (!SLEEP_MODE) { 144 | unsigned long newBlink = micros(); 145 | unsigned long interval = newBlink-lastBlink; 146 | lastPulse = millis(); 147 | if (interval<500000L) { 148 | // Sometimes we get interrupt on RISING, 500000 = 0.5sek debounce ( max 120 l/min) 149 | return; 150 | } 151 | 152 | flow = (60000000.0 /interval) / ppl; 153 | lastBlink = newBlink; 154 | // Serial.println(flow, 4); 155 | } 156 | if( (millis() - lastDebounce) > debounceDelay){ 157 | pulseCount++; 158 | lastDebounce = millis(); 159 | } 160 | } 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /WaterMeterPulseSensor3-gs.ino: -------------------------------------------------------------------------------- 1 | // This #include statement was automatically added by the Particle IDE. 2 | #include "HttpClient/HttpClient.h" 3 | 4 | 5 | /* 6 | Use this sensor to measure volume and flow of your house watermeter. 7 | You need to set the correct pulsefactor of your meter (pulses per m3). 8 | The sensor starts by fetching current volume reading from gateway (VAR 1). 9 | Reports both volume and flow back to gateway. 10 | Sensor on pin analog 0 11 | 12 | Particle Photon 13 | Capteur TRT5000 14 | Watermeter sensus Residia Jet 15 | 16 | */ 17 | 18 | #define ANALOG_DIGITAL_SENSOR 0 // The digital input you attached your sensor. 19 | #define PULSE_FACTOR 1 // Number of blinks per m3 of your meter (One rotation/liter) 20 | #define SLEEP_MODE false // flowvalue can only be reported when sleep mode is false. 21 | #define MAX_FLOW 40 // Max flow (l/min) value to report. This filetrs outliers. 22 | unsigned long SEND_FREQUENCY = 1;//20000; // Minimum time between send (in seconds). We don't want to spam the gateway. 23 | 24 | double ppl = ((double)PULSE_FACTOR); // Pulses per liter 25 | 26 | volatile unsigned long pulseCount = 0; 27 | volatile unsigned long lastBlink = 0; 28 | volatile double flow = 0; 29 | boolean pcReceived = false; 30 | unsigned long oldPulseCount = 0; 31 | unsigned long newBlink = 0; 32 | double oldflow = 0; 33 | double volume; 34 | double oldvolume; 35 | unsigned long lastSend; 36 | unsigned long lastPulse; 37 | unsigned long currentTime; 38 | double val = 0; 39 | double oldval = 0; 40 | int led2 = D7; 41 | 42 | String idx="594"; 43 | HttpClient http; 44 | http_header_t headers[] = { 45 | // { "Content-Type", "application/json" }, 46 | // { "Accept" , "application/json" }, 47 | { "Accept" , "*/*"}, 48 | { NULL, NULL } 49 | }; 50 | 51 | http_request_t request; 52 | http_response_t response; 53 | http_request_t request2; 54 | http_response_t response2; 55 | 56 | void setup() { 57 | Serial.begin(115200); 58 | pulseCount = oldPulseCount = 0; 59 | lastSend = millis(); 60 | pinMode(13, OUTPUT); 61 | Particle.publish("Particle 2","started"); 62 | pinMode(led2, OUTPUT); 63 | 64 | } 65 | 66 | void loop() { 67 | 68 | currentTime = millis(); 69 | 70 | val=digitalRead(0); 71 | //Serial.println(val); 72 | if ((oldval!=val)&&(val==1)) { 73 | digitalWrite(led2, HIGH); 74 | Particle.publish("Sensor",String((long)val)+ " "+String((long)oldval)); 75 | pulseCount++; 76 | oldval=val; 77 | delay(500); 78 | digitalWrite(led2, LOW); 79 | //Serial.print('.'); 80 | } else { 81 | oldval=val; 82 | delay(100); 83 | } 84 | 85 | // Only send values at a maximum frequency or woken up from sleep 86 | bool sendTime; 87 | if ((currentTime - lastSend) > SEND_FREQUENCY) {sendTime=1;}else{sendTime=0;}; 88 | 89 | 90 | // Pulse count has changed 91 | if (pulseCount != oldPulseCount) { 92 | double volume = ((double)pulseCount/((double)PULSE_FACTOR)); 93 | oldPulseCount = pulseCount; 94 | if (((volume != oldvolume)||(sendTime))&&(pcReceived)) { 95 | //SEND gw.send(volumeMsg.set(volume, 3)); // Send volume value to gw 96 | request2.hostname = "192.168.1.10"; //1.10 97 | request2.port = 8080; 98 | request2.path = "/json.htm?type=command¶m=udevice&idx="+idx+"&svalue="+String((int)volume); 99 | // Get request 100 | http.get(request2, response2, headers); 101 | Particle.publish("url2",request2.hostname+":"+request2.port+" "+request2.path+ " "+response2.status); 102 | //Serial.print("V="); 103 | //Serial.println(volume); 104 | Particle.publish("Volume",String((double)volume,3)); 105 | oldvolume = volume; 106 | } 107 | } 108 | lastSend = currentTime; 109 | //Particle.publish("Status",String(oldvolume)+" "+String(volume)+" "+String(pulseCount)+" "+String(sendTime)+" "+String(pcReceived)); 110 | 111 | /*Serial.print(oldvolume); 112 | Serial.print(" "); 113 | Serial.print(volume); 114 | Serial.print(" "); 115 | Serial.print(pulseCount); 116 | Serial.print(" "); 117 | Serial.print(sendTime); 118 | Serial.print(" "); 119 | Serial.println(pcReceived);*/ 120 | 121 | if (! pcReceived) { 122 | // No count received. Try requesting it again 123 | request.hostname = "192.168.1.10"; //1.10 124 | request.port = 8080; 125 | request.path = "/json.htm?type=devices&rid="+idx; 126 | // Get request 127 | http.get(request, response, headers); 128 | String str(response.body); 129 | Particle.publish("index",request.path+ " "+response.status); 130 | if (response.status==200) { 131 | const char* stringArgs = str.c_str(); 132 | char* myCopy = strtok(strdup(stringArgs), "\n"); 133 | int i=0; 134 | double res; 135 | 136 | while (myCopy != NULL) { 137 | if (i==12) { 138 | //Particle.publish(myCopy); 139 | String str = myCopy; 140 | String resp= str.substring(22, str.length()-4); 141 | res=strtof(resp,NULL); 142 | Particle.publish("Histo",String((double)res,3)); 143 | } 144 | i++; 145 | myCopy = strtok(NULL, "\n"); 146 | } 147 | 148 | lastSend=currentTime; 149 | pulseCount = oldPulseCount = res*1000; 150 | //Serial.print("Received last pulse count from gw:"); 151 | //Serial.println(res); 152 | //Spark.publish("PC"+pulseCount); 153 | 154 | pcReceived = true;delay(1000); 155 | Particle.publish("Status init",String(oldvolume)+" "+String(volume)+" "+String(pulseCount)+" "+String(sendTime)+" "+String(pcReceived)); 156 | 157 | } 158 | } 159 | 160 | 161 | } 162 | 163 | -------------------------------------------------------------------------------- /DustSensor_Shinyei_PPD42NS.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MySensors Arduino library handles the wireless radio link and protocol 3 | * between your home built sensors/actuators and HA controller of choice. 4 | * The sensors forms a self healing radio network with optional repeaters. Each 5 | * repeater and gateway builds a routing tables in EEPROM which keeps track of the 6 | * network topology allowing messages to be routed to nodes. 7 | * 8 | * Created by Henrik Ekblad 9 | * Copyright (C) 2013-2015 Sensnology AB 10 | * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 11 | * 12 | * Documentation: http://www.mysensors.org 13 | * Support Forum: http://forum.mysensors.org 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License 17 | * version 2 as published by the Free Software Foundation. 18 | * 19 | ******************************* 20 | * 21 | * DESCRIPTION 22 | * Arduino Dust Sensor for Shinyei PPD42NS 23 | * connect the sensor as follows : 24 | * Pin 1 of dust sensor -> Ground 25 | * Pin 2 of dust sensor PM2.5 -> Digital 6 (PWM) 26 | * Pin 3 of dust sensor -> +5V 27 | * Pin 4 of dust sensor PM1 -> Digital 3 (PMW) 28 | * 29 | * Based on: http://www.howmuchsnow.com/arduino/airquality/grovedust/ 30 | * Authors: Chris Nafis Apris 2012 31 | * Datasheet: http://www.sca-shinyei.com/pdf/PPD42NS.pdf 32 | * Contribution: epierre 33 | * 34 | * The dust sensor used (see purchase guide for latest link): 35 | */ 36 | 37 | #include 38 | #include 39 | 40 | #define CHILD_ID_DUST_PM10 0 41 | #define CHILD_ID_DUST_PM25 1 42 | #define DUST_SENSOR_DIGITAL_PIN_PM10 3 43 | #define DUST_SENSOR_DIGITAL_PIN_PM25 6 44 | 45 | unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds) 46 | //VARIABLES 47 | int val = 0; // variable to store the value coming from the sensor 48 | float valDUSTPM25 =0.0; 49 | float lastDUSTPM25 =0.0; 50 | float valDUSTPM10 =0.0; 51 | float lastDUSTPM10 =0.0; 52 | unsigned long duration; 53 | unsigned long starttime; 54 | unsigned long endtime; 55 | unsigned long sampletime_ms = 30000; 56 | unsigned long lowpulseoccupancy = 0; 57 | float ratio = 0; 58 | long concentrationPM25 = 0; 59 | long concentrationPM10 = 0; 60 | int temp=20; //external temperature, if you can replace this with a DHT11 or better 61 | long ppmv; 62 | 63 | MySensor gw; 64 | MyMessage dustMsgPM10(CHILD_ID_DUST_PM10, V_LEVEL); 65 | MyMessage msgPM10(CHILD_ID_DUST_PM10, V_UNIT_PREFIX); 66 | MyMessage dustMsgPM25(CHILD_ID_DUST_PM25, V_LEVEL); 67 | MyMessage msgPM25(CHILD_ID_DUST_PM25, V_UNIT_PREFIX); 68 | 69 | 70 | void setup() 71 | { 72 | gw.begin(); 73 | 74 | // Send the sketch version information to the gateway and Controller 75 | gw.sendSketchInfo("Dust Sensor PPD42NS", "1.4"); 76 | 77 | // Register all sensors to gateway (they will be created as child devices) 78 | gw.present(CHILD_ID_DUST_PM10, S_DUST); 79 | gw.send(msgPM10.set("ppm")); 80 | gw.present(CHILD_ID_DUST_PM25, S_DUST); 81 | gw.send(msgPM25.set("ppm")); 82 | 83 | pinMode(DUST_SENSOR_DIGITAL_PIN_PM10,INPUT); 84 | pinMode(DUST_SENSOR_DIGITAL_PIN_PM25,INPUT); 85 | Serial.begin(115200); 86 | } 87 | 88 | void loop() 89 | { 90 | 91 | //get PM 2.5 density of particles over 2.5 μm. 92 | concentrationPM25=getPM(DUST_SENSOR_DIGITAL_PIN_PM25); 93 | Serial.print("PM25: "); 94 | Serial.println(concentrationPM25); 95 | Serial.print("\n"); 96 | //ppmv=mg/m3 * (0.08205*Tmp)/Molecular_mass 97 | //0.08205 = Universal gas constant in atm·m3/(kmol·K) 98 | ppmv=(concentrationPM25*0.0283168/100/1000) * (0.08205*temp)/0.01; 99 | 100 | if ((ceil(concentrationPM25) != lastDUSTPM25)&&((long)concentrationPM25>0)) { 101 | gw.send(dustMsgPM25.set((long)ppmv)); 102 | lastDUSTPM25 = ceil(concentrationPM25); 103 | } 104 | //get PM 1.0 - density of particles over 1 μm. 105 | concentrationPM10=getPM(DUST_SENSOR_DIGITAL_PIN_PM10); 106 | Serial.print("PM10: "); 107 | Serial.println(concentrationPM10); 108 | Serial.print("\n"); 109 | //ppmv=mg/m3 * (0.08205*Tmp)/Molecular_mass 110 | //0.08205 = Universal gas constant in atm·m3/(kmol·K) 111 | ppmv=(concentrationPM10*0.0283168/100/1000) * (0.08205*temp)/0.01; 112 | 113 | if ((ceil(concentrationPM10) != lastDUSTPM10)&&((long)concentrationPM10>0)) { 114 | gw.send(dustMsgPM10.set((long)ppmv)); 115 | lastDUSTPM10 = ceil(concentrationPM10); 116 | } 117 | 118 | //sleep to save on radio 119 | gw.sleep(SLEEP_TIME); 120 | 121 | } 122 | 123 | float conversion25(long concentrationPM25) { 124 | double pi = 3.14159; 125 | double density = 1.65 * pow (10, 12); 126 | double r25 = 0.44 * pow (10, -6); 127 | double vol25 = (4/3) * pi * pow (r25, 3); 128 | double mass25 = density * vol25; 129 | double K = 3531.5; 130 | return (concentrationPM25) * K * mass25; 131 | } 132 | 133 | float conversion10(long concentrationPM10) { 134 | double pi = 3.14159; 135 | double density = 1.65 * pow (10, 12); 136 | double r10 = 0.44 * pow (10, -6); 137 | double vol10 = (4/3) * pi * pow (r10, 3); 138 | double mass10 = density * vol10; 139 | double K = 3531.5; 140 | return (concentrationPM10) * K * mass10; 141 | } 142 | 143 | long getPM(int DUST_SENSOR_DIGITAL_PIN) { 144 | 145 | starttime = millis(); 146 | 147 | while (1) { 148 | 149 | duration = pulseIn(DUST_SENSOR_DIGITAL_PIN, LOW); 150 | lowpulseoccupancy += duration; 151 | endtime = millis(); 152 | 153 | if ((endtime-starttime) > sampletime_ms) 154 | { 155 | ratio = (lowpulseoccupancy-endtime+starttime)/(sampletime_ms*10.0); // Integer percentage 0=>100 156 | long concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // using spec sheet curve 157 | //Serial.print("lowpulseoccupancy:"); 158 | //Serial.print(lowpulseoccupancy); 159 | //Serial.print("\n"); 160 | //Serial.print("ratio:"); 161 | //Serial.print(ratio); 162 | //Serial.print("\n"); 163 | //Serial.print("PPDNS42:"); 164 | //Serial.println(concentration); 165 | //Serial.print("\n"); 166 | 167 | lowpulseoccupancy = 0; 168 | return(concentration); 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /LuxUVSensor-c.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino BH1750FVI Light sensor 3 | communicate using I2C Protocol. 4 | this library enable 2 slave device addresses 5 | Main address 0x23 6 | secondary address 0x5C 7 | connect the sensor as follows : 8 | VCC >>> 5V/3.3V 9 | Gnd >>> Gnd 10 | ADDR >>> NC or GND 11 | SCL >>> A5 12 | SDA >>> A4 13 | 14 | Arduino UVM-30A UV Sensor 15 | connect the sensor as follows : 16 | 17 | + >>> 5V/3.3V 18 | - >>> GND 19 | out >>> A3 20 | 21 | Contribution: idefix/epierre for ceech 22 | 23 | */ 24 | #include 25 | #include 26 | #include // I2C 27 | #include 28 | 29 | #define LTC4067_CHRG_PIN A1 //analog input A1 on ATmega 328 is /CHRG signal from LTC4067 30 | #define batteryVoltage_PIN A0 //analog input A0 on ATmega328 is battery voltage ( /2) 31 | #define solarVoltage_PIN A2 //analog input A2 is solar cell voltage (/ 2) 32 | #define solarCurrent_PIN A6 //analog input A6 is input current ( I=V/Rclprog x 1000 ) 33 | #define batteryChargeCurrent_PIN A7 //analog input A7 is battery charge current ( I=V/Rprog x 1000 ) 34 | #define LTC4067_SUSPEND_PIN 9 //digital output D9 - drive it high to put LTC4067 in SUSPEND mode 35 | 36 | const float VccMin = 1.0*3.5; // Minimum expected Vcc level, in Volts. Example for 1 rechargeable lithium-ion. 37 | const float VccMax = 1.0*4.2; // Maximum expected Vcc level, in Volts. 38 | 39 | #define LIGHT_SENSOR_ANALOG_PIN 3 // Digital input did you attach your soil sensor. 40 | #define CHILD_ID_LIGHT 0 // Id of the sensor child 41 | #define CHILD_ID_UV 1 42 | #define BATT_CHILD_ID 10 43 | #define SOLAR_CHILD_ID 11 44 | #define UV_SENSOR_ANALOG_PIN 3 45 | 46 | // PIN Radio 47 | #define RADIO_CE_PIN 7 // radio chip enable 48 | #define RADIO_SS_PIN 8 // CS SS serial select 49 | 50 | float lastBattVoltage; 51 | float lastBattCurrent; 52 | float lastSolarVoltage; 53 | float lastSolarCurrent; 54 | int lastBattPct = 0; 55 | uint16_t lastlux; 56 | float VccReference = 3.3 ; // voltage reference for measurement, definitive init in setup 57 | int lastUV = -1; 58 | int uvIndexValue [13] = { 50, 227, 318, 408, 503, 606, 696, 795, 881, 976, 1079, 1170, 3000}; 59 | int uvIndex; 60 | 61 | 62 | BH1750 lightSensor; 63 | MySensor gw(RADIO_CE_PIN, RADIO_SS_PIN); 64 | unsigned long SLEEP_TIME = 30*1000; // sleep time between reads (seconds * 1000 milliseconds) 65 | MyMessage msg(CHILD_ID_LIGHT, V_LIGHT_LEVEL); 66 | MyMessage batteryVoltageMsg(BATT_CHILD_ID, V_VOLTAGE); // Battery voltage (V) 67 | MyMessage batteryCurrentMsg(BATT_CHILD_ID, V_CURRENT); // Battery current (A) 68 | MyMessage solarVoltageMsg(SOLAR_CHILD_ID, V_VOLTAGE); // Solar voltage (V) 69 | MyMessage solarCurrentMsg(SOLAR_CHILD_ID, V_CURRENT); 70 | MyMessage uvMsg(CHILD_ID_UV, V_UV); // Solar current (A) 71 | 72 | void setup() 73 | { 74 | 75 | gw.begin(); 76 | 77 | // Send the sketch version information to the gateway and Controller 78 | gw.sendSketchInfo("Light Lux UV Sensor", "1.0"); 79 | // Register all sensors to gw (they will be created as child devices) 80 | gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL); 81 | gw.present(CHILD_ID_UV, S_UV); 82 | gw.present(BATT_CHILD_ID, S_POWER); // Battery parameters 83 | gw.present(SOLAR_CHILD_ID, S_POWER); // Solar parameters 84 | 85 | // use VCC (3.3V) reference 86 | analogReference(DEFAULT); // default external reference = 3.3v for Ceech board 87 | VccReference = 3.323 ; // measured Vcc input (on board LDO) 88 | pinMode(LTC4067_SUSPEND_PIN, OUTPUT); // suspend of Lion charger set 89 | digitalWrite(LTC4067_SUSPEND_PIN,LOW); // active (non suspend) at start 90 | 91 | lightSensor.begin(); 92 | } 93 | 94 | void loop() 95 | { 96 | 97 | sendVoltage(); 98 | 99 | uint16_t lux = lightSensor.readLightLevel();// Get Lux value 100 | Serial.println(lux); 101 | if (lux != lastlux) { 102 | gw.send(msg.set(lux)); 103 | lastlux = lux; 104 | } 105 | 106 | uint16_t uv = analogRead(0);// Get UV value 107 | Serial.print("Uv reading: "); 108 | Serial.println(uv); 109 | for (int i = 0; i < 13; i++) 110 | { 111 | if (uv <= uvIndexValue[i]) 112 | { 113 | uvIndex = i; 114 | break; 115 | } 116 | } 117 | Serial.print("Uv index: "); 118 | Serial.println(uvIndex); 119 | 120 | if (uvIndex != lastUV) { 121 | gw.send(uvMsg.set(uvIndex)); 122 | lastUV = uvIndex; 123 | } 124 | 125 | // Power down the radio 126 | gw.sleep(SLEEP_TIME); 127 | } 128 | 129 | void sendVoltage(void) 130 | // battery and charging values 131 | { 132 | // get Battery Voltage & charge current 133 | float batteryVoltage = ((float)analogRead(batteryVoltage_PIN)* VccReference/1024) * 2; // actual voltage is double 134 | Serial.print("Batt: "); 135 | Serial.print(batteryVoltage); 136 | Serial.print("V ; "); 137 | float batteryChargeCurrent = ((float)analogRead(batteryChargeCurrent_PIN) * VccReference/1024)/ 2.5 ; // current(A) = V/Rprog(kohm) 138 | Serial.print(batteryChargeCurrent); 139 | Serial.println("A "); 140 | 141 | 142 | // get Solar Voltage & charge current 143 | float solarVoltage = ((float)analogRead(solarVoltage_PIN)/1024 * VccReference) * 2 ; // actual voltage is double 144 | Serial.print("Solar: "); 145 | Serial.print(solarVoltage); 146 | Serial.print("V ; "); 147 | // get Solar Current 148 | float solarCurrent = ((float)analogRead(solarCurrent_PIN)/1024 * VccReference)/ 2.5; // current(A) = V/Rclprog(kohm) 149 | Serial.print(solarCurrent); 150 | Serial.print(" A; charge: "); 151 | Serial.println(digitalRead(LTC4067_CHRG_PIN)?"No":"Yes"); 152 | 153 | // send battery percentage for node 154 | int battPct = 1 ; 155 | if (batteryVoltage > VccMin){ 156 | battPct = 100.0*(batteryVoltage - VccMin)/(VccMax - VccMin); 157 | } 158 | Serial.print("BattPct: "); 159 | Serial.print(battPct); 160 | Serial.println("% "); 161 | 162 | if (lastBattPct != battPct) { 163 | gw.send(batteryVoltageMsg.set(batteryVoltage, 3)); // Send (V) 164 | gw.send(batteryCurrentMsg.set(batteryChargeCurrent, 6)); // Send (Amps) 165 | gw.send(solarVoltageMsg.set(solarVoltage, 3)); // Send (V) 166 | gw.send(solarCurrentMsg.set(solarCurrent, 6)); // Send (Amps) 167 | gw.sendBatteryLevel(battPct); 168 | lastBattPct = battPct; 169 | } 170 | } 171 | 172 | -------------------------------------------------------------------------------- /SoilMoistSensor.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MySensors Arduino library handles the wireless radio link and protocol 3 | * between your home built sensors/actuators and HA controller of choice. 4 | * The sensors forms a self healing radio network with optional repeaters. Each 5 | * repeater and gateway builds a routing tables in EEPROM which keeps track of the 6 | * network topology allowing messages to be routed to nodes. 7 | * 8 | * Created by Henrik Ekblad 9 | * Copyright (C) 2013-2015 Sensnology AB 10 | * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 11 | * 12 | * Documentation: http://www.mysensors.org 13 | * Support Forum: http://forum.mysensors.org 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License 17 | * version 2 as published by the Free Software Foundation. 18 | * 19 | ******************************* 20 | * 21 | * DESCRIPTION 22 | * 23 | * Arduino soil moisture based on gypsum sensor/resistive sensor to avoid electric catalyse in soil 24 | * Required to interface the sensor: 2 * 4.7kOhm + 2 * 1N4148 25 | * 26 | * Gypsum sensor and calibration: 27 | * DIY: See http://vanderleevineyard.com/1/category/vinduino/1.html 28 | * Built: Davis / Watermark 200SS 29 | * http://www.cooking-hacks.com/watermark-soil-moisture-sensor?_bksrc=item2item&_bkloc=product 30 | * http://www.irrometer.com/pdf/supportmaterial/sensors/voltage-WM-chart.pdf 31 | * cb (centibar) http://www.irrometer.com/basics.html 32 | * 0-10 Saturated Soil. Occurs for a day or two after irrigation 33 | * 10-20 Soil is adequately wet (except coarse sands which are drying out at this range) 34 | * 30-60 Usual range to irrigate or water (except heavy clay soils). 35 | * 60-100 Usual range to irrigate heavy clay soils 36 | * 100-200 Soil is becoming dangerously dry for maximum production. Proceed with caution. 37 | * 38 | * Connection: 39 | * D6, D7: alternative powering to avoid sensor degradation 40 | * A0, A1: alternative resistance mesuring 41 | * 42 | * Based on: 43 | * "Vinduino" portable soil moisture sensor code V3.00 44 | * Date December 31, 2012 45 | * Reinier van der Lee and Theodore Kaskalis 46 | * www.vanderleevineyard.com 47 | * Contributor: epierre 48 | **/ 49 | 50 | // Copyright (C) 2015, Reinier van der Lee 51 | // www.vanderleevineyard.com 52 | 53 | // This program is free software: you can redistribute it and/or modify 54 | // it under the terms of the GNU General Public License as published by 55 | // the Free Software Foundation, either version 3 of the License, or 56 | // any later version. 57 | 58 | // This program is distributed in the hope that it will be useful, 59 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 60 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 61 | // GNU General Public License for more details. 62 | 63 | #include // Conversion equation from resistance to % 64 | #include 65 | #include 66 | 67 | // Setting up format for reading 3 soil sensors 68 | #define NUM_READS 10 // Number of sensor reads for filtering 69 | #define CHILD_ID 0 70 | 71 | MySensor gw; // Arduino initialization 72 | MyMessage msg(CHILD_ID, V_LEVEL); 73 | unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds) 74 | 75 | long buffer[NUM_READS]; 76 | int index; 77 | 78 | typedef struct { // Structure to be used in percentage and resistance values matrix to be filtered (have to be in pairs) 79 | int moisture; 80 | long resistance; 81 | } values; 82 | 83 | const long knownResistor = 4700; // Constant value of known resistor in Ohms 84 | 85 | int supplyVoltage; // Measured supply voltage 86 | int sensorVoltage; // Measured sensor voltage 87 | 88 | values valueOf[NUM_READS]; // Calculated moisture percentages and resistances to be sorted and filtered 89 | 90 | int i; // Simple index variable 91 | 92 | void setup() { 93 | // initialize serial communications at 9600 bps: 94 | Serial.begin(115200); 95 | gw.begin(); 96 | gw.sendSketchInfo("Soil Moisture Sensor Reverse Polarity", "1.0"); 97 | gw.present(CHILD_ID, S_HUM); 98 | // initialize the digital pins as an output. 99 | // Pin 6,7 is for sensor 1 100 | // initialize the digital pin as an output. 101 | // Pin 6 is sense resistor voltage supply 1 102 | pinMode(6, OUTPUT); 103 | 104 | // initialize the digital pin as an output. 105 | // Pin 7 is sense resistor voltage supply 2 106 | pinMode(7, OUTPUT); 107 | 108 | 109 | } 110 | 111 | void loop() { 112 | 113 | measure(1,6,7,1); 114 | Serial.print ("\t"); 115 | Serial.println (average()); 116 | long read1 = average(); 117 | 118 | measure(1,7,6,0); 119 | Serial.print ("\t"); 120 | Serial.println (average()); 121 | long read2= average(); 122 | 123 | long sensor1 = (read1 + read2)/2; 124 | 125 | Serial.print ("resistance bias =" ); 126 | Serial.println (read1-read2); 127 | Serial.print ("sensor bias compensated value = "); 128 | Serial.println (sensor1); 129 | Serial.println (); 130 | 131 | //send back the values 132 | gw.send(msg.set((long int)ceil(sensor1))); 133 | // delay until next measurement (msec) 134 | gw.sleep(SLEEP_TIME); 135 | } 136 | 137 | void measure (int sensor, int phase_b, int phase_a, int analog_input) 138 | { 139 | // read sensor, filter, and calculate resistance value 140 | // Noise filter: median filter 141 | 142 | for (i=0; i= NUM_READS) index = 0; 177 | } 178 | 179 | long average(){ 180 | long sum = 0; 181 | for (int i = 0; i < NUM_READS; i++){ 182 | sum += buffer[i]; 183 | } 184 | return (long)(sum / NUM_READS); 185 | } 186 | -------------------------------------------------------------------------------- /DustSensor-BJHIKE-HK-A5.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MySensors Arduino library handles the wireless radio link and protocol 3 | * between your home built sensors/actuators and HA controller of choice. 4 | * The sensors forms a self healing radio network with optional repeaters. Each 5 | * repeater and gateway builds a routing tables in EEPROM which keeps track of the 6 | * network topology allowing messages to be routed to nodes. 7 | * 8 | * Created by Henrik Ekblad 9 | * Copyright (C) 2013-2017 Sensnology AB 10 | * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 11 | * 12 | * Documentation: http://www.mysensors.org 13 | * Support Forum: http://forum.mysensors.org 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License 17 | * version 2 as published by the Free Software Foundation. 18 | * 19 | ******************************* 20 | * 21 | * DESCRIPTION 22 | * Arduino Dust Sensor for DfRobot HK-A5 laser sensor 23 | * connect the sensor as follows : 24 | * RX -> TX 25 | * TX -> RX 26 | * VCC -> VCC 27 | * GND -> GND 28 | * 29 | * Based on: https://www.dfrobot.com/wiki/index.php/PM2.5_laser_dust_sensor_SKU:SEN0177 30 | * Author:Zuyang @ HUST 31 | * Modified by Cain for Arduino Hardware Serial port compatibility 32 | * Datasheet: https://github.com/Arduinolibrary/DFRobot_PM2.5_Sensor_module/raw/master/HK-A5%20Laser%20PM2.5%20Sensor%20V1.0.pdf 33 | * Contribution: epierre 34 | * 35 | * The dust sensor used (see purchase guide for latest link): 36 | */ 37 | 38 | #include 39 | #include 40 | 41 | #define CHILD_ID_DUST_PM10 0 42 | #define CHILD_ID_DUST_PM25 1 43 | #define DUST_SENSOR_DIGITAL_PIN_PM10 3 44 | #define DUST_SENSOR_DIGITAL_PIN_PM25 6 45 | 46 | unsigned long SLEEP_TIME = 30*1000; // Sleep time between reads (in milliseconds) 47 | //VARIABLES 48 | int val = 0; // variable to store the value coming from the sensor 49 | float valDUSTPM25 =0.0; 50 | float lastDUSTPM25 =0.0; 51 | float valDUSTPM10 =0.0; 52 | float lastDUSTPM10 =0.0; 53 | float PM01Value; 54 | float PM10_0Value; 55 | float PM2_5Value; 56 | float ppmv2_5; 57 | float ppmv10_0; 58 | float ppmv1_0; 59 | unsigned long duration; 60 | unsigned long starttime; 61 | unsigned long endtime; 62 | unsigned long sampletime_ms = 30000; 63 | unsigned long lowpulseoccupancy = 0; 64 | float ratio = 0; 65 | long concentrationPM25 = 0; 66 | long concentrationPM10 = 0; 67 | int temp=20; //external temperature, if you can replace this with a DHT11 or better 68 | long ppmv; 69 | #define LENG 31 //0x42 + 31 bytes equal to 32 bytes 70 | unsigned char buf[LENG]; 71 | 72 | 73 | MySensor gw; 74 | MyMessage dustMsgPM10(CHILD_ID_DUST_PM10, V_LEVEL); 75 | MyMessage msgPM10(CHILD_ID_DUST_PM10, V_UNIT_PREFIX); 76 | MyMessage dustMsgPM25(CHILD_ID_DUST_PM25, V_LEVEL); 77 | MyMessage msgPM25(CHILD_ID_DUST_PM25, V_UNIT_PREFIX); 78 | 79 | 80 | void setup() 81 | { 82 | gw.begin(); 83 | 84 | // Send the sketch version information to the gateway and Controller 85 | gw.sendSketchInfo("Dust Sensor DFROBOT HK-A5", "1.4"); 86 | 87 | // Register all sensors to gateway (they will be created as child devices) 88 | gw.present(CHILD_ID_DUST_PM10, S_DUST); 89 | gw.send(msgPM10.set("ppm")); 90 | gw.present(CHILD_ID_DUST_PM25, S_DUST); 91 | gw.send(msgPM25.set("ppm")); 92 | 93 | pinMode(DUST_SENSOR_DIGITAL_PIN_PM10,INPUT); 94 | pinMode(DUST_SENSOR_DIGITAL_PIN_PM25,INPUT); 95 | Serial.begin(9600); //use serial0 96 | Serial.setTimeout(1500); //set the Timeout to 1500ms, longer than the data transmission periodic time of the sensor 97 | 98 | } 99 | 100 | void loop() 101 | { 102 | if(Serial.find(0x42)){ //start to read when detect 0x42 103 | Serial.readBytes(buf,LENG); 104 | 105 | if(buf[0] == 0x4d){ 106 | if(checkValue(buf,LENG)){ 107 | PM01Value=transmitPM01(buf); //count PM1.0 value of the air detector module 108 | PM2_5Value=transmitPM2_5(buf);//count PM2.5 value of the air detector module 109 | PM10_0Value=transmitPM10(buf); //count PM10 value of the air detector module 110 | //ppmv=mg/m3 * (0.08205*Tmp)/Molecular_mass 111 | //0.08205 = Universal gas constant in atm·m3/(kmol·K) 112 | ppmv2_5=(PM2_5Value*0.0283168/100/1000) * (0.08205*temp)/0.01; 113 | //ppmv=mg/m3 * (0.08205*Tmp)/Molecular_mass 114 | //0.08205 = Universal gas constant in atm·m3/(kmol·K) 115 | ppmv10_0=(PM10_0Value*0.0283168/100/1000) * (0.08205*temp)/0.01; 116 | } 117 | } 118 | } 119 | 120 | if ((ceil(ppmv2_5) != lastDUSTPM25)&&((long)ppmv2_5>0)) { 121 | gw.send(dustMsgPM25.set((long)ppmv2_5)); 122 | lastDUSTPM25 = ceil(ppmv2_5); 123 | } 124 | 125 | if ((ceil(ppmv10_0) != lastDUSTPM10)&&((long)ppmv10_0>0)) { 126 | gw.send(dustMsgPM10.set((long)ppmv10_0)); 127 | lastDUSTPM10 = ceil(ppmv10_0); 128 | } 129 | 130 | //sleep to save on radio 131 | gw.sleep(SLEEP_TIME); 132 | 133 | 134 | static unsigned long OledTimer=millis(); 135 | if (millis() - OledTimer >=1000) 136 | { 137 | OledTimer=millis(); 138 | 139 | Serial.print("PM1.0: "); 140 | Serial.print(PM01Value); 141 | Serial.println(" ug/m3"); 142 | 143 | Serial.print("PM2.5: "); 144 | Serial.print(PM2_5Value); 145 | Serial.println(" ug/m3"); 146 | 147 | Serial.print("PM1 0: "); 148 | Serial.print(PM10_0Value); 149 | Serial.println(" ug/m3"); 150 | Serial.println(); 151 | } 152 | 153 | } 154 | 155 | float conversion25(long concentrationPM25) { 156 | double pi = 3.14159; 157 | double density = 1.65 * pow (10, 12); 158 | double r25 = 0.44 * pow (10, -6); 159 | double vol25 = (4/3) * pi * pow (r25, 3); 160 | double mass25 = density * vol25; 161 | double K = 3531.5; 162 | return (concentrationPM25) * K * mass25; 163 | } 164 | 165 | float conversion10(long concentrationPM10) { 166 | double pi = 3.14159; 167 | double density = 1.65 * pow (10, 12); 168 | double r10 = 0.44 * pow (10, -6); 169 | double vol10 = (4/3) * pi * pow (r10, 3); 170 | double mass10 = density * vol10; 171 | double K = 3531.5; 172 | return (concentrationPM10) * K * mass10; 173 | } 174 | 175 | 176 | 177 | char checkValue(unsigned char *thebuf, char leng) 178 | { 179 | char receiveflag=0; 180 | int receiveSum=0; 181 | 182 | for(int i=0; i<(leng-2); i++){ 183 | receiveSum=receiveSum+thebuf[i]; 184 | } 185 | receiveSum=receiveSum + 0x42; 186 | 187 | if(receiveSum == ((thebuf[leng-2]<<8)+thebuf[leng-1])) //check the serial data 188 | { 189 | receiveSum = 0; 190 | receiveflag = 1; 191 | } 192 | return receiveflag; 193 | } 194 | 195 | int transmitPM01(unsigned char *thebuf) 196 | { 197 | int PM01Val; 198 | PM01Val=((thebuf[3]<<8) + thebuf[4]); //count PM1.0 value of the air detector module 199 | return PM01Val; 200 | } 201 | 202 | //transmit PM Value to PC 203 | int transmitPM2_5(unsigned char *thebuf) 204 | { 205 | int PM2_5Val; 206 | PM2_5Val=((thebuf[5]<<8) + thebuf[6]);//count PM2.5 value of the air detector module 207 | return PM2_5Val; 208 | } 209 | 210 | //transmit PM Value to PC 211 | int transmitPM10(unsigned char *thebuf) 212 | { 213 | int PM10Val; 214 | PM10Val=((thebuf[7]<<8) + thebuf[8]); //count PM10 value of the air detector module 215 | return PM10Val; 216 | } 217 | -------------------------------------------------------------------------------- /AirQuality-CO-NO2-NH3.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * The MySensors Arduino library handles the wireless radio link and protocol 3 | * between your home built sensors/actuators and HA controller of choice. 4 | * The sensors forms a self healing radio network with optional repeaters. Each 5 | * repeater and gateway builds a routing tables in EEPROM which keeps track of the 6 | * network topology allowing messages to be routed to nodes. 7 | * 8 | * Created by Henrik Ekblad 9 | * Copyright (C) 2013-2015 Sensnology AB 10 | * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors 11 | * 12 | * Documentation: http://www.mysensors.org 13 | * Support Forum: http://forum.mysensors.org 14 | * 15 | * This program is free software; you can redistribute it and/or 16 | * modify it under the terms of the GNU General Public License 17 | * version 2 as published by the Free Software Foundation. 18 | * 19 | ******************************* 20 | * 21 | * DESCRIPTION 22 | * 23 | * Air Quality Sensors for MICS-6814 24 | * 25 | * Wiring: 26 | * 27 | * 5V 28 | * GDN 29 | * SDA -> A4 (on nano) 30 | * SCL -> A5 (on nano) 31 | * 32 | * based on Jacky Zhang, Embedded Software Engineer qi.zhang@seeed.cc 33 | * Contribution: epierre 34 | * 35 | * Precaution: 36 | * The gasses detected by these gas sensors can be deadly in high concentrations. Always be careful to perform gas tests in well ventilated areas. 37 | * 38 | * Note: 39 | * THESE GAS SENSOR MODULES ARE NOT DESIGNED FOR OR APPROVED FOR ANY APPLICATION INVOLVING HEALTH OR HUMAN SAFETY. THESE GAS SENSOR MODULES ARE FOR EXPERIMENTAL PURPOSES ONLY.* 40 | **/ 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | /**********************Application Related Macros**********************************/ 48 | #define GAS_CL2 (0) 49 | #define GAS_O3 (1) 50 | #define GAS_CO2 (2) 51 | #define GAS_CO (3) 52 | #define GAS_NH4 (4) 53 | #define GAS_CH3 (6) 54 | #define GAS_CH3_2CO (7) 55 | #define GAS_H2 (8) 56 | #define GAS_C2H5OH (9) //Alcohol, Ethanol 57 | #define GAS_C4H10 (10) 58 | #define GAS_LPG (11) 59 | #define GAS_Smoke (12) 60 | #define GAS_CO_sec (13) 61 | #define GAS_LPG_sec (14) 62 | #define GAS_CH4 (15) 63 | #define GAS_NO2 (16) 64 | #define GAS_SO2 (17) 65 | #define GAS_C7H8 (18) //Toluene 66 | #define GAS_H2S (19) //Hydrogen Sulfide 67 | #define GAS_NH3 (20) //Ammonia 68 | #define GAS_C6H6 (21) //Benzene 69 | #define GAS_C3H8 (22) //Propane 70 | #define GAS_NHEX (23) //n-hexa 71 | #define GAS_HCHO (24) //HCHO / CH2O Formaldehyde 72 | /*****************************Globals***********************************************/ 73 | 74 | unsigned long SLEEP_TIME = 600; // Sleep time between reads (in seconds) 75 | //VARIABLES 76 | int val = 0; // variable to store the value coming from the sensor 77 | 78 | 79 | float calcVoltage = 0; 80 | boolean metric = true; 81 | //test 82 | float a=0; 83 | boolean pcReceived = false; 84 | 85 | #define CHILD_ID_NH3 0 86 | #define CHILD_ID_CO 1 87 | #define CHILD_ID_NO2 2 88 | #define CHILD_ID_C3H8 3 89 | #define CHILD_ID_C4H10 4 90 | #define CHILD_ID_CH4 5 91 | #define CHILD_ID_H2 6 92 | #define CHILD_ID_C2H5OH 7 93 | 94 | MySensor gw; // initializing arduino 95 | MyMessage msg_nh3(CHILD_ID_NH3, V_UNIT_PREFIX); 96 | MyMessage msg_co(CHILD_ID_CO, V_UNIT_PREFIX); 97 | MyMessage msg_no2(CHILD_ID_NO2, V_UNIT_PREFIX); 98 | MyMessage msg_c3h8(CHILD_ID_C3H8, V_UNIT_PREFIX); 99 | MyMessage msg_c4h10(CHILD_ID_C4H10, V_UNIT_PREFIX); 100 | MyMessage msg_ch4(CHILD_ID_CH4, V_UNIT_PREFIX); 101 | MyMessage msg_h2(CHILD_ID_H2, V_UNIT_PREFIX); 102 | MyMessage msg_c2h5oh(CHILD_ID_C2H5OH, V_UNIT_PREFIX); 103 | 104 | int val_nh3=0; 105 | int val_co=0; 106 | int val_no2=0; 107 | int val_c3h8=0; 108 | int val_c4h10=0; 109 | int val_ch4=0; 110 | int val_h2=0; 111 | int val_c2h5oh=0; 112 | 113 | void setup() 114 | { 115 | gw.begin(); 116 | 117 | 118 | // Send the sketch version information to the gateway and Controller 119 | gw.sendSketchInfo("AIQ Multi Sensors MiCS", "1.0"); 120 | 121 | // Register all sensors to gateway (they will be created as child devices) 122 | gw.present(CHILD_ID_NH3, S_AIR_QUALITY); 123 | gw.send(msg_nh3.set("ppm")); 124 | gw.present(CHILD_ID_CO, S_AIR_QUALITY); 125 | gw.send(msg_co.set("ppm")); 126 | gw.present(CHILD_ID_NO2, S_AIR_QUALITY); 127 | gw.send(msg_no2.set("ppm")); 128 | gw.present(CHILD_ID_C3H8, S_AIR_QUALITY); 129 | gw.send(msg_c3h8.set("ppm")); 130 | gw.present(CHILD_ID_C4H10, S_AIR_QUALITY); 131 | gw.send(msg_c4h10.set("ppm")); 132 | gw.present(CHILD_ID_CH4, S_AIR_QUALITY); 133 | gw.send(msg_ch4.set("ppm")); 134 | gw.present(CHILD_ID_H2, S_AIR_QUALITY); 135 | gw.send(msg_h2.set("ppm")); 136 | gw.present(CHILD_ID_C2H5OH, S_AIR_QUALITY); 137 | gw.send(msg_c2h5oh.set("ppm")); 138 | mutichannelGasSensor.begin(0x04);//the default I2C address of the slave is 0x04 139 | mutichannelGasSensor.powerOn(); 140 | 141 | } 142 | 143 | 144 | void loop() //code written in here will be in a loop 145 | { 146 | 147 | float c; 148 | 149 | c = mutichannelGasSensor.measure_NH3(); 150 | 151 | if((c>=0)&&(c!=val_nh3)) { 152 | Serial.print("NH3: "); 153 | Serial.print(c); 154 | val_nh3=c; 155 | gw.send(msg_nh3.set(c,3)); 156 | } 157 | 158 | c = mutichannelGasSensor.measure_CO(); 159 | 160 | if((c>=0)&&(c!=val_co)) { 161 | Serial.print("CO: "); 162 | Serial.print(c); 163 | val_co=c; 164 | gw.send(msg_co.set(c,3)); 165 | } 166 | 167 | c = mutichannelGasSensor.measure_NO2(); 168 | 169 | if((c>=0)&&(c!=val_no2)) { 170 | Serial.print("NO2: "); 171 | Serial.print(c); 172 | val_no2=c; 173 | gw.send(msg_no2.set(c,3)); 174 | } 175 | 176 | /*c = mutichannelGasSensor.measure_C3H8(); 177 | Serial.print("The concentration of C3H8 is "); 178 | if(c>=0) Serial.print(c); 179 | else Serial.print("invalid"); 180 | Serial.println(" ppm"); 181 | gw.send(msg_c3h8.set(c,3)); 182 | 183 | c = mutichannelGasSensor.measure_C4H10(); 184 | Serial.print("The concentration of C4H10 is "); 185 | if(c>=0) Serial.print(c); 186 | else Serial.print("invalid"); 187 | Serial.println(" ppm"); 188 | gw.send(msg_c4h10.set(c,3)); 189 | 190 | c = mutichannelGasSensor.measure_CH4(); 191 | Serial.print("The concentration of CH4 is "); 192 | if(c>=0) Serial.print(c); 193 | else Serial.print("invalid"); 194 | Serial.println(" ppm"); 195 | gw.send(msg_ch4.set(c,3)); 196 | 197 | c = mutichannelGasSensor.measure_H2(); 198 | Serial.print("The concentration of H2 is "); 199 | if(c>=0) Serial.print(c); 200 | else Serial.print("invalid"); 201 | Serial.println(" ppm"); 202 | gw.send(msg_h2.set(c,3)); 203 | 204 | c = mutichannelGasSensor.measure_C2H5OH(); 205 | Serial.print("The concentration of C2H5OH is "); 206 | if(c>=0) Serial.print(c); 207 | else Serial.print("invalid"); 208 | Serial.println(" ppm"); 209 | gw.send(msg_c2h5oh.set(c,3));*/ 210 | 211 | // Power down the radio. Note that the radio will get powered back up 212 | // on the next write() call. 213 | gw.sleep(SLEEP_TIME*4); //sleep for: sleepTime 214 | } 215 | 216 | 217 | -------------------------------------------------------------------------------- /1.3/PressureSensor.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | // License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 9 | 10 | 11 | #define LIGHT_SENSOR_ANALOG_PIN 0 12 | unsigned long SLEEP_TIME = 60; // Sleep time between reads (in seconds) 13 | 14 | Adafruit_BMP085 bmp = Adafruit_BMP085(); // Digital Pressure Sensor 15 | Sensor gw; 16 | 17 | float lastPressure = -1; 18 | float lastTemp = -1; 19 | int lastForecast = -1; 20 | Sleep sleep; 21 | char *weather[]={"stable","sunny","cloudy","unstable","thunderstorm","unknown"}; 22 | int minutes; 23 | float pressureSamples[180]; 24 | int minuteCount = 0; 25 | bool firstRound = true; 26 | float pressureAvg[7]; 27 | float dP_dt; 28 | boolean metric; 29 | 30 | void setup() { 31 | gw.begin(); 32 | 33 | // Send the sketch version information to the gateway and Controller 34 | gw.sendSketchInfo("Pressure Sensor", "1.0"); 35 | 36 | if (!bmp.begin()) { 37 | Serial.println("Could not find a valid BMP085 sensor, check wiring!"); 38 | while (1) { } 39 | } 40 | 41 | // Register sensors to gw (they will be created as child devices) 42 | gw.sendSensorPresentation(0, S_BARO); 43 | gw.sendSensorPresentation(1, S_TEMP); 44 | metric = gw.isMetricSystem(); 45 | } 46 | 47 | 48 | void loop() { 49 | float pressure = bmp.readPressure()/100; 50 | float temperature = bmp.readTemperature(); 51 | float altitude = bmp.readAltitude(); 52 | 53 | if (!metric) { 54 | // Convert to fahrenheit 55 | temperature = temperature * 9.0 / 5.0 + 32.0; 56 | } 57 | 58 | int forecast = sample(pressure); 59 | 60 | Serial.print("Temperature = "); 61 | Serial.print(temperature); 62 | Serial.println(metric?" *C":" *F"); 63 | Serial.print("Pressure = "); 64 | Serial.print(pressure); 65 | Serial.println(" hPa"); 66 | Serial.println(weather[forecast]); 67 | Serial.print("Altitude = "); 68 | Serial.print(bmp.readAltitude()); 69 | 70 | 71 | if (temperature != lastTemp) { 72 | gw.sendVariable(1, V_TEMP, temperature,1); 73 | lastTemp = temperature; 74 | } 75 | 76 | if (pressure != lastPressure) { 77 | gw.sendVariable(0, V_PRESSURE, pressure, 0); 78 | lastPressure = pressure; 79 | } 80 | 81 | if (forecast != lastForecast) { 82 | gw.sendVariable(0, V_FORECAST, weather[forecast]); 83 | lastForecast = forecast; 84 | } 85 | 86 | /* 87 | DP/Dt explanation 88 | 89 | 0 = "Stable Weather Pattern" 90 | 1 = "Slowly rising Good Weather", "Clear/Sunny " 91 | 2 = "Slowly falling L-Pressure ", "Cloudy/Rain " 92 | 3 = "Quickly rising H-Press", "Not Stable" 93 | 4 = "Quickly falling L-Press", "Thunderstorm" 94 | 5 = "Unknown (More Time needed) 95 | */ 96 | 97 | // Power down the radio. Note that the radio will get powered back up 98 | // on the next write() call. 99 | delay(1000); //delay to allow serial to fully print before sleep 100 | gw.powerDown(); 101 | sleep.pwrDownMode(); //set sleep mode 102 | sleep.sleepDelay(SLEEP_TIME * 1000); // sleep to conserve power 103 | } 104 | 105 | int sample(float pressure) { 106 | // Algorithm found here 107 | // http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf 108 | if (minuteCount > 180) 109 | minuteCount = 6; 110 | 111 | pressureSamples[minuteCount] = pressure; 112 | minuteCount++; 113 | 114 | if (minuteCount == 5) { 115 | // Avg pressure in first 5 min, value averaged from 0 to 5 min. 116 | pressureAvg[0] = ((pressureSamples[1] + pressureSamples[2] 117 | + pressureSamples[3] + pressureSamples[4] + pressureSamples[5]) 118 | / 5); 119 | } else if (minuteCount == 35) { 120 | // Avg pressure in 30 min, value averaged from 0 to 5 min. 121 | pressureAvg[1] = ((pressureSamples[30] + pressureSamples[31] 122 | + pressureSamples[32] + pressureSamples[33] 123 | + pressureSamples[34]) / 5); 124 | float change = (pressureAvg[1] - pressureAvg[0]); 125 | if (firstRound) // first time initial 3 hour 126 | dP_dt = ((65.0 / 1023.0) * 2 * change); // note this is for t = 0.5hour 127 | else 128 | dP_dt = (((65.0 / 1023.0) * change) / 1.5); // divide by 1.5 as this is the difference in time from 0 value. 129 | } else if (minuteCount == 60) { 130 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 131 | pressureAvg[2] = ((pressureSamples[55] + pressureSamples[56] 132 | + pressureSamples[57] + pressureSamples[58] 133 | + pressureSamples[59]) / 5); 134 | float change = (pressureAvg[2] - pressureAvg[0]); 135 | if (firstRound) //first time initial 3 hour 136 | dP_dt = ((65.0 / 1023.0) * change); //note this is for t = 1 hour 137 | else 138 | dP_dt = (((65.0 / 1023.0) * change) / 2); //divide by 2 as this is the difference in time from 0 value 139 | } else if (minuteCount == 95) { 140 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 141 | pressureAvg[3] = ((pressureSamples[90] + pressureSamples[91] 142 | + pressureSamples[92] + pressureSamples[93] 143 | + pressureSamples[94]) / 5); 144 | float change = (pressureAvg[3] - pressureAvg[0]); 145 | if (firstRound) // first time initial 3 hour 146 | dP_dt = (((65.0 / 1023.0) * change) / 1.5); // note this is for t = 1.5 hour 147 | else 148 | dP_dt = (((65.0 / 1023.0) * change) / 2.5); // divide by 2.5 as this is the difference in time from 0 value 149 | } else if (minuteCount == 120) { 150 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 151 | pressureAvg[4] = ((pressureSamples[115] + pressureSamples[116] 152 | + pressureSamples[117] + pressureSamples[118] 153 | + pressureSamples[119]) / 5); 154 | float change = (pressureAvg[4] - pressureAvg[0]); 155 | if (firstRound) // first time initial 3 hour 156 | dP_dt = (((65.0 / 1023.0) * change) / 2); // note this is for t = 2 hour 157 | else 158 | dP_dt = (((65.0 / 1023.0) * change) / 3); // divide by 3 as this is the difference in time from 0 value 159 | } else if (minuteCount == 155) { 160 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 161 | pressureAvg[5] = ((pressureSamples[150] + pressureSamples[151] 162 | + pressureSamples[152] + pressureSamples[153] 163 | + pressureSamples[154]) / 5); 164 | float change = (pressureAvg[5] - pressureAvg[0]); 165 | if (firstRound) // first time initial 3 hour 166 | dP_dt = (((65.0 / 1023.0) * change) / 2.5); // note this is for t = 2.5 hour 167 | else 168 | dP_dt = (((65.0 / 1023.0) * change) / 3.5); // divide by 3.5 as this is the difference in time from 0 value 169 | } else if (minuteCount == 180) { 170 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 171 | pressureAvg[6] = ((pressureSamples[175] + pressureSamples[176] 172 | + pressureSamples[177] + pressureSamples[178] 173 | + pressureSamples[179]) / 5); 174 | float change = (pressureAvg[6] - pressureAvg[0]); 175 | if (firstRound) // first time initial 3 hour 176 | dP_dt = (((65.0 / 1023.0) * change) / 3); // note this is for t = 3 hour 177 | else 178 | dP_dt = (((65.0 / 1023.0) * change) / 4); // divide by 4 as this is the difference in time from 0 value 179 | pressureAvg[0] = pressureAvg[5]; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past. 180 | firstRound = false; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop. 181 | } 182 | 183 | if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval. 184 | return 5; // Unknown, more time needed 185 | else if (dP_dt < (-0.25)) 186 | return 4; // Quickly falling LP, Thunderstorm, not stable 187 | else if (dP_dt > 0.25) 188 | return 3; // Quickly rising HP, not stable weather 189 | else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05))) 190 | return 2; // Slowly falling Low Pressure System, stable rainy weather 191 | else if ((dP_dt > 0.05) && (dP_dt < 0.25)) 192 | return 1; // Slowly rising HP stable good weather 193 | else if ((dP_dt > (-0.05)) && (dP_dt < 0.05)) 194 | return 0; // Stable weather 195 | else 196 | return 5; // Unknown 197 | } 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /PressureSensor.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Pressure sensor based on BMP085 3 | 4 | Contribution: epierre 5 | 6 | G GND 7 | V VCC 5V/3.3V 8 | SCL A4 9 | SDA A5 10 | 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | float myAltitude = 45; 19 | 20 | #define CHILD_ID_PRESSURE 0 21 | #define CHILD_ID_TEMP 1 22 | #define CHILD_ID_FORECAST 2 23 | #define PRESSURE_SENSOR_ANALOG_PIN 0 24 | unsigned long SLEEP_TIME = 60; // Sleep time between reads (in seconds) 25 | 26 | Adafruit_BMP085 bmp = Adafruit_BMP085(); // Digital Pressure Sensor 27 | MySensor gw; 28 | MyMessage pressureMsg(CHILD_ID_PRESSURE, V_PRESSURE); 29 | MyMessage tempMsg(CHILD_ID_TEMP, V_TEMP); 30 | MyMessage forecastMsg(CHILD_ID_FORECAST, V_FORECAST); 31 | 32 | float lastPressure = -1; 33 | float lastTemp = -1; 34 | int lastForecast = -1; 35 | char *weather[]={"stable","sunny","cloudy","unstable","thunderstorm","unknown"}; 36 | int minutes; 37 | float pressureSamples[180]; 38 | int minuteCount = 0; 39 | bool firstRound = true; 40 | float pressureAvg[7]; 41 | float dP_dt; 42 | boolean metric; 43 | 44 | void setup() { 45 | gw.begin(); 46 | 47 | // Send the sketch version information to the gateway and Controller 48 | gw.sendSketchInfo("Pressure Sensor", "1.0"); 49 | 50 | if (!bmp.begin(BMP085_ULTRAHIGHRES)) { 51 | Serial.println("Could not find a valid BMP085 sensor, check wiring!"); 52 | while (1) { } 53 | } 54 | 55 | // Register sensors to gw (they will be created as child devices) 56 | 57 | gw.present(CHILD_ID_PRESSURE, S_BARO); 58 | gw.present(CHILD_ID_TEMP, S_TEMP); 59 | metric = gw.getConfig().isMetric; 60 | 61 | } 62 | 63 | 64 | void loop() { 65 | 66 | //BMP085 pressure from adafruit gives you absolute pressure, that is NOT the barometric pressure the meteo forecast shows. You have to recalculate it by the following equation. 67 | float pressure_raw = bmp.readPressure(); 68 | float pressure = pressure_raw/pow((1.0 - ( myAltitude/44330.0 )), 5.255); 69 | float temperature = bmp.readTemperature(); 70 | float altitude = bmp.readAltitude(); 71 | 72 | if (!metric) { 73 | // Convert to fahrenheit 74 | temperature = temperature * 9.0 / 5.0 + 32.0; 75 | } 76 | 77 | int forecast = sample(pressure); 78 | 79 | Serial.print("Temperature = "); 80 | Serial.print(temperature); 81 | Serial.println(metric?" *C":" *F"); 82 | Serial.print("Pressure = "); 83 | Serial.print(pressure); 84 | Serial.println(" hPa"); 85 | Serial.println(weather[forecast]); 86 | Serial.print("Altitude = "); 87 | Serial.print(bmp.readAltitude()); 88 | 89 | 90 | if (temperature != lastTemp) { 91 | gw.send(tempMsg.set(temperature,1)); 92 | lastTemp = temperature; 93 | } 94 | 95 | if (pressure != lastPressure) { 96 | gw.send(pressureMsg.set(pressure,1)); 97 | lastPressure = pressure; 98 | } 99 | 100 | if (forecast != lastForecast) { 101 | gw.send(forecastMsg.set(weather[forecast])); 102 | lastForecast = forecast; 103 | } 104 | 105 | /* 106 | DP/Dt explanation 107 | 108 | 0 = "Stable Weather Pattern" 109 | 1 = "Slowly rising Good Weather", "Clear/Sunny " 110 | 2 = "Slowly falling L-Pressure ", "Cloudy/Rain " 111 | 3 = "Quickly rising H-Press", "Not Stable" 112 | 4 = "Quickly falling L-Press", "Thunderstorm" 113 | 5 = "Unknown (More Time needed) 114 | */ 115 | 116 | // Power down the radio. Note that the radio will get powered back up 117 | // on the next write() call. 118 | delay(1000); //delay to allow serial to fully print before sleep 119 | 120 | gw.sleep(SLEEP_TIME * 1000); // sleep to conserve power 121 | } 122 | 123 | int sample(float pressure) { 124 | // Algorithm found here 125 | // http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf 126 | if (minuteCount > 180) 127 | minuteCount = 6; 128 | 129 | pressureSamples[minuteCount] = pressure; 130 | minuteCount++; 131 | 132 | if (minuteCount == 5) { 133 | // Avg pressure in first 5 min, value averaged from 0 to 5 min. 134 | pressureAvg[0] = ((pressureSamples[1] + pressureSamples[2] 135 | + pressureSamples[3] + pressureSamples[4] + pressureSamples[5]) 136 | / 5); 137 | } else if (minuteCount == 35) { 138 | // Avg pressure in 30 min, value averaged from 0 to 5 min. 139 | pressureAvg[1] = ((pressureSamples[30] + pressureSamples[31] 140 | + pressureSamples[32] + pressureSamples[33] 141 | + pressureSamples[34]) / 5); 142 | float change = (pressureAvg[1] - pressureAvg[0]); 143 | if (firstRound) // first time initial 3 hour 144 | dP_dt = ((65.0 / 1023.0) * 2 * change); // note this is for t = 0.5hour 145 | else 146 | dP_dt = (((65.0 / 1023.0) * change) / 1.5); // divide by 1.5 as this is the difference in time from 0 value. 147 | } else if (minuteCount == 60) { 148 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 149 | pressureAvg[2] = ((pressureSamples[55] + pressureSamples[56] 150 | + pressureSamples[57] + pressureSamples[58] 151 | + pressureSamples[59]) / 5); 152 | float change = (pressureAvg[2] - pressureAvg[0]); 153 | if (firstRound) //first time initial 3 hour 154 | dP_dt = ((65.0 / 1023.0) * change); //note this is for t = 1 hour 155 | else 156 | dP_dt = (((65.0 / 1023.0) * change) / 2); //divide by 2 as this is the difference in time from 0 value 157 | } else if (minuteCount == 95) { 158 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 159 | pressureAvg[3] = ((pressureSamples[90] + pressureSamples[91] 160 | + pressureSamples[92] + pressureSamples[93] 161 | + pressureSamples[94]) / 5); 162 | float change = (pressureAvg[3] - pressureAvg[0]); 163 | if (firstRound) // first time initial 3 hour 164 | dP_dt = (((65.0 / 1023.0) * change) / 1.5); // note this is for t = 1.5 hour 165 | else 166 | dP_dt = (((65.0 / 1023.0) * change) / 2.5); // divide by 2.5 as this is the difference in time from 0 value 167 | } else if (minuteCount == 120) { 168 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 169 | pressureAvg[4] = ((pressureSamples[115] + pressureSamples[116] 170 | + pressureSamples[117] + pressureSamples[118] 171 | + pressureSamples[119]) / 5); 172 | float change = (pressureAvg[4] - pressureAvg[0]); 173 | if (firstRound) // first time initial 3 hour 174 | dP_dt = (((65.0 / 1023.0) * change) / 2); // note this is for t = 2 hour 175 | else 176 | dP_dt = (((65.0 / 1023.0) * change) / 3); // divide by 3 as this is the difference in time from 0 value 177 | } else if (minuteCount == 155) { 178 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 179 | pressureAvg[5] = ((pressureSamples[150] + pressureSamples[151] 180 | + pressureSamples[152] + pressureSamples[153] 181 | + pressureSamples[154]) / 5); 182 | float change = (pressureAvg[5] - pressureAvg[0]); 183 | if (firstRound) // first time initial 3 hour 184 | dP_dt = (((65.0 / 1023.0) * change) / 2.5); // note this is for t = 2.5 hour 185 | else 186 | dP_dt = (((65.0 / 1023.0) * change) / 3.5); // divide by 3.5 as this is the difference in time from 0 value 187 | } else if (minuteCount == 180) { 188 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 189 | pressureAvg[6] = ((pressureSamples[175] + pressureSamples[176] 190 | + pressureSamples[177] + pressureSamples[178] 191 | + pressureSamples[179]) / 5); 192 | float change = (pressureAvg[6] - pressureAvg[0]); 193 | if (firstRound) // first time initial 3 hour 194 | dP_dt = (((65.0 / 1023.0) * change) / 3); // note this is for t = 3 hour 195 | else 196 | dP_dt = (((65.0 / 1023.0) * change) / 4); // divide by 4 as this is the difference in time from 0 value 197 | pressureAvg[0] = pressureAvg[5]; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past. 198 | firstRound = false; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop. 199 | } 200 | 201 | if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval. 202 | return 5; // Unknown, more time needed 203 | else if (dP_dt < (-0.25)) 204 | return 4; // Quickly falling LP, Thunderstorm, not stable 205 | else if (dP_dt > 0.25) 206 | return 3; // Quickly rising HP, not stable weather 207 | else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05))) 208 | return 2; // Slowly falling Low Pressure System, stable rainy weather 209 | else if ((dP_dt > 0.05) && (dP_dt < 0.25)) 210 | return 1; // Slowly rising HP stable good weather 211 | else if ((dP_dt > (-0.05)) && (dP_dt < 0.05)) 212 | return 0; // Stable weather 213 | else 214 | return 5; // Unknown 215 | } 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /1.3/MQ2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Vera Arduino MQ2 3 | 4 | connect the sensor as follows : 5 | 6 | A H A >>> 5V 7 | B >>> A0 8 | H >>> GND 9 | B >>> 10K ohm >>> GND 10 | 11 | Contribution: epierre 12 | Based on http://sandboxelectronics.com/?p=165 13 | 14 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 15 | 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define CHILD_ID_MQ 0 26 | /************************Hardware Related Macros************************************/ 27 | #define MQ_SENSOR_ANALOG_PIN (0) //define which analog input channel you are going to use 28 | #define RL_VALUE (5) //define the load resistance on the board, in kilo ohms 29 | #define RO_CLEAN_AIR_FACTOR (9.83) //RO_CLEAR_AIR_FACTOR=(Sensor resistance in clean air)/RO, 30 | //which is derived from the chart in datasheet 31 | /***********************Software Related Macros************************************/ 32 | #define CALIBARAION_SAMPLE_TIMES (50) //define how many samples you are going to take in the calibration phase 33 | #define CALIBRATION_SAMPLE_INTERVAL (500) //define the time interal(in milisecond) between each samples in the 34 | //cablibration phase 35 | #define READ_SAMPLE_INTERVAL (50) //define how many samples you are going to take in normal operation 36 | #define READ_SAMPLE_TIMES (5) //define the time interal(in milisecond) between each samples in 37 | //normal operation 38 | /**********************Application Related Macros**********************************/ 39 | #define GAS_LPG (0) 40 | #define GAS_CO (1) 41 | #define GAS_SMOKE (2) 42 | /*****************************Globals***********************************************/ 43 | unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) 44 | //VARIABLES 45 | float Ro = 10000.0; // this has to be tuned 10K Ohm 46 | int val = 0; // variable to store the value coming from the sensor 47 | float valMQ =0.0; 48 | float lastMQ =0.0; 49 | float LPGCurve[3] = {2.3,0.21,-0.47}; //two points are taken from the curve. 50 | //with these two points, a line is formed which is "approximately equivalent" 51 | //to the original curve. 52 | //data format:{ x, y, slope}; point1: (lg200, 0.21), point2: (lg10000, -0.59) 53 | float COCurve[3] = {2.3,0.72,-0.34}; //two points are taken from the curve. 54 | //with these two points, a line is formed which is "approximately equivalent" 55 | //to the original curve. 56 | //data format:{ x, y, slope}; point1: (lg200, 0.72), point2: (lg10000, 0.15) 57 | float SmokeCurve[3] ={2.3,0.53,-0.44}; //two points are taken from the curve. 58 | //with these two points, a line is formed which is "approximately equivalent" 59 | //to the original curve. 60 | //data format:{ x, y, slope}; point1: (lg200, 0.53), point2:(lg10000,-0.22) 61 | 62 | 63 | Sensor gw; 64 | Sleep sleep; 65 | 66 | void setup() 67 | { 68 | gw.begin(); 69 | 70 | // Send the sketch version information to the gateway and Controller 71 | gw.sendSketchInfo("MQ-2 Sensor", "1.0"); 72 | 73 | // Register all sensors to gateway (they will be created as child devices) 74 | gw.sendSensorPresentation(CHILD_ID_MQ, 22); 75 | 76 | Ro = MQCalibration(MQ_SENSOR_ANALOG_PIN); //Calibrating the sensor. Please make sure the sensor is in clean air 77 | //when you perform the calibration 78 | Serial.println(Ro); 79 | } 80 | 81 | void loop() 82 | { 83 | uint16_t valMQ = MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_CO); 84 | Serial.println(val); 85 | 86 | Serial.print("LPG:"); 87 | Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_LPG) ); 88 | Serial.print( "ppm" ); 89 | Serial.print(" "); 90 | Serial.print("CO:"); 91 | Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_CO) ); 92 | Serial.print( "ppm" ); 93 | Serial.print(" "); 94 | Serial.print("SMOKE:"); 95 | Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_SMOKE) ); 96 | Serial.print( "ppm" ); 97 | Serial.print("\n"); 98 | 99 | if (valMQ != lastMQ) { 100 | gw.sendVariable(CHILD_ID_MQ, V_VAR1, (int)ceil(valMQ)); 101 | lastMQ = ceil(valMQ); 102 | } 103 | 104 | // Power down the radio. Note that the radio will get powered back up 105 | // on the next write() call. 106 | delay(1000); //delay to allow serial to fully print before sleep 107 | gw.powerDown(); 108 | sleep.pwrDownMode(); //set sleep mode 109 | sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime 110 | } 111 | 112 | /****************** MQResistanceCalculation **************************************** 113 | Input: raw_adc - raw value read from adc, which represents the voltage 114 | Output: the calculated sensor resistance 115 | Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage 116 | across the load resistor and its resistance, the resistance of the sensor 117 | could be derived. 118 | ************************************************************************************/ 119 | float MQResistanceCalculation(int raw_adc) 120 | { 121 | return ( ((float)RL_VALUE*(1023-raw_adc)/raw_adc)); 122 | } 123 | 124 | /***************************** MQCalibration **************************************** 125 | Input: mq_pin - analog channel 126 | Output: Ro of the sensor 127 | Remarks: This function assumes that the sensor is in clean air. It use 128 | MQResistanceCalculation to calculates the sensor resistance in clean air 129 | and then divides it with RO_CLEAN_AIR_FACTOR. RO_CLEAN_AIR_FACTOR is about 130 | 10, which differs slightly between different sensors. 131 | ************************************************************************************/ 132 | float MQCalibration(int mq_pin) 133 | { 134 | int i; 135 | float val=0; 136 | 137 | for (i=0;i 34 | #include 35 | #include 36 | #include "MutichannelGasSensor.h" 37 | 38 | /********************************************************************************************************* 39 | ** Function name: begin 40 | ** Descriptions: initialize I2C 41 | *********************************************************************************************************/ 42 | void MutichannelGasSensor::begin(int address) 43 | { 44 | Wire.begin(); 45 | i2cAddress = address; 46 | is_connected = 0; 47 | if (readR0() >= 0) is_connected = 1; 48 | } 49 | 50 | void MutichannelGasSensor::begin() 51 | { 52 | begin(DEFAULT_I2C_ADDR); 53 | } 54 | 55 | /********************************************************************************************************* 56 | ** Function name: sendI2C 57 | ** Descriptions: send one byte to I2C Wire 58 | *********************************************************************************************************/ 59 | void MutichannelGasSensor::sendI2C(unsigned char dta) 60 | { 61 | Wire.beginTransmission(i2cAddress); // transmit to device #4 62 | Wire.write(dta); // sends one byte 63 | Wire.endTransmission(); // stop transmitting 64 | } 65 | 66 | /********************************************************************************************************* 67 | ** Function name: readData 68 | ** Descriptions: read 4 bytes from I2C slave 69 | *********************************************************************************************************/ 70 | int16_t MutichannelGasSensor::readData(uint8_t cmd) 71 | { 72 | uint16_t timeout = 0; 73 | uint8_t buffer[4]; 74 | uint8_t checksum = 0; 75 | int16_t rtnData = 0; 76 | 77 | //send command 78 | sendI2C(cmd); 79 | //wait for a while 80 | delay(2); 81 | //get response 82 | Wire.requestFrom(i2cAddress, (uint8_t)4); // request 4 bytes from slave device 83 | while(Wire.available() == 0) 84 | { 85 | if(timeout++ > 100) 86 | return -2;//time out 87 | delay(2); 88 | } 89 | if(Wire.available() != 4) 90 | return -3;//rtnData length wrong 91 | buffer[0] = Wire.read(); 92 | buffer[1] = Wire.read(); 93 | buffer[2] = Wire.read(); 94 | buffer[3] = Wire.read(); 95 | checksum = (uint8_t)(buffer[0] + buffer[1] + buffer[2]); 96 | if(checksum != buffer[3]) 97 | return -4;//checksum wrong 98 | rtnData = ((buffer[1] << 8) + buffer[2]); 99 | 100 | return rtnData;//successful 101 | } 102 | 103 | /********************************************************************************************************* 104 | ** Function name: readR0 105 | ** Descriptions: read R0 stored in slave MCU 106 | *********************************************************************************************************/ 107 | int16_t MutichannelGasSensor::readR0(void) 108 | { 109 | int16_t rtnData = 0; 110 | 111 | rtnData = readData(0x11); 112 | if(rtnData >= 0) 113 | res0[0] = rtnData; 114 | else 115 | return rtnData;//unsuccessful 116 | 117 | rtnData = readData(0x12); 118 | if(rtnData >= 0) 119 | res0[1] = rtnData; 120 | else 121 | return rtnData;//unsuccessful 122 | 123 | rtnData = readData(0x13); 124 | if(rtnData >= 0) 125 | res0[2] = rtnData; 126 | else 127 | return rtnData;//unsuccessful 128 | 129 | return 0;//successful 130 | } 131 | 132 | /********************************************************************************************************* 133 | ** Function name: readR 134 | ** Descriptions: read resistance value of each channel from slave MCU 135 | *********************************************************************************************************/ 136 | int16_t MutichannelGasSensor::readR(void) 137 | { 138 | int16_t rtnData = 0; 139 | 140 | rtnData = readData(0x01); 141 | if(rtnData >= 0) 142 | res[0] = rtnData; 143 | else 144 | return rtnData;//unsuccessful 145 | 146 | rtnData = readData(0x02); 147 | if(rtnData >= 0) 148 | res[1] = rtnData; 149 | else 150 | return rtnData;//unsuccessful 151 | 152 | rtnData = readData(0x03); 153 | if(rtnData >= 0) 154 | res[2] = rtnData; 155 | else 156 | return rtnData;//unsuccessful 157 | 158 | return 0;//successful 159 | } 160 | 161 | /********************************************************************************************************* 162 | ** Function name: readR 163 | ** Descriptions: calculate gas concentration of each channel from slave MCU 164 | ** Parameters: 165 | gas - gas type 166 | ** Returns: 167 | float value - concentration of the gas 168 | *********************************************************************************************************/ 169 | float MutichannelGasSensor::calcGas(int gas) 170 | { 171 | if(!is_connected) 172 | { 173 | if(readR0() >= 0) is_connected = 1; 174 | else return -1.0f; 175 | } 176 | 177 | if(readR() < 0) 178 | return -2.0f; 179 | 180 | float ratio0 = (float)res[0] / res0[0]; 181 | float ratio1 = (float)res[1] / res0[1]; 182 | float ratio2 = (float)res[2] / res0[2]; 183 | 184 | float c = 0; 185 | 186 | switch(gas) 187 | { 188 | case CO: 189 | { 190 | if(ratio1 < 0.01) ratio1 = 0.01; 191 | if(ratio1 > 3) ratio1 = 3; 192 | //c = pow(10, 0.6) / pow(ratio1, 1.2); 193 | c = pow(ratio1, -1.179)*4.385; //mod by jack 194 | break; 195 | } 196 | case NO2: 197 | { 198 | if(ratio2 < 0.07) ratio2 = 0.07; 199 | if(ratio2 > 40) ratio2 = 40; 200 | //c = ratio2 / pow(10, 0.8); 201 | c = pow(ratio2, 1.007)/6.855; //mod by jack 202 | break; 203 | } 204 | case NH3: 205 | { 206 | if(ratio0 < 0.04) ratio0 = 0.04; 207 | if(ratio0 > 0.8) ratio0 = 0.8; 208 | //c = 1 / (ratio0 * ratio0 * pow(10, 0.4)); 209 | c = pow(ratio0, -1.67)/1.47; //modi by jack 210 | break; 211 | } 212 | case C3H8: //add by jack 213 | { 214 | if(ratio0 < 0.23) ratio0 = 0.23; 215 | if(ratio0 > 0.8) ratio0 = 0.8; 216 | c = pow(ratio0, -2.518)*570.164; 217 | break; 218 | } 219 | case C4H10: //add by jack 220 | { 221 | if(ratio0 < 0.15) ratio0 = 0.15; 222 | if(ratio0 > 0.65) ratio0 = 0.65; 223 | c = pow(ratio0, -2.138)*398.107; 224 | break; 225 | } 226 | case CH4: //add by jack 227 | { 228 | if(ratio1 < 0.5) ratio1 = 0.5; 229 | if(ratio1 > 0.7) ratio1 = 0.7; 230 | c = pow(ratio1, -4.363)*630.957; 231 | break; 232 | } 233 | case H2: //add by jack 234 | { 235 | if(ratio1 < 0.04) ratio1 = 0.04; 236 | if(ratio1 > 0.8) ratio1 = 0.8; 237 | c = pow(ratio1, -1.8)*0.73; 238 | break; 239 | } 240 | case C2H5OH: //add by jack 241 | { 242 | if(ratio1 < 0.04) ratio1 = 0.04; 243 | if(ratio1 > 1.1) ratio1 = 1.1; 244 | c = pow(ratio1, -1.552)*1.622; 245 | break; 246 | } 247 | default: 248 | break; 249 | } 250 | 251 | return isnan(c)?-3:c; 252 | } 253 | 254 | /********************************************************************************************************* 255 | ** Function name: changeI2cAddr 256 | ** Descriptions: change I2C address of the slave MCU, and this address will be stored in EEPROM of slave MCU 257 | *********************************************************************************************************/ 258 | void MutichannelGasSensor::changeI2cAddr(uint8_t newAddr) 259 | { 260 | Wire.beginTransmission(i2cAddress); // transmit to device 261 | Wire.write(0x23); // sends one byte 262 | Wire.write(newAddr); // sends one byte 263 | Wire.endTransmission(); // stop transmitting 264 | i2cAddress = newAddr; 265 | } 266 | 267 | /********************************************************************************************************* 268 | ** Function name: doCalibrate 269 | ** Descriptions: tell slave to do a calibration, it will take about 8s 270 | after the calibration, must reread the R0 values 271 | *********************************************************************************************************/ 272 | void MutichannelGasSensor::doCalibrate(void) 273 | { 274 | sendI2C(0x22); 275 | delay(8000); 276 | if(readR0() >= 0) is_connected = 1; 277 | else is_connected = 0; 278 | } 279 | 280 | /********************************************************************************************************* 281 | ** Function name: powerOn 282 | ** Descriptions: power on sensor heater 283 | *********************************************************************************************************/ 284 | void MutichannelGasSensor::powerOn(void) 285 | { 286 | sendI2C(0x21); 287 | } 288 | 289 | /********************************************************************************************************* 290 | ** Function name: powerOff 291 | ** Descriptions: power off sensor heater 292 | *********************************************************************************************************/ 293 | void MutichannelGasSensor::powerOff(void) 294 | { 295 | sendI2C(0x20); 296 | } 297 | 298 | /********************************************************************************************************* 299 | ** Function name: measure_xxx 300 | ** Descriptions: measure the concentration of xxx, with unit ppm 301 | *********************************************************************************************************/ 302 | float MutichannelGasSensor::measure_CO() 303 | { 304 | return calcGas(CO); 305 | } 306 | float MutichannelGasSensor::measure_NO2() 307 | { 308 | return calcGas(NO2); 309 | } 310 | float MutichannelGasSensor::measure_NH3() 311 | { 312 | return calcGas(NH3); 313 | } 314 | float MutichannelGasSensor::measure_C3H8() 315 | { 316 | return calcGas(C3H8); 317 | } 318 | float MutichannelGasSensor::measure_C4H10() 319 | { 320 | return calcGas(C4H10); 321 | } 322 | float MutichannelGasSensor::measure_CH4() 323 | { 324 | return calcGas(CH4); 325 | } 326 | float MutichannelGasSensor::measure_H2() 327 | { 328 | return calcGas(H2); 329 | } 330 | float MutichannelGasSensor::measure_C2H5OH() 331 | { 332 | return calcGas(C2H5OH); 333 | } 334 | 335 | 336 | 337 | MutichannelGasSensor mutichannelGasSensor; 338 | /********************************************************************************************************* 339 | END FILE 340 | *********************************************************************************************************/ 341 | -------------------------------------------------------------------------------- /mysensors-gw1.4.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 3 | use warnings; 4 | use strict; 5 | use POSIX qw(strftime ceil floor); 6 | use Device::SerialPort; 7 | use IO::Handle; 8 | use DateTime; 9 | use Scalar::Util qw(looks_like_number); 10 | use DBI; 11 | use Config::Simple; 12 | 13 | # Initialization strings 14 | my $base="/home/in/"; 15 | my $conf="/home/in/.conf-mysensors"; 16 | my $ccnt; 17 | my $cfg; 18 | my ($count, $string, $radioId, $value); 19 | my $Config=&read_conf($conf); 20 | my $port = $Config->{"Internal.usb_port"}; 21 | my $domo_ip = $Config->{"Internal.domo_ip"}; 22 | my $domo_port = $Config->{"Internal.domo_port"}; 23 | my $hardware_id = $Config->{"Internal.hardware_domo"}; 24 | my $dbh; 25 | my %sensor_tab; 26 | 27 | # SQLite database connexion 28 | if (! -e "mysensors.db") { 29 | $dbh=&init_database(); 30 | &create_table($dbh); 31 | } else {$dbh=&init_database();} 32 | 33 | #SQLite sensors init 34 | my $sth = $dbh->prepare( "SELECT * FROM sensors" ); 35 | $sth->execute(); 36 | my $row; 37 | while($row = $sth->fetchrow_hashref()) { 38 | my $id=$row->{id}; 39 | my $subType=$row->{subtype}; 40 | my $value=$row->{value}; 41 | $sensor_tab{$id}->{$subType}=$value; 42 | print "Read $id $subType $value\n"; 43 | } 44 | 45 | # USB port opening 46 | my $ob = &connect_usb($port); 47 | $ob->close || warn "close failed";; 48 | $ob = &connect_usb($port); 49 | my $sleep = 5; 50 | print "Sleeping $sleep second to let arduino get ready...\n"; 51 | sleep $sleep; 52 | 53 | # Now parse output 54 | my @vals; 55 | open(FIC,">>".$base."log-gw.txt")||die $!; 56 | print FIC "Starting\n"; 57 | FIC->autoflush(1); 58 | while(1) { 59 | $ccnt++; 60 | $ob->lastline("\n"); 61 | 62 | ($count, $string) = $ob->read(255); 63 | 64 | #print "$ccnt $string\n\n"; 65 | @vals = split("\n", $string); 66 | foreach (@vals) { 67 | $_=~ s/\t/\=/; 68 | $_=~ s/\r//; 69 | $_=~ s/\n//; 70 | my ($radioId,$childId,$messageType,$ack,$subType,$payload) = split(";", $_); 71 | if (! $childId) {$childId="0";} 72 | if (! $messageType) {$messageType="0";} 73 | if (! $subType) {$subType="0";} 74 | if (! $payload) {$payload="0";} 75 | next unless ($radioId); 76 | next if (! looks_like_number $radioId); 77 | #$value = 0 unless ($value); 78 | #$value = $value/1000 if $radioId =~ /I/g; 79 | my $dt = DateTime->now(time_zone=>'local'); 80 | my $date=join ' ', $dt->ymd, $dt->hms; 81 | print "$date $radioId $childId $messageType $ack $subType $payload\n"; 82 | if ($radioId>=0) { 83 | print FIC "$date $radioId $childId $messageType $ack $subType $payload\n"; 84 | } 85 | if (($messageType==3)&&($subType==3)) { 86 | #Answer the node ID 87 | my $msg = "$radioId;$childId;3;0;4;9\n"; 88 | my $co = $ob->write($msg); 89 | warn "write failed\n" unless ($co); 90 | print "$date W ($co) : $msg \n"; 91 | print FIC "$date W : $msg \n"; 92 | $ob->write_drain; 93 | } 94 | if (($messageType==2)&&($subType==24)) { 95 | #Answer the node VAR_1 96 | my $msg; 97 | my $val=$sensor_tab{$radioId}->{$subType}||36890; 98 | $msg = "$radioId;$childId;0;3;24;$val\n"; 99 | my $co = $ob->write($msg); 100 | warn "write failed\n" unless ($co); 101 | print "$date W ($co) : $msg \n"; 102 | print FIC "$date W : $msg \n"; 103 | $ob->write_drain; 104 | } 105 | if (($messageType==4)&&($subType==13)) { 106 | #Answer we are Metric 107 | my $msg = "$radioId;$childId;0;4;13;M\n"; 108 | my $co = $ob->write($msg); 109 | warn "write failed\n" unless ($co); 110 | print "$date W ($co) : $msg \n"; 111 | print FIC "$date W : $msg \n"; 112 | $ob->write_drain; 113 | } 114 | if (($messageType==1)&&($subType==0)) { 115 | # Read the Temp value 116 | $sensor_tab{$radioId}->{$subType}=$payload; 117 | &update_or_insert($radioId,$subType,$payload); 118 | my $hum=$sensor_tab{$radioId}->{1}||0; 119 | next if ($hum<=0); 120 | print "sending to DZ 164 $payload $hum\n"; 121 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=164&svalue=$payload;$hum;2" &`; 122 | 123 | } 124 | if (($messageType==1)&&($subType==1)) { 125 | # Read the Humidity value 126 | $sensor_tab{$radioId}->{$subType}=$payload; 127 | &update_or_insert($radioId,$subType,$payload); 128 | my $temp=$sensor_tab{$radioId}->{0}||0; 129 | next if ($temp<=0); 130 | print "sending to DZ 164 $payload\n"; 131 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=164&svalue=$temp;$payload;2" &`; 132 | } 133 | if (($messageType==1)&&($subType==16)) { 134 | # Read the leaf wetness 135 | $sensor_tab{$radioId}->{$subType}=$payload; 136 | &update_or_insert($radioId,$subType,$payload); 137 | my $val=100-($payload);my $h=1; 138 | if ($val>50) {$h=2} elsif ($val<25) {$h=0} else {$h=1}; 139 | print "sending to DZ 243 $val\n"; 140 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=243&nvalue=$val&svalue=$val;$h" &`; 141 | } 142 | if (($messageType==1)&&($subType==24)) { 143 | # AqCO2 144 | $sensor_tab{$radioId}->{$subType}=$payload; 145 | &update_or_insert($radioId,$subType,$payload); 146 | if ($radioId==7) { 147 | print "sending to DZ 124 $payload\n"; 148 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=124&nvalue=$payload" &`; 149 | } 150 | } 151 | if (($messageType==1)&&($subType==37)) { 152 | # save a DUST_LEVEL 153 | $sensor_tab{$radioId}->{$subType}=$payload; 154 | &update_or_insert($radioId,$subType,$payload); 155 | if ($radioId==3) { 156 | if ($childId==0) {#PM10 157 | print "sending to DZ 244 $payload\n"; 158 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=244&nvalue=$payload" &`; 159 | 160 | } elsif ($childId=1) {#PM25 161 | print "sending to DZ 245 $payload\n"; 162 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=245&nvalue=$payload" &`; 163 | 164 | } 165 | } 166 | if ($radioId==6) { 167 | if ($childId==0) {#PM10 168 | print "sending to DZ 208 $payload\n"; 169 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=208&nvalue=$payload" &`; 170 | 171 | } elsif ($childId=1) {#PM25 172 | print "sending to DZ 225 $payload\n"; 173 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=225&nvalue=$payload" &`; 174 | 175 | } 176 | } 177 | } 178 | if (($messageType==1)&&($subType>=40)) { 179 | # save a VAR 180 | $sensor_tab{$radioId}->{$subType}=$payload; 181 | &update_or_insert($radioId,$subType,$payload); 182 | if ($radioId==8) { 183 | if ($subType==40) {#Smoke 184 | print "sending $payload to DZ 223 $payload\n"; 185 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=223&nvalue=$payload" &`; 186 | } elsif ($subType==41) {#LPG 187 | print "sending $payload to DZ 226 $payload\n"; 188 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=226&nvalue=$payload" &`; 189 | } elsif ($subType==42) {#O3 190 | print "sending $payload to DZ 210 $payload\n"; 191 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=210&nvalue=$payload" &`; 192 | } elsif ($subType==43) {#H2 193 | #print "sending $payload to DZ 235 $payload\n"; 194 | #`curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=235&nvalue=$payload" &`; 195 | } elsif ($subType==44) {#CO 196 | print "sending $payload to DZ 209 $payload\n"; 197 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=209&nvalue=$payload" &`; 198 | } elsif ($subType==45) {#PM10 199 | #print "sending $payload to DZ 208 $payload\n"; 200 | #`curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=208&nvalue=$payload" &`; 201 | } elsif ($subType==46) {#SO2 202 | print "sending $payload to DZ 224 $payload\n"; 203 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=224&nvalue=$payload" &`; 204 | } 205 | } 206 | } 207 | if (($messageType==1)&&($subType==4)) { 208 | # save a BARO 209 | $sensor_tab{$radioId}->{$subType}=$payload; 210 | &update_or_insert($radioId,$subType,$payload); 211 | print "sending to DZ 198 $payload\n"; 212 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=198&svalue=$payload" &`; 213 | } 214 | if (($messageType==1)&&($subType==35)) { 215 | # Watermeter 216 | $sensor_tab{$radioId}->{$subType}=$payload; 217 | &update_or_insert($radioId,$subType,$payload); 218 | my $hum=$sensor_tab{$radioId}->{1}||0; 219 | if ($radioId==2) { 220 | $payload=$payload*1000; 221 | print "sending to DZ 129 $payload\n"; 222 | `curl -s "http://$domo_ip:$domo_port/json.htm?type=command¶m=udevice&idx=129&svalue=$payload" &`; 223 | } else { 224 | print "What to do ?\n"; 225 | } 226 | } 227 | } 228 | sleep(1); 229 | 230 | 231 | } 232 | close(FIC); 233 | $ob->write_drain; 234 | $ob->close; 235 | undef $ob; 236 | 237 | sub connect_usb { 238 | my $port=$_[0]; 239 | Device::SerialPort->new($port, 1) || die "Can't open $port: $ +!"; 240 | $ob = Device::SerialPort->new($port, 1) || die "Can't open $port: $ +!"; 241 | $ob->databits(8); 242 | $ob->baudrate(115200); 243 | $ob->parity("none"); 244 | $ob->stopbits(1); 245 | $ob->buffers( 4096, 4096 ); 246 | $ob->write_settings(); 247 | return $ob; 248 | } 249 | 250 | sub read_conf { 251 | my $conf=$_[0]; 252 | $cfg = new Config::Simple($conf) or die Config::Simple->error();; 253 | $cfg->autosave(1); 254 | # getting the values as a hash: 255 | my %Config = $cfg->vars(); 256 | return \%Config; 257 | #$user = $cfg->param("mysql.user") 258 | #$cfg->param("User", "sherzodR"); 259 | #$cfg->delete('mysql.user'); 260 | } 261 | sub save_conf { 262 | $cfg=$_[0]; 263 | $cfg->save(); 264 | } 265 | sub init_database { 266 | my $dbh = DBI->connect( 267 | "dbi:SQLite:dbname=mysensors.db", 268 | "", 269 | "", 270 | { RaiseError => 1 }, 271 | ) or die $DBI::errstr; 272 | return $dbh; 273 | } 274 | sub create_table { 275 | #Last sensors values table 276 | my $stmt = qq(CREATE TABLE sensors 277 | (id INT NOT NULL, 278 | subtype REAL, 279 | value REAL 280 | );); 281 | my $rv = $dbh->do($stmt); 282 | if($rv < 0){ 283 | print $DBI::errstr; 284 | } else { 285 | print "Table created successfully\n"; 286 | } 287 | #Sensors type table 288 | $stmt = qq(CREATE TABLE hardware 289 | (id INT PRIMARY KEY NOT NULL, 290 | I_BATTERY_LEVEL REAL, 291 | I_RELAY_NODE INT, 292 | I_SKETCH_NAME CHAR, 293 | I_SKETCH_VERSION REAL 294 | );); 295 | $rv = $dbh->do($stmt); 296 | if($rv < 0){ 297 | print $DBI::errstr; 298 | } else { 299 | print "Table created successfully\n"; 300 | } 301 | #device table 302 | $stmt = qq(CREATE TABLE device 303 | (id INT PRIMARY KEY NOT NULL, 304 | hwid INT NOT NULL, 305 | sensorType INT, 306 | library CHAR 307 | );); 308 | $rv = $dbh->do($stmt); 309 | if($rv < 0){ 310 | print $DBI::errstr; 311 | } else { 312 | print "Table created successfully\n"; 313 | } 314 | } 315 | sub insert_sensor { 316 | my $stmt = qq(INSERT INTO sensors (id,subtype,value) 317 | VALUES ($_[0], $_[1], $_[2] )); 318 | my $rv = $dbh->do($stmt) or die $DBI::errstr; 319 | $sth->finish(); 320 | } 321 | sub update_sensor { 322 | my $stmt = qq(UPDATE sensors set value=$_[2] where id=$_[0] and subtype=$_[1] ); 323 | my $rv = $dbh->do($stmt) or die $DBI::errstr; 324 | $sth->finish(); 325 | } 326 | sub update_or_insert { 327 | my $sth = $dbh->prepare( "SELECT value FROM sensors WHERE id=$_[0] AND subtype=$_[1]"); 328 | $sth->execute(); 329 | my $row = $sth->fetch; 330 | if (!$row) { 331 | insert_sensor($_[0],$_[1],$_[2]); 332 | } else { 333 | update_sensor($_[0],$_[1],$_[2]); 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /PressureSensor-c.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Pressure sensor based on BMP085 3 | Requires Lib BMP085 V2 4 | https://github.com/adafruit/Adafruit_BMP085_Unified 5 | https://github.com/adafruit/Adafruit_Sensor 6 | 7 | Contribution: epierre 8 | 9 | G GND 10 | V VCC 5V/3.3V 11 | SCL A4 12 | SDA A5 13 | 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | //float seaLevelPressure = 1013; 23 | float myAltitude = 45; 24 | 25 | #define LTC4067_CHRG_PIN A1 //analog input A1 on ATmega 328 is /CHRG signal from LTC4067 26 | #define batteryVoltage_PIN A0 //analog input A0 on ATmega328 is battery voltage ( /2) 27 | #define solarVoltage_PIN A2 //analog input A2 is solar cell voltage (/ 2) 28 | #define solarCurrent_PIN A6 //analog input A6 is input current ( I=V/Rclprog x 1000 ) 29 | #define batteryChargeCurrent_PIN A7 //analog input A7 is battery charge current ( I=V/Rprog x 1000 ) 30 | #define LTC4067_SUSPEND_PIN 9 //digital output D9 - drive it high to put LTC4067 in SUSPEND mode 31 | 32 | const float VccMin = 1.0 * 3.5; // Minimum expected Vcc level, in Volts. Example for 1 rechargeable lithium-ion. 33 | const float VccMax = 1.0 * 4.2; // Maximum expected Vcc level, in Volts. 34 | 35 | #define CHILD_ID_PRESSURE 0 36 | #define CHILD_ID_TEMP 1 37 | #define CHILD_ID_FORECAST 2 38 | #define BATT_CHILD_ID 10 39 | #define SOLAR_CHILD_ID 11 40 | #define PRESSURE_SENSOR_ANALOG_PIN 0 41 | 42 | // PIN Radio 43 | #define RADIO_CE_PIN 7 // radio chip enable 44 | #define RADIO_SS_PIN 8 // CS SS serial select 45 | 46 | //float lastBattVoltage; 47 | //float lastBattCurrent; 48 | //float lastSolarVoltage; 49 | //float lastSolarCurrent; 50 | int lastBattPct = 0; 51 | 52 | float VccReference = 3.3 ; // voltage reference for measurement, definitive init in setup 53 | unsigned long SLEEP_TIME = 10; // Sleep time between reads (in seconds) 54 | 55 | Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085); // Digital Pressure Sensor 56 | MySensor gw(RADIO_CE_PIN, RADIO_SS_PIN); 57 | MyMessage batteryVoltageMsg(BATT_CHILD_ID, V_VOLTAGE); // Battery voltage (V) 58 | MyMessage batteryCurrentMsg(BATT_CHILD_ID, V_CURRENT); // Battery current (A) 59 | MyMessage solarVoltageMsg(SOLAR_CHILD_ID, V_VOLTAGE); // Solar voltage (V) 60 | MyMessage solarCurrentMsg(SOLAR_CHILD_ID, V_CURRENT); 61 | MyMessage pressureMsg(CHILD_ID_PRESSURE, V_PRESSURE); 62 | MyMessage tempMsg(CHILD_ID_TEMP, V_TEMP); 63 | MyMessage forecastMsg(CHILD_ID_FORECAST, V_FORECAST); 64 | 65 | float lastPressure = -1; 66 | float lastTemp = -1; 67 | int lastForecast = -1; 68 | char *weather[] = {"stable", "sunny", "cloudy", "unstable", "thunderstorm", "unknown"}; 69 | int minutes; 70 | float pressureSamples[180]; 71 | int minuteCount = 0; 72 | bool firstRound = true; 73 | float pressureAvg[7]; 74 | float dP_dt; 75 | boolean metric; 76 | 77 | void setup() { 78 | gw.begin(); 79 | 80 | // Send the sketch version information to the gateway and Controller 81 | gw.sendSketchInfo("Pressure Sensor", "1.0"); 82 | 83 | gw.present(BATT_CHILD_ID, S_POWER); // Battery parameters 84 | gw.present(SOLAR_CHILD_ID, S_POWER); // Solar parameters 85 | 86 | // use VCC (3.3V) reference 87 | analogReference(DEFAULT); // default external reference = 3.3v for Ceech board 88 | VccReference = 3.323 ; // measured Vcc input (on board LDO) 89 | pinMode(LTC4067_SUSPEND_PIN, OUTPUT); // suspend of Lion charger set 90 | digitalWrite(LTC4067_SUSPEND_PIN, LOW); 91 | 92 | if (!bmp.begin(BMP085_MODE_ULTRAHIGHRES)) { 93 | Serial.println("Could not find a valid BMP085 sensor, check wiring!"); 94 | while (1) { } 95 | } 96 | 97 | // Register sensors to gw (they will be created as child devices) 98 | 99 | gw.present(CHILD_ID_PRESSURE, S_BARO); 100 | gw.present(CHILD_ID_TEMP, S_TEMP); 101 | metric = gw.getConfig().isMetric; 102 | 103 | } 104 | 105 | 106 | void loop() { 107 | 108 | sendVoltage(); 109 | 110 | sensors_event_t event; 111 | bmp.getEvent(&event); 112 | 113 | float pressure_raw; 114 | float pressure; 115 | float temperature; 116 | float altitude; 117 | 118 | if (event.pressure) { 119 | pressure_raw = event.pressure; 120 | //BMP085 pressure from adafruit gives you absolute pressure, that is NOT the barometric pressure the meteo forecast shows. You have to recalculate it by the above equation into the p0. 121 | pressure = pressure_raw/pow((1.0 - ( myAltitude/44330.0 )), 5.255); 122 | 123 | bmp.getTemperature(&temperature); 124 | 125 | // altitude = bmp.pressureToAltitude(seaLevelPressure, 126 | // event.pressure 127 | // ); 128 | 129 | } 130 | 131 | if (!metric) { 132 | // Convert to fahrenheit 133 | temperature = temperature * 9.0 / 5.0 + 32.0; 134 | } 135 | 136 | int forecast = sample(pressure); 137 | 138 | Serial.print("Temperature = "); 139 | Serial.print(temperature); 140 | Serial.println(metric ? " *C" : " *F"); 141 | Serial.print("Pressure = "); 142 | Serial.print(pressure); 143 | Serial.println(" hPa"); 144 | Serial.println(weather[forecast]); 145 | //Serial.print("Altitude = "); 146 | //Serial.print(altitude); 147 | 148 | 149 | if (ceil(temperature) != ceil(lastTemp)) { 150 | gw.send(tempMsg.set(temperature, 1)); 151 | lastTemp = temperature; 152 | } 153 | 154 | if (ceil(pressure) != ceil(lastPressure)) { 155 | gw.send(pressureMsg.set(pressure, 1)); 156 | lastPressure = pressure; 157 | } 158 | 159 | if (forecast != lastForecast) { 160 | gw.send(forecastMsg.set(weather[forecast])); 161 | lastForecast = forecast; 162 | } 163 | 164 | /* 165 | DP/Dt explanation 166 | 167 | 0 = "Stable Weather Pattern" 168 | 1 = "Slowly rising Good Weather", "Clear/Sunny " 169 | 2 = "Slowly falling L-Pressure ", "Cloudy/Rain " 170 | 3 = "Quickly rising H-Press", "Not Stable" 171 | 4 = "Quickly falling L-Press", "Thunderstorm" 172 | 5 = "Unknown (More Time needed) 173 | */ 174 | 175 | // Power down the radio. Note that the radio will get powered back up 176 | // on the next write() call. 177 | delay(1000); //delay to allow serial to fully print before sleep 178 | 179 | gw.sleep(SLEEP_TIME * 1000); // sleep to conserve power 180 | } 181 | 182 | int sample(float pressure) { 183 | // Algorithm found here 184 | // http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf 185 | if (minuteCount > 180) 186 | minuteCount = 6; 187 | 188 | pressureSamples[minuteCount] = pressure; 189 | minuteCount++; 190 | 191 | if (minuteCount == 5) { 192 | // Avg pressure in first 5 min, value averaged from 0 to 5 min. 193 | pressureAvg[0] = ((pressureSamples[1] + pressureSamples[2] 194 | + pressureSamples[3] + pressureSamples[4] + pressureSamples[5]) 195 | / 5); 196 | } else if (minuteCount == 35) { 197 | // Avg pressure in 30 min, value averaged from 0 to 5 min. 198 | pressureAvg[1] = ((pressureSamples[30] + pressureSamples[31] 199 | + pressureSamples[32] + pressureSamples[33] 200 | + pressureSamples[34]) / 5); 201 | float change = (pressureAvg[1] - pressureAvg[0]); 202 | if (firstRound) // first time initial 3 hour 203 | dP_dt = ((65.0 / 1023.0) * 2 * change); // note this is for t = 0.5hour 204 | else 205 | dP_dt = (((65.0 / 1023.0) * change) / 1.5); // divide by 1.5 as this is the difference in time from 0 value. 206 | } else if (minuteCount == 60) { 207 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 208 | pressureAvg[2] = ((pressureSamples[55] + pressureSamples[56] 209 | + pressureSamples[57] + pressureSamples[58] 210 | + pressureSamples[59]) / 5); 211 | float change = (pressureAvg[2] - pressureAvg[0]); 212 | if (firstRound) //first time initial 3 hour 213 | dP_dt = ((65.0 / 1023.0) * change); //note this is for t = 1 hour 214 | else 215 | dP_dt = (((65.0 / 1023.0) * change) / 2); //divide by 2 as this is the difference in time from 0 value 216 | } else if (minuteCount == 95) { 217 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 218 | pressureAvg[3] = ((pressureSamples[90] + pressureSamples[91] 219 | + pressureSamples[92] + pressureSamples[93] 220 | + pressureSamples[94]) / 5); 221 | float change = (pressureAvg[3] - pressureAvg[0]); 222 | if (firstRound) // first time initial 3 hour 223 | dP_dt = (((65.0 / 1023.0) * change) / 1.5); // note this is for t = 1.5 hour 224 | else 225 | dP_dt = (((65.0 / 1023.0) * change) / 2.5); // divide by 2.5 as this is the difference in time from 0 value 226 | } else if (minuteCount == 120) { 227 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 228 | pressureAvg[4] = ((pressureSamples[115] + pressureSamples[116] 229 | + pressureSamples[117] + pressureSamples[118] 230 | + pressureSamples[119]) / 5); 231 | float change = (pressureAvg[4] - pressureAvg[0]); 232 | if (firstRound) // first time initial 3 hour 233 | dP_dt = (((65.0 / 1023.0) * change) / 2); // note this is for t = 2 hour 234 | else 235 | dP_dt = (((65.0 / 1023.0) * change) / 3); // divide by 3 as this is the difference in time from 0 value 236 | } else if (minuteCount == 155) { 237 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 238 | pressureAvg[5] = ((pressureSamples[150] + pressureSamples[151] 239 | + pressureSamples[152] + pressureSamples[153] 240 | + pressureSamples[154]) / 5); 241 | float change = (pressureAvg[5] - pressureAvg[0]); 242 | if (firstRound) // first time initial 3 hour 243 | dP_dt = (((65.0 / 1023.0) * change) / 2.5); // note this is for t = 2.5 hour 244 | else 245 | dP_dt = (((65.0 / 1023.0) * change) / 3.5); // divide by 3.5 as this is the difference in time from 0 value 246 | } else if (minuteCount == 180) { 247 | // Avg pressure at end of the hour, value averaged from 0 to 5 min. 248 | pressureAvg[6] = ((pressureSamples[175] + pressureSamples[176] 249 | + pressureSamples[177] + pressureSamples[178] 250 | + pressureSamples[179]) / 5); 251 | float change = (pressureAvg[6] - pressureAvg[0]); 252 | if (firstRound) // first time initial 3 hour 253 | dP_dt = (((65.0 / 1023.0) * change) / 3); // note this is for t = 3 hour 254 | else 255 | dP_dt = (((65.0 / 1023.0) * change) / 4); // divide by 4 as this is the difference in time from 0 value 256 | pressureAvg[0] = pressureAvg[5]; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past. 257 | firstRound = false; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop. 258 | } 259 | 260 | if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval. 261 | return 5; // Unknown, more time needed 262 | else if (dP_dt < (-0.25)) 263 | return 4; // Quickly falling LP, Thunderstorm, not stable 264 | else if (dP_dt > 0.25) 265 | return 3; // Quickly rising HP, not stable weather 266 | else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05))) 267 | return 2; // Slowly falling Low Pressure System, stable rainy weather 268 | else if ((dP_dt > 0.05) && (dP_dt < 0.25)) 269 | return 1; // Slowly rising HP stable good weather 270 | else if ((dP_dt > (-0.05)) && (dP_dt < 0.05)) 271 | return 0; // Stable weather 272 | else 273 | return 5; // Unknown 274 | } 275 | 276 | void sendVoltage(void) 277 | // battery and charging values 278 | { 279 | // get Battery Voltage & charge current 280 | float batteryVoltage = ((float)analogRead(batteryVoltage_PIN) * VccReference / 1024) * 2; // actual voltage is double 281 | Serial.print("Batt: "); 282 | Serial.print(batteryVoltage); 283 | Serial.print("V ; "); 284 | float batteryChargeCurrent = ((float)analogRead(batteryChargeCurrent_PIN) * VccReference / 1024) / 2.5 ; // current(A) = V/Rprog(kohm) 285 | Serial.print(batteryChargeCurrent); 286 | Serial.println("A "); 287 | 288 | 289 | // get Solar Voltage & charge current 290 | float solarVoltage = ((float)analogRead(solarVoltage_PIN) / 1024 * VccReference) * 2 ; // actual voltage is double 291 | Serial.print("Solar: "); 292 | Serial.print(solarVoltage); 293 | Serial.print("V ; "); 294 | // get Solar Current 295 | float solarCurrent = ((float)analogRead(solarCurrent_PIN) / 1024 * VccReference) / 2.5; // current(A) = V/Rclprog(kohm) 296 | Serial.print(solarCurrent); 297 | Serial.print(" A; charge: "); 298 | Serial.println(digitalRead(LTC4067_CHRG_PIN) ? "No" : "Yes"); 299 | 300 | // send battery percentage for node 301 | int battPct = 1 ; 302 | if (batteryVoltage > VccMin) { 303 | battPct = 100.0 * (batteryVoltage - VccMin) / (VccMax - VccMin); 304 | } 305 | Serial.print("BattPct: "); 306 | Serial.print(battPct); 307 | Serial.println("% "); 308 | 309 | if (lastBattPct != battPct) { 310 | gw.send(batteryVoltageMsg.set(batteryVoltage, 3)); // Send (V) 311 | gw.send(batteryCurrentMsg.set(batteryChargeCurrent, 6)); // Send (Amps) 312 | gw.send(solarVoltageMsg.set(solarVoltage, 3)); // Send (V) 313 | gw.send(solarCurrentMsg.set(solarCurrent, 6)); // Send (Amps) 314 | gw.sendBatteryLevel(battPct); 315 | lastBattPct = battPct; 316 | } 317 | } 318 | 319 | 320 | -------------------------------------------------------------------------------- /1.3/MQv0.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Vera Arduino MQ135 3 | 4 | connect the sensor as follows : 5 | 6 | A H A >>> 5V 7 | B >>> A0 8 | H >>> GND 9 | B >>> 10K ohm >>> GND 10 | 11 | Contribution: epierre 12 | 13 | License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) 14 | 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define CHILD_ID_AIQ 0 25 | #define MQ2_SENSOR_ANALOG_PIN 0 26 | #define MQ6_SENSOR_ANALOG_PIN 1 27 | #define MQ131_SENSOR_ANALOG_PIN 2 28 | #define TGS2600_SENSOR_ANALOG_PIN 3 29 | /************************Hardware Related Macros************************************/ 30 | #define MQ_SENSOR_ANALOG_PIN (0) //define which analog input channel you are going to use 31 | #define RL_VALUE (5) //define the load resistance on the board, in kilo ohms 32 | #define RO_CLEAN_AIR_FACTOR (9.83) //RO_CLEAR_AIR_FACTOR=(Sensor resistance in clean air)/RO, 33 | /***********************Software Related Macros************************************/ 34 | #define CALIBRATION_SAMPLE_TIMES (50) //define how many samples you are going to take in the calibration phase 35 | #define CALIBRATION_SAMPLE_INTERVAL (500) //define the time interal(in milisecond) between each samples in the 36 | //cablibration phase 37 | #define READ_SAMPLE_INTERVAL (50) //define how many samples you are going to take in normal operation 38 | #define READ_SAMPLE_TIMES (5) //define the time interal(in milisecond) between each samples in 39 | /***********************Software Related Macros************************************/ 40 | #define CALIBARAION_SAMPLE_TIMES (50) //define how many samples you are going to take in the calibration phase 41 | #define CALIBRATION_SAMPLE_INTERVAL (500) //define the time interal(in milisecond) between each samples in the 42 | //cablibration phase 43 | #define READ_SAMPLE_INTERVAL (50) //define how many samples you are going to take in normal operation 44 | #define READ_SAMPLE_TIMES (5) //define the time interal(in milisecond) between each samples in 45 | /**********************Application Related Macros**********************************/ 46 | #define GAS_CL2 (0) 47 | #define GAS_O3 (1) 48 | #define GAS_CO2 (2) 49 | #define GAS_CO (3) 50 | #define GAS_NH4 (4) 51 | #define GAS_CO2H50H (5) 52 | #define GAS_CH3 (6) 53 | #define GAS_CH3_2CO (7) 54 | #define GAS_H2 (8) 55 | #define GAS_C2H5OH (9) 56 | #define GAS_C4H10 (10) 57 | #define GAS_LPG (11) 58 | #define GAS_Smoke (12) 59 | #define GAS_CO_sec (13) 60 | #define GAS_LPG_sec (14) 61 | #define GAS_CH4 (15) 62 | /*****************************Globals***********************************************/ 63 | float CL2Curve[3] = {0.70,0.78,-0.78}; 64 | float O3Curve[3] = {0.69,0.90,-0.93}; 65 | float CO2Curve[3] = {1.0,0.34,-0.34}; 66 | float COCurve[3] = {1.0,0.46,-1.01}; 67 | float CO_secCurve[3] = {2.3,0.72,-0.34}; 68 | float NH4Curve[3] = {1.0,0.20,-0.23}; 69 | float CO2H50HCurve[3] = {1.0,0.29,-0.33}; 70 | float CH3Curve[3] = {1.0,0.20,-0.30}; 71 | float CH3_2COCurve[3] = {1.0,-0.82,0.55}; 72 | float H2Curve[3] = {0,-0.16,-0.34}; 73 | float C2H5OHCurve[3] = {0,-0.16,-0.32}; 74 | float C4H10Curve[3] = {0,-0.15,-0.30}; 75 | float LPGCurve[3] = {2.3,0.21,-0.47}; 76 | float SmokeCurve[3] = {2.3,0.53,-0.44}; 77 | float LPG_secCurve[3] = {3, 0, -0.4}; 78 | float CH4Curve[3] = {3.3, 0, -0.38}; 79 | float Ro = 10; //Ro is initialized to 10 kilo ohms 80 | 81 | 82 | unsigned long SLEEP_TIME = 30; // Sleep time between reads (in seconds) 83 | //VARIABLES 84 | //float Ro = 10000.0; // this has to be tuned 10K Ohm 85 | float Ro0 = 4.340; //5.28 this has to be tuned 10K Ohm 86 | float Ro1 = 1.755; //36.94 this has to be tuned 10K Ohm 87 | float Ro2 = 2.501; //3.61 this has to be tuned 10K Ohm 88 | float Ro3 = 2.511; //0.04 this has to be tuned 10K Ohm 89 | int val = 0; // variable to store the value coming from the sensor 90 | float valAIQ0 =0.0; 91 | float lastAIQ0 =0.0; 92 | float valAIQ1 =0.0; 93 | float lastAIQ1 =0.0; 94 | float valAIQ2 =0.0; 95 | float lastAIQ2 =0.0; 96 | float valAIQ3 =0.0; 97 | float lastAIQ3 =0.0; 98 | 99 | Sleep sleep; 100 | Sensor gw; 101 | 102 | #define CHILD_ID_MQ2 0 103 | #define CHILD_ID_MQ6 1 104 | #define CHILD_ID_MQ131 2 105 | #define CHILD_ID_TGS2600 3 106 | 107 | 108 | void setup() 109 | { 110 | Serial.begin(115200); 111 | 112 | Serial.print("Ro -->\n MQ2:"); 113 | Ro0 = MQCalibration(MQ2_SENSOR_ANALOG_PIN); 114 | Serial.println(Ro0); 115 | Serial.print(" MQ6:"); 116 | Ro1 = MQCalibration(MQ6_SENSOR_ANALOG_PIN); 117 | Serial.println(Ro1); 118 | Serial.print(" MQ131:"); 119 | Ro2 = MQCalibration(MQ131_SENSOR_ANALOG_PIN); 120 | Serial.println(Ro2); 121 | Serial.print(" TGZS2600:"); 122 | Ro3 = MQCalibration(TGS2600_SENSOR_ANALOG_PIN); 123 | Serial.println(Ro3); 124 | 125 | } 126 | 127 | // get CO ppm 128 | float get_CO (float ratio){ 129 | float ppm = 0.0; 130 | ppm = 37143 * pow (ratio, -3.178); 131 | return ppm; 132 | } 133 | 134 | void loop() 135 | { 136 | //MQ2 CO LPG Smoke 137 | Serial.print("MQ2 :"); 138 | Serial.print("LPG :"); 139 | Serial.print(MQGetGasPercentage(MQRead(MQ2_SENSOR_ANALOG_PIN)/Ro0,GAS_LPG) ); 140 | Serial.print( "ppm" ); 141 | Serial.print(" "); 142 | Serial.print("CO :"); 143 | Serial.print(MQGetGasPercentage(MQRead(MQ2_SENSOR_ANALOG_PIN)/Ro0,GAS_CO_sec) ); 144 | Serial.print( "ppm" ); 145 | Serial.print(" "); 146 | Serial.print("SMOKE :"); 147 | Serial.print(MQGetGasPercentage(MQRead(MQ2_SENSOR_ANALOG_PIN)/Ro0,GAS_Smoke) ); 148 | Serial.print( "ppm" ); 149 | Serial.print("\n"); 150 | //MQ6 151 | Serial.print("MQ6 :"); 152 | Serial.print("LPG :"); 153 | Serial.print(MQGetGasPercentage(MQRead(MQ6_SENSOR_ANALOG_PIN)/Ro1,GAS_LPG_sec) ); 154 | Serial.print( "ppm" ); 155 | Serial.print(" "); 156 | Serial.print("CH4 :"); 157 | Serial.print(MQGetGasPercentage(MQRead(MQ6_SENSOR_ANALOG_PIN)/Ro1,GAS_CH4) ); 158 | Serial.print( "ppm" ); 159 | Serial.print("\n"); 160 | //MQ131 CL2 O3 161 | Serial.print("MQ131 :"); 162 | Serial.print("CL2 :"); 163 | Serial.print(MQGetGasPercentage(MQRead(MQ131_SENSOR_ANALOG_PIN)/Ro2,GAS_CL2) ); 164 | Serial.print( "ppm" ); 165 | Serial.print(" "); 166 | Serial.print("O3 :"); 167 | Serial.print(MQGetGasPercentage(MQRead(MQ131_SENSOR_ANALOG_PIN)/Ro2,GAS_O3) ); 168 | Serial.print( "ppm" ); 169 | Serial.print("\n"); 170 | //TGS2600 H2 C2H5OH C4H10 171 | Serial.print("TGS2600:"); 172 | Serial.print("H2 :"); 173 | Serial.print(MQGetGasPercentage(MQRead(TGS2600_SENSOR_ANALOG_PIN)/Ro3,GAS_H2) ); 174 | Serial.print( "ppm" ); 175 | Serial.print(" "); 176 | Serial.print("C2H5OH:"); 177 | Serial.print(MQGetGasPercentage(MQRead(TGS2600_SENSOR_ANALOG_PIN)/Ro3,GAS_C2H5OH) ); 178 | Serial.print( "ppm" ); 179 | Serial.print(" "); 180 | Serial.print("C4H10 :"); 181 | Serial.print(MQGetGasPercentage(MQRead(TGS2600_SENSOR_ANALOG_PIN)/Ro3,GAS_C4H10) ); 182 | Serial.print( "ppm" ); 183 | Serial.print("\n"); 184 | 185 | 186 | // Power down the radio. Note that the radio will get powered back up 187 | // on the next write() call. 188 | delay(10000); //delay to allow serial to fully print before sleep 189 | //sleep.pwrDownMode(); //set sleep mode 190 | //sleep.sleepDelay(SLEEP_TIME * 1000); //sleep for: sleepTime 191 | } 192 | 193 | 194 | 195 | 196 | /****************** MQResistanceCalculation **************************************** 197 | Input: raw_adc - raw value read from adc, which represents the voltage 198 | Output: the calculated sensor resistance 199 | Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage 200 | across the load resistor and its resistance, the resistance of the sensor 201 | could be derived. 202 | ************************************************************************************/ 203 | float MQResistanceCalculation(int raw_adc) 204 | { 205 | return ( ((float)RL_VALUE*(1023-raw_adc)/raw_adc)); 206 | } 207 | 208 | /***************************** MQCalibration **************************************** 209 | Input: mq_pin - analog channel 210 | Output: Ro of the sensor 211 | Remarks: This function assumes that the sensor is in clean air. It use 212 | MQResistanceCalculation to calculates the sensor resistance in clean air 213 | and then divides it with RO_CLEAN_AIR_FACTOR. RO_CLEAN_AIR_FACTOR is about 214 | 10, which differs slightly between different sensors. 215 | ************************************************************************************/ 216 | float MQCalibration(int mq_pin) 217 | { 218 | int i; 219 | float val=0; 220 | 221 | for (i=0;i