MECCANICA e DINTORNI

COSTRUIAMO LE NOSTRE CNC DIVERTENDOCI CON L'AIUTO DI TANTI PROFESSIONISTI ESPERTI
Oggi è mar mar 19, 2024 07:22

Tutti gli orari sono UTC +1 ora




Apri un nuovo argomento Rispondi all’argomento  [ 7 messaggi ] 
Autore Messaggio
 Oggetto del messaggio: Driver per controllo remoto Wabeco
MessaggioInviato: sab mar 07, 2020 11:27 
Non connesso
APPRENDISTA E ADDETTO ALLE PULIZIE
Avatar utente

Iscritto il: dom ott 01, 2017 18:12
Messaggi: 9
Località: Bologna
Ciao a tutti,
vorrei condividere un driver che ho scritto e che potrebbe essere utile ad altri.
Serve a controllare, da LinuxCNC, il motore principale di una fresa o un tornio Wabeco nella versione 'hs', ovvero equipaggiati di un motore da 2kW invece che quello standard da 1.4kW.
Più in generale, per controllare un Varicon ("Three-Phase Motor with Integrated Frequency Converter") della ditta Hanning Elektro-Werke GmbH, che poi sarebbe il motore che entrambe le macchine della Wabeco montano.

Premessa:
un paio di anni fa ho acquistato una fresa e un tornio Wabeco, entrambi nella versione 'hs'.
Dopo aver costruito un sistema per il controllo degli assi per poter utilizzare LinuxCNC (entrambe le macchine erano già dotate di motori passo) e aver installato sul tornio un decoder sull'asse del mandrino (questi potrebbero essere argomenti di altri post; se qualcuno è interessato mi faccia sapere), mi mancava solo realizzare il controllo del motore principale.

Il motore Hanning Varicon è dotato di un box di controllo che prevede un deviatore per la selezione del movimento (CW o CCW) e di un potenziometro per l'impostazione della velocità di rotazione.
Dal manuale delle macchine ho visto che il controllo della velocità veniva fatto con una tensione da 0 a 10 volt e del movimento con un paio di segnali a 24 volt. Questo però mi avrebbe imposto l'uso di relè e cablaggi a vari fili per selezionare da PC la macchina da controllare e per scegliere la modalità di controllo, manuale o da programma. Una vera scocciatura per un pigro come me!

Aprendo il box ho notato con piacere la presenza di un connettore che suggeriva la possibilità di collegamenti esterni.
Allegato:
Box prima.jpg
Ho scritto alla Wabeco che mi ha gentilmente risposto che non aveva niente di più dello schema già presente nei manuali xXx... Allora mi sono messo alla ricerca su Internet e alla fine ho trovato un pdf con le istruzioni:
https://www.cncitalia.net/forum/www.gather-industrie.fr/wp-content/uploads/2015/09/Varicon-1.2-BA-OI_65158-NEUE-BAUREIHE-08_2007-engl.pdf
da cui ho visto che il motore può essere controllato anche tramite una linea seriale RS485! :grin:

La ricerca si è orientata quindi a reperire qualcosa di già fatto. Purtroppo con nessun esito!
Gli unici driver HAL seriali che si trovano per LinuxCNC sono per variatori di velocità con controllo ModBus.
A questo punto ho preso la decisione di scrivermi il driver che mi serviva. Ho selezionato il driver esistente che più si avvicinava alle mie esigenze, ovvero 'Mitsub VFD Driver' per controllo di VFD Mitsubishi, scritto in Python nel 2017 da Chris Morley: http://linuxcnc.org/docs/html/drivers/mitsub_vfd.html e l'ho modificato per adattarsi al protocollo di comunicazione utilizzato dal Varicon.

