Monday, June 1, 2026

Java: using thermal printer 6106 - 80mm esc/pos java in debian 13

insert your printer using usb cable


# lsusb
...
Bus 008 Device 002: ID 28e9:0289 GDMicroelectronics micro-printer
Bus 009 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
# dmesg
[   70.532011] usb 8-1: new full-speed USB device number 2 using xhci_hcd
[   70.688254] usb 8-1: New USB device found, idVendor=28e9, idProduct=0289, bcdDevice= 2.00
[   70.688270] usb 8-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[   70.688276] usb 8-1: Product: micro-printer
[   70.688282] usb 8-1: Manufacturer: GEZHI
[   70.688286] usb 8-1: SerialNumber: 000000000004
[   70.730721] usblp 8-1:1.0: usblp1: USB Bidirectional printer dev 2 if 0 alt 0 proto 2 vid 0x28E9 pid 0x0289
[   70.730783] usbcore: registered new interface driver usblp
use nl80211
# ls -al /dev/usb/
total 0
drwxr-xr-x  2 root root     80 May 31 14:31 .
drwxr-xr-x 20 root root   3840 May 31 14:31 ..
crw-------  1 root root 180, 0 May 31 14:30 hiddev0
crw-rw----  1 root lp   180, 1 May 31 14:31 lp1
# ls -al /dev/usb/lp1
crw-rw---- 1 root lp 180, 1 May 31 14:31 /dev/usb/lp1

we found the device known as usb lp1 i.e. /dev/usb/lp1. test to print something.


# printf '\x1b\x40Hello World\n\n\n' > /dev/usb/lp1

your printer should print "Hello World"

add your account or linux user as lp group 


# usermod -aG lp [username]

you need to sign out and re sign in, then try to print as [username]


$ printf '\x1b\x40Hello World\n\n\n' > /dev/usb/lp1

if everything work, now you can work as [username]. this is simple netbeans 30 - maven, to test your printer.

This is pom.xml for your project.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.dedetok</groupId>
    <artifactId>EnumComPort</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.release>21</maven.compiler.release>
        <exec.mainClass>com.dedetok.enumcomport.EnumComPort</exec.mainClass>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.fazecast</groupId>
            <artifactId>jSerialComm</artifactId>
            <version>2.11.0</version>
            <type>jar</type>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.5.0</version>

                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>

                        <configuration>
                            <createDependencyReducedPom>false</createDependencyReducedPom>

                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.dedetok.enumcomport.EnumComPort</mainClass>
                                </transformer>
                            </transformers>

                        </configuration>
                    </execution>
                </executions>

            </plugin>

        </plugins>
    </build>
</project>

This is the java code


package com.dedetok.enumcomport;

import com.fazecast.jSerialComm.SerialPort;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 *
 * @author dedetok
 */
public class EnumComPort {

    public static void main(String[] args) throws IOException {
        System.out.println("Hello World!");
        System.out.println(System.getProperty("user.name"));
        Process p = Runtime.getRuntime().exec("id");
        p.getInputStream().transferTo(System.out);
        
        SerialPort[] ports = SerialPort.getCommPorts();

        if (ports.length == 0) {
            System.out.println("No serial ports detected.");
        }

        System.out.println("Available Serial Ports:");
        System.out.println("------------------------");

        for (SerialPort port : ports) {

            System.out.println("System Port Name : " + port.getSystemPortName());
            System.out.println("Descriptive Name: " + port.getDescriptivePortName());
            System.out.println("Port Description: " + port.getPortDescription());
            System.out.println("System Location : " + port.getSystemPortPath());
            System.out.println("------------------------");
        }
        
        String devicePath = "/dev/usb/lp1";
        // Open file in read-write mode for raw bidirectional access
        RandomAccessFile deviceFile = new RandomAccessFile(devicePath, "rw");
        FileDescriptor fd = deviceFile.getFD();
        
        try (OutputStream out = new FileOutputStream(fd)) {
            // ESC @ (initialize)
            out.write(new byte[]{0x1B, 0x40});

            out.write("Hello World\n".getBytes());

            // Feed
            out.write("\n\n".getBytes());

            // Cut
            out.write(new byte[]{0x1D, 0x56, 0x00});

            out.flush();
            out.close(); // NO NEED
        } catch (FileNotFoundException ex) {
            System.getLogger(EnumComPort.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
        }
        System.out.println("End");
    }
}

In my test, you can run inside your netbeans, Run -> Build Project or Run -> Clean and Run Project. to run the jar, you go to project root and run like this


$ java -jar ./target/EnumComPort-1.0.jar 
Hello World!
[username]
uid=1000([username]) gid=1000([username]) groups=1000([username]),7(lp),20(dialout),24(cdrom),25(floppy),29(audio),30(dip),33(www-data),44(video),46(plugdev),100(users),106(netdev),111(bluetooth),113(lpadmin),116(scanner),124(libvirt)
No serial ports detected.
Available Serial Ports:
------------------------
End

your print should print "Hello World". "No serial ports" means, you can use com.fazecast.jSerialComm library for lp printer.

Credit: https://www.freeformatter.com/html-escape.html for free html escape 

Saturday, May 30, 2026

Java: enumerate com serial using fazecast jSerialComm

Simple code to enumerate serial com port


/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 */

package com.dedetok.enumcomport;

import com.fazecast.jSerialComm.SerialPort;

/**
 *
 * @author dedetok
 */
public class EnumComPort {

