The following source code demonstrates arduino-cli which is a command line tool for compiling and uploading an arduino sketch to an arduino usb device. To get started you will need to download arduino-cli to your system if you donβt already have it. On Mac/Linux platforms one option is to install it with Homebrew; on Windows you will need to manually download it. Make certain that you know the path of where it was installed. References to help with installation and getting started are here:
https://docs.arduino.cc/arduino-cli/installation/
https://docs.arduino.cc/arduino-cli/getting-started/
To use the demo you will need to have your arduino sketches in a handy place such as a folder on your Desktop. The arduino sketch itself needs to inside of another folder with the same name, eg sketchName/sketchName.ino. This is important because it will not work if done otherwise. More than likely the initial xxxx.ino file will have been created in the Arduino.IDE and copy/pasted to your Desktop folder, although you could create the xxxx.ino file in any text editor or in this demo. However, there is no debugging feedback in either of the latter editors while there is feedback in the Arduino.IDE editor.
After the arduino sketch has been opened into this demo it will be time to compile and upload it to your device. Make certain that the device port path, board name and FQBN (Fully Qualified Board Name) field are all correctly set, but do not open the serial port until after the upload. The demo will first compile the arduino sketch and then upload it when the βUpload sketchβ button is selected. If this was successful, the serial monitor text area will show that both operations were performed; this may take a few seconds so be patient. The monitor text area should also show an error message it there was a problem.
Following the upload of your sketch you may then open the serial port and run the sketch by selecting the βConnectβ button. Make certain that all of the fields at the top are set correctly, ie port path, baud rate, frame marker, and element separator. There is a βRescanβ button in case you forgot to plug in your arduino device before opening the app. If there is no frame marker then set both fields to whatever you have used to separate the data elements in your Arduino sketch. An example might look like this: data comma data comma data line feed where the data is separated by commas and at the end of the line there is a line feed, usually by adding Serial.println(), which automatically adds a line feed. There is a test Arduino sketch at the bottom of the code section which demonstrates comma separated values (no frame marker in this sketch).
At the very bottom of the demo is a canvas from the default Processing window which may be used with draw(). The test sketch creates a sawtooth pattern which should be displayed in this area. You should also see the data values stream through the serial monitor simultaneously when the βConnectβ button has been selected. The graph proceeds from left to right and then resets to the left when it reaches the right margin. The βClear canvasβ button may be selected at any time. Be sure to select βDisconnectβ if you want to stop data flow or quit the app. The βConnectβ button may be re-selected to resume data flow if so desired.
This demo was created and tested on MacOS (M2) with a Homebrew installation and an Arduino UNO board, but may also be run on Windows 11 (not tested on Linux). However, some of the code will need to be changed to reflect your operating system, eg code lines 157, 306, and 329 which have the path to arduino-cli. This path will depend on where you installed arduino-cli initially and will need to be changed accordingly.
Source code (with Arduino test sketch):
/*
* Avoid Processing v4.4.8 (No Serial capability)
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import processing.serial.*;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
javax.swing.JFrame frame;
java.awt.Canvas canvas;
int _wndW = 750;
int _wndH = 750;
int _baseline = 25;
PVector v1 = new PVector();
PVector v2 = new PVector();
Serial myPort;
JTextArea txtArea;
JTextArea monitorTxtArea;
JTextField fqbnTxtFld;
JComboBox portsComboBox;
String portName;
String arduinoSketchPath;
String fqbnStr;
String[] boards;
String[] ports;
char frameMarkerChar;
char elementSeparatorChar;
int baudRate;
int counter;
int value;
boolean clearCanvas;
void getSerialPorts(int x, int y, int w, int h) {
ports = Serial.list();
portsComboBox = new JComboBox(ports);
portsComboBox.setBounds(x,y,w,h);
frame.add(portsComboBox);
portsComboBox.addActionListener(evt -> {
int selectedIndex = portsComboBox.getSelectedIndex();
portName = Serial.list()[selectedIndex];
}
);
portsComboBox.repaint();
}
void baudRates(int x, int y, int w, int h) {
String baudRates[] = {"2400", "4800", "9600", "19200", "38400", "48000", "57600", "115200"};
JComboBox bauds = new JComboBox(baudRates);
bauds.setBounds(x,y,w,h);
frame.add(bauds);
bauds.addActionListener(evt -> {
Object baudRateSelected = bauds.getSelectedItem();
baudRate = Integer.parseInt((String) baudRateSelected);
}
);
bauds.repaint();
}
void frameMarkerLabel(int x, int y, int w, int h, String title) {
JLabel label = new JLabel(title);
label.setHorizontalAlignment(JLabel.LEFT);
label.setBounds(x,y,w,h);
frame.add(label);
label.repaint();
}
void frameMarkerBtn(int x, int y, int w, int h) {
String frameMarkers[] = {"line feed","comma","asterisk","colon"};
JComboBox frameMarker = new JComboBox(frameMarkers);
frameMarker.setBounds(x,y,w,h);
frame.add(frameMarker);
frameMarker.addActionListener(e -> {
int frameMarkerIndex = frameMarker.getSelectedIndex();
switch(frameMarkerIndex){
case 0:
frameMarkerChar = '\n';
break;
case 1:
frameMarkerChar = ',';
break;
case 2:
frameMarkerChar = '*';
break;
case 3:
frameMarkerChar = ':';
break;
}
}
);
frameMarker.repaint();
}
void elementSeparatorLabel(int x, int y, int w, int h, String title) {
JLabel label = new JLabel(title);
label.setHorizontalAlignment(JLabel.LEFT);
label.setBounds(x,y,w,h);
frame.add(label);
label.repaint();
}
void elementSeparatorBtn(int x, int y, int w, int h){
String elementSeparators[] = {"line feed","comma","asterisk","colon"};
JComboBox elementSeparator = new JComboBox(elementSeparators);
elementSeparator.setBounds(x,y,w,h);
frame.add(elementSeparator);
elementSeparator.addActionListener(e -> {
int elemSeparatorIndex = elementSeparator.getSelectedIndex();
switch(elemSeparatorIndex){
case 0:
elementSeparatorChar = '\n';
break;
case 1:
elementSeparatorChar = ',';
break;
case 2:
elementSeparatorChar = '*';
break;
case 3:
elementSeparatorChar = ':';
break;
}
}
);
elementSeparator.repaint();
}
void rescanBtn(int x, int y, int w, int h, String title) {
JButton btn = new JButton(title);
btn.setBounds(x,y,w,h);
frame.add(btn);
btn.addActionListener(e -> {
ports = Serial.list();
DefaultComboBoxModel<String> newModel = new DefaultComboBoxModel<>(ports);
portsComboBox.setModel(newModel);
}
);
btn.repaint();
}
void getBoardTypes() {
String[] commands = {"/usr/local/bin/arduino-cli", "board", "listall"};
try {
ProcessBuilder pb = new ProcessBuilder(commands);
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
List<String> boardTypes = new ArrayList<>();
while ((line = reader.readLine()) != null) {
boardTypes.add(line);
}
int exitVal = p.waitFor();
if (exitVal != 0) {
monitorTxtArea.append("There was an error listing board types.\n");
}
boards = boardTypes.toArray(new String[0]);
}
catch(Exception e) {
e.printStackTrace();
}
}
void boardTypeBtn(int x, int y, int w, int h) {
JComboBox board = new JComboBox(boards);
board.setBounds(x,y,w,h);
frame.add(board);
board.addActionListener(evt -> {
Object boardName = board.getSelectedItem();
String boardTypeStr = (String)boardName;
String[] splitArray = split(boardTypeStr," ");
fqbnStr = splitArray[splitArray.length - 1];
fqbnStr = fqbnStr.trim(); // It will likely have unwanted spaces
fqbnTxtFld.setText(fqbnStr);
}
);
board.repaint();
}
void fqbnLabel(int x, int y, int w, int h, String title) {
JLabel label = new JLabel(title);
label.setHorizontalAlignment(JLabel.LEFT);
label.setBounds(x, y, w, h);
frame.add(label);
label.repaint();
}
void fqbnFld(int x, int y, int w, int h, String title) {
fqbnTxtFld = new JTextField(title);
fqbnTxtFld.setBounds(x,y,w,h);
fqbnTxtFld.setEditable(false);
frame.add(fqbnTxtFld);
fqbnTxtFld.repaint();
}
void openMyFile() {
JFileChooser fileChooser = new JFileChooser();
int i = fileChooser.showOpenDialog(fileChooser);
if (i == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
arduinoSketchPath = file.getPath();
try {
BufferedReader buffer = new BufferedReader(new FileReader(arduinoSketchPath));
String s1 = "", s2 = "";
while ((s1 = buffer.readLine())!= null) {
s2 += s1 + "\n";
}
txtArea.setText(s2);
buffer.close();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
void openSketchBtn(int x, int y, int w, int h, String title) {
JButton btn = new JButton(title);
btn.setBounds(x,y,w,h);
frame.add(btn);
btn.addActionListener(e -> {
openMyFile();
}
);
btn.repaint();
}
void saveMyFile() {
if (arduinoSketchPath != null) {
try {
String content = txtArea.getText();
File file = new File(arduinoSketchPath);
FileWriter fw = new FileWriter(file) ;
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
}
catch (IOException e) {
e.printStackTrace();
}
} else {
JOptionPane.showMessageDialog(null, "There is no file to save!");
}
}
void saveBtn(int x, int y, int w, int h, String title) {
JButton btn = new JButton(title);
btn.setBounds(x,y,w,h);
frame.add(btn);
btn.addActionListener(e -> {
saveMyFile();
}
);
btn.repaint();
}
void saveAsMyFile() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setSelectedFile(new File("untitled.ino"));
int i = fileChooser.showSaveDialog(fileChooser);
if (i == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
arduinoSketchPath = file.getPath();
}
if (arduinoSketchPath != null) {
try {
String content = txtArea.getText();
File file = new File(arduinoSketchPath);
FileWriter fw = new FileWriter(file) ;
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
void saveAsBtn(int x, int y, int w, int h, String title) {
JButton btn = new JButton(title);
btn.setBounds(x,y,w,h);
frame.add(btn);
btn.addActionListener(e -> {
saveAsMyFile();
}
);
btn.repaint();
}
void uploadAction() {
String[] commands = {"/usr/local/bin/arduino-cli", "upload", "-p", portName, "--fqbn", fqbnStr, arduinoSketchPath};
try {
ProcessBuilder pb = new ProcessBuilder(commands);
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line = null;
while ((line = reader.readLine()) != null) {
monitorTxtArea.append(line);
}
int exitVal = p.waitFor();
if (exitVal != 0) {
monitorTxtArea.append("There was a problem uploading the sketch.\n");
} else {
monitorTxtArea.append("Compile OK\n");
monitorTxtArea.append("Upload OK\n");
}
}
catch(Exception e) {
e.printStackTrace();
}
}
void compileAction() {
String[] commands = {"/usr/local/bin/arduino-cli", "compile", "--fqbn", fqbnStr, arduinoSketchPath};
try {
ProcessBuilder pb = new ProcessBuilder(commands);
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line = null;
while ((line = reader.readLine()) != null) {
monitorTxtArea.setText(line);
}
int exitVal = p.waitFor();
if (exitVal != 0) {
monitorTxtArea.append("There was a problem compiling the sketch.\n");
} else {
uploadAction();
}
}
catch(Exception e) {
e.printStackTrace();
}
}
void uploadSketchBtn(int x, int y, int w, int h, String title) {
JButton btn = new JButton(title);
btn.setBounds(x,y,w,h);
frame.add(btn);
btn.addActionListener(e -> {
compileAction();
}
);
btn.repaint();
}
void clearSketchBtn(int x, int y, int w, int h, String title) {
JButton btn = new JButton(title);
btn.setBounds(x, y, w, h);
frame.add(btn);
btn.addActionListener(e -> {
txtArea.setText("");
}
);
btn.repaint();
}
void clearMonitorBtn(int x, int y, int w, int h, String title) {
JButton btn = new JButton(title);
btn.setBounds(x, y, w, h);
frame.add(btn);
btn.addActionListener(e -> {
monitorTxtArea.setText("");
}
);
btn.repaint();
}
void textArea(int x, int y, int w, int h) {
txtArea = new JTextArea();
JScrollPane scrlPane = new JScrollPane(txtArea);
scrlPane.setBounds(x, y, w, h);
frame.add(scrlPane);
txtArea.setEditable(true);
txtArea.setToolTipText("arduino sketch.ino");
// txtArea.setFont(new Font("Menlo", Font.BOLD, 16));
txtArea.setLineWrap(false);
txtArea.setWrapStyleWord(true);
txtArea.repaint();
}
void monitorTxtArea(int x, int y, int w, int h) {
monitorTxtArea = new JTextArea();
JScrollPane scrlPane = new JScrollPane(monitorTxtArea);
scrlPane.setBounds(x,y,w,h);
frame.add(scrlPane);
monitorTxtArea.setEditable(true);
monitorTxtArea.setToolTipText("serial monitor");
// monitorTxtArea.setFont(new Font("Menlo", Font.BOLD, 16));
monitorTxtArea.setLineWrap(true);
monitorTxtArea.setWrapStyleWord(true);
monitorTxtArea.repaint();
}
void connectBtn(int x, int y, int w, int h, String title) {
JButton btn = new JButton(title);
btn.setBounds(x, y, w, h);
frame.add(btn);
btn.addActionListener(e -> {
myPort = new Serial(this, portName, baudRate);
}
);
btn.repaint();
}
void disconnectBtn(int x, int y, int w, int h, String title) {
JButton btn = new JButton(title);
btn.setBounds(x,y,w,h);
frame.add(btn);
btn.addActionListener(e -> {
myPort.clear(); // Clear any remaining data in the buffer
myPort.stop(); // Close the serial port
}
);
btn.repaint();
}
void clearCanvasBtn(int x, int y, int w, int h, String title){
JButton btn = new JButton(title);
btn.setBounds(x,y,w,h);
frame.add(btn);
btn.addActionListener(e -> {
clearCanvas = true;
}
);
btn.repaint();
}
void buildWnd() {
getSerialPorts(20, 20, 270, 24);
baudRates(300, 20, 100, 24);
frameMarkerLabel(410, 0, 110, 24,"Frame marker:");
frameMarkerBtn(410, 20, 110, 24);
elementSeparatorLabel(525, 0, 130, 24, "Element separator:");
elementSeparatorBtn(530, 20, 110, 24);
rescanBtn(650, 20, 80, 24, "Rescan");
getBoardTypes();
boardTypeBtn(20, 55, 370, 24);
fqbnLabel(400, 55, 40, 24, "FQBN:");
fqbnFld(440, 55, 230, 24,"");
openSketchBtn(20, 85, 170, 24, "Open Arduino sketch");
saveBtn(195, 85, 70, 24, "Save");
saveAsBtn(270, 85, 90, 24, "SaveAs...");
uploadSketchBtn(365, 85, 130, 24, "Upload sketch");
clearSketchBtn(500, 85, 110, 24, "Clear sketch");
clearMonitorBtn(615, 85, 120, 24, "Clear monitor");
textArea(20, 120, 450, 290);
monitorTxtArea(475, 120, 255, 290);
connectBtn(330, 420, 100, 24, "Connect");
disconnectBtn(440, 420, 100, 24, "Disconnect");
clearCanvasBtn(550, 420, 120, 24, "Clear canvas");
frame.setVisible(true);
}
void setup() {
size(_wndW, _wndH); // Uses default window
frame = (javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas) surface.getNative()).getFrame();
canvas = (processing.awt.PSurfaceAWT.SmoothCanvas) ((processing.awt.PSurfaceAWT)surface).getNative();
canvas.setBounds(0, 450, _wndW, _wndH - 450); // Drop down to make room for Swing components
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
clearCanvas = false;
background(255);
javax.swing.SwingUtilities.invokeLater(() -> {
buildWnd(); // Builds components on EventDispatchThread
}
);
}
void serialEvent( Serial myPort) {
String inStr = myPort.readStringUntil(frameMarkerChar); // frameMarker separated strings
//make sure inStr isn't empty before continuing
if (inStr != null) {
int[]num = int(split(inStr, elementSeparatorChar));
int value = num[0];
String dataStr = str(value) + "\n";
monitorTxtArea.append("value = " + dataStr);
monitorTxtArea.setCaretPosition(monitorTxtArea.getDocument().getLength());
v1.x = counter;
v1.y = height - value - _baseline;
counter++;
if (counter > width) {
counter = 0;
}
}
}
void draw() {
strokeWeight(3.0);
line(v1.x, v1.y, v2.x, v2.y);
v2.x = v1.x;
v2.y = v1.y;
if (v2.x >= width) {
v2.x = 0; // at edge of screen -> back to beginning
background(255);
}
if(clearCanvas){
background(255);
counter = 0;
value = 0;
v1.set(0.0,0.0);
v2.set(0.0,0.0);
clearCanvas = false;
}
}
// **** Arduino sketch used for testing **** //
/*
unsigned long LoopTimer = 0;
byte outputValue = 0;
char outStr[5];
const int LoopTimeInterval = 50;
void setup() {
Serial.begin(9600);
}
void loop() {
if (millis() >= LoopTimer) {
LoopTimer += LoopTimeInterval;
outputValue += 8;
sprintf(outStr,"%03d",outputValue); // Left pads with zeroes
Serial.print(outStr);
Serial.print(","); // comma separated values - no carriage return or line feed
}
}
*/
Output:
