Motor paso a paso y celda de carga en funcionamiento al mismo tiempo

Buenas Tardes,

Estoy trabajando en un pequeño proyecto que consiste en apretar una uva (mediante un mecanismo de tornillo sin fin que esta accionado con un motor paso a paso) contra una celda de carga (estoy usando el ampilificador HX711)
durante todo el proceso, necesito leer el valor de la celda de carga continuamente hasta que explote la uva, para mas tarde dibujar una curva del comportamiento de la uva.

He utilizado la librería PROTOTHREADS para simular un funcionamiento en "Paralelo" del motor paso a paso girando, mientras registro los datos de la celda de carga cada 10 milisegundos.

Os dejo el código que he realizado:

#include <pt.h>
#include "HX711.h"
#include <AccelStepper.h>

AccelStepper stepper_motor(1, 10, 9);



struct pt hilo1;
struct pt hilo2;
struct pt hilo3;

HX711 scale(6, 5); //HX711 scale(6, 5);
float calibration_factor = -443;

void setup() {
  Serial.begin(9600);
  Serial.println("HX711 weighing");
  scale.set_scale(calibration_factor);
  scale.tare();
  Serial.println("Lecturas:");

  stepper_motor.setSpeed(3200);
  stepper_motor.setAcceleration(800);
  stepper_motor.setMaxSpeed(6400);
  
  PT_INIT(&hilo1);
  PT_INIT(&hilo2);

}

void loop() {
  lectura(&hilo1);
  bajar(&hilo2);


}

void lectura(struct pt *pt){
  
    PT_BEGIN(pt);
    
    float units;
    float ounces;

    static long t = 0;

  do {
    Serial.print("Leyendo:");
    units = scale.get_units(),10;
    if (units < 0)
    {
      units = 0.00;
    }
    ounces = units * 0.035274;
    Serial.print(units);
    Serial.println(" gramos");
    t = millis();
    PT_WAIT_WHILE(pt, (millis()-t)<10);
   } while(true);
   
  PT_END(pt);
}
void bajar(struct pt *pt){
  
    PT_BEGIN(pt);

   static long t = 0;

  //do {
    stepper_motor.moveTo(-4200);
    stepper_motor.run();
    t = millis();
    PT_WAIT_WHILE(pt, (millis()-t)<1);
   //} while(true);
   
  PT_END(pt);
}

En principio he conseguido que funcione, o por lo menos que cumpla la necesidad que tengo.

Mi problema ahora, es que la velocidad del motor paso a paso, es demasiado lenta, y por mucho que suba el valor de:

stepper_motor.setSpeed(3200);

no consigo aumentar la velocidad de rotación del motor.

Os estaré muy agradecido por cualquier sugerencia o idea. Gracias de antemano.

Bueno olvidemos el objetivo y concentrémosnos en el movimiento de tu tornillo sin fin.
Que motor usas, que driver, intenta dar mas datos.

Estoy usando un motor nema 23, y el driver DM556T

Estoy observando que cuando hago cada proceso por separado (y no al mismo tiempo) el motor me funciona perfectamente, pero cuando ambas acciones al mismo tiempo, el motor gira con un pequeño "clack" cada paso que hace.

Vas a tener que darle cierta prioridad al motor.
No se porque usas PROTOTHREADS, tranquilamente con una Maquina de Estados y millis() resuelves todo sin problema.

EDITO: ya se cual es el problema.
El manejo del driver DM556T.
Ese driver solo requiere que tu le envies PULSOS pero no con la libreria que estas usando, por eso se mueve lento, tmb es posible que se mueva lento por el seteo de los dip switch que tiene a un costado.

Debes hacerlo con un simple togle de un pin, un pin que va a high y low a la frecuencia que gustes mientras la controles.

Si controlas direccion y ENABLE pero el avances es subiendo y bajando un pin. Esta pensado para PLC y funciona bien.
Prueba con un simple for() y dentro coloca el digitalWrite(pin, HIGH) y luego digitalWrite(pin, LOW) y veras de lo que te hablo.

Te dejo un programa cambia los pines según tu configuración
No uses 1 que es para el monitor serie.

