Skip to content

Arduino library for controlling Power Functions motors

License

Notifications You must be signed in to change notification settings

pink0D/TechnicPF

Repository files navigation

About

TechnicPF is an Arduino library for controlling Power Functions motors.

Author: Dmitry Akulov [email protected]

Important notice

The example included in this library is for demonstration purposes only. The example hardware and the library itself DO NOT INCLUDE any kind of battery discharge protection. Since electric motors consume quite a lot of power, this can lead to permament damage to Li-ion/LiPo batteries if there's no such protection. For a real application please consider adding discharge control or use batteries with integrated protection curcuit.

Table of contents

Supported motors

The library supports several types of motors:

  • Original PF Motors (including proportional Servos)
  • PF Simple motors (M/L/XL-motors)
  • PF Servo Motors (which usually have only 3 positions: -90 / 0 / +90)
  • Mould King Servo motors (which normally also have just 3 positions, however this library introduces support for fully proportional steering)
  • GeekServo (Technic-compatible motors with 3-wire plug)

Library istallation

Copy contents of this repo to <Worspace folder>/libraries/TechnicPF

Basic Power Functions motors

For basic use of Power Functions motors you will need:

  • Arduino Board
  • TB6612FNG motor driver
  • 7.4V LiPo battery
  • Any PF compatible motors

More details on Power Functions and PF-plug pinout are available on Philo's site:

Basic usage!

Add the following code to your sketch:

// library includes
#include <PFMotor.h>

// default pins
#include <PinConfig.h>

// if your installation is using different pinout, copy the default config to your project and redefine pins
// #include "PinConfig.h"

// define motor instance
PFMotor motor;

void setup() {
...
motor.begin(MOTOR_A);

// when using original PF motors, this option is recommended both for Servo and M/L/XL motors
// motor.begin(MOTOR_A, PWM_FIXED_15_POSITIONS); // PF with 15-positions (almost proportional)

// when using PF-compatible servo-motor, this option is recommended to avoid fluttering and power drain issues
// motor.begin(MOTOR_A, PWM_FIXED_3_POSITIONS);  // PF with 3-positions  (-90 / 0 / +90)

...
}

void loop() {
...
// when needed, set motor rotation speed:
// any value in -1.0 .. +1.0 range, 1.0 = full speed, 0.5 = half speed, zero value (0.0) stops the motor
// positive/negative values set rotation direction.
// for a servo: 1.0 = 90 degree rotation, 0.5 = 45 degree rotation, 0 = zero position, -1.0 = -90 degree rotation
double speed_control = 1.0; 
bool brake = false; // set to true to brake the motor (the driver shorts motor brushes which results to a fast stop)
motor.update_motor(speed_control,brake); 
...
}

GeekServo

GeekServo is a standard 3-wire Servo with Technic bricks compatible housing. By specification, GeekServo has 3.3V to 6V operating voltage, so you will need a voltage regulator. Unofficially, GeekServo can be powered directly with 2S (7.4V) LiPo battery (but use this setup at your own risk).

GeekServo!

Code:

// library includes
#include <GeekServo.h>

// default pins
#include <PinConfig.h>

// if your installation is using different pinout, copy the default config to your project and redefine pins
// #include "PinConfig.h"

// define motor instance
GeekServo servo;

void setup() {
...
// basic setup for a servo with 1000..2000 microseconds signals for -90..+90 rotation
servo.begin(GEEK_SERVO);

// GeekServo 360 supports wider range: 500..2500 microseconds for -180..+180 rotation
// servo.begin(GEEK_SERVO,500,2500);
...
}

void loop() {
...
// any value in -1.0 .. +1.0 range
// positive/negative values set rotation direction.
// for a servo: 1.0 = maximum rotation, 0.5 = half rotation, 0 = zero position, -1.0 = maximum rotation in other direction
double rotation = 1.0; 
servo.update_motor(rotation); 
...
}

Mould King Servo proportional control

Mould King Servos by default have only 3 fixed positions (-90, 0 and +90 rotation) when connected to control hubs. However, by sending a correct PWM signal, it is possible to make it rotate in a proportional way. There are also some side effects, including motor fluttering and hence significant power drain. This library provides a solution for proportional control of Mould King Servo Motor:

  • Sending correct PWM signals to MK Servo
  • Disconnecting power supply from the motor when it does not need to rotate, and connecting the power again before moving to a new position

To achive this, the hardware setup contatins N-channel MOSFETs, along with some resistors (100 Ohm and 10 kOhm). You can choose any N-channel MOSFET that can be driven by 3.3V logic. To reduce soldering time, you can use power control circuit just for one motor, and connect GND pin of the second motor directly to PCB's GND.

More details about this solution in my video on Youtube

MKServo!

Code:

// library includes
#include <MKServo.h>

// default pins
#include <PinConfig.h>

