Banner publicitario de PCBWay

Comunicación entre escáner ( iluminación) conector BM9 ( 9 pines )

estuve mirando videso y no encontre motores paso a paso que se muevan rapido, creo que un paso a paso no tiene la reaccion para hacer los barridos de una punta a la otra a buena velocidad como muestra el video de funcionamiento original . talvez los topes no sean topes en si , si no centradores y amortiguadores, o sea que talvez sea normal que golpee el motor para que entre en juego el embriage del resorte
Como que no? Si señor, se puede tener muy buena velocidad 😉
Mira estos videos; no refiere al tema principal, pero mira la velocidad del PAP


 
estuve mirando videso y no encontre motores paso a paso que se muevan rapido, creo que un paso a paso no tiene la reaccion para hacer los barridos de una punta a la otra a buena velocidad como muestra el video de funcionamiento original . talvez los topes no sean topes en si , si no centradores y amortiguadores, o sea que talvez sea normal que golpee el motor para que entre en juego el embriage del resorte
Creo que tienes una idea equivocada de los PAP. 🤦‍♀️

Por ejemplo los 42PM48L-01 de 7.5° son usados en los Scanner económicos, incluso los M42SP-5 de 7.5° que los encuentras a montones en impresoras son perfectos para este tipo de proyectos.

Los que usa los Revo Scan son muy rápidos y son como los que indico 42PM48L-01 de 7.5°, como que no encuentras mas bien eso si cuestan mas de lo normal.


1765051811263.png

Los PAP dependen del Step Angle entre mismo sea menor este alcanzara velocidades mas altas, los hay desde un ángulo de paso de 0.9°, 1,8°, 3.6° y así sucesivamente, por ejemplo uno de 1.8° necesita 200 pasos para dar una vuelta, uno de 7.5° 48 pasos por vuelta, ves la diferencia.

Revo Scann.jpg

La velocidad depende de la frecuencia de los pulsos de control y del numero de dientes del rotor😁, si estos se configuran en Microstep son mas precisos pero pierden fuerza en cambio en paso completo o Fullstep tiene mas torque pero por ende hay mas vibración.


Un par de diagramas y demás de un Scanner y así se nos damos una buena idea de como es su funcionamiento el cual integra el famoso CMOS CD4093.

Scaner Motor Driver Kitana.jpg


Scaner MotoSwitches Kitana.jpg






Un ejemplo de una restauración y así se puede apreciar todo incluso casi se puede palpar.



Viejo dicho >Colombiano si quieren mas que le piquen caña :ROFLMAO:🤦‍♀️
 

Adjuntos

  • Kitana Explosion Drawing StageScan.pdf
    677.1 KB · Visitas: 1
  • Kitana Schematics StageScan.pdf
    445.5 KB · Visitas: 1
  • M42SP-5.PDF
    59 KB · Visitas: 0
  • 42PM48L01.pdf
    133.1 KB · Visitas: 0
Última edición:
Hola a todos
Bueno creo que habria que definir que quiere hacer
Yo estoy dispuesto a colaborar . dentro del poco tiempo que tengo disponible .
Ya sea en clonar la placa con el sprint layout ., o si quiere hacerlo con micro y motores pap
Pero como dice el maestro Kitana y el querido profe torres.electronico ., eso seria mas caro y mas complejo
Esos motores con reductora., se usan mucho en los espiedo de los hornos de las cosinas domesticas
Con respecto a usar micros., NO SERIA CON ARDUINO .,.si bien usariamos AVR como el ATTINy328p ., es con otro lenguaje de programcion
Yo creo que ya esta demostrado que info y soluciones hay de sobra ja ja ja ja ja ja
Subo 2 videos en .RAR que No estan en youtube sobre moviento de pap usando dmx
En los dos trato de explicar (muy mal por cierto) la aceleracion y torque de los pap
Bajen los .RAR dele a desconprimir el primero de cada uno y solo se juntan los videos
ATTE : juan
 

Adjuntos

  • Prueba 1 dmx steeper.part1.rar
    10 MB · Visitas: 0
  • Prueba 1 dmx steeper.part2.rar
    10 MB · Visitas: 0
  • Prueba 1 dmx steeper.part3.rar
    1.3 MB · Visitas: 0
  • Prueba 2 dmx steeper.part1.rar
    10 MB · Visitas: 0
  • Prueba 2 dmx steeper.part2.rar
    10 MB · Visitas: 0
  • Prueba 2 dmx steeper.part3.rar
    10 MB · Visitas: 0
  • Prueba 2 dmx steeper.part4.rar
    1.3 MB · Visitas: 0
Como que no? Si señor, se puede tener muy buena velocidad 😉
Mira estos videos; no refiere al tema principal, pero mira la velocidad del PAP


Si se ven mucho más rápidos de los que ví. Porsupuesto todo tiene que ver con la relación de sus reductores. Pero no miento he visto videos con papá probados a su máxima velocidad y lo primero que pensé que lo máximo que podría aspirar era cambiar a colores de al lado . De todas formas el DC no es una opción precisa y el servo no gira 360° así que no hay elección. Llevará un paso a paso.
 
Tirada de cables:
aclarando que todavía tengo que conseguir herramientas para desmantelar la plaqueta sin romper ( el chupador de antaño) . La scaneee, Ustedes se encarguen de clonar y simular. Luego de la devolución las mande a imprimir, compre todos los componentes , los suelde y quedé bien . Hay un largo camino .
Por lo que mientras tanto continuaremos simulando la placa con Arduino.
El problema que se presenta ahora es como el título lo dice : tirada de cables.
Lo que tengo pensado es hacerme una cajita fuera de borda , nada del otro mundo , una cajita de madera con una tapa ,que pienso tener en mi mesa al lado de la PC por ejemplo por si necesito conectar para modificar algo ..o en el rack.
Dicha caja mágica que contendra la fuente , el arduino, los 2 puente h y los drivers de los paso a paso.
Por detras tendra conector interlook o algo similar para alimentar la fuente.
Y la salida hacia los escáners.

Los voy a dividir en dos grupos de 2 .
Es decir un puente h para 4 dc. Puentiando alimentación directo de la salida a los motores del módulo.
Entonces para los motores DC. Pienso llevar la señal lista , es decir 4 cables , dos para motor abajo- arriba y 2 para motor iz- derecha. Los cuales se copiaran al otro scaner.
Y con el otro grupo de scaner lo mismo descrito arriba

Entonces pensé utilizar utp . Es decir un solo conductor de 8 conductores que atraviese los 4 scaner.
4 pines quedan en los primeros dos .
Y los otros 4 pines alimentan el otro grupo.
Hasta ahí estamos
Ahora lo que necesito saber es si puedo puentiar dos motores paso a paso en unos solo driver?🤔🤔🤔 Para hacer lo mismo
Dependiendo la respuesta sería la cantidad y tipo de conductor que necesito.
Hame olvidaba de aclarar que entre aparato y aparato le vamos a dejar máximo 1.5 m . Por lo que la tirada más larga hasta el último scaner entre bajada y todo serían unos 10m

Lampara halógena o led ?
se me ocurrió analizar la idea de dejar de depender de los Transformers pesados (y usarlos en algún proyecto de audio ) y talvez cuando tenga la placa puedo alimentarla directamente con la fuente . Es de 12v 10A.
Por lo que para las luces tengo las siguientes opciones ( más económicas)
1- lámparas de cuarzo 220v 500w ,se que se desperdicia energía porque no se concentra. Pero talvez podría con aislar parte de la luz más la dos lupas que tengo en el scaner alcanzaría bien.

2- chip cob led blanco 100w 220. Algo que tampoco concentra al 100% la luz ( ya que no dispongo de 4 colimadored) además de requerir difusor de calor y talvez ventilacion adicional. Tampoco estoy seguro de la potencia lumínica final.
3- viendo los vídeos rusos note que utilizan leds RGB . Entonces otra opción sería ponerle varios led de alta potencia ( 3w cada color ) . Supongo que necesitaría de otra fuente de 24v y para controlar los colores vi unos que vienen para luces de pileta con control remoto y presets ya programados. El problema de estos es el consumo de los leds. Estamos hablando de casi 3 amperes por led por lo que necesitaria una fuente enorme y no creo que el controlador RGB soporte toda la potencia
 

