2018-04-20 13:31:09 +01:00
/*
2019-10-27 10:13:24 +00:00
xsns_22_sr04 . ino - SR04 ultrasonic sensor support for Tasmota
2018-04-20 13:31:09 +01:00
2021-01-01 12:44:04 +00:00
Copyright ( C ) 2021 Nuno Ferreira and Theo Arends
2018-04-20 13:31:09 +01:00
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 < http : //www.gnu.org/licenses/>.
*/
# 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
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-03-19 09:44:47 +00:00
# define XSNS_22 22
# ifndef SR04_MAX_SENSOR_DISTANCE
# define SR04_MAX_SENSOR_DISTANCE 500
# endif
2019-03-11 15:49:59 +00:00
2022-01-22 15:56:46 +00:00
enum Sr04CommsMode { SR04_MODE_NONE , // No hardware detected
SR04_MODE_TRIGGER_ECHO , // Mode 1 - Trigger and Echo connection
SR04_MODE_SER_RECEIVER , // Mode 2 - Serial receive only
SR04_MODE_SER_TRANSCEIVER , // Mode 3 - Serial transmit and receive
SR04_NOT_DETECTED } ; // Not yet detected
2022-01-20 12:37:48 +00:00
# include <NewPing.h>
# include <TasmotaSerial.h>
struct {
2022-01-22 15:56:46 +00:00
float distance ;
uint8_t valid ;
uint8_t type = SR04_NOT_DETECTED ;
2024-03-31 14:12:29 +01:00
NewPing * sonar = nullptr ;
TasmotaSerial * sonar_serial = nullptr ;
} SR04 [ MAX_SR04 ] ;
2018-04-20 13:31:09 +01:00
2024-04-09 09:20:03 +01:00
uint8_t sr04_sensor_count = 0 ;
2019-11-15 22:10:07 +00:00
2022-01-20 12:37:48 +00:00
uint16_t Sr04TMiddleValue ( uint16_t first , uint16_t second , uint16_t third ) {
2019-11-15 22:10:07 +00:00
uint16_t ret = first ;
if ( first > second ) {
first = second ;
second = ret ;
}
if ( third < first ) {
return first ;
} else if ( third > second ) {
return second ;
} else {
return third ;
2019-12-31 13:23:34 +00:00
}
2019-11-15 22:10:07 +00:00
}
2024-03-31 14:12:29 +01:00
uint16_t Sr04TMode2Distance ( uint32_t i ) {
2022-02-04 13:53:42 +00:00
uint8_t buffer [ 4 ] ; // Accommodate either 2 or 4 bytes of data
2022-01-22 15:56:46 +00:00
uint32_t buffer_idx = 0 ;
uint32_t end = millis ( ) + 100 ;
while ( millis ( ) < end ) {
2024-03-31 14:12:29 +01:00
if ( SR04 [ i ] . sonar_serial - > available ( ) & & ( buffer_idx < sizeof ( buffer ) ) ) {
buffer [ buffer_idx + + ] = SR04 [ i ] . sonar_serial - > read ( ) ;
2022-01-22 15:56:46 +00:00
end = millis ( ) + 10 ;
}
delay ( 1 ) ;
}
2024-03-31 14:12:29 +01:00
if ( SR04_MODE_NONE = = SR04 [ i ] . type ) { // Only log during detection
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SR4-%d: Received '%*_H' " ) , i + 1 , buffer_idx , buffer ) ;
2019-12-31 13:23:34 +00:00
}
2022-02-04 13:53:42 +00:00
uint32_t distance = 0 ;
if ( buffer_idx > 2 ) { // JSN-SR04T serial has four bytes
// FF00FAF9
uint8_t crc = buffer [ 0 ] ;
crc + = buffer [ 1 ] ;
crc + = buffer [ 2 ] ;
if ( crc = = buffer [ 3 ] ) { // Check crc sum
distance = ( buffer [ 1 ] < < 8 ) + buffer [ 2 ] ;
} else {
2024-03-31 14:12:29 +01:00
AddLog ( LOG_LEVEL_ERROR , PSTR ( " SR4-%d: CRC error " ) , i + 1 ) ;
2022-01-22 15:56:46 +00:00
}
2019-12-31 13:23:34 +00:00
}
2022-02-04 13:53:42 +00:00
else if ( buffer_idx > 1 ) { // US-100 serial has no CRC
// 00FA = 250 millimeter
distance = ( buffer [ 0 ] < < 8 ) + buffer [ 1 ] ;
}
2019-12-31 13:23:34 +00:00
return distance ;
2019-11-15 22:10:07 +00:00
}
2024-03-31 14:12:29 +01:00
uint16_t Sr04TMode3Distance ( uint32_t i ) {
SR04 [ i ] . sonar_serial - > write ( 0x55 ) ;
SR04 [ i ] . sonar_serial - > flush ( ) ;
2019-12-31 13:23:34 +00:00
2024-03-31 14:12:29 +01:00
return Sr04TMode2Distance ( i ) ;
2022-01-20 12:37:48 +00:00
}
2019-11-15 22:10:07 +00:00
2022-01-20 12:37:48 +00:00
/*********************************************************************************************/
void Sr04TModeDetect ( void ) {
2024-03-31 14:12:29 +01:00
for ( uint32_t i = 0 ; i < MAX_SR04 ; i + + ) {
SR04 [ i ] . type = SR04_MODE_NONE ;
if ( ! PinUsed ( GPIO_SR04_ECHO , i ) ) { continue ; }
2024-04-09 09:20:03 +01:00
sr04_sensor_count + + ;
2024-03-31 14:12:29 +01:00
int sr04_echo_pin = Pin ( GPIO_SR04_ECHO , i ) ;
int sr04_trig_pin = Pin ( GPIO_SR04_TRIG , i ) ; // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_TRIG as -1
SR04 [ i ] . sonar_serial = new TasmotaSerial ( sr04_echo_pin , sr04_trig_pin , 1 ) ;
2022-01-20 12:37:48 +00:00
2024-03-31 14:12:29 +01:00
if ( SR04 [ i ] . sonar_serial & & SR04 [ i ] . sonar_serial - > begin ( 9600 ) ) {
DEBUG_SENSOR_LOG ( PSTR ( " SR4: Detect mode " ) ) ;
2018-11-28 09:33:59 +00:00
2024-03-31 14:12:29 +01:00
if ( PinUsed ( GPIO_SR04_TRIG , i ) ) {
SR04 [ i ] . type = ( Sr04TMiddleValue ( Sr04TMode3Distance ( i ) , Sr04TMode3Distance ( i ) , Sr04TMode3Distance ( i ) ) ! = 0 ) ? SR04_MODE_SER_TRANSCEIVER : SR04_MODE_TRIGGER_ECHO ;
} else {
SR04 [ i ] . type = ( Sr04TMiddleValue ( Sr04TMode2Distance ( i ) , Sr04TMode2Distance ( i ) , Sr04TMode2Distance ( i ) ) ! = 0 ) ? SR04_MODE_SER_RECEIVER : SR04_MODE_TRIGGER_ECHO ;
}
2022-01-20 12:37:48 +00:00
} else {
2024-03-31 14:12:29 +01:00
SR04 [ i ] . type = SR04_MODE_TRIGGER_ECHO ;
2022-01-20 12:37:48 +00:00
}
2024-03-31 14:12:29 +01:00
if ( SR04 [ i ] . type < SR04_MODE_SER_RECEIVER ) {
if ( SR04 [ i ] . sonar_serial ) {
delete SR04 [ i ] . sonar_serial ;
SR04 [ i ] . sonar_serial = nullptr ;
}
sr04_trig_pin = ( PinUsed ( GPIO_SR04_TRIG , i ) ) ? Pin ( GPIO_SR04_TRIG , i ) : Pin ( GPIO_SR04_ECHO , i ) ; // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only
SR04 [ i ] . sonar = new NewPing ( sr04_trig_pin , sr04_echo_pin , SR04_MAX_SENSOR_DISTANCE ) ;
delay ( 100 ) ; // give time to inizialise, preventing ping_median fails
if ( ! SR04 [ i ] . sonar | | ! SR04 [ i ] . sonar - > ping_median ( 5 ) ) {
SR04 [ i ] . type = SR04_MODE_NONE ;
}
} else {
if ( SR04 [ i ] . sonar_serial ) {
if ( SR04 [ i ] . sonar_serial - > hardwareSerial ( ) ) {
ClaimSerial ( ) ;
}
# ifdef ESP32
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SR4-%d: Serial UART%d " ) , i + 1 , SR04 [ i ] . sonar_serial - > getUart ( ) ) ;
# endif
2022-09-26 13:06:28 +01:00
}
2022-01-20 12:37:48 +00:00
}
2024-03-31 14:12:29 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " SR4-%d: Mode %d " ) , i + 1 , SR04 [ i ] . type ) ;
}
2022-01-20 12:37:48 +00:00
}
2024-03-31 14:12:29 +01:00
void Sr04TReading ( uint32_t i ) {
2022-01-22 15:56:46 +00:00
if ( TasmotaGlobal . uptime < 3 ) { return ; }
2024-03-31 14:12:29 +01:00
if ( SR04 [ i ] . valid ) {
SR04 [ i ] . valid - - ;
2022-01-22 15:56:46 +00:00
} else {
2024-03-31 14:12:29 +01:00
SR04 [ i ] . distance = 0 ;
2022-01-22 15:56:46 +00:00
}
float distance ;
2024-03-31 14:12:29 +01:00
switch ( SR04 [ i ] . type ) {
2022-01-22 15:56:46 +00:00
case SR04_NOT_DETECTED :
Sr04TModeDetect ( ) ;
2024-03-31 14:12:29 +01:00
SR04 [ i ] . valid = ( SR04 [ i ] . type ) ? SENSOR_MAX_MISS : 0 ;
2022-01-22 15:56:46 +00:00
break ;
case SR04_MODE_SER_TRANSCEIVER :
2024-03-31 14:12:29 +01:00
distance = ( float ) ( Sr04TMiddleValue ( Sr04TMode3Distance ( i ) , Sr04TMode3Distance ( i ) , Sr04TMode3Distance ( i ) ) ) / 10 ; // Convert to cm
2022-01-22 15:56:46 +00:00
break ;
case SR04_MODE_SER_RECEIVER :
//empty input buffer first
2024-03-31 14:12:29 +01:00
while ( SR04 [ i ] . sonar_serial - > available ( ) ) { SR04 [ i ] . sonar_serial - > read ( ) ; }
distance = ( float ) ( Sr04TMiddleValue ( Sr04TMode2Distance ( i ) , Sr04TMode2Distance ( i ) , Sr04TMode2Distance ( i ) ) ) / 10 ; // Convert to cm
2022-01-22 15:56:46 +00:00
break ;
case SR04_MODE_TRIGGER_ECHO :
2024-03-31 14:12:29 +01:00
distance = ( float ) ( SR04 [ i ] . sonar - > ping_median ( 5 ) ) / US_ROUNDTRIP_CM ;
2022-01-22 15:56:46 +00:00
break ;
default :
distance = 0 ;
}
if ( distance ) {
2024-03-31 14:12:29 +01:00
SR04 [ i ] . distance = distance ;
SR04 [ i ] . valid = SENSOR_MAX_MISS ;
2022-01-22 15:56:46 +00:00
}
}
2024-03-31 14:12:29 +01:00
void Sr04Show ( uint32_t i , bool json ) {
if ( SR04 [ i ] . valid ) {
char types [ 12 ] ;
2024-04-09 08:56:31 +01:00
// backward compatibility check
2024-04-09 09:20:03 +01:00
if ( i = = 0 & & sr04_sensor_count = = 1 ) {
2024-04-09 08:56:31 +01:00
strcpy_P ( types , PSTR ( " SR04 " ) ) ;
} else {
snprintf_P ( types , sizeof ( types ) , PSTR ( " SR04%c%d " ) , IndexSeparator ( ) , i + 1 ) ;
}
2018-04-20 13:31:09 +01:00
if ( json ) {
2024-03-31 14:12:29 +01:00
ResponseAppend_P ( PSTR ( " , \" %s \" :{ \" " D_JSON_DISTANCE " \" :%1_f} " ) , types , & SR04 [ i ] . distance ) ;
2018-11-11 12:21:46 +00:00
# ifdef USE_DOMOTICZ
2020-10-29 12:37:09 +00:00
if ( 0 = = TasmotaGlobal . tele_period ) {
2024-03-31 14:12:29 +01:00
DomoticzFloatSensor ( DZ_COUNT , SR04 [ i ] . distance ) ; // Send distance as Domoticz Counter value
2018-11-11 12:21:46 +00:00
}
# endif // USE_DOMOTICZ
2018-04-20 13:31:09 +01:00
# ifdef USE_WEBSERVER
} else {
2024-03-31 14:12:29 +01:00
WSContentSend_PD ( HTTP_SNS_F_DISTANCE_CM , types , & SR04 [ i ] . distance ) ;
2018-04-20 13:31:09 +01:00
# endif // USE_WEBSERVER
}
}
}
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-11-11 09:44:56 +00:00
bool Xsns22 ( uint32_t function ) {
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-04-20 13:31:09 +01:00
2024-04-09 09:20:03 +01:00
if ( PinUsed ( GPIO_SR04_ECHO , GPIO_ANY ) ) {
for ( uint32_t i = 0 ; i < MAX_SR04 ; i + + ) {
if ( SR04 [ i ] . type ) {
switch ( function ) {
case FUNC_EVERY_SECOND :
Sr04TReading ( i ) ;
result = true ;
break ;
case FUNC_JSON_APPEND :
Sr04Show ( i , 1 ) ;
break ;
# ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR :
Sr04Show ( i , 0 ) ;
break ;
# endif // USE_WEBSERVER
}
2024-03-31 14:12:29 +01:00
}
2018-04-20 13:31:09 +01:00
}
}
return result ;
}
# endif // USE_SR04