├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
├── examples
├── SonarI2Cdaisy
│ └── SonarI2Cdaisy.ino
├── SonarI2Ctest
│ └── SonarI2Ctest.ino
├── SonarI2Cv2
│ └── SonarI2Cv2.ino
└── SonarI2Cx10
│ └── SonarI2Cx10.ino
├── extras
└── sonarI2Cdemo1_bb.png
├── library.properties
└── src
├── SonarI2C.cpp
├── SonarI2C.h
└── src.ino
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files
2 | *.slo
3 | *.lo
4 | *.o
5 | *.obj
6 |
7 | # Precompiled Headers
8 | *.gch
9 | *.pch
10 |
11 | # Compiled Dynamic libraries
12 | *.so
13 | *.dylib
14 | *.dll
15 |
16 | # Fortran module files
17 | *.mod
18 |
19 | # Compiled Static libraries
20 | *.lai
21 | *.la
22 | *.a
23 | *.lib
24 |
25 | # Executables
26 | *.exe
27 | *.out
28 | *.app
29 |
30 | # =========================
31 | # Operating System Files
32 | # =========================
33 |
34 | # OSX
35 | # =========================
36 |
37 | .DS_Store
38 | .AppleDouble
39 | .LSOverride
40 |
41 | # Thumbnails
42 | ._*
43 |
44 | # Files that might appear in the root of a volume
45 | .DocumentRevisions-V100
46 | .fseventsd
47 | .Spotlight-V100
48 | .TemporaryItems
49 | .Trashes
50 | .VolumeIcon.icns
51 |
52 | # Directories potentially created on remote AFP share
53 | .AppleDB
54 | .AppleDesktop
55 | Network Trash Folder
56 | Temporary Items
57 | .apdisk
58 |
59 | # Windows
60 | # =========================
61 |
62 | # Windows image file caches
63 | Thumbs.db
64 | ehthumbs.db
65 |
66 | # Folder config file
67 | Desktop.ini
68 |
69 | # Recycle Bin used on file shares
70 | $RECYCLE.BIN/
71 |
72 | # Windows Installer files
73 | *.cab
74 | *.msi
75 | *.msm
76 | *.msp
77 |
78 | # Windows shortcuts
79 | *.lnk
80 |
81 | # Arduino IDE
82 | .development
83 |
84 | # vim
85 | *~
86 | *.swp
87 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013,2015,2017 Alastair Young
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SonarI2C library for Arduino
2 |
3 | Version: 1.1.1
4 | Release date: 2017 March 13
5 | [redhunter.com](http://redhunter.com/blog/2016/04/28/sonari2c-multiple-hc-sr04-sensors-on-arduino-i2c/)
6 | [hackaday.io](https://hackaday.io/project/19950-hc-sr04-i2c-octopus-octosonar)
7 | [tindie.com](https://www.tindie.com/products/arielnh56/octosonar-connect-8-x-hc-sr04-to-arduino/)
8 |
9 | ## Summary
10 |
11 | This is a library for the Arduino IDE that allows the polling of multiple ultrasonic distance sensors using the I2C bus and a single hardware interrupt pin. It assumes a PCF8574(A) type port expander to trigger the sensors, and external logic circuitry to multiplex the echo signals.
12 |
13 | It has been tested with the HC-SR04 sensor - the cheapest and most widely available at this time. Up to 12 units have been tested on a breadboard, using two of my Octosonar modules in a daisy-chain configuration.
14 |
15 | ## Supported Platforms
16 |
17 | This library is designed to work with the Arduino IDE versions 1.6.x or later; it is not tested it with earlier versions. It should work with any Arduino compatible board with an available hardware interrupt pin. Note that other interrupt activity may interfere with the accuracy and reliability of this code.
18 |
19 | ## Getting Started
20 |
21 | ### Hardware
22 |
23 | The minimum setup to breadboard this requires
24 |
25 | * a PCF8574 or PCF8574A IC
26 | * two HC-SR04 sensors
27 | * two NPN transistors and some resistors to make a NOR gate
28 | * example sketch SonarI2Cv2
29 |
30 | 
31 |
32 | For pracitcal use you can replicate my prototype design at [redhunter.com](http://redhunter.com/blog/2016/04/28/sonari2c-multiple-hc-sr04-sensors-on-arduino-i2c/) or purchase my Octosonar boards at [tindie.com](https://www.tindie.com/products/arielnh56/octosonar-connect-8-x-hc-sr04-to-arduino/)
33 |
34 | ### Software
35 |
36 | If you are using version 1.6.2 or later of the [Arduino software (IDE)](http://www.arduino.cc/en/Main/Software), you can use the Library Manager to install this library:
37 |
38 | 1. In the Arduino IDE, open the "Sketch" menu, select "Include Library", then "Manage Libraries...".
39 | 2. Search for "SonarI2C".
40 | 3. Click the SonarI2C entry in the list.
41 | 4. Click "Install".
42 |
43 | If this does not work, you can manually install the library:
44 |
45 | 1. Download the [latest release archive from GitHub](https://github.com/arielnh56/SonarI2C/releases) and decompress it.
46 | 2. Rename the folder "SonarI2C-master" to "SonarI2C".
47 | 3. Move the "SonarI2C" folder into the "libraries" directory inside your Arduino sketchbook directory. You can view your sketchbook location by opening the "File" menu and selecting "Preferences" in the Arduino IDE. If there is not already a "libraries" folder in that location, you should make the folder yourself.
48 | 4. After installing the library, restart the Arduino IDE.
49 |
50 | (note - the above instructions adapted from a Pololu readme)
51 |
52 | ## Examples
53 |
54 | Two examples are included showing the use of two and ten sensors. Aother example will display 8 outputs on an LCD.
55 |
56 | ## Library reference
57 |
58 | * Constructor. Call once for each sensor outside loop(0 and setup(). This sets the I2C address of the pin expander (0x20 - 0x27, 0x38 - 0x3F), the pin on the expander (0-7) and the maximum range in mm (0-4000).
59 |
60 | ```c
61 | SonarI2C(uint8_t address, uint8_t pin, uint16_t max_mm)
62 | ```
63 |
64 | * call one of the begin() functions from setup() before you call init() on the individual SonarI2C members. This sets the interrupt pin to use and the spacing between sonar pings. The default is 50ms to ensure no interference from old echoes. Reduce this for faster but less reliable operation.
65 |
66 | ```c
67 | static void begin(); // call from setup(). Defaults to pin 2 and 50ms
68 | static void begin(uint8_t interrupt); // same thing but set the pin
69 | static void begin(uint8_t interrupt, uint16_t spacing); // same thing but set the pin and spacing
70 | ```
71 |
72 | * by default the interrupt pin is looking for a positive echo pulse. If you are using NOR logic (as in the breadboard example) set 'inverse' to 'true'
73 |
74 | ```c
75 | static boolean inverse;
76 | ```
77 |
78 | * by default we skip one out of range/failed echo,keeping the last value. It may be useful to raise this count in sub-optimal echo environments.
79 |
80 | ```c
81 | static uint8_t maxOOR; // how many OOR to skip. Raise this in noisy environments
82 | ```
83 |
84 | * call init() on each SonarI2C from setup(). It initilizes some values and places it in the polling queue
85 |
86 | ```c
87 | void init();
88 | ```
89 |
90 | * Disabling sensors that you are not interested in right now allows the other sensors to poll more often.
91 |
92 | ```c
93 | boolean enabled(); // query if enabled
94 | void enable(boolean enabled); // set _enable
95 | ```
96 |
97 | * Call this every loop(). If it is time, it will poll the next enabled sensor.
98 |
99 | ```c
100 | static void doSonar();
101 | ```
102 |
103 | * Return values
104 | ```c
105 | int16_t mm(); // return distance in mm.
106 | int16_t cm(); // return distance in cm
107 | int16_t inch(); // return distance in inches
108 | int16_t us(); // return echo time in microseconds
109 | ```
110 | currently uses floats, which is inefficient
111 |
112 | ## Version history
113 |
114 | * 1.0.0 (2016 April 28): Original release.
115 | * 1.1.0 (2017 March 13): Improved OOR handling, MIT license.
116 | * 1.1.1 (2017 March 13): Update README
117 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/examples/SonarI2Cdaisy/SonarI2Cdaisy.ino:
--------------------------------------------------------------------------------
1 |
2 |
3 | /*
4 | * Test script for SonaI2C
5 | *
6 | * This uses the LiquidCrystal_PCF8574 lubrary to drive an 2004 LCD display
7 | * mm results are displayed anti-clockwise around the display to match the OctoSonar board layout
8 | * Values are updated every 500ms
9 | *
10 | */
11 | // #define I2C_MASTER_ADDRESS 9 // for author's dual brain test bot
12 | #define I2C_LCD_ADDRESS 0x27 // default for the current flavor of cheap I2C backpack
13 | #define I2C_SONAR_ADDRESS 0x20 // change to 0x3B for default OctoSonar
14 | #include "SonarI2C.h"
15 | // #include // old NewLiquidCrystal library
16 | #include
17 |
18 | // LiquidCrystal_I2C lcd(I2C_LCD_ADDRESS, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // // old NewLiquidCrystal library
19 | LiquidCrystal_PCF8574 lcd(I2C_LCD_ADDRESS);
20 |
21 | uint8_t numsonars = 8;
22 |
23 | // Numeric Sequence
24 | SonarI2C sonars[] = {
25 | SonarI2C (I2C_SONAR_ADDRESS, 0, 4000),
26 | SonarI2C (I2C_SONAR_ADDRESS, 1, 4000),
27 | SonarI2C (I2C_SONAR_ADDRESS, 2, 4000),
28 | SonarI2C (I2C_SONAR_ADDRESS, 3, 4000),
29 | SonarI2C (I2C_SONAR_ADDRESS, 4, 4000),
30 | SonarI2C (I2C_SONAR_ADDRESS, 5, 4000),
31 | SonarI2C (I2C_SONAR_ADDRESS, 6, 4000),
32 | SonarI2C (I2C_SONAR_ADDRESS, 7, 4000),
33 | };
34 |
35 |
36 |
37 |
38 | void setup() {
39 | lcd.begin(20, 4); // initialize the lcd
40 | lcd.setBacklight(255); // turn on the backlight
41 | lcd.clear(); // go home
42 | Serial.begin(115200);
43 | SonarI2C::begin(); // initialize bus, pins etc
44 | // SonarI2C::begin(2); // alternate form, set interrupt pin, 2 is the default
45 | // SonarI2C::begin(2, 50); // alternate form also sets spacing, 50ms is the default
46 | SonarI2C::inverse = false;
47 | for (uint8_t i = 0; i < numsonars; i++) {
48 | sonars[i].init();
49 | }
50 | }
51 |
52 | uint32_t last_print = 0;
53 | void loop() {
54 | char buffer[20];
55 | SonarI2C::doSonar(); // call every cycle, SonarI2C handles the spacing
56 |
57 | if (last_print + 500 < millis()) {
58 | last_print = millis();
59 | for (uint8_t i = 0; i < numsonars; i++) {
60 | // Serial.print(sonars[i].mm()); Serial.print(" ");
61 | }
62 | // Serial.println();
63 | sprintf(buffer, "0 %5d %5d 7 ", sonars[0].mm(), sonars[7].mm());
64 | lcd.setCursor(0, 0); lcd.print(buffer);
65 | sprintf(buffer, "1 %5d %5d 6", sonars[1].mm(), sonars[6].mm());
66 | lcd.setCursor(0, 1); lcd.print(buffer);
67 | sprintf(buffer, "2 %5d %5d 5", sonars[2].mm(), sonars[5].mm());
68 | lcd.setCursor(0, 2); lcd.print(buffer);
69 | sprintf(buffer, "3 %5d %5d 4", sonars[3].mm(), sonars[4].mm());
70 | lcd.setCursor(0, 3); lcd.print(buffer);
71 | }
72 | }
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/examples/SonarI2Ctest/SonarI2Ctest.ino:
--------------------------------------------------------------------------------
1 |
2 |
3 | /*
4 | * Test script for SonaI2C
5 | *
6 | * This uses the LiquidCrystal_PCF8574 lubrary to drive an 2004 LCD display
7 | * mm results are displayed anti-clockwise around the display to match the OctoSonar board layout
8 | * Values are updated every 500ms
9 | *
10 | */
11 | // #define I2C_MASTER_ADDRESS 9 // for author's dual brain test bot
12 | #define I2C_LCD_ADDRESS 0x27 // default for the current flavor of cheap I2C backpack
13 | #define I2C_SONAR_ADDRESS 0x20 // change to 0x3B for default OctoSonar
14 | #include "SonarI2C.h"
15 | // #include // old NewLiquidCrystal library
16 | #include
17 |
18 | // LiquidCrystal_I2C lcd(I2C_LCD_ADDRESS, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // // old NewLiquidCrystal library
19 | LiquidCrystal_PCF8574 lcd(I2C_LCD_ADDRESS);
20 |
21 | uint8_t numsonars = 8;
22 |
23 | // Numeric Sequence
24 | SonarI2C sonars[] = {
25 | SonarI2C (I2C_SONAR_ADDRESS, 0, 4000),
26 | SonarI2C (I2C_SONAR_ADDRESS, 1, 4000),
27 | SonarI2C (I2C_SONAR_ADDRESS, 2, 4000),
28 | SonarI2C (I2C_SONAR_ADDRESS, 3, 4000),
29 | SonarI2C (I2C_SONAR_ADDRESS, 4, 4000),
30 | SonarI2C (I2C_SONAR_ADDRESS, 5, 4000),
31 | SonarI2C (I2C_SONAR_ADDRESS, 6, 4000),
32 | SonarI2C (I2C_SONAR_ADDRESS, 7, 4000),
33 | };
34 |
35 |
36 |
37 |
38 | void setup() {
39 | lcd.begin(20, 4); // initialize the lcd
40 | lcd.setBacklight(255); // turn on the backlight
41 | lcd.clear(); // go home
42 | Serial.begin(115200);
43 | SonarI2C::begin(); // initialize bus, pins etc
44 | // SonarI2C::begin(2); // alternate form, set interrupt pin, 2 is the default
45 | // SonarI2C::begin(2, 200); // alternate form also sets spacing, 50ms is the default
46 | SonarI2C::inverse = false;
47 | // SonarI2C::maxOOR = 5;
48 | for (uint8_t i = 0; i < numsonars; i++) {
49 | sonars[i].init();
50 | }
51 | }
52 |
53 | uint32_t last_print = 0;
54 | void loop() {
55 | char buffer[20];
56 | SonarI2C::doSonar(); // call every cycle, SonarI2C handles the spacing
57 |
58 | if (last_print + 500 < millis()) {
59 | last_print = millis();
60 | for (uint8_t i = 0; i < numsonars; i++) {
61 | // Serial.print(sonars[i].mm()); Serial.print(" ");
62 | }
63 | // Serial.println();
64 | sprintf(buffer, "0 %5d %5d 7 ", sonars[0].mm(), sonars[7].mm());
65 | lcd.setCursor(0, 0); lcd.print(buffer);
66 | sprintf(buffer, "1 %5d %5d 6", sonars[1].mm(), sonars[6].mm());
67 | lcd.setCursor(0, 1); lcd.print(buffer);
68 | sprintf(buffer, "2 %5d %5d 5", sonars[2].mm(), sonars[5].mm());
69 | lcd.setCursor(0, 2); lcd.print(buffer);
70 | sprintf(buffer, "3 %5d %5d 4", sonars[3].mm(), sonars[4].mm());
71 | lcd.setCursor(0, 3); lcd.print(buffer);
72 | }
73 | }
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/examples/SonarI2Cv2/SonarI2Cv2.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * This demo goes with the fritzing breadboard example
3 | * two sensors on pins 4 and 5 with the outputs multiplexed back to pin 2
4 | * via transistor NOR gate
5 | */
6 |
7 |
8 | #include "SonarI2C.h"
9 |
10 | SonarI2C frontSonar(0x20, 4, 4000);
11 | SonarI2C backSonar(0x20, 5, 4000);
12 |
13 | void setup() {
14 | Serial.begin(115200);
15 | SonarI2C::begin(); // initialize bus, pins etc
16 | SonarI2C::inverse = true; // NOR logic
17 | // SonarI2C::begin(2); // alternate form, set interrupt pin, 2 is the default
18 | // SonarI2C::begin(2, 50); // alternate form also sets spacing, 50ms is the default
19 |
20 | frontSonar.init(); // clear expander and add to list
21 | backSonar.init(); // clear expander and add to list
22 | }
23 |
24 | uint32_t last_print = 0;
25 | void loop() {
26 | SonarI2C::doSonar(); // call every cycle, SonarI2C handles the spacing
27 |
28 | if (last_print + 100 < millis()) {
29 | last_print=millis();
30 | Serial.print(frontSonar.mm()); Serial.print(" ");
31 | Serial.println(backSonar.mm());
32 | }
33 | }
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/examples/SonarI2Cx10/SonarI2Cx10.ino:
--------------------------------------------------------------------------------
1 |
2 | #include "SonarI2C.h"
3 |
4 | uint8_t numsonars = 10;
5 | SonarI2C sonars[] = {
6 | SonarI2C (0x20, 0, 4000),
7 | SonarI2C (0x20, 1, 4000),
8 | SonarI2C (0x20, 2, 4000),
9 | SonarI2C (0x20, 3, 4000),
10 | SonarI2C (0x20, 4, 4000),
11 | SonarI2C (0x20, 5, 4000),
12 | SonarI2C (0x20, 6, 4000),
13 | SonarI2C (0x20, 7, 4000),
14 | SonarI2C (0x21, 0, 4000),
15 | SonarI2C (0x21, 1, 4000),
16 | };
17 |
18 | void setup() {
19 | Serial.begin(115200);
20 | SonarI2C::begin(); // initialize bus, pins etc
21 | // SonarI2C::begin(2); // alternate form, set interrupt pin, 2 is the default
22 | // SonarI2C::begin(2, 50); // alternate form also sets spacing, 50ms is the default
23 | SonarI2C::inverse = false;
24 | for (uint8_t i = 0; i < numsonars; i++) {
25 | sonars[i].init();
26 | }
27 | }
28 |
29 | uint32_t last_print = 0;
30 | void loop() {
31 | SonarI2C::doSonar(); // call every cycle, SonarI2C handles the spacing
32 |
33 | if (last_print + 100 < millis()) {
34 | last_print = millis();
35 | for (uint8_t i = 0; i < numsonars; i++) {
36 | Serial.print(sonars[i].mm()); Serial.print(" ");
37 | }
38 | Serial.println();
39 | }
40 | }
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/extras/sonarI2Cdemo1_bb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arielnh56/SonarI2C/e67d3c7e3374a9906cc4e1ebb8af111ceececd03/extras/sonarI2Cdemo1_bb.png
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=SonarI2C
2 | version=1.1.2
3 | author=Alastair Young
4 | maintainer=Alastair Young
5 | sentence=A library to support cheap ultrasonic sensors on I2C bus
6 | paragraph=triggers via PCF8574, echo via OR/NOR logic to hardware interrupt. Tested with up to 10 HC-SR04
7 | category=Sensors
8 | url=http://redhunter.com/
9 | architectures=*
10 |
--------------------------------------------------------------------------------
/src/SonarI2C.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | SonarI2C.cpp
3 | Copyright (c) 2016,2017 Alastair Young.
4 | This project is licensed under the terms of the MIT license.
5 | */
6 | #include "SonarI2C.h"
7 | #include
8 |
9 | // constructor
10 | SonarI2C::SonarI2C(uint8_t address, uint8_t pin, uint16_t max_mm) {
11 | _address = (address < 0x38) ? constrain(address, 0x20, 0x27) : constrain(address, 0x38, 0x3F);
12 | _pin = constrain(pin, 0, 7);
13 | _max_micros = constrain(max_mm, 20, 4000) / 0.170145; // constrain to spec sheet
14 | _enabled = true;
15 | _OORcount = 0;
16 | }
17 |
18 | // member initializer - call from setup()
19 | void SonarI2C::init() {
20 | Wire.beginTransmission(_address);
21 | Wire.write(0); // clear the expander
22 | Wire.endTransmission();
23 | _micros = 0;
24 | // insert into circular linked list
25 | if (_currentSonar) {
26 | _nextSonar = _currentSonar->_nextSonar;
27 | _currentSonar->_nextSonar = this;
28 | } else { // new list
29 | _currentSonar = this;
30 | _nextSonar = this;
31 | }
32 | }
33 |
34 | // private member functions
35 | // send the trigger signal and attach the interrupt
36 | void SonarI2C::_send_ping() {
37 | _pulseBegin = 0; // cleared until we see it
38 | attachInterrupt(digitalPinToInterrupt(_interrupt), _startPulse, inverse ? FALLING : RISING); // NOR gate - pulse is inverted
39 | Wire.beginTransmission(_address);
40 | Wire.write(1 << _pin); // set the pin
41 | Wire.endTransmission();
42 | // at 100kHZ this seems to be about 240us pulse. More than the spec but works fine.
43 | Wire.beginTransmission(_address);
44 | Wire.write(0); // clear all pins
45 | Wire.endTransmission();
46 | }
47 |
48 | void SonarI2C::_startPulse() {
49 | _pulseBegin = micros(); // pulse is starting now
50 | attachInterrupt(digitalPinToInterrupt(_interrupt), _endPulse, inverse ? RISING : FALLING); // now look for pulse end
51 | }
52 |
53 | void SonarI2C::_endPulse() {
54 | uint32_t now = micros();
55 | detachInterrupt(digitalPinToInterrupt(_interrupt)); // clean up after ourselves
56 | // ignore wacko values
57 | if (now < _pulseBegin) return; // we started in the future - micros rollover or the like
58 | uint16_t pulseLen = now - _pulseBegin; // calculate length of pulse
59 | if (pulseLen > _currentSonar->_max_micros) { // took too long 0 = out of range
60 | _currentSonar->_OORcount++;
61 | if (_currentSonar->_OORcount > maxOOR) {
62 | _currentSonar->_micros = 0;
63 | } else {
64 | return; //skip this time
65 | }
66 | } else {
67 | _currentSonar->_OORcount = 0;
68 | _currentSonar->_micros = pulseLen;
69 | }
70 | }
71 |
72 | // public member functions
73 | int16_t SonarI2C::us() {
74 | return _micros;
75 | }
76 |
77 | int16_t SonarI2C::mm() {
78 | return _micros * 0.170145;
79 | }
80 |
81 | int16_t SonarI2C::cm() {
82 | return _micros * 0.0170145;
83 | }
84 |
85 | int16_t SonarI2C::inch() {
86 | return _micros * 0.00669862;
87 | }
88 |
89 | void SonarI2C::enable(boolean enabled) {
90 | _enabled = enabled;
91 | if (!enabled) _micros = 0;
92 | }
93 |
94 | // declare private static class variables
95 | SonarI2C *SonarI2C::_currentSonar = NULL;
96 | uint32_t SonarI2C::_last_sonar_millis = 0;
97 | uint32_t SonarI2C::_pulseBegin = 0;
98 | uint8_t SonarI2C::_interrupt = SONAR_INT_PIN;
99 | uint16_t SonarI2C::_spacing = SONAR_SPACING;
100 | boolean SonarI2C::inverse = false;
101 | uint8_t SonarI2C::maxOOR = 1;
102 |
103 | // public static class functions
104 | // call a begin() from setup()
105 |
106 | void SonarI2C::begin(uint8_t interrupt, uint16_t spacing) {
107 | _interrupt = interrupt;
108 | _spacing = spacing;
109 | begin();
110 | }
111 |
112 | void SonarI2C::begin() {
113 | Wire.begin();
114 | pinMode(_interrupt, INPUT);
115 | }
116 |
117 | void SonarI2C::begin(uint8_t interrupt) {
118 | _interrupt = interrupt;
119 | begin();
120 | }
121 |
122 | // call from loop() every cycle
123 | void SonarI2C::doSonar() {
124 | if (!_currentSonar) return; // no sonars initiated
125 | if (digitalRead(_interrupt) == inverse ? LOW : HIGH) return; // interrupt pin is active - skip
126 | if (_last_sonar_millis + _spacing < millis()) {
127 | // look for next enabled sonar
128 | SonarI2C *thisSonar = _currentSonar->_nextSonar;
129 | while (!thisSonar->_enabled) {
130 | if (thisSonar == _currentSonar) return; // no enables found - skip
131 | thisSonar = thisSonar->_nextSonar;
132 | }
133 |
134 | _last_sonar_millis = millis();
135 | _currentSonar = thisSonar;
136 | _currentSonar->_send_ping();
137 | }
138 | }
139 |
140 |
--------------------------------------------------------------------------------
/src/SonarI2C.h:
--------------------------------------------------------------------------------
1 | /*
2 | SonarI2C.cpp
3 | Copyright (c) 2016,2017 Alastair Young.
4 | This project is licensed under the terms of the MIT license.
5 | */
6 | #ifndef SONARI2C_H
7 | #define SONARI2C_H
8 |
9 | #if defined(ARDUINO) && ARDUINO >= 100
10 | #include "Arduino.h"
11 | #else
12 | #include "WProgram.h"
13 | #endif
14 |
15 | #define SONAR_INT_PIN 2
16 | #define SONAR_SPACING 50 // millis to avoid echo
17 |
18 | class SonarI2C {
19 | public:
20 |
21 | SonarI2C(uint8_t address, uint8_t pin, uint16_t max_mm); // constructor
22 |
23 | void init(); // member initializer. Call for each member in setup()
24 | int16_t mm(); // return distance in mm
25 | int16_t cm(); // return distance in cm
26 | int16_t inch(); // return distance in inches
27 | int16_t us(); // return echo time in microseconds
28 | boolean enabled(); // query if enabled
29 | void enable(boolean enabled); // set _enable
30 | static boolean inverse; // is logic inverted from sensor?
31 | static void begin(); // call from setup(). Defaults to pin 2 and 50ms
32 | static void begin(uint8_t interrupt); // same thing but set the pin
33 | static void begin(uint8_t interrupt, uint16_t spacing); // same thing but set the pin and spacing
34 | static void doSonar(); // call every loop()
35 | static uint8_t maxOOR; // how many OOR to skip. Raise this in noisy environments
36 |
37 | private:
38 | uint8_t _address; // PCF8574 0x20 - 0x27 or PCF8574A 0x38 - 0x3F
39 | uint8_t _pin; // expander pin 0 -7
40 | uint16_t _max_micros; // limit in microseconds. Above this return zero.
41 | uint16_t _micros; // last good response time in microseconds
42 | SonarI2C *_nextSonar; // link to the next SonarI2C in the list
43 | boolean _enabled; // skip if enabled = FALSE
44 | void _send_ping(); // trigger the sensor and set the interrupt
45 | uint8_t _OORcount; // count how many out of range pings in a row
46 | static uint32_t _pulseBegin; // to remember when the pulse started
47 | static uint16_t _spacing; // time between pings - default 50ms
48 | static uint8_t _interrupt; // interrupt pin - default 2
49 | static uint32_t _last_sonar_millis; // timestamp of last ping sent
50 | static void _startPulse(); // interrupt callout when a pulse starts
51 | static void _endPulse(); // interrupt callout when a pulse ends
52 | static SonarI2C *_currentSonar; // pointer to the active sensor
53 | };
54 |
55 | #endif
56 |
--------------------------------------------------------------------------------
/src/src.ino:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arielnh56/SonarI2C/e67d3c7e3374a9906cc4e1ebb8af111ceececd03/src/src.ino
--------------------------------------------------------------------------------