Adjuntos

  • Screenshot_20251207_090208.jpg
    Screenshot_20251207_090208.jpg
    109.5 KB · Visitas: 2
  • Screenshot_20251207_090243.jpg
    Screenshot_20251207_090243.jpg
    149.7 KB · Visitas: 0
  • Screenshot_20251207_090449.jpg
    Screenshot_20251207_090449.jpg
    171.1 KB · Visitas: 0
  • Screenshot_20251207_090322.jpg
    Screenshot_20251207_090322.jpg
    145.4 KB · Visitas: 2
  • Screenshot_20251207_090342.jpg
    Screenshot_20251207_090342.jpg
    186.6 KB · Visitas: 2
  • IMG_20251207_092332.jpg
    IMG_20251207_092332.jpg
    188.5 KB · Visitas: 1
  • IMG_20251207_092341.jpg
    IMG_20251207_092341.jpg
    163.3 KB · Visitas: 1
Última edición:
Yo estoy dispuesto a colaborar . dentro del poco tiempo que tengo disponible .
Ya sea en clonar la placa con el sprint layout ., o si quiere hacerlo con micro y motores pap
Muchas gracias por esa maravillosa colaboración y sacar de tu tiempo disponible para brindar una mano se te agradece inmensamente tus grandes aportes, de igual manera de mi parte tratare de sacar un tiempo y a lo mejor podamos clonarla conjuntamente te parece.

":ROFLMAO:A ver si nos volvemos Masters en clonación de animales Extintos y nos tomamos unos tintos :ROFLMAO: " { Tinto es un café cargado Colombiano }

Pero como dice el maestro Kitana
1765131514073.png

Maestro🙋‍♀️ 🤣, bueno si me gustaría ser una docente pero aun me falta mucho por recorrer.
 
Última edición:
Bueno, arme 7 versiones distintas con el driver uln2003 y el motor 28BYJ-48 y no me termino de convencer...

CSS:
/*/////////////////////////////////////////////////////////////////////////////////
//  ArduRITMICO v4.0g - Scanner DJ – FFT +28BYJ-48 +ULN2003 + KY037 (v1.0)
// ETI Patagonia - prof.martintorres@educ.ar - https://github.com/ETI-PATAGONIA-AR
///////////////////////////////////////////////////////////////////////////////////
   - Arduino Nano
   - ULN 2003
   - 28BYJ-48
   - KY-037 analog -> A0
   - Pots: A1 (B/M) , A2 (M/A), A3 (gain RGB), A4 (maxTiltSteps), A5 (maxPanSteps)
   - Vert: 2,3,4,5  Horiz: 6,7,8,12
   - RGB MOSFET gates: D9 (R), D10 (G), D11 (B)  (N-channel MOSFET low-side)
   - Button mode / save: D13 (INPUT_PULLUP, press to GND)
   - librerias: fix_fft, AccelStepper, EEPROM
*/

#include <fix_fft.h>
#include <AccelStepper.h>
#include <EEPROM.h>
#include <math.h>

// ---------------- CONFIG PINES ----------------
#define AUDIO_PIN A0
#define POT_BM A1
#define POT_MA A2
#define POT_GAIN A3
#define POT_TILT_MAX A4
#define POT_PAN_MAX A5

AccelStepper stepperVert(AccelStepper::FULL4WIRE, 2, 3, 4, 5);
AccelStepper stepperHoriz(AccelStepper::FULL4WIRE, 6, 7, 8, 12);

#define PIN_R 9
#define PIN_G 10
#define PIN_B 11

#define BUTTON_PIN 13  // INPUT_PULLUP -> press to GND

// ---------------- FFT / BUFFERS ----------------
#define MUESTRAS 128
#define LOGM 7
#define BIN_OFFSET 2   // ignorar DC / ruido

char vReal[MUESTRAS];
char vImag[MUESTRAS];
unsigned long magn[MUESTRAS/2];

// ---------------- MOTION / STEPS ----------------
// 28BYJ-48 typical half-step ~4096 steps/rev depending firmware; usamos 4096
const long STEPS_PER_REV = 4096L;
const int DEFAULT_PAN_STEPS = STEPS_PER_REV / 2;   // 180° -> 2048
const int DEFAULT_TILT_STEPS = STEPS_PER_REV / 4;  //  90° -> 1024

int maxStepsPan = DEFAULT_PAN_STEPS;
int maxStepsTilt = DEFAULT_TILT_STEPS;

long centerPan = 0;
long centerTilt = 0;

float phasePan = 0;
float phaseTilt = 0;
float basePhaseInc = 0.015; // base increment for smooth motion

// kick parameters
int kickStepsPanBase = 120;
int kickStepsTiltBase = 140;
int kickDecayMs = 120;
long kickTargetPan = 0;
unsigned long kickEndPan = 0;
long kickTargetTilt = 0;
unsigned long kickEndTilt = 0;

// ---------------- RGB / LIGHTS ----------------
float gainRGB = 1.0;
const unsigned long MAX_MAGN_EXPECTED = 12000UL; // ajustar si hace falta

// ---------------- LIMIT BINS (pots) ----------------
int binBMax = 7;
int binMMax = 35;

// ---------------- STROBE / MODES ----------------
bool strobeEnabled = true;
int strobeThreshold = 2500;
unsigned long lastStrobe = 0;
int strobeLenMs = 40;

enum MODE {MODE_SIN=0, MODE_BEAT=1, MODE_STROBE=2, MODE_RANDOM=3, MODE_IDLE=4};
MODE currentMode = MODE_SIN;

// ---------------- EEPROM ADDRESSES ----------------
const int EE_ADDR_VALID = 0;
const int EE_ADDR_MODE = 1;
const int EE_ADDR_PAN = 10;   // 2 bytes
const int EE_ADDR_TILT = 20;  // 2 bytes
const int EE_ADDR_GAIN = 30;  //byte (0..255)

// ---------------- SMOOTHERS ----------------
float smoothB = 0, smoothM = 0, smoothA = 0;

// ---------------- UTILS ----------------
unsigned long lastMillis = 0;
unsigned long sampleDelayMs = 8;

void setup() {
  pinMode(PIN_R, OUTPUT);
  pinMode(PIN_G, OUTPUT);
  pinMode(PIN_B, OUTPUT);

  digitalWrite(PIN_R, LOW);
  digitalWrite(PIN_G, LOW);
  digitalWrite(PIN_B, LOW);

  pinMode(BUTTON_PIN, INPUT);

  stepperVert.setMaxSpeed(1200);
  stepperVert.setAcceleration(2000);
  stepperHoriz.setMaxSpeed(1200);
  stepperHoriz.setAcceleration(2000);
  stepperVert.setCurrentPosition(0);
  stepperHoriz.setCurrentPosition(0);

  centerPan = 0;
  centerTilt = 0;

  // ADC ref
  analogReference(INTERNAL); // más sensible para mic con voltajes bajos (si KY-037 funciona ok)
  delay(50);

  loadConfigFromEEPROM();
}

