/* * NodeManager */ #include "NodeManager.h" /*************************************** Global functions */ // return vcc in V float getVcc() { #ifndef MY_GATEWAY_ESP8266 // Measure Vcc against 1.1V Vref #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)); #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) ADMUX = (_BV(MUX5) | _BV(MUX0)); #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) ADMUX = (_BV(MUX3) | _BV(MUX2)); #else ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)); #endif // Vref settle wait(70); // Do conversion ADCSRA |= _BV(ADSC); while (bit_is_set(ADCSRA, ADSC)) {}; // return Vcc in mV return (float)((1125300UL) / ADC) / 1000; #else return (float)0; #endif } /*************************************** PowerManager */ // set the vcc and ground pin the sensor is connected to void PowerManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) { #if DEBUG == 1 Serial.print(F("PWR G=")); Serial.print(ground_pin); Serial.print(F(" V=")); Serial.println(vcc_pin); #endif // configure the vcc pin as output and initialize to high (power on) _vcc_pin = vcc_pin; pinMode(_vcc_pin, OUTPUT); digitalWrite(_vcc_pin, HIGH); // configure the ground pin as output and initialize to low _ground_pin = ground_pin; pinMode(_ground_pin, OUTPUT); digitalWrite(_ground_pin, LOW); _wait = wait_time; } // return true if power pins have been configured bool PowerManager::isConfigured() { if (_vcc_pin != -1 && _ground_pin != -1) return true; return false; } // turn on the sensor by activating its power pins void PowerManager::powerOn() { if (! isConfigured()) return; #if DEBUG == 1 Serial.print(F("ON P=")); Serial.println(_vcc_pin); #endif // power on the sensor by turning high the vcc pin digitalWrite(_vcc_pin, HIGH); // wait a bit for the device to settle down if (_wait > 0) wait(_wait); } // turn off the sensor void PowerManager::powerOff() { if (! isConfigured()) return; #if DEBUG == 1 Serial.print(F("OFF P=")); Serial.println(_vcc_pin); #endif // power off the sensor by turning low the vcc pin digitalWrite(_vcc_pin, LOW); } /****************************************** Sensors */ /* Sensor class */ // constructor Sensor::Sensor(int child_id, int pin) { _child_id = child_id; _pin = pin; _msg = MyMessage(_child_id, _type); } // setter/getter void Sensor::setPin(int value) { _pin = value; } int Sensor::getPin() { return _pin; } void Sensor::setChildId(int value) { _child_id = value; } int Sensor::getChildId() { return _child_id; } void Sensor::setPresentation(int value) { _presentation = value; } int Sensor::getPresentation() { return _presentation; } void Sensor::setType(int value) { _type = value; _msg.setType(_type); } int Sensor::getType() { return _type; } void Sensor::setDescription(char* value) { _description = value; } void Sensor::setAck(bool value) { _ack = value; } void Sensor::setRetries(int value) { _retries = value; } void Sensor::setSamples(int value) { _samples = value; } void Sensor::setSamplesInterval(int value) { _samples_interval = value; } void Sensor::setTackLastValue(bool value) { _track_last_value = value; } void Sensor::setForceUpdate(int value) { _force_update = value; } void Sensor::setValueType(int value) { _value_type = value; } int Sensor::getValueType() { return _value_type; } void Sensor::setFloatPrecision(int value) { _float_precision = value; } #if POWER_MANAGER == 1 void Sensor::setPowerPins(int ground_pin, int vcc_pin, int wait_time) { _powerManager.setPowerPins(ground_pin, vcc_pin, wait_time); } void Sensor::setAutoPowerPins(bool value) { _auto_power_pins = value; } void Sensor::powerOn() { _powerManager.powerOn(); } void Sensor::powerOff() { _powerManager.powerOff(); } #endif void Sensor::setSleepBetweenSend(int value) { _sleep_between_send = value; } void Sensor::setInterruptPin(int value) { _interrupt_pin = value; } int Sensor::getInterruptPin() { return _interrupt_pin; } int Sensor::getValueInt() { return _last_value_int; } float Sensor::getValueFloat() { return _last_value_float; } char* Sensor::getValueString() { return _last_value_string; } // present the sensor to the gateway and controller void Sensor::presentation() { #if DEBUG == 1 Serial.print(F("PRES I=")); Serial.print(_child_id); Serial.print(F(" T=")); Serial.println(_presentation); #endif present(_child_id, _presentation,_description,_ack); } // call the sensor-specific implementation of before void Sensor::before() { if (_pin == -1) return; onBefore(); } // call the sensor-specific implementation of setup void Sensor::setup() { if (_pin == -1) return; onSetup(); } // call the sensor-specific implementation of loop void Sensor::loop(const MyMessage & message) { if (_pin == -1) return; #if POWER_MANAGER == 1 // turn the sensor on if (_auto_power_pins) powerOn(); #endif // for numeric sensor requiring multiple samples, keep track of the total float total = 0; // keep track of the number of cycles since the last update if (_force_update > 0) _cycles++; // collect multiple samples if needed for (int i = 0; i < _samples; i++) { // call the sensor-specific implementation of the main task which will store the result in the _value variable if (message.sender == 0 && message.sensor == 0 && message.getCommand() == 0 && message.type == 0) { // empty message, we'be been called from loop() onLoop(); } else { // we've been called from receive(), pass the message along onReceive(message); } // for integers and floats, keep track of the total if (_value_type == TYPE_INTEGER) total += (float)_value_int; else if (_value_type == TYPE_FLOAT) total += _value_float; // wait between samples if (_samples_interval > 0) wait(_samples_interval); } // process the result and send a response back. if (_value_type == TYPE_INTEGER && total > -1) { // if the value is an integer, calculate the average value of the samples int avg = (int) (total / _samples); // if track last value is disabled or if enabled and the current value is different then the old value, send it back if (! _track_last_value || (_track_last_value && avg != _last_value_int) || (_track_last_value && _force_update > 0 && _cycles > _force_update)) { _cycles = 0; _last_value_int = avg; _send(_msg.set(avg)); } } // process a float value else if (_value_type == TYPE_FLOAT && total > -1) { // calculate the average value of the samples float avg = total / _samples; // if track last value is disabled or if enabled and the current value is different then the old value, send it back if (! _track_last_value || (_track_last_value && avg != _last_value_float) || (_track_last_value && _cycles >= _force_update)) { _cycles = 0; _last_value_float = avg; _send(_msg.set(avg, _float_precision)); } } // process a string value else if (_value_type == TYPE_STRING) { // if track last value is disabled or if enabled and the current value is different then the old value, send it back if (! _track_last_value || (_track_last_value && strcmp(_value_string, _last_value_string) != 0) || (_track_last_value && _cycles >= _force_update)) { _cycles = 0; _last_value_string = _value_string; _send(_msg.set(_value_string)); } } // turn the sensor off #if POWER_MANAGER == 1 if (_auto_power_pins) powerOff(); #endif } // receive a message from the radio network void Sensor::receive(const MyMessage &message) { // return if not for this sensor if (message.sensor != _child_id || message.type != _type) return; // a request would make the sensor executing its main task passing along the message loop(message); } // send a message to the network void Sensor::_send(MyMessage & message) { // send the message, multiple times if requested for (int i = 0; i < _retries; i++) { // if configured, sleep beetween each send if (_sleep_between_send > 0) sleep(_sleep_between_send); #if DEBUG == 1 Serial.print(F("SEND D=")); Serial.print(message.destination); Serial.print(F(" I=")); Serial.print(message.sensor); Serial.print(F(" C=")); Serial.print(message.getCommand()); Serial.print(F(" T=")); Serial.print(message.type); Serial.print(F(" S=")); Serial.print(message.getString()); Serial.print(F(" I=")); Serial.print(message.getInt()); Serial.print(F(" F=")); Serial.println(message.getFloat()); #endif send(message,_ack); } } /* SensorAnalogInput */ // contructor SensorAnalogInput::SensorAnalogInput(int child_id, int pin): Sensor(child_id, pin) { } // setter/getter void SensorAnalogInput::setReference(int value) { _reference = value; } void SensorAnalogInput::setReverse(bool value) { _reverse = value; } void SensorAnalogInput::setOutputPercentage(bool value) { _output_percentage = value; } void SensorAnalogInput::setRangeMin(int value) { _range_min = value; } void SensorAnalogInput::setRangeMax(int value) { _range_max = value; } // what to do during before void SensorAnalogInput::onBefore() { // prepare the pin for input pinMode(_pin, INPUT); } // what to do during setup void SensorAnalogInput::onSetup() { } // what to do during loop void SensorAnalogInput::onLoop() { // read the input int adc = _getAnalogRead(); // calculate the percentage int percentage = 0; if (_output_percentage) percentage = _getPercentage(adc); #if DEBUG == 1 Serial.print(F("A-IN I=")); Serial.print(_child_id); Serial.print(F(" V=")); Serial.print(adc); Serial.print(F(" %=")); Serial.println(percentage); #endif // store the result _value_int = _output_percentage ? percentage : adc; } // what to do during loop void SensorAnalogInput::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } // read the analog input int SensorAnalogInput::_getAnalogRead() { #ifndef MY_GATEWAY_ESP8266 // set the reference if (_reference != -1) { analogReference(_reference); wait(100); } #endif // read and return the value int value = analogRead(_pin); if (_reverse) value = _range_max - value; return value; } // return a percentage from an analog value int SensorAnalogInput::_getPercentage(int adc) { float value = (float)adc; // restore the original value if (_reverse) value = 1024 - value; // scale the percentage based on the range provided float percentage = ((value - _range_min) / (_range_max - _range_min)) * 100; if (_reverse) percentage = 100 - percentage; if (percentage > 100) percentage = 100; if (percentage < 0) percentage = 0; return (int)percentage; } /* SensorLDR */ // contructor SensorLDR::SensorLDR(int child_id, int pin): SensorAnalogInput(child_id, pin) { // set presentation and type and reverse (0: no light, 100: max light) setPresentation(S_LIGHT_LEVEL); setType(V_LIGHT_LEVEL); setReverse(true); } /* SensorThermistor */ // contructor SensorThermistor::SensorThermistor(int child_id, int pin): Sensor(child_id, pin) { // set presentation, type and value type setPresentation(S_TEMP); setType(V_TEMP); setValueType(TYPE_FLOAT); } // setter/getter void SensorThermistor::setNominalResistor(long value) { _nominal_resistor = value; } void SensorThermistor::setNominalTemperature(int value) { _nominal_temperature = value; } void SensorThermistor::setBCoefficient(int value) { _b_coefficient = value; } void SensorThermistor::setSeriesResistor(long value) { _series_resistor = value; } void SensorThermistor::setOffset(float value) { _offset = value; } // what to do during before void SensorThermistor::onBefore() { // set the pin as input pinMode(_pin, INPUT); } // what to do during setup void SensorThermistor::onSetup() { } // what to do during loop void SensorThermistor::onLoop() { // read the voltage across the thermistor float adc = analogRead(_pin); // calculate the temperature float reading = (1023 / adc) - 1; reading = _series_resistor / reading; float temperature; temperature = reading / _nominal_resistor; // (R/Ro) temperature = log(temperature); // ln(R/Ro) temperature /= _b_coefficient; // 1/B * ln(R/Ro) temperature += 1.0 / (_nominal_temperature + 273.15); // + (1/To) temperature = 1.0 / temperature; // Invert temperature -= 273.15; // convert to C if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; #if DEBUG == 1 Serial.print(F("THER I=")); Serial.print(_child_id); Serial.print(F(" V=")); Serial.print(adc); Serial.print(F(" T=")); Serial.print(temperature); Serial.print(F(" M=")); Serial.println(getControllerConfig().isMetric); #endif // store the value _value_float = temperature; } // what to do as the main task when receiving a message void SensorThermistor::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } /* SensorML8511 */ // contructor SensorML8511::SensorML8511(int child_id, int pin): Sensor(child_id, pin) { // set presentation, type and value type setPresentation(S_UV); setType(V_UV); setValueType(TYPE_FLOAT); } // what to do during before void SensorML8511::onBefore() { // set the pin as input pinMode(_pin, INPUT); } // what to do during setup void SensorML8511::onSetup() { } // what to do during loop void SensorML8511::onLoop() { // read the voltage int uvLevel = analogRead(_pin); int refLevel = getVcc()*1024/3.3; //Use the 3.3V power pin as a reference to get a very accurate output value from sensor float outputVoltage = 3.3 / refLevel * uvLevel; //Convert the voltage to a UV intensity level float uvIntensity = _mapfloat(outputVoltage, 0.99, 2.8, 0.0, 15.0); #if DEBUG == 1 Serial.print(F("UV I=")); Serial.print(_child_id); Serial.print(F(" V=")); Serial.print(outputVoltage); Serial.print(F(" I=")); Serial.println(uvIntensity); #endif // store the value _value_float = uvIntensity; } // what to do as the main task when receiving a message void SensorML8511::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } // The Arduino Map function but for floats float SensorML8511::_mapfloat(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } /* SensorACS712 */ // contructor SensorACS712::SensorACS712(int child_id, int pin): Sensor(child_id, pin) { // set presentation, type and value type setPresentation(S_MULTIMETER); setType(V_CURRENT); setValueType(TYPE_FLOAT); } // setter/getter void SensorACS712::setmVPerAmp(int value) { _mv_per_amp = value; } void SensorACS712::setOffset(int value) { _ACS_offset = value; } // what to do during before void SensorACS712::onBefore() { // set the pin as input pinMode(_pin, INPUT); } // what to do during setup void SensorACS712::onSetup() { } // what to do during loop void SensorACS712::onLoop() { int value = analogRead(_pin); // convert the analog read in mV double voltage = (value / 1024.0) * 5000; // convert voltage in amps _value_float = ((voltage - _ACS_offset) / _mv_per_amp); #if DEBUG == 1 Serial.print(F("ACS I=")); Serial.print(_child_id); Serial.print(F(" A=")); Serial.println(_value_float); #endif } // what to do as the main task when receiving a message void SensorACS712::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } /* SensorRainGauge */ // contructor SensorRainGauge::SensorRainGauge(int child_id, int pin): Sensor(child_id, pin) { // set presentation, type and value type setPresentation(S_RAIN); setType(V_RAIN); setValueType(TYPE_FLOAT); } // initialize static variables long SensorRainGauge::_last_tip = 0; long SensorRainGauge::_count = 0; // setter/getter void SensorRainGauge::setReportInterval(int value) { _report_interval = value; } void SensorRainGauge::setSingleTip(float value) { _single_tip = value; } // what to do during before void SensorRainGauge::onBefore() { // set the pin as input and enabled pull up pinMode(_pin, INPUT_PULLUP); // attach to the pin's interrupt and execute the routine on falling attachInterrupt(digitalPinToInterrupt(_pin), _onTipped, FALLING); } // what to do during setup void SensorRainGauge::onSetup() { } // what to do when when receiving an interrupt void SensorRainGauge::_onTipped() { long now = millis(); // on tipping, two consecutive interrupts are received, ignore the second one if ( (now - _last_tip > 100) || (now < _last_tip) ){ // increase the counter _count++; #if DEBUG == 1 Serial.println(F("RAIN+")); #endif } _last_tip = now; } // what to do during loop void SensorRainGauge::onLoop() { // avoid reporting the same value multiple times _value_float = -1; long now = millis(); // time elapsed since the last report long elapsed = now - _last_report; // minimum time interval between reports long min_interval = ((long)_report_interval*1000)*60; // time to report or millis() reset if ( (elapsed > min_interval) || (now < _last_report)) { // report the total amount of rain for the last period _value_float = _count*_single_tip; #if DEBUG == 1 Serial.print(F("RAIN I=")); Serial.print(_child_id); Serial.print(F(" T=")); Serial.println(_value_float); #endif // reset the counters _count = 0; _last_report = now; } } // what to do as the main task when receiving a message void SensorRainGauge::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) { // report the total amount of rain for the last period _value_float = _count*_single_tip; } } /* * SensorMQ */ SensorMQ::SensorMQ(int child_id, int pin): Sensor(child_id,pin) { setPresentation(S_AIR_QUALITY); setType(V_LEVEL); } //setter/getter void SensorMQ::setRlValue(float value) { _rl_value = value; } void SensorMQ::setRoValue(float value) { _ro = value; } void SensorMQ::setCleanAirFactor(float value) { _ro_clean_air_factor = value; } void SensorMQ::setCalibrationSampleTimes(int value) { _calibration_sample_times = value; } void SensorMQ::setCalibrationSampleInterval(int value){ _calibration_sample_interval = value; } void SensorMQ::setReadSampleTimes(int value) { _read_sample_times = value; } void SensorMQ::setReadSampleInterval(int value) { _read_sample_interval = value; } void SensorMQ::setLPGCurve(float *value) { _LPGCurve[0] = value[0]; _LPGCurve[2] = value[1]; _LPGCurve[2] = value[2]; } void SensorMQ::setCOCurve(float *value) { _COCurve[0] = value[0]; _COCurve[2] = value[1]; _COCurve[2] = value[2]; } void SensorMQ::setSmokeCurve(float *value) { _SmokeCurve[0] = value[0]; _SmokeCurve[2] = value[1]; _SmokeCurve[2] = value[2]; } // what to do during before void SensorMQ::onBefore() { // prepare the pin for input pinMode(_pin, INPUT); } // what to do during setup void SensorMQ::onSetup() { _ro = _MQCalibration(); } // what to do during loop void SensorMQ::onLoop() { if (_pin == -1) return; // calculate rs/ro float mq = _MQRead()/_ro; // calculate the ppm float lpg = _MQGetGasPercentage(mq,_gas_lpg); float co = _MQGetGasPercentage(mq,_gas_co); float smoke = _MQGetGasPercentage(mq,_gas_smoke); // assign to the value the requested gas uint16_t value; if (_target_gas == _gas_lpg) value = lpg; if (_target_gas == _gas_co) value = co; if (_target_gas == _gas_smoke) value = smoke; #if DEBUG == 1 Serial.print(F("MQ I=")); Serial.print(_child_id); Serial.print(F(" V=")); Serial.print(value); Serial.print(F(" LPG=")); Serial.print(lpg); Serial.print(F(" CO=")); Serial.print(co); Serial.print(F(" SMOKE=")); Serial.println(smoke); #endif // store the value _value_int = (int16_t)ceil(value); } // what to do as the main task when receiving a message void SensorMQ::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } // returns the calculated sensor resistance float SensorMQ::_MQResistanceCalculation(int raw_adc) { return ( ((float)_rl_value*(1023-raw_adc)/raw_adc)); } // This function assumes that the sensor is in clean air float SensorMQ::_MQCalibration() { int i; float val=0; //take multiple samples for (i=0; i< _calibration_sample_times; i++) { val += _MQResistanceCalculation(analogRead(_pin)); wait(_calibration_sample_interval); } //calculate the average value val = val/_calibration_sample_times; //divided by RO_CLEAN_AIR_FACTOR yields the Ro val = val/_ro_clean_air_factor; //according to the chart in the datasheet return val; } // This function use MQResistanceCalculation to caculate the sensor resistenc (Rs). float SensorMQ::_MQRead() { int i; float rs=0; for (i=0; i<_read_sample_times; i++) { rs += _MQResistanceCalculation(analogRead(_pin)); wait(_read_sample_interval); } rs = rs/_read_sample_times; return rs; } // This function passes different curves to the MQGetPercentage function which calculates the ppm (parts per million) of the target gas. int SensorMQ::_MQGetGasPercentage(float rs_ro_ratio, int gas_id) { if ( gas_id == _gas_lpg ) { return _MQGetPercentage(rs_ro_ratio,_LPGCurve); } else if ( gas_id == _gas_co) { return _MQGetPercentage(rs_ro_ratio,_COCurve); } else if ( gas_id == _gas_smoke) { return _MQGetPercentage(rs_ro_ratio,_SmokeCurve); } return 0; } // returns ppm of the target gas int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) { return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0]))); } /* SensorDigitalInput */ // contructor SensorDigitalInput::SensorDigitalInput(int child_id, int pin): Sensor(child_id, pin) { } // what to do during before void SensorDigitalInput::onBefore() { // set the pin for input pinMode(_pin, INPUT); } // what to do during setup void SensorDigitalInput::onSetup() { } // what to do during loop void SensorDigitalInput::onLoop() { // read the value int value = digitalRead(_pin); #if DEBUG == 1 Serial.print(F("D-IN I=")); Serial.print(_child_id); Serial.print(F(" P=")); Serial.print(_pin); Serial.print(F(" V=")); Serial.println(value); #endif // store the value _value_int = value; } // what to do as the main task when receiving a message void SensorDigitalInput::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } /* SensorDigitalOutput */ // contructor SensorDigitalOutput::SensorDigitalOutput(int child_id, int pin): Sensor(child_id, pin) { } // what to do during before void SensorDigitalOutput::onBefore() { // set the pin as output and initialize it accordingly pinMode(_pin, OUTPUT); _state = _initial_value == LOW ? LOW : HIGH; digitalWrite(_pin, _state); // the initial value is now the current value _value_int = _initial_value; } // what to do during setup void SensorDigitalOutput::onSetup() { } // setter/getter void SensorDigitalOutput::setInitialValue(int value) { _initial_value = value; } void SensorDigitalOutput::setPulseWidth(int value) { _pulse_width = value; } void SensorDigitalOutput::setOnValue(int value) { _on_value = value; } void SensorDigitalOutput::setLegacyMode(bool value) { _legacy_mode = value; } // main task void SensorDigitalOutput::onLoop() { // do nothing on loop } // what to do as the main task when receiving a message void SensorDigitalOutput::onReceive(const MyMessage & message) { // by default handle a SET message but when legacy mode is set when a REQ message is expected instead if ( (message.getCommand() == C_SET && ! _legacy_mode) || (message.getCommand() == C_REQ && _legacy_mode)) { // retrieve from the message the value to set int value = message.getInt(); if (value != 0 && value != 1) return; #if DEBUG == 1 Serial.print(F("DOUT I=")); Serial.print(_child_id); Serial.print(F(" P=")); Serial.print(_pin); Serial.print(F(" V=")); Serial.print(value); Serial.print(F(" P=")); Serial.println(_pulse_width); #endif // reverse the value if needed int value_to_write = value; if (_on_value == LOW) { if (value == HIGH) value_to_write = LOW; if (value == LOW) value_to_write = HIGH; } // set the value digitalWrite(_pin, value_to_write); if (_pulse_width > 0) { // if this is a pulse output, restore the value to the original value after the pulse wait(_pulse_width); digitalWrite(_pin, value_to_write == 0 ? HIGH: LOW); } // store the current value so it will be sent to the controller _state = value; _value_int = value; } if (message.getCommand() == C_REQ && ! _legacy_mode) { // return the current status _value_int = _state; } } /* SensorRelay */ // contructor SensorRelay::SensorRelay(int child_id, int pin): SensorDigitalOutput(child_id, pin) { // set presentation and type setPresentation(S_BINARY); setType(V_STATUS); } // define what to do during loop void SensorRelay::onLoop() { // set the value to -1 so to avoid reporting to the gateway during loop _value_int = -1; } /* SensorLatchingRelay */ // contructor SensorLatchingRelay::SensorLatchingRelay(int child_id, int pin): SensorRelay(child_id, pin) { // like a sensor with a default pulse set setPulseWidth(50); } /* SensorDHT */ #if MODULE_DHT == 1 // contructor SensorDHT::SensorDHT(int child_id, int pin, DHT* dht, int sensor_type, int dht_type): Sensor(child_id, pin) { // store the dht object _dht = dht; _sensor_type = sensor_type; _dht_type = dht_type; if (_sensor_type == SensorDHT::TEMPERATURE) { // temperature sensor setPresentation(S_TEMP); setType(V_TEMP); setValueType(TYPE_FLOAT); } else if (_sensor_type == SensorDHT::HUMIDITY) { // humidity sensor setPresentation(S_HUM); setType(V_HUM); setValueType(TYPE_FLOAT); } } // what to do during before void SensorDHT::onBefore() { // initialize the dht library _dht->begin(); } // what to do during setup void SensorDHT::onSetup() { } // what to do during loop void SensorDHT::onLoop() { // temperature sensor if (_sensor_type == SensorDHT::TEMPERATURE) { // read the temperature float temperature = _dht->readTemperature(); // convert it if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; #if DEBUG == 1 Serial.print(F("DHT I=")); Serial.print(_child_id); Serial.print(F(" T=")); Serial.println(temperature); #endif // store the value if (! isnan(temperature)) _value_float = temperature; } // humidity sensor else if (_sensor_type == SensorDHT::HUMIDITY) { // read humidity float humidity = _dht->readHumidity(); if (isnan(humidity)) return; #if DEBUG == 1 Serial.print(F("DHT I=")); Serial.print(_child_id); Serial.print(F(" H=")); Serial.println(humidity); #endif // store the value if (! isnan(humidity)) _value_float = humidity; } } // what to do as the main task when receiving a message void SensorDHT::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } #endif /* SensorSHT21 */ #if MODULE_SHT21 == 1 // contructor SensorSHT21::SensorSHT21(int child_id, int sensor_type): Sensor(child_id,A2) { // store the sensor type (0: temperature, 1: humidity) _sensor_type = sensor_type; if (_sensor_type == SensorSHT21::TEMPERATURE) { // temperature sensor setPresentation(S_TEMP); setType(V_TEMP); setValueType(TYPE_FLOAT); } else if (_sensor_type == SensorSHT21::HUMIDITY) { // humidity sensor setPresentation(S_HUM); setType(V_HUM); setValueType(TYPE_FLOAT); } } // what to do during before void SensorSHT21::onBefore() { // initialize the library Wire.begin(); } // what to do during setup void SensorSHT21::onSetup() { } // what to do during loop void SensorSHT21::onLoop() { // temperature sensor if (_sensor_type == SensorSHT21::TEMPERATURE) { // read the temperature float temperature = SHT2x.GetTemperature(); // convert it if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; #if DEBUG == 1 Serial.print(F("SHT I=")); Serial.print(_child_id); Serial.print(F(" T=")); Serial.println(temperature); #endif // store the value if (! isnan(temperature)) _value_float = temperature; } // Humidity Sensor else if (_sensor_type == SensorSHT21::HUMIDITY) { // read humidity float humidity = SHT2x.GetHumidity(); if (isnan(humidity)) return; #if DEBUG == 1 Serial.print(F("SHT I=")); Serial.print(_child_id); Serial.print(F(" H=")); Serial.println(humidity); #endif // store the value if (! isnan(humidity)) _value_float = humidity; } } // what to do as the main task when receiving a message void SensorSHT21::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } #endif /* * SensorHTU21D */ #if MODULE_SHT21 == 1 // constructor SensorHTU21D::SensorHTU21D(int child_id, int pin): SensorSHT21(child_id, pin) { } #endif /* * SensorSwitch */ SensorSwitch::SensorSwitch(int child_id, int pin): Sensor(child_id,pin) { setType(V_TRIPPED); } // setter/getter void SensorSwitch::setMode(int value) { _mode = value; } int SensorSwitch::getMode() { return _mode; } void SensorSwitch::setDebounce(int value) { _debounce = value; } void SensorSwitch::setTriggerTime(int value) { _trigger_time = value; } void SensorSwitch::setInitial(int value) { _initial = value; } int SensorSwitch::getInitial() { return _initial; } // what to do during before void SensorSwitch::onBefore() { // initialize the value if (_mode == RISING) _value_int = LOW; else if (_mode == FALLING) _value_int = HIGH; } // what to do during setup void SensorSwitch::onSetup() { } // what to do during loop void SensorSwitch::onLoop() { // wait to ensure the the input is not floating if (_debounce > 0) wait(_debounce); // read the value of the pin int value = digitalRead(_pin); // process the value if ( (_mode == RISING && value == HIGH ) || (_mode == FALLING && value == LOW) || (_mode == CHANGE) ) { #if DEBUG == 1 Serial.print(F("SWITCH I=")); Serial.print(_child_id); Serial.print(F(" P=")); Serial.print(_pin); Serial.print(F(" V=")); Serial.println(value); #endif _value_int = value; // allow the signal to be restored to its normal value if (_trigger_time > 0) wait(_trigger_time); } else { // invalid _value_int = -1; } } // what to do as the main task when receiving a message void SensorSwitch::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } /* * SensorDoor */ SensorDoor::SensorDoor(int child_id, int pin): SensorSwitch(child_id,pin) { setPresentation(S_DOOR); } /* * SensorMotion */ SensorMotion::SensorMotion(int child_id, int pin): SensorSwitch(child_id,pin) { setPresentation(S_MOTION); // capture only when it triggers setMode(RISING); // set initial value to LOW setInitial(LOW); } /* SensorDs18b20 */ #if MODULE_DS18B20 == 1 // contructor SensorDs18b20::SensorDs18b20(int child_id, int pin, DallasTemperature* sensors, int index): Sensor(child_id, pin) { setPresentation(S_TEMP); setType(V_TEMP); setValueType(TYPE_FLOAT); _index = index; _sensors = sensors; // retrieve and store the address from the index _sensors->getAddress(_device_address, index); } // what to do during before void SensorDs18b20::onBefore() { } // what to do during setup void SensorDs18b20::onSetup() { } // what to do during loop void SensorDs18b20::onLoop() { // do not wait for conversion, will sleep manually during it if (_sleep_during_conversion) _sensors->setWaitForConversion(false); // request the temperature _sensors->requestTemperatures(); if (_sleep_during_conversion) { // calculate conversion time and sleep int16_t conversion_time = _sensors->millisToWaitForConversion(_sensors->getResolution()); sleep(conversion_time); } // read the temperature float temperature = _sensors->getTempCByIndex(_index); // convert it if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; #if DEBUG == 1 Serial.print(F("DS18B20 I=")); Serial.print(_child_id); Serial.print(F(" T=")); Serial.println(temperature); #endif // store the value _value_float = temperature; } // what to do as the main task when receiving a message void SensorDs18b20::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } // function to print a device address DeviceAddress* SensorDs18b20::getDeviceAddress() { return &_device_address; } // returns the sensor's resolution in bits int SensorDs18b20::getResolution() { return _sensors->getResolution(_device_address); } // set the sensor's resolution in bits void SensorDs18b20::setResolution(int value) { _sensors->setResolution(_device_address, value); } // sleep while DS18B20 calculates temperature void SensorDs18b20::setSleepDuringConversion(bool value) { _sleep_during_conversion = value; } #endif /* SensorBH1750 */ #if MODULE_BH1750 == 1 // contructor SensorBH1750::SensorBH1750(int child_id): Sensor(child_id,A4) { setPresentation(S_LIGHT_LEVEL); setType(V_LEVEL); _lightSensor = new BH1750(); } // what to do during before void SensorBH1750::onBefore() { _lightSensor->begin(); } // what to do during setup void SensorBH1750::onSetup() { } // what to do during loop void SensorBH1750::onLoop() { // request the light level _value_int = _lightSensor->readLightLevel(); #if DEBUG == 1 Serial.print(F("BH1 I=")); Serial.print(_child_id); Serial.print(F(" L=")); Serial.println(_value_int); #endif } // what to do as the main task when receiving a message void SensorBH1750::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } #endif /* SensorMLX90614 */ #if MODULE_MLX90614 == 1 // contructor SensorMLX90614::SensorMLX90614(int child_id, Adafruit_MLX90614* mlx, int sensor_type): Sensor(child_id,A4) { _sensor_type = sensor_type; _mlx = mlx; // set presentation and type setPresentation(S_TEMP); setType(V_TEMP); setValueType(TYPE_FLOAT); } // what to do during before void SensorMLX90614::onBefore() { // initialize the library _mlx->begin(); } // what to do during setup void SensorMLX90614::onSetup() { } // what to do during loop void SensorMLX90614::onLoop() { float temperature = _sensor_type == SensorMLX90614::TEMPERATURE_OBJECT ? _mlx->readAmbientTempC() : _mlx->readObjectTempC(); // convert it if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; #if DEBUG == 1 Serial.print(F("MLX I=")); Serial.print(_child_id); Serial.print(F(" T=")); Serial.println(temperature); #endif if (! isnan(temperature)) _value_float = temperature; } // what to do as the main task when receiving a message void SensorMLX90614::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } #endif /* SensorBosch */ #if MODULE_BME280 == 1 || MODULE_BMP085 == 1 // contructor SensorBosch::SensorBosch(int child_id, int sensor_type): Sensor(child_id,A4) { _sensor_type = sensor_type; if (_sensor_type == SensorBosch::TEMPERATURE) { // temperature sensor setPresentation(S_TEMP); setType(V_TEMP); setValueType(TYPE_FLOAT); } else if (_sensor_type == SensorBosch::HUMIDITY) { // humidity sensor setPresentation(S_HUM); setType(V_HUM); setValueType(TYPE_FLOAT); } else if (_sensor_type == SensorBosch::PRESSURE) { // pressure sensor setPresentation(S_BARO); setType(V_PRESSURE); setValueType(TYPE_FLOAT); } else if (_sensor_type == SensorBosch::FORECAST) { // pressure sensor setPresentation(S_BARO); setType(V_FORECAST); setValueType(TYPE_STRING); } } // setter/getter void SensorBosch::setForecastSamplesCount(int value) { _forecast_samples_count = value; } // what to do during before void SensorBosch::onBefore() { // initialize the forecast samples array _forecast_samples = new float[_forecast_samples_count]; } // what to do during setup void SensorBosch::onSetup() { } // what to do during loop void SensorBosch::onLoop() { } // what to do as the main task when receiving a message void SensorBosch::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } void SensorBosch::_forecast(float pressure) { if (isnan(pressure)) return; // Calculate the average of the last n minutes. int index = _minute_count % _forecast_samples_count; _forecast_samples[index] = pressure; _minute_count++; if (_minute_count > 185) _minute_count = 6; if (_minute_count == 5) _pressure_avg = _getLastPressureSamplesAverage(); else if (_minute_count == 35) { float last_pressure_avg = _getLastPressureSamplesAverage(); float change = (last_pressure_avg - _pressure_avg) * 0.1; // first time initial 3 hour if (_first_round) _dP_dt = change * 2; // note this is for t = 0.5hour else _dP_dt = change / 1.5; // divide by 1.5 as this is the difference in time from 0 value. } else if (_minute_count == 65) { float last_pressure_avg = _getLastPressureSamplesAverage(); float change = (last_pressure_avg - _pressure_avg) * 0.1; //first time initial 3 hour if (_first_round) _dP_dt = change; //note this is for t = 1 hour else _dP_dt = change / 2; //divide by 2 as this is the difference in time from 0 value } else if (_minute_count == 95) { float last_pressure_avg = _getLastPressureSamplesAverage(); float change = (last_pressure_avg - _pressure_avg) * 0.1; // first time initial 3 hour if (_first_round)_dP_dt = change / 1.5; // note this is for t = 1.5 hour else _dP_dt = change / 2.5; // divide by 2.5 as this is the difference in time from 0 value } else if (_minute_count == 125) { float last_pressure_avg = _getLastPressureSamplesAverage(); // store for later use. _pressure_avg2 = last_pressure_avg; float change = (last_pressure_avg - _pressure_avg) * 0.1; if (_first_round) _dP_dt = change / 2; // note this is for t = 2 hour else _dP_dt = change / 3; // divide by 3 as this is the difference in time from 0 value } else if (_minute_count == 155) { float last_pressure_avg = _getLastPressureSamplesAverage(); float change = (last_pressure_avg - _pressure_avg) * 0.1; if (_first_round) _dP_dt = change / 2.5; // note this is for t = 2.5 hour else _dP_dt = change / 3.5; // divide by 3.5 as this is the difference in time from 0 value } else if (_minute_count == 185) { float last_pressure_avg = _getLastPressureSamplesAverage(); float change = (last_pressure_avg - _pressure_avg) * 0.1; if (_first_round) _dP_dt = change / 3; // note this is for t = 3 hour else _dP_dt = change / 4; // divide by 4 as this is the difference in time from 0 value } // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past. _pressure_avg = _pressure_avg2; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop. _first_round = false; // calculate the forecast (STABLE = 0, SUNNY = 1, CLOUDY = 2, UNSTABLE = 3, THUNDERSTORM = 4, UNKNOWN = 5) int forecast = 5; //if time is less than 35 min on the first 3 hour interval. if (_minute_count < 35 && _first_round) forecast = 5; else if (_dP_dt < (-0.25)) forecast = 5; else if (_dP_dt > 0.25) forecast = 4; else if ((_dP_dt > (-0.25)) && (_dP_dt < (-0.05))) forecast = 2; else if ((_dP_dt > 0.05) && (_dP_dt < 0.25)) forecast = 1; else if ((_dP_dt >(-0.05)) && (_dP_dt < 0.05)) forecast = 0; else forecast = 5; _value_string = _weather[forecast]; #if DEBUG == 1 Serial.print(F("BMP I=")); Serial.print(_child_id); Serial.print(F(" M=")); Serial.print(_minute_count); Serial.print(F(" dP=")); Serial.print(_dP_dt); Serial.print(F(" F=")); Serial.println(_value_string); #endif } // returns the average of the latest pressure samples float SensorBosch::_getLastPressureSamplesAverage() { float avg = 0; for (int i = 0; i < _forecast_samples_count; i++) avg += _forecast_samples[i]; avg /= _forecast_samples_count; return avg; } // search for a given chip on i2c bus uint8_t SensorBosch::GetI2CAddress(uint8_t chip_id) { uint8_t addresses[] = {0x77, 0x76}; uint8_t register_address = 0xD0; for (int i = 0; i <= sizeof(addresses); i++) { uint8_t i2c_address = addresses[i]; uint8_t value; Wire.beginTransmission((uint8_t)i2c_address); Wire.write((uint8_t)register_address); Wire.endTransmission(); Wire.requestFrom((uint8_t)i2c_address, (byte)1); value = Wire.read(); if (value == chip_id) { #if DEBUG == 1 Serial.print(F("I2C=")); Serial.println(i2c_address); #endif return i2c_address; } } return addresses[0]; } #endif /* * SensorBME280 */ #if MODULE_BME280 == 1 SensorBME280::SensorBME280(int child_id, Adafruit_BME280* bme, int sensor_type): SensorBosch(child_id,sensor_type) { _bme = bme; } void SensorBME280::onLoop() { // temperature sensor if (_sensor_type == SensorBME280::TEMPERATURE) { // read the temperature float temperature = _bme->readTemperature(); // convert it if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; #if DEBUG == 1 Serial.print(F("BME I=")); Serial.print(_child_id); Serial.print(F(" T=")); Serial.println(temperature); #endif if (isnan(temperature)) return; // store the value _value_float = temperature; } // Humidity Sensor else if (_sensor_type == SensorBME280::HUMIDITY) { // read humidity float humidity = _bme->readHumidity(); #if DEBUG == 1 Serial.print(F("BME I=")); Serial.print(_child_id); Serial.print(F(" H=")); Serial.println(humidity); #endif if (isnan(humidity)) return; // store the value _value_float = humidity; } // Pressure Sensor else if (_sensor_type == SensorBME280::PRESSURE) { // read pressure float pressure = _bme->readPressure() / 100.0F; if (isnan(pressure)) return; #if DEBUG == 1 Serial.print(F("BME I=")); Serial.print(_child_id); Serial.print(F(" P=")); Serial.println(pressure); #endif if (isnan(pressure)) return; // store the value _value_float = pressure; } // Forecast Sensor else if (_sensor_type == SensorBME280::FORECAST) { float pressure = _bme->readPressure() / 100.0F; _forecast(pressure); } } #endif /* SensorBMP085 */ #if MODULE_BMP085 == 1 // contructor SensorBMP085::SensorBMP085(int child_id, Adafruit_BMP085* bmp, int sensor_type): SensorBosch(child_id,sensor_type) { _bmp = bmp; } // what to do during loop void SensorBMP085::onLoop() { // temperature sensor if (_sensor_type == SensorBMP085::TEMPERATURE) { // read the temperature float temperature = _bmp->readTemperature(); // convert it if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; #if DEBUG == 1 Serial.print(F("BMP I=")); Serial.print(_child_id); Serial.print(F(" T=")); Serial.println(temperature); #endif if (isnan(temperature)) return; // store the value _value_float = temperature; } // Pressure Sensor else if (_sensor_type == SensorBMP085::PRESSURE) { // read pressure float pressure = _bmp->readPressure() / 100.0F; #if DEBUG == 1 Serial.print(F("BMP I=")); Serial.print(_child_id); Serial.print(F(" P=")); Serial.println(pressure); #endif if (isnan(pressure)) return; // store the value _value_float = pressure; } // Forecast Sensor else if (_sensor_type == SensorBMP085::FORECAST) { float pressure = _bmp->readPressure() / 100.0F; _forecast(pressure); } } #endif /* SensorSonoff */ #if MODULE_SONOFF == 1 // contructor SensorSonoff::SensorSonoff(int child_id): Sensor(child_id,1) { setPresentation(S_BINARY); setType(V_STATUS); } // setter/getter void SensorSonoff::setButtonPin(int value) { _button_pin = value; } void SensorSonoff::setRelayPin(int value) { _relay_pin = value; } void SensorSonoff::setLedPin(int value) { _led_pin = value; } // what to do during before void SensorSonoff::onBefore() { } // what to do during setup void SensorSonoff::onSetup() { // Setup the button pinMode(_button_pin, INPUT_PULLUP); // After setting up the button, setup debouncer _debouncer.attach(_button_pin); _debouncer.interval(5); // Make sure relays and LED are off when starting up digitalWrite(_relay_pin, _relay_off); digitalWrite(_led_pin, _led_off); // Then set relay pins in output mode pinMode(_relay_pin, OUTPUT); pinMode(_led_pin, OUTPUT); _blink(); } // what to do during loop void SensorSonoff::onLoop() { // set the value to -1 so to avoid reporting to the gateway during loop _value_int = -1; _debouncer.update(); // Get the update value from the button int value = _debouncer.read(); if (value != _old_value && value == 0) { // button pressed, toggle the state _toggle(); } _old_value = value; } // what to do as the main task when receiving a message void SensorSonoff::onReceive(const MyMessage & message) { if (message.getCommand() == C_SET) { // retrieve from the message the value to set int value = message.getInt(); if (value != 0 && value != 1 || value == _state) return; // toggle the state _toggle(); } if (message.getCommand() == C_REQ) { // return the current state _value_int = _state; } } // toggle the state void SensorSonoff::_toggle() { // toggle the state _state = _state ? false : true; // Change relay state digitalWrite(_relay_pin, _state? _relay_on: _relay_off); // Change LED state digitalWrite(_led_pin, _state? _led_on: _led_off); #if DEBUG == 1 Serial.print(F("SONOFF I=")); Serial.print(_child_id); Serial.print(F(" V=")); Serial.println(_state); #endif _value_int = _state; } // blink the led void SensorSonoff::_blink() { digitalWrite(_led_pin, digitalRead(_led_pin) ? _led_on : _led_off); wait(200); digitalWrite(_led_pin, digitalRead(_led_pin) ? _led_on : _led_off); wait(200); digitalWrite(_led_pin, digitalRead(_led_pin) ? _led_on : _led_off); wait(200); digitalWrite(_led_pin, digitalRead(_led_pin) ? _led_on : _led_off); } #endif /* SensorHCSR04 */ #if MODULE_HCSR04 == 1 // contructor SensorHCSR04::SensorHCSR04(int child_id, int pin): Sensor(child_id, pin) { // set presentation and type setPresentation(S_DISTANCE); setType(V_DISTANCE); _trigger_pin = pin; _echo_pin = pin; } // what to do during before void SensorHCSR04::onBefore() { // initialize the library _sonar = new NewPing(_trigger_pin,_echo_pin,_max_distance); } // setter/getter void SensorHCSR04::setTriggerPin(int value) { _trigger_pin = value; } void SensorHCSR04::setEchoPin(int value) { _echo_pin = value; } void SensorHCSR04::setMaxDistance(int value) { _max_distance = value; } // what to do during setup void SensorHCSR04::onSetup() { } // what to do during loop void SensorHCSR04::onLoop() { int distance = getControllerConfig().isMetric ? _sonar->ping_cm() : _sonar->ping_in(); #if DEBUG == 1 Serial.print(F("HC I=")); Serial.print(_child_id); Serial.print(F(" D=")); Serial.println(distance); #endif _value_int = distance; } // what to do as the main task when receiving a message void SensorHCSR04::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } #endif /* SensorMCP9808 */ #if MODULE_MCP9808 == 1 // contructor SensorMCP9808::SensorMCP9808(int child_id, Adafruit_MCP9808* mcp): Sensor(child_id,A2) { _mcp = mcp; setPresentation(S_TEMP); setType(V_TEMP); setValueType(TYPE_FLOAT); } // what to do during before void SensorMCP9808::onBefore() { } // what to do during setup void SensorMCP9808::onSetup() { } // what to do during loop void SensorMCP9808::onLoop() { float temperature = _mcp->readTempC(); // convert it if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; #if DEBUG == 1 Serial.print(F("MCP I=")); Serial.print(_child_id); Serial.print(F(" T=")); Serial.println(temperature); #endif // store the value if (! isnan(temperature)) _value_float = temperature; } // what to do as the main task when receiving a message void SensorMCP9808::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } #endif /******************************************* NodeManager */ // initialize the node manager NodeManager::NodeManager() { // setup the service message container _msg = MyMessage(CONFIGURATION_CHILD_ID, V_CUSTOM); } // setter/getter void NodeManager::setRetries(int value) { _retries = value; } #if BATTERY_MANAGER == 1 void NodeManager::setBatteryMin(float value) { _battery_min = value; } void NodeManager::setBatteryMax(float value) { _battery_max = value; } void NodeManager::setBatteryReportCycles(int value) { _battery_report_cycles = value; } void NodeManager::setBatteryInternalVcc(bool value) { _battery_internal_vcc = value; } void NodeManager::setBatteryPin(int value) { _battery_pin = value; } void NodeManager::setBatteryVoltsPerBit(float value) { _battery_volts_per_bit = value; } void NodeManager::setBatteryReportWithInterrupt(bool value) { _battery_report_with_interrupt = value; } #endif void NodeManager::setSleepMode(int value) { _sleep_mode = value; } void NodeManager::setMode(int value) { setSleepMode(value); } void NodeManager::setSleepTime(int value) { _sleep_time = value; } void NodeManager::setSleepUnit(int value) { _sleep_unit = value; } void NodeManager::setSleep(int value1, int value2, int value3) { _sleep_mode = value1; _sleep_time = value2; _sleep_unit = value3; } void NodeManager::setSleepInterruptPin(int value) { _sleep_interrupt_pin = value; } void NodeManager::setInterrupt(int pin, int mode, int pull) { if (pin == INTERRUPT_PIN_1) { _interrupt_1_mode = mode; _interrupt_1_pull = pull; } if (pin == INTERRUPT_PIN_2) { _interrupt_2_mode = mode; _interrupt_2_pull = pull; } } #if POWER_MANAGER == 1 void NodeManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) { _powerManager.setPowerPins(ground_pin, vcc_pin, wait_time); } void NodeManager::setAutoPowerPins(bool value) { _auto_power_pins = value; } void NodeManager::powerOn() { _powerManager.powerOn(); } void NodeManager::powerOff() { _powerManager.powerOff(); } #endif void NodeManager::setSleepBetweenSend(int value) { _sleep_between_send = value; } void NodeManager::setAck(bool value) { _ack = value; } // register a sensor to this manager int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { // get a child_id if not provided by the user if (child_id < 0) child_id = _getAvailableChildId(); // based on the given sensor type instantiate the appropriate class if (sensor_type == 0) return -1; #if MODULE_ANALOG_INPUT == 1 else if (sensor_type == SENSOR_ANALOG_INPUT) return registerSensor(new SensorAnalogInput(child_id, pin)); else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(child_id, pin)); else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(child_id, pin)); else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(child_id, pin)); else if (sensor_type == SENSOR_ML8511) return registerSensor(new SensorML8511(child_id, pin)); else if (sensor_type == SENSOR_ACS712) return registerSensor(new SensorACS712(child_id, pin)); else if (sensor_type == SENSOR_RAIN_GAUGE) return registerSensor(new SensorRainGauge(child_id, pin)); #endif #if MODULE_DIGITAL_INPUT == 1 else if (sensor_type == SENSOR_DIGITAL_INPUT) return registerSensor(new SensorDigitalInput(child_id, pin)); #endif #if MODULE_DIGITAL_OUTPUT == 1 else if (sensor_type == SENSOR_DIGITAL_OUTPUT) return registerSensor(new SensorDigitalOutput(child_id, pin)); else if (sensor_type == SENSOR_RELAY) return registerSensor(new SensorRelay(child_id, pin)); else if (sensor_type == SENSOR_LATCHING_RELAY) return registerSensor(new SensorLatchingRelay(child_id, pin)); #endif #if MODULE_DHT == 1 else if (sensor_type == SENSOR_DHT11 || sensor_type == SENSOR_DHT22) { int dht_type = sensor_type == SENSOR_DHT11 ? DHT11 : DHT22; DHT* dht = new DHT(pin,dht_type); // register temperature sensor registerSensor(new SensorDHT(child_id,pin,dht,SensorDHT::TEMPERATURE,dht_type)); // register humidity sensor child_id = _getAvailableChildId(); return registerSensor(new SensorDHT(child_id,pin,dht,SensorDHT::HUMIDITY,dht_type)); } #endif #if MODULE_SHT21 == 1 else if (sensor_type == SENSOR_SHT21) { // register temperature sensor registerSensor(new SensorSHT21(child_id,SensorSHT21::TEMPERATURE)); // register humidity sensor child_id = _getAvailableChildId(); return registerSensor(new SensorSHT21(child_id,SensorSHT21::HUMIDITY)); } else if (sensor_type == SENSOR_HTU21D) { // register temperature sensor registerSensor(new SensorHTU21D(child_id,SensorHTU21D::TEMPERATURE)); // register humidity sensor child_id = _getAvailableChildId(); return registerSensor(new SensorHTU21D(child_id,SensorHTU21D::HUMIDITY)); } #endif #if MODULE_SWITCH == 1 else if (sensor_type == SENSOR_SWITCH || sensor_type == SENSOR_DOOR || sensor_type == SENSOR_MOTION) { // ensure an interrupt pin is provided if (pin != INTERRUPT_PIN_1 && pin != INTERRUPT_PIN_2) return -1; // register the sensor int index = 0; if (sensor_type == SENSOR_SWITCH) index = registerSensor(new SensorSwitch(child_id, pin)); else if (sensor_type == SENSOR_DOOR) index = registerSensor(new SensorDoor(child_id, pin)); else if (sensor_type == SENSOR_MOTION) index = registerSensor(new SensorMotion(child_id, pin)); // set an interrupt on the pin and set the initial value SensorSwitch* sensor = (SensorSwitch*)getSensor(index); sensor->setInterruptPin(pin); setInterrupt(pin,sensor->getMode(),sensor->getInitial()); return index; } #endif #if MODULE_DS18B20 == 1 else if (sensor_type == SENSOR_DS18B20) { // initialize the library OneWire* oneWire = new OneWire(pin); DallasTemperature* sensors = new DallasTemperature(oneWire); // initialize the sensors sensors->begin(); int index = 0; // register a new child for each sensor on the bus for(int i = 0; i < sensors->getDeviceCount(); i++) { if (i > 0) child_id = _getAvailableChildId(); index = registerSensor(new SensorDs18b20(child_id,pin,sensors,i)); } return index; } #endif #if MODULE_BH1750 == 1 else if (sensor_type == SENSOR_BH1750) { return registerSensor(new SensorBH1750(child_id)); } #endif #if MODULE_MLX90614 == 1 else if (sensor_type == SENSOR_MLX90614) { Adafruit_MLX90614* mlx = new Adafruit_MLX90614(); // register ambient temperature sensor registerSensor(new SensorMLX90614(child_id,mlx,SensorMLX90614::TEMPERATURE_AMBIENT)); // register object temperature sensor child_id = _getAvailableChildId(); return registerSensor(new SensorMLX90614(child_id,mlx,SensorMLX90614::TEMPERATURE_OBJECT)); } #endif #if MODULE_BME280 == 1 else if (sensor_type == SENSOR_BME280) { Adafruit_BME280* bme = new Adafruit_BME280(); if (! bme->begin(SensorBosch::GetI2CAddress(0x60))) { #if DEBUG == 1 Serial.println(F("NO BME")); #endif return -1; } // register temperature sensor registerSensor(new SensorBME280(child_id,bme,SensorBME280::TEMPERATURE)); child_id = _getAvailableChildId(); // register humidity sensor registerSensor(new SensorBME280(child_id,bme,SensorBME280::HUMIDITY)); // register pressure sensor child_id = _getAvailableChildId(); registerSensor(new SensorBME280(child_id,bme,SensorBME280::PRESSURE)); // register forecast sensor child_id = _getAvailableChildId(); return registerSensor(new SensorBME280(child_id,bme,SensorBME280::FORECAST)); } #endif #if MODULE_SONOFF == 1 else if (sensor_type == SENSOR_SONOFF) { return registerSensor(new SensorSonoff(child_id)); } #endif #if MODULE_BMP085 == 1 else if (sensor_type == SENSOR_BMP085) { Adafruit_BMP085* bmp = new Adafruit_BMP085(); if (! bmp->begin(SensorBosch::GetI2CAddress(0x55))) { #if DEBUG == 1 Serial.println(F("NO BMP")); #endif return -1; } // register temperature sensor registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::TEMPERATURE)); // register pressure sensor child_id = _getAvailableChildId(); registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::PRESSURE)); // register forecast sensor child_id = _getAvailableChildId(); return registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::FORECAST)); } #endif #if MODULE_HCSR04 == 1 else if (sensor_type == SENSOR_HCSR04) { return registerSensor(new SensorHCSR04(child_id, pin)); } #endif #if MODULE_MCP9808 == 1 else if (sensor_type == SENSOR_MCP9808) { Adafruit_MCP9808 * mcp = new Adafruit_MCP9808(); if (! mcp->begin()) { #if DEBUG == 1 Serial.println(F("NO MCP")); #endif return -1; } // register temperature sensor registerSensor(new SensorMCP9808(child_id,mcp)); } #endif else { #if DEBUG == 1 Serial.print(F("INVALID ")); Serial.println(sensor_type); #endif return -1; }; } // attach a built-in or custom sensor to this manager int NodeManager::registerSensor(Sensor* sensor) { #if DEBUG == 1 Serial.print(F("REG I=")); Serial.print(sensor->getChildId()); Serial.print(F(" P=")); Serial.print(sensor->getPin()); Serial.print(F(" P=")); Serial.print(sensor->getPresentation()); Serial.print(F(" T=")); Serial.println(sensor->getType()); #endif #if POWER_MANAGER == 1 // set auto power pin sensor->setAutoPowerPins(_auto_power_pins); #endif // add the sensor to the array of registered sensors _sensors[sensor->getChildId()] = sensor; // return the child_id return sensor->getChildId(); } // un-register a sensor to this manager void NodeManager::unRegisterSensor(int sensor_index) { // unlink the pointer to this sensor _sensors[sensor_index] == 0; } // return a sensor given its index Sensor* NodeManager::get(int child_id) { // return a pointer to the sensor from the given child_id return _sensors[child_id]; } Sensor* NodeManager::getSensor(int child_id) { return get(child_id); } // assign a different child id to a sensor' bool NodeManager::renameSensor(int old_child_id, int new_child_id) { // ensure the old id exists and the new is available if (_sensors[old_child_id] == 0 || _sensors[new_child_id] != 0) return false; // assign the sensor to new id _sensors[new_child_id] = _sensors[old_child_id]; // set the new child id _sensors[new_child_id]->setChildId(new_child_id); // free up the old id _sensors[old_child_id] = 0; return true; } // setup NodeManager void NodeManager::before() { #if DEBUG == 1 Serial.print(F("NodeManager v")); Serial.println(VERSION); #endif // setup the sleep interrupt pin if (_sleep_interrupt_pin > -1) { // set the interrupt when the pin is connected to ground setInterrupt(_sleep_interrupt_pin,FALLING,HIGH); } // setup the interrupt pins if (_interrupt_1_mode != MODE_NOT_DEFINED) { pinMode(INTERRUPT_PIN_1,INPUT); if (_interrupt_1_pull > -1) digitalWrite(INTERRUPT_PIN_1,_interrupt_1_pull); } if (_interrupt_2_mode != MODE_NOT_DEFINED) { pinMode(INTERRUPT_PIN_2, INPUT); if (_interrupt_2_pull > -1) digitalWrite(INTERRUPT_PIN_2,_interrupt_2_pull); } #if DEBUG == 1 Serial.print(F("INT1 M=")); Serial.println(_interrupt_1_mode); Serial.print(F("INT2 M=")); Serial.println(_interrupt_2_mode); #endif #if REMOTE_CONFIGURATION == 1 && PERSIST == 1 // restore sleep configuration from eeprom if (loadState(EEPROM_SLEEP_SAVED) == 1) { // sleep settings found in the eeprom, restore them _sleep_mode = loadState(EEPROM_SLEEP_MODE); _sleep_time = loadState(EEPROM_SLEEP_TIME_MINOR); int major = loadState(EEPROM_SLEEP_TIME_MAJOR); if (major == 1) _sleep_time = _sleep_time + 250; else if (major == 2) _sleep_time = _sleep_time + 250 * 2; else if (major == 3) _sleep_time = _sleep_time + 250 * 3; _sleep_unit = loadState(EEPROM_SLEEP_UNIT); #if DEBUG == 1 Serial.print(F("LOADSLP M=")); Serial.print(_sleep_mode); Serial.print(F(" T=")); Serial.print(_sleep_time); Serial.print(F(" U=")); Serial.println(_sleep_unit); #endif } #endif #if BATTERY_MANAGER == 1 && !defined(MY_GATEWAY_ESP8266) // set analogReference to internal if measuring the battery through a pin if (! _battery_internal_vcc && _battery_pin > -1) analogReference(INTERNAL); #endif // setup individual sensors for (int i = 0; i < MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; // call each sensor's setup() _sensors[i]->before(); } } // present NodeManager and its sensors void NodeManager::presentation() { #if DEBUG == 1 Serial.println(F("RADIO OK")); #endif // Send the sketch version information to the gateway and Controller if (_sleep_between_send > 0) sleep(_sleep_between_send); sendSketchInfo(SKETCH_NAME,SKETCH_VERSION); // present the service as a custom sensor to the controller _present(CONFIGURATION_CHILD_ID, S_CUSTOM); #if BATTERY_MANAGER == 1 && BATTERY_SENSOR == 1 // present the battery service _present(BATTERY_CHILD_ID, S_MULTIMETER); // report battery level _process("BATTERY"); #endif // present each sensor for (int i = 0; i < MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; // call each sensor's presentation() if (_sleep_between_send > 0) sleep(_sleep_between_send); _sensors[i]->presentation(); } #if DEBUG == 1 Serial.println(F("READY")); Serial.println(""); #endif } // setup NodeManager void NodeManager::setup() { #if DEBUG == 1 Serial.print(F("MY I=")); Serial.print(getNodeId()); Serial.print(F(" M=")); Serial.println(getControllerConfig().isMetric); #endif #if SERVICE_MESSAGES == 1 _send(_msg.set("STARTED")); #endif // run setup for all the registered sensors for (int i = 0; i < MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; // call each sensor's setup() _sensors[i]->setup(); } } // run the main function for all the register sensors void NodeManager::loop() { MyMessage empty; // if in idle mode, do nothing if (_sleep_mode == IDLE) return; // if sleep time is not set, do nothing if ((_sleep_mode == SLEEP || _sleep_mode == WAIT) && _sleep_time == 0) return; #if POWER_MANAGER == 1 // turn on the pin powering all the sensors if (_auto_power_pins) powerOn(); #endif // run loop for all the registered sensors for (int i = 0; i < MAX_SENSORS; i++) { // skip not configured sensors if (_sensors[i] == 0) continue; // if waking up from an interrupt skip all the sensor without that interrupt configured if (_last_interrupt_pin != -1 && _sensors[i]->getInterruptPin() != _last_interrupt_pin) continue; // call each sensor's loop() _sensors[i]->loop(empty); } #if POWER_MANAGER == 1 // turn off the pin powering all the sensors if (_auto_power_pins) powerOff(); #endif // continue/start sleeping as requested if (_sleep_mode == SLEEP || _sleep_mode == WAIT) _sleep(); } // dispacth inbound messages void NodeManager::receive(const MyMessage &message) { #if DEBUG == 1 Serial.print(F("RECV S=")); Serial.print(message.sender); Serial.print(F(" I=")); Serial.print(message.sensor); Serial.print(F(" C=")); Serial.print(message.getCommand()); Serial.print(F(" T=")); Serial.print(message.type); Serial.print(F(" P=")); Serial.println(message.getString()); #endif // process incoming service messages if (message.sensor == CONFIGURATION_CHILD_ID && message.getCommand() == C_REQ && message.type == V_CUSTOM) { _process(message.getString()); } // dispatch the message to the registered sensor else if (_sensors[message.sensor] != 0) { #if POWER_MANAGER == 1 // turn on the pin powering all the sensors if (_auto_power_pins) powerOn(); #endif // call the sensor's receive() _sensors[message.sensor]->receive(message); #if POWER_MANAGER == 1 // turn off the pin powering all the sensors if (_auto_power_pins) powerOff(); #endif } } // request and return the current timestamp from the controller long NodeManager::getTimestamp() { int retries = 3; _timestamp = -1; while (_timestamp == -1 && retries > 0) { #if DEBUG == 1 Serial.println(F("TIME")); #endif // request the time to the controller requestTime(); // keep asking every 1 second wait(1000); retries--; } return _timestamp; } // receive the time from the controller and save it void NodeManager::receiveTime(unsigned long ts) { _timestamp = ts; #if DEBUG == 1 Serial.print(F("TIME T=")); Serial.print(_timestamp); #endif } // send a message to the network void NodeManager::_send(MyMessage & message) { // send the message, multiple times if requested for (int i = 0; i < _retries; i++) { // if configured, sleep beetween each send if (_sleep_between_send > 0) sleep(_sleep_between_send); #if DEBUG == 1 Serial.print(F("SEND D=")); Serial.print(message.destination); Serial.print(F(" I=")); Serial.print(message.sensor); Serial.print(F(" C=")); Serial.print(message.getCommand()); Serial.print(F(" T=")); Serial.print(message.type); Serial.print(F(" S=")); Serial.print(message.getString()); Serial.print(F(" I=")); Serial.print(message.getInt()); Serial.print(F(" F=")); Serial.println(message.getFloat()); #endif send(message,_ack); } } // process a service message void NodeManager::_process(const char * message) { // HELLO: hello request if (strcmp(message, "HELLO") == 0) { _send(_msg.set(message)); } #if BATTERY_MANAGER == 1 // BATTERY: return the battery level else if (strcmp(message, "BATTERY") == 0) { // measure the board vcc float volt = 0; if (_battery_internal_vcc || _battery_pin == -1) volt = getVcc(); else volt = analogRead(_battery_pin) * _battery_volts_per_bit; // calculate the percentage int percentage = ((volt - _battery_min) / (_battery_max - _battery_min)) * 100; if (percentage > 100) percentage = 100; if (percentage < 0) percentage = 0; #if DEBUG == 1 Serial.print(F("BATT V=")); Serial.print(volt); Serial.print(F(" P=")); Serial.println(percentage); #endif #if BATTERY_SENSOR == 1 // report battery voltage MyMessage battery_msg(BATTERY_CHILD_ID, V_VOLTAGE); _send(battery_msg.set(volt, 2)); #endif // report battery level percentage sendBatteryLevel(percentage,_ack); } #endif #ifndef MY_GATEWAY_ESP8266 // REBOOT: reboot the board else if (strcmp(message, "REBOOT") == 0) { #if DEBUG == 1 Serial.println(F("REBOOT")); #endif // set the reboot pin connected to RST to low so to reboot the board _send(_msg.set(message)); // Software reboot with watchdog timer. Enter Watchdog Configuration mode: WDTCSR |= (1< -1) { // woke up by an interrupt int pin_number = -1; int interrupt_mode = -1; // map the interrupt to the pin if (digitalPinToInterrupt(INTERRUPT_PIN_1) == interrupt) { pin_number = INTERRUPT_PIN_1; interrupt_mode = _interrupt_1_mode; } if (digitalPinToInterrupt(INTERRUPT_PIN_2) == interrupt) { pin_number = INTERRUPT_PIN_2; interrupt_mode = _interrupt_2_mode; } _last_interrupt_pin = pin_number; #if DEBUG == 1 Serial.print(F("WAKE P=")); Serial.print(pin_number); Serial.print(F(", M=")); Serial.println(interrupt_mode); #endif // when waking up from an interrupt on the wakup pin, stop sleeping if (_sleep_interrupt_pin == pin_number) _sleep_mode = IDLE; } } // coming out of sleep #if DEBUG == 1 Serial.println(F("AWAKE")); #endif #if SERVICE_MESSAGES == 1 // notify the controller I am awake _send(_msg.set("AWAKE")); #endif #if BATTERY_MANAGER == 1 // keep track of the number of sleeping cycles (ignoring if woke up by an interrupt) if (interrupt == -1 || _battery_report_with_interrupt) _cycles++; // battery has to be reported after the configured number of sleep cycles if (_battery_report_cycles == _cycles) { // time to report the battery level again _process("BATTERY"); _cycles = 0; } #endif } // present the service void NodeManager::_present(int child_id, int type) { #if DEBUG == 1 Serial.print(F("PRES I=")); Serial.print(child_id); Serial.print(F(", T=")); Serial.println(type); #endif if (_sleep_between_send > 0) sleep(_sleep_between_send); present(child_id,type,"",_ack); } // return the next available child_id int NodeManager::_getAvailableChildId() { for (int i = 1; i < MAX_SENSORS; i++) { if (i == CONFIGURATION_CHILD_ID) continue; if (i == BATTERY_CHILD_ID) continue; // empty place, return it if (_sensors[i] == 0) return i; } } // guess the initial value of a digital output based on the configured interrupt mode int NodeManager::_getInterruptInitialValue(int mode) { if (mode == RISING) return LOW; if (mode == FALLING) return HIGH; return -1; }