RR76
Urgestein
- Registriert
- 8 Apr. 2019
- Beiträge
- 1.217
Bevor der PU-Thread zu unübersichtlich wird will ich in einem eigenen Thread auf die Entwicklung einer Fernsteuerung für PU eingehen.
Idee des ganzen ist, ein Gamepad so zu programmieren dass man mehrere PU-Hubs damit verbinden kann und die Belegung der Tasten möglichst frei programmieren kann, ähnlich BC2, nur eben ohne ein Smartphone als "Vermittler".
Meine bisherigen Versuche basieren auf einem esp32 und der legoino Library, die .
Die gewünschten Bilder zur Hardware zeige ich wenn ich etwas ansehnliches fertig habe, das derzeitige Gebrate ist nicht vorzeigbar.
Die Potis für die ersten Versuche stammen aus einem ausgeschlachteten Gamepad. Leider ist es nichts besonders hochwertiges, und ein PS4-Gamepad auszuschlachten bin ich aber zu geizig.
Vielleicht hat ja jemand eine Idee, welches Gamepad man verwenden kann. Wichtig ist eigentlich nur dass darin genug Platz für esp32 und einen Akku ist.
Die von @Ruppie vorgeschlagene Kombination aus https://www.dfrobot.com/product-858.html und https://www.crowdsupply.com/macchina/superb gefällt mir gut, ist aber auch nicht gerade geschenkt.
Aktueller Stand ist dass ich die Legoino-Library um eine eigene Klasse für den Technic-Hub ergänzt habe, die auch eine absolute Positionierung von L- und XL-Motoren zulässt.
Für erste Tests habe ich ein paar Zeilen geschrieben, um den 42099 mit dem Gamepad zu steuern.
Der Code ist von einigem Müll bereigt damit er halbwegs lesbar ist, seitdem aber nicht mehr getestet da ich angefangen habe, das Gamepad zu bauen und gerade keinen Prototyp zur Verfügung habe.
Und ja, sauberer Code sieht bestimmt anders aus, ich pfusche mich da halt so halbwegs mit meinen schlechten Programmierkenntnissen durch
Was mir nicht gefällt ist dass der L-Motor als Servo viel zittert, das scheint mit der C+-App besser zu sein.
Und für @gatewalker noch ein Testprogramm für den Trailer:
Port B fährt immer die Position von Port A an. Weniger als 1/10 Sekunde zwischen zwei Messungen führen leider zu wildem Hin- und Herfahren des Motors an Port B, diese Verzögerung ist also das Minimum.
Auch hier stört das Zittern.
Bevor ich jetzt zu große Hoffnungen wecke: Bis etwas wirklich nutzbares fertig ist wird es noch eine Zeitlang dauern und ich bin auch nicht in der Lage, so schnellen und guten Support zu leisten wie es z.B. der Entwickler von BC2 schafft. In erster Linie bastle ich hier an einem Gamepad für mich, werde aber wenn möglich auch Eure Ideen mit einbringen und Euch den Code zur Verfügung stellen. Mithilfe ist natürlich auch gerne gesehen.
Hier auch gleich die erste Frage: Wie würdet Ihr Euch die Programmierung vorstellen?
Für mich kann ich die verschiedenen Modelle am einfachsten fest im Code hinterlegen, aber eine freie Programmierbarkeit ohne den Quellcode zu ändern wäre schöner.
Eine Idee ist, eine SD-Karte mit Text-Dateien zu beschreiben, die Hubs und deren Ports mit Tasten am Gamepad verbinden.
Eine andere Idee wäre, über Tastenkombinationen Hubs anzulernen. Das kommt ohne PC aus, ist aber aufwändiger und wenn mehr als nur Vorwärts/Rückwärts programmiert werden soll auch ziemlich unkomfortabel.
Die meisten Gamepads haben vier LEDs, die würde ich als Statusanzeige nutzen und auch vier Modelle hinterlegen, die dann beim Einschalten ausgewählt werden können.
Und jetzt bin ich auf Meinungen und Ideen gespannt.
Gruß
Patrick
Idee des ganzen ist, ein Gamepad so zu programmieren dass man mehrere PU-Hubs damit verbinden kann und die Belegung der Tasten möglichst frei programmieren kann, ähnlich BC2, nur eben ohne ein Smartphone als "Vermittler".
Meine bisherigen Versuche basieren auf einem esp32 und der legoino Library, die .
Die gewünschten Bilder zur Hardware zeige ich wenn ich etwas ansehnliches fertig habe, das derzeitige Gebrate ist nicht vorzeigbar.
Die Potis für die ersten Versuche stammen aus einem ausgeschlachteten Gamepad. Leider ist es nichts besonders hochwertiges, und ein PS4-Gamepad auszuschlachten bin ich aber zu geizig.
Vielleicht hat ja jemand eine Idee, welches Gamepad man verwenden kann. Wichtig ist eigentlich nur dass darin genug Platz für esp32 und einen Akku ist.
Die von @Ruppie vorgeschlagene Kombination aus https://www.dfrobot.com/product-858.html und https://www.crowdsupply.com/macchina/superb gefällt mir gut, ist aber auch nicht gerade geschenkt.
Aktueller Stand ist dass ich die Legoino-Library um eine eigene Klasse für den Technic-Hub ergänzt habe, die auch eine absolute Positionierung von L- und XL-Motoren zulässt.
Code:
/*
* TechnicHub.h - Arduino Library for controlling Technic hubs
*
* (c) Copyright 2019 - Cornelius Munz
* Released under MIT License
*
*/
#ifndef TechnicHub_h
#define TechnicHub_h
#include "Arduino.h"
#include "BLEDevice.h"
#include "Lpf2Hub.h"
class TechnicHub : public Lpf2Hub
{
public:
//Port definitions specific to Technic Hubs
enum Port
{
A = 0x00,
B = 0x01,
AB = 0x10,
C = 0x02,
D = 0x03,
TILT = 0x3A,
CURRENT = 0x3B,
VOLTAGE = 0x3C
};
//Constructor
TechnicHub();
//Basic Hub methods
void requestSensorValue();
void setInputFormatSingle();
//Basic Motor methods
void setAccelerationProfile(Port port, int16_t time, int8_t profileNumber);
void setDecelerationProfile(Port port, int16_t time, int8_t profileNumber);
void stopMotor(Port port);
void setMotorSpeed(Port port, int speed);
void setMotorSpeedForTime(Port port, int speed, int16_t time);
void setMotorSpeedForDegrees(Port port, int speed, int32_t degrees);
void setMotorSpeedsForDegrees(int speedLeft, int speedRight, int32_t degrees);
void GotoAbsolutePosition(int32_t degrees_absolut, int speed, int power, int EndState, int UseProfile, Port port); // Output Command 0x81 - Sub Command 0x0D
void PresetEncoder(int32_t degrees); // Output Command 0x81 - Sub Command N/A
void PresetEncoder(int32_t degrees_left, int32_t degrees_right); // Output Command 0x81 - Sub Command 0x14
};
#endif
Code:
/*
* TechnicHub.cpp - Arduino Library for controlling Technic hubs
*
* (c) Copyright 2019 - Cornelius Munz
* Released under MIT License
*
*/
#include "TechnicHub.h"
TechnicHub::TechnicHub(){};
/**
* @brief Set the motor speed on a defined port.
* @param [in] port Port of the Hub on which the speed of the motor will set (A, B, AB)
* @param [in] speed Speed of the Motor -100..0..100 negative values will reverse the rotation
*/
void TechnicHub::setMotorSpeed(Port port, int speed)
{
byte setMotorCommand[8] = {0x81, port, 0x11, 0x01, MapSpeed(speed), 0x64, 0x7f, 0x03}; //boost
WriteValue(setMotorCommand, 8);
}
/**
* @brief Set the acceleration profile
* @param [in] port Port of the Hub on which the speed of the motor will set (A, B, AB)
* @param [in] time Time value in ms of the acceleration from 0-100% speed/Power
* @param [in] profileNumber Number for which the acceleration profile is stored
*/
void TechnicHub::setAccelerationProfile(Port port, int16_t time, int8_t profileNumber)
{
byte *timeBytes = Int16ToByteArray(time);
byte setMotorCommand[7] = {0x81, port, 0x10, 0x05, timeBytes[0], timeBytes[1], profileNumber};
WriteValue(setMotorCommand, 7);
}
/**
* @brief Set the deceleration profile
* @param [in] port Port of the Hub on which the speed of the motor will set (A, B, AB)
* @param [in] time Time value in ms of the deceleration from 100-0% speed/Power
* @param [in] profileNumber Number for which the deceleration profile is stored
*/
void TechnicHub::setDecelerationProfile(Port port, int16_t time, int8_t profileNumber)
{
byte *timeBytes = Int16ToByteArray(time);
byte setMotorCommand[7] = {0x81, port, 0x10, 0x06, timeBytes[0], timeBytes[1], profileNumber};
WriteValue(setMotorCommand, 7);
}
/**
* @brief Set the motor speed on a defined port.
* @param [in] port Port of the Hub on which the speed of the motor will set (A, B, AB)
* @param [in] speed Speed of the Motor -100..0..100 negative values will reverse the rotation
* @param [in] time Time in miliseconds for running the motor on the desired speed
*/
void TechnicHub::setMotorSpeedForTime(Port port, int speed, int16_t time = 0)
{
//max power 100 (0x64)
//End state Brake (127)
//Use acc and dec profile (0x03 last two bits set)
byte *timeBytes = Int16ToByteArray(time);
byte setMotorCommand[10] = {0x81, port, 0x11, 0x09, timeBytes[0], timeBytes[1], MapSpeed(speed), 0x64, 0x7F, 0x03}; //boost with time
WriteValue(setMotorCommand, 10);
}
/**
* @brief Set the motor speed on a defined port.
* @param [in] port Port of the Hub on which the speed of the motor will set (A, B, AB)
* @param [in] speed Speed of the Motor -100..0..100 negative values will reverse the rotation
* @param [in] time Time in miliseconds for running the motor on the desired speed
*/
void TechnicHub::setMotorSpeedForDegrees(Port port, int speed, int32_t degrees)
{
byte *degreeBytes = Int32ToByteArray(degrees);
//max power 100 (0x64)
//End state Brake (127)
//Use acc and dec profile (0x03 last two bits set)
byte setMotorCommand[12] = {0x81, port, 0x11, 0x0B, degreeBytes[0], degreeBytes[1], degreeBytes[2], degreeBytes[3], MapSpeed(speed), 0x64, 0x7F, 0x03}; //boost with time
WriteValue(setMotorCommand, 12);
}
void TechnicHub::setMotorSpeedsForDegrees(int speedLeft, int speedRight, int32_t degrees)
{
byte *degreeBytes = Int32ToByteArray(degrees);
Port port = AB;
//both ports A and B
//max power 100 (0x64)
//End state Brake (127)
//Use acc and dec profile (0x03 last two bits set)
byte setMotorCommand[13] = {0x81, port, 0x11, 0x0C, degreeBytes[0], degreeBytes[1], degreeBytes[2], degreeBytes[3], MapSpeed(speedLeft), MapSpeed(speedRight), 0x64, 0x7F, 0x03}; //boost with time
WriteValue(setMotorCommand, 13);
}
void TechnicHub::GotoAbsolutePosition(int32_t degrees_absolut, int speed, int power, int EndState, int UseProfile, Port port) // Output Command 0x81 - Sub Command 0x0D
{
byte *degreeBytes = Int32ToByteArray(degrees_absolut);
//both ports A and B
//max power 100 (0x64)
//End state Brake (127)
//Use acc and dec profile (0x03 last two bits set)
byte setMotorCommand[13] = {0x81, port, 0x11, 0x0D, degreeBytes[0], degreeBytes[1], degreeBytes[2], degreeBytes[3], speed, 0x64, 0x7F, 0x03}; //100%, 127 (Brake)
WriteValue(setMotorCommand, 12);
}
void TechnicHub::requestSensorValue()
{
byte requestPortValue[3] = {0x21, 0x01, 0x00};
WriteValue(requestPortValue, 3);
}
void TechnicHub::setInputFormatSingle(){
//byte inputFormatValue[8] = {0x41, 0x01, 0x08, 0x01, 0x00, 0x00, 0x00, 0x01}; //color and distance on port C (1)
byte inputFormatValue[8] = {0x41, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01}; //boost tacho motor on port C (1)
WriteValue(inputFormatValue, 8);
}
/**
* @brief Stop the motor on a defined port. If no port is set, all motors (AB) will be stopped
* @param [in] port Port of the Hub on which the motor will be stopped (A, B, AB, C, D)
*/
void TechnicHub::stopMotor(Port port = AB)
{
setMotorSpeed(port, 0);
}
Für erste Tests habe ich ein paar Zeilen geschrieben, um den 42099 mit dem Gamepad zu steuern.
Code:
/**
Gamepad für 42099
Mittlerer Anschluss Poti links X-Achse an Pin 32 des esp32
Mittlerer Anschluss Poti rechts Y-Achse an Pin 35 des esp32
Äußere Anschlüsse der Potis an 3V und GND
Buttons an Masse und Pin 12 / 14 des esp32
Antriebsmotoren an Technic-Hub Port A und B
Servo-Motor an Technic-Hub Port C
*/
#include "TechnicHub.h"
// create a hub instance
TechnicHub myTechnicHub;
TechnicHub::Port _portA = TechnicHub::Port::A;
TechnicHub::Port _portB = TechnicHub::Port::B;
TechnicHub::Port _portC = TechnicHub::Port::C;
TechnicHub::Port _portD = TechnicHub::Port::D;
const int Poti_L_X = 32;
const int Poti_L_Y = 33;
const int Poti_R_X = 34;
const int Poti_R_Y = 35;
const int Button_L = 12;
const int Button_R = 14;
int Poti_LX = 1950;
int Poti_LY = 1950;
int Poti_RX = 1950;
int Poti_RY = 1950;
void setup() {
Serial.begin(115200);
myTechnicHub.init(); // initalize the TechnicHub instance
pinMode(Button_L, INPUT_PULLUP);
pinMode(Button_R, INPUT_PULLUP);
}
// main loop
void loop() {
// connect flow. Search for BLE services and try to connect if the uuid of the hub is found
if (myTechnicHub.isConnecting()) {
myTechnicHub.connectHub();
if (myTechnicHub.isConnected()) {
Serial.println("Connected to HUB");
// connect color/distance sensor to port c, activate sensor for updates
// myTechnicHub.activatePortDevice(_portA, 37); // BOOST_DISTANCE = 37
// connect boost tacho motor to port d, activate sensor for updates
// myTechnicHub.activatePortDevice(_portB, 38); // BOOST_TACHO_MOTOR = 38
myTechnicHub.activatePortDevice(_portA, 38); // BOOST_TACHO_MOTOR = 38
myTechnicHub.setLedColor(GREEN);
} else {
Serial.println("Failed to connect to HUB");
}
}
// if connected, you can set the name of the hub, the led color and shut it down
if (myTechnicHub.isConnected()) {
int rotation;
delay(100);
// read rotation value in degrees of the boost tacho motor
rotation = myTechnicHub.getTachoMotorRotation();
Serial.print("rotation - ");
Serial.println(rotation);
Serial.print("Poti_L_X - ");
Serial.println(digitalRead(Poti_L_X));
Serial.print("Poti_L_Y - ");
Serial.println(digitalRead(Poti_L_Y));
Serial.print("Poti_R_X - ");
Serial.println(digitalRead(Poti_R_X));
Serial.print("Poti_R_Y - ");
Serial.println(digitalRead(Poti_R_Y));
Serial.print("Button_L - ");
Serial.println(digitalRead(Button_L));
Serial.print("Button_R - ");
Serial.println(digitalRead(Button_R));
// set hub LED color dependent on the absolute angle of the rotation (mapping from angle to rainbow color)
// myTechnicHub.setLedHSVColor(abs(rotation), 1.0, 1.0);
// Links X ist Geber für Antriebsmotor A + B
Poti_LX = analogRead(Poti_L_X);
//Serial.println(Poti_LX);
if (Poti_LX < 2000 && Poti_LX > 1900)
{
myTechnicHub.stopMotor(_portA);
myTechnicHub.stopMotor(_portB);
}
else if (Poti_RX > 2000)
{
myTechnicHub.setMotorSpeed(_portA, (Poti_RX - 2100) / 21);
myTechnicHub.setMotorSpeed(_portB, (Poti_RX - 2100) / 21);
}
else if (Poti_RX < 1900)
{
myTechnicHub.setMotorSpeed(_portA, (Poti_RX - 1900) / 19);
myTechnicHub.setMotorSpeed(_portB, (Poti_RX - 1900) / 19);
}
// Rechts Y ist Geber für Lenkung an Port C
Poti_RY = analogRead(Poti_R_Y);
if (Poti_RY < 2000 && Poti_RY > 1900)
{
myTechnicHub.GotoAbsolutePosition(0, 100, 50, 127, 0, _portC);
}
else if (Poti_RY > 2000)
{
myTechnicHub.GotoAbsolutePosition((Poti_RY - 2000) / 20, 100, 50, 127, 0, _portC);
}
else if (Poti_RY < 1900)
{
myTechnicHub.GotoAbsolutePosition((Poti_RY - 1900) / 19, 100, 50, 127, 0, _portC);
}
/*
// Funktioniert nicht, Motor fährt auch bei halber Entfernung immer über das Ziel hinaus.
if (rotation > 0)
{
myTechnicHub.setMotorSpeedForDegrees(_portA, -50, (rotation/2));
}
else
{
myTechnicHub.setMotorSpeedForDegrees(_portA, 50, (rotation/2));
}
*/
}
} // End of loop
Der Code ist von einigem Müll bereigt damit er halbwegs lesbar ist, seitdem aber nicht mehr getestet da ich angefangen habe, das Gamepad zu bauen und gerade keinen Prototyp zur Verfügung habe.
Und ja, sauberer Code sieht bestimmt anders aus, ich pfusche mich da halt so halbwegs mit meinen schlechten Programmierkenntnissen durch
Was mir nicht gefällt ist dass der L-Motor als Servo viel zittert, das scheint mit der C+-App besser zu sein.
Und für @gatewalker noch ein Testprogramm für den Trailer:
Code:
/**
Synchronisation zwischen Motoren an Port A und B
Port B fährt die Position von Port A an
*/
#include "TechnicHub.h"
// create a hub instance
TechnicHub myTechnicHub;
TechnicHub::Port _portA = TechnicHub::Port::A;
TechnicHub::Port _portB = TechnicHub::Port::B;
TechnicHub::Port _portC = TechnicHub::Port::C;
TechnicHub::Port _portD = TechnicHub::Port::D;
const int Poti_L_X = 32;
const int Poti_L_Y = 33;
const int Poti_R_X = 34;
const int Poti_R_Y = 35;
const int Button_L = 33;
const int Button_R = 33;
int Poti_LX = 1950;
int Poti_LY = 1950;
int Poti_RX = 1950;
int Poti_RY = 1950;
void setup() {
Serial.begin(115200);
myTechnicHub.init(); // initalize the TechnicHub instance
pinMode(Button_L, INPUT_PULLUP);
pinMode(Button_R, INPUT_PULLUP);
}
// main loop
void loop() {
// connect flow. Search for BLE services and try to connect if the uuid of the hub is found
if (myTechnicHub.isConnecting()) {
myTechnicHub.connectHub();
if (myTechnicHub.isConnected()) {
Serial.println("Connected to HUB");
// connect color/distance sensor to port c, activate sensor for updates
// myTechnicHub.activatePortDevice(_portA, 37); // BOOST_DISTANCE = 37
// connect boost tacho motor to port d, activate sensor for updates
// myTechnicHub.activatePortDevice(_portD, 38); // BOOST_TACHO_MOTOR = 38
myTechnicHub.activatePortDevice(_portA, 38); // Port A liest Position aus
myTechnicHub.setLedColor(GREEN);
} else {
Serial.println("Failed to connect to HUB");
}
}
// if connected, you can set the name of the hub, the led color and shut it down
if (myTechnicHub.isConnected()) {
delay(100);
int rotation = myTechnicHub.getTachoMotorRotation();
Serial.println(rotation);
myTechnicHub.GotoAbsolutePosition(rotation, 100, 50, 127, 0, _portB); // PortB fährt Position von PortA an
}
} // End of loop
Port B fährt immer die Position von Port A an. Weniger als 1/10 Sekunde zwischen zwei Messungen führen leider zu wildem Hin- und Herfahren des Motors an Port B, diese Verzögerung ist also das Minimum.
Auch hier stört das Zittern.
Bevor ich jetzt zu große Hoffnungen wecke: Bis etwas wirklich nutzbares fertig ist wird es noch eine Zeitlang dauern und ich bin auch nicht in der Lage, so schnellen und guten Support zu leisten wie es z.B. der Entwickler von BC2 schafft. In erster Linie bastle ich hier an einem Gamepad für mich, werde aber wenn möglich auch Eure Ideen mit einbringen und Euch den Code zur Verfügung stellen. Mithilfe ist natürlich auch gerne gesehen.
Hier auch gleich die erste Frage: Wie würdet Ihr Euch die Programmierung vorstellen?
Für mich kann ich die verschiedenen Modelle am einfachsten fest im Code hinterlegen, aber eine freie Programmierbarkeit ohne den Quellcode zu ändern wäre schöner.
Eine Idee ist, eine SD-Karte mit Text-Dateien zu beschreiben, die Hubs und deren Ports mit Tasten am Gamepad verbinden.
Eine andere Idee wäre, über Tastenkombinationen Hubs anzulernen. Das kommt ohne PC aus, ist aber aufwändiger und wenn mehr als nur Vorwärts/Rückwärts programmiert werden soll auch ziemlich unkomfortabel.
Die meisten Gamepads haben vier LEDs, die würde ich als Statusanzeige nutzen und auch vier Modelle hinterlegen, die dann beim Einschalten ausgewählt werden können.
Und jetzt bin ich auf Meinungen und Ideen gespannt.
Gruß
Patrick
Zuletzt bearbeitet: