arduino-emulator
Loading...
Searching...
No Matches
Servo.h
1#pragma once
2/*
3 Servo.h - Servo library for Raspberry Pi using HardwareGPIO_RPI (sysfs PWM)
4 Copyright (c) 2025 Phil Schatzmann. All right reserved.
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#if defined(USE_RPI)
22#include <stdint.h>
23#include <algorithm>
24#include <cstdio>
25#include "HardwareGPIO_RPI.h"
26#include "HardwareSetupRPI.h"
27
29#define SERVO_DEFAULT_MIN_US 544
30#define SERVO_DEFAULT_MAX_US 2400
31
33#define SERVO_PERIOD_NS 20000000UL
34
36#define SERVO_NO_PIN 255
37
60class Servo {
61 public:
65 Servo() : _gpio(arduino::RPI.getGPIO()),
66 _pin(SERVO_NO_PIN),
67 _min(SERVO_DEFAULT_MIN_US),
68 _max(SERVO_DEFAULT_MAX_US),
69 _pulseUs((SERVO_DEFAULT_MIN_US + SERVO_DEFAULT_MAX_US) / 2) {}
70
74 Servo(HardwareGPIO_RPI& gpio)
75 : _gpio(&gpio),
76 _pin(SERVO_NO_PIN),
77 _min(SERVO_DEFAULT_MIN_US),
78 _max(SERVO_DEFAULT_MAX_US),
79 _pulseUs((SERVO_DEFAULT_MIN_US + SERVO_DEFAULT_MAX_US) / 2) {}
80
81 ~Servo() {
82 if (attached()) detach();
83 }
84
90 uint8_t attach(int pin) {
91 return attach(pin, SERVO_DEFAULT_MIN_US, SERVO_DEFAULT_MAX_US);
92 }
93
101 uint8_t attach(int pin, int min, int max) {
102 _pin = pin;
103 _min = min;
104 _max = max;
105 if (_gpio) {
106 _gpio->pinMode(_pin, OUTPUT);
107 _gpio->analogWriteFrequency(_pin, 50); // Fix to 50 Hz for servo control
108 }
109 _writePulse(_pulseUs);
110 return 0;
111 }
112
116 void detach() {
117 if (attached()) {
118 _writePulse(0);
119 _pin = SERVO_NO_PIN;
120 }
121 }
122
127 void write(int value) {
128 if (value <= 200) {
129 value = std::max(0, std::min(value, 180));
130 writeMicroseconds(_angleToPulse(value));
131 } else {
132 writeMicroseconds(value);
133 }
134 }
135
140 void writeMicroseconds(int us) {
141 _pulseUs = std::max(_min, std::min(us, _max));
142 if (attached()) _writePulse(_pulseUs);
143 }
144
148 int read() const {
149 return _pulseToAngle(_pulseUs);
150 }
151
155 int readMicroseconds() const {
156 return _pulseUs;
157 }
158
162 bool attached() const {
163 return _pin != SERVO_NO_PIN;
164 }
165
166 private:
167 HardwareGPIO_RPI* _gpio;
168 int _pin;
169 int _min;
170 int _max;
171 int _pulseUs;
172
174 int _angleToPulse(int angle) const {
175 return _min + (angle * (_max - _min)) / 180;
176 }
177
179 int _pulseToAngle(int us) const {
180 if (_max == _min) return 0;
181 int angle = ((us - _min) * 180) / (_max - _min);
182 return std::max(0, std::min(angle, 180));
183 }
184
193 void _writePulse(int us) {
194 int pwm_channel = -1;
195 if (_pin == 18 || _pin == 12) pwm_channel = 0;
196 else if (_pin == 19 || _pin == 13) pwm_channel = 1;
197 if (pwm_channel < 0) return;
198
199 // Export the PWM channel (no-op if already exported)
200 FILE* fexp = fopen("/sys/class/pwm/pwmchip0/export", "w");
201 if (fexp) { fprintf(fexp, "%d", pwm_channel); fclose(fexp); }
202
203 char period_path[64], duty_path[64], enable_path[64];
204 snprintf(period_path, sizeof(period_path),
205 "/sys/class/pwm/pwmchip0/pwm%d/period", pwm_channel);
206 snprintf(duty_path, sizeof(duty_path),
207 "/sys/class/pwm/pwmchip0/pwm%d/duty_cycle", pwm_channel);
208 snprintf(enable_path, sizeof(enable_path),
209 "/sys/class/pwm/pwmchip0/pwm%d/enable", pwm_channel);
210
211 // Set period to 20,000,000 ns (50 Hz)
212 FILE* fp = fopen(period_path, "w");
213 if (fp) { fprintf(fp, "%lu", SERVO_PERIOD_NS); fclose(fp); }
214
215 // Convert us to ns and write duty cycle (0 ns when disabling)
216 unsigned long duty_ns = (us > 0) ? (unsigned long)us * 1000UL : 0UL;
217 FILE* fd = fopen(duty_path, "w");
218 if (fd) { fprintf(fd, "%lu", duty_ns); fclose(fd); }
219
220 // Enable or disable PWM output
221 FILE* fe = fopen(enable_path, "w");
222 if (fe) { fprintf(fe, "%d", us > 0 ? 1 : 0); fclose(fe); }
223 }
224};
225
226#endif // USE_RPI
Arduino-compatible Servo library for Raspberry Pi.
Definition Servo.h:60
void detach()
Detach the servo: stop PWM and release the pin.
Definition Servo.h:116
bool attached() const
Check if the servo is attached to a pin.
Definition Servo.h:162
Servo(HardwareGPIO_RPI &gpio)
Constructor with explicit HardwareGPIO_RPI instance.
Definition Servo.h:74
uint8_t attach(int pin, int min, int max)
Attach the servo to a GPIO pin with custom min/max pulse widths.
Definition Servo.h:101
Servo()
Default constructor. Uses the global RPI GPIO instance.
Definition Servo.h:65
void write(int value)
Write a position to the servo.
Definition Servo.h:127
int read() const
Read the current servo position in degrees (0-180).
Definition Servo.h:148
int readMicroseconds() const
Read the current pulse width in microseconds.
Definition Servo.h:155
uint8_t attach(int pin)
Attach the servo to a GPIO pin using default pulse range.
Definition Servo.h:90
void writeMicroseconds(int us)
Write a pulse width directly in microseconds.
Definition Servo.h:140
We provide the WiFi class to simulate the Arduino WIFI. In in Linux we can expect that networking is ...
Definition CanMsg.cpp:31