lunes, 11 de abril de 2016

Configurar entradas y salidas digitales del expansor MCP23017

Para este tutorial se recomienda visitar los anteriores tutoriales:
Instalación de glade y gladevcp
Gui básica Glade

Antes de  utilizar la librería hal de MCP23017, se va a realizar una prueba utilizando un pequeño programa hecho en Python.

import time
from drivers.MCP23017 import MCP23017 as MCP

mcp = MCP(0x27,1,False)

mcp.init()

for i in range(0,8):
    mcp.setDir(MCP.PORT_A, i, MCP.DIR_OUT)
    mcp.setDir(MCP.PORT_B, i, MCP.DIR_OUT)

while True:
  
    for i in range(0,8): 
        mcp.setValue(MCP.PORT_A,i,1)
        mcp.setValue(MCP.PORT_B,i,1)
    mcp.write()
    time.sleep(1)

    for i in range(0,8):
        mcp.setValue(MCP.PORT_A,i,0)
        mcp.setValue(MCP.PORT_B,i,0)
    mcp.write()
    time.sleep(1)


Si se obtiene el siguiente error, hay que modificar la librería MCP23017.py.

Traceback (most recent call last):
  File "mcp23017.py", line 23, in <module>
    mcp.write()
  File "/usr/lib/python2.7/dist-packages/drivers/MCP23017.py", line 158, in write
    self.updateValue(i, self.port[i].value)
  File "/usr/lib/python2.7/dist-packages/drivers/MCP23017.py", line 119, in updateValue
    self.i2c.write_byte_data(self.address, reg, value)
IOError: [Errno 121] Remote I/O error

El error sucede por no inicializar de forma correcta las interrupciones. Para solucionar hay que aumentar lo siguiente a la librería MCP23017.py.

sudo nano /usr/lib/python2.7/dist-packages/drivers/MCP23017.py

    def init(self):
        config = 0
        config |= self.__MCP23017_IOCON_BANK1
        config |= self.__MCP23017_IOCON_MIRROR_DIS
        config |= self.__MCP23017_IOCON_SEQOP_DIS
        config |= self.__MCP23017_IOCON_DISSLW_EN
        config |= self.__MCP23017_IOCON_HAEN_EN
        config |= self.__MCP23017_IOCON_ODR_DIS
        config |= self.__MCP23017_IOCON_INTPOL_HIGH
        
        self.i2c.write_byte_data(self.address, self.__MCP23017_REG_IOCON, config)
        
        for i in range(0, 2):
            ##### Interrupt defaults
            # Disable interrupts on all pins by default
            reg = self.portBase[i] + self.__MCP23017_REG_GPINTEN
            self.i2c.write_byte_data(self.address, reg, 0x00)
            # Interrupt on change register set to compare to previous value by default
            reg = self.portBase[i] + self.__MCP23017_REG_INTCON
            self.i2c.write_byte_data(self.address, reg, 0x00)
            # Interrupt compare value registers
            reg = self.portBase[i] + self.__MCP23017_REG_DEFVAL
            self.i2c.write_byte_data(self.address, reg, 0x00)
            # Clear any interrupts to start fresh
            reg = self.portBase[i] + self.__MCP23017_REG_GPIO
            self.i2c.read_byte_data(self.address, reg)
            # Enable all latches
            reg = self.portBase[i] + self.__MCP23017_REG_OLAT
            self.i2c.write_byte_data(self.address, reg, 0xFF)

            self.port[i].reset()
            self.portOld[i].reset()
            self.updateDir(i, self.port[i].dir)
            self.updatePullup(i, self.port[i].pullup)
            self.updateValue(i, self.port[i].value)


Para utilizar las interrupciones del MCP23017, revisar el siguiente link:

MCP23017 Interrupciones.

Para detectar la dirección i2c ejecutar:

En el caso de estar conectado al pin P9.19 y P9.20.

i2cdetect -y -r 1

 
Si se desea usar los pines P9.17 y P9.18, hay que exportar el Device Tree Overlay del I2C1.

sudo nano BB-I2C1-00A0.dts

Copiar el código DTS.

BB-I2C1-00A0.dts

Compilar Device Tree Overlay.

dtc -O dtb -o BB-I2C1-00A0.dtbo -b 0 -@ BB-I2C1-00A0.dts

Mover a "/lib/firmware" y Ejecutar.

sudo mv BB-I2C1-00A0.dtbo /lib/firmware/
sudo su
cd /lib/firmware/
echo BB-I2C1 > /sys/devices/bone_capemgr.9/slots
cat /sys/devices/bone_capemgr.9/slots

Ahora ya se puede usar I2C_1 en el bus 2.

i2cdetect -l
i2cdetect -y -r 2



En el terminal ejecutar halrun para visualizar los pines que creará el componente hal_gpio_mcp23017.

halrun
loadusr -Wn buttons hal_gpio_mcp23017 -n buttons -b 1 -a 39 -op A00,B00 -ip B01
show pin 

buttons es el nombre con el que se crearán los pines hal, -b indica el bus del i2c (puede ser 0, 1 o 2) en este caso se ocupa el i2c de los pines P9.19 y P9.20, -a 39 indica que se esta usando la dirección i2c 0x27 (39) conectado A0,A1,A2 a VCC, -op indican las salidas que se van a usar y -ip las entradas que se usarán.

Activar buttons.A.out-00.

setp buttons.A.out-00 1
show pin 
exit


Para probar algunas entradas y salidas del MCP23017, se va a crear una GUI simple con Glade. En la GUI se pondrá 2 HAL_ToggleButton y 2 HAL_LED. Los HAL_ToggleButton tienen el nombre de boton0 y boton1; los HAL_LED tienen el nombre de LED0 y LED1. Se ha guardado con el nombre de InputOutputGPIO.glade.


Se ha creado un archivo hal para realizar las conexiones correspondientes con los botones y los led´s.

sudo nano InputOutputGPIO.hal

#Outputs
net boton0 InputOutputGPIO.boton0 buttons.A.out-00 InputOutputGPIO.LED0
net boton1 InputOutputGPIO.boton1 buttons.B.out-00 InputOutputGPIO.LED1
#Input
net boton-info <= buttons.B.in-01
net boton-info => gmoccapy.v-button-7 

EL pin GPB1 está conectado a un interuptor infrarrojo que hace la función de pulsador para activar y desactivar el boton de info de gmoccapy_lcd7. Las salidas GPA0 y GPB0, están conectadas a LED´s para visualizar el funcionamiento al pulsar el boton HAL_ToggleButton.

Lo siguiente es incluir el archivo  .hal y .glade en el archivo .ini.

[DISPLAY]

# Name of display program, e.g., tkemc
#DISPLAY =               tkemc
#DISPLAY =              axis
DISPLAY =              gmoccapy_lcd7

EMBED_TAB_NAME = Boton
EMBED_TAB_LOCATION = box_left
EMBED_TAB_COMMAND = gladevcp -g 80x80+10+10 -H InputOutputGPIO.hal InputOutputGPIO.glade

La Gui se abrirá en una ventana de 80x80 pixeles y se recorrerá 10 en X y 10 en Y pixeles.

Lo último es iniciar el componente hal MCP23017 en el arhivo .hal principal.

loadusr -Wn buttons hal_gpio_mcp23017 -n buttons -b 1 -a 39 -op A00,B00 -ip B01

Si se desea agregar más MCP23017 se debe cambiar el nombre y dirección del i2c.

loadusr -Wn buttons1 hal_gpio_mcp23017 -n buttons1 -b 1 -a 32 -op A03,A05,B00 -ip B01,A00

buttons es el nombre con el que se crearán los pines hal, -b indica el bus del i2c (puede ser 0, 1 o 2) en este caso se ocupa el i2c de los pines P9.19 y P9.20, -a 39 indica que se esta usando la dirección i2c 0x27 (39) conectado A0,A1,A2 a VCC, -op indican las salidas que se van a usar y -ip las entradas que se usarán.




InputOutputGPIO.glade.





martes, 15 de marzo de 2016

Utilizar GUI gmoccapy_lcd7 con Control de Xbox 360.

Se necesita instalar unas librerías para que la Beaglebone Black pueda detectar el control del Xbox 360.

//Instalar xboxdrv, para simular el control de Xbox 360 en Linux.

sudo apt-get install xboxdrv

//Instalar evtest, para visualizar los eventos generados por cada uno de los botones del
//control de Xbox 360

sudo apt-get install evtest

//Al finalizar se debe correr el programa de xboxdrv para detectar el control de Xbox 360

sudo xboxdrv --silent --detach-kernel-driver


//Identificar el control de xbox 360
nano /proc/bus/input/devices
//Identificar el tipo de evento (js0 event1)
ctrl+X


//Al ejecutar el siguiente comando sale cuales son los tipos de eventos soportados e ira
// cambiando el valor de acuerdo a los botones que se hayan presionado. 

sudo evtest /dev/input/event1

ctrl+C


Dar permisos para poder acceder a los eventos del control desde machinekit.

//90-Xbox.rules es el nombre que le he dado al archivo, pero se puede usar cualquier
//nombre (myname.rules).

sudo nano /etc/udev/rules.d/90-Xbox.rules

//Copiar esto en el archivo

SUBSYSTEM=="input", GROUP="plugdev", MODE=="0660"

//Salir del editor
ctrl+O
ctrl+X

//Reiniciar las reglas para que inicie 90-Xbox.rules con el cambio realizado.

sudo udevadm trigger

//Comprobar que los eventos (dispositivos) se han cargado

sudo udevadm trigger





Comprobar control de Xbox 360 con halrun.

//halrun

halrun

//Iniciar el evento del control con halrun, en este caso el control del Xbox 360
// se puede usar con cualquier nombre con el que haya reconocido. Siendo el nombre
// reconocido "Xbox Gamepad (userspace driver)" se puedes usar Xbox o Gamepad.

loadusr -W hal_input -KRAL Gamepad

//Mantener pulsado cualquier botón, en este caso se ha pulsado el botón A y al 
// correr show pin se visualizarán los cambios.

show pin 

//Para salir
exit



AL pulsar el boton A del control de Xbox 360 cambia a TRUE input.0.btn-a

Nota: Realizar el paso de pulsar cualquier botón y visualizar, para poder identificar a que botón pertenece cada evento "input.0...".



Integrar archivo joypad.hal a Gui gmoccapy_lcd7.

El archivo .hal fue descargado de LinuxCNC Joypads y se ha realizado algunas modificaciones para adaptarlo al control de Xbox 360.

Probar el funcionamiento del control, ejecutando:

sudo evtest /dev/input/event1

En este caso la palanca izquierda del mando, reacciona cuando se pulsa a la izquierda con valores negativos y a la derecha con valores positivos; pero cuando se mueve de arriba a bajo funciona de forma inversa, dando valores negativos cuando se pulsa hacia arriba y valores positivos pulsando hacia abajo. Algo parecido ocurre en la palanca derecha del mando.


Componente joyhandle.
Código del Componente joyhandle.

Se inicializa el componente.

loadusr -W hal_input -KRAL Xbox

# load joyhandle component and attach to threads (in this case 3 instances)
loadrt joyhandle count=3
addf joyhandle.0 servo-thread   # x
addf joyhandle.1 servo-thread   # y
addf joyhandle.2 servo-thread   # z

Se configura la velocidad (máxima). Los valores de power varían de acuerdo a la sensibilidad, al aumentarlo se recomienda aumentar el valor de de deadband (0-0.99). La escala al ser negativa, invierte la dirección; al tener valores menores a 1 disminuye la velocidad y viceversa.

setp halui.jog-speed   1500 # desired maximum jog speed mm/min

# --Start-- These parameters ara used to set up joyhandle
setp halui.jog-deadband   0. # important: default value is 0.2, that would override joyhandle.deadband
setp joyhandle.0.power 3. # select nonlinearity to handele low jog values
setp joyhandle.1.power 3.
setp joyhandle.2.power 4. # in my case the z-axis is set up more sensitive
setp joyhandle.0.deadband 0.1
setp joyhandle.1.deadband 0.1
setp joyhandle.2.deadband 0.1
setp joyhandle.0.scale  1. 
setp joyhandle.1.scale -1. # negative values invert jogging
setp joyhandle.2.scale -0.75    # invert jogging and the z-axis is scaled to lower speed
# --End--

Se configura el componente joyhandle con las palancas del mando.

# connect hal_input to halui via joyhandle (in case use your own axes-names)

#left stick -> left-rigth (X jogging)
net velX input.0.abs-x-position => joyhandle.0.in
net velXout joyhandle.0.out => halui.jog.0.analog
#left stick -> up-down (Y jogging)
net velY input.0.abs-y-position => joyhandle.1.in
net velYout joyhandle.1.out => halui.jog.1.analog
#rigth stick -> up-down (Z jogging)
net velZ input.0.abs-ry-position => joyhandle.2.in
net velZout joyhandle.2.out => halui.jog.2.analog 

Se configura los botones del mando a las acciones que se requiera dar. Importate revisar la guía de comandos de la herramienta halui (HAL User Interface).

# [BUTTON-SAMPLES] use your own joypad pin-names
#One button sample
net spindleOff input.0.btn-x => halui.spindle.stop
net spindleOn input.0.btn-y => halui.spindle.start
net estopActivate input.0.btn-start => halui.estop.activate
net estopNotActivate input.0.btn-start-not => halui.estop.reset

#    Hal configuration file to move a cnc machine using a joypad using joyhandle component

#    Copyright 2008 Paul Willutzki <paul[at]willutzki[dot]de>

#    Licence: GPL

#    Version 3

#    This Hal-File needs the joyhandle component.
#    This uses the following formula for a non linear joypad movements:
#    y = (scale * (a*x^power + b*x)) + offset  
#
#    The parameters a and b are adjusted in such a way, that the function starts at (deadband,offset) and ends at (1,scale+offset).
#    Negative values will be treated point symetrically to origin. Values -deadband < x < +deadband will be set to zero.
#    Values x > 1 and x < -1 will be skipped to +-scale+offset. Negative scale values invert the movement.
#    With power one can adjust the nonlinearity (default = 2).
#    Default for deadband is 0.
#    Valid values are: power >= 1.0 (reasonable values are 1.x .. 4-5), 0 <= deadband < 0.99 (reasonable 0.1). If you use high deadbands (>0.5) you need higher power values to smoothly start at (deadband,offset). 
#    The additional offset component can be set in special cases (default = 0).
#    All values can be adjusted for each instance (joypad axis) separately.

#    Please take also a look at the manpages for johandle.

#    Insert the following lines in the INI-File (section [HAL])
#    HALUI = halui
#    HALFILE = joypad_V3.hal

# Load the hal_input component that creates pins for axes and buttons
# See man hal_input for details and finding input devices
loadusr -W hal_input -KRAL Xbox

# load joyhandle component and attach to threads (in this case 3 instances)
loadrt joyhandle count=3
addf joyhandle.0 servo-thread   # x
addf joyhandle.1 servo-thread   # y
addf joyhandle.2 servo-thread   # z

setp halui.jog-speed   1500 # desired maximum jog speed mm/min

# --Start-- These parameters ara used to set up joyhandle
setp halui.jog-deadband   0. # important: default value is 0.2, that would override joyhandle.deadband
setp joyhandle.0.power 3. # select nonlinearity to handele low jog values
setp joyhandle.1.power 3.
setp joyhandle.2.power 4. # in my case the z-axis is set up more sensitive
setp joyhandle.0.deadband 0.1
setp joyhandle.1.deadband 0.1
setp joyhandle.2.deadband 0.1
setp joyhandle.0.scale 1. 
setp joyhandle.1.scale -1. # negative values invert jogging
setp joyhandle.2.scale -0.75    # in my case the z-axis is scaled to lower speed
# --End--

# connect hal_input to halui via joyhandle (in case use your own axes-names)

#left stick -> left-rigth (X jogging)
net velX input.0.abs-x-position => joyhandle.0.in
net velXout joyhandle.0.out => halui.jog.0.analog
#left stick -> up-down (Y jogging)
net velY input.0.abs-y-position => joyhandle.1.in
net velYout joyhandle.1.out => halui.jog.1.analog
#rigth stick -> up-down (Z jogging)
net velZ input.0.abs-ry-position => joyhandle.2.in
net velZout joyhandle.2.out => halui.jog.2.analog


# connect hal_joypad directly to halui without joyhandle (in case use your own axes-names)
#net velX joypad.axis.3 => halui.jog.0.analog
#net velY joypad.axis.2 => halui.jog.1.analog
#net velZ joypad.axis.1 => halui.jog.2.analog


# [BUTTON-SAMPLES] use your own joypad pin-names
#One button sample
net spindleOff       input.0.btn-x                => halui.spindle.stop
net spindleF         input.0.btn-a                => halui.spindle.forward
net spindleR         input.0.btn-b                => halui.spindle.reverse
net spindleSlow      input.0.abs-hat0x-is-neg     => halui.spindle.decrease
net spindleFast      input.0.abs-hat0x-is-pos     => halui.spindle.increase
net homeAllAxis      input.0.btn-mode             => halui.home-all
net increasePosA     input.0.btn-tr               => halui.jog.3.plus
net decreasePosA     input.0.btn-tl               => halui.jog.3.minus
net estopActivate    input.0.btn-start            => halui.estop.activate
net estopNotActivate input.0.btn-start-not        => halui.estop.reset

Para visualizar el incremento y decremento de la velocidad del husillo, se va utilizar el archivo spindle_sim.hal de la configuración de gmoccapy y en el archivo postgui.hal, se va unir las señales incluyendo las que pertenecen a gmoccapy.

#archivo potgui.hal
loadrt abs names=abs_spindle_feedback
addf abs_spindle_feedback servo-thread

net spindle-speed-limited  =>  abs_spindle_feedback.in
net spindle-abs  abs_spindle_feedback.out  =>  gmoccapy.spindle_feedback_bar

net spindle-at-speed      gmoccapy.spindle_at_speed_led
net boton-info       input.0.btn-select           => gmoccapy.v-button-7

Si se desea se podrían reemplazar las señales de halui con las de gmoccapy.




Parte del código del archivo gmoccapy encargado de conectar los botones verticales. No hay que cambiar nada solo es una breve explicación de como funcionan los botones verticales y horizontales de gmoccapy.

Se guardan los botónes en la variable v_tabs y se da un número a cada uno para poder buscarlos.

self.v_tabs = [(0, "tbtn_estop"), (1, "tbtn_on"), (2, "rbt_manual"), (3, "rbt_mdi"),
                       (4, "rbt_auto"), (5, "tbtn_setup"), (6, "tbtn_user_tabs"), (7, "tbtn_info")
                      ]


Se define la función que se encarga de detectar si se ha presionado algún botón.

def _on_v_button_changed(self, pin):
        self._add_alarm_entry("got v_button_signal %s" % pin.name)
        if not pin.get():
            return
        btn = str(pin.name)
        nr = int(btn[-1])
        tab = self.v_tabs # see in the __init__ section for the declaration of self.tabs
        button = None
        for index in tab:
            if int(index[0]) == nr:
                # this is the name of the button
                button = index[1]
        if button:
            # only emit a signal if the button is sensitive, otherwise
            # running actions may be interupted
            if self.widgets[button].get_sensitive() == False:
                print("%s not_sensitive" % button)
                self._add_alarm_entry("%s not_sensitive" % button)
                return
            button_pressed_list = ("rbt_manual", "rbt_mdi", "rbt_auto")
            button_toggled_list = ("tbtn_setup")
            if button in button_pressed_list:
                self.widgets[button].set_active(True)
                self.widgets[button].emit("pressed")
            elif button in button_toggled_list:
                self.widgets[button].set_active(not self.widgets[button].get_active())
            else:
                self.widgets[button].emit("clicked")
        else:
            print("No button found in v_tabs from %s" % pin.name)
            self._add_alarm_entry("No button found in v_tabs from %s" % pin.name)

Se encarga de crear las conexiones hal para poder usar los botones en archivos .hal.

     # generate the vertical button pins
        for v_button in range(0, 8):
            pin = self.halcomp.newpin("v-button-%s" % v_button, hal.HAL_BIT, hal.HAL_IN)
            hal_glib.GPin(pin).connect("value_changed", self._on_v_button_changed)

Nota: Se actualizado la conexión del botón de info en gmoccapy_lcd7 para que se pueda controlar con pulsadores.

El botón start del control de Xbox 360 simula el comportamiento de un botón de emergencia. Al tenerlo pulsado se activa estop, caso contrario mantiene activada la máquina.



El botón Guide (btn-mode), se encarga de mover los ejes al origen. Debe estar configurado el archivo .ini para hacer un "home-all" a los ejes que se quiera.


El botón Back (btn-mode), activa y desactiva el toggled button de info.



Activa, desactiva y cambia la dirección el husillo.






Aumenta y disminuye la velocidad del husillo.



Incrementa y decrementa la posición del eje A.







Movimiento de ejes X, Y y Z.






Descargar archivos


MF5-LCD7-mill



jueves, 21 de enero de 2016

Obtener valores de PID con componente AT PID y configurar temperatura.

Al final del tutorial se dejarán todos los archivos que se necesitan para poder hacer funcionar la configuración de la impresora 3d con Machinekit. 

En este tutorial se va ha explicar como funciona el componente AT PID para poder calibrar los extrusores de una impresora 3d, para la cama caliente se realiza de la misma manera pero considerando algunos cambios que también se explicarán.

Lo primero que se debe realizar es configurar el Device Tree Overlay para que inicie cuando se prenda la Beaglebone Black, con esto se evita que se calienten los extrusores y cama caliente, mientras no se inicie Machinekit (LinuxCNC).

//This source file is provided under MIT License terms.
//Copyright (c) 2013 Calypso Ventures, Inc.
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.

/dts-v1/;
/plugin/;

/ {
 compatible = "ti,beaglebone", "ti,beaglebone-black";

 /* identification */
 part-number = "BBB-LCNC-MF5";
 version = "00A0";

 /* state the resources this cape uses */
 exclusive-use =
  /* the pin header uses */
/*  "J8.3",        gpio1_6  eMMC*/
/*  "J8.4",        gpio1_7  eMMC*/
/*  "J8.5",        gpio1_2  eMMC*/
/*  "J8.6",        gpio1_3  eMMC*/
  "J8.7",     /* gpio2_2 -> Libre*/
  "J8.8",     /* gpio2_3 -> Libre*/
  "J8.9",     /* gpio2_5 -> Libre*/
  "J8.10",    /* gpio2_4 -> Libre*/
  "J8.11",    /* gpio1_13-> lcd R1 -> Libre*/
  "J8.12",    /* gpio1_12-> lcd G1 -> Libre*/
  "J8.13",    /* gpio0_23-> lcd G0 -> Libre*/
  "J8.14",    /* gpio0_26-> lcd R0 -> Libre*/
  "J8.15",    /* gpio1_15-> lcd R2 -> Libre*/
  "J8.16",    /* gpio1_14-> lcd B2 -> Libre*/
  "J8.17",    /* gpio0_27-> lcd B1 -> Libre*/
  "J8.18",    /* gpio2_1 -> Libre*/
  "J8.19",    /* gpio0_22-> lcd B0 -> Libre*/
/*  "J8.20",       gpio1_31  eMMC*/
/*  "J8.21",       gpio1_30  eMMC*/
/*  "J8.22",       gpio1_5   eMMC*/
/*  "J8.23",       gpio1_4   eMMC*/
/*  "J8.24",       gpio1_1   eMMC*/
/*  "J8.25",       gpio1_0   eMMC*/
  "J8.26",    /* gpio1_29-> Libre*/
/*  "J8.27",       gpio2_22-> lcd VSYNC*/
/*  "J8.28",       gpio2_24-> lcd PCLK*/
/*  "J8.29",       gpio2_23-> lcd HSYNC*/
/*  "J8.30",       gpio2_25-> lcd DE*/
/*  "J8.31",       gpio0_10-> lcd B6*/
/*  "J8.32",       gpio0_11-> lcd B7*/
/*  "J8.33",       gpio0_9 -> lcd B5*/
/*  "J8.34",       gpio2_17-> lcd B3*/
/*  "J8.35",       gpio0_8 -> lcd B4*/
/*  "J8.36",       gpio2_16-> lcd G7*/
/*  "J8.37",       gpio2_14-> lcd G5*/
/*  "J8.38",       gpio2_15-> lcd G6*/
/*  "J8.39",       gpio2_12-> lcd G3*/
/*  "J8.40",       gpio2_13-> lcd G4*/
/*  "J8.41",       gpio2_10-> lcd R7*/
/*  "J8.42",       gpio2_11-> lcd G2*/
/*  "J8.43",       gpio2_8 -> lcd R5*/
/*  "J8.44",       gpio2_9 -> lcd R6*/
/*  "J8.45",       gpio2_6 -> lcd R3*/
/*  "J8.46",       gpio2_7 -> lcd R4*/
  
  "J9.11",    /* gpio0_30-> Libre*/
  "J9.12",    /* gpio1_28-> Libre*/
  "J9.13",    /* gpio0_31-> Libre*/
/*  "J9.14",       gpio1_18-> LCD ehrpwm1A*/
  "J9.15",    /* gpio1_16-> Libre*/ 
  "J9.16",    /* gpio1_19-> Libre*/
/*  "J9.17",       gpio0_5 -> spi0_cs0*/
/*  "J9.18",       gpio0_4 -> spi0_d1*/
/*  "J9.19",       gpio0_13-> EEPROM/I2C*/
/*  "J9.20",       gpio0_12-> EEPROM/I2C*/
/*  "J9.21",       gpio0_3 -> spi0_d0*/
/*  "J9.22",       gpio0_2 -> spi0_sclk*/
  "J9.23",    /* gpio1_17-> Libre*/
  "J9.24",    /* gpio0_15-> Libre*/
  "J9.25",    /* gpio3_21-> Libre*/
  "J9.26",    /* gpio0_14-> Libre*/
  "J9.27",    /* gpio3_19-> Libre*/
  "J9.28",    /* gpio3_17-> Libre*/
  "J9.29",    /* gpio3_15-> Libre*/
  "J9.30",    /* gpio3_16-> Libre*/
  "J9.31",    /* gpio3_14-> Libre*/
  "J9.41",    /* gpio3_20-> Libre*/
  "J9.42",    /* gpio3_18-> Libre*/
  
  /* the hardware IP uses */
/*  "gpio1_6", */
/*  "gpio1_7", */
/*  "gpio1_2", */
/*  "gpio1_3", */
  "gpio2_2", 
  "gpio2_3", 
  "gpio2_5",  
  "gpio2_4", 
  "gpio1_13", 
  "gpio1_12", 
  "gpio0_23", 
  "gpio0_26", 
  "gpio1_15", 
  "gpio1_14", 
  "gpio0_27", 
  "gpio2_1", 
  "gpio0_22", 
/*  "gpio1_31", */
/*  "gpio1_30", */
/*  "gpio1_5", */
/*  "gpio1_4", */
/*  "gpio1_1", */
/*  "gpio1_0", */
  "gpio1_29", 
/*  "gpio2_22", */
/*  "gpio2_24", */
/*  "gpio2_23", */
/*  "gpio2_25", */
/*  "gpio0_10", */
/*  "gpio0_11", */
/*  "gpio0_9", */
/*  "gpio2_17", */
/*  "gpio0_8", */
/*  "gpio2_16", */
/*  "gpio2_14", */
/*  "gpio2_15", */
/*  "gpio2_12", */
/*  "gpio2_13", */
/*  "gpio2_10", */
/*  "gpio2_11", */
/*  "gpio2_8", */
/*  "gpio2_9", */
/*  "gpio2_6", */
/*  "gpio2_7", */
  
  "gpio0_30",
  "gpio1_28",
  "gpio0_31",
/*  "gpio1_18", */
  "gpio1_16",
  "gpio1_19", 
/*  "gpio0_5",      */
/*  "gpio0_4",      */
/*  "gpio0_13", */
/*  "gpio0_12", */
/*  "gpio0_3", */
/*  "gpio0_2",      */
  "gpio1_17",
  "gpio0_15",
  "gpio3_21",
  "gpio0_14",
  "gpio3_19",
  "gpio3_17",
  "gpio3_15",
  "gpio3_16",
  "gpio3_14",
  "gpio3_20",
  "gpio3_18", 
  
  "pru0";

 fragment@0 {
  target = <&am33xx_pinmux>;
  __overlay__ {

   MF5_gpio_pins: MF5_gpio_pins {
    pinctrl-single,pins = <
/*     0x018     mmc      J8.3   gpio1_6  */       
/*     0x01C     mmc      J8.4   gpio1_7  */       
/*     0x008     mmc      J8.5   gpio1_2  */       
/*     0x00C     mmc      J8.6   gpio1_3  */       
     0x090     0x3F  /* J8.7   gpio2_2  */       
     0x094     0x3F  /* J8.8   gpio2_3  */       
     0x09C     0x3F  /* J8.9   gpio2_5  */       
     0x098     0x3F  /* J8.10  gpio2_4  */       
     0x034     0x3F  /* J8.11  gpio1_13 */       
     0x030     0x3F  /* J8.12  gpio1_12 */       
     0x024     0x3F  /* J8.13  gpio0_23 */       
     0x028     0x3F  /* J8.14  gpio0_26 */       
     0x03C     0x3F  /* J8.15  gpio1_15 */       
     0x038     0x3F  /* J8.16  gpio1_14 */       
     0x02C     0x3F  /* J8.17  gpio0_27 */       
     0x08C     0x3F  /* J8.18  gpio2_1  */       
     0x020     0x3F  /* J8.19  gpio0_22 */       
/*     0x084     mmc      J8.20  gpio1_31 */       
/*     0x080     mmc      J8.21  gpio1_30 */       
/*     0x014     mmc      J8.22  gpio1_5  */       
/*     0x010     mmc      J8.23  gpio1_4  */       
/*     0x004     mmc      J8.24  gpio1_1  */       
/*     0x000     mmc      J8.25  gpio1_0  */       
     0x07C     0x3F  /* J8.26  gpio1_29 */       
/*     0x0E0     0x3F     J8.27  gpio2_22 */       
/*     0x0E8     0x3F     J8.28  gpio2_24 */       
/*     0x0E4     0x3F     J8.29  gpio2_23 */       
/*     0x0EC     0x3F     J8.30  gpio2_25 */       
/*     0x0D8     0x3F     J8.31  gpio0_10 */       
/*     0x0DC     0x3F     J8.32  gpio0_11 */       
/*     0x0D4     0x3F     J8.33  gpio0_9  */       
/*     0x0CC     0x3F     J8.34  gpio2_17 */       
/*     0x0D0     0x3F     J8.35  gpio0_8  */       
/*     0x0C8     0x3F     J8.36  gpio2_16 */       
/*     0x0C0     0x3F     J8.37  gpio2_14 */       
/*     0x0C4     ???      J8.38  gpio2_15 */       
/*     0x0B8     0x3F     J8.39  gpio2_12 */       
/*     0x0BC     0x3F     J8.40  gpio2_13 */       
/*     0x0B0     0x3F     J8.41  gpio2_10 */       
/*     0x0B4     0x3F     J8.42  gpio2_11 */       
/*     0x0A8     0x3F     J8.43  gpio2_8  */       
/*     0x0AC     0x3F     J8.44  gpio2_9  */       
/*     0x0A0     0x3F     J8.45  gpio2_6  */       
/*     0x0A4     0x3F     J8.46  gpio2_7  */       
                                   
     0x070     0x3F  /* J9.11  gpio0_30 */       
     0x078     0x3F  /* J9.12  gpio1_28 */       
     0x074     0x3F  /* J9.13  gpio0_31 */       
/*     0x048     0x3F     J9.14  gpio1_18 */       
     0x040     0x3F  /* J9.15  gpio1_16 */       
     0x04C     0x3F  /* J9.16  gpio1_19 */       
/*     0x15C     0x3F     J9.17  gpio0_5  */       
/*     0x158     0x3F     J9.18  gpio0_4  */       
/*     0x17C     N/C      J9.19  gpio0_13 */       
/*     0x178     N/C      J9.20  gpio0_12 */       
/*     0x154     0x3F     J9.21  gpio0_3  */       
/*     0x150     0x3F     J9.22  gpio0_2  */       
     0x044     0x3F  /* J9.23  gpio1_17 */       
     0x184     0x3F  /* J9.24  gpio0_15 */       
     0x1AC     0x3F  /* J9.25  gpio3_21 */       
     0x180     0x3F  /* J9.26  gpio0_14 */       
     0x1A4     0x3F  /* J9.27  gpio3_19 */       
     0x19C     0x3F  /* J9.28  gpio3_17 */       
     0x194     0x3F  /* J9.29  gpio3_15 */       
     0x198     0x3F  /* J9.30  gpio3_16 */       
     0x190     0x3F  /* J9.31  gpio3_14 */          
     0x1A8     0x3F  /* J9.41  gpio3_20 */       
     0x1A0     0x3F  /* J9.42  gpio3_18  */       
    >;
   };
  };
 };
        
 fragment@1 {
  target = <&ocp>;
  __overlay__ {

   MF5_gpio {
    compatible = "gpio-of-helper";
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&MF5_gpio_pins>;
                                
                                P9_15 {
                                    gpio-name = "P9_15";
                                    gpio = <&gpio2 16 0>;
                                    output;
                                    init-low;
                                    dir-changeable;
                                };

                                P9_26 {
                                    gpio-name = "P9_26";
                                    gpio = <&gpio1 14 0>;
                                    output;
                                    init-low;
                                    dir-changeable;
                                };

   };
                };
 };

        fragment@2 {
                target = <&pruss>;
                __overlay__ {

                        status = "okay";

                };
        };

};