void loop() {
  unsigned long now = millis();

  int pBM = analogRead(POT_BM);
  int pMA = analogRead(POT_MA);
  int pGain = analogRead(POT_GAIN);
  int pTiltMax = analogRead(POT_TILT_MAX);
  int pPanMax = analogRead(POT_PAN_MAX);

  int minBin = BIN_OFFSET + 0;
  binBMax = constrain(map(pBM, 0, 1023, minBin+1, 14), minBin+1, 24);
  binMMax = constrain(map(pMA, 0, 1023, binBMax+1, MUESTRAS/2 - 1), binBMax+1, MUESTRAS/2 - 1);

  gainRGB = constrain(map(pGain, 0, 1023, 50, 350) / 100.0, 0.5, 3.5);
  maxStepsTilt = constrain(map(pTiltMax, 0, 1023, 64, DEFAULT_TILT_STEPS), 32, DEFAULT_TILT_STEPS);
  maxStepsPan  = constrain(map(pPanMax, 0, 1023, 64, DEFAULT_PAN_STEPS), 64, DEFAULT_PAN_STEPS);

  for (int i = 0; i < MUESTRAS; i++) {
    int raw = analogRead(AUDIO_PIN);
    int v = raw / 4 - 128;
    if (v < -128) v = -128;
    if (v > 127) v = 127;
    vReal[i] = (char)v;
    vImag[i] = 0;
  }


  aplicaVentana(vReal);
  fix_fft(vReal, vImag, LOGM, 0);

  // magnitudes
  for (int b = 0; b < MUESTRAS/2; b++) {
    int re = (int)vReal[b];
    int im = (int)vImag[b];
    unsigned long mag = (unsigned long)(re * re) + (unsigned long)(im * im);
    magn[b] = mag;
  }

  unsigned long sumB = 0;
  for (int b = BIN_OFFSET; b <= binBMax; b++) sumB += magn[b];

  unsigned long sumM = 0;
  for (int b = binBMax + 1; b <= binMMax; b++) sumM += magn[b];

  unsigned long sumA = 0;
  for (int b = binMMax + 1; b < MUESTRAS/2; b++) sumA += magn[b];

  // smoothing
  smoothB = smoothB * 0.75 + sumB * 0.25;
  smoothM = smoothM * 0.75 + sumM * 0.25;
  smoothA = smoothA * 0.75 + sumA * 0.25;

  float normB = constrain((sumB / (float)MAX_MAGN_EXPECTED) * gainRGB, 0.0, 1.0);
  float normM = constrain((sumM / (float)MAX_MAGN_EXPECTED) * gainRGB, 0.0, 1.0);
  float normA = constrain((sumA / (float)MAX_MAGN_EXPECTED) * gainRGB, 0.0, 1.0);

  float totalEnergy = constrain((sumB + sumM + sumA) / (3.0 * (float)MAX_MAGN_EXPECTED) * gainRGB, 0.0, 1.0);

  bool beatB = (sumB > smoothB * 1.6);
  bool beatA = (sumA > smoothA * 1.6);

  if (beatB) {
    int dir = (random(0,2) ? 1 : -1);
    kickTargetTilt = centerTilt + dir * (kickStepsTiltBase + (int)(normB * kickStepsTiltBase));
    kickEndTilt = now + kickDecayMs;
  }
  if (beatA) {
    int dir = (random(0,2) ? 1 : -1);
    kickTargetPan = centerPan + dir * (kickStepsPanBase + (int)(normA * kickStepsPanBase));
    kickEndPan = now + kickDecayMs;
  }

  bool strobeNow = false;
  if (strobeEnabled && sumA > strobeThreshold && currentMode == MODE_STROBE) {
    strobeNow = true;
    lastStrobe = now;
  }

  if (strobeNow) {
    flashRGB(255,255,255);
  } else {
    renderRGB(normB, normM, normA, totalEnergy, beatB, beatA);
  }

  float panSpeedFactor = 0.5 + totalEnergy * 3.0;
  float tiltSpeedFactor = 0.5 + totalEnergy * 3.5;
  phasePan += basePhaseInc * panSpeedFactor;
  phaseTilt += basePhaseInc * tiltSpeedFactor;

  long targetPan = centerPan + (long)( sin(phasePan) * maxStepsPan * totalEnergy );
  long targetTilt = centerTilt + (long)( sin(phaseTilt) * maxStepsTilt * totalEnergy );

  if (now < kickEndPan) {
    float frac = float(kickEndPan - now) / float(kickDecayMs);
    frac = constrain(frac, 0.0, 1.0);
    targetPan = (long)( targetPan * (1.0 - frac) + kickTargetPan * frac );
  } else {
    kickTargetPan = centerPan;
  }
  if (now < kickEndTilt) {
    float frac = float(kickEndTilt - now) / float(kickDecayMs);
    frac = constrain(frac, 0.0, 1.0);
    targetTilt = (long)( targetTilt * (1.0 - frac) + kickTargetTilt * frac );
  } else {
    kickTargetTilt = centerTilt;
  }

  if (targetPan > centerPan + maxStepsPan) targetPan = centerPan + maxStepsPan;
  if (targetPan < centerPan - maxStepsPan) targetPan = centerPan - maxStepsPan;
  if (targetTilt > centerTilt + maxStepsTilt) targetTilt = centerTilt + maxStepsTilt;
  if (targetTilt < centerTilt - maxStepsTilt) targetTilt = centerTilt - maxStepsTilt;

  switch (currentMode) {
    case MODE_SIN:

      break;
    case MODE_BEAT:

      if (!beatA && !beatB) {
        targetPan = (long)(targetPan * 0.8 + centerPan * 0.2);
        targetTilt = (long)(targetTilt * 0.8 + centerTilt * 0.2);
      }
      break;
    case MODE_RANDOM:
      targetPan += random(-20, 21);
      targetTilt += random(-15, 16);
      break;
    case MODE_IDLE:
      targetPan = (long)(centerPan + sin(phasePan) * (maxStepsPan * 0.15));
      targetTilt = (long)(centerTilt + sin(phaseTilt) * (maxStepsTilt * 0.12));
      break;
    case MODE_STROBE:
      break;
  }

  stepperHoriz.moveTo(targetPan);
  stepperVert.moveTo(targetTilt);

  stepperHoriz.run();
  stepperVert.run();

  handleButton();

  delay(sampleDelayMs);
}

void aplicaVentana(char *vData) {
  double n1 = double(MUESTRAS) - 1.0;
  for (int i = 0; i < MUESTRAS/2; i++){
    double r = double(i) / n1;
    double w = 0.5 * (1.0 - cos(6.283185307179586 * r));
    vData[i] = (char)((double)vData[i] * w);
    vData[MUESTRAS - 1 - i] = (char)((double)vData[MUESTRAS - 1 - i] * w);
  }
}

void renderRGB(float nB, float nM, float nA, float energy, bool beatB, bool beatA) {
  // build components
  float rf = nA; // agudos -> rojo
  float gf = nM; // medios -> verde
  float bf = nB; // bajos -> azul

  rf = constrain(rf * 0.9 + energy * 0.4, 0.0, 1.0);
  gf = constrain(gf * 0.9 + energy * 0.4, 0.0, 1.0);
  bf = constrain(bf * 0.9 + energy * 0.4, 0.0, 1.0);

  if (beatB) bf = min(1.0, bf + 0.45);
  if (beatA) rf = min(1.0, rf + 0.45);

  uint8_t R = (uint8_t)(rf * 255.0);
  uint8_t G = (uint8_t)(gf * 255.0);
  uint8_t B = (uint8_t)(bf * 255.0);

  analogWrite(PIN_R, R);
  analogWrite(PIN_G, G);
  analogWrite(PIN_B, B);
}

void flashRGB(uint8_t R, uint8_t G, uint8_t B) {
  analogWrite(PIN_R, R);
  analogWrite(PIN_G, G);
  analogWrite(PIN_B, B);
}

// ---------------- BUTTON / EEPROM ----------------
unsigned long btnPressStart = 0;
bool btnDown = false;

void handleButton() {
  bool pressed = (digitalRead(BUTTON_PIN) == LOW);
  unsigned long now = millis();
  if (pressed && !btnDown) {
    btnDown = true;
    btnPressStart = now;
  } else if (!pressed && btnDown) {

    btnDown = false;
    unsigned long held = now - btnPressStart;
    if (held >= 2000) {

      saveConfigToEEPROM();

      flashRGB(255,255,255);
      delay(80);
      renderRGB(0,0,0,0,false,false);
    } else {
      int next = (int(currentMode) + 1) % 5;
      currentMode = (MODE)next;
    }
  }
}

void saveConfigToEEPROM() {
  EEPROM.update(EE_ADDR_VALID, 0xA5);
  EEPROM.update(EE_ADDR_MODE, (uint8_t)currentMode);
  EEPROM.update(EE_ADDR_PAN, (uint8_t)(maxStepsPan & 0xFF));
  EEPROM.update(EE_ADDR_PAN+1, (uint8_t)((maxStepsPan >> 8) & 0xFF));
  EEPROM.update(EE_ADDR_TILT, (uint8_t)(maxStepsTilt & 0xFF));
  EEPROM.update(EE_ADDR_TILT+1, (uint8_t)((maxStepsTilt >> 8) & 0xFF));
  EEPROM.update(EE_ADDR_GAIN, (uint8_t)constrain((int)(gainRGB*50), 0, 255));
}

void loadConfigFromEEPROM() {
  uint8_t v = EEPROM.read(EE_ADDR_VALID);
  if (v == 0xA5) {
    uint8_t m = EEPROM.read(EE_ADDR_MODE);
    if (m <= 4) currentMode = (MODE)m;
    uint16_t pan = EEPROM.read(EE_ADDR_PAN) | (EEPROM.read(EE_ADDR_PAN+1) << 8);
    uint16_t tilt = EEPROM.read(EE_ADDR_TILT) | (EEPROM.read(EE_ADDR_TILT+1) << 8);
    if (pan >= 64 && pan <= DEFAULT_PAN_STEPS) maxStepsPan = pan;
    if (tilt >= 32 && tilt <= DEFAULT_TILT_STEPS) maxStepsTilt = tilt;
    uint8_t g = EEPROM.read(EE_ADDR_GAIN);
    if (g > 0) gainRGB = constrain(g / 50.0, 0.5, 5.0);
  }
}

ArduRITMICO_V4.png

Así que mire con amor la CNC casera con los nemas 17 y como tengo por ahi el shield cnc del arduino uno (el GRBL v1.1), arranque con una version mas completita...

CSS:
///////////////////////////////////////////////////////////////////////////////////
//  ArduRITMICO v4.1 - Scanner DJ – FFT + NEMA17 + DRV8825 + KY037 (v1.0)
// ETI Patagonia - prof.martintorres@educ.ar - https://github.com/ETI-PATAGONIA-AR
///////////////////////////////////////////////////////////////////////////////////
#include <fix_fft.h>
#include <EEPROM.h>