const byte PUL=7; //Pin para la señal de pulso
const byte DIR=6; //define pin Direccion
const byte EN=5;   //define Enable Pin
void setup() {
  pinMode (PUL, OUTPUT);
  pinMode (DIR, OUTPUT);
  pinMode (EN, OUTPUT);
  digitalWrite(EN,HIGH);

}

void loop() {
 
  digitalWrite(DIR,LOW);
  for (int i=0; i<1600; i++) {   // adelante 1600 pasos
      digitalWrite(PUL,HIGH);
      delayMicroseconds(400);
      digitalWrite(PUL,LOW);
      delayMicroseconds(400);
  }
  delay(100);
  digitalWrite(DIR,HIGH);
 
  for (int i=0; i<1600; i++) {  // Atras 1600 pasos
      digitalWrite(PUL,HIGH);
      delayMicroseconds(400);
      digitalWrite(PUL,LOW);
      delayMicroseconds(400);
  }
}

Buenos días surbyte, acabo de probar tu código, y el motor gira perfectamente con un movimiento continuo y sin pausas.

Mis problemas surgen cuando quiero hacer los dos procesos simultáneamente, mis motivos de usar la librería PROTOTHREADS es su cómodo funcionamiento, pero me temo que falla por el motivo de

delayMicroseconds(400);

He leído información sobre el tema de maquinas de estados, y sinceramente no se como aplicarlo a este proyecto, soy un simple técnico mecánico y mis conocimientos de programación son mínimos.

He aplicado la función millis(), consigo que funcionen los 2 procesos, pero el motor primero va lento por que la pausa entre pulsos es de 1 milisegundo, y sigue apareciendo esa micropausa entre paso y paso del motor, dejo el nuevo código que aplico:

#include <pt.h>
#include "HX711.h"

struct pt hilo1;
struct pt hilo2;

HX711 scale(6, 5); //HX711 scale(6, 5);
float calibration_factor = -443;

const int enPin1 = 8;
const int dirPin1 = 9;
const int stepPin1 = 10;
 
const int steps1 = 200;

void setup() {
  Serial.begin(9600);
  Serial.println("HX711 weighing");
  scale.set_scale(calibration_factor);
  Serial.println("Lecturas:");
  digitalWrite(enPin1,HIGH);
  PT_INIT(&hilo1);
  PT_INIT(&hilo2);
}

void loop() {
  lectura(&hilo1);
  bajar(&hilo2);
}

void lectura(struct pt *pt){
  
    PT_BEGIN(pt);
    
    float units;
    float ounces;
    scale.tare();
    static long t = 0;
    
  do {
    Serial.print("Leyendo:");
    units = scale.get_units(),10;
    if (units < 0)
    {
      units = 0.00;
    }
    ounces = units * 0.035274;
    Serial.print(units);
    Serial.println(" gramos");
    t = millis();
    PT_WAIT_WHILE(pt, (millis()-t)<10);
   } while(true);
   
  PT_END(pt);
}

void bajar(struct pt *pt){
  
  PT_BEGIN(pt);

   static long t = 0;
   digitalWrite(dirPin1, LOW);
 
   for (int x = 0; x < 1600; x++) {
      digitalWrite(stepPin1, HIGH);
    t = millis();
    PT_WAIT_WHILE(pt, (millis()-t)<1);
      digitalWrite(stepPin1, LOW);
    t = millis();
    PT_WAIT_UNTIL(pt, (millis()-t)>=1);
   }

  PT_END(pt);
}

Si, el anterior fue un código de prueba para que vieras si el driver movía bien el motor NEMA 23. Ya sabemos que lo hace bien, entonces probemos otra manera.

Estoy sorprendido de que ph.h escrita en 2006 siga funcionando. Aplausos para su programador!!!

Mas alla de eso necesitas una máquina de estados veloz o bien generar pulsos con un timer independientemente del funcionamiento del código pero donde puedas controlar los pulsos enviados. Asi que vamos a ver de menor a mayor complejidad que se puede hacer.

No puedo probar el código porque no se que librería HX711 usas. Siempre cuando programes y pidas ayuda en un foro coloca a la derecha de cada librería un comentario con el enlace de donde la descargaste. Si es con el administrador de librerías pones exactamente su nombre y versión. Eso ayuda mucho.

Este es el código no probado.

#include "HX711.h"


HX711 scale(6, 5); //HX711 scale(6, 5);
float calibration_factor = -443;

