All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.silverspoon.bulldog.devices.servo.Servo Maven / Gradle / Ivy

There is a newer version: 0.3.0
Show newest version
package io.silverspoon.bulldog.devices.servo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import io.silverspoon.bulldog.core.gpio.Pwm;
import io.silverspoon.bulldog.core.util.DaemonThreadFactory;
import io.silverspoon.bulldog.core.util.easing.SineEasing;
import io.silverspoon.bulldog.devices.actuators.Actuator;
import io.silverspoon.bulldog.devices.actuators.movement.DirectMove;
import io.silverspoon.bulldog.devices.actuators.movement.EasedMove;
import io.silverspoon.bulldog.devices.actuators.movement.LinearMove;
import io.silverspoon.bulldog.devices.actuators.movement.Move;

public class Servo implements Actuator {

   private static final float MIN_ANGLE_DEFAULT = 0.05f;
   private static final float MAX_ANGLE_DEFAULT = 0.10f;
   private static final float FREQUENCY_HZ = 50.0f;
   private static final float INITIAL_POSITION_DEFAULT = 0.0f;
   private static final int TIME_PER_DEGREE_DEFAULT = (int) (0.1f / 60.0f * 1000);

   private Pwm pwm;
   private double angle;
   private double minAngleDuty;
   private double maxAngleDuty;
   private int degreeMilliseconds;

   private ExecutorService executor = Executors.newSingleThreadExecutor(new DaemonThreadFactory());
   private Future currentMove = null;

   private List listeners = Collections.synchronizedList(new ArrayList());

   public Servo(Pwm pwm) {
      this(pwm, INITIAL_POSITION_DEFAULT);
   }

   public Servo(Pwm pwm, float initialAngle) {
      this(pwm, initialAngle, MIN_ANGLE_DEFAULT, MAX_ANGLE_DEFAULT, TIME_PER_DEGREE_DEFAULT);
   }

   public Servo(Pwm pwm, double initialAngle, double minAngleDuty, double maxAngleDuty, int degreeMilliseconds) {
      this.pwm = pwm;
      this.minAngleDuty = minAngleDuty;
      this.maxAngleDuty = maxAngleDuty;
      this.degreeMilliseconds = degreeMilliseconds;
      this.pwm.setFrequency(FREQUENCY_HZ);
      this.pwm.setDuty(getDutyForAngle(initialAngle));
      if (!this.pwm.isEnabled()) {
         this.pwm.enable();
      }
   }

   public void setAngle(double degrees) {
      double oldAngle = angle;
      angle = degrees;
      if (angle < 0.0f) {
         angle = 0.0f;
      }

      if (angle > 180.0f) {
         angle = 180.0f;
      }

      pwm.setDuty(getDutyForAngle(angle));
      fireAngleChanged(oldAngle, angle);
   }

   public void moveTo(double angle) {
      move(new DirectMove(angle));
   }

   public void moveTo(double angle, int milliseconds) {
      move(new LinearMove(angle, milliseconds));
   }

   public void moveSmoothTo(double angle) {
      move(new EasedMove(new SineEasing(), angle));
   }

   public void moveSmoothTo(double angle, int milliseconds) {
      move(new EasedMove(new SineEasing(), angle, milliseconds));
   }

   public void move(Move move) {
      double startAngle = getAngle();
      move.execute(this);
      fireMoveCompleted(startAngle, getAngle());
   }

   public void moveAsync(final Move move) {
      currentMove = executor.submit(new Runnable() {

         @Override
         public void run() {
            move(move);
         }

      });
   }

   public void moveAsyncTo(double angle) {
      moveAsync(new DirectMove(angle));
   }

   public void moveAsyncTo(double angle, int milliseconds) {
      moveAsync(new LinearMove(angle, milliseconds));
   }

   public void moveSmoothAsyncTo(double angle) {
      moveAsync(new EasedMove(new SineEasing(), angle));
   }

   public void moveSmoothAsyncTo(double angle, int milliseconds) {
      moveAsync(new EasedMove(new SineEasing(), angle, milliseconds));
   }

   public void awaitMoveCompleted() {
      if (currentMove != null) {
         try {
            currentMove.get();
         } catch (Exception e) {
            throw new RuntimeException(e);
         }
      }
   }

   public double getAngle() {
      return angle;
   }

   protected double getDutyForAngle(double angle) {
      double maxAngle = 180.0;
      double anglePercent = angle / maxAngle;
      double dutyLength = (maxAngleDuty - minAngleDuty) * anglePercent;
      return minAngleDuty + dutyLength;
   }

   public Pwm getPwm() {
      return this.pwm;
   }

   public void addServoListener(ServoListener listener) {
      listeners.add(listener);
   }

   public void removeServoListener(ServoListener listener) {
      listeners.remove(listener);
   }

   public void clearServoListeners() {
      listeners.clear();
   }

   protected void fireAngleChanged(double oldAngle, double newAngle) {
      synchronized (listeners) {
         for (ServoListener listener : listeners) {
            listener.angleChanged(this, oldAngle, newAngle);
         }
      }
   }

   protected void fireMoveCompleted(double oldAngle, double newAngle) {
      synchronized (listeners) {
         for (ServoListener listener : listeners) {
            listener.moveCompleted(this, oldAngle, newAngle);
         }
      }
   }

   public int getMillisecondsPerDegree() {
      return degreeMilliseconds;
   }

   @Override
   public double getPosition() {
      return getAngle();
   }

   public void setPosition(double position) {
      this.setAngle(position);
   }

   @Override
   public int getMillisecondsPerUnit() {
      return this.getMillisecondsPerDegree();
   }

   @Override
   public int getRefreshIntervalMilliseconds() {
      return (int) (1000 / this.getPwm().getFrequency());
   }

   @Override
   public boolean isMoving() {
      if (currentMove != null) {
         return !currentMove.isDone();
      }

      return false;
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy