// ---------------------------------------------------------------------------
// NewPing Library - v1.9.6 - 12/15/2022
//
// AUTHOR/LICENSE:
// Created by Tim Eckel - eckel.tim@gmail.com
// Copyright 2022 License: Forks and derivitive works are NOT permitted without
// permission. Permission is only granted to use as-is for private and
// commercial use. If you wish to contribute, make changes, or enhancements,
// please create a pull request. I get a lot of support issues from this
// library, much of which comes from old versions, buggy forks or sites without
// proper documentation. Trying to wrangle this project together, thanks!
//
// LINKS:
// Project home: https://bitbucket.org/teckel12/arduino-new-ping/wiki/Home
// Support: https://bitbucket.org/teckel12/arduino-new-ping/issues?status=new&status=open
//
// DISCLAIMER:
// This software is furnished "as is", without technical support, and with no 
// warranty, express or implied, as to its usefulness for any purpose.
//
// ATTENTION:
// Special ESPEasy adapted version, using DIRECT_Pin_IO by Paul Stoffregen
// See: https://github.com/PaulStoffregen/OneWire/blob/master/util/
//
// BACKGROUND:
// Initially, I wasn't happy with how poorly ultrasonic sensors performed. I
// soon realized the problem was not the sensor, it was the available ping and
// ultrasonic libraries causing the problem. The NewPing library totally fixes
// these problems, adds many new features, and breathes new life into these
// very affordable distance sensors.
//
// FEATURES:
// * Works with many different ultrasonic sensors: SR04, SRF05, SRF06, DYP-ME007, URM37 & Parallax PING)))™.
// * Compatible with the entire Arduino line-up (and clones), Teensy family (including $19.80 96Mhz 32 bit Teensy 3.2) and non-AVR microcontrollers.
// * Interface with all but the SRF06 sensor using only one Arduino pin.
// * Doesn't lag for a full second if no ping/echo is received.
// * Ping sensors consistently and reliably at up to 30 times per second.
// * Timer interrupt method for event-driven sketches.
// * Built-in digital filter method ping_median() for easy error correction.
// * Uses port registers for a faster pin interface and smaller code size.
// * Allows you to set a maximum distance where pings beyond that distance are read as no ping "clear".
// * Ease of using multiple sensors (example sketch with 15 sensors).
// * More accurate distance calculation (cm, inches & uS).
// * Doesn't use pulseIn, which is slow and gives incorrect results with some ultrasonic sensor models.
// * Actively developed with features being added and bugs/issues addressed.
//
// CONSTRUCTOR:
//   NewPing sonar(trigger_pin, echo_pin [, max_cm_distance] [,trigger_width])
//     trigger_pin & echo_pin - Arduino pins connected to sensor trigger and echo.
//       NOTE: To use the same Arduino pin for trigger and echo, specify the same pin for both values.
//     max_cm_distance - [Optional] Maximum distance you wish to sense. Default=500cm.
//		 trigger_width - [Optional] Width of the trigger-pulse to start the measurement. Default=10usec.
//
// METHODS:
//   sonar.ping([max_cm_distance]) - Send a ping and get the echo time (in microseconds) as a result. [max_cm_distance] allows you to optionally set a new max distance. 
//   sonar.ping_in([max_cm_distance]) - Send a ping and get the distance in whole inches. [max_cm_distance] allows you to optionally set a new max distance.
//   sonar.ping_cm([max_cm_distance]) - Send a ping and get the distance in whole centimeters. [max_cm_distance] allows you to optionally set a new max distance.
//   sonar.ping_median(iterations [, max_cm_distance]) - Do multiple pings (default=5), discard out of range pings and return median in microseconds. [max_cm_distance] allows you to optionally set a new max distance.
//   NewPing::convert_in(echoTime) - Convert echoTime from microseconds to inches (rounds to nearest inch).
//   NewPing::convert_cm(echoTime) - Convert echoTime from microseconds to centimeters (rounds to nearest cm).
//   sonar.ping_timer(function [, max_cm_distance]) - Send a ping and call function to test if ping is complete. [max_cm_distance] allows you to optionally set a new max distance.
//   sonar.check_timer() - Check if ping has returned within the set distance limit.
//   NewPing::timer_us(frequency, function) - Call function every frequency microseconds.
//   NewPing::timer_ms(frequency, function) - Call function every frequency milliseconds.
//   NewPing::timer_stop() - Stop the timer.
//
// HISTORY:
// 29/12/2022 tonhuisman: Add triggerWidth option, defaults to 10usec
//
// 12/15/2022 v1.9.6 - Reverted timer overflow commit as it contained bugs.
//
// 12/13/2022 v1.9.5 - Resolved micros() timer overflow bug. Changed example
//   sketch extensions so they load in latest release of the Arduino IDE.
//   Changed private members to protected for easier 3rd party customization.
//
// 01/26/2022 v1.9.4 - Added esp32 to the compatible architectures (note:
//   non-timer methods only)
//
// 01/20/2022 v1.9.3 - Default to disable timer methods for non-AVR
//   microcontrollers and on the ATmega4809 used in the Nano Every.
//
// 07/15/2018 v1.9.1 - Added support for ATtiny441 and ATtiny841
//   microcontrollers.
//
// 12/09/2017 v1.9.0 - Added support for the ARM-based Particle devices. If
//   other ARM-based microcontrollers adopt a similar timer method that the
//   Particle and Teensy 3.x share, support for other ARM-based microcontrollers
//   could be possible. Attempt to add to Arduino library manager. License
//   changed.
//
// 07/30/2016 v1.8 - Added support for non-AVR microcontrollers. For non-AVR
//   microcontrollers, advanced ping_timer() timer methods are disabled due to
//   inconsistencies or no support at all between platforms. However, standard
//   ping methods are all supported. Added new optional variable to ping(),
//   ping_in(), ping_cm(), ping_median(), and ping_timer() methods which allows
//   you to set a new maximum distance for each ping. Added support for the
//   ATmega16, ATmega32 and ATmega8535 microcontrollers. Changed convert_cm()
//   and convert_in() methods to static members. You can now call them without
//   an object. For example: cm = NewPing::convert_cm(distance);
//
// 09/29/2015 v1.7 - Removed support for the Arduino Due and Zero because
//   they're both 3.3 volt boards and are not 5 volt tolerant while the HC-SR04
//   is a 5 volt sensor.  Also, the Due and Zero don't support pin manipulation
//   compatibility via port registers which can be done (see the Teensy 3.2).
//
// 06/17/2014 v1.6 - Corrected delay between pings when using ping_median()
//   method. Added support for the URM37 sensor (must change URM37_ENABLED from
//   false to true). Added support for Arduino microcontrollers like the $20
//   32 bit ARM Cortex-M4 based Teensy 3.2. Added automatic support for the
//   Atmel ATtiny family of microcontrollers. Added timer support for the
//   ATmega8 microcontroller. Rounding disabled by default, reduces compiled
//   code size (can be turned on with ROUNDING_ENABLED switch). Added
//   TIMER_ENABLED switch to get around compile-time "__vector_7" errors when
//   using the Tone library, or you can use the toneAC, NewTone or
//   TimerFreeTone libraries: https://bitbucket.org/teckel12/arduino-toneac/
//   Other speed and compiled size optimizations.
//
// 08/15/2012 v1.5 - Added ping_median() method which does a user specified
//   number of pings (default=5) and returns the median ping in microseconds
//   (out of range pings ignored). This is a very effective digital filter.
//   Optimized for smaller compiled size (even smaller than sketches that
//   don't use a library).
//
// 07/14/2012 v1.4 - Added support for the Parallax PING)))� sensor. Interface
//   with all but the SRF06 sensor using only one Arduino pin. You can also
//   interface with the SRF06 using one pin if you install a 0.1uf capacitor
//   on the trigger and echo pins of the sensor then tie the trigger pin to
//   the Arduino pin (doesn't work with Teensy). To use the same Arduino pin
//   for trigger and echo, specify the same pin for both values. Various bug
//   fixes.
//
// 06/08/2012 v1.3 - Big feature addition, event-driven ping! Uses Timer2
//   interrupt, so be mindful of PWM or timing conflicts messing with Timer2
//   may cause (namely PWM on pins 3 & 11 on Arduino, PWM on pins 9 and 10 on
//   Mega, and Tone library). Simple to use timer interrupt functions you can
//   use in your sketches totally unrelated to ultrasonic sensors (don't use if
//   you're also using NewPing's ping_timer because both use Timer2 interrupts).
//   Loop counting ping method deleted in favor of timing ping method after
//   inconsistent results kept surfacing with the loop timing ping method.
//   Conversion to cm and inches now rounds to the nearest cm or inch. Code
//   optimized to save program space and fixed a couple minor bugs here and
//   there. Many new comments added as well as line spacing to group code
//   sections for better source readability.
//
// 05/25/2012 v1.2 - Lots of code clean-up thanks to Arduino Forum members.
//   Rebuilt the ping timing code from scratch, ditched the pulseIn code as it
//   doesn't give correct results (at least with ping sensors). The NewPing
//   library is now VERY accurate and the code was simplified as a bonus.
//   Smaller and faster code as well. Fixed some issues with very close ping
//   results when converting to inches. All functions now return 0 only when
//   there's no ping echo (out of range) and a positive value for a successful
//   ping. This can effectively be used to detect if something is out of range
//   or in-range and at what distance. Now compatible with Arduino 0023.
//
// 05/16/2012 v1.1 - Changed all I/O functions to use low-level port registers
//   for ultra-fast and lean code (saves from 174 to 394 bytes). Tested on both
//   the Arduino Uno and Teensy 2.0 but should work on all Arduino-based
//   platforms because it calls standard functions to retrieve port registers
//   and bit masks. Also made a couple minor fixes to defines.
//
// 05/15/2012 v1.0 - Initial release.
// ---------------------------------------------------------------------------

