Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save soundanalogous/e94c6ebbc457b98b8383 to your computer and use it in GitHub Desktop.
Save soundanalogous/e94c6ebbc457b98b8383 to your computer and use it in GitHub Desktop.

Revisions

  1. soundanalogous revised this gist Feb 18, 2015. 1 changed file with 8 additions and 5 deletions.
    13 changes: 8 additions & 5 deletions ping.ino
    Original file line number Diff line number Diff line change
    @@ -22,6 +22,7 @@
    byte register_map[REGISTER_MAP_SIZE];

    volatile int32_t duration; // duration of the ping
    int32_t startPulse = 0;
    int32_t last_ping_time = 0;
    int32_t ping_freq = PING_FREQUENCY;

    @@ -31,7 +32,7 @@ int32_t ping_freq = PING_FREQUENCY;
    ISR(PCINT0_vect) {
    // we are interested in when the PING_PIN goes high
    if (digitalRead(PING_PIN) == HIGH) {
    duration = millis() - last_ping_time;
    duration = millis() - startPulse;

    // should never have a print statement or delay
    // in an ISR, but for debugging this may be okay
    @@ -79,7 +80,7 @@ void disablePCInterrupt() {
    cli();

    // disable pin change interrupt
    PCMSK &= ~_BV(PCINT4);
    PCMSK0 &= ~_BV(PCINT4);

    // clear pin change interrupt flag register
    #if defined( __AVR_ATtiny85__ )
    @@ -96,7 +97,7 @@ void enablePCInterrupt() {
    cli();

    // enable pin change interrupt on PB4 (D4 on Trinket, D12 on Uno)
    PCMSK |= _BV(PCINT4);
    PCMSK0 |= _BV(PCINT4);

    // enable pin change interrupt 0
    #if defined( __AVR_ATtiny85__ )
    @@ -105,7 +106,7 @@ void enablePCInterrupt() {
    PCICR |= _BV(PCIE0);
    #endif
    // re-enable all interrupts
    set();
    sei();
    }

    void get_distance() {
    @@ -132,6 +133,8 @@ void get_distance() {
    // Arduino's built-in pulseIn function
    enablePCInterrupt();

    startPulse = millis();

    last_ping_time = millis();
    }
    }
    @@ -157,4 +160,4 @@ void requestData() {
    Serial.print(register_map[1]);
    Serial.println();
    #endif
    }
    }
  2. soundanalogous revised this gist Feb 16, 2015. 1 changed file with 1 addition and 4 deletions.
    5 changes: 1 addition & 4 deletions ping.ino
    Original file line number Diff line number Diff line change
    @@ -22,7 +22,6 @@
    byte register_map[REGISTER_MAP_SIZE];

    volatile int32_t duration; // duration of the ping
    int32_t startPulse = 0;
    int32_t last_ping_time = 0;
    int32_t ping_freq = PING_FREQUENCY;

    @@ -32,7 +31,7 @@ int32_t ping_freq = PING_FREQUENCY;
    ISR(PCINT0_vect) {
    // we are interested in when the PING_PIN goes high
    if (digitalRead(PING_PIN) == HIGH) {
    duration = millis() - startPulse;
    duration = millis() - last_ping_time;

    // should never have a print statement or delay
    // in an ISR, but for debugging this may be okay
    @@ -133,8 +132,6 @@ void get_distance() {
    // Arduino's built-in pulseIn function
    enablePCInterrupt();

    startPulse = millis()

    last_ping_time = millis();
    }
    }
  3. soundanalogous revised this gist Feb 16, 2015. 1 changed file with 125 additions and 64 deletions.
    189 changes: 125 additions & 64 deletions ping.ino
    Original file line number Diff line number Diff line change
    @@ -1,102 +1,163 @@
    #define I2C_SENSOR_ADDRESS 0x27
    #define I2C_SENSOR_ADDRESS 0x27
    #define REGISTER_MAP_SIZE 2
    #define PING_PIN 4
    #define PING_FREQUENCY 150 // milliseconds between pings

    // Uncomment to use ATMEGA
    //#include <Wire.h>
    #include <avr/interrupt.h>


    // Uncomment to use ATTINY
    #include <TinyWireS.h>
    #include <avr/power.h>
    #if defined( __AVR_ATtiny85__ )
    #include <TinyWireS.h>
    #include <avr/power.h>
    #define PING_PIN 4
    #else
    #include <Wire.h>
    #define PING_PIN 12
    #endif


    // this won't work on ATTINY
    #ifndef _DEBUG
    #define _DEBUG false
    #define _DEBUG false
    #endif

    byte register_map[REGISTER_MAP_SIZE];

    int32_t duration; // duration of the ping
    volatile int32_t duration; // duration of the ping
    int32_t startPulse = 0;
    int32_t last_ping_time = 0;
    int32_t ping_freq = PING_FREQUENCY;

    // Interrupt vector for external interrupt on pin PCINT7..0
    // This will be called when any of the pins D0 - D4 on the trinket change
    // or pins D8 - D13 on an Arduino Uno change.
    ISR(PCINT0_vect) {
    // we are interested in when the PING_PIN goes high
    if (digitalRead(PING_PIN) == HIGH) {
    duration = millis() - startPulse;

    // should never have a print statement or delay
    // in an ISR, but for debugging this may be okay
    #if _DEBUG
    Serial.println(duration);
    #endif
    }
    }

    void setup(){

    // Use Wire commands for ATMEGA
    // Wire.begin(I2C_SENSOR_ADDRESS);
    // Wire.onRequest(requestData);

    // ATTINY
    void setup() {

    // Set prescaler so CPU runs at 16MHz
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
    #if defined( __AVR_ATtiny85__ )
    // Set prescaler so CPU runs at 16MHz
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);

    // Use TinyWire for ATTINY
    TinyWireS.begin(I2C_SENSOR_ADDRESS);
    TinyWireS.onRequest(requestData);
    // Use TinyWire for ATTINY
    TinyWireS.begin(I2C_SENSOR_ADDRESS);
    TinyWireS.onRequest(requestData);
    #else
    Wire.begin(I2C_SENSOR_ADDRESS);
    Wire.onRequest(requestData);
    #endif

    #if _DEBUG
    Serial.begin(9600);
    Serial.println("Ping Sensor I2C");
    #endif
    #if _DEBUG
    Serial.begin(9600);
    Serial.println("Ping Sensor I2C");
    #endif
    }



    void loop(){
    void loop() {

    get_distance();

    #if defined( __AVR_ATtiny85__ )
    // USE this for ATTINY as you can't use delay
    TinyWireS_stop_check();
    #else
    delay(20);
    #endif
    }

    void disablePCInterrupt() {
    // disable all interrupts temporarily
    cli();

    // disable pin change interrupt
    PCMSK &= ~_BV(PCINT4);

    // clear pin change interrupt flag register
    #if defined( __AVR_ATtiny85__ )
    GIFR &= ~_BV(PCIF);
    #else
    PCIFR &= ~_BV(PCIF2);
    #endif
    // re-enable all interrupts
    sei();
    }

    void enablePCInterrupt() {
    // disable all interrupts temporarily
    cli();

    // enable pin change interrupt on PB4 (D4 on Trinket, D12 on Uno)
    PCMSK |= _BV(PCINT4);

    // Use this for ATMEGA
    // delay(20);
    // enable pin change interrupt 0
    #if defined( __AVR_ATtiny85__ )
    GIMSK |= _BV(PCIE);
    #else
    PCICR |= _BV(PCIE0);
    #endif
    // re-enable all interrupts
    set();
    }

    void get_distance() {

    if ((millis() - last_ping_time) > ping_freq) {

    pinMode(PING_PIN, OUTPUT);
    digitalWrite(PING_PIN, LOW);
    delayMicroseconds(2);
    digitalWrite(PING_PIN, HIGH);
    delayMicroseconds(5);
    digitalWrite(PING_PIN, LOW);

    pinMode(PING_PIN, INPUT);
    duration = pulseIn(PING_PIN, HIGH);

    register_map[0] = duration >> 8; // msb
    register_map[1] = duration & 0xFF; //LSB

    #if _DEBUG
    Serial.println(duration);
    #endif

    last_ping_time = millis();
    }
    if ((millis() - last_ping_time) > ping_freq) {

    // disable interrupt while pinMode is OUTPUT
    // not sure if this is actually necessary, but just
    // playing it safe to avoid false interrupt when
    // pin mode is OUTPUT
    disablePCInterrupt();

    pinMode(PING_PIN, OUTPUT);
    digitalWrite(PING_PIN, LOW);
    delayMicroseconds(2);
    digitalWrite(PING_PIN, HIGH);
    delayMicroseconds(5);
    digitalWrite(PING_PIN, LOW);

    pinMode(PING_PIN, INPUT);

    // we'll use a pin change interrupt to notify when the
    // ping pin goes high rather than being blocked by
    // Arduino's built-in pulseIn function
    enablePCInterrupt();

    startPulse = millis()

    last_ping_time = millis();
    }
    }

    void requestData(){
    void requestData() {

    // ATMEGA cat write out a buffer
    // Wire.write(register_map, REGISTER_MAP_SIZE);
    register_map[0] = duration >> 8; // msb
    register_map[1] = duration & 0xFF; //LSB

    // ATTINY you need to do a send of each register individually.
    TinyWireS.send(register_map[0]);
    TinyWireS.send(register_map[1]);
    #if defined( __AVR_ATtiny85__ )
    // ATTINY you need to do a send of each register individually.
    TinyWireS.send(register_map[0]);
    TinyWireS.send(register_map[1]);
    #else
    // ATMEGA cat write out a buffer
    Wire.write(register_map, REGISTER_MAP_SIZE);
    #endif

    #if _DEBUG
    Serial.print("rm: ");
    Serial.print(register_map[0]);
    Serial.print(" ");
    Serial.print(register_map[1]);
    Serial.println();
    #endif
    #if _DEBUG
    Serial.print("rm: ");
    Serial.print(register_map[0]);
    Serial.print(" ");
    Serial.print(register_map[1]);
    Serial.println();
    #endif
    }
  4. @ajfisher ajfisher revised this gist Feb 15, 2015. 2 changed files with 0 additions and 1 deletion.
    File renamed without changes.
    1 change: 0 additions & 1 deletion ping.ino
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,3 @@

    #define I2C_SENSOR_ADDRESS 0x27
    #define REGISTER_MAP_SIZE 2
    #define PING_PIN 4
  5. @ajfisher ajfisher created this gist Feb 15, 2015.
    53 changes: 53 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,53 @@
    # Building an ultrasonic sensor backpack

    __Note: This is a bit of a scratch to see what's involved in getting the ping sensor to work as an I2C backpack.__

    ## Dependencies.

    I did this on a Trinket as that's what I had to hand to make programming a bit faster. If you use a trinket you
    will need the custom arduino. Check out the Adafruit Trinket info https://learn.adafruit.com/introducing-trinket/introduction

    You also need a custom form of the TinyWireS library that manages sending multiple registers on one read request (the
    default implementation doesn't do this and locks up the buffer). This can be had here: https://github.com/Godziluu/TinyWire/ Do the usual thing and install into libraries folder of your sketch directory.

    ## Files

    ping.ino can work on both an ATTiny85 and standard Arduino. It sets up as an I2C peripheral device.
    However, ATTINY can't use the wire library so has to use tiny wire. Read the code for the places to comment /
    uncomment to get this to work.

    ping.js A hack of the existing ping.js library in J5 using that as the base. Removed things that were no longer
    necessary and migrated relevant calls to I2C rather than pin manipulation.

    pingtest.js is simply a demo to get it spitting out the data events. Note that this is using a hardcoded
    port just to eliminate any confusion across devices (I was using an arduino at one point so was getting port
    conflicts - easier to be explicit).


    ## Usage

    * Arduino requires only standard firmata
    * Wire up the Trinket / ATTINY85 so that the ping trigger / echo is on pin 4.
    * Wire up VCC and GND, then SDA and SCL to the arduino master.

    Power everything up then run your sketch.

    ```
    npm install johnny-five
    node ping-test.js
    ```

    You should get an output of distance in CM.

    ## Known issues

    At the moment you get a bit of lag creep in after a while. I think this might be something to do
    with the I2C send buffer but will dig in more.

    ## Todo

    * Track down this lag thing
    * Try this on a raw ATTINY using avr-gcc etc so no arduino deps
    * Figure out how this comes back into Johnny-five as backpack


    103 changes: 103 additions & 0 deletions ping.ino
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,103 @@

    #define I2C_SENSOR_ADDRESS 0x27
    #define REGISTER_MAP_SIZE 2
    #define PING_PIN 4
    #define PING_FREQUENCY 150 // milliseconds between pings

    // Uncomment to use ATMEGA
    //#include <Wire.h>


    // Uncomment to use ATTINY
    #include <TinyWireS.h>
    #include <avr/power.h>


    // this won't work on ATTINY
    #ifndef _DEBUG
    #define _DEBUG false
    #endif

    byte register_map[REGISTER_MAP_SIZE];

    int32_t duration; // duration of the ping
    int32_t last_ping_time = 0;
    int32_t ping_freq = PING_FREQUENCY;


    void setup(){

    // Use Wire commands for ATMEGA
    // Wire.begin(I2C_SENSOR_ADDRESS);
    // Wire.onRequest(requestData);

    // ATTINY

    // Set prescaler so CPU runs at 16MHz
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);

    // Use TinyWire for ATTINY
    TinyWireS.begin(I2C_SENSOR_ADDRESS);
    TinyWireS.onRequest(requestData);

    #if _DEBUG
    Serial.begin(9600);
    Serial.println("Ping Sensor I2C");
    #endif
    }



    void loop(){

    get_distance();

    // USE this for ATTINY as you can't use delay
    TinyWireS_stop_check();

    // Use this for ATMEGA
    // delay(20);
    }

    void get_distance() {

    if ((millis() - last_ping_time) > ping_freq) {

    pinMode(PING_PIN, OUTPUT);
    digitalWrite(PING_PIN, LOW);
    delayMicroseconds(2);
    digitalWrite(PING_PIN, HIGH);
    delayMicroseconds(5);
    digitalWrite(PING_PIN, LOW);

    pinMode(PING_PIN, INPUT);
    duration = pulseIn(PING_PIN, HIGH);

    register_map[0] = duration >> 8; // msb
    register_map[1] = duration & 0xFF; //LSB

    #if _DEBUG
    Serial.println(duration);
    #endif

    last_ping_time = millis();
    }
    }

    void requestData(){

    // ATMEGA cat write out a buffer
    // Wire.write(register_map, REGISTER_MAP_SIZE);

    // ATTINY you need to do a send of each register individually.
    TinyWireS.send(register_map[0]);
    TinyWireS.send(register_map[1]);

    #if _DEBUG
    Serial.print("rm: ");
    Serial.print(register_map[0]);
    Serial.print(" ");
    Serial.print(register_map[1]);
    Serial.println();
    #endif
    }
    102 changes: 102 additions & 0 deletions ping.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,102 @@
    var events = require("events"),
    util = require("util");

    var priv = new Map();

    /**
    * Ping
    * @param {Object} opts Options: addr freq
    */

    function Ping(opts) {

    if (!(this instanceof Ping)) {
    return new Ping(opts);
    }

    var last = null;

    this.addr = opts && opts.addr || 0x27;
    this.freq = opts.freq || 100;
    this.board = opts.board || null;

    var state = {
    value: 0
    };

    // Private settings object
    var settings = {
    addr: this.addr,
    value: this.board.io.HIGH,
    readBytes: 2, // the duration that comes off the ping is a 16 bit int
    pingFreq: 110, // an optimium period to poll the sensor on i2c.
    };

    this.board.io.setMaxListeners(100);

    this.board.io.i2cConfig();
    // Interval for polling pulse duration as reported in microseconds
    setInterval(function() {
    this.board.io.i2cReadOnce(settings.addr, settings.readBytes, function(data) {
    state.value = (data[0] << 8) + data[1];
    });
    }.bind(this), settings.pingFreq);

    // Interval for throttled event
    setInterval(function() {
    var err = null;

    if (!state.value) {
    return;
    }

    // The "read" event has been deprecated in
    // favor of a "data" event.
    this.emit("data", err, state.value);

    // If the state.value for this interval is not the same as the
    // state.value in the last interval, fire a "change" event.
    if (state.value !== last) {
    this.emit("change", err, state.value);
    }

    // Store state.value for comparison in next interval
    last = state.value;

    // Reset samples;
    // samples.length = 0;
    }.bind(this), this.freq);

    Object.defineProperties(this, {
    value: {
    get: function() {
    return state.value;
    }
    },
    // Based on the round trip travel time in microseconds,
    // Calculate the distance in inches and centimeters
    inches: {
    get: function() {
    return +(state.value / 74 / 2).toFixed(2);
    }
    },
    in: {
    get: function() {
    return this.inches;
    }
    },
    cm: {
    get: function() {
    return +(state.value / 29 / 2).toFixed(3);
    }
    }
    });

    priv.set(this, state);
    }

    util.inherits(Ping, events.EventEmitter);

    module.exports = Ping;

    //http://protolab.pbworks.com/w/page/19403657/TutorialPings
    22 changes: 22 additions & 0 deletions pingtest.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    var five = require("johnny-five");
    var ping = require('./ping.js');

    var board = five.Board({port: '/dev/ttyACM0'});

    board.on('ready', function() {

    var p = new ping({
    addr: 0x27,
    freq: 300,
    board: this,
    });

    p.on("data", function(){
    console.log(this.cm);
    });
    });

    board.on('error', function(err) {
    console.log(err);
    process.exit();
    });