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 ;
2022-01-20 12:37:48 +00:00
} SR04 ;
2018-04-20 13:31:09 +01:00
2019-03-26 17:26:50 +00:00
NewPing * sonar = nullptr ;
2019-11-15 22:10:07 +00:00
TasmotaSerial * sonar_serial = nullptr ;
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
}
2022-01-20 12:37:48 +00:00
uint16_t Sr04TMode2Distance ( void ) {
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 ) {
if ( sonar_serial - > available ( ) & & ( buffer_idx < sizeof ( buffer ) ) ) {
buffer [ buffer_idx + + ] = sonar_serial - > read ( ) ;
end = millis ( ) + 10 ;
}
delay ( 1 ) ;
}
2022-02-04 13:53:42 +00:00
if ( SR04_MODE_NONE = = SR04 . type ) { // Only log during detection
2022-01-22 15:56:46 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " SR4: Received '%*_H' " ) , 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 {
AddLog ( LOG_LEVEL_ERROR , PSTR ( " SR4: CRC error " ) ) ;
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
}
2022-01-20 12:37:48 +00:00
uint16_t Sr04TMode3Distance ( ) {
sonar_serial - > write ( 0x55 ) ;
sonar_serial - > flush ( ) ;
2019-12-31 13:23:34 +00:00
2022-01-20 12:37:48 +00:00
return Sr04TMode2Distance ( ) ;
}
2019-11-15 22:10:07 +00:00
2022-01-20 12:37:48 +00:00
/*********************************************************************************************/
void Sr04TModeDetect ( void ) {
2022-01-22 15:56:46 +00:00
SR04 . type = SR04_MODE_NONE ;
2022-01-20 12:37:48 +00:00
if ( ! PinUsed ( GPIO_SR04_ECHO ) ) { return ; }
int sr04_echo_pin = Pin ( GPIO_SR04_ECHO ) ;
2022-09-26 13:06:28 +01:00
int sr04_trig_pin = Pin ( GPIO_SR04_TRIG ) ; // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_TRIG as -1
2022-01-20 12:37:48 +00:00
sonar_serial = new TasmotaSerial ( sr04_echo_pin , sr04_trig_pin , 1 ) ;
2022-01-22 15:56:46 +00:00
if ( sonar_serial & & sonar_serial - > begin ( 9600 ) ) {
2022-01-20 12:37:48 +00:00
DEBUG_SENSOR_LOG ( PSTR ( " SR4: Detect mode " ) ) ;
2018-11-28 09:33:59 +00:00
2022-01-20 12:37:48 +00:00
if ( PinUsed ( GPIO_SR04_TRIG ) ) {
2022-01-22 15:56:46 +00:00
SR04 . type = ( Sr04TMiddleValue ( Sr04TMode3Distance ( ) , Sr04TMode3Distance ( ) , Sr04TMode3Distance ( ) ) ! = 0 ) ? SR04_MODE_SER_TRANSCEIVER : SR04_MODE_TRIGGER_ECHO ;
2022-01-20 12:37:48 +00:00
} else {
2022-01-22 15:56:46 +00:00
SR04 . type = SR04_MODE_SER_RECEIVER ;
2022-01-20 12:37:48 +00:00
}
} else {
2022-01-22 15:56:46 +00:00
SR04 . type = SR04_MODE_TRIGGER_ECHO ;
2022-01-20 12:37:48 +00:00
}
2022-01-22 15:56:46 +00:00
if ( SR04 . type < SR04_MODE_SER_RECEIVER ) {
if ( sonar_serial ) {
delete sonar_serial ;
sonar_serial = nullptr ;
2022-01-20 12:37:48 +00:00
}
2022-09-26 13:06:28 +01:00
sr04_trig_pin = ( PinUsed ( GPIO_SR04_TRIG ) ) ? Pin ( GPIO_SR04_TRIG ) : Pin ( GPIO_SR04_ECHO ) ; // if GPIO_SR04_TRIG is not configured use single PIN mode with GPIO_SR04_ECHO only
2022-01-20 12:37:48 +00:00
sonar = new NewPing ( sr04_trig_pin , sr04_echo_pin , SR04_MAX_SENSOR_DISTANCE ) ;
2022-02-17 18:16:24 +00:00
delay ( 100 ) ; // give time to inizialise, preventing ping_median fails
2022-01-22 15:56:46 +00:00
if ( ! sonar | | ! sonar - > ping_median ( 5 ) ) {
SR04 . type = SR04_MODE_NONE ;
}
2022-01-20 12:37:48 +00:00
} else {
2022-09-26 13:06:28 +01:00
if ( sonar_serial ) {
if ( sonar_serial - > hardwareSerial ( ) ) {
ClaimSerial ( ) ;
}
2022-01-20 12:37:48 +00:00
}
}
AddLog ( LOG_LEVEL_INFO , PSTR ( " SR4: Mode %d " ) , SR04 . type ) ;
}
2022-01-22 15:56:46 +00:00
void Sr04TReading ( void ) {
if ( TasmotaGlobal . uptime < 3 ) { return ; }
if ( SR04 . valid ) {
SR04 . valid - - ;
} else {
SR04 . distance = 0 ;
}
float distance ;
switch ( SR04 . type ) {
case SR04_NOT_DETECTED :
Sr04TModeDetect ( ) ;
SR04 . valid = ( SR04 . type ) ? SENSOR_MAX_MISS : 0 ;
break ;
case SR04_MODE_SER_TRANSCEIVER :
distance = ( float ) ( Sr04TMiddleValue ( Sr04TMode3Distance ( ) , Sr04TMode3Distance ( ) , Sr04TMode3Distance ( ) ) ) / 10 ; // Convert to cm
break ;
case SR04_MODE_SER_RECEIVER :
//empty input buffer first
while ( sonar_serial - > available ( ) ) { sonar_serial - > read ( ) ; }
distance = ( float ) ( Sr04TMiddleValue ( Sr04TMode2Distance ( ) , Sr04TMode2Distance ( ) , Sr04TMode2Distance ( ) ) ) / 10 ; // Convert to cm
break ;
case SR04_MODE_TRIGGER_ECHO :
distance = ( float ) ( sonar - > ping_median ( 5 ) ) / US_ROUNDTRIP_CM ;
break ;
default :
distance = 0 ;
}
if ( distance ) {
SR04 . distance = distance ;
SR04 . valid = SENSOR_MAX_MISS ;
}
}
2022-01-20 12:37:48 +00:00
void Sr04Show ( bool json ) {
2022-01-22 15:56:46 +00:00
if ( SR04 . valid ) { // Check if read failed
2018-12-21 15:17:06 +00:00
char distance_chr [ 33 ] ;
2022-01-20 12:37:48 +00:00
dtostrfd ( SR04 . distance , 3 , distance_chr ) ;
2018-04-20 13:31:09 +01:00
if ( json ) {
2019-03-23 16:57:31 +00:00
ResponseAppend_P ( PSTR ( " , \" SR04 \" :{ \" " D_JSON_DISTANCE " \" :%s} " ) , distance_chr ) ;
2018-11-11 12:21:46 +00:00
# ifdef USE_DOMOTICZ
2020-10-29 12:37:09 +00:00
if ( 0 = = TasmotaGlobal . tele_period ) {
2018-11-28 09:33:59 +00:00
DomoticzSensor ( DZ_COUNT , distance_chr ) ; // 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 {
2021-03-23 11:57:56 +00:00
WSContentSend_PD ( HTTP_SNS_DISTANCE_CM , " SR04 " , distance_chr ) ;
2018-04-20 13:31:09 +01:00
# endif // USE_WEBSERVER
}
}
}
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-01-20 12:37:48 +00:00
bool Xsns22 ( uint8_t function ) {
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-04-20 13:31:09 +01:00
2022-01-22 15:56:46 +00:00
if ( SR04 . type ) {
2018-04-20 13:31:09 +01:00
switch ( function ) {
2019-11-15 22:10:07 +00:00
case FUNC_EVERY_SECOND :
Sr04TReading ( ) ;
result = true ;
2018-04-20 13:31:09 +01:00
break ;
case FUNC_JSON_APPEND :
Sr04Show ( 1 ) ;
break ;
# ifdef USE_WEBSERVER
2019-03-19 16:31:43 +00:00
case FUNC_WEB_SENSOR :
2018-04-20 13:31:09 +01:00
Sr04Show ( 0 ) ;
break ;
# endif // USE_WEBSERVER
}
}
return result ;
}
# endif // USE_SR04