Se añadido un pequeño cambio al archivo BBB-LCNC-MF5-00A0.dts. Al archivo se le añade el control de inicializar los pines P9.15 y P9.26 como salida en estado bajo. Añadiendo los pines mensionados como salida en estado bajo, se evita tener que desconectar la fuente para no calentar los extrusores hasta que inicie Machinekit.

Para poder visualizar los pines se usa el controlador gpio-of-helper. Cada pin necesita un nombre, para poder usar el gpio se debe poner el número de gpio desfasado en 1, finalmente se pone si se quiere usar como salida o entrada. En el caso de ser salida se debe poner si se quiere en estado bajo o alto.

Ejemplo:

 fragment@1 {
  target = <&ocp>;
  __overlay__ {

   MF5_gpio {
    compatible = "gpio-of-helper";
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&MF5_gpio_pins>;
                                //P9.15
                                Extrusor0 {
                                    gpio-name = "MF5:Extrusor0";
                                    gpio = <&gpio2 16 0>;
                                    output;
                                    init-low;
                                    dir-changeable;
                                };
                                //P9.26
                                Extrusor1 {
                                    gpio-name = "MF5:Extrusor1";
                                    gpio = <&gpio1 14 0>;
                                    output;
                                    init-low;
                                    dir-changeable;
                                };

   };
                };
 };

target = <&ocp> -> define que se podrá visualizar la configuración de los pines en la siguiente dirección "/sys/devices/ocp.*/"

compatible = "gpio-of-helper" -> indica que se usará el controlador gpio-of-helper.

pinctrl-names = "default" -> indica que los pines se configurán por defecto.

pinctrl-0 = <&MF5_gpio_pins> -> indica que se va usar los pines configuados en MF5_gpio_pins.

P9_15 y P9_26 son funciones, puede ir con cualquier nombre ejemplo Extrusor0 y Extrusor1.

gpio-name indica el nombre con el que va a apraecer en "/sys/devices/ocp.3/MF5_gpio.*/status"

En gpio se pone el gpio desfasado 1, P9.15 "gpio1_16" quedaría "gpio2 16 0" lo mismo con P9.26.

Se puede poner al gpio como salida o como entrada, input para entrada y output para salida.

Si se usa como salida se puede poner el estado con el cuál se quiere inicializar el gpio, en alto "init-high" o en bajo "init-low".

dir-changeable permite cambiar el gpio como output o input.

Para que inicie al reiniciar, se debe agregar el archivo DTO a /etc/default/capemgr, ver este link para entender mejor DVK530-LCD7.



Al prender o reiniciar la Beaglebone Black se podrá ver la función MF5_gpio con la dirección en salida usando "cat /sys/devices/ocp.*/MF5_gpio.14/status". Para poder obtener el estado del gpio (alto o bajo), se usa "cat /sys/class/gpio/gpio48/value" si se quiere ver el valor de P9.15 gpio1_16 (1*32+16=48). Para P9.26 se realiza lo mismo.

 

Se recomienda ver los siguientes tutoriales para poder entender:

Device Tree Overlay personalizada para Machinekit y LCD 7"
Configurar los pines gpio para usar con Machinekit. 

Como usar el componente AT PID para encontrar los valores de PID.

Para poder usar el componente AT PID se necesita entender como funciona. En el siguiente link At_pid esta la información de como usar este componente.

El componente AT PID inicia por defecto con los siguientes valores.

pin in float deadband = 0.0 "Amount of error that will be ignored";
pin in float maxerror = 0.0 "Limit on error";
pin in float maxerrorI = 0.0 "Limit on error integrator";
pin in float maxerrorD = 0.0 "Limit on error differentiator";
pin in float maxcmdD = 0.0 "Limit on command differentiator";
pin in float maxcmdDD = 0.0 "Limit on command 2nd derivative";

pin io float bias = 0.0 "Constant offset on output";
pin io float Pgain = 1.0 "Proportional gain";
pin io float Igain = 0.0 "Integral gain";
pin io float Dgain = 0.0 "Derivative gain";
pin io float FF0 = 0.0 "Zeroth order Feedfoioard gain";
pin io float FF1 = 0.0 "First order Feedforward gain";
pin io float FF2 = 0.0 "Second order Feedforward gain";
pin io float maxoutput = 0.0 "Limit on output value";
pin io float tuneEffort = 0.5 " Control effort for limit cycle.";
pin io u32 tuneCycles = 50;
pin io u32 tuneType = 0;

pin out float errorI "Integral of error";
pin out float errorD "Derivative of error";
pin out float commandD "Derivative of the command";
pin out float commandDD "2nd derivative of the command";
pin out float ultimateGain "Calc by auto-tune from limit cycle.";
pin io float ultimatePeriod  "Calc by auto-tune from limit cycle.";


pin in bit enable = 0 "Enable/disabled the PID loop";
pin in float command = 0.0 "Commanded value";
pin in float feedback = 0.0 "Feedback input";
pin out float error "Current error";
pin out float output "Ouput value";
pin in bit tuneMode = 0  "0=PID, 1=tune.";
pin io bit tuneStart = 0  "Set to 1 to start an auto-tune cycle. \
                        Clears automatically when the cycle has finished.";

 
Para poder iniciar la calibración automática de PID es necesario que tuneMode sea uno y tuneStart sea uno. Si se quiere obtener los valores de PID es necesario que tuneType sea cero de lo contrario se obtendrá la función PI con FF1 ("para uso de motores").

if(tuneType == TYPE_PID){
            // PID.
            Pgain = (0.6 * ultimateGain);
            Igain = (Pgain / (ultimatePeriod / 2.0));
            Dgain = (Pgain * (ultimatePeriod / 8.0));
            FF1 = 0;
        }else{
            // PI FF1.
            Pgain = (0.45 * ultimateGain);
            Igain = (Pgain / (ultimatePeriod / 1.2));
            Dgain = 0;

            // Scaling must be set so PID output is in user units per second.
            FF1 = 1;
        }

El componente de At_pid usa el método de Ziegler-Nichols para calcular los valores de PID. Los siguientes links indican como funciona esta configuración.

Reglas de Ziegler-Nichols.
Calibrando un controlador PID.
Guía de uso PID para Arduino.

Primero se debe realizar la siguiente configuración en el archivo .hal

# ##################################################
# PWM Signals
# ##################################################

newsig e0.temp.set   float
newsig e0.temp.meas  float
newsig bed.temp.set  float
newsig bed.temp.meas float
newsig e1.temp.set   float
newsig e1.temp.meas  float

setp hpg.pwmgen.00.pwm_period       1000000

#net spindle-speed-cmd <= motion.spindle-speed-out
#net spindle-speed-cmd => hpg.pwmgen.00.out.00.value

# P9.26 gpio0_14 Extrusor 1
setp hpg.pwmgen.00.out.01.pin       0x2E
setp hpg.pwmgen.00.out.01.enable    1
setp hpg.pwmgen.00.out.01.value     0.0

# J9.16 gpio1_19 Bed
#setp hpg.pwmgen.00.out.00.pin       0x53
#setp hpg.pwmgen.00.out.00.enable    1
#setp hpg.pwmgen.00.out.00.value     0.0

# P9.15 gpio1_16 Extrusor 0
setp hpg.pwmgen.00.out.02.pin       0x50
setp hpg.pwmgen.00.out.02.enable    1
setp hpg.pwmgen.00.out.02.value     0.0


# PID for Extruder 0 temperature control
net e0.temp.meas    <= Extruder0.Temp.meas
net e0.temp.meas    => at_pid.0.feedback => wcomp.0.in

sets e0.temp.set  0
net e0.temp.set motion.analog-out-02    => at_pid.0.command

setp wcomp.0.min 0
setp wcomp.0.max 300

net e0.temp.set => Extruder0.Temp.set
net e0.temp.done motion.digital-in-02 <= Extruder0.Temp.set.done

net enable.pid.extrusor0 at_pid.0.enable <= wcomp.0.out
net e0.heater  <= at_pid.0.output
net e0.heater  => limit1.0.in
net e0.heaterl <= limit1.0.out
net e0.heaterl => hpg.pwmgen.00.out.02.value 

# Limit heater PWM to positive values
# PWM mimics hm2 implementation, which generates output for negative values
setp limit1.0.min 0

# PID for Extruder 1 temperature control
net e1.temp.meas    <= Extruder1.Temp.meas
net e1.temp.meas    => at_pid.1.feedback => wcomp.1.in

sets e1.temp.set  0
net e1.temp.set motion.analog-out-03    => at_pid.1.command 

setp wcomp.1.min 0
setp wcomp.1.max 300

net e1.temp.set => Extruder1.Temp.set
net e1.temp.done motion.digital-in-03 <= Extruder1.Temp.set.done

net enable.pid.extrusor1 at_pid.1.enable <= wcomp.1.out
net e1.heater  <= at_pid.1.output
net e1.heater  => limit1.1.in
net e1.heaterl <= limit1.1.out
net e1.heaterl => hpg.pwmgen.00.out.01.value


# Limit heater PWM to positive values
# PWM mimics hm2 implementation, which generates output for negative values
setp limit1.1.min 0

# PID for Bed temperature control
#net bed.temp.meas    <= Therm.ch-05.value
#net bed.temp.meas    => at_pid.2.feedback => wcomp.2.in

#sets bed.temp.set  0
#net bed.temp.set     => at_pid.2.command

#setp wcomp.2.min 0
#setp wcomp.2.max 120

#net enable.pid.bed at_pid.2.enable <= wcomp.2.out
#net bed.heater  <= at_pid.2.output
#net bed.heater  => limit1.2.in
#net bed.heaterl <= limit1.2.out
#net bed.heaterl => hpg.pwmgen.00.out.00.value

# Limit heater PWM to positive values
# PWM mimics hm2 implementation, which generates output for negative values
setp limit1.1.min 0

# PID Parameters for adjusting temperature control
# Extruder0
setp at_pid.0.Pgain  1
setp at_pid.0.Igain  0
setp at_pid.0.Dgain  0
setp at_pid.0.tuneEffort 0.5
setp at_pid.0.tuneMode 1
setp at_pid.0.tuneStart 1
setp at_pid.0.tuneType 0
setp at_pid.0.tuneCycles 50
setp at_pid.0.maxoutput 0.8
#setp at_pid.0.maxerrorI 12
#setp at_pid.0.bias 0.5

# Extruder1
setp at_pid.1.Pgain  1
setp at_pid.1.Igain  0
setp at_pid.1.Dgain  0
setp at_pid.1.tuneEffort 0.5
setp at_pid.1.tuneMode 1
setp at_pid.1.tuneStart 1
setp at_pid.1.tuneType 0
setp at_pid.1.tuneCycles 50
setp at_pid.1.maxoutput 0.8
#setp at_pid.1.maxerrorI 12
#setp at_pid.1.bias 0.5