//-------------------- HARDWARE -------------------
// Micrófono
#define AUDIO_PIN A0             // AO → FFT
#define MIC_DO_PIN 2             // DO → BEAT instantáneo (INT0)
// Botón de modo
#define MODE_BUTTON 3
// RGB MOSFET (PWM)
#define PIN_R 9
#define PIN_G 10
#define PIN_B 11
// CtrlMotores (DRV8825)
#define PAN_STEP 4
#define PAN_DIR 5
#define TILT_STEP 6
#define TILT_DIR 7

// -------------------- CONFIG --------------------
// Parámetros FFT
#define MUESTRAS 128
#define LOGM 7
char dataR[MUESTRAS];
char dataI[MUESTRAS];
unsigned int spectrum[MUESTRAS/2];
// Bandas
#define CUT_BM 7
#define CUT_MA 35
// Variables FFT
unsigned long lastFFT = 0;

// -------------------- MOVIMIENTO --------------------
float centerPan  = 0;
float centerTilt = 0;
int panRange  = 90;   // grados totales (reales: pasos luego)
int tiltRange = 60;
bool modeSinusoidal = true;
unsigned long lastStepPan  = 0;
unsigned long lastStepTilt = 0;
float panPhase  = 0;
float tiltPhase = 0;
// Kick
unsigned long kickPanEnd  = 0;
unsigned long kickTiltEnd = 0;
int kickPanTarget  = 0;
int kickTiltTarget = 0;
// Strobe
unsigned long lastStrobe = 0;
unsigned int strobeLen   = 60;

// -------------------- MODOS --------------------
int modo = 0;
#define MODO_MAX 4
unsigned long lastButton = 0;

// -------------------- BEAT DETECTOR KY037 --------------------
volatile bool beatInstant = false;
unsigned long lastBeatHardware = 0;

void IRAM_ATTR micISR()
    {
     unsigned long t = millis();
     if (t - lastBeatHardware > 40)
       {
        beatInstant = true;
        lastBeatHardware = t;
       }
    }

// -------------------- STEP UTILS --------------------
void stepMotor(int stepPin, int dirPin, bool dir)
  {
  digitalWrite(dirPin, dir);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(3);
  digitalWrite(stepPin, LOW);
  delayMicroseconds(3);
 }

void moveToAngle(int stepPin, int dirPin, float angleDeg)
   {
    static float lastPanDeg = 0;
    static float lastTiltDeg = 0;
    float &last = (stepPin == PAN_STEP ? lastPanDeg : lastTiltDeg);
    float delta = angleDeg - last;
    last = angleDeg;

  // RELACIÓN PASOS → GRADOS
  // NEMA17 típico: 200 pasos/rev con DRV8825 microstep 1/8 → 1600 steps/rev → 4.44 steps/degree
  const float stepsPerDeg = 4.44;
  long steps = (long)(delta * stepsPerDeg);
  bool d = (steps >= 0);
  steps = abs(steps);

  for (long i = 0; i < steps; i++)
    {
    stepMotor(stepPin, dirPin, d);
    }
  }

// -------------------- RGB --------------------
void rgb(int r, int g, int b)
 {
  analogWrite(PIN_R, r);
  analogWrite(PIN_G, g);
  analogWrite(PIN_B, b);
 }

// -------------------- SETUP --------------------
void setup()
 {
  pinMode(MIC_DO_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), micISR, RISING);
  pinMode(MODE_BUTTON, INPUT_PULLUP);
  pinMode(PIN_R, OUTPUT);
  pinMode(PIN_G, OUTPUT);
  pinMode(PIN_B, OUTPUT);
  pinMode(PAN_STEP, OUTPUT);
  pinMode(PAN_DIR,  OUTPUT);
  pinMode(TILT_STEP, OUTPUT);
  pinMode(TILT_DIR,  OUTPUT);
  rgb(0,0,0);
  delay(400);
}

// -------------------- FFT --------------------
void computeFFT()
 {
  for (int i = 0; i < MUESTRAS; i++)
   {
    int v = analogRead(AUDIO_PIN);
    dataR[i] = (v / 4) - 128;
    dataI[i] = 0;
   }

  fix_fft(dataR, dataI, LOGM, 0);

  for (int i = 0; i < MUESTRAS/2; i++)
   {
    int r = dataR[i];
    int im = dataI[i];
    spectrum[i] = r*r + im*im;
   }
 }

void getBands(unsigned long &bajos, unsigned long &medios, unsigned long &agudos)
 {
  bajos = medios = agudos = 0;

  for (int i=2; i<CUT_BM; i++) bajos += spectrum[i];
  for (int i=CUT_BM; i<CUT_MA; i++) medios += spectrum[i];
  for (int i=CUT_MA; i<MUESTRAS/2; i++) agudos += spectrum[i];
 }

// -------------------- LOOP --------------------
void loop()
  {
  //--------------------------------------------------------
  // 1) Botón de Modo
  //--------------------------------------------------------
  if (digitalRead(MODE_BUTTON) == LOW && millis() - lastButton > 300)
   {
    modo++;
    if (modo > MODO_MAX) modo = 0;
    lastButton = millis();
   }

  //--------------------------------------------------------
  // 2) FFT (cada 8 ms)
  //--------------------------------------------------------
  if (millis() - lastFFT > 8)
   {
    lastFFT = millis();
    computeFFT();
   }

  unsigned long B, M, A;
  getBands(B, M, A);

  float energy = (B + M + A) / 30000.0;
  if (energy > 1.0) energy = 1.0;

  //--------------------------------------------------------
  // 3) Beat detection por FFT (suave)
  //--------------------------------------------------------
  bool beatFFT = (B > 20000 || A > 25000);

  //--------------------------------------------------------
  // 4) Si beat instantáneo por KY037-DO
  //--------------------------------------------------------
  bool beatNow = false;
  if (beatInstant)
   {
    beatInstant = false;
    beatNow = true;
   }

  //--------------------------------------------------------
  // 5) Movimiento PAN y TILT
  //--------------------------------------------------------
  float angPan = centerPan;
  float angTilt = centerTilt;

  // Kick por beat
  if (beatNow)
   {
    kickPanTarget  = random(-panRange/2,  panRange/2);
    kickTiltTarget = random(-tiltRange/2, tiltRange/2);
    kickPanEnd  = millis() + 120;
    kickTiltEnd = millis() + 120;
   }

  if (millis() < kickPanEnd)
   {
    angPan = kickPanTarget;
   }
   else
   {
    if (modo == 0 || modo == 1)
     {
      // suave sinusoidal
      panPhase += 0.035 * energy;
      angPan = (sin(panPhase) * (panRange / 2.0));
     }
     else
     {
      // brusco
      angPan = random(-panRange/2, panRange/2) * energy;
     }
   }

  if (millis() < kickTiltEnd)
   {
    angTilt = kickTiltTarget;
   }
    else
   {
    if (modo == 0 || modo == 2)
     {
      tiltPhase += 0.045 * energy;
      angTilt = (sin(tiltPhase) * (tiltRange / 2.0));
     }
     else
     {
      angTilt = random(-tiltRange/2, tiltRange/2) * energy;
     }
   }

  moveToAngle(PAN_STEP, PAN_DIR, angPan);
  moveToAngle(TILT_STEP, TILT_DIR, angTilt);

  //--------------------------------------------------------
  // 6) RGB según modo
  //--------------------------------------------------------
  if (modo == 3)
   {
    // STROBE
    if (beatNow || beatFFT)
     {
      rgb(255,255,255);
      lastStrobe = millis();
     }
    if (millis() - lastStrobe > strobeLen) rgb(0,0,0);
   }
  else if (modo == 4)
   {
    // COLOR DINÁMICO
    int r = min(255, (int)(A / 250));
    int g = min(255, (int)(M / 250));
    int b = min(255, (int)(B / 250));
    rgb(r,g,b);
   }
  else
   {
    // COLOR por bandas
    int r = min(255, (int)(A / 300));
    int g = min(255, (int)(M / 300));
    int b = min(255, (int)(B / 300));
    rgb(r,g,b);
   }
}

Despues de mirar nuevamente algunos aportes en el topico, edite y mejore un poco en una segunda version (la v4.2i):

