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

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