Did some exploration of a CITAQ H10-3 that I got from a friend who does not need it anymore for his shop.

I also contributed a few other useful informations like how to factory reset it, so if you need such information then head on to this Factory Reset Guide For CITAQ H10. I've also contributed other more detailed technical notes to this technical repository notes by netguy netguycode/citaq.

Below is an exploration log of how the SDK and device work because in the future I may want to give a shot at writing an app for it. This doesn't quite fit into the above repository as it is more general notes and timeline of discoveries I found while exploring it and is a bit more disorganized (but is eventually cleaned up and entered into the repository). Still there are other useful notes that cannot be easily transferred and so may be of use to anyone still exploring this device as well so enjoy!

CITAQ H10-3 SDK

Where is the print logic in POSFactory App?

~/git/Citaq-H10-3/CitaqSDK/src/com/citaq/citaqfactory/PrintActivity.java

How does the print demo text work? (PrintActivity.java)

This button to print demo text will call either Command.getPrintDemoZH() and Command.getPrintDemo() from ./CitaqSDK/src/com/citaq/util/Command.java which would output a byte array of ESC/POS formatted text.

This is sent to printerWrite() in PrintActivity.java which calls mSendThread.addData() from public class SendThread extends Thread which would call either serialWrite() or usbWrite() to send the ESC/POS data to the internal printer.

Serial

public class PrintActivity extends SerialPortActivity{
	private void initSerial(){
			mSerialPort = mApplication.getPrintSerialPort();
			mOutputStream = mSerialPort.getOutputStream();
    ... } ... } ...

Where getPrintSerialPort() from ./CitaqSDK/src/com/citaq/citaqfactory/CitaqApplication.java uses SerialPort() from ./CitaqSDK/src/android_serialport_api/SerialPort.java

	public SerialPort getPrintSerialPort() throws SecurityException, IOException, InvalidParameterException {
		if (mSerialPort == null) {
			/* Open the serial port */
			mSerialPort = new SerialPort(new File("/dev/ttyS1"), 115200, 0, true);
		}
		return mSerialPort;
	}

Where

	public SerialPort(File device, int baudrate, int flags, boolean flowCon)

But the main point to learn from the above function is that the internal serial port we should be using to talk to the internal printer is:

  • CTE-RP80 Internal Printer Serial Port
    • path: /dev/ttyS1
    • baud: 115200
    • flag: none
    • flow control: enabled

How does led bar works

The test page for this led bar main business logic page is at ./CitaqSDK/src/com/citaq/citaqfactory/LedActivity.java

Which essentally calls functions like trunOnRedRight() which is from

./CitaqSDK/src/com/citaq/util/LEDControl.java

The virtual gpio file that this function writes is dependent on cpu type (different board revision?)

This is determined via getCpuHardware() which calls a linux command cat /proc/cpuinfo that dumps the board hardware info and scans for lines starting with Hardware: and grab the hardware ID after it.

getCpuHardware() Recognised Board Type hardware ID string (/proc/cpuinfo)
SMDKV210 SMDKV210
RK3188 RK30BOARD
RK30BOARD SUN50IW1P1
MSM8625Q QRD MSM8625Q SKUD
RK3368 RK3368

Fortunately as shown in trunOnRedRight()... in both CPU/board models, you just need to write 1 to turn led on or 0 to turn led off as shown in trunOnRedRight(). As for what files should be written to based on board type, there is some clues based on what trunOnRedRight() and trunOnBlueRight()

  • MainBoardUtil.RK3188 or MainBoardUtil.RK30BOARD
    • RED: /sys/class/gpio/gpio190/value
    • BLUE: /sys/class/gpio/gpio172/value
  • MainBoardUtil.RK3368
    • RED: /sys/class/gpio/gpio124/value
    • BLUE: /sys/class/gpio/gpio106/value

There is also MainBoardUtil.MSM8625Q referenced in trunOnRedRight() to control the led bar, but instead of control via linux gpio virual files interface... it is via calling isRedlightOn() function contained within a JNI (Java Native Interface) libarary from /CitaqSDK/libs/x86/libposctrl_jni.so. This fits my experience when working with Qualcomm SoC in that they don't typically give you datasheets for direct access to the gpio memory map as those tend to be behind NDAs for some reason...

As for other boards/cpu mentioned above, there is no led reference to it... so its likely an older model that may not have an led bar.

But anyway for the context of this repository this does not matter. We got what we needed for system speccing now.

Whats with the other serial virtual file ports???

In ./CitaqSDK/src/com/citaq/citaqfactory/CitaqApplication.java we see

serial port getter name Serial Port Devices Baud Flow Control
getPrintSerialPort() '/dev/ttyS1` 115200 true
getPrintSerialPortMT() '/dev/ttyMT0` 115200 true
getMSRSerialPort() '/dev/ttyS2` 19200 false
getCtmDisplaySerialPort() '/dev/ttyS3` 9600 false
getMSRSerialPort_S4() '/dev/ttyS4` 19200 false