CSS:
/*/////////////////////////////////////////////////////////////////////////////////
//  ArduRITMICO v4.2 - Scanner DJ – FFT + NEMA17 + DRV8825 + KY037 (v1.0)
// ETI Patagonia - prof.martintorres@educ.ar - https://github.com/ETI-PATAGONIA-AR
///////////////////////////////////////////////////////////////////////////////////

  Hardware:
  - Arduino UNO
  - Shield CNC (GRBL V1.1)
  - Driver DRV8825
  - Motores Nema 17
  - PAN (Eje X) STEP = pin D2, DIR = Pin D5
  - TILT (Eje Y) STEP = Pin D3, DIR = Pin D6
  - ENABLE (todos los drivers) = Pin D8 (LOW = habilitado)
  - Micrófono KY-037 AO -> Pin A0 (para FFT)
  - POT1 Pin A1 -> velocidad / agresividad
  - POT2 Pin A2 -> límite de PAN (grados)
  - POT3 Pin A3 -> límite de TILT (grados)
  - MOSFET RGB: Pin D9 (R), Pin D10 (G), Pin D11 (B)  (low-side)
  - Botón de modos: Pin D12 (INPUT_PULLUP → al presionarse va a GND)
  - OLED I2C: Pin A4 SDA, Pin A5 SCL (SSD1306 128x32)
  - Librerías usadas: fix_fft, AccelStepper, Adafruit_SSD1306, Adafruit_GFX
  y creo que no se me esta olvidando nada...
*/

#include <fix_fft.h>
#include <AccelStepper.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// ---------------------------- CONFIG HARDWARE ----------------------------
#define PAN_STEP 2
#define PAN_DIR  5
#define TILT_STEP 3
#define TILT_DIR  6
#define ENABLE_DRV 8
#define AUDIO_PIN A0
#define POT_SPEED A1
#define POT_PAN_LIMIT A2
#define POT_TILT_LIMIT A3
#define RGB_R 9
#define RGB_G 10
#define RGB_B 11
#define BTN_MODE 12
// OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ---------------------------- FFT / AUDIO ----------------------------
#define MUESTRAS 128
#define LOGM 7
#define BIN_OFFSET 2
char vReal[MUESTRAS];
char vImag[MUESTRAS];
unsigned long magn[MUESTRAS/2];
// Valor máximo esperado de energía FFT (ajustable si satura)
const unsigned long MAX_MAGN_EXPECTED = 20000UL;
// Límites de bandas (bajos, medios, agudos)
int binBMaxDefault = 7;
int binMMaxDefault = 35;
// Suavizados (EMA)
double smoothB = 0, smoothM = 0, smoothA = 0;

// ---------------------------- MOTORES ----------------------------
AccelStepper stepperPan(AccelStepper::DRIVER, PAN_STEP, PAN_DIR);
AccelStepper stepperTilt(AccelStepper::DRIVER, TILT_STEP, TILT_DIR);
// Config de motores
const int STEPPER_FULL_STEP = 200; // 1.8°
int microstep = 16; // DRV8825 configurado con jumpers
long stepsPerRev = STEPPER_FULL_STEP * microstep;
// Límites de movimiento según grados
long defaultPanSteps = (stepsPerRev * 180L) / 360L;
long defaultTiltSteps = (stepsPerRev * 90L) / 360L;
long maxPanSteps = defaultPanSteps;
long maxTiltSteps = defaultTiltSteps;
// centro en 0 (posición virtual)
long centerPan = 0;
long centerTilt = 0;
// Fases para el movimiento sinusoidal
double phasePan = 0;
double phaseTilt = 0;
double basePhaseInc = 0.02;
// "Patadas" de movimiento para el modo BRUSCO
int kickPanBase = 300;
int kickTiltBase = 200;
int kickDecayMs = 120;
long kickTargetPan = 0;
unsigned long kickEndPan = 0;
long kickTargetTilt = 0;
unsigned long kickEndTilt = 0;

// ---------------------------- MODOS ----------------------------
enum MODE { MODE_SIN = 0, MODE_BRUSCO = 1, MODE_AUDIO = 2 };
MODE currentMode = MODE_SIN;

// ---------------------------- BOTÓN ----------------------------
unsigned long lastButtonMillis = 0;
bool lastButtonState = HIGH;
const unsigned long debounceMs = 50;

// ---------------------------- STROBE ----------------------------
unsigned long lastStrobeMillis = 0;
int strobeLenMs = 40;
int strobeThresholdFactor = 160; // fuerza necesaria de agudos

// ---------------------------- TIMERS ----------------------------
unsigned long lastFFTMillis = 0;
const unsigned long fftIntervalMs = 8;
unsigned long loopDelay = 4;
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
void setup() {

  Serial.begin(115200);
  //Serial.println("Iniciando ScannerDJ...");
  // OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("OLED no detectado");
  } else {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
  }
  // Pines
  pinMode(ENABLE_DRV, OUTPUT);
  digitalWrite(ENABLE_DRV, LOW); // habilitar drivers
  pinMode(RGB_R, OUTPUT);
  pinMode(RGB_G, OUTPUT);
  pinMode(RGB_B, OUTPUT);
  analogWrite(RGB_R, 0);
  analogWrite(RGB_G, 0);
  analogWrite(RGB_B, 0);
  pinMode(BTN_MODE, INPUT_PULLUP);
  // Motores – velocidad y aceleración muy altas
  stepperPan.setMaxSpeed(6000.0);
  stepperPan.setAcceleration(40000.0);
  stepperTilt.setMaxSpeed(5000.0);
  stepperTilt.setAcceleration(35000.0);
  stepperPan.setCurrentPosition(0);
  stepperTilt.setCurrentPosition(0);
  randomSeed(analogRead(A3));
  // Pantalla de inicio
  display.clearDisplay();
  display.setCursor(0,0);
  display.println("ScannerDJ");
  display.println("Modo: Sinusoidal");
  display.display();
  delay(400);
}
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// ---------------------------- FUNCIONES DE FFT ----------------------------
// Ventana hanning
void applyWindow(char *data) {
  double n1 = double(MUESTRAS) - 1.0;
  for (int i = 0; i < MUESTRAS/2; i++) {
    double r = double(i) / n1;
    double w = 0.5 * (1.0 - cos(6.2831853 * r));
    data[i] = (char)(data[i] * w);
    data[MUESTRAS - 1 - i] = (char)(data[MUESTRAS - 1 - i] * w);
  }
}

// Ejecutar FFT completa
void computeFFT() {

  // Tomar muestras
  for (int i = 0; i < MUESTRAS; i++) {
    int raw = analogRead(AUDIO_PIN);
    int v = raw / 4 - 128;
    v = constrain(v, -128, 127);
    vReal[i] = (char)v;
    vImag[i] = 0;
  }

  // Ventana
  applyWindow(vReal);

  // FFT
  fix_fft(vReal, vImag, LOGM, 0);

  // Magnitudes
  for (int b = 0; b < MUESTRAS/2; b++) {
    int re = vReal[b];
    int im = vImag[b];
    magn[b] = (unsigned long)(re * re) + (unsigned long)(im * im);
  }
}

// Suma de una banda FFT
unsigned long sumBand(int fromBin, int toBin) {
  unsigned long s = 0;
  for (int b = fromBin; b <= toBin && b < MUESTRAS/2; b++) s += magn[b];
  return s;
}

// ---------------------------- RGB ----------------------------
void renderRGB(double normB, double normM, double normA, bool beatB, bool beatA, bool strobeNow) {

  // Si hay strobe → blanco total
  if (strobeNow) {
    analogWrite(RGB_R, 255);
    analogWrite(RGB_G, 255);
    analogWrite(RGB_B, 255);
    return;
  }

  // Agudos → rojo
  // Medios → verde
  // Bajos → azul
  uint8_t R = normA * 255.0;
  uint8_t G = normM * 255.0;
  uint8_t B = normB * 255.0;

  // Más brillo si hay beats
  if (beatB) B = min(255, B + 120);
  if (beatA) R = min(255, R + 120);

  analogWrite(RGB_R, R);
  analogWrite(RGB_G, G);
  analogWrite(RGB_B, B);
}

// ---------------------------- UTILIDADES ÁNGULOS ----------------------------
double stepsPerDegree() {
  return double(stepsPerRev) / 360.0;
}

long degToStepPan(double deg) {
  return centerPan + (deg * stepsPerDegree());
}

long degToStepTilt(double deg) {
  return centerTilt + (deg * stepsPerDegree());
}

