refactor: rewrite and fix pca9685 controllers
* implement throttle controller directly with pca9685 pin * close actuator controllers on exit * add trace logs * handle TERM signals to properly exit * add info logs at startup
This commit is contained in:
		| @@ -3,11 +3,12 @@ package main | |||||||
| import ( | import ( | ||||||
| 	"flag" | 	"flag" | ||||||
| 	"github.com/cyrilix/robocar-base/cli" | 	"github.com/cyrilix/robocar-base/cli" | ||||||
| 	actuator2 "github.com/cyrilix/robocar-pca9685/pkg/actuator" | 	"github.com/cyrilix/robocar-pca9685/pkg/actuator" | ||||||
| 	"github.com/cyrilix/robocar-pca9685/pkg/part" | 	"github.com/cyrilix/robocar-pca9685/pkg/part" | ||||||
| 	"go.uber.org/zap" | 	"go.uber.org/zap" | ||||||
| 	"log" | 	"log" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"periph.io/x/conn/v3/physic" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -21,6 +22,8 @@ const ( | |||||||
|  |  | ||||||
| 	SteeringLeftPWM  = 1004 | 	SteeringLeftPWM  = 1004 | ||||||
| 	SteeringRightPWM = 1986 | 	SteeringRightPWM = 1986 | ||||||
|  |  | ||||||
|  | 	DefaultFrequency = 60 * physic.Hertz | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| @@ -106,10 +109,47 @@ func main() { | |||||||
| 	} | 	} | ||||||
| 	defer client.Disconnect(50) | 	defer client.Disconnect(50) | ||||||
|  |  | ||||||
| 	t := actuator2.NewThrottle(throttleChannel, throttleStoppedPWM, throttleMinPWM, throttleMaxPWM) | 	freq := DefaultFrequency | ||||||
| 	s := actuator2.NewSteering(steeringChannel, steeringLeftPWM, steeringRightPWM, steeringCenterPWM) |  | ||||||
|  | 	zap.S().Infof("throttle channel  : %v", throttleChannel) | ||||||
|  | 	zap.S().Infof("throttle frequency: %v", freq) | ||||||
|  | 	zap.S().Infof("throttle zero     : %v", throttleStoppedPWM) | ||||||
|  | 	zap.S().Infof("throttle min      : %v", throttleMinPWM) | ||||||
|  | 	zap.S().Infof("throttle max      : %v", throttleMaxPWM) | ||||||
|  |  | ||||||
|  | 	zap.S().Infof("steering channel  : %v", steeringChannel) | ||||||
|  | 	zap.S().Infof("steering frequency: %v", freq) | ||||||
|  | 	zap.S().Infof("steering center   : %v", steeringCenterPWM) | ||||||
|  | 	zap.S().Infof("steering left     : %v", steeringLeftPWM) | ||||||
|  | 	zap.S().Infof("steering right    : %v", steeringRightPWM) | ||||||
|  |  | ||||||
|  | 	dev := actuator.NewDevice(freq) | ||||||
|  |  | ||||||
|  | 	t, err := actuator.NewPca9685Controller( | ||||||
|  | 		dev, | ||||||
|  | 		throttleChannel, | ||||||
|  | 		actuator.PWM(throttleMinPWM), actuator.PWM(throttleMaxPWM), actuator.PWM(throttleStoppedPWM), | ||||||
|  | 		freq, | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		zap.S().Panicf("unable to init throttle controller: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s, err := actuator.NewPca9685Controller( | ||||||
|  | 		dev, | ||||||
|  | 		steeringChannel, | ||||||
|  | 		actuator.PWM(steeringLeftPWM), actuator.PWM(steeringRightPWM), actuator.PWM(steeringCenterPWM), | ||||||
|  | 		freq, | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		zap.S().Panicf("unable to init steering controller: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	p := part.NewPca9685Part(client, t, s, updatePWMFrequency, topicThrottle, topicSteering) | 	p := part.NewPca9685Part(client, t, s, updatePWMFrequency, topicThrottle, topicSteering) | ||||||
|  |  | ||||||
|  | 	cli.HandleExit(p) | ||||||
|  |  | ||||||
|  | 	zap.S().Info("devices ready, start event listener") | ||||||
| 	err = p.Start() | 	err = p.Start() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		zap.S().Fatalf("unable to start service: %v", err) | 		zap.S().Fatalf("unable to start service: %v", err) | ||||||
|   | |||||||
| @@ -1,39 +1,112 @@ | |||||||
| package actuator | package actuator | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"github.com/cyrilix/robocar-pca9685/pkg/util" | ||||||
| 	"go.uber.org/zap" | 	"go.uber.org/zap" | ||||||
|  | 	"periph.io/x/conn/v3/gpio" | ||||||
| 	"periph.io/x/conn/v3/i2c/i2creg" | 	"periph.io/x/conn/v3/i2c/i2creg" | ||||||
| 	"periph.io/x/conn/v3/physic" | 	"periph.io/x/conn/v3/physic" | ||||||
| 	"periph.io/x/devices/v3/pca9685" | 	"periph.io/x/devices/v3/pca9685" | ||||||
| 	"periph.io/x/host/v3" | 	"periph.io/x/host/v3" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | const ( | ||||||
| 	device *pca9685.Dev | 	MinPercent = -1. | ||||||
|  | 	MaxPercent = 1. | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func init() { | type PWM int | ||||||
| 	zap.S().Info("init pca9685 controller") |  | ||||||
|  | func NewDevice(freq physic.Frequency) *pca9685.Dev { | ||||||
|  | 	zap.S().Info("NewDevice pca9685 controller") | ||||||
| 	_, err := host.Init() | 	_, err := host.Init() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		zap.S().Fatalf("unable to init host: %v", err) | 		zap.S().Fatalf("unable to NewDevice host: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	zap.S().Info("open i2c bus") | 	zap.S().Info("open i2c bus") | ||||||
| 	bus, err := i2creg.Open("") | 	bus, err := i2creg.Open("") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		zap.S().Fatalf("unable to init i2c bus: %v", err) | 		zap.S().Fatalf("unable to NewDevice i2c bus: %v", err) | ||||||
| 	} | 	} | ||||||
| 	zap.S().Info("i2c bus opened") | 	zap.S().Infof("i2c bus opened: %v", bus) | ||||||
|  |  | ||||||
| 	device, err = pca9685.NewI2C(bus, pca9685.I2CAddr) | 	device, err := pca9685.NewI2C(bus, pca9685.I2CAddr) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		zap.S().Fatalf("unable to init pca9685 bus: %v", err) | 		zap.S().Fatalf("unable to NewDevice pca9685 bus: %v", err) | ||||||
| 	} | 	} | ||||||
| 	zap.S().Infof("set pwm frequency to %d", 60) | 	zap.S().Infof("set pwm frequency to %d", 60) | ||||||
| 	err = device.SetPwmFreq(60 * physic.Hertz) | 	err = device.SetPwmFreq(freq) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		zap.S().Panicf("unable to set pwm frequency: %v", err) | 		zap.S().Panicf("unable to set pwm frequency: %v", err) | ||||||
| 	} | 	} | ||||||
| 	zap.S().Info("init done") | 	zap.S().Info("NewDevice done") | ||||||
|  | 	return device | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func convertToDuty(percent float32, freq physic.Frequency, centerPWM, minPWM, maxPWM PWM) (gpio.Duty, error) { | ||||||
|  | 	// map absolute angle to angle that vehicle can implement. | ||||||
|  | 	pw := int(centerPWM) | ||||||
|  | 	if percent > 0 { | ||||||
|  | 		pw = util.MapRange(float64(percent), 0, MaxPercent, float64(centerPWM), float64(maxPWM)) | ||||||
|  | 	} else if percent < 0 { | ||||||
|  | 		pw = util.MapRange(float64(percent), MinPercent, 0, float64(minPWM), float64(centerPWM)) | ||||||
|  | 	} | ||||||
|  | 	zap.S().Debugf("convert value %v to pw: %v", percent, pw) | ||||||
|  |  | ||||||
|  | 	per := freq.Period().Microseconds() | ||||||
|  |  | ||||||
|  | 	draw := float64(pw) / float64(per) | ||||||
|  | 	d, err := gpio.ParseDuty(fmt.Sprintf("%.f%%", draw*100)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, fmt.Errorf("unable to parse duty, probably bad compute: %w", err) | ||||||
|  | 	} | ||||||
|  | 	return d, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Pca9685Controller struct { | ||||||
|  | 	pin                        gpio.PinIO | ||||||
|  | 	minPWM, maxPWM, neutralPWM PWM | ||||||
|  | 	freq                       physic.Frequency | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *Pca9685Controller) Close() error { | ||||||
|  | 	return c.pin.Halt() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *Pca9685Controller) SetDuty(d gpio.Duty) error { | ||||||
|  | 	err := c.pin.PWM(d, c.freq) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("unable to set pwm value: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetPercentValue Set percent value | ||||||
|  | func (c *Pca9685Controller) SetPercentValue(p float32) error { | ||||||
|  | 	d, err := convertToDuty(p, c.freq, c.neutralPWM, c.minPWM, c.maxPWM) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("unable to compute Duty value for steering: %w", err) | ||||||
|  | 	} | ||||||
|  | 	err = c.SetDuty(d) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("unable to apply duty value '%v' for pin '%v': '%w' ", d, c.pin.Name(), err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewPca9685Controller(device *pca9685.Dev, channel int, minPWM, maxPWM, neutralPWM PWM, freq physic.Frequency) (*Pca9685Controller, error) { | ||||||
|  | 	p, err := device.CreatePin(channel) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("unable to create pin for channel %v: %w", channel, err) | ||||||
|  | 	} | ||||||
|  | 	s := Pca9685Controller{ | ||||||
|  | 		pin:        p, | ||||||
|  | 		minPWM:     minPWM, | ||||||
|  | 		maxPWM:     maxPWM, | ||||||
|  | 		neutralPWM: neutralPWM, | ||||||
|  | 		freq:       freq, | ||||||
|  | 	} | ||||||
|  | 	return &s, nil | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								pkg/actuator/pca9685_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								pkg/actuator/pca9685_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | package actuator | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | 	"periph.io/x/conn/v3/physic" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	l, _ := zap.NewDevelopment() | ||||||
|  | 	zap.ReplaceGlobals(l) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Test_convertToDuty(t *testing.T) { | ||||||
|  | 	type fields struct { | ||||||
|  | 	} | ||||||
|  | 	type args struct { | ||||||
|  | 		percent   float32 | ||||||
|  | 		leftPWM   PWM | ||||||
|  | 		rightPWM  PWM | ||||||
|  | 		centerPWM PWM | ||||||
|  | 		freq      physic.Frequency | ||||||
|  | 	} | ||||||
|  | 	tests := []struct { | ||||||
|  | 		name    string | ||||||
|  | 		fields  fields | ||||||
|  | 		args    args | ||||||
|  | 		want    string | ||||||
|  | 		wantErr bool | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			name: "center", | ||||||
|  | 			args: args{ | ||||||
|  | 				percent:   0., | ||||||
|  | 				leftPWM:   1000, | ||||||
|  | 				rightPWM:  2000, | ||||||
|  | 				centerPWM: 1500, | ||||||
|  | 				freq:      60 * physic.Hertz, | ||||||
|  | 			}, | ||||||
|  | 			want: "9%", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "left", | ||||||
|  | 			args: args{ | ||||||
|  | 				percent:   -1., | ||||||
|  | 				leftPWM:   1000, | ||||||
|  | 				rightPWM:  2000, | ||||||
|  | 				centerPWM: 1500, | ||||||
|  | 				freq:      60 * physic.Hertz, | ||||||
|  | 			}, | ||||||
|  | 			want: "6%", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "right", | ||||||
|  | 			args: args{ | ||||||
|  | 				percent:   1., | ||||||
|  | 				leftPWM:   1000, | ||||||
|  | 				rightPWM:  2000, | ||||||
|  | 				centerPWM: 1500, | ||||||
|  | 				freq:      60 * physic.Hertz, | ||||||
|  | 			}, | ||||||
|  | 			want: "12%", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	for _, tt := range tests { | ||||||
|  | 		t.Run(tt.name, func(t *testing.T) { | ||||||
|  |  | ||||||
|  | 			if got, err := convertToDuty(tt.args.percent, tt.args.freq, tt.args.centerPWM, tt.args.leftPWM, tt.args.rightPWM); got.String() != tt.want { | ||||||
|  | 				if tt.wantErr && err == nil { | ||||||
|  | 					t.Errorf("an error is expected") | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				if err != nil { | ||||||
|  | 					t.Errorf("unexpected error: %v", err) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				t.Errorf("convertToDuty() = %v, want %v", got, tt.want) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,52 +0,0 @@ | |||||||
| package actuator |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/cyrilix/robocar-pca9685/pkg/util" |  | ||||||
| 	"go.uber.org/zap" |  | ||||||
| 	"periph.io/x/conn/v3/gpio" |  | ||||||
| 	"periph.io/x/devices/v3/pca9685" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	LeftAngle  = -1. |  | ||||||
| 	RightAngle = 1. |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type Steering struct { |  | ||||||
| 	channel                      int |  | ||||||
| 	leftPWM, rightPWM, centerPWM int |  | ||||||
| 	dev                          *pca9685.Dev |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *Steering) SetPulse(pulse int) { |  | ||||||
| 	err := s.dev.SetPwm(s.channel, 0, gpio.Duty(pulse)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		zap.S().Warnf("unable to set steering pwm value: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SetPercentValue Set percent value steering |  | ||||||
| func (s *Steering) SetPercentValue(p float32) { |  | ||||||
| 	// map absolute angle to angle that vehicle can implement. |  | ||||||
|  |  | ||||||
| 	pulse := 0 |  | ||||||
| 	if p > 0 { |  | ||||||
| 		pulse = util.MapRange(float64(p), 0, RightAngle, float64(s.centerPWM), float64(s.rightPWM)) |  | ||||||
| 	} else if p < 0 { |  | ||||||
| 		pulse = util.MapRange(float64(p), LeftAngle, 0, float64(s.leftPWM), float64(s.centerPWM)) |  | ||||||
| 	} |  | ||||||
| 	zap.S().Debugf("convert steering %v to %v", p, pulse) |  | ||||||
| 	s.SetPulse(pulse) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewSteering(channel, leftPWM, rightPWM, centerPWM int) *Steering { |  | ||||||
| 	s := Steering{ |  | ||||||
| 		channel:   channel, |  | ||||||
| 		dev:       device, |  | ||||||
| 		leftPWM:   leftPWM, |  | ||||||
| 		rightPWM:  rightPWM, |  | ||||||
| 		centerPWM: centerPWM, |  | ||||||
| 	} |  | ||||||
| 	return &s |  | ||||||
| } |  | ||||||
| @@ -1,54 +0,0 @@ | |||||||
| package actuator |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/cyrilix/robocar-pca9685/pkg/util" |  | ||||||
| 	"go.uber.org/zap" |  | ||||||
| 	"periph.io/x/conn/v3/gpio" |  | ||||||
| 	"periph.io/x/devices/v3/pca9685" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	MinThrottle = -1 |  | ||||||
| 	MaxThrottle = 1 |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type Throttle struct { |  | ||||||
| 	channel                       int |  | ||||||
| 	zeroPulse, minPulse, maxPulse int |  | ||||||
| 	dev                           *pca9685.Dev |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (t *Throttle) SetPulse(pulse int) { |  | ||||||
| 	err := t.dev.SetPwm(t.channel, 0, gpio.Duty(pulse)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		zap.S().Errorf("unable to set throttle pwm value: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Set percent value throttle |  | ||||||
| func (t *Throttle) SetPercentValue(p float32) { |  | ||||||
| 	var pulse int |  | ||||||
| 	if p > 0 { |  | ||||||
| 		pulse = util.MapRange(float64(p), 0, MaxThrottle, float64(t.zeroPulse), float64(t.maxPulse)) |  | ||||||
| 	} else { |  | ||||||
| 		pulse = util.MapRange(float64(p), MinThrottle, 0, float64(t.minPulse), float64(t.zeroPulse)) |  | ||||||
| 	} |  | ||||||
| 	zap.S().Debugf("set throttle to %v-> %v (%v, %v, %v, %v, %v)", p, pulse, LeftAngle, RightAngle, t.minPulse, t.maxPulse, t.zeroPulse) |  | ||||||
| 	t.SetPulse(pulse) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewThrottle(channel, zeroPulse, minPulse, maxPulse int) *Throttle { |  | ||||||
| 	t := Throttle{ |  | ||||||
| 		channel:   channel, |  | ||||||
| 		dev:       device, |  | ||||||
| 		zeroPulse: zeroPulse, |  | ||||||
| 		minPulse:  minPulse, |  | ||||||
| 		maxPulse:  maxPulse, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	zap.S().Info("send zero pulse to calibrate ESC") |  | ||||||
| 	t.SetPercentValue(0) |  | ||||||
|  |  | ||||||
| 	return &t |  | ||||||
| } |  | ||||||
| @@ -3,18 +3,24 @@ package part | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"github.com/cyrilix/robocar-base/service" | 	"github.com/cyrilix/robocar-base/service" | ||||||
| 	"github.com/cyrilix/robocar-pca9685/pkg/actuator" |  | ||||||
| 	"github.com/cyrilix/robocar-protobuf/go/events" | 	"github.com/cyrilix/robocar-protobuf/go/events" | ||||||
| 	MQTT "github.com/eclipse/paho.mqtt.golang" | 	MQTT "github.com/eclipse/paho.mqtt.golang" | ||||||
| 	"go.uber.org/zap" | 	"go.uber.org/zap" | ||||||
| 	"google.golang.org/protobuf/proto" | 	"google.golang.org/protobuf/proto" | ||||||
|  | 	"io" | ||||||
| 	"sync" | 	"sync" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type ActuatorController interface { | ||||||
|  | 	// SetPercentValue Set percent value | ||||||
|  | 	SetPercentValue(p float32) error | ||||||
|  | 	io.Closer | ||||||
|  | } | ||||||
|  |  | ||||||
| type Pca9685Part struct { | type Pca9685Part struct { | ||||||
| 	client       MQTT.Client | 	client       MQTT.Client | ||||||
| 	throttleCtrl *actuator.Throttle | 	throttleCtrl ActuatorController | ||||||
| 	steeringCtrl *actuator.Steering | 	steeringCtrl ActuatorController | ||||||
|  |  | ||||||
| 	muSteering    sync.Mutex | 	muSteering    sync.Mutex | ||||||
| 	steeringValue float32 | 	steeringValue float32 | ||||||
| @@ -29,7 +35,7 @@ type Pca9685Part struct { | |||||||
| 	cancel chan interface{} | 	cancel chan interface{} | ||||||
| } | } | ||||||
|  |  | ||||||
| func NewPca9685Part(client MQTT.Client, throttleCtrl *actuator.Throttle, steeringCtrl *actuator.Steering, updateFrequency int, throttleTopic, steeringTopic string) *Pca9685Part { | func NewPca9685Part(client MQTT.Client, throttleCtrl ActuatorController, steeringCtrl ActuatorController, updateFrequency int, throttleTopic, steeringTopic string) *Pca9685Part { | ||||||
| 	return &Pca9685Part{ | 	return &Pca9685Part{ | ||||||
| 		client:          client, | 		client:          client, | ||||||
| 		throttleCtrl:    throttleCtrl, | 		throttleCtrl:    throttleCtrl, | ||||||
| @@ -46,8 +52,12 @@ func (p *Pca9685Part) Start() error { | |||||||
| 		return fmt.Errorf("unable to start service: %v", err) | 		return fmt.Errorf("unable to start service: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	p.steeringCtrl.SetPercentValue(0) | 	if err := p.steeringCtrl.SetPercentValue(0); err != nil { | ||||||
| 	p.throttleCtrl.SetPercentValue(0) | 		return fmt.Errorf("unable to start steering controller: %w", err) | ||||||
|  | 	} | ||||||
|  | 	if err := p.throttleCtrl.SetPercentValue(0); err != nil { | ||||||
|  | 		return fmt.Errorf("unable to set init value '0%%': %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		select { | ||||||
| @@ -59,6 +69,12 @@ func (p *Pca9685Part) Start() error { | |||||||
|  |  | ||||||
| func (p *Pca9685Part) Stop() { | func (p *Pca9685Part) Stop() { | ||||||
| 	close(p.cancel) | 	close(p.cancel) | ||||||
|  | 	if err := p.steeringCtrl.Close(); err != nil { | ||||||
|  | 		zap.S().Errorf("unable to close steering controller: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if err := p.throttleCtrl.Close(); err != nil { | ||||||
|  | 		zap.S().Errorf("unable to close throttle controller: %v", err) | ||||||
|  | 	} | ||||||
| 	service.StopService("pca9685", p.client, p.throttleTopic, p.steeringTopic) | 	service.StopService("pca9685", p.client, p.throttleTopic, p.steeringTopic) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -74,7 +90,10 @@ func (p *Pca9685Part) onThrottleChange(_ MQTT.Client, message MQTT.Message) { | |||||||
| 	zap.S().Debugf("new throttle value: %v", throttle.GetThrottle()) | 	zap.S().Debugf("new throttle value: %v", throttle.GetThrottle()) | ||||||
| 	p.muThrottle.Lock() | 	p.muThrottle.Lock() | ||||||
| 	defer p.muThrottle.Unlock() | 	defer p.muThrottle.Unlock() | ||||||
| 	p.throttleCtrl.SetPercentValue(throttle.GetThrottle()) | 	err = p.throttleCtrl.SetPercentValue(throttle.GetThrottle()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		zap.S().Errorf("unable to set steering value '%v': %w", throttle.GetThrottle(), err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p *Pca9685Part) onSteeringChange(_ MQTT.Client, message MQTT.Message) { | func (p *Pca9685Part) onSteeringChange(_ MQTT.Client, message MQTT.Message) { | ||||||
| @@ -90,7 +109,10 @@ func (p *Pca9685Part) onSteeringChange(_ MQTT.Client, message MQTT.Message) { | |||||||
| 	zap.S().Debugf("new steering value: %v", steering.GetSteering()) | 	zap.S().Debugf("new steering value: %v", steering.GetSteering()) | ||||||
| 	p.muSteering.Lock() | 	p.muSteering.Lock() | ||||||
| 	defer p.muSteering.Unlock() | 	defer p.muSteering.Unlock() | ||||||
| 	p.steeringCtrl.SetPercentValue(steering.GetSteering()) | 	err = p.steeringCtrl.SetPercentValue(steering.GetSteering()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		zap.S().Errorf("unable to set steering value '%v': %w", steering.GetSteering(), err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p *Pca9685Part) registerCallbacks() error { | func (p *Pca9685Part) registerCallbacks() error { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user