// if your installation is using different pinout, copy the default config to your project and redefine pins
// #include "PinConfig.h"

// define motor instance - MKServo class contain special logic for proportional control
MKServo servo;

void setup() {
...
// MKServo initialization
servo.begin(MOTOR_A);
...
}

void loop() {
...
// any value in -1.0 .. +1.0 range
// positive/negative values set rotation direction.
// for a servo: 1.0 = maximum rotation, 0.5 = half rotation, 0 = zero position, -1.0 = maximum rotation in other direction
double rotation = 1.0; 
servo.update_motor(rotation); 
...
}

Bluetooth gamepad controller example

Demo video on Youtube

The example project demostrates how to use the library and control Technic motors with a Bluetooth gamepad. Components required:

  • Arduino Pro Mini 3.3V
  • Mini USB Host Shield 3.3V
  • USB Bluetooth 4.0 dongle
  • Bluetooth gamepad
  • TB6612FNG motor driver
  • LM7805 voltage regulator (optional for GeekServo)
  • IRLB3034 or similar N-channel MOSFET + 100 Ohm + 10kOhm resistors (needed only for Mould King Servo proportional control)
  • 7.4V LiPo battery

To reduce soldering time, you can:

  • exclude LM7805 if you do not plan to use the GeekServo (or if you will use direct 7.4V power supply for it)
  • include only one power control circuit for PF motors or even connect PF motors directly to PCB's GND if you do not need proportional control fo Mould King Servos

More examples for other Bluetooth gamepads are available in USB_Host_Shield_2.0 library

Full version!

//
// Copyright (c) Dmitry Akulov. All rights reserved.
//
// Repository info:     https://github.com/pink0D/TechnicPF
// Contact information: [email protected]
//
// Licensed under the MIT license. See LICENSE file in the project root for details.
//

#include <PS4BT.h>

#include <PFMotor.h>
#include <GeekServo.h>
#include <MKServo.h>

//
// define pins
//
#include "PinConfig.h"

//
// PlayStation style controls
//
#define INPUT_STEER         LeftHatX
#define INPUT_ACCELERATOR   R2
#define INPUT_BRAKE         L2
#define INPUT_REVERSE       TRIANGLE

#define INPUT_DEADZONE      10
#define INPUT_MAX           255

#define STEERING_MAX_ANGLE  90        // used to limit max servo rotation angle

//
// Controller setup
//
// for details about connecting Bluetooth devices to Arduino, please refer to
// examples https://github.com/felis/USB_Host_Shield_2.0
//
USB Usb;
BTD Btd(&Usb); 
PS4BT controller(&Btd, PAIR); // always start in pairing mode
//PS4BT controller(&Btd);     // not working...

//
// Technic motors setup
//
PFMotor motor;      // Motor A - PF Large motor
GeekServo servo;    // GeekServo - 3-wire servo
//MKServo servo;    // Motor B - MK Servo
//PFMotor servo;    // Motor B - PF Servo

bool reverse = false;

double input_to_steering(int input) {

  double limit = (((double)STEERING_MAX_ANGLE) / 90.0);

  if (input < INPUT_DEADZONE)
    return -limit; // full left

  if (input > INPUT_MAX - INPUT_DEADZONE)
    return limit;  // full right

  if (abs(INPUT_MAX / 2 - input) < INPUT_DEADZONE)
    return 0; // center

  return (2.0 * ( (double) input ) / ( (double) INPUT_MAX) - 1) * limit; // calculate
}

void setup() {
  Serial.begin(115200);
  
  motor.begin(MOTOR_A);
  servo.begin(GEEK_SERVO);
  //servo.begin(MOTOR_B); // MK Servo with proportional control
  //servo.begin(MOTOR_B, PWM_FIXED_3_POSITIONS);  // PF with 3-positions  (-90 / 0 / +90)
  //servo.begin(MOTOR_B, PWM_FIXED_15_POSITIONS); // PF with 15-positions (almost proportional)

  // init Bluetooth
  if (Usb.Init() == -1) {
    Serial.println("Bluetooth did not start");
    while (1); // halt
  }
  Serial.println("Startup OK");
}

void loop() {

  // get updates from controller
  Usb.Task();

  if (controller.connected()) {

    // get inputs from controller
    double accelerator = ((double) controller.getAnalogButton(INPUT_ACCELERATOR)) / ((double) INPUT_MAX);
    double steer = input_to_steering(controller.getAnalogHat(INPUT_STEER));
    bool brake = controller.getAnalogButton(INPUT_BRAKE) > INPUT_DEADZONE;
    if (controller.getButtonClick(INPUT_REVERSE))
      reverse = !reverse;
    if (reverse) 
      accelerator = -accelerator;

    // update motor outputs
    motor.update_motor(accelerator,brake); 
    servo.update_motor(steer);
  }
}

About

Arduino library for controlling Power Functions motors

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published