const int enPin1 = 8;
const int dirPin1 = 9;
const int stepPin1 = 10;
 
const int steps1 = 200;
unsigned int pasos = 0; // no puedes superar 65535 pasos o dará overflow o desborde
unsigned long start;
 
void setup() {
  Serial.begin(9600);
  Serial.println("HX711 weighing");
  scale.set_scale(calibration_factor);
  Serial.println("Lecturas:");
  digitalWrite(enPin1,HIGH);
}

void loop() {
  lectura();
  bajar();
}

void lectura(){
   
    float units;
    float ounces;
    scale.tare();
    static long t = 0;
   
    Serial.print("Leyendo:");
    units = scale.get_units(),10;
    if (units < 0.0) {
        units = 0.00;
    }
    ounces = units * 0.035274;
    Serial.print(String(units)+" gramos"); // simplifico lecturas
}

void bajar(){
   digitalWrite(dirPin1, LOW);
   switch(estado) {
        case 0: digitalWrite(stepPin1, HIGH);
                estado = 1;
                start = micros();
                break;
        case 1: if (micros() - start > 400UL) {
                    digitalWrite(stepPin1, LOW);
                    estado = 2;
                    start = micros();
                }
                break;
        case 2: if (micros() - start > 400UL) {
                    if (pasos++ > = 1600){
                        // me detengo. Ahora pongo un cartel pero debería haber una condición con la medición
                        // que lo haga.
                        // el salto abrupto de presión sería esa condición.
                        while(1) {
                          Serial.println("Debi haber llegado al extremo del tornillo sin fin");
                        }
                    }
                estado = 0;       // vuelvo a comenzar el ciclo
                }
                break;
   }
}

Muchas gracias por tu respuesta surbyte,

Acabo de añadir el nombre y el lugar de donde he instalado la librería HX711.

He subido el código al Arduino, funciona la celda de carga, pero el motor no se mueve en absoluto.

#include "HX711.h" //Libreria instalada desde el administrador de bibliotecas de Arduino
                   //HX711 Arduino Library, by Bogdan Necula, Andreas Motl, Version 0.7.2


HX711 scale(6, 5); //HX711 scale(6, 5);
float calibration_factor = -443;

const int enPin1 = 8;
const int dirPin1 = 9;
const int stepPin1 = 10;
 
const int steps1 = 200;
unsigned int pasos = 0; // no puedes superar 65535 pasos o dará overflow o desborde
unsigned long start;
int estado = 0;
 
void setup() {
  Serial.begin(9600);
  Serial.println("HX711 weighing");
  scale.set_scale(calibration_factor);
  Serial.println("Lecturas:");
  digitalWrite(enPin1,HIGH);
}

void loop() {
  lectura();
  bajar();
}

void lectura(){
   
    float units;
    float ounces;
    scale.tare();
    static long t = 0;
   
    Serial.print("Leyendo:");
    units = scale.get_units(),10;
    if (units < 0.0) {
        units = 0.00;
    }
    ounces = units * 0.035274;
    Serial.println(String(units)+" gramos"); // simplifico lecturas
}

void bajar(){
   digitalWrite(dirPin1, LOW);
   switch(estado) {
        case 0: digitalWrite(stepPin1, HIGH);
                estado = 1;
                start = micros();
                break;
        case 1: if (micros() - start > 400UL) {
                    digitalWrite(stepPin1, LOW);
                    estado = 2;
                    start = micros();
                }
                break;
        case 2: if (micros() - start > 400UL) {
                    if (pasos++ >= 1600){
                        // me detengo. Ahora pongo un cartel pero debería haber una condición con la medición
                        // que lo haga.
                        // el salto abrupto de presión sería esa condición.
                        while(1) {
                          Serial.println("Debi haber llegado al extremo del tornillo sin fin");
                        }
                    }
                estado = 0;       // vuelvo a comenzar el ciclo
                }
                break;
   }
}

Ya veo mi error.
Estamos leyendo muchas veces la celda de carga.
Cada cuantos pasos deberiamos leer la celda de carga?

Corrígeme si estoy equivocado, cada 10 mseg pero 10 mseg no asegura que el HX711 termine la conversión en ese lapso.
No hemos medido cuanto tarda el HX711 en hacerlo y dudo que sea asi de rápido. Si mal recuerdo tiene una velocidad rápida de lectura.
Eso conspira con el adelantamiento pero será una pausa imperceptible.

