I am looking to add THC (torch height control) to my plasma table. I am currently using an Arduino Mega 2560 with grbl and a breakout board to control the table. The THC function needs only control the Z axis while torch is cutting, the grbl controls Z axis to set Z zero before each cut. Basically the THC reads plasma voltage which is directly related to the height of the tip from cutting surface. There is an optimal distance that needs to be maintained for optimal cuts. As the metal heats during the cuts, it moves, so torch height needs to move the hold same distance relative to the shifting metal surface being cut.
I'm know enough to change pin locations and basic parameters in Arduino IDE. Changing code functions like I am hoping to receive help with, I know nothing. I currently have a setup that uses a nextion screen and 2 mega 2560's, but it's more complicated than I want using a slider button on the screen to adjust the target voltage for the current cut, and it doesn't always work smooth. The setup I would love to go with uses a Nano, a 10K pot, a 7 segment 4 digit lcd and some other small elec components to achieve the same thing with far less fuss. It should even be possible to use 1 Mega 2560 to run both Grbl and THC. Again I just have no clue how to do it.
The code I'm going to show and link is setup to send commands to linuxCNC and then it tell the machine what to do. I don't run that setup. I just need help getting the code changed to command a stepper motor driver using step and direction instead of UP Down. Help getting the code to play nice inside Grbl code and using a Mega 2560 would be fantstic.
The THC code I need help modifying or including in the grbl code.
/* A braindead plasma torch THC. Goes along with the board schematic in this repo.
The gist of how this is works is, on startup, you read the potentiometer and
pick a set point. Then it reads the analog pin for the plasma voltage, does
comparisons to that setpoint, and triggers the optocouplers accordingly.
There's a little bit of smoothing that goes into to the ADC reads to improve
the signal's stability a bit, but we can't do too much, since otherwise, the
LinuxCNC can't respond fast enough when the torch height really is changing.
I do a few tricks in here to make sure the loop() function runs quickly. Stuff
like only reading the setpoint on startup (so if you want to adjust it, hit the
reset button), using bitwise shifts for division, etc. Last I checked, this was
able to do about 6000 samples per second, whereas the maximum you could get
from simply looping analogRead() is supposed to be 9000 samples per second
(with 10 bit precision).
Two more noteworthy quirks:
Firstly, while we let the signals for the plasma change rather rapidly, we only
update the display several times a second and give it a much, much longer
average. This just makes it easier on the eyes.
Secondly, this is going to be giving the "UP" signal whenever the plasma
is not cutting (since the voltage will be 0), so it is important to use
LinuxCNC's HAL configs to only respect the THC's signals once the torch is
cutting and is not piercing or cornering. The "thcud" component should help
with that, as well as additional YouTube videos and files coming soon.
(c) The Swolesoft Development Group, 2019
License: http://250bpm.com/blog:82
*/
// Need this for my common-cathod sevseg LCD.
// The pins follow the schematic and were picked to try to make it lay out nicely.
#include <SevSeg.h>
SevSeg sevseg;
byte numDigits = 4;
byte digitPins[4] = {12, 10, 9, 2};
byte segmentPins[8] = {13, 8, 4, 6, 7, 11, 3, 5};
// Setting the scale for the converting analogRead values to volts.
// 4.450 AREF voltage * 50 built-in voltage divider / 1023 resolution = 0.21749755 ADC counts per volt
// As far as I can tell, the arithmetic below *does* get optimized out by the compiler.
#define SCALE (4.450*50/1023)
// Threshold in ADC counts for when we say the torch is out of range.
// Multiply by SCALE for the threshold in volts.
// FYI: Some degree of buffer is needed to prevent awful see-sawing.
#define THRESH 5
// Adjustment range for the knob.
#define MINSET 110
#define MAXSET 150
// Naming other pins.
#define ADJUST A0
#define PLASMA A1
#define UP A2
#define DOWN A3
#define BUFSIZE 512 // Would technically let us do running averages up to BUFSIZE samples. In testing, shorter averages seemed better.
#define SAMP 16 // Use this many samples in the average; must be a power of 2 and no larger than BUFSIZE.
#define DISP 1024 // The number of samples to use in calculating a slower average for the display. Must also be a power of 2.
unsigned int shift = 0;
unsigned int values[BUFSIZE] = {0}; // buffer for ADC reads
unsigned long total = 0; // for keeping a rolling total of the buffer
unsigned long disp = 0; // for separately tracking ADC reads for the display
unsigned long target = 0; // voltage target, in ADC counts
// for tracking when to set opto pins
int diff = 0;
int mean = 0;
int mode = -1;
// generic temp vars
unsigned long tmp = 0;
float ftmp = 0;
float ftmp2 = 0;
// generic looping vars
int i = 0;
int j = 0;
// for the startup adjustment period
unsigned long timelimit = 0;
unsigned long ms = 0;
void setup() {
// The usual.
pinMode(ADJUST, INPUT);
pinMode(PLASMA, INPUT);
pinMode(UP, OUTPUT);
pinMode(DOWN, OUTPUT);
// Set the reference voltage to the external linear regulator
// Do a few throwaway reads so the ADC stabilizes, as recommended by the docs.
analogReference(EXTERNAL);
analogRead(PLASMA); analogRead(PLASMA); analogRead(PLASMA); analogRead(PLASMA); analogRead(PLASMA);
// We need to calculate how big the shift must be, for a given sample size.
// Since we are using bitshifting instead of division, I'm using a != here,
// so your shit will be totally broke if you don't set SAMP to a power of 2.
while((1 << shift) != SAMP)
shift++;
// Set up the LCD. Set an easily identifiable string (looks like "ABCD" all caps)
// and show it for a moment. This makes it easy to see when the arduino reboots.
sevseg.begin(COMMON_CATHODE, numDigits, digitPins, segmentPins, 0, 0, 1);
sevseg.setBrightness(50);
sevseg.setChars("a8c0");
for (i = 0; i < 500; i++) {
sevseg.refreshDisplay();
delay(1);
}
// Now enter the period where you can set the voltage via the potentiomenter.
// Default 5s period, plus an extension 2s as long as you keep adjusting it.
// By fixing this after boot, we save cycles from needing to do two ADC reads per loop(),
// avoid any nonsense from potentiometer drift, and don't need to think about the
// capacitance of the ADC muxer.
i=0;
ms = millis();
timelimit = ms + 5000;
while (ms < timelimit) {
tmp = analogRead(ADJUST);
// Keep a rotating total, buffer, and average. Since this value only moves
// a small amount due to noise in the AREF voltage and the physical
// potentiometer itself, 10 samples is fine.
total = total + tmp - values[i];
values[i] = tmp;
target = total / 10;
// Calculate the setpoint, based on min/max, and chop it to one decimal point.
ftmp2 = MINSET + ((MAXSET-MINSET) * (target/1023.0));
ftmp2 = ((int) (ftmp2*10))/10.0;
if (ftmp != ftmp2) {
ftmp = ftmp2;
timelimit = max(timelimit, ms + 2000);
sevseg.setNumber(ftmp, 1);
}
i = (i + 1) % 10;
ms = millis();
// Need to continuously refresh the display for it to stay lit.
sevseg.refreshDisplay();
}
// Convert the voltage target back into an int, for ADC comparison, with the scale the plasma pin uses.
target = ftmp / SCALE;
// Before carrying on, we now reset some of those variables.
for (i = 0; i < BUFSIZE; i++)
values[i] = 0;
total = 0;
i = 0;
j = 1; // Keeps display from triggering until we've done BUFSIZE samples.
}
void loop() {
tmp = analogRead(PLASMA);
disp += tmp; // non-rolling tally for the lower sample rate display
// Rolling window for a smaller sample
total = total + tmp - values[i];
values[i] = tmp;
// This mean truncates downwards. Oh well. At least it's fast.
mean = total >> shift;
diff = mean - target;
// If the mean is very low, then the plasma is turned off - it's just ADC
// noise you're seeing and it and should be ignored.
// This effectively checks if it's less than 2^4, ie. 16 counts, or ~3V with my scale factor.
if (!(mean>>4)) {
mode = 0;
digitalWrite(UP, 0);
digitalWrite(DOWN, 0);
}
// Otherwise, set pins as per reading.
// Set 0's first to turn off one direction before turning on reverse.
// We should never have both the UP and DOWN pins set to 1 - that would be nonsense.
// Checking for current setting before flipping saves a few cycles.
else if (diff > THRESH) {
if (mode != 2) {
mode = 2;
digitalWrite(UP, 0);
digitalWrite(DOWN, 1);
}
}
else if (diff < -THRESH) {
if (mode != 1) {
mode = 1;
digitalWrite(DOWN, 0);
digitalWrite(UP, 1);
}
}
else {
mode = 0;
digitalWrite(UP, 0);
digitalWrite(DOWN, 0);
}
// Every DISP reads, update what's displayed on the screen with a slower average.
// This would be roughly 5 or 6 times per second at our current speeds.
if (!j) {
sevseg.setNumber((float) ((disp / DISP) * SCALE), 1);
disp = 0;
}
// Need this to keep the display lit up.
sevseg.refreshDisplay();
// Faster than modular arithmetic, by far. Doing that drops us down to ~3kS/sec.
i = (i + 1) & (SAMP - 1);
j = (j + 1) & (DISP - 1);
}
https://github.com/swolebro/swolebro-youtube
The grbl I used on my Mega to control the TB6600 drivers. I shared the main Ino file code, the grbl.h code, and cpu_map_atmega2560.h code. There's many listed in the grbl.h code, not sure what wuld be needed to include the THC code into the grbl code.
/***********************************************************************
This sketch compiles and uploads Grbl to your 328p-based Arduino!
To use:
- First make sure you have imported Grbl source code into your Arduino
IDE. There are details on our Github website on how to do this.
- Select your Arduino Board and Serial Port in the Tools drop-down menu.
NOTE: Grbl only officially supports 328p-based Arduinos, like the Uno.
Using other boards will likely not work!
- Then just click 'Upload'. That's it!
For advanced users:
If you'd like to see what else Grbl can do, there are some additional
options for customization and features you can enable or disable.
Navigate your file system to where the Arduino IDE has stored the Grbl
source code files, open the 'config.h' file in your favorite text
editor. Inside are dozens of feature descriptions and #defines. Simply
comment or uncomment the #defines or alter their assigned values, save
your changes, and then click 'Upload' here.
Copyright (c) 2015 Sungeun K. Jeon
Released under the MIT-license. See license.txt for details.
***********************************************************************/
#include <grbl.h>
// Do not alter this file!
/*
grbl.h - main Grbl include file
Part of Grbl
Copyright (c) 2015 Sungeun K. Jeon
Grbl 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.
Grbl 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 Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef grbl_h
#define grbl_h
// Grbl versioning system
#define GRBL_VERSION "0.9j"
#define GRBL_VERSION_BUILD "20160317"
// Define standard libraries used by Grbl.
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include <math.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
// Define the Grbl system include files. NOTE: Do not alter organization.
#include "config.h"
#include "nuts_bolts.h"
#include "settings.h"
#include "system.h"
#include "defaults.h"
#include "cpu_map.h"
#include "coolant_control.h"
#include "eeprom.h"
#include "gcode.h"
#include "limits.h"
#include "motion_control.h"
#include "planner.h"
#include "print.h"
#include "probe.h"
#include "protocol.h"
#include "report.h"
#include "serial.h"
#include "spindle_control.h"
#include "stepper.h"
#endif
/*
cpu_map_atmega2560.h - CPU and pin mapping configuration file
Part of Grbl
Copyright (c) 2012-2015 Sungeun K. Jeon
Grbl 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.
Grbl 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 Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
/* This cpu_map file serves as a central pin mapping settings file for AVR Mega 2560 */
#ifdef GRBL_PLATFORM
#error "cpu_map already defined: GRBL_PLATFORM=" GRBL_PLATFORM
#endif
#define GRBL_PLATFORM "Atmega2560"
// Serial port pins
#define SERIAL_RX USART0_RX_vect
#define SERIAL_UDRE USART0_UDRE_vect
// Increase Buffers to make use of extra SRAM
//#define RX_BUFFER_SIZE 256
//#define TX_BUFFER_SIZE 128
//#define BLOCK_BUFFER_SIZE 36
//#define LINE_BUFFER_SIZE 100
// Define step pulse output pins. NOTE: All step bit pins must be on the same port.
#define STEP_DDR DDRA
#define STEP_PORT PORTA
#define STEP_PIN PINA
#define X_STEP_BIT 2 // MEGA2560 Digital Pin 24
#define Y_STEP_BIT 3 // MEGA2560 Digital Pin 25
#define Z_STEP_BIT 4 // MEGA2560 Digital Pin 26
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
// Define step direction output pins. NOTE: All direction pins must be on the same port.
#define DIRECTION_DDR DDRC
#define DIRECTION_PORT PORTC
#define DIRECTION_PIN PINC
#define X_DIRECTION_BIT 7 // MEGA2560 Digital Pin 30
#define Y_DIRECTION_BIT 6 // MEGA2560 Digital Pin 31
#define Z_DIRECTION_BIT 5 // MEGA2560 Digital Pin 32
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits
// Define stepper driver enable/disable output pin.
#define STEPPERS_DISABLE_DDR DDRB
#define STEPPERS_DISABLE_PORT PORTB
#define STEPPERS_DISABLE_BIT 7 // MEGA2560 Digital Pin 13
#define STEPPERS_DISABLE_MASK (1<<STEPPERS_DISABLE_BIT)
// Define homing/hard limit switch input pins and limit interrupt vectors.
// NOTE: All limit bit pins must be on the same port
#define LIMIT_DDR DDRB
#define LIMIT_PORT PORTB
#define LIMIT_PIN PINB
#define X_LIMIT_BIT 4 // MEGA2560 Digital Pin 10
#define Y_LIMIT_BIT 5 // MEGA2560 Digital Pin 11
#define Z_LIMIT_BIT 6 // MEGA2560 Digital Pin 12
#define LIMIT_INT PCIE0 // Pin change interrupt enable pin
#define LIMIT_INT_vect PCINT0_vect
#define LIMIT_PCMSK PCMSK0 // Pin change interrupt register
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
// Define spindle enable and spindle direction output pins.
#define SPINDLE_ENABLE_DDR DDRH
#define SPINDLE_ENABLE_PORT PORTH
#define SPINDLE_ENABLE_BIT 3 // MEGA2560 Digital Pin 6
#define SPINDLE_DIRECTION_DDR DDRE
#define SPINDLE_DIRECTION_PORT PORTE
#define SPINDLE_DIRECTION_BIT 3 // MEGA2560 Digital Pin 5
// Define flood and mist coolant enable output pins.
// NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at
// a later date if flash and memory space allows.
#define COOLANT_FLOOD_DDR DDRH
#define COOLANT_FLOOD_PORT PORTH
#define COOLANT_FLOOD_BIT 5 // MEGA2560 Digital Pin 8
#ifdef ENABLE_M7 // Mist coolant disabled by default. See config.h to enable/disable.
#define COOLANT_MIST_DDR DDRH
#define COOLANT_MIST_PORT PORTH
#define COOLANT_MIST_BIT 6 // MEGA2560 Digital Pin 9
#endif
// Define user-control CONTROLs (cycle start, reset, feed hold) input pins.
// NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits).
#define CONTROL_DDR DDRK
#define CONTROL_PIN PINK
#define CONTROL_PORT PORTK
#define RESET_BIT 0 // MEGA2560 Analog Pin 8
#define FEED_HOLD_BIT 1 // MEGA2560 Analog Pin 9
#define CYCLE_START_BIT 2 // MEGA2560 Analog Pin 10
#define SAFETY_DOOR_BIT 3 // MEGA2560 Analog Pin 11
#define CONTROL_INT PCIE2 // Pin change interrupt enable pin
#define CONTROL_INT_vect PCINT2_vect
#define CONTROL_PCMSK PCMSK2 // Pin change interrupt register
#define CONTROL_MASK ((1<<RESET_BIT)|(1<<FEED_HOLD_BIT)|(1<<CYCLE_START_BIT)|(1<<SAFETY_DOOR_BIT))
#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins.
// Define probe switch input pin.
#define PROBE_DDR DDRK
#define PROBE_PIN PINK
#define PROBE_PORT PORTK
#define PROBE_BIT 7 // MEGA2560 Analog Pin 15
#define PROBE_MASK (1<<PROBE_BIT)
// Start of PWM & Stepper Enabled Spindle
#ifdef VARIABLE_SPINDLE
// Advanced Configuration Below You should not need to touch these variables
// Set Timer up to use TIMER4B which is attached to Digital Pin 7
#define PWM_MAX_VALUE 65535.0
#define TCCRA_REGISTER TCCR4A
#define TCCRB_REGISTER TCCR4B
#define OCR_REGISTER OCR4B
#define COMB_BIT COM4B1
#define WAVE0_REGISTER WGM40
#define WAVE1_REGISTER WGM41
#define WAVE2_REGISTER WGM42
#define WAVE3_REGISTER WGM43
#define SPINDLE_PWM_DDR DDRH
#define SPINDLE_PWM_PORT PORTH
#define SPINDLE_PWM_BIT 4 // MEGA2560 Digital Pin 97
#endif // End of VARIABLE_SPINDLE