Il collegamento fisico l'ho realizzato utilizzando un semplice cavetto bipolare schermato e un adattatore USB seriale RS485 della Moxa (UPort 1130), mettendo in parallelo le due macchine in modalità 'RS485 a 2 fili'. Fortunatamente i box presenti sulle macchine prevedono dei 'tappi' per uscite di cavi ausiliari, che ho prontamente utilizzato!
Allegato:
Box dopo.jpg
Il drive, scritto in Python, mette a disposizione una serie di 'pin' (do per scontato una conoscenza base dell'HAL di LinuxCNC) di input e di output che permettono il controllo del motore aggiungendo alcune linee al file <xxx>.hal della configurazione utilizzata.

Questo è l'elenco dei pin del driver:
Codice:
  Nome pin          Tipo   Dir    Significato
  ----------------  -----  ----   -------------------------------------------------------------
  fwd               BIT    IN     Impostare a True per rotazione destrorsa
  run               BIT    IN     Impostare a True per avviare il motore
  debug             BIT    IN     Impostare a True per abilitare i messaggi diagnostici
  enabled           BIT    IN     Impostare a True per abilitare il controllo via RS485 (Reg 000, 001 and 002)
  monitor           BIT    IN     Impostare a True per abilitare l'aggiornamento continuo dello stato
  motion-reversed   BIT    IN     Impostare a True se la rotazione del motore e' invertita (es. nella fresa Wabeco)
  speed-scale       FLOAT  IN     Configurare questo valore per impostare il rapporto di trasmissione meccanico
  motor-speed-rpm   FLOAT  IN     Modificare questo valore per impostare la velocita' desiderata, in RPM
  motor-fb-rpm      FLOAT  OUT    Velocita' attuale del motore, in RPM
  motor-fb-rps      FLOAT  OUT    Velocita' attuale del motore, in RPS
  motor-amps        FLOAT  OUT    Valore della corrente di assorbimento del motore, in ampere
  motor-amps-enab   BIT    IN     Impostare a True per aggiornare il valore di 'motor-amps' (quando anche 'monitor' e' True)
  estop             BIT    IN     Impostare a True per eseguire uno stop di emergenza
  error-code        U32    OUT    Valore del codice di errore, quando 'sts-gen-error' e' True
  sts-temp-warn     BIT    OUT    Avviso di temperatura: 10'C o meno sotto il limite di blocco
  sts-temp-rise     BIT    OUT    Temperatura dell'inverter in aumento
  sts-brake-stop    BIT    OUT    Freno motore disattivato (tensione troppo elevata)
  sts-chopper-on    BIT    OUT    Chopper attivo
  sts-enabled       BIT    OUT    Abilitazione negata (pin #14 del connettore a 0)
  sts-clkpuls-on    BIT    OUT    Impulsi di clock attivi
  sts-gen-error     BIT    OUT    Errore generale (vedi 'error-code')
  sts-standstill    BIT    OUT    Frequenza 0 / motore fermo
  sts-speed-ok      BIT    OUT    Velocità impostata raggiunta
  sts-rot-ccw       BIT    OUT    Direzione di rotazione sinistrorsa (negativa)
  sts-pwr-115v      BIT    OUT    Alimentazione principale a 115 V
  sts-pwr-400v      BIT    OUT    Alimentazione principale a 400 V
Per esempio, il mio file fresa.hal contiene le seguenti linee:
Codice:
  loadusr -Wn spindle hanning_vfd.py -p /dev/ttyUSB0 spindle=32

  # ********** mill spindle vfd setup (slave 32) *************
  net  spindle-vel-cmd-rpm-abs   <=  motion.spindle-speed-out-abs
  net  spindle-vel-cmd-rpm-abs    => spindle.motor-speed-rpm
  net  spindle-enable            <=  motion.spindle-on
  net  spindle-enable             => spindle.run
  net  spindle-cw                <=  motion.spindle-forward
  net  spindle-cw                 => spindle.fwd
  net  spindle-at-speed          <=  spindle.sts-speed-ok
  net  spindle-at-speed           => motion.spindle-at-speed
  net  spindle-speed-rps         <=  spindle.motor-fb-rps
  net  spindle-speed-rps          => motion.spindle-speed-in
  net  machine-is-enabled        <=  motion.motion-enabled
  net  machine-is-enabled         => spindle.monitor
  net  machine-is-enabled         => spindle.enabled
  net  estop-out                 <=  iocontrol.0.user-enable-out
  net  estop-out                  => iocontrol.0.emc-enable-in
  net  estop-out                  => spindle.estop
  net  spindle-error-condition   <=  spindle.sts-gen-error
  net  spindle-error-code        <=  spindle.error-code
  net  spindle-current           <=  spindle.motor-amps

  # --- config mill spindle pins ---
  setp spindle.speed-scale        1.46154    # 38/26
  setp spindle.debug              0
  setp spindle.motor-amps-enab    0
  setp spindle.motion-reversed    1
Per installare il driver si procede come segue:

Hardware:

    > procurarsi un cavo coassiale a due fili + calza, tipo quello usati per i microfoni lungo a sufficenza per collegare il Varicon al PC e un connettore femmina volante DB9 a vaschetta;

    > saldare quello che sarà il filo del segnale A (il positivo dell'RS485) al terminale 3, il filo del segnale B (negativo) al 4 e la calza al 5;

    > aprire il box del varicon e infilare l'altro capo del cavo coassiale in uno dei fori ausiliari previsti nel box, quindi collegare il filo del segnale A al terminale 1 del connettore grigio a 15 posizioni, il segnale B al terminale 2 e la calza al terminale 6.

    NOTA: in teoria un collegamento RS485 dovrebbe essere terminato alle sue estremità su una resistenza da 120 ohm. Se il collegamento non è troppo lungo si può anche evitare di farlo; se invece volete essere tranquilli si può inserire una resistenza da 120 ohm tra i terminali 1 e 2 dentro al box. Io ho preferito metterla in quanto ho collegato le due macchine tra loro con un cavo non tanto corto (vedi foto)

    > richiudere il tutto facendo passare il cavo dove dia fastidio per collegarlo all'adattatore RS485 USB.

Software:

    > verificare, se si è editato il file del driver sotto Windows, che il formato degli 'a capo' sia quello Unix, cioè solo un LF a fine linea e non CR+LF usato da Windows;

    > copiare il file in una directory inclusa nel PATH; io ho usato /usr/local/bin. Per copiare il file in questa directory occorre avere i diritti di 'root', cioè utilizzare il comando 'sudo';

    > collegare l'adattatore seriale RS485 ad una porta USB del PC;

    > identificare il nome del dispositivo dell'adattatore RS485 collegato, utilizzando comandi linux tipo
Codice:
ls /dev | grep USB
    o il più articolato
Codice:
echo -n "/dev/";dmesg|grep tty|grep USB|rev|awk '{print $1}'|rev
    Sul mio PC è '/dev/ttyUSB0'.

    > modificare il file .hal della configurazione di LinuxCNC utilizzata, includendo linee tipo quelle riportate sopra (esempio 'fresa.hal') e verificando che il nome dell'adattatore RS485 nel comando 'loadusr' sia impostato correttamente;

    > verificare anche i valori di configurazione dei pin 'spindle.speed-scale' e 'spindle.motion-reversed'; per me hanno questi valori:

    fresa:
    Codice:
    setp spindle.speed-scale        1.46154
    setp spindle.motion-reversed    1
    tornio:
    Codice:
    setp spindle.speed-scale        1
    setp spindle.motion-reversed    0


Note aggiuntive:

Il Varicon è configurato di default per utilizzare l'indirizzo 32 e un baudrate di 9600.
I valori dell'indirizzo possono andare da 32 a 63 e il baudrate massimo è di 19200.
Se si collegano più macchine in parallelo, come ho fatto io, occorre modificare gli indirizzi in modo che siano tutti diversi tra loro!
Per il baudrate ho deciso di utilizzare quello di default, ma nel caso si volesse modificare, portandolo a 19200 baud, occorrerà modificarlo sia sulle macchine che specificndolo come parametro sempre nel comando 'loadusr'.
Per la modifica dell'indirizzo e/o del baudrate sul Varicon, si deve seguire un procedura un po' più complessa; se qualcuno fosse interessato sono più che disponibile a fornire i dettagli.

------------------------------------------------------------------------------------------------------------------

E finalmente, il driver hanning_vfd.py:

Codice:
#!/usr/bin/env python
#   This is a component of linuxcnc
#   hanning_vfd - Copyright 2020 by Franco Basso
#   version 1.0 - 2020-03-02
#   (based on mitsub_vfd, by Chris Morley)
#
#----------------------------------------------------------------------------------
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
#----------------------------------------------------------------------------------
#
#   LinuxCNC user space component for controlling a Hanning Varicon inverter
#   over the serial port using RS485 standard; this model of inverter
#   is integrated in 2kW motors used in Wabeco lathes and mills ('hs' series)
#
#   This driver is based on the Hanning "Varicon Operating Instructions"
#   manual, version 1.2 (08/2007), 222 pages.
#


import re
import hal
import time
import serial
import traceback


SLEEPTIME = 0.03 # delay between tx and rx messages

HannReg = {      # Varicon registers used by this driver
   'mode':      0,
   'direction': 1,
   'speed_0':   2,
   'speed':     3,
   'setspeed':  22,
   'errorcode': 23,
   'status':    51,
   'I_motor':   156
}


class hanning_serial:

   def __init__ (self, vfd_names = [['hanning_vfd', '32']], baudrate = 9600, port = '/dev/ttyUSB0'):
      try:
         self.ser = serial.Serial (
            port,
            baudrate,
            parity = serial.PARITY_EVEN,
            stopbits = serial.STOPBITS_ONE,
            bytesize = serial.SEVENBITS
         )
         self.ser.timeout = 0.1
         self.ser.is_open
      except:
         print ('ERROR : hanning_vfd - No serial interface found at {0}'.format (port))
         raise SystemExit
      print ('Hanning VFD serial computer link has loaded')
      print ('Port: {0},{1},7,E,1'.format (port, baudrate))

      self.h = []
      self.comp_names = vfd_names
      for index, name in enumerate (self.comp_names):
         # print (index, ' NAME:', name[0], ' SLAVE:', name[1])
         c = hal.component (name[0])
         c.newpin ('fwd',             hal.HAL_BIT,   hal.HAL_IN)      #  Set to True for forward motion
         c.newpin ('run',             hal.HAL_BIT,   hal.HAL_IN)      #  Set to True to start the spindle
         c.newpin ('debug',           hal.HAL_BIT,   hal.HAL_IN)      #  Set to True to enable debug output messages
         c.newpin ('enabled',         hal.HAL_BIT,   hal.HAL_IN)      #  Set to True to enable spindle control via RS485 (Reg 000, 001 and 002)
         c.newpin ('monitor',         hal.HAL_BIT,   hal.HAL_IN)      #  Set to True to enable continuous status polling
         c.newpin ('motion-reversed', hal.HAL_BIT,   hal.HAL_IN)    #  Set to True if the spindle motion is reversed (i.e. in Wabeco mills)
         c.newpin ('speed-scale',     hal.HAL_FLOAT, hal.HAL_IN)      #  Configure this to set the ratio of mechanical transmission
         c.newpin ('motor-speed-rpm', hal.HAL_FLOAT, hal.HAL_IN)      #  Modify this to set the desired spindle speed, in RPM
         c.newpin ('motor-fb-rpm',    hal.HAL_FLOAT, hal.HAL_OUT)   #  Value of actual spindle speed, in RPM
         c.newpin ('motor-fb-rps',    hal.HAL_FLOAT, hal.HAL_OUT)   #  Value of actual spindle speed, in RPS
         c.newpin ('motor-amps',      hal.HAL_FLOAT, hal.HAL_OUT)   #  Value of motor current, in ampere
         c.newpin ('motor-amps-enab', hal.HAL_BIT,   hal.HAL_IN)      #  Set to True to poll for 'motor-amps' value (when 'monitor' is True)
         c.newpin ('estop',           hal.HAL_BIT,   hal.HAL_IN)      #  Set to True to do an emergecy stop
         c.newpin ('rx-error-cnt',    hal.HAL_U32,   hal.HAL_IO)    #  Number of communication errors
         c.newpin ('error-code',      hal.HAL_U32,   hal.HAL_OUT)   #  Value of error code, when 'sts-gen-error' is True
         c.newpin ('sts-temp-warn',   hal.HAL_BIT,   hal.HAL_OUT)   #  Temperature warning: 10'C or less below shutdown temperature
         c.newpin ('sts-temp-rise',   hal.HAL_BIT,   hal.HAL_OUT)   #  Temperature rise inverter
         c.newpin ('sts-brake-stop',  hal.HAL_BIT,   hal.HAL_OUT)   #  Brake ramp stopped (voltage too high)
         c.newpin ('sts-chopper-on',  hal.HAL_BIT,   hal.HAL_OUT)   #  Chopper active
         c.newpin ('sts-enabled',     hal.HAL_BIT,   hal.HAL_OUT)   #  Enable issued
         c.newpin ('sts-clkpuls-on',  hal.HAL_BIT,   hal.HAL_OUT)   #  Clock pulse turned on
         c.newpin ('sts-gen-error',   hal.HAL_BIT,   hal.HAL_OUT)   #  General error (see 'error-code')
         c.newpin ('sts-standstill',  hal.HAL_BIT,   hal.HAL_OUT)   #  Frequency 0 / standstill
         c.newpin ('sts-speed-ok',    hal.HAL_BIT,   hal.HAL_OUT)   #  Setspeed attained
         c.newpin ('sts-rot-ccw',     hal.HAL_BIT,   hal.HAL_OUT)   #  Direction of rotation counter-clockwise (negative)
         c.newpin ('sts-pwr-115v',    hal.HAL_BIT,   hal.HAL_OUT)   #  Device for 115 V mains voltage
         c.newpin ('sts-pwr-400v',    hal.HAL_BIT,   hal.HAL_OUT)   #  Device for 400 V mains voltag

         # set reasonable defaults
         c['fwd']             = True
         c['run']             = False
         c['debug']           = False
         c['enabled']         = False
         c['monitor']         = False
         c['motion-reversed'] = False
         c['motor-speed-rpm'] = False
         c['motor-amps-enab'] = False
         c['estop']           = False
         c['speed-scale']     = 1
         c['rx-error-cnt']    = 0

         # flags for each device
         self['last_enabled%d'%index]  = c['enabled']
         self['last_run%d'%index]      = c['run']
         self['last_fwd%d'%index]      = c['fwd']
         self['last_cmd%d'%index]      = c['motor-speed-rpm']
         self['last_monitor%d'%index]  = c['monitor']
         self['last_estop%d'%index]    = c['estop']
         self['last_enq_code%d'%index] = 0

         #add device to component reference variable
         self.h.append (c)
         print ('Hanning {0} VFD: slave# {1} added'.format (name[0], name[1]))

      # only issue ready when all the components are ready
      for i in self.h:
         i.ready ()


   def loop (self):
      global SLEEPTIME, HannReg
      while 1:
         try:
            for index, ids in enumerate (self.comp_names):
               self.slave_num = int (ids[1])

               # MONITOR for up-to-speed (sts-speed-ok), alarms and running frequency
               # 51 is the address for 13 status bits ( b0 - b12 )
               if self.h[index]['monitor']:
                  # empty the rx buffer
                  while self.ser.in_waiting > 0:
                     raw = self.ser.read (1)
                  # build the message with the status enquiry
                  msg = self.prepare_enquiry (index, HannReg['status'])
                  if self.h[index]['debug']:
                     print ('DEBUG: Tx ' + repr (str (msg)))
                  # and sends it
                  self.ser.write (msg)
                  # wait for the response
                  time.sleep (SLEEPTIME)
                  resp_ok, value, resp = self.poll_output (index)
                  if self.h[index]['debug']:
                     print ('DEBUG: Rx ' + repr (resp))
                  if resp_ok:
                     # print (value)
                     # decode the status word
                     try:
                        binary = '{0:#015b}'.format (value, 10)
                     except:
                        binary = '0b0000000000000'
                     self.h[index]['sts-temp-warn']  = ((int (binary, 2) &    2) != 0)
                     self.h[index]['sts-temp-rise']  = ((int (binary, 2) &    4) != 0)
                     self.h[index]['sts-brake-stop'] = ((int (binary, 2) &    8) != 0)
                     self.h[index]['sts-chopper-on'] = ((int (binary, 2) &   16) != 0)
                     self.h[index]['sts-enabled']    = ((int (binary, 2) &   32) != 0)
                     self.h[index]['sts-clkpuls-on'] = ((int (binary, 2) &   64) != 0)
                     self.h[index]['sts-gen-error']  = ((int (binary, 2) &  128) != 0)
                     self.h[index]['sts-standstill'] = ((int (binary, 2) &  256) != 0)
                     self.h[index]['sts-speed-ok']   = ((int (binary, 2) &  512) != 0)
                     self.h[index]['sts-rot-ccw']    = ((int (binary, 2) & 1024) != 0)
                     self.h[index]['sts-pwr-115v']   = ((int (binary, 2) & 2048) != 0)
                     self.h[index]['sts-pwr-400v']   = ((int (binary, 2) & 4096) != 0)

                  # if error, reads the error code
                  if self.h[index]['sts-gen-error']:
                     msg = self.prepare_enquiry (index, HannReg['errorcode'])
                     if self.h[index]['debug']:
                        print ('DEBUG: Tx ' + repr (str (msg)))
                     self.ser.write (msg)
                     time.sleep (SLEEPTIME)
                     resp_ok, value, resp = self.poll_output (index)
                     if self.h[index]['debug']:
                        print ('DEBUG: Rx ' + repr (resp))
                     if resp_ok:
                        #   Code   Error description
                        #     0:   No ERROR
                        #     1:   Undervoltage error
                        #     2:   Overvoltage error
                        #     4:   Inverter overtemperature error
                        #     5:   Motor overtemperature error
                        #     6:   Fan overtemperature error
                        #     8:   EEPROM error
                        #     9:   Inverter peak current error
                        #    10:   Inverter overcurrent error
                        #    11:   Inverter continuous current error
                        #    12:   Motor overcurrent error
                        #    16:   Enable error
                        #    64:   Short-circuit error
                        #   128:   Power On error
                        #   250:   Watch Dog error
                        #   255:   Timeout error
                        self.h[index]['error-code'] = int (value)
                     else:
                        #   256:   Communication error
                        self.h[index]['error-code'] = 256
                  else:
                        #     0:   No ERROR
                        self.h[index]['error-code'] = 0

                  # gets the running motor speed status
                  if self.h[index]['sts-standstill']:
                     self.h[index]['motor-fb-rpm'] = 0.0
                     self.h[index]['motor-fb-rps'] = 0.0
                  else:
                     msg = self.prepare_enquiry (index, HannReg['speed'])
                     if self.h[index]['debug']:
                        print ('DEBUG: Tx ' + repr (str (msg)))
                     self.ser.write (msg)
                     time.sleep (SLEEPTIME)
                     resp_ok, value, resp = self.poll_output (index)
                     if self.h[index]['debug']:
                        print ('DEBUG: Rx ' + repr (resp))
                     if resp_ok:
                        if self.h[index]['motion-reversed']:
                           value = -value
                        self.h[index]['motor-fb-rpm'] = int (value * self.h[index]['speed-scale'])
                        self.h[index]['motor-fb-rps'] = self.h[index]['motor-fb-rpm'] / 60.0
                        if self.h[index]['debug'] and 1 == 2:
                           print ('monitor frequency:', value, string, string[3:7], len (string))

                  # gets the motor current (amps)
                  if self.h[index]['motor-amps-enab']:
                     msg = self.prepare_enquiry (index, HannReg['I_motor'])
                     if self.h[index]['debug']:
                        print ('DEBUG: Tx ' + repr (str (msg)))
                     self.ser.write (msg)
                     time.sleep (SLEEPTIME)
                     resp_ok, value, resp = self.poll_output (index)
                     if self.h[index]['debug']:
                        print ('DEBUG: Rx ' + repr (resp))
                     if resp_ok:
                        self.h[index]['motor-amps'] = value * 0.1
                        if self.h[index]['debug'] and 1 == 2:
                           print ('monitor amps:', value, string, string[3:7], len (string))

               # stop on ESTOP
               # if ESTOP is false it stops the output
               # when ESTOP is reset the run command must be re-issued (cycled false to true) to start motor
               if not self['last_estop%d'%index] == self.h[index]['estop']:
                  if not self.h[index]['estop']:
                     msg = self.prepare_select (HannReg['mode'], 0)
                     self.ser.write (msg)
                     time.sleep (SLEEPTIME)
                     resp = self.poll_acknak (index)
                     self['last_run%d'%index] = self.h[index]['run'] = False
                     msg = self.prepare_select (HannReg['setspeed'], 7)
                     self.ser.write (msg)
                     time.sleep (SLEEPTIME)
                     resp = self.poll_acknak (index)
                     self['last_enabled%d'%index] = self.h[index]['enabled'] = False
                     self['last_estop%d'%index] = self.h[index]['estop']
                     print ('**** Hanning VFD: {0} stopped due to Estop Signal'.format (ids[0]))
                     continue
                  print ('**** Hanning VFD: Estop cleared - Must re-issue run command to start {0}.'.format (ids[0]))
                  self['last_estop%d'%index] = self.h[index]['estop']

               # set input mode
               if not self['last_enabled%d'%index] == self.h[index]['enabled']:
                  if self.h[index]['enabled']:
                     msg = self.prepare_select (HannReg['errorcode'], 0)
                     if self.h[index]['debug']:
                        print ('DEBUG: Tx ' + repr (str (msg)))
                     self.ser.write (msg)
                     time.sleep (SLEEPTIME)
                     resp = self.poll_acknak (index)
                     if self.h[index]['debug']:
                        print ('DEBUG: Rx ' + resp)
                     time.sleep (SLEEPTIME)
                     data = 0   # Regs 000, 001 and 002 controlled by PC via RS485 link
                  else:
                     data = 7   # Regs 000, 001 and 002 controlled by manual switch and pot knob
                  msg = self.prepare_select (HannReg['setspeed'], data)
                  if self.h[index]['debug']:
                     print ('DEBUG: Tx ' + repr (str (msg)))
                  self.ser.write (msg)
                  time.sleep (SLEEPTIME)
                  resp = self.poll_acknak (index)
                  self['last_enabled%d'%index] = self.h[index]['enabled']
                  if self.h[index]['debug']:
                     print ('DEBUG: Rx ' + resp)

               # set run
               if not self['last_run%d'%index] == self.h[index]['run']:
                  if self.h[index]['run']:
                     if self.h[index]['fwd']:
                        data = 1   # forward (CW)
                     else:
                        data = 0   # backward (CCW)
                     if self.h[index]['motion-reversed']:
                        data = 1 - data
                     msg = self.prepare_select (HannReg['direction'], data)
                     if self.h[index]['debug']:
                        print ('DEBUG: Tx ' + repr (str (msg)))
                     self.ser.write (msg)
                     time.sleep (SLEEPTIME)
                     resp = self.poll_acknak (index)
                     self['last_fwd%d'%index] = self.h[index]['fwd']
                     if self.h[index]['debug']:
                        print ('DEBUG: Rx ' + resp)
                     data = 1   # run
                  else:
                     data = 0   # stop
                  msg = self.prepare_select (HannReg['mode'], data)
                  if self.h[index]['debug']:
                     print ('DEBUG: Tx ' + repr (str (msg)))
                  self.ser.write (msg)
                  time.sleep (SLEEPTIME)
                  resp = self.poll_acknak (index)
                  self['last_run%d'%index] = self.h[index]['run']
                  if self.h[index]['debug']:
                     print ('DEBUG: Rx ' + resp)

               # set direction
               if not self['last_fwd%d'%index] == self.h[index]['fwd']:
                  if self.h[index]['fwd']:
                     data = 1   # forward (CW)
                  else:
                     data = 0   # backward (CCW)
                  if self.h[index]['motion-reversed']:
                     data = 1 - data
                  msg = self.prepare_select (HannReg['direction'], data)
                  if self.h[index]['debug']:
                     print ('DEBUG: Tx ' + repr (str (msg)))
                  self.ser.write (msg)
                  time.sleep (SLEEPTIME)
                  resp = self.poll_acknak (index)
                  self['last_fwd%d'%index] = self.h[index]['fwd']
                  if self.h[index]['debug']:
                     print ('DEBUG: Rx ' + resp)

               # set speed
               if not self['last_cmd%d'%index] == self.h[index]['motor-speed-rpm']:
                  rpm = int (abs (self.h[index]['motor-speed-rpm']) / self.h[index]['speed-scale'])
                  self['last_cmd%d'%index] = self.h[index]['motor-speed-rpm']
                  msg = self.prepare_select (HannReg['speed_0'], rpm)
                  if self.h[index]['debug']:
                     print ('DEBUG: Tx ' + repr (str (msg)))
                  self.ser.write (msg)
                  time.sleep (SLEEPTIME)
                  resp = self.poll_acknak (index)
                  if self.h[index]['debug']:
                     print ('DEBUG: Rx ' + resp)

         except KeyboardInterrupt:
               self.kill_output ()
               raise
         except:
               print ('error', ids)
               print (sys.exc_info ()[0])

      print ('Loop end!')


   def kill_output (self):
      global SLEEPTIME, HannReg
      for index, ids in enumerate (self.comp_names):
         # stop the motion
         self.slave_num = int (ids[1])
         time.sleep (SLEEPTIME)
         msg = self.prepare_select (HannReg['mode'], 0)
         self.ser.write (msg)
         time.sleep (SLEEPTIME)
         resp = self.poll_acknak (index)
         time.sleep (SLEEPTIME)
         # reset to manual control
         msg = self.prepare_select (HannReg['setspeed'], 7)
         self.ser.write (msg)
         time.sleep (SLEEPTIME)
         resp = self.poll_acknak (index)
         print ('Hanning VFD: Kill-> ' + ids[0])


   # build the 'enquiry' message (to read a Varicon register)
   def prepare_enquiry (self, index, code = 51):
      self['last_enq_code%d'%index] = code
      converted_data = bytearray (chr (0x04) + chr (self.slave_num) + '{:03}'.format(code) + chr (0x05), 'utf8')
      return converted_data


   # build the 'select' message (to modify a Varicon register)
   def prepare_select (self, code, data):
      textval = '{:03}={}\x03'.format (code, data)
      s = 0
      for i in range (0, len (textval)):
         s = s ^ ord (textval[i])
      converted_data = bytearray(chr (0x04) + chr (self.slave_num) + chr (0x02) + textval + chr (s), 'utf8')
      return converted_data


   # decode the 'enquiry' response
   def poll_output (self, index):
      code = value = 0
      ok = False
      resp = raw = self.ser.read (1)
      if raw == '\x02':
         core = self.ser.read_until ('\x03', 12)
         resp += core
         if core[-1] == '\x03':
            # message received, check BCC
            bcc = self.ser.read (1)
            resp += bcc
            s = 0
            for i in range (0, len (core)):
               s = s ^ ord (core[i])
            if ord (bcc[0]) == s:
               # BCC ok, message valid
               mtch = re.match ('([0-9]+)=(-?[0-9]+)\x03', core)
               if mtch != None:
                  # extract code and value
                  code = int (mtch.group(1))
                  if code == self['last_enq_code%d'%index]:
                     value = int (mtch.group(2))
                     ok = True
                     return ok, value, resp
      self.h[index]['rx-error-cnt'] += 1
      return ok, value, resp


   # decode the 'select' response
   def poll_acknak (self, index):
      resp = b''
      res = 'err'
      while self.ser.in_waiting > 0:
         resp += self.ser.read (1)
      if len (resp) > 0:
         if resp[0] == '\x06':
            return 'ack'
         elif resp[0] == '\x15':
            res = 'nak'
      self.h[index]['rx-error-cnt'] += 1
      return res


   def __getitem__ (self, item):
      return getattr (self, item)


   def __setitem__ (self, item, value):
      return setattr (self, item, value)


if __name__ == '__main__':
   import getopt, sys
   letters = 'p:b:h'                   # the : means an argument needs to be passed after the letter
   keywords = ['port = ', 'baud = ']   # the = means that a value is expected after the keyword
   try:
      # starts at the second element of argv since the first one is the script name
      # extraparms are extra arguments passed after all option/keywords are assigned
      # opts is a list containing the pair 'option'/'value'
      opts, extraparam = getopt.getopt (sys.argv[1:], letters, keywords)
   except getopt.GetoptError as err:
      # print help information and exit:
      print str(err)  # will print something like "option -a not recognized"
      print ('Use --help option to get help')
      sys.exit(0)
   port = '/dev/ttyUSB0'
   device_ids = []
   baud = 9600

   for o, p in opts:
      if o in ['-p', '--port']:
         port = p
      elif o in ['-b', '--baud']:
         baud = p
      elif o in ['-h', '--help']:
         print ('Hanning VFD computer-link interface')
         print ('  LinuxCNC user space component for controlling a Hanning Varicon inverter')
         print ('  over the serial port using RS485 standard;')
         print ('  this inverter is used in Wabeco lathes and mills using 2kW motors')
         print ('')
         print (''' Sample LinuxCNC code (in <filename>.hal file)

   loadusr -Wn spindle hanning_vfd -p /dev/tty0 spindle=32
   # ********** mill spindle vfd setup (slave 32) *************
   net  spindle-vel-cmd-rpm-abs   <=  motion.spindle-speed-out-abs
   net  spindle-vel-cmd-rpm-abs    => spindle.motor-speed-rpm
   net  spindle-enable            <=  motion.spindle-on
   net  spindle-enable             => spindle.run
   net  spindle-cw                <=  motion.spindle-forward
   net  spindle-cw                 => spindle.fwd
   net  spindle-brake             <=  motion.spindle-brake
   net  spindle-position           => motion.spindle-revs
   net  spindle-at-speed          <=  spindle.sts-speed-ok
   net  spindle-at-speed           => motion.spindle-at-speed
   net  spindle-speed-rps         <=  spindle.motor-fb-rps
   net  spindle-speed-rps          => motion.spindle-speed-in
   net  spindle-index-enable      <=> motion.spindle-index-enable
   net  machine-is-enabled        <=  motion.motion-enabled
   net  machine-is-enabled         => spindle.monitor
   net  machine-is-enabled         => spindle.enabled
   net  estop-out                 <=  iocontrol.0.user-enable-out
   net  estop-out                  => iocontrol.0.emc-enable-in
   net  estop-out                  => spindle.estop
   net  spindle-error-condition   <=  spindle.sts-gen-error
   net  spindle-error-code        <=  spindle.error-code
   net  spindle-current           <=  spindle.motor-amps
   # --- config mill spindle pins ---
   setp spindle.speed-scale        1.46154
   setp spindle.debug              0
   setp spindle.motor-amps-enab    0
   setp spindle.motion-reversed    1
''')
      sys.exit (0)

   if extraparam:
      for dids in extraparam:
         device_ids.append (dids.split ('='))
   else:
      device_ids = [['hanning_vfd', '32']]

   # info gathered, now use them
   try:
      app = hanning_serial (vfd_names = device_ids, baudrate = int (baud), port = port)
      app.loop ()
   except KeyboardInterrupt:
      sys.exit (0)
   else:
      exc_type, exc_value, exc_traceback = sys.exc_info ()
      formatted_lines = traceback.format_exc ().splitlines ()
      print ('')
      print ('**** hanning_vfd debugging:', formatted_lines[0])
      traceback.print_tb (exc_traceback, limit = 1, file = sys.stdout)
      print (formatted_lines[-1])


P.S. nelle foto il filo giallo collegato al terminale 12 della morsettiera, deve essere in effetti collegato al terminale 13. Era collegato al 12 di fabbrica per errore: l'ho segnalato alla Wabeco che mi ha confermato che deve in effetti essere collegato al 13!

Sono ovviamente a disposizione per chiarimenti e dettagli


Non hai i permessi necessari per visualizzare i file allegati in questo messaggio.

_________________
Fresa Wabeco F1410 LF-C hs; Tornio Wabeco D6000-C hs; Segatrice Nebes TM178


Top
 Profilo  
 
 Oggetto del messaggio: Re: Driver per controllo remoto Wabeco
MessaggioInviato: sab mar 07, 2020 11:29 
Non connesso
CAPO OFFICINA
Avatar utente

Iscritto il: mar mar 23, 2010 22:18
Messaggi: 41196
Località: Artena(Roma)
grazie.

_________________
la democrazia funziona fino a quando fate quello che dico io!
Mc Mazzafrusten meccanico mannaro
il riparatore di riparazioni
er robin hood de no'antri
tagliatore scelto di puntarelle
http://www.lievitoedintorni.it/phpbb/index.php


Top
 Profilo  
 
 Oggetto del messaggio: Re: Driver per controllo remoto Wabeco
MessaggioInviato: sab mar 07, 2020 13:38 
Non connesso
TORNITORE E FRESATORE
Avatar utente

Iscritto il: mer set 10, 2008 14:42
Messaggi: 27703
Località: Vicenza
Lavoro importante ! :grin:

Grazie !

_________________
Mario

I grandi Uomini si riconoscono dalla grandezza dei loro obbiettivi ....


Top
 Profilo  
 
 Oggetto del messaggio: Re: Driver per controllo remoto Wabeco
MessaggioInviato: sab mar 07, 2020 13:48 
Non connesso
APPRENDISTA E ADDETTO ALLE PULIZIE
Avatar utente

Iscritto il: dom ott 01, 2017 18:12
Messaggi: 9
Località: Bologna
E' stato un piacere!
E poi sono un fervente sostenitore del software open source! :grin:

_________________
Fresa Wabeco F1410 LF-C hs; Tornio Wabeco D6000-C hs; Segatrice Nebes TM178


Top
 Profilo  
 
 Oggetto del messaggio: Re: Driver per controllo remoto Wabeco
MessaggioInviato: sab mar 07, 2020 15:52 
Non connesso
TORNITORE E FRESATORE

Iscritto il: lun gen 01, 2018 21:07
Messaggi: 19936
Località: Como
Se operi con Linux devi essere per forza un sostenitore ! :grin: Il lavoro mi sembra accurato e professionale ( il mio invece è un parere da dilettante avendo poco in simpatia le macchine cnc ) Mi piacerebbe capire se puoi descrivermelo in termini semplici il vantaggio che porta questa modifica.


Top
 Profilo  
 
 Oggetto del messaggio: Re: Driver per controllo remoto Wabeco
MessaggioInviato: sab mar 07, 2020 16:04 
Non connesso
CAPO OFFICINA
Avatar utente

Iscritto il: dom gen 31, 2010 21:46
Messaggi: 8841
Località: Bussero (MI)
Bel lavoro bravo, un peccato che sia così specifico per le macchine Wabeco...

_________________
McMax

“None of us can change the things we’ve done. But we can all change what we do next.” – Fred Johnson

fulminato in tenera età


Top
 Profilo  
 
 Oggetto del messaggio: Re: Driver per controllo remoto Wabeco
MessaggioInviato: sab mar 07, 2020 16:38 
Non connesso
APPRENDISTA E ADDETTO ALLE PULIZIE
Avatar utente

Iscritto il: dom ott 01, 2017 18:12
Messaggi: 9
Località: Bologna
Ciao Carlino, non capisco bene a quale modifica ti riferisci. Se intendi tutto il lavoro in generale, bè, il vantaggio principale, lavorando in modalità cnc e cioè a controllo di programma, è il fatto che il programma stesso può decidere quando attivare la rotazione del mandrino e a quale velocità.
Faccio un paio di esempi:

1 - se vuoi eseguire col tornio una lavorazione trasversale a velocità costante(*), per intenderci facendo muovere solo la torretta, il controllo della velocità ti permette di aumentare il numero di giri man mano che ti avvicini all'asse, certo sempre entro i limiti operativi della macchina;

2 - devi eseguire una filettatura su un pezzo, sempre sul tornio; dando i comandi opportuni (comando GCode 76) puoi attivare il movimento dell'utensile in sincrono con la rotazione del pezzo;

3 - con la fresa devi eseguire in sequenza una serie di lavorazioni cnc con lo stesso utensile ma a velocità diverse; se è il programma a gestire il tutto non devi essere tu ogni volta a modificare la velocità, col rischio magari di sbagliarti.

Se invece la modifica a cui ti riferisci è quella di aver voluto fare il controllo tramite RS485 invece di portare una tensione 0~10 volt e i segnali di controllo per avviare il motore in un senso o nell'altro, il motivo fondamentale è la semplicità del cablaggio che nel mio caso si raddoppiava dovendo fare la modifica su due macchine. Oltre a ciò ci sono le possibilità aggiunte da un controllo seriale: poter monitorare l'esatta velocità di rotazione, l'avere un feedback di velocità di lavoro raggiunta, poter monitorare altri parametri come temperature varie (attualmente non implementato ma facilmente fattibile), l'assorbimento di corrente, etc.

E poi, stavo quasi dimenticando, forse il primo motivo è stato il divertimento e la soddisfazione nel farlo, che credo sia in questo campo il motore principale! :risatina:


(*) chiedo venia per la terminologia poco appropriata: sono un novizio un po' carente nel lessico

---------------------------------

Per McMax: quelle avevo! :rotfl:

Grazie comunque per l'apprezzamento!

_________________
Fresa Wabeco F1410 LF-C hs; Tornio Wabeco D6000-C hs; Segatrice Nebes TM178


Top
 Profilo  
 
Visualizza ultimi messaggi:  Ordina per  
Apri un nuovo argomento Rispondi all’argomento  [ 7 messaggi ] 

Tutti gli orari sono UTC +1 ora


Chi c’è in linea

Visitano il forum: Nessuno e 1 ospite


Non puoi aprire nuovi argomenti
Non puoi rispondere negli argomenti
Non puoi modificare i tuoi messaggi
Non puoi cancellare i tuoi messaggi
Non puoi inviare allegati

Cerca per:
Vai a:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Traduzione Italiana phpBB.it