diff --git a/sonoff/i18n.h b/sonoff/i18n.h index ce984f1e4227..2114b4dc1ba4 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -126,6 +126,7 @@ #define D_JSON_WRONG "Wrong" #define D_JSON_YESTERDAY "Yesterday" #define D_JSON_ZERO_POINT_CALIBRATION "Zero Point Calibration" +#define D_JSON_DISTANCE "Distance" #define D_RSLT_ENERGY "ENERGY" #define D_RSLT_INFO "INFO" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index d5555b094470..0f5515ab16a2 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -157,6 +157,7 @@ #define D_VOLTAGE "Voltage" #define D_WARMLIGHT "Warm" #define D_WEB_SERVER "Web Server" +#define D_DISTANCE "Distance" // sonoff.ino #define D_WARNING_MINIMAL_VERSION "WARNING This version does not support persistent settings" @@ -429,6 +430,8 @@ #define D_SENSOR_SDS0X1 "SDS0X1" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Trig" +#define D_SENSOR_SR04_ECHO "SR04 Echo" // Units #define D_UNIT_AMPERE "A" @@ -438,6 +441,7 @@ #define D_UNIT_LUX "lx" #define D_UNIT_MICROGRAM_PER_CUBIC_METER "ug/m3" #define D_UNIT_MICROMETER "um" +#define D_UNIT_CENTIMETER "cm" #define D_UNIT_MICROSECOND "us" #define D_UNIT_MILLIAMPERE "mA" #define D_UNIT_MILLISECOND "ms" diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 4455519d063d..e097a05bfe1a 100644 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -74,6 +74,7 @@ void WifiWpsStatusCallback(wps_cb_status status); #endif // #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow #define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) +#define USE_SR04 // Add support for ultrasonic SR04x sensors (+xxk code) #endif // USE_ALL_SENSORS ===================== #if defined(USE_DS18x20) || defined(USE_DS18x20_LEGACY) @@ -147,6 +148,9 @@ void WifiWpsStatusCallback(wps_cb_status status); #ifdef DEBUG_THEO #undef DEBUG_THEO // Disable debug code #endif +#ifdef USE_SR04 +#undef USE_SR04 // Disable SR04 support +#endif #endif // BE_MINIMAL ========================== #ifndef SWITCH_MODE diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index cbb197abb23e..a2cbf133943d 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -91,6 +91,8 @@ enum UserSelectablePins { GPIO_SDS0X1, // Nova Fitness SDS011 Serial interface GPIO_SBR_TX, // Serial Bridge Serial interface GPIO_SBR_RX, // Serial Bridge Serial interface + GPIO_SR04_TRIG, // SR04 Trigger pin + GPIO_SR04_ECHO, // SR04 Echo pin GPIO_SENSOR_END }; // Programmer selectable GPIO functionality offset by user selectable GPIOs @@ -133,7 +135,8 @@ const char kSensorNames[] PROGMEM = D_SENSOR_SAIR_TX "|" D_SENSOR_SAIR_RX "|" D_SENSOR_SPI_CS "|" D_SENSOR_SPI_DC "|" D_SENSOR_BACKLIGHT "|" D_SENSOR_PMS5003 "|" D_SENSOR_SDS0X1 "|" - D_SENSOR_SBR_TX "|" D_SENSOR_SBR_RX; + D_SENSOR_SBR_TX "|" D_SENSOR_SBR_RX "|" + D_SENSOR_SR04_TRIG "|" D_SENSOR_SR04_ECHO; /********************************************************************************************/ diff --git a/sonoff/user_config.h b/sonoff/user_config.h index cf1d29985eb5..5663e5f80665 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -281,6 +281,8 @@ #define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) +#define USE_SR04 // Add support for SR04 ultrasonic devices (+xk code) + /*********************************************************************************************\ * Select all sensors - overrides above undefines!! \*********************************************************************************************/ diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index 20ddb0b0ced2..849f2bdf8de4 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -653,6 +653,14 @@ boolean GetUsedInModule(byte val, uint8_t *arr) return true; } #endif +#ifndef USE_SR04 + if (GPIO_SR04_TRIG == val) { + return true; + } + if (GPIO_SR04_ECHO == val) { + return true; + } +#endif #ifndef USE_WS2812 if (GPIO_WS2812 == val) { return true; diff --git a/sonoff/xsns_91_sr04.ino b/sonoff/xsns_91_sr04.ino new file mode 100644 index 000000000000..8d6c3fa25270 --- /dev/null +++ b/sonoff/xsns_91_sr04.ino @@ -0,0 +1,167 @@ +/* + xsns_20_sr04.ino - SR04 ultrasonic sensor support for Sonoff-Tasmota + + Copyright (C) 2018 Nuno Ferreira and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_SR04 +/*********************************************************************************************\ + * HC-SR04, HC-SR04+, JSN-SR04T - Ultrasonic distance sensor + * + * Code for SR04 family of ultrasonic distance sensors + * References: + * - https://www.dfrobot.com/wiki/index.php/Weather-proof_Ultrasonic_Sensor_SKU_:_SEN0207 +\*********************************************************************************************/ +#define max(a,b) ((a)>(b)?(a):(b)) + +uint8_t sr04_echo_pin = 0; +uint8_t sr04_trig_pin = 0; + +/*********************************************************************************************\ + * Embedded stripped and tuned NewPing library from Tim Eckel - teckel@leethost.com + * https://bitbucket.org/teckel12/arduino-new-ping +\*********************************************************************************************/ +#define US_ROUNDTRIP_CM 58 // Microseconds (uS) it takes sound to travel round-trip 1cm (2cm total), uses integer to save compiled code space. Default: 58 +#define US_ROUNDTRIP_IN 148 // Microseconds (uS) it takes sound to travel round-trip 1 inch (2 inches total), uses integer to save compiled code space. Default: 148 +#define PING_MEDIAN_DELAY 29000 +#define MAX_SENSOR_DISTANCE 500 +#define PING_OVERHEAD 5 + +// Conversion from uS to distance (round result to nearest cm or inch). +#define EchoConvert(echoTime, conversionFactor) (max(((unsigned int)echoTime + conversionFactor / 2) / conversionFactor, (echoTime ? 1 : 0))) + +/********************************************************************************************/ + +void Sr04Init() +{ + sr04_echo_pin = pin[GPIO_SR04_ECHO]; + sr04_trig_pin = pin[GPIO_SR04_TRIG]; + pinMode(sr04_trig_pin, OUTPUT); + pinMode(sr04_echo_pin, INPUT_PULLUP); +} + +boolean Sr04Read(uint16_t *distance) +{ + uint16_t duration = 0; + + *distance = 0; + + /* Send ping and get delay */ + duration = Sr04GetSamples(9, 250); + + /* Calculate the distance (in cm) based on the speed of sound. */ + *distance = EchoConvert(duration, US_ROUNDTRIP_CM); + + return 1; +} + +uint16_t Sr04Ping(uint16_t max_cm_distance) +{ + uint16_t duration = 0; + uint16_t maxEchoTime, maxTime; + + maxEchoTime = min(max_cm_distance + 1, (uint16_t) MAX_SENSOR_DISTANCE + 1) * US_ROUNDTRIP_CM; + + /* The following trigPin/echoPin cycle is used to determine the + distance of the nearest object by bouncing soundwaves off of it. */ + digitalWrite(sr04_trig_pin, LOW); + delayMicroseconds(2); + digitalWrite(sr04_trig_pin, HIGH); + delayMicroseconds(10); + digitalWrite(sr04_trig_pin, LOW); + + /* Wait for the echo */ + duration = pulseIn(sr04_echo_pin, HIGH, 26000) - PING_OVERHEAD; + + return (duration > maxEchoTime) ? 0 : duration; +} + +uint16_t Sr04GetSamples(uint8_t it, uint16_t max_cm_distance) { + uint16_t uS[it], last; + uint8_t j, i = 0; + uint16_t t; + uS[0] = 0; + + while (i < it) { + t = micros(); + last = Sr04Ping(max_cm_distance); + + if (last != 0) { + if (i > 0) { + for (j = i; j > 0 && uS[j - 1] < last; j--) + uS[j] = uS[j - 1]; + } else j = 0; + uS[j] = last; + i++; + } else it--; + + if (i < it && micros() - t < PING_MEDIAN_DELAY) + delay((PING_MEDIAN_DELAY + t - micros()) / 1000); + } + + return (uS[1]); // Return the ping distance from the 2nd highest reading +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_DISTANCE[] PROGMEM = + "%s{s}SR04 " D_DISTANCE "{m}%d" D_UNIT_CENTIMETER "{e}"; // {s} = , {m} = , {e} = +#endif // USE_WEBSERVER + +void Sr04Show(boolean json) +{ + uint16_t distance; + + if (Sr04Read(&distance)) { // Check if read failed + + if(json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"SR04\":{\"" D_JSON_DISTANCE "\":%d}"), mqtt_data, distance); +#ifdef USE_WEBSERVER + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_DISTANCE, mqtt_data, distance, "cm"); +#endif // USE_WEBSERVER + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +#define XSNS_91 + +boolean Xsns91(byte function) +{ + boolean result = false; + + if ((pin[GPIO_SR04_ECHO] < 99) && (pin[GPIO_SR04_TRIG] < 99)) { + switch (function) { + case FUNC_INIT: + Sr04Init(); + break; + case FUNC_JSON_APPEND: + Sr04Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_APPEND: + Sr04Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_SR04