    public static void main(String[] args) {
        System.out.println("Hello World!");

        SerialPort[] ports = SerialPort.getCommPorts();

        if (ports.length == 0) {
            System.out.println("No serial ports detected.");
            return;
        }

        System.out.println("Available Serial Ports:");
        System.out.println("------------------------");

        for (SerialPort port : ports) {

            System.out.println("System Port Name : " + port.getSystemPortName());
            System.out.println("Descriptive Name: " + port.getDescriptivePortName());
            System.out.println("Port Description: " + port.getPortDescription());
            System.out.println("System Location : " + port.getSystemPortPath());
            System.out.println("------------------------");
        }
        
    }
}

Note: lpx device (e.g /dev/usb/lp1) does not use serial com, you need to send hex code direct via OutputStream.

Java: Hook shutdown to run java application as systemd

This is minimum java code to hook shutdown


/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 */

package com.dedetok.javaservicetest;

/**
 *
 * @author dedetok
 */
public class JavaServiceTest {

    private static volatile boolean running = true;
        
    public static void main(String[] args) {
        System.out.println("Hello World!");
        
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("Shutdown hook triggered");

            running = false;

            // Cleanup resources here
            // Close database connections
            // Flush logs
            // Stop worker threads
            // Main shutdown path
            //cleanupResources();

            System.out.println("Cleanup complete");
        }));

        System.out.println("Daemon started");

        while (running) {
            System.out.println("Working...");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException ex) {
                System.getLogger(JavaServiceTest.class.getName()).log(System.Logger.Level.ERROR, (String) null, ex);
            }
        }

    }
}

to run/test this application, you can not run it inside netbeans. use terminal to run it, go to your project root and run


$ java -cp target/classes \
     com.dedetok.javaservicetest.JavaServiceTest
Hello World!
Daemon started
Working...
Working...
^CShutdown hook triggered
Cleanup complete

 

 

Java Netbeans: upgrade binary Netbeans 29 to Netbeans 30

 This step is straight forward:

  1. go to your root directory (e.g your home dir ~/), and rename old folder e.g ~/netbeans29.
  2. extract netbeans 30 binary and put it in root directory to replace old folder (e.g your home dir ~/).
  3. Run your netbeans 30, import previous configuration
  4. if no error found you safe to remove
    1. old netbeans 29 (e.g ~/netbeans29) 
    2. ~/.netbeans/29
    3. ~/.cache/netbeans/29

 

Monday, May 25, 2026

Java: maven escpos-coffee library, output to binary file

This is working demo how to create print preview and generate output.bin which is valid byte format to send to thermal printer.

Edit pom.xml and add escpos-coffee

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.dedetok</groupId>
    <artifactId>myapp</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.6.3</version>
                <configuration>
                    <mainClass>com.dedetok.myapp.App</mainClass>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.4.2</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.dedetok.myapp.App</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>

        </plugins>
    </build>

	<dependencies>

		<dependency>
			<groupId>com.github.anastaciocintra</groupId>
			<artifactId>escpos-coffee</artifactId>
			<version>4.1.0</version>
		</dependency>

	</dependencies>
</project>

run package or compile to download library

$ mvn compile

Edit App.java


package com.dedetok.myapp;

import com.github.anastaciocintra.escpos.EscPos;
import com.github.anastaciocintra.escpos.EscPosConst;
import com.github.anastaciocintra.escpos.Style;
import com.github.anastaciocintra.output.PrinterOutputStream;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;

public class App extends JFrame {

    private JTextArea txtPreview;
    private DecimalFormat formatter;

