tinygo-org / drivers

TinyGo drivers for sensors, displays, wireless adaptors, and other devices that use I2C, SPI, GPIO, ADC, and UART interfaces.
https://tinygo.org
BSD 3-Clause "New" or "Revised" License
617 stars 195 forks source link

HC-SR04 reads 0mm no matter what #223

Closed patrickhener closed 3 years ago

patrickhener commented 3 years ago

I followed the example of HC-SR04 and tried the official drivers package.

But no matter what I do it always reads 0mm.

What am I doing wrong and which additional information can I provide?

tinygo version 0.16.0 linux/amd64 (using go version go1.15.7 and LLVM version 11.0.1)

On my arch linux machine

tinygo build -o arduino.hex -size=short -target=arduino-mega2560 arduino.go

On my pi connected to Arduino

 avrdude -p m2560 -D -c wiring -P /dev/ttyACM0 -b 115200 -v -F -U flash:w:arduino.hex

Main func

    sensor := hcsr04.New(machine.D9, machine.D8)
    sensor.Configure()

    println("Ultrasonic starts")
    for {
        println("Distance:", sensor.ReadDistance(), "mm")

        time.Sleep(100 * time.Millisecond)
    }

Thanks for the help

conejoninja commented 3 years ago

Hello patrick, I'm assuming hardware is fine and everything connected as it should, but please check pin numbers and that trigger/echo is correct too.

Try sensor.ReadPulse instead of sensor.ReadDistance, if it still returns 0, then the timeout is reached, which means the echo pin didn't get a signal.

patrickhener commented 3 years ago

I don't think it is timeout to be honest. I added this code parts at the timeout statement:

        if i > 10 {
            if time.Since(t).Microseconds() > TIMEOUT {
                println("Timeout reached")
                return 0
            }
            i = 0
        }

And the result is not Timeout reached before I get the 0 mm output.

I double- and triple-checked pins which are correct. Do I need to somehow init() the PWM mode on my Arduino Mega2560 with tinygo as I would need for ADC?

Just for reference here is a ino-sketch which works:

const int echoPin = 8;
const int trigPin = 9;
long duration;
int distance;

void setup() {
    pinMode(trigPin, OUTPUT);
    pinMode(echoPin, INPUT);
    Serial.begin(9600);
}

void loop() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = duration*0.034/2;
  Serial.println(distance);
  delay(100);
}

The output of it will look like:

4
4
4
4
26
26
27
26
26
...

So pins and hardware is fine. Any suggestions?

conejoninja commented 3 years ago

There are 3 parts where it returns 0, ln 62, 75 and 80. Did you put the println in all of them?

patrickhener commented 3 years ago

This is my hcsr04.go

// Package hcsr04 provides a driver for the HC-SR04 ultrasonic distance sensor
//
// Datasheet:
// https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf
package hcsr04

import (
    "machine"
    "time"
)

const TIMEOUT = 23324 // max sensing distance (4m)

// Device holds the pins
type Device struct {
    trigger machine.Pin
    echo    machine.Pin
}

// New returns a new ultrasonic driver given 2 pins
func New(trigger, echo machine.Pin) Device {
    return Device{
        trigger: trigger,
        echo:    echo,
    }
}

// Configure configures the pins of the Device
func (d *Device) Configure() {
    d.trigger.Configure(machine.PinConfig{Mode: machine.PinOutput})
    d.echo.Configure(machine.PinConfig{Mode: machine.PinInput})
}

// ReadDistance returns the distance of the object in mm
func (d *Device) ReadDistance() int32 {
    pulse := d.ReadPulse()

    // sound speed is 343000 mm/s
    // pulse is roundtrip measured in microseconds
    // distance = velocity * time
    // 2 * distance = 343000 * (pulse/1000000)
    return (pulse * 1715) / 10000 //mm
}

// ReadPulse returns the time of the pulse (roundtrip) in microseconds
func (d *Device) ReadPulse() int32 {
    t := time.Now()
    d.trigger.Low()
    time.Sleep(2 * time.Microsecond)
    d.trigger.High()
    time.Sleep(10 * time.Microsecond)
    d.trigger.Low()
    i := uint8(0)
    for {
        if d.echo.Get() {
            t = time.Now()
            break
        }
        i++
        if i > 10 {
            if time.Since(t).Microseconds() > TIMEOUT {
                println("Timeout reached")
                return 0
            }
            i = 0
        }
    }
    i = 0
    for {
        if !d.echo.Get() {
            return int32(time.Since(t).Microseconds())
        }
        i++
        if i > 10 {
            if time.Since(t).Microseconds() > TIMEOUT {
                println("Timeout reached")
                return 0
            }
            i = 0
        }
    }
    println("Timeout reached")
    return 0
}

This is my main.go

package main

import (
    "machine"
    "time"

    hcsr04 "github.com/dascr/dascr-machine/arduino/hcsrc04"
)

const (
    // Ultrasonic
    echoPin    = machine.D8
    triggerPin = machine.D9
)

func main() {
    ultrasonic := hcsr04.New(echoPin, triggerPin)
    ultrasonic.Configure()

    for {
        distance := ultrasonic.ReadDistance()
        println(distance)
        time.Sleep(time.Millisecond * 100)
    }
}
conejoninja commented 3 years ago

I'll need to find my atmega2560 and try to reproduce your issue, I'm out of ideas since ReadPulse should not be returning 0 if it's not a timeout/invalid measure

patrickhener commented 3 years ago

okay thanks for the effort. I really appreciate it. I am keen to see if you get it to work then. Thanks

Nerzal commented 3 years ago

Could be just related to the AVR backend Screenshot from the TinyGo slack. A few days ago, i had problems getting the ultrasonic distance sensor to run on an Arduino UNO.

grafik

patrickhener commented 3 years ago

So I guess I need to stick to C++/Arduino IDE for now.

conejoninja commented 3 years ago

I was afraid it was related, that's why I wanted to check myself. I will close this issue then.