#ifndef NewPing_h

	#define NewPing_h

	#if defined (ARDUINO) && ARDUINO >= 100
		#include <Arduino.h>
	#else
		#include <WProgram.h>
		#if defined (PARTICLE)
			#include <SparkIntervalTimer.h>
		#else
			#include <pins_arduino.h>
		#endif
	#endif

	#if defined (__AVR__)
		#include <avr/io.h>
		#include <avr/interrupt.h>
	#endif

	// Shouldn't need to change these values unless you have a specific need to do so.
	#define MAX_SENSOR_DISTANCE 500 // Maximum sensor distance can be as high as 500cm, no reason to wait for ping longer than sound takes to travel this distance and back. Default=500
	#define US_ROUNDTRIP_CM 57      // Microseconds (uS) it takes sound to travel round-trip 1cm (2cm total), uses integer to save compiled code space. Default=57
	#define US_ROUNDTRIP_IN 146     // Microseconds (uS) it takes sound to travel round-trip 1 inch (2 inches total), uses integer to save compiled code space. Default=146
	#define ONE_PIN_ENABLED true    // Set to "false" to disable one pin mode which saves around 14-26 bytes of binary size. Default=true
	#define ROUNDING_ENABLED false  // Set to "true" to enable distance rounding which also adds 64 bytes to binary size. Default=false
	#define URM37_ENABLED false     // Set to "true" to enable support for the URM37 sensor in PWM mode. Default=false
	#define DEFAULT_TRIGGER_WIDTH 10 // Length of the pulse that starts the measurement. Default=10usec.
	#define MAX_TRIGGER_WIDTH 20 		 // Max. length of the pulse that starts the measurement.

	// Probably shouldn't change these values unless you really know what you're doing.
	#define NO_ECHO 0               // Value returned if there's no ping echo within the specified MAX_SENSOR_DISTANCE or max_cm_distance. Default=0
	#define MAX_SENSOR_DELAY 5800   // Maximum uS it takes for sensor to start the ping. Default=5800
	#define ECHO_TIMER_FREQ 24      // Frequency to check for a ping echo (every 24uS is about 0.4cm accuracy). Default=24
	#define PING_MEDIAN_DELAY 29000 // Microsecond delay between pings in the ping_median method. Default=29000
	#if URM37_ENABLED == true
		#undef  US_ROUNDTRIP_CM
		#undef  US_ROUNDTRIP_IN
		#define US_ROUNDTRIP_CM 50  // Every 50uS PWM signal is low indicates 1cm distance. Default=50
		#define US_ROUNDTRIP_IN 127 // If 50uS is 1cm, 1 inch would be 127uS (50 x 2.54 = 127). Default=127
	#endif

	// Conversion from uS to distance (round result to nearest cm or inch).
	#define NewPingConvert(echoTime, conversionFactor) (max(((unsigned int)echoTime + conversionFactor / 2) / conversionFactor, (echoTime ? 1 : 0)))

	// Detect non-AVR microcontrollers (Teensy 3.x, Arduino DUE, etc.) and don't use port registers or timer interrupts as required.
	#if (defined (__arm__) && (defined (TEENSYDUINO) || defined (PARTICLE)))
		#define PING_OVERHEAD 1
		#define PING_TIMER_OVERHEAD 1
		#define TIMER_ENABLED true
		#define DO_BITWISE true
	#elif defined (__AVR__)
		#define PING_OVERHEAD 5         // Ping overhead in microseconds (uS). Default=5
		#define PING_TIMER_OVERHEAD 13  // Ping timer overhead in microseconds (uS). Default=13
		#define TIMER_ENABLED true
		#define DO_BITWISE true
	#else
		#define PING_OVERHEAD 1
		#define PING_TIMER_OVERHEAD 1
		#define TIMER_ENABLED false
		#define DO_BITWISE false
	#endif

	// Disable the timer interrupts when using ATmega128, ATmega4809 and all ATtiny microcontrollers.
	#if defined(__AVR_ATmega128__) || defined(__AVR_ATmega4809__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny441__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny841__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__) || defined(__AVR_ATtiny43U__) || defined(__AVR_ATtiny1614__)
		#undef  TIMER_ENABLED
		#define TIMER_ENABLED false
	#endif

	// Define timers when using ATmega8, ATmega16, ATmega32 and ATmega8535 microcontrollers.
	#if defined (__AVR_ATmega8__) || defined (__AVR_ATmega16__) || defined (__AVR_ATmega32__) || defined (__AVR_ATmega8535__)
		#define OCR2A OCR2
		#define TIMSK2 TIMSK
		#define OCIE2A OCIE2
	#endif

	class NewPing {
		public:
			enum errorState {
				STATUS_SENSOR_READY,  					// no error, no measruement started
				STATUS_MEASUREMENT_VALID, 				// no errors
				STATUS_ECHO_TRIGGERED,					// echo triggered, intermediate state
				STATUS_MAX_DISTANCE_EXCEEDED, 			// echo too late, maximum distance exceeded
				STATUS_ECHO_START_TIMEOUT_50ms, 		// no echo start signal after 50 ms
				STATUS_ECHO_START_TIMEOUT_DISTANCE,		// no echo start signal after max. distance
				STATUS_ECHO_STATE_ERROR 				// echopin level not low after triggered
			};

			NewPing(uint8_t trigger_pin, uint8_t echo_pin, unsigned int max_cm_distance = MAX_SENSOR_DISTANCE, uint8_t trigger_width = DEFAULT_TRIGGER_WIDTH);
			unsigned int ping(unsigned int max_cm_distance = 0);
			unsigned long ping_cm(unsigned int max_cm_distance = 0);
			unsigned long ping_in(unsigned int max_cm_distance = 0);
			unsigned long ping_median(uint8_t it = 5, unsigned int max_cm_distance = 0);
			static unsigned int convert_cm(unsigned int echoTime);
			static unsigned int convert_in(unsigned int echoTime);
			static float convert_cm_F(unsigned int echoTime);
		    static float convert_in_F(unsigned int echoTime);
			unsigned int getMaxEchoTime() { return _maxEchoTime; };
			errorState getErrorState() { return _errorState; };

	#if TIMER_ENABLED == true
			void ping_timer(void (*userFunc)(void), unsigned int max_cm_distance = 0);
			boolean check_timer();
			unsigned long ping_result;
			static void timer_us(unsigned int frequency, void (*userFunc)(void));
			static void timer_ms(unsigned long frequency, void (*userFunc)(void));
			static void timer_stop();
	#endif
		protected:
			boolean ping_trigger();
			void set_max_distance(unsigned int max_cm_distance);
	#if TIMER_ENABLED == true
			boolean ping_trigger_timer(unsigned int trigger_delay);
			boolean ping_wait_timer();
			static void timer_setup();
			static void timer_ms_cntdwn();
	#endif
	#if DO_BITWISE == true
			uint8_t _triggerBit;
			uint8_t _echoBit;
		#if defined(PARTICLE)
			#if !defined(portModeRegister)
				#if defined (STM32F10X_MD)
					#define portModeRegister(port)     ( &(port->CRL) )
				#elif defined (STM32F2XX)
					#define portModeRegister(port)     ( &(port->MODER) )
				#endif
			#endif
			volatile uint32_t *_triggerOutput;
			volatile uint32_t *_echoInput;
			volatile uint32_t *_triggerMode;
		#else
			volatile uint8_t *_triggerOutput;
			volatile uint8_t *_echoInput;
			volatile uint8_t *_triggerMode;
		#endif
	#else
			uint8_t _triggerPin;
			uint8_t _echoPin;
	#endif
			uint8_t _triggerWidth = DEFAULT_TRIGGER_WIDTH;
	
			unsigned int _maxEchoTime;
			unsigned long _max_time;

			errorState _errorState;
	};


#endif