    public App() {

        setTitle("ESC/POS Receipt Preview");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(420, 700);
        setLocationRelativeTo(null);

        JPanel root = new JPanel(new GridBagLayout());
        root.setBackground(new Color(180, 180, 180));

        JPanel paper = new JPanel(new BorderLayout());
        paper.setPreferredSize(new Dimension(300, 600));
        paper.setBackground(Color.WHITE);

        txtPreview = new JTextArea();

        txtPreview.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 15));
        txtPreview.setEditable(false);
        txtPreview.setLineWrap(false);

        txtPreview.setBackground(Color.WHITE);
        txtPreview.setForeground(Color.BLACK);

        txtPreview.setBorder(new EmptyBorder(12, 12, 12, 12));

        JScrollPane scroll = new JScrollPane(txtPreview);
        scroll.setBorder(null);

        paper.add(scroll, BorderLayout.CENTER);

        root.add(paper);

        add(root);

        generatePreview();

        // generate ESC/POS binary file
        try {
            generateEscPosFile();
            System.out.println("ESC/POS output saved to output.bin");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void generatePreview() {

        DecimalFormatSymbols symbols = new DecimalFormatSymbols();
        symbols.setGroupingSeparator('.');

        formatter = new DecimalFormat("#,###", symbols);

        StringBuilder sb = new StringBuilder();

        sb.append(center("MY COFFEE SHOP")).append("\n");
        sb.append(center("Bluetooth ESC/POS")).append("\n");
        sb.append(center("Jl. Ubud Bali")).append("\n");

        sb.append("--------------------------------\n");

        addItem(sb, "Espresso", 2, 18000);
        addItem(sb, "Cappuccino", 1, 25000);
        addItem(sb, "Croissant", 3, 12000);

        sb.append("--------------------------------\n");

        sb.append(String.format("%-20s %10s\n",
                "SUBTOTAL",
                formatter.format(97000)));

        sb.append(String.format("%-20s %10s\n",
                "TAX",
                formatter.format(9700)));

        sb.append("--------------------------------\n");

        sb.append(String.format("%-20s %10s\n",
                "TOTAL",
                formatter.format(106700)));

        sb.append("\n");

        sb.append(center("THANK YOU")).append("\n");
        sb.append(center("PLEASE COME AGAIN")).append("\n");

        sb.append("\n\n\n");

        txtPreview.setText(sb.toString());
    }

    private void generateEscPosFile() throws IOException {

        FileOutputStream fos = new FileOutputStream("output.bin");

        EscPos escpos = new EscPos(fos);

        Style titleStyle = new Style()
                .setJustification(EscPosConst.Justification.Center)
                .setBold(true)
                .setFontSize(Style.FontSize._2, Style.FontSize._2);

        Style centerStyle = new Style()
                .setJustification(EscPosConst.Justification.Center);

        Style normalStyle = new Style();

        escpos.writeLF(titleStyle, "MY COFFEE SHOP");
        escpos.writeLF(centerStyle, "Bluetooth ESC/POS");
        escpos.writeLF(centerStyle, "Jl. Ubud Bali");

        escpos.writeLF("--------------------------------");

        addItemEscPos(escpos, "Espresso", 2, 18000);
        addItemEscPos(escpos, "Cappuccino", 1, 25000);
        addItemEscPos(escpos, "Croissant", 3, 12000);

        escpos.writeLF("--------------------------------");

        escpos.writeLF(normalStyle,
                String.format("%-20s %10s",
                        "SUBTOTAL",
                        formatter.format(97000)));

        escpos.writeLF(normalStyle,
                String.format("%-20s %10s",
                        "TAX",
                        formatter.format(9700)));

        escpos.writeLF("--------------------------------");

        escpos.writeLF(normalStyle,
                String.format("%-20s %10s",
                        "TOTAL",
                        formatter.format(106700)));

        escpos.writeLF("");

        escpos.writeLF(centerStyle, "THANK YOU");
        escpos.writeLF(centerStyle, "PLEASE COME AGAIN");

        escpos.feed(4);
        escpos.cut(EscPos.CutMode.FULL);

        escpos.close();
        fos.close();
    }

    private void addItemEscPos(EscPos escpos,
                               String item,
                               int qty,
                               int price) throws IOException {

        int total = qty * price;

        escpos.writeLF(item);

        escpos.writeLF(
                String.format(
                        " %2dx %-14s %10s",
                        qty,
                        "",
                        formatter.format(total)
                )
        );
    }

    private void addItem(StringBuilder sb,
                         String item,
                         int qty,
                         int price) {

        int total = qty * price;

        sb.append(item).append("\n");

        sb.append(String.format(
                " %2dx %-14s %10s\n",
                qty,
                "",
                formatter.format(total)
        ));
    }

    private String center(String text) {

        int width = 32;

        if (text.length() >= width)
            return text;

        int leftPadding = (width - text.length()) / 2;

        return " ".repeat(leftPadding) + text;
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(() -> {
            new App().setVisible(true);
        });
    }
}

Note: lpx device (e.g /dev/usb/lp1) does not use serial com, you need to send hex code direct via OutputStream