My all format scanner

Here we’re really getting into the technical side, and my English might make me misunderstand or express myself poorly.
I think that if I want to be at least a bit clear, I need to show you how my scanner is designed:

Here is the wiring of the box at the back, which provides power and acts as a “hub” for the motors, lighting, ventilation, and the laser:
Schematic_Schéma de branchement pour boitier arrière_2023-07-30.pdf (55.3 KB)

Then there is the control box, which manages and coordinates all of these components:
Schematic_Schéma pour boitier de commande avant_2023-07-30.pdf (78.2 KB)

And finally, there is the program that runs on the Arduino, which brings us back to your question:

Basically, each perforation sends a pulse to the Teensy, each pulse triggers this method, and the regulation is carried out with this code, each time a new pulse is received:


// === CAPTEUR LASER ===
void checkPerf() {
digitalWrite(CamShoot, HIGH);
timerDelayCam.begin(temporisateurCam, 200);

// Mise à jour de la durée du flash selon le potentiomètre
flashDuration = map(analogRead(potFlashDuration), 0, 1023, 50, 1500);

// Calcul du temps écoulé entre deux images
endTime = millis();
frameInterval = endTime - startTime;
startTime = endTime;

// Calcul du FPS actuel et envoi au filtre de lissage + affichage
float currentFPS = (frameInterval > 0) ? (1000.0 / frameInterval) : 0;
computeSmoothedFPS(currentFPS,false);

// Régulation de la vitesse moteur en fonction de l’erreur sur l’intervalle cible
float targetInterval = (imagesPerSecond > 0) ? (1000.0 / imagesPerSecond) : 0;
float error = frameInterval - targetInterval;
float correction = computeCorrection(error);

currentMotorSpeed += correction;
currentMotorSpeed = constrain(currentMotorSpeed, 50, 10000);
if (targetInterval == 0) currentMotorSpeed = 0;

// Application de la vitesse aux moteurs selon l’état courant
if (currentState == REWIND) {
stepperG.spin(currentMotorSpeed * getInvert());
} else if (currentState == FORWARD) {
stepperD.spin(currentMotorSpeed * getInvert());
}

// Gestion du flash et de la caméra
digitalWrite(Light, HIGH);
}

void temporisateurCam() {
digitalWrite(CamShoot, LOW);
timerflashDuration.begin(timerflash, flashDuration);
timerDelayCam.end();
}

void timerflash() {
digitalWrite(Light, LOW);
timerflashDuration.end();
}

float computeCorrection(float error) {
float Kp = 0.40;
float Ki = 0.02;

if (abs(error) > 0.5) integral += error;
integral = constrain(integral, -50, 50);

float correction = Kp * error + Ki * integral;
return constrain(correction, -120, 120);
}

float computeSmoothedFPS(float newFPS, bool reset){
static const int bufferSize = 10;
static float buffer[bufferSize];
static int index = 0;
static bool filled = false;

// Si reset demandé, vider le buffer et repartir de zéro
if (reset) {
for (int i = 0; i < bufferSize; i++) buffer[i] = 0;
index = 0;
filled = false;
return 0;
}

// Ajouter la valeur seulement si elle est dans une plage raisonnable
if (newFPS > 0 && newFPS < 60) {
buffer[index++] = newFPS;
if (index >= bufferSize) {
index = 0;
filled = true;
}
} else {
// logMessage("[DEBUG] Valeur FPS rejetée : " + String(newFPS)); // Optionnel pour debug
}

// Si le buffer n’est pas encore rempli, ne rien afficher
if (!filled) {
return 0;
}

// Calcul de la moyenne
float sum = 0;
for (int i = 0; i < bufferSize; i++) {
sum += buffer[i];
}
float average = sum / bufferSize;

// Affichage toutes les 500 ms
static unsigned long lastPrint = 0;
if (millis() - lastPrint >= 500) {
logMessage(“[FROM_SCAN_INFO] fsp actuel : " + String(average, 1) + " img/s”);
lastPrint = millis();
}

return average;
}

Tell me if this answers your question.

1 Like