// ---------------------------- BOTÓN ----------------------------
void handleButton() {

  bool raw = digitalRead(BTN_MODE);
  unsigned long now = millis();

  if (raw != lastButtonState) lastButtonMillis = now;

  if ((now - lastButtonMillis) > debounceMs) {
    if (raw == LOW && lastButtonState == HIGH) {

      int nm = currentMode + 1;
      if (nm > MODE_AUDIO) nm = MODE_SIN;
      currentMode = (MODE)nm;

      // Blink de confirmación
      for (int i = 0; i < 2; i++) {
        analogWrite(RGB_R,255); analogWrite(RGB_G,255); analogWrite(RGB_B,255); delay(80);
        analogWrite(RGB_R,0); analogWrite(RGB_G,0); analogWrite(RGB_B,0); delay(60);
      }

      // Mostrar en OLED
      display.clearDisplay();
      display.setCursor(0,0);
      display.print("Modo: ");
      if (currentMode == MODE_SIN) display.println("Sinusoidal");
      else if (currentMode == MODE_BRUSCO) display.println("Brusco");
      else display.println("Audio");
      display.display();

      delay(200);
    }
  }

  lastButtonState = raw;
}
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
void loop() {

  unsigned long now = millis();

  // Leer potenciómetros
  int potSpeed = analogRead(POT_SPEED);
  int potPan = analogRead(POT_PAN_LIMIT);
  int potTilt = analogRead(POT_TILT_LIMIT);

  // Parámetros derivados
  double speedFactor = map(potSpeed, 0, 1023, 20, 400) / 100.0;
  int panDegreesMax = map(potPan, 0, 1023, 30, 180);
  int tiltDegreesMax = map(potTilt, 0, 1023, 10, 90);

  maxPanSteps = (panDegreesMax / 360.0) * stepsPerRev;
  maxTiltSteps = (tiltDegreesMax / 360.0) * stepsPerRev;

  // FFT cada cierto tiempo
  if (now - lastFFTMillis > fftIntervalMs) {

    lastFFTMillis = now;

    computeFFT();

    unsigned long sumB = sumBand(BIN_OFFSET, binBMaxDefault);
    unsigned long sumM = sumBand(binBMaxDefault+1, binMMaxDefault);
    unsigned long sumA = sumBand(binMMaxDefault+1, (MUESTRAS/2)-1);

    // Suavizado EMA
    smoothB = smoothB * 0.75 + sumB * 0.25;
    smoothM = smoothM * 0.75 + sumM * 0.25;
    smoothA = smoothA * 0.75 + sumA * 0.25;

    // Normalizar
    double normB = constrain((double)sumB / MAX_MAGN_EXPECTED * speedFactor, 0.0, 1.0);
    double normM = constrain((double)sumM / MAX_MAGN_EXPECTED * speedFactor, 0.0, 1.0);
    double normA = constrain((double)sumA / MAX_MAGN_EXPECTED * speedFactor, 0.0, 1.0);

    bool beatB = (sumB > smoothB * 1.6);
    bool beatA = (sumA > smoothA * 1.6);

    bool strobeNow = false;
    if (currentMode == MODE_BRUSCO && (sumA > (unsigned long)strobeThresholdFactor * 100)) {
      strobeNow = true;
      lastStrobeMillis = now;
    }

    // Si hay beat, generar patadas
    if (beatB) {
      kickTargetTilt = (random(0,2)?1:-1) * (kickTiltBase + normB * kickTiltBase);
      kickEndTilt = now + kickDecayMs;
    }
    if (beatA) {
      kickTargetPan = (random(0,2)?1:-1) * (kickPanBase + normA * kickPanBase);
      kickEndPan = now + kickDecayMs;
    }

    // Movimiento según modo
    long targetPanSteps = centerPan;
    long targetTiltSteps = centerTilt;

    double totalEnergy = (normB + normM + normA) / 3.0;
    if (totalEnergy < 0.01) totalEnergy = 0.0;

    // ---------- MODO 1: Sinusoidal suave ----------
    if (currentMode == MODE_SIN) {

      phasePan += basePhaseInc * (0.5 + speedFactor * 1.5);
      phaseTilt += basePhaseInc * (0.7 + speedFactor * 1.3);

      double ampPanDeg = panDegreesMax/2.0 * max(0.25, totalEnergy + 0.2);
      double ampTiltDeg = tiltDegreesMax/2.0 * max(0.25, totalEnergy + 0.2);

      double panDeg = sin(phasePan) * ampPanDeg;
      double tiltDeg = sin(phaseTilt) * ampTiltDeg;

      targetPanSteps = degToStepPan(panDeg);
      targetTiltSteps = degToStepTilt(tiltDeg);
    }

    // ---------- MODO 2: BRUSCO ----------
    else if (currentMode == MODE_BRUSCO) {

      if (now < kickEndPan) targetPanSteps = centerPan + kickTargetPan;
      else targetPanSteps = centerPan + random(-20,21) * speedFactor;

      if (now < kickEndTilt) targetTiltSteps = centerTilt + kickTargetTilt;
      else targetTiltSteps = centerTilt + random(-12,13) * speedFactor;
    }

    // ---------- MODO 3: AUDIO REACTIVO ----------
    else {

      double panDeg = mapDouble(normB, 0.0, 1.0, -panDegreesMax/2.0, panDegreesMax/2.0);
      double tiltDeg = mapDouble(normM, 0.0, 1.0, -tiltDegreesMax/2.0, tiltDegreesMax/2.0);

      // movimiento suave adicional
      phasePan += basePhaseInc * 0.5;
      phaseTilt += basePhaseInc * 0.6;
      panDeg += sin(phasePan) * (panDegreesMax * 0.05);
      tiltDeg += sin(phaseTilt) * (tiltDegreesMax * 0.05); // ← CORREGIDO

      targetPanSteps = degToStepPan(panDeg);
      targetTiltSteps = degToStepTilt(tiltDeg);
    }

    // Aplicar límites mecánicos
    long panLimit = maxPanSteps;
    long tiltLimit = maxTiltSteps;

    targetPanSteps = constrain(targetPanSteps, centerPan - panLimit, centerPan + panLimit);
    targetTiltSteps = constrain(targetTiltSteps, centerTilt - tiltLimit, centerTilt + tiltLimit);

    // Enviar movimiento
    stepperPan.moveTo(targetPanSteps);
    stepperTilt.moveTo(targetTiltSteps);

    // LED RGB
    renderRGB(normB, normM, normA, beatB, beatA, strobeNow);
  }

  // Ejecutar motores
  stepperPan.run();
  stepperTilt.run();

  // Botón
  handleButton();

  // Actualizar OLED cada 200 ms
  static unsigned long lastOled = 0;
  if (now - lastOled > 200) {
    lastOled = now;
    drawOLED(currentMode, potSpeed, potPan, potTilt);
  }

  delay(loopDelay);
}
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// ---------------------------- FUNCIONES EXTRA ----------------------------
double mapDouble(double x, double in_min, double in_max, double out_min, double out_max) {
  if (x <= in_min) return out_min;
  if (x >= in_max) return out_max;
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void drawOLED(MODE m, int potSpeed, int potPan, int potTilt) {

  display.clearDisplay();
  display.setCursor(0,0);

  display.print("Modo: ");
  if (m == MODE_SIN) display.println("Sinusoidal");
  else if (m == MODE_BRUSCO) display.println("Brusco");
  else display.println("Audio");

  int spdPct = map(potSpeed, 0, 1023, 0, 100);
  int panDeg = map(potPan, 0, 1023, 30, 180);
  int tiltDeg = map(potTilt, 0, 1023, 10, 90);

  display.print("SPD:");
  display.print(spdPct);
  display.print("% ");

  display.print("PAN:");
  display.print(panDeg);

  display.print(" TLT:");
  display.println(tiltDeg);

  display.display();
}
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////

Me iba a poner armar el hardware y los driver los tengo en el rap con el mega2550, sumado a que me dio fiaca armar la etapa de los mosfet para el rgb... Veo si en la semana encierro a flojera en un cuarto y me pongo a jugar en el armado... por lo menos con la parte de los PAP y la pantalla... Si ven errores o quieren modificarlos, metan mano y propongan
 
Última edición:
Bueno, arme 7 versiones distintas con el driver uln2003 y el motor 28BYJ-48 y no me termino de convencer...
Que complejo que es todo, no se ni papá, yo estuve todo el día de ayer para hacer círculos y encima me quedo chueco . A ver que te puedo robar. Ya me decidí. Paso a paso y halógena en dos de los scaner.
Y en los otros dos vuelan los globos y pruebo con los módulos Neopixel ring rgb. Uno círculo chico , debajo de la lente y otro por fuera del scaner, rodeando la lente junto debajo del espejo .
Estoy viendo unos colimadores de 8° pero no tengo idea como quedará el haz. Me da miedo comprarlo y que después se vean puntitos chiquitos.
Cuando los tenga en mi poder tal vez me puedas ayudar con la escena de los leds . Y bueno, justo esos pap con los drivers 2003 son los que pedí para el disco de colores ... Solo ruego que sea compatible el eje con el disco de gobos.. por ahora solo tengo DC no puedo robarte nada.
 
hola
El agujero es demasiado grande. Me engañó la imagen del paso a paso. Ahora con que lo relleno y que quede centrado 🤔
Ver el archivo adjunto 334728
Ese es un problma ., el otro que vas a tener es la cantidad de pasos que vas a necesitar para dar un giro de 360°
Adjunto un PDF con datos de ese motor
Hola querido dosme
Metés un tornillo largo bien embebido de vaselina en la rosca , rellenás el agujero con Poxipol , una vez seco retirás el tornillo y agujereás justo en el centro con taladro de banco o con soporte ...
Si fuera el relleno cilindrico no habria problema ...... pero .......
medidas 28BYJ-48.jpg
Como seve en la imagen estandart ., el eje es muy "cortiro" solo 10mm de largo ., su diametro 5mm ., pero tien un desvate a 3mm
En la imagen del disco ., su brida o acople ., se ve un solo prisionero .,( si tuviera dos estaria mejor)
Seria mas facil centrarlo en el desvaste de 3mm ., yo diria que es mejor hacer unos semi-bujes .,con alumiinio de algun envase de desodorante personal (es mas grueso) ., tal vez con dos se rellene los 5mm que faltan
Porque si mal no recuerdo (y ando muy mal de la memoria ...ja ja ja ja ja ) los eje de los motores de CC con reductor son de 10mm de diametro
Despues quedaria rellenar el desvaste de 3mm ., que podria se con una media caña de algun pote-mini de esos estriados con ranura al medio ,. con un pote tenemos dos medias cañas
Se podria pegar con "la gotita" para que no se mueva al colocarlo ....va estar dificil ....pero es una idea

ATTE : juan
 

Adjuntos

  • medidas 28BYJ-48.jpg
    medidas 28BYJ-48.jpg
    64.2 KB · Visitas: 0
  • motor pap 28BYJ-48.pdf
    388.9 KB · Visitas: 0
Metés un tornillo largo bien embebido de vaselina en la rosca , rellenás el agujero con Poxipol , una vez seco retirás el tornillo y agujereás justo en el centro con taladro de banco o con soporte ...
Muchas gracias por tu solución. Hice algo similar con un pequeño cilindro de madera provisorio pero lo que propones me parece lo más aceptable posible para que tenga la rigidez suficiente sin tener que recurrir a tornear alguna pieza de adaptación.
De todas formas me llevó un largo rato nada más hacerlo andar. Simplemente vibraba y no hacía nada. Lo había conectado en las salidas digitales de mi Arduino mega ( porque me lo dijo la IA) varias horas más tarde cambié a los pines de 10 a 14 y santo remedio. Dio una vuelta entera y entonces apague todo y me acosté a dormir tranquilo.
Ahora procede hacer el reconocimiento necesario para lograr que mi pap se pare pueda ubicar de forma precisa los colores de mi disco y permanezca el tiempo que yo le asigne. Me interesa muy poco si la selección es consecutiva mientras pueda controlarla.
Otra cosa que necesitaré es que reconozca un punto de partida físico en el disco. Porque suponiendo que algun día le armo la programación y va perfecto .pero en algún momento lo apago en pleno proceso ( algo totalmente obvio). Simplemente cuando lo vuelva a encender el motor tomará como punto de partida la última posición en la que quedó🤔.
 
...el otro que vas a tener es la cantidad de pasos que vas a necesitar para dar un giro de 360°...
Exacto, por eso mencioné que no me gustaron las pruebas que hice con ese motorcito.
El 28BYJ-48 tiene 2048 pasos por vuelta y las velocidad más alta por su reducción interna es de tan solo 15 rpm.
Con el nema 17 para dar una vuelta solo necesitas 200 pasos y podes llegar a las 3000 rpm...
Es como la de los 600 grones en un fitito
 
Última edición:
Muchas gracias por tu solución. Hice algo similar con un pequeño cilindro de madera provisorio pero lo que propones me parece lo más aceptable posible para que tenga la rigidez suficiente sin tener que recurrir a tornear alguna pieza de adaptación.
De todas formas me llevó un largo rato nada más hacerlo andar. Simplemente vibraba y no hacía nada. Lo había conectado en las salidas digitales de mi Arduino mega ( porque me lo dijo la IA) varias horas más tarde cambié a los pines de 10 a 14 y santo remedio. Dio una vuelta entera y entonces apague todo y me acosté a dormir tranquilo.
Ahora procede hacer el reconocimiento necesario para lograr que mi pap se pare pueda ubicar de forma precisa los colores de mi disco y permanezca el tiempo que yo le asigne. Me interesa muy poco si la selección es consecutiva mientras pueda controlarla.
Otra cosa que necesitaré es que reconozca un punto de partida físico en el disco. Porque suponiendo que algun día le armo la programación y va perfecto .pero en algún momento lo apago en pleno proceso ( algo totalmente obvio). Simplemente cuando lo vuelva a encender el motor tomará como punto de partida la última posición en la que quedó🤔.

🤦 Usar la IA para esto es como intentar poner en un toma corriente dos cables pelados a oscuras... Quizás te sale bien y podes hacer la conexión de una, o quizás quedas pegado...
 
El agujero es demasiado grande. Me engañó la imagen del paso a paso. Ahora con que lo relleno y que quede centrado 🤔
Ver el archivo adjunto 334728

Voy a tratar de colaborar porque veo que le pones ganas a tu proyecto y con IA no lo vas a solucionar mágicamente como lo dice el Ingeniero Torres, espero no ser un fantasma por aquí 😂

En primer lugar ese motor no te va a servir para la función que buscas, con motores reciclados de impresoras viejitas como los EM-257 incluso con los 42PM48L-01 de 7.5° o los M42SP-5 de 7.5° son perfectos para montar una maqueta, ya luego miras si necesitas mas torque y mas precisión con motores Nema 17 de 1.8° eso ya depende en robustes.

1. Para realizar tanto el giro del disco de los gobos como el paneo del espejo no te fíes solo en los topes mecánicos, si se quedan pegados por alguna razón habrá un sobrecalentamiento en el driver, la mejor solución es usar sensores Hall dejare un video muy sustancioso que lo explica básicamente.


También puedes implementarlos con ecoders ópticos, con imanes mecánicos de posición que cuando gira el motor hace saltos exactos de posición, también con sensor de barrera infrarroja, incluso una simple fotocelda, si te fías únicamente en que la programación si te va a dar la posición exacta pero como estas luces están en uso prologando en cualquier momento te fallara.

2. En cuanto al acople del motor si hubieras preguntado antes de comprar no te estaría pasando esto, así como @locodelafonola te explica para ello esta el datasheet del motor con un eje de 5mm, y debiste medir el acople del disco no se puede a ojimetro.

Veo que ese acople brida con diámetro interno tiene entre 10mm a 12mm, me corriges si estoy equivocada, si deseas un acople aproximado usa el siguiente acople rígido, pero haya tu 👇

1765286025163.png


Pero como te digo para tu proyecto no te va a servir esos motores y @torres.electronico lo comprobó incluso implementado librerías avanzadas no se va a lograr porque este tiene una caja reductora a menos que uses el viejo truco de cambiar la relación, es por ello que en comentario #42 explique que tipo de motores lleva las luces económicas, para las robustas y mas grandes hay que usar motores PAP de mas torque y tocaría implementar driver mas eficientes como los A4988 y sus derivados.

Cualquier duda es mejor preguntarle a un ser humano del Foro aquí hay expertos en la materia el profe @torres.electronico y @locodelafonola que con buena voluntad te colaboraran y guiaran.

La IA para cosas tangibles no es mala pero hay que saber consultar y saber aunque sea un poco de programación básica o de lo contrario estarás en limbo por horas y horas, y si le vas a dejar que la IA te haga tu proyecto te pondrá a trasnochar 😂
 
Última edición:
🤦 Usar la IA para esto es como intentar poner en un toma corriente dos cables pelados a oscuras... Quizás te sale bien y podes hacer la conexión de una, o quizás quedas pegado...
Cuando vuelva a mi casa voy a probar este ejemplo que econtre para un disco de gobos . Creo que bastaría con un conteo de gobos, dividirlos por la cantidad de pasos necesarios para una vuelta de 360° (2048) ,entonces obtendre el divisor exacto para colocar en la incognita...la distancia exacta entre gobo y gobo ( suponiendo que estén todos a la misma distancia)

#include <Stepper.h> // Incluye la librería Stepper

// Define los pines del driver ULN2003 conectados a Arduino (ej. 8, 9, 10, 11)
Stepper miMotor(2048, 8, 9, 10, 11); // 2048 pasos por revolución (ajusta si es necesario)

void setup() {
Serial.begin(9600);
miMotor.setSpeed(10); // Velocidad del motor en RPM (ajusta)
// pinMode(pines de señal) no es necesario con la librería Stepper
}

void loop() {
// Ejemplo: Mover al primer gobo (0 grados)
Serial.println("Mostrando Gobo 1");
miMotor.step(0); // O un número de pasos para posicionar
delay(1000); // Espera 1 segundo

// Ejemplo: Mover al segundo gobo (ej. 2048/3 pasos)
Serial.println("Mostrando Gobo 2");
miMotor.step(2048/3); // Avanza un tercio de vuelta
delay(1000);

// Ejemplo: Mover al tercer gobo (ej. 2*2048/3 pasos)
Serial.println("Mostrando Gobo 3");
miMotor.step(2048/3); // Avanza otro tercio
delay(1000);

// Regresar al inicio o al siguiente
Serial.println("Regresando al inicio");
miMotor.step(-2048/3 * 2); // Retrocede
delay(1000);
}
No es muy rápido pero para pasar de un gobo a otro tiene que alcanzar... Ánimo que ya estoy a medio camino

 
Cuando vuelva a mi casa voy a probar este ejemplo que econtre para un disco de gobos . Creo que bastaría con un conteo de gobos, dividirlos por la cantidad de pasos necesarios para una vuelta de 360° (2048) ,entonces obtendre el divisor exacto para colocar en la incognita...la distancia exacta entre gobo y gobo ( suponiendo que estén todos a la misma distancia)

#include <Stepper.h> // Incluye la librería Stepper

// Define los pines del driver ULN2003 conectados a Arduino (ej. 8, 9, 10, 11)
Stepper miMotor(2048, 8, 9, 10, 11); // 2048 pasos por revolución (ajusta si es necesario)

void setup() {
Serial.begin(9600);
miMotor.setSpeed(10); // Velocidad del motor en RPM (ajusta)
// pinMode(pines de señal) no es necesario con la librería Stepper
}

void loop() {
// Ejemplo: Mover al primer gobo (0 grados)
Serial.println("Mostrando Gobo 1");
miMotor.step(0); // O un número de pasos para posicionar
delay(1000); // Espera 1 segundo

// Ejemplo: Mover al segundo gobo (ej. 2048/3 pasos)
Serial.println("Mostrando Gobo 2");
miMotor.step(2048/3); // Avanza un tercio de vuelta
delay(1000);

// Ejemplo: Mover al tercer gobo (ej. 2*2048/3 pasos)
Serial.println("Mostrando Gobo 3");
miMotor.step(2048/3); // Avanza otro tercio
delay(1000);

// Regresar al inicio o al siguiente
Serial.println("Regresando al inicio");
miMotor.step(-2048/3 * 2); // Retrocede
delay(1000);
}
No es muy rápido pero para pasar de un gobo a otro tiene que alcanzar... Ánimo que ya estoy a medio camino

Al ver ese video, me la imaginé a Kitana con esa voz 🤣
Te va a quedar muy muy lento, pero está bueno que experimentes y veas las limitaciones
Salvo que encuentres algún stl para imprimir y que te haga la multiplicadora...
 
Al ver ese video, me la imaginé a Kitana con esa voz 🤣
Te va a quedar muy muy lento, pero está bueno que experimentes y veas las limitaciones
Salvo que encuentres algún stl para imprimir y que te haga la multiplicadora...
Mi paisano el Ingeniero Edison lo explica correctamente y casi me quedo dormida esperando quede una vuelta😁

Si señor estas en lo cierto hay que cambiar la multiplicadora en si solo es el piñón central y listo, pero no vale la pena bueno si es para formación didáctica ahí si vale la pena, porque casos se han visto la tortuguita venció a la liebre gruñona pues ni modos:ROFLMAO:


Caty Kanal es muy divertida su canal es chévere de electrónica básica, manualidades y cosplayer y ha tenido evolución, su acento es diferente, al igual que la ingeniera de mecatrónica Marisol de la UNAM mas conocida como Estudia Con Marisol ambas son de México.

Mi acento es mas Paisa y eso que no soy de haya, aunque cuando viví en Bogotá las niñas hablan super diferente y algo se me pego el famoso dicho todo bien :ROFLMAO:

 
Última edición:
Voy a tratar de colaborar porque veo que le pones ganas a tu proyecto y con IA no lo vas a solucionar mágicamente como lo dice el Ingeniero Torres, espero no ser un fantasma por aquí 😂

En primer lugar ese motor no te va a servir para la función que buscas, con motores reciclados de impresoras viejitas como los EM-257 incluso con los 42PM48L-01 de 7.5° o los M42SP-5 de 7.5° son perfectos para montar una maqueta, ya luego miras si necesitas mas torque y mas precisión con motores Nema 17 de 1.8° eso ya depende en robustes.

1. Para realizar tanto el giro del disco de los gobos como el paneo del espejo no te fíes solo en los topes mecánicos, si se quedan pegados por alguna razón habrá un sobrecalentamiento en el driver, la mejor solución es usar sensores Hall dejare un video muy sustancioso que lo explica básicamente.


También puedes implementarlos con ecoders ópticos, con imanes mecánicos de posición que cuando gira el motor hace saltos exactos de posición, también con sensor de barrera infrarroja, incluso una simple fotocelda, si te fías únicamente en que la programación si te va a dar la posición exacta pero como estas luces están en uso prologando en cualquier momento te fallara.

2. En cuanto al acople del motor si hubieras preguntado antes de comprar no te estaría pasando esto, así como @locodelafonola te explica para ello esta el datasheet del motor con un eje de 5mm, y debiste medir el acople del disco no se puede a ojimetro.

Veo que ese acople brida con diámetro interno tiene entre 10mm a 12mm, me corriges si estoy equivocada, si deseas un acople aproximado usa el siguiente acople rígido, pero haya tu 👇

Ver el archivo adjunto 334747


Pero como te digo para tu proyecto no te va a servir esos motores y @torres.electronico lo comprobó incluso implementado librerías avanzadas no se va a lograr porque este tiene una caja reductora a menos que uses el viejo truco de cambiar la relación, es por ello que en comentario #42 explique que tipo de motores lleva las luces económicas, para las robustas y mas grandes hay que usar motores PAP de mas torque y tocaría implementar driver mas eficientes como los A4988 y sus derivados.

Cualquier duda es mejor preguntarle a un ser humano del Foro aquí hay expertos en la materia el profe @torres.electronico y @locodelafonola que con buena voluntad te colaboraran y guiaran.

La IA para cosas tangibles no es mala pero hay que saber consultar y saber aunque sea un poco de programación básica o de lo contrario estarás en limbo por horas y horas, y si le vas a dejar que la IA te haga tu proyecto te pondrá a trasnochar 😂
Cuando llegue a mi casa voy a medir bien el interior con un calibre . Entiendo que esas piezas metálicas son las que van. Pero no nos olvidemos que son 4 bichos y se genera un gasto enorme. He probado con una varilla de teflón pero al faltarme la guia para hacer un agujero centrado y perpendicular se me tuerce un poquito . Veré si puedo inventarme algo de guía o recurriré a las soluciones dadas en los comentarios anterior. En cuanto a los motores DC no los voy a tocar. Son los mejorcito que tengo. ( Lo único que funciona ahora ja) Es más me sobrarían 3 motores de los gobos que reemplazo por paso a paso ..que los pienso usar para hacer algún otro proyecto
 
Cuando llegue a mi casa voy a medir bien el interior con un calibre . Entiendo que esas piezas metálicas son las que van. Pero no nos olvidemos que son 4 bichos y se genera un gasto enorme. He probado con una varilla de teflón pero al faltarme la guia para hacer un agujero centrado y perpendicular se me tuerce un poquito . Veré si puedo inventarme algo de guía o recurriré a las soluciones dadas en los comentarios anterior. En cuanto a los motores DC no los voy a tocar. Son los mejorcito que tengo. ( Lo único que funciona ahora ja) Es más me sobrarían 3 motores de los gobos que reemplazo por paso a paso ..que los pienso usar para hacer algún otro proyecto
Si no los consigues de los que te indico, también hay acoples de PVC muy económicos para manguera de acuarios o similares los encuentras al escoger de todos los diámetros.

1765292735948.png
1765293626738.png


Bueno si vas a dejar los DC para paneo no es mala idea porque si ees cierto te sale mas costoso ya lo había comentado, intenta con los 28byj-48 pero como te digo los PAP de impresoras viejitas son ultra económicos y te van a dar el efecto que buscas, a lo mejor para canciones lentas van bien pero para música mas rápida no te van a dar el efecto prueba y luego nos cuentas.
 
Última edición:
Atrás
Arriba