kb:projects:arduino:how_to_use_arduino_to_extend_tng_functionality

How to use Arduino to extend TNG functionality

A lot of times, users have specific requests or requirements regarding communication or control of external devices and equipment. More complex CNC systems can use a lot of sensors, actuators, measuring devices and all this needs it own control logic, which sometimes can be a lot to process. So instead of putting this burden on the TNG you can use external control device to handle all the data processing and control logic, while TNG processes the data at a later stage at much reduced complexity.

TNG offers a lot of tools for USB, serial communication and data manipulation at a higher abstraction level, making them very easy to use.

Great example of an external control device is an Arduino board(or any other embedded system or even a standalone PLC). Advantage of Arduino in this case can be understood in the following points:

  • high number of GPIO pins that can be used for connection and/or communication of external hardware
  • C programming language allowing for more efficient code manipulation
  • wide array of available libraries to support the chosen hardware


This and upcoming projects will show just how quickly you can create something with Arduino that can be used with TNG software.

Previous project described how to start with Arduino environment. How to install Arduino IDE, and how to create your first Arduino project using Arduino UNO board(blinking LED).
So, you should be able to know the minimum basics to move things forward.

What will you need

  • Arduino board
  • two pushbuttons
  • Arduino IDE
  • PlanetCNC TNG software


Project requirements

  • We will use Arduino board to communicate with TNG trough serial port.
    • Arduino board will present itself as a virtual serial port when connected to PC.
  • Arduino will use two pushbuttons.
    • Each pushbutton will be used to jog X axis in different direction
    • Pushbuttons are connected to GND terminal and to digital pins 8 and 9 of Arduino.
    • Arduino code will evaluate pushbuttons digital inputs. At pushbutton state change, send data trough serial port
  • TNG will read and interpret sent data and make appropriate jogging action using dedicated expression file
    • expression file will use serial and array functions to read, save and manipulate data from serial port.


Arduino code

Sections of the code are described below.

First we declare the variables which will be used for evaluation of pushbuttons state as also the pushbutton values sent trough serial port. Both btn_last and btn are declared as 8bit unsigned integer.
Macros BUTTON_1 and BUTTON_2 will help with more understandable naming of pin parameters for functions that will initialize the digital pins of Arduino microcontroller.
Macros OUT_1 and OUT_2 represent values used with bitwise operators in the main loop.

uint8_t btn_last;
uint8_t btn;
 
//macros for digital pins
#define BUTTON_1 8
#define BUTTON_2 9
 
//macros for btn values
#define OUT_1 0
#define OUT_2 1



Setup code will run only once, that is when microcontroller is reset or once it is powered-up.
In this function we initialize digital pins of Arduino board. This task is done with use of function pinMode(pin,mode).
I choose pins of microcontroller 8 and 9, represented with macros BUTTON_1 and BUTTON_2. These two pins are where pushbuttons will be connected, so I set them as inputs.
On-board LED is represented with macro LED_BUILTIN. This macro is part of Arduino internal library, this is why we did not need to declare it at the beginning. This pin will be set as output.
Here is also a good place where we set init values of button variables btn and btn_last. We set them at zero value. serial.Begin(speed) function sets the data rate for serial data transmission. We set it at 9600 bits per second.

