-
Notifications
You must be signed in to change notification settings - Fork 2
2. Software
Antes de nada, al tratarse de un código desarrollado para Arduino, el fichero tiene la extensión '.ino'.
Para editar este tipo de ficheros se puede hacer con Arduino IDE (descargable en el enlace ).
NOTA : el fichero ejecutable está disponile en este mismo repositorio. Haz click aqui para acceder
Este software actúa como el "cerebro" del robot, integrando las funcionalidades de control de motores, activación de la bomba y recepción de señales desde la emisora.
Esta sección explica cómo se estructuran estos códigos, su funcionamiento y cómo interactúan con los componentes del robot. Además, se incluye un análisis detallado de la calibración del receptor, una descripción precisa del montaje eléctrico relevante (como el uso de un transistor para la bomba) y una sección de problemas comunes con soluciones prácticas y ejemplos claros.
El receptor FlySky Eachine TX06 es el encargado de traducir las señales enviadas por la emisora en pulsos eléctricos que el microcontrolador puede interpretar. Este código permite medir esos pulsos y observar sus rangos para su posterior calibración. La calibración es un paso esencial, ya que garantiza que las señales sean interpretadas correctamente por el software principal.
int rcPins[3] = {3, 5, 6}; // Pines de entrada para los canales (deben ser PWM, marcados en el arduino con el símbolo ~)
void setup() {
Serial.begin(9600); // Inicializa la comunicación serie
for (int i = 0; i < 3; i++) {
pinMode(rcPins[i], INPUT); // Configura los pines como entradas
}
}
void loop() {
for (int i = 0; i < 3; i++) {
int value = pulseIn(rcPins[i], HIGH, 30000); // Lee el ancho del pulso en microsegundos
Serial.print("Canal ");
Serial.print(i + 1);
Serial.print(": ");
Serial.println(value); // Imprime el valor leído
}
Serial.println("----"); // Separador para cada iteración
delay(1000); // Espera 1 segundo antes de leer nuevamente
}-
int rcPins[3] = {3, 5, 6};: Define un arreglo que contiene los pines PWM conectados a los canales del receptor. -
pinMode(rcPins[i], INPUT);: Configura estos pines como entradas, permitiendo al microcontrolador "escuchar" las señales que llegan del receptor. -
pulseIn(): Esta función mide la duración de un pulso HIGH en microsegundos. La duración varía según la posición de los joysticks y switches de la emisora. Al principio usamos 20000 microsegundos y tuvimos problemas ya que era la misma frecuencia de actualización del receptor y no se leían bien los valores ya que se leían y actualizaban a la vez. Lo cambiamos a 30000 microsegundos y funcionó perfectamente. -
Serial.printySerial.println: Utilizados para enviar información al monitor serie, facilitando el monitoreo en tiempo real de las señales recibidas.
Los valores leídos deberían imprimirse por el monitor serie en el siguiente formato (los valores variarán según el mando y receptor utilizados):
Canal 1: 995
Canal 2: 1495
Canal 3: 1200
Canal x: ...
----
Esto permite al usuario observar los rangos y calibrar el comportamiento del robot.
-
Canal 1 (dirección):
- Rango: 995 a 1990.
- Valor medio (~1500) corresponde a la posición neutral del joystick derecho de la Turnigy Evolution.
-
Canal 2 (throttle):
- Rango: 995 (mínimo) a 1990 (máximo) en el joystick izquierdo.
- Valores bajos significan que el robot está detenido.
-
Canal 3 (switches):
- Base: 995.
- Cada posición de los switches incrementa este valor (switch izquierdo: +300, derecho: +200). Se aplicará una lógica en el código para identificar ambas posiciones teniendo en cuenta que ambos switch utilizan el mismo canal.
Para garantizar que las señales del receptor se interpreten correctamente, se deben registrar los valores extremos y ajustar los rangos en el código principal:
- Ejemplo: Si el joystick derecho (dirección) arroja valores entre 1000 y 2000, estos deben mapearse a un rango de -255 a 255. Esta relación permite que el robot gire suavemente y mantenga el control preciso al utilizar todo el rango del joystick.
- Los valores también se deben validar para los switches traseros, comprobando cómo las combinaciones afectan al comportamiento del sistema, y utilizando un margen de error (en nuestro caso de ±10 unidades)
La bomba de agua, un componente clave del robot, requiere un circuito de control que incluye un transistor (BC547 en nuestro caso). Se estudió cómo el transistor permite gestionar la alta corriente que requiere la bomba, ya que no es suficiente con la corriente que proporciona únicamente la salida del pin digital. Este código permite activar la bomba de manera sencilla para verificar su funcionamiento.
const int pin = 9;
void setup() {
pinMode(pin, OUTPUT);
}
void loop() {
digitalWrite(pin, HIGH);
}Dado que el microcontrolador no puede alimentar directamente la bomba debido a su alto consumo de corriente, se utiliza un transistor como interruptor electrónico (deberá ser elegido según la potencia de la bomba, en nuestro caso un transistor BC547)
- Colector: Conectado al pin 5V del Arduino.
- Base: Conectada al pin digital del Arduino mediante una resistencia (1kΩ).
- Emisor: Conectado al terminal positivo de la bomba.
- El terminal negativo de la bomba debe estar conectado al pin GND del Arduino.
Especificaciones del Transistor BC547:
- Corriente máxima de colector (Ic): 100 mA
- Tensión máxima colector-emisor (Vce): 45 V
- Ganancia de corriente (hFE): 110-800
- Encapsulado: TO-92
Este circuito permite que el microcontrolador controle la bomba actuando como un interruptor. Cuando el transistor está activado, permite el paso de la corriente desde el pin 5V del Arduino hacia la bomba, completando el circuito.
-
pinMode(pin, OUTPUT);: Configura el pin como salida. -
digitalWrite(pin, HIGH);: Envía una señal alta, activando el transistor y, por ende, la bomba. La bomba permanece encendida durante el tiempo que el pin esté en HIGH.
- Si la bomba no se activa, verifica las conexiones del transistor y asegúrate de que la resistencia en la base sea adecuada.
- Si el transistor se calienta demasiado, revisa que esté utilizando un modelo adecuado para la corriente de la bomba.
Este código integra todas las funcionalidades del robot, incluyendo la lectura de valores del receptor, el control de motores mediante el controlador L298N y la activación de la bomba. Se explican tanto los procesos lógicos como los físicos para garantizar que el sistema opere de manera eficiente y segura.
// Pines del receptor
int receiverPins[3] = {3, 5, 6}; // Pines conectados a los canales del receptor (Dirección, Throttle, Switches)
// Pines del controlador L298N
const int EN_A = 11; // Control de velocidad Motor A
const int IN1 = A0; // Dirección Motor A
const int IN2 = A1; // Dirección Motor A
const int EN_B = 10; // Control de velocidad Motor B
const int IN3 = A2; // Dirección Motor B
const int IN4 = A3; // Dirección Motor B
const int PUMP_PIN = 8; // Pin para la bomba
// Pines para LEDs y buzzer
const int RED_LED = 7; // Pin para LED rojo
const int BLUE_LED = 9; // Pin para LED azul
const int BUZZER = 12; // Pin para el buzzer
unsigned long previousMillis = 0; // Variable para temporizador
const long interval = 500; // Intervalo de parpadeo (500ms)
bool ledState = false; // Estado actual de los LEDs
void setup() {
Serial.begin(9600);
// Configurar pines del controlador como salida
pinMode(EN_A, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(EN_B, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
pinMode(PUMP_PIN, OUTPUT); // Configurar pin de la bomba como salida
digitalWrite(PUMP_PIN, LOW); // Asegurarse de que la bomba esté apagada al inicio
// Configurar pines para LEDs y buzzer como salida
pinMode(RED_LED, OUTPUT);
pinMode(BLUE_LED, OUTPUT);
pinMode(BUZZER, OUTPUT);
// Apagar LEDs y buzzer al inicio
digitalWrite(RED_LED, LOW);
digitalWrite(BLUE_LED, LOW);
digitalWrite(BUZZER, LOW);
}
void loop() {
// Leer señal de los canales del receptor
int directionValue = pulseIn(receiverPins[0], HIGH, 30000); // Canal 1 (Dirección)
int throttleValue = pulseIn(receiverPins[1], HIGH, 30000); // Canal 2 (Throttle)
int switchesValue = pulseIn(receiverPins[2], HIGH, 30000); // Canal 3 (Switches)
// Mapear valores del receptor
int motorSpeed = map(throttleValue, 995, 1990, 0, 255); // Throttle mapeado a PWM
motorSpeed = constrain(motorSpeed, 0, 255); // Limitar valores al rango válido
int directionPWM = map(directionValue, 995, 1990, -255, 255); // Dirección mapeada a -255 a 255
directionPWM = constrain(directionPWM, -255, 255); // Limitar valores al rango válido
// Detectar switches con margen de error
bool reverseMode = (switchesValue >= 1295 - 10 && switchesValue <= 1295 + 10) || (switchesValue >= 1495 - 10 && switchesValue <= 1495 + 10) ; // Switch izquierdo posición 1
bool pumpActive = (switchesValue >= 1195 - 10 && switchesValue <= 1195 + 10) || (switchesValue >= 1495 - 10 && switchesValue <= 1495 + 10); // Switch derecho posición 1
// Controlar motores en función de dirección y throttle
int leftMotorSpeed, rightMotorSpeed;
if (motorSpeed == 0) {
// Giro sobre sí mismo
leftMotorSpeed = directionPWM;
rightMotorSpeed = -directionPWM;
} else {
// Ajuste de velocidad para giros suaves
leftMotorSpeed = motorSpeed + (directionPWM / 2);
rightMotorSpeed = motorSpeed - (directionPWM / 2);
}
// Limitar velocidades
leftMotorSpeed = constrain(leftMotorSpeed, -255, 255);
rightMotorSpeed = constrain(rightMotorSpeed, -255, 255);
// Cambiar dirección si está en modo reversa
if (reverseMode) {
leftMotorSpeed = -leftMotorSpeed;
rightMotorSpeed = -rightMotorSpeed;
}
// Configurar dirección y velocidad de Motor A
if (leftMotorSpeed > 0) {
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
} else {
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
}
// Velocidad mimima para que se mueve el motor por construcción
if(abs(leftMotorSpeed) > 75){
analogWrite(EN_A, abs(leftMotorSpeed));
} else {
analogWrite(EN_A, 0);
}
// Configurar dirección y velocidad de Motor B
if (rightMotorSpeed > 0) {
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
} else {
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
}
// Velocidad mimima para que se mueve el motor por construcción
if(abs(rightMotorSpeed) > 75){
analogWrite(EN_B, abs(rightMotorSpeed));
} else {
analogWrite(EN_B, 0);
}
// Controlar bomba
if (pumpActive) {
digitalWrite(PUMP_PIN, HIGH); // Activar bomba
} else {
digitalWrite(PUMP_PIN, LOW); // Apagar bomba
}
// Controlar LEDs y buzzer
if (rightMotorSpeed < 50 && leftMotorSpeed < 50 && !pumpActive){
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
ledState = !ledState; // Cambiar estado de los LEDs
// Alternar LEDs
digitalWrite(RED_LED, ledState);
digitalWrite(BLUE_LED, !ledState);
// Generar sonido de sirena en el buzzer
tone(BUZZER, ledState ? 1000 : 500, 500); // Alternar entre 1000Hz y 500Hz cada medio segundo
}
}
// Debug para monitorear valores
Serial.print("Throttle: ");
Serial.print(throttleValue);
Serial.print(" -> Speed: ");
Serial.println(motorSpeed);
Serial.print("Direction: ");
Serial.print(directionValue);
Serial.print(" -> Left: ");
Serial.print(leftMotorSpeed);
Serial.print(", Right: ");
Serial.println(rightMotorSpeed);
Serial.print("Switches: ");
Serial.print(switchesValue);
Serial.print(" -> Reverse Mode: ");
Serial.print(reverseMode);
Serial.print(", Pump: ");
Serial.println(pumpActive);
Serial.println("----");
delay(20); // Pequeño retraso para estabilidad
}
La función pulseIn() mide los pulsos enviados por el receptor. Estos valores se convierten usando map() a rangos útiles para controlar los motores y la bomba, asegurando una respuesta adecuada según las entradas de la emisora.
-
Mapeo:
-
throttleValuese mapea a una velocidad de 0 a 255, controlando la velocidad. -
directionValueajusta el giro, desde -255 a 255, controlando la dirección (se sumará con lógica a la velocidad de cada motor dependiendo de la dirección de giro)
-
-
L298N:
- Los pines
IN1-IN4determinan la dirección. -
EN_AyEN_Bmanejan la velocidad con PWM, logrando control continuo sobre los motores.
- Los pines
Cuando switchesValue detecta una posición 1 (en medio) del switch derecho, el pin PUMP_PIN envía una señal alta para activarla. Esto asegura que la bomba se utilice solo cuando sea necesario.
-
Causa: Rango incorrecto en
map(). - Solución: Verificar y ajustar los valores extremos del receptor.
- Causa: Conexiones incorrectas del transistor.
- Solución: Revisar las conexiones y resistencia.
- Causa: Valores mal calibrados.
-
Solución: Ajustar los rangos en
map()para dirección.