So let's at least annotate this for documentation

(Note: My best guess is that MSR means Magnetic Stripe Reader in the context of Point Of Sales equiment)

CitaqApplication.java func() Serial Port Devices Baud Flow Control Note On Usage
getPrintSerialPort() '/dev/ttyS1` 115200 true Internal Thermal Printer
getPrintSerialPortMT() '/dev/ttyMT0` 115200 true Unknown Usage
getMSRSerialPort() '/dev/ttyS2` 19200 false Magnetic Stripe Reader via serial port
getCtmDisplaySerialPort() '/dev/ttyS3` 9600 false Customer Display facing serial port. Used by FSK Caller ID and PD (ESC/POS Printer Device) example in POSFactory app
getMSRSerialPort_S4() '/dev/ttyS4` 19200 false Magnetic Stripe Reader via serial port

I'm not sure which one would control the serial port that is externally accessible from the CITAQ H10-3

Dumping

Enable Android dev mode by tapping Settings > About tablet > Build Number multiple times. Then install a terminal emulator android app and run start adbd command to start the adb daemon server. Then use ifconfig to find the ip address that this device is using.

Then on the PC type these command (Replace ${IP_ADDRESS} with the ip address of the device)

sudo apt install adb
adb connect ${IP_ADDRESS}:5555
adb shell

Then within the adb shell I ran /proc/cpuinfo and dumped the content here:

shell@rk3368:/ $ cat /proc/cpuinfo
processor	: 0
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer	: 0x41
CPU architecture: AArch64
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 3

processor	: 1
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer	: 0x41
CPU architecture: AArch64
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 3

processor	: 2
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer	: 0x41
CPU architecture: AArch64
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 3

processor	: 3
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer	: 0x41
CPU architecture: AArch64
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 3

processor	: 4
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer	: 0x41
CPU architecture: AArch64
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 3

processor	: 5
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer	: 0x41
CPU architecture: AArch64
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 3

processor	: 6
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer	: 0x41
CPU architecture: AArch64
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 3

processor	: 7
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer	: 0x41
CPU architecture: AArch64
CPU variant	: 0x0
CPU part	: 0xd03
CPU revision	: 3

Hardware	: rockchip,rk3368
Revision	: 0000
Serial		: 3omdcoosnxpb7jmo

Did a bit of test if I could simplify getCpuHardware() with pure shell commands avaliable in android shell.

Found this shell command works well.

cat /proc/cpuinfo | grep 'Hardware' | cut -d':' -f2 | cut -d',' -f2 | tr -d '[:space:]' | tr '[:lower:]' '[:upper:]'

So given /proc/cpuinfo file where one of the line is: Hardware: rockchip,rk3368 this is the breakdown of the above command.

  • cat /proc/cpuinfo: Reads the input line from /proc/cpuinfo.
  • grep 'Hardware': Searches for lines containing the text "Hardware"
  • cut -d':' -f2: Splits the line at the colon (":") delimiter and selects the second field, which is " rockchip,rk3368" (with a leading space).
  • cut -d',' -f2: Splits the text at the comma (",") delimiter and selects the second field, which is "rk3368" (without any spaces).
  • tr -d '[:space:]': Removes any remaining spaces, resulting in "rk3368."
  • tr '[:lower:]' '[:upper:]': Converts the text to uppercase, producing the final output: "RK3368."

So, the command successfully processes the input line "Hardware: rockchip,rk3368" and outputs "RK3368" as the hardware identifier, which you could try to detect via getCpuHardware().contains('RK3368').

