From 032d05d1773f27917e17536ce0d9e04b1f20123d Mon Sep 17 00:00:00 2001 From: Tom Aarts Date: Fri, 18 Sep 2026 17:59:43 -0500 Subject: [PATCH 1/4] fs90r servo 360 degree continual rotation --- assets/runFS90R.sh | 5 + pom.xml | 78 ++++---- .../com/pi4j/devices/fs90r/FS90R_App.java | 175 ++++++++++++++++++ .../java/com/pi4j/devices/fs90r/README.md | 59 ++++++ 4 files changed, 280 insertions(+), 37 deletions(-) create mode 100644 assets/runFS90R.sh create mode 100644 src/main/java/com/pi4j/devices/fs90r/FS90R_App.java create mode 100644 src/main/java/com/pi4j/devices/fs90r/README.md diff --git a/assets/runFS90R.sh b/assets/runFS90R.sh new file mode 100644 index 0000000..685b05c --- /dev/null +++ b/assets/runFS90R.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + + + + java -classpath ./*:../classes com.pi4j.devices.fs90r.FS90R_App $@ diff --git a/pom.xml b/pom.xml index b4292f8..d41f14e 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 25 ${java.version} ${java.version} - 3.2.0 + 3.2.0 3.3.1 5.1.9 3.3.2 @@ -33,8 +33,8 @@ 2.0.17 - 4.0.1 - + + 5.0.0-SNAPSHOT 1.6.0 @@ -84,27 +84,27 @@ pi4j-plugin-gpiod ${pi4j.version} - + - net.java.dev.jna - jna - 5.12.1 - + net.java.dev.jna + jna + 5.12.1 + - org.slf4j - slf4j-api - ${slf4j.version} - - - org.slf4j - slf4j-simple - ${slf4j.version} - - + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-simple + ${slf4j.version} + + com.pi4j pi4j-core @@ -119,11 +119,11 @@ - + com.pi4j pi4j-drivers @@ -190,19 +190,19 @@ - + org.apache.maven.plugins maven-source-plugin - ${maven-source-plugin.version} - - - attach-sources - - jar - - - - + ${maven-source-plugin.version} + + + attach-sources + + jar + + + + + com.pi4j.devices.mcp23008/com.pi4j.devices.mcp23008.Mcp23008App @@ -373,7 +373,7 @@ maven-antrun-plugin ${maven-antrun-plugin.version} - + copySH install @@ -461,3 +461,7 @@ + + + + diff --git a/src/main/java/com/pi4j/devices/fs90r/FS90R_App.java b/src/main/java/com/pi4j/devices/fs90r/FS90R_App.java new file mode 100644 index 0000000..5e9cd4a --- /dev/null +++ b/src/main/java/com/pi4j/devices/fs90r/FS90R_App.java @@ -0,0 +1,175 @@ +/* + * /* + * * + * + */ + +package com.pi4j.devices.fs90r; + + +import com.pi4j.drivers.motor.fs90r.Fs90RDriver; +import com.pi4j.io.pwm.Pwm; +import com.pi4j.io.pwm.PwmConfigBuilder; +import com.pi4j.boardinfo.util.PwmChipUtil; +import com.pi4j.Pi4J; +import com.pi4j.context.Context; +import com.pi4j.io.pwm.PwmConfig; +import com.pi4j.io.pwm.PwmType; +import com.pi4j.util.Console; +import com.pi4j.util.Delay; + + /** + * SG90R servo motor control. The driver will use the PWM device created by + * this app. It is assumed the device is a HardWare PWM. Hardware PWM creates + * a more consistent PWM signal. + */ + public class FS90R_App { + private static Pwm pwm ; + private static double degree = 90; + private static Integer channel = 2 ; + private static Console console ; + private static Fs90RDriver fs90R ; + private static final Delay delay = new Delay(); + private static java .util.Scanner scanner; + + + public FS90R_App() { + super(); + + } + + + /** + *

main.

+ * + * @param args an array of {@link java.lang.String} objects. + * @throws java.lang.Exception if any. + */ + public static void main(String[] args) throws Exception { + System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "INFO"); + + int onlyOne = 0; // numerous parms a mutually exclusive + + scanner = new java.util.Scanner(System.in); + + console = new Console(); + Context pi4j = Pi4J.newAutoContext(); + console.title("<-- The Pi4J V5 Project Extension -->", "SG90_App"); + String helpString = " Parms: -d Degree -c channel -cw (clockwise) S M F -ccw (counter-clockwise) S M F -h HELP \n" + + " -d -cw -ccw mutually exclusive \n" + + " -d degree must be in the range 0..180. 90 stop, 0..89 CW, 91..180 CCW "; + + + for (int i = 0; i < args.length; i++) { + String o = args[i]; + if (o.contentEquals("-d")) { + String a = args[i + 1]; + degree = Double.parseDouble(a.substring(0)); + if((degree < 0) || (degree > 180)){ + console.println("-d degree must be in range 0..180"); + System.exit(40); + } + onlyOne++; + i++; + } else if (o.contentEquals("-c")) { + String a = args[i + 1]; + channel = Integer.parseInt(a.substring(0)); + i++; + } else if (o.contentEquals("-cw")) { /* 0-89 */ + String a = args[i + 1]; + onlyOne++; + i++; + if (a.equalsIgnoreCase("S")) { + degree = 82; + } else if (a.equalsIgnoreCase("M")) { + degree = 60; + } else if (a.equalsIgnoreCase("F")) { + degree = 0; + } else { + console.println(" -cw invalid "); + System.exit(53); + } + } else if (o.contentEquals("-ccw")) { /* 191-180 */ + String a = args[i + 1]; + i++; + onlyOne++; + if (a.equalsIgnoreCase("S")) { + degree = 95; + } else if (a.equalsIgnoreCase("M")) { + degree = 120; + } else if (a.equalsIgnoreCase("F")) { + degree = 180; + } else { + console.println(" -cw invalid "); + System.exit(53); + } + } else if (o.contentEquals("-q")) { + console.println("Exit"); + System.exit(0); + } else if (o.contentEquals("-h")) { + console.println(helpString); + System.exit(0); + }else { + console.println(" !!! Invalid Parm " + o); + console.println(helpString); + System.exit(42); + } + } + + if (onlyOne > 1 ) { + console.println(" mutually exclusive parms used."); + console.println(helpString); + System.exit(44); + } + pwm = createPwm(1, channel, pi4j); + + fs90R = new Fs90RDriver(pwm); + + fs90R.setServoRotation(degree); + + waitChange( 10l); + + pi4j.shutdown(pwm); + + } + + + static Pwm createPwm(int servoNumber, Integer channel, Context pi4j) { + + final PwmConfig config = PwmConfigBuilder.newInstance (pi4j) + .id ("fs90RNumber" + servoNumber) + .name ("fs90R number " + servoNumber) + .channel(3) // this.address) //or 1 LED on gpio13 the second channel + .pwmType(PwmType.HARDWARE) + .channel(channel) + .chip(PwmChipUtil.getPWMChip()) + .frequency(50) + .dutyCycle(3) + .build (); + + return pi4j.create (config); + } + +// todo -cw -ccw -d + static void waitChange(long c){ + while(true){ + delay.setMillis(c).materialize(); + console.println("Enter q - quit, or degree value "); + if (scanner.hasNextInt() ) { + int nextDegree = scanner.nextInt(); + if((nextDegree < 0) || (nextDegree > 180)){ + console.println("-d degree must be in the range 0..180. 90 stop, 0..89 CW, 91..180 CCW"); + }else { + fs90R.setServoRotation(nextDegree); + } + } else{ + break; + } + } + } + } + + + + + diff --git a/src/main/java/com/pi4j/devices/fs90r/README.md b/src/main/java/com/pi4j/devices/fs90r/README.md new file mode 100644 index 0000000..29581c1 --- /dev/null +++ b/src/main/java/com/pi4j/devices/fs90r/README.md @@ -0,0 +1,59 @@ + + +##Device application to operate a FS90R stepping servo. This servo can rotate 360 degree continuously. +The PWM signal sent to the servo device set the direction and RPM of rotation. + + + + + + The Fs90R defines a specific PWM interface and exactly how the PWN signal + controls the servo + + https://www.pololu.com/product/2820 + + FS90R Duty Cycle Reference TableAssuming a standard 50Hz frequency + (20ms total wave period), here is how the pulse width and duty cycle + map to direction and rotation speed for the FS90R Servo Motor: + | Direction Rotation | Pulse Width (us) | Duty Cycle (%) | + |-----------------------------------------------------------------------| + | clockwise max | 0.5 us | 2.5 % | + | stopped | 1.5 us | 7.5 % | + | counter clockwise max | 2.5 us | 12.5 % | + |-----------------------------------------------------------------------| + + + +FS90R Pi +Grey/Brown Ground +Red 5V +Yellow GPIO18 config.sys contains dtoverlay=pwm-2chan + + + +1. ./mvnw clean package +2. cd target/distribution +3. ./runFS90R.sh args + +-h help +-c channel (default 2) set the channel, depending on config.txt 1 2 3 4 +-d degree +-cw clockwise S M F +-ccw counter-clockwise S M F +-q quit servo will stop + +Set output shaft stationary +./runFS90R.sh -d 90 + +Set output shaft max rotation Counter Clockwise +./runFS90R.sh -d 180 + +Set output shaft max Clockwise +./runFS90R.sh -d 0 + + +Set output shaft max Clockwise +./runFS90R.sh -cw F + +Set output shaft slow Counter-Clockwise +./runFS90R.sh -ccw S From f5ad10045fd62610f2950f83515ff4b9bc0a9116 Mon Sep 17 00:00:00 2001 From: Tom Aarts Date: Fri, 18 Sep 2026 17:59:43 -0500 Subject: [PATCH 2/4] update README.md included fs90r and sg90 fs90r servo 360 degree continual rotation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 323d68b..a0aae67 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ The following table the currently supported devices within this project: | Sensor | 1-Wire | (1) | [DHT22 Temp/Humidity sensor](src/main/java/com/pi4j/devices/dht22/README.md) | Sensor | | | [HC-SR04 Ultrasonic Sensor](src/main/java/com/pi4j/devices/hcsr04/README.md) | Sensor | I2C | (1) | [MPL3115A2 Temp/Pressure/Altitude device](src/main/java/com/pi4j/devices/mpl3115a2/README.md) +| Servo | PWM | | [SG90 180 degree servo motor device](src/main/java/com/pi4j/devices/sg90/README.md) +| Servo | PWM | | [FS90R 360 degree continuous servo motor device](src/main/java/com/pi4j/devices/fs90r/README.md) ### Notes: From 549e72120121880772e08c650939fce93b96f8d4 Mon Sep 17 00:00:00 2001 From: Tom Aarts <45747691+taartspi@users.noreply.github.com> Date: Thu, 8 Oct 2026 17:32:17 -0500 Subject: [PATCH 3/4] Fix console title in Fs90rApp --- src/main/java/com/pi4j/devices/fs90r/Fs90rApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/pi4j/devices/fs90r/Fs90rApp.java b/src/main/java/com/pi4j/devices/fs90r/Fs90rApp.java index e2e25c1..97bdc67 100644 --- a/src/main/java/com/pi4j/devices/fs90r/Fs90rApp.java +++ b/src/main/java/com/pi4j/devices/fs90r/Fs90rApp.java @@ -69,7 +69,7 @@ static void main(String[] args) throws Exception { console = new Console(); Context pi4j = Pi4J.newAutoContext(); - console.title("<-- The Pi4J V5 Project Extension -->", "FS90R_App"); + console.title("<-- The Pi4J V5 Project Extension -->", "Fs90rApp"); String helpString = " Parms: -c channel -cw (clockwise) S M F -ccw (counter-clockwise) S M F -h HELP \n" + " -low microseconds -high microseconds -cw -ccw mutually exclusive -d degree \n" + " degree must be in the range 0..180. 90 stop, 0..89 CW, 91..180 CCW "; From a8c20101318ed2c533ae0dcdabf0bcc1f867deab Mon Sep 17 00:00:00 2001 From: Tom Aarts <45747691+taartspi@users.noreply.github.com> Date: Thu, 8 Oct 2026 17:35:33 -0500 Subject: [PATCH 4/4] Revise command-line options in README.md Updated command-line options in README for clarity and consistency. --- src/main/java/com/pi4j/devices/fs90r/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/pi4j/devices/fs90r/README.md b/src/main/java/com/pi4j/devices/fs90r/README.md index b817d01..b266a57 100644 --- a/src/main/java/com/pi4j/devices/fs90r/README.md +++ b/src/main/java/com/pi4j/devices/fs90r/README.md @@ -44,13 +44,13 @@ Yellow GPIO18 config.sys contains dtoverlay=pwm-2chan 2. cd target/distribution 3. ./runFs90r.sh args --h help --c channel (default 2) set the channel, depending on config.txt 1 2 3 4 --cw clockwise S M F --ccw counter-clockwise S M F --low float pulse width for 0 degree microseconds --high float pulse width for 180 degree microseconds --q quit servo will stop +-h help +-c channel (default 2) set the channel, depending on config.txt 1 2 3 4 +-cw clockwise S M F +-ccw counter-clockwise S M F +-low double pulse width for 0 degree microseconds +-high double pulse width for 180 degree microseconds +-q quit servo will stop Application running, awaiting input of 0-180 degree