# Bed
#setp at_pid.2.Pgain  1
#setp at_pid.2.Igain  0
#setp at_pid.2.Dgain  0
#setp at_pid.2.tuneEffort 1
#setp at_pid.2.tuneMode 1
#setp at_pid.2.tuneStart 1
#setp at_pid.2.tuneType 0
#setp at_pid.2.tuneCycles 50
setp at_pid.1.maxoutput 1
#setp at_pid.2.maxerrorI 1.0
#setp at_pid.2.bias    0.5
#setp at_pid.2.enable  1

Pgain, Igain, Dgain, tuneEffort, tuneType, tuneCycles están configurados con los valores por defecto. Se los puede comentar si se desea. Lo más importante es que maxoutput se active cuando el componente at_pid haya encontrado los valores de PID, ya que de lo contrario no funcionará de forma correcta.

Pgain -> Indica la ganancia proporcional, cambiarlo con el valor P calculado.
Igain -> Indica la ganancia integral, cambiarlo con el valor I calculado.
Dgain -> Indica la ganancia derivativa, cambiarlo con el valor D calculado.
tuneEffort -> Indica el ciclo de trabajo enviado a la señal PWM, con valores muy bajos tardará mucho en calentar el extrusor y con valores altos se tendrán unos valores PID un poco inestables. Para los extrusores que calientan de forma rápida es recomendable usar valores de 0.4 a 0.6, mientras que para la cama caliente que demora en calentar usar un valor de 1.0 será perfecto.
tuneMode -> Indica si se va usar la calibración automática (1) o si se va realizar el proceso de control PID como el componente PID (0).
tuneStart -> Indica que se iniciará la calibración, cuando termina el proceso de calibración se desactiva automáticamente y mientras no se cambie el valor de tuneMode a cero no realizará el proceso de control PID con los valores encontrados.
tuneType -> Indica si se va obtener los valores PID (0) o los valores PI FF1(1).
tuneCycles -> Indica cuántos veces va a calcular los valores, entre más ciclos tenga mejor será el resultado final. Usando 30 ciclos se obtiene valores aceptables (varía la temperatura ±2 grados celsius), se recomienda usar 50 o más ciclos (varía la temperatura ±0.25-0.5 grados celsius). El amplificador max31855 tiene una resolución de 0.25 grados celsius con lo cuál sería una calibración casi perfecta (tomar en cuenta que dependiendo el tipo de termocupla se tiene un error comparado con el valor real, generalmente ±2).
maxoutput -> Indica el ciclo de trabajo con el que va a funcionar la señal PWM una vez que se haya terminado la calibración automática. Para el extrusor con un valor de 0.8 se obtiene un calentamiento rápido y un buen control PID, para la cama caliente usar un valor de 1, ya que el proceso de calentamiento es lento. Es importante poner un valor mayor a cero en maxoutput de lo contrario no funcionará de forma correcta el control PID.
maxerrorI -> Indica el error del integrador, es necesario ir aumentando el valor para disminuir el exceso de temperatura.
bias -> Indica cuanto se desea compensar en temperatura, es ideal cuando se obtiene valores menores a la temperatura asignada. Es recomendable dejarlo por defecto en 0.5 e ir compensando con maxerrorI.
enable -> Activa o desactiva el uso de At_pid.

Existen otras funciones del componente At_pid, pero no se usarán, ya que con las mencionadas anteriormente se han obtenido buenos resultados.


En la configuración del componente AT PID se indicará como funcionan los valores de PID obtenidos, y luego como cambia el control al implementar lo valores de bias y maxerrorI.

Se usará el código O descrito en el anterior tutorial para poder asignar el valor de temperatura con el que trabajarán los extrusores.

Obtener valores PID

Para poder obtener los valores correctos de PID se recomienda dejar por defecto la configuración.


Para poder observar las variable se debe ingresar a settings y luego ejecutar Halshow. Aparecerá el cuadro de la derecha que se puede observar en la imagen de abajo.




Como se puede observar la temperatura es de 20.25 para el extrusor0 y los valores de PID están Pgain 1, Igain 0, Dgain 0, bias 0, feedback 20.25 (valor de tempeartura), maxerror 0, maxerrorI 0, maxerrorD 0, maxoutput 0.8 (80% ciclo de trabajo), output -0.5 (apagado el extrusor), tuneCycles 0x32 (50 en decimal), tuneEffort 0.5 (50% cuando se calibra), tuneMode 1, tuneStart 1, tuneType 0 (PID). Los mismos valores son asignados al extrusor1, solo cambia tuneEffort a 0.8 para que pueda llegar a temperaturas mayores de 200 grados.

Al mandar el comando M104 que asigna la temperatura para el extrusor0 y el extrusor1 en el MDI se dará comienzo a la obtensión automática de los valores PID.

En el extrusor0 se determinara un valor de 180 grados celsius, ideal para trabjar con PLA (175-220 grados celsius). Para el extrusor1 se asignará un valor de 230 grados celsius, ideal para trabajar con ABS (210-250 grados celsius).

Para asignar la temperatura al extrusor0 se usa el comando M104 P180, para el extrusor1 M104 P230 T1.


En este momento los extrusores estarán calentando hasta llegar al valor de temperatura asignado y comenzará el proceso de obtener los valores de PID para los dos extrusores.



El proceso dura alrededor de unos 10 minutos para una temperatura menor a 200 grados celsius con un ciclo de trabajo de 0.5, para el segundo extrusor dura alrededor de 15 minutos con una temperatura de 230 y un ciclo de trabajo de 0.8. Esto ocurre ya que tardá mas en calentar por el ciclo de trabajo de 0.8 con lo que cada conteo tardá de subir de 227 a 230, al bajar y subir es un ciclo de conteo realizado.

Cuando el proceso termina el calentamiento de los dos extrusores termina (se pone en Standby), esto pasa ya que tuneStart esta desactivado pero tuneMode aún sigue en 1.


Si se desea realizar el control PID se debe cambiar la variable tuneMode a 0 para que comience el proceso. Esto se lo realiza usando el comando setp y la variable que querramos cambiar.

Extrusor0 => setp at_pid.0.tuneMode 0
Extrusor1 => setp at_pid.1.tuneMode 0



En el Extrusor0 con los valores de PID obtenidos cambia la temperatura entre 177 y 182.25, en el extrusor1 cambia la temperatura entre 219 y 234. Como se puede observar los valores no están muy mal pero lo que se requiere es que la temperatura que controla el componente AT PID no baje de la asiganada. Para eso lo que se debe realizar es configurar el bias a 0.5 para compensar la temperatura e ir aumentando el valor de maxerrorI para bajar el exceso.

Extrusor0 => setp at_pid.0.bias 0.5
Extrusor1 => setp at_pid.1.bias 0.5

Al colocar estos valores se obtiene los mismos resultados, esto pasa porque bias trabaja en conjunto con maxerrorI y maxerrorD (casi nunca se lo utiliza). Lo que se debe realizar es ir aumentando el maxerrorI.

Con los valores asignados de bias y los siguientes valores de maxerrorI para los extrusores.

Extrusor0 => setp at_pid.0.maxerrorI 1
Extrusor1 => setp at_pid.1.maxerrorI 3 

Se obtiene valores de temperatura para el extrusor0 de entre 180.5-181 grados celsius, y para el extrusor1 de entre 228.75-231.25 grados centigrados.

El extrusor0 estaría perfectamente calibrado mientras que el extrusor1 aun falta compensar la temperatura para que no baje de la asiganda.

Aumentando el bias del segundo extrusor a 0.6 y disminuyendo maxerrorI a 2 ya se regula la temperatura entre 230.25 a 230.75 grados celsius.




He olvidado guardar los valores PID obtenidos en la anterior configuración, con los nuevos valores de PID que pondré a continuación, ha cambiado el valor de maxerrorI en el extrusor0 a 4 y el bias se mantiene en 0.5. Para el extrusor1 el valor de maxerrorI es de 2 y el bias en 0.7.

Con esos valores se ha obtenido una temperatura de 180.75-181 grados celsius en el extrusor0 y  231-231.25 grados celsius en el extrusor1.

Como resultado final se pasa de esto:

# PID Parameters for adjusting temperature control
# Extruder0
setp at_pid.0.Pgain  1
setp at_pid.0.Igain  0
setp at_pid.0.Dgain  0
setp at_pid.0.tuneEffort 0.5
setp at_pid.0.tuneMode 1
setp at_pid.0.tuneStart 1
setp at_pid.0.tuneType 0
setp at_pid.0.tuneCycles 50
setp at_pid.0.maxoutput 0.8
#setp at_pid.0.maxerrorI 12
#setp at_pid.0.bias 0.5

# Extruder1
setp at_pid.1.Pgain  1
setp at_pid.1.Igain  0
setp at_pid.1.Dgain  0
setp at_pid.1.tuneEffort 0.8
setp at_pid.1.tuneMode 1
setp at_pid.1.tuneStart 1
setp at_pid.1.tuneType 0
setp at_pid.1.tuneCycles 50
setp at_pid.1.maxoutput 0.8
#setp at_pid.1.maxerrorI 12
#setp at_pid.1.bias 0.5

a esto:

# PID Parameters for adjusting temperature control
# Extruder0
setp at_pid.0.Pgain  0.05076038
setp at_pid.0.Igain  0.03180257
setp at_pid.0.Dgain  0.02025478
setp at_pid.0.tuneEffort 0.5
setp at_pid.0.tuneMode 0
setp at_pid.0.tuneStart 0
setp at_pid.0.tuneType 0
setp at_pid.0.tuneCycles 50
setp at_pid.0.maxoutput 0.8
setp at_pid.0.maxerrorI 4
setp at_pid.0.bias 0.5

# Extruder1
setp at_pid.1.Pgain  0.0596831
setp at_pid.1.Igain  0.0327806
setp at_pid.1.Dgain  0.02716601
setp at_pid.1.tuneEffort 0.8
setp at_pid.1.tuneMode 0
setp at_pid.1.tuneStart 0
setp at_pid.1.tuneType 0
setp at_pid.1.tuneCycles 50
setp at_pid.1.maxoutput 0.8
setp at_pid.1.maxerrorI 2
setp at_pid.1.bias 0.7

Conclusión: Al cambiar de temperatura se puede descalibrar con los valores de PID obtenidos en la temperatura asignada en el momento de la calibración, pero para no tener que hacer los mismos pasos se puede usar la compensación de bias junto con el maxerrorI y poder calibrar la temperatura a lo que se requiera. Cuanta más temperatura se requiera tuneEffort debe ser mayor de lo contrario tardaría demasiado en llegar a temperaturas altas, lo mismo ocurre con maxoutput se debe dejar con un valor con el cúal no caliente demasiado rápido ni tampoco demasiado lento. La variable maxoutput y tuneEffot no tiene nada que ver ya que tuneEffort se usa solo cuando de realiza la calibración mientras que maxoutput se usa cuando se realiza el control PID. Para realizar el control PID tuneMode y tuneStart deben valer 0 (False).

A continuación dejo los archivos que he usado para esta configuración.

MF5-LCD7

Nota: Leer el archivo README, copiar la carpeta MF5-LCD7 a machinekit/configs y dar permisos de ejecucción con chmod +x a setup.MF5-DTO.sh

jueves, 7 de enero de 2016

Introducción a código O (subrutinas) y descripción de algunos componentes hal.

Ver el anterior tutorial para poder comprender mejor Configurar archivo .ini y .hal (PID) para usar librería MAX31855 con gmoccapy_lcd7.

Este tutorial se enfoca en como utilizar código O para poder usar en una impresora 3D y se describe como usar algunos componentes para utilizarlos en el archivo .hal.

Se recomienda leer esta información O Code para poder entender lo que se explicará en este tutorial.

El código de Machinekit alojado en github contiene subrutinas hechas para poder controlar diferentes acciones de una impresora 3D, este código O esta implementado con un postprocesador para usarse con el programa slic3r.

En el siguiente link G-code Impresora 3D se encuentran códigos G y M para usar con una impresora 3D.

En el siguiente link G-code RepRap se encuentra la información de como usar los códigos G y M. 

g22.ngc y g23.ngc son subrutinas especiales que sirven para usar la impresora 3d sin tener las posiciones del extrusor, para poder crear los objetos calcula por medio de varios componentes la altura y ancho de las capas a insertar y también calcula el grosor del filamento para poder indicar cuanto filamento debe extruir. Para saber como funciona recomiendo revisar los archivos del siguiente link velocidad-extrusión es una configuración compleja de realizar.

g28.ngc se utiliza para poder mover los ejes al punto de referencia inicial (origen) con una impresora 3D.

o<g28> sub
#500=0; home x
#501=0; home y
#502=0; home z
#503=0; home a
#504=38; probe selection io index
#505=38; probe enable io index
o100 if [EXISTS[#<x>] or EXISTS[#<y>] or EXISTS[#<z>] or EXISTS[#<a>]]
    o110 if [EXISTS[#<x>]]
        #500=1;
    o110 endif
    o111 if [EXISTS[#<y>]]
        #501=1;
    o111 endif
    o112 if [EXISTS[#<z>]]
        #502=1;
    o112 endif
    o113 if [EXISTS[#<a>]]
        #503=1
    o113 endif
o100 else
    #500=1; home x
    #501=1; home y
    #502=1; home z
    #503=1; home a
o100 endif
; M65 P#505; disable probe sensor if it was enabled
o101 if [#500 EQ 1]
    G0 X0
    ; M68 E#504 Q1; select probe
    ; M64 P#505; enable probe sensor
    ; G38.2 X-100.0 F100; probe X axis
    ; M65 P#505; disable probe sensor
o101 endif
o102 if [#501 EQ 1]
    G0 Y0
    ; M68 E#504 Q2; select probe
    ; M64 P#505; enable probe sensor
    ; G38.2 Y-100.0 F100; probe Y axis
    ; M65 P#505; disable probe sensor
o102 endif
o103 if [#502 EQ 1]
    G0 Z0
    ; M68 E#504 Q3; select probe
    ; M64 P#505; enable probe sensor
    ; G38.2 Z-10.0 F100; probe Z axis
    ; M65 P#505; disable probe sensor
o103 endif
o104 if [#503 EQ 1]
    o114 if [#<_ini[FDM]VELOCITY_EXTRUSION_ENABLE> EQ 0]    
        G92 A0; set extruder axis to 0
    o114 endif
o104 endif
o<g28> end sub 

Este código se creo para tener compatibilidad con RepRap flavor GCode, lo que hace es usar G28 para mover los ejes al punto de referencia inicial (home). Se pueden mover todos los ejes a la vez solo ejecutando g28 o ir refrenciando los que se deseen.

Ejemplo:

G28 X30 Y0 -> lo que hace es ignorar el valor de X30 y ponerlo como X0, Y0 seguiría en lo mismo. Por lo que como resultado final quedaría G28 X0 Y0 y solo los ejes X e Y se moverían a cero, el eje de A y Z se omitirán y quedarán en en la posición actual.

Nota: El código M y G que esta con ";" al inicio no se ejecuta (esta comentado), se lo puede usar en el caso de tener finales de carrera, donde la posición de cada eje indica el máximo de recorrido de no encontrar el sensor genera un error y se detiene todo el proceso. Para poder usar con finales de carrera es necesario activar "HOME_IGNORE_LIMITS = NO" en el archivo .ini para recorrer los ejes hasta topar el final de carrera. Con eso ya se puede descomentar los ejes que se requieran usar con finales de carrera y poner una posición de recorrido de acuerdo a la máquina.

Para que funcione esta subrutina es necesario tener esto en el archivo .ini.
[FDM]
VELOCITY_EXTRUSION_ENABLE = 1

g29.ngc

o<g29> sub
o100 if [EXISTS[#<_ini[PROBE]HBP_TEMP>]]
    M190 S#<_ini[PROBE]HBP_TEMP>
o100 endif
#501=38; probe selection io index
#502=0; probe enable io index
M65 P#502; disable probe sensor if it was enabled
M68 E#501 Q0; select probe
G10 L2 P1 Z0.0; reset z offset
G0 Z#<_ini[PROBE]START_Z>
G43 H#<_ini[PROBE]TOOL_NR>
G0 X#<_ini[P0]X> Y#<_ini[P0]Y>
M64 P#502; enable probe sensor
G38.2 Z#<_ini[AXIS_2]MIN_LIMIT> F#<_ini[PROBE]SEARCH_VEL>; probe Z axis
M65 P#502; disable probe sensor
G10 L20 P1 Z0.0; set Z offset
G49
G0 X#<_ini[P0]X> Y#<_ini[P0]Y> Z#<_ini[PROBE]END_Z>
o<g29> end sub

La subrutina g29 sirve para probar la altura de la cama caliente en tres o más puntos para poder realizar una impresión 3d de acuerdo al nivel de altura tomado en cada punto de la cama caliente. Por el momento solo la comprobación en un punto es soportada.

Se necesita esto en el archivo .ini para que funcione.

[PROBE]
COUNT = 1
SEARCH_VEL = 10.0
TOOL_NR = 100
START_Z = 0.0
END_Z = 1.0
#HBP_TEMP = 50.0

[P0]
X = 0.0
Y = 0.0

tool.tbl

T0 P0 ; Extruder 0
T1 P1 ; Extruder 1
T2 P2 ; Extruder 2
T3 P3 ; Extruder 3
T100 P100 X+35.000000 Z+0.900000 ; probe head
 
Lo primero que hace es indicar si la variable "HBP_TEMP" existe, el valor de esta variable indica la temperatura a la que se va a calentar la cama. Si existe espera a que se caliente.

M65 se deshabilita el sensor para poder comenzar con la prueba de calibración de nivel.
M68 escoge la señal analoga a probar (E=38) y recibe un valor de cero (Q=0)
G10 con L2 indica que se requiere guardar el valor de Z=0 en el código G54, ya que P=1. Por lo tanto el eje Z regresa a la posición 0.
G0 Z#<_ini[PROBE]START_Z> verifica si Z esta en la posición guardada en la variable START_Z, sino se mueve a la posición que este designada.
G43 H#<_ini[PROBE]TOOL_NR> con G43 se indica  la compensación de la herramienta, con el valor de H se le indica que escoga los valores del archivo tool.tbl. Al ser TOOL_NR=100, se ecoge la herramienta T100 con los valores de X+35 y Z+0.9 (estos valores estan configurados en el archivo .ini usando mm).
G0 X#<_ini[P0]X> Y#<_ini[P0]Y> mueve los ejes X e Y a las posiciones designadas en [PO].
M64 habilita el sensor.
G38.2 Z#<_ini[AXIS_2]MIN_LIMIT> F#<_ini[PROBE]SEARCH_VEL> Una vez que esta habilitada la señal del sensor, G38.2 buscará si ha hecho contacto con la cama caliente siendo el punto máximo que se moverá el valor minimo asignado en la configuración del eje Z en el archivo .ini (son los límites de movimento). Con la variable SEARCH_VEL se le da la velocidad con la que se quiere mover el eje, en este caso  SEARCH_VEL=10 (se coloca velocidades bajas para poder recibir la señal del sensor, de lo contrario la señal del sensor podría activarse luego de haber sobrepasado el punto de contacto).
M65 desactiva de nuevo la señal una vez que se ha hecho contacto con la cama caliente.
G10 L20 se determina la compensación de la herramienta, en este caso Z=0 no hay compensación y esto se guarda en el código G54, ya que P=1.
G49 activa la compensación de la herramienta.
G0 X#<_ini[P0]X> Y#<_ini[P0]Y> Z#<_ini[PROBE]END_Z> Por último se mueve a los ejes X e Y  a cero y se le da a Z un offset de 1.0 mm.

Ejemplo:

G29

Nota: se puede cambiar los valores de X e Y en [PO] para realizar la prueba en varios puntos de la cama caliente.

g29_1.ngc y g29_1.ngc son similares a g9, en g29.1 se determina la compensación de la cabezera del extrusor, insertando manualmente las coordenadas de X,Y y Z. En g29.2 se realiza la compensación de la herramienta insertando X,Y y Z solo insertando el valor de Z, para luego comparar con los valores de la herramienta determinados en tool.tbl.

Ejemplo:

G29.1 X30 Y20 Z0.5
G29.2 Z0.0

g30.ngc es exactamente lo mismo a g29.ngc ya que verifica la altura de la  cabezera del extrusor en un punto de forma automática.

Ejemplo:

G30 X10 Y0

Códigos Auxiliares M

Como usar M68 con el archivo .hal.

Para poder controlar la temperatura es necesario usar el componente de pid, para mas información revisar el siguiente link PID.

Para poder medir la temperatura se conecta lo siguiente. Donde Extruder0 es la variable asignada al componente hal_temp_max31855.

net e0.temp.meas    <= Extruder0.Temp.meas
net e0.temp.meas    => pid.0.feedback

Indica como mandar la temperatura al componente pid para poder comenzar a calentar los extrusores. La variable motion.analog-out-02 sirve para guardar el valor de temperatura enviado en el código M68. Para luego almacenarlo en la variable e0.temp.set.

sets e0.temp.set  0
net e0.temp.set motion.analog-out-02    => pid.0.command 

Con el valor almacenado en e0.temp.set, se envia a la variable hal creada en el componente hal_temp_max31855 para poder comparar la temperatura asignada con la temperatura medida por la termocupla. Si la temperatura medida por la termocupla llega a ser mayor que la asignada, se activa la señal de motion.digital-in-02 finalizando el proceso de espera para calentar el extrusor.  Ver código M109.

net e0.temp.set => Extruder0.Temp.set
net Temp.done motion.digital-in-02 <= Extruder0.Temp.set.done
 
Con el valor almacenado en pid.0.command se obtiene el valor de pid.0.out que es la temperatura asiganda, en el limit1.0.in se guarda el valor de temperatura asigando y con limit1.0.out se genera la señal pwm que calentará el fusor del extrusor.

net e0.heater  <= pid.0.output
net e0.heater  => limit1.0.in
net e0.heaterl <= limit1.0.out
net e0.heaterl => hpg.pwmgen.00.out.02.value
 
Si no se configura los valores de pid por defecto se inicializa con los siguientes valores.

pin in float Pgain = 1.0 "Proportional gain";
pin in float Igain = 0.0 "Integral gain";
pin in float Dgain = 0.0 "Derivative gain";
pin in float bias = 0.0 "Constant offset on output";
pin in float FF0 = 0.0 "Zeroth order Feedforward gain";
pin in float FF1 = 0.0 "First order Feedforward gain";
pin in float FF2 = 0.0 "Second order Feedforward gain";
pin in float deadband = 0.0 "Amount of error that will be ignored";
pin in float maxerror = 0.0 "Limit on error";
pin in float maxerrorI = 0.0 "Limit on error integrator";
pin in float maxerrorD = 0.0 "Limit on error differentiator";
pin in float maxcmdD = 0.0 "Limit on command differentiator";
pin in float maxcmdDD = 0.0 "Limit on command 2nd derivative";
pin in float maxoutput = 0.0 "Limit on output value";
pin in bit index_enable = false "Index enable";
pin in bit error_previous_target = false "Error previous target";

Señales PWM

# P9.26 gpio0_14 Extrusor 1
setp hpg.pwmgen.00.out.01.pin       0x2E
setp hpg.pwmgen.00.out.01.enable    1
setp hpg.pwmgen.00.out.01.value     0.0

# J9.16 gpio1_19 Bed
#setp hpg.pwmgen.00.out.00.pin       0x53
#setp hpg.pwmgen.00.out.00.enable    1
#setp hpg.pwmgen.00.out.00.value     0.0

# P9.15 gpio1_16 Extrusor 0
setp hpg.pwmgen.00.out.02.pin       0x50
setp hpg.pwmgen.00.out.02.enable    1
setp hpg.pwmgen.00.out.02.value     0.0

# P9.16 gpio1_19 Fan
setp hpg.pwmgen.00.out.03.pin       0x4C
setp hpg.pwmgen.00.out.03.enable    1
setp hpg.pwmgen.00.out.03.value     0.0 

m104.ngc

o<m104> sub
#500=2; extruder io start index
#501=0; not synchronized
#502=#500
o100 if [EXISTS[#<i>]]
    o110 if [#<i> EQ 1]
        #501=1
    o110 endif
o100 endif
o101 if [EXISTS[#<t>]]
    #502=[#<t>+#500]
o101 endif
o102 if [#501 EQ 1]
    M67 E#502 Q#<P>
o102 else
    M68 E#502 Q#<P>
o102 endif
o<m104> end sub
 
La subrutina m104 sirve para poder calentar los extrusores con un valor de temperatura asigando.

#500=2 indica que se va a usar M68 con motion.analog-out-02
#501=0 inidica que se va a usar M68 de forma inmediata (no hay sincronización).
#502=#500 se guarda la variable 500 en 502, con esta variable podemos escoger otros motion.analog-out-xx.
Si existe la variable i y equivale a 1 #501=1 se realiza un proceso de forma no sincronizada.

Ejemplo:

M014 I1 P200 -> La temperatura se calienta de forma sincronizada hasta 200 grados celsius.

Si existe la variable t, se puede controlar más extrusores ya que la variable #500 canbiará de acuerdo al número en t asignado.

Ejemplo:

M104 P200 T1 -> El extrusor a calentar será el que este con motion.analog-out-03, ya que se suma el valor de t con el de #502.

Por último siempre se calienta de forma no sincronizada (inmediata).

m106.ngc

Activa los ventiladores.

o<m106> sub
#500=12; fan io start index
#501=1; synchronized
#502=#500
o100 if [EXISTS[#<i>]]
    o110 if [#<i> EQ 1]
        #501=0
    o110 endif
o100 endif
o101 if [EXISTS[#<t>]]
    #502=[#<t>+#500]
o101 endif
o102 if [#501 EQ 1]
    M67 E#502 Q#<P>
o102 else
    M68 E#502 Q#<P>
o102 endif
o<m106> end sub

La subrutina m106 sirve para poder controlar los ventiladores de la impresora 3D de forma sincronizada. Se utiliza el mismo procedimiento, explicado anteriormente. Se debe cambiar el GPIO a usarse como pwm en el archivo .hal y realizar las conexiones necesarias.

sets e0.fan.set  0
net e0.fan.set motion.analog-out-12    => hpg.pwmgen.00.out.03.value

Ejemplo:

M106 P127

Donde P asigna la velocidad (0-255)

M106 P127 t1

ver ejemplo M104

m107.ngc 

Desactiva los ventiladores


o<m107> sub
#500=12; fan io start index
#501=0; not synchronized
#502=#500
o100 if [EXISTS[#<i>]]
    o110 if [#<i> EQ 1]
        #501=1
    o110 endif
o100 endif
o101 if [EXISTS[#<t>]]
    #502=[#<t>+#500]
o101 endif
o102 if [#501 EQ 1]
    M67 E#502 Q0
o102 else
    M68 E#502 Q0
o102 endif
o<m107> end sub

Controla los ventiladores de forma no sincronizada. Se utiliza el mismo procedimiento, explicado anteriormente.

Ejemplo:

M107 P0

Donde P asigna la velocidad (0-255) -> en este caso con P0 se detiene el ventilador.

Esto es lo mismo que M106 pero al crear en el código G en Slic3r sin el postprocesador de machinekit se puede obtener errores si no existe M107.

M106 P127 T1 I1

ver ejemplo M104

m109.ngc

o<m109> sub
#500=2; extruder io start index
#502=#500
o100 if [EXISTS[#<t>]]
    #502=[#<t>+#500]
o100 endif
M68 E#502 Q#<P>
M66 P#502 L3 Q1000
o<m109> end sub
 
Esta subrutina sirve para poder calentar el fusor del extrusor y hasta que no sea igual o mayor a la temperatura asignada, se pausará el proceso por 1000 segundos.

M66 Con m66 se comprueba una señal digital, existen 4 tipos. El valor de P indica que se va a usar motion.input-out-xx con L elegimos el tipo de señal, en este caso es WAIT_MODE_HIGH y con Q se escoge el tiempo de espera en segundos.

Nota: Para que se llegue a enviar la temperatura asignada a la variable  Extruder0.Temp.set del archivo hal_temp_max31855 se debe realizar un tiempo de espera pequeño de lo contrario no se enviará nada a la variable. Con G4 P5 se espera 5 segundos. Esto no afecta en nada ya qe hasta mientras los extrusores se seguirán calentando.

o<m109> sub
#500=2; extruder io start index
#502=#500
o100 if [EXISTS[#<t>]]
    #502=[#<t>+#500]
o100 endif
M68 E#502 Q#<P>
G4 P5
M66 P#502 L3 Q1000
o<m109> end sub

Ejemplo:

M109 P185

Donde P determina la temperatura Q#<P>.

M109 P127 T1

ver ejemplo M104
m140.ngc

o<m140> sub
#501=0; not synchronized
#502=0; hbp index
o100 if [EXISTS[#<i>]]
    o110 if [#<i> EQ 1]
        #501=1
    o110 endif
o100 endif
o101 if [#501 EQ 1]
    M67 E#502 Q#<P>
o101 else
    M68 E#502 Q#<P>
o101 endif
o<m140> end sub 

La subrutina m140 envia la temperatura a la cama caliente de forma inmediata.

Ejemplo:

M140 P55

Donde P asigna la temperatura Q#<P>.

M140 P127 I1

ver ejemplo M104

m141.ngc

o<m141> sub
#501=0; not synchronized
#502=1; hbc index
o100 if [EXISTS[#<i>]]
    o110 if [#<i> EQ 1]
        #501=0
    o110 endif
o100 endif
o101 if [#501 EQ 1]
    M67 E#502 Q#<P>
o101 else
    M68 E#502 Q#<P>
o101 endif
o<m141> end sub 

Se usa en impresoras 3d con cámara cerrada, tiene la misma función que M140.

Ejemplo:

M141 P30

Donde P asigna la temperatura Q#<P>.

M141 P127 I1

ver ejemplo M104

m190.ngc

o<m190> sub
#502=0; hbp index
M68 E#502 Q#<P>
M66 P#502 L3 Q1000
o<m190> end sub

Espera a que la cama se caliente para seguir con el proceso.

Ejemplo:

M190 P60

Donde P asigna la temperatura Q#<P>.

M190 P127

ver ejemplo M109

Nota: No existe la variable T ya que solo se usa una cama caliente.

m191.ngc

o<m191> sub
#502=1; hbc index
M68 E#502 Q#<P>
M66 P#502 L3 Q1000
o<m191> end sub

Se usa en impresoras 3d con cámara cerrada, tiene la misma función que M190.

Ejemplo:

M191 P50

Donde P asigna la temperatura Q#<P>.

M191 P127

ver ejemplo M109

Nota: No existe la variable T ya que solo se usa una cámara cerrada.
m200.ngc

o<m200> sub
#500=44 ; filament diameter index
M68 E#500 Q#<D>
o<m200> endsub
 
Determina el diámetro del filamento, ideal para ser usado con g22 y g23.

Ejemplo:

M200 D1.75.

m207.ngc

o<m207> sub
#500=47 ; retract length index
#501=48 ; retract velocity index
M68 E#500 Q#<P>
M68 E#501 Q#<Q>
o<m207> endsub

Utilizado para determinar la longitud y velocidad de extrusión sin tener código G para el extrusor. Ver g22 y g23.

Ejemplo:

M207 P0.5 Q10.0

Donde P determina la longitud en mm y Q la velocidad en mm/s.

m221.ngc

o<m221> sub
#500=49 ; extrude scale index
M68 E#500 Q#<P>
o<m221> endsub

Determina la escala con la que se quiere obtener la salida (extrusión).

Ejemplo:

M221 P0.96

P indica la escala de salida en porcentaje, en este caso 96%.

m226.ngc

o<M226> sub
    M1
o<M226> end sub

Pausa el programa.

Ejemplo:

M226

m280.ngc

o<m280> sub
#500=22; servo io start index
#501=0; not synchronized
#502=#500
o100 if [EXISTS[#<i>]]
    o110 if [#<i> EQ 1]
        #501=1
    o110 endif
o100 endif
o101 if [EXISTS[#<t>]]
    #502=[#<t>+#500]
o101 endif
o102 if [#501 EQ 1]
    M67 E#502 Q#<P>
o102 else
    M68 E#502 Q#<P>
o102 endif
o<m280> end sub

Sirve para controlar la posición del motor.

Ejemplo:

M280 T0 P1500

Donde T selecciona el motor a usar y P indica el ángulo o microsegundos.

m300.ngc

o<m300> sub
#500=39; buzzer frequency index
#501=40; buzzer duration index
#502=2; buzzer trigger index
#503=0; not synchronized
o100 if [EXISTS[#<i>]]
    o110 if [#<i> EQ 1]
        #503=0
    o110 endif
o100 endif
o101 if [#503 EQ 1]
    o111 if [EXISTS[#<q>]]
        M67 E#500 Q#<q>; frequency
    o111 endif
    M67 E[#500+1] Q#<P>; duration
    o113 if [#[#502+100] EQ 1]; trigger beep
        M63 P#502
        #[#502+100]=0
    o113 else
        M62 P#502
        #[#502+100]=1
    o113 endif
o101 else
    o121 if [EXISTS[#<q>]]
        M68 E#500 Q#<q>; frequency
    o121 endif
    M68 E[#500+1] Q#<P>; duration
    o123 if [#[#502+100] EQ 1]; trigger beep
        M65 P#502
        #[#502+100]=0
    o123 else
        M64 P#502
        #[#502+100]=1
    o123 endif
o101 endif
o<m300> end sub

Emite una señal sonora.

Ejemplo:

M300 Q300 P1000

Donde Q es la frecuencia y P es la duración en milisegundos

m400.ngc

o<m400> sub
    G4 P0
o<m400> end sub

Espera que los movimientos que se están realizando finalicen.

Ejemplo:

M400

si se usa G4 P10

P es el tiempo de espera en segundos.

m420.ngc

o<m420> sub
#500=26; light io start index
#501=0; not synchronized
#502=#500
o104 if [EXISTS[#<t>]]
    #502=[#<t>*4+#500]
o104 endif
#503=[#502+1]
#504=[#502+2]
#505=[#502+3]
o100 if [EXISTS[#<r>]]
    o110 if [#501 EQ 1]
        M67 E#502 Q#<r>
    o110 else
        M68 E#502 Q#<r>
    o110 endif
o100 endif
o101 if [EXISTS[#<e>]]
    o111 if [#501 EQ 1]
        M67 E#503 Q#<e>
    o111 else
        M68 E#503 Q#<e>
    o111 endif
o101 endif
o102 if [EXISTS[#<d>]]
    o112 if [#501 EQ 1]
        M67 E#504 Q#<d>
    o112 else
        M68 E#504 Q#<d>
    o112 endif
o102 endif
o103 if [EXISTS[#<p>]]
    o113 if [#501 EQ 1]
        M67 E#505 Q#<p>
    o113 else
        M68 E#505 Q#<p>
    o113 endif
o103 endif
o<m420> end sub

Controla los colores RGBW con PWM.

M420 T<LED index (0-2)> R<Red PWM (0-1)> E<Green PWM (0-1)> D<Blue PWM (0-1)> P<White PWM (0-1)>

Ejemplo:

M420 R1.0 E1.0 D1.0 P1.0
 
m700.ngc

o<m700> sub
#500=41; filament area index
#501=1; synchronized
#502=#500
o100 if [EXISTS[#<i>]]
    o110 if [#<i> EQ 1]
        #501=0
    o110 endif
o100 endif
o101 if [#501 EQ 1]
    M67 E#502 Q#<P>
o101 else
    M68 E#502 Q#<P>
o101 endif
o<m700> end sub

Cálcula la sección de área del filamento, se usa con g22 y g23.

Ejemplo:

M700 P0.061

P determina el valor que se va a calcular con la función de velocidad de extrusión.

m701.ngc

o<m701> sub
#500=42; line width index
#501=1; synchronized
#502=#500
o100 if [EXISTS[#<i>]]
    o110 if [#<i> EQ 1]
        #501=0
    o110 endif
o100 endif
o101 if [#501 EQ 1]
    M67 E#502 Q#<P>
o101 else
    M68 E#502 Q#<P>
o101 endif
o<m701> end sub

Determina el ancho de línea a calcular.

Ejemplo:

M701 P0.2

P determina el valor del ancho que se puede usar con  la altura de línea para calcular la sección de cruce. Forma parte de la función velocidad de extrusión.

m701.ngc

o<m701> sub
#500=43; line height index
#501=1; synchronized
#502=#500
o100 if [EXISTS[#<i>]]
    o110 if [#<i> EQ 1]
        #501=0
    o110 endif
o100 endif
o101 if [#501 EQ 1]
    M67 E#502 Q#<P>
o101 else
    M68 E#502 Q#<P>
o101 endif
o<m701> end sub

Determina la altura de línea a calcular.

Ejemplo:

M702 P0.15

P determina el valor de la altura que se puede usar con  el ancho de línea para calcular la sección de cruce. Forma parte de la función velocidad de extrusión.

m710.ngc

o<m710> sub
#500=45; jog velocity index
#501=46; jog length index
#502=12; jog trigger index
#503=13; jog continuous index
#504=14; jog direction index
o100 if [EXISTS[#<q>]]
    o110 if [#<q> LT 0]
        M68 E#500 Q[#<q> * -1]; inverse velocity
        M64 P#504
    o110 else
        M68 E#500 Q#<q>; velocity
        M65 P#504
    o110 endif
o100 endif
o101 if [EXISTS[#<p>]]
    M68 E#501 Q#<p>; length
o101 endif
o102 if [EXISTS[#<e>]]
    o112 if [#<e> EQ 1]; trigger continuous
        M64 P#503
    o112 else
        M65 P#503
    o112 endif
o102 else
    M64 P#502
o102 endif
o<m710> end sub

Determina cuanto filamento se va a extraer.

Ejemplo:

M710 P10.0 Q2.0

Donde P indica la longitud en mm y Q  la velocidad en mm/s.

Con esto finaliza la explicación de códigos G y M usados en las impresoras 3d.

Para poder configurar los códigos explicados se debe incluir lo siguiente en el archivo .ini.

[RS274NGC]
# Machinekit fdm subroutines path
SUBROUTINE_PATH = /usr/share/linuxcnc/ncfiles/remap-subroutines/fdm
# alternatively you can copy the subroutines a local subroutines folder
# SUBROUTINE_PATH = subroutines

# remapping Machinekit FDM GCodes
REMAP=G22 modalgroup=1 ngc=g22
REMAP=G23 modalgroup=1 ngc=g23
REMAP=G28 modalgroup=1 argspec=xyza ngc=g28
REMAP=G29 modalgroup=1 ngc=g29
REMAP=G29.1 modalgroup=1 argspec=xyz ngc=g29_1
REMAP=G29.2 modalgroup=1 argspec=xyz ngc=g29_2
REMAP=G30 modalgroup=1 argspec=pxy ngc=g30
REMAP=M104 modalgroup=10 argspec=iPt ngc=m104
REMAP=M106 modalgroup=10 argspec=iPt ngc=m106
REMAP=M107 modalgroup=10 argspec=it ngc=m107
REMAP=M109 modalgroup=10 argspec=tP ngc=m109
REMAP=M140 modalgroup=10 argspec=iP ngc=m140
REMAP=M141 modalgroup=10 argspec=iP ngc=m141
REMAP=M190 modalgroup=10 argspec=P ngc=m190
REMAP=M191 modalgroup=10 argspec=P ngc=m191
REMAP=M200 modalgroup=10 argspec=D ngc=m200
REMAP=M207 modalgroup=10 argspec=PQ ngc=m207
REMAP=M221 modalgroup=10 argspec=P ngc=m221
REMAP=M226 modalgroup=10 ngc=m226
REMAP=M280 modalgroup=10 argspec=itP ngc=m280
REMAP=M300 modalgroup=10 argspec=iqP ngc=m300
REMAP=M400 modalgroup=10 ngc=m400
REMAP=M420 modalgroup=10 argspec=itredp ngc=m420
REMAP=M700 modalgroup=10 argspec=iP ngc=m700
REMAP=M701 modalgroup=10 argspec=iP ngc=m701
REMAP=M702 modalgroup=10 argspec=iP ngc=m702
REMAP=M710 modalgroup=10 argspec=epq ngc=m710
# enable ini parameter passing
FEATURES = 4

[FILTER]
PROGRAM_EXTENSION = .gcode RepRap Flavour GCode
gcode = gcode-to-ngc

El path puede ser cambiado a la carpeta donde se quiera guardar los códigos G y M.

Toda esta información fue extraida del siguiente link Machinekit-configs, en ese link se indican diferentes configuraciones para varias máquinas. Incluso como configurar la impresora 3d reemplazando el archivo .hal por un archivo python .py.

Componentes Machinekit

Abs
Abs s32
And2
Andn
At pid
Bin2gray
Biquad
Bitslice
Bitwise
Bldc hall3
Blend
Clarke2
Clarke3
Clarkeinv
Comp
Constant
Ddt
Deadzone
Debounce
Div2
Edge
Estop latch
Feedcomp
Flipflop
Gantry
Gearchange
Gray2bin
Hbridge
Hypot
Idb
Ilowpass
Integ
Invert
Io muxn
Joyhandle
Knob2float
Latencybins
Led dim
Lgantry
Limit1
Limit2
Limit3
Lincurve
Lowpass
Lut5
Lutn
Maj3
Match8
Minmax
Mult2
Multiclick
Multiswitch
Mux16
Mux2
Mux4
Mux8
Muxn
Muxn u32
Near
Neg
Not
Offset
Oneshot
Or2
Orient
Orn
Out to io
Pid
Reset
Safety latch
Sample hold
Scale
Select8
Selectn
Sphereprobe
Stats
Sum2
Thc
Thcud
Threadtest
Time
Timedelay
Toggle
Toggle2nist
Tristate bit
Tristate float
Updown
Wcomp
Wcompn
Weighted sum
Xor2

Para entender como usar estos componentes se debe revisar el siguiente link Machinekit Documentation. En la parte izquierda se encuentra el índice, desplazando la barra se encuentra la información de todos los componentes.

Existe componentes muy útiles como el THC para controlar la altura de la antorcha de una plasma, PID, AT_PID para encontrar los valores PID de forma automática, DDT para derivar un valor, útil para encontrar velocidad y aceleración a partir de la distancia y muchos más que se irán usando en nuevos tutoriales.

Se va dar un ejemplo de como usar el componente wcomp, ya que con este se va controlar los límites máximos y mínimos de la temperatura.

En el archivo .hal se debe añadir los componentes que se quiera usar.

Cargando el componente wcomp para usar en los dos extrusores.

loadrt wcomp count=2
loadrt pid count=2

Conectando la señal de wcomp al comando pid feedback para obtener el valor que esta midiendo.

net e0.temp.meas    <= Extruder0.Temp.meas
net e0.temp.meas    => pid.0.feedback => wcomp.0.in

Ahora se establece los límtes máximo y mínimo para controlar que no sobrepase la temperatura, importante para extrusores de plástico. En mi caso uso extrusores de aluminio asi que no hay ningún problema con trabajar con temperaturas altas, con le que he asignado un límite máximo de 300 grados celsius.

setp wcomp.0.min 0
setp wcomp.0.max 300 # Maximum temperature limit

Si el valor se mantiene dentro de los límites de temperatura, wcomp.0.out tendrá el valor de 1 con lo que mantendrá activada la función PID.

net enable.pid.extrusor0 pid.0.enable <= wcomp.0.out


Conclusiones: Existen varios códigos G y M que se pueden usar directamente con linuxcnc, principalmente con la GUI gmoccapy que por medio de botones se puede hacer lo mismo que las subrutinas descritas en la parte superior.

Los componentes Hal ahorran mucho el proceso de estar programando para funciones especificas de una máquina y al estar distribuidos en varios permite usar cada código en lo que se necesite.
En el caso del componente hal_temp_max31855 se le añadido una comparación directa que determina si la temperatura medida es igual o mayor a la asignada para continuar con el código G. Pero esto se puede realizar con el componente hal wcomp como se ha explicado en la parte superior. En mi caso la dejaré así y usaré los límites de temperatura con el termistor de la cama caliente.

El siguiente tutorial va ha ser de como usar el componente at_pid, donde se explicará como llegar a configurar de forma correcta la temperatura una vez obtenido los valores PID de forma automática.