Si me permites una sugerencia yo no presentaría los datos hasta que se logre que la uva explote.
Entonces si presento todo.
Mientras que hago, los guardo en RAM si no son muchos claro esta.

Nueva pregunta, cuantos datos presenta el sistema?

Voy a corregir el error de leer muy seguido la celda y restringirla cada INTERVALO en mseg con un tiempo que tu podras cambiar.
Mira la hoja de datos del HX711

Dice que 80Hz es la máxima frecuencia de Sampleo. Eso es 1/80 seg = 12.5 mseg
Entonces si ademas le agregas impresiones, te pasas facilmente de digamos 20 (hay que medirlo)

Asi que hay que considerar que hacer. Medir y tomar decisiones y mostrar cuando corresponda o bueno.

Comienza con 25 a 50 mseg en INTERVALO y a ver como va.

Dejo el codigo con 50UL
Puse la librería del autor que indicas y sigue fallando

#include <EEPROM.h>

#include "HX711.h"

#define INTERVALO   50UL     // son 10 milisegundos pero me parece muy pequeño

HX711 scale(6, 5); //HX711 scale(6, 5);
float calibration_factor = -443;

const int enPin1 = 8;
const int dirPin1 = 9;
const int stepPin1 = 10;
 
const int steps1 = 200;
unsigned int pasos = 0; // no puedes superar 65535 pasos o dará overflow o desborde
unsigned long start, muestrolectura;
 
void setup() {
  Serial.begin(9600);
  Serial.println("HX711 weighing");
  scale.set_scale(calibration_factor);
  Serial.println("Lecturas:");
  digitalWrite(enPin1,HIGH);
}

void loop() {
  if (millis - muestrolectura > INTERVALO){
      lectura();
      timelectura = millis();
  }
  bajar();
}

void lectura(){
   
    float units;
    float ounces;
    scale.tare();
    static long t = 0;
   
    Serial.print("Leyendo:");
    units = scale.get_units(),10;
    if (units < 0.0) {
        units = 0.00;
    }
    ounces = units * 0.035274;
    Serial.print(String(units)+" gramos"); // simplifico lecturas
}

void bajar(){
   digitalWrite(dirPin1, LOW);
   switch(estado) {
        case 0: digitalWrite(stepPin1, HIGH);
                estado = 1;
                start = micros();
                break;
        case 1: if (micros() - start > 400UL) {
                    digitalWrite(stepPin1, LOW);
                    estado = 2;
                    start = micros();
                }
                break;
        case 2: if (micros() - start > 400UL) {
                    if (pasos++ > = 1600){
                        // me detengo. Ahora pongo un cartel pero debería haber una condición con la medición
                        // que lo haga.
                        // el salto abrupto de presión sería esa condición.
                        while(1) {
                          Serial.println("Debi haber llegado al extremo del tornillo sin fin");
                        }
                    }
                estado = 0;       // vuelvo a comenzar el ciclo
                }
                break;
   }
}

Para después tener una buena curva con los datos obtenidos, debería obtener un registro cada 10 milisegundos

Buenos días surbyte, muchas gracias de nuevo por tu respuesta, me parece interesante la frecuencia del HX711, no sabia era tan lento, considerare en buscar otro hardware para esta solución, porque es necesario que la curva sea los mas detallada posible para su posterior estudio.

He probado tu nuevo código con intervalos de 50 milisegundos, pero el motor sigue sin hacer absolutamente nada.

Dejo el código que compila bien por si alguien le puede interesar:

#include <EEPROM.h>

#include "HX711.h"

#define INTERVALO   50UL     // son 10 milisegundos pero me parece muy pequeño

HX711 scale(6, 5); //HX711 scale(6, 5);
float calibration_factor = -443;

const int enPin1 = 8;
const int dirPin1 = 9;
const int stepPin1 = 10;
 
const int steps1 = 200;
unsigned int pasos = 0; // no puedes superar 65535 pasos o dará overflow o desborde
unsigned long start, muestrolectura;
int estado = 0;
 
void setup() {
  Serial.begin(9600);
  Serial.println("HX711 weighing");
  scale.set_scale(calibration_factor);
  Serial.println("Lecturas:");
  digitalWrite(enPin1,HIGH);
}

