From d9c4def6cb3c57b62d2e5c9ba4d367d0f29f5215 Mon Sep 17 00:00:00 2001 From: Kevin Watters Date: Mon, 19 May 2025 10:12:03 -0400 Subject: [PATCH 01/15] sync local changes to get as5048 data flowing to a motor. --- .../java/org/myrobotlab/service/Arduino.java | 7 +- .../myrobotlab/service/As5048AEncoder.java | 18 +++++ .../java/org/myrobotlab/service/Motor.java | 65 +++++++++++++------ .../org/myrobotlab/service/MotorDualPwm.java | 1 - .../service/abstracts/AbstractMotor.java | 19 ++++++ .../service/abstracts/AbstractPinEncoder.java | 1 + 6 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/Arduino.java b/src/main/java/org/myrobotlab/service/Arduino.java index 8385273fe6..613ad18835 100644 --- a/src/main/java/org/myrobotlab/service/Arduino.java +++ b/src/main/java/org/myrobotlab/service/Arduino.java @@ -1696,14 +1696,17 @@ public EncoderData publishEncoderData(Integer deviceId, Integer position) { // type = 1; pin = ((As5048AEncoder) ec).getPin(); angle = 360.0 * position / ((As5048AEncoder) ec).resolution; - log.info("Angle : {}", angle); + // log.info("Angle : {}", angle); } else { error("unknown encoder type {}", ec.getClass().getName()); } EncoderData data = new EncoderData(ec.getName(), pin, position, angle); // log.info("Publish Encoder Data Raw {}", data); - + // TODO: how do i publish the data from the encoder? + // ec.publishEncoderData(data); + ((As5048AEncoder)ec).updateEncoderData(data); + // invoke("publishEncoderData", data); // TODO: all this code needs to move out of here! return data; } diff --git a/src/main/java/org/myrobotlab/service/As5048AEncoder.java b/src/main/java/org/myrobotlab/service/As5048AEncoder.java index 1795c0bbc1..deb4382bd6 100755 --- a/src/main/java/org/myrobotlab/service/As5048AEncoder.java +++ b/src/main/java/org/myrobotlab/service/As5048AEncoder.java @@ -1,6 +1,8 @@ package org.myrobotlab.service; import org.myrobotlab.logging.LoggingFactory; +import org.myrobotlab.sensor.EncoderData; +import org.myrobotlab.sensor.EncoderListener; import org.myrobotlab.service.abstracts.AbstractPinEncoder; import org.myrobotlab.service.config.ServiceConfig; import org.myrobotlab.service.interfaces.EncoderControl; @@ -42,4 +44,20 @@ public static void main(String[] args) throws Exception { log.info("Here we are.."); } + // TODO: move to base class / interface? + void attachEncoderListener(EncoderListener service) { + // TODO: move this to the EncoderPublisher interface. + if (service == null) { + log.warn("{}.attachEncoderListener(null)", getName()); + return; + } + log.info("Adding listener for encoder data {}", service.getName()); + addListener("publishEncoderData", service.getName()); + } + + void updateEncoderData(EncoderData data) { + // publish the updated encoder data (this is updated from the arduino..) + invoke("publishEncoderData", data); + } + } diff --git a/src/main/java/org/myrobotlab/service/Motor.java b/src/main/java/org/myrobotlab/service/Motor.java index 7ee06fe258..97257e133b 100644 --- a/src/main/java/org/myrobotlab/service/Motor.java +++ b/src/main/java/org/myrobotlab/service/Motor.java @@ -4,7 +4,6 @@ import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.service.abstracts.AbstractMotor; import org.myrobotlab.service.config.MotorConfig; -import org.myrobotlab.service.config.ServiceConfig; /** * A general motor implementation with a "simple H-bridge" where one control @@ -99,18 +98,18 @@ public static void main(String[] args) { try { - Runtime.start("gui", "SwingGui"); + Runtime.start("python", "Python"); Runtime.start("webgui", "WebGui"); - Runtime.start("motor", "Motor"); + Runtime.start("m1", "MotorDualPwm"); Runtime.start("arduino", "Arduino"); - boolean done = true; + boolean done = false; if (done) { return; } // FIXME - all testing or replacing of main code should be new JUnit // tests - with virtual arduino !!!) - String port = "COM15"; + String port = "COM3"; // Arduino arduino = (Arduino) Runtime.start("arduino", "Arduino"); // Runtime.createAndStart("gui", "SwingGui"); @@ -137,19 +136,27 @@ public static void main(String[] args) { Arduino arduino = (Arduino) Runtime.start("arduino", "Arduino"); arduino.connect(port); - arduino.pinMode(6, Arduino.OUTPUT); - arduino.pinMode(7, Arduino.OUTPUT); + + + - arduino.digitalWrite(7, 1); + // arduino.pinMode(6, Arduino.OUTPUT); + // arduino.pinMode(7, Arduino.OUTPUT); +// +// arduino.digitalWrite(7, 1); // arduino.digitalWrite(6, 1); - arduino.analogWrite(6, 255); - arduino.analogWrite(6, 200); - arduino.analogWrite(6, 100); - arduino.analogWrite(6, 0); + // arduino.analogWrite(6, 255); + // arduino.analogWrite(6, 200); + // arduino.analogWrite(6, 100); + // arduino.analogWrite(6, 0); - Motor m1 = (Motor) Runtime.start("m1", "Motor"); + MotorDualPwm m1 = (MotorDualPwm) Runtime.start("m1", "Motor"); + m1.setPwmPins(6, 7); + + + /* * m1.setType2Pwm(leftPwm, rightPwm); m1.setTypeStepper(); * m1.setTypePulseStep(pwmPin, dirPin); @@ -160,14 +167,30 @@ public static void main(String[] args) { // m1.attach(arduino, Motor.TYPE_SIMPLE, pwmPin, dirPin); m1.attachMotorController(arduino); - m1.move(1.0); - m1.move(-1.0); - - arduino.enableBoardInfo(true); - arduino.enableBoardInfo(false); - m1.stop(); - m1.move(0.5); - m1.stop(); + As5048AEncoder encoder = (As5048AEncoder) Runtime.start("encoder", "As5048AEncoder"); + encoder.setPin(10); + arduino.attachEncoderControl(encoder); + + // attach the motor as an encoder listener? + encoder.attachEncoderListener(m1); +// for (int i = 0; i < 1; i++) { +// m1.move(1.0); +// Thread.sleep(1000); +// m1.move(-1.0); +// Thread.sleep(1000); +// m1.move(1.0); +// Thread.sleep(1000); +// m1.move(-1.0); +// Thread.sleep(1000); +// m1.stop(); +// +// Thread.sleep(1000); +// } + //# arduino.enableBoardInfo(true); + //# arduino.enableBoardInfo(false); + // m1.stop(); + // m1.move(0.5); + // m1.stop(); // Runtime.start("webgui", "WebGui"); diff --git a/src/main/java/org/myrobotlab/service/MotorDualPwm.java b/src/main/java/org/myrobotlab/service/MotorDualPwm.java index 6060be0fb9..56d7429c50 100644 --- a/src/main/java/org/myrobotlab/service/MotorDualPwm.java +++ b/src/main/java/org/myrobotlab/service/MotorDualPwm.java @@ -3,7 +3,6 @@ import java.util.Arrays; import java.util.List; -import org.myrobotlab.framework.Platform; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.service.abstracts.AbstractMotor; diff --git a/src/main/java/org/myrobotlab/service/abstracts/AbstractMotor.java b/src/main/java/org/myrobotlab/service/abstracts/AbstractMotor.java index 6916db6269..f5ae969a6a 100644 --- a/src/main/java/org/myrobotlab/service/abstracts/AbstractMotor.java +++ b/src/main/java/org/myrobotlab/service/abstracts/AbstractMotor.java @@ -290,6 +290,25 @@ public double getTargetPos() { @Override public void onEncoderData(EncoderData data) { // TODO Auto-generated method stub + log.info("Encoder Data (to motor): {}", data); + // TODO: this should probably not be here.. but rather in a DiyServo service instead. + if (false) { + double target = 180.0; + double delta = data.angle - target; + + if (Math.abs(delta) > 0.5) { + // move the motor a bit. + // TODO: this hsould be controlled by a PID algorithm. + if (delta > 0) { + this.move(0.5); + } else { + this.move(0.5); + } + + } else { + this.move(0); + } + } } diff --git a/src/main/java/org/myrobotlab/service/abstracts/AbstractPinEncoder.java b/src/main/java/org/myrobotlab/service/abstracts/AbstractPinEncoder.java index 6aa5720aeb..4c5c1fe5be 100644 --- a/src/main/java/org/myrobotlab/service/abstracts/AbstractPinEncoder.java +++ b/src/main/java/org/myrobotlab/service/abstracts/AbstractPinEncoder.java @@ -100,6 +100,7 @@ public Boolean isEnabled() { @Override public EncoderData publishEncoderData(EncoderData data) { + // log.info("Abstract pin encoder publish data {}", data); return data; } From 9d607c824d3baa7abc49e0896f36ac65019d3edf Mon Sep 17 00:00:00 2001 From: kwatters Date: Thu, 22 May 2025 14:58:00 -0400 Subject: [PATCH 02/15] some interface cleanup for encoder publisher/listener --- .../myrobotlab/sensor/EncoderListener.java | 24 +++++++++-- .../myrobotlab/sensor/EncoderPublisher.java | 43 ++++++++++++++++++- .../myrobotlab/service/As5048AEncoder.java | 14 +----- 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/myrobotlab/sensor/EncoderListener.java b/src/main/java/org/myrobotlab/sensor/EncoderListener.java index 33bc18558a..d216f46e92 100644 --- a/src/main/java/org/myrobotlab/sensor/EncoderListener.java +++ b/src/main/java/org/myrobotlab/sensor/EncoderListener.java @@ -1,9 +1,27 @@ package org.myrobotlab.sensor; -import org.myrobotlab.framework.interfaces.Attachable; +public interface EncoderListener { + + public String getName(); -public interface EncoderListener extends Attachable { + public void onEncoderData(EncoderData encoderData); + + default public void attachEncoderPublisher(EncoderPublisher publisher) { + attachEncoderPublisher(publisher.getName()); + } - void onEncoderData(EncoderData data); + default public void attachEncoderPublisher(String name) { + send(name, "attachEncoderListener", getName()); + } + + default public void detachEncoderPublisher(EncoderPublisher publisher) { + detachEncoderPublisher(publisher.getName()); + } + + default public void detachEncoderPublisher(String name) { + send(name, "detachEncoderListener", getName()); + } + + public void send(String name, String method, Object... data); } diff --git a/src/main/java/org/myrobotlab/sensor/EncoderPublisher.java b/src/main/java/org/myrobotlab/sensor/EncoderPublisher.java index 59b029b165..99b06ceab1 100644 --- a/src/main/java/org/myrobotlab/sensor/EncoderPublisher.java +++ b/src/main/java/org/myrobotlab/sensor/EncoderPublisher.java @@ -1,5 +1,44 @@ package org.myrobotlab.sensor; -public interface EncoderPublisher { - public EncoderData publishEncoderData(EncoderData Data); +import org.myrobotlab.framework.interfaces.NameProvider; + +public interface EncoderPublisher extends NameProvider { + + // These are all the methods that the Encoder publisher should produce. + public static String[] publishMethods = new String[] { "publishEncoderData" }; + + // Define the methods that an Encoder publisher should have for publishing data. + default public EncoderData publishEncoderData(EncoderData data) { + return data; + }; + + // helper default methods to attach and detach a listener. + default public void attachEncoderListener(String name) { + for (String publishMethod : EncoderPublisher.publishMethods) { + addListener(publishMethod, name); + } + } + + default public void attachEncoderListener(EncoderListener listener) { + for (String publishMethod : EncoderPublisher.publishMethods) { + addListener(publishMethod, listener.getName()); + } + } + + // detach methods + default public void detachEncoderListener(String name) { + for (String publishMethod : EncoderPublisher.publishMethods) { + removeListener(publishMethod, name); + } + } + + default public void detachEncoderListener(EncoderListener listener) { + detachEncoderListener(listener.getName()); + } + + // Add the addListener method to the interface all services implement this. + public void addListener(String topicMethod, String callbackName); + + public void removeListener(String topicMethod, String callbackName); + } diff --git a/src/main/java/org/myrobotlab/service/As5048AEncoder.java b/src/main/java/org/myrobotlab/service/As5048AEncoder.java index deb4382bd6..c4f8dccb3e 100755 --- a/src/main/java/org/myrobotlab/service/As5048AEncoder.java +++ b/src/main/java/org/myrobotlab/service/As5048AEncoder.java @@ -3,6 +3,7 @@ import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.sensor.EncoderData; import org.myrobotlab.sensor.EncoderListener; +import org.myrobotlab.sensor.EncoderPublisher; import org.myrobotlab.service.abstracts.AbstractPinEncoder; import org.myrobotlab.service.config.ServiceConfig; import org.myrobotlab.service.interfaces.EncoderControl; @@ -13,7 +14,7 @@ * @author kwatters * */ -public class As5048AEncoder extends AbstractPinEncoder implements EncoderControl { +public class As5048AEncoder extends AbstractPinEncoder implements EncoderControl, EncoderPublisher { private static final long serialVersionUID = 1L; @@ -43,17 +44,6 @@ public static void main(String[] args) throws Exception { encoder.setZeroPoint(); log.info("Here we are.."); } - - // TODO: move to base class / interface? - void attachEncoderListener(EncoderListener service) { - // TODO: move this to the EncoderPublisher interface. - if (service == null) { - log.warn("{}.attachEncoderListener(null)", getName()); - return; - } - log.info("Adding listener for encoder data {}", service.getName()); - addListener("publishEncoderData", service.getName()); - } void updateEncoderData(EncoderData data) { // publish the updated encoder data (this is updated from the arduino..) From d0b355066f3d8e8b71a8d88a41809ece54992f48 Mon Sep 17 00:00:00 2001 From: kwatters Date: Sun, 1 Jun 2025 12:19:47 -0400 Subject: [PATCH 03/15] javadocs --- pom.xml | 361 +++++++++--------- .../myrobotlab/sensor/EncoderListener.java | 4 + .../myrobotlab/sensor/EncoderPublisher.java | 5 + 3 files changed, 199 insertions(+), 171 deletions(-) diff --git a/pom.xml b/pom.xml index 37bf21e80a..1b97232ed9 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,26 @@ - + + + + au.edu.federation.caliko + caliko + 1.3.8 + + + au.edu.federation.caliko.visualisation + caliko-visualisation + 1.3.8 + + + au.edu.federation.caliko.demo + caliko-demo + 1.3.8 + + + + javazoom jlayer @@ -156,49 +175,49 @@ 1.0.3.3 provided - + - - - + + + - + org.boofcv boofcv-all 0.40.1 provided - + - + ChessBoard ChessBoard 1.0.0 provided - + - + it.sauronsoftware.cron4j cron4j 2.2.5 provided - + - + net.sf.opencsv opencsv 2.3 provided - + - + net.dv8tion JDA @@ -211,9 +230,9 @@ - + - + com.github.docker-java docker-java @@ -226,10 +245,10 @@ - - + + - + org.apache.tika tika-core @@ -316,58 +335,58 @@ 1.4.19 provided - + - + pl.allegro.tech embedded-elasticsearch 2.7.0 provided - + - + javax.mail mail 1.4.7 provided - + - + com.github.pnavais state-machine 1.2.0 provided - + - + org.eclipse.jgit org.eclipse.jgit 6.6.1.202309021850-r provided - + - + com.google.cloud google-cloud-vision 2.1.3 provided - + - - - + + + - + com.google.cloud google-cloud-translate @@ -380,9 +399,9 @@ 1.3.0 provided - + - + org.jsoup jsoup @@ -395,34 +414,34 @@ 3.3.2 provided - + - - - + + + - - - + + + - - - + + + - - - + + + - + jfugue jfugue 5.0.7 provided - + - + org.jmonkeyengine jme3-core @@ -489,9 +508,9 @@ 3.2.3 provided - + - + net.java.jinput jinput @@ -505,27 +524,27 @@ provided zip - + - + org.apache.kafka kafka-clients 1.0.1 provided - + - + com.1stleg jnativehook 2.0.3 provided - + - + io.github.ollama4j ollama4j @@ -546,9 +565,9 @@ - + - + leapmotion leap @@ -576,9 +595,9 @@ provided zip - + - + org.myrobotlab.audio voice-effects @@ -586,10 +605,10 @@ provided zip - + - - + + de.dfki.mary marytts @@ -929,18 +948,18 @@ 2.12.1 provided - + - + org.eclipse.paho org.eclipse.paho.client.mqttv3 1.2.1 provided - + - + io.moquette moquette-broker @@ -961,22 +980,22 @@ - - - - - + + + + + - + com.github.nicholasastuart myo-java 0.9.1 provided - + - + org.saintandreas jovr @@ -1021,9 +1040,9 @@ - + - + org.bytedeco javacv-platform @@ -1042,8 +1061,8 @@ 0.3.28-1.5.11 provided - - + + net.sf.jipcam jipcam @@ -1102,9 +1121,9 @@ provided zip - + - + simpleopenni openni @@ -1136,31 +1155,31 @@ 1.3.1 provided - + - + org.json json 20230227 provided - + - + com.illposed.osc javaosc-core 0.4 provided - + - - - - - + + + + + com.amazonaws aws-java-sdk-polly @@ -1189,11 +1208,11 @@ - - + + - - + + program-ab program-ab-kw @@ -1216,8 +1235,8 @@ 2.14.0 provided - - + + org.apache.lucene lucene-analysis-common @@ -1230,35 +1249,35 @@ 9.10.0 provided - + - + net.sf.py4j py4j 0.10.9.7 provided - + - + org.python jython-standalone 2.7.2 - + - + rome rome 1.0 provided - + - + com.pi4j pi4j-core @@ -1272,18 +1291,18 @@ provided pom - + - + com.amazonaws aws-java-sdk-rekognition 1.11.263 provided - + - + ch.qos.logback logback-classic @@ -1344,19 +1363,19 @@ okhttp 3.9.0 - - + + - + io.github.java-native jssc 2.9.4 provided - + - + com.slack.api bolt @@ -1381,9 +1400,9 @@ 1.19 provided - + - + org.apache.lucene lucene-core @@ -1486,16 +1505,16 @@ - + org.glassfish.jersey.core jersey-server 3.1.5 provided - + - + edu.cmu.sphinx sphinx4-core @@ -1508,9 +1527,9 @@ 5prealpha-SNAPSHOT provided - + - + org.bytedeco tesseract @@ -1530,37 +1549,37 @@ provided zip - - + + - + junit junit 4.13.1 provided - + - + topcodes topcodes 1.0.0 provided - + - + org.twitter4j twitter4j-core 3.0.5 provided - + - + io.vertx vertx-core @@ -1585,20 +1604,20 @@ - - + + - - + + com.voicerss tts 1.0 provided - + - + org.jmdns jmdns @@ -1633,35 +1652,35 @@ netty-all 4.1.82.Final - + - + com.github.sarxos webcam-capture-driver-v4l4j 0.3.13-SNAPSHOT provided - + - - - + + + - - - + + + - + wiiusej wiiusej wiiusej provided - + - + org.wikidata.wdtk wdtk-client @@ -1678,21 +1697,21 @@ - - - - + + + + - + WolframAlpha WolframAlpha 1.1 provided - + - + org.igniterealtime.smack smack-java7 @@ -1717,7 +1736,7 @@ 4.1.6 provided - + @@ -1766,7 +1785,7 @@ - + @@ -1782,7 +1801,7 @@ - + org.apache.maven.plugins @@ -1813,11 +1832,11 @@ myrobotlab - + true myrobotlab-full false - + ${git.tags} ${git.branch} ${git.dirty} @@ -1846,7 +1865,7 @@ ${git.commit.id.describe-short} ${git.commit.user.name} ${git.commit.user.email} - @@ -1937,7 +1956,7 @@ false true ${project.build.outputDirectory}/git.properties - + false false @@ -1949,12 +1968,12 @@ maven-surefire-plugin org.apache.maven.plugins - + 3.2.2 1 true - + ${argLine} -Djava.library.path=libraries/native -Djna.library.path=libraries/native @@ -1974,12 +1993,12 @@ test - + - + org.apache.maven.plugins maven-clean-plugin diff --git a/src/main/java/org/myrobotlab/sensor/EncoderListener.java b/src/main/java/org/myrobotlab/sensor/EncoderListener.java index d216f46e92..90e8b3abc4 100644 --- a/src/main/java/org/myrobotlab/sensor/EncoderListener.java +++ b/src/main/java/org/myrobotlab/sensor/EncoderListener.java @@ -1,5 +1,9 @@ package org.myrobotlab.sensor; +/** + * Any device/service that wants to handle the onEncoderData method + * and encoder publisher will publish the encoder data to listeners. + */ public interface EncoderListener { public String getName(); diff --git a/src/main/java/org/myrobotlab/sensor/EncoderPublisher.java b/src/main/java/org/myrobotlab/sensor/EncoderPublisher.java index 99b06ceab1..e91fb564ca 100644 --- a/src/main/java/org/myrobotlab/sensor/EncoderPublisher.java +++ b/src/main/java/org/myrobotlab/sensor/EncoderPublisher.java @@ -2,6 +2,11 @@ import org.myrobotlab.framework.interfaces.NameProvider; +/** + * The EncoderPublisher interface is used with any device that publishes encoder data + * such as the AS5048A and the AMT203 encoders. Analog pins (potentiometers) can also + * emulate and publish encoder data. + */ public interface EncoderPublisher extends NameProvider { // These are all the methods that the Encoder publisher should produce. From 96dee84ff8e64f6df8e481d50b3c3bfb3ccc3c30 Mon Sep 17 00:00:00 2001 From: kwatters Date: Sun, 1 Jun 2025 12:33:40 -0400 Subject: [PATCH 04/15] more wiring including the amt203 --- .../java/org/myrobotlab/service/Amt203Encoder.java | 10 +++++++++- src/main/java/org/myrobotlab/service/Arduino.java | 5 +++-- .../java/org/myrobotlab/service/As5048AEncoder.java | 2 +- .../myrobotlab/service/interfaces/EncoderControl.java | 11 +++++++++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/Amt203Encoder.java b/src/main/java/org/myrobotlab/service/Amt203Encoder.java index 870f01e0a7..8549ef9051 100755 --- a/src/main/java/org/myrobotlab/service/Amt203Encoder.java +++ b/src/main/java/org/myrobotlab/service/Amt203Encoder.java @@ -1,6 +1,8 @@ package org.myrobotlab.service; import org.myrobotlab.logging.LoggingFactory; +import org.myrobotlab.sensor.EncoderData; +import org.myrobotlab.sensor.EncoderPublisher; import org.myrobotlab.service.abstracts.AbstractPinEncoder; import org.myrobotlab.service.config.ServiceConfig; import org.myrobotlab.service.interfaces.EncoderControl; @@ -23,7 +25,7 @@ * @author kwatters * */ -public class Amt203Encoder extends AbstractPinEncoder implements EncoderControl { +public class Amt203Encoder extends AbstractPinEncoder implements EncoderControl, EncoderPublisher { private static final long serialVersionUID = 1L; @@ -50,4 +52,10 @@ public static void main(String[] args) throws Exception { log.info("Here we are.."); } + @Override + public void updateEncoderData(EncoderData data) { + // publish the updated encoder data (this is updated from the arduino..) + invoke("publishEncoderData", data); + } + } diff --git a/src/main/java/org/myrobotlab/service/Arduino.java b/src/main/java/org/myrobotlab/service/Arduino.java index 613ad18835..53289d062e 100644 --- a/src/main/java/org/myrobotlab/service/Arduino.java +++ b/src/main/java/org/myrobotlab/service/Arduino.java @@ -1700,12 +1700,13 @@ public EncoderData publishEncoderData(Integer deviceId, Integer position) { } else { error("unknown encoder type {}", ec.getClass().getName()); } - + // TODO: figure out how to handle analog pin data as encoder data.. EncoderData data = new EncoderData(ec.getName(), pin, position, angle); // log.info("Publish Encoder Data Raw {}", data); // TODO: how do i publish the data from the encoder? // ec.publishEncoderData(data); - ((As5048AEncoder)ec).updateEncoderData(data); + // This will pass the encoder data to the encoder and the encoder will publish it to listeners. + ((EncoderControl)ec).updateEncoderData(data); // invoke("publishEncoderData", data); // TODO: all this code needs to move out of here! return data; diff --git a/src/main/java/org/myrobotlab/service/As5048AEncoder.java b/src/main/java/org/myrobotlab/service/As5048AEncoder.java index c4f8dccb3e..8c93b684ee 100755 --- a/src/main/java/org/myrobotlab/service/As5048AEncoder.java +++ b/src/main/java/org/myrobotlab/service/As5048AEncoder.java @@ -45,7 +45,7 @@ public static void main(String[] args) throws Exception { log.info("Here we are.."); } - void updateEncoderData(EncoderData data) { + public void updateEncoderData(EncoderData data) { // publish the updated encoder data (this is updated from the arduino..) invoke("publishEncoderData", data); } diff --git a/src/main/java/org/myrobotlab/service/interfaces/EncoderControl.java b/src/main/java/org/myrobotlab/service/interfaces/EncoderControl.java index 895fc7f7f3..5e6192f581 100755 --- a/src/main/java/org/myrobotlab/service/interfaces/EncoderControl.java +++ b/src/main/java/org/myrobotlab/service/interfaces/EncoderControl.java @@ -2,8 +2,9 @@ import org.myrobotlab.framework.interfaces.Attachable; import org.myrobotlab.sensor.EncoderData; +import org.myrobotlab.sensor.EncoderPublisher; -public interface EncoderControl extends Attachable { +public interface EncoderControl extends EncoderPublisher, Attachable { /** * stop the stream of encoder data @@ -41,4 +42,10 @@ public interface EncoderControl extends Attachable { */ public Double getPos(); -} + /** + * encoder controls can update their copy of the encoder data and then publish it to listeners. + * @param data + */ + public void updateEncoderData(EncoderData data); + +} \ No newline at end of file From d569c65b2143307f3e10d03d45959a248258e233 Mon Sep 17 00:00:00 2001 From: kwatters Date: Mon, 2 Jun 2025 13:25:43 -0400 Subject: [PATCH 05/15] small webgui for the As5048AEncoder simply displays the angle and raw value from the encoder. --- .../app/service/js/As5048AEncoderGui.js | 39 +++++++++++++++++++ .../app/service/views/As5048AEncoderGui.html | 18 +++++++++ 2 files changed, 57 insertions(+) create mode 100755 src/main/resources/resource/WebGui/app/service/js/As5048AEncoderGui.js create mode 100755 src/main/resources/resource/WebGui/app/service/views/As5048AEncoderGui.html diff --git a/src/main/resources/resource/WebGui/app/service/js/As5048AEncoderGui.js b/src/main/resources/resource/WebGui/app/service/js/As5048AEncoderGui.js new file mode 100755 index 0000000000..d0f11e12cc --- /dev/null +++ b/src/main/resources/resource/WebGui/app/service/js/As5048AEncoderGui.js @@ -0,0 +1,39 @@ +angular.module('mrlapp.service.As5048AEncoderGui', []).controller('As5048AEncoderGuiCtrl', ['$scope', 'mrl', function($scope, mrl) { + console.info('As5048AEncoderGuiCtrl') + var _self = this + var msg = this.msg + + $scope.controllers = [] + + // published EncoderData + $scope.data = null + + // GOOD TEMPLATE TO FOLLOW + this.updateState = function(service) { + $scope.service = service + } + + this.onMsg = function(inMsg) { + var data = inMsg.data[0] + switch (inMsg.method) { + case 'onState': + _self.updateState(data) + $scope.$apply() + break + case 'onEncoderData': + $scope.data = data + $scope.$apply() + break + case 'onStatus': + console.info("On status") + default: + console.info("ERROR - unhandled method " + $scope.name + " Method " + inMsg.method) + break + } + + }; + + msg.subscribe('publishEncoderData') + msg.subscribe(this) +} +]) diff --git a/src/main/resources/resource/WebGui/app/service/views/As5048AEncoderGui.html b/src/main/resources/resource/WebGui/app/service/views/As5048AEncoderGui.html new file mode 100755 index 0000000000..a3bda34fa5 --- /dev/null +++ b/src/main/resources/resource/WebGui/app/service/views/As5048AEncoderGui.html @@ -0,0 +1,18 @@ +
+
 
+
+ AS5048A Encoder + + + + + + + + + + +
Source{{ data.source }}
Angle{{ data.angle.toFixed(3) }}
Raw Value{{ data.value }}
+
+
+ From 5ada961312dc8740d916b82b90e24106a8eb4c4d Mon Sep 17 00:00:00 2001 From: kwatters Date: Mon, 2 Jun 2025 13:41:54 -0400 Subject: [PATCH 06/15] adding simplistic webgui for amt203 encoder --- .../WebGui/app/service/js/Amt203EncoderGui.js | 39 +++++++++++++++++++ .../app/service/views/Amt203EncoderGui.html | 18 +++++++++ 2 files changed, 57 insertions(+) create mode 100755 src/main/resources/resource/WebGui/app/service/js/Amt203EncoderGui.js create mode 100755 src/main/resources/resource/WebGui/app/service/views/Amt203EncoderGui.html diff --git a/src/main/resources/resource/WebGui/app/service/js/Amt203EncoderGui.js b/src/main/resources/resource/WebGui/app/service/js/Amt203EncoderGui.js new file mode 100755 index 0000000000..0339e3b824 --- /dev/null +++ b/src/main/resources/resource/WebGui/app/service/js/Amt203EncoderGui.js @@ -0,0 +1,39 @@ +angular.module('mrlapp.service.Amt203EncoderGui', []).controller('Amt203EncoderGuiCtrl', ['$scope', 'mrl', function($scope, mrl) { + console.info('Amt203EncoderGuiCtrl') + var _self = this + var msg = this.msg + + $scope.controllers = [] + + // published EncoderData + $scope.data = null + + // GOOD TEMPLATE TO FOLLOW + this.updateState = function(service) { + $scope.service = service + } + + this.onMsg = function(inMsg) { + var data = inMsg.data[0] + switch (inMsg.method) { + case 'onState': + _self.updateState(data) + $scope.$apply() + break + case 'onEncoderData': + $scope.data = data + $scope.$apply() + break + case 'onStatus': + console.info("On status") + default: + console.info("ERROR - unhandled method " + $scope.name + " Method " + inMsg.method) + break + } + + }; + + msg.subscribe('publishEncoderData') + msg.subscribe(this) +} +]) diff --git a/src/main/resources/resource/WebGui/app/service/views/Amt203EncoderGui.html b/src/main/resources/resource/WebGui/app/service/views/Amt203EncoderGui.html new file mode 100755 index 0000000000..a3bda34fa5 --- /dev/null +++ b/src/main/resources/resource/WebGui/app/service/views/Amt203EncoderGui.html @@ -0,0 +1,18 @@ +
+
 
+
+ AS5048A Encoder + + + + + + + + + + +
Source{{ data.source }}
Angle{{ data.angle.toFixed(3) }}
Raw Value{{ data.value }}
+
+
+ From 0fcdd8b8ba0e12c63f5111caf20c72905042fcab Mon Sep 17 00:00:00 2001 From: kwatters Date: Thu, 5 Jun 2025 11:20:49 -0400 Subject: [PATCH 07/15] minor fixes for the motordualpwm webgui --- .../resource/WebGui/app/service/js/MotorDualPwmGui.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/resource/WebGui/app/service/js/MotorDualPwmGui.js b/src/main/resources/resource/WebGui/app/service/js/MotorDualPwmGui.js index c9c86d4041..5a0d02719f 100644 --- a/src/main/resources/resource/WebGui/app/service/js/MotorDualPwmGui.js +++ b/src/main/resources/resource/WebGui/app/service/js/MotorDualPwmGui.js @@ -85,7 +85,7 @@ angular.module('mrlapp.service.MotorDualPwmGui', []).controller('MotorDualPwmGui $scope.update = function() { console.info('update') - msg.send('map', $scope.service.config.mapper.minIn, $scope.service.config.mapper.minIn, $scope.service.config.mapper.minOut, $scope.service.config.mapper.maxOut) + msg.send('map', $scope.service.config.mapper.minIn, $scope.service.config.mapper.maxIn, $scope.service.config.mapper.minOut, $scope.service.config.mapper.maxOut) } $scope.setController = function(c) { @@ -110,7 +110,7 @@ angular.module('mrlapp.service.MotorDualPwmGui', []).controller('MotorDualPwmGui $scope.setSpeed = function() { - msg.send('setSpeed', $scope.requestedPower) + msg.send('move', $scope.requestedPower) } From c54bcbe304eb21f993e28adbfa80ee9abe431739 Mon Sep 17 00:00:00 2001 From: kwatters Date: Thu, 5 Jun 2025 14:00:12 -0400 Subject: [PATCH 08/15] small updates --- src/main/java/org/myrobotlab/sensor/TimeEncoder.java | 8 ++++++++ src/main/java/org/myrobotlab/service/Arduino.java | 2 +- src/main/java/org/myrobotlab/service/As5048AEncoder.java | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/myrobotlab/sensor/TimeEncoder.java b/src/main/java/org/myrobotlab/sensor/TimeEncoder.java index 43233f46d2..fb006cf4c9 100644 --- a/src/main/java/org/myrobotlab/sensor/TimeEncoder.java +++ b/src/main/java/org/myrobotlab/sensor/TimeEncoder.java @@ -372,4 +372,12 @@ public void stopMove() { public void attachEncoderController(EncoderController controller) { // NoOp, the TimeEncoder doesn't need a controller. } + + @Override + public void updateEncoderData(EncoderData data) { + // NoOp, this encoder updates itself. + // TODO: should we publish an invoke here? + // invoke("publishEncoderData", data); + + } } \ No newline at end of file diff --git a/src/main/java/org/myrobotlab/service/Arduino.java b/src/main/java/org/myrobotlab/service/Arduino.java index 53289d062e..bb828ddf3e 100644 --- a/src/main/java/org/myrobotlab/service/Arduino.java +++ b/src/main/java/org/myrobotlab/service/Arduino.java @@ -1685,7 +1685,6 @@ public EncoderData publishEncoderData(EncoderData data) { @Override public EncoderData publishEncoderData(Integer deviceId, Integer position) { // Also need to log this - EncoderControl ec = (EncoderControl) getDevice(deviceId); String pin = null; Double angle = null; @@ -1706,6 +1705,7 @@ public EncoderData publishEncoderData(Integer deviceId, Integer position) { // TODO: how do i publish the data from the encoder? // ec.publishEncoderData(data); // This will pass the encoder data to the encoder and the encoder will publish it to listeners. + //log.info("Encoder data! {}", data); ((EncoderControl)ec).updateEncoderData(data); // invoke("publishEncoderData", data); // TODO: all this code needs to move out of here! diff --git a/src/main/java/org/myrobotlab/service/As5048AEncoder.java b/src/main/java/org/myrobotlab/service/As5048AEncoder.java index 8c93b684ee..3bebeebbc2 100755 --- a/src/main/java/org/myrobotlab/service/As5048AEncoder.java +++ b/src/main/java/org/myrobotlab/service/As5048AEncoder.java @@ -47,6 +47,7 @@ public static void main(String[] args) throws Exception { public void updateEncoderData(EncoderData data) { // publish the updated encoder data (this is updated from the arduino..) + // log.info("Encoder data: {}", data); invoke("publishEncoderData", data); } From c1108ce8be9aa67b9a814c62adfab65fde42bcf4 Mon Sep 17 00:00:00 2001 From: kwatters Date: Thu, 5 Jun 2025 14:29:36 -0400 Subject: [PATCH 09/15] moving the diyservo2 work into this branch --- .../org/myrobotlab/service/DiyServo2.java | 629 ++++++++++++++++++ .../service/config/DiyServo2Config.java | 37 ++ .../WebGui/app/service/js/DiyServo2Gui.js | 34 + .../app/service/views/DiyServo2Gui.html | 4 + 4 files changed, 704 insertions(+) create mode 100755 src/main/java/org/myrobotlab/service/DiyServo2.java create mode 100755 src/main/java/org/myrobotlab/service/config/DiyServo2Config.java create mode 100755 src/main/resources/resource/WebGui/app/service/js/DiyServo2Gui.js create mode 100755 src/main/resources/resource/WebGui/app/service/views/DiyServo2Gui.html diff --git a/src/main/java/org/myrobotlab/service/DiyServo2.java b/src/main/java/org/myrobotlab/service/DiyServo2.java new file mode 100755 index 0000000000..4927c74d21 --- /dev/null +++ b/src/main/java/org/myrobotlab/service/DiyServo2.java @@ -0,0 +1,629 @@ +/** + * + * This file is part of MyRobotLab (http://myrobotlab.org). + * + * MyRobotLab is free software: you can redistribute it and/or modify + * it under the terms of the Apache License 2.0 as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version (subject to the "Classpath" exception + * as provided in the LICENSE.txt file that accompanied this code). + * + * MyRobotLab is distributed in the hope that it will be useful or fun, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Apache License 2.0 for more details. + * + * All libraries in thirdParty bundle are subject to their own license + * requirements - please refer to http://myrobotlab.org/libraries for + * details. + * + * Enjoy ! + * + * */ + +package org.myrobotlab.service; + +import org.myrobotlab.framework.Service; +import org.myrobotlab.logging.LoggingFactory; +import org.myrobotlab.math.interfaces.Mapper; +import org.myrobotlab.sensor.EncoderData; +import org.myrobotlab.sensor.EncoderListener; +import org.myrobotlab.service.data.ServoMove; +import org.myrobotlab.service.data.ServoSpeed; +import org.myrobotlab.service.interfaces.EncoderControl; +import org.myrobotlab.service.interfaces.MotorControl; +import org.myrobotlab.service.interfaces.ServoControl; +import org.myrobotlab.service.interfaces.ServoControlPublisher; +import org.myrobotlab.service.interfaces.ServoController; +import org.myrobotlab.service.interfaces.ServoEvent; +import org.myrobotlab.service.interfaces.ServoStatusPublisher; + +/** + * Simple(ish) DiyServo2. + * This service requires an Encoder and a MotorControl. It uses PID control to + * control the position of an actuator measured by the encoder with the motor. + * The standard PID params are supported Kp,Ki,Kd. + * This implements servo control. + * + * Data is published from the encoder to this service, that updates the input to the Pid control. + * The Pid output is computed by default at 20Hz and is controlled by the sampleTime parameter. + * The output of the pid control is then written to the motor control + */ + +public class DiyServo2 extends Service implements EncoderListener, ServoControl, ServoControlPublisher, ServoStatusPublisher { + + private static final long serialVersionUID = 1L; + private volatile boolean enabled = true; + private MotorControl motorControl; + private Double currentAngle; + public Pid pid; + + // TODO: use the motor name. + public String pidKey = "diy2"; + + private double kp = 0.05; + private double ki = 0.001; // 0.020; + private double kd = 0.001; // 0.020; + public double setpoint = 90.0; // Intial + // samples per second. + public int sampleTime = 20; + static final public int MODE_AUTOMATIC = 1; + + transient MotorUpdater motorUpdater; + EncoderControl encoder; + private Double rest = 90.0; + private long lastActivityTimeMS; + + public DiyServo2(String reservedKey, String inId) { + super(reservedKey, inId); + } + + @Override + synchronized public void startService() { + super.startService(); + pidKey = getFullName(); + // TODO:figure out what we should do here? perhaps nothing? + initPid(); + } + + void initPid() { + // are the peers going to be created? + pid = (Pid)Runtime.start("pid", "Pid"); + //pid = (Pid) createPeer("pid"); + pidKey = this.getName(); + pid.setPid(pidKey, kp, ki, kd); // Create a PID with the name of this + // service instance + pid.setMode(pidKey, MODE_AUTOMATIC); // Initial mode is manual + pid.setOutputRange(pidKey, -1.0, 1.0); // Set the Output range to match the Motor input + pid.setSampleTime(pidKey, sampleTime); // Sets the sample time + pid.setSetpoint(pidKey, setpoint); + pid.startService(); + } + + @Override + public void onEncoderData(EncoderData data) { + // System.err.println("DIY Servo Encoder Data: " + data); + this.currentAngle = data.angle; + } + + public void attachEncoderControl(EncoderControl enc) { + encoder = enc; + // Tell the encoder to publish encoder data to this service + encoder.attachEncoderListener(this); + } + + private void attachMotorControl(MotorControl mot) { + // use the motor name as the pid key + this.motorControl = mot; + + // this.pidKey = mot.getName(); + if (motorUpdater == null) { + log.info("Starting MotorUpdater"); + motorUpdater = new MotorUpdater(getName()); + motorUpdater.start(); + log.info("MotorUpdater started"); + } + + } + + public Double moveTo(Double angle) { + // This updates the setpoint of the pid control. + this.setpoint = angle; + pid.setSetpoint(pidKey, angle); + lastActivityTimeMS = System.currentTimeMillis(); + // Why does this return a boolean? + return angle; + } + + /** + * MotorUpdater The control loop to update the MotorControl with new values + * based on the PID calculations + * + */ + public class MotorUpdater extends Thread { + + double lastOutput = 0.0; + // degree threshold for saying that we've arrived. + double threshold = 0.25; + // goal is to not use this + + public MotorUpdater(String name) { + super(String.format("%s.motorUpdater", name)); + } + + @Override + public void run() { + log.info("Motor updater started"); + try { + while (true) { + if (isRunning()) { + log.info("Updating control loop"); + // Calculate the new value for the motor + if (pid.data.containsKey(pidKey)) { + // Update the pid input value. + // pass the current angle from the encoder to the pid controller + double output = pid.compute(pidKey, currentAngle); + double delta = Math.abs(currentAngle - setpoint); + if (delta < threshold ) { + log.info("Arrived!"); + // TODO: some debouncing logic here. + // TODO: publish the servo events for started/stopped here. + } else if (output != lastOutput) { + log.info("move motor : Power: {} Target: {} Current: {} Delta: {}", output, setpoint, currentAngle, delta); + motorControl.move(output); + lastOutput = output; + } else { + log.info("delta {} threshold {} current {} setpoint {} output {} lastOutput {}", delta, threshold, currentAngle, setpoint, output, lastOutput); + } + } + // TODO: how long do we need to sleep + // TODO: maybe a more accurate loop timer? + // This is a samples per second.. we need to only wait for the remainaing amount of time in the period. + Thread.sleep(1000 / sampleTime); + } else { + log.info("Not running?!"); + } + + } + } catch (Exception e) { + if (e instanceof InterruptedException) { + motorControl.stop(); + } else { + log.error("motor updater threw", e); + } + } + } + + private boolean isRunning() { + // if we are enabled, have a motor connected and have received encoder data. + return enabled && motorControl != null && currentAngle != null; + } + } + + @Override + public void disable() { + // TODO: what do do here? + // motorControl.disable(); + motorControl.stop(); + enabled = false; + // TODO: broadcast enabled/disabled messages? + } + + @Override + public void enable() { + // TODO: what do to here? + // motorControl.enable(); + enabled = true; + } + + @Override + public double getRest() { + // Ok.. not a bad idea.. let's have a rest position for the servo.. default to 90 deg? or something? + return rest; + } + + @Override + public boolean isAutoDisable() { + // TODO: impl this.. safety is good. + return false; + } + + @Override + public void attach(ServoController listener) { + // TODO: remove from ServoControl interface... NoOp here. + // NoOp : no servo controllers here.. + log.warn("Diy Servo doesn't use a controller.. no implemented."); + } + + @Override + public void detach(ServoController listener) { + // TODO maybe remove from interface? this service doesn't give a crapola about servo controllers. + log.warn("Diy Servo doesn't use a controller.. no implemented."); + } + + @Override + public String getController() { + // TODO remove from interface?. we have no controller. + log.warn("Diy Servo doesn't use a controller.. no implemented."); + return null; + } + + @Override + public EncoderControl getEncoder() { + // TODO: we just subscribe to the encoder.. we don't have/need a handle to it! + // why are we expected to return it.. remove from interface. + return encoder; + } + + @Override + public long getLastActivityTime() { + return lastActivityTimeMS; + } + + @Override + public Mapper getMapper() { + // TODO - we have no mapper... + return null; + } + + @Override + public double getMax() { + // TODO: should implement.. safety limits are important. + // This might be useful to know what the max/min value that this encoder can get to.. but for us.. it's 360 degrees.. and can rotate as much as we like. + return 360; + } + + @Override + public double getMin() { + // TODO safety limits are good.. + return 0; + } + + @Override + public String getPin() { + log.warn("DiyServo doesn't have pins. No implemented."); + // TODO: This doesn't mean anything here. + // maybe this is the pin from the encoder? but why.. + return null; + } + + @Override + public double getCurrentInputPos() { + // TODO: return currentAngle? we have no mapper. + return currentAngle; + } + + @Override + public double getCurrentOutputPos() { + // TODO: this interface has way too much stuff in it... + // return the last known encoder angle + return currentAngle; + } + + @Override + public Double getSpeed() { + // TODO: implement speed control + return null; + } + + @Override + public double getTargetOutput() { + // the setPoint for the pid control is the target output. + // we have no mapper.. + return setpoint; + } + + @Override + public double getTargetPos() { + // This is the setPoint for the pid control.. the target position. + return setpoint; + } + + @Override + public boolean isBlocking() { + // TODO What does this mean? should we remove this from the interface? + return false; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public boolean isInverted() { + // TODO not sure what this means for a diyservo. + return false; + } + + @Override + public boolean isMoving() { + // TODO we should trigger this off the power output being sent to the motor. + return false; + } + + @Override + public void map(double minX, double maxX, double minY, double maxY) { + // TODO No mapper in the diyservo (yet.) + } + + @Override + public Double moveToBlocking(Double pos) { + return moveToBlocking(pos, null); + } + + @Override + public Double moveToBlocking(Double pos, Long timeoutMs) { + // TODO : implement a timed out blocking move. + this.moveTo(pos); + //TODO: block until we get there! + return null; + } + + @Override + public void rest() { + // ok. move to base class. + moveTo(rest); + } + + @Override + public void setMapper(Mapper m) { + // TODO Do we actually need a mapper? + } + + @Override + public void setMinMax(double minXY, double maxXY) { + // TODO: implement this.. for safty limits.. dont support a move call outside the specified range. + // we don't even have a mapper.. we dont' need one..we might want a mapper that gives us a phase shift. + // but reality is. that should be handled by the encoder. + } + + @Override + public void setMinMaxOutput(double minY, double maxY) { + // TODO: implement this.. for safty limits.. dont support a move call outside the specified range. + } + + @Override + public void setPin(Integer pin) { + // TODO: There are no pins! we have no pins! + log.warn("setPin not implemented in DiyServo."); + } + + @Override + public void setPin(String pin) { + // TODO: remove from interface? We don't have any pins. + log.warn("setPin not implemented in DiyServo."); + } + + @Override + public void setPosition(double pos) { + // TODO: maybe deprecate and remove from interface ? + // This method had a strange functionality of setting a position + // even though the servo wasn't attached / enabled? + moveTo(pos); + } + + @Override + public void setRest(double rest) { + // TODO move to base class + this.rest = rest; + } + + @Override + public void setSpeed(Double degreesPerSecond) { + // TODO: velocity control. + } + + @Override + public void stop() { + // Stop the motor... anything else? + motorControl.move(0.0); + } + + @Override + public void sync(ServoControl sc) { + // TODO Impl me. + } + + @Override + public void unsync(ServoControl sc) { + // TODO Impl me + } + + @Override + public void waitTargetPos() { + // TODO Auto-generated method stub + // really? ok. + // here we should wait until we have "arrived" ... + + } + + @Override + public void writeMicroseconds(int uS) { + // NoOp here... should be removed from ServoControl interface.. this is specific to a pwm controlled servo + log.warn("Write Microseconds not implemented for DiyServo."); + } + + + + @Override + public void fullSpeed() { + // TODO: add a velocity control. + // TODO: deprecated, remove from interface? + } + + @Override + public ServoControl publishMoveTo(ServoControl sc) { + return sc; + } + + @Override + public ServoControl publishServoStop(ServoControl sc) { + return sc; + } + + @Override + public Double moveToBlocking(Integer newPos) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Double moveToBlocking(Integer newPos, Long timeoutMs) { + // TODO Auto-generated method stub + return null; + } + + @Override + public ServoEvent publishServoStarted(String name, Double position) { + // TODO Auto-generated method stub + return null; + } + + @Override + public ServoEvent publishServoStopped(String name, Double position) { + // TODO Auto-generated method stub + return null; + } + + @Override + public ServoMove publishServoMoveTo(ServoMove pos) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String publishServoEnable(String name) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void attachServoControlListener(String name) { + // TODO Auto-generated method stub + + } + + @Override + public void setAutoDisable(boolean autoDisable) { + // TODO Auto-generated method stub + + } + + @Override + public void setInverted(boolean invert) { + // TODO Auto-generated method stub + + } + + @Override + public void setSpeed(Integer degreesPerSecond) { + // TODO Auto-generated method stub + + } + + @Override + public void sync(String name) { + // TODO Auto-generated method stub + + } + + @Override + public void unsync(String name) { + // TODO Auto-generated method stub + + } + + @Override + public void attachServoController(String sc) { + // TODO Auto-generated method stub + + } + + @Override + public void setMaxSpeed() { + // TODO Auto-generated method stub + + } + + @Override + public Double moveTo(Integer newPos) { + // TODO Auto-generated method stub + return null; + } + + @Override + public ServoSpeed publishServoSetSpeed(ServoControl sc) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String publishServoEnable(ServoControl sc) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String publishServoDisable(ServoControl sc) { + // TODO Auto-generated method stub + return null; + } + + + public static void main(String[] args) throws Exception { + + LoggingFactory.init("info"); + + + WebGui webgui = (WebGui)Runtime.start("webgui", "WebGui"); + + // Compose the components of the diy servo and attach them. + // Make one.. and stuff. + // setup the encoder. + Arduino ard = (Arduino)Runtime.start("ard", "Arduino"); + ard.connect("COM5"); + // ard.setDebug(true); + As5048AEncoder encoder = (As5048AEncoder) Runtime.start("encoder", "As5048AEncoder"); + encoder.setPin(10); + ard.attach(encoder); + // setup the motor. + // encoder.ttach + MotorDualPwm mot = (MotorDualPwm) Runtime.start("diyServo.motor", "MotorDualPwm"); + mot.setPwmPins(6, 7); + ard.attach(mot); + // TODO: attach both to the diyservo and set the pin. + + + + if (true) { + // What else do we need? + DiyServo2 diy = (DiyServo2)Runtime.start("diy", "DiyServo2"); + // attach the encoder and motor to the diy servo. + diy.attachEncoderControl(encoder); + diy.attachMotorControl(mot); + + // Now we can move it?? + + + + // attach the encoder and motor + // diy.attachEncoderControl(encoder); + // diy.attachMotorControl(mot); + // Tell the servo to move somewhere. + + diy.moveTo(75.0); + Thread.sleep(2000); + // diy.disable(); + // Thread.sleep(1000); + // diy.enable(); + diy.moveTo(125.0); + } + System.out.println("Press the any key"); + System.in.read(); + + } + + + +} \ No newline at end of file diff --git a/src/main/java/org/myrobotlab/service/config/DiyServo2Config.java b/src/main/java/org/myrobotlab/service/config/DiyServo2Config.java new file mode 100755 index 0000000000..15def3a605 --- /dev/null +++ b/src/main/java/org/myrobotlab/service/config/DiyServo2Config.java @@ -0,0 +1,37 @@ +package org.myrobotlab.service.config; + +import org.myrobotlab.framework.Plan; +import org.myrobotlab.service.Pid.PidData; + +public class DiyServo2Config extends ServiceConfig { + // TODO: add config here + @Override + public Plan getDefault(Plan plan, String name) { + super.getDefault(plan, name); + + // Ok.. what do we need to add here for the DIYServo? + // we need an encoder... + // we need a motor + // we need a Pid + addDefaultPeerConfig(plan, name, "motor", "MotorDualPwm"); + addDefaultPeerConfig(plan, name, "encoder", "As5048AEncoder"); + addDefaultPeerConfig(plan, name, "pid", "Pid"); + + + MotorDualPwmConfig motor = (MotorDualPwmConfig) plan.get(getPeerName("motor")); + // TODO: controller?! + // motor.controller + motor.leftPwmPin = "6"; + motor.rightPwmPin = "7"; + + // TODO: how do we handle the controller for the encoder? (could be different than the motor) + As5048AEncoderConfig encoder = (As5048AEncoderConfig) plan.get(getPeerName("encoder")); + + PidConfig pid = (PidConfig) plan.get(getPeerName("pid")); + pid.data.put(name, new PidData()); + + // default settings? + return plan; + } +} + diff --git a/src/main/resources/resource/WebGui/app/service/js/DiyServo2Gui.js b/src/main/resources/resource/WebGui/app/service/js/DiyServo2Gui.js new file mode 100755 index 0000000000..d61db6a424 --- /dev/null +++ b/src/main/resources/resource/WebGui/app/service/js/DiyServo2Gui.js @@ -0,0 +1,34 @@ +angular.module('mrlapp.service.DiyServo2Gui', []).controller('DiyServo2GuiCtrl', ['$scope', 'mrl', function($scope, mrl) { + console.info('DiyServo2GuiCtrl') + var _self = this + var msg = this.msg + + // GOOD TEMPLATE TO FOLLOW + this.updateState = function(service) { + $scope.service = service + } + + this.onMsg = function(inMsg) { + var data = inMsg.data[0] + switch (inMsg.method) { + case 'onState': + _self.updateState(data) + $scope.$apply() + break + // TODO: figure out which callbacks we will subscribe to here. + case 'onServoData': + $scope.data = data + $scope.$apply() + break + case 'onStatus': + console.info("On status") + default: + console.info("ERROR - unhandled method " + $scope.name + " Method " + inMsg.method) + break + } + + }; + + // msg.subscribe('publishEncoderData') + msg.subscribe(this)} +]) diff --git a/src/main/resources/resource/WebGui/app/service/views/DiyServo2Gui.html b/src/main/resources/resource/WebGui/app/service/views/DiyServo2Gui.html new file mode 100755 index 0000000000..b5e1232f0a --- /dev/null +++ b/src/main/resources/resource/WebGui/app/service/views/DiyServo2Gui.html @@ -0,0 +1,4 @@ + +
+ Diy Servo 2 ! +
From 8ea3559008d6929ee6371e9f10df930aa110011e Mon Sep 17 00:00:00 2001 From: kwatters Date: Thu, 5 Jun 2025 16:07:16 -0400 Subject: [PATCH 10/15] meta file for the diyservo2 --- .../service/meta/DiyServo2Meta.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100755 src/main/java/org/myrobotlab/service/meta/DiyServo2Meta.java diff --git a/src/main/java/org/myrobotlab/service/meta/DiyServo2Meta.java b/src/main/java/org/myrobotlab/service/meta/DiyServo2Meta.java new file mode 100755 index 0000000000..51ed2c8d52 --- /dev/null +++ b/src/main/java/org/myrobotlab/service/meta/DiyServo2Meta.java @@ -0,0 +1,27 @@ +package org.myrobotlab.service.meta; + +import org.myrobotlab.framework.Platform; +import org.myrobotlab.logging.LoggerFactory; +import org.myrobotlab.service.meta.abstracts.MetaData; +import org.slf4j.Logger; + +public class DiyServo2Meta extends MetaData { + private static final long serialVersionUID = 1L; + public final static Logger log = LoggerFactory.getLogger(DiyServo2Meta.class); + + /** + * This class is contains all the meta data details of a service. It's peers, + * dependencies, and all other meta data related to the service. + * + */ + public DiyServo2Meta(String name) { + Platform platform = Platform.getLocalInstance(); + addDescription("Controls a motor so that it can be used as a Servo with an encoder feedback"); + addCategory("control", "servo"); + // TODO: add peers here? + //addPeer("motor", "MotorDualPwm", "MotorControl service"); + //addPeer("pid", "Pid", "PID service"); + + } + +} From 74bdf5b865b915a22bc8eeb6eb166681903a04a5 Mon Sep 17 00:00:00 2001 From: kwatters Date: Fri, 6 Jun 2025 12:17:40 -0400 Subject: [PATCH 11/15] sync some small changes.. add a move to slider in the diyservo2 gui for testing. --- src/main/java/org/myrobotlab/service/DiyServo2.java | 13 +++++++++---- .../resource/WebGui/app/service/js/DiyServo2Gui.js | 10 +++++++++- .../WebGui/app/service/views/DiyServo2Gui.html | 9 ++++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/DiyServo2.java b/src/main/java/org/myrobotlab/service/DiyServo2.java index 4927c74d21..891a4b1b2c 100755 --- a/src/main/java/org/myrobotlab/service/DiyServo2.java +++ b/src/main/java/org/myrobotlab/service/DiyServo2.java @@ -28,6 +28,7 @@ import org.myrobotlab.math.interfaces.Mapper; import org.myrobotlab.sensor.EncoderData; import org.myrobotlab.sensor.EncoderListener; +import org.myrobotlab.service.config.DiyServo2Config; import org.myrobotlab.service.data.ServoMove; import org.myrobotlab.service.data.ServoSpeed; import org.myrobotlab.service.interfaces.EncoderControl; @@ -50,7 +51,7 @@ * The output of the pid control is then written to the motor control */ -public class DiyServo2 extends Service implements EncoderListener, ServoControl, ServoControlPublisher, ServoStatusPublisher { +public class DiyServo2 extends Service implements EncoderListener, ServoControl, ServoControlPublisher, ServoStatusPublisher { private static final long serialVersionUID = 1L; private volatile boolean enabled = true; @@ -66,7 +67,7 @@ public class DiyServo2 extends Service implements EncoderListener, ServoControl, private double kd = 0.001; // 0.020; public double setpoint = 90.0; // Intial // samples per second. - public int sampleTime = 20; + public int sampleTime = 4; static final public int MODE_AUTOMATIC = 1; transient MotorUpdater motorUpdater; @@ -127,11 +128,15 @@ private void attachMotorControl(MotorControl mot) { } public Double moveTo(Double angle) { + log.info("Servo Move to {}", angle); // This updates the setpoint of the pid control. this.setpoint = angle; pid.setSetpoint(pidKey, angle); lastActivityTimeMS = System.currentTimeMillis(); // Why does this return a boolean? + + // invoke("publishMoveTo", this); + invoke("publishServoEvent", angle); return angle; } @@ -157,7 +162,7 @@ public void run() { try { while (true) { if (isRunning()) { - log.info("Updating control loop"); + // log.info("Updating control loop"); // Calculate the new value for the motor if (pid.data.containsKey(pidKey)) { // Update the pid input value. @@ -549,7 +554,7 @@ public void setMaxSpeed() { @Override public Double moveTo(Integer newPos) { // TODO Auto-generated method stub - return null; + return moveTo(Double.valueOf(newPos)); } @Override diff --git a/src/main/resources/resource/WebGui/app/service/js/DiyServo2Gui.js b/src/main/resources/resource/WebGui/app/service/js/DiyServo2Gui.js index d61db6a424..87dbdfde5a 100755 --- a/src/main/resources/resource/WebGui/app/service/js/DiyServo2Gui.js +++ b/src/main/resources/resource/WebGui/app/service/js/DiyServo2Gui.js @@ -8,6 +8,10 @@ angular.module('mrlapp.service.DiyServo2Gui', []).controller('DiyServo2GuiCtrl', $scope.service = service } + $scope.moveTo = function(pos) { + msg.send('moveTo', pos) + } + this.onMsg = function(inMsg) { var data = inMsg.data[0] switch (inMsg.method) { @@ -16,6 +20,10 @@ angular.module('mrlapp.service.DiyServo2Gui', []).controller('DiyServo2GuiCtrl', $scope.$apply() break // TODO: figure out which callbacks we will subscribe to here. + case 'onServoEvent': + $scope.data = data + $scope.$apply() + break case 'onServoData': $scope.data = data $scope.$apply() @@ -29,6 +37,6 @@ angular.module('mrlapp.service.DiyServo2Gui', []).controller('DiyServo2GuiCtrl', }; - // msg.subscribe('publishEncoderData') + msg.subscribe('publishServoEvent') msg.subscribe(this)} ]) diff --git a/src/main/resources/resource/WebGui/app/service/views/DiyServo2Gui.html b/src/main/resources/resource/WebGui/app/service/views/DiyServo2Gui.html index b5e1232f0a..4461bfaa70 100755 --- a/src/main/resources/resource/WebGui/app/service/views/DiyServo2Gui.html +++ b/src/main/resources/resource/WebGui/app/service/views/DiyServo2Gui.html @@ -1,4 +1,11 @@
- Diy Servo 2 ! + Diy Servo 2 ! {{ serivce.targetPos }} + Diy Servo a..! {{ targetPos }} + Data {{ data }} + +
+
From c7f6db2710fbbdb74a1a3f8c27ddd65fae5dd721 Mon Sep 17 00:00:00 2001 From: Kevin Watters Date: Tue, 10 Jun 2025 10:07:50 -0400 Subject: [PATCH 12/15] sync some more changes, allow the encoder and dualpwmmotor to reattach if the arduino is reset. --- .../org/myrobotlab/sensor/EncoderData.java | 48 +++++++++++++++++++ .../java/org/myrobotlab/service/Arduino.java | 14 ++++++ .../myrobotlab/service/As5048AEncoder.java | 28 ++++++++++- .../org/myrobotlab/service/DiyServo2.java | 22 +++++---- 4 files changed, 102 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/myrobotlab/sensor/EncoderData.java b/src/main/java/org/myrobotlab/sensor/EncoderData.java index a016c9f2a5..c45ec857b4 100644 --- a/src/main/java/org/myrobotlab/sensor/EncoderData.java +++ b/src/main/java/org/myrobotlab/sensor/EncoderData.java @@ -30,6 +30,54 @@ public class EncoderData { */ public double value; + public String getPin() { + return pin; + } + + public void setPin(String pin) { + this.pin = pin; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public Double getAngle() { + return angle; + } + + public void setAngle(Double angle) { + this.angle = angle; + } + + public double getValue() { + return value; + } + + public void setValue(double value) { + this.value = value; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + /** * time data was generated */ diff --git a/src/main/java/org/myrobotlab/service/Arduino.java b/src/main/java/org/myrobotlab/service/Arduino.java index bb828ddf3e..796ceb1ccc 100644 --- a/src/main/java/org/myrobotlab/service/Arduino.java +++ b/src/main/java/org/myrobotlab/service/Arduino.java @@ -503,6 +503,20 @@ public void reattach(DeviceMapping dm) { if (servo.isEnabled()) { msg.servoAttachPin(dm.getId(), pin); } + } else if (attachable instanceof As5048AEncoder) { + // TODO: reattach it! + As5048AEncoder enc = (As5048AEncoder)attachable; + log.info("================ re-attaching {} {} {} ================", enc.getName(), dm.getId(), enc.getPin()); + msg.encoderAttach(dm.getId(), 1, Integer.valueOf(enc.getPin())); + } else if (attachable instanceof MotorDualPwm) { + // TODO: reattach it! + MotorDualPwm motor = (MotorDualPwm)attachable; + int[] pins = new int[] {Integer.valueOf(motor.getLeftPwmPin()), Integer.valueOf(motor.getRightPwmPin())}; + // TODO: what's the type?! + int type = 0; + log.info("================ re-attaching {} {} {} ================", motor.getName(), dm.getId(), pins); + msg.motorAttach(dm.getId(),type, pins ); + } else if (attachable instanceof UltrasonicSensorControl) { log.warn("UltrasonicSensorControl not implemented"); // reattach logic diff --git a/src/main/java/org/myrobotlab/service/As5048AEncoder.java b/src/main/java/org/myrobotlab/service/As5048AEncoder.java index 3bebeebbc2..b39512133f 100755 --- a/src/main/java/org/myrobotlab/service/As5048AEncoder.java +++ b/src/main/java/org/myrobotlab/service/As5048AEncoder.java @@ -1,5 +1,8 @@ package org.myrobotlab.service; +import java.util.concurrent.LinkedBlockingQueue; + +import org.apache.commons.math3.util.Precision; import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.sensor.EncoderData; import org.myrobotlab.sensor.EncoderListener; @@ -16,8 +19,12 @@ */ public class As5048AEncoder extends AbstractPinEncoder implements EncoderControl, EncoderPublisher { + private static final int HISTORY_SIZE = 5; + private static final long serialVersionUID = 1L; + private LinkedBlockingQueue history = new LinkedBlockingQueue(HISTORY_SIZE); + public As5048AEncoder(String n, String id) { super(n, id); // 14 bit encoder is 2^16 steps of resolution @@ -48,7 +55,26 @@ public static void main(String[] args) throws Exception { public void updateEncoderData(EncoderData data) { // publish the updated encoder data (this is updated from the arduino..) // log.info("Encoder data: {}", data); - invoke("publishEncoderData", data); + // pop the first value + if (history.remainingCapacity() == 0) { + history.poll(); + } + history.offer(data); + // This is computing a moving average for the encoder value to smooth it out a bit. + double avgVal = history.stream().mapToDouble(EncoderData::getValue).average().orElse(0.0); + double avgAngle = Precision.round(history.stream().mapToDouble(EncoderData::getAngle).average().orElse(0.0), 1); + + // Precision.round(avgAngle, 2); + // double avgVal = (previousData.value + data.value)/2; + // double avgAngle = (previousData.angle + data.angle)/2; + // 0.1 degree resolution... truncate and filter the value for stability.. + EncoderData filteredData = new EncoderData( data.source, data.pin, avgVal, avgAngle); + // log.info("Original Angle: {} Filtered Angle: {}", data.angle, filteredData.angle); + + // previousData = data; + //invoke("publishEncoderData", data); + + invoke("publishEncoderData", filteredData); } } diff --git a/src/main/java/org/myrobotlab/service/DiyServo2.java b/src/main/java/org/myrobotlab/service/DiyServo2.java index 891a4b1b2c..c6d22ab5a2 100755 --- a/src/main/java/org/myrobotlab/service/DiyServo2.java +++ b/src/main/java/org/myrobotlab/service/DiyServo2.java @@ -67,7 +67,7 @@ public class DiyServo2 extends Service implements EncoderListen private double kd = 0.001; // 0.020; public double setpoint = 90.0; // Intial // samples per second. - public int sampleTime = 4; + public int sampleTime = 20; static final public int MODE_AUTOMATIC = 1; transient MotorUpdater motorUpdater; @@ -136,7 +136,7 @@ public Double moveTo(Double angle) { // Why does this return a boolean? // invoke("publishMoveTo", this); - invoke("publishServoEvent", angle); + // invoke("publishServoEvent", angle.doubleValue()); return angle; } @@ -164,13 +164,17 @@ public void run() { if (isRunning()) { // log.info("Updating control loop"); // Calculate the new value for the motor - if (pid.data.containsKey(pidKey)) { + if (pid.data.containsKey(pidKey) && currentAngle != null && pidKey != null) { // Update the pid input value. // pass the current angle from the encoder to the pid controller - double output = pid.compute(pidKey, currentAngle); + Double output = pid.compute(pidKey, currentAngle); + if (output == null) { + continue; + } double delta = Math.abs(currentAngle - setpoint); if (delta < threshold ) { log.info("Arrived!"); + motorControl.move(0); // TODO: some debouncing logic here. // TODO: publish the servo events for started/stopped here. } else if (output != lastOutput) { @@ -580,14 +584,14 @@ public static void main(String[] args) throws Exception { LoggingFactory.init("info"); - + Runtime.start("python", "Python"); WebGui webgui = (WebGui)Runtime.start("webgui", "WebGui"); // Compose the components of the diy servo and attach them. // Make one.. and stuff. // setup the encoder. Arduino ard = (Arduino)Runtime.start("ard", "Arduino"); - ard.connect("COM5"); + ard.connect("COM3"); // ard.setDebug(true); As5048AEncoder encoder = (As5048AEncoder) Runtime.start("encoder", "As5048AEncoder"); encoder.setPin(10); @@ -617,12 +621,12 @@ public static void main(String[] args) throws Exception { // diy.attachMotorControl(mot); // Tell the servo to move somewhere. - diy.moveTo(75.0); - Thread.sleep(2000); + // diy.moveTo(75.0); + //Thread.sleep(2000); // diy.disable(); // Thread.sleep(1000); // diy.enable(); - diy.moveTo(125.0); + diy.moveTo(250.0); } System.out.println("Press the any key"); System.in.read(); From c17d36c989014926b533917a603be59948483386 Mon Sep 17 00:00:00 2001 From: kwatters Date: Mon, 16 Jun 2025 13:58:04 -0400 Subject: [PATCH 13/15] reverting pom changes from the PR --- pom.xml | 361 +++++++++++++++++++++++++++----------------------------- 1 file changed, 171 insertions(+), 190 deletions(-) diff --git a/pom.xml b/pom.xml index 1b97232ed9..37bf21e80a 100644 --- a/pom.xml +++ b/pom.xml @@ -137,26 +137,7 @@ - - - - au.edu.federation.caliko - caliko - 1.3.8 - - - au.edu.federation.caliko.visualisation - caliko-visualisation - 1.3.8 - - - au.edu.federation.caliko.demo - caliko-demo - 1.3.8 - - - - + javazoom jlayer @@ -175,49 +156,49 @@ 1.0.3.3 provided - + - - - + + + - + org.boofcv boofcv-all 0.40.1 provided - + - + ChessBoard ChessBoard 1.0.0 provided - + - + it.sauronsoftware.cron4j cron4j 2.2.5 provided - + - + net.sf.opencsv opencsv 2.3 provided - + - + net.dv8tion JDA @@ -230,9 +211,9 @@ - + - + com.github.docker-java docker-java @@ -245,10 +226,10 @@ - - + + - + org.apache.tika tika-core @@ -335,58 +316,58 @@ 1.4.19 provided - + - + pl.allegro.tech embedded-elasticsearch 2.7.0 provided - + - + javax.mail mail 1.4.7 provided - + - + com.github.pnavais state-machine 1.2.0 provided - + - + org.eclipse.jgit org.eclipse.jgit 6.6.1.202309021850-r provided - + - + com.google.cloud google-cloud-vision 2.1.3 provided - + - - - + + + - + com.google.cloud google-cloud-translate @@ -399,9 +380,9 @@ 1.3.0 provided - + - + org.jsoup jsoup @@ -414,34 +395,34 @@ 3.3.2 provided - + - - - + + + - - - + + + - - - + + + - - - + + + - + jfugue jfugue 5.0.7 provided - + - + org.jmonkeyengine jme3-core @@ -508,9 +489,9 @@ 3.2.3 provided - + - + net.java.jinput jinput @@ -524,27 +505,27 @@ provided zip - + - + org.apache.kafka kafka-clients 1.0.1 provided - + - + com.1stleg jnativehook 2.0.3 provided - + - + io.github.ollama4j ollama4j @@ -565,9 +546,9 @@ - + - + leapmotion leap @@ -595,9 +576,9 @@ provided zip - + - + org.myrobotlab.audio voice-effects @@ -605,10 +586,10 @@ provided zip - + - - + + de.dfki.mary marytts @@ -948,18 +929,18 @@ 2.12.1 provided - + - + org.eclipse.paho org.eclipse.paho.client.mqttv3 1.2.1 provided - + - + io.moquette moquette-broker @@ -980,22 +961,22 @@ - - - - - + + + + + - + com.github.nicholasastuart myo-java 0.9.1 provided - + - + org.saintandreas jovr @@ -1040,9 +1021,9 @@ - + - + org.bytedeco javacv-platform @@ -1061,8 +1042,8 @@ 0.3.28-1.5.11 provided - - + + net.sf.jipcam jipcam @@ -1121,9 +1102,9 @@ provided zip - + - + simpleopenni openni @@ -1155,31 +1136,31 @@ 1.3.1 provided - + - + org.json json 20230227 provided - + - + com.illposed.osc javaosc-core 0.4 provided - + - - - - - + + + + + com.amazonaws aws-java-sdk-polly @@ -1208,11 +1189,11 @@ - - + + - - + + program-ab program-ab-kw @@ -1235,8 +1216,8 @@ 2.14.0 provided - - + + org.apache.lucene lucene-analysis-common @@ -1249,35 +1230,35 @@ 9.10.0 provided - + - + net.sf.py4j py4j 0.10.9.7 provided - + - + org.python jython-standalone 2.7.2 - + - + rome rome 1.0 provided - + - + com.pi4j pi4j-core @@ -1291,18 +1272,18 @@ provided pom - + - + com.amazonaws aws-java-sdk-rekognition 1.11.263 provided - + - + ch.qos.logback logback-classic @@ -1363,19 +1344,19 @@ okhttp 3.9.0 - - + + - + io.github.java-native jssc 2.9.4 provided - + - + com.slack.api bolt @@ -1400,9 +1381,9 @@ 1.19 provided - + - + org.apache.lucene lucene-core @@ -1505,16 +1486,16 @@ - + org.glassfish.jersey.core jersey-server 3.1.5 provided - + - + edu.cmu.sphinx sphinx4-core @@ -1527,9 +1508,9 @@ 5prealpha-SNAPSHOT provided - + - + org.bytedeco tesseract @@ -1549,37 +1530,37 @@ provided zip - - + + - + junit junit 4.13.1 provided - + - + topcodes topcodes 1.0.0 provided - + - + org.twitter4j twitter4j-core 3.0.5 provided - + - + io.vertx vertx-core @@ -1604,20 +1585,20 @@ - - + + - - + + com.voicerss tts 1.0 provided - + - + org.jmdns jmdns @@ -1652,35 +1633,35 @@ netty-all 4.1.82.Final - + - + com.github.sarxos webcam-capture-driver-v4l4j 0.3.13-SNAPSHOT provided - + - - - + + + - - - + + + - + wiiusej wiiusej wiiusej provided - + - + org.wikidata.wdtk wdtk-client @@ -1697,21 +1678,21 @@ - - - - + + + + - + WolframAlpha WolframAlpha 1.1 provided - + - + org.igniterealtime.smack smack-java7 @@ -1736,7 +1717,7 @@ 4.1.6 provided - + @@ -1785,7 +1766,7 @@ - + @@ -1801,7 +1782,7 @@ - + org.apache.maven.plugins @@ -1832,11 +1813,11 @@ myrobotlab - + true myrobotlab-full false - + ${git.tags} ${git.branch} ${git.dirty} @@ -1865,7 +1846,7 @@ ${git.commit.id.describe-short} ${git.commit.user.name} ${git.commit.user.email} - @@ -1956,7 +1937,7 @@ false true ${project.build.outputDirectory}/git.properties - + false false @@ -1968,12 +1949,12 @@ maven-surefire-plugin org.apache.maven.plugins - + 3.2.2 1 true - + ${argLine} -Djava.library.path=libraries/native -Djna.library.path=libraries/native @@ -1993,12 +1974,12 @@ test - + - + org.apache.maven.plugins maven-clean-plugin From e8e2dfe22d2f6dcb06eb23c43683390f74a29b39 Mon Sep 17 00:00:00 2001 From: kwatters Date: Fri, 20 Jun 2025 17:31:15 -0400 Subject: [PATCH 14/15] sync some code changes.. a little more clean up. --- .../org/myrobotlab/sensor/EncoderData.java | 40 ++++++++++++++----- .../org/myrobotlab/service/Amt203Encoder.java | 3 +- .../java/org/myrobotlab/service/Arduino.java | 14 ++++--- .../myrobotlab/service/As5048AEncoder.java | 32 ++++++++++++++- .../service/abstracts/AbstractMotor.java | 28 ++++--------- .../service/config/As5048AEncoderConfig.java | 3 +- .../service/config/DiyServo2Config.java | 1 + 7 files changed, 83 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/myrobotlab/sensor/EncoderData.java b/src/main/java/org/myrobotlab/sensor/EncoderData.java index c45ec857b4..97dc3a0e39 100644 --- a/src/main/java/org/myrobotlab/sensor/EncoderData.java +++ b/src/main/java/org/myrobotlab/sensor/EncoderData.java @@ -1,5 +1,7 @@ package org.myrobotlab.sensor; +import java.util.Objects; + public class EncoderData { /** @@ -30,6 +32,16 @@ public class EncoderData { */ public double value; + /** + * time data was generated + */ + public long timestamp; + + /** + * mapped value of input + */ + public double mappedValue; + public String getPin() { return pin; } @@ -78,15 +90,6 @@ public void setTimestamp(long timestamp) { this.timestamp = timestamp; } - /** - * time data was generated - */ - public long timestamp; - - /** - * mapped value of input - */ - public double mappedValue; public EncoderData(String source, String pin, double value, Double angle) { this.timestamp = System.currentTimeMillis(); @@ -109,4 +112,23 @@ public String toString() { return sb.toString(); } + @Override + public int hashCode() { + return Objects.hash(angle, mappedValue, pin, source, timestamp, type, value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EncoderData other = (EncoderData) obj; + return Objects.equals(angle, other.angle) && Double.doubleToLongBits(mappedValue) == Double.doubleToLongBits(other.mappedValue) && Objects.equals(pin, other.pin) + && Objects.equals(source, other.source) && timestamp == other.timestamp && Objects.equals(type, other.type) + && Double.doubleToLongBits(value) == Double.doubleToLongBits(other.value); + } + } diff --git a/src/main/java/org/myrobotlab/service/Amt203Encoder.java b/src/main/java/org/myrobotlab/service/Amt203Encoder.java index 8549ef9051..ce2b80219e 100755 --- a/src/main/java/org/myrobotlab/service/Amt203Encoder.java +++ b/src/main/java/org/myrobotlab/service/Amt203Encoder.java @@ -4,6 +4,7 @@ import org.myrobotlab.sensor.EncoderData; import org.myrobotlab.sensor.EncoderPublisher; import org.myrobotlab.service.abstracts.AbstractPinEncoder; +import org.myrobotlab.service.config.Amt203EncoderConfig; import org.myrobotlab.service.config.ServiceConfig; import org.myrobotlab.service.interfaces.EncoderControl; @@ -25,7 +26,7 @@ * @author kwatters * */ -public class Amt203Encoder extends AbstractPinEncoder implements EncoderControl, EncoderPublisher { +public class Amt203Encoder extends AbstractPinEncoder implements EncoderControl, EncoderPublisher { private static final long serialVersionUID = 1L; diff --git a/src/main/java/org/myrobotlab/service/Arduino.java b/src/main/java/org/myrobotlab/service/Arduino.java index 796ceb1ccc..f45695c452 100644 --- a/src/main/java/org/myrobotlab/service/Arduino.java +++ b/src/main/java/org/myrobotlab/service/Arduino.java @@ -504,19 +504,23 @@ public void reattach(DeviceMapping dm) { msg.servoAttachPin(dm.getId(), pin); } } else if (attachable instanceof As5048AEncoder) { - // TODO: reattach it! + // reattach it! As5048AEncoder enc = (As5048AEncoder)attachable; log.info("================ re-attaching {} {} {} ================", enc.getName(), dm.getId(), enc.getPin()); msg.encoderAttach(dm.getId(), 1, Integer.valueOf(enc.getPin())); + } else if (attachable instanceof Amt203Encoder) { + // reattach it! + Amt203Encoder enc = (Amt203Encoder)attachable; + log.info("================ re-attaching {} {} {} ================", enc.getName(), dm.getId(), enc.getPin()); + msg.encoderAttach(dm.getId(), 0, Integer.valueOf(enc.getPin())); } else if (attachable instanceof MotorDualPwm) { - // TODO: reattach it! + // reattach it! MotorDualPwm motor = (MotorDualPwm)attachable; int[] pins = new int[] {Integer.valueOf(motor.getLeftPwmPin()), Integer.valueOf(motor.getRightPwmPin())}; // TODO: what's the type?! int type = 0; log.info("================ re-attaching {} {} {} ================", motor.getName(), dm.getId(), pins); msg.motorAttach(dm.getId(),type, pins ); - } else if (attachable instanceof UltrasonicSensorControl) { log.warn("UltrasonicSensorControl not implemented"); // reattach logic @@ -525,7 +529,6 @@ public void reattach(DeviceMapping dm) { } else if (attachable instanceof PinListener) { PinListener pl = (PinListener) attachable; attachPinListener(pl); - // on reattach get back to its previous state enabled/disabled if (attachable instanceof Pir) { Pir pir = (Pir) attachable; @@ -533,7 +536,6 @@ public void reattach(DeviceMapping dm) { pir.enable(); } } - } else if (attachable instanceof I2CControl) { error("I2CControl sync not implemented"); } else { @@ -1705,6 +1707,8 @@ public EncoderData publishEncoderData(Integer deviceId, Integer position) { if (ec instanceof Amt203Encoder) { // type = 0; pin = ((Amt203Encoder) ec).getPin(); + // TODO: test this is this correct? is the resolution correct? + angle = 360.0 * position / ((Amt203Encoder) ec).resolution; } else if (ec instanceof As5048AEncoder) { // type = 1; pin = ((As5048AEncoder) ec).getPin(); diff --git a/src/main/java/org/myrobotlab/service/As5048AEncoder.java b/src/main/java/org/myrobotlab/service/As5048AEncoder.java index b39512133f..d26f0c63b8 100755 --- a/src/main/java/org/myrobotlab/service/As5048AEncoder.java +++ b/src/main/java/org/myrobotlab/service/As5048AEncoder.java @@ -1,13 +1,19 @@ package org.myrobotlab.service; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.concurrent.LinkedBlockingQueue; import org.apache.commons.math3.util.Precision; import org.myrobotlab.logging.LoggingFactory; +import org.myrobotlab.programab.BotInfo; import org.myrobotlab.sensor.EncoderData; import org.myrobotlab.sensor.EncoderListener; import org.myrobotlab.sensor.EncoderPublisher; import org.myrobotlab.service.abstracts.AbstractPinEncoder; +import org.myrobotlab.service.config.As5048AEncoderConfig; +import org.myrobotlab.service.config.ProgramABConfig; import org.myrobotlab.service.config.ServiceConfig; import org.myrobotlab.service.interfaces.EncoderControl; @@ -17,7 +23,7 @@ * @author kwatters * */ -public class As5048AEncoder extends AbstractPinEncoder implements EncoderControl, EncoderPublisher { +public class As5048AEncoder extends AbstractPinEncoder implements EncoderControl, EncoderPublisher { private static final int HISTORY_SIZE = 5; @@ -45,7 +51,8 @@ public static void main(String[] args) throws Exception { ard.connect(port); ard.setDebug(true); As5048AEncoder encoder = (As5048AEncoder) Runtime.start("encoder", "As5048AEncoder"); - encoder.setPin(10); + As5048AEncoderConfig cfg = (As5048AEncoderConfig)(encoder.config); + encoder.setPin(cfg.pin); ard.attachEncoderControl(encoder); Thread.sleep(10000); encoder.setZeroPoint(); @@ -76,5 +83,26 @@ public void updateEncoderData(EncoderData data) { invoke("publishEncoderData", filteredData); } + + @Override + public As5048AEncoderConfig getConfig() { + return (As5048AEncoderConfig)super.getConfig(); + } + + @Override + public As5048AEncoderConfig apply(As5048AEncoderConfig c) { + // TODO?? : controller ? attach? + this.setPin(c.pin); + return c; + } + + public void apply() { + // TODO?? + As5048AEncoderConfig config = getConfig(); + this.setPin(config.pin); + } + } + + diff --git a/src/main/java/org/myrobotlab/service/abstracts/AbstractMotor.java b/src/main/java/org/myrobotlab/service/abstracts/AbstractMotor.java index f5ae969a6a..b9f5577b9f 100644 --- a/src/main/java/org/myrobotlab/service/abstracts/AbstractMotor.java +++ b/src/main/java/org/myrobotlab/service/abstracts/AbstractMotor.java @@ -92,6 +92,8 @@ abstract public class AbstractMotor extends Servic * isAttached is if that controller is or is not attached */ protected boolean isAttached = false; + + protected EncoderData encData = null; public AbstractMotor(String n, String id) { super(n, id); @@ -289,27 +291,13 @@ public double getTargetPos() { @Override public void onEncoderData(EncoderData data) { - // TODO Auto-generated method stub - log.info("Encoder Data (to motor): {}", data); - // TODO: this should probably not be here.. but rather in a DiyServo service instead. - if (false) { - double target = 180.0; - double delta = data.angle - target; + // log.info("Encoder Data (to motor): {}", data); + // What do we want to do with encoder data here? + // grab a handle to the last encoder data returned + // TODO: does a motor need to be an encoder listener? + // Nothing uses this! + this.encData = data; - if (Math.abs(delta) > 0.5) { - // move the motor a bit. - // TODO: this hsould be controlled by a PID algorithm. - if (delta > 0) { - this.move(0.5); - } else { - this.move(0.5); - } - - } else { - this.move(0); - } - } - } @Override diff --git a/src/main/java/org/myrobotlab/service/config/As5048AEncoderConfig.java b/src/main/java/org/myrobotlab/service/config/As5048AEncoderConfig.java index 4f1c8cf2bd..646b846ea1 100644 --- a/src/main/java/org/myrobotlab/service/config/As5048AEncoderConfig.java +++ b/src/main/java/org/myrobotlab/service/config/As5048AEncoderConfig.java @@ -1,5 +1,6 @@ package org.myrobotlab.service.config; public class As5048AEncoderConfig extends ServiceConfig { - // Add your configuration here + // the only config i guess is what pin it attaches to it's controller on. + public Integer pin = 10; } diff --git a/src/main/java/org/myrobotlab/service/config/DiyServo2Config.java b/src/main/java/org/myrobotlab/service/config/DiyServo2Config.java index 15def3a605..cccd711201 100755 --- a/src/main/java/org/myrobotlab/service/config/DiyServo2Config.java +++ b/src/main/java/org/myrobotlab/service/config/DiyServo2Config.java @@ -26,6 +26,7 @@ public Plan getDefault(Plan plan, String name) { // TODO: how do we handle the controller for the encoder? (could be different than the motor) As5048AEncoderConfig encoder = (As5048AEncoderConfig) plan.get(getPeerName("encoder")); + encoder.pin = 10; PidConfig pid = (PidConfig) plan.get(getPeerName("pid")); pid.data.put(name, new PidData()); From c13d16c14b6fa2d1fa57192b88fb55cd0c4feb2e Mon Sep 17 00:00:00 2001 From: kwatters Date: Sun, 29 Jun 2025 20:59:38 -0400 Subject: [PATCH 15/15] sync some local changes --- .../org/myrobotlab/service/Amt203Encoder.java | 19 ++++++ .../myrobotlab/service/As5048AEncoder.java | 61 ++++++++++--------- .../org/myrobotlab/service/DiyServo2.java | 30 +++++---- .../service/config/Amt203EncoderConfig.java | 7 ++- .../service/config/As5048AEncoderConfig.java | 6 ++ 5 files changed, 76 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/myrobotlab/service/Amt203Encoder.java b/src/main/java/org/myrobotlab/service/Amt203Encoder.java index ce2b80219e..182a3a6f9d 100755 --- a/src/main/java/org/myrobotlab/service/Amt203Encoder.java +++ b/src/main/java/org/myrobotlab/service/Amt203Encoder.java @@ -59,4 +59,23 @@ public void updateEncoderData(EncoderData data) { invoke("publishEncoderData", data); } + @Override + public Amt203EncoderConfig apply(Amt203EncoderConfig c) { + super.apply(c); + this.setPin(c.pin); + // TODO: how we apply the config of the controller? + // String controllerName = c.controller; + // this.controller= null; + // TODO: should we have a handle to our controller? + //this.controller = c.controller; + if (c.controller != null) { + try { + attach(c.controller); + } catch (Exception e) { + error(e); + } + } + return c; + } + } diff --git a/src/main/java/org/myrobotlab/service/As5048AEncoder.java b/src/main/java/org/myrobotlab/service/As5048AEncoder.java index d26f0c63b8..45ab45c49d 100755 --- a/src/main/java/org/myrobotlab/service/As5048AEncoder.java +++ b/src/main/java/org/myrobotlab/service/As5048AEncoder.java @@ -42,23 +42,6 @@ public void setZeroPoint() { log.warn("Setting the Zero point not supported on AS5048A because memory register is OTP"); } - public static void main(String[] args) throws Exception { - - LoggingFactory.init("INFO"); - String port = "COM4"; - Runtime.start("gui", "SwingGui"); - Arduino ard = (Arduino) Runtime.start("ard", "Arduino"); - ard.connect(port); - ard.setDebug(true); - As5048AEncoder encoder = (As5048AEncoder) Runtime.start("encoder", "As5048AEncoder"); - As5048AEncoderConfig cfg = (As5048AEncoderConfig)(encoder.config); - encoder.setPin(cfg.pin); - ard.attachEncoderControl(encoder); - Thread.sleep(10000); - encoder.setZeroPoint(); - log.info("Here we are.."); - } - public void updateEncoderData(EncoderData data) { // publish the updated encoder data (this is updated from the arduino..) // log.info("Encoder data: {}", data); @@ -70,20 +53,17 @@ public void updateEncoderData(EncoderData data) { // This is computing a moving average for the encoder value to smooth it out a bit. double avgVal = history.stream().mapToDouble(EncoderData::getValue).average().orElse(0.0); double avgAngle = Precision.round(history.stream().mapToDouble(EncoderData::getAngle).average().orElse(0.0), 1); - - // Precision.round(avgAngle, 2); - // double avgVal = (previousData.value + data.value)/2; - // double avgAngle = (previousData.angle + data.angle)/2; + // Precision.round(avgAngle, 2); + // double avgVal = (previousData.value + data.value)/2; + // double avgAngle = (previousData.angle + data.angle)/2; // 0.1 degree resolution... truncate and filter the value for stability.. EncoderData filteredData = new EncoderData( data.source, data.pin, avgVal, avgAngle); - // log.info("Original Angle: {} Filtered Angle: {}", data.angle, filteredData.angle); - + // log.info("Original Angle: {} Filtered Angle: {}", data.angle, filteredData.angle); // previousData = data; //invoke("publishEncoderData", data); - invoke("publishEncoderData", filteredData); } - + @Override public As5048AEncoderConfig getConfig() { return (As5048AEncoderConfig)super.getConfig(); @@ -93,15 +73,36 @@ public As5048AEncoderConfig getConfig() { public As5048AEncoderConfig apply(As5048AEncoderConfig c) { // TODO?? : controller ? attach? this.setPin(c.pin); + // TODO: how we apply the config of the controller? + // String controllerName = c.controller; + // this.controller= null; + // TODO: should we have a handle to our controller? + //this.controller = c.controller; + if (c.controller != null) { + try { + attach(c.controller); + } catch (Exception e) { + error(e); + } + } return c; } - public void apply() { - // TODO?? - As5048AEncoderConfig config = getConfig(); - this.setPin(config.pin); + public static void main(String[] args) throws Exception { + LoggingFactory.init("INFO"); + String port = "COM4"; + Runtime.start("gui", "SwingGui"); + Arduino ard = (Arduino) Runtime.start("ard", "Arduino"); + ard.connect(port); + ard.setDebug(true); + As5048AEncoder encoder = (As5048AEncoder) Runtime.start("encoder", "As5048AEncoder"); + As5048AEncoderConfig cfg = (As5048AEncoderConfig)(encoder.config); + encoder.setPin(cfg.pin); + ard.attachEncoderControl(encoder); + Thread.sleep(10000); + encoder.setZeroPoint(); + log.info("Here we are.."); } - } diff --git a/src/main/java/org/myrobotlab/service/DiyServo2.java b/src/main/java/org/myrobotlab/service/DiyServo2.java index c6d22ab5a2..fb65534e0f 100755 --- a/src/main/java/org/myrobotlab/service/DiyServo2.java +++ b/src/main/java/org/myrobotlab/service/DiyServo2.java @@ -105,6 +105,7 @@ void initPid() { public void onEncoderData(EncoderData data) { // System.err.println("DIY Servo Encoder Data: " + data); this.currentAngle = data.angle; + // TODO: could we just update the PID here? } public void attachEncoderControl(EncoderControl enc) { @@ -213,6 +214,7 @@ private boolean isRunning() { public void disable() { // TODO: what do do here? // motorControl.disable(); + // TODO: we should disable the encoder also here.. motorControl.stop(); enabled = false; // TODO: broadcast enabled/disabled messages? @@ -222,7 +224,7 @@ public void disable() { public void enable() { // TODO: what do to here? // motorControl.enable(); - enabled = true; + enabled = true; } @Override @@ -241,7 +243,7 @@ public boolean isAutoDisable() { public void attach(ServoController listener) { // TODO: remove from ServoControl interface... NoOp here. // NoOp : no servo controllers here.. - log.warn("Diy Servo doesn't use a controller.. no implemented."); + log.warn("Diy Servo 2 doesn't use a controller.. no implemented."); } @Override @@ -253,7 +255,7 @@ public void detach(ServoController listener) { @Override public String getController() { // TODO remove from interface?. we have no controller. - log.warn("Diy Servo doesn't use a controller.. no implemented."); + log.warn("Diy Servo 2 doesn't use a controller.. no implemented."); return null; } @@ -394,7 +396,7 @@ public void setMinMaxOutput(double minY, double maxY) { @Override public void setPin(Integer pin) { - // TODO: There are no pins! we have no pins! + // TODO: There are no pins! we have no pins! perhaps this could be the pin that the encoder is connected to? log.warn("setPin not implemented in DiyServo."); } @@ -425,7 +427,7 @@ public void setSpeed(Double degreesPerSecond) { @Override public void stop() { - // Stop the motor... anything else? + // Stop the motor. motorControl.move(0.0); } @@ -441,10 +443,7 @@ public void unsync(ServoControl sc) { @Override public void waitTargetPos() { - // TODO Auto-generated method stub - // really? ok. - // here we should wait until we have "arrived" ... - + // TODO: here we should wait until we have "arrived" ... } @Override @@ -453,12 +452,11 @@ public void writeMicroseconds(int uS) { log.warn("Write Microseconds not implemented for DiyServo."); } - - @Override public void fullSpeed() { // TODO: add a velocity control. // TODO: deprecated, remove from interface? + // This would disable any velocity control for the servo. } @Override @@ -473,13 +471,13 @@ public ServoControl publishServoStop(ServoControl sc) { @Override public Double moveToBlocking(Integer newPos) { - // TODO Auto-generated method stub + // TODO this should get implemented for certain. return null; } @Override public Double moveToBlocking(Integer newPos, Long timeoutMs) { - // TODO Auto-generated method stub + // TODO this should get implemented for certain. return null; } @@ -509,7 +507,7 @@ public String publishServoEnable(String name) { @Override public void attachServoControlListener(String name) { - // TODO Auto-generated method stub + // TODO: this should get implemented like the normal Servo } @@ -545,8 +543,8 @@ public void unsync(String name) { @Override public void attachServoController(String sc) { - // TODO Auto-generated method stub - + // NoOp for DiyServo2, the Motor Control and the Encoder will have their own controllers... + log.info("DiyServo2 doesn't use attachServoController"); } @Override diff --git a/src/main/java/org/myrobotlab/service/config/Amt203EncoderConfig.java b/src/main/java/org/myrobotlab/service/config/Amt203EncoderConfig.java index 250b198e03..7bae7db652 100644 --- a/src/main/java/org/myrobotlab/service/config/Amt203EncoderConfig.java +++ b/src/main/java/org/myrobotlab/service/config/Amt203EncoderConfig.java @@ -1,5 +1,10 @@ package org.myrobotlab.service.config; +/** + * configuration for an AMT203 absolute position rotary encoder. + * This is a capacitive encoder. + */ public class Amt203EncoderConfig extends ServiceConfig { - // Add your configuration here + public Integer pin = 10; + public String controller = null; } diff --git a/src/main/java/org/myrobotlab/service/config/As5048AEncoderConfig.java b/src/main/java/org/myrobotlab/service/config/As5048AEncoderConfig.java index 646b846ea1..730a52cb49 100644 --- a/src/main/java/org/myrobotlab/service/config/As5048AEncoderConfig.java +++ b/src/main/java/org/myrobotlab/service/config/As5048AEncoderConfig.java @@ -1,6 +1,12 @@ package org.myrobotlab.service.config; +/** + * config for an as5048a spi based absolute position encoder + * This is a magenetic encoder. + */ public class As5048AEncoderConfig extends ServiceConfig { // the only config i guess is what pin it attaches to it's controller on. public Integer pin = 10; + public String controller = null; + }