With this insight, we could simplify getCpuHardware() from the SDK into something like this

    String getCpuHardware() {
        try {
            Process process = Runtime.getRuntime().exec("cat /proc/cpuinfo | grep 'Hardware' | cut -d':' -f2 | cut -d',' -f2 | tr -d '[:space:]' | tr '[:lower:]' '[:upper:]'");
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
			return reader.readLine();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return "Unknown";
    }

or in bash script function if you somehow need it...

function getCpuHardware { cat /proc/cpuinfo | grep 'Hardware' | cut -d':' -f2 | cut -d',' -f2 | tr -d '[:space:]' | tr '[:lower:]' '[:upper:]'; }
getCpuHardware # call get cpu hardware check

What shell programs accessible in Android v6.0

Citaq can supply firmware up to Androiv v6.0 for Citaq H10-3 as of 2023-09-11.

Often the examples and experiments I've done is via bash commands so best to know what I can use. Android uses toybox and busybox for shell utils, but does not have all the typical commands of a full desktop linux OS.

For future reference here it is:

root@rk3368:/ # toybox
acpi base64 basename blkid blockdev bzcat cal cat chattr chcon chgrp
chmod chown chroot cksum clear cmp comm cp cpio cut date dd df dirname
dmesg dos2unix du echo egrep env expand expr fallocate false fgrep
find free freeramdisk fsfreeze getenforce getprop grep groups head
help hostname hwclock id ifconfig inotifyd insmod install kill killall
ln load_policy logname losetup ls lsattr lsmod lsusb makedevs md5sum
mkdir mkfifo mknod mkswap mktemp modinfo more mount mountpoint mv
nbd-client nc netcat netstat nice nl nohup od partprobe paste patch
pgrep pidof pivot_root pkill pmap printenv printf pwd pwdx readlink
realpath renice restorecon rev rfkill rm rmdir rmmod route runcon
sed seq setenforce setprop setsid sha1sum sleep sort split stat strings
swapoff swapon switch_root sync sysctl tac tail tar taskset tee time
timeout top touch tr traceroute traceroute6 true truncate tty umount
uname uniq unix2dos usleep vconfig vmstat wc which whoami xargs yes

root@rk3368:/sdcard # busybox
BusyBox v1.22.1 (2014-11-27 12:30:44 CST) multi-call binary.
BusyBox is copyrighted by many authors between 1998-2012.
Licensed under GPLv2. See source distribution for detailed
copyright notices.

Usage: busybox [function [arguments]...]
   or: busybox --list
   or: function [arguments]...

	BusyBox is a multi-call binary that combines many common Unix
	utilities into a single executable.  Most people will create a
	link to busybox for each function they wish to use and BusyBox
	will act like whatever it was invoked as.

Currently defined functions:
	[, [[, ar, arp, ash, awk, base64, basename, bash, beep, blkid,
	blockdev, bootchartd, bunzip2, bzcat, bzip2, cal, cat, catv, chat,
	chattr, chgrp, chmod, chown, chpst, chroot, chrt, chvt, cksum, clear,
	cmp, comm, cp, cpio, crond, crontab, cttyhack, cut, dc, dd, deallocvt,
	depmod, devmem, df, diff, dirname, dmesg, dnsd, dos2unix, dpkg,
	dpkg-deb, du, dumpkmap, echo, ed, egrep, env, envdir, envuidgid,
	expand, expr, fakeidentd, false, fbset, fbsplash, fdflush, fdformat,
	fdisk, fgconsole, fgrep, find, findfs, flash_lock, flash_unlock,
	flashcp, flock, fold, free, freeramdisk, fstrim, fsync, ftpd, ftpget,
	ftpput, fuser, getopt, grep, gunzip, gzip, halt, hd, hdparm, head,
	hexdump, httpd, hwclock, ifconfig, ifdown, ifup, init, inotifyd,
	insmod, install, iostat, ip, ipaddr, ipcalc, iplink, iproute, iprule,
	iptunnel, klogd, less, linuxrc, ln, loadkmap, losetup, lpd, lpq, lpr,
	ls, lsattr, lsmod, lsof, lspci, lsusb, lzcat, lzma, lzop, lzopcat,
	makedevs, makemime, man, md5sum, mdev, mesg, mkdir, mkfifo, mknod,
	mkswap, mktemp, modinfo, modprobe, more, mount, mpstat, mv, nanddump,
	nbd-client, nc, netstat, nice, nmeter, nohup, od, openvt, patch, pidof,
	ping, pipe_progress, pkill, pmap, popmaildir, poweroff, powertop,
	printenv, printf, ps, pscan, pstree, pwd, pwdx, raidautorun, rdev,
	readlink, readprofile, realpath, reboot, reformime, renice, reset,
	resize, rev, rm, rmdir, rmmod, route, rpm, rpm2cpio, rtcwake,
	run-parts, runsv, runsvdir, rx, script, scriptreplay, sed, sendmail,
	seq, setconsole, setkeycodes, setlogcons, setserial, setsid, setuidgid,
	sh, sha1sum, sha256sum, sha3sum, sha512sum, showkey, sleep, smemcap,
	softlimit, sort, split, start-stop-daemon, strings, stty, sum, sv,
	svlogd, switch_root, sync, sysctl, tac, tail, tar, tcpsvd, tee, telnet,
	telnetd, test, tftp, tftpd, time, timeout, top, tr, traceroute, true,
	ttysize, tunctl, tune2fs, udhcpc, udpsvd, umount, uname, uncompress,
	unexpand, uniq, unix2dos, unlzma, unlzop, unxz, unzip, uptime, usleep,
	uudecode, uuencode, vconfig, vi, volname, watch, wc, wget, which,
	whoami, whois, xargs, xz, xzcat, yes, zcat

A good reference for all commands across android version is https://android.googlesource.com/platform/system/core/+/master/shell_and_utilities/README.md