Skip to content

Add a servo library (like Arduino Servo.h) #131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Jacalz opened this issue Feb 27, 2020 · 12 comments
Closed

Add a servo library (like Arduino Servo.h) #131

Jacalz opened this issue Feb 27, 2020 · 12 comments
Labels
enhancement New feature or request

Comments

@Jacalz
Copy link

Jacalz commented Feb 27, 2020

For Arduino programming using their language, a specific driver library exists for easy and simple control over Servo parts. This seems to be non supported at the moment when using tinygo.

This functionality would be very useful for especially Arduino users that want to use their servos, but I guess that it might be useful for other boards too, depending on implementation. I will mostly be using it on an Arduino Uno though...

See the Servo Reference and Servo Library Repository for more information.

Related to tinygo-org/tinygo#855 about PWM support needed to implement this.

@deadprogram deadprogram added the enhancement New feature or request label Mar 6, 2020
@wlwatkins
Copy link

IS there any ETA for servo support on arduino uno/nano?

@alankrantas
Copy link
Contributor

alankrantas commented Aug 2, 2020

I wrote a test code that can control servos, however this does not work on 8-bit AVR boards (like Arduino Unos and Nanos), probably not until they can get more accurate timing support.

Modified: making ServoRoutine timing more accurate.

package main

import (
	"machine"
	"time"
)

type Device struct {
	pin      machine.Pin
	on       bool
	angle    uint8
	pulseMin uint16
	pulseMax uint16
}

func Init(pin machine.Pin) Device {
	pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
	return Device{pin: pin, on: false, pulseMin: 600, pulseMax: 2400}
}

func (d *Device) ServoRoutine() {
	for {
		if d.attached {
			pulse := valueMapping(int32(d.angle), 0, 180, d.pulseMin, d.pulseMax)
			d.pin.High()
			time.Sleep(time.Microsecond * time.Duration(pulse))
			d.pin.Low()
			time.Sleep(time.Microsecond * time.Duration(20000.0-pulse))
		}
	}
}

func (d *Device) Angle(angle uint8) {
	if angle < 0 {
		angle = 0
	} else if angle > 180 {
		angle = 180
	}
	d.on = true
	d.angle = angle
}

func (d *Device) PulseRange(min, max uint16) {
	d.pulseMin = min
	d.pulseMax = max
}

func (d *Device) Deinit() {
	d.on = false
}

func valueMapping(value, min, max, newMin, newMax uint16) float32 {
	scale := float32(value-min) / float32(max-min)
	return float32(newMin) + scale*float32(newMax-newMin)
}

func main() {

	servo1 := Init(machine.P8)
	servo2 := Init(machine.P12)

	go servo1.ServoRoutine()
	go servo2.ServoRoutine()

	for i := 0; i <= 2; i++ {
		servo1.Angle(0)
		servo2.Angle(0)
		time.Sleep(time.Millisecond * 1000)

		servo1.Angle(90)
		servo2.Angle(90)
		time.Sleep(time.Millisecond * 1000)

		servo1.Angle(180)
		servo2.Angle(180)
		time.Sleep(time.Millisecond * 1000)
	}

	// stop "PWM" signal (otherwise the servos may turn again when main() ended)
	servo1.Deinit()
	servo2.Deinit()

}

@ewwaller
Copy link

alankrantas: I wrote code very similar to that this weekend for a Cortex M4 (sam19g). I found it was not nearly low enough in jitter to allow for stable servo control. Did you get it to work in practice? Also, a minor comment. The period of the signal should be 20 mSec to provide a 50Hz update, In your code, the period is 20mS + the pulse width.

@alankrantas
Copy link
Contributor

@ewwaller I tested it on micro:bit and Arduino Nano 33 IoT and it actually worked well enough on several types of servos. I guess I was being lazy, since 0.5-2.5 ms is small compared to 20ms.

How about this?

func (d *Device) ServoRoutine() {
	for {
		if d.attached {
			pulse := valueMapping(int32(d.angle), 0, 180, d.pulseMin, d.pulseMax)
			d.pin.High()
			time.Sleep(time.Microsecond * time.Duration(pulse))
			d.pin.Low()
			time.Sleep(time.Microsecond * time.Duration(20000.0-pulse))
		}
	}
}

@ewwaller
Copy link

Looks right.   I am interested in why it may have been more stable on the Nano 33 vs the Cortex M4.  Which scheduler were you using?

@alankrantas
Copy link
Contributor

The default one I think, never tried to change it.

Just did a test on my Metro M4 (powered via the DC jack). Works fine except some very small jittering.

@Nerzal
Copy link
Contributor

Nerzal commented Nov 25, 2020

Won't work on Arduino UNO on any scheduler (none, tasks, coroutines)

@aykevl
Copy link
Member

aykevl commented Dec 14, 2020

I have a prototype servo driver that works on both the nrf52832 and Arduino Uno (AVR) and probably works on the SAM D21 too (so far untested). It is totally jitter free with the one servo I have, a problem that I've heard is common with Servo.h and should allow driving multiple servos from a single PWM instance.

@Nerzal
Copy link
Contributor

Nerzal commented Dec 15, 2020

@aykevl can you give us a link? :)

@aykevl
Copy link
Member

aykevl commented Dec 24, 2020

@Nerzal here is the WIP code: dev...servo
It currently does not compile with tinygo-org/tinygo#1121 as I changed the interface a little bit.

@Nerzal
Copy link
Contributor

Nerzal commented Dec 24, 2020

Thank U ❤️

@deadprogram
Copy link
Member

See #264 so now closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

7 participants