void loop() {
  if (millis - muestrolectura > INTERVALO){
      lectura();
      muestrolectura = millis();
  }
  bajar();
}

void lectura(){
   
    float units;
    float ounces;
    scale.tare();
    static long t = 0;
   
    Serial.print("Leyendo:");
    units = scale.get_units(),10;
    if (units < 0.0) {
        units = 0.00;
    }
    ounces = units * 0.035274;
    Serial.println(String(units)+" gramos"); // simplifico lecturas
}

void bajar(){
   digitalWrite(dirPin1, LOW);
   switch(estado) {
        case 0: digitalWrite(stepPin1, HIGH);
                estado = 1;
                start = micros();
                break;
        case 1: if (micros() - start > 400UL) {
                    digitalWrite(stepPin1, LOW);
                    estado = 2;
                    start = micros();
                }
                break;
        case 2: if (micros() - start > 400UL) {
                    if (pasos++ >= 1600){
                        // me detengo. Ahora pongo un cartel pero debería haber una condición con la medición
                        // que lo haga.
                        // el salto abrupto de presión sería esa condición.
                        while(1) {
                          Serial.println("Debi haber llegado al extremo del tornillo sin fin");
                        }
                    }
                estado = 0;       // vuelvo a comenzar el ciclo
                }
                break;
   }
}

,

considerare en buscar otro hardware para esta solución, porque es necesario que la curva sea los mas detallada posible para su posterior estudio.

No vas a encontrar muchas opciones a ese precio.

12.5 mseg por muestra esta solo 2.5mseg por encima de lo que suponías. No cambia mucho.

Bueno, hay una alternativa mas rapida que samplea a 1mseg 1000Hz y es esta Load Cell / Wheatstone Amplifier Shield

Tambien tienes estos sensores capacitivos muy veloces link
Te dejo mas links

Gracias de nuevo surbyte, me parece muy interesante el AD8426, y tiene un precio bastante bajo.

Si, estoy de acuerdo que el AD8426 es una solución al poblema de tu dinamómetro de compresión.
También he leído este documento en el que se caracterízan las velocidades del tornillo sin fin a 80 mm/min o sea 1.6mm/min para el ensayo destructivo.
En la página 3 Fig 2 se ve el ensayo destructuvo de la textura de la uva a 1.6mm x seg o sea 1600 um x mseg que permitiría el nuevo shiled.
También se ve la caida abrupta de la presión de 6.5 a 5 N. Esta variacion de presion debería ser tu preparación (ahora me corrijo) para ir deteniendo la prueba, mas allá que será visible para quien la realice.

Realmente interesante el estudio :slight_smile: , no había visto ese articulo, se de gran ayuda para mi estudio sin duda alguna, muchísimas gracias por tu aportación surbyte.

Ahora pues estoy investigando la forma de como poder manejar el motor nema y registrar los datos de la celda de carga simultáneamente, seguiré usando el HX711 a nivel experimental hasta solucionar dicho problema, y posteriormente sustituirlo por el AD8426.

Como te dije, si tienes RAM la solución es guardar los datos en un vector, que a intervalos predeterminados digamos 12.5mseg del HX711 a 80Hz tome un dato de presión.
Haces eso y no comprometes nada.

Segun el código para hacer funcionar el driver debes hacer un toggle el pulso cada 400useg x 2 o sea 800uSeg. quiere decir que cada 3 medio pulsos deberias tener una lectura del HX711.
Deberíamos ver la forma de leer el HX711 sin bloquear el funcionamiento del driver.

Posible solución, librería y ejemplo No Bloqueante Non Blocking HX711 Arduino Library
El pin 15 del HX711 controla la velocidad de sampleo. GND para 10 hz y VCC para 80Hz.
Entonces hay que poner a VCC dicho pin.

#include <NBHX711.h>
NBHX711 hx711(A2, A3);
void setup() {
  Serial.begin(9600);
}

void loop() {
  static unsigned long lastRead;
  hx711.update();
  if (millis() - lastRead >= 500) {  // por supuesto 500 mseg esta fuera de toda lógica.
    lastRead = millis();
    Serial.println(hx711.getRaw());
  }
}

Para terminar encontré otra librería no bloqueante y tiene un intersante ejemplo

Librería 2 No Bloqueando HX711

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.