void setup() {
  //digital pins 8 and 9 are set as inputs with internal pullup 
  //digital pin for LED set at output
  pinMode(BUTTON_1, INPUT_PULLUP);
  pinMode(BUTTON_2, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
 
  //variables btn_last and btn init 
  btn_last = btn = 0;
 
  //set up serial library baud rate to 9600
  Serial.begin(9600); 
}



Main loop() will run indefinitely, this is where our main code will reside.

Expression btn |= digitalRead(BUTTON_1) « OUT_1 sets the value of btn variable. It does this by using return value(1 when idle, 0 when pushbutton pressed) of function digitalRead() and left shifting it for value of OUT_1, which is always 0.
Saying it in other words:
Pushbutton 1 idle: Shift value 1 for 0 places, giving us the decimal value 1.
Pushbutton 1 pressed: Shift value 0 for 0 places, giving us the decimal value 0.
If BUTTON_1 is in its idle state, meaning it is not pressed, the bit-wise OR operator |= will add value 1, to current value of btn.

Expression btn |= digitalRead(BUTTON_2) « OUT_2 sets the value of btn variable. It does this by using return value(1 when idle, 0 when pushbutton pressed) of function digitalRead() and left shifting it for value of OUT_2, which is always 1.
Saying it in other words:
Pushbutton 2 idle: Shift value 1 for 1 place, giving us the decimal value 2.
Pushbutton 2 pressed: Shift value 0 for 1 place, giving us the decimal value 0.
If BUTTON_2 is in its idle state, meaning it is not pressed, the bit-wise OR operator |= will add value 2, to current value of btn.

So, if none of the pushbuttons is pressed, btn variable will have value of 3.
If pushbutton 1 is pressed, btn variable will have value of 2.
If pushbutton 2 is pressed, btn variable will have value of 1.
If both pushbuttons are pressed, btn variable will have value of 0.

These values will be considered in our expression file code.

void loop() {
  //btn variable value set at 0
  btn = 0;
 
  //when no button is pressed, btn returns value 3 
  //when only button 1 is pressed, btn returns value 2
  //when only button 2 is pressed, btn returns value 1
  btn |= digitalRead(BUTTON_1) << OUT_1; 
  btn |= digitalRead(BUTTON_2) << OUT_2; 


In the setup() loop we set btn_last = 0. This variable is used to check if pushbuttons state have changed, and therefore, if value of btn variable has changed.
Once the btn value changes, the condition btn != btn_last is true, and code within the body of if statement is executed. Immediately we copy the btn value to btn_last.
For each such event, Serial.write(btn) writes the btn value to serial port and on-board LED will toggle.

Until we release the button, the condition btn != btn_last will not be true, and btn value is not written to serial port.

  //checks if pushbutton state has changed i.e. pushbutton pressed or released
  //on button state change, save button state and send btn value trough serial port and toggle led                               
  if(btn_last != btn) 
  {
    btn_last = btn;
    Serial.write(btn);  
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
  delay(100);
}
 


Full code below.

// the setup function runs once when you press reset or power the board
uint8_t btn_last;
uint8_t btn;
 
//macros for digital pins
#define BUTTON_1 8
#define BUTTON_2 9
 
//macros for btn values
#define OUT_1 0
#define OUT_2 1
 
void setup() {
  //digital pin 8 and 9 are set as inputs with internal pullup 
  //digital pin for LED set at output
  pinMode(BUTTON_1, INPUT_PULLUP);
  pinMode(BUTTON_2, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
 
  //variables btn_last and btn init 
  btn_last = btn = 0;
 
  //set up serial library baud rate to 9600
  Serial.begin(9600); 
}
 
// the loop function runs over and over again forever
void loop() {
  //btn variable value set at 0
  btn = 0;
 
  //when no button is pressed, btn returns value 3 
  //when only button 1 is pressed, btn returns value 2
  //when only button 2 is pressed, btn returns value 1
  btn |= digitalRead(BUTTON_1) << OUT_1; 
  btn |= digitalRead(BUTTON_2) << OUT_2; 
 
 
  //checks if pushbutton state has changed i.e. pushbutton pressed or released
  //on button state change, save button state and send btn value trough serial port and toggle led                               
  if(btn_last != btn) 
  {
    btn_last = btn;
    Serial.write(btn);  
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
  delay(100);
}
 

Verify and upload the code to your Arduino board.

Expression file

Locate your profile folder and create new expression file and name it e.g. Expr_Arduino_pushbuttons. Open the file and paste the code below.

I recommend reading up on serial_addlistener() and array functions Functions, that play a key role in this code.

#OnInit
;!!For port variable, use port name of your Arduino board!!
;open serial port
;serial port parameter config
;add serial listener
port = "COM13";
hnd = array_new();
serial_open(port);
serial_config(port, 9600, 8, 0, 1, 1);
serial_addlistener(port, "#On_Serial_Event", hnd, 1); 
 
 
;***************************************************************
 
#On_Serial_Event
 
 array_printdata(.arg2);
 
;obtain serial data
 .dir = array_getdata(.arg2, 0);
 
 if(.dir == 0x01, .dirX = 1);
 if(.dir == 0x02, .dirX = 2);
 if(.dir == 0x03, .dirX = 3);
 
 
 .ax = 0;
 if(.dirX == 1, .ax = -1);
 if(.dirX == 2, .ax = +1);
 if(.dirX == 3, .ax = 0);
 
;jogs X axis of machine, depending on the serial data
 jog(0, .ax, 0, 0);
 
;***************************************************************
 
#OnShutdown
serial_close(port);
serial_remlistener(port);
array_delete(hnd);

Testing the functionality with the TNG

Launch TNG and open Output window: View/Panel/Utilities

Output window will appear:

Under Control Panel/Device Manager check if your Arduino board is presented as virtual com port. In my case as COM13. Make sure that your expression file uses this name for port.

Now connect the Arduino board to computers USB port, and observe the output window while press one of the pushbuttons:

To interpret the values:
0x01 → Button 2 has been pressed. As per our expression file, machine will jog in -X direction.

0x03 → Push button has been released, and no pushbuttons is pressed.

0x01 → Button 2 has been pressed. As per our expression file, machine will jog in -X direction.

0x03 → Push button has been released, and no pushbuttons is pressed.

0x02 → Button 1 has been pressed. As per our expression file, machine will jog in X direction.

0x03 → Push button has been released, and no pushbuttons is pressed.

kb/projects/arduino/how_to_use_arduino_to_extend_tng_functionality.txt · Last modified: 2024/04/29 20:07 by andrej

Page Tools