commit 71bdc4afae2615f7e893446c4817123185e0e0b1 Author: rdkartono Date: Wed Dec 4 08:59:37 2024 +0700 first commit diff --git a/lib/bananapi/dynamic/libpi4j.so b/lib/bananapi/dynamic/libpi4j.so new file mode 100644 index 0000000..509c84e Binary files /dev/null and b/lib/bananapi/dynamic/libpi4j.so differ diff --git a/lib/bananapi/static/libpi4j.so b/lib/bananapi/static/libpi4j.so new file mode 100644 index 0000000..090bf2f Binary files /dev/null and b/lib/bananapi/static/libpi4j.so differ diff --git a/lib/bananapro/dynamic/libpi4j.so b/lib/bananapro/dynamic/libpi4j.so new file mode 100644 index 0000000..91dda23 Binary files /dev/null and b/lib/bananapro/dynamic/libpi4j.so differ diff --git a/lib/bananapro/static/libpi4j.so b/lib/bananapro/static/libpi4j.so new file mode 100644 index 0000000..9d7da10 Binary files /dev/null and b/lib/bananapro/static/libpi4j.so differ diff --git a/lib/bpi/dynamic/libpi4j.so b/lib/bpi/dynamic/libpi4j.so new file mode 100644 index 0000000..039bf75 Binary files /dev/null and b/lib/bpi/dynamic/libpi4j.so differ diff --git a/lib/bpi/static/libpi4j.so b/lib/bpi/static/libpi4j.so new file mode 100644 index 0000000..4175064 Binary files /dev/null and b/lib/bpi/static/libpi4j.so differ diff --git a/lib/hidapi/darwin/libhidapi.dylib b/lib/hidapi/darwin/libhidapi.dylib new file mode 100644 index 0000000..91389ae Binary files /dev/null and b/lib/hidapi/darwin/libhidapi.dylib differ diff --git a/lib/hidapi/linux-aarch64/libhidapi-libusb.so b/lib/hidapi/linux-aarch64/libhidapi-libusb.so new file mode 100644 index 0000000..3999076 Binary files /dev/null and b/lib/hidapi/linux-aarch64/libhidapi-libusb.so differ diff --git a/lib/hidapi/linux-aarch64/libhidapi.so b/lib/hidapi/linux-aarch64/libhidapi.so new file mode 100644 index 0000000..8de0ea5 Binary files /dev/null and b/lib/hidapi/linux-aarch64/libhidapi.so differ diff --git a/lib/hidapi/linux-amd64/libhidapi-libusb.so b/lib/hidapi/linux-amd64/libhidapi-libusb.so new file mode 100644 index 0000000..c773a3b Binary files /dev/null and b/lib/hidapi/linux-amd64/libhidapi-libusb.so differ diff --git a/lib/hidapi/linux-amd64/libhidapi.so b/lib/hidapi/linux-amd64/libhidapi.so new file mode 100644 index 0000000..ef1a11f Binary files /dev/null and b/lib/hidapi/linux-amd64/libhidapi.so differ diff --git a/lib/hidapi/linux-arm/libhidapi-libusb.so b/lib/hidapi/linux-arm/libhidapi-libusb.so new file mode 100644 index 0000000..bf2b6ae Binary files /dev/null and b/lib/hidapi/linux-arm/libhidapi-libusb.so differ diff --git a/lib/hidapi/linux-arm/libhidapi.so b/lib/hidapi/linux-arm/libhidapi.so new file mode 100644 index 0000000..8aad408 Binary files /dev/null and b/lib/hidapi/linux-arm/libhidapi.so differ diff --git a/lib/hidapi/linux-armel/libhidapi-libusb.so b/lib/hidapi/linux-armel/libhidapi-libusb.so new file mode 100644 index 0000000..4e5117a Binary files /dev/null and b/lib/hidapi/linux-armel/libhidapi-libusb.so differ diff --git a/lib/hidapi/linux-armel/libhidapi.so b/lib/hidapi/linux-armel/libhidapi.so new file mode 100644 index 0000000..8aad408 Binary files /dev/null and b/lib/hidapi/linux-armel/libhidapi.so differ diff --git a/lib/hidapi/linux-x86-64/libhidapi-libusb.so b/lib/hidapi/linux-x86-64/libhidapi-libusb.so new file mode 100644 index 0000000..c773a3b Binary files /dev/null and b/lib/hidapi/linux-x86-64/libhidapi-libusb.so differ diff --git a/lib/hidapi/linux-x86-64/libhidapi.so b/lib/hidapi/linux-x86-64/libhidapi.so new file mode 100644 index 0000000..ef1a11f Binary files /dev/null and b/lib/hidapi/linux-x86-64/libhidapi.so differ diff --git a/lib/hidapi/linux-x86/libhidapi-libusb.so b/lib/hidapi/linux-x86/libhidapi-libusb.so new file mode 100644 index 0000000..7de1e57 Binary files /dev/null and b/lib/hidapi/linux-x86/libhidapi-libusb.so differ diff --git a/lib/hidapi/linux-x86/libhidapi.so b/lib/hidapi/linux-x86/libhidapi.so new file mode 100644 index 0000000..513f993 Binary files /dev/null and b/lib/hidapi/linux-x86/libhidapi.so differ diff --git a/lib/hidapi/win32-x86-64/hidapi.dll b/lib/hidapi/win32-x86-64/hidapi.dll new file mode 100644 index 0000000..070b39c Binary files /dev/null and b/lib/hidapi/win32-x86-64/hidapi.dll differ diff --git a/lib/hidapi/win32-x86/hidapi.dll b/lib/hidapi/win32-x86/hidapi.dll new file mode 100644 index 0000000..e9a055b Binary files /dev/null and b/lib/hidapi/win32-x86/hidapi.dll differ diff --git a/lib/nanopi/dynamic/libpi4j.so b/lib/nanopi/dynamic/libpi4j.so new file mode 100644 index 0000000..05b056a Binary files /dev/null and b/lib/nanopi/dynamic/libpi4j.so differ diff --git a/lib/nanopi/static/libpi4j.so b/lib/nanopi/static/libpi4j.so new file mode 100644 index 0000000..aa4cd94 Binary files /dev/null and b/lib/nanopi/static/libpi4j.so differ diff --git a/lib/odroid/dynamic/libpi4j.so b/lib/odroid/dynamic/libpi4j.so new file mode 100644 index 0000000..91dda23 Binary files /dev/null and b/lib/odroid/dynamic/libpi4j.so differ diff --git a/lib/odroid/static/libpi4j.so b/lib/odroid/static/libpi4j.so new file mode 100644 index 0000000..198c7da Binary files /dev/null and b/lib/odroid/static/libpi4j.so differ diff --git a/lib/orangepi/dynamic/libpi4j.so b/lib/orangepi/dynamic/libpi4j.so new file mode 100644 index 0000000..91dda23 Binary files /dev/null and b/lib/orangepi/dynamic/libpi4j.so differ diff --git a/lib/orangepi/static/libpi4j.so b/lib/orangepi/static/libpi4j.so new file mode 100644 index 0000000..4ec0b5c Binary files /dev/null and b/lib/orangepi/static/libpi4j.so differ diff --git a/lib/raspberrypi/dynamic/libpi4j.so b/lib/raspberrypi/dynamic/libpi4j.so new file mode 100644 index 0000000..039bf75 Binary files /dev/null and b/lib/raspberrypi/dynamic/libpi4j.so differ diff --git a/src/ModbusWrapper/jModbusCoil.java b/src/ModbusWrapper/jModbusCoil.java new file mode 100644 index 0000000..a41bcbf --- /dev/null +++ b/src/ModbusWrapper/jModbusCoil.java @@ -0,0 +1,22 @@ +package ModbusWrapper; + +import com.intelligt.modbus.jlibmodbus.data.ModbusCoils; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("ModbusCoils") +/*** + * ModbusCoil dataholder + * by default, create 1024 coils + * @author rdkartono + * + */ +public class jModbusCoil extends ModbusCoils { + public jModbusCoil() { + super(1024); + } + public jModbusCoil(int size) { + super(size); + } + +} diff --git a/src/ModbusWrapper/jModbusHoldingRegister.java b/src/ModbusWrapper/jModbusHoldingRegister.java new file mode 100644 index 0000000..2395ad1 --- /dev/null +++ b/src/ModbusWrapper/jModbusHoldingRegister.java @@ -0,0 +1,22 @@ +package ModbusWrapper; + +import com.intelligt.modbus.jlibmodbus.data.ModbusHoldingRegisters; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("ModbusHoldingRegisters") +/** + * ModbusHoldingRegister dataholder + * by default create 1024 registers + * @author rdkartono + * + */ +public class jModbusHoldingRegister extends ModbusHoldingRegisters { + public jModbusHoldingRegister(){ + super(1024); + } + + public jModbusHoldingRegister(int size){ + super(size); + } +} diff --git a/src/ModbusWrapper/jModbusMaster.java b/src/ModbusWrapper/jModbusMaster.java new file mode 100644 index 0000000..910a8b3 --- /dev/null +++ b/src/ModbusWrapper/jModbusMaster.java @@ -0,0 +1,447 @@ +package ModbusWrapper; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import com.fazecast.jSerialComm.SerialPort; +import com.intelligt.modbus.jlibmodbus.Modbus; +import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException; +import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException; +import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException; +import com.intelligt.modbus.jlibmodbus.master.ModbusMaster; +import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory; +import com.intelligt.modbus.jlibmodbus.serial.SerialParameters; +import com.intelligt.modbus.jlibmodbus.serial.SerialUtils; +import com.intelligt.modbus.jlibmodbus.serial.SerialPort.BaudRate; +import com.intelligt.modbus.jlibmodbus.serial.SerialPort.Parity; +import com.intelligt.modbus.jlibmodbus.serial.SerialPortException; +import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters; +import com.intelligt.modbus.jlibmodbus.utils.FrameEvent; +import com.intelligt.modbus.jlibmodbus.utils.FrameEventListener; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.objects.collections.List; + +@BA.ShortName("jModbusMaster") +@BA.Events(values= { + "log(msg as string)", + "datasent(bb() as byte)", + "datareceived(bb() as byte)" +}) +//@BA.DependsOn(values = { "jSerialComm-2.6.2","jlibmodbus-1.2.9.7" }) + +/** + * Create Modbus Master + * in Modbus, Master is actually a client that request data from server (Slave) + * @author rdkartono + * + */ +public class jModbusMaster { + private Object caller; + private String event; + private BA ba; + private final Object Me = this; + + private boolean need_log_event = false; + private boolean need_datasent_event = false; + private boolean need_datareceived_event = false; + + private ModbusMaster modbus; + + /** + * Initialize ModbusSerialSlaveRTU + * @param callerobject : caller object + * @param eventname : event name + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + this.ba = ba; + event = eventname.trim(); + caller = callerobject; + + if (this.ba!=null) { + if (caller!=null) { + if (!event.isEmpty()) { + need_log_event = this.ba.subExists(event+"_log"); + need_datasent_event = this.ba.subExists(event+"_datasent"); + need_datareceived_event = this.ba.subExists(event+"_datareceived"); + } + } + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + } + + /** + * Get available Serial Port Names + * @return List of port names. If exists, List size > 0 + */ + public List GetSerialPortNames() { + List result = new List(); + result.Initialize(); + + + SerialPort[] sp = SerialPort.getCommPorts(); + if (sp!=null) { + if (sp.length>0) { + for(SerialPort xx : sp) { + result.Add(xx.getSystemPortName()); + } + } + } + return result; + } + + /** + * Open Modbus Master in Serial ASCII format + * @param serialname : serial port name + * @param baudrate : baud rate, typically 4800, 9600, 144400, 19200, 38400, 57600, 115200 + * @param databit : data bit length , typically 8 + * @param stopbit : typically 1 + * @param parity : 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space + * @return true if success + */ + public boolean OpenSerial_ASCII(String serialname, int baudrate, int databit, int stopbit, int parity) { + SerialParameters sp = new SerialParameters(); + + sp.setDataBits(databit); + sp.setDevice(serialname); + sp.setStopBits(stopbit); + sp.setBaudRate(BaudRate.getBaudRate(baudrate)); + sp.setParity(Parity.getParity(parity)); + + SerialUtils.setSerialPortFactoryJSerialComm(); + + try { + modbus = ModbusMasterFactory.createModbusMasterASCII(sp); + modbus.addListener(listener); + modbus.connect(); + return true; + } catch (SerialPortException e) { + raise_log("OpenSerial_ASCII failed, Msg : "+e.getMessage()); + return false; + } catch (ModbusIOException e) { + raise_log("Modbus Connect failed, Msg : "+e.getMessage()); + return false; + } + + } + + /** + * Open Modbus Master in Serial RTU format + * @param serialname : serial port name + * @param baudrate : baud rate, typically 4800, 9600, 144400, 19200, 38400, 57600, 115200 + * @param databit : data bit length , typically 8 + * @param stopbit : typically 1 + * @param parity : 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space + * @return true if success + */ + public boolean OpenSerial_RTU(String serialname, int baudrate, int databit, int stopbit, int parity) { + SerialParameters sp = new SerialParameters(); + + sp.setDataBits(databit); + sp.setDevice(serialname); + sp.setStopBits(stopbit); + sp.setBaudRate(BaudRate.getBaudRate(baudrate)); + sp.setParity(Parity.getParity(parity)); + + SerialUtils.setSerialPortFactoryJSerialComm(); + + try { + modbus = ModbusMasterFactory.createModbusMasterRTU(sp); + modbus.addListener(listener); + modbus.connect(); + return true; + } catch (SerialPortException e) { + raise_log("OpenSerial_RTU failed, Msg : "+e.getMessage()); + return false; + } catch (ModbusIOException e) { + raise_log("Modbus Connect failed, Msg : "+e.getMessage()); + return false; + } + + } + + /** + * Open Modbus Master in TCP + * @param hostaddress : target slave IP address + * @param port : tcp port. if negative, will use default 502 + * @param enable_keepalive : set true to enable keep alive connection + * @return true if success + */ + public boolean OpenTCP(String hostaddress, int port, boolean enable_keepalive) { + InetAddress host; + try { + host = InetAddress.getByName(hostaddress); + } catch (UnknownHostException e) { + raise_log("OpenTCP failed, Invalid hostaddress="+hostaddress+", Msg : "+e.getMessage()); + return false; + } + + if (port<1) port = Modbus.TCP_PORT; + TcpParameters tp = new TcpParameters(); + tp.setPort(port); + tp.setHost(host); + tp.setKeepAlive(enable_keepalive); + + modbus = ModbusMasterFactory.createModbusMasterTCP(tp); + modbus.addListener(listener); + try { + modbus.connect(); + return true; + } catch (ModbusIOException e) { + raise_log("Modbus Connect failed, Msg : "+e.getMessage()); + return false; + } + } + + /** + * Close Modbus Master connection + * @return true if success + */ + public boolean Close() { + if (modbus instanceof ModbusMaster) { + modbus.removeListeners(); + try { + modbus.disconnect(); + } catch (ModbusIOException e) { + raise_log("Modbus Disconnect failed, Msg : "+e.getMessage()); + return false; + } + } + return true; + } + + /*** + * Write Single Coil (command = 5) + * @param slave_address : slave address + * @param coil_address : coil address + * @param is_on : true = 1, false = 0 + * @return true if success + */ + public boolean WriteSingleCoil(int slave_address, int coil_address, boolean is_on) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + modbus.writeSingleCoil(slave_address, coil_address, is_on); + return true; + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("WriteSingleCoil failed, Msg : "+e.getMessage()); + + } + } + } + return false; + } + + /** + * Write Single Register (command = 6) + * @param slave_address : slave address + * @param register_address : register address + * @param value : register value , 16 bit + * @return true if success + */ + public boolean WriteSingleRegister(int slave_address, int register_address, int value) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + modbus.writeSingleRegister(slave_address, register_address, value); + return true; + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("WriteSingleRegister failed, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Write multiple coils (command = 15) + * @param slave_address : slave address + * @param start_address : start address of coils + * @param values : array of boolean for coils, true = 1, false = 0 + * @return true if success + */ + public boolean WriteMultipleCoils(int slave_address, int start_address, boolean[] values) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + modbus.writeMultipleCoils(slave_address, start_address, values); + return true; + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("WriteMultipleCoils failed, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Write Multiple Register (command = 16) + * @param slave_address : slave address + * @param start_address : start address of register + * @param values : array of int (16 bit) for registers + * @return true if success + */ + public boolean WriteMultipleRegisters(int slave_address, int start_address, int[] values) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + modbus.writeMultipleRegisters(slave_address, start_address, values); + return true; + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("WriteMultipleRegisters failed, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Read Single coil (command = 1) + * @param slave_address : slave address + * @param coil_address : coil address + * @return -1 = failed, 0 = false, 1 = true + */ + public int ReadSingleCoil(int slave_address, int coil_address) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + boolean[] result = modbus.readCoils(slave_address, coil_address, 1); + if (result!=null) { + if (result.length>0) { + if (result[0]) return 1; else return 0; + } + } + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("ReadSingleCoil failed, Msg : "+e.getMessage()); + } + } + } + return -1; + } + + /** + * Read single Register (16 bit) (command = 3) + * @param slave_address : slave address + * @param register_address : register address + * @return -1 = failed, other = register value + */ + public int ReadSingleRegister(int slave_address, int register_address) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + int[] result = modbus.readHoldingRegisters(slave_address, register_address, 1); + if (result!=null) { + if (result.length>0) { + return Bit.And(result[0], 0xFFFF); + } + } + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("ReadSingleRegister failed, Msg : "+e.getMessage()); + } + } + } + return -1; + } + + /** + * Read multiple coils + * @param slave_address : slave address + * @param start_address : coil start address + * @param quantity : how many coils want to be read + * @return array of boolean, or null if failed. + */ + public boolean[] ReadMultipleCoils(int slave_address, int start_address, int quantity) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + boolean[] result = modbus.readCoils(slave_address, start_address, quantity); + if (result!=null) { + if (result.length>0) { + return result; + } + } + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("ReadMultipleCoils failed, Msg : "+e.getMessage()); + } + + } + } + return null; + } + + /** + * Read multiple registers + * @param slave_address : slave address + * @param start_address : register start address + * @param quantity : how many registers want to be read + * @return array of int, or null if failed + */ + public int[] ReadMultipleRegisters(int slave_address, int start_address, int quantity) { + if (modbus instanceof ModbusMaster) { + if (modbus.isConnected()) { + try { + int[] result = modbus.readHoldingRegisters(slave_address, start_address, quantity); + if (result!=null) { + if (result.length>0) { + return result; + } + } + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("ReadMultipleRegisters failed, Msg : "+e.getMessage()); + } + } + } + return null; + } + + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_datasent(byte[] value) { + if (need_datasent_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_datasent", false, new Object[] {value}); + } + + private void raise_datareceived(byte[] value) { + if (need_datareceived_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_datareceived", false, new Object[] {value}); + } + + private String printbytes(byte[] bb) { + if (bb!=null) { + if (bb.length>0) { + StringBuilder str = new StringBuilder(); + for(byte xx: bb) { + str.append(Bit.ToHexString(Bit.And(xx, 0xFF))).append(" "); + } + return str.toString(); + } + } + return ""; + } + + FrameEventListener listener = new FrameEventListener() { + + @Override + public void frameSentEvent(FrameEvent event) { + + raise_log("Data Sent : "+printbytes(event.getBytes())); + raise_datasent(event.getBytes()); + } + + @Override + public void frameReceivedEvent(FrameEvent event) { + raise_log("Data Received : "+printbytes(event.getBytes())); + raise_datareceived(event.getBytes()); + } + + + }; +} diff --git a/src/ModbusWrapper/jModbusSlave.java b/src/ModbusWrapper/jModbusSlave.java new file mode 100644 index 0000000..b41f3cf --- /dev/null +++ b/src/ModbusWrapper/jModbusSlave.java @@ -0,0 +1,345 @@ +package ModbusWrapper; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import com.fazecast.jSerialComm.SerialPort; +import com.intelligt.modbus.jlibmodbus.Modbus; +import com.intelligt.modbus.jlibmodbus.data.DataHolder; +import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException; +import com.intelligt.modbus.jlibmodbus.serial.SerialParameters; +import com.intelligt.modbus.jlibmodbus.serial.SerialPort.BaudRate; +import com.intelligt.modbus.jlibmodbus.serial.SerialPort.Parity; +import com.intelligt.modbus.jlibmodbus.serial.SerialPortException; +import com.intelligt.modbus.jlibmodbus.serial.SerialUtils; +import com.intelligt.modbus.jlibmodbus.slave.ModbusSlave; +import com.intelligt.modbus.jlibmodbus.slave.ModbusSlaveFactory; +import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters; +import com.intelligt.modbus.jlibmodbus.utils.FrameEvent; +import com.intelligt.modbus.jlibmodbus.utils.FrameEventListener; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.objects.collections.List; + +@BA.ShortName("jModbusSlave") +@BA.Events(values= { + "log(msg as string)", + "datasent(bb() as byte)", + "datareceived(bb() as byte)" +}) +//@BA.DependsOn(values = { "jSerialComm-2.6.2","jlibmodbus-1.2.9.7" }) + +/** + * Create Modbus Slave + * in Modbus, Slave is actually a server that hold data. + * Master is a client, that request data from server. + * @author rdkartono + * + */ +public class jModbusSlave { + + + private Object caller; + private String event; + private BA ba; + private final Object Me = this; + + private boolean need_log_event = false; + private boolean need_datasent_event = false; + private boolean need_datareceived_event = false; + + + private ModbusSlave modbus = null; + private DataHolder dh = null; + private jModbusCoil coil = null; + private jModbusHoldingRegister reg = null; + + /** + * Initialize ModbusSerialSlaveRTU + * @param callerobject : caller object + * @param eventname : event name + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + this.ba = ba; + event = eventname.trim(); + caller = callerobject; + dh = new DataHolder(); + if (this.ba!=null) { + if (caller!=null) { + if (!event.isEmpty()) { + need_log_event = this.ba.subExists(event+"_log"); + need_datasent_event = this.ba.subExists(event+"_datasent"); + need_datareceived_event = this.ba.subExists(event+"_datareceived"); + } + } + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + } + + /** + * Get available Serial Port Names + * @return List of port names. If exists, List size > 0 + */ + public List GetSerialPortNames() { + List result = new List(); + result.Initialize(); + + + SerialPort[] sp = SerialPort.getCommPorts(); + if (sp!=null) { + if (sp.length>0) { + for(SerialPort xx : sp) { + result.Add(xx.getSystemPortName()); + } + } + } + return result; + } + + /** + * Open Modbus Slave in Serial ASCII format + * @param serialname : serial port name + * @param baudrate : baud rate, typically 4800, 9600, 144400, 19200, 38400, 57600, 115200 + * @param databit : data bit length , typically 8 + * @param stopbit : typically 1 + * @param parity : 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space + * @return true if success + */ + public boolean OpenSerial_ASCII(String serialname, int baudrate, int databit, int stopbit, int parity) { + SerialParameters sp = new SerialParameters(); + + sp.setDataBits(databit); + sp.setDevice(serialname); + sp.setStopBits(stopbit); + sp.setBaudRate(BaudRate.getBaudRate(baudrate)); + sp.setParity(Parity.getParity(parity)); + + SerialUtils.setSerialPortFactoryJSerialComm(); + + try { + modbus = ModbusSlaveFactory.createModbusSlaveASCII(sp); + modbus.addListener(listener); + if (dh==null) dh = new DataHolder(); + modbus.setDataHolder(dh); + modbus.listen(); + return true; + } catch(SerialPortException e) { + raise_log("OpenSerial_ASCII failed, Msg : "+e.getMessage()); + return false; + } catch (ModbusIOException e) { + raise_log("Modbus Listen failed, Msg : "+e.getMessage()); + return false; + } + } + + /** + * Open Modbus Slave in Serial RTU format + * @param serialname : serial port name + * @param baudrate : baud rate, typically 4800, 9600, 144400, 19200, 38400, 57600, 115200 + * @param databit : data bit length , typically 8 + * @param stopbit : typically 1 + * @param parity : 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space + * @return true if success + */ + public boolean OpenSerial_RTU(String serialname, int baudrate, int databit, int stopbit, int parity) { + + SerialParameters sp = new SerialParameters(); + + sp.setDataBits(databit); + sp.setDevice(serialname); + sp.setStopBits(stopbit); + sp.setBaudRate(BaudRate.getBaudRate(baudrate)); + sp.setParity(Parity.getParity(parity)); + + SerialUtils.setSerialPortFactoryJSerialComm(); + + try { + + modbus = ModbusSlaveFactory.createModbusSlaveRTU(sp); + + modbus.addListener(listener); + if (dh==null) dh = new DataHolder(); + modbus.setDataHolder(dh); + modbus.listen(); + return true; + } catch (SerialPortException e) { + raise_log("OpenSerial_RTU failed, Msg : "+e.getMessage()); + return false; + } catch (ModbusIOException e) { + raise_log("Modbus Listen failed, Msg : "+e.getMessage()); + return false; + } + } + + /** + * Open Modbus Slave in TCP + * @param hostaddress : network adapter IP address, or empty string for default ip address + * @param port : port number, if negative number, will use default TCP port 502 + * @param enable_keepalive : set true to enable keep alive connection + * @return true if success + */ + public boolean OpenTCP(String hostaddress, int port, boolean enable_keepalive) { + InetAddress host; + try { + if (hostaddress.isEmpty()) { + host = InetAddress.getLocalHost(); + } else { + host = InetAddress.getByName(hostaddress); + } + } catch (UnknownHostException | SecurityException e) { + raise_log("Unable to get InetAddress for host="+hostaddress+", Msg : "+e.getMessage()); + return false; + } + + if (port<1) port = Modbus.TCP_PORT; + + TcpParameters tp = new TcpParameters(); + tp.setPort(port); + tp.setHost(host); + tp.setKeepAlive(enable_keepalive); + + modbus = ModbusSlaveFactory.createModbusSlaveTCP(tp); + if (dh==null) dh = new DataHolder(); + + modbus.setDataHolder(dh); + modbus.addListener(listener); + try { + modbus.listen(); + } catch (ModbusIOException e) { + raise_log("Unable to start Modbus Listen, Msg : "+e.getMessage()); + return false; + } + return true; + } + + /** + * Get / Set Slave ID. + * On getting, when returning -1, means modbus not yet opened + */ + public void setID(int value) { + if (modbus!=null) { + modbus.setServerAddress(value); + } + } + + public int getID() { + if (modbus!=null) { + return modbus.getServerAddress(); + } else return -1; + } + + /** + * Initialize coils + * coil is binary (true/false) value, 1 bit + * @param size : how many coils want to initialize. If < 1, default to 1024 + */ + public void Initialize_Coils(int size) { + if (size>0) + coil = new jModbusCoil(size); + else + coil = new jModbusCoil(); + + if (dh==null) dh = new DataHolder(); + dh.setCoils(coil); + } + + /** + * Get Coils that already been initialized + * @return null if failed + */ + public jModbusCoil getCoils() { + return coil; + } + + /** + * Get Registers that already been initialized + * @return null if failed + */ + public jModbusHoldingRegister getRegisters() { + return reg; + } + + /** + * Initialize Register + * Register is 16 bit value, can read and write + * @param size : how many registers want to initialize. If < 1, default to 1024 + */ + public void Initialize_Registers(int size) { + if (size>0) + reg = new jModbusHoldingRegister(size); + else + reg = new jModbusHoldingRegister(); + + if (dh==null) dh = new DataHolder(); + dh.setHoldingRegisters(reg); + } + + /** + * Close Modbus Serial Slave RTU + * @return true if success + */ + public boolean Close() { + if (modbus!=null) { + try { + modbus.removeListeners(); + modbus.shutdown(); + } catch (ModbusIOException e) { + raise_log("Modbus Close failed, Msg : "+e.getMessage()); + return false; + } + } + return true; + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_datasent(byte[] value) { + if (need_datasent_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_datasent", false, new Object[] {value}); + } + + private void raise_datareceived(byte[] value) { + if (need_datareceived_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_datareceived", false, new Object[] {value}); + } + + private String printbytes(byte[] bb) { + if (bb!=null) { + if (bb.length>0) { + StringBuilder str = new StringBuilder(); + for(byte xx: bb) { + str.append(Bit.ToHexString(Bit.And(xx, 0xFF))).append(" "); + } + return str.toString(); + } + } + return ""; + } + + FrameEventListener listener = new FrameEventListener() { + + @Override + public void frameSentEvent(FrameEvent event) { + + raise_log("Data Sent : "+printbytes(event.getBytes())); + raise_datasent(event.getBytes()); + } + + @Override + public void frameReceivedEvent(FrameEvent event) { + raise_log("Data Received : "+printbytes(event.getBytes())); + raise_datareceived(event.getBytes()); + } + + + }; + + +} diff --git a/src/SBC/BasicSBCInfo.java b/src/SBC/BasicSBCInfo.java new file mode 100644 index 0000000..0c24c44 --- /dev/null +++ b/src/SBC/BasicSBCInfo.java @@ -0,0 +1,1275 @@ +package SBC; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.Enumeration; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.keywords.DateTime; +import anywheresoftware.b4a.keywords.Regex; +import anywheresoftware.b4a.objects.collections.List; +import anywheresoftware.b4a.objects.collections.Map; +import jGPIO.jGPIO; + +@BA.Events(values= { + "log(msg as string)", + "isvalidip(toip as string, isvalid as boolean)", + "isreachableip(toip as string, isreachable as boolean)", + "networkstatistic(value as NetworkMeterInfo)", + "cpuusage(value as Map)" +}) + +public abstract class BasicSBCInfo { + + + private boolean need_log_event = false; + private boolean need_isvalidip_event = false; + private boolean need_isreachableip_event = false; + private boolean need_networkstatistic_event = false; + private boolean need_cpuusage_event = false; + private Object caller; + private String eventname; + private BA ba; + private Object Me; + private String hardware = ""; + private String revision = ""; + private String serialnumber = ""; + + protected BasicSBCInfo(){ + if (jGPIO.osname.isEmpty()) jGPIO.detectOS(); + + Map data = new Map(); + + data.Initialize(); + + try { + boolean result = Get_CPUINFO(data); + if (result) { + + if (data.ContainsKey("Hardware")) + hardware = (String) data.Get("Hardware"); + if (data.ContainsKey("Revision")) + revision = (String) data.Get("Revision"); + if (data.ContainsKey("Serial")) + serialnumber = (String) data.Get("Serial"); + } else { + hardware = ""; + revision = ""; + serialnumber = ""; + } + } catch (IOException e) { + hardware = ""; + revision = ""; + serialnumber = ""; + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Stop_CPU_Usage_Monitoring(); + Stop_Network_Meter(); + } + }); + + + } + + public abstract double Get_CPU_CoreVolt(); + + + protected void setup_events(final BA bax, final Object callerobject, final String event, final Object Sender) { + ba = bax; + caller = callerobject; + eventname = event; + Me = Sender; + if (ba instanceof BA) { + if (caller != null) { + if (eventname instanceof String) { + if (!eventname.isEmpty()) { + if (Me != null) { + need_isvalidip_event = ba.subExists(eventname + "_isvalidip"); + need_log_event = ba.subExists(eventname + "_log"); + need_isreachableip_event = ba.subExists(eventname + "_isreachableip"); + need_networkstatistic_event = ba.subExists(eventname+"_networkstatistic"); + need_cpuusage_event = ba.subExists(eventname+"_cpuusage"); + } + } + } + } + } + } + + + protected double pembulatan_1desimal(double value) { + int temp = (int)(value * 10); + return (temp / 10.0); + } + + private long split_meminfo_string(String value) { + value = value.trim(); + String result[] = value.split("\\s+"); + if (result.length==2) { + result[0] = result[0].trim(); + result[1] = result[1].trim(); + try { + Long xx = Long.parseLong(result[0]); + return xx; + } catch(NumberFormatException e) { + BA.Log("Failed to Long.parseLong "+result[0]+", Message="+e.getMessage()+", Caused="+e.getCause()); + return -1; + } + + } else return -1; + } + + /** + * Get CPU Hardware Model + * + * @return empty string if failed to get + */ + public String getCPU_Hardware() { + return hardware; + } + + /** + * Get CPU Revision Code + * + * @return empty string if failed to get + */ + public String getCPU_Revision() { + return revision; + } + + /** + * Get CPU Serial Number + * + * @return empty string if failed to get + */ + public String getCPU_SerialNumber() { + return serialnumber; + } + + /** + * Get CPU INFO Provide Map object to contain result : key="Hardware", value = + * CPU hardware, if available key="Revision", value = CPU revision code, if + * available key="Serial", value = CPU serial number, if available + * + * @param data : Map of to contain result + * @return true if success + * @throws IOException + */ + protected boolean Get_CPUINFO(Map data) throws IOException { + boolean result = false; + if (!jGPIO.IsLinux) { + return result; + } + if (data == null) + return result; + if (data.IsInitialized() == false) + return result; + if (!jGPIO.IsLinux) { + return result; + } + + data.Clear(); + File cpuinfo = new File("/proc/cpuinfo"); + if (cpuinfo.exists()) { + if (cpuinfo.canRead()) { + + BufferedReader reader = new BufferedReader(new FileReader(cpuinfo.getAbsolutePath())); + String line ; + do { + line = reader.readLine(); + if (line!=null) { + line = line.trim(); + if (line.startsWith("Hardware")) { + String[] vv = split_procinfo_string(line); + if (vv != null) { + data.Put(vv[0], vv[1]); + } + } + if (line.startsWith("Revision")) { + String[] vv = split_procinfo_string(line); + if (vv != null) { + data.Put(vv[0], vv[1]); + } + } + if (line.startsWith("Serial")) { + String[] vv = split_procinfo_string(line); + if (vv != null) { + data.Put(vv[0], vv[1]); + } + } + } + + } while(line!=null); + + + reader.close(); + result = true; + } else { + BA.Log("/proc/cpuinfo can't be read"); + } + } else { + BA.Log("/proc/cpuinfo not found"); + } + + return result; + } + + private String[] split_procinfo_string(String value) { + value = value.trim(); + String result[] = value.split(":"); + if (result.length == 2) { + result[0] = result[0].trim(); + result[1] = result[1].trim(); + return result; + } else { + return null; + } + } + + private final double memory_MB = 1024; + private final double memory_GB = memory_MB * 1024; + + /** + * Convert Memory size (in KB) for easy reading + * @param value : memory size + * @return string value + */ + public String Convert_MemorySize_toString(double value) { + if (value < memory_MB) { + return pembulatan_1desimal(value)+ " KB"; + } else if (value < memory_GB) { + return pembulatan_1desimal(value / memory_MB)+" MB"; + } else return pembulatan_1desimal(value / memory_GB)+" GB"; + } + + /** + * Same as Get_MemoryInfo, but with dedicated RAM_Info class + * @return RAM_Info class, or null if invalid + */ + public RAM_Info Get_RAMInfo() { + Map vv = Get_MemoryInfo(); + if (vv instanceof Map) { + if (vv.IsInitialized()) { + if (vv.getSize()>0) { + RAM_Info result = new RAM_Info(Get_MemoryInfo()); + return result; + } + } + } + return null; + } + + /** + * Get Memory Info. + * Map will contain keys : + * key = "JVMTotalMemory" , value (Double) in KB + * key = "JVMFreeMemory" , value (Double) in KB + * key = "JVMMaxMemory" , value (Double) in KB + * key = "JVMFreePercentage" , value (Double) in Percentage + * key = "MemTotal" , value (Double) in KB + * key = "MemFree" , value (Double) in KB + * key = "MemAvailable" , value (Double) in KB + * key = "MemFreePercentage" , value (Double) in Percentage + * key = "SwapTotal" , value (Double) in KB + * key = "SwapFree" , value (Double) in KB + * + * Use function Convert_MemorySize_toString for easy reading + * @return Map result + */ + public Map Get_MemoryInfo() { + Map data = new Map(); + data.Initialize(); + + Runtime jvm = java.lang.Runtime.getRuntime(); + double jvmtotalmemory = jvm.totalMemory() / 1024.0; + double jvmfreememory = jvm.freeMemory() / 1024.0 ; + double jvmmaxmemory = jvm.maxMemory() / 1024.0 ; + + data.Put("JVMTotalMemory", pembulatan_1desimal(jvmtotalmemory)); + data.Put("JVMFreeMemory", pembulatan_1desimal(jvmfreememory)); + data.Put("JVMMaxMemory", pembulatan_1desimal(jvmmaxmemory)); + data.Put("JVMFreePercentage", pembulatan_1desimal((jvmfreememory/jvmtotalmemory)*100)); + + File meminfo = new File("/proc/meminfo"); + if (meminfo.exists()) { + if (meminfo.canRead()) { + try { + BufferedReader reader = new BufferedReader(new FileReader(meminfo.getAbsolutePath())); + String line; + do { + line = reader.readLine(); + if (line!=null) { + line = line.trim(); + if (line.startsWith("MemTotal:")) { + double memtotal = split_meminfo_string(line.substring(line.indexOf(":")+1)); + if (memtotal!=-1) data.Put("MemTotal", pembulatan_1desimal( memtotal)); + } + if (line.startsWith("MemFree:")) { + double memfree = split_meminfo_string(line.substring(line.indexOf(":")+1)); + if (memfree!=-1) data.Put("MemFree", pembulatan_1desimal(memfree)); + } + if (line.startsWith("MemAvailable:")) { + double memavailable = split_meminfo_string(line.substring(line.indexOf(":")+1)); + if (memavailable!=-1) data.Put("MemAvailable", pembulatan_1desimal(memavailable)); + } + if (line.startsWith("SwapTotal:")) { + double swaptotal = split_meminfo_string(line.substring(line.indexOf(":")+1)); + if (swaptotal!=-1) data.Put("SwapTotal", pembulatan_1desimal(swaptotal)); + } + if (line.startsWith("SwapFree:")) { + double swapfree = split_meminfo_string(line.substring(line.indexOf(":")+1)); + if (swapfree!=-1) data.Put("SwapFree", pembulatan_1desimal(swapfree)); + } + } + } while(line!=null); + + reader.close(); + + if (data.ContainsKey("MemTotal")) { + if (data.ContainsKey("MemFree")) { + double memtotal = (double)data.Get("MemTotal"); + double memfree = (double)data.Get("MemFree"); + data.Put("MemFreePercentage",pembulatan_1desimal( (memfree/memtotal)*100)); + } + } + } catch(IOException e) { + raise_log("Error reading /proc/meminfo, Msg : "+e.getMessage()); + } + + } else { + raise_log("/proc/meminfo can't be read"); + } + } else { + raise_log("/proc/meminfo not found"); + } + + return data; + } + + /** + * Same as Get_StorageInfo, but with dedicated Storage_Info class + * @param folderpath : path to check + * @return Storage_Info class, or null if failed + */ + public Storage_Info Get_StorageInformation(String folderpath) { + Map vv = Get_StorageInfo(folderpath); + if (vv instanceof Map) { + if (vv.IsInitialized()) { + if (vv.getSize()>0) { + Storage_Info result = new Storage_Info(vv); + return result; + } + } + } + return null; + } + + /** + * Get Storage Info + * Map will contain keys : + * key = "Path" , requested path + * key = "Total" , value (Double) in bytes + * key = "Free", , value (Double) in bytes + * key = "FreePercentage" , value (Double) in Percentage + * + * Use function Convert_FileSize_toString for easy reading string + * @param folderpath : path to check + * @return Map result + */ + public Map Get_StorageInfo(String folderpath) { + Map data = new Map(); + data.Initialize(); + + File dir = new File(folderpath); + if (dir.exists()) { + if (dir.isDirectory()) { + double total = dir.getTotalSpace(); + double free = dir.getUsableSpace(); + double percentage = (free / total) * 100; + data.Put("Path", folderpath); + data.Put("Total", total); + data.Put("Free", free); + data.Put("FreePercentage", pembulatan_1desimal(percentage)); + + } + } + return data; + } + + + private final double file_KB = 1024; + private final double file_MB = file_KB * 1024; + private final double file_GB = file_MB * 1024; + private final double file_TB = file_GB * 1024; + /** + * Convert a filesize to easy to read string + * @param value : size in bytes + * @return string of filesize + */ + public String Convert_FileSize_toString(double value) { + if (value < file_KB) { + return pembulatan_1desimal(value)+" B"; + } else if (value < file_MB) { + return pembulatan_1desimal(value/file_KB)+" KB"; + } else if (value < file_GB) { + return pembulatan_1desimal(value / file_MB)+" MB"; + } else if (value < file_TB) { + return pembulatan_1desimal(value / file_GB)+" GB"; + } else return pembulatan_1desimal(value / file_TB)+" TB"; + } + + /** + * Get MAC Address + * @param networkdevice : network device name eth0 / wlan0 + * @return MAC (String) if success, or empty string if failed + */ + public String Get_MAC(String networkdevice) { + try { + NetworkInterface inf = NetworkInterface.getByName(networkdevice); + if (inf==null) return ""; + + byte[] mac = inf.getHardwareAddress(); + StringBuilder str = new StringBuilder(); + for (byte bb : mac) { + int bx = Bit.And(bb, 0xFF); + String hx = ""; + + if (bx < 10) { + hx += "0"; // tambah nol di depan, biar 2 digit + } + hx += Bit.ToHexString(bx).toUpperCase(); + + if (str.length() > 0) + str.append("-"); + str.append(hx); + } + return str.toString(); + } catch (SocketException e) { + raise_log("Get_MAC device="+networkdevice+" exception, Msg : "+e.getMessage()); + return ""; + } + + + } + + /** + * Get Network Status if Up or Down + * @param networkdevice : network device name eth0 / wlan0 + * @return true if UP, or false if DOWN + */ + public boolean NetworkIsActive(String networkdevice) { + + try { + NetworkInterface inf = NetworkInterface.getByName(networkdevice); + if (inf==null) return false; + return inf.isUp(); + } catch (SocketException e) { + raise_log("NetworkIsActive device="+networkdevice+" exception, Msg : "+e.getMessage()); + return false; + } + } + + /** + * Get IP Address in String + * Revision 12072020: Sometimes an interface may contain more than 1 IP addres + * so this function will return List instead of one String + * @param networkdevice : network device name eth0 / wlan0 + * @return List contain IP Address (String) if success, or empty List if failed + */ + public List Get_IPAddress(String networkdevice) { + List result = new List(); + result.Initialize(); + try { + NetworkInterface inf = NetworkInterface.getByName(networkdevice); + + if (inf instanceof NetworkInterface) { + if (inf.isUp()) { + Enumeration inetAddress = inf.getInetAddresses(); + InetAddress currentAddress; + + while (inetAddress.hasMoreElements()) { + currentAddress = inetAddress.nextElement(); + if (currentAddress instanceof Inet4Address && !currentAddress.isLoopbackAddress()) { + String ip = currentAddress.getHostAddress(); + if (!ip.isEmpty()) + result.Add(ip); + } + } + } + } + + } catch (SocketException e) { + raise_log("Get_IPAddress device="+networkdevice+" exception, Msg : "+e.getMessage()); + } + + return result; + } + + /** + * Get IP Bytes + * @param networkdevice : network device name eth0 / wlan0 + * @return IP Bytes (4 integers) if success, or null if failed + */ + public int[] Get_IP_Bytes(String networkdevice) { + try { + NetworkInterface inf = NetworkInterface.getByName(networkdevice); + if (inf instanceof NetworkInterface) { + if (inf.isUp()) { + Enumeration inetAddress = inf.getInetAddresses(); + InetAddress currentAddress; + while (inetAddress.hasMoreElements()) { + currentAddress = inetAddress.nextElement(); + if (currentAddress instanceof Inet4Address && !currentAddress.isLoopbackAddress()) { + byte[] result = currentAddress.getAddress(); + if (result == null) + return null; + if (result.length != 4) + return null; + int[] intresult = new int[4]; + for (int ii = 0; ii < 4; ii++) { + intresult[ii] = Bit.And(result[ii], 0xFF); + } + return intresult; + } + } + } + } + return null; + } catch (SocketException e) { + raise_log("Get_IP_Bytes device="+networkdevice+" exception, Msg : "+e.getMessage()); + return null; + } + } + + private boolean cpu_monitoring = false; + + public boolean Start_CPU_Usage_Monitoring() { + if (jGPIO.IsLinux) { + Thread tx = new Thread(new cpumonitorrunnable()); + tx.start(); + return true; + } + return false; + } + + private class cpumonitorrunnable implements Runnable{ + private Map cpu_usage = new Map(); // yang terakhir kali, isinya class cpudata + private boolean success_result = false; + cpumonitorrunnable(){ + cpu_usage.Initialize(); + } + + @Override + public void run() { + raise_log("Start CPU Usage Monitoring"); + cpu_monitoring = true; + while(cpu_monitoring) { + Map cpumap = new Map(); //yang sekarang , isinya class cpudata + cpumap.Initialize(); + Map result = new Map(); // hasil akhir, isinya double values + result.Initialize(); + success_result = false; // anggap gagal dulu + + try { + if (Get_CPUDATA(cpumap)) { + for(int ii=0;ii + } + success_result = true; + } + } catch(IOException e) { + raise_log("Failed to Get_CPUDATA, Msg : "+e.getMessage()); + break; + } + + if (success_result) raise_cpuusage(result); + + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + raise_log("CPU Usage Monitoring interrupted, Msg : "+e.getMessage()); + break; + } + } + raise_log("Finished CPU Usage Monitoring"); + } + + + private boolean Get_CPUDATA(Map data) throws IOException { + boolean result = false; + if (data == null) + return false; + if (data.IsInitialized() == false) + return false; + if (!jGPIO.IsLinux) { + return result; + } + data.Clear(); + File statinfo = new File("/proc/stat"); + + if (statinfo.exists()) { + if (statinfo.canRead()) { + BufferedReader reader = new BufferedReader(new FileReader(statinfo.getAbsolutePath())); + + String line; + do { + line = reader.readLine(); + if (line!=null) { + line = line.trim(); + cpudata cpu = new cpudata(line); + if (cpu.correct_data) { + data.Put(cpu.name, cpu); + // Untuk dapat Total CPU Usage since boot, tinggal + // cpu.getCPU_Usage(null); + // untuk dapat realtime CPU usage, harus buat prev_cpudata dan current_cpudata, + // kemudian kurangkan + // current_cpudata.getCPU_Usage(prev_cpudata) + // supaya bisa jalan baik, harus pakai thread + } + } + } while (line!=null); + + + reader.close(); + if (data.getSize() > 0) + result = true; + } + } + + return result; + } + } + + public void Stop_CPU_Usage_Monitoring() { + cpu_monitoring = false; + } + + + + + + /** + * Check if a value is valid IP address + * will raise event isvalidip(toip as string, isvalid as boolean) + * @param value : target host ip or name + */ + public void IP_isValid(String value) { + if (value.isEmpty()) { + raise_isvalidip(value, false); + return; + } + Thread tx = new Thread(new CheckIPValid(value)); + tx.start(); + } + + private class CheckIPValid implements Runnable{ + final String targetip; + + public CheckIPValid(String value){ + targetip = value.trim(); + + } + + + + @Override + public void run() { + try { + InetAddress result = InetAddress.getByName(targetip); + if (result instanceof InetAddress) { + raise_isvalidip(targetip, true); // ikut default + } else { + raise_isvalidip(targetip, false); + } + + } catch(UnknownHostException | SecurityException e) { + raise_log("CheckIPValid "+targetip+" Exception, Msg : "+e.getMessage()); + raise_isvalidip(targetip, false); + } + } + + }; + + private class CheckIPReachable implements Runnable{ + final String targetip; + final String networkdev; + + // revisi 31052021 + // timeout berubah dari 2000 ke 5000 + final int pingtimeout = 5000; + public CheckIPReachable(String value, String networkdevice){ + targetip = value; + networkdev = networkdevice; + } + + + @Override + public void run() { + + try { + NetworkInterface nef = NetworkInterface.getByName(networkdev); + if (nef instanceof NetworkInterface) { + InetAddress ip = InetAddress.getByName(targetip); + if (ip instanceof InetAddress) { + boolean result = ip.isReachable(nef, 0, pingtimeout); + raise_isreachableip(targetip, result); // ikut default + return; + } + } + + + } catch (SocketException e1) { // dari NetworkInterface.getByName + raise_log("CheckIPReachable IP="+targetip+" Device="+networkdev+" Exception, Msg : "+e1.getMessage()); + + } catch (UnknownHostException | SecurityException e) { // dari InetAddress.getByName + raise_log("CheckIPReachable IP="+targetip+" Device="+networkdev+" Exception, Msg : "+e.getMessage()); + + + } catch (IOException e) { // dari InetAddress.IsReachable + raise_log("CheckIPReachable IP="+targetip+" Device="+networkdev+" Exception, Msg : "+e.getMessage()); + } + + raise_isreachableip(targetip, false); // ikut default + } + } + + + /** + * Check if IP address is reachable / Ping-able + * will raise event isreachableip(toip as string, isreachable as boolean) + * @param value : Ip address + * @param networkdevice : network device name eth0 /wlan0 + */ + public void IP_isReachable(String value, String networkdevice) { + if (value.isEmpty()) { + raise_isreachableip(value, false); + return; + } + + if (networkdevice.isEmpty()) { + raise_isreachableip(value, false); + return; + } + + Thread tx = new Thread(new CheckIPReachable(value, networkdevice)); + tx.start(); + + } + + + + /** + * Get Single Board Computer Model + * @return empty string if not available + */ + public String read_SBC_model() { + if (!jGPIO.IsLinux) return ""; + + File ff = new File("/sys/firmware/devicetree/base/model"); + String result = ""; + if (ff.exists()) { + if (ff.canRead()) { + try { + BufferedReader reader = new BufferedReader(new FileReader(ff.getAbsolutePath())); + result = reader.readLine(); + if (result!=null) { + result = result.trim(); + } else result = ""; + reader.close(); + + } catch (IOException e) { + raise_log("Failed to read from "+ff.getAbsolutePath()+", Message="+e.getMessage()+", Caused="+e.getCause()); + } + + } + } + return result; + } + + /** + * Get CPU Temperature + * On multiple cores , Map will contains several keys + * Map key = name of thermal zone, start from 0 (thermal_zone0) + * Map value = CPU temperature in double + * @return Map containing values if success, or empty map if failed + */ + public Map Get_CPU_Temp() { + Map result = new Map(); + result.Initialize(); + final String childstring = "thermal_zone"; + final String parentpath = "/sys/devices/virtual/thermal"; + final String tempstring = "temp"; + + File parent = new File(parentpath); + if (parent.exists()) { + String parentcontent[] = parent.list(); + if (parentcontent!=null) { + if (parentcontent.length>0) { + for(String xx: parentcontent) { + if (xx.contains(childstring)) { + // ketemu nya thermal_zoneX + + File parentchild = new File(parentpath, xx); + if (parentchild.isDirectory()) { + File tempfile = new File(parentchild.getAbsolutePath(), tempstring); + if (tempfile.exists()) { + if (tempfile.canRead()) { + try { + BufferedReader reader = new BufferedReader(new FileReader(tempfile.getAbsolutePath())); + String readresult = reader.readLine(); + if (readresult!=null) { + readresult = readresult.trim(); + } else readresult = ""; + reader.close(); + + double doubleresult = Double.valueOf(readresult) / 1000.0; + // pembulatan 1 desimal + int pembulatan = (int) (doubleresult * 10); + doubleresult = pembulatan / 10.0; + + result.Put(xx, doubleresult); + } catch(NumberFormatException e) { + // readresult bukan angka + result.Put(xx, -1); + raise_log("NumberFormatException "+e.getMessage()); + } + catch ( IOException e) { + raise_log("Failed to access "+tempfile.getAbsolutePath()+", Msg:"+e.getMessage()); + result.Put(xx, -1); + + } + } + } else { + + result.Put(xx, -1); + raise_log(tempfile.getAbsolutePath()+" is not exist"); + } + } + } + } + } + } + } + + return result; + } + + protected boolean network_metering = false; + private final String rx_bytes_file = "rx_bytes"; + private final String tx_bytes_file = "tx_bytes"; + private final String rx_packets_file = "rx_packets"; + private final String tx_packets_file = "tx_packets"; + /** + * Start Network Metering + * Will raise networkstatistic(ifname as string, rx_pps as long, tx_pps as long, rx_bps as long, tx_bps as long) + * @param ifname : valid interface name + * @return true if started + */ + public boolean Start_Network_Meter(String ifname) { + if (jGPIO.IsLinux) { + if (NetworkIsActive(ifname)) { + String networkpath = "/sys/class/net/"+ifname+"/statistics"; + File netdir = new File(networkpath); + if (netdir.exists()) { + if (netdir.isDirectory()) { + if (FileExistAndReadable(networkpath, rx_bytes_file)) { + if (FileExistAndReadable(networkpath, tx_bytes_file)) { + if (FileExistAndReadable(networkpath, rx_packets_file)) { + if (FileExistAndReadable(networkpath, tx_packets_file)) { + Thread tx = new Thread(new networkmeterrunnable(ifname,networkpath)); + tx.start(); + return true; + } else raise_log("Unable to access "+tx_packets_file); + } else raise_log("Unable to access "+rx_packets_file); + } else raise_log("Unable to access "+tx_bytes_file); + } else raise_log("Unable to access "+rx_bytes_file); + } else raise_log(networkpath+" is not directory"); + } else raise_log(networkpath+" is not exist"); + + } else raise_log(ifname+" is down"); + } else raise_log("System is not Linux"); + return false; + } + + public void Stop_Network_Meter() { + network_metering = false; + + } + + /** + * Update manually Linux Date and Time + * @param datenya : must in format yyyy-MM-dd + * year only valid from 2000 - 2999 + * @param timenya : must in format hh:mm:ss + * @return true if success + */ + public boolean Set_Linux_DateTime(String datenya, String timenya) { + if (!Regex.IsMatch("^(2[0-9]{3})-([0]?[1-9]|[1][0-2])-([0]?[1-9]|[1|2][0-9]|[3][0-1])$", datenya)) { + raise_log("Date Format must be yyyy-MM-dd"); + return false; + } + + if (!Regex.IsMatch("^([0]?[0-9]|[1][0-9]|[2][0-3]):([0]?[0-9]|[1-5][0-9]):([0]?[0-9]|[1-5][0-9])$", timenya)) { + raise_log("Time Format must be hh:mm:ss"); + return false; + } + try { + Process changetime = Runtime.getRuntime().exec("sudo date -s '"+datenya+" "+timenya+"'"); + if (changetime!=null) { + if (changetime.exitValue()==0) { + return true; + } + } + } catch (Exception e) { + raise_log("SetDateTime Exception, Msg : "+e.getMessage()); + + } + return false; + } + + /** + * Manually set Linux Date and Time + * @param DayOfMonth : start from 1 + * Month = 1, 3, 5, 7, 8 10, 12 --> Day Of Month up to 31 + * Month = 4, 6, 9, 11 --> Day of Month up to 30 + * Month = 2, normal 28, kabisat 29 + * @param Month : 1 - 12 + * @param Year : 2000 - 2999 + * @param Hour : 0 - 23 + * @param Minute : 0 - 59 + * @param Second : 0 - 59 + * @return true if success + */ + public boolean Set_Linux_Date_and_Time(byte DayOfMonth, byte Month, int Year, byte Hour, byte Minute, byte Second) { + if (Hour<0) return false; + if (Hour>23) return false; + if (Minute<0) return false; + if (Minute>59) return false; + if (Second<0) return false; + if (Second>59) return false; + + if (Year < 2000) return false; + if (Year > 2999) return false; + if (Month<1) return false; + if (Month>12) return false; + + if (DayOfMonth<1) return false; + switch(Month) { + case 2 : + // february + if ((Year % 4)==0) { + // kabisat + if (DayOfMonth>29) return false; + } else { + if (DayOfMonth>28) return false; + } + + break; + // bulan -bulan yang berakhir di 31 + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12 : + if (DayOfMonth>31) return false; + break; + // sisanya, berarti bulan yang berakhir di 30 + default: + if (DayOfMonth>30) return false; + break; + } + + // sampe sini dah valid + // bentuk datevalue dengan format yyyy-MM-dd + String datevalue = Create_YYYY_MM_DD(DayOfMonth, Month, Year); + // bentuk timevalue dengan format hh:mm:ss + String timevalue = Create_HH_MM_SS(Hour, Minute, Second); + return Set_Linux_DateTime(datevalue, timevalue); + + } + + private String Create_YYYY_MM_DD(byte DD, byte MM, int YYYY) { + StringBuilder str = new StringBuilder(); + if (DD<10) str.append("0"); + str.append(DD); + str.append("-"); + if (MM<10) str.append("0"); + str.append(MM); + str.append("-"); + str.append(YYYY); + return str.toString(); + } + + private String Create_HH_MM_SS(byte HH, byte MM, byte SS) { + StringBuilder str = new StringBuilder(); + if (HH<10) str.append("0"); + str.append(HH); + str.append(":"); + if (MM<10) str.append("0"); + str.append(MM); + str.append(":"); + if (SS <10) str.append("0"); + str.append(SS); + return str.toString(); + } + + protected boolean FileExistAndReadable(String path, String filename) { + File ff = new File(path, filename); + if (ff.exists()) { + if (ff.canRead()) { + return true; + } + } + return false; + } + + private class networkmeterrunnable implements Runnable{ + + private final String ifname; + private final String rx_bytes_path; + private final String tx_bytes_path; + private final String rx_packets_path; + private final String tx_packets_path; + private NetworkMeterInfo previous = null; + networkmeterrunnable(String ifname, String pathname){ + this.ifname = ifname; + this.rx_bytes_path = new File(pathname,rx_bytes_file).getAbsolutePath(); + this.tx_bytes_path = new File(pathname,tx_bytes_file).getAbsolutePath(); + this.rx_packets_path = new File(pathname,rx_packets_file).getAbsolutePath(); + this.tx_packets_path = new File(pathname,tx_packets_file).getAbsolutePath(); + } + @Override + public void run() { + raise_log("Network Meter started"); + network_metering = true; + while(network_metering) { + NetworkMeterInfo current = new NetworkMeterInfo(ifname, DateTime.getNow(), + read_value(rx_bytes_path), read_value(tx_bytes_path), + read_value(rx_packets_path), read_value(tx_packets_path)); + + if (current.IsValidValue()) { + current.calculate(previous); + previous = current; + raise_networkstatistic(current); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + break; + } + + + } + raise_log("Network Meter stopped"); + + } + + private long read_value(String path) { + long result = 0; + try { + BufferedReader reader = new BufferedReader(new FileReader(path)); + String line = reader.readLine(); + if (line!=null){ + line = line.trim(); + } else line = ""; + reader.close(); + result = Long.valueOf(line); + return result; + } catch (IOException | NumberFormatException e) { + result = -1; + } + + return result; + } + + }; + + protected void raise_log(String msg) { + if (need_log_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, eventname + "_log", false, new Object[] { msg }); + } + } + + protected void raise_isreachableip(String toip, boolean isreachable) { + if (need_isreachableip_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, eventname + "_isreachableip", false, + new Object[] { toip, isreachable }); + } + } + +// protected void raise_isreachableip(String toip, boolean isreachable, BA targetba, Object targetcaller, String targetevent) { +// if (targetba != null) { +// if (targetcaller!=null) { +// if (!targetevent.isEmpty()) { +// ba.raiseEventFromDifferentThread(targetcaller, null, 0, targetevent + "_isreachableip", false, +// new Object[] { toip, isreachable }); +// } +// } +// } +// } + + protected void raise_isvalidip(String toip, boolean isvalid) { + if (need_isvalidip_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, eventname + "_isvalidip", false, + new Object[] { toip, isvalid }); + } + } + +// protected void raise_isvalidip(String toip, boolean isvalid, BA targetba, Object targetcaller, String targetevent) { +// if (targetba != null) { +// if (targetcaller!=null) { +// if (!targetevent.isEmpty()) { +// ba.raiseEventFromDifferentThread(targetcaller, null, 0, targetevent + "_isvalidip", false, +// new Object[] { toip, isvalid }); +// } +// } +// } +// +// +// } + + protected void raise_networkstatistic(NetworkMeterInfo value) { + if (need_networkstatistic_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, eventname+"_networkstatistic", false, new Object[] {value}); + } + + } + + protected void raise_cpuusage(Map value) { + if (need_cpuusage_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, eventname+"_cpuusage", false, new Object[] {value}); + } + } + + + /** + * Untuk write hasil 'Sub Application_Error (Error As Exception, StackTrace As String) As Boolean' ke file + * hasil nya file di run directory, dengan format Crash-ddmmyyyy-hhmmss.log + * @param error : exception dari Application Error + * @param stacktrace : stacktrace dari Application Error + * @return true kalau success create file + */ + public boolean Create_Crash_Log(Exception error, String stacktrace) { + if (error instanceof Exception) { + final String errormessage = error.getMessage(); + if (errormessage instanceof String) { + if (!errormessage.isEmpty()) { + if (stacktrace instanceof String) { + if (!stacktrace.isEmpty()) { + File crashfile = new File(jGPIO.rundir, create_crashlog_name()); + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(crashfile.getAbsolutePath())); + writer.write(error.getMessage()); + writer.newLine(); + writer.write(stacktrace); + writer.close(); + return true; + } catch (IOException e) { + BA.Log("Unable to Create Crash Log, Msg : "+e.getMessage()); + } + } + } + } + } + } + return false; + + } + + + /** + * Get Current Running Directory + * @return current running directory + */ + public String getRunDirectory() { + return jGPIO.rundir; + } + + private String create_crashlog_name() { + // simpan format awal + String dateformat = DateTime.getDateFormat(); + String timeformat = DateTime.getTimeFormat(); + + // ganti format Datetime + DateTime.setDateFormat("ddMMyyyy"); + DateTime.setTimeFormat("HHmmss"); + + StringBuilder result = new StringBuilder(); + long tt = DateTime.getNow(); + result.append("Crash-"); + result.append(DateTime.Date(tt)); + result.append("-"); + result.append(DateTime.Time(tt)); + result.append(".log"); + + // balikin format Datetime ke format awal + DateTime.setDateFormat(dateformat); + DateTime.setTimeFormat(timeformat); + return result.toString(); + } + + public void WriteLog(String appname, String msg) { + File logfile = new File(getRunDirectory(), create_applog_name(appname)); + + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logfile.getAbsolutePath(),true)); + writer.write(create_log_time()); + writer.write(msg); + writer.newLine(); + writer.close(); + } catch (IOException e) { + BA.Log("Unable to Create Crash Log, Msg : "+e.getMessage()); + } + } + + private String create_log_time() { + String timeformat = DateTime.getTimeFormat(); + DateTime.setTimeFormat("HH:mm:ss"); + String result = DateTime.Time(DateTime.getNow())+"\t"; + DateTime.setTimeFormat(timeformat); + return result; + } + + private String create_applog_name(String appname) { + // simpan format awal + String dateformat = DateTime.getDateFormat(); + + // ganti format Datetime + DateTime.setDateFormat("ddMMyyyy"); + + StringBuilder result = new StringBuilder(); + long tt = DateTime.getNow(); + result.append(appname.trim()); + result.append("-"); + result.append(DateTime.Date(tt)); + result.append(".log"); + + // balikin format Datetime ke format awal + DateTime.setDateFormat(dateformat); + return result.toString(); + } + +} diff --git a/src/SBC/NanoPi/Duo2.java b/src/SBC/NanoPi/Duo2.java new file mode 100644 index 0000000..6cb3a3d --- /dev/null +++ b/src/SBC/NanoPi/Duo2.java @@ -0,0 +1,126 @@ +package SBC.NanoPi; + +import SBC.BasicSBCInfo; +import anywheresoftware.b4a.BA; + +@BA.ShortName("Nanopi_Duo2_Pin") + +/** + * Nanopi Duo2 + * Source : https://wiki.friendlyelec.com/wiki/index.php/NanoPi_Duo2#Hardware_Spec + * @author rdkartono + * + */ +public class Duo2 extends BasicSBCInfo { + + /** + * Pin 09, shared with Infrared RX port + */ + public final int pin09 = 363; + + public final int pin11 = 203; + + /** + * Pin 02, shared with Debug Port UART 0 RX + */ + public final int pin02 = 5; + + /** + * Pin 04, shared with Debug Port UART 0 TX + */ + public final int pin04 = 4; + + /** + * Pin 08, shared with I2C-0 SCL + */ + public final int pin08 = 11; + + /** + * Pin 10, shared with I2C-0 SDA + */ + public final int pin10 = 12; + + /** + * Pin 12, shared with UART 3 TX , or SPI 1 CS + */ + public final int pin12 = 13; + + /** + * Pin 14, shared with UART 3 RX, or SPI 1 CLK + */ + public final int pin14 = 14; + + /** + * Pin 16, shared with UART 3 CTS, or SPI 1 MISO + */ + public final int pin16 = 16; + + /** + * Pin 18, shared with UART 3 RTS, or SPI 1 MOSI + */ + public final int pin18 = 15; + + /** + * Pin 20, shared with UART 1 RX + */ + public final int pin20 = 199; + + /** + * Pin 22, shared with UART 1 TX + */ + public final int pin22 = 198; + + /** + * I2C 0 + */ + public final String i2c_0 = "/dev/i2c-0"; + + /** + * Ethernet name + */ + public final String eth_name = "eth0"; + + /** + * UART 0 used for Debug Port + */ + public final String serial0_name = "/dev/ttyS0"; + + /** + * UART 1 name + */ + public final String serial1_name = "/dev/ttyS1"; + + /** + * UART 3 name + */ + public final String serial3_name = "/dev/ttyS3"; + + /** + * GPIO-based Infrared Receiver name + */ + public final String GPIO_IR_Receiver_name = "/dev/input/gpio_ir_recv"; + + public final LibFriendlyArmHardware WiringNP; + + public Duo2() { + super(); + WiringNP = new LibFriendlyArmHardware(); + } + + @BA.Hide + @Override + public double Get_CPU_CoreVolt() { + // TODO Auto-generated method stub + return 0; + } + + /** + * Initialize Nanopi Duo2 + * @param callerobject Caller object + * @param event Event string + */ + public void Initialize(BA bax, Object callerobject, String event) { + setup_events(bax, callerobject, event, this); + + } +} diff --git a/src/SBC/NanoPi/LibFriendlyArmHardware.java b/src/SBC/NanoPi/LibFriendlyArmHardware.java new file mode 100644 index 0000000..a451145 --- /dev/null +++ b/src/SBC/NanoPi/LibFriendlyArmHardware.java @@ -0,0 +1,238 @@ +package SBC.NanoPi; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +public class LibFriendlyArmHardware { + static { + try { + Native.register(LibFriendlyArmHardware.class,"fahw"); + System.out.println("registered to library fahw"); + }catch(Exception e) { + System.out.println("Unable to register fahw library"); + } + + } + + // Board + public final static int BOARD_MINI6410 = 6410; + public final static int BOARD_MINI210 = 210; + public final static int BOARD_TINY4412 = 4412; + public final static int BOARD_NANOPI_M1 = 68330; // H3 + public final static int BOARD_NANOPI_2 = 44180; + public final static int BOARD_NANOPI_2_FIRE = 44184; + public final static int BOARD_NANOPI_M2 = 44185; + public final static int BOARD_NANOPC_T2 = 44181; + public final static int BOARD_NANOPI_M3 = 68187; + public final static int BOARD_NANOPC_T3 = 68181; + + native public int boardInit(); + //void clearLastError(); // gak jelas + native public int writeValueToFile(String fileName, byte[] buff); + native public int writeIntValueToFile(String fileName, int value); + native public int readValueFromFile(String fileName, byte[] buff, int len); + native public int readIntValueFromFile(String fileName); + + // Note : const char *devname --> string devName + native public int openHW(String devName, int flags); + // Note : const void *_data --> Pointer _data + native public int writeHW(int fd, final Pointer _data, int len); + native public int readHW(int fd, Pointer _data, int len); + native public int selectHW(int fd, int sec, int usec); + native public void closeHW(int fd); + native public int ioctlWithIntValue(int fd, int cmd, int value); + + + + // GPIO + // GPIO Direction + public final static int GPIO_IN = 1; + public final static int GPIO_OUT = 2; + // GPIO Value + public final static int GPIO_LOW = 0; + public final static int GPIO_HIGH = 1; + + native public int initPinGPIO(int board); + native public int pintoGPIO(int pin); + native public int exportGPIOPin(int pin); + native public int unexportGPIOPin(int pin); + native public int setGPIODirection(int pin, int direction); + native public int getGPIODirection(int pin); + native public int setGPIOValue(int pin, int value); + native public int getGPIOValue(int pin); + native public int getLedState(int ledID); + native public int setLedState(int ledID, int ledState); + + //I2C + native public int I2CReadByte(int fd, int wait_ms); + native public int I2CReadByteFrom(int fd, int pos, int wait_ms); + // Note : unsigned char byteData --> byte byteData + native public int I2CWriteByte(int fd, byte byteData, int wait_ms); + // Note : unsigned char byteData --> byte byteData + native public int I2CWriteByteTo(int fd, int pos, byte byteData, int wait_ms); + native public int setI2CRetries(int fd, int retries); + native public int setI2CSlave(int fd, int slave); + native public int setI2CTimeout(int fd, int timeout); + // gak ada ternyata + //native public int openI2CDevice(); + + //SPI + // SPI Bit Order + public static final int LSB_First = 0; + public static final int MSB_First = 1; + // SPI Mode + public static final int SPI_Mode0 = 0 ; ///< CPOL (0, CPHA (0 + public static final int SPI_Mode1 = 1 ; ///< CPOL (0, CPHA (1 + public static final int SPI_Mode2 = 2 ; ///< CPOL (1, CPHA (0 + public static final int SPI_Mode3 = 3 ; ///< CPOL (1, CPHA (1 + native public int SPItransferBytes(int spi_fd, byte[] writeData, int writeLen, byte[] readBuffer, int readLen, int spi_delay, int spi_speed, int spi_bits); + native public int SPItransferOneByte(int spi_fd, byte byteData, int spi_delay, int spi_speed, int spi_bits); + native public int readBytesFromSPI(int spi_fd, byte[] readBuffer, int readLen, int spi_delay, int spi_speed, int spi_bits); + native public int writeBytesToSPI(int spi_fd, byte[] writeData, int writeLen, int spi_delay, int spi_speed, int spi_bits); + native public int setSPIBitOrder( int spi_fd, int order); + native public int setSPIDataMode( int spi_fd, int mode); + // Note : unsigned int spi_speed --> int spi_speed + native public int setSPIMaxSpeed(int spi_fd, int spi_speed); + native public int setSPIReadBitsPerWord( int spi_fd, int bits ); + native public int setSPIWriteBitsPerWord( int spi_fd, int bits ); + + + + // OLED + native public int OLEDInit(int resetPin, int cmdDatPin); + native public int OLEDCleanScreen(int devFD); + // Note : char ch[] --> byte[] ch + native public int OLEDDisp8x16Str(int devFD, int x, int y, byte[] ch); + // Note : char ch --> byte ch + native public int OLEDDisp8x16Char(int devFD, int x, int y, byte ch); + native public void OLEDDeInit(int devFD); + + // LCD1602 + + // LCD1602 Using PCF8574 + native public int LCD1602Clear(int devFD); + native public void LCD1602DeInit(int devFD); + // Note : unsigned char x --> byte x, unsigned char y --> byte y, unsigned char data --> byte data + native public int LCD1602DispChar(int devFD, byte x, byte y, byte data); + // Note : char* line1, line2 --> String line1, line2 + native public int LCD1602DispLines(int devFD, String line1, String line2); + // Note : unsigned char x, y --> byte x, y , char* str --> String str + native public int LCD1602DispStr(int devFD, byte x, byte y, String str); + native public int LCD1602Init(int i2cDev); + + // LCD1602 using MCP23017 + native public int LCD1602GetKey(int devFD); + native public int LCD1602KeyClear(int devFD); + native public void LCD1602KeyDeInit(int devFD); + // Note : unsigned char x, y, data --> byte x, y, data + native public int LCD1602KeyDispChar(int devFD, byte x, byte y, byte data); + // Note : char* line1, line2 --> String line1, line2 + native public int LCD1602KeyDispLines(int devFD, String line1, String line2); + // Note : unsigned char x, y --> byte x, y. char *str --> String str + native public int LCD1602KeyDispStr(int devFD, byte x, byte y, String str); + native public int LCD1602KeyInit(int i2cDev); + + // HCSR04 + native public void Hcsr04DeInit(); + native public int Hcsr04Init(int echoPin); + // Note int *data --> int[] data + + /** + * Hcsr04Read + * @param data integer array with length = 1 + * @return not checked yet + */ + native public int Hcsr04Read(int[] data); + + // PWM + public static final int PWM0 = 96 + 1; // GPIO D1 + public static final int PWM1 = 64+13; // GPIO C13 + public static final int PWM2 = 64 + 14; // GPIO C14 + native public int PWMPlay(int pin, int freq, int duty); + native public int PWMStop(int pin); + native public int initPwmGPIO(int board); + native public int pwmtoGPIO(int pwm); + + //ADXL34X + // Note : char *position --> String position + native public int adxl34xRead(String position); + + // BMP180 + // NOte : int *data --> int[] data + /** + * BMP180 read + * @param type what to read + * @param data array of integer with length = 1 + * @return not checked yet + */ + native public int bmp180Read(int type, int[] data); + + // DHT11 + public static final int DHT_TEMP = 1; + public static final int DHT_HUMIDITY = 2; + // Note : int *data --> int[] data + /** + * DHT read + * @param type what to read (DHT_TEMP or DHT_HUMIDITY) + * @param data array of integer with length = 1 + * @return not checked yet + */ + native public int dht11Read(int type, int[] data); + + // DS18B20 + // Note : char * temperature --> byte[] temperature + + /** + * Read from DS18B20 + * @param temperature array of byte with length = 1 + * @return not checked yet + */ + native public int ds18b20Read(byte[] temperature); + + // HMC5883 + native public void hmc5883DeInit(int devFD); + native public int hmc5883Init(int i2cDev); + native public double hmc5883Read(int devFD); + + // MCP23017 + native public void mcpDeInit(int devFD); + native public int mcpInit(int i2cDev); + // Note : unsigned char command --> byte command + native public int mcpWriteCmd4(int devFD, byte command); + native public int mcpWriteCmd8(int devFD, byte command); + // Note : unsigned char data --> byte data + native public int mcpWriteData4(int devFD, byte data); + native public int mcpWriteData8(int devFD, byte data); + + // PCF8574 + native public void pcf8574DeInit(int devFD); + native public int pcf8574Init(int i2cDev); + // Note : unsigned char command --> byte command + native public int pcf8574WriteCmd4(int devFD, byte command); + native public int pcf8574WriteCmd8(int devFD, byte command); + // Note : unsigned char data --> byte data + native public int pcf8574WriteData4(int devFD, byte data); + native public int pcf8574WriteData8(int devFD, byte data); + + // PCF8591 + // Note : int *value --> int[] value + /** + * Read from PCF8591 + * @param channel channel number + * @param value array of integer with length = 1 + * @return not checked yet + */ + native public int pcf8591Read(int channel, int[] value); + + // Rotary Encoder + native public int rotaryEncoderDeInit(); + native public int rotaryEncoderInit(int swPin, int siaPin, int sibPin); + // Note : int *data --> int[] data + /** + * Read from Rotary encodier + * @param type what to read + * @param data aray of integer with length = 1 + * @return not checked yet + */ + native public int rotaryEncoderRead(int type, int[] data); +} diff --git a/src/SBC/NanoPi/Nanopi_IR_Receiver.java b/src/SBC/NanoPi/Nanopi_IR_Receiver.java new file mode 100644 index 0000000..2d55622 --- /dev/null +++ b/src/SBC/NanoPi/Nanopi_IR_Receiver.java @@ -0,0 +1,114 @@ +package SBC.NanoPi; + +import java.text.MessageFormat; + +import com.sun.jna.Platform; +import com.sun.jna.platform.linux.Fcntl; + +import anywheresoftware.b4a.BA; +import jGPIO.input_event; +import jGPIO.mycodes; + +@BA.ShortName("Nanopi_IR_Receiver") +@BA.Events(values= { + "log(msg as string)", + "newvalue(tipe as int, code as int, value as int)" +}) +public class Nanopi_IR_Receiver { + private BA ba; + private String event; + private Object Me; + private boolean need_log_event = false; + private boolean need_newvalue_event = false; + private int boardtype; + private LibFriendlyArmHardware wiringnp; + private static int irFD; + private final String driver_module = "matrix_ir_recv"; + + public void Initialize(BA bax, LibFriendlyArmHardware wiringNP, String eventname) { + this.ba = bax; + this.event = eventname; + this.wiringnp = wiringNP; + Me = this; + check_events(); + } + + + + public boolean Open(String IR_Name, int pin) { + if (Platform.isLinux()) { + if (Platform.isARM()) { + if (!Platform.is64Bit()) { + if (wiringnp==null) wiringnp = new LibFriendlyArmHardware(); + boardtype = wiringnp.boardInit(); + if (boardtype>0) { + modprobe(driver_module, pin); + + irFD = wiringnp.openHW(IR_Name, Fcntl.O_RDWR); + if (irFD>0) { + Thread tt = new Thread(()->{ + int retsize = 0; + input_event event = new input_event(); + while(true) { + if (wiringnp.selectHW(irFD, 0, 0)==1) { + retsize = wiringnp.readHW(irFD, event.getPointer(), event.size()); + // convert dari signed short --> unsigned value + raise_newvalue(event.type & 0xFFFF, event.code & 0xFFFF, event.value); + raise_log(MessageFormat.format("readHW retsize={0}, type={1}, code={2}, value{3}", retsize, event.type, event.code, event.value)); + } else break; + } + Close(); + + }); + tt.start(); + return true; + } else { + raise_log("Nanopi_IR_Receiver Open failed, openHW failed"); + rmmod(driver_module); + } + } else raise_log("Nanopi_IR_Receiver Open failed, boardInit failed"); + } else raise_log("Nanopi_IR_Receiver Open failed, current driver works on 32bit model"); + } else raise_log("Nanopi_IR_Receiver Open failed, must run on ARM Architecture"); + } else raise_log("Nanopi_IR_Receiver Open failed, must run on Linux"); + return false; + } + + public void Close() { + if (irFD>0) { + wiringnp.closeHW(irFD); + irFD = 0; + } + } + + //TODO belum tau caranya + private void modprobe(String drivermodule, int pin) { + //sprintf(modStr, "modprobe %s gpio=%d", DRIVER_MODULE, pintoGPIO(pin)); + //system(modStr); + } + + //TODO belum tau caranya + private void rmmod(String drivermodule) { + //system("rmmod "DRIVER_MODULE); + } + + private void check_events() { + if (ba!=null) { + if (mycodes.ValidString(event)) { + need_log_event = ba.subExists(event+"_log"); + need_newvalue_event = ba.subExists(event+"_newvalue"); + } + } + } + + private void raise_log(String msg) { + if (need_log_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + } + + private void raise_newvalue(int type, int code, int value) { + if (need_newvalue_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, event+"_newvalue", false, new Object[] {type, code, value}); + } + } +} diff --git a/src/SBC/NanoPi/Neo1_Pin.java b/src/SBC/NanoPi/Neo1_Pin.java new file mode 100644 index 0000000..48c6d59 --- /dev/null +++ b/src/SBC/NanoPi/Neo1_Pin.java @@ -0,0 +1,69 @@ +package SBC.NanoPi; + +import SBC.BasicSBCInfo; +import anywheresoftware.b4a.BA; + +@BA.ShortName("Nanopi_Neo1_Pin") + + +/** + * Source : http://wiki.friendlyarm.com/wiki/index.php/NanoPi_NEO#Hardware_Spec + * @author rdkartono + * + */ +public class Neo1_Pin extends BasicSBCInfo { + + public final LibFriendlyArmHardware WiringNP; + + public Neo1_Pin() { + super(); + WiringNP = new LibFriendlyArmHardware(); + } + public final int pin07 = 203; + + public final int pin11 = 0; + + public final int pin13 = 2; + + public final int pin15 = 3; + + public final int pin19 = 64; + + public final int pin21 = 65; + + public final int pin23 = 66; + + public final int pin08 = 198; + + public final int pin10 = 199; + + public final int pin12 = 6; + + public final int pin16 = 200; + + public final int pin18 = 201; + + public final int pin22 = 1; + + public final int pin24 = 67; + + public final String i2c_0 = "/dev/i2c-0"; + + public void Initialize(BA bax, Object callerobject, String event) { + setup_events(bax, callerobject, event, this); + + } + + /** + * Belum ketemu, selalu return 0 + */ + @Override + public double Get_CPU_CoreVolt() { + // TODO belum ketemu caranya + return 0; + } + + + + +} diff --git a/src/SBC/NanoPi/Neo2_Pin.java b/src/SBC/NanoPi/Neo2_Pin.java new file mode 100644 index 0000000..b4b526e --- /dev/null +++ b/src/SBC/NanoPi/Neo2_Pin.java @@ -0,0 +1,58 @@ +package SBC.NanoPi; + +import SBC.BasicSBCInfo; +import anywheresoftware.b4a.BA; + +@BA.ShortName("Nanopi_Neo2_Pin") + + +/** + * Source : http://wiki.friendlyarm.com/wiki/index.php/NanoPi_NEO2#Hardware_Spec + * @author rdkartono + */ +public class Neo2_Pin extends BasicSBCInfo { + public final LibFriendlyArmHardware WiringNP; + + public Neo2_Pin() { + super(); + WiringNP = new LibFriendlyArmHardware(); + } + + public final int pin03 = 12; + public final int pin05 = 11; + public final int pin07 = 203; + public final int pin11 = 0; + public final int pin13 = 2; + public final int pin15 = 3; + public final int pin19 = 64; + public final int pin21 = 65; + public final int pin23 = 66; + public final int pin08 = 198; + public final int pin10 = 199; + public final int pin12 = 6; + public final int pin16 = 200; + public final int pin18 = 201; + public final int pin22 = 1; + public final int pin24 = 67; + + public final String i2c_0= "/dev/i2c-0"; + + + public void Initialize(BA bax, Object callerobject, String event) { + setup_events(bax, callerobject, event, this); + + } + + + /** + * Belum ketemu + * selalu return 0 + */ + @Override + public double Get_CPU_CoreVolt() { + // TODO belum ketemu caranya + return 0; + } + + +} diff --git a/src/SBC/NetworkMeterInfo.java b/src/SBC/NetworkMeterInfo.java new file mode 100644 index 0000000..2694bf6 --- /dev/null +++ b/src/SBC/NetworkMeterInfo.java @@ -0,0 +1,309 @@ +package SBC; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.DateTime; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("NetworkMeterInfo") +public class NetworkMeterInfo { + public final String InterfaceName; + public final long ticktime; + public final long Total_RX_Bytes; + public final long Total_TX_Bytes; + public final long Total_RX_Packets; + public final long Total_TX_Packets; + private boolean isvalid = false; + + private long rx_pps = 0; + private long tx_pps = 0; + private long rx_bps = 0; + private long tx_bps = 0; + public NetworkMeterInfo() { + InterfaceName=""; + ticktime = DateTime.getNow(); + Total_RX_Bytes = 0; + Total_TX_Bytes = 0; + Total_RX_Packets =0; + Total_TX_Packets = 0; + isvalid = false; + } + public NetworkMeterInfo(String ifname, long tick, long rx_bytes, long tx_bytes, long rx_packets, long tx_packets){ + InterfaceName = ifname; + ticktime = tick; + Total_RX_Bytes = rx_bytes; + Total_TX_Bytes = tx_bytes; + Total_RX_Packets = rx_packets; + Total_TX_Packets = tx_packets; + + if (!InterfaceName.isEmpty()) { + if (ticktime>0) { + if (Total_TX_Bytes>0) { + if (Total_RX_Bytes>0) { + if (Total_TX_Packets>0) { + if (Total_RX_Packets>0) { + isvalid = true; + } + } + } + } + } + } + } + + /** + * Check if contain valid value + * @return true if valid + */ + public boolean IsValidValue() { + return isvalid; + } + + /** + * RX Bytes per second + * @return value in long + */ + public long RX_BytesPerSecond() { + return rx_bps; + } + + /** + * RX Packets per second + * @return value in long + */ + public long RX_PacketsPerSecond() { + return rx_pps; + } + + /** + * TX Bytes per second + * @return value in long + */ + public long TX_BytesPerSecond() { + return tx_bps; + } + + /** + * TX Packets per second + * @return value in long + */ + public long TX_PacketsPerSecond() { + return tx_pps; + } + + protected void calculate(NetworkMeterInfo prev) { + if (prev instanceof NetworkMeterInfo) { + if (prev.isvalid) { + rx_pps = Total_RX_Packets - prev.Total_RX_Packets; + tx_pps = Total_TX_Packets - prev.Total_TX_Packets; + rx_bps = Total_RX_Bytes - prev.Total_RX_Bytes; + tx_bps = Total_TX_Bytes - prev.Total_TX_Bytes; + + if (rx_pps<1) rx_pps = 0; // tidak boleh negatif + if (tx_pps<1) tx_pps = 0; + if (rx_bps<1) rx_bps = 0; + if (tx_bps<1) tx_bps = 0; + } + } + } + + private final long kp = 1000; + private final long mp = kp * 1000; + private final long gp = mp * 1000; + private final long tp = gp * 1000; + private final long kb = 1024; + private final long mb = kb * 1024; + private final long gb = mb * 1024; + private final long tb = gb * 1024; + + /** + * Current Total received packets, in readable string + * @return value in string + */ + public String Current_RX_PacketTotal() { + if (this.Total_RX_Packets < kp) { + return String.valueOf(Total_RX_Packets); + } else if (Total_RX_Packets < mp) { + return pembulatan_1_desimal(Total_RX_Packets, kp)+" K"; + } else if (Total_RX_Packets hardware based UART + * RXD = pin 10, TXD = pin 8 + * + * Di Pi3 standard, ttyAMA0 handle bluetooth. Kalau mau dijadikan hardware UART biasa + * mesti setting dtoverlay=pi3-disable-bt di /boot/config.txt + */ + public final String serial0_name = "/dev/serial0"; + + /** + * Default Raspbian Serial name untuk port 1 + * akan symbolic-link ke /dev/ttyS0 --> software based UART + * + * di Pi3 standard ttyS0 handle serial, tapi akurasi kurang pas. + * kalau mau swap ttyS0 handle bluetooth, mesti setting dtoverlay=pi3-miniuart-bt + * di /boot/config.txt + */ + public final String serial1_name = "/dev/serial1"; + + + public Pi3_Pin() { + + super(); + + } + + + + /** + * I2C SDA : pin 3 SCL : pin 5 + */ + public final String i2c_1 = "/dev/i2c-1"; + + /** + * SPI Dev 0.0 MOSI : pin 19 MISO : pin 21 SCLOCK : pin 23 CE : pin 24 + */ + public final String spi0 = "/dev/spidev0.0"; + + /** + * SPI Dev 0.1 MOSI : pin 38 MISO : pin 35 SCLOCK : pin 40 CE : pin 26 + */ + public final String spi1 = "/dev/spidev0.1"; + + + public void Initialize(BA bax, Object callerobject, String event) { + setup_events(bax, callerobject, event, this); + + } + + + + /** + * Get RPi Core Temperature + * + * @return value in xx.y value + */ + @Override + public double Get_CPU_CoreVolt() { + try { + Process process = Runtime.getRuntime().exec("vcgencmd measure_volts core"); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String result = reader.readLine(); + if (result!=null) { + result = result.trim(); + if (result.startsWith("volt=")) { + int startindex = result.indexOf(61); // tanda = + if (startindex < 0) { + return -1; + } + int stopindex = result.indexOf(86); // tanda V + if (stopindex < 0) { + return -1; + } + double value = Double.parseDouble(result.substring(startindex + 1, stopindex)); + int exitvalue = process.waitFor(); + if (exitvalue == 0) + return Math.round(value * 10.0) / 10.0; // pembulatan 1 desimal + else + return -1; + } else + return -1; + } else return -1; + + } catch (IOException | InterruptedException e) { + BA.Log("Get_RPI_Temp Exception, Msg : " + e.getMessage() + ", Caused: " + e.getCause()); + return -1; + } + } + + +} diff --git a/src/SBC/RockPi/RockPi4B.java b/src/SBC/RockPi/RockPi4B.java new file mode 100644 index 0000000..b2fd3bf --- /dev/null +++ b/src/SBC/RockPi/RockPi4B.java @@ -0,0 +1,128 @@ +package SBC.RockPi; + +import anywheresoftware.b4a.BA; +import SBC.BasicSBCInfo; + +@BA.ShortName("RockPi4B") + +/** + * Library untuk RockPi 4B + * + * Untuk disable/enable i2c, pwm/ spi, uart + * sudo nano /boot/hw_intfc.conf + * + * intfc:pwm0=off + * intfc:pwm1=off + * intfc:uart2=on + * intfc:uart4=on + * intfc:spi1=off + * intfc:spi2=off + * intfc:i2c6=on + * intfc:i2c7=on + * + * edit on/off nya, save, kemudian sudo reboot + * + * https://wiki.radxa.com/Rockpi4/hardware/devtree_overlays + * https://wiki.radxa.com/Rockpi4/dev/libmraa + * + * Catatan : + * UART4 dan SPI1 pin sama. Prioritas di UART4. Kalau mau pake SPI1, UART4 mesti off + * I2C 6 dan SPI2 pin sama. Prioritas di I2C 6. Kalau mau pake SPI2, I2C 6 mesti off + * @author rdkartono + * + */ +public class RockPi4B extends BasicSBCInfo { + //pin1 = +3.3V + //pin2 = +5V + public final int pin03 = 71; + //pin4 = +5V + public final int pin05 = 72; + //pin6 = GND + public final int pin07 = 75; + public final int pin08 = 148; + //pin9 = GND + public final int pin10 = 147; + public final int pin11 = 146; + public final int pin12 = 131; + public final int pin13 = 150; + //pin14 = GND + public final int pin15 = 149; + public final int pin16 = 154; + //pin17 = +3.3V + public final int pin18 = 156; + public final int pin19 = 40; + //pin20 = GND + public final int pin21 = 39; + public final int pin22 = 157; + public final int pin23 = 41; + public final int pin24 = 42; + //pin25 = GND + // pin26 = ADC + public final int pin27 = 64; + public final int pin28 = 65; + public final int pin29 = 74; + //pin30 = GND + public final int pin31 = 73; + public final int pin32 = 112; + public final int pin33 = 76; + //pin34 = GND + public final int pin35 = 133; + public final int pin36 = 132; + public final int pin37 = 158; + public final int pin38 = 134; + //pin39 = GND + public final int pin40 = 135; + + public final String wlan_name = "wlan0"; + public final String eth_name = "eth0"; + + /** + * UART2 = /dev/ttyS2 + * TX = pin 8 (Sama seperti Raspi) + * RX = pin10 (Sama seperti Raspi) + * jangan lupa nyalakan intfc:uart2=on di /boot/hw_intfc.conf + */ + public final String serial2_name = "/dev/ttyS2"; + + /** + * UART4 = /dev/ttyS4 + * TX = pin19 + * RX = pin21 + * jangan lupa nyalakan intfc:uart4=on di /boot/hw_intfc.conf + */ + public final String serial4_name = "/dev/ttyS4"; + + /** + * i2c-7 + * SDA = pin03 (sama seperti Raspi) + * SCL = pin05 (sama seperti Raspi) + * jangan lupa nyalakan intfc:i2c7=on di /boot/hw_intfc.conf + */ + public final String i2c_7 = "/dev/i2c-7"; + + /** + * i2c-6 + * SDA = pin31 + * SCL = pin29 + * jangan lupa nyalakan intfc:i2c6=on di /boot/hw_intfc.conf + */ + public final String i2c_6 = "/dev/i2c-6"; + + + + @Override + public double Get_CPU_CoreVolt() { + // TODO Auto-generated method stub + return 0; + } + + public RockPi4B() { + super(); + } + + public void Initialize(BA bax, Object callerobject, String event) { + setup_events(bax, callerobject, event, this); + + } + +} diff --git a/src/SBC/Storage_Info.java b/src/SBC/Storage_Info.java new file mode 100644 index 0000000..2ae0a35 --- /dev/null +++ b/src/SBC/Storage_Info.java @@ -0,0 +1,80 @@ +package SBC; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("Storage_Info") +public class Storage_Info { + public final String Path; + public final double Total; + public final double Free; + public final double FreePercentage; + public final boolean isvalid; + + public Storage_Info() { + Path=""; + Total=0; + Free=0; + FreePercentage=0; + isvalid = false; + } + + public Storage_Info(Map value) { + Path = (String) value.GetDefault("Path", ""); + Total = (double) value.GetDefault("Total", 0); + Free = (double) value.GetDefault("Free", 0); + FreePercentage = (double) value.GetDefault("FreePercentage", 0); // sudah pembulatan 1 desimal + if (!Path.isEmpty()) { + if (Total>0) { + isvalid = true; + } else isvalid = false; + } else isvalid = false; + } + + private final double file_KB = 1024; + private final double file_MB = file_KB * 1024; + private final double file_GB = file_MB * 1024; + private final double file_TB = file_GB * 1024; + + private double pembulatan_1desimal(double value) { + int temp = (int)(value * 10); + return (temp / 10.0); + } + + /** + * Return an easy to read from values inside this class + * @param value : Member of this class, either Total or Free + * @return String value + */ + public String EasyRead_Value(double value) { + if (value < file_KB) { + return pembulatan_1desimal(value)+ " B"; + } else if (value < file_MB) { + return pembulatan_1desimal(value / file_KB)+" KB"; + } else if (value < file_GB) { + return pembulatan_1desimal(value / file_MB)+" MB"; + } else if (value < file_TB) { + return pembulatan_1desimal(value / file_GB)+" GB"; + } else { + return pembulatan_1desimal(value / file_TB)+" TB"; + } + } + + /** + * Create values in JSON Map + * @return Map value + */ + public Map MakeJSONMap() { + Map result = new Map(); + result.Initialize(); + + result.Put("Path", this.Path); + result.Put("Total", EasyRead_Value(this.Total)); + result.Put("Free", EasyRead_Value(this.Free)); + result.Put("FreePercentage", this.FreePercentage); + + return result; + } + + +} diff --git a/src/SBC/cpudata.java b/src/SBC/cpudata.java new file mode 100644 index 0000000..7c1f202 --- /dev/null +++ b/src/SBC/cpudata.java @@ -0,0 +1,109 @@ +package SBC; + +/** + * Untuk olah data dari /proc/stat + * Mencari CPU usage + * https://stackoverflow.com/questions/23367857/accurate-calculation-of-cpu-usage-given-in-percentage-in-linux + * @author rdkartono + * + */ +public class cpudata { + public String name; + public long user; + public long nice; + public long system; + public long idle; + public long iowait; + public long irq; + public long softirq; + public long steal; + public long guest; + public long guest_nice; + public boolean correct_data; + + public cpudata(String vv) { + String ww[] = vv.split("\\s+"); + + try { + name=ww[0].trim(); + + if (name.startsWith("cpu")) { + user=getlong(ww[1]); + nice=getlong(ww[2]); + system=getlong(ww[3]); + idle=getlong(ww[4]); + iowait=getlong(ww[5]); + irq=getlong(ww[6]); + softirq=getlong(ww[7]); + steal=getlong(ww[8]); + guest=getlong(ww[9]); + guest_nice=getlong(ww[10]); + correct_data = true; + } else { + correct_data = false; + + } + + } catch(ArrayIndexOutOfBoundsException | NumberFormatException e) { + correct_data = false; + } + } + + private long getlong(String ss) throws NumberFormatException { + ss = ss.trim(); + return Long.parseLong(ss); + } + + /** + * IdleData = idle + iowait + * @return IdleData (Long) + */ + public long getIdleData() { + if (correct_data) { + return idle+iowait; + } else return 0; + } + + /** + * BusyData = user + nice + system + irq + softirq + steal + * @return BusyData (Long) + */ + public long getBusyData() { + if (correct_data) { + return user+nice+system+irq+softirq+steal; + } else return 0; + } + + /** + * TotalData = IdleData + BusyData + * @return TotalData (Long) + */ + public long getTotalData() { + if (correct_data) { + return getIdleData()+getBusyData(); + } else return 0; + } + + /** + * Get CPU Usage, in percentage + * @param prev : previous cpudata. if null, then will calculate total average cpu_usage since boot + * @return CPU Usage (double) + */ + public double getCPU_Usage(cpudata prev) { + long prev_total = 0; + long prev_idle = 0; + if (prev != null) { + if (prev.correct_data) { + prev_total = prev.getTotalData(); + prev_idle =prev.getIdleData(); + } + } + + long delta_total = getTotalData() - prev_total; + long delta_idle = getIdleData() - prev_idle; + + double result = delta_total - delta_idle; + result = (result / delta_total) * 100; + return Math.round(result * 10.0) / 10.0; // pembulatan 1 desimal + } +} diff --git a/src/UVC/Aver_VC520.java b/src/UVC/Aver_VC520.java new file mode 100644 index 0000000..c4cf700 --- /dev/null +++ b/src/UVC/Aver_VC520.java @@ -0,0 +1,34 @@ +package UVC; + +import anywheresoftware.b4a.BA; + +// gak jadi kepake + +@SuppressWarnings("unused") +public class Aver_VC520 { + + public final int VendorID = 0x2574; + public final int ProductID = 0x0901; + + private BA bax; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + public void Initialize(BA ba, Object callerobject, String eventname) { + bax = ba; + caller = callerobject; + event = eventname.trim(); + if (bax instanceof BA) { + if (caller != null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + } + } + } + } + + private void raise_log(String value) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {value}); + } +} diff --git a/src/UVC/UVC_Control.java b/src/UVC/UVC_Control.java new file mode 100644 index 0000000..bb77e39 --- /dev/null +++ b/src/UVC/UVC_Control.java @@ -0,0 +1,68 @@ +package UVC; + +import jGPIO.Linux_C_lib_DirectMapping; +import jGPIO.jGPIO; + +// https://github.com/allanbian1017/uvc-ctrl/blob/master/uvc-ctrl.c +// https://github.com/makenai/node-uvc-control/blob/master/lib/constants.js +// https://www.kernel.org/doc/html/v4.10/media/uapi/v4l/user-func.html +// https://github.com/spotify/linux/blob/master/include/asm-generic/ioctl.h +// https://developer.sony.com/develop/spresense/developer-tools/api-reference/api-references-spresense-sdk/group__video__ioctl.html +// https://developer.sony.com/develop/spresense/developer-tools/api-reference/api-references-spresense-sdk/video_8h_source.html + +// gak jadi kepake + +@SuppressWarnings("unused") +public class UVC_Control { + private final String devicepath; + + // request + + private final int RC_UNDEFINED=0x00; + private final int SET_CUR=0x01; + private final int GET_CUR=0x81; + private final int GET_MIN=0x82; + private final int GET_MAX=0x83; + private final int GET_RES=0x84; + private final int GET_LEN=0x85; + private final int GET_INFO=0x86; + private final int GET_DEF=0x87; + + // control + private final int CT_CONTROL_UNDEFINED = 0; + private final int CT_FOCUS_ABSOLUTE_CONTROL=0x06; + private final int CT_FOCUS_AUTO_CONTROL=0x08; + private final int CT_ZOOM_ABSOLUTE_CONTROL=0x0B; + private final int CT_ZOOM_RELATIVE_CONTROL=0x0C; + private final int CT_PANTILT_ABSOLUTE_CONTROL=0x0D; + private final int CT_PANTILT_RELATIVE_CONTROL=0x0E; + + int camerahandle = -1; + private final int O_RDWR = 0x00000002; + Linux_C_lib_DirectMapping linuxmap; + + public UVC_Control(String path) { + devicepath = path.trim(); + linuxmap = new Linux_C_lib_DirectMapping(); + + } + + + public boolean Open() { + camerahandle = linuxmap.open(devicepath, O_RDWR); + return camerahandle<0 ? false : true; + } + + public void Close() { + if (camerahandle>=0) { + jGPIO.libC.close(camerahandle); + } + camerahandle = -1; + } + + public boolean IsOpened() { + return (camerahandle >=0); + } + + +} diff --git a/src/customsocket/AckRequestWrapper.java b/src/customsocket/AckRequestWrapper.java new file mode 100644 index 0000000..01cd7a9 --- /dev/null +++ b/src/customsocket/AckRequestWrapper.java @@ -0,0 +1,98 @@ +package customsocket; + +import com.corundumstudio.socketio.AckRequest; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("AckRequest") +public class AckRequestWrapper { + private final AckRequest request; + + public AckRequestWrapper(AckRequest req) { + request = req; + } + + /** + * Check if Acknowledge is required + * @return true if required + */ + public boolean isAckRequested() { + if (request!=null) { + return request.isAckRequested(); + } + return false; + } + + /** + * Reply with array of object + * @param value array of object + */ + public void Reply_Array(Object[] value) { + if (value!=null) { + if (value.length>0) { + request.sendAckData(value); + } + } + notify_this(); + } + + /** + * Reply with a String + * @param value String value + */ + public void Reply_String(String value) { + if (value != null) { + if (value.length()>0) { + request.sendAckData(value); + } + } + notify_this(); + + } + + /** + * Reply with a Map + * @param value Map value + */ + public void Reply_Map(Map value) { + + if (value!=null) { + if (value.IsInitialized()) { + if (value.getSize()>0) { + request.sendAckData(value.getObject()); + } + } + } + notify_this(); + } + + /** + * Reply with a List + * @param value List value + */ + public void Reply_List(List value) { + + if (value!=null) { + if (value.IsInitialized()) { + if (value.getSize()>0) { + request.sendAckData(value.getObject()); + } + } + } + notify_this(); + + } + + private void notify_this() { + synchronized(this) { + try { + this.notify(); + } catch(IllegalMonitorStateException e) { + System.out.println("this.notify exception = "+e.getMessage()); + } + + } + } +} diff --git a/src/customsocket/JsonArray.java b/src/customsocket/JsonArray.java new file mode 100644 index 0000000..0655240 --- /dev/null +++ b/src/customsocket/JsonArray.java @@ -0,0 +1,597 @@ +package customsocket; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +import anywheresoftware.b4a.objects.streams.File; +import anywheresoftware.b4a.objects.streams.File.InputStreamWrapper; + +/** + * JSON Array Object + * @author rdkartono + * + */ +@BA.ShortName("JsonArray") +public class JsonArray { + + private JSONArray ja; + + @BA.Hide + /** + * Get Native JSONArray Object + * @return JSONArray object + */ + public JSONArray getObject() { + return ja; + } + + @BA.Hide + /** + * Set Native JSONArray object + * @param value : source value + */ + public void setObject(JSONArray value) { + ja = value; + } + + /** + * Initialize empty JsonArray + */ + public void InitializeEmpty() { + ja = new JSONArray(); + + } + + /** + * Initialize JsonArray from String + * @param value : string with format of Json Array + * @return true if can be initialized + */ + public boolean InitializeFromString(String value) { + if (value instanceof String) { + if (!value.isEmpty()) { + try { + ja = new JSONArray(value); + return true; + } catch(JSONException e) { + BA.Log("Unable to InitializeFromString, Msg : "+e.getMessage()); + } + + } + } + return false; + } + + /*** + * Initialize JsonArray from a file containing plain string + * Reading result from file, will be used in InitializeFromString function + * @param directory : directory path + * @param filename : filename + * @return true if value can be translated to JsonArray + */ + public boolean InitializeFromFile(String directory, String filename) { + try { + if (File.Exists(directory, filename)) { + InputStreamWrapper isw = File.OpenInput(directory, filename); + if (isw.IsInitialized()) { + int bytecount = isw.BytesAvailable(); + if (bytecount>0) { + byte[] bb = new byte[bytecount]; + isw.ReadBytes(bb, 0, bytecount); + isw.Close(); + String value = new String(bb); + return InitializeFromString(value); + } + } + } + } catch (IOException e) { + BA.Log("Unable to InitializeFromFile, Msg : "+e.getMessage()); + } + return false; + } + + /** + * Initialize JsonArray from B4X List + * @param value : B4X List + * @return true if value can be translated to JsonArray + */ + public boolean InitializeFromList(List value) { + if (value instanceof List) { + if (value.IsInitialized()) { + if (value.getSize()>0) { + try { + ja = new JSONArray(value.getObject()); + return true; + } catch(JSONException e) { + BA.Log("Unable to InitializeFromList, Msg : "+e.getMessage()); + } + + } + } + } + return false; + } + + /** + * Initialize JsonArray from array of Object + * @param value : array of object, which can be anything from String, int, long, etc. + * @return true if value can be translated to JsonArray + */ + public boolean InitializeFromObject(Object... value) { + if (value != null) { + if (value.length>0) { + try { + ja = new JSONArray(value); + return true; + } catch(JSONException e) { + BA.Log("Unable to InitializeFromObject, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Check if JsonArray is initialized + * @return true if initialized + */ + public boolean IsInitialized() { + if (ja instanceof JSONArray) { + return true; + } + return false; + } + + /** + * Clear content of JsonArray + */ + public void Clear() { + if (IsInitialized()) ja.clear(); + } + + /** + * Check if JsonArray has some value + * @return true if has value + */ + public boolean HasSomeValue() { + if (IsInitialized()) { + + return !ja.isEmpty(); + } + return false; + } + + /** + * Get Size of JsonArray + * @return 0 if empty or not initialized + */ + public int getSize() { + if (HasSomeValue()) { + return ja.length(); + } + return 0; + } + + /** + * Get JsonArray in string format + * @return String value + */ + public String ToString() { + if (HasSomeValue()) { + return ja.toString(); + } + return ""; + } + + /** + * Get JsonArray in pretty string format + * @param indentvalue how many space to indent string + * @return String value + */ + public String ToStringWithIndent(int indentvalue) { + if (HasSomeValue()) { + return ja.toString(indentvalue); + } + return ""; + } + + /** + * Write JsonArray to a File + * @param directory : directory path + * @param filename : filename + * @return true if can be written + */ + public boolean WriteToFile(String directory, String filename) { + if (HasSomeValue()) { + try { + Writer ww = new FileWriter(File.Combine(directory, filename)); + ja.write(ww); + ww.close(); + return true; + } catch (IOException | JSONException e) { + BA.Log("Unable to WriteToFile, Msg : "+e.getMessage()); + } + + } + return false; + } + + /** + * Write JsonArray to a file with indentation for pretty string + * @param directory : directory path + * @param filename : filename + * @param indentvalue : how many spaces added for indentation + * @return true if can be written + */ + public boolean WriteToFileWithIndent(String directory, String filename, int indentvalue) { + if (HasSomeValue()) { + try { + Writer ww = new FileWriter(File.Combine(directory, filename)); + ja.write(ww, indentvalue, indentvalue); + ww.close(); + return true; + }catch (IOException | JSONException e) { + BA.Log("Unable to WriteToFileWithIndent, Msg : "+e.getMessage()); + } + } + return false; + } + + /** + * Get an Object at index + * @param index : zero-based index + * @return null if index not found + */ + public Object Get(int index) { + if (HasSomeValue()) { + try { + Object result = ja.get(index); + return result; + } catch(JSONException e){ + BA.Log("Unable to Get, Msg : "+e.getMessage()); + } + + } + return null; + } + + /** + * Put an Object to JsonArray + * @param value : value to put + * @return true if can be put + */ + public boolean Put(Object value) { + if (IsInitialized()) { + try { + ja.put(value); + return true; + } catch(JSONException e) { + BA.Log("Unable to Put, Msg : "+e.getMessage()); + } + + } + return false; + } + + /** + * Get a boolean at index + * @param index : zero-based index + * @param defaultvalue : if index not found, will return this value + * @return boolean value + */ + public boolean GetBoolean(int index, boolean defaultvalue) { + if (HasSomeValue()) { + try { + boolean result = ja.getBoolean(index); + return result; + } catch(JSONException e){ + BA.Log("Unable to GetBoolean, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Put an boolean to JsonArray + * @param value : value to put + * @return true if can be put + */ + public boolean PutBoolean(boolean value) { + if (IsInitialized()) { + try { + ja.put(value); + return true; + } catch(JSONException e) { + BA.Log("Unable to PutBoolean, Msg : "+e.getMessage()); + } + + } + return false; + } + + /** + * Get a double at index + * @param index : zero-based index + * @param defaultvalue : if index not found, will return this value + * @return double value + */ + public double GetDouble(int index, double defaultvalue) { + if (HasSomeValue()) { + try { + double result = ja.getDouble(index); + return result; + } catch(JSONException e){ + BA.Log("Unable to GetDouble, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Put an double to JsonArray + * @param value : value to put + * @return true if can be put + */ + public boolean PutDouble(double value) { + if (IsInitialized()) { + try { + ja.put(value); + return true; + } catch(JSONException e) { + BA.Log("Unable to PutDouble, Msg : "+e.getMessage()); + } + + } + return false; + } + + /** + * Get a float at index + * @param index : zero-based index + * @param defaultvalue : if index not found, will return this value + * @return float value + */ + public float GetFloat(int index, float defaultvalue) { + if (HasSomeValue()) { + try { + float result = ja.getFloat(index); + return result; + } catch(JSONException e){ + BA.Log("Unable to GetFloat, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Put an float to JsonArray + * @param value : value to put + * @return true if can be put + */ + public boolean PutFloat(float value) { + if (IsInitialized()) { + try { + ja.put(value); + return true; + } catch(JSONException e) { + BA.Log("Unable to PutFloat, Msg : "+e.getMessage()); + } + + } + return false; + } + + /** + * Get a int at index + * @param index : zero-based index + * @param defaultvalue : if index not found, will return this value + * @return int value + */ + public int GetInt(int index, int defaultvalue) { + if (HasSomeValue()) { + try { + int result = ja.getInt(index); + return result; + } catch(JSONException e){ + BA.Log("Unable to GetInt, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Put an int to JsonArray + * @param value : value to put + * @return true if can be put + */ + public boolean PutInt(int value) { + if (IsInitialized()) { + try { + ja.put(value); + return true; + } catch(JSONException e) { + BA.Log("Unable to PutInt, Msg : "+e.getMessage()); + } + + } + return false; + } + + /** + * Get a long at index + * @param index : zero-based index + * @param defaultvalue : if index not found, will return this value + * @return long value + */ + public long GetLong(int index, long defaultvalue) { + if (HasSomeValue()) { + try { + long result = ja.getLong(index); + return result; + } catch(JSONException e){ + BA.Log("Unable to GetLong, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Put an long to JsonArray + * @param value : value to put + * @return true if can be put + */ + public boolean PutLong(long value) { + if (IsInitialized()) { + try { + ja.put(value); + return true; + } catch(JSONException e) { + BA.Log("Unable to PutLong, Msg : "+e.getMessage()); + } + + } + return false; + } + + /** + * Get a JsonObject at index + * @param index : zero-based index + * @param defaultvalue : if index nt found, will return this value + * @return JsonObject value + */ + public JsonObject GetJsonObject(int index, JsonObject defaultvalue) { + if (HasSomeValue()) { + try { + JSONObject jo = ja.getJSONObject(index); + JsonObject result = new JsonObject(); + result.setObject(jo); + return result; + } catch(JSONException e){ + BA.Log("Unable to GetJsonObject, Msg : "+e.getMessage()); + } + + } + return defaultvalue; + } + + /** + * Put an JsonObject to JsonArray + * @param value : value to put + * @return true if can be put + */ + public boolean PutJsonObject(JsonObject value) { + if (IsInitialized()) { + if (value instanceof JsonObject) { + if (value.HasSomeValue()) { + JSONObject xx = value.getObject(); + if (xx instanceof JSONObject) { + try { + ja.put(xx); + return true; + } catch(JSONException e) { + BA.Log("Unable to PutJsonObject, Msg : "+e.getMessage()); + } + } + } + + } + } + return false; + } + + /** + * Get a String at index + * @param index : zero-based index + * @param defaultvalue : if index not found, will return this value + * @return String value + */ + public String GetString(int index, String defaultvalue) { + if (HasSomeValue()) { + try { + String result = ja.getString(index); + return result; + } catch(JSONException e){ + BA.Log("Unable to GetString, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Put a String to JsonArray + * @param value : value to put + * @return true if can be put + */ + public boolean PutString(String value) { + if (IsInitialized()) { + if (value instanceof String) { + try { + ja.put(value); + return true; + } catch(JSONException e) { + BA.Log("Unable to PutString, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Put a B4X List to JsonArray + * @param value : value to put + * @return true if can be put + */ + public boolean PutList(List value) { + if (IsInitialized()) { + if (value instanceof List) { + if (value.IsInitialized()) { + if (value.getSize()>0) { + try { + ja.put(value.getObject()); + return true; + } catch(JSONException e) { + BA.Log("Unable to PutList, Msg : "+e.getMessage()); + } + } + } + } + } + return false; + } + + @SuppressWarnings("unchecked") + /** + * Get a B4X List at index + * @param index : zero-based index + * @param defaultvalue : if index not found, will return this value + * @return List value + */ + public List GetList(int index, List defaultvalue) { + if (HasSomeValue()) { + try { + Object xx = ja.get(index); + List result = new List(); + result.Initialize(); + if (xx instanceof java.util.List) { + java.util.List mm = (java.util.List) xx; + mm.forEach((vv)->{ + result.Add(vv); + }); + } else { + result.Add(xx); + } + + } catch(JSONException e) { + BA.Log("Unable to GetList, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } +} diff --git a/src/customsocket/JsonObject.java b/src/customsocket/JsonObject.java new file mode 100644 index 0000000..15e560f --- /dev/null +++ b/src/customsocket/JsonObject.java @@ -0,0 +1,726 @@ +package customsocket; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Iterator; + +import org.json.JSONException; +import org.json.JSONObject; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +import anywheresoftware.b4a.objects.collections.Map; +import anywheresoftware.b4a.objects.collections.Map.MyMap; +import anywheresoftware.b4a.objects.streams.File; +import anywheresoftware.b4a.objects.streams.File.InputStreamWrapper; + + + +/** + * JsonObject is almost like a Map + * it contain key and value pair + * used mainly in SocketIO javascript + * + * Source : https://github.com/stleary/JSON-java + * @author rdkartono + * + */ +@BA.ShortName("JsonObject") +public class JsonObject { + private JSONObject jo; + + + @BA.Hide + /** + * Get Native JSONObject object from org.json java + * @return JSONObject + */ + public JSONObject getObject() { + return jo; + } + + @BA.Hide + /** + * Set Native JSONObject + * @param vv : other value + */ + public void setObject(JSONObject vv) { + jo = vv; + } + + /** + * Initialize empty JsonObject + */ + public void InitializeEmpty() { + jo = new JSONObject(); + } + + /** + * Initialize JsonObject from B4X Map + * @param value : B4X Map + * @return true if value can be translated to JsonObject + */ + public boolean InitializeFromMap(Map value) { + if (value instanceof Map) { + if (value.IsInitialized()) { + MyMap mm = value.getObject(); + if (mm instanceof MyMap) { + try { + jo = new JSONObject(mm); + return (!jo.isEmpty()); // kalau empty, return false. kalau gak empty return true + } catch(JSONException | NullPointerException e) { + BA.Log("Unable to Initialize JsonObject from Map, Msg : "+e.getMessage()); + } + + } + } + } + return false; + } + + /** + * Initialize JsonObject from JSON String + * @param value : String + * @return true if value can be translated to JsonObject + */ + public boolean InitializeFromString(String value) { + if (value instanceof String) { + if (!value.isEmpty()) { + try { + jo = new JSONObject(value); + return (!jo.isEmpty()); + } catch(JSONException e) { + BA.Log("Unable to Initialize JsonObject from String, Msg : "+e.getMessage()); + } + + } + } + return false; + } + + /** + * Initialize JsonObject from a file containing plain string + * Reading result from file, will be used in InitializeFromString function + * @param directory : directory path + * @param filename : filename + * @return true if value can be translated to JsonObject + */ + public boolean InitializeFromFile(String directory, String filename) { + try { + if (File.Exists(directory, filename)) { + InputStreamWrapper isw = File.OpenInput(directory, filename); + if (isw.IsInitialized()) { + int bytecount = isw.BytesAvailable(); + if (bytecount>0) { + byte[] bb = new byte[bytecount]; + isw.ReadBytes(bb, 0, bytecount); + isw.Close(); + String value = new String(bb); + return InitializeFromString(value); + } + } + } + } catch (IOException e) { + BA.Log("InitializeFromFile failed, Msg : "+e.getMessage()); + } + return false; + } + + /*** + * Initialize JsonObject from another JsonObject . + * @param othervalue : other JsonObject as copy source + * @param keys : array of string, containing keys to copy from other JsonObject + * @return true if othervalue can be copied + */ + public boolean InitializeFromOtherJsonObject(JsonObject othervalue, String... keys) { + if (keys!=null) { + if (keys.length>0) { + if (othervalue instanceof JsonObject) { + if (othervalue.HasSomeValue()) { + jo = new JSONObject(othervalue.getObject(), keys); + return true; + } + } + } + } + return false; + } + + /** + * Check if JsonObject is initialized + * @return true if inited + */ + public boolean IsInitialized() { + if (jo instanceof JSONObject) { + return true; + } + return false; + } + + /** + * Check if JsonObject has been initialized and has some value + * @return true if has value + */ + public boolean HasSomeValue() { + if (jo instanceof JSONObject) { + if (!jo.isEmpty()) { + return true; + } + } + return false; + } + + /** + * Get List of Keys in this JsonObject + * @return B4X List + */ + public List GetListofKeys() { + List result = new List(); + result.Initialize(); + if (HasSomeValue()) { + Iterator iter = jo.keys(); + iter.forEachRemaining(value->{ + if (value instanceof String) { + if (!value.isEmpty()) { + result.Add(value); + } + } + + }); + } + return result; + } + + /** + * Check if this JsonObject contain spesific key + * @param value : String key to search + * @return true if it has + */ + public boolean ContainKey(String value) { + if (HasSomeValue()) { + return jo.has(value); + } + return false; + } + + /** + * Get JsonObject size + * @return -1 if not initialized yet, 0 if empty, positive numbers if has value + */ + public int Size() { + if (jo instanceof JSONObject) { + return jo.length(); + } + return -1; + } + + /** + * Clear JsonObject contents and keys + * @return true if can be cleared + */ + public boolean Clear() { + if (IsInitialized()) { + jo.clear(); + return true; + } + return false; + } + + /** + * Remove a content with specific key + * @param key : String key to remove + * @return Object removed from JsonObject, or null if not available or not initialized + */ + public Object Remove(String key) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + return jo.remove(key); + } + } + + } + return null; + } + + /** + * Get JsonObject key-value in string + * @return empty string if JsonObject not initialized, or is empty + */ + public String ToString() { + if (HasSomeValue()) { + return jo.toString(); + } + return ""; + } + + /** + * Get JsonObject key-value in pretty string + * @param indentvalue : how many spaces for indentation to be added + * @return empty string if JsonObject not initialized or is empty + */ + public String ToStringWithIndent(int indentvalue) { + if (HasSomeValue()) { + return jo.toString(indentvalue); + } + return ""; + } + + /** + * Write JsonObject to a file , in plain string value + * @param directory : directory path + * @param filename : filename + * @return true if write operation succesful + */ + public boolean WriteToFile(String directory, String filename) { + if (HasSomeValue()) { + try { + Writer ww = new FileWriter(File.Combine(directory, filename)); + jo.write(ww); + ww.close(); + return true; + } catch(IOException | JSONException e) { + BA.Log("WriteToFile failed, Msg : "+e.getMessage()); + } + + } + return false; + } + + /** + * Write JsonObject to a file, in plain string value + * @param directory : directory path + * @param filename : filename + * @param indentvalue : how many spaces for indentation to be added + * @return true if write operation succesful + */ + public boolean WriteToFileWithIndent(String directory, String filename, int indentvalue) { + if (HasSomeValue()) { + + try { + Writer ww = new FileWriter(File.Combine(directory, filename)); + jo.write(ww, indentvalue, indentvalue); + ww.close(); + return true; + } catch (IOException | JSONException e) { + BA.Log("WriteToFileWithIndent failed, Msg : "+e.getMessage()); + } + } + return false; + } + + /** + * Put a boolean value in JsonObject with specific key + * @param key : String key + * @param value : boolean value + * @return true if can be added + */ + public boolean PutBoolean(String key, boolean value) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + BA.Log("Unable to PutBoolean, Msg : "+e.getMessage()); + } + + } + } + + } + return false; + } + + /** + * Put a double value in JsonObject with specific key + * @param key : String key + * @param value : double value + * @return true if can be added + */ + public boolean PutDouble(String key, double value) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + BA.Log("Unable to PutDouble, Msg : "+e.getMessage()); + } + + } + } + + } + return false; + } + + /** + * Put a float value in JsonObject with specific key + * @param key : String key + * @param value : float value + * @return true if can be added + */ + public boolean PutFloat(String key, float value) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + BA.Log("Unable to PutFloat, Msg : "+e.getMessage()); + } + + } + } + + } + return false; + } + + /** + * Put an Int value in JsonObject with specific key + * @param key : String key + * @param value : Int value + * @return true if can be added + */ + public boolean PutInt(String key, int value) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + BA.Log("Unable to PutInt, Msg : "+e.getMessage()); + } + + } + } + + } + return false; + } + + /** + * Put a long value in JsonObject with specific key + * @param key : String key + * @param value : long value + * @return true if can be added + */ + public boolean PutLong(String key, long value) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + BA.Log("Unable to PutLong, Msg : "+e.getMessage()); + } + + } + } + + } + return false; + } + + /** + * Put an Object value in JsonObject with specific key + * @param key : String key + * @param value : Object value + * @return true if can be added + */ + public boolean PutObject(String key, Object value) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + BA.Log("Unable to PutObject, Msg : "+e.getMessage()); + } + + } + } + + } + return false; + } + + /** + * Put a String value in JsonObject with specific key + * @param key : String key + * @param value : String value + * @return true if can be added + */ + public boolean PutString(String key, String value) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + if (value instanceof String) { + try { + jo.put(key, value); + return true; + } catch(JSONException | NullPointerException e) { + BA.Log("Unable to PutString, Msg : "+e.getMessage()); + } + } + } + } + + } + return false; + } + + /** + * Put a B4X Map value to JsonObject with specific kety + * @param key : String key + * @param value : B4X Map value + * @return true if can be added + */ + public boolean PutMap(String key, Map value) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + if (value instanceof Map) { + + MyMap obj = value.getObject(); + if (obj instanceof MyMap) { + try { + + jo.put(key, obj); + return true; + } catch(JSONException | NullPointerException e) { + BA.Log("Unable to PutMap, Msg : "+e.getMessage()); + } + } + } + } + } + } + + return false; + } + + /** + * Put a B4X List value to JsonObject with specific key + * @param key : String key + * @param value : B4X List value + * @return true if can be added + */ + public boolean PutList(String key, List value) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + if (value instanceof List) { + if (value.IsInitialized()) { + if (value.getSize()>0) { + try { + jo.put(key, value.getObject()); + return true; + } catch(JSONException | NullPointerException e) { + BA.Log("Unable to PutList, Msg : "+e.getMessage()); + } + } + } + } + } + } + } + return false; + } + + /** + * Put other JsonObject to this JsonObject with specific key + * @param key : String key + * @param value : other JsonObject + * @return true if can be added + */ + public boolean PutJsonObject(String key, JsonObject value) { + if (IsInitialized()) { + if (key instanceof String) { + if (!key.isEmpty()) { + if (value instanceof JsonObject) { + if (value.HasSomeValue()) { + try { + jo.put(key, value.getObject()); + return true; + }catch(JSONException | NullPointerException e) { + BA.Log("Unable to PutJsonObject, Msg : "+e.getMessage()); + } + } + } + } + } + } + return false; + } + + /** + * Check if this JsonObject is similar key-value with other JsonObject + * @param othervalue : other JsonObject to compare + * @return true if similar + */ + public boolean IsSimilar(JsonObject othervalue) { + if (IsInitialized()) { + return jo.similar(othervalue); + } + return false; + } + + /** + * Get a String value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return String value + */ + public String GetStringValue(String key, String defaultvalue) { + if (ContainKey(key)) { + try { + String value = jo.getString(key); + return value; + } catch(JSONException e) { + BA.Log("Unable to GetStringValue, Msg : "+e.getMessage()); + } + + } + return defaultvalue; + } + + /** + * Get a Long value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return long value + */ + public long GetLongValue(String key, long defaultvalue) { + if (ContainKey(key)) { + try { + long value = jo.getLong(key); + return value; + } catch(JSONException e) { + BA.Log("Unable to GetLongValue, Msg : "+e.getMessage()); + } + + } + return defaultvalue; + } + + /** + * Get a JsonObject value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return JsonObject value + */ + public JsonObject GetJsonObject(String key, JsonObject defaultvalue) { + if (ContainKey(key)) { + try { + JSONObject value = jo.getJSONObject(key); + JsonObject jvalue = new JsonObject(); + jvalue.setObject(value); + return jvalue; + } catch(JSONException e) { + BA.Log("Unable to GetJsonObject, Msg : "+e.getMessage()); + } + + } + return defaultvalue; + } + + /** + * Get an int value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return int value + */ + public int GetInt(String key, int defaultvalue) { + if (ContainKey(key)) { + try { + int value = jo.getInt(key); + return value; + } catch(JSONException e) { + BA.Log("Unable to GetInt, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Get an float value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return float value + */ + public float GetFloat(String key, float defaultvalue) { + if (ContainKey(key)) { + try { + float value = jo.getFloat(key); + return value; + } catch(JSONException e) { + BA.Log("Unable to GetFloat, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Get an double value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return double value + */ + public double GetDouble(String key, double defaultvalue) { + if (ContainKey(key)) { + try { + double value = jo.getDouble(key); + return value; + } catch(JSONException e) { + BA.Log("Unable to GetDouble, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Get an boolean value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return boolean value + */ + public boolean GetBoolean(String key, boolean defaultvalue) { + if (ContainKey(key)) { + try { + boolean value = jo.getBoolean(key); + return value; + } catch(JSONException e) { + BA.Log("Unable to GetBoolean, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } + + /** + * Get an Object value matched with the key + * @param key : key linked to value + * @param defaultvalue : if specific key not found, return this value + * @return Object value + */ + public Object GetObject(String key, Object defaultvalue) { + if (ContainKey(key)) { + try { + Object value = jo.get(key); + return value; + } catch(JSONException e) { + BA.Log("Unable to GetObject, Msg : "+e.getMessage()); + } + } + return defaultvalue; + } +} diff --git a/src/customsocket/NettySocketWrapper.java b/src/customsocket/NettySocketWrapper.java new file mode 100644 index 0000000..08d7f67 --- /dev/null +++ b/src/customsocket/NettySocketWrapper.java @@ -0,0 +1,386 @@ +package customsocket; + +import java.net.InetSocketAddress; + + +import com.corundumstudio.socketio.AckCallback; +import com.corundumstudio.socketio.SocketIOClient; + + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +import anywheresoftware.b4a.objects.collections.Map; +import io.socket.engineio.client.Socket; + + +/** + * NettySocketWrapper dipakai di class jSocketIOServer + * sebagai client yang connected ke jSocketIOServer + * @author rdkartono + * + */ +@BA.ShortName("NettySocketWrapper") +@BA.Events(values= { + "log(msg as string)" + }) +public class NettySocketWrapper implements SocketIOClientInterface { + + private final SocketIOClient _socket; + + private final String _id; + private final String _namespace; + private final String _remote; + private final String _local; + private final boolean _isvalid; + + private final Object Me = this; + private BA bax; + private Object callerobject; + private String event; + private boolean _inited = false; + private boolean _connected = false; + + private boolean need_log_event = false; + + + + public NettySocketWrapper() { + // gak ngapa-ngapain, nyediain B4X doang + _socket = null; + + _id = ""; + _namespace = ""; + _remote = "N/A"; + _isvalid = false; + _local = "N/A"; + _connected = false; + + + } + + @BA.Hide + public NettySocketWrapper(SocketIOClient sock) { + _socket = sock; + if (_socket instanceof SocketIOClient) { + + _id = _socket.getSessionId().toString(); + _namespace = _socket.getNamespace().getName(); + InetSocketAddress xx = _socket.getHandshakeData().getAddress(); + _remote = xx.getHostString()+":"+xx.getPort(); + InetSocketAddress yy = _socket.getHandshakeData().getLocal(); + _local = yy.getHostString()+":"+yy.getPort(); + _isvalid = true; + _connected = true; + + } else { + _isvalid = false; + _id = ""; + _remote = "N/A"; + _local = "N/A"; + _namespace =""; + _connected = false; + + + } + + } + + /** + * Setup Events + * @param caller : caller object + * @param eventname : event name + */ + public void SetupEvent(BA ba, Object caller, String eventname) { + bax = ba; + callerobject = caller; + event = eventname; + if (bax instanceof BA) { + if (callerobject!=null) { + if (event instanceof String) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + _inited = true; + if (need_log_event) raise_log("Will raise log event"); + } + } + } + } + } + + /** + * Check if Event is Initialized + * @return true if initialized + */ + public boolean EventInited() { + return _inited; + } + + @Override + public void Disconnect() { + if (_isvalid) { + _socket.disconnect(); + } + _connected = false; + } + + @Override + public boolean IsConnected() { + return _connected; + } + + @Override + public String getID() { + return _id; + } + + @Override + public String getRemote() { + return _remote; + } + + @Override + public String getLocal() { + return _local; + } + + /** + * Get Namespace in String + * @return Namespace + */ + public String getNamespace() { + return _namespace; + } + + /** + * Description string in format ID=[ID], Remote=[Remote], Namespace=[Namespace] + * @return Description String + */ + public String CompleteDescription() { + return "ID="+getID()+", Remote="+getRemote()+", Namespace="+getNamespace(); + } + + /** + * Emit List to target socket + * @param emitname : event name + * @param value : value in B4X List Object + * @param showtype : if true, then will show each object type of List members + * @return true if can be send + */ + public boolean Emit_List(String emitname, List value, boolean showtype) { + if (value instanceof List) { + if (value.IsInitialized()) { + + int valuesize = value.getSize(); + if (valuesize>0) { + return Emit(emitname, value.getObject()); + + } + } + } + return false; + } + + /** + * Emit a command with List value to target socket, but expect return value + *
will raise event emitname_complete(valueback as Object) + *
Must check valueback type using GetType(valueback) or using If (valueback Is ...) Then + * @param emitname emint name + * @return true if can be emitted + */ + public boolean Emit_List_withCallback(final BA ba,String emitname, List value) { + + if (emitname != null && emitname.length()>0) { + final String callbackevent = emitname+"_complete"; + if (_connected) { + _socket.sendEvent(emitname, new AckCallback(Object.class,5000) { + + @Override + public void onSuccess(Object result) { + ba.raiseEventFromDifferentThread(Me, null, 0, callbackevent, false, new Object[] {result}); + } + + }, value.getObject()); + return true; + } + } + return false; + } + + /** + * Emit Map to target socket + * @param emitname : event name + * @param value : value in B4X Map object + * @return true if can be send + */ + public boolean Emit_Map(String emitname, Map value) { + if (value instanceof Map) { + if (value.IsInitialized()) { + + int valuesize = value.getSize(); + if (valuesize>0) { + return Emit(emitname, value.getObject()); + + } + } + } + return false; + } + + /** + * Emit a command with Map value to target socket, but expect return value + *
will raise event emitname_complete(valueback as Object) + *
Must check valueback type using GetType(valueback) or using If (valueback Is ...) Then + * @param emitname emint name + * @return true if can be emitted + */ + public boolean Emit_Map_withCallback(final BA ba,String emitname, Map value) { + if (emitname != null && emitname.length()>0) { + final String callbackevent = emitname+"_complete"; + if (_connected) { + + _socket.sendEvent(emitname, new AckCallback(Object.class, 5000) { + + @Override + public void onSuccess(Object result) { + ba.raiseEventFromDifferentThread(Me, null, 0, callbackevent, false, new Object[] {result}); + } + + }, value.getObject()); + return true; + } + } + return false; + } + + /** + * Emit a command without value to target socket + * @param emitname : event name + * @return true if can be send + */ + public boolean Emit_noValue(String emitname) { + return Emit(emitname); + } + + /** + * Emit a command with no value to target socket, but expect return value + *
will raise event emitname_complete(valueback as Object) + *
Must check valueback type using GetType(valueback) or using If (valueback Is ...) Then + * @param emitname emint name + * @return true if can be emitted + */ + public boolean Emit_noValue_withCallback(final BA ba,String emitname) { + if (emitname != null && emitname.length()>0) { + final String callbackevent = emitname+"_complete"; + if (_connected) { + _socket.sendEvent(emitname, new AckCallback(Object.class,5000) { + + @Override + public void onSuccess(Object result) { + ba.raiseEventFromDifferentThread(Me, null, 0, callbackevent, false, new Object[] {result}); + } + + }, new Object[0]); + return true; + } + } + return false; + } + + /** + * Emit a String to target socket + * @param emitname : event name + * @param value : String to send + * @return true if can be send + */ + public boolean Emit_String(String emitname, String value) { + if (value instanceof String) { + if (!value.isEmpty()) { + return Emit(emitname, value); + } + } + return false; + } + + /** + * Emit a command with String value to target socket, but expect return value + *
will raise event emitname_complete(valueback as Object) + *
Must check valueback type using GetType(valueback) or using If (valueback Is ...) Then + * @param emitname emint name + * @return true if can be emitted + */ + public boolean Emit_String_withCallback(final BA ba,String emitname, String value) { + if (emitname != null && emitname.length()>0) { + final String callbackevent = emitname+"_complete"; + if (_connected) { + _socket.sendEvent(emitname, new AckCallback(Object.class,5000) { + + @Override + public void onSuccess(Object result) { + ba.raiseEventFromDifferentThread(Me, null, 0, callbackevent, false, new Object[] {result}); + } + + }, value); + return true; + } + } + return false; + } + + + @Override + public boolean Emit(String emitname, Object... value) { + if (_connected) { + _socket.sendEvent(emitname, value); + return true; + } + return false; + } + + /** + * Emit a command with Object value to target socket, but expect return value + *
will raise event emitname_complete(valueback as Object) + *
Must check valueback type using GetType(valueback) or using If (valueback Is ...) Then + * @param emitname emint name + * @return true if can be emitted + */ + public boolean Emit_withCallback(final BA ba,String emitname, Object... value) { + if (emitname != null && emitname.length()>0) { + final String callbackevent = emitname+"_complete"; + if (_connected) { + _socket.sendEvent(emitname, new AckCallback(Object.class,5000) { + + @Override + public void onSuccess(Object result) { + ba.raiseEventFromDifferentThread(Me, null, 0, callbackevent, false, new Object[] {result}); + } + + }, value); + return true; + } + } + return false; + } + + @Override + public boolean SendMessage(String value) { + return Emit(Socket.EVENT_MESSAGE, value); + } + + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + @SuppressWarnings("unused") + private void printlog(String... strings) { + if (strings!=null) { + if (strings.length==1) { + BA.Log(strings[0]); + } else { + StringBuilder str = new StringBuilder(); + for(String xx : strings) str.append(xx); + BA.Log(str.toString()); + } + } + } +} diff --git a/src/customsocket/SimpleTCPSocket.java b/src/customsocket/SimpleTCPSocket.java new file mode 100644 index 0000000..a57b579 --- /dev/null +++ b/src/customsocket/SimpleTCPSocket.java @@ -0,0 +1,419 @@ +package customsocket; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.ByteBuffer; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.DateTime; + +@BA.ShortName("SimpleTCPSocket") +@BA.Events(values= { + "log(msg as string)", + "connected(targetip as string, targetport as int)", + "disconnected(targetip as string, targetport as int, duration as long)", + "newbytesdata(value() as byte)", + "txrxstatus(packetsent as int, packetreceived as int, totalsent as long, totalreceived as long)" + +}) + +public class SimpleTCPSocket { + private BA bax; + private Object Me = this; + private String event; + + private boolean need_log_event = false; + private boolean need_connected_event = false; + private boolean need_disconnected_event = false; + private boolean need_newbytesdata_event = false; + private boolean need_txrxstatus_event = false; + + + private Socket _socket = null; + private boolean isprefix = false; + private Integer packetsent = 0; + private Integer packetreceived = 0; + private Long totalsent = 0l; + private Long totalreceived = 0l; + /** + * Initialize Simple TCP Socket universal + * @param event + */ + public void Initialize(BA ba, String event) { + this.bax = ba; + this.event = event; + need_log_event = bax.subExists(this.event+"_log"); + need_connected_event = bax.subExists(this.event+"_connected"); + need_disconnected_event = bax.subExists(this.event+"_disconnected"); + need_newbytesdata_event = bax.subExists(this.event+"_newbytesdata"); + need_txrxstatus_event = bax.subExists(this.event+"_txrxstatus"); + } + + /** + * Close Socket + */ + public void Close() { + if (_socket != null) { + try { + _socket.close(); + } catch (IOException e) { + raise_log_event("Close failed, exception="+e.getMessage()); + } + } + _socket = null; + + } + + /** + * Check if Still connected + * @return true if connected + */ + public boolean IsConnected() { + if (_socket != null) { + if (!_socket.isClosed()) { + if (!_socket.isInputShutdown()) { + if (!_socket.isOutputShutdown()) { + return true; + } + } + } + } + return false; + } + + /** + * Write bytes to Socket + * @param data : array of bytes to write + * @return -1 if failed, or number of bytes written + */ + public int Write(byte[] data) { + if (IsConnected()) { + if (data instanceof byte[] && data.length>0) { + int length = data.length; + byte[] newdata; + if (isprefix) { + newdata = new byte[length+4]; + newdata[0] = (byte)(length & 0xFF); + newdata[1] = (byte)((length & 0xFF00)>>8); + newdata[2] = (byte)((length & 0xFF0000)>>16); + newdata[3] = (byte)((length& 0xFF000000)>>24); + System.arraycopy(data, 0, newdata, 4, length); + } else { + newdata = data; + } + + try { + + _socket.getOutputStream().write(newdata); + packetsent = packetsent < Integer.MAX_VALUE ? packetsent+1 : 0; + totalsent = ((Long.MAX_VALUE - totalsent) > newdata.length) ? (totalsent + newdata.length) : newdata.length; + raise_txrxstatus_event(); + return newdata.length; + } catch (IOException e) { + raise_log_event("Write failed, exception="+e.getMessage()+", closing socket"); + if (_socket!=null) { + String remoteip = _socket.getInetAddress().getHostAddress(); + int remoteport = _socket.getPort(); + raise_disconnected_event(remoteip, remoteport,0); + } + Close(); + } + } + } + return -1; + } + + private class socketprefixrunnable implements Runnable{ + private final Socket trysocket; + private final String remoteip; + private final int remoteport; + private final String localip; + private final int localport; + private final long _now; + private InputStream sock_is = null; + private boolean validrun = false; + + public socketprefixrunnable(Socket _sock) { + trysocket = _sock; + remoteip = trysocket.getInetAddress().getHostAddress(); + remoteport = trysocket.getPort(); + localip = trysocket.getLocalAddress().getHostAddress(); + localport = trysocket.getLocalPort(); + _now = DateTime.getNow(); + + } + + @Override + public void run() { + + + + validrun = false; + try { + _socket = trysocket; + sock_is = _socket.getInputStream(); + validrun = true; + } catch (IOException e1) { + raise_log_event("getInputStream failed, exception="+e1.getMessage()); + + } + if (validrun) { + raise_connected_event(remoteip, remoteport); + raise_log_event("socket prefix connected to "+remoteip+":"+remoteport+", binded with "+localip+":"+localport); + + + + int readcount = 0; + boolean need_continue = true; + while(need_continue) { + if (_socket==null) break; + if (_socket.isClosed()) break; + if (_socket.isInputShutdown()) break; + ByteBuffer buf = ByteBuffer.allocate(0); + do { + readcount = -1; + byte[] bb = new byte[1024]; + try { + readcount = sock_is.read(bb); + + if (readcount>0) { + if (readcount>buf.capacity()) { + ByteBuffer newbuf = ByteBuffer.allocate(buf.capacity()+readcount); + if (buf.capacity()>0) newbuf.put(buf); + newbuf.put(bb,0,readcount); + buf = newbuf; + raise_log_event("Updating ByteBuffer with "+readcount+" bytes, now capacity="+buf.capacity()+", position="+buf.position()+", limit="+buf.limit()); + } + + packetreceived = packetreceived < Integer.MAX_VALUE ? packetreceived+1 : 0; + totalreceived = ((Long.MAX_VALUE - totalreceived) > readcount) ? (totalreceived+readcount) : readcount; + raise_txrxstatus_event(); + } + } catch (IOException e) { + raise_log_event("InputStream read failed, exception="+e.getMessage()); + need_continue = false; + break; + } + + } while(readcount!=-1); + + if (buf.capacity()>0) { + raise_log_event("Final ByteBuffer got capacity="+buf.capacity()+", position="+buf.position()+", limit="+buf.limit()); + byte[] bufdata = buf.array(); + int bufdatalength = bufdata.length; + if (bufdatalength>4) { + int p_len = Byte.toUnsignedInt(bufdata[0]); + p_len |= Byte.toUnsignedInt(bufdata[1])<<8; + p_len |= Byte.toUnsignedInt(bufdata[2])<<16; + p_len |= Byte.toUnsignedInt(bufdata[3])<<24; + if (p_len==(bufdatalength-4)) { + byte[] newbuf = new byte[p_len]; + System.arraycopy(bufdata, 4, newbuf, 0, p_len); + raise_newbytesdata_event(newbuf); + } else raise_log_event("Received ByteBuffer prefix length="+p_len+", packetlength="+(bufdatalength-4)+", not valid prefix"); + } else raise_log_event("Received ByteBuffer length less than 4, not valid prefix"); + + } + } + } + raise_disconnected_event(remoteip, remoteport, (DateTime.getNow()-_now)); + } + + } + + private class socketrunnable implements Runnable{ + private final Socket trysocket; + private final String remoteip; + private final int remoteport; + private final String localip; + private final int localport; + private final long _now; + private InputStream sock_is = null; + private boolean validrun = false; + + + public socketrunnable(Socket _sock) { + + trysocket = _sock; + remoteip = trysocket.getInetAddress().getHostAddress(); + remoteport = trysocket.getPort(); + localip = trysocket.getLocalAddress().getHostAddress(); + localport = trysocket.getLocalPort(); + _now = DateTime.getNow(); + try { + sock_is = trysocket.getInputStream(); + + validrun = true; + } catch (IOException e) { + raise_log_event("Unable to getInputStream from socket, exception="+e.getMessage()); + } + } + + @Override + public void run() { + + + validrun = false; + try { + _socket = trysocket; + sock_is = _socket.getInputStream(); + validrun = true; + } catch (IOException e1) { + raise_log_event("getInputStream failed, exception="+e1.getMessage()); + + } + + if (validrun) { + raise_connected_event(remoteip, remoteport); + raise_log_event("socket connected to "+remoteip+":"+remoteport+", binded with "+localip+":"+localport); + + int readcount = 0; + boolean need_continue = true; + while(need_continue) { + if (_socket==null) break; + if (_socket.isClosed()) break; + if (_socket.isInputShutdown()) break; + ByteBuffer buf = ByteBuffer.allocate(0); + do { + readcount = -1; + byte[] bb = new byte[1024]; + try { + readcount = sock_is.read(bb); + if (readcount>0) { + if (readcount>buf.capacity()) { + ByteBuffer newbuf = ByteBuffer.allocate(buf.capacity()+readcount); + if (buf.capacity()>0) newbuf.put(buf); + newbuf.put(bb,0,readcount); + buf = newbuf; + raise_log_event("Updating ByteBuffer with "+readcount+" bytes, now capacity="+buf.capacity()+", position="+buf.position()+", limit="+buf.limit()); + } + + packetreceived = packetreceived < Integer.MAX_VALUE ? packetreceived+1 : 0; + totalreceived = ((Long.MAX_VALUE - totalreceived) > readcount) ? (totalreceived+readcount) : readcount; + raise_txrxstatus_event(); + } + } catch (IOException e) { + raise_log_event("InputStream read failed, exception="+e.getMessage()); + need_continue = false; + break; + } + + } while(readcount!=-1); + + if (buf.capacity()>0) { + raise_log_event("Final ByteBuffer got capacity="+buf.capacity()+", position="+buf.position()+", limit="+buf.limit()); + byte[] result = new byte[buf.capacity()]; + System.arraycopy(buf.array(), 0, result, 0, buf.capacity()); + raise_newbytesdata_event(result); + } + } + } + raise_disconnected_event(remoteip, remoteport, (DateTime.getNow()-_now)); + } + + } + + /** + * Connect to Remote socket + * will raise event connected + * @param targetip : target remote ip + * @param targetport : target remote port + * @param timeout : how many miliseconds to wait for connection, put 0 for unlimited + * @param isprefix: if true, will connect in prefix mode + */ + public void ConnectTo(String targetip, int targetport, int timeout, boolean isprefix) { + totalsent = 0l; + totalreceived =0l; + packetsent = 0; + packetreceived = 0; + Socket trysocket = null; + if (targetip instanceof String && !targetip.isEmpty()) { + if (targetport>0 && targetport<65536) { + try { + trysocket = new Socket(); + InetSocketAddress rem = new InetSocketAddress(targetip, targetport); + trysocket.connect(rem, timeout); + + if (trysocket.isConnected()) { + this.isprefix = isprefix; + if (isprefix) { + BA.submitRunnable(new socketprefixrunnable(trysocket), null, 0); + } else { + BA.submitRunnable(new socketrunnable(trysocket), null, 0); + } + return; + } + + + } catch (IOException e) { + raise_log_event("ConnectTo failed, exception="+e.getMessage()); + } + } + } + raise_connected_event("",0); + } + + /** + * Connect to Remote Socket, with local IP address bind + * will raise event connected + * @param targetip : target remote ip + * @param targetport : target remote port + * @param localip : local ip address to bind + * @param localport : local port to bind, put 0 to set random number + * @param timeout : how many milisecods to wait for connection + * @param isprefix : if true, will connect in prefix mode + */ + public void ConnectTo_withBind(String targetip, int targetport, String localip, int localport, int timeout, boolean isprefix) { + totalsent = 0l; + totalreceived =0l; + packetsent = 0; + packetreceived = 0; + Socket trysocket = null; + if (targetip instanceof String && !targetip.isEmpty()) { + if (targetport>0 && targetport<65536) { + + try { + trysocket = new Socket(); + InetSocketAddress loc = new InetSocketAddress(localip, localport); + InetSocketAddress rem = new InetSocketAddress(targetip, targetport); + trysocket.bind(loc); + trysocket.connect(rem, timeout); + if (trysocket.isConnected()) { + this.isprefix = isprefix; + if (isprefix) { + BA.submitRunnable(new socketprefixrunnable(trysocket), null, 0); + } else { + BA.submitRunnable(new socketrunnable(trysocket), null, 0); + } + return; + } + + } catch (IOException e) { + raise_log_event("ConnectTo_withBind failed, exception="+e.getMessage()); + } + } + } + raise_connected_event("",0); + } + + private void raise_log_event(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_connected_event(String targetip, int targetport) { + if (need_connected_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_connected", false, new Object[] {targetip, targetport}); + } + + private void raise_disconnected_event(String targetip, int targetport, long duration) { + if (need_disconnected_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_disconnected", false, new Object[] {targetip, targetport, duration}); + } + + private void raise_newbytesdata_event(byte[] value) { + if (need_newbytesdata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newbytesdata", false, new Object[] {value}); + } + private void raise_txrxstatus_event() { + if (need_txrxstatus_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_txrxstatus", false, new Object[] {packetsent, packetreceived, totalsent, totalreceived}); + } +} diff --git a/src/customsocket/SocketIOClientInterface.java b/src/customsocket/SocketIOClientInterface.java new file mode 100644 index 0000000..f140b77 --- /dev/null +++ b/src/customsocket/SocketIOClientInterface.java @@ -0,0 +1,48 @@ +package customsocket; + + +public interface SocketIOClientInterface { + /** + * Send String to target with standard EVENT_MESSAGE command + * @param value : string to send + * @return true if can be send + */ + public boolean SendMessage(String value); + + /** + * Disconnect socket + */ + public void Disconnect(); + + /** + * Emit an event to target socketio + * @param emitname : event name + * @param value : array of objects + * @return true if can be send + */ + public boolean Emit(String emitname, Object... value); + + /** + * Get SocketIO ID + * @return ID as string + */ + public String getID(); + + /** + * Get Remote Target information + * @return String in format [ip]:[port] + */ + public String getRemote(); + + /** + * Check if connected + * @return true if connected + */ + public boolean IsConnected(); + + /** + * Get Local IP and Port + * @return in format [IP]:[Port] or N/A if Not connected + */ + public String getLocal() ; +} diff --git a/src/customsocket/SocketIOClientWrapper.java b/src/customsocket/SocketIOClientWrapper.java new file mode 100644 index 0000000..17ff8d6 --- /dev/null +++ b/src/customsocket/SocketIOClientWrapper.java @@ -0,0 +1,428 @@ +package customsocket; + +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.regex.Pattern; + + +import io.socket.client.IO; +import io.socket.client.Socket; +import anywheresoftware.b4a.BA; + + + +@BA.ShortName("jSocketIOClient") +@BA.Events(values= { + "log(msg as string)", + "connected(socketid as string, remote as string)", + "disconnected(socketid as string, remote as string, reason as string)", + "message(socketid as string, remote as string, value as string)" +}) + +/** + * Ini adalah Socket.io client yang inisiatif konek ke Socket.io server + * @author rdkartono + * + */ +public class SocketIOClientWrapper implements SocketIOClientInterface { + + private final Object Me = this; + + private BA bax; + private Object caller; + private String event; + private boolean _inited = false; + private boolean need_log_event = false; + private boolean need_connected_event = false; + private boolean need_disconnected_event = false; + private boolean need_message_event = false; + private Socket _client; + + private String _remote; + private String _id; + + + + /** + * Initialize Events for Socket.IO Client + * This is client side Socket that to be use to connect with SocketIO Server + * @param callerobject : caller object + * @param eventname : event name + */ + public void Initialize(final BA ba, final Object callerobject, final String eventname) { + bax = ba; + caller = callerobject; + event = eventname; + if (bax instanceof BA) { + if (caller != null) { + if (event instanceof String) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_connected_event = bax.subExists(event+"_connected"); + need_disconnected_event = bax.subExists(event+"_disconnected"); + need_message_event = bax.subExists(event+"_message"); + _inited = true; + } + } + } + } + + + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + BA.Log("ShutdownHook for SocketIOClient"); + Disconnect(); + } + }); + + _id = ""; + _remote = ""; + } + + /** + * Check if Socket.IO Client is initialized + * @return true if inited + */ + public boolean IsInitialized() { + return _inited; + } + + private void printvaluedebug(String eventname, Object...value) { + if (value!=null) { + String valuetype = value.getClass().getTypeName(); + int length = value.length; + String valuestring = Arrays.toString(value); + if (length>0) { + BA.Log("Event="+eventname+", type="+valuetype+", length="+length+", value="+valuestring); + } else { + BA.Log("Event="+eventname+" has no value"); + } + + } else { + BA.Log("Event="+eventname+" value is null"); + } + + } + + /** + * Connect to SocketIO Server + * Will raise connected event or disconnected event + * @param uri : valid uri string, example ws://192.168.31.2:8080 + * @param debugmode : if true, will dislay debug log + * @param events : array of string, containing event name that want to be intercepted as soon as possible + * @return true if socket can be created or false if parameters invalid + */ + public boolean ConnectTo(String uri, boolean debugmode, String...events ) { + + if (!_id.isEmpty()) Disconnect(); //kalau ID sudah ada, berarti dah connected. Kalau gitu, disconnect dulu + + if (uri instanceof String) { + if (!uri.isEmpty()) { + try { + Socket newsock = IO.socket(uri); + newsock.on(Socket.EVENT_CONNECT,(value)->{ + // di sini, gak ada value, tandanya value.length = 0 + //if (debugmode) printvaluedebug(Socket.EVENT_CONNECT, value); + + _id = newsock.id(); + _remote = uri; + if (_client instanceof Socket) { + _client.off(); + _client.disconnect(); + _client = null; + } + _client = newsock; + raise_connected(_id, _remote); + }); + newsock.on(Socket.EVENT_CONNECT_ERROR, (value)->{ + // di sini, ada 1 value, tandanya value.length = 1 + // isinya keterangan kenapa gagal connect + //if (debugmode) printvaluedebug(Socket.EVENT_CONNECT_ERROR, value); + + String _reason = ""; + if (value!=null) { + + if (value.length>0) { + _reason = String.valueOf(value[0]); + } + + } + + raise_disconnected(_id, _remote,_reason); + _id = ""; + _remote = ""; + if (_client instanceof Socket) { + _client.off(); + _client.disconnect(); + _client = null; + } + + }); + newsock.on(Socket.EVENT_DISCONNECT, (value)->{ + // di sini ada 1 value, tandanya value.length = 1 + // isinya keterangan kenapa disconnect + // disconnect artinya sudah pernah connect, beda dengan connect_error + //if (debugmode) printvaluedebug(Socket.EVENT_DISCONNECT, value); + + String _reason = ""; + if (value!=null) { + + if (value.length>0) { + _reason = String.valueOf(value[0]); + } + + } + + raise_disconnected(_id, _remote,_reason); + _id = ""; + _remote = ""; + if (_client instanceof Socket) { + _client.off(); + _client.disconnect(); + _client = null; + } + + + }); + + newsock.on(Socket.EVENT_MESSAGE, (value)->{ + if (debugmode) printvaluedebug(Socket.EVENT_MESSAGE, value); + + if (value!=null) { + if (value.length>0) { + String msg = String.valueOf(value[0]); + raise_message(_id, _remote, msg); + } + + } + }); + + if (events!=null) { + if (events.length>0) { + for(String ev : events) { + if (!ev.isEmpty()) { + final String eventtoraise = event+"_"+ev; + final boolean need_event = bax.subExists(eventtoraise); + if (need_event) BA.Log("Will raise event "+eventtoraise); + newsock.on(ev,(value)->{ + if (debugmode) printvaluedebug(ev, value); + if (need_event) bax.raiseEventFromDifferentThread(Me, null, 0, eventtoraise, false, new Object[] {value}); + }); + } + + } + } + } + + + newsock.connect(); + return true; + } catch (URISyntaxException e) { + raise_log("ConnectTo failed, URISyntaxException for "+uri+", Msg : "+e.getMessage()); + } + + } + } + return false; + } + + + /** + * Disconnect Socket + */ + public void Disconnect() { + + if (_client instanceof Socket) { + Remove_All_Events(); // remove semua event + _client.disconnect(); // disconnect + } + _client = null; + + //TODO : konfirmasi di Log nanti, ini perlu di clear di Disconnect() atau di onDisconnect() + _remote = ""; + _id = ""; + } + + /** + * Remove all Socket Events + */ + public void Remove_All_Events() { + if (_client instanceof Socket) { + _client.off(); + } + } + + /*** + * Check if Client is still connected + * @return true if connected + */ + public boolean IsConnected() { + if (_client instanceof Socket) { + return _client.connected(); + } + return false; + } + + + /** + * Add Custom Event to Socket + * If data arrived, will raise event_eventname(value as Object) + * @param eventname : eventname, lowercase characters and numbers only + * @param debugmode : if true, will give debug output + * @return true if event added + */ + public boolean Add_Event(final String eventname, boolean debugmode) { + if (is_correct_eventname(eventname)) { + if (_client instanceof Socket) { + if (_client.hasListeners(eventname)) { + _client.off(eventname); + raise_log("Add_Event with eventname = "+eventname+" already exist, removing the previous"); + } + + final String eventtoraise = event+"_"+eventname; + final boolean need_event = bax.subExists(eventtoraise); + if (need_event && debugmode) BA.Log("Will raise "+eventtoraise); + + _client.on(eventname, (value)->{ + if (debugmode) printvaluedebug(eventname, value); + + if (need_event) bax.raiseEventFromDifferentThread(Me, null, 0, eventtoraise, false, new Object[] {value}); + + }); + + return true; + } else raise_log("Add_Event failed, Socket not connected"); + } else raise_log("Add_Event failed, eventname is invalid"); + return false; + } + + /** + * Remove specified eventname from socket + * @param eventname : eventname to remove, lowercase characters and numbers only + * @return true if removed + */ + public boolean Remove_Event(String eventname) { + if (is_correct_eventname(eventname)) { + if (_client instanceof Socket) { + if (_client.hasListeners(eventname)) { + _client.off(eventname); + return true; + } else raise_log("Remove_Event failed, Socket dont have event : "+eventname); + } else raise_log("Remove_Event failed, Socket not connected"); + } else raise_log("Remove_Event failed, eventname is invalid"); + return false; + } + + /** + * Emit JsonObject to target socket + * @param emitname : event name + * @param value : JsonObject + * @return true if value can be send + */ + public boolean Emit_JsonObject(String emitname, JsonObject value) { + if (emitname instanceof String) { + if (!emitname.isEmpty()) { + if (value instanceof JsonObject) { + if (value.HasSomeValue()) { + _client.emit(emitname, value.getObject()); + return true; + } + } + } + } + return false; + } + + /** + * Emit JsonArray to target socket + * @param emitname : event name + * @param value : JsonArray + * @return true if value can be send + */ + public boolean Emit_JsonArray(String emitname, JsonArray value) { + if (emitname instanceof String) { + if (!emitname.isEmpty()) { + if (value instanceof JsonArray) { + if (value.HasSomeValue()) { + _client.emit(emitname, value.getObject().toList()); + return true; + } + } + } + } + return false; + } + + /** + * Send simple String to event 'message' + * @param value : String to send + * @return true if can be send + */ + public boolean SendMessage(String value) { + if (IsConnected()) { + if (value instanceof String) { + if (!value.isEmpty()) { + _client.emit(io.socket.engineio.client.Socket.EVENT_MESSAGE, value); + return true; + } else raise_log("SendMessage failed, value is zero length"); + } else raise_log("SendMessage failed, value is not String"); + } else raise_log("SendMessage failed, Socket not connected"); + return false; + } + + private boolean is_correct_eventname(String value) { + if (value instanceof String) { + if (!value.isEmpty()) { + return Pattern.matches("^[a-z0-9]+$", value);// hanya lowercase dan angka + } + } + return false; + } + + /** + * Get SessionID from socket + * @return empty string if socket not connected + */ + public String getID() { + return _id; + } + + /** + * Get RemoteSocket info, in form of [IP]:[Port] + * @return empty string if socket not connected + */ + public String getRemote() { + return _remote; + } + + + /////////// Events /////////////// + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_connected(String id, String remote){ + if (need_connected_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_connected", false, new Object[] {id, remote}); + } + + private void raise_disconnected(String id, String remote, String reason) { + if (need_disconnected_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_disconnected", false, new Object[] {id,remote, reason}); + } + + private void raise_message(String id, String remote, String value) { + if (need_message_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_message", false, new Object[] {id,remote, value}); + } + + @Override + public boolean Emit(String emitname, Object... value) { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getLocal() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/customsocket/SocketIOServerWrapper.java b/src/customsocket/SocketIOServerWrapper.java new file mode 100644 index 0000000..80f3d75 --- /dev/null +++ b/src/customsocket/SocketIOServerWrapper.java @@ -0,0 +1,726 @@ +package customsocket; + + + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.UUID; + +import org.apache.commons.validator.routines.InetAddressValidator; + +import com.corundumstudio.socketio.AckRequest; +import com.corundumstudio.socketio.Configuration; + +import com.corundumstudio.socketio.SocketIOClient; +import com.corundumstudio.socketio.SocketIOServer; +import com.corundumstudio.socketio.listener.ConnectListener; +import com.corundumstudio.socketio.listener.DataListener; +import com.corundumstudio.socketio.listener.DisconnectListener; + + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +import anywheresoftware.b4a.objects.collections.Map; +import jGPIO.mycodes; + + + +@BA.ShortName("jSocketIOServer") +@BA.Events(values= { + "log(msg as string)", + "clientconnected(sock as NettySocketWrapper)", + "clientdisconnected(sock as NettySocketWrapper)", + "clientmessage(sock as NettySocketWrapper, value as string)" +}) +//@BA.DependsOn(values= { +// "netty-socketio-1.7.23", +// "slf4j-api-1.7.33", +// "netty-transport-4.1.80.Final", +// "netty-common-4.1.80.Final", +// "netty-handler-4.1.80.Final", +// "netty-codec-4.1.80.Final", +// "netty-buffer-4.1.80.Final", +// "netty-codec-http-4.1.80.Final", +// "netty-resolver-4.1.80.Final", +// "netty-transport-classes-epoll-4.1.80.Final", +// "netty-transport-native-epoll-4.1.80.Final", +// "netty-transport-native-unix-common-4.1.80.Final", +// "jackson-core-2.13.4", +// "jackson-annotations-2.14.0-rc1", +// "jackson-databind-2.14.0-rc1" +//}) + + +/** + * Socket.io Server for JAVA + * Compatible with Socket.io V2, not with V3/V4 + * Latest Socket.io from NPM is 2.4.0 + * @author rdkartono + * + */ +public class SocketIOServerWrapper { + private BA bax; + private Object caller; + private String event; + private final Object Me = this; + private boolean _inited = false; + private boolean need_log_event = false; + private boolean need_clientconnected_event = false; + private boolean need_clientdisconnected_event = false; + private boolean need_clientmessage_event = false; + + + private SocketIOServer _server; + + // source : https://github.com/socketio/engine.io-client-java/blob/master/src/main/java/io/socket/engineio/client/Socket.java + private final String EVENT_MESSAGE = "message"; + private final String EVENT_ERROR = "error"; + + + + /** + * Initialize SocketIO Server + * @param callerobject : caller object + * @param eventname : event name + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + bax = ba; + caller = callerobject; + event = eventname; + if (bax instanceof BA) { + if (caller != null) { + if (event instanceof String) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_clientconnected_event = bax.subExists(event+"_clientconnected"); + need_clientdisconnected_event = bax.subExists(event+"_clientdisconnected"); + need_clientmessage_event = bax.subExists(event+"_clientmessage"); + _inited = true; + } + } + } + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + BA.Log("ShutdownHook for jSocketIOServer is running"); + StopListen(); + } + }); + } + + /** + * Check if SocketIO Server is initialized or not + * @return true if initalized + */ + public boolean IsInitialized() { + return _inited; + } + + /** + * ConnectListener callback + */ + private ConnectListener cl = new ConnectListener() { + + @Override + public void onConnect(SocketIOClient client) { + NettySocketWrapper _netty = new NettySocketWrapper(client); + + raise_clientconnected(_netty); + } + + }; + + private DisconnectListener dl = new DisconnectListener() { + + @Override + public void onDisconnect(SocketIOClient client) { + NettySocketWrapper _netty = new NettySocketWrapper(client); + raise_clientdisconnected(_netty); + } + + }; + + private DataListener messagelistener = new DataListener() { + + @Override + public void onData(SocketIOClient client, String valuestring, AckRequest arg2) throws Exception { + NettySocketWrapper _netty = new NettySocketWrapper(client); + raise_clientmessage(_netty, valuestring); + } + + }; + + private DataListener errorlistener = new DataListener() { + + @Override + public void onData(SocketIOClient client, String reasonstring, AckRequest arg2) throws Exception { + NettySocketWrapper _netty = new NettySocketWrapper(client); + + raise_log("Error happened on "+_netty.CompleteDescription()+", Reason = "+reasonstring); + } + + }; + + /** + * Start Listening at specified port + * @param hostip : Host IP, set valid IP address if want to listen at specific network interface + * @param Port : Port number + * @return true if server can listening + */ + public boolean StartListen(String hostip, int Port) { + + if (ServerReady()) { + raise_log("Closing previous SocketIOServer instance"); + StopListen(); + } + + if (Port < 1 || Port > 65535) { + raise_log("StartListen failed, invalid Port="+Port); + return false; + } + + if (mycodes.ValidString(hostip)==false) { + raise_log("StartListen failed, hostip is not valid string"); + return false; + } + + InetAddressValidator validator = InetAddressValidator.getInstance(); + if (validator.isValid(hostip)==false) { + raise_log("StartListen failed, hostip is not valid ip format"); + return false; + } + + + InetAddress ip; + try { + ip = InetAddress.getByName(hostip); + } catch (UnknownHostException e) { + raise_log("StartListen failed, UnknownHostException="+e.getMessage()); + return false; + } + + + Configuration _config = new Configuration(); + + _config.setPort(Port); + // example : https://levelup.gitconnected.com/comparing-java-websockets-jetty-vs-netty-ba128ddca313 + + if (hostip!=null && hostip.length()>0) { + if (ip!=null) { + _config.setHostname(ip.getHostAddress()); + } + } + + SocketIOServer newserver = new SocketIOServer(_config); + + newserver.addConnectListener(cl); + + + newserver.addDisconnectListener(dl); + + newserver.addEventListener(EVENT_MESSAGE, String.class, messagelistener); + + newserver.addEventListener(EVENT_ERROR, String.class, errorlistener); + + + + _server = newserver; + _server.start(); + + raise_log("jSocketIOServer StartListen at Port = "+Port); + return true; + } + + /** + * Stop SocketIO Server Listening + * @return true if can be stopped + */ + public void StopListen() { + raise_log("jSocketIOServer StopListening"); + Disconnect_Clients(); + if (_server!=null) { + _server.stop(); + _server = null; + } + + + + } + + + /** + * Get Server Port + * @return -1 if server is invalid, or configuration is invalid + */ + public int getServerPort() { + if (ServerReady()) { + Configuration _config = _server.getConfiguration(); + if (_config instanceof Configuration) { + return _config.getPort(); + } + } + return -1; + } + + /** + * Get Server Host Name + * @return null if invalid, or host name if valid + */ + public String getHostName() { + if (ServerReady()) { + Configuration _config = _server.getConfiguration(); + if (_config instanceof Configuration) { + return _config.getHostname(); + } + } + return null; + } + + /** + * Check if Server ready or not + * @return true if server ready + */ + public boolean ServerReady() { + if (_server != null && _server instanceof SocketIOServer) { + return true; + } else return false; + } + + /** + * Add a Namespace + * @param namespace : value in string + * @return true if namespace can be added + */ + public boolean Add_Namespace(String namespace) { + if (ServerReady() && Valid_String(namespace)) { + _server.addNamespace(namespace); + return true; + } + return false; + } + + /** + * Remove a Namespace + * @param namespace : value in string + * @return true if namespace can be removed + */ + public boolean Remove_Namespace(String namespace) { + if (ServerReady() && Valid_String(namespace)) { + _server.removeNamespace(namespace); + return true; + } + return false; + } + + /** + * Get Server Namespaces + * @return List of String , containing namespace + */ + public List getServerNameSpaces() { + List result = new List(); + result.Initialize(); + if (ServerReady()) { + _server.getAllNamespaces().forEach(xx -> result.Add(xx.getName())); + }; + + return result; + } + + /** + * Get list of NettySocketWrapper + * @return List of String, containing connected client's ID + */ + public List getConnectedClientsID() { + List result = new List(); + result.Initialize(); + + if (ServerReady()) { + _server.getAllClients().forEach(xx -> result.Add(xx.getSessionId().toString())); + } + return result; + } + + /** + * Check if Server has client connected with specific ID + * @param ID ID to check + * @return true if has client with that ID + */ + public boolean HasClientWithID(String ID) { + if (ServerReady() && Valid_String(ID)) { + SocketIOClient result =_server.getAllClients().stream().filter(xx -> xx.getSessionId().toString()==ID).findFirst().orElse(null); + return (result!=null); + } + return false; + } + + /** + * Get Client with specific ID + * @param ID ID string + * @return null if not found + */ + public NettySocketWrapper GetClient(String ID) { + if (ServerReady() && Valid_String(ID)) { + SocketIOClient cl = _server.getClient(UUID.fromString(ID)); + if (cl != null) { + return new NettySocketWrapper(cl); + } + } + return null; + } + + /** + * Send a String to all clients connected + * @param emitname event name of the broadcast + * @param value data to send + * @return true if can be send + */ + public boolean Broadcast_String(String emitname, String value) { + if (ServerReady() && Valid_String(emitname) && Valid_String(value)) { + _server.getAllClients().forEach(cl -> cl.sendEvent(emitname, value)); + return true; + } + return false; + } + + /** + * Send a Map to all clients connected + * @param emitname event name of the broadcast + * @param value data to send + * @return true if can be send + */ + public boolean Broadcast_Map(String emitname, Map value) { + if (ServerReady() && Valid_String(emitname) && Valid_Map(value)) { + _server.getAllClients().forEach(cl -> cl.sendEvent(emitname, value.getObject())); + return true; + } + return false; + } + + /** + * Send a List to all clients connected + * @param emitname event name of the broadcast + * @param value data to send + * @return true if can be send + */ + public boolean Broadcast_List(String emitname, List value) { + if (ServerReady() && Valid_String(emitname) && Valid_List(value)) { + _server.getAllClients().forEach(cl -> cl.sendEvent(emitname, value.getObject())); + return true; + } + return false; + } + + /** + * Send an Array to all clients connected + * @param emitname event name of the broadcast + * @param value data to send + * @return true if can be send + */ + public boolean Broadcast_Array(String emitname, Object[] value) { + if (ServerReady() && Valid_String(emitname) && Valid_Array(value)) { + _server.getAllClients().forEach(cl -> cl.sendEvent(emitname, value)); + return true; + } + return false; + } + + /*** + * Add Custom Event + *
will raise event [event at initalize]_[eventname](netty as NettySocketWrapper, value as Object) + *
Value : kalau dari JSON object, value akan dalam bentuk Map --> test pakai Is di B4X + *
value lain , value akan dalam bentuk String --> test pakai Is di B4X + *
example : at Initialization , event = testserver. + *
Add_Event(devicehealth) + *
Later will raise event testserver_devicehealth(netty as NettySocketWrapper, value as Object, ack as AckRequestWrapper) + * @param eventname eventname + * @param printdebug if true , then will print incoming data from this event, for examination + * @return true if Custom Event can be added + */ +public boolean Add_Event(final String eventname, boolean printdebug) { + if (ServerReady()) { + final String eventtoraise = event+"_"+eventname; + final boolean need_custom_event = bax.subExists(eventtoraise); + if (printdebug) { + if (need_custom_event) { + printlog("will raise ",eventtoraise); + } else { + printlog("Sub ",eventtoraise," is not exists, event will not raised"); + + } + } + + + Remove_Event(eventname); // remove dulu yang sebelumnya, penting ini ! + + _server.addEventListener(eventname, Object.class, new DataListener() { + + @Override + public void onData(SocketIOClient client, Object value, AckRequest ackSender) throws Exception { + NettySocketWrapper _netty = new NettySocketWrapper(client); + AckRequestWrapper ack = new AckRequestWrapper(ackSender); + + if (value!=null) { + String valuetype = value.getClass().getTypeName(); + if (printdebug) { + printlog("Got event ",eventname,", valuetype =",valuetype,", value =",value.toString()); + } + if (valuetype.contains("LinkedHashMap")) { + // untuk javascript tipe JSON Object + if (need_custom_event) { + LinkedHashMap lhm = (LinkedHashMap) value; + bax.raiseEventFromDifferentThread(Me, null, 0, eventtoraise, false, new Object[] {_netty,LinkedHashMap_to_B4XMap(lhm).getObject(), ack }); + if (ack.isAckRequested()) waitfor_ack(eventname,ack, 5000, printdebug); + + } + } else if (valuetype.contains("ArrayList")) { + // untuk javascript tipe array + if (need_custom_event) { + ArrayList arr = (ArrayList) value; + bax.raiseEventFromDifferentThread(Me, null, 0, eventtoraise, false, new Object[] {_netty, ArrayList_to_B4XList(arr).getObject(), ack}); + if (ack.isAckRequested()) waitfor_ack(eventname, ack, 5000, printdebug); + } + + } else { + // java.lang.String + // java.lang.Integer + // java.lang.Boolean + // java.lang.Double + // javascript Infinity dan NaN --> null + if (need_custom_event) { + if (printdebug) printlog("Raising ",eventtoraise," with "+valuetype+" value and waiting for returnvalue"); + bax.raiseEventFromDifferentThread(Me, null, 0, eventtoraise, false, new Object[] {_netty,value, ack }); + if (ack.isAckRequested()) waitfor_ack(eventname,ack, 5000, printdebug); + } + } + } else { + // value = null + if (printdebug) printlog("Got event ", eventname, ", value = null"); + if (need_custom_event) { + if (printdebug) printlog("Raising ",eventtoraise," with null value and waiting for returnvalue"); + bax.raiseEventFromDifferentThread(Me, null, 0, eventtoraise, false, new Object[] {_netty, null, ack}); + if (ack.isAckRequested()) waitfor_ack(eventname,ack,5000, printdebug); + } + } + + + + + + } + + }); + + return true; + } + return false; + } + + private void printlog(String... str) { + if (str!=null) { + if (str.length==1) { + BA.Log(str[0]); + } else if (str.length>1) { + StringBuilder strbuild = new StringBuilder(); + + for(String xx : str) strbuild.append(xx); + BA.Log(strbuild.toString()); + } + } + } + + private void waitfor_ack(String functionname, AckRequestWrapper ack, long timeout, boolean printdebug) { + if (ack != null) { + synchronized(ack) { + try { + ack.wait(timeout); + if (printdebug) printlog("Function ", functionname," ack.wait finished"); + } catch(IllegalMonitorStateException | InterruptedException | IllegalArgumentException e) { + printlog("Function ", functionname," ack.wait exception = ", e.getMessage()); + } + } + } + + } + + private Map LinkedHashMap_to_B4XMap(LinkedHashMap value) { + Map result = new Map(); + result.Initialize(); + +// MyMap mmresult = new MyMap(); +// if (value!=null) { +// if (value.size()>0) { +// mmresult.putAll(value); +// } +// } +// result.setObject(mmresult); + if (value!=null) { + if (value.size()>0) { + value.forEach((kk,vv)->{ + if (vv!=null) { + String vvtype = vv.getClass().getTypeName(); + if (vvtype.contains("LinkedHashMap")) { + // map di dalam map + result.Put(kk, LinkedHashMap_to_B4XMap((LinkedHashMap) vv).getObject()); + } else if (vvtype.contains("ArrayList")) { + // array di dalam map + result.Put(kk, ArrayList_to_B4XList((ArrayList) vv).getObject()); + } else { + // tipe biasa, masukin langsung + result.Put(kk, vv); + } + } + }); + } + } + return result; + } + + private List ArrayList_to_B4XList(ArrayList value) { + List result = new List(); + result.Initialize(); + if (value != null) { + if (value.size()>0) { + value.forEach(xx -> { + if (xx != null) { + String xxtype = xx.getClass().getTypeName(); + if (xxtype.contains("LinkedHashMap")) { + // array of maps + result.Add(LinkedHashMap_to_B4XMap((LinkedHashMap) xx).getObject()); + } else if (xxtype.contains("ArrayList")) { + // array of array, kayaknya gak pernah ada + result.Add(ArrayList_to_B4XList((ArrayList) xx).getObject()); + } else { + // tipe biasa, masukin langsung + result.Add(xx); + } + } + }); + } + + } + return result; + } + + /** + * Remove Custom Event + * @param eventname : event name yang mau diremove + * @return true kalau bisa di remove + */ + public boolean Remove_Event(String eventname) { + if (ServerReady() && Valid_String(eventname)) { + _server.removeAllListeners(eventname); + return true; + } + return false; + } + + /** + * Check if a string is valid and have content + * @param xx String to check + * @return true if valid + */ + private boolean Valid_String(String xx) { + if (xx != null) { + if (xx instanceof String) { + return xx.length()>0 ; + } + } + return false; + } + + /** + * Check if a Map is valid and has content + * @param xx Map to check + * @return true if valid and have content + */ + private boolean Valid_Map(Map xx) { + if (xx != null) { + if (xx instanceof Map) { + if (xx.IsInitialized()) { + if (xx.getSize()>0) { + return true; + } + } + } + } + return false; + } + + /** + * Check if a LIst is valid and has content + * @param xx List to check + * @return true if valid and have content + */ + private boolean Valid_List(List xx) { + if (xx != null) { + if (xx instanceof List) { + if (xx.IsInitialized()) { + if (xx.getSize()>0) { + return true; + } + } + } + } + return false; + } + + /** + * Check if an array is not null and have length + * @param xx Array to check + * @return true if not null and have length + */ + private boolean Valid_Array(Object[] xx) { + if (xx != null) { + if (xx.length>0) { + return true; + } + } + return false; + } + + ////////// Private methods ////////////// + + /** + * Disconnect all SocketIOClient in this SocketIOServer + */ + private void Disconnect_Clients() { + if (_server!=null) { + _server.getAllClients().forEach(xx -> xx.disconnect()); + } + + } + + + + + + //////////// Event //////////////////// + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_clientdisconnected(NettySocketWrapper netty) { + if (need_clientdisconnected_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_clientdisconnected", false, new Object[] {netty}); + } + } + + private void raise_clientconnected(NettySocketWrapper netty) { + if (need_clientconnected_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_clientconnected", false, new Object[] {netty}); + } + } + + + + private void raise_clientmessage(NettySocketWrapper netty, String value) { + if (need_clientmessage_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_clientmessage", false, new Object[] {netty, value}); + } + } + + +} diff --git a/src/customsocket/TCPSocket.java b/src/customsocket/TCPSocket.java new file mode 100644 index 0000000..e393dd9 --- /dev/null +++ b/src/customsocket/TCPSocket.java @@ -0,0 +1,817 @@ +package customsocket; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Timer; +import java.util.TimerTask; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.keywords.DateTime; +import anywheresoftware.b4a.objects.collections.Map; +import anywheresoftware.b4a.objects.collections.Map.MyMap; +import anywheresoftware.b4a.randomaccessfile.B4XSerializator; +import jGPIO.mycodes; + +@BA.ShortName("TCPSocket") +@BA.Events(values= { + "log(msg as string)", + "connected(targetip as string, targetport as int)", + "disconnected(targetip as string, targetport as int, duration as long)", + "newmapdata(value as Map)", + "newbytesdata(value() as byte)", + "newstringdata(value as String)", + "newintsdata(value() as int)", + +}) + +//@BA.DependsOn(values= {"jRandomAccessFile.jar"}) + +public class TCPSocket { + private BA bax; + private Object caller; + private Object Me = this; + private String event; + private TcpSocketJavaEvent _javaevent = null; + + private boolean _inited = false; + private boolean need_log_event = false; + private boolean need_connected_event = false; + private boolean need_disconnected_event = false; + private boolean need_newmapdata_event = false; + private boolean need_newbytesdata_event = false; + private boolean need_newstringdata_event = false; + private boolean need_newintsdata_event = false; + + + private Socket _socket; + private ObjectOutputStream _output; + private ObjectInputStream _input; + private B4XSerializator _converter; + + private String _remoteip = ""; + private int _remoteport = 0; + private String _localip = ""; + private int _localport = 0; + + private long _connected_tick = 0; + + private int _txokcount = 0; + private long _txbytescount = 0; + private int _rxokcount = 0; + private long _rxbytescount = 0; + + private Timer onesecondtimer; + private int _rxtimercount; + private int _txtimercount; + + private String _mapkey = ""; + + public TCPSocket() { + // nothing to do here + _converter = new B4XSerializator(); + + } + + @BA.Hide + public TCPSocket(Socket newsocket, Object event) { + this(); + _javaevent = (TcpSocketJavaEvent) event; + Thread tx = new Thread(new tcpsocketrun(newsocket)); + tx.start(); + } + + /** + * Get / Set Identifier Key + * by default, identifier key is RemoteIPAddress:RemotePort + * if identifier key is empty, then TCPSocket has never been connected yet + * User may change IdentifierKey to any String value , and used it for matching Map + * @return key as string + */ + public String getIdentifierKey() { + return _mapkey; + } + + /** + * Get / Set Identifier Key + * by default, identifier key is RemoteIPAddress:RemotePort + * if identifier key is empty, then TCPSocket has never been connected yet + * User may change IdentifierKey to any String value , and used it for matching Map + * @param value as string + */ + public void setIdentifierKey(String value) { + if (value instanceof String) { + if (!value.isEmpty()) { + if (_javaevent instanceof TcpSocketJavaEvent) { + _javaevent.renameidentifier(_mapkey, value); + } + _mapkey = value; + } + } + } + + /** + * Initialize TCPSocket events + * Necessary to call this, so event log, connected, disconnected will be called + * @param callerobject : caller object + * @param eventname : eventname + */ + public void InitializeEvents(BA ba, final Object callerobject, final String eventname) { + + bax = ba; + caller = callerobject; + event = eventname; + if (bax instanceof BA) { + if (caller != null) { + if (event instanceof String) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_connected_event = bax.subExists(event+"_connected"); + need_disconnected_event = bax.subExists(event+"_disconnected"); + need_newmapdata_event = bax.subExists(event+"_newmapdata"); + need_newbytesdata_event = bax.subExists(event+"_newbytesdata"); + need_newstringdata_event = bax.subExists(event+"_newstringdata"); + need_newintsdata_event = bax.subExists(event+"_newintsdata"); + + _inited = true; + } + } + } + } + + } + + /** + * Check if Events already Initialized for this TCPSocket + * @return true if already initialized + */ + public boolean EventsInitialized() { + return _inited; + } + + /** + * Check if TCPSocket is connected + * @return true if connected + */ + public boolean IsConnected() { + if (_socket instanceof Socket) { + if (!_socket.isClosed()) { + return _socket.isConnected(); + } + } + return false; + } + + + + /** + * Connect to Target Ip and Target Port + * Will raise Connected(remoteip as string, remoteport as int) event + * if TCPSocket connected, remoteip has ip address, and remoteport != 0; + * @param targetip : target ip + * @param port : target port + */ + public void ConnectTo(String targetip, int port) { + if (targetip instanceof String) { + if (!targetip.isEmpty()) { + if (port>0) { + try { + Socket trysock = new Socket(); + SocketAddress rem = new InetSocketAddress(targetip, port); + trysock.connect(rem); + Thread tx = new Thread(new tcpsocketrun(trysock)); + tx.start(); + return; + } catch (UnknownHostException e) { + raise_log_error("UnknownHostException on ConnectTo ["+targetip+":"+port+"]",e); + + } catch (IOException e) { + raise_log_error("IOException on ConnectTo ["+targetip+"]:["+port+"]",e); + } + } + } + } + raise_connected("",0); + } + + /** + * Connect to Target IP and Target Port , and specifically bind to Local IP address + * Will raise Connected(remoteip as string, remoteport as int) event + * if TCPSocket connected, remoteip has ip address, and remoteport != 0; + * @param targetip remote IP address + * @param port remote port + * @param localip local IP address + */ + public void ConnectTo_withBind(String targetip, int port, String localip) { + if (targetip instanceof String && !targetip.isEmpty()) { + if (port>0) { + + try { + SocketAddress loc = new InetSocketAddress(localip, port); + SocketAddress rem = new InetSocketAddress (targetip,port); + Socket trysocket = new Socket(); + trysocket.bind(loc); + trysocket.connect(rem); + Thread tx = new Thread(new tcpsocketrun(trysocket)); + tx.start(); + return; + } catch(IOException e) { + raise_log_error("IOException on ConnectTO ["+targetip+"]:]"+port+"] Bind="+localip,e); + } + + } + } + raise_connected("",0); + } + + private class tcpsocketrun implements Runnable{ + private boolean validrun = false; + private ByteBuffer bytebuf ; + public tcpsocketrun(Socket xx) { + // reset + _remoteip = ""; + _remoteport = 0; + _localip = ""; + _localport = 0; + _connected_tick = 0; + _rxbytescount = 0; + _rxokcount = 0; + _txbytescount = 0; + _txokcount = 0; + _txtimercount = 0; + _rxtimercount = 0; + _mapkey = ""; + if (onesecondtimer instanceof Timer) { + onesecondtimer.cancel(); + } + onesecondtimer = null; + + try { + BA.Log("tcpsocketrun about to call Disconnect on previous _socket if available"); + if (_socket instanceof Socket) Disconnect(); + _socket = xx; + + + ObjectInputStream _in = new ObjectInputStream(_socket.getInputStream()); + ObjectOutputStream _out = new ObjectOutputStream(_socket.getOutputStream()); + + + bytebuf = ByteBuffer.allocate(1024); + _input = _in; + _output = _out; + + + _remoteip = _socket.getInetAddress() == null ? "" : _socket.getInetAddress().getHostAddress(); + _localip = _socket.getLocalAddress() == null ? "" : _socket.getLocalAddress().getHostAddress(); + _remoteport = _socket.isClosed() ? 0 : _socket.getPort(); + _localport = _socket.getLocalPort(); + _mapkey = _remoteip+":"+String.valueOf(_remoteport); + validrun = true; + + } catch(IOException e) { + raise_log_error("IOException on tcpsocketrun",e); + } + + if (validrun) { + /// shutdownhook taruh di sini, buat auto close ketika socket connected + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + BA.Log("ShutdownHook on TCPSocket identifier="+_mapkey); + Disconnect(); + } + }); + } + + } + + public void run() { + if (validrun) { + raise_log("tcpsocketrun running"); + raise_connected(_socket.getInetAddress().getHostAddress(), _socket.getPort()); + _connected_tick = DateTime.getNow(); + // create new timer + onesecondtimer = new Timer(); + onesecondtimer.scheduleAtFixedRate(checktxrxtimercounter, 1000, 1000); + + + + while(true) { + // selama _input masih ObjectInputStream, coba looping lagi + // Kalau Disconnect dipanggil, _input akan jadi null + // maka while ini jadi break + + if (_input instanceof ObjectInputStream) { + int readcount = 0; + while(true) { + // coba ambil data di sini , loop terus sampe break + // kondisi terus, kalau readcount = buf.length, artinya bisa ambil data banyak + // mungkin masih ada data lagi setelahnya + // + // kondisi break, kalau readcount < buf.length, artinya buffernya aja gak sampe penuh + // berarti datanya cuma dikit + + byte[] buf = new byte[1024]; + + try { + readcount = _input.read(buf); // ngeblok di sini + } catch (IOException e) { + raise_log_error("IOException from input.read", e); + + // kalau gak online, connection closed + String msg = e.getMessage(); + + // Network disconnect + if (msg.equalsIgnoreCase("connection reset")) { + Disconnect(); // Disconnect di sini nutup _input, _output, dan _socket + break; // keluar dari while(true) dalam + } + break; + } + + if (readcount>0) { // perlu disimpen apa nggak + _rxtimercount = 0; // kalau berhasil terima something, reset jadi zero + if (readcount>bytebuf.remaining()) { + // gak cukup, gedein byte buffer + ByteBuffer newbytebuf = ByteBuffer.allocate(bytebuf.capacity()+readcount); + newbytebuf.put(bytebuf); + bytebuf = newbytebuf; // alokasikan ke sini + + } + + // sampe sini , size cukup + bytebuf.put(buf,0,readcount); + + // kalau readcount = 1024, artinya datanya cukup banyak, mungkin perlu baca lagi + // kalau readcount < 1024, artinya datanya sedikit, cukup sekali baca aja + if (readcount!=buf.length) break; + }; + + } + + if (bytebuf.position()>0) { + bytebuf.flip(); + byte[] totalbytes = new byte[bytebuf.remaining()]; + bytebuf.get(totalbytes); + //printbytes("TotalBytes",totalbytes); + try { + Object rxobject = _converter.ConvertBytesToObject(totalbytes); + if (rxobject instanceof MyMap) { + // MyMap adalah class asli Map Java , diwrapping oleh Erel + Map result = new Map(); // Map ini adalah B4X object + result.setObject((MyMap)rxobject); + raise_newmapdata(result); + } else if (rxobject instanceof String) { + String result = (String) rxobject; + raise_newstringdata(result); + } else if (rxobject instanceof ArrayList) { + ArrayList tempresult = (ArrayList) rxobject; + if (tempresult.size()>0) { + Object _firstobject = tempresult.get(0); + if (_firstobject instanceof Byte) { + byte[] result = new byte[tempresult.size()]; + for(int ii=0;ii0) { + for (int ii=0;ii0) { + try { + return WriteObject(value); + } catch (IOException e) { + raise_log_error("IOException on WriteMap",e); + Disconnect(); + } + } else raise_log("WriteMap canceled, value Size = 0"); + } else raise_log("WriteMap canceled, value is not initialized"); + } else raise_log("WriteMap canceled, value is not Map"); + return false; + } + + /** + * Write bytes array to TCPSocket + * @param value : bytes array + * @return true if can be sent + */ + public boolean WriteBytes(byte[] value) { + if (value instanceof byte[]) { + if (value.length>0) { + try { + ArrayList result = new ArrayList(); + for(int ii=0;ii0) { + try { + ArrayList result = new ArrayList(); + for(int ii=0;ii0) { + long now = DateTime.getNow(); + if ((now - _connected_tick)>0) { + return ((now - _connected_tick) / DateTime.TicksPerSecond); + } + } + return 0; + } + + /** + * Get Date and Time when the socket is connected + * String will formatted as DD-MM-YYYY HH:MM:SS + * @return empty string if not yet connected + */ + public String Connected_DateTime_String() { + if (_connected_tick>0) { + return mycodes.Tick_To_DDMMYYYY_HHMMSS(_connected_tick); + } + return ""; + } + + /** + * Get how many succesful Sending package + * @return zero to 2,147,483,647 + */ + public int getTX_OK_Counter() { + + return _txokcount; + } + + /** + * Get how many succesful Receiving package + * @return zero to 2,147,483,647 + */ + public int getRX_OK_Counter() { + return _rxokcount; + } + + /** + * Get how much bytes has been sucesfully sent + * @return value in bytes, from zero to 9,223,372,036,854,775,807 + */ + public long getTX_Bytes_Counter() { + return _txbytescount; + } + + /** + * Get how much bytes has been sucesfully received + * @return value in bytes, from zero to 9,223,372,036,854,775,807 + */ + public long getRX_Bytes_Counter() { + return _rxbytescount; + } + + /** + * TimerTask to execute, for checking txtimercounter and rxtimercounter + * this TimerTask will execute every 1000 ms + */ + private TimerTask checktxrxtimercounter = new TimerTask() { + + @Override + public void run() { + if (_txtimercount>5) { + raise_log("Not sending more than 5 seconds, sending [PING] to check connection"); + WriteString("[PING]"); // pancingan aja, gak penting hasilnya + } + + if (_rxtimercount>10) { + raise_log("Not receiving more than 10 seconds, connection have problem"); + Disconnect(); + } + } + + }; + + private void Close_ObjectOutputStream() { + if (_output instanceof ObjectOutputStream) { + try { + _output.close(); + raise_log("ObjectOutputStream closed"); + } catch (IOException e) { + raise_log_error("IOException on Close_ObjectOutputStream",e); + } + } + _output = null; + } + + private void Close_ObjectInputStream() { + if (_input instanceof ObjectInputStream) { + try { + _input.close(); + raise_log("ObjectInputStream closed"); + } catch (IOException e) { + raise_log_error("IOException on Close_ObjectInputStream",e); + } + } + _input = null; + } + + private void raise_log_error(String msg, Exception e) { + StringBuffer sbf = new StringBuffer(); + sbf.append(msg); + if (e instanceof Exception) { + if (e.getMessage() instanceof String) { + sbf.append("\r\n"); + sbf.append("Msg : "+e.getMessage()); + } + if (e.getCause() instanceof Throwable) { + sbf.append("\r\n"); + sbf.append(e.getCause()); + } + } + raise_log(sbf.toString()); + e.printStackTrace(); + } + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_connected(String remoteip, int remoteport) { + if (need_connected_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_connected", false, new Object[] {remoteip, remoteport}); + } + } + + private void raise_disconnected(String remoteip, int remoteport) { + if (need_disconnected_event) { + long tt = 0; + if (_connected_tick > 0) { + tt = (DateTime.getNow() - _connected_tick) / DateTime.TicksPerSecond; + } + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_disconnected", false, new Object[] {remoteip,remoteport, tt / DateTime.TicksPerSecond }); + } + } + + private void raise_newmapdata(Map value) { + if (need_newmapdata_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newmapdata", false, new Object[] {value}); + } + } + + private void raise_newbytesdata(byte[] value) { + if (need_newbytesdata_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newbytesdata", false, new Object[] {value}); + } + } + + private void raise_newstringdata(String value) { + if (need_newstringdata_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newstringdata", false, new Object[] {value}); + } + } + + private void raise_newintsdata(int[] value) { + if (need_newintsdata_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newintsdata", false, new Object[] {value}); + } + } + +} diff --git a/src/customsocket/TCPSocketPrefixMode.java b/src/customsocket/TCPSocketPrefixMode.java new file mode 100644 index 0000000..f191efd --- /dev/null +++ b/src/customsocket/TCPSocketPrefixMode.java @@ -0,0 +1,418 @@ +package customsocket; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.concurrent.Callable; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.DateTime; +import jGPIO.mycodes; + +@BA.ShortName("TCPSocketPrefixMode") +@BA.Events(values= { + "log(msg as string)", + "connected(targetip as string, targetport as int)", + "disconnected(targetip as string, targetport as int, duration as long)", + "newbytesdata(value() as byte)", + "newstringdata(value as String)", + +}) +public class TCPSocketPrefixMode { + private BA bax; + private String event; + private boolean need_log_event = false; + private boolean need_connected_event = false; + private boolean need_disconnected_event = false; + private boolean need_newbytesdata_event = false; + private boolean need_newstringdata_event = false; + private boolean inited = false; + private Socket _socket; + + private long _connected_tick = 0; + + private int _txokcount = 0; + private long _txbytescount = 0; + private int _rxokcount = 0; + private long _rxbytescount = 0; + + private String _remoteip = ""; + private int _remoteport = 0; + private String _localip = ""; + private int _localport = 0; + + + + /** + * Initialize TCP Socket Prefix Mode + * @param EventName Eventname + */ + public void Initialize(BA ba, String EventName) { + this.bax = ba; + this.event = EventName.toLowerCase(); + if (bax!=null) { + if (event != null && event.length() > 0) { + need_log_event = bax.subExists(event+"_log"); + need_connected_event = bax.subExists(event+"_connected"); + need_disconnected_event = bax.subExists(event+"_disconnected"); + need_newbytesdata_event = bax.subExists(event+"_newbytesdata"); + need_newstringdata_event = bax.subExists(event+"_newstringdata"); + inited = true; + } + } + } + + public boolean IsInitialized() { + return inited; + } + + public String getRemoteIP() { + return _remoteip; + } + + public int getRemotePort() { + return _remoteport; + } + + public String getLocalIP() { + return _localip; + } + + public int getLocalPort() { + return _localport; + } + + public void ConnectTo(String targetip, int port) { + if (targetip != null && targetip.length()>0) { + if (port>0 && port < 65536) { + BA.runAsync(bax, TCPSocketPrefixMode.this, event+"_connected", new Object[] {"",0}, new Callable(){ + + @Override + public Object[] call() throws Exception { + try { + Socket trysock = new Socket(); + SocketAddress rem = new InetSocketAddress(targetip, port); + trysock.connect(rem); + Thread tx = new Thread(new tcpsocketrun(trysock)); + tx.start(); + return new Object[] {targetip,port}; + } catch (UnknownHostException e) { + raise_log_error("UnknownHostException on ConnectTo ["+targetip+":"+port+"]",e); + + } catch (IOException e) { + raise_log_error("IOException on ConnectTo ["+targetip+"]:["+port+"]",e); + } + return new Object[] {"",0}; + } + + }); + return; + } else raise_log("ConnectTo failed, invalid targetport"); + } else raise_log("ConnectTo failed, Invalid targetip"); + raise_connected("",0); + } + + public void ConnectTo_withBind(String targetip, int port, String localip) { + if (targetip != null && !targetip.isEmpty()) { + if (port>0 && port < 65536) { + if (localip!=null && !localip.isEmpty()) { + BA.runAsync(bax, TCPSocketPrefixMode.this, event+"_connected", new Object[] {"",0}, new Callable() { + + @Override + public Object[] call() throws Exception { + try { + SocketAddress loc = new InetSocketAddress(localip, port); + SocketAddress rem = new InetSocketAddress (targetip,port); + Socket trysocket = new Socket(); + trysocket.bind(loc); + trysocket.connect(rem); + Thread tx = new Thread(new tcpsocketrun(trysocket)); + tx.start(); + return new Object[] {targetip,port}; + } catch(IOException e) { + raise_log_error("IOException on ConnectTo_withBind ["+targetip+"]:]"+port+"] Bind="+localip,e); + } + return new Object[] {"",0}; + } + + }); + } else raise_log("ConnectTo_withBind failed, invalid localip"); + } else raise_log("ConnectTo_withBind failed, invalid targetport"); + } else raise_log("ConnectTo_withBind failed, Invalid targetip"); + raise_connected("",0); + } + + public void Disconnect() { + + + if (_socket != null) { + try { + _socket.close(); + raise_disconnected(_remoteip, _remoteport); + } catch (IOException e) { + raise_log_error("IOException on Disconnect",e); + raise_disconnected("",0); + } + } else { + raise_log("Disconnect has not effect, Socket is not created yet"); + raise_disconnected("",0); + } + + _socket = null; + _remoteip = ""; + _localip = ""; + _remoteport =0; + _localport = 0; + + + } + + public boolean WriteBytes(byte[] data) { + if (IsConnected()) { + if (data != null && data.length>0) { + ByteBuffer bb = ByteBuffer.allocate(data.length+4); + bb.order(ByteOrder.BIG_ENDIAN); + bb.putInt(data.length); + bb.put(data); + try { + _socket.getOutputStream().write(bb.array()); + + _txokcount++; + _txbytescount += data.length; + return true; + } catch (IOException e) { + raise_log_error("IOException on WriteBytes", e); + Disconnect(); + } + } else raise_log("WriteBytes failed, data is null or empty"); + } else raise_log("WriteBytes failed, Socket is not connected"); + return false; + } + + public boolean WriteString(String data) { + try { + byte[] xx = data.getBytes("UTF-8"); + return WriteBytes(xx); + } catch (UnsupportedEncodingException e) { + raise_log_error("UnsupportedEncodingException on WriteString", e); + } + return false; + } + + public long ConnectionDuration() { + if (_connected_tick>0) { + long now = DateTime.getNow(); + if ((now - _connected_tick)>0) { + return ((now - _connected_tick) / DateTime.TicksPerSecond); + } + } + return 0; + } + + public String Connected_DateTime_String() { + if (_connected_tick>0) { + return mycodes.Tick_To_DDMMYYYY_HHMMSS(_connected_tick); + } + return ""; + } + + public int getTX_OK_Counter() { + + return _txokcount; + } + + public int getRX_OK_Counter() { + return _rxokcount; + } + + public long getTX_Bytes_Counter() { + return _txbytescount; + } + + public long getRX_Bytes_Counter() { + return _rxbytescount; + } + + /** + * Check if Socket is connected + * @return true if connected + */ + public boolean IsConnected() { + if (_socket != null) { + if (_socket.isConnected()) { + if (!_socket.isInputShutdown()) { + if (!_socket.isOutputShutdown()) { + return !_socket.isClosed(); + } + } + } + } + return false; + } + + + + class tcpsocketrun implements Runnable{ + private boolean validrun = false; + private InputStream _input; + public tcpsocketrun(Socket xx) { + // reset + _remoteip = ""; + _remoteport = 0; + _localip = ""; + _localport = 0; + _connected_tick = 0; + _rxbytescount = 0; + _rxokcount = 0; + _txbytescount = 0; + _txokcount = 0; + + try { + BA.Log("tcpsocketrun about to call Disconnect on previous _socket if available"); + if (_socket != null) Disconnect(); + _socket = xx; + _input = _socket.getInputStream(); + + _remoteip = _socket.getInetAddress() == null ? "" : _socket.getInetAddress().getHostAddress(); + _localip = _socket.getLocalAddress() == null ? "" : _socket.getLocalAddress().getHostAddress(); + _remoteport = _socket.isClosed() ? 0 : _socket.getPort(); + _localport = _socket.getLocalPort(); + + validrun = true; + + } catch(IOException e) { + raise_log_error("IOException on tcpsocketrun",e); + } + + if (validrun) { + /// shutdownhook taruh di sini, buat auto close ketika socket connected + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + + Disconnect(); + } + }); + } + + } + + public void run() { + + if (validrun) { + raise_log("tcpsocketrun running"); + //raise_connected(_socket.getInetAddress().getHostAddress(), _socket.getPort()); + _connected_tick = DateTime.getNow(); + + int readcount; + byte[] xx; + while(IsConnected()) { + xx = new byte[15000]; + readcount = 0; + try { + + readcount = _input.read(xx); // ngeblok di sini sampai dapat data + + } catch (IOException e) { + raise_log_error("IOException on tcpsocketrun read",e); + break; + } + + // prefix mode, 4 byte awal adalah size, sisanya adalah data + // jadi minimal length = 4 + if (readcount>4) { + ByteBuffer bb = ByteBuffer.wrap(xx); + bb.order(ByteOrder.BIG_ENDIAN); + try { + int datasize = bb.getInt(); + byte[] data = new byte[datasize]; + bb.get(data); + _rxokcount++; + _rxbytescount += readcount; + byte[] yy = new byte[readcount]; + System.arraycopy(xx, 0, yy, 0, readcount); + raise_newbytesdata(yy); + try { + String zz = new String(yy, "UTF-8"); + raise_newstringdata(zz); + } catch (UnsupportedEncodingException e) { + raise_log_error("UnsupportedEncodingException on tcpsocketrun read", e); + } + } catch (BufferUnderflowException e) { + + raise_log_error("BufferUnderflowException on tcpsocketrun read, prefix invalid", e); + break; + } + + + } + } + Disconnect(); + + + } else raise_disconnected("",0); + raise_log("tcpsocketrun ended"); + + } + } + + + private void raise_log_error(String msg, Exception e) { + StringBuffer sbf = new StringBuffer(); + sbf.append(msg); + if (e instanceof Exception) { + if (e.getMessage() instanceof String) { + sbf.append("\r\n"); + sbf.append("Msg : "+e.getMessage()); + } + if (e.getCause() instanceof Throwable) { + sbf.append("\r\n"); + sbf.append(e.getCause()); + } + } + raise_log(sbf.toString()); + e.printStackTrace(); + } + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(TCPSocketPrefixMode.this, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_connected(String remoteip, int remoteport) { + if (need_connected_event) { + bax.raiseEventFromDifferentThread(TCPSocketPrefixMode.this, null, 0, event+"_connected", false, new Object[] {remoteip, remoteport}); + } + } + + private void raise_disconnected(String remoteip, int remoteport) { + if (need_disconnected_event) { + if (remoteport > 0 && remoteip.length() > 0) { + // pernah connect + long tt = 0; + if (_connected_tick > 0) { + tt = (DateTime.getNow() - _connected_tick) / DateTime.TicksPerSecond; + } + bax.raiseEventFromDifferentThread(TCPSocketPrefixMode.this, null, 0, event+"_disconnected", false, new Object[] {remoteip,remoteport, tt / DateTime.TicksPerSecond }); + } + + } + } + + private void raise_newbytesdata(byte[] value) { + if (need_newbytesdata_event) { + bax.raiseEventFromDifferentThread(TCPSocketPrefixMode.this, null, 0, event+"_newbytesdata", false, new Object[] {value}); + } + } + + private void raise_newstringdata(String value) { + if (need_newstringdata_event) { + bax.raiseEventFromDifferentThread(TCPSocketPrefixMode.this, null, 0, event+"_newstringdata", false, new Object[] {value}); + } + } +} diff --git a/src/customsocket/TCPSocketServer.java b/src/customsocket/TCPSocketServer.java new file mode 100644 index 0000000..867473e --- /dev/null +++ b/src/customsocket/TCPSocketServer.java @@ -0,0 +1,380 @@ +package customsocket; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("TCPSocketServer") +@BA.Events(values= { + "log(msg as string)", + "newtcpsocket(value as TCPSocket)", + "listeningstatus(islistening as boolean)", + "connectedclients(value as int, keys as List)" +}) +public class TCPSocketServer implements TcpSocketJavaEvent { + + private BA bax; + private Object caller; + private Object Me = this; + private String event; + private boolean _inited = false; + private boolean need_log_event = false; + private boolean need_newtcpsocket_event = false; + private boolean need_listeningstatus_event = false; + private boolean need_connectedclients_event = false; + private ServerSocket _server; + + private Map _socketmap = new Map(); + + /** + * Initialize TCP Socket Server + * @param callerobject : caller object + * @param eventname : eventname + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + bax = ba; + caller = callerobject; + event = eventname; + if (bax instanceof BA) { + if (caller != null) { + if (event instanceof String) { + if (!event.isEmpty()) { + _inited = true; + need_log_event = bax.subExists(event+"_log"); + need_newtcpsocket_event = bax.subExists(event+"_newtcpsocket"); + need_listeningstatus_event = bax.subExists(event+"_listeningstatus"); + need_connectedclients_event = bax.subExists(event+"_connectedclients"); + } + } + } + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + BA.Log("ShutdownHook on TCPSocketServer at "+getLocalIPAddress()+":"+getLocalPort()); + StopListening(); + Disconnect_All_TCPSocket(); + } + }); + + _socketmap.Initialize(); + } + + /** + * Check if TCPSocketServer is initialized + * @return true if inited + */ + public boolean Initialized() { + return _inited; + } + + /** + * Stop Listening for Connections + */ + public void StopListening() { + if (_server instanceof ServerSocket) { + try { + _server.close(); + } catch (IOException e) { + raise_log_error("IOException on StopListening",e); + } + } + _server = null; + } + + /** + * Get Local IP Address used for listening + * @return empty string if failed + */ + public String getLocalIPAddress() { + if (_server instanceof ServerSocket) { + if (_server.getInetAddress()!=null) { + return _server.getInetAddress().getHostAddress(); + } + } + return ""; + } + + /** + * Get Local Port used for Listening + * @return zero if failed + */ + public int getLocalPort() { + if (_server instanceof ServerSocket) { + if (_server.getLocalPort()>0) { + return _server.getLocalPort(); + } + + } + return 0; + } + + /** + * Start Listening + * @param port : listen port + * @return true if success + */ + public boolean StartListening(int port) { + if (_server instanceof ServerSocket) StopListening(); + try { + ServerSocket newserver = new ServerSocket(port); + Thread tx = new Thread(new tcpsocketserverrun(newserver)); + tx.start(); + return true; + } catch (IOException e) { + raise_log_error("IOException on StartListening at port "+port,e); + } + return false; + } + + /** + * Get how many TCPSockets has been received + * @return value as integer + */ + public int getConnectedTCPSocket() { + return _socketmap.IsInitialized() ? _socketmap.getSize() : 0; + } + + /** + * Get List of IdentifierKey from all connected TCPSockets + * @return List of IdentifierKey, or empty List of none connected + */ + public List getListOfTCPSocketKeys() { + List result = new List(); + result.Initialize(); + if (_socketmap.IsInitialized()) { + if (_socketmap.getSize()>0) { + for(int ii=0;ii<_socketmap.getSize();ii++) { + result.Add((String)_socketmap.GetKeyAt(ii)); + } + } + } + return result; + } + + /** + * Disconnect all TCPSocket in Map + */ + public void Disconnect_All_TCPSocket() { + List result = getListOfTCPSocketKeys(); + if (result.getSize()>0) { + for(int ii=0;ii0) { + for (int ii=0;ii<_socketmap.getSize();ii++) { + String key = (String) _socketmap.GetKeyAt(ii); + if (key.startsWith(ipvalue)) { + Object xx = _socketmap.Get(key); + if (xx instanceof TCPSocket) { + return (TCPSocket)xx; + } + + } + } + } + } + } + return null; + } + + /** + * Disconnect TCPSocket and remove it from Map + * @param identifierkey : identifierkey in string + * @return true if identifierkey can be found + */ + public boolean Disconnect_TCPSocket(String identifierkey) { + if (identifierkey instanceof String) { + if (!identifierkey.isEmpty()) { + if (_socketmap.ContainsKey(identifierkey)) { + Object xx = _socketmap.Get(identifierkey); + if (xx instanceof TCPSocket) { + TCPSocket tcp = (TCPSocket)xx; + tcp.Disconnect(); + tcp = null; + } + xx = null; + _socketmap.Remove(identifierkey); + raise_connectedclients(_socketmap.getSize(), getListOfTCPSocketKeys()); + return true; + } + } + } + return false; + } + + /** + * Rename Map identifier from old_id to new_id, but retain the same TCPSocket object + * @param old_id : old identifier + * @param new_id : new identifier + * @return true if Map contain old_id and succesfully renamed, or false if otherwise + */ + public boolean Rename_Identifier(String old_id, String new_id) { + if (_socketmap instanceof Map) { + if (_socketmap.IsInitialized()) { + if (_socketmap.ContainsKey(old_id)) { + Object xx = _socketmap.Get(old_id); + if (xx instanceof TCPSocket) { + TCPSocket tcp = (TCPSocket) xx; + + _socketmap.Remove(old_id); + _socketmap.Put(new_id, tcp); + if (tcp.getIdentifierKey().equals(new_id)==false) { + tcp.setIdentifierKey(new_id); + } + return true; + } + } + } + } + return false; + } + + private class tcpsocketserverrun implements Runnable{ + private boolean validrun = false; + public tcpsocketserverrun(ServerSocket xx) { + _server = xx; + if (_server instanceof ServerSocket) { + if (!_server.isClosed()) { + validrun = true; + } + } + } + + public void run() { + if (validrun) { + raise_listeningstatus(true); + while(true) { + if (_server instanceof ServerSocket) { + if (!_server.isClosed()) { + try { + Socket newsocket = _server.accept(); + raise_log("New connection from "+newsocket.getInetAddress().getHostAddress()+":"+newsocket.getPort()+" to TCPSocketServer"); + TCPSocket newtcp = new TCPSocket(newsocket, Me); + + raise_newtcpsocket(newtcp); + } catch (IOException e) { + raise_log_error("IOException on server.accept", e); + } + } else break; + } else break; + } + + } + raise_listeningstatus(false); + } + } + + private void raise_log_error(String msg, Exception e) { + StringBuffer sbf = new StringBuffer(); + sbf.append(msg); + if (e instanceof Exception) { + if (e.getMessage() instanceof String) { + sbf.append("\r\n"); + sbf.append("Msg : "+e.getMessage()); + } + if (e.getCause() instanceof Throwable) { + sbf.append("\r\n"); + sbf.append(e.getCause()); + } + } + raise_log(sbf.toString()); + } + + private void raise_newtcpsocket(TCPSocket value) { + if (need_newtcpsocket_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newtcpsocket", false, new Object[] {value}); + } + } + + private void raise_log(String msg) { + if (need_log_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + } + + private void raise_listeningstatus(boolean value) { + if (need_listeningstatus_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_listeningstatus", false, new Object[] {value}); + } + } + + private void raise_connectedclients(int value, List keys) { + if (need_connectedclients_event) { + bax.raiseEventFromDifferentThread(Me, null, 0, event+"_connectedclients", false, new Object[] {value, keys}); + } + } + + //////////////// Event from TcpSocketJavaEvent ///////////////////////// + @Override + @BA.Hide + public void newidentifierkey(String identifier, TCPSocket Sender) { + if (!_socketmap.IsInitialized()) _socketmap.Initialize(); + if (identifier instanceof String) { + if (!identifier.isEmpty()) { + Object prev_in_map = null; + if (identifier.contains(":")) { + // mengandung ip:port + // coba cek apakah ip nya pernah ada + String ipnya = identifier.substring(0, identifier.indexOf(":")); + prev_in_map = Have_TCPSocket_with_IPAddress(ipnya); + if (prev_in_map instanceof TCPSocket) { + TCPSocket oldsock = (TCPSocket) prev_in_map; + // ada + raise_log("Found another TCPSocket with same IP. ID 1 = "+identifier+", ID 2="+ oldsock.getIdentifierKey()); + _socketmap.Remove(oldsock.getIdentifierKey()); + raise_log("Removing Old TCPSocket with identifier="+oldsock.getIdentifierKey()); + _socketmap.Put(identifier, Sender); + raise_log("Save New TCPSocket with identifer="+Sender.getIdentifierKey()); + } + } else { + prev_in_map = _socketmap.Put(identifier, Sender); + raise_log("Save TCPSocket with identifier="+identifier+" to Map"); + } + + + if (prev_in_map != null) { + if (prev_in_map instanceof TCPSocket) { + TCPSocket prevsock = (TCPSocket) prev_in_map; + prevsock.Disconnect(); + prevsock = null; + raise_log("Disconnect old TCPSocket with the identifier="+identifier); + } + prev_in_map = null; + } + } + } + + + raise_connectedclients(_socketmap.getSize(), getListOfTCPSocketKeys()); + } + + @Override + @BA.Hide + public void disconnected(String identifier) { + Disconnect_TCPSocket(identifier); + } + + @Override + @BA.Hide + public void renameidentifier(String oldvalue, String newvalue) { + Rename_Identifier(oldvalue, newvalue); + + } +} diff --git a/src/customsocket/TcpSocketJavaEvent.java b/src/customsocket/TcpSocketJavaEvent.java new file mode 100644 index 0000000..d587c18 --- /dev/null +++ b/src/customsocket/TcpSocketJavaEvent.java @@ -0,0 +1,8 @@ +package customsocket; + +public interface TcpSocketJavaEvent { + public void newidentifierkey(String identifier, TCPSocket Sender); + public void disconnected(String identifier); + public void renameidentifier(String oldvalue, String newvalue); + +} diff --git a/src/customsocket/UDPSocket.java b/src/customsocket/UDPSocket.java new file mode 100644 index 0000000..f8a69ae --- /dev/null +++ b/src/customsocket/UDPSocket.java @@ -0,0 +1,273 @@ +package customsocket; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetSocketAddress; +import java.net.SocketException; + +import anywheresoftware.b4a.*; + +//@BA.ShortName("UDPSocketNew") +@BA.Permissions(values = {"android.permission.INTERNET"}) +@BA.Events(values={"PacketArrived (Packet As UDPPacket)"}) +@BA.Hide +public class UDPSocket { + private UDPReader reader; + private DatagramSocket ds; + + /** + * Initialize UDPSocket + * @param EventName eventname + * @param Port listening port + * @param ReceiveBufferSize size of buffer + * @throws SocketException + */ + public void Initialize(BA ba, String EventName, int Port, int ReceiveBufferSize) throws SocketException { + Close(); + DatagramSocket newds = null; + try { + if (Port==0) { + newds = new DatagramSocket(); + } else { + newds = new DatagramSocket(Port); + } + } catch(IOException e) { + BA.Log("UDPSocketNew Initialize failed, exception = "+e.getMessage()); + } finally { + if (newds instanceof DatagramSocket) { + init(ba, EventName, ReceiveBufferSize, newds); + } else BA.Log("Not calling init"); + } + + + } + + /** + * Initialize UDPSocket with specific bind + * @param EventName eventname + * @param Port listening port + * @param ReceiveBufferSize size of buffer + * @param localip Local IP Address bind to UDPSocket + * @throws SocketException + */ + public void Initialize_withBind(BA ba, String EventName, int Port, int ReceiveBufferSize, String localip) throws SocketException { + Close(); + DatagramSocket newds = null; + try { + InetSocketAddress bindadd = new InetSocketAddress(localip, Port); + newds = new DatagramSocket(bindadd); + + } catch(IOException e) { + BA.Log("UDPSocketNew Initialize_withBind failed, exception="+e.getMessage()); + } finally { + if (newds instanceof DatagramSocket) { + BA.Log("UDPSocketNew Initialize_withBind will call init"); + init(ba, EventName, ReceiveBufferSize, newds); + } else BA.Log("UDPSocketNew Initialize_withBind not calling init"); + } + + } + + @BA.Hide + public void init(BA ba, String EventName, int ReceiveBufferSize, DatagramSocket ds) { + this.ds = ds; + if (ReceiveBufferSize > 0) { + reader = new UDPReader(); + reader.working = true; + reader.socket = ds; + reader.receiveLength = ReceiveBufferSize; + reader.ba = ba; + reader.eventName = EventName.toLowerCase(BA.cul); + Thread t = new Thread(reader); + t.setDaemon(true); + t.start(); + } + } + + public boolean IsInitialized() { + return ds != null && !ds.isClosed(); + } + + /** + * Get Local Port where UDPSocket is listening + * @return -1 if UDPsocket closed, or 0 if not bound + */ + public int getPort() { + return ds.getLocalPort(); + } + + /** + * Get Local IP Address where UDPSocket is bound + * @return null if UDPSocket is closed + */ + public String getLocalIP() { + return ds.getLocalAddress().getHostAddress(); + } + + /** + * Get Remote Port where UDPSocket is connected + * @return -1 if not connected + */ + public int getRemotePort() { + return ds.getPort(); + } + + /** + * Get Remote IP where UDPSocket is connected + * @return null if not connnected + */ + public String getRemoteIP() { + return ds.getInetAddress().getHostAddress(); + } + + /** + * Send UDP Packet + * @param Packet packet to send + * @throws IOException + */ + public void Send(final UDPPacket Packet) throws IOException { + BA.submitRunnable(new Runnable() { + + @Override + public void run() { + try { + Packet.getObject().packet.setSocketAddress(new InetSocketAddress(Packet.getObject().host, Packet.getObject().port)); + ds.send(Packet.getObject().packet); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + }, this, 1); + } + + public void Close() { + if (ds!=null) { + ds.close(); + } + ds = null; + if (reader!=null) { + reader.working = false; + } + reader = null; + } + + @Override + public String toString() { + if (ds == null) + return "Not initialized"; + return getLocalIP()+":"+getPort(); + } + + private static class UDPReader implements Runnable { + volatile boolean working; + DatagramSocket socket; + int receiveLength; + BA ba; + String eventName; + @Override + public void run() { + while (working) { + try { + DatagramPacket p = new DatagramPacket(new byte[receiveLength], receiveLength); + socket.receive(p); + UDPPacket u = new UDPPacket(); + u.setObject(new MyDatagramPacket("", 0, p)); + ba.raiseEventFromDifferentThread(null, null, 0, eventName + "_packetarrived", false, new Object[] {u}); + } catch (IOException e) { + if (working) { + try { + e.printStackTrace(); + Thread.sleep(100); + } catch (InterruptedException e1) { + } + } + } + } + } + } + + + + /** + * A packet of data that is being sent or received. + *To send a packet call one of the Initialize methods and then send the packet by passing it to UDPSocket.Send. + *When a packet arrives you can get the data in the packet from the available properties. + */ +// @BA.ShortName("UDPPacket") + @BA.Hide + public static class UDPPacket extends AbsObjectWrapper { + /** + * Initializes the packet and makes it ready for sending. + *Data - The data that will be send. + *Host - The target host name or IP address. + *Port - The target port. + */ + public void Initialize(byte[] Data, String Host, int Port) throws SocketException { + Initialize2(Data, 0, Data.length, Host, Port); + } + /** + * Similar to Initialize. The data sent is based on the Offset and Length values. + */ + public void Initialize2(byte[] Data, int Offset, int Length, String Host, int Port) throws SocketException { + + DatagramPacket d = new DatagramPacket(Data, Offset, Length); + MyDatagramPacket m = new MyDatagramPacket(Host, Port, d); + setObject(m); + } + /** + * Gets the length of available bytes in the data. This can be shorter than the array length. + */ + public int getLength() { + return getObject().packet.getLength(); + } + /** + * Gets the data array received. + */ + public byte[] getData() { + return getObject().packet.getData(); + } + /** + * Gets the offset in the data array where the available data starts. + */ + public int getOffset() { + return getObject().packet.getOffset(); + } + /** + * Gets the port of the sending machine. + */ + public int getPort() { + return getObject().packet.getPort(); + } + /** + *This method is deprecated and will not work properly on Android 4+ device. + *Use HostAddress instead. + */ + public String getHost() { + return getObject().packet.getAddress().getHostName(); + } + /** + * Gets the IP address of the sending machine. + */ + public String getHostAddress() { + return getObject().packet.getAddress().getHostAddress(); + } + @Override + public String toString() { + if (getObjectOrNull() == null) + return super.toString(); + return "Length=" + getLength() + ", Offset=" + getOffset() + ", Host=" + getHost() + ", Port=" + getPort(); + } + } + @BA.Hide + public static class MyDatagramPacket { + public final String host; + public final int port; + public final DatagramPacket packet; + public MyDatagramPacket(String host, int port, DatagramPacket packet) { + this.host = host; + this.port = port; + this.packet = packet; + } + } +} diff --git a/src/devices/AT24C32.java b/src/devices/AT24C32.java new file mode 100644 index 0000000..48d8e3a --- /dev/null +++ b/src/devices/AT24C32.java @@ -0,0 +1,219 @@ +package devices; + +import anywheresoftware.b4a.BA; +import jGPIO.jGPIO; +import jGPIO.I2C.I2C_BUS; +import jGPIO.I2C.I2C_BUS_Event; +import jGPIO.I2C.I2C_Device; +import jGPIO.I2C.I2C_Device_Event; + + +// belum bisa +@BA.ShortName("AT24C32") +@BA.Events(values= { + "log(msg as string)" +}) +public class AT24C32 implements I2C_BUS_Event, I2C_Device_Event { + private BA bax; + private Object myobject; + private String event; + private boolean need_log_event = false; + + private int dev_address = 0x57; + private I2C_BUS i2cbus; + private I2C_Device i2cdev; + + private boolean iscorrect = false; + + public AT24C32() { + if (jGPIO.osname.isEmpty()) jGPIO.detectOS(); + } + + /** + * Initialize a AT24C32. Used in B4J only + * @param caller : caller object + * @param eventname : event name + */ + public void Initialize(BA ba, Object caller, String eventname) { + bax = ba; + myobject = caller; + event = eventname.trim(); + if (bax!=null) { + if (myobject!=null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + } + } + } + + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + + } + + public void Close() { + if (i2cdev!=null) { + i2cdev.CloseDevice(); + } + i2cdev = null; + + if (i2cbus!=null) { + i2cbus.Close_Bus(); + } + i2cbus = null; + } + + /** + * Open a Slave + * @param index : I2C bus Index + * @param slave_address : slave address of this PCF + * @return true if succcess + */ + public boolean Open(String bus_name, int slave_address) { + iscorrect = false; + if (!jGPIO.IsLinux) return iscorrect; + + i2cbus = new I2C_BUS(bus_name); + i2cbus.SetJavaEvent(this); + if (i2cbus.Open_Bus()) { + i2cdev = new I2C_Device(i2cbus.I2C_Handle(), slave_address); + i2cdev.SetJavaEvent(this); + if (i2cdev.OpenDevice_SLAVE()) { + iscorrect = true; + this.dev_address = slave_address; + } + } + + if (iscorrect) { + raise_log("AT24C32 is Opened"); + } else { + raise_log("AT24C32 failed to Open"); + } + return iscorrect; + } + + /** + * Check associated Slave Address + * @return -1 if no address + */ + public int getSlaveAddress() { + return this.dev_address; + } + + /** + * Check if I2C to this AT24C32 is opened + * @return true if opened + */ + public boolean getIsOpened() { + return iscorrect; + } + + /** + * Write bytes to AT24C32 + * AT24C32 only have 4096 bytes, addressed from 0 to 4095. + * @param start_address : 0 ~ 4095 + * @param bb : bytes to write + * @return true if success + * @throws InterruptedException + */ + public boolean Write(int start_address, byte[] bb) { + if (start_address<0) { + raise_log("AT24C32 Write Fail, start_address invalid"); + return false; + } + if (start_address>4095) { + raise_log("AT24C32 Write Fail, start_address invalid"); + return false; + } + + if (bb==null) { + raise_log("AT24C32 Write Fail, bytes to write is null"); + return false; + } + + if (bb.length<1) { + raise_log("AT24C32 Write Fail, bytes to write is less than 1"); + return false; + } + + if (bb.length>32) { + raise_log("AT24C32 Write Fail, bytes to write more than 32 bytes"); + return false; + } + + if (!iscorrect) { + raise_log("AT24C32 Write Fail, I2C not opened"); + return false; + } + + boolean result = false; + byte[] towrite = new byte[2+bb.length]; + towrite[1] = (byte) (start_address); + towrite[0] = (byte) (start_address >> 8); // MSB di index 0 + for(int ii=0;ii4095) { + raise_log("AT24C32 Read Fail, start_address invalid"); + return null; + } + if (!iscorrect) { + raise_log("AT24C32 Read Fail, I2C not opened"); + return null; + } + + byte[] address = new byte[2]; + address[1] = (byte) (start_address); + address[0] = (byte) (start_address>>8); + + if (i2cdev.WriteBytes(address)==2) { + byte[] result = i2cdev.ReadBytes(readcount); + if (result!=null) { + if (result.length==readcount) { + return result; + } + } + } + + return null; + } + + private void raise_log(String msg) { + if (need_log_event) { + bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg}); + } + } + + @Override + ///from I2C_BUS_Event and I2C_Device_Event + public void Log(String msg) { + raise_log(msg); + } +} diff --git a/src/devices/Button.java b/src/devices/Button.java new file mode 100644 index 0000000..4e615e3 --- /dev/null +++ b/src/devices/Button.java @@ -0,0 +1,215 @@ +package devices; + + + +import java.util.Timer; +import java.util.TimerTask; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.DateTime; +import jGPIO.jGPIO; +import jGPIO.DigitalInput.DigitalInput; +import jGPIO.DigitalInput.DigitalInputEvent; + + +@BA.ShortName("HardwareButton") +@BA.Events(values= { + "log(msg as string)", + "shortpressed(tick as long)", + "longpressed(tick as long)", + "currentstate(isON as boolean, tick as long)" +}) + +/** + * Button class + * use class DigitalInput + * Button is always Active Low, so Pin need to be Pull Up using Resistor to VCC + * @author rdkartono + * + */ +public class Button implements DigitalInputEvent { + private DigitalInput di; + private BA bax; + private Object myobject; + private String event; + private int mypinnumber; + + private Timer tt; // scan DigitalInput using timer + private final int timerms = 50; // timer trigger per 50 ms + private int statecounter = 0; // for detecting shortpressed or longpressed + private final int shortpresslimit = 4; // 4 x 50ms = 200ms + private final int longpresslimit = 40; // 40 x 50ms = 2000ms = 2 seconds + + + private boolean initialized = false; + + + private boolean need_log_event = false; + private boolean need_shortpressed_event = false; + private boolean need_longpressed_event = false; + private boolean need_currentstate_event = false; + + public Button() { + if (jGPIO.osname=="") jGPIO.detectOS(); + } + + + public boolean Initialize(BA ba, Object caller, String buttonname, int pin_number) { + myobject = caller; + bax = ba; + + event = buttonname.trim(); + mypinnumber = pin_number; + initialized = false; + + if (bax!=null) { + if (caller!=null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_shortpressed_event = bax.subExists(event+"_shortpressed"); + need_longpressed_event = bax.subExists(event+"_longpressed"); + need_currentstate_event = bax.subExists(event+"_currentstate"); + } + } + } + + + if (jGPIO.IsLinux==false) { + raise_log("Target device is not Linux"); + return false; + } + + di = new DigitalInput(); + di.SetJavaEvent(this); // link javaevent to this class + + di.Initialize(bax, caller, event, pin_number); + + if (di.getIsInitialized()) { + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Release(); + } + }); + + TimerTask task = new TimerTask() { + + @Override + public void run() { + if (di == null) { + cancel(); // stop timer + raise_log("DigitalInput is null, Button Timer stopped"); + return; + } + if (di.getIsInitialized()==false) { + cancel(); // stop timer + raise_log("DigitalInput is not initialized, Button Timer stopped"); + return; + } + int value = di.ReadState(); + + if (value==-1) { + cancel(); // stop timer + raise_log("DigitalInput ReadState failed, Button Timer stopped"); + return; + } + + + if (value==1) { + // high + if (statecounter>=shortpresslimit) { + if (statecounter0) { + raise_rxbytes(msg); // raise event for raw bytes + Simplex4100Data vv = new Simplex4100Data(new String(msg)); + raise_newdata(vv); // taruh di sini dulu , supaya keluar event + + if (vv.getIsValid()) { + if (EventMap.IsInitialized()==false) EventMap.Initialize(); + Object old = EventMap.Put(vv.getDetectorLocation(), vv); + if (old instanceof Simplex4100Data) { + raise_statuschanged((Simplex4100Data)old,vv); + } + } + } + } + } + } + + } + + @Override + public byte[] getMessageDelimiter() { + // CR - LF + return new byte[] {(byte) 13, (byte) 10}; + } + + @Override + public boolean delimiterIndicatesEndOfMessage() { + // delimiter adalah akhir dari message + return true; + } + + }; + + /** + * Open SerialPort for communication with Simplex 4100 + * @param portname : portname + * @return true if success + */ + public boolean OpenPort(String portname) { + if (!portname.isEmpty()) { + try { + myport = SerialPort.getCommPort(portname); + } catch(SerialPortInvalidPortException e) { + myport = null; + raise_log("Error getCommPort for "+portname+", Msg : "+e.getMessage()); + return false; + } + + if (myport instanceof SerialPort) { + if (myport.openPort()) { + this.portname = portname; + if (myport.setBaudRate(9600)) { + if (myport.setNumDataBits(8)) { + if (myport.setNumStopBits(1)) { + if (myport.setParity(Parity.NONE.getValue())) { + myport.addDataListener(new SimplexEvent()); + + isopened = true; + return true; // success + } else raise_log("Unable to set Parity None"); + } else raise_log("Unable to set Stopbit 1"); + } else raise_log("Unable to set Databits 8"); + } else raise_log("Unable to set Baudrate 9600"); + } else raise_log("Unable to OpenPort"); + } + } + + return false; + } + + /** + * Close Serial Port connected to Simplex 4100 + */ + public void ClosePort() { + isopened = false; + if (myport instanceof SerialPort) { + myport.removeDataListener(); + myport.closePort(); + myport = null; + raise_log("Serial Port Closed"); + } + } + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_rxbytes(byte[] bb) { + if (need_rxbytes_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_rxbytes", false, new Object[] {portname, bb}); + } + + private void raise_newdata(Simplex4100Data value) { + if (need_newdata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newdata", false, new Object[] {value}); + } + + private void raise_statuschanged(Simplex4100Data oldvalue, Simplex4100Data newvalue) { + if (need_statuschanged_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_statuschanged", false, new Object[] {oldvalue, newvalue}); + } +} diff --git a/src/devices/FA/Simplex4100/Simplex4100Data.java b/src/devices/FA/Simplex4100/Simplex4100Data.java new file mode 100644 index 0000000..f16a177 --- /dev/null +++ b/src/devices/FA/Simplex4100/Simplex4100Data.java @@ -0,0 +1,214 @@ +package devices.FA.Simplex4100; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.keywords.DateTime; +import anywheresoftware.b4a.keywords.Regex; + +import java.text.ParseException; + + +@BA.ShortName("Simplex4100Data") +public class Simplex4100Data { + + private boolean valid_data = false; + private String datastring; + private String exceptionmsg; + private long datetimetick = 0; + private String _detectorlocation=""; + private String _detectorstatus=""; + + public Simplex4100Data() { + datastring = ""; + exceptionmsg = ""; + } + public Simplex4100Data(String msg) { + this(); + Analyze(msg); + } + + /** + * Try to analyze a string + * @param msg : Simplex String + * @return true if valid + */ + public boolean Analyze(String msg) { + valid_data = false; + + if (msg!=null) { + if (msg.length()>0) { + datastring = msg.trim(); + Check_Validity(); + } else datastring = ""; + } else datastring = ""; + + return valid_data; + } + + /** + * Check if Data is valid + * @return true if valid + */ + public boolean getIsValid() { + return valid_data; + } + + + // Contoh : + // -134424-6250108 @2-2-0 T1* --> trouble + // -134319-6250108 @2-2-0 T1- --> trouble acknowledged + // -134335-6250108 @2-2-0 T0- --> trouble selesai + // -134424-6250108 @2-2-0 F1* --> ada fire + // -134430-6250108 @2-2-0 F1- --> fire acknowledged + // -134515-6250108 @2-2-0 F0- --> fire sudah normal + + private void Check_Validity() { + valid_data = false; + if (datastring == null) { + exceptionmsg = "Data is null"; + } else if (datastring.isEmpty()) { + exceptionmsg = "Data is empty"; + } else { + String[] cmds = Regex.Split(" ", datastring); + if (cmds.length>=3) { + if (cmds[0].startsWith("-")) { + if (cmds[1].startsWith("@")) { + if (cmds[2].length()>=3) { + + // Date and Time part + boolean valid_date = false; + this.datetimetick = 0; + + try { + String timereceived = cmds[0].substring(1, 7); // skip '-' awal, sampai sebelum '-' di tengah + String datereceived = cmds[0].substring(9); // skip '-6' + + String old_dateformat = DateTime.getDateFormat(); + String old_timeformat = DateTime.getTimeFormat(); + + DateTime.setDateFormat("ddMMyy"); + DateTime.setTimeFormat("HHmmss"); + + datetimetick = DateTime.DateTimeParse(datereceived, timereceived); + + // sampe sini sudah berhasil parse + // balikin ke format awal + DateTime.setDateFormat(old_dateformat); + DateTime.setTimeFormat(old_timeformat); + + valid_date = true; + + } catch(IndexOutOfBoundsException e) { + exceptionmsg = "Invalid Date Time String"; + return; + } catch (ParseException e) { + exceptionmsg = "Failed parsing Date Time"; + return; + } + + if (!valid_date) return; + + // Detector Address Part + this._detectorlocation = cmds[1].substring(1).trim(); + if (this._detectorlocation.isEmpty()) { + exceptionmsg = "Empty Detector Location"; + return; + } + + // Detector Status Part + _detectorstatus = cmds[2].trim(); + if (this._detectorstatus.isEmpty()) { + exceptionmsg = "Empty Detector Status"; + return; + } + + valid_data = true; + + } else exceptionmsg = "Invalid Detector Status Part"; + } else exceptionmsg = "Invalid Detector Address Part"; + } else exceptionmsg = "Invalid Date Time Part"; + } else exceptionmsg = "Regex is not 3"; + } + + } + + /** + * Print original protocol string to byte string for ASCII examination + * @param mode : 0 = desimal, 1 = hexadesimal + * @return "null" if data is null, "0" if data length 0, or data bytes separated with space(32) + */ + public String GetByteString(int mode) { + if (datastring==null) { + return "null"; + } else if (datastring.isEmpty()) { + return "0"; + } else { + byte[] datanya = datastring.getBytes(); + StringBuilder str = new StringBuilder(); + for(byte xx : datanya) { + if (str.length()>0) str.append(" "); + if (mode>0) + str.append(Bit.ToHexString(Bit.And(xx, 0xFF))); + else + str.append(Bit.And(xx, 0xFF)); + } + return str.toString(); + } + } + + /** + * Get Original Protocol String + * @return protocol string + */ + public String GetOriginalProtocolString() { + return datastring; + } + + /** + * if IsValid() return false, check Exception message here + * @return exception message if available, or null if not available + */ + public String getExceptionMessage() { + return exceptionmsg; + } + + /** + * Get Date Received + * @return if not valid, return empty string + */ + public String getDateReceived() { + if (datetimetick>0) return DateTime.Date(datetimetick); else return ""; + } + + /** + * Get Time Received + * @return if not valid, return empty string + */ + public String getTimeReceived() { + if (datetimetick>0) return DateTime.Time(datetimetick); else return ""; + } + + /** + * Get Detector Location + * Detector Location is device(FA sensor) identification + * @return if not valid, return empty string + */ + public String getDetectorLocation() { + return _detectorlocation; + } + + /** + * Get Detector Status + * status = + * T1* --> Detector have trouble + * T1- --> Trouble have been acknowledged + * T0- --> Trouble have been solved + * F1* --> Fire detected + * F1- --> Fire have been acknowledged + * F0- --> Fire have been solved + * @return if not valid, return empty string + */ + public String getDetectorStatus() { + return _detectorstatus; + } +} diff --git a/src/devices/IntPointer.java b/src/devices/IntPointer.java new file mode 100644 index 0000000..a8dd10b --- /dev/null +++ b/src/devices/IntPointer.java @@ -0,0 +1,16 @@ +package devices; + +import com.sun.jna.ptr.IntByReference; + +import anywheresoftware.b4a.keywords.Bit; + +public class IntPointer extends IntByReference { + + public IntPointer() { + super(); + } + + public String toHex() { + return Bit.ToHexString(super.getValue()); + } +} diff --git a/src/devices/JSecureDongleV2.java b/src/devices/JSecureDongleV2.java new file mode 100644 index 0000000..b500435 --- /dev/null +++ b/src/devices/JSecureDongleV2.java @@ -0,0 +1,574 @@ +package devices; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.ShortByReference; + +import anywheresoftware.b4a.BA; +import jGPIO.mycodes; + +@BA.Events(values= { + "log(msg as string)", + "finddongle(success as boolean , harwareID as int, message as string)", + "opendongle(success as boolean, hardwareID as int, SessionHandle as int, message as string)", + "closedongle(success as boolean, SessionHandle as int, message as string)", + "readuserdatazone(success as boolean, SessionHandle as int, data() as byte, message as string)", + "writeuserdatazone(success as boolean, SessionHandle as int, message as string)", + "readuserid(success as boolean, SessionHandle as int, UserID as int, message as string)", + "writeuserid(success as boolean, SessionHandle as int, message as string)" +}) +@BA.ShortName("JSecureDongleV2") +public class JSecureDongleV2 { + private final int SD_FIND = 1;//Find Dongle + private final int SD_FIND_NEXT = 2;//Find Next Dongle + private final int SD_OPEN = 3;//Open Dongle + private final int SD_CLOSE = 4;//Close Dongle + private final int SD_READ = 5 ; //Read Dongle + private final int SD_WRITE = 6 ; //Write Dongle + private final int SD_RANDOM = 7;//Generate Random Number + private final int SD_SEED = 8;//Generate Seed Code + private final int SD_WRITE_USERID=9;//Write User ID + private final int SD_READ_USERID= 10;//Read User ID + private final int SD_SET_MODULE= 11;//Set Module + private final int SD_CHECK_MODULE=12;//Check Module + private final int SD_WRITE_ARITHMETIC=13;//Write Algorithm + private final int SD_CALCULATE1= 14;//Calculate 1 + private final int SD_CALCULATE2= 15;//Calculate 2 + private final int SD_CALCULATE3= 16;//Calculate 3 + private final int SD_CALCULATE4= 18;//Calculate 4 + private final int SD_CALCULATE5= 19;//Calculate 5 + private final int SD_DECREASE= 17;//Decrease Module Unit + + private final int SD_SETPASSWORDID =0xf0; + private final int SD_AGENTBURN =0xf3; + private final int SD_GETVERSION=0xf7; + + private final int SD_SET_COUNTER=20;//Set Counter + private final int SD_GET_COUNTER= 21;//Get Counter + private final int SD_DEC_COUNTER=22; + private final int SD_SET_TIMER=23;//Set Timer + private final int SD_GET_TIMER= 24;//Get Timer + private final int SD_ADJUST_TIMER=25; //Adjust System Clock + private final int SD_SET_TIMER_ITV=26; + private final int SD_GET_TIMER_ITV=27; + private final int SD_DEC_TIMER=28; + private final int SD_SET_RSAKEY_N=29;//Write N into RSA keypair + private final int SD_SET_RSAKEY_D=30;//Write D into RSA keypair + private final int SD_UPDATE_GEN_HEADER= 31;//Generate header of cipher-text file + private final int SD_UPDATE_GEN=32;//Generate cipher-text file + private final int SD_UPDATE_CHECK=33;//Check and Upgrade cipher-text file + private final int SD_UPDATE=34;//Upgrade cipher-text file + private final int SD_UNPACK= 35; + + + private final int SD_FREEEPROM= 89; + + // private final int SD_TESTXU= 0xA1 //jw test cos + + //private final int SD_GET_DES_KEY==41 + private final int SD_SET_DES_KEY= 41;//Set DES Key + private final int SD_DES_ENC=42;//DES Encryption + private final int SD_DES_DEC=43;//DES Decryption + private final int SD_RSA_ENC=44;//RSA Encryption= + private final int SD_RSA_DEC=45;//RSA Decryption + + private final int SD_READ_EX =46; + private final int SD_WRITE_EX=47; + + private final int SD_SET_COUNTER_EX=0xA0; //Set value to Count Unit (DWORD) + private final int SD_GET_COUNTER_EX=0xA1; //Get value from Count Unit (DWORD) + private final int SD_SET_TIMER_EX= 0xA2; //Set value to Time Unit (DWORD) + private final int SD_GET_TIMER_EX= 0xA3; //Get value from Time Unit (DWORD) + private final int SD_ADJUST_TIMER_EX=0xA4; //Adjust Timer in Dongle + + private final int SD_UPDATE_GEN_EX=0xA6; //Generate Update File + + private final int SD_UPDATE_EX= 0xA8; //Update Dongle + private final int SD_SET_UPDATE_KEY=0xA9; //Set RSA Keys for Generate Update File + private final int SD_ADD_UPDATE_HEADER= 0xAA; //Fill in the content of Update File Head + private final int SD_ADD_UPDATE_CONTENT= 0xAB; //Fill in the content of Update File Body + private final int SD_GET_TIME_DWORD=0xAC; //Translate Time to DWORD value (the minutes from 2006.1.1) + + private final int SD_VERSION= 100; //Get COS Version + + //add by bgc on 09/2006 + private final int DES_SINGLE_MODE=0; + private final int DES_TRIPLE_MODE=1; + + private final int RSA_PRIVATE_KEY=0; + private final int RSA_PUBLIC_KEY=1; + + private final int RSA_DONGLE_PADDING=0; + private final int RSA_USER_PADDING=1; + //end + + private String GetErrorMessage(int code) { + switch(code) { + case ERR_SUCCESS : + return "Success"; + case ERR_NO_DONGLE : + return "No Driver installed"; + case ERR_INVALID_PASSWORD : + return "Found Dongle, but basic passwords (pass1, pass2) are wrong"; + case ERR_INVALID_PASSWORD_OR_ID : + return "Wrong password or Hardware ID"; + case ERR_SETID : + return "Error in Hardware ID setting"; + case ERR_INVALID_ADDR_OR_SIZE : + return "Error in read/write address or length"; + case ERR_UNKNOWN_COMMAND : + return "No such command"; + case ERR_NOTBELEVEL3 : + return "Internal Error"; + case ERR_READ : + return "Read Error"; + case ERR_WRITE : + return "Write Error"; + case ERR_RANDOM : + return "Random Number Error"; + case ERR_SEED : + return "Seed Code Error"; + case ERR_CALCULATE : + return "Error in Calculation"; + case ERR_NO_OPEN : + return "Unopen dongle before operation"; + case ERR_OPEN_OVERFLOW : + return "Too many open dongles (>16)"; + case ERR_NOMORE : + return "No more dongles"; + case ERR_NEED_FIND : + return "No 'Find' function called before using 'FindNext'"; + case ERR_DECREASE : + return "Decrease Error"; + case ERR_AR_BADCOMMAND : + return "Error in Arithmetic Instruction"; + case ERR_AR_UNKNOWN_OPCODE : + return "Error in Arithmetic Operator"; + case ERR_AR_WRONGBEGIN : + return "Constant cannot be used in the first arithmetic instruction"; + case ERR_AR_WRONG_END : + return "Constant cannot be used in the last arithmetic instruction"; + case ERR_AR_VALUEOVERFLOW : + return "The value of Constant cannot be greater than 63"; + case ERR_TOOMUCHTHREAD : + return "The threads opened in the same process cannot be greater than 100"; + case ERR_SET_DES_KEY : + return "Set DES Key Error"; + case ERR_DES_ENCRYPT : + return "DES Encryption Error"; + case ERR_DES_DECRYPT : + return "DES Decryption Error"; + case ERR_SET_RSAKEY_N : + return "Error in writing N into RSA keypair"; + case ERR_SET_RSAKEY_D : + return "Error in writing D into RSA keypair"; + case ERR_RSA_ENCRYPT : + return "RSA Encryption Error"; + case ERR_RSA_DECRYPT : + return "RSA Decryption Error"; + case ERR_INVALID_LENGTH : + return "Invalid data length"; + case ERR_RECEIVE_NULL : + return "Receive nothing"; + case ERR_UNKNOWN_SYSTEM : + return "Unknown Operating System"; + case ERROR_UNINIT_TIME_UNIT : + return "Time uint uninitialized"; + case ERR_UNKNOWN : + return "Unknown Error"; + default : + return "Misterious Error, code = "+String.valueOf(code); + } + } + // Error Codes + private final int ERR_SUCCESS = 0; //Success + private final int ERR_NO_DONGLE = 3; // No driver installed + private final int ERR_INVALID_PASSWORD = 4; //Found Dongle, but basic passwords (pass1, pass2) are wrong + private final int ERR_INVALID_PASSWORD_OR_ID = 5; //Wrong password or Hardware ID + private final int ERR_SETID=6; //Error in Hardware ID setting + private final int ERR_INVALID_ADDR_OR_SIZE = 7; //Error in read/write address or length + private final int ERR_UNKNOWN_COMMAND=8; //No such command + private final int ERR_NOTBELEVEL3= 9; //Internal Error + private final int ERR_READ=10; //Read Error + private final int ERR_WRITE=11; //Write Error + private final int ERR_RANDOM=12; //Random Number Error + private final int ERR_SEED=13; //Seed Code Error + private final int ERR_CALCULATE=14; //Error in Calculation + private final int ERR_NO_OPEN= 15; //Unopen dongle before operation + private final int ERR_OPEN_OVERFLOW=16; //Too many open dongles (>16) + private final int ERR_NOMORE=17; //No more dongles + private final int ERR_NEED_FIND=18; //No "Find" function called before using "FindNext" + private final int ERR_DECREASE=19; //Decrease Error + private final int ERR_AR_BADCOMMAND=20; //Error in Arithmetic Instruction + private final int ERR_AR_UNKNOWN_OPCODE=21; //Error in Arithmetic Operator + private final int ERR_AR_WRONGBEGIN=22; //Constant cannot be used in the first arithmetic instruction + private final int ERR_AR_WRONG_END=23; //Constant cannot be used in the last arithmetic instruction + private final int ERR_AR_VALUEOVERFLOW=24; //The value of Constant cannot be greater than 63 + private final int ERR_TOOMUCHTHREAD=25; //The threads opened in the same process cannot be greater than 100 + + private final int ERR_INVALID_SD=30; //Try to handle non-Securedongle + private final int ERR_INVALID_PARAMETER= 31 ;//Invalid parameter + private final int ERR_INVALID_TIMEVALUE= 32; //Invalid Date-Time value + + //Set DES Key Error + private final int ERR_SET_DES_KEY= 40; + + //DES Encryption Error + private final int ERR_DES_ENCRYPT= 41; + + //DES Decryption Error + private final int ERR_DES_DECRYPT= 42; + + //Error in writing N into RSA keypair + private final int ERR_SET_RSAKEY_N=43; + + //Error in writing D into RSA keypair + private final int ERR_SET_RSAKEY_D=44; + + //RSA Encryption Error + private final int ERR_RSA_ENCRYPT= 45; + + //RSA Decryption Error + private final int ERR_RSA_DECRYPT= 46; + + //Invalid data length + private final int ERR_INVALID_LENGTH=47; + + private final int ERR_RECEIVE_NULL=0x100; //Receive nothing + private final int ERR_UNKNOWN_SYSTEM=0x102; //Unknown Operating System + private final int ERROR_UNINIT_TIME_UNIT = 0x103; //Time uint uninitialized + private final int ERR_UNKNOWN= 0xffff; //Unknown Error + + private final int ERR_XUTEST=0xAA; + + native short SecureDongle(short function, ShortByReference handle, IntByReference lp1, IntByReference lp2, ShortByReference p1, ShortByReference p2, ShortByReference p3, ShortByReference p4, Memory buffer); + + static { + try { + Native.register(JSecureDongleV2.class,"SecureDongle"); + System.out.println("SecureDongle library loaded"); + } catch(Exception e) { + System.out.println("Unable to register SecureDongle Library"); + } + + } + + + /** + * Secure Dongle session address + * 16 bit pointer + */ + ShortPointer handle; + /** + * Parameter 1 + * 16 bit pointer + */ + ShortPointer p1; + /** + * Parameter 2 + * 16 bit pointer + */ + ShortPointer p2; + /** + * Parameter 3 + * 16 bit pointer + */ + ShortPointer p3; + /** + * Parameter 4 + * 16 bit pointer + */ + ShortPointer p4; + /** + * Return Code + * 16 bit value + */ + Short retcode; + /** + * Long Parameter 1 + * 32 bit pointer + */ + IntPointer lp1; + /** + * Long Parameter 2 + * 32 bit pointer + */ + IntPointer lp2; + /** + * Byte array buffer + * 1024 bytes 8 bit + */ + Memory buffer; + + /** + * Single thread executor untuk run function secure dongle + */ + private ExecutorService exec ; + + public JSecureDongleV2() { + buffer = new Memory(1024); + lp1 = new IntPointer(); + lp2 = new IntPointer(); + p1 = new ShortPointer(); + p2 = new ShortPointer(); + p3 = new ShortPointer(); + p4 = new ShortPointer(); + handle = new ShortPointer(); + exec = Executors.newSingleThreadExecutor(); + } + + private BA bax; + private String event; + private Object Me; + + + private boolean need_log_event = false; + private boolean need_finddongle_event = false; + private boolean need_opendongle_event = false; + private boolean need_closedongle_event = false; + private boolean need_readuserdatazone_event = false; + private boolean need_writeuserdatazone_event = false; + private boolean need_readuserid_event = false; + private boolean need_writeuserid_event = false; + + /** + * Initialize JSecureDongleV2 + * @param eventname Eventname + */ + public void Initialize(BA ba, String eventname) { + this.bax = ba; + this.event = eventname; + Me = this; + check_events(); + } + + private void check_events() { + if (bax!=null) { + if (mycodes.ValidString(event)) { + need_log_event = bax.subExists(event+"_log"); + need_finddongle_event = bax.subExists(event+"_finddongle"); + need_opendongle_event = bax.subExists(event+"_opendongle"); + need_closedongle_event =bax.subExists(event+"_closedongle"); + need_readuserdatazone_event = bax.subExists(event+"_readuserdatazone"); + need_writeuserdatazone_event = bax.subExists(event+"_writeuserdatazone"); + need_readuserid_event = bax.subExists(event+"_readuserid"); + need_writeuserid_event = bax.subExists(event+"_writeuserid"); + } + } + } + + /** + * Check if a specific SecureDongle is attached to USB port + * will raise event finddongle + * @param password1 Basic Password 1 + * @param password2 Basic Password 2 + * @param password3 Advanced Password 1 (optional) + * @param password4 Advanced Password 2 (optional) + */ + public void Find_SecureDongle(int password1, int password2, int password3, int password4) { + p1.setValue(ShortPointer.ShortValue_FromInt(password1)); + p2.setValue(ShortPointer.ShortValue_FromInt(password2)); + p3.setValue(ShortPointer.ShortValue_FromInt(password3)); + p4.setValue(ShortPointer.ShortValue_FromInt(password4)); + exec.submit(()->{ + retcode = this.SecureDongle(ShortPointer.ShortValue_FromInt(SD_FIND), handle, lp1, lp2, p1, p2, p3, p4, buffer); + raise_finddongle( + retcode==0 ? true : false, + retcode==0 ? lp1.getValue() : 0, + GetErrorMessage(retcode) + ); + }); + } + + /** + * To check if another specific SecureDongle is attached to USB port + * Must call Find_SecureDongle first before calling this function + * will raise event finddongle + * @param password1 Basic Password 1 + * @param password2 Basic Password 2 + * @param password3 Advanced Password 1 (optional) + * @param password4 Advanced Password 2 (optional) + */ + public void Find_Next_SecureDongle(int password1, int password2, int password3, int password4) { + p1.setValue(ShortPointer.ShortValue_FromInt(password1)); + p2.setValue(ShortPointer.ShortValue_FromInt(password2)); + p3.setValue(ShortPointer.ShortValue_FromInt(password3)); + p4.setValue(ShortPointer.ShortValue_FromInt(password4)); + exec.submit(()->{ + retcode = this.SecureDongle(ShortPointer.ShortValue_FromInt(SD_FIND_NEXT), handle, lp1, lp2, p1, p2, p3, p4, buffer); + raise_finddongle( + retcode==0 ? true : false, + retcode==0 ? lp1.getValue() : 0, + GetErrorMessage(retcode) + ); + }); + } + + /** + * Open SecureDongle with specific password or HardwareID + * will raise event opendongle + * @param password1 Basic Password 1 + * @param password2 Basic Password 2 + * @param password3 Advanced Password 1 (optional) + * @param password4 Advanced Password 2 (optional) + * @param HardwareID Hardware ID + */ + public void Open_SecureDongle(int password1, int password2, int password3, int password4, final int HardwareID) { + p1.setValue(ShortPointer.ShortValue_FromInt(password1)); + p2.setValue(ShortPointer.ShortValue_FromInt(password2)); + p3.setValue(ShortPointer.ShortValue_FromInt(password3)); + p4.setValue(ShortPointer.ShortValue_FromInt(password4)); + lp1.setValue(HardwareID); + exec.submit(()->{ + retcode = this.SecureDongle(ShortPointer.ShortValue_FromInt(SD_OPEN), handle, lp1, lp2, p1, p2, p3, p4, buffer); + raise_opendongle( + retcode==0 ? true : false, + HardwareID, + retcode==0 ? handle.toInt() : 0, + GetErrorMessage(retcode) + ); + + }); + } + + /** + * Close SecureDongle with specific Handle + * will raise event closedongle + * @param Handle Session Handle + */ + public void Close_SecureDongle(final int Handle) { + handle.setValue(ShortPointer.ShortValue_FromInt(Handle)); + exec.submit(()->{ + retcode = this.SecureDongle(ShortPointer.ShortValue_FromInt(SD_CLOSE), handle, lp1, lp2, p1, p2, p3, p4, buffer); + raise_closedongle( + retcode==0 ? true : false, + Handle, + GetErrorMessage(retcode) + ); + }); + + } + + /** + * Read content from User Data Zone + * will raise event readuserdatazone + * @param Handle Session Handle + * @param offset from zero (0) + * @param length max 1000 bytes + */ + public void Read_UserDataZone(final int Handle,final int offset, final int length) { + + handle.setValue(ShortPointer.ShortValue_FromInt(Handle)); + p1.setValue(ShortPointer.ShortValue_FromInt(offset)); + p2.setValue(ShortPointer.ShortValue_FromInt(length)); + buffer.clear(); + + exec.submit(()->{ + retcode = this.SecureDongle(ShortPointer.ShortValue_FromInt(SD_READ), handle, lp1, lp2, p1, p2, p3, p4, buffer); + raise_readuserdatazone( + retcode==0 ? true : false, + Handle, + retcode==0 ? buffer.getByteArray(offset, length) : null, + GetErrorMessage(retcode) + ); + }); + + } + + /** + * Write content to User Data Zone + * will raise event writeuserdatazone + * @param Handle Session Handle + * @param offset offset of User Data Zone + * @param length length of content to be written + * @param content content to write + */ + public void Write_UserDataZone(final int Handle, final int offset, final int length, final byte[] content) { + handle.setValue(ShortPointer.ShortValue_FromInt(Handle)); + p1.setValue(ShortPointer.ShortValue_FromInt(offset)); + p2.setValue(ShortPointer.ShortValue_FromInt(length)); + buffer.clear(); + buffer.write(0, content, 0, length); + exec.submit(()->{ + retcode = this.SecureDongle(ShortPointer.ShortValue_FromInt(SD_WRITE), handle, lp1, lp2, p1, p2, p3, p4, buffer); + raise_writeuserdatazone( + retcode==0 ? true : false, + Handle, + GetErrorMessage(retcode) + ); + }); + } + + /** + * Write UserID + * will raise event writeuserid + * @param Handle Session Handle + * @param UserID value of User ID + */ + public void Write_UserID(final int Handle, int UserID) { + handle.setValue(ShortPointer.ShortValue_FromInt(Handle)); + lp1.setValue(UserID); + exec.submit(()->{ + retcode = this.SecureDongle(ShortPointer.ShortValue_FromInt(SD_WRITE_USERID), handle, lp1, lp2, p1, p2, p3, p4, buffer); + raise_writeuserid( + retcode==0 ? true : false, + Handle, + GetErrorMessage(retcode) + ); + }); + } + + /** + * Read UserID + * will raise event readuserid + * @param Handle Session Handle + */ + public void Read_UserID(final int Handle) { + handle.setValue(ShortPointer.ShortValue_FromInt(Handle)); + exec.submit(()->{ + retcode = this.SecureDongle(ShortPointer.ShortValue_FromInt(SD_READ_USERID), handle, lp1, lp2, p1, p2, p3, p4, buffer); + raise_readuserid( + retcode==0 ? true : false, + Handle, + retcode==0 ? lp1.getValue() : 0, + GetErrorMessage(retcode) + ); + }); + } + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_finddongle(boolean success, int HardwareID, String message ) { + if (need_finddongle_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_finddongle", false, new Object[] {success, HardwareID, message}); + } + + private void raise_opendongle(boolean success, int HardwareID, int SessionHandle, String message) { + if (need_opendongle_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_opendongle", false, new Object[] {success, HardwareID, SessionHandle, message}); + } + + private void raise_closedongle(boolean success, int SessionHandle, String message) { + if (need_closedongle_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_closedongle", false, new Object[] {success, SessionHandle, message}); + } + + private void raise_readuserdatazone(boolean success, int SessionHandle, byte[] data, String message) { + if (need_readuserdatazone_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_readuserdatazone", false, new Object[] {success, SessionHandle, data, message}); + } + + private void raise_writeuserdatazone(boolean success, int SessionHandle, String message) { + if (need_writeuserdatazone_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_writeuserdatazone", false, new Object[] {success, SessionHandle, message}); + } + + private void raise_readuserid(boolean success, int SessionHandle, int UserID, String message) { + if (need_readuserid_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_readuserid", false, new Object[] {success, SessionHandle, UserID, message}); + } + + private void raise_writeuserid(boolean success, int SessionHandle, String message) { + if (need_writeuserid_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_writeuserid", false, new Object[] {success, SessionHandle, message}); + } +} diff --git a/src/devices/Kernel32.java b/src/devices/Kernel32.java new file mode 100644 index 0000000..103772d --- /dev/null +++ b/src/devices/Kernel32.java @@ -0,0 +1,118 @@ +package devices; + +import com.sun.jna.Native; +import com.sun.jna.Structure; +import com.sun.jna.win32.StdCallLibrary; +import anywheresoftware.b4a.BA; + +public interface Kernel32 extends StdCallLibrary { + public Kernel32 INSTANCE = (Kernel32) Native.load("Kernel32", Kernel32.class); + + /** + * Fill the structure. + */ + public int GetSystemPowerStatus(SYSTEM_POWER_STATUS result); + + public class SYSTEM_POWER_STATUS extends Structure{ + public byte ACLineStatus; + public byte BatteryFlag; + public byte BatteryLifePercent; + public byte Reserved1; + public int BatteryLifeTime; + public int BatteryFullLifeTime; + + @BA.Hide + public void autoRead() { + if (getAutoRead()) { + read(); + } + } + + @BA.Hide + public void autoWrite() { + if (getAutoWrite()) { + write(); + } + } + + @Override + protected java.util.List getFieldOrder() { + return java.util.Arrays.asList(new String[] { "ACLineStatus", "BatteryFlag", "BatteryLifePercent", "Reserved1", + "BatteryLifeTime", "BatteryFullLifeTime" }); + } + + /** + * Get AC power status + * @return Offline, Online or Unknown + */ + public String GetACLineStatusString() { + switch (ACLineStatus) { + case 0: + return "Offline"; + case 1: + return "Online"; + default: + return "Unknown"; + } + } + + /** + * Get Battery Flag Status + * @return High, Low, Critical, Charging, No system battery or Unknown + */ + public String GetBatteryFlagString() { + switch (BatteryFlag) { + case 1: + return "High"; + case 2: + return "Low"; + case 4: + return "Critical"; + case 8: + return "Charging"; + case (byte)128: + return "No system battery"; + default: + return "Unknown"; + } + } + + /** + * Get Battery Life Percent + * + * @return Battery Life Percent or Unknown + */ + public String GetBatteryLifePercentString() { + return (BatteryLifePercent==(byte)255) ? "Unknown" : String.valueOf(BatteryLifePercent)+" %"; + } + + /** + * Get Battery Life Time + * @return Battery Life Time in seconds or Unknown + */ + public String GetBatteryLifeTimeString() { + return (BatteryLifeTime == (int) -1) ? "Unknown" : String.valueOf(BatteryLifeTime)+ " seconds"; + } + + /** + * Get Battery Full Life Time + * + * @return Battery Full Life Time in seconds or Unknown + */ + public String GetBatteryFullLifeTimeString() { + return (BatteryFullLifeTime == (int) -1) ? "Unknown" : String.valueOf(BatteryFullLifeTime) + " seconds"; + } + + @Override + /** + * Get all information + * + */ + public String toString() { + return "ACLineStatus: " + GetACLineStatusString() + "\n" + "BatteryFlag: " + GetBatteryFlagString() + "\n" + + "BatteryLifePercent: " + GetBatteryLifePercentString() + "\n" + "BatteryLifeTime: " + + GetBatteryLifeTimeString() + "\n" + "BatteryFullLifeTime: " + GetBatteryFullLifeTimeString(); + } + + } +} diff --git a/src/devices/LCDDotMatrix/LCD.java b/src/devices/LCDDotMatrix/LCD.java new file mode 100644 index 0000000..d53bf1e --- /dev/null +++ b/src/devices/LCDDotMatrix/LCD.java @@ -0,0 +1,574 @@ +package devices.LCDDotMatrix; + +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import anywheresoftware.b4a.BA; +import devices.PCF8574; +import jGPIO.jGPIO; + +@BA.ShortName("LCD") +@BA.Events(values= { + "log(msg as string)", + "lcdupdated(row as int, msg as string)" +}) + +public class LCD implements LCDStringEvent { + + private BA ba; + private Object myobject; + private String event; + private boolean need_log_event = false; + private boolean need_lcdupdated_event = false; + + private int rowcount = 2; // default value + private int columncount = 16; // default value + private PCF8574 pcf; + private boolean opened = false; + private LCDString[] messages; + private int menuindex = 0; + + private final int BL_Pin = 8; + private final int RS_pin = 1; + private final int RW_pin= 2; + private final int EN_pin = 4; + private final int D7_pin = 128; + private final int D6_pin = 64; + private final int D5_pin = 32; + private final int D4_pin = 16; + private int bufdata = 0; + + private int backlightcounter = 0; + private Timer backlighttimer ; + private int backlightdefault = 60; + + + ExecutorService executor = Executors.newSingleThreadExecutor(); + + public LCD() { + if (jGPIO.osname=="") jGPIO.detectOS(); + } + /** + * Initialize LCD object in B4J + * @param callerobject : caller object + * @param Eventname : eventname + */ + public void Initialize(BA bax, Object callerobject, String Eventname) { + ba = bax; + myobject = callerobject; + event = Eventname.trim(); + + if (ba instanceof BA) { + if (myobject!=null) { + if (!event.isEmpty()) { + need_log_event = ba.subExists(event+"_log"); + need_lcdupdated_event = ba.subExists(event+"_lcdupdated"); + } + } + } + + // auto close kalau java program closed + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + } + + /** + * Open LCD + * @param bus_name : i2c bus name + * @param slave_address : i2c address + * @param row : row count. default value = 2 + * @param column : column count. default value = 16 + * @return true if opened + */ + public boolean Open(String bus_name, int slave_address, int row, int column) { + if (!jGPIO.IsLinux) return false; + opened = false; + if (row>0) rowcount = row; + if (column>0) columncount = column; + pcf = new PCF8574(); + //pcf.Initialize(ba, caller, eventname); No need to call this, only for B4J + opened = pcf.Open(bus_name, slave_address); + if (opened) { + messages = new LCDString[row]; + for(int ii=0;ii0) { + backlightcounter -= 1; + if (backlightcounter==0) { + BackLight(false); // turn off backlight + } + } + + } + + }, 1000, 1000); + + } + + return opened; + } + + /** + * Close LCD + */ + public void Close() { + + //if (executor instanceof ExecutorService) { + // executor.shutdownNow(); + //} + + if (messages!=null) { + for(int ii=0;ii messages.length-1) return false; + menuindex = menutag; + messages[row].UpdateContent(msg, menutag); + return true; + } + + /** + * Set LCD Content, and crop the message if length more than colum size + * @param row : row index, start from 0 + * @param msg : Message to display + * @param menutag : menuindex which this message belong to + * @return true if content stored + */ + public boolean SetContentCrop(int row, String msg, int menutag) { + if (messages != null) { + if (row < messages.length) { + menuindex = menutag; + if (msg.length()>columncount) { + msg = msg.substring(0,columncount); + } + messages[row].UpdateContent(msg, menutag); + return true; + } + } + return false; + } + + /** + * Get current LCD row content + * @param row : row index, start from 0 + * @return empty string if not found + */ + public String GetContent(int row) { + if (messages!=null) { + if (row < messages.length) { + return messages[row].CurrentContent(); + } + } + return ""; + } + + /** + * Compare content with row content, and return true if same value + * @param row : row index, start from 0 + * @param content : content to compare with internal row content + * @return true if the same (case sensitive) + */ + public boolean CompareContent(int row, String content) { + if (messages!=null) { + if (row0) return true; else return false; + } + + /** + * Get / Set Default Backlight timeout , in seconds + */ + public int getBackLightInterval() { + return backlightdefault; + } + + public void setBackLightInterval(int value) { + backlightdefault = value; + } + + private void BitSet(int pin) { + bufdata |= pin; + } + + private void BitReset(int pin) { + bufdata &= ~pin; + } + + private void LCD_gotoxy(int x, int y) { + RS(false); + switch(y) { + case 0 : + LCD_write(0x80+x); + break; + case 1 : + LCD_write(0xC0 + x ); + break; + case 2 : + LCD_write(0x94 + x ); + break; + default : + LCD_write(0xD4 + x ); + break; + } + + + + } + + private void LCD_write(int bx) { + EN(false); + Nibble((bx & 0x80)>0, (bx & 0x40)>0, (bx & 0x20)>0, (bx & 0x10)>0); + EN(true); + EN(false); + Nibble((bx & 0x08)>0, (bx & 0x04)>0, (bx & 0x02)>0, (bx & 0x01)>0); + EN(true); + EN(false); + + } + + private void Nibble(boolean D7 , boolean D6 , boolean D5 , boolean D4 ) { + if (D7) BitSet(D7_pin); else BitReset(D7_pin); + if (D6) BitSet(D6_pin); else BitReset(D6_pin); + if (D5) BitSet(D5_pin); else BitReset(D5_pin); + if (D4) BitSet(D4_pin); else BitReset(D4_pin); + PCFWrite((byte) bufdata); + + } + + private void RW(boolean isON) { + if (isON) { + BitSet(RW_pin); + } else { + BitReset(RW_pin); + } + PCFWrite((byte) bufdata); + + } + + + + private void RS(boolean isON) { + if (isON) { + BitSet(RS_pin); + } else { + BitReset(RS_pin); + } + PCFWrite((byte) bufdata); + + } + + private void EN(boolean isON) { + if (isON) { + BitSet(EN_pin); + } else { + BitReset(EN_pin); + } + PCFWrite((byte) bufdata); + + } + + private void PCFWrite(byte bb) { + if (pcf instanceof PCF8574) { + pcf.Write(bb); // gak perlu cek isopen, dah dilakuin di dalam + } + } + + + + private void raise_log(String msg) { + if (need_log_event) { + ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg}); + } + } + + private void raise_lcdupdated(int row, String msg) { + if (need_lcdupdated_event) { + ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_lcdupdated", false, new Object[] {row,msg}); + } + } + + + @SuppressWarnings("unused") + private class LCD_UPDATE implements Runnable{ + private int row; + private String msg; + public LCD_UPDATE(int row, String msg) { + this.row = row; + this.msg = msg; + } + @Override + public void run() { + LCD_gotoxy(0,row); + RS(true); + + byte[] bbm = msg.getBytes(); + for(int ii=0;ii0) { + result+= message.charAt(startbaca); + count-= 1; + if (startbaca < (message.length()-1)) { + startbaca+=1; + } + else { + startbaca=0; + startindex = message.length(); // stop, dan mulai dari awal + break; + } + + + } + + + raise_update_lcd(result); + //BA.Log("LCD TimerTask row="+rowindex+", hasil="+result); + + if (startindex< (message.length()-1)) + startindex+=1; + else + startindex = 0; + } + } + }; + + // recreate lcdtimer + lcdtimer = new Timer(); + lcdtimer.scheduleAtFixedRate(lcdtask, 0, scrolltime); + //BA.Log("Message panjang "+msglen+" character, pakai timer"); + } + } + + /** + * Set Event handler for this object + * @param lcd_event : LCDStringEvent object + */ + public void SetJavaEvent(LCDStringEvent lcd_event) { + this.javaevent = lcd_event; + } + + private void raise_update_lcd(String msg) { + if (javaevent instanceof LCDStringEvent) { + javaevent.UpdateLCD(rowindex, msg, menutag); + } + } +} diff --git a/src/devices/LCDDotMatrix/LCDStringEvent.java b/src/devices/LCDDotMatrix/LCDStringEvent.java new file mode 100644 index 0000000..d1e7ce4 --- /dev/null +++ b/src/devices/LCDDotMatrix/LCDStringEvent.java @@ -0,0 +1,5 @@ +package devices.LCDDotMatrix; + +public interface LCDStringEvent { + void UpdateLCD(int row, String msg, int menutag); +} diff --git a/src/devices/LCDSPI/LcdSpi.java b/src/devices/LCDSPI/LcdSpi.java new file mode 100644 index 0000000..2e1a116 --- /dev/null +++ b/src/devices/LCDSPI/LcdSpi.java @@ -0,0 +1,451 @@ +package devices.LCDSPI; + +import anywheresoftware.b4a.BA; +import jGPIO.mycodes; + +//@BA.ShortName("LCDSPI") +@BA.Events(values= { + "log(msg as string)" +}) + +/** + * LCD SPI Low level + * Source : https://github.com/gitcnd/LCDWIKI_SPI + * @author rdkartono + * + */ +@SuppressWarnings("unused") +public class LcdSpi { + private BA ba; + private String event; + private Object Me; + private boolean need_log_event = false; + + public enum LCD_Mode_Identifiers{ + ILI9325("ILI9325",0), + ILI9328("ILI9328 ",1), + ILI9341("ILI9341 ",2), + HX8357D("HX8357D ",3), + HX8347G("HX8347G ",4), + HX8347I("HX8347I ",5), + ILI9486("ILI9486 ",6), + ST7735S("ST7735S ",7), + SSD1283A("SSD1283A ",8); + public final String label; + public final int mode_identifiers; + + private LCD_Mode_Identifiers(String label, int mode_identifier) { + this.label = label; + this.mode_identifiers = mode_identifier; + } + } + + public enum LCD_Identifiers{ + ID_932X("ID_932X",0), + ID_7575("ID_7575",1), + ID_9341("ID_9341",2), + ID_HX8357D("ID_HX8357D",3), + ID_4535("ID_4535",4), + ID_9486("ID_9486",5), + ID_7735("ID_7735",6), + ID_1283A("ID_1283A",7), + ID_UNKNOWN("ID_UNKNOWN",0xFF); + + public final String label; + public final int identifier; + private LCD_Identifiers(String label, int identifier) { + this.label = label; + this.identifier = identifier; + } + } + + protected short WIDTH, HEIGHT, width, height, rotation, lcd_driver, lcd_model; + protected boolean hw_spi=false; + + + private short XC, YC, CC, RC, SC1, SC2, MD, VL, R24BIT; + private volatile static byte spicsPort, spicdPort, spimisoPort, spimosiPort, spiclkPort; + private volatile static byte spicsPinSet, spicdPinSet, spimisoPinSet, spimosiPinSet, spiclkPinSet; + private volatile static byte spicsPinUnset, spicdPinUnset, spimisoPinUnset, spimosiPinUnset, spiclkPinUnset; + private volatile byte _cs, _cd, _miso, _mosi, _clk, _reset, _led; + + + /** + * Initialize SPI LCD + * @param event Event name + */ + public void Initialize(BA ba, String event) { + this.ba = ba; + this.event = event; + Me = this; + check_events(); + } + + void Init_LCD() { + //TODO lanjutin + } + + void reset() { + //TODO lanjutin + } + + void start(short ID) { + //TODO lanjutin + } + + void Draw_Pixel(short x, short y, int color) { + //TODO lanjutin + } + + void Spi_Write(byte data) { + //TODO lanjutin + } + + byte Spi_Read() { + //TODO lanjutin + return 0; + } + + void Write_Cmd(int cmd) { + //TODO lanjutin + } + + void Write_Data(int data) { + //TODO lanjutin + } + + void Write_Cmd_Data(int cmd, int data) { + //TODO lanjutin + } + + void init_table8(cmd_data[] table, int size) { + //TODO lanjutin + } + + void init_table16(cmd_data[] table, int size) { + //TODO lanjutin + } + + void Push_Command(byte cmd, byte[] block, int size) { + //TODO lanjutin + } + + short Color_To_565(byte R, byte G, byte B) { + //TODO lanjutin + return 0; + } + + short Read_ID() { + //TODO lanjutin + return 0; + } + + void Fill_Rect(int x, int y, int w, int h, int color) { + //TODO lanjutin + } + + void Set_Rotation(byte r) { + //TODO lanjutin + } + + byte Get_Rotation() { + //TODO lanjutin + return 0; + } + + void Invert_Display(boolean i) { + //TODO lanjutin + } + + int Read_Reg(int reg, byte index) { + //TODO lanjutin + return 0; + } + + int Read_GRAM(int x, int y, byte[] block, int w, int h) { + //TODO lanjutin + return 0; + } + + void Set_Addr_Window(int x1, int y1, int x2, int y2) { + //TODO lanjutin + + } + + void Push_Any_Color(short[] block, int n, boolean first, byte flags) { + //TODO lanjutin + } + + void Vert_Scroll(short top, short scrolllines, short offset) { + //TODO lanjutin + } + + short Get_Height() { + //TODO lanjutin + return 0; + } + + short Get_Width() { + //TODO lanjutin + return 0; + } + + void Set_LR() { + //TODO lanjutin + } + + void Let_control(boolean i) { + //TODO lanjutin + } + + + + @BA.ShortName("SPI_CMD_DATA") + class cmd_data{ + public int cmd; + public int data; + } + + + class lcd_info{ + short lcd_id; + int lcd_wid; + int lcd_heg; + } + + static class mcu_spi_magic{ + public static void CD_COMMAND() { + spicdPort &= spicdPinUnset; + } + public static void CD_DATA() { + spicdPort |= spicdPinSet; + } + + public static void CS_ACTIVE() { + spicsPort &= spicsPinUnset; + } + + public static void CS_IDLE() { + spicsPort |= spicsPinSet; + } + public static byte MISO_STATE() { + return (byte)(spimisoPort & spimisoPinSet); + } + public static void MOSI_LOW() { + spimosiPort &= spimosiPinUnset; + } + public static void MOSI_HIGH() { + spimosiPort |= spimosiPinSet; + } + + public static void CLK_LOW() { + spiclkPort &= spiclkPinUnset; + } + + public static void CLK_HIGH() { + spiclkPort |= spiclkPinSet; + } + + } + + static class lcd_spi_register{ + public final static int ILI932X_START_OSC = 0x00; + public final static int ILI932X_DRIV_OUT_CTRL = 0x01; + public final static int ILI932X_DRIV_WAV_CTRL = 0x02; + public final static int ILI932X_ENTRY_MOD = 0x03; + public final static int ILI932X_RESIZE_CTRL = 0x04; + public final static int ILI932X_DISP_CTRL1 = 0x07; + public final static int ILI932X_DISP_CTRL2 = 0x08; + public final static int ILI932X_DISP_CTRL3 = 0x09; + public final static int ILI932X_DISP_CTRL4 = 0x0A; + public final static int ILI932X_RGB_DISP_IF_CTRL1 = 0x0C; + public final static int ILI932X_FRM_MARKER_POS = 0x0D; + public final static int ILI932X_RGB_DISP_IF_CTRL2 = 0x0F; + public final static int ILI932X_POW_CTRL1 = 0x10; + public final static int ILI932X_POW_CTRL2 = 0x11; + public final static int ILI932X_POW_CTRL3 = 0x12; + public final static int ILI932X_POW_CTRL4 = 0x13; + public final static int ILI932X_GRAM_HOR_AD = 0x20; + public final static int ILI932X_GRAM_VER_AD = 0x21; + public final static int ILI932X_RW_GRAM = 0x22; + public final static int ILI932X_POW_CTRL7 = 0x29; + public final static int ILI932X_FRM_RATE_COL_CTRL = 0x2B; + public final static int ILI932X_GAMMA_CTRL1 = 0x30; + public final static int ILI932X_GAMMA_CTRL2 = 0x31; + public final static int ILI932X_GAMMA_CTRL3 = 0x32; + public final static int ILI932X_GAMMA_CTRL4 = 0x35; + public final static int ILI932X_GAMMA_CTRL5 = 0x36; + public final static int ILI932X_GAMMA_CTRL6 = 0x37; + public final static int ILI932X_GAMMA_CTRL7 = 0x38; + public final static int ILI932X_GAMMA_CTRL8 = 0x39; + public final static int ILI932X_GAMMA_CTRL9 = 0x3C; + public final static int ILI932X_GAMMA_CTRL10 = 0x3D; + public final static int ILI932X_HOR_START_AD = 0x50; + public final static int ILI932X_HOR_END_AD = 0x51; + public final static int ILI932X_VER_START_AD = 0x52; + public final static int ILI932X_VER_END_AD = 0x53; + public final static int ILI932X_GATE_SCAN_CTRL1 = 0x60; + public final static int ILI932X_GATE_SCAN_CTRL2 = 0x61; + public final static int ILI932X_GATE_SCAN_CTRL3 = 0x6A; + public final static int ILI932X_PART_IMG1_DISP_POS = 0x80; + public final static int ILI932X_PART_IMG1_START_AD = 0x81; + public final static int ILI932X_PART_IMG1_END_AD = 0x82; + public final static int ILI932X_PART_IMG2_DISP_POS = 0x83; + public final static int ILI932X_PART_IMG2_START_AD = 0x84; + public final static int ILI932X_PART_IMG2_END_AD = 0x85; + public final static int ILI932X_PANEL_IF_CTRL1 = 0x90; + public final static int ILI932X_PANEL_IF_CTRL2 = 0x92; + public final static int ILI932X_PANEL_IF_CTRL3 = 0x93; + public final static int ILI932X_PANEL_IF_CTRL4 = 0x95; + public final static int ILI932X_PANEL_IF_CTRL5 = 0x97; + public final static int ILI932X_PANEL_IF_CTRL6 = 0x98; + + public final static int HX8347G_COLADDRSTART_HI = 0x02; + public final static int HX8347G_COLADDRSTART_LO = 0x03; + public final static int HX8347G_COLADDREND_HI = 0x04; + public final static int HX8347G_COLADDREND_LO = 0x05; + public final static int HX8347G_ROWADDRSTART_HI = 0x06; + public final static int HX8347G_ROWADDRSTART_LO = 0x07; + public final static int HX8347G_ROWADDREND_HI = 0x08; + public final static int HX8347G_ROWADDREND_LO = 0x09; + public final static int HX8347G_MEMACCESS = 0x16; + + + + public final static int ILI9341_SOFTRESET = 0x01; + public final static int ILI9341_SLEEPIN = 0x10; + public final static int ILI9341_SLEEPOUT = 0x11; + public final static int ILI9341_NORMALDISP = 0x13; + public final static int ILI9341_INVERTOFF = 0x20; + public final static int ILI9341_INVERTON = 0x21; + public final static int ILI9341_GAMMASET = 0x26; + public final static int ILI9341_DISPLAYOFF = 0x28; + public final static int ILI9341_DISPLAYON = 0x29; + public final static int ILI9341_COLADDRSET = 0x2A; + public final static int ILI9341_PAGEADDRSET = 0x2B; + public final static int ILI9341_MEMORYWRITE = 0x2C; + public final static int ILI9341_MEMORYACCESS = 0x36; + public final static int ILI9341_PIXELFORMAT = 0x3A; + public final static int ILI9341_RGBSIGNAL = 0xB0; + public final static int ILI9341_FRAMECONTROL = 0xB1; + public final static int ILI9341_INVERSIONCONRTOL = 0xB4; + public final static int ILI9341_DISPLAYFUNC = 0xB6; + public final static int ILI9341_ENTRYMODE = 0xB7; + public final static int ILI9341_POWERCONTROL1 = 0xC0; + public final static int ILI9341_POWERCONTROL2 = 0xC1; + public final static int ILI9341_VCOMCONTROL1 = 0xC5; + public final static int ILI9341_VCOMCONTROL2 = 0xC7; + public final static int ILI9341_POWERCONTROLB = 0xCF; + public final static int ILI9341_POWERCONTROLA = 0xCB; + public final static int ILI9341_UNDEFINE0 = 0xE0; + public final static int ILI9341_UNDEFINE1 = 0xE1; + public final static int ILI9341_DRIVERTIMINGA = 0xE8; + public final static int ILI9341_DRIVERTIMINGB = 0xEA; + public final static int ILI9341_POWERONSEQ = 0xED; + public final static int ILI9341_ENABLE3G = 0xF2; + public final static int ILI9341_INTERFACECONTROL = 0xF6; + public final static int ILI9341_RUMPRATIO = 0xF7; + public final static int ILI9341_MEMCONTROL = 0x36; + public final static int ILI9341_MADCTL = 0x36; + + public final static int ILI9341_MADCTL_MY = 0x80; + public final static int ILI9341_MADCTL_MX = 0x40; + public final static int ILI9341_MADCTL_MV = 0x20; + public final static int ILI9341_MADCTL_ML = 0x10; + public final static int ILI9341_MADCTL_RGB = 0x00; + public final static int ILI9341_MADCTL_BGR = 0x08; + public final static int ILI9341_MADCTL_MH = 0x04; + + + + public final static int HX8357_NOP = 0x00; + public final static int HX8357_SWRESET = 0x01; + public final static int HX8357_RDDID = 0x04; + public final static int HX8357_RDDST = 0x09; + + public final static int HX8357B_RDPOWMODE = 0x0A; + public final static int HX8357B_RDMADCTL = 0x0B; + public final static int HX8357B_RDCOLMOD = 0x0C; + public final static int HX8357B_RDDIM = 0x0D; + public final static int HX8357B_RDDSDR = 0x0F; + + public final static int HX8357_SLPIN = 0x10; + public final static int HX8357_SLPOUT = 0x11; + public final static int HX8357B_PTLON = 0x12; + public final static int HX8357B_NORON = 0x13; + + public final static int HX8357_INVOFF = 0x20; + public final static int HX8357_INVON = 0x21; + public final static int HX8357_DISPOFF = 0x28; + public final static int HX8357_DISPON = 0x29; + + public final static int HX8357_CASET = 0x2A; + public final static int HX8357_PASET = 0x2B; + public final static int HX8357_RAMWR = 0x2C; + public final static int HX8357_RAMRD = 0x2E; + + public final static int HX8357B_PTLAR = 0x30; + public final static int HX8357_TEON = 0x35; + public final static int HX8357_TEARLINE = 0x44; + public final static int HX8357_MADCTL = 0x36; + public final static int HX8357_COLMOD = 0x3A; + + public final static int HX8357_SETOSC = 0xB0; + public final static int HX8357_SETPWR1 = 0xB1; + public final static int HX8357B_SETDISPLAY = 0xB2; + public final static int HX8357_SETRGB = 0xB3; + public final static int HX8357D_SETCOM = 0xB6; + + public final static int HX8357B_SETDISPMODE = 0xB4; + public final static int HX8357D_SETCYC = 0xB4; + public final static int HX8357B_SETOTP = 0xB7; + public final static int HX8357D_SETC = 0xB9; + + public final static int HX8357B_SET_PANEL_DRIVING = 0xC0; + public final static int HX8357D_SETSTBA = 0xC0; + public final static int HX8357B_SETDGC = 0xC1; + public final static int HX8357B_SETID = 0xC3; + public final static int HX8357B_SETDDB = 0xC4; + public final static int HX8357B_SETDISPLAYFRAME = 0xC5; + public final static int HX8357B_GAMMASET = 0xC8; + public final static int HX8357B_SETCABC = 0xC9; + public final static int HX8357_SETPANEL = 0xCC; + + + public final static int HX8357B_SETPOWER = 0xD0; + public final static int HX8357B_SETVCOM = 0xD1; + public final static int HX8357B_SETPWRNORMAL = 0xD2; + + public final static int HX8357B_RDID1 = 0xDA; + public final static int HX8357B_RDID2 = 0xDB; + public final static int HX8357B_RDID3 = 0xDC; + public final static int HX8357B_RDID4 = 0xDD; + + public final static int HX8357D_SETGAMMA = 0xE0; + + public final static int HX8357B_SETGAMMA = 0xC8; + public final static int HX8357B_SETPANELRELATED = 0xE9; + + public final static int HX8357B_MADCTL_MY = 0x80; + public final static int HX8357B_MADCTL_MX = 0x40; + public final static int HX8357B_MADCTL_MV = 0x20; + public final static int HX8357B_MADCTL_ML = 0x10; + public final static int HX8357B_MADCTL_RGB = 0x00; + public final static int HX8357B_MADCTL_BGR = 0x08; + public final static int HX8357B_MADCTL_MH = 0x04; + } + + private void check_events() { + if (ba!=null) { + if (mycodes.ValidString(event)) { + need_log_event = ba.subExists(event+"_log"); + } + } + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + +} diff --git a/src/devices/LEDMax7219/Graphic_Max7219.java b/src/devices/LEDMax7219/Graphic_Max7219.java new file mode 100644 index 0000000..7a2c57a --- /dev/null +++ b/src/devices/LEDMax7219/Graphic_Max7219.java @@ -0,0 +1,109 @@ +package devices.LEDMax7219; + +import com.sun.jna.Memory; +import com.sun.jna.Pointer; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Graphic_Max7219") +public class Graphic_Max7219 { + private Pointer mem; + private final int blocks; + private final int size; + private final LEDBlock[][] ledblock; + + + /** + * Initialize 1 block 8x8 pixel + */ + public Graphic_Max7219() { + this.blocks = 1; + + this.size = this.blocks * 8; + mem = new Memory(this.size); + + ledblock = new LEDBlock[1][1]; + ledblock[0][0] = new LEDBlock(); + + for(int ii=0;ii<8;ii++) { + ledblock[0][0].setpointer(ii, mem.getPointer(ii)); + } + + } + + /** + * Initialize n block 8x8 pixel, dianggap 1 row dan n column + * @param blocks : banyaknya block, dianggap sebagai column + */ + public Graphic_Max7219(int blocks) { + + this.blocks = blocks>0 ? blocks : 1; + this.size = this.blocks * 8; + mem = new Memory(this.size); + BA.Log("MemSize = "+this.size); + + ledblock = new LEDBlock[blocks][1]; + for(int ii=0;ii (blocks-1)) return null; +// if (mem==null) return null; +// +// short[] result = new short[8]; +// for(int ii=0;ii<8;ii++) { +// int tmp = get_mask(ii) | (mem.getByte(ii*blocks + blocknumber) & 0xFF); +// result[ii] = (short)tmp; +// } +// return result; +// } + + + public LEDBlock GetLEDBlock(int x, int y) { + return ledblock[x][y]; + } + + + + +} diff --git a/src/devices/LEDMax7219/LEDBlock.java b/src/devices/LEDMax7219/LEDBlock.java new file mode 100644 index 0000000..4e2e36a --- /dev/null +++ b/src/devices/LEDMax7219/LEDBlock.java @@ -0,0 +1,58 @@ +package devices.LEDMax7219; + +import com.sun.jna.Pointer; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("LEDBlock") +public class LEDBlock { + private Pointer[] rows = new Pointer[8]; + + @BA.Hide + public void setpointer(int row, Pointer value) { + rows[row] = value; + } + + public void SetPixel(int rownumber, int cellnumber, boolean value) { + Pointer vv = rows[rownumber]; + + int mask = (1 << cellnumber); + int prev = vv.getByte(0); + + if (value) { + prev = prev | mask; + } else { + prev = prev & ~(mask); + } + + vv.setByte(0, (byte) prev); + } + + public short GetRowValue(int rownumber) { + Pointer vv = rows[rownumber]; + short result = (short)(get_mask(rownumber) | (vv.getByte(0) & 0xFF)); + return result; + } + + private short get_mask(int baris) { + switch(baris) { + case 1: // baris 1 + return 0x200; + case 2: + return 0x300; + case 3: + return 0x400; + case 4: + return 0x500; + case 5: + return 0x600; + case 6: + return 0x700; + case 7: + return 0x800; + default : // baris 0 + return 0x100; + } +} + +} diff --git a/src/devices/LEDMax7219/LEDMax7219.java b/src/devices/LEDMax7219/LEDMax7219.java new file mode 100644 index 0000000..24998a3 --- /dev/null +++ b/src/devices/LEDMax7219/LEDMax7219.java @@ -0,0 +1,291 @@ +package devices.LEDMax7219; + +import java.io.IOException; + +import com.pi4j.io.spi.SpiChannel; +import com.pi4j.io.spi.SpiDevice; +import com.pi4j.io.spi.SpiFactory; +import com.pi4j.io.spi.SpiMode; + +import anywheresoftware.b4a.BA; +import jGPIO.SPI.SPI_BitBang; + +@BA.ShortName("LEDMax7219") +//@BA.DependsOn(values = { "pi4j-core" }) +@BA.Events(values= { + "log(msg as string)", + "updateresult(success as boolean)" +}) + +public class LEDMax7219 { + static { + + } + private BA ba; + private Object myobject; + private String event; + private boolean need_log_event = false; + private boolean need_updateresult_event = false; + + private SPI_BitBang spi_bitbang; + private SpiDevice spidev; + + private boolean is_open = false; + + public Graphic_Max7219 graphic; + + /** + * Initialize LEDMax7219 + * @param callerobject : caller object + * @param Eventname : event name + */ + public void Initialize(BA bax, Object callerobject, String Eventname) { + ba = bax; + myobject = callerobject; + event = Eventname.trim(); + if (ba!=null) { + if (myobject!=null) { + if (!event.isEmpty()) { + need_log_event = ba.subExists(event+"_log"); + need_updateresult_event =ba.subExists(event+"_updateresult"); + } + } + } + + // auto close kalau java program closed + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + + + } + + /** + * Open SPI Device using Pi4J + * @param channel : 0 or 1 + * @param blocks : how many led blocks + * @return true if success + */ + public boolean Open_SpiDevice(int channel, int blocks) { + if (is_open) { // sebelumnya terbuka pake metode lain + Close(); // tutup dulu, is_open jadi false + } + if (blocks<0) blocks = 1; // default 1 + + graphic = new Graphic_Max7219(blocks); + SpiChannel channelspi = SpiChannel.getByNumber(channel); + + if (channelspi!=null) { + int mode = 0; + SpiMode modespi = SpiMode.getByNumber(mode); // mode 0 = idle di low (0), active rising edge + int speed = 500000; // speed max di 10 Mhz + if (modespi!=null) { + try { + spidev = SpiFactory.getInstance(channelspi, speed, modespi); + is_open = true; + raise_log("SpiDev opened at channel "+channel+", mode="+mode+", speed="+speed); + } catch (IOException e) { + raise_log("Unable to SPIFactory GetInstance, Msg : "+e.getMessage()); + } + } else raise_log("Invalid SPI Mode "+mode); + } else raise_log("Invalid SPI Channel "+channel); + return false; + } + + /** + * Open in SPI BitBang mode for compatibility + * @param pin_do : pin for Serial Data + * @param pin_cs : pin for Chip Select + * @param pin_clk : pin for Clock + * @param led_blocks : how many led blocks in cascaded + * @return true if success + */ + public boolean Open_BitBang(int pin_do, int pin_cs, int pin_clk, int blocks) { + if (is_open) { // sebelumnya terbuka pake metode lain + Close(); // tutup dulu, is_open jadi false + } + if (blocks<0) blocks = 1; // default 1 + + graphic = new Graphic_Max7219(blocks); + spi_bitbang = new SPI_BitBang(-1, pin_do, pin_cs, pin_clk); + if (spi_bitbang.IsOpened()) { + // initialize + is_open = true; + DecodeMode_None(); + ScanLimiter_All(); + } + + return spi_bitbang.IsOpened(); + } + + + private void DecodeMode_None() { + + if (is_open) { + + short command = 0x900; // decode mode = 0 --> no decode + boolean result = WriteSPI(command); + + if (result) + raise_log("DecodeMode_None success"); + else + raise_log("DecodeMode_None failed"); + + } else { + raise_log("DecodeMode_None failed, no SPI opened"); + } + + } + + + private void ScanLimiter_All() { + if (is_open) { + short command = 0xB07; + boolean result = WriteSPI(command); + + if (result) + raise_log("ScanLimiter_All success"); + else + raise_log("ScanLimiter_All failed"); + + } else { + raise_log("ScanLimiter_All failed, no SPI opened"); + } + + } + + + /** + * Set Display testing mode + * @param testing : if true, will turn on all LED. If false, normal operation + * @return true if success + */ + public boolean Test_Display(boolean testing) { + short command = 0; + if (testing) { + command = 0xF01; // all LED should ON + } else { + command = 0xF00; // normal operation + } + + if (is_open) { + boolean result = WriteSPI(command); + return result; + + } else raise_log("Test_Display failed, no SPI opened"); + return false; + } + + /** + * Set Display Shutdown mode + * @param shutdown : if true, will shutdown display. If false, normal operation + * @return true if success + */ + public boolean Shutdown_Display(boolean shutdown) { + short command = 0; + if (shutdown) { + command = 0xC00; // shutdown display + } else { + command = 0xC01; // normal operation + } + + if (is_open) { + + boolean result = WriteSPI(command); + + return result; + } else raise_log("Shutdown_Display failed, no SPI opened"); + return false; + } + + /** + * Check if LEDMax7219 is opened or not + * @return true if opened + */ + public boolean getIsOpened() { + return is_open; + } + + private boolean WriteSPI(short value) { + boolean result = true; + if (spi_bitbang!=null) { + spi_bitbang.setCS(true); + result = spi_bitbang.Write16(value); + spi_bitbang.setCS(false); + } else if (spidev!=null) { + try { + spidev.write(value); + } catch (IOException e) { + result = false; + raise_log("WriteSPI spidev value= "+value+", exception, Msg = "+e.getMessage()); + } + + } else { + raise_log("Error WriteSPI value="+value); + result =false; + } + return result; + } + + /** + * Write Buffer to LED Max7219 + * @return true if success + */ + public boolean Update_Display() { + boolean result = true; + if (is_open) { + if (graphic!=null) { + int blockcount = graphic.getBlocks(); + for(int row=0;row<8;row++) { + for(int ii=0;ii + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ! + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // " + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // # + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // $ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // % + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // & + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ' + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ( + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ) + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // * + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // , + 0x0000,0x0000,0x0100,0x0380,0x0380,0x0380,0x0380,0x0380,0x0380,0x0100,0x0000,0x0000, // - + 0x0000,0x0000,0x0000,0x0000,0x0000,0xc000,0xc000,0x0000,0x0000,0x0000,0x0000,0x0000, // . + 0x0000,0x0000,0x8102,0xc386,0xc386,0xc386,0xc386,0xc386,0xc386,0x8102,0x0000,0x0000, // / + + 0x0000,0x7efc,0xbc7a,0xc006,0xc006,0xc006,0xc006,0xc006,0xc006,0xbc7a,0x7efc,0x0000, // 0 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3c78,0x7efc,0x0000, // 1 + 0x0000,0x7e00,0xbd02,0xc386,0xc386,0xc386,0xc386,0xc386,0xc386,0x817a,0x00fc,0x0000, // 2 + 0x0000,0x0000,0x8102,0xc386,0xc386,0xc386,0xc386,0xc386,0xc386,0xbd7a,0x7efc,0x0000, // 3 + 0x0000,0x00fc,0x0178,0x0380,0x0380,0x0380,0x0380,0x0380,0x0380,0x3d78,0x7efc,0x0000, // 4 + 0x0000,0x00fc,0x817a,0xc386,0xc386,0xc386,0xc386,0xc386,0xc386,0xbd02,0x7e00,0x0000, // 5 + 0x0000,0x7efc,0xbd7a,0xc386,0xc386,0xc386,0xc386,0xc386,0xc386,0xbd02,0x7e00,0x0000, // 6 + 0x0000,0x0000,0x0002,0x0006,0x0006,0x0006,0x0006,0x0006,0x0006,0x3c7a,0x7efc,0x0000, // 7 + 0x0000,0x7efc,0xbd7a,0xc386,0xc386,0xc386,0xc386,0xc386,0xc386,0xbd7a,0x7efc,0x0000, // 8 + 0x0000,0x00fc,0x817a,0xc386,0xc386,0xc386,0xc386,0xc386,0xc386,0xbd7a,0x7efc,0x0000, // 9 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // : + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ; + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // < + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // = + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // > + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ? + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // @ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // A + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // B + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // C + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // D + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // E + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // F + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // G + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // H + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // I + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // J + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // K + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // L + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // M + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // N + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // O + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // P + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Q + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // R + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // S + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // T + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // U + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // V + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // W + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // X + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Y + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // Z + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // [ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // \ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ] + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ^ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // _ + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ` + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // a + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // b + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // c + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // d + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // e + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // f + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // g + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // h + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // i + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // j + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // k + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // l + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // m + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // n + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // o + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // p + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // q + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // r + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // s + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // t + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // u + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // v + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // w + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // x + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // y + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // z + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // { + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // | + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // } + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 // ~ + }; + } + +} diff --git a/src/devices/Oled/SSD1306/Font16X16.java b/src/devices/Oled/SSD1306/Font16X16.java new file mode 100644 index 0000000..2b8f3e5 --- /dev/null +++ b/src/devices/Oled/SSD1306/Font16X16.java @@ -0,0 +1,112 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font16X16") +public class Font16X16 extends FontPack { + public Font16X16() { + super("Font16X16",16,16,16,16,32,126); + data = new int[] { + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // + 0x0000,0x0000,0x0000,0x0000,0x00f8,0x73fc,0x73fc,0x73fc,0x00f8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ! + 0x0000,0x0000,0x0000,0x0000,0x001e,0x003e,0x003e,0x0000,0x0000,0x0000,0x003e,0x003e,0x001e,0x0000,0x0000,0x0000, // " + 0x0000,0x0c30,0x0c30,0x0c30,0x7ffe,0x7ffe,0x0c30,0x0c30,0x0c30,0x0c30,0x7ffe,0x7ffe,0x0c30,0x0c30,0x0c30,0x0000, // # + 0x0000,0x0000,0x0000,0x18f0,0x19f8,0x1998,0x7ffe,0x1998,0x1998,0x7ffe,0x1998,0x1f98,0x0f18,0x0000,0x0000,0x0000, // $ + 0x0000,0x0000,0x0000,0x0000,0x1c38,0x0e38,0x0738,0x0380,0x01c0,0x1ce0,0x1c70,0x1c38,0x0000,0x0000,0x0000,0x0000, // % + 0x0000,0x0000,0x0000,0x1e38,0x3ffc,0x21c4,0x21c4,0x33fc,0x3f38,0x1e00,0x1e00,0x3700,0x2380,0x0000,0x0000,0x0000, // & + 0x0000,0x0000,0x0000,0x0000,0x0020,0x003c,0x003c,0x001c,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ' + 0x0000,0x0000,0x0000,0x0000,0x03c0,0x07e0,0x0ff0,0x1c38,0x381c,0x300c,0x2004,0x2004,0x0000,0x0000,0x0000,0x0000, // ( + 0x0000,0x0000,0x0000,0x0000,0x2004,0x2004,0x300c,0x381c,0x1c38,0x0ff0,0x07e0,0x03c0,0x0000,0x0000,0x0000,0x0000, // ) + 0x0000,0x0000,0x0180,0x1188,0x0990,0x07e0,0x07e0,0x3ffc,0x3ffc,0x07e0,0x07e0,0x0990,0x1188,0x0180,0x0000,0x0000, // * + 0x0000,0x0000,0x0000,0x0000,0x0180,0x0180,0x0180,0x0ff0,0x0ff0,0x0180,0x0180,0x0180,0x0000,0x0000,0x0000,0x0000, // + + 0x0000,0x0000,0x0000,0x0000,0x4000,0x7800,0x7800,0x3800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // , + 0x0000,0x0000,0x0000,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0000,0x0000,0x0000, // - + 0x0000,0x0000,0x0000,0x0000,0x0000,0x3800,0x3800,0x3800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // . + 0x0000,0x0000,0x0000,0x2000,0x3000,0x3800,0x1c00,0x0e00,0x0700,0x0380,0x01c0,0x00e0,0x0070,0x0038,0x001c,0x0000, // / + + 0x0000,0x0000,0x0000,0x1ff8,0x3ffc,0x3ffc,0x2e04,0x2784,0x21e4,0x2074,0x3ffc,0x3ffc,0x1ff8,0x0000,0x0000,0x0000, // 0 + 0x0000,0x0000,0x0000,0x2060,0x2060,0x2060,0x3ff0,0x3ffc,0x3ffc,0x2000,0x2000,0x2000,0x0000,0x0000,0x0000,0x0000, // 1 + 0x0000,0x0000,0x0000,0x3018,0x381c,0x3c1c,0x2e04,0x2704,0x2384,0x21cc,0x38fc,0x3878,0x3830,0x0000,0x0000,0x0000, // 2 + 0x0000,0x0000,0x0000,0x1818,0x381c,0x381c,0x2184,0x2184,0x2184,0x33cc,0x3e7c,0x1e78,0x0c30,0x0000,0x0000,0x0000, // 3 + 0x0000,0x0000,0x0000,0x0380,0x03c0,0x0360,0x2330,0x2318,0x3ffc,0x3ffc,0x3ffc,0x2300,0x2300,0x0000,0x0000,0x0000, // 4 + 0x0000,0x0000,0x0000,0x19fc,0x39fc,0x39fc,0x2184,0x2184,0x2184,0x3384,0x3f84,0x1f04,0x0e04,0x0000,0x0000,0x0000, // 5 + 0x0000,0x0000,0x0000,0x1fe0,0x3ff0,0x3ff8,0x219c,0x218c,0x2184,0x2184,0x3f84,0x3f80,0x1f00,0x0000,0x0000,0x0000, // 6 + 0x0000,0x0000,0x0000,0x003c,0x003c,0x003c,0x3804,0x3c04,0x3e04,0x0704,0x0384,0x01fc,0x00fc,0x007c,0x0000,0x0000, // 7 + 0x0000,0x0000,0x0000,0x1e78,0x3e7c,0x3ffc,0x21c4,0x21c4,0x2384,0x2384,0x3ffc,0x3e7c,0x1e78,0x0000,0x0000,0x0000, // 8 + 0x0000,0x0000,0x0000,0x00f8,0x01fc,0x21fc,0x2184,0x2184,0x3184,0x3984,0x1ffc,0x0ffc,0x07f8,0x0000,0x0000,0x0000, // 9 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0e70,0x0e70,0x0e70,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // : + 0x0000,0x0000,0x0000,0x0000,0x0000,0x1000,0x1e70,0x1e70,0x0e70,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ; + 0x0000,0x0000,0x0000,0x0180,0x03c0,0x07e0,0x0e70,0x1c38,0x381c,0x700e,0x6006,0x4002,0x0000,0x0000,0x0000,0x0000, // < + 0x0000,0x0000,0x0660,0x0660,0x0660,0x0660,0x0660,0x0660,0x0660,0x0660,0x0660,0x0660,0x0660,0x0660,0x0000,0x0000, // = + 0x0000,0x0000,0x0000,0x4002,0x6006,0x700e,0x381c,0x1c38,0x0e70,0x07e0,0x03c0,0x0180,0x0000,0x0000,0x0000,0x0000, // > + 0x0000,0x0000,0x0000,0x0018,0x001c,0x000c,0x000e,0x7306,0x7386,0x73ce,0x00fc,0x007c,0x0038,0x0000,0x0000,0x0000, // ? + + 0x0000,0x0000,0x0000,0x3ffc,0x3ffe,0x7ffe,0x6002,0x6002,0x63c2,0x63c2,0x63c2,0x63fe,0x43fe,0x03fc,0x0000,0x0000, // @ + 0x0000,0x0000,0x0000,0x3fe0,0x3ff0,0x3ff8,0x021c,0x020c,0x020c,0x021c,0x3ff8,0x3ff0,0x3fe0,0x0000,0x0000,0x0000, // A + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x3ffc,0x3ffc,0x2184,0x2184,0x2184,0x3ffc,0x3ffc,0x1e78,0x0000,0x0000,0x0000, // B + 0x0000,0x0000,0x0000,0x0ff0,0x1ff8,0x3ffc,0x300c,0x2004,0x2004,0x2004,0x381c,0x381c,0x1818,0x0000,0x0000,0x0000, // C + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x3ffc,0x3ffc,0x2004,0x2004,0x300c,0x3ffc,0x1ff8,0x0ff0,0x0000,0x0000,0x0000, // D + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x3ffc,0x3ffc,0x2184,0x2184,0x2184,0x23c4,0x33cc,0x381c,0x0000,0x0000,0x0000, // E + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x3ffc,0x3ffc,0x2184,0x0184,0x0184,0x03c4,0x03cc,0x001c,0x0000,0x0000,0x0000, // F + 0x0000,0x0000,0x0000,0x0ff0,0x1ff8,0x3ffc,0x300c,0x2004,0x2204,0x2204,0x3e3c,0x3e3c,0x3e38,0x0000,0x0000,0x0000, // G + 0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x3ffc,0x0180,0x0180,0x0180,0x3ffc,0x3ffc,0x3ffc,0x0000,0x0000,0x0000,0x0000, // H + 0x0000,0x0000,0x0000,0x0000,0x2004,0x2004,0x3ffc,0x3ffc,0x3ffc,0x2004,0x2004,0x0000,0x0000,0x0000,0x0000,0x0000, // I + 0x0000,0x0000,0x1e00,0x1e00,0x3e00,0x2000,0x2000,0x2004,0x2004,0x3ffc,0x3ffc,0x1ffc,0x0004,0x0004,0x0000,0x0000, // J + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x3ffc,0x3ffc,0x03c0,0x07e0,0x0e70,0x3c3c,0x381c,0x300c,0x0000,0x0000,0x0000, // K + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x3ffc,0x3ffc,0x2004,0x2000,0x2000,0x3000,0x3800,0x3c00,0x0000,0x0000,0x0000, // L + 0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x3ffc,0x0078,0x00f0,0x01e0,0x00f0,0x0078,0x3ffc,0x3ffc,0x3ffc,0x0000,0x0000, // M + 0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x3ffc,0x0070,0x00e0,0x01c0,0x0380,0x0700,0x3ffc,0x3ffc,0x3ffc,0x0000,0x0000, // N + 0x0000,0x0000,0x0000,0x07e0,0x0ff0,0x1ff8,0x381c,0x300c,0x300c,0x300c,0x381c,0x1ff8,0x0ff0,0x07e0,0x0000,0x0000, // O + + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x3ffc,0x3ffc,0x2184,0x0184,0x0184,0x01fc,0x01fc,0x0078,0x0000,0x0000,0x0000, // P + 0x0000,0x0000,0x0000,0x07e0,0x1ff8,0x1ff8,0x181c,0x180c,0x5c04,0x5e0c,0x7e1c,0x7ff8,0x7ff8,0x47e0,0x0000,0x0000, // Q + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x3ffc,0x3ffc,0x0184,0x0184,0x0384,0x3ffc,0x3ffc,0x3c78,0x0000,0x0000,0x0000, // R + 0x0000,0x0000,0x0000,0x1c78,0x3cfc,0x3dfc,0x2184,0x2184,0x2184,0x2184,0x3fbc,0x3f3c,0x1e38,0x0000,0x0000,0x0000, // S + 0x0000,0x0000,0x0000,0x001c,0x000c,0x2004,0x2004,0x3ffc,0x3ffc,0x3ffc,0x2004,0x2004,0x000c,0x001c,0x0000,0x0000, // T + 0x0000,0x0000,0x0000,0x1ffc,0x3ffc,0x3ffc,0x2000,0x2000,0x2000,0x3ffc,0x3ffc,0x1ffc,0x0000,0x0000,0x0000,0x0000, // U + 0x0000,0x0000,0x0000,0x07fc,0x0ffc,0x1ffc,0x3800,0x3000,0x3800,0x1ffc,0x0ffc,0x07fc,0x0000,0x0000,0x0000,0x0000, // V + 0x0000,0x0000,0x0000,0x03fc,0x0ffc,0x3ffc,0x3c00,0x3c00,0x0f80,0x3c00,0x3c00,0x3ffc,0x0ffc,0x03fc,0x0000,0x0000, // W + 0x0000,0x0000,0x0000,0x381c,0x3c3c,0x3e7c,0x07e0,0x03c0,0x07e0,0x3e7c,0x3c3c,0x381c,0x0000,0x0000,0x0000,0x0000, // X + 0x0000,0x0000,0x0000,0x007c,0x20fc,0x21fc,0x3f80,0x3f00,0x3f80,0x21fc,0x20fc,0x007c,0x0000,0x0000,0x0000,0x0000, // Y + 0x0000,0x0000,0x0000,0x383c,0x3c1c,0x3e0c,0x2704,0x2384,0x21c4,0x20e4,0x307c,0x383c,0x3c1c,0x0000,0x0000,0x0000, // Z + 0x0000,0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x3ffc,0x2004,0x2004,0x2004,0x2004,0x0000,0x0000,0x0000,0x0000, // [ + 0x0000,0x0000,0x0000,0x001c,0x0038,0x0070,0x00e0,0x01c0,0x0380,0x0700,0x0e00,0x1c00,0x1800,0x3000,0x2000,0x2000, // + 0x0000,0x0000,0x0000,0x0000,0x0000,0x2004,0x2004,0x2004,0x2004,0x3ffc,0x3ffc,0x3ffc,0x0000,0x0000,0x0000,0x0000, // ] + 0x0000,0x0000,0x0000,0x0020,0x0030,0x0038,0x001c,0x000e,0x000e,0x001c,0x0038,0x0030,0x0020,0x0000,0x0000,0x0000, // ^ + 0x0000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000,0xc000, // _ + + 0x0000,0x0000,0x0000,0x000c,0x000c,0x003c,0x0030,0x0030,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ` + 0x0000,0x0000,0x0000,0x1c00,0x3e40,0x3e40,0x2240,0x2240,0x2240,0x3fc0,0x1fc0,0x3f80,0x2000,0x0000,0x0000,0x0000, // a + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x1ffc,0x3ffc,0x2040,0x2040,0x2040,0x3fc0,0x3fc0,0x1f80,0x0000,0x0000,0x0000, // b + 0x0000,0x0000,0x0000,0x1f80,0x3fc0,0x3fc0,0x2040,0x2040,0x2040,0x39c0,0x39c0,0x1980,0x0000,0x0000,0x0000,0x0000, // c + 0x0000,0x0000,0x0000,0x1f80,0x3fc0,0x3fc0,0x2040,0x2040,0x2044,0x3ffc,0x1ffc,0x3ffc,0x2004,0x0000,0x0000,0x0000, // d + 0x0000,0x0000,0x0000,0x1f80,0x3fc0,0x3fc0,0x2240,0x2240,0x2240,0x3bc0,0x3bc0,0x1b80,0x0000,0x0000,0x0000,0x0000, // e + 0x0000,0x0000,0x0000,0x2180,0x2180,0x3ff8,0x3ffc,0x3ffc,0x2184,0x219c,0x019c,0x0018,0x0000,0x0000,0x0000,0x0000, // f + 0x0000,0x0000,0x0000,0x4780,0xcfc0,0xdfc0,0x9840,0x9840,0x9840,0xffc0,0xff80,0x7fc0,0x0040,0x0000,0x0000,0x0000, // g + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x3ffc,0x3ffc,0x0180,0x0040,0x0040,0x3fc0,0x3fc0,0x3f80,0x0000,0x0000,0x0000, // h + 0x0000,0x0000,0x0000,0x0000,0x2040,0x2040,0x2040,0x3fdc,0x3fdc,0x3fdc,0x2000,0x2000,0x2000,0x0000,0x0000,0x0000, // i + 0x0000,0x0000,0x0000,0x2000,0x6000,0xe000,0x8040,0x8040,0xc040,0xffdc,0xffdc,0x7fdc,0x0000,0x0000,0x0000,0x0000, // j + 0x0000,0x0000,0x0000,0x2004,0x3ffc,0x3ffc,0x3ffc,0x0200,0x0700,0x0f80,0x3dc0,0x38c0,0x3040,0x0000,0x0000,0x0000, // k + 0x0000,0x0000,0x0000,0x0000,0x2004,0x2004,0x2004,0x3ffc,0x3ffc,0x3ffc,0x2000,0x2000,0x2000,0x0000,0x0000,0x0000, // l + 0x0000,0x0000,0x0000,0x3fc0,0x3fc0,0x3fc0,0x0040,0x0040,0x3fc0,0x0040,0x0040,0x3fc0,0x3fc0,0x3f80,0x0000,0x0000, // m + 0x0000,0x0000,0x0000,0x3fc0,0x3fc0,0x3fc0,0x0040,0x0040,0x0040,0x3fc0,0x3fc0,0x3f80,0x0000,0x0000,0x0000,0x0000, // n + 0x0000,0x0000,0x0000,0x1f80,0x3fc0,0x3fc0,0x2040,0x2040,0x2040,0x3fc0,0x3fc0,0x1f80,0x0000,0x0000,0x0000,0x0000, // o + + 0x0000,0x0000,0x0000,0x8040,0xffc0,0xff80,0xffc0,0x9040,0x1040,0x1040,0x1fc0,0x1fc0,0x0f80,0x0000,0x0000,0x0000, // p + 0x0000,0x0000,0x0f80,0x1fc0,0x1fc0,0x1040,0x1040,0x9040,0xffc0,0xff80,0xffc0,0x8040,0x0000,0x0000,0x0000,0x0000, // q + 0x0000,0x0000,0x0000,0x2040,0x3fc0,0x3fc0,0x3fc0,0x2180,0x00c0,0x00c0,0x01c0,0x01c0,0x0180,0x0000,0x0000,0x0000, // r + 0x0000,0x0000,0x0000,0x1980,0x3bc0,0x23c0,0x2640,0x2640,0x2640,0x3c40,0x3dc0,0x1980,0x0000,0x0000,0x0000,0x0000, // s + 0x0000,0x0000,0x0000,0x0040,0x0040,0x1fe0,0x3ff0,0x3ff8,0x2040,0x3840,0x3840,0x1840,0x0000,0x0000,0x0000,0x0000, // t + 0x0000,0x0000,0x0000,0x1fc0,0x3fc0,0x3fc0,0x2000,0x2000,0x2000,0x3fc0,0x1fc0,0x3fc0,0x2000,0x0000,0x0000,0x0000, // u + 0x0000,0x0000,0x0000,0x07c0,0x0fc0,0x1fc0,0x3800,0x3000,0x3800,0x1fc0,0x0fc0,0x07c0,0x0000,0x0000,0x0000,0x0000, // v + 0x0000,0x0000,0x0000,0x07c0,0x0fc0,0x3fc0,0x3800,0x3800,0x0e00,0x3800,0x3800,0x3fc0,0x0fc0,0x07c0,0x0000,0x0000, // w + 0x0000,0x0000,0x0000,0x30c0,0x39c0,0x3fc0,0x0f00,0x0f00,0x3fc0,0x39c0,0x30c0,0x0000,0x0000,0x0000,0x0000,0x0000, // x + 0x0000,0x0000,0x0000,0x8000,0x87c0,0x8fc0,0x9fc0,0xd800,0xf800,0x7800,0x3fc0,0x0fc0,0x07c0,0x0000,0x0000,0x0000, // y + 0x0000,0x0000,0x0000,0x31c0,0x38c0,0x3c40,0x2e40,0x2740,0x23c0,0x31c0,0x38c0,0x0000,0x0000,0x0000,0x0000,0x0000, // z + 0x0000,0x0000,0x0000,0x0180,0x0180,0x03c0,0x1e78,0x3e7c,0x3c3c,0x2004,0x2004,0x2004,0x2004,0x0000,0x0000,0x0000, // { + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7ffe,0x7ffe,0x7ffe,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // | + 0x0000,0x0000,0x0000,0x2004,0x2004,0x2004,0x2004,0x3c3c,0x3e7c,0x1e78,0x03c0,0x0180,0x0180,0x0000,0x0000,0x0000, // } + 0x0000,0x0000,0x0038,0x003c,0x003c,0x0004,0x000c,0x001c,0x0038,0x0030,0x0020,0x003c,0x003c,0x001c,0x0000,0x0000 // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/Font5X7.java b/src/devices/Oled/SSD1306/Font5X7.java new file mode 100644 index 0000000..277d8bc --- /dev/null +++ b/src/devices/Oled/SSD1306/Font5X7.java @@ -0,0 +1,112 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font5X7") +public class Font5X7 extends FontPack { + + public Font5X7() { + super("Font_5X7", 5, 7, 6, 9, 32, 126); + data = new int[] { + 0x00, 0x00, 0x00, 0x00, 0x00, // char ' ' (0x20/32) + 0x00, 0x00, 0x2E, 0x00, 0x00, // char '!' (0x21/33) + 0x06, 0x00, 0x06, 0x00, 0x00, // char '"' (0x22/34) + 0x14, 0x7F, 0x14, 0x7F, 0x14, // char '#' (0x23/35) + 0x06, 0x49, 0x7F, 0x49, 0x30, // char '$' (0x24/36) + 0x24, 0x10, 0x08, 0x24, 0x00, // char '%' (0x25/37) + 0x36, 0x49, 0x36, 0x50, 0x00, // char '&' (0x26/38) + 0x00, 0x00, 0x00, 0x06, 0x00, // char ''' (0x27/39) + 0x00, 0x41, 0x36, 0x00, 0x00, // char '(' (0x28/40) + 0x00, 0x36, 0x41, 0x00, 0x00, // char ')' (0x29/41) + 0x00, 0x08, 0x00, 0x00, 0x00, // char '*' (0x2A/42) + 0x00, 0x08, 0x1C, 0x08, 0x00, // char '+' (0x2B/43) + 0x40, 0x20, 0x00, 0x00, 0x00, // char ',' (0x2C/44) + 0x00, 0x08, 0x08, 0x00, 0x00, // char '-' (0x2D/45) + 0x20, 0x00, 0x00, 0x00, 0x00, // char '.' (0x2E/46) + 0x00, 0x30, 0x06, 0x00, 0x00, // char '/' (0x2F/47) + 0x36, 0x41, 0x41, 0x36, 0x00, // char '0' (0x30/48) + 0x00, 0x00, 0x00, 0x36, 0x00, // char '1' (0x31/49) + 0x30, 0x49, 0x49, 0x06, 0x00, // char '2' (0x32/50) + 0x00, 0x49, 0x49, 0x36, 0x00, // char '3' (0x33/51) + 0x06, 0x08, 0x08, 0x36, 0x00, // char '4' (0x34/52) + 0x06, 0x49, 0x49, 0x30, 0x00, // char '5' (0x35/53) + 0x36, 0x49, 0x49, 0x30, 0x00, // char '6' (0x36/54) + 0x00, 0x01, 0x01, 0x36, 0x00, // char '7' (0x37/55) + 0x36, 0x49, 0x49, 0x36, 0x00, // char '8' (0x38/56) + 0x06, 0x49, 0x49, 0x36, 0x00, // char '9' (0x39/57) + 0x00, 0x14, 0x00, 0x00, 0x00, // char ':' (0x3A/58) + 0x20, 0x14, 0x00, 0x00, 0x00, // char ';' (0x3B/59) + 0x00, 0x08, 0x14, 0x22, 0x00, // char '<' (0x3C/60) + 0x00, 0x14, 0x14, 0x14, 0x00, // char '=' (0x3D/61) + 0x00, 0x22, 0x14, 0x08, 0x00, // char '>' (0x3E/62) + 0x00, 0x01, 0x31, 0x06, 0x00, // char '?' (0x3F/63) + 0x36, 0x49, 0x55, 0x59, 0x2E, // char '@' (0x40/64) + 0x36, 0x09, 0x09, 0x36, 0x00, // char 'A' (0x41/65) + 0x77, 0x49, 0x49, 0x36, 0x00, // char 'B' (0x42/66) + 0x36, 0x41, 0x41, 0x00, 0x00, // char 'C' (0x43/67) + 0x77, 0x41, 0x41, 0x36, 0x00, // char 'D' (0x44/68) + 0x36, 0x49, 0x49, 0x00, 0x00, // char 'E' (0x45/69) + 0x36, 0x09, 0x09, 0x00, 0x00, // char 'F' (0x46/70) + 0x36, 0x41, 0x51, 0x30, 0x00, // char 'G' (0x47/71) + 0x36, 0x08, 0x08, 0x36, 0x00, // char 'H' (0x48/72) + 0x00, 0x00, 0x36, 0x00, 0x00, // char 'I' (0x49/73) + 0x00, 0x40, 0x40, 0x36, 0x00, // char 'J' (0x4A/74) + 0x36, 0x08, 0x14, 0x22, 0x00, // char 'K' (0x4B/75) + 0x36, 0x40, 0x40, 0x00, 0x00, // char 'L' (0x4C/76) + 0x36, 0x01, 0x06, 0x01, 0x36, // char 'M' (0x4D/77) + 0x36, 0x04, 0x10, 0x36, 0x00, // char 'N' (0x4E/78) + 0x36, 0x41, 0x41, 0x36, 0x00, // char 'O' (0x4F/79) + 0x36, 0x09, 0x09, 0x06, 0x00, // char 'P' (0x50/80) + 0x36, 0x41, 0x21, 0x56, 0x00, // char 'Q' (0x51/81) + 0x36, 0x09, 0x19, 0x26, 0x00, // char 'R' (0x52/82) + 0x06, 0x49, 0x49, 0x30, 0x00, // char 'S' (0x53/83) + 0x00, 0x01, 0x37, 0x01, 0x00, // char 'T' (0x54/84) + 0x36, 0x40, 0x40, 0x36, 0x00, // char 'U' (0x55/85) + 0x36, 0x40, 0x36, 0x00, 0x00, // char 'V' (0x56/86) + 0x36, 0x40, 0x30, 0x40, 0x36, // char 'W' (0x57/87) + 0x36, 0x08, 0x08, 0x36, 0x00, // char 'X' (0x58/88) + 0x06, 0x48, 0x48, 0x36, 0x00, // char 'Y' (0x59/89) + 0x20, 0x51, 0x49, 0x45, 0x02, // char 'Z' (0x5A/90) + 0x77, 0x41, 0x41, 0x00, 0x00, // char '[' (0x5B/91) + 0x00, 0x06, 0x30, 0x00, 0x00, // char '\' (0x5C/92) + 0x00, 0x41, 0x41, 0x77, 0x00, // char ']' (0x5D/93) + 0x00, 0x02, 0x01, 0x02, 0x00, // char '^' (0x5E/94) + 0x00, 0x40, 0x40, 0x00, 0x00, // char '_' (0x5F/95) + 0x00, 0x01, 0x02, 0x00, 0x00, // char '`' (0x60/96) + 0x20, 0x54, 0x54, 0x38, 0x40, // char 'a' (0x61/97) + 0x00, 0x36, 0x48, 0x30, 0x00, // char 'b' (0x62/98) + 0x30, 0x48, 0x48, 0x00, 0x00, // char 'c' (0x63/99) + 0x30, 0x48, 0x48, 0x76, 0x00, // char 'd' (0x64/100) + 0x38, 0x54, 0x54, 0x08, 0x00, // char 'e' (0x65/101) + 0x08, 0x6C, 0x0A, 0x00, 0x00, // char 'f' (0x66/102) + 0x08, 0x54, 0x54, 0x38, 0x00, // char 'g' (0x67/103) + 0x36, 0x08, 0x08, 0x30, 0x00, // char 'h' (0x68/104) + 0x00, 0x34, 0x00, 0x00, 0x00, // char 'i' (0x69/105) + 0x00, 0x40, 0x34, 0x00, 0x00, // char 'j' (0x6A/106) + 0x36, 0x10, 0x28, 0x00, 0x00, // char 'k' (0x6B/107) + 0x36, 0x00, 0x00, 0x00, 0x00, // char 'l' (0x6C/108) + 0x30, 0x08, 0x10, 0x08, 0x30, // char 'm' (0x6D/109) + 0x30, 0x08, 0x08, 0x30, 0x00, // char 'n' (0x6E/110) + 0x30, 0x48, 0x48, 0x30, 0x00, // char 'o' (0x6F/111) + 0x78, 0x14, 0x14, 0x08, 0x00, // char 'p' (0x70/112) + 0x08, 0x14, 0x14, 0x68, 0x00, // char 'q' (0x71/113) + 0x30, 0x08, 0x08, 0x00, 0x00, // char 'r' (0x72/114) + 0x08, 0x54, 0x54, 0x20, 0x00, // char 's' (0x73/115) + 0x08, 0x2C, 0x48, 0x00, 0x00, // char 't' (0x74/116) + 0x30, 0x40, 0x40, 0x30, 0x00, // char 'u' (0x75/117) + 0x30, 0x40, 0x30, 0x00, 0x00, // char 'v' (0x76/118) + 0x30, 0x40, 0x20, 0x40, 0x30, // char 'w' (0x77/119) + 0x28, 0x10, 0x10, 0x28, 0x00, // char 'x' (0x78/120) + 0x08, 0x50, 0x50, 0x38, 0x00, // char 'y' (0x79/121) + 0x24, 0x34, 0x2C, 0x24, 0x00, // char 'z' (0x7A/122) + 0x08, 0x36, 0x41, 0x00, 0x00, // char '{' (0x7B/123) + 0x00, 0x36, 0x00, 0x00, 0x00, // char '|' (0x7C/124) + 0x00, 0x41, 0x36, 0x08, 0x00, // char '}' (0x7D/125) + 0x08, 0x08, 0x10, 0x10, 0x00, // char '~' (0x7E/126) + 0x36, 0x41, 0x36, 0x00, 0x00, // char '' (0x7F/127) + }; + } + + + +} diff --git a/src/devices/Oled/SSD1306/Font6X8.java b/src/devices/Oled/SSD1306/Font6X8.java new file mode 100644 index 0000000..d9b7b4b --- /dev/null +++ b/src/devices/Oled/SSD1306/Font6X8.java @@ -0,0 +1,111 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font6X8") +public class Font6X8 extends FontPack { + + public Font6X8() { + super("Font6X8", 6, 8, 7, 9, 32, 126); + data = new int[] { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp + 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // ! + 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // " + 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // # + 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $ + 0x00, 0x23, 0x13, 0x08, 0x64, 0x62, // % + 0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // & + 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // ' + 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // ( + 0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // ) + 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // * + 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // + + 0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // , + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // - + 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // . + 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // / + 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 + 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 + 0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2 + 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3 + 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 + 0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5 + 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 + 0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7 + 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8 + 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 + 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // : + 0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ; + 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // < + 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // = + 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // > + 0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ? + 0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @ + 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A + 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C + 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D + 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E + 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F + 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G + 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H + 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I + 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J + 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K + 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L + 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M + 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O + 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P + 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q + 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R + 0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S + 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T + 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U + 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V + 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W + 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X + 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y + 0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z + 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [ + 0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55 + 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ] + 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^ + 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _ + 0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // ' + 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a + 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b + 0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c + 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d + 0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e + 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f + 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g + 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h + 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i + 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j + 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k + 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l + 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m + 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n + 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o + 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p + 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q + 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r + 0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s + 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t + 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u + 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v + 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w + 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x + 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y + 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, // z + 0x00, 0x00, 0x08, 0x77, 0x00, 0x00, // { + 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, // | + 0x00, 0x00, 0x77, 0x08, 0x00, 0x00, // } + 0x00, 0x10, 0x08, 0x10, 0x08, 0x00, // ~ + }; + } + + + +} diff --git a/src/devices/Oled/SSD1306/Font8X12.java b/src/devices/Oled/SSD1306/Font8X12.java new file mode 100644 index 0000000..82e1768 --- /dev/null +++ b/src/devices/Oled/SSD1306/Font8X12.java @@ -0,0 +1,112 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font8X12") +public class Font8X12 extends FontPack{ + public Font8X12() { + super("Font8X12", 8, 12, 8, 12, 32, 126); + data = new int[] { + 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000, // + 0x000,0x000,0x2fc,0x000,0x000,0x000,0x000,0x000, // ! + 0x000,0x00c,0x002,0x00c,0x002,0x000,0x000,0x000, // " + 0x090,0x3d0,0x0bc,0x3d0,0x0bc,0x090,0x000,0x000, // # + 0x318,0x224,0x7fe,0x244,0x18c,0x000,0x000,0x000, // $ + 0x018,0x324,0x0d8,0x1b0,0x24c,0x180,0x000,0x000, // % + 0x1c0,0x238,0x2e4,0x138,0x2e0,0x200,0x000,0x000, // & + 0x008,0x006,0x000,0x000,0x000,0x000,0x000,0x000, // ' + 0x000,0x000,0x000,0x1f8,0x204,0x402,0x000,0x000, // ( + 0x000,0x402,0x204,0x1f8,0x000,0x000,0x000,0x000, // ) + 0x090,0x060,0x1f8,0x060,0x090,0x000,0x000,0x000, // * + 0x020,0x020,0x1fc,0x020,0x020,0x000,0x000,0x000, // + + 0x800,0x600,0x000,0x000,0x000,0x000,0x000,0x000, // , + 0x020,0x020,0x020,0x020,0x020,0x000,0x000,0x000, // - + 0x000,0x200,0x000,0x000,0x000,0x000,0x000,0x000, // . + 0x400,0x380,0x060,0x01c,0x002,0x000,0x000,0x000, // / + + 0x1f8,0x204,0x204,0x204,0x1f8,0x000,0x000,0x000, // 0 + 0x000,0x208,0x3fc,0x200,0x000,0x000,0x000,0x000, // 1 + 0x318,0x284,0x244,0x224,0x218,0x000,0x000,0x000, // 2 + 0x108,0x204,0x224,0x224,0x1d8,0x000,0x000,0x000, // 3 + 0x040,0x0b0,0x088,0x3fc,0x280,0x000,0x000,0x000, // 4 + 0x13c,0x224,0x224,0x224,0x1c4,0x000,0x000,0x000, // 5 + 0x1f8,0x224,0x224,0x22c,0x1c0,0x000,0x000,0x000, // 6 + 0x00c,0x004,0x3e4,0x01c,0x004,0x000,0x000,0x000, // 7 + 0x1d8,0x224,0x224,0x224,0x1d8,0x000,0x000,0x000, // 8 + 0x038,0x344,0x244,0x244,0x1f8,0x000,0x000,0x000, // 9 + 0x000,0x000,0x210,0x000,0x000,0x000,0x000,0x000, // : + 0x000,0x000,0x620,0x000,0x000,0x000,0x000,0x000, // ; + 0x000,0x020,0x050,0x088,0x104,0x202,0x000,0x000, // < + 0x090,0x090,0x090,0x090,0x090,0x000,0x000,0x000, // = + 0x000,0x202,0x104,0x088,0x050,0x020,0x000,0x000, // > + 0x018,0x004,0x2c4,0x024,0x018,0x000,0x000,0x000, // ? + + 0x1f8,0x204,0x2e4,0x294,0x2f8,0x000,0x000,0x000, // @ + 0x200,0x3e0,0x09c,0x0f0,0x380,0x200,0x000,0x000, // A + 0x204,0x3fc,0x224,0x224,0x1d8,0x000,0x000,0x000, // B + 0x1f8,0x204,0x204,0x204,0x10c,0x000,0x000,0x000, // C + 0x204,0x3fc,0x204,0x204,0x1f8,0x000,0x000,0x000, // D + 0x204,0x3fc,0x224,0x274,0x30c,0x000,0x000,0x000, // E + 0x204,0x3fc,0x224,0x074,0x00c,0x000,0x000,0x000, // F + 0x0f0,0x108,0x204,0x244,0x1cc,0x040,0x000,0x000, // G + 0x204,0x3fc,0x020,0x020,0x3fc,0x204,0x000,0x000, // H + 0x204,0x204,0x3fc,0x204,0x204,0x000,0x000,0x000, // I + 0x600,0x404,0x404,0x3fc,0x004,0x004,0x000,0x000, // J + 0x204,0x3fc,0x224,0x0d0,0x30c,0x204,0x000,0x000, // K + 0x204,0x3fc,0x204,0x200,0x200,0x300,0x000,0x000, // L + 0x3fc,0x03c,0x3c0,0x03c,0x3fc,0x000,0x000,0x000, // M + 0x204,0x3fc,0x230,0x0c4,0x3fc,0x004,0x000,0x000, // N + 0x1f8,0x204,0x204,0x204,0x1f8,0x000,0x000,0x000, // O + + 0x204,0x3fc,0x224,0x024,0x018,0x000,0x000,0x000, // P + 0x1f8,0x284,0x284,0x704,0x5f8,0x000,0x000,0x000, // Q + 0x204,0x3fc,0x224,0x064,0x398,0x200,0x000,0x000, // R + 0x318,0x224,0x224,0x244,0x18c,0x000,0x000,0x000, // S + 0x00c,0x204,0x3fc,0x204,0x00c,0x000,0x000,0x000, // T + 0x004,0x1fc,0x200,0x200,0x1fc,0x004,0x000,0x000, // U + 0x004,0x07c,0x380,0x0e0,0x01c,0x004,0x000,0x000, // V + 0x01c,0x3e0,0x03c,0x3e0,0x01c,0x000,0x000,0x000, // W + 0x204,0x39c,0x060,0x39c,0x204,0x000,0x000,0x000, // X + 0x004,0x21c,0x3e0,0x21c,0x004,0x000,0x000,0x000, // Y + 0x20c,0x384,0x264,0x21c,0x304,0x000,0x000,0x000, // Z + 0x000,0x000,0x7fe,0x402,0x402,0x000,0x000,0x000, // [ + 0x000,0x00e,0x030,0x1c0,0x200,0x000,0x000,0x000, // + 0x000,0x402,0x402,0x7fe,0x000,0x000,0x000,0x000, // ] + 0x000,0x004,0x002,0x004,0x000,0x000,0x000,0x000, // ^ + 0x800,0x800,0x800,0x800,0x800,0x800,0x000,0x000, // _ + + 0x000,0x000,0x002,0x000,0x000,0x000,0x000,0x000, // ` + 0x000,0x140,0x2a0,0x2a0,0x3c0,0x200,0x000,0x000, // a + 0x004,0x3fc,0x220,0x220,0x1c0,0x000,0x000,0x000, // b + 0x000,0x1c0,0x220,0x220,0x260,0x000,0x000,0x000, // c + 0x000,0x1c0,0x220,0x224,0x3fc,0x200,0x000,0x000, // d + 0x000,0x1c0,0x2a0,0x2a0,0x2c0,0x000,0x000,0x000, // e + 0x000,0x220,0x3f8,0x224,0x224,0x004,0x000,0x000, // f + 0x000,0x740,0xaa0,0xaa0,0xa60,0x420,0x000,0x000, // g + 0x204,0x3fc,0x220,0x020,0x3c0,0x200,0x000,0x000, // h + 0x000,0x220,0x3e4,0x200,0x000,0x000,0x000,0x000, // i + 0x800,0x800,0x820,0x7e4,0x000,0x000,0x000,0x000, // j + 0x204,0x3fc,0x280,0x0e0,0x320,0x220,0x000,0x000, // k + 0x204,0x204,0x3fc,0x200,0x200,0x000,0x000,0x000, // l + 0x3e0,0x020,0x3e0,0x020,0x3c0,0x000,0x000,0x000, // m + 0x220,0x3e0,0x220,0x020,0x3c0,0x200,0x000,0x000, // n + 0x000,0x1c0,0x220,0x220,0x1c0,0x000,0x000,0x000, // o + + 0x820,0xfe0,0xa20,0x220,0x1c0,0x000,0x000,0x000, // p + 0x000,0x1c0,0x220,0xa20,0xfe0,0x800,0x000,0x000, // q + 0x220,0x3e0,0x240,0x020,0x020,0x000,0x000,0x000, // r + 0x000,0x260,0x2a0,0x2a0,0x320,0x000,0x000,0x000, // s + 0x000,0x020,0x1f8,0x220,0x200,0x000,0x000,0x000, // t + 0x020,0x1e0,0x200,0x220,0x3e0,0x200,0x000,0x000, // u + 0x020,0x0e0,0x320,0x180,0x060,0x020,0x000,0x000, // v + 0x060,0x380,0x0e0,0x380,0x060,0x000,0x000,0x000, // w + 0x220,0x360,0x080,0x360,0x220,0x000,0x000,0x000, // x + 0x820,0x8e0,0x720,0x180,0x060,0x020,0x000,0x000, // y + 0x000,0x220,0x3a0,0x260,0x220,0x000,0x000,0x000, // z + 0x000,0x000,0x020,0x7de,0x402,0x000,0x000,0x000, // { + 0x000,0x000,0x000,0xfff,0x000,0x000,0x000,0x000, // | + 0x000,0x402,0x7de,0x020,0x000,0x000,0x000,0x000, // } + 0x002,0x001,0x002,0x004,0x004,0x002,0x000,0x000 // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/Font8X16.java b/src/devices/Oled/SSD1306/Font8X16.java new file mode 100644 index 0000000..c2b6f85 --- /dev/null +++ b/src/devices/Oled/SSD1306/Font8X16.java @@ -0,0 +1,114 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font8X16") +public class Font8X16 extends FontPack { + + public Font8X16() { + super("Font8X16", 8, 16, 9, 18, 32, 126); + data = new int[] { + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // + 0x0000,0x0000,0x0000,0x33ff,0x0000,0x0000,0x0000,0x0000, // ! + 0x0000,0x0000,0x003f,0x0000,0x0000,0x003f,0x0000,0x0000, // " + 0x0000,0x0330,0x3fff,0x0330,0x0330,0x3fff,0x0330,0x0000, // # + 0x0000,0x0c30,0x0ccc,0x3fff,0x0ccc,0x030c,0x0000,0x0000, // $ + 0x0000,0x303c,0x0c3c,0x0300,0x00c0,0x3c30,0x3c0c,0x0000, // % + 0x0000,0x0f3c,0x30c3,0x30c3,0x333c,0x0c00,0x3300,0x0000, // & + 0x0000,0x0000,0x0000,0x003f,0x0000,0x0000,0x0000,0x0000, // ' + 0x0000,0x03f0,0x0c0c,0x3003,0x0000,0x0000,0x0000,0x0000, // ( + 0x0000,0x0000,0x0000,0x3003,0x0c0c,0x03f0,0x0000,0x0000, // ) + 0x0000,0x0c0c,0x0330,0x3fff,0x0330,0x0c0c,0x0000,0x0000, // * + 0x0000,0x00c0,0x00c0,0x0ffc,0x00c0,0x00c0,0x0000,0x0000, // + + 0x0000,0x0000,0x0000,0x3000,0x0f00,0x0000,0x0000,0x0000, // , + 0x0000,0x00c0,0x00c0,0x00c0,0x00c0,0x00c0,0x00c0,0x0000, // - + 0x0000,0x0000,0x0000,0x3000,0x0000,0x0000,0x0000,0x0000, // . + 0x0000,0x3000,0x0c00,0x0300,0x00c0,0x0030,0x000c,0x0000, // / + + 0x0000,0x0ffc,0x3303,0x30c3,0x30c3,0x3033,0x0ffc,0x0000, // 0 + 0x0000,0x0000,0x0000,0x300c,0x3fff,0x3000,0x0000,0x0000, // 1 + 0x0000,0x3c0c,0x3303,0x30c3,0x30c3,0x30c3,0x303c,0x0000, // 2 + 0x0000,0x0c03,0x3003,0x30c3,0x30c3,0x30f3,0x0f0f,0x0000, // 3 + 0x0000,0x0300,0x03c0,0x0330,0x030c,0x3fff,0x0300,0x0000, // 4 + 0x0000,0x0c3f,0x3033,0x3033,0x3033,0x3033,0x0fc3,0x0000, // 5 + 0x0000,0x0ff0,0x30cc,0x30c3,0x30c3,0x30c3,0x0f03,0x0000, // 6 + 0x0000,0x0003,0x0003,0x3f03,0x00c3,0x0033,0x000f,0x0000, // 7 + 0x0000,0x0f3c,0x30c3,0x30c3,0x30c3,0x30c3,0x0f3c,0x0000, // 8 + 0x0000,0x303c,0x30c3,0x30c3,0x30c3,0x0cc3,0x03fc,0x0000, // 9 + 0x0000,0x0000,0x0000,0x0330,0x0000,0x0000,0x0000,0x0000, // : + 0x0000,0x0000,0x0000,0x1000,0x0f30,0x0000,0x0000,0x0000, // ; + 0x0000,0x0000,0x00c0,0x0330,0x0c0c,0x3003,0x0000,0x0000, // < + 0x0000,0x0330,0x0330,0x0330,0x0330,0x0330,0x0330,0x0000, // = + 0x0000,0x0000,0x3003,0x0c0c,0x0330,0x00c0,0x0000,0x0000, // > + 0x0000,0x000c,0x0003,0x0000,0x0000,0x0000,0x0000,0x0000, // ? + + 0x0000,0x0ffc,0x3003,0x30c3,0x3333,0x33c3,0x30fc,0x0000, // @ + 0x0000,0x3ff0,0x030c,0x0303,0x0303,0x030c,0x3ff0,0x0000, // A + 0x0000,0x3fff,0x30c3,0x30c3,0x30c3,0x30c3,0x0f3c,0x0000, // B + 0x0000,0x0ffc,0x3003,0x3003,0x3003,0x3003,0x0c0c,0x0000, // C + 0x0000,0x3fff,0x3003,0x3003,0x3003,0x3003,0x0ffc,0x0000, // D + 0x0000,0x3fff,0x30c3,0x30c3,0x30c3,0x30c3,0x3003,0x0000, // E + 0x0000,0x3fff,0x00c3,0x00c3,0x00c3,0x00c3,0x0003,0x0000, // F + 0x0000,0x0ffc,0x3003,0x3003,0x3183,0x3183,0x3f83,0x0000, // G + 0x0000,0x3fff,0x00c0,0x00c0,0x00c0,0x00c0,0x3fff,0x0000, // H + 0x0000,0x0000,0x3003,0x3fff,0x3003,0x0000,0x0000,0x0000, // I + 0x0000,0x0c00,0x3000,0x3000,0x3000,0x3000,0x0fff,0x0000, // J + 0x0000,0x3fff,0x00c0,0x00c0,0x0330,0x0c0c,0x3003,0x0000, // K + 0x0000,0x3fff,0x3000,0x3000,0x3000,0x3000,0x3000,0x0000, // L + 0x0000,0x3fff,0x000c,0x00f0,0x00f0,0x000c,0x3fff,0x0000, // M + 0x0000,0x3fff,0x000c,0x00f0,0x03c0,0x0c00,0x3fff,0x0000, // N + 0x0000,0x0ffc,0x3003,0x3003,0x3003,0x3003,0x0ffc,0x0000, // O + + 0x0000,0x3fff,0x00c3,0x00c3,0x00c3,0x00c3,0x003c,0x0000, // P + 0x0000,0x0ffc,0x3003,0x3003,0x3303,0x0c03,0x33fc,0x0000, // Q + 0x0000,0x3fff,0x00c3,0x00c3,0x03c3,0x0cc3,0x303c,0x0000, // R + 0x0000,0x0c3c,0x30c3,0x30c3,0x30c3,0x30c3,0x0f0c,0x0000, // S + 0x0000,0x0003,0x0003,0x3fff,0x0003,0x0003,0x0000,0x0000, // T + 0x0000,0x0fff,0x3000,0x3000,0x3000,0x3000,0x0fff,0x0000, // U + 0x0000,0x03ff,0x0c00,0x3000,0x3000,0x0c00,0x03ff,0x0000, // V + 0x0000,0x3fff,0x0c00,0x03c0,0x03c0,0x0c00,0x3fff,0x0000, // W + 0x0000,0x3c0f,0x0330,0x00c0,0x00c0,0x0330,0x3c0f,0x0000, // X + 0x0000,0x000f,0x0030,0x3fc0,0x0030,0x000f,0x0000,0x0000, // Y + 0x0000,0x3c03,0x3303,0x30c3,0x30c3,0x3033,0x300f,0x0000, // Z + 0x0000,0x3fff,0x3fff,0x3003,0x3003,0x3003,0x3003,0x0000, // [ + 0x0000,0x000c,0x0030,0x00c0,0x0300,0x0c00,0x3000,0x0000, // + 0x0000,0x3003,0x3003,0x3003,0x3003,0x3fff,0x3fff,0x0000, // ] + 0x0000,0x0300,0x00c0,0x0030,0x00c0,0x0300,0x0000,0x0000, // ^ + 0x0000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x0000, // _ + + 0x0000,0x0000,0x0003,0x000c,0x0030,0x0000,0x0000,0x0000, // ` + 0x0000,0x0c00,0x3330,0x3330,0x3330,0x3330,0x3fc0,0x0000, // a + 0x0000,0x3fff,0x3030,0x3030,0x3030,0x3030,0x0fc0,0x0000, // b + 0x0000,0x0fc0,0x3030,0x3030,0x3030,0x3030,0x3030,0x0000, // c + 0x0000,0x0fc0,0x3030,0x3030,0x3030,0x3030,0x3fff,0x0000, // d + 0x0000,0x0fc0,0x3330,0x3330,0x3330,0x3330,0x33c0,0x0000, // e + 0x0000,0x00c0,0x3ffc,0x00c3,0x00c3,0x00c3,0x000c,0x0000, // f + 0x0000,0x03c0,0xcc30,0xcc30,0xcc30,0xcc30,0x3fc0,0x0000, // g + 0x0000,0x3fff,0x0030,0x0030,0x0030,0x0030,0x3fc0,0x0000, // h + 0x0000,0x0000,0x3030,0x3ff3,0x3000,0x0000,0x0000,0x0000, // i + 0x0000,0x3000,0xc030,0xc030,0xc030,0x3ff3,0x0000,0x0000, // j + 0x0000,0x3fff,0x0300,0x0300,0x0300,0x0cc0,0x3030,0x0000, // k + 0x0000,0x0000,0x3003,0x3fff,0x3000,0x0000,0x0000,0x0000, // l + 0x0000,0x3ff0,0x0030,0x0fc0,0x0fc0,0x0030,0x3ff0,0x0000, // m + 0x0000,0x3ff0,0x0030,0x0030,0x0030,0x0030,0x3fc0,0x0000, // n + 0x0000,0x0fc0,0x3030,0x3030,0x3030,0x3030,0x0fc0,0x0000, // o + + 0x0000,0xfff0,0x0c30,0x0c30,0x0c30,0x0c30,0x03c0,0x0000, // p + 0x0000,0x03c0,0x0c30,0x0c30,0x0c30,0x0c30,0xfff0,0x0000, // q + 0x0000,0x3ff0,0x00c0,0x0030,0x0030,0x0030,0x0030,0x0000, // r + 0x0000,0x30c0,0x3330,0x3330,0x3330,0x3330,0x0c30,0x0000, // s + 0x0000,0x0030,0x0030,0x0fff,0x3030,0x3030,0x0c00,0x0000, // t + 0x0000,0x0ff0,0x3000,0x3000,0x3000,0x0c00,0x3ff0,0x0000, // u + 0x0000,0x03f0,0x0c00,0x3000,0x3000,0x0c00,0x03f0,0x0000, // v + 0x0000,0x3ff0,0x3000,0x0f00,0x0f00,0x3000,0x3ff0,0x0000, // w + 0x0000,0x3030,0x0cc0,0x0300,0x0300,0x0cc0,0x3030,0x0000, // x + 0x0000,0x03f0,0xcc00,0xcc00,0xcc00,0xcc00,0x3ff0,0x0000, // y + 0x0000,0x3030,0x3c30,0x3330,0x3330,0x30f0,0x3030,0x0000, // z + 0x0000,0x00c0,0x00c0,0x0ffc,0x3f3f,0x3003,0x3003,0x0000, // { + 0x0000,0x0000,0x0000,0x3fff,0x0000,0x0000,0x0000,0x0000, // | + 0x0000,0x3003,0x3003,0x3f3f,0x0ffc,0x00c0,0x00c0,0x0000, // } + 0x0000,0x03c0,0x0030,0x00c0,0x0300,0x00f0,0x0000,0x0000 // ~ + }; + } + +} diff --git a/src/devices/Oled/SSD1306/FontPack.java b/src/devices/Oled/SSD1306/FontPack.java new file mode 100644 index 0000000..47a9eec --- /dev/null +++ b/src/devices/Oled/SSD1306/FontPack.java @@ -0,0 +1,134 @@ +package devices.Oled.SSD1306; + + + +// Source : https://lexus2k.github.io/ssd1306/ssd1306__fonts_8c_source.html +// Source : https://github.com/lynniemagoo/oled-font-pack/tree/master/fonts +// Source : http://www.rinkydinkelectronics.com/r_fonts.php + +/** + * Base Class for OLED Fonts + * Do not create variable from it in B4X + * @author rdkartono + * + */ +public abstract class FontPack { + private final String fontname; + private final int width; + private final int height; + private final int outer_width; + private final int outer_height; + private final int min_ascii_code; + private final int max_ascii_code; + protected int[] data; + + /** + * Initialize FontPack + * @param name Name of font + * @param width width of a character in pixel + * @param height height of a character in pixel + * @param outerwidth width plus extra pixel for spacing , of a character + * @param outerheight height plus extra pixel for spacing, of a character + * @param minchar minimum ASCII code value, usually 32 (space) + * @param maxchar maximum ASCII code value, usually 127 + */ + public FontPack(String name, int width, int height, int outerwidth, int outerheight, int minchar, int maxchar) { + this.fontname = name; + this.width = width; + this.height = height; + this.outer_width = outerwidth; + this.outer_height = outerheight; + this.min_ascii_code = minchar; + this.max_ascii_code = maxchar; + } + + /** + * Get Font Name + * @return font name + */ + public String getName() { + return fontname; + } + + /** + * Get width of a character + * @return width in pixel + */ + public int getWidth() { + return width; + } + + /** + * Get height of a character + * @return height in pixel + */ + public int getHeight() { + return height; + } + + /** + * Get width + extra pixel for spacing, of a character + * @return width in pixel + */ + public int getOuterWidth() { + return outer_width; + } + + /** + * Get Height + extra pixel for spacing, of a character + * @return height in pixel + */ + public int getOuterHeight() { + return outer_height; + } + + /** + * Get minimum supported ascii code, usually 32 , which is space + * @return ascii code + */ + public int getMinChar() { + return min_ascii_code; + } + + /** + * Get maximum supported ascii code, usually 127 + * @return ascii code + */ + public int getMaxChar() { + return max_ascii_code; + } + + /** + * Draw a character on specific coordinate + * @param display OLED display to draw the character + * @param c character to display + * @param x top left coordinate + * @param y top left coordinate + * @param on if true, will turn ON the pixel at character X Y + */ + public void drawChar(OLEDDisplay display, char c, int x, int y, boolean on) { + if (c < getMinChar()) return; + if (c > getMaxChar()) return; + if (display==null) return; + if (x < 1) return; + if (y < 1) return; + if (x > display.getWidth()) return; + if (y > display.getHeight()) return; + + //System.out.println("Drawing char "+c); + c-= getMinChar(); // if c = min_ascii_code , will point to index 0 at data + //System.out.println("Ascii code = "+(int)c); + for(int ii=0;ii + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2ffc,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ! + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x003c,0x0000,0x0000,0x003c,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // " + 0x0000,0x0000,0x0440,0x3440,0x0f40,0x04f0,0x344c,0x0f40,0x04f0,0x044c,0x0440,0x0000,0x0000,0x0000,0x0000,0x0000, // # + 0x0000,0x0000,0x0000,0x0000,0x0870,0x1088,0x2084,0x7ffe,0x2104,0x1104,0x0e18,0x0000,0x0000,0x0000,0x0000,0x0000, // $ + 0x0000,0x0000,0x00f8,0x0104,0x0104,0x3104,0x0cf8,0x0200,0x0180,0x0060,0x1f18,0x2084,0x2080,0x2080,0x1f00,0x0000, // % + 0x0000,0x0000,0x0000,0x0000,0x0e00,0x1100,0x20b8,0x20c4,0x2184,0x2644,0x1838,0x1400,0x2200,0x0000,0x0000,0x0000, // & + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x003c,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ' + 0x0000,0x0000,0x0000,0x0000,0x0000,0x07f0,0x180c,0x2002,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ( + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2002,0x180c,0x07f0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ) + 0x0000,0x0000,0x0000,0x0080,0x00c0,0x0c80,0x0f80,0x03f0,0x03f0,0x0780,0x0d80,0x00c0,0x0080,0x0000,0x0000,0x0000, // * + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0100,0x0100,0x0100,0x0fe0,0x0100,0x0100,0x0100,0x0000,0x0000,0x0000,0x0000, // + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // , + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0100,0x0100,0x0100,0x0100,0x0100,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // - + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // . + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3000,0x0e00,0x01c0,0x0038,0x0004,0x0000,0x0000,0x0000,0x0000,0x0000, // / + + 0x0000,0x0000,0x0000,0x0000,0x0ff0,0x1008,0x2004,0x2004,0x2004,0x1008,0x0ff0,0x0000,0x0000,0x0000,0x0000,0x0000, // 0 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0020,0x0010,0x0008,0x3ffc,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // 1 + 0x0000,0x0000,0x0000,0x0000,0x2010,0x3008,0x2804,0x2404,0x2204,0x218c,0x2070,0x0000,0x0000,0x0000,0x0000,0x0000, // 2 + 0x0000,0x0000,0x0000,0x0000,0x1810,0x1008,0x2084,0x2084,0x20c4,0x1138,0x0e00,0x0000,0x0000,0x0000,0x0000,0x0000, // 3 + 0x0000,0x0000,0x0000,0x0000,0x0600,0x0500,0x04c0,0x0430,0x0408,0x3ffc,0x0400,0x0400,0x0000,0x0000,0x0000,0x0000, // 4 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x08e0,0x105c,0x2044,0x2044,0x2044,0x1084,0x0f04,0x0000,0x0000,0x0000, // 5 + 0x0000,0x0000,0x0000,0x0000,0x0ff0,0x1088,0x2044,0x2044,0x2044,0x1088,0x0f18,0x0000,0x0000,0x0000,0x0000,0x0000, // 6 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0004,0x0004,0x3804,0x0784,0x0064,0x001c,0x0004,0x0000,0x0000,0x0000, // 7 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0e30,0x1148,0x2084,0x2084,0x2084,0x1148,0x0e30,0x0000,0x0000,0x0000, // 8 + 0x0000,0x0000,0x0000,0x0000,0x08f0,0x3108,0x2204,0x2204,0x2204,0x1108,0x0ff0,0x0000,0x0000,0x0000,0x0000,0x0000, // 9 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0808,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // : + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3808,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ; + 0x0000,0x0000,0x0000,0x0000,0x0080,0x0140,0x0140,0x0220,0x0220,0x0410,0x0410,0x0808,0x0000,0x0000,0x0000,0x0000, // < + 0x0000,0x0000,0x0000,0x0000,0x0220,0x0220,0x0220,0x0220,0x0220,0x0220,0x0220,0x0220,0x0000,0x0000,0x0000,0x0000, // = + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0808,0x0410,0x0410,0x0220,0x0220,0x0140,0x0140,0x0080,0x0000,0x0000,0x0000, // > + 0x0000,0x0000,0x0000,0x0000,0x0030,0x0008,0x0004,0x2e04,0x0104,0x0088,0x0070,0x0000,0x0000,0x0000,0x0000,0x0000, // ? + + 0x0000,0x0000,0x03e0,0x0418,0x09c4,0x1224,0x1212,0x1212,0x13e2,0x1232,0x0a04,0x090c,0x04f0,0x0000,0x0000,0x0000, // @ + 0x0000,0x0000,0x3000,0x0c00,0x0300,0x02e0,0x0218,0x0204,0x0218,0x02e0,0x0300,0x0c00,0x3000,0x0000,0x0000,0x0000, // A + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x2084,0x2084,0x2084,0x2084,0x2084,0x2084,0x11c8,0x0e30,0x0000,0x0000,0x0000, // B + 0x0000,0x0000,0x0000,0x07e0,0x0810,0x1008,0x2004,0x2004,0x2004,0x2004,0x2004,0x1008,0x0810,0x0000,0x0000,0x0000, // C + 0x0000,0x0000,0x0000,0x3ffc,0x2004,0x2004,0x2004,0x2004,0x2004,0x2004,0x1008,0x0810,0x07e0,0x0000,0x0000,0x0000, // D + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x2084,0x2084,0x2084,0x2084,0x2084,0x2084,0x2084,0x2004,0x0000,0x0000,0x0000, // E + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x0084,0x0084,0x0084,0x0084,0x0084,0x0084,0x0004,0x0000,0x0000,0x0000,0x0000, // F + 0x0000,0x0000,0x0000,0x0000,0x07e0,0x0810,0x1008,0x2004,0x2004,0x2104,0x2104,0x1108,0x0910,0x0700,0x0000,0x0000, // G + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x0080,0x0080,0x0080,0x0080,0x0080,0x0080,0x0080,0x3ffc,0x0000,0x0000,0x0000, // H + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3ffc,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // I + 0x0000,0x0000,0x0000,0x0000,0x0c00,0x3000,0x2000,0x2000,0x2000,0x1000,0x0ffc,0x0000,0x0000,0x0000,0x0000,0x0000, // J + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x0200,0x0100,0x0080,0x00c0,0x0120,0x0610,0x1808,0x2004,0x0000,0x0000,0x0000, // K + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x2000,0x2000,0x2000,0x2000,0x2000,0x2000,0x0000,0x0000,0x0000,0x0000,0x0000, // L + 0x0000,0x0000,0x3ffc,0x0018,0x0060,0x0380,0x0c00,0x3000,0x0e00,0x0180,0x0060,0x0018,0x3ffc,0x0000,0x0000,0x0000, // M + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x0008,0x0030,0x0040,0x0180,0x0200,0x0c00,0x1000,0x3ffc,0x0000,0x0000,0x0000, // N + 0x0000,0x0000,0x0000,0x07e0,0x0810,0x1008,0x2004,0x2004,0x2004,0x2004,0x1008,0x0810,0x07e0,0x0000,0x0000,0x0000, // O + + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x0104,0x0104,0x0104,0x0104,0x0104,0x0104,0x0088,0x0070,0x0000,0x0000,0x0000, // P + 0x0000,0x0000,0x0000,0x07e0,0x0810,0x1008,0x2004,0x2004,0x2804,0x2804,0x1008,0x3810,0x2fe0,0x0000,0x0000,0x0000, // Q + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x0104,0x0104,0x0104,0x0104,0x0304,0x0d04,0x1088,0x2070,0x0000,0x0000,0x0000, // R + 0x0000,0x0000,0x0000,0x0000,0x0c30,0x1048,0x2084,0x2084,0x2084,0x2104,0x2104,0x1108,0x0e10,0x0000,0x0000,0x0000, // S + 0x0000,0x0000,0x0000,0x0000,0x0004,0x0004,0x0004,0x0004,0x3ffc,0x0004,0x0004,0x0004,0x0004,0x0000,0x0000,0x0000, // T + 0x0000,0x0000,0x0000,0x0000,0x0ffc,0x1000,0x2000,0x2000,0x2000,0x2000,0x2000,0x1000,0x0ffc,0x0000,0x0000,0x0000, // U + 0x0000,0x0000,0x0000,0x0000,0x000c,0x0070,0x0380,0x0c00,0x3000,0x0c00,0x0380,0x0070,0x000c,0x0000,0x0000,0x0000, // V + 0x0000,0x0000,0x000c,0x00f0,0x0f00,0x3000,0x0f00,0x00f8,0x000c,0x00f0,0x0f00,0x3000,0x0f00,0x00f0,0x000c,0x0000, // W + 0x0000,0x0000,0x2000,0x1004,0x0808,0x0630,0x0140,0x0080,0x0140,0x0630,0x0808,0x1004,0x2000,0x0000,0x0000,0x0000, // X + 0x0000,0x0000,0x0000,0x0000,0x0004,0x0018,0x0020,0x00c0,0x3f00,0x00c0,0x0020,0x0018,0x0004,0x0000,0x0000,0x0000, // Y + 0x0000,0x0000,0x0000,0x0000,0x2000,0x3004,0x2c04,0x2204,0x2184,0x2044,0x2034,0x200c,0x2004,0x0000,0x0000,0x0000, // Z + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7ffe,0x4002,0x4002,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // [ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0004,0x0038,0x01c0,0x0e00,0x3000,0x0000,0x0000,0x0000,0x0000,0x0000, // + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4002,0x4002,0x7ffe,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ] + 0x0000,0x0000,0x0000,0x0000,0x0080,0x0070,0x000c,0x0002,0x000c,0x0070,0x0080,0x0000,0x0000,0x0000,0x0000,0x0000, // ^ + 0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000, // _ + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0004,0x0008,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ` + 0x0000,0x0000,0x0000,0x0000,0x0e40,0x1120,0x1110,0x1110,0x1090,0x0890,0x1fe0,0x0000,0x0000,0x0000,0x0000,0x0000, // a + 0x0000,0x0000,0x0000,0x0000,0x1ffe,0x0820,0x1010,0x1010,0x1010,0x0820,0x07c0,0x0000,0x0000,0x0000,0x0000,0x0000, // b + 0x0000,0x0000,0x0000,0x0000,0x07c0,0x0820,0x1010,0x1010,0x1010,0x0820,0x0440,0x0000,0x0000,0x0000,0x0000,0x0000, // c + 0x0000,0x0000,0x0000,0x0000,0x07c0,0x0820,0x1010,0x1010,0x1010,0x0820,0x1ffe,0x0000,0x0000,0x0000,0x0000,0x0000, // d + 0x0000,0x0000,0x0000,0x0000,0x07c0,0x0920,0x1110,0x1110,0x1110,0x0920,0x05c0,0x0000,0x0000,0x0000,0x0000,0x0000, // e + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0010,0x1ffc,0x0012,0x0012,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // f + 0x0000,0x0000,0x0000,0x0000,0x23e0,0x4410,0x4808,0x4808,0x4808,0x2410,0x1ff8,0x0000,0x0000,0x0000,0x0000,0x0000, // g + 0x0000,0x0000,0x0000,0x0000,0x1ffe,0x0020,0x0010,0x0010,0x0010,0x0010,0x1fe0,0x0000,0x0000,0x0000,0x0000,0x0000, // h + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1fe4,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // i + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x8000,0x8000,0x7fe4,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // j + 0x0000,0x0000,0x0000,0x0000,0x0000,0x1ffe,0x0200,0x0100,0x0180,0x0240,0x0c20,0x1010,0x0000,0x0000,0x0000,0x0000, // k + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1ffe,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // l + 0x0000,0x0000,0x1ff0,0x0020,0x0010,0x0010,0x0010,0x1fe0,0x0020,0x0010,0x0010,0x0010,0x1fe0,0x0000,0x0000,0x0000, // m + 0x0000,0x0000,0x0000,0x0000,0x1ff0,0x0020,0x0010,0x0010,0x0010,0x0010,0x1fe0,0x0000,0x0000,0x0000,0x0000,0x0000, // n + 0x0000,0x0000,0x0000,0x0000,0x07c0,0x0820,0x1010,0x1010,0x1010,0x0820,0x07c0,0x0000,0x0000,0x0000,0x0000,0x0000, // o + + 0x0000,0x0000,0x0000,0x0000,0xfff0,0x0820,0x1010,0x1010,0x1010,0x0820,0x07c0,0x0000,0x0000,0x0000,0x0000,0x0000, // p + 0x0000,0x0000,0x0000,0x0000,0x07c0,0x0820,0x1010,0x1010,0x1010,0x0820,0xfff0,0x0000,0x0000,0x0000,0x0000,0x0000, // q + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1ff0,0x0020,0x0010,0x0010,0x0010,0x0000,0x0000,0x0000,0x0000,0x0000, // r + 0x0000,0x0000,0x0000,0x0000,0x0000,0x08e0,0x1110,0x1110,0x1110,0x1110,0x0e20,0x0000,0x0000,0x0000,0x0000,0x0000, // s + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0010,0x1ffe,0x1010,0x1010,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // t + 0x0000,0x0000,0x0000,0x0000,0x0ff0,0x1000,0x1000,0x1000,0x1000,0x0800,0x1ff0,0x0000,0x0000,0x0000,0x0000,0x0000, // u + 0x0000,0x0000,0x0000,0x0000,0x0030,0x01c0,0x0600,0x1800,0x0600,0x01c0,0x0030,0x0000,0x0000,0x0000,0x0000,0x0000, // v + 0x0000,0x0000,0x0030,0x07c0,0x1800,0x0700,0x00c0,0x0030,0x00c0,0x0700,0x1800,0x07c0,0x0030,0x0000,0x0000,0x0000, // w + 0x0000,0x0000,0x0000,0x0000,0x1010,0x0820,0x06c0,0x0100,0x06c0,0x0820,0x1010,0x0000,0x0000,0x0000,0x0000,0x0000, // x + 0x0000,0x0000,0x0000,0x0000,0x8070,0x8380,0x4c00,0x3000,0x0e00,0x0380,0x0070,0x0000,0x0000,0x0000,0x0000,0x0000, // y + 0x0000,0x0000,0x0000,0x0000,0x0000,0x1010,0x1810,0x1610,0x1110,0x10d0,0x1030,0x1010,0x0000,0x0000,0x0000,0x0000, // z + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0080,0x0080,0x3f7e,0x4001,0x4001,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // { + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xffff,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // | + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4001,0x4001,0x3f7e,0x0080,0x0080,0x0000,0x0000,0x0000,0x0000,0x0000, // } + 0x0000,0x0000,0x0000,0x0000,0x0008,0x0004,0x0004,0x0004,0x000c,0x0008,0x0008,0x0008,0x0004,0x0000,0x0000,0x0000 // ~ + }; + } + +} diff --git a/src/devices/Oled/SSD1306/Font_Arial_16X24.java b/src/devices/Oled/SSD1306/Font_Arial_16X24.java new file mode 100644 index 0000000..73fa3a9 --- /dev/null +++ b/src/devices/Oled/SSD1306/Font_Arial_16X24.java @@ -0,0 +1,113 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font_Arial_16X24") +public class Font_Arial_16X24 extends FontPack{ + + public Font_Arial_16X24() { + super("Font_Arial_16X24", 16,24,16,24,32,126); + data = new int[] { + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0303fe,0x079fff,0x079fff,0x0301fe,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ! + 0x000000,0x000000,0x000000,0x000000,0x0000f0,0x0001fc,0x0001cc,0x000000,0x000000,0x0000f0,0x0001fc,0x0001cc,0x000000,0x000000,0x000000,0x000000, // " + 0x000000,0x000000,0x0030c0,0x07f0c0,0x07fec0,0x01fff0,0x003ffc,0x0030fc,0x07f0c0,0x07ffc0,0x00fff0,0x0037fc,0x0030fc,0x0030c0,0x000000,0x000000, // # + 0x000000,0x000000,0x01e1e0,0x03e3f8,0x03c7f8,0x07073c,0x060e1c,0x3fffff,0x3fffff,0x060c1c,0x071c78,0x03fcf8,0x03f8f0,0x00f000,0x000000,0x000000, // $ + 0x0000fc,0x0001fe,0x000106,0x018106,0x00e1fe,0x0070f8,0x001c00,0x000700,0x0001c0,0x007c70,0x01fe1c,0x018206,0x018200,0x01fe00,0x007c00,0x000000, // % + 0x000000,0x01e000,0x03f000,0x03f9c0,0x071be0,0x060ff0,0x061e30,0x067e30,0x07f3f0,0x03e3e0,0x01c1c0,0x03f000,0x077800,0x071000,0x020000,0x000000, // & + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x00000c,0x00000f,0x000007,0x000001,0x000000,0x000000,0x000000,0x000000,0x000000, // ' + 0x000000,0x000000,0x000000,0x000000,0x000000,0x003fc0,0x00fff0,0x03fffc,0x07c03e,0x0e0007,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ( + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0e0007,0x07c03e,0x03fffc,0x00fff0,0x003fc0,0x000000,0x000000,0x000000,0x000000,0x000000, // ) + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000c00,0x00cc00,0x00e800,0x003f80,0x003f80,0x006800,0x00cc00,0x000c00,0x000000,0x000000,0x000000, // * + 0x000000,0x000000,0x000000,0x000c00,0x000c00,0x000c00,0x000c00,0x00ffc0,0x00ffc0,0x000c00,0x000c00,0x000c00,0x000c00,0x000000,0x000000,0x000000, // + + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x270000,0x3f0000,0x1e0000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // , + 0x000000,0x000000,0x000000,0x000000,0x000c00,0x000c00,0x000c00,0x000c00,0x000c00,0x000c00,0x000c00,0x000c00,0x000000,0x000000,0x000000,0x000000, // - + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x070000,0x070000,0x070000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // . + 0x000000,0x000000,0x000000,0x000000,0x000000,0x070000,0x07f000,0x03ff00,0x007ff0,0x0007fe,0x00007f,0x000007,0x000000,0x000000,0x000000,0x000000, // / + + 0x000000,0x000000,0x007fe0,0x01fff8,0x03fffc,0x07801e,0x07000e,0x07000e,0x07000e,0x07000e,0x07801e,0x03fffc,0x01fff8,0x007fe0,0x000000,0x000000, // 0 + 0x000000,0x000000,0x000000,0x0001c0,0x0001c0,0x0000e0,0x000070,0x000078,0x07fffc,0x07fffe,0x07fffe,0x000000,0x000000,0x000000,0x000000,0x000000, // 1 + 0x000000,0x000000,0x078070,0x07c07c,0x07e07c,0x07f01e,0x07780e,0x07380e,0x071c0e,0x070e0e,0x070f1e,0x0707fc,0x0703fc,0x0600f0,0x000000,0x000000, // 2 + 0x000000,0x000000,0x00f030,0x01f038,0x03e03c,0x07801e,0x07000e,0x07030e,0x07070e,0x07879e,0x07cffe,0x03fffc,0x01fcf8,0x007800,0x000000,0x000000, // 3 + 0x000000,0x000000,0x007800,0x007c00,0x007f00,0x006780,0x0061e0,0x0060f0,0x00603c,0x07fffe,0x07fffe,0x07fffc,0x006000,0x006000,0x000000,0x000000, // 4 + 0x000000,0x000000,0x01c000,0x03c7fe,0x03c7fe,0x0787fe,0x07038e,0x07038e,0x07038e,0x07038e,0x07878e,0x03ff0e,0x01fe0e,0x007c06,0x000000,0x000000, // 5 + 0x000000,0x000000,0x007fe0,0x01fff8,0x03fffc,0x038e3c,0x07070e,0x07070e,0x07070e,0x07070e,0x078f1e,0x03fe3c,0x01fc38,0x00f800,0x000000,0x000000, // 6 + 0x000000,0x000000,0x000006,0x00000e,0x00000e,0x07e00e,0x07fc0e,0x03ff0e,0x003f8e,0x0003ee,0x0000fe,0x00007e,0x00001e,0x00000e,0x000000,0x000000, // 7 + 0x000000,0x000000,0x00f800,0x01fcf8,0x03fffc,0x0787fe,0x07039e,0x07030e,0x07030e,0x07039e,0x0787fe,0x03fffc,0x01fcf8,0x00f800,0x000000,0x000000, // 8 + 0x000000,0x000000,0x0001f0,0x01c3f8,0x03c7fc,0x078f1e,0x070e0e,0x070e0e,0x070e0e,0x070e0e,0x03c71c,0x03fffc,0x01fff8,0x007fe0,0x000000,0x000000, // 9 + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x01c0e0,0x01c0e0,0x01c0e0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // : + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x670380,0x3f0380,0x1e0380,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ; + 0x000000,0x000000,0x000000,0x000e00,0x001f00,0x001f00,0x003b80,0x003b80,0x0071c0,0x0071c0,0x00e0e0,0x00e0e0,0x01e0f0,0x000000,0x000000,0x000000, // < + 0x000000,0x000000,0x00e700,0x00e700,0x00e700,0x00e700,0x00e700,0x00e700,0x00e700,0x00e700,0x00e700,0x00e700,0x00e700,0x00e700,0x000000,0x000000, // = + 0x000000,0x000000,0x000000,0x00f078,0x007070,0x007070,0x0038e0,0x0038e0,0x001dc0,0x001dc0,0x000f80,0x000f80,0x000700,0x000000,0x000000,0x000000, // > + 0x000000,0x000000,0x000078,0x00007c,0x00003e,0x00000f,0x079c07,0x07be07,0x079f07,0x00078f,0x0003fe,0x0001fe,0x000078,0x000000,0x000000,0x000000, // ? + + 0x000000,0x01fc00,0x070300,0x0cf8c0,0x19fc60,0x130e60,0x330330,0x330330,0x318330,0x33fe30,0x33ff30,0x330760,0x198060,0x1cc1c0,0x043f00,0x000000, // @ + 0x000000,0x070000,0x07e000,0x03fc00,0x00ff80,0x007fe0,0x0063f8,0x00607c,0x00607c,0x0067f8,0x007fe0,0x00ff00,0x07fc00,0x07e000,0x070000,0x000000, // A + 0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x06060c,0x06060c,0x06060c,0x06060c,0x06061c,0x070ffc,0x07fff8,0x03f9f0,0x01f000,0x000000,0x000000, // B + 0x000000,0x003f80,0x00ffe0,0x01fff0,0x03c0f8,0x07803c,0x07001c,0x06000c,0x06000c,0x07001c,0x07803c,0x03c078,0x01f0f0,0x00f0e0,0x000000,0x000000, // C + 0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x06000c,0x06000c,0x06000c,0x07001c,0x07803c,0x03c078,0x03fff8,0x00ffe0,0x003f80,0x000000,0x000000, // D + 0x000000,0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x06060c,0x06060c,0x06060c,0x06060c,0x06060c,0x06060c,0x06060c,0x06000c,0x000000,0x000000, // E + 0x000000,0x000000,0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x00060c,0x00060c,0x00060c,0x00060c,0x00060c,0x00060c,0x00000c,0x000000,0x000000, // F + 0x000000,0x003f80,0x00ffe0,0x01fff0,0x03c078,0x030018,0x06000c,0x06000c,0x060c0c,0x060c0c,0x070c1c,0x070c38,0x03fcf8,0x03fcf0,0x01fc40,0x000000, // G + 0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x000600,0x000600,0x000600,0x000600,0x000600,0x000600,0x07fffc,0x07fffc,0x07fffc,0x000000,0x000000, // H + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // I + 0x000000,0x00f000,0x03f000,0x03f000,0x078000,0x070000,0x070000,0x078000,0x03fffc,0x03fffc,0x00fffc,0x000000,0x000000,0x000000,0x000000,0x000000, // J + 0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x001e00,0x000f00,0x000780,0x000fc0,0x003fe0,0x00fcf0,0x01f878,0x07e03c,0x07c01c,0x070000,0x000000, // K + 0x000000,0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x060000,0x060000,0x060000,0x060000,0x060000,0x060000,0x060000,0x000000,0x000000,0x000000, // L + 0x07fffc,0x07fffc,0x07fffc,0x00007c,0x0007fc,0x007ff0,0x03ff00,0x07f000,0x07f000,0x03ff00,0x007ff0,0x0007fc,0x00007c,0x07fffc,0x07fffc,0x07fffc, // M + 0x000000,0x000000,0x07fff8,0x07fffc,0x07fffc,0x0000f8,0x0003e0,0x000f80,0x003e00,0x00f800,0x03e000,0x07fffc,0x07fffc,0x07fffc,0x000000,0x000000, // N + 0x000000,0x003f80,0x00ffe0,0x01fff0,0x03e0f8,0x07803c,0x07001c,0x07001c,0x07001c,0x07001c,0x07803c,0x03e0f8,0x01fff0,0x00ffe0,0x003f80,0x000000, // O + + 0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x000c0c,0x000c0c,0x000c0c,0x000c0c,0x000c0c,0x000e1c,0x0007fc,0x0007f8,0x0001f0,0x000000,0x000000, // P + 0x000000,0x003f80,0x00ffe0,0x01fff0,0x03e0f8,0x07803c,0x07001c,0x07201c,0x07601c,0x07c01c,0x07c03c,0x03e078,0x07fff0,0x0effe0,0x0e3f80,0x0c0000, // Q + 0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x00060c,0x00060c,0x00060c,0x000e0c,0x003e0c,0x00fe0c,0x01f71c,0x07e3fc,0x07c3f8,0x0701f0,0x000000, // R + 0x000000,0x000000,0x01e1e0,0x03e3f8,0x03c7f8,0x07079c,0x06070c,0x060f0c,0x060e0c,0x060e0c,0x071e3c,0x03fc78,0x03fc70,0x00f000,0x000000,0x000000, // S + 0x000000,0x00000c,0x00000c,0x00000c,0x00000c,0x00000c,0x07fffc,0x07fffc,0x07fffc,0x00000c,0x00000c,0x00000c,0x00000c,0x00000c,0x000000,0x000000, // T + 0x000000,0x000000,0x00fffc,0x01fffc,0x03fffc,0x078000,0x070000,0x070000,0x070000,0x070000,0x078000,0x03fffc,0x01fffc,0x007ffc,0x000000,0x000000, // U + 0x000000,0x000000,0x00001c,0x0000fc,0x000ffc,0x007fe0,0x03ff00,0x07f000,0x078000,0x07f000,0x01fe00,0x003fe0,0x0007f8,0x0000fc,0x00001c,0x000000, // V + 0x00007c,0x000ffc,0x01fff0,0x07fc00,0x07e000,0x03ff00,0x001ff8,0x0000fc,0x0000fc,0x001ff8,0x03ff00,0x07e000,0x07fc00,0x01fff0,0x000ffc,0x00007c, // W + 0x000000,0x070000,0x07801c,0x07e07c,0x03f0f8,0x00fbf0,0x007fc0,0x003f80,0x003f80,0x007fc0,0x00fbe0,0x03f1f8,0x07e07c,0x07803c,0x070008,0x000000, // X + 0x000000,0x000008,0x00003c,0x0000fc,0x0001f8,0x0007e0,0x07ff80,0x07fe00,0x07ff80,0x0007c0,0x0001f0,0x0000fc,0x00003c,0x000008,0x000000,0x000000, // Y + 0x000000,0x070000,0x07800c,0x07e00c,0x07f00c,0x06fc0c,0x067e0c,0x061f8c,0x060fcc,0x0603fc,0x0601fc,0x0600fc,0x06003c,0x060000,0x000000,0x000000, // Z + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x3ffffe,0x3ffffe,0x300006,0x300006,0x000000,0x000000,0x000000,0x000000,0x000000, // [ + 0x000000,0x000000,0x000000,0x000008,0x00003c,0x0001fc,0x000ff8,0x007fe0,0x03ff00,0x07f800,0x07c000,0x060000,0x000000,0x000000,0x000000,0x000000, // + 0x000000,0x000000,0x000000,0x000000,0x300006,0x300006,0x300006,0x3ffffe,0x3ffffe,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ] + 0x000000,0x000000,0x000400,0x000700,0x0007c0,0x0003f8,0x00007c,0x00003c,0x0001fc,0x0007e0,0x000780,0x000400,0x000000,0x000000,0x000000,0x000000, // ^ + 0x080000,0x080000,0x080000,0x080000,0x080000,0x080000,0x080000,0x080000,0x080000,0x080000,0x080000,0x080000,0x080000,0x080000,0x080000,0x080000, // _ + + 0x000000,0x000070,0x0000f8,0x00018c,0x00018c,0x00018c,0x0000f8,0x000070,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ` + 0x000000,0x000000,0x01c000,0x03e700,0x07f380,0x063180,0x063180,0x021180,0x031980,0x03ff80,0x07ff00,0x07fe00,0x040000,0x000000,0x000000,0x000000, // a + 0x000000,0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x038700,0x060180,0x060180,0x060180,0x070380,0x03ff80,0x03ff00,0x00fc00,0x000000,0x000000, // b + 0x000000,0x000000,0x00fc00,0x01fe00,0x03ff00,0x078780,0x060180,0x060180,0x060180,0x070380,0x038700,0x03cf00,0x018400,0x000000,0x000000,0x000000, // c + 0x000000,0x000000,0x00fc00,0x03ff00,0x03ff80,0x070380,0x060180,0x060180,0x060180,0x038700,0x07fffc,0x07fffc,0x07fffc,0x000000,0x000000,0x000000, // d + 0x000000,0x000000,0x00fc00,0x01fe00,0x03ff00,0x073380,0x063180,0x063180,0x063180,0x063380,0x033f00,0x03be00,0x011c00,0x000000,0x000000,0x000000, // e + 0x000000,0x000000,0x000000,0x000180,0x000180,0x07fff8,0x07fffc,0x07fffc,0x00018c,0x00018c,0x00000c,0x000000,0x000000,0x000000,0x000000,0x000000, // f + 0x000000,0x000000,0x10fc00,0x7bff00,0x73ff80,0xe70380,0xc60180,0xc60180,0xc60180,0xe30300,0x7fff80,0x7fff80,0x1fff80,0x000000,0x000000,0x000000, // g + 0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x000300,0x000180,0x000180,0x000380,0x07ff80,0x07ff00,0x07fe00,0x000000,0x000000,0x000000,0x000000, // h + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x07ff9c,0x07ff9c,0x07ff9c,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // i + 0x000000,0x000000,0x000000,0xc00000,0xc00000,0xc00000,0xffff9c,0xffff9c,0x7fff9c,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // j + 0x000000,0x000000,0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x003800,0x001c00,0x007e00,0x01f700,0x03e380,0x078180,0x060000,0x000000,0x000000, // k + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x07fffc,0x07fffc,0x07fffc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // l + 0x07ff80,0x07ff80,0x07ff80,0x000300,0x000180,0x000180,0x000180,0x07ff80,0x07ff80,0x07ff00,0x000300,0x000180,0x000180,0x07ff80,0x07ff80,0x07ff00, // m + 0x000000,0x000000,0x000000,0x07ff80,0x07ff80,0x07ff80,0x000300,0x000180,0x000180,0x000380,0x07ff80,0x07ff00,0x07fe00,0x000000,0x000000,0x000000, // n + 0x000000,0x00fc00,0x01fe00,0x03ff00,0x070380,0x060180,0x060180,0x060180,0x070380,0x03ff00,0x01fe00,0x00fc00,0x000000,0x000000,0x000000,0x000000, // o + + 0x000000,0x000000,0xffff80,0xffff80,0xffff80,0x038700,0x060180,0x060180,0x060180,0x070380,0x03ff00,0x03ff00,0x00fc00,0x000000,0x000000,0x000000, // p + 0x000000,0x000000,0x00fc00,0x03ff00,0x03ff00,0x070380,0x060180,0x060180,0x060180,0x038700,0xffff80,0xffff80,0xffff80,0x000000,0x000000,0x000000, // q + 0x000000,0x000000,0x000000,0x000000,0x07ff80,0x07ff80,0x07ff80,0x000300,0x000180,0x000180,0x000180,0x000300,0x000000,0x000000,0x000000,0x000000, // r + 0x000000,0x000000,0x000000,0x018e00,0x039f00,0x079f80,0x073980,0x063980,0x063180,0x067380,0x07f700,0x03e700,0x01c000,0x000000,0x000000,0x000000, // s + 0x000000,0x000000,0x000000,0x000000,0x000100,0x000180,0x03fffc,0x07fffc,0x07fffc,0x060180,0x060180,0x060000,0x000000,0x000000,0x000000,0x000000, // t + 0x000000,0x000000,0x000000,0x01ff80,0x03ff80,0x07ff80,0x070000,0x060000,0x060000,0x030000,0x07ff80,0x07ff80,0x07ff80,0x000000,0x000000,0x000000, // u + 0x000000,0x000000,0x000000,0x000380,0x001f80,0x007f00,0x03f800,0x07c000,0x07c000,0x03f800,0x007f00,0x001f80,0x000380,0x000000,0x000000,0x000000, // v + 0x000380,0x001f80,0x00ff00,0x07f000,0x078000,0x07f800,0x00ff00,0x000f80,0x000f80,0x00ff00,0x07f800,0x078000,0x07f000,0x00ff00,0x001f80,0x000380, // w + 0x000000,0x000000,0x070180,0x078380,0x03e780,0x01ff00,0x00fe00,0x003c00,0x00fe00,0x01ff00,0x07e780,0x078380,0x070180,0x000000,0x000000,0x000000, // x + 0x000000,0x000000,0x000000,0xc00380,0xc01f80,0xc0ff00,0xf7f800,0x7fc000,0x3f8000,0x07f800,0x00ff00,0x001f80,0x000380,0x000000,0x000000,0x000000, // y + 0x000000,0x000000,0x060180,0x070180,0x078180,0x07e180,0x07f180,0x067980,0x063d80,0x061f80,0x060f80,0x060780,0x060380,0x000000,0x000000,0x000000, // z + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000c00,0x001e00,0x0ffffc,0x1ffffe,0x3fe1ff,0x380007,0x380007,0x000000,0x000000,0x000000,0x000000, // { + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x3fffff,0x3fffff,0x3fffff,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // | + 0x000000,0x000000,0x000000,0x380007,0x380007,0x3fe1ff,0x1ffffe,0x0ffffc,0x001e00,0x000c00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // } + 0x000000,0x000000,0x000070,0x000038,0x000038,0x000038,0x000038,0x000030,0x000070,0x000070,0x000070,0x000070,0x000038,0x000000,0x000000,0x000000 // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/Font_Dingbats_32X24.java b/src/devices/Oled/SSD1306/Font_Dingbats_32X24.java new file mode 100644 index 0000000..9e735c6 --- /dev/null +++ b/src/devices/Oled/SSD1306/Font_Dingbats_32X24.java @@ -0,0 +1,211 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font_Dingbats_32X24") +public class Font_Dingbats_32X24 extends FontPack { + + public final char kosong = 32; + public final char horse_head = 33; + public final char dog_head = 34; + public final char pig_head = 35; + public final char panda_head = 36; + public final char elephant_head = 37; + public final char gorilla_head = 38; + public final char crocodile = 39; + public final char lion_head = 40; + public final char penguin = 41; + public final char whale = 42; + public final char fish1 = 43; + public final char fish2 = 44; + public final char turtle = 45; + public final char lobster = 46; + public final char dinosaur = 47; + public final char telephone_lingkaran = 48; + public final char map = 49; + public final char lembar_cek = 50; + public final char uang_cash = 51; + public final char plug_male = 52; + public final char plug_female = 53; + public final char trees = 54; + public final char tulip = 55; + public final char tree = 56; + public final char house = 57; + public final char office1 = 58; + public final char office2 = 59; + public final char gereja = 60; + public final char pabrik = 61; + public final char mercusuar = 62; + public final char clocktower = 63; + public final char melody1 = 64; + public final char camera_filmmaker = 65; + public final char gakjelas1 = 66; + public final char gakjelas2 = 67; + public final char gakjelas3 = 68; + public final char gakjelas4 = 69; + public final char gakjelas5 = 70; + public final char gakjelas6 = 71; + public final char gakjelas7 = 72; + public final char ufo = 73; + public final char tank = 74; + public final char conductor = 75; + public final char melody2 = 76; + public final char melody3 = 77; + public final char televisi_tabung = 78; + public final char radio = 79; + public final char microphone = 80; + public final char telephone = 81; + public final char mailbox_isi = 82; + public final char mailbox_kosong = 83; + public final char folder = 84; + public final char briefcase = 85; + public final char camera = 86; + public final char time = 87; + public final char gunting = 88; + public final char gembok = 89; + public final char kunci = 90; + public final char mobil = 91; + public final char taksi = 92; + public final char bus = 93; + public final char kereta = 94; + public final char pesawat = 95; + public final char truk = 96; + public final char pulpen = 97; + public final char handwriting = 98; + public final char pena_bulu = 99; + public final char lemari_dokumen = 100; + public final char jam_pasir = 101; + public final char buku_terbuka = 102; + public final char kertas_kosong = 103; + public final char kertas_isi = 104; + public final char kertas_banyak = 105; + public final char amplop_depan = 106; + public final char amplop_belakang = 107; + public final char disket_kecil = 108; + public final char disket_besar = 109; + public final char cassete = 110; + public final char cd1 = 111; + public final char cd2 = 112; + public final char laptop = 113; + public final char keyboard = 114; + public final char mouse = 115; + public final char diskdrive =116; + public final char tang_potong = 117; + public final char palu = 118; + public final char kunci_pas = 119; + public final char obeng = 120; + public final char tempat_dokumen = 121; + public final char printer = 122; + public final char kapal = 123; + public final char pesawat_luarangkasa = 124; + public final char sepeda_motor = 125; + public final char sepeda = 126; + /** + * Source : http://www.rinkydinkelectronics.com/images/fonts/Dingbats1_XL.png + */ + public Font_Dingbats_32X24() { + super("Font_Dingbats_32X24", 32, 24, 32, 24, 32, 126); + data = new int[] { + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // + 0x000000,0x000000,0x000000,0x000000,0x000000,0x001000,0x001800,0x003c00,0x006700,0x004380,0x0040c0,0x00406c,0x002338,0x0ff338,0x0ff86c,0x0c00d8,0x0c01b8,0x0c0330,0x0c0e60,0x0ffcc0,0x0ff380,0x000f00,0x0ffc00,0x0ff000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ! + 0x000000,0x000000,0x000000,0x000000,0x000000,0x0003fe,0x0007fe,0x000e06,0x001c06,0x003806,0x003006,0x0e3006,0x0ff006,0x09f066,0x0a0066,0x0e0006,0x0a0006,0x0a0006,0x0e7fe6,0x0affe6,0x0ac006,0x0ec006,0x00fffe,0x007ffc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // " + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x003e00,0x00ff80,0x01c1fc,0x030078,0x070018,0x060030,0x0ccc18,0x0c8c18,0x0ce018,0x0ca018,0x0c8c18,0x0ccc18,0x060030,0x070018,0x030078,0x01c1fc,0x00ff80,0x003e00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // # + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x003e78,0x00fff8,0x01c1fc,0x03107c,0x073e7c,0x063f38,0x0c3910,0x0c1f18,0x0c0018,0x0c0618,0x0c0a18,0x0c3910,0x063f38,0x073e7c,0x03107c,0x01c1fc,0x00fffc,0x003e78,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // $ + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x03ff00,0x0c00c0,0x080020,0x13c010,0x144188,0x144188,0x1c4004,0x004004,0x004004,0x03f004,0x040004,0x040004,0x04000c,0x040008,0x040008,0x040008,0x030008,0x00fe08,0x0003f0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // % + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x01ff00,0x07ffc0,0x0639f0,0x0c00f8,0x0c42fc,0x0c2efc,0x0c6efe,0x0c42fe,0x0c42fe,0x0c6efe,0x0c2efc,0x0c42fc,0x0c00f8,0x0639f0,0x07ffe0,0x01ff00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // & + 0x000000,0x000000,0x000000,0x038000,0x06c200,0x04e3c0,0x048340,0x048420,0x04d860,0x04e1e0,0x048600,0x040800,0x0e0400,0x0a3400,0x0a4400,0x087800,0x0e1000,0x043000,0x0c3000,0x0a1000,0x0a3000,0x081000,0x081dc0,0x0e2640,0x020080,0x018300,0x00fc00,0x000000,0x000000,0x000000,0x000000,0x000000, // ' + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x01ff00,0x01ffe0,0x07fff8,0x07c1cc,0x0e00ec,0x0e006c,0x0ccc78,0x0c8c38,0x18a038,0x18e038,0x0c8c38,0x0ccc78,0x0e006c,0x0e00ec,0x07c1cc,0x07fff8,0x01fff0,0x01fb80,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ( + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000040,0x0cf8a0,0x0b8ff0,0x0b00f8,0x0a01cc,0x0bffcc,0x0ffffc,0x0ffffc,0x0ffff8,0x0ffff0,0x0ffe00,0x0c3800,0x002000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ) + 0x000000,0x000000,0x000000,0x000f80,0x003180,0x006d80,0x007180,0x00ed80,0x00d980,0x00d780,0x00ff80,0x00ff80,0x00ff80,0x01ff80,0x01ff80,0x01ff80,0x03ff80,0x007f80,0x003f80,0x003f80,0x001f80,0x000780,0x000380,0x000780,0x000fe0,0x001fe0,0x001830,0x000000,0x000000,0x000000,0x000000,0x000000, // * + 0x000000,0x000000,0x000000,0x000000,0x001000,0x003800,0x007c00,0x006600,0x00e700,0x00ff00,0x00ff00,0x01a180,0x01cfe0,0x018fe0,0x00ffc0,0x00ffc0,0x00ff80,0x00ff80,0x00ff00,0x01fe00,0x003c00,0x001800,0x003c00,0x007e00,0x006600,0x008200,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // + + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000800,0x000c00,0x001200,0x003300,0x002d00,0x004c80,0x008040,0x010020,0x038070,0x07c0f8,0x0fc0fc,0x002100,0x001200,0x000c00,0x000c00,0x001e00,0x001e00,0x003f00,0x007f80,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // , + 0x000000,0x000000,0x000000,0x000000,0x000000,0x00fc00,0x0fff80,0x087ec0,0x083ee0,0x083ef0,0x087ef0,0x0f9a28,0x02ff88,0x0381d8,0x0300d8,0x0300d8,0x02e6d8,0x02e688,0x0fb1a8,0x087ff0,0x083ef0,0x083ee0,0x087ec0,0x0fff80,0x00fe00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // - + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0000f0,0x0001e0,0x0001e0,0x0069fc,0x0c6ae4,0x0efe0c,0x0ffff0,0x0dff80,0x0ffff0,0x0efe0c,0x0c6ae4,0x0069fc,0x0001e0,0x0001f0,0x0000e0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // . + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x001f80,0x0fffe0,0x0ffff0,0x0ffff0,0x00fe78,0x0ffc78,0x0ffe38,0x0ffe7c,0x03fe7c,0x03fe6c,0x03fe78,0x0ffc00,0x0ff800,0x0fe000,0x070000,0x040000,0x080000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // / + + 0x000000,0x000000,0x000000,0x000000,0x000000,0x003f00,0x00c0c0,0x010030,0x000018,0x000008,0x0ec004,0x09f804,0x10fe02,0x137f82,0x10c3c2,0x1001e2,0x1000f2,0x100172,0x0801e4,0x080064,0x040008,0x060018,0x030030,0x00c0c0,0x003f00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // 0 + 0x000000,0x000000,0x000000,0x000000,0x000000,0x03ffe0,0x020320,0x0203e0,0x0203a0,0x0203a0,0x020320,0x020320,0x020320,0x020320,0x020320,0x020320,0x020320,0x020320,0x020320,0x020320,0x020320,0x020320,0x020320,0x020320,0x03ffe0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // 1 + 0x000000,0x000000,0x000000,0x000000,0x000000,0x01ff00,0x017fc0,0x014040,0x015b40,0x015b40,0x015b40,0x015b40,0x015b40,0x015b40,0x015b40,0x014840,0x015840,0x015840,0x015940,0x015940,0x015940,0x015940,0x015940,0x01c040,0x007fc0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // 2 + 0x000000,0x000000,0x000000,0x000000,0x000000,0x07fc00,0x05ff00,0x050100,0x050100,0x050100,0x050100,0x057900,0x054900,0x058500,0x058500,0x058500,0x0549e0,0x057a20,0x050410,0x050410,0x050410,0x050220,0x0501e0,0x070100,0x01ff00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // 3 + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0003e0,0x0007bc,0x0007a0,0x03ffa0,0x0607a0,0x0407bc,0x0403e0,0x060000,0x03e000,0x003000,0x001000,0x001000,0x003000,0x0fe000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // 4 + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0007c0,0x000a20,0x000a20,0x03f8e0,0x060a20,0x040a20,0x0407c0,0x060000,0x03e000,0x003000,0x001000,0x001000,0x003000,0x0fe000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // 5 + 0x000000,0x000000,0x000000,0x000000,0x040000,0x032000,0x03f900,0x03ff80,0x0fffe0,0x0e6ff0,0x0eb640,0x02ee80,0x00ffe0,0x0ffff0,0x0ffffc,0x0ffff0,0x00ffe0,0x02ee80,0x0eb640,0x0e6ff0,0x0fffe0,0x03ff80,0x03f900,0x032000,0x040000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // 6 + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000800,0x001800,0x003800,0x007800,0x00f07c,0x01e0fc,0x03c1f0,0x0781fc,0x0ffffc,0x0ffffc,0x0781fc,0x03c1f0,0x01e0fc,0x00f07c,0x007800,0x003800,0x001800,0x000800,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // 7 + 0x000000,0x000000,0x000000,0x000000,0x000000,0x020000,0x010000,0x018000,0x01d000,0x01c800,0x01ed00,0x01fe80,0x01fe80,0x0fffd8,0x0fffee,0x0fffd8,0x01fec0,0x01fe80,0x01ed00,0x01c800,0x01d000,0x018000,0x010000,0x020000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // 8 + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ffe00,0x0fff00,0x0fff80,0x0c63fc,0x0c63fc,0x0c63f0,0x0c63f8,0x0ffffc,0x0c63f8,0x0c63f0,0x0c63e0,0x0c63c0,0x0fff80,0x0fff00,0x0ffe00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // 9 + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ffffc,0x080004,0x083334,0x083334,0x080004,0x080004,0x0f3334,0x0f3334,0x080004,0x083334,0x083334,0x080004,0x0ffffc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // : + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ffffe,0x080002,0x085aaa,0x085aaa,0x0f5aaa,0x0f5aaa,0x0f5aaa,0x0f5aaa,0x0f5aaa,0x085aaa,0x085aaa,0x080002,0x0ffffe,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ; + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ff800,0x0ffc00,0x0ffe00,0x0ffe00,0x0fff00,0x0fff98,0x001f98,0x000ffe,0x000ffe,0x001f98,0x0fff98,0x0fff00,0x0ffe00,0x0ffe00,0x0ffc00,0x0ff800,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // < + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0fff38,0x0fff0c,0x0f8004,0x0fc084,0x0fe0cc,0x0ff084,0x0ff884,0x0f8084,0x0fc048,0x0fe084,0x0ff084,0x0ff884,0x0fc0cc,0x0fc084,0x0fe084,0x0ff0cc,0x0ff878,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // = + 0x000000,0x000000,0x000000,0x0000fc,0x000084,0x000088,0x000088,0x000088,0x000048,0x000058,0x000030,0x0ffff8,0x0fff8c,0x0fff8c,0x0fff8c,0x0ffffe,0x0fff8c,0x0fff8c,0x0fc1f8,0x000030,0x000058,0x000048,0x000088,0x000088,0x000088,0x000084,0x0000fc,0x000000,0x000000,0x000000,0x000000,0x000000, // > + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ff000,0x0ff000,0x0ffe00,0x0ffe00,0x0ffe00,0x0ffffc,0x003f8c,0x001f04,0x001f34,0x001f24,0x003f8c,0x0ffffc,0x0ffe00,0x0ffe00,0x0ffe00,0x0ff000,0x0ff000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ? + + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x070000,0x078000,0x07c000,0x07c000,0x03c000,0x01fffe,0x000044,0x0000cc,0x1800cc,0x3c0088,0x3e0198,0x3e0198,0x1e0110,0x0ffff0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // @ + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x003f80,0x001f00,0x001f00,0x080e38,0x0e0e7c,0x039f7c,0x00ff7c,0x003f38,0x0fff00,0x0fff00,0x003f38,0x00ff7c,0x039f7c,0x0e007c,0x080038,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // A + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0807f0,0x0fb928,0x0f4ff4,0x0bf10c,0x084104,0x084104,0x084104,0x084104,0x084104,0x084104,0x084104,0x084184,0x0c5f7c,0x0bebe4,0x07fc1c,0x008004,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // B + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0007c0,0x000740,0x000540,0x03fd40,0x03fde0,0x03ffe0,0x03ffe0,0x03ffa0,0x03fda0,0x03fd50,0x03ff70,0x03ff70,0x03ff50,0x03fd50,0x03fd28,0x03fd38,0x03ff38,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // C + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x007ffc,0x034aa4,0x054aa4,0x04caa4,0x054ba4,0x0dfba4,0x091bbc,0x0bfae4,0x0a2a84,0x0a2a84,0x0ffffc,0x0c4000,0x078000,0x068000,0x038000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // D + 0x000000,0x000000,0x000000,0x000000,0x000000,0x008000,0x008000,0x008000,0x014000,0x0e3870,0x01438c,0x008470,0x0089a0,0x009340,0x002440,0x00a8c0,0x015b00,0x00b400,0x00d800,0x00fc00,0x01e800,0x03e000,0x03c000,0x03c000,0x01c000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // E + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x008000,0x01e1e0,0x01e618,0x03e408,0x03c804,0x07f804,0x07a804,0x07b804,0x0f8408,0x0fc618,0x03e5e0,0x01fe00,0x00fb00,0x0071c0,0x0013e0,0x000c20,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // F + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000400,0x000600,0x000f00,0x001f80,0x00bf80,0x08fe40,0x0dbc20,0x137af8,0x095708,0x0ce5e4,0x07aaf8,0x03e558,0x0435e4,0x04fb48,0x07b278,0x003ec0,0x003d80,0x003c00,0x002000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // G + 0x000000,0x000000,0x000000,0x000000,0x020200,0x010408,0x010410,0x008420,0x004040,0x001e00,0x303f00,0x0c7d06,0x04f898,0x01f480,0x07e5f0,0x07c3f0,0x77c3f0,0x07c3e0,0x07c7f8,0x07c7c6,0x07df82,0x303f80,0x00bf40,0x011e20,0x060010,0x040010,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // H + 0x000000,0x000000,0x000000,0x000000,0x000000,0x008000,0x00c000,0x03e000,0x07e000,0x07ef00,0x07ef80,0x03e9c0,0x00e9e0,0x03efe0,0x07e9f8,0x07e9f8,0x03efe0,0x00e9e0,0x03e9c0,0x07ef80,0x07ef00,0x07e000,0x03e000,0x00c000,0x008000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // I + 0x000000,0x000000,0x000000,0x000000,0x000100,0x000100,0x000100,0x0fc300,0x1be300,0x13e300,0x13e200,0x13e200,0x13e200,0x13ef00,0x13ef80,0x13ef80,0x13ef80,0x13ef80,0x13ef80,0x13ef80,0x13ef80,0x13ef00,0x13e000,0x1be000,0x0fc000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // J + 0x000000,0x000000,0x000000,0x000000,0x006000,0x007d00,0x00fe80,0x00fc80,0x00fe40,0x0ff1e0,0x0ff010,0x0fe388,0x0fc440,0x0fb820,0x0e2820,0x0fb820,0x0fc440,0x0fe38c,0x0ff014,0x0ff864,0x01fbc4,0x00fff8,0x007fe0,0x003f80,0x001c00,0x001000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // K + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x007800,0x01fc00,0x198600,0x3b3b60,0x3a4b9c,0x22fd8e,0x1f0cc6,0x011c78,0x01f800,0x007000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // L + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0e0000,0x0e0000,0x0f0000,0x0f0000,0x070000,0x03fffc,0x000038,0x000070,0x002060,0x0010c0,0x000f80,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // M + 0x000000,0x000000,0x000000,0x000000,0x000000,0x0ffffc,0x080004,0x0affe4,0x0afff4,0x08fff4,0x08fff4,0x0afff4,0x0afff4,0x08fff4,0x0afff4,0x0afff4,0x0afe34,0x0afe34,0x08fe34,0x08fe34,0x08f034,0x08fff4,0x08ffe4,0x080004,0x0ffffc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // N + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x07ff00,0x080100,0x080100,0x0fff00,0x0fc980,0x0fc940,0x0fff20,0x0f0f10,0x0cf308,0x0dfb04,0x0bfd06,0x0bfd02,0x0bfd00,0x0df900,0x0cf300,0x0f0f00,0x07ff00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // O + + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x00fe00,0x013ff8,0x097a94,0x097b6c,0x0f7b6c,0x0f7a94,0x0f7b6c,0x097b6c,0x097a94,0x013ff8,0x00fe00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // P + 0x000000,0x000000,0x000000,0x000000,0x000580,0x0005c0,0x0005c0,0x0f05c0,0x0fc5e0,0x0fe1e0,0x0ff8e0,0x0f9ce0,0x0e06e0,0x0c06e0,0x0c02e0,0x0c02e0,0x0c06e0,0x0e06e0,0x0f9ce0,0x0ff8e0,0x0fe1e0,0x0fc5e0,0x0f05c0,0x0005c0,0x0005c0,0x000580,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // Q + 0x003800,0x006400,0x008400,0x014400,0x024400,0x044400,0x084600,0x084980,0x0850c0,0x089040,0x083040,0x084c60,0x08f460,0x090a60,0x0a0aa0,0x0e0990,0x0ffe10,0x080010,0x083ff8,0x043ff8,0x0401e8,0x0400e8,0x020044,0x020004,0x020004,0x020004,0x010004,0x010008,0x010010,0x00ffe0,0x000000,0x000000, // R + 0x003800,0x006400,0x008400,0x014400,0x024400,0x044400,0x084600,0x084980,0x085080,0x089040,0x082040,0x084040,0x088060,0x090060,0x0a00a0,0x0c0190,0x0ffe10,0x080010,0x083010,0x043008,0x041808,0x041808,0x021804,0x023804,0x027c04,0x027c04,0x011c04,0x010208,0x010010,0x00ffe0,0x000000,0x000000, // S + 0x000000,0x0fffe0,0x0e0020,0x098020,0x08603c,0x081004,0x080c04,0x080304,0x080104,0x080104,0x080104,0x08011c,0x080120,0x080120,0x080120,0x080120,0x080120,0x080120,0x080120,0x080120,0x080120,0x080120,0x0801e0,0x060100,0x018100,0x004100,0x003100,0x000d00,0x000300,0x000000,0x000000,0x000000, // T + 0x000000,0x000000,0x000000,0x000000,0x000000,0x0fffc0,0x0fffc0,0x0fffc0,0x0fffc0,0x0fffc0,0x0ffff8,0x0ffffc,0x0fffcc,0x0fffcc,0x0fffcc,0x0fffcc,0x0fffcc,0x0fffcc,0x0ffffc,0x0ffff8,0x0fffc0,0x0fffc0,0x0fffc0,0x0fffc0,0x0fffc0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // U + 0x000000,0x000000,0x000000,0x000000,0x000000,0x0fffe0,0x0fffe0,0x0fffe0,0x0fff80,0x0fffc0,0x0fffa0,0x0f07b0,0x0e03b8,0x0c01b8,0x0c01b8,0x0c01b8,0x0c01b8,0x0c01b8,0x0e03b8,0x0f07b0,0x0fffa0,0x0fffc0,0x0fff80,0x0fff80,0x0fff80,0x0fff80,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // V + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x003f00,0x00c0c0,0x010020,0x020010,0x040008,0x040008,0x080004,0x080004,0x080fe4,0x080fe4,0x080c04,0x080c04,0x040c08,0x040c08,0x020c10,0x010020,0x00c0c0,0x003f00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // W + 0x000000,0x000000,0x000000,0x070380,0x088440,0x088440,0x088440,0x088440,0x078780,0x008400,0x00cc00,0x00fc00,0x007800,0x007800,0x007800,0x007800,0x007800,0x00fc00,0x00fc00,0x01ce00,0x01ce00,0x018600,0x038700,0x038700,0x030300,0x030300,0x020200,0x000000,0x000000,0x000000,0x000000,0x000000, // X + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x00fc00,0x01ffc0,0x03fc30,0x07ffd8,0x07fc28,0x0ffc24,0x0fdc14,0x0e0c14,0x0fdc14,0x0ffc34,0x07fc28,0x07ffd8,0x03fc30,0x01ffc0,0x00fc00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // Y + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0f0000,0x0f0000,0x0fc000,0x0fc000,0x07f000,0x03f000,0x01fe00,0x00ffe0,0x007ff0,0x003ff8,0x001ffc,0x001ffc,0x001f1c,0x001f1c,0x001f1c,0x000ff8,0x0007f0,0x0003e0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // Z + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x01f000,0x01f800,0x0f9c00,0x0f0b80,0x0f0860,0x019820,0x01f820,0x01f820,0x01f820,0x01f820,0x01f820,0x01f820,0x019820,0x0f0860,0x0f0b80,0x0f9c00,0x01f800,0x01f800,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // [ + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x01f000,0x01f800,0x0f9c00,0x0f0b80,0x0f0840,0x019820,0x01f820,0x01f838,0x01f828,0x01f828,0x01f838,0x01f820,0x019820,0x0f08c0,0x0f0b00,0x0f9c00,0x01f800,0x01f800,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x01fff0,0x01f83c,0x0f983c,0x0f083c,0x0f0824,0x019824,0x01f824,0x01f824,0x01f824,0x01f824,0x019824,0x0f0824,0x0f083c,0x0f983c,0x01f83c,0x01fff0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ] + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x080000,0x080000,0x04fffc,0x03880c,0x01880c,0x00880c,0x00f80c,0x00f80c,0x00f80c,0x00f80c,0x00880c,0x00880c,0x01880c,0x03f80c,0x04fffc,0x080000,0x080000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ^ + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x00ff80,0x007f00,0x001c00,0x001c00,0x001c00,0x001c00,0x0ffffc,0x0ffffc,0x0ffffc,0x01ffe0,0x003f00,0x001c00,0x001c00,0x001c00,0x001c00,0x001c00,0x001c00,0x001c00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // _ + + 0x000000,0x000000,0x000000,0x000000,0x03dfe0,0x03dfe0,0x01dfe0,0x06dfe0,0x0f5fe0,0x0f5fe0,0x065fe0,0x005fe0,0x065fe0,0x0f5fe0,0x0f5fe0,0x06dfe0,0x01dfe0,0x03dfe0,0x03ff00,0x01ff00,0x06ff00,0x0f6100,0x0f6100,0x06e100,0x01ee00,0x03f000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // ` + 0x000000,0x000000,0x000000,0x100000,0x0c0000,0x0e0000,0x090000,0x08c000,0x044000,0x07a000,0x055000,0x02a800,0x01a800,0x015400,0x00aa00,0x006a00,0x005500,0x002a80,0x001980,0x001540,0x000ae0,0x0005d0,0x0002d0,0x000308,0x000108,0x000098,0x000070,0x000000,0x000000,0x000000,0x000000,0x000000, // a + 0x000000,0x000000,0x000000,0x000000,0x070000,0x04e000,0x039800,0x018600,0x00c100,0x013080,0x013c40,0x013e40,0x013f20,0x0123a0,0x0111e0,0x0110e0,0x0100f0,0x01013c,0x010118,0x010210,0x010200,0x008200,0x01ff00,0x010100,0x010100,0x03ff00,0x03ff00,0x03ff00,0x000000,0x000000,0x000000,0x000000, // b + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x1e0000,0x098000,0x087800,0x08be00,0x07eb80,0x047fe0,0x00d7f8,0x007ff8,0x002ffc,0x0007fe,0x0000fc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // c + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ffff0,0x081018,0x081018,0x085054,0x085054,0x085054,0x081014,0x081014,0x0ffff4,0x04000c,0x02000c,0x01fffc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // d + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x078078,0x0861a4,0x0e1244,0x0f0cc4,0x0f03c4,0x0fffc4,0x0f0dc4,0x0e12e4,0x086184,0x078078,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // e + 0x000000,0x000000,0x07fff0,0x07fff0,0x070018,0x04fffc,0x048004,0x048004,0x048004,0x048004,0x048004,0x048004,0x0c8004,0x09000c,0x0ffff8,0x09000c,0x0c8004,0x048004,0x048004,0x048004,0x048004,0x048004,0x048004,0x05fffc,0x060018,0x07fff0,0x07fff0,0x000000,0x000000,0x000000,0x000000,0x000000, // f + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ffffc,0x080004,0x080004,0x080004,0x080004,0x080004,0x080004,0x080004,0x080004,0x080004,0x0f8004,0x048004,0x028004,0x018004,0x00fffc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // g + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ffffc,0x080004,0x08ab24,0x08ab24,0x08ab24,0x08ab24,0x08ab24,0x08ab24,0x08ab24,0x082b04,0x0fab04,0x04ab04,0x02ab04,0x018004,0x00fffc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // h + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0xffff80,0x800080,0xaa54e0,0xaa54a0,0xaa54a0,0xaa54bc,0xaa54a4,0xaa54a4,0x8a54a4,0x8a54a4,0x8000a4,0x8000a4,0xffffa4,0x400024,0x7fffe4,0x080004,0x080004,0x0ffffc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // i + 0x000000,0x000000,0x000000,0x000000,0x0ffffc,0x080004,0x080004,0x0950a4,0x0950a4,0x0950a4,0x0950a4,0x0950a4,0x0950a4,0x0950a4,0x095004,0x095004,0x095004,0x095004,0x095004,0x095004,0x095004,0x080004,0x0801f4,0x0801f4,0x0801f4,0x080004,0x0ffffc,0x000000,0x000000,0x000000,0x000000,0x000000, // j + 0x000000,0x000000,0x000000,0x000000,0x0ffffc,0x0c000c,0x0a0014,0x090024,0x088044,0x084084,0x082104,0x081204,0x081c04,0x080804,0x083004,0x082004,0x083004,0x080804,0x081c04,0x081204,0x082104,0x084084,0x088044,0x090024,0x0a0014,0x0c000c,0x0ffffc,0x000000,0x000000,0x000000,0x000000,0x000000, // k + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ffffc,0x0ffffc,0x0803fc,0x0803fc,0x080204,0x080204,0x080204,0x080204,0x080204,0x0802f4,0x0802f4,0x080204,0x0803fc,0x0803fc,0x0ffffc,0x0ffff8,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // l + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ffffc,0x0fff84,0x0fff84,0x0fff84,0x0fff84,0x0fff84,0x0fe384,0x0fc184,0x084184,0x084184,0x0fe384,0x0fff84,0x0fff84,0x0fff84,0x0fff84,0x0fff84,0x0ffffc,0x0fffbc,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // m + 0x000000,0x000000,0x000000,0x000000,0x000000,0x01ffe0,0x01c0e0,0x00cce0,0x01ffe0,0x01f1e0,0x01f1e0,0x01f1e0,0x01ffe0,0x01dee0,0x01c0e0,0x01c0e0,0x01dee0,0x01ffe0,0x01f1e0,0x01f1e0,0x01f1e0,0x01ffe0,0x00cce0,0x01c0e0,0x01ffe0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // n + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x003f00,0x00cec0,0x010fa0,0x020ff0,0x041ff8,0x0463f8,0x08417c,0x0880bc,0x089cbc,0x0f9c84,0x0f8084,0x0fc104,0x07e308,0x07fe08,0x03f810,0x01f820,0x00f8c0,0x003f00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // o + + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x001e00,0x00ffc0,0x01ffe0,0x03f950,0x07fea8,0x07c158,0x0f8154,0x0f80b4,0x0f9cb4,0x0a9cfc,0x0a80fc,0x0b80fc,0x05e1f8,0x055ff8,0x02fff0,0x013fe0,0x00efc0,0x001e00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // p + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0f0000,0x09fffc,0x0a6004,0x0b7ff4,0x097014,0x0a7014,0x0b7014,0x0b7014,0x0b7014,0x0b7014,0x0a7014,0x097014,0x0b7014,0x0b7014,0x0b7014,0x0a7ff4,0x087ffc,0x09c000,0x0f0000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // q + 0x000000,0x000000,0x000000,0x0ffe00,0x080200,0x095200,0x095200,0x0953c0,0x085220,0x090220,0x095220,0x095220,0x095220,0x095220,0x090230,0x08521c,0x095200,0x095200,0x095200,0x080200,0x095200,0x095200,0x080200,0x095200,0x095200,0x0ffe00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // r + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000400,0x000400,0x000400,0x0003f0,0x000008,0x000008,0x000008,0x000008,0x000008,0x000008,0x03ff08,0x060488,0x0c0488,0x080488,0x0807f0,0x080480,0x0c0480,0x060580,0x03ff00,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // s + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0ff000,0x081000,0x081000,0x085000,0x085000,0x085000,0x085000,0x085000,0x085000,0x085000,0x085000,0x085000,0x085000,0x095000,0x081000,0x081000,0x0ff000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // t + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x03fc00,0x0fe4f0,0x1fe30c,0x181d3c,0x000bfe,0x00113c,0x182108,0x1fe6f0,0x0ff800,0x03e000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // u + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000300,0x000f80,0x000fc0,0x000fd8,0x0007e4,0x0003e4,0x0003f8,0x000df0,0x0031f8,0x00c3f0,0x01047c,0x06187c,0x08603c,0x088018,0x0b0000,0x040000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // v + 0x000000,0x000000,0x000000,0x000000,0x000000,0x012000,0x033000,0x052800,0x05e800,0x048800,0x040800,0x031000,0x008800,0x004800,0x002400,0x002400,0x001200,0x001100,0x0008c0,0x001020,0x001220,0x001520,0x001520,0x000dc0,0x000500,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // w + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x0c0000,0x120000,0x110000,0x090000,0x068000,0x014000,0x00a000,0x005000,0x002800,0x001500,0x000b00,0x0007e0,0x000ff0,0x0003f8,0x0003fc,0x0001fc,0x0000fc,0x00007c,0x000038,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // x + 0x000000,0x000000,0x000000,0x000000,0x000000,0x0fffc0,0x0fffc0,0x0c0040,0x0c0040,0x0fffc0,0x080040,0x0ffc40,0x0807ff,0x0b8401,0x0b8555,0x0b8555,0x080555,0x0bf455,0x0bf455,0x080455,0x0bf451,0x0bf457,0x0bf456,0x0bf404,0x0807f8,0x0fffc0,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // y + 0x000000,0x000000,0x000000,0x000000,0x07f000,0x041000,0x0c1c00,0x0cf400,0x0c97fc,0x0c9404,0x0cf404,0x0c9404,0x0c9404,0x0cf404,0x0c9404,0x0c9404,0x0cf404,0x0c1404,0x0c143c,0x0c143c,0x0c1438,0x0c17f0,0x0c1400,0x0c1c00,0x041000,0x07f000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // z + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x00f000,0x079000,0x0f9c00,0x0f87e0,0x0f97e0,0x0f97e0,0x0f97e0,0x0f9400,0x0f9780,0x0f9080,0x0f8080,0x0f9080,0x0f9780,0x0f9400,0x0f8400,0x079c00,0x019000,0x00f000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // { + 0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x00f800,0x00c400,0x00c600,0x00c300,0x13c100,0x1b3ef0,0x3b2018,0x13cd6c,0x60dfd4,0x13cd6c,0x3b2018,0x1b3ef0,0x13c100,0x00c300,0x00c600,0x00c400,0x00f800,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // | + 0x000000,0x000000,0x000000,0x000000,0x03c000,0x042000,0x091000,0x0b9000,0x09d000,0x093300,0x043f80,0x03c780,0x001980,0x003e40,0x007e40,0x00fe40,0x01fe00,0x01fe00,0x01be00,0x03ce00,0x05ae00,0x099600,0x099200,0x081600,0x042600,0x03c600,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000, // } + 0x000000,0x000000,0x000000,0x000000,0x03e000,0x063000,0x0c1800,0x080800,0x08c800,0x087800,0x0c1c00,0x063600,0x01e380,0x000e80,0x003280,0x00c280,0x00e200,0x009a00,0x01c600,0x06b600,0x049800,0x08f800,0x08c800,0x080800,0x0c1800,0x063000,0x03e000,0x000000,0x000000,0x000000,0x000000,0x000000 // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/Font_FranklinGothic_16X16.java b/src/devices/Oled/SSD1306/Font_FranklinGothic_16X16.java new file mode 100644 index 0000000..92646d8 --- /dev/null +++ b/src/devices/Oled/SSD1306/Font_FranklinGothic_16X16.java @@ -0,0 +1,112 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font_FranklinGothic_16X16") +public class Font_FranklinGothic_16X16 extends FontPack { + public Font_FranklinGothic_16X16() { + super("Font_FranklinGothic_16X16", 16, 16, 16, 16, 32, 126); + data = new int[] { + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x37fc,0x37fc,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ! + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x007c,0x007c,0x0000,0x007c,0x007c,0x0000,0x0000,0x0000,0x0000,0x0000, // " + 0x0000,0x0000,0x0600,0x3e60,0x3fe0,0x07fc,0x067c,0x3e60,0x3fe0,0x07fc,0x067c,0x0060,0x0000,0x0000,0x0000,0x0000, // # + 0x0000,0x0000,0x0000,0x0800,0x18f0,0x31f8,0x2188,0x7ffc,0x2308,0x3f18,0x1e10,0x0000,0x0000,0x0000,0x0000,0x0000, // $ + 0x0000,0x0078,0x00fc,0x2084,0x3084,0x1cfc,0x0e78,0x0380,0x01c0,0x1e70,0x3f38,0x210c,0x2104,0x3f00,0x1e00,0x0000, // % + 0x0000,0x0000,0x0000,0x0e00,0x1f38,0x39fc,0x30cc,0x31cc,0x37fc,0x1e38,0x1c00,0x3f80,0x3380,0x0000,0x0000,0x0000, // & + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x007c,0x007c,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ' + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03e0,0x0ff8,0x1c1c,0x1004,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ( + 0x0000,0x0000,0x0000,0x0000,0x1004,0x1c1c,0x0ff8,0x03e0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ) + 0x0000,0x0000,0x0000,0x0060,0x0060,0x0460,0x0ec0,0x07c0,0x01fc,0x01fc,0x07c0,0x0ec0,0x0460,0x0060,0x0060,0x0000, // * + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0300,0x0300,0x0300,0x3ff0,0x3ff0,0x0300,0x0300,0x0300,0x0000,0x0000,0x0000, // + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2c00,0x1c00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // , + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0180,0x0180,0x0180,0x0180,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // - + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1800,0x1800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // . + 0x0000,0x0000,0x0000,0x0000,0xc000,0x3000,0x0c00,0x0300,0x00c0,0x0030,0x000c,0x0002,0x0000,0x0000,0x0000,0x0000, // / + + 0x0000,0x0000,0x0000,0x0000,0x07e0,0x1ff8,0x381c,0x300c,0x300c,0x381c,0x1ff8,0x07e0,0x0000,0x0000,0x0000,0x0000, // 0 + 0x0000,0x0000,0x0000,0x0000,0x3060,0x3030,0x3018,0x3ffc,0x3ffc,0x3000,0x3000,0x3000,0x0000,0x0000,0x0000,0x0000, // 1 + 0x0000,0x0000,0x0000,0x0000,0x3030,0x3838,0x3c1c,0x3e0c,0x370c,0x339c,0x31f8,0x30f0,0x0000,0x0000,0x0000,0x0000, // 2 + 0x0000,0x0000,0x0000,0x0000,0x0c30,0x1c38,0x381c,0x318c,0x318c,0x318c,0x1ff8,0x0e70,0x0000,0x0000,0x0000,0x0000, // 3 + 0x0000,0x0000,0x0000,0x0000,0x0700,0x0780,0x06e0,0x0670,0x061c,0x3ffc,0x3ffc,0x0600,0x0600,0x0000,0x0000,0x0000, // 4 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x19fc,0x19fc,0x318c,0x30cc,0x30cc,0x39cc,0x1f8c,0x0f00,0x0000,0x0000,0x0000, // 5 + 0x0000,0x0000,0x0000,0x0000,0x07e0,0x1ff8,0x399c,0x30cc,0x30cc,0x39cc,0x1f9c,0x0f18,0x0000,0x0000,0x0000,0x0000, // 6 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x000c,0x000c,0x3c0c,0x3f8c,0x03cc,0x007c,0x003c,0x000c,0x0000,0x0000,0x0000, // 7 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0e70,0x1ff8,0x318c,0x318c,0x318c,0x318c,0x1ff8,0x0e70,0x0000,0x0000,0x0000, // 8 + 0x0000,0x0000,0x0000,0x0000,0x08f0,0x19f8,0x339c,0x330c,0x330c,0x199c,0x1ff8,0x07e0,0x0000,0x0000,0x0000,0x0000, // 9 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0c30,0x0c30,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // : + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2c30,0x1c30,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ; + 0x0000,0x0000,0x0000,0x0000,0x0100,0x0380,0x0380,0x06c0,0x06c0,0x0c60,0x0c60,0x1830,0x0000,0x0000,0x0000,0x0000, // < + 0x0000,0x0000,0x0000,0x0000,0x0360,0x0360,0x0360,0x0360,0x0360,0x0360,0x0360,0x0360,0x0000,0x0000,0x0000,0x0000, // = + 0x0000,0x0000,0x0000,0x0000,0x0000,0x1830,0x0c60,0x0c60,0x06c0,0x06c0,0x0380,0x0380,0x0100,0x0000,0x0000,0x0000, // > + 0x0000,0x0000,0x0000,0x0000,0x0030,0x0038,0x000c,0x360c,0x370c,0x019c,0x00f8,0x0070,0x0000,0x0000,0x0000,0x0000, // ? + + 0x0000,0x0000,0x0000,0x07c0,0x0820,0x1390,0x2448,0x2448,0x2248,0x23c8,0x2418,0x1230,0x01e0,0x0000,0x0000,0x0000, // @ + 0x0000,0x0000,0x2000,0x3c00,0x3f80,0x0ff0,0x02fc,0x021c,0x02fc,0x0ff0,0x3f80,0x3c00,0x2000,0x0000,0x0000,0x0000, // A + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x318c,0x318c,0x318c,0x318c,0x1ff8,0x0e70,0x0000,0x0000,0x0000,0x0000, // B + 0x0000,0x0000,0x0000,0x07e0,0x1ff8,0x1c38,0x300c,0x300c,0x300c,0x300c,0x1c38,0x0c30,0x0000,0x0000,0x0000,0x0000, // C + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x300c,0x300c,0x300c,0x381c,0x1c38,0x0ff0,0x07e0,0x0000,0x0000,0x0000, // D + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x318c,0x318c,0x318c,0x318c,0x300c,0x0000,0x0000,0x0000,0x0000,0x0000, // E + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x018c,0x018c,0x018c,0x018c,0x000c,0x0000,0x0000,0x0000,0x0000,0x0000, // F + 0x0000,0x0000,0x07e0,0x1ff8,0x1818,0x300c,0x300c,0x330c,0x1b1c,0x1f38,0x3f30,0x0000,0x0000,0x0000,0x0000,0x0000, // G + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x0180,0x0180,0x0180,0x0180,0x3ffc,0x3ffc,0x0000,0x0000,0x0000,0x0000, // H + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // I + 0x0000,0x0000,0x0000,0x0000,0x0000,0x3000,0x3000,0x3ffc,0x1ffc,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // J + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x0300,0x01c0,0x03f0,0x0f38,0x3c0c,0x3004,0x0000,0x0000,0x0000,0x0000, // K + 0x0000,0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x3000,0x3000,0x3000,0x3000,0x0000,0x0000,0x0000,0x0000,0x0000, // L + 0x0000,0x0000,0x3ffc,0x3ffc,0x003c,0x01f0,0x0f80,0x3c00,0x0f80,0x01f0,0x003c,0x3ffc,0x3ffc,0x0000,0x0000,0x0000, // M + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x0078,0x01e0,0x0780,0x1e00,0x3ffc,0x3ffc,0x0000,0x0000,0x0000,0x0000, // N + 0x0000,0x0000,0x0000,0x0000,0x07e0,0x0ff0,0x1818,0x300c,0x300c,0x300c,0x300c,0x1818,0x0ff0,0x07e0,0x0000,0x0000, // O + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x018c,0x018c,0x018c,0x018c,0x00f8,0x0070,0x0000,0x0000,0x0000, // P + 0x0000,0x0000,0x0000,0x0000,0x07e0,0x1ff8,0x1818,0x300c,0x300c,0x300c,0x700c,0xd818,0xcff8,0xc7e0,0x0000,0x0000, // Q + 0x0000,0x0000,0x0000,0x0000,0x3ffc,0x3ffc,0x018c,0x018c,0x018c,0x078c,0x1f8c,0x38f8,0x2070,0x0000,0x0000,0x0000, // R + 0x0000,0x0000,0x0000,0x0c00,0x1c70,0x38f8,0x30dc,0x31cc,0x318c,0x3b8c,0x1f18,0x0e10,0x0000,0x0000,0x0000,0x0000, // S + 0x0000,0x0000,0x0000,0x0000,0x000c,0x000c,0x000c,0x3ffc,0x3ffc,0x000c,0x000c,0x000c,0x0000,0x0000,0x0000,0x0000, // T + 0x0000,0x0000,0x0000,0x0000,0x0ffc,0x1ffc,0x3000,0x3000,0x3000,0x1ffc,0x0ffc,0x0000,0x0000,0x0000,0x0000,0x0000, // U + 0x0000,0x0000,0x0000,0x0000,0x001c,0x03fc,0x3fe0,0x3c00,0x3fe0,0x03fc,0x001c,0x0000,0x0000,0x0000,0x0000,0x0000, // V + 0x0000,0x0000,0x003c,0x03fc,0x3fc0,0x3c00,0x3fc0,0x03fc,0x003c,0x03fc,0x3fc0,0x3c00,0x3fc0,0x03fc,0x003c,0x0000, // W + 0x0000,0x0000,0x0000,0x2004,0x381c,0x1e38,0x07e0,0x01c0,0x07e0,0x1e38,0x381c,0x2004,0x0000,0x0000,0x0000,0x0000, // X + 0x0000,0x0000,0x0000,0x0000,0x000c,0x003c,0x00f0,0x3fc0,0x3fc0,0x00f0,0x003c,0x000c,0x0000,0x0000,0x0000,0x0000, // Y + 0x0000,0x0000,0x0000,0x0000,0x300c,0x3c0c,0x3e0c,0x338c,0x31cc,0x307c,0x303c,0x300c,0x0000,0x0000,0x0000,0x0000, // Z + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7ffe,0x7ffe,0x4002,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // [ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0006,0x0018,0x0060,0x0180,0x0600,0x1800,0x6000,0x8000,0x0000,0x0000,0x0000, // + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4002,0x7ffe,0x7ffe,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ] + 0x0000,0x0000,0x0000,0x0000,0x0040,0x0060,0x0038,0x001e,0x0006,0x001e,0x0038,0x0060,0x0040,0x0000,0x0000,0x0000, // ^ + 0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000,0x6000, // _ + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0038,0x0044,0x0044,0x0038,0x0000,0x0000,0x0000, // ` + 0x0000,0x0000,0x0000,0x0000,0x0e60,0x1f70,0x1930,0x19b0,0x09b0,0x1ff0,0x1fe0,0x0000,0x0000,0x0000,0x0000,0x0000, // a + 0x0000,0x0000,0x0000,0x0000,0x1ffe,0x0ffe,0x1820,0x1830,0x1830,0x0fe0,0x07c0,0x0000,0x0000,0x0000,0x0000,0x0000, // b + 0x0000,0x0000,0x0000,0x0000,0x0000,0x07c0,0x0fe0,0x1c70,0x1830,0x1830,0x1c70,0x0c60,0x0000,0x0000,0x0000,0x0000, // c + 0x0000,0x0000,0x0000,0x0000,0x07c0,0x0fe0,0x1830,0x1830,0x0820,0x1ffe,0x1ffe,0x0000,0x0000,0x0000,0x0000,0x0000, // d + 0x0000,0x0000,0x0000,0x0000,0x07c0,0x0fe0,0x19b0,0x19b0,0x19b0,0x0de0,0x05c0,0x0000,0x0000,0x0000,0x0000,0x0000, // e + 0x0000,0x0000,0x0000,0x0000,0x0030,0x1ffc,0x1ffe,0x0036,0x0036,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // f + 0x0000,0x0000,0x3000,0x7ef0,0x6ff8,0x6d98,0x6d98,0x6d98,0x6dfc,0x3cf4,0x1804,0x0000,0x0000,0x0000,0x0000,0x0000, // g + 0x0000,0x0000,0x0000,0x0000,0x1ffe,0x1ffe,0x0060,0x0030,0x0030,0x1ff0,0x1fe0,0x0000,0x0000,0x0000,0x0000,0x0000, // h + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1fec,0x1fec,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // i + 0x0000,0x0000,0x0000,0x0000,0x0000,0x8000,0x8000,0xffec,0x7fec,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // j + 0x0000,0x0000,0x0000,0x0000,0x1ffe,0x1ffe,0x0300,0x0380,0x0fe0,0x1c70,0x1010,0x0000,0x0000,0x0000,0x0000,0x0000, // k + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1ffe,0x1ffe,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // l + 0x0000,0x0000,0x1ff0,0x1ff0,0x0060,0x0030,0x0030,0x1ff0,0x1fe0,0x0060,0x0030,0x0030,0x1ff0,0x1fe0,0x0000,0x0000, // m + 0x0000,0x0000,0x0000,0x0000,0x1ff0,0x1ff0,0x0060,0x0030,0x0030,0x1ff0,0x1fe0,0x0000,0x0000,0x0000,0x0000,0x0000, // n + 0x0000,0x0000,0x0000,0x0000,0x07c0,0x0fe0,0x1830,0x1830,0x1830,0x0fe0,0x07c0,0x0000,0x0000,0x0000,0x0000,0x0000, // o + + 0x0000,0x0000,0x0000,0x0000,0xfff0,0xfff0,0x0820,0x1830,0x1830,0x0fe0,0x07c0,0x0000,0x0000,0x0000,0x0000,0x0000, // p + 0x0000,0x0000,0x0000,0x0000,0x07c0,0x0fe0,0x1830,0x1830,0x0830,0xffe0,0xfff0,0x0000,0x0000,0x0000,0x0000,0x0000, // q + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1ff0,0x1ff0,0x0060,0x0030,0x0030,0x0000,0x0000,0x0000,0x0000,0x0000, // r + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0ce0,0x1df0,0x19b0,0x1b30,0x1f70,0x0e60,0x0000,0x0000,0x0000,0x0000,0x0000, // s + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0030,0x0ffc,0x1ffc,0x1830,0x1830,0x0000,0x0000,0x0000,0x0000,0x0000, // t + 0x0000,0x0000,0x0000,0x0000,0x0ff0,0x1ff0,0x1800,0x1800,0x0c00,0x1ff0,0x1ff0,0x0000,0x0000,0x0000,0x0000,0x0000, // u + 0x0000,0x0000,0x0000,0x0000,0x0030,0x01f0,0x0fc0,0x1e00,0x0fc0,0x01f0,0x0030,0x0000,0x0000,0x0000,0x0000,0x0000, // v + 0x0000,0x0000,0x0030,0x03f0,0x1fc0,0x1c00,0x0f80,0x01f0,0x01f0,0x0f80,0x1c00,0x1fc0,0x03f0,0x0030,0x0000,0x0000, // w + 0x0000,0x0000,0x0000,0x0000,0x1010,0x1c70,0x0fe0,0x0380,0x0fe0,0x1c70,0x1010,0x0000,0x0000,0x0000,0x0000,0x0000, // x + 0x0000,0x0000,0x0000,0x0000,0x0030,0xc3f0,0xffc0,0x7c00,0x1fc0,0x03f0,0x0030,0x0000,0x0000,0x0000,0x0000,0x0000, // y + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1c30,0x1e30,0x1bb0,0x18f0,0x1870,0x0000,0x0000,0x0000,0x0000,0x0000, // z + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0080,0x3f7c,0x7f7e,0x4002,0x4002,0x0000,0x0000,0x0000,0x0000,0x0000, // { + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7fff,0x7fff,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // | + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4002,0x4002,0x7f7e,0x3f7c,0x0080,0x0000,0x0000,0x0000,0x0000,0x0000, // } + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0018,0x001c,0x000c,0x001c,0x0018,0x0018,0x001c,0x000c,0x0000,0x0000,0x0000 // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/Font_Grotesk_16X32.java b/src/devices/Oled/SSD1306/Font_Grotesk_16X32.java new file mode 100644 index 0000000..5c55666 --- /dev/null +++ b/src/devices/Oled/SSD1306/Font_Grotesk_16X32.java @@ -0,0 +1,113 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font_Grotesk_16X32") +public class Font_Grotesk_16X32 extends FontPack { + public Font_Grotesk_16X32() { + super("Font_Grotesk_16X32", 16, 32, 16, 32, 32, 126); + data = new int[] { + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0f07fff8,0x0f07fff8,0x0f07fff8,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ! + 0x00000000,0x00000000,0x00000000,0x00000fc0,0x00000ff0,0x00000ffc,0x0000007c,0x00000000,0x00000000,0x00000fc0,0x00000ff0,0x00000ffc,0x0000007c,0x00000000,0x00000000,0x00000000, // " + 0x001c0000,0x001c0000,0x0e1c0e00,0x0ffc0e00,0x07ff8e00,0x003ffe00,0x001cfff0,0x0c1c0ff8,0x0ffc0e38,0x0fff0e00,0x007ffe00,0x001dffc0,0x001c1ff8,0x001c0e78,0x00180e00,0x00000e00, // # + 0x00000000,0x00000000,0x01c00e00,0x03c03fc0,0x03807fe0,0x0300f0e0,0x0300e070,0x0701e070,0xffffffff,0x0701c070,0x0301c070,0x0383c070,0x01ff80e0,0x00ff00c0,0x003c0000,0x00000000, // $ + 0x00080780,0x000c1fe0,0x000e38f0,0x00067070,0x00077030,0x00037070,0x0003b8f0,0x01f19fe0,0x07f9cf80,0x0f1cc000,0x0e0ee000,0x0c0e6000,0x0e0e7000,0x071c3000,0x03f83800,0x01e01000, // % + 0x00000000,0x01ff0000,0x03ff8000,0x07c3e7e0,0x0f00fff0,0x0e007ff8,0x1c00f83c,0x1c03e01c,0x0c0f801c,0x0e3f001c,0x0e7c0038,0x07f00038,0x07e00000,0x0fff8000,0x0e7f8000,0x080f8000, // & + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000f80,0x00000ff0,0x00000ffc,0x0000007c,0x00000004,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ' + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x000ff800,0x01ffffc0,0x0ffffff0,0x3fc001fe,0x7c00001f,0x60000003,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ( + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x60000003,0x7c00001f,0x3fe003fe,0x07fffff0,0x00ffff80,0x000ff000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ) + 0x00000000,0x00180c00,0x001c1c00,0x001c1800,0x000e3800,0x00077000,0x0003e000,0x01ffffc0,0x03ffffc0,0x0003e000,0x00076000,0x00063000,0x000e3800,0x001c1c00,0x00380c00,0x00000000, // * + 0x00000000,0x0000c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x00ffffc0,0x00ffffc0,0x00ffff80,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x00000000, // + + 0x00000000,0x00000000,0x00000000,0x00000000,0x80000000,0xfc000000,0xfff00000,0x7ff00000,0x0ff00000,0x03f00000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // , + 0x00000000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x0001c000,0x00000000, // - + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0fe00000,0x0fe00000,0x0fe00000,0x0fe00000,0x0fe00000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // . + 0x00000000,0x00000000,0x0c000000,0x0f800000,0x07e00000,0x01f80000,0x007f0000,0x000fc000,0x0003f800,0x0000fe00,0x00001f80,0x000007e0,0x000001f0,0x00000030,0x00000000,0x00000000, // / + + 0x00000000,0x00000000,0x007fff00,0x01ffffe0,0x07fffff0,0x0ffc00f8,0x0e1f0038,0x1c07c01c,0x1c01f01c,0x0e007c3c,0x0f000f78,0x07f80ff0,0x03ffffe0,0x00ffff80,0x0001e000,0x00000000, // 0 + 0x00000000,0x00000000,0x00000000,0x040001e0,0x0e0000e0,0x0e0000f0,0x0e000070,0x0e000078,0x0ffffff8,0x0ffffff8,0x0e7ffff8,0x0e000000,0x0e000000,0x0e000000,0x04000000,0x00000000, // 1 + 0x00000000,0x00000000,0x0f000070,0x0f800078,0x0fc00038,0x0fe0003c,0x0ef0001c,0x0e3c001c,0x0e1e001c,0x0e0f001c,0x0e07c038,0x0e01f078,0x0e00fff0,0x0e003fe0,0x0e000f80,0x00000000, // 2 + 0x00000000,0x00000000,0x0f000030,0x0e000038,0x0e000038,0x0e00e03c,0x1e00e01c,0x1e00e01c,0x1e00e01c,0x0e00e03c,0x0f01f078,0x0f87bff8,0x07ff1ff0,0x03fe0fc0,0x00780000,0x00000000, // 3 + 0x00000000,0x003c0000,0x003f0000,0x003fc000,0x0039f000,0x00387c00,0x00381f00,0x003807c0,0x003800f0,0x0779fc78,0x0ffffff8,0x0ffffff8,0x00380000,0x00380000,0x00380000,0x00000000, // 4 + 0x00000000,0x00000000,0x0f000000,0x0e007ff8,0x0e007ff8,0x1e003ff8,0x1e003838,0x1e003838,0x1e003838,0x0e007838,0x0f00f038,0x0781f038,0x07ffe038,0x01ffc000,0x007e0000,0x00000000, // 5 + 0x00000000,0x00000000,0x007ffe00,0x03ffffc0,0x07ffeff0,0x0f00e0f8,0x0e007038,0x1c00383c,0x1c00381c,0x1c00381c,0x0e00781c,0x0f81f03c,0x07ffe038,0x01ffc000,0x003c0000,0x00000000, // 6 + 0x00000000,0x00000000,0x00000038,0x00000038,0x08000038,0x0f000038,0x0fe00038,0x07fc0038,0x00ff8038,0x001ff838,0x0003ff38,0x00003ff8,0x000007f8,0x000000f8,0x00000018,0x00000000, // 7 + 0x00000000,0x00000000,0x01fc0780,0x07ff1ff0,0x0fffbff8,0x0f03f878,0x0e01e03c,0x1c00e01c,0x1c00e01c,0x1e01e01c,0x0e01f038,0x0f87bff8,0x07ff1ff0,0x03fe0fc0,0x00780000,0x00000000, // 8 + 0x00000000,0x00000000,0x0000ffc0,0x0e03fff0,0x0e07f3f8,0x0e070038,0x1e0f001c,0x1e0e001c,0x0e0e001c,0x0e07003c,0x0f838078,0x07f1fff8,0x01ffffe0,0x007fff80,0x0001e000,0x00000000, // 9 + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x03f03f80,0x03f03f80,0x03f03f80,0x03f03f80,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // : + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x78000000,0x7ff03f80,0x7ff03f80,0x1ff03f80,0x03f03f80,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ; + 0x00000000,0x0001c000,0x0003e000,0x0003e000,0x0007f000,0x00077000,0x000f3800,0x000e3800,0x001e1c00,0x001c1c00,0x003c0e00,0x00380e00,0x00780700,0x00700700,0x00f00380,0x00000000, // < + 0x00000000,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x00000000, // = + 0x00000000,0x00e00380,0x00f00780,0x00700700,0x00380f00,0x00380e00,0x001c1e00,0x001c1c00,0x000e3c00,0x000e3800,0x00077800,0x00077000,0x0003f000,0x0003e000,0x0001e000,0x00000000, // > + 0x00000000,0x00000000,0x00000000,0x00000070,0x00000078,0x00000038,0x0700001c,0x0f1f801c,0x0f1fe01c,0x001bf01c,0x0000783c,0x00003ff8,0x00001ff0,0x000007e0,0x00000000,0x00000000, // ? + + 0x003ff000,0x01fffe00,0x07e01f80,0x1f0003e0,0x3c0000f0,0x781fc070,0x707ff038,0x60f87818,0xe0e01c18,0xe1c01c18,0xe1c01c38,0xe0e01c38,0xe07038f0,0xe0ffffe0,0x40ffff80,0x00000000, // @ + 0x0c000000,0x0fc00000,0x0ffc0000,0x03ffc000,0x003ffe00,0x003dffe0,0x003c0ff8,0x003c0078,0x003c01f8,0x003c3ff8,0x003fff80,0x007ff800,0x0fff8000,0x0ff80000,0x0f000000,0x00000000, // A + 0x00000000,0x00000000,0x0ffffff8,0x0ffffff8,0x0ffffff8,0x0e007038,0x0e007038,0x0e007038,0x0e007038,0x0e00f038,0x0f00f878,0x0783fff0,0x07ff9fe0,0x01ff07c0,0x00780000,0x00000000, // B + 0x00000000,0x0003f000,0x007fff00,0x01ffffc0,0x03fc1ff0,0x078000f8,0x0f000038,0x0e00003c,0x1c00001c,0x1c00001c,0x1e00001c,0x0e00003c,0x0f000038,0x07000070,0x00000000,0x00000000, // C + 0x00000000,0x07fffff8,0x0ffffff8,0x0ffffff8,0x0e000038,0x0e000038,0x0e000038,0x0e000038,0x0f000038,0x07000078,0x07c001f0,0x03ffffe0,0x01ffffc0,0x003fff00,0x00000000,0x00000000, // D + 0x00000000,0x00000000,0x0ffffff8,0x0ffffff8,0x0ffffff8,0x0e007038,0x0e007038,0x0e007038,0x0e007038,0x0e007038,0x0e007038,0x0e007038,0x0e007038,0x0e006038,0x00000000,0x00000000, // E + 0x00000000,0x00000000,0x00000000,0x0ffffff8,0x0ffffff8,0x0ffffff8,0x00007038,0x00007038,0x00007038,0x00007038,0x00007038,0x00007038,0x00007038,0x00006038,0x00000000,0x00000000, // F + 0x00000000,0x000ff800,0x00ffff80,0x03ffffe0,0x07f007f0,0x0f000078,0x0e000038,0x0e00001c,0x1c00001c,0x1c03801c,0x0e03801c,0x0f738038,0x0fff8078,0x07ff8070,0x00000000,0x00000000, // G + 0x00000000,0x07fffff8,0x0ffffff8,0x0ffffff8,0x0000f000,0x00007000,0x00007000,0x00007000,0x00007000,0x00007000,0x00007000,0x0ffffff8,0x0ffffff8,0x0ffffff8,0x00000000,0x00000000, // H + 0x00000000,0x00000000,0x0e000038,0x0e000038,0x0e000038,0x0e000038,0x0ffffff8,0x0ffffff8,0x0ffffff8,0x0e000038,0x0e000038,0x0e000038,0x0e000038,0x00000000,0x00000000,0x00000000, // I + 0x00000000,0x07800000,0x0f000000,0x0e000000,0x0e000018,0x1c000038,0x1c000038,0x1e000038,0x0e000038,0x0fefffb8,0x07fffff8,0x03fffff8,0x00000000,0x00000000,0x00000000,0x00000000, // J + 0x00000000,0x07fffff8,0x0ffffff8,0x0ffffff8,0x0003e000,0x0000f000,0x0000f800,0x0003fe00,0x000fcf00,0x003f0780,0x00fc03e0,0x03f000f0,0x0fc00078,0x0f800038,0x0e000008,0x08000000, // K + 0x00000000,0x00000000,0x07fffff8,0x0ffffff8,0x0ffffff8,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x00000000, // L + 0x0ffffff8,0x0ffffff8,0x0ffffff8,0x000001f8,0x00001fe0,0x0001fe00,0x0007e000,0x0007c000,0x0003fc00,0x00007f80,0x000003f8,0x000000f8,0x0ffffff8,0x0ffffff8,0x00000000,0x00000000, // M + 0x0ffffff8,0x0ffffff8,0x0ffffff8,0x000001f8,0x00000fe0,0x00007f80,0x0001fc00,0x000fe000,0x007f8000,0x03fc0000,0x0fe00000,0x0ffffff8,0x0ffffff8,0x0ffffff8,0x00000000,0x00000000, // N + 0x00000000,0x000ff800,0x00ffffc0,0x03fffff0,0x07f003f8,0x0f000078,0x0e00003c,0x1c00001c,0x1c00001c,0x0e00003c,0x0f000078,0x07ffdff0,0x03ffffe0,0x00ffff80,0x0001e000,0x00000000, // O + + 0x00000000,0x00000000,0x0ffffff8,0x0ffffff8,0x0ffffff8,0x00038038,0x00038038,0x00038038,0x00038038,0x00038038,0x0003c038,0x0001e0f8,0x0001fff0,0x0000ffe0,0x00001f80,0x00000000, // P + 0x00000000,0x000ffc00,0x00ffffc0,0x03fffff0,0x07f003f8,0x07000038,0x0e00001c,0x0e00001c,0x0e00001c,0x1f00003c,0x7f800078,0xf3fedff8,0x61ffffe0,0x007fff80,0x0001e000,0x00000000, // Q + 0x00000000,0x00000000,0x0ffffff8,0x0ffffff8,0x07fbdfb8,0x0003c038,0x0003c038,0x0003c038,0x0003c038,0x0007c038,0x001fe0f8,0x00fefff0,0x07f87fe0,0x0fe01f80,0x0f000000,0x08000000, // R + 0x00000000,0x00000600,0x07003fe0,0x0f007ff0,0x0e00f8f8,0x0e00f038,0x1c01e01c,0x1c01e01c,0x1c01e01c,0x0e03c01c,0x0f03c03c,0x0fdf8038,0x07ff0078,0x01fe0000,0x00000000,0x00000000, // S + 0x00000018,0x00000038,0x00000038,0x00000038,0x00000038,0x00000038,0x07ffffb8,0x0ffffff8,0x0ffffff8,0x00000038,0x00000038,0x00000038,0x00000038,0x00000038,0x00000038,0x00000018, // T + 0x00000000,0x00000000,0x01fffff8,0x07fffff8,0x0ffffff8,0x0e000000,0x0e000000,0x1c000000,0x1c000000,0x0e000000,0x0e000000,0x0ffffff8,0x07fffff8,0x01fffff8,0x00000000,0x00000000, // U + 0x00000000,0x00000078,0x000007f8,0x0000fff8,0x000fff80,0x01fff000,0x0ffe0000,0x0f800000,0x0f800000,0x0ffe0000,0x01fff000,0x000fff80,0x0000fff8,0x000007f8,0x00000078,0x00000000, // V + 0x000000f8,0x0001fff8,0x03fffff8,0x0ffff000,0x0fe00000,0x0fff0000,0x003ffc00,0x00007e00,0x0000fe00,0x003ffc00,0x0fff0000,0x0fe00000,0x0ffff000,0x03fffff8,0x0001fff8,0x000000f8, // W + 0x08000000,0x0e000008,0x0f800038,0x07e000f8,0x01f803f0,0x007e0fc0,0x001fbf00,0x0007f800,0x0003f800,0x000ffe00,0x007f1f80,0x01f807e0,0x07e001f8,0x0f800078,0x0e000018,0x08000000, // X + 0x00000000,0x00000018,0x000000f8,0x000003f8,0x00000fc0,0x00003f00,0x07fdfc00,0x0fffe000,0x0fffe000,0x0000fc00,0x00003f00,0x00000fe0,0x000003f8,0x00000078,0x00000018,0x00000000, // Y + 0x00000000,0x00000000,0x0f000038,0x0fc00038,0x0ff00038,0x0efc0038,0x0e3f0038,0x0e0fc038,0x0e03f038,0x0e00fc38,0x0e003f38,0x0e000ff8,0x0e0003f8,0x0e0000f8,0x0e000038,0x00000000, // Z + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0fffffff,0x0c000003,0x0c000003,0x0c000003,0x00000000,0x00000000,0x00000000, // [ + 0x00000000,0x00000010,0x00000070,0x000001f0,0x00000fe0,0x00003f80,0x0000fc00,0x0007f000,0x001fc000,0x007e0000,0x03f80000,0x0fe00000,0x0f000000,0x0c000000,0x00000000,0x00000000, // + + 0x00000000,0x00000000,0x00000000,0x00000000,0x0c000003,0x0c000003,0x0c000003,0x0fffffff,0x0fffffff,0x07fffffe,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ] + 0x00000000,0x00030000,0x00038000,0x0003e000,0x0000f000,0x00007c00,0x00003f00,0x00000f00,0x00000f00,0x00003f00,0x00007c00,0x0000f800,0x0003e000,0x00038000,0x00030000,0x00000000, // ^ + 0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0e000000, // _ + + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000fc0,0x00000ff8,0x00000ffc,0x0000003c,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ` + 0x00000000,0x00e00000,0x07fc1c00,0x0ffe1e00,0x0f0e0e00,0x1e070e00,0x1c070600,0x1c070700,0x0e070600,0x0e070e00,0x03871e00,0x0ffffc00,0x0ffff800,0x0fffc000,0x00000000,0x00000000, // a + 0x00000000,0x00000000,0x0ffffffc,0x0ffffffc,0x0ffffffc,0x07001c00,0x0e000e00,0x1c000600,0x1c000700,0x1e000e00,0x0f001e00,0x0fe0fc00,0x03fff800,0x00ffe000,0x00000000,0x00000000, // b + 0x00000000,0x00000000,0x003f8000,0x01fff000,0x03fff800,0x07803c00,0x0f001e00,0x0e000e00,0x1c000600,0x1c000700,0x1c000600,0x0e000e00,0x0e001e00,0x07001c00,0x00000000,0x00000000, // c + 0x00000000,0x001f0000,0x01fff000,0x07fffc00,0x0f807e00,0x0e000e00,0x1c000e00,0x1c000700,0x0c000e00,0x0e000e00,0x07803c00,0x0ffffffc,0x0ffffffc,0x0ffffffc,0x00000000,0x00000000, // d + 0x00000000,0x001f0000,0x00ffe000,0x03fff800,0x07c77c00,0x0f071e00,0x0e070e00,0x1c070600,0x1c070700,0x1c070e00,0x0e070e00,0x0e073c00,0x0e07f800,0x0707f000,0x00038000,0x00000000, // e + 0x00000000,0x00000000,0x00000600,0x00000e00,0x00000e00,0x00000e00,0x0fffffe0,0x0ffffff8,0x0ffffffc,0x00000e1c,0x00000e1c,0x00000e1c,0x00000e1c,0x0000060c,0x00000000,0x00000000, // f + 0x00000000,0x00078000,0x007ff000,0x60fffc00,0xe1e03e00,0xe3c00e00,0xc3800600,0xc3800700,0xe3800600,0xe1800e00,0x70e01c00,0x7ffffe00,0x1ffffe00,0x07fffe00,0x00000000,0x00000000, // g + 0x00000000,0x00000000,0x0ffffffc,0x0ffffffc,0x0ffffffc,0x00001800,0x00000e00,0x00000e00,0x00000e00,0x00000f00,0x00001e00,0x0ffffe00,0x0ffff800,0x0fffc000,0x00000000,0x00000000, // h + 0x00000000,0x00000000,0x00000000,0x0c000000,0x0e000e00,0x0e000e00,0x0e000e00,0x0e000e00,0x0ffffe3c,0x0ffffe3c,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x0c000000,0x00000000, // i + 0x00000000,0x00000000,0xc0000000,0xe0000600,0xe0000600,0xe0000600,0xe0000600,0xfbffee38,0x7ffffe3c,0x1ffffe38,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // j + 0x00000000,0x00000000,0x00000000,0x0ffffffc,0x0ffffffc,0x07df3ffc,0x00070000,0x0007c000,0x001fe000,0x007ef000,0x00f83c00,0x03e01e00,0x0f800e00,0x0f000600,0x0c000000,0x00000000, // k + 0x00000000,0x00000000,0x00000000,0x0000000c,0x0000000e,0x0000000e,0x0000000e,0x01fffffe,0x07fffffe,0x0f800000,0x0e000000,0x0e000000,0x0e000000,0x0e000000,0x04000000,0x00000000, // l + 0x00000000,0x0ffffe00,0x0ffffe00,0x07ff7c00,0x00000e00,0x00000600,0x00001e00,0x0ffffe00,0x0ffffc00,0x00000e00,0x00000600,0x00000f00,0x0ffffe00,0x0ffffc00,0x0ffff000,0x00000000, // m + 0x00000000,0x00000000,0x0ffffe00,0x0ffffe00,0x0ffffe00,0x00001800,0x00000e00,0x00000e00,0x00000e00,0x00000f00,0x00001e00,0x0ffffe00,0x0ffff800,0x0fffc000,0x00000000,0x00000000, // n + 0x00000000,0x000e0000,0x01fff000,0x03fff800,0x0fc07c00,0x0f001e00,0x0e000e00,0x1c000700,0x1c000600,0x0e000e00,0x0f001e00,0x07f7fc00,0x03fff800,0x00ffe000,0x00000000,0x00000000, // o + + 0x00000000,0x00000000,0xfffffe00,0xfffffe00,0xfffffe00,0x01c01c00,0x03800e00,0x03000600,0x03000700,0x03800600,0x03c00e00,0x01f87e00,0x00fffc00,0x003ff000,0x00000000,0x00000000, // p + 0x00000000,0x00070000,0x007ff000,0x00fffc00,0x01f03e00,0x03800e00,0x03800600,0x03000700,0x03800600,0x01800e00,0x01e01c00,0xfffffe00,0xfffffe00,0xfffffe00,0x00000000,0x00000000, // q + 0x00000000,0x00000000,0x00000000,0x00000000,0x07fffc00,0x0ffffe00,0x0ffffe00,0x00007800,0x00001c00,0x00000e00,0x00000e00,0x00000f00,0x00000e00,0x00000e00,0x00001e00,0x00000000, // r + 0x00000000,0x00000000,0x00000000,0x0f03f800,0x0e07fc00,0x0e079e00,0x1c0f0e00,0x1c0e0600,0x1c0e0700,0x0e1e0600,0x0f1e0e00,0x07fc0e00,0x03f81c00,0x00400000,0x00000000,0x00000000, // s + 0x00000000,0x00000400,0x00000e00,0x00000e00,0x00000e00,0x00fffff0,0x07fffff0,0x07fffff0,0x0f000e00,0x0e000e00,0x0e000e00,0x0e000e00,0x0e000e00,0x00000000,0x00000000,0x00000000, // t + 0x00000000,0x00000000,0x007ffe00,0x07fffe00,0x0ffffe00,0x0f000000,0x1e000000,0x1c000000,0x0c000000,0x0e000000,0x07000000,0x0ffffe00,0x0ffffe00,0x0ffffe00,0x00000000,0x00000000, // u + 0x00000000,0x00000600,0x00003e00,0x0003fe00,0x001ff000,0x01ff8000,0x0ff80000,0x0f800000,0x0f800000,0x0ff80000,0x01ff8000,0x001ff800,0x0001fe00,0x00003e00,0x00000200,0x00000000, // v + 0x00001e00,0x0003fe00,0x00fffc00,0x0fff8000,0x0fc00000,0x0fc00000,0x01ff0000,0x000fc000,0x000fc000,0x01ff0000,0x0fc00000,0x0fc00000,0x0fff8000,0x00fffc00,0x0003fe00,0x00001e00, // w + 0x00000000,0x08000000,0x0e000600,0x0f801e00,0x07c07c00,0x01f0f800,0x007fe000,0x001f8000,0x001f8000,0x007fe000,0x01f0f800,0x07c07c00,0x0f801e00,0x0e000600,0x08000000,0x00000000, // x + 0x00000000,0x00000200,0xc0001e00,0xe000fe00,0xe007f800,0xe01fc000,0x78fe0000,0x3ff00000,0x0fe00000,0x03fc0000,0x007f8000,0x000ff000,0x0001fe00,0x00003e00,0x00000600,0x00000000, // y + 0x00000000,0x00000000,0x06000000,0x0f000e00,0x0fc00e00,0x0ff00e00,0x0ef80e00,0x0e3e0e00,0x0e0f8e00,0x0e07ce00,0x0e01fe00,0x0e007e00,0x0e003e00,0x0c000e00,0x00000000,0x00000000, // z + 0x00000000,0x00000000,0x00006000,0x0000e000,0x0000e000,0x0000e000,0x0001f000,0x03ffbffc,0x07ff1ffe,0x0ffe0fff,0x0e000007,0x0c000003,0x0c000003,0x0c000002,0x00000000,0x00000000, // { + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0fffffff,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // | + 0x00000000,0x00000000,0x00000000,0x0c000003,0x0c000003,0x0e000007,0x0f7803ef,0x07ff1ffe,0x03ffbffc,0x0003f800,0x0000e000,0x0000e000,0x0000e000,0x0000e000,0x00000000,0x00000000, // } + 0x0000c000,0x0001f000,0x00007c00,0x00001c00,0x00001e00,0x00001e00,0x00003c00,0x0000f800,0x0001e000,0x0003c000,0x00070000,0x00070000,0x00078000,0x0003e000,0x0001f000,0x00006000 // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/Font_Helvetica_16X16.java b/src/devices/Oled/SSD1306/Font_Helvetica_16X16.java new file mode 100644 index 0000000..dcd2c70 --- /dev/null +++ b/src/devices/Oled/SSD1306/Font_Helvetica_16X16.java @@ -0,0 +1,112 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font_Helvetica_16X16") +public class Font_Helvetica_16X16 extends FontPack { + public Font_Helvetica_16X16() { + super("Font_Helvetica_16X16", 16, 16, 16, 16, 32, 126); + data = new int[] { + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x27f8,0x27f8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ! + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0038,0x0038,0x0000,0x0038,0x0038,0x0000,0x0000,0x0000,0x0000,0x0000, // " + 0x0000,0x0000,0x0000,0x0480,0x0480,0x0fe0,0x0fe0,0x0480,0x0fe0,0x0fe0,0x0480,0x0000,0x0000,0x0000,0x0000,0x0000, // # + 0x0000,0x0000,0x0000,0x0000,0x10e0,0x19f8,0x1998,0x3ff8,0x3ff8,0x1998,0x1f98,0x0f18,0x0018,0x0000,0x0000,0x0000, // $ + 0x0000,0x0078,0x00fc,0x2084,0x3084,0x1cfc,0x0e78,0x0380,0x01c0,0x1e70,0x3f38,0x210c,0x2104,0x3f00,0x1e00,0x0000, // % + 0x0000,0x0000,0x0000,0x0e00,0x1f38,0x39fc,0x30cc,0x31cc,0x37fc,0x1e38,0x1c00,0x3f80,0x3380,0x0000,0x0000,0x0000, // & + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0038,0x0038,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ' + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03e0,0x0ff8,0x1c1c,0x1004,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ( + 0x0000,0x0000,0x0000,0x0000,0x1004,0x1c1c,0x0ff8,0x03e0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ) + 0x0000,0x0000,0x0000,0x0060,0x02e0,0x06e0,0x0fe0,0x07f8,0x03f8,0x03f0,0x07c0,0x0fe0,0x06e0,0x00e0,0x0000,0x0000, // * + 0x0000,0x0000,0x0000,0x0000,0x0400,0x0400,0x0400,0x1f80,0x1f80,0x0400,0x0400,0x0400,0x0000,0x0000,0x0000,0x0000, // + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2c00,0x1c00,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // , + 0x0000,0x0000,0x0000,0x0000,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0000,0x0000,0x0000,0x0000,0x0000, // - + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1800,0x1800,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // . + 0x0000,0x0000,0x0000,0x0000,0x0000,0x2000,0x3800,0x1e00,0x0780,0x01e0,0x0078,0x001c,0x0004,0x0000,0x0000,0x0000, // / + + 0x0000,0x0000,0x0000,0x0000,0x1ff0,0x3ff8,0x3018,0x2008,0x2008,0x2008,0x3018,0x3ff8,0x1ff0,0x0000,0x0000,0x0000, // 0 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3ff8,0x3ff8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // 1 + 0x0000,0x0000,0x0000,0x0000,0x3e08,0x3f08,0x2108,0x2108,0x2108,0x2108,0x2108,0x21f8,0x20f0,0x0000,0x0000,0x0000, // 2 + 0x0000,0x0000,0x0000,0x0000,0x2008,0x2108,0x2108,0x2108,0x2108,0x2108,0x2108,0x3ff8,0x1ff0,0x0000,0x0000,0x0000, // 3 + 0x0000,0x0000,0x0000,0x0000,0x01f8,0x03f8,0x0200,0x0200,0x0200,0x3f80,0x3f80,0x0200,0x0200,0x0000,0x0000,0x0000, // 4 + 0x0000,0x0000,0x0000,0x0000,0x21f8,0x21f8,0x2108,0x2108,0x2108,0x2108,0x2108,0x3f08,0x1e08,0x0000,0x0000,0x0000, // 5 + 0x0000,0x0000,0x0000,0x0000,0x1ff0,0x3ff8,0x3188,0x2088,0x2088,0x2088,0x2088,0x3188,0x1f88,0x1f00,0x0000,0x0000, // 6 + 0x0000,0x0000,0x0000,0x0000,0x0008,0x0008,0x3008,0x3c08,0x0f08,0x03c8,0x00f8,0x0038,0x0008,0x0000,0x0000,0x0000, // 7 + 0x0000,0x0000,0x0000,0x0000,0x1ef0,0x3ff8,0x2108,0x2108,0x2108,0x2108,0x2108,0x3ff8,0x1ff0,0x0000,0x0000,0x0000, // 8 + 0x0000,0x0000,0x0000,0x0000,0x01f0,0x21f8,0x2318,0x2208,0x2208,0x2208,0x2208,0x2318,0x3ff8,0x1ff0,0x0000,0x0000, // 9 + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0c30,0x0c30,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // : + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2c30,0x1c30,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ; + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0300,0x0780,0x0780,0x0cc0,0x0840,0x1860,0x1020,0x0000,0x0000,0x0000,0x0000, // < + 0x0000,0x0000,0x0000,0x0000,0x0240,0x0240,0x0240,0x0240,0x0240,0x0240,0x0240,0x0240,0x0240,0x0000,0x0000,0x0000, // = + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1020,0x1860,0x0cc0,0x0cc0,0x0780,0x0300,0x0300,0x0000,0x0000,0x0000, // > + 0x0000,0x0000,0x0000,0x0000,0x0008,0x0008,0x2708,0x2788,0x0088,0x0088,0x0088,0x00f8,0x0070,0x0000,0x0000,0x0000, // ? + + 0x0000,0x0000,0x0000,0x0000,0x0ff0,0x1008,0x13c8,0x1428,0x1428,0x1428,0x17e8,0x1408,0x17f0,0x0000,0x0000,0x0000, // @ + 0x0000,0x0000,0x0000,0x2000,0x3c00,0x1f80,0x03f0,0x0278,0x0278,0x03f0,0x1f80,0x3c00,0x2000,0x0000,0x0000,0x0000, // A + 0x0000,0x0000,0x0000,0x0000,0x3ff8,0x3ff8,0x2108,0x2108,0x2108,0x2108,0x2108,0x3ff8,0x1ff0,0x0000,0x0000,0x0000, // B + 0x0000,0x0000,0x0000,0x0000,0x1ff0,0x3ff8,0x2008,0x2008,0x2008,0x2008,0x2008,0x2008,0x2008,0x0000,0x0000,0x0000, // C + 0x0000,0x0000,0x0000,0x0000,0x3ff8,0x3ff8,0x2008,0x2008,0x2008,0x2008,0x2008,0x3ff8,0x1ff0,0x0000,0x0000,0x0000, // D + 0x0000,0x0000,0x0000,0x0000,0x1ff0,0x3ff8,0x2108,0x2108,0x2108,0x2108,0x2108,0x2108,0x2108,0x0000,0x0000,0x0000, // E + 0x0000,0x0000,0x0000,0x0000,0x3ff0,0x3ff8,0x0108,0x0108,0x0108,0x0108,0x0108,0x0108,0x0108,0x0000,0x0000,0x0000, // F + 0x0000,0x0000,0x0000,0x0000,0x1ff0,0x3ff8,0x2008,0x2008,0x2088,0x2088,0x2088,0x2088,0x3f88,0x3f88,0x0000,0x0000, // G + 0x0000,0x0000,0x0000,0x0000,0x3ff8,0x3ff8,0x0100,0x0100,0x0100,0x0100,0x0100,0x0100,0x3ff8,0x3ff8,0x0000,0x0000, // H + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3ff8,0x3ff8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // I + 0x0000,0x0000,0x0000,0x0000,0x2000,0x2000,0x2000,0x2000,0x2000,0x2000,0x2000,0x3ff8,0x1ff8,0x0000,0x0000,0x0000, // J + 0x0000,0x0000,0x0000,0x0000,0x3ff8,0x3ff8,0x0100,0x0100,0x0180,0x01c0,0x01f0,0x3f38,0x3e18,0x0000,0x0000,0x0000, // K + 0x0000,0x0000,0x0000,0x0000,0x1ff8,0x3ff8,0x2000,0x2000,0x2000,0x2000,0x2000,0x2000,0x2000,0x0000,0x0000,0x0000, // L + 0x0000,0x3ff8,0x3ff8,0x0008,0x0008,0x0008,0x0008,0x3ff8,0x3ff8,0x0008,0x0008,0x0008,0x0008,0x3ff8,0x3ff0,0x0000, // M + 0x0000,0x0000,0x0000,0x0000,0x3ff8,0x3ff8,0x0008,0x0008,0x0008,0x0008,0x0008,0x0008,0x3ff8,0x3ff0,0x0000,0x0000, // N + 0x0000,0x0000,0x0000,0x0000,0x0000,0x1ff0,0x3ff8,0x2008,0x2008,0x2008,0x2008,0x2008,0x2008,0x3ff8,0x1ff0,0x0000, // O + + 0x0000,0x0000,0x0000,0x0000,0x3ff8,0x3ff8,0x0108,0x0108,0x0108,0x0108,0x0108,0x01f8,0x00f0,0x0000,0x0000,0x0000, // P + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0ff8,0x1ffc,0x1004,0x1004,0x7004,0xf004,0xd004,0x9004,0x9ffc,0x8ff8,0x0000, // Q + 0x0000,0x0000,0x0000,0x0000,0x3ff8,0x3ff8,0x0108,0x0108,0x0108,0x0108,0x0108,0x3ff8,0x3ff0,0x0000,0x0000,0x0000, // R + 0x0000,0x0000,0x0000,0x0000,0x20f0,0x21f8,0x2108,0x2108,0x2108,0x2108,0x2108,0x3f08,0x1e08,0x0000,0x0000,0x0000, // S + 0x0000,0x0000,0x0000,0x0000,0x0008,0x0008,0x0008,0x0008,0x3ff8,0x3ff8,0x0008,0x0008,0x0008,0x0008,0x0000,0x0000, // T + 0x0000,0x0000,0x0000,0x1ff8,0x3ff8,0x2000,0x2000,0x2000,0x2000,0x2000,0x2000,0x3ff8,0x1ff8,0x0000,0x0000,0x0000, // U + 0x0000,0x0000,0x0000,0x0008,0x0078,0x03f0,0x1f80,0x3c00,0x3c00,0x1f80,0x03f0,0x0078,0x0008,0x0000,0x0000,0x0000, // V + 0x0000,0x0000,0x01f8,0x1fe0,0x3e00,0x3f80,0x07f0,0x0078,0x03f8,0x3fc0,0x3c00,0x3fc0,0x03f0,0x0038,0x0000,0x0000, // W + 0x0000,0x0000,0x0000,0x2008,0x3018,0x3c78,0x0ee0,0x03c0,0x03c0,0x0ee0,0x3c78,0x3018,0x2008,0x0000,0x0000,0x0000, // X + 0x0000,0x0000,0x0000,0x0008,0x0018,0x0078,0x00e0,0x3fc0,0x3fc0,0x00e0,0x0078,0x0018,0x0008,0x0000,0x0000,0x0000, // Y + 0x0000,0x0000,0x0000,0x2008,0x3008,0x3c08,0x2e08,0x2388,0x21c8,0x2078,0x2038,0x2008,0x2000,0x0000,0x0000,0x0000, // Z + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7ffe,0x7ffe,0x6002,0x6002,0x6002,0x0000,0x0000,0x0000,0x0000,0x0000, // [ + 0x0000,0x0000,0x0000,0x0000,0x0004,0x003c,0x00f8,0x03f0,0x0fc0,0x3f00,0x7800,0x6000,0x0000,0x0000,0x0000,0x0000, // + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x6002,0x6002,0x7ffe,0x7ffe,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ] + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0010,0x0018,0x001c,0x000c,0x001c,0x0018,0x0000,0x0000,0x0000,0x0000,0x0000, // ^ + 0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000,0x4000, // _ + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0038,0x007c,0x0044,0x0044,0x0044,0x007c,0x0038,0x0000,0x0000,0x0000,0x0000, // ` + 0x0000,0x0000,0x0000,0x0000,0x0e20,0x1f20,0x1b20,0x1120,0x1120,0x1120,0x1fe0,0x1fc0,0x0000,0x0000,0x0000,0x0000, // a + 0x0000,0x0000,0x0000,0x0000,0x1ffc,0x1ffc,0x1020,0x1020,0x1020,0x1020,0x1fe0,0x0fc0,0x0000,0x0000,0x0000,0x0000, // b + 0x0000,0x0000,0x0000,0x0000,0x0fc0,0x1fe0,0x1020,0x1020,0x1020,0x1020,0x1020,0x1020,0x0000,0x0000,0x0000,0x0000, // c + 0x0000,0x0000,0x0000,0x0000,0x0fc0,0x1fe0,0x1020,0x1020,0x1020,0x1020,0x1ffc,0x1ffc,0x0000,0x0000,0x0000,0x0000, // d + 0x0000,0x0000,0x0000,0x0000,0x0fc0,0x1fe0,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x0000,0x0000,0x0000,0x0000, // e + 0x0000,0x0000,0x0000,0x0000,0x0020,0x1ff8,0x1ffc,0x0024,0x0024,0x0024,0x0004,0x0000,0x0000,0x0000,0x0000,0x0000, // f + 0x0000,0x0000,0x0000,0x07e0,0x4ff0,0x4810,0x4810,0x4810,0x4810,0x7ff0,0x3ff0,0x0000,0x0000,0x0000,0x0000,0x0000, // g + 0x0000,0x0000,0x0000,0x0000,0x1ffc,0x1ffc,0x0020,0x0020,0x0020,0x0020,0x1fe0,0x1fc0,0x0000,0x0000,0x0000,0x0000, // h + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1fc8,0x1fe8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // i + 0x0000,0x0000,0x0000,0x0000,0x0000,0x4000,0x6000,0x6000,0x7fe8,0x3fe8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // j + 0x0000,0x0000,0x0000,0x0000,0x1ffc,0x1ffc,0x0100,0x0100,0x0180,0x01c0,0x1f60,0x1e20,0x0000,0x0000,0x0000,0x0000, // k + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1ffc,0x1ffc,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // l + 0x0000,0x0000,0x1fe0,0x1fe0,0x0020,0x0020,0x0020,0x1fe0,0x1fe0,0x0020,0x0020,0x0020,0x1fe0,0x1fc0,0x0000,0x0000, // m + 0x0000,0x0000,0x0000,0x0000,0x1fe0,0x1fe0,0x0020,0x0020,0x0020,0x0020,0x1fe0,0x1fc0,0x0000,0x0000,0x0000,0x0000, // n + 0x0000,0x0000,0x0000,0x0000,0x0fc0,0x1fe0,0x1020,0x1020,0x1020,0x1020,0x1fe0,0x0fc0,0x0000,0x0000,0x0000,0x0000, // o + + 0x0000,0x0000,0x0000,0x0000,0xffe0,0xffe0,0x1020,0x1020,0x1020,0x1020,0x1fe0,0x0fc0,0x0000,0x0000,0x0000,0x0000, // p + 0x0000,0x0000,0x0000,0x0000,0x0fc0,0x1fe0,0x1020,0x1020,0x1020,0x1020,0xffe0,0xffe0,0x0000,0x0000,0x0000,0x0000, // q + 0x0000,0x0000,0x0000,0x0000,0x1fe0,0x1fe0,0x0020,0x0020,0x0020,0x0020,0x00e0,0x00c0,0x0000,0x0000,0x0000,0x0000, // r + 0x0000,0x0000,0x0000,0x0000,0x10c0,0x11e0,0x1120,0x1120,0x1120,0x1120,0x1f20,0x0e20,0x0000,0x0000,0x0000,0x0000, // s + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0020,0x0ffc,0x1ffc,0x1020,0x1020,0x1020,0x1000,0x0000,0x0000,0x0000,0x0000, // t + 0x0000,0x0000,0x0000,0x0000,0x0fe0,0x1fe0,0x1000,0x1000,0x1000,0x1000,0x1fe0,0x1fe0,0x0000,0x0000,0x0000,0x0000, // u + 0x0000,0x0000,0x0000,0x0000,0x0020,0x01e0,0x07c0,0x1e00,0x1800,0x1f00,0x07c0,0x00e0,0x0020,0x0000,0x0000,0x0000, // v + 0x0000,0x0000,0x0fe0,0x1fe0,0x1000,0x1000,0x1000,0x1fe0,0x1fe0,0x1000,0x1000,0x1000,0x1fe0,0x1fe0,0x0000,0x0000, // w + 0x0000,0x0000,0x0000,0x0000,0x0020,0x1060,0x1ce0,0x0f80,0x0700,0x0fc0,0x18e0,0x1060,0x0000,0x0000,0x0000,0x0000, // x + 0x0000,0x0000,0x0000,0x0000,0x8fe0,0x9fe0,0x9000,0x9000,0x9000,0x9000,0xffe0,0x7fe0,0x0000,0x0000,0x0000,0x0000, // y + 0x0000,0x0000,0x0000,0x0000,0x0000,0x1020,0x1820,0x1c20,0x1720,0x13a0,0x10e0,0x1060,0x1020,0x1000,0x0000,0x0000, // z + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0180,0x3ffe,0x7006,0x6002,0x6002,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // { + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x7fff,0x7fff,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // | + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4002,0x7006,0x7f7e,0x3ffe,0x01c0,0x0000,0x0000,0x0000,0x0000,0x0000, // } + 0x0000,0x0000,0x0000,0x0000,0x0018,0x001c,0x0004,0x0004,0x001c,0x0038,0x0020,0x0030,0x0038,0x0018,0x0000,0x0000 // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/Font_Retro_16X32.java b/src/devices/Oled/SSD1306/Font_Retro_16X32.java new file mode 100644 index 0000000..6d72fb2 --- /dev/null +++ b/src/devices/Oled/SSD1306/Font_Retro_16X32.java @@ -0,0 +1,116 @@ +package devices.Oled.SSD1306; +// Source : https://github.com/lynniemagoo/oled-font-pack/blob/master/fonts/16x32/grotesk-font-16x32.js + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font_Retro_16X32") +public class Font_Retro_16X32 extends FontPack{ + + public Font_Retro_16X32() { + super("Font_Retro_16X32", 16, 32, 16, 32, 32, 126); + data =new int[] { + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0f0fffff,0x0f0fffff,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ! + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000fff,0x00000fff,0x00000000,0x00000000,0x00000000,0x00000000,0x00000fff,0x00000fff,0x00000000,0x00000000,0x00000000,0x00000000, // " + 0x00000000,0x00000000,0x000f0f00,0x000f0f00,0x0fffffff,0x0fffffff,0x000f0f00,0x000f0f00,0x000f0f00,0x000f0f00,0x0fffffff,0x0fffffff,0x000f0f00,0x000f0f00,0x00000000,0x00000000, // # + 0x00000000,0x00000000,0x00f00f00,0x00f00f00,0x00f0f0f0,0x00f0f0f0,0x0fffffff,0x0fffffff,0x00f0f0f0,0x00f0f0f0,0x000f00f0,0x000f00f0,0x00000000,0x00000000,0x00000000,0x00000000, // $ + 0x00000000,0x00000000,0x0f000ff0,0x0f000ff0,0x00f00ff0,0x00f00ff0,0x000f0000,0x000f0000,0x0000f000,0x0000f000,0x0ff00f00,0x0ff00f00,0x0ff000f0,0x0ff000f0,0x00000000,0x00000000, // % + 0x00000000,0x00000000,0x00ff0ff0,0x00ff0ff0,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f0f0ff0,0x0f0f0ff0,0x00f00000,0x00f00000,0x0f0f0000,0x0f0f0000,0x00000000,0x00000000, // & + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000fff,0x00000fff,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ' + 0x00000000,0x00000000,0x000fff00,0x000fff00,0x00f000f0,0x00f000f0,0x0f00000f,0x0f00000f,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ( + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0f00000f,0x0f00000f,0x00f000f0,0x00f000f0,0x000fff00,0x000fff00,0x00000000,0x00000000,0x00000000,0x00000000, // ) + 0x00000000,0x00000000,0x00f000f0,0x00f000f0,0x000f0f00,0x000f0f00,0x0fffffff,0x0fffffff,0x000f0f00,0x000f0f00,0x00f000f0,0x00f000f0,0x00000000,0x00000000,0x00000000,0x00000000, // * + 0x00000000,0x00000000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x00fffff0,0x00fffff0,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x00000000,0x00000000,0x00000000,0x00000000, // + + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0f000000,0x0f000000,0x00ff0000,0x00ff0000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // , + 0x00000000,0x00000000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x00000000,0x00000000, // - + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0f000000,0x0f000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // . + 0x00000000,0x00000000,0x0f000000,0x0f000000,0x00f00000,0x00f00000,0x000f0000,0x000f0000,0x0000f000,0x0000f000,0x00000f00,0x00000f00,0x000000f0,0x000000f0,0x00000000,0x00000000, // / + + 0x00000000,0x00000000,0x00fffff0,0x00fffff0,0x0f0f000f,0x0f0f000f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f000f0f,0x0f000f0f,0x00fffff0,0x00fffff0,0x00000000,0x00000000, // 0 + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0f0000f0,0x0f0000f0,0x0fffffff,0x0fffffff,0x0f000000,0x0f000000,0x00000000,0x00000000,0x00000000,0x00000000, // 1 + 0x00000000,0x00000000,0x0ff000f0,0x0ff000f0,0x0f0f000f,0x0f0f000f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f000ff0,0x0f000ff0,0x00000000,0x00000000, // 2 + 0x00000000,0x00000000,0x00f0000f,0x00f0000f,0x0f00000f,0x0f00000f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00ff0f,0x0f00ff0f,0x00ff00ff,0x00ff00ff,0x00000000,0x00000000, // 3 + 0x00000000,0x00000000,0x000f0000,0x000f0000,0x000ff000,0x000ff000,0x000f0f00,0x000f0f00,0x000f00f0,0x000f00f0,0x0fffffff,0x0fffffff,0x000f0000,0x000f0000,0x00000000,0x00000000, // 4 + 0x00000000,0x00000000,0x00f00fff,0x00f00fff,0x0f000f0f,0x0f000f0f,0x0f000f0f,0x0f000f0f,0x0f000f0f,0x0f000f0f,0x0f000f0f,0x0f000f0f,0x00fff00f,0x00fff00f,0x00000000,0x00000000, // 5 + 0x00000000,0x00000000,0x00ffff00,0x00ffff00,0x0f00f0f0,0x0f00f0f0,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x00ff000f,0x00ff000f,0x00000000,0x00000000, // 6 + 0x00000000,0x00000000,0x0000000f,0x0000000f,0x0000000f,0x0000000f,0x0fff000f,0x0fff000f,0x0000f00f,0x0000f00f,0x00000f0f,0x00000f0f,0x000000ff,0x000000ff,0x00000000,0x00000000, // 7 + 0x00000000,0x00000000,0x00ff0ff0,0x00ff0ff0,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x00ff0ff0,0x00ff0ff0,0x00000000,0x00000000, // 8 + 0x00000000,0x00000000,0x0f000ff0,0x0f000ff0,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x00f0f00f,0x00f0f00f,0x000ffff0,0x000ffff0,0x00000000,0x00000000, // 9 + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x000f0f00,0x000f0f00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // : + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x03000000,0x03000000,0x00ff0f00,0x00ff0f00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ; + 0x00000000,0x00000000,0x00000000,0x00000000,0x0000f000,0x0000f000,0x000f0f00,0x000f0f00,0x00f000f0,0x00f000f0,0x0f00000f,0x0f00000f,0x00000000,0x00000000,0x00000000,0x00000000, // < + 0x00000000,0x00000000,0x000f0f00,0x000f0f00,0x000f0f00,0x000f0f00,0x000f0f00,0x000f0f00,0x000f0f00,0x000f0f00,0x000f0f00,0x000f0f00,0x000f0f00,0x000f0f00,0x00000000,0x00000000, // = + 0x00000000,0x00000000,0x00000000,0x00000000,0x0f00000f,0x0f00000f,0x00f000f0,0x00f000f0,0x000f0f00,0x000f0f00,0x0000f000,0x0000f000,0x00000000,0x00000000,0x00000000,0x00000000, // > + 0x00000000,0x00000000,0x000000f0,0x000000f0,0x0000000f,0x0000000f,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ? + + 0x00000000,0x00000000,0x00fffff0,0x00fffff0,0x0f00000f,0x0f00000f,0x0f00f00f,0x0f00f00f,0x0f0f0f0f,0x0f0f0f0f,0x0f0ff00f,0x0f0ff00f,0x0f00fff0,0x0f00fff0,0x00000000,0x00000000, // @ + 0x00000000,0x00000000,0x0fffff00,0x0fffff00,0x000f00f0,0x000f00f0,0x000f000f,0x000f000f,0x000f000f,0x000f000f,0x000f00f0,0x000f00f0,0x0fffff00,0x0fffff00,0x00000000,0x00000000, // A + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x00ff0ff0,0x00ff0ff0,0x00000000,0x00000000, // B + 0x00000000,0x00000000,0x00fffff0,0x00fffff0,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x00f000f0,0x00f000f0,0x00000000,0x00000000, // C + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x00fffff0,0x00fffff0,0x00000000,0x00000000, // D + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00000f,0x0f00000f,0x00000000,0x00000000, // E + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000000f,0x0000000f,0x00000000,0x00000000, // F + 0x00000000,0x00000000,0x00fffff0,0x00fffff0,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f03c00f,0x0f03c00f,0x0f03c00f,0x0f03c00f,0x0fffc00f,0x0fffc00f,0x00000000,0x00000000, // G + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x0fffffff,0x0fffffff,0x00000000,0x00000000, // H + 0x00000000,0x00000000,0x00000000,0x00000000,0x0f00000f,0x0f00000f,0x0fffffff,0x0fffffff,0x0f00000f,0x0f00000f,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // I + 0x00000000,0x00000000,0x00f00000,0x00f00000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x00ffffff,0x00ffffff,0x00000000,0x00000000, // J + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x000f0f00,0x000f0f00,0x00f000f0,0x00f000f0,0x0f00000f,0x0f00000f,0x00000000,0x00000000, // K + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x00000000,0x00000000, // L + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x000000f0,0x000000f0,0x0000ff00,0x0000ff00,0x0000ff00,0x0000ff00,0x000000f0,0x000000f0,0x0fffffff,0x0fffffff,0x00000000,0x00000000, // M + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x000000f0,0x000000f0,0x0000ff00,0x0000ff00,0x000ff000,0x000ff000,0x00f00000,0x00f00000,0x0fffffff,0x0fffffff,0x00000000,0x00000000, // N + 0x00000000,0x00000000,0x00fffff0,0x00fffff0,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x00fffff0,0x00fffff0,0x00000000,0x00000000, // O + + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x00000ff0,0x00000ff0,0x00000000,0x00000000, // P + 0x00000000,0x00000000,0x00fffff0,0x00fffff0,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f0f000f,0x0f0f000f,0x00f0000f,0x00f0000f,0x0f0ffff0,0x0f0ffff0,0x00000000,0x00000000, // Q + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x000ff00f,0x000ff00f,0x00f0f00f,0x00f0f00f,0x0f000ff0,0x0f000ff0,0x00000000,0x00000000, // R + 0x00000000,0x00000000,0x00f00ff0,0x00f00ff0,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x00ff00f0,0x00ff00f0,0x00000000,0x00000000, // S + 0x00000000,0x00000000,0x0000000f,0x0000000f,0x0000000f,0x0000000f,0x0fffffff,0x0fffffff,0x0000000f,0x0000000f,0x0000000f,0x0000000f,0x00000000,0x00000000,0x00000000,0x00000000, // T + 0x00000000,0x00000000,0x00ffffff,0x00ffffff,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x00ffffff,0x00ffffff,0x00000000,0x00000000, // U + 0x00000000,0x00000000,0x000fffff,0x000fffff,0x00f00000,0x00f00000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x00f00000,0x00f00000,0x000fffff,0x000fffff,0x00000000,0x00000000, // V + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x00f00000,0x00f00000,0x000ff000,0x000ff000,0x000ff000,0x000ff000,0x00f00000,0x00f00000,0x0fffffff,0x0fffffff,0x00000000,0x00000000, // W + 0x00000000,0x00000000,0x0ff000ff,0x0ff000ff,0x000f0f00,0x000f0f00,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x000f0f00,0x000f0f00,0x0ff000ff,0x0ff000ff,0x00000000,0x00000000, // X + 0x00000000,0x00000000,0x000000ff,0x000000ff,0x00000f00,0x00000f00,0x0ffff000,0x0ffff000,0x00000f00,0x00000f00,0x000000ff,0x000000ff,0x00000000,0x00000000,0x00000000,0x00000000, // Y + 0x00000000,0x00000000,0x0ff0000f,0x0ff0000f,0x0f0f000f,0x0f0f000f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f00f00f,0x0f000f0f,0x0f000f0f,0x0f0000ff,0x0f0000ff,0x00000000,0x00000000, // Z + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0fffffff,0x0fffffff,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x00000000,0x00000000, // [ + 0x00000000,0x00000000,0x000000f0,0x000000f0,0x00000f00,0x00000f00,0x0000f000,0x0000f000,0x000f0000,0x000f0000,0x00f00000,0x00f00000,0x0f000000,0x0f000000,0x00000000,0x00000000, // + + 0x00000000,0x00000000,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0fffffff,0x0fffffff,0x0fffffff,0x0fffffff,0x00000000,0x00000000, // ] + 0x00000000,0x00000000,0x000f0000,0x000f0000,0x0000f000,0x0000f000,0x00000f00,0x00000f00,0x0000f000,0x0000f000,0x000f0000,0x000f0000,0x00000000,0x00000000,0x00000000,0x00000000, // ^ + 0x00000000,0x00000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x00000000,0x00000000, // _ + + 0x00000000,0x00000000,0x00000000,0x00000000,0x0000000f,0x0000000f,0x000000f0,0x000000f0,0x00000f00,0x00000f00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ` + 0x00000000,0x00000000,0x00f00000,0x00f00000,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0ffff000,0x0ffff000,0x00000000,0x00000000, // a + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x00fff000,0x00fff000,0x00000000,0x00000000, // b + 0x00000000,0x00000000,0x00fff000,0x00fff000,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x00000000,0x00000000, // c + 0x00000000,0x00000000,0x00fff000,0x00fff000,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0fffffff,0x0fffffff,0x00000000,0x00000000, // d + 0x00000000,0x00000000,0x00fff000,0x00fff000,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0ff000,0x0f0ff000,0x00000000,0x00000000, // e + 0x00000000,0x00000000,0x0000f000,0x0000f000,0x0ffffff0,0x0ffffff0,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x0000f00f,0x000000f0,0x000000f0,0x00000000,0x00000000, // f + 0x00000000,0x00000000,0x000ff000,0x000ff000,0xf0f00f00,0xf0f00f00,0xf0f00f00,0xf0f00f00,0xf0f00f00,0xf0f00f00,0xf0f00f00,0xf0f00f00,0x0ffff000,0x0ffff000,0x00000000,0x00000000, // g + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x0ffff000,0x0ffff000,0x00000000,0x00000000, // h + 0x00000000,0x00000000,0x00000000,0x00000000,0x0f000f00,0x0f000f00,0x0fffff0f,0x0fffff0f,0x0f000000,0x0f000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // i + 0x00000000,0x00000000,0x0f000000,0x0f000000,0xf0000f00,0xf0000f00,0xf0000f00,0xf0000f00,0xf0000f00,0xf0000f00,0x0fffff0f,0x0fffff0f,0x00000000,0x00000000,0x00000000,0x00000000, // j + 0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x000f0000,0x000f0000,0x000f0000,0x000f0000,0x000f0000,0x000f0000,0x00f0f000,0x00f0f000,0x0f000f00,0x0f000f00,0x00000000,0x00000000, // k + 0x00000000,0x00000000,0x00000000,0x00000000,0x0f00000f,0x0f00000f,0x0fffffff,0x0fffffff,0x0f000000,0x0f000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // l + 0x00000000,0x00000000,0x0fffff00,0x0fffff00,0x00000f00,0x00000f00,0x00fff000,0x00fff000,0x00fff000,0x00fff000,0x00000f00,0x00000f00,0x0fffff00,0x0fffff00,0x00000000,0x00000000, // m + 0x00000000,0x00000000,0x0fffff00,0x0fffff00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x0ffff000,0x0ffff000,0x00000000,0x00000000, // n + 0x00000000,0x00000000,0x00fff000,0x00fff000,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x00fff000,0x00fff000,0x00000000,0x00000000, // o + + 0x00000000,0x00000000,0xffffff00,0xffffff00,0x00f00f00,0x00f00f00,0x00f00f00,0x00f00f00,0x00f00f00,0x00f00f00,0x00f00f00,0x00f00f00,0x000ff000,0x000ff000,0x00000000,0x00000000, // p + 0x00000000,0x00000000,0x000ff000,0x000ff000,0x00f00f00,0x00f00f00,0x00f00f00,0x00f00f00,0x00f00f00,0x00f00f00,0x00f00f00,0x00f00f00,0xffffff00,0xffffff00,0x00000000,0x00000000, // q + 0x00000000,0x00000000,0x0fffff00,0x0fffff00,0x0000f000,0x0000f000,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00000000,0x00000000, // r + 0x00000000,0x00000000,0x0f00f000,0x0f00f000,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x00f00f00,0x00f00f00,0x00000000,0x00000000, // s + 0x00000000,0x00000000,0x00000f00,0x00000f00,0x00000f00,0x00000f00,0x00ffffff,0x00ffffff,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x00f00000,0x00f00000,0x00000000,0x00000000, // t + 0x00000000,0x00000000,0x00ffff00,0x00ffff00,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x00f00000,0x00f00000,0x0fffff00,0x0fffff00,0x00000000,0x00000000, // u + 0x00000000,0x00000000,0x000fff00,0x000fff00,0x00f00000,0x00f00000,0x0f000000,0x0f000000,0x0f000000,0x0f000000,0x00f00000,0x00f00000,0x000fff00,0x000fff00,0x00000000,0x00000000, // v + 0x00000000,0x00000000,0x0fffff00,0x0fffff00,0x0f000000,0x0f000000,0x00ff0000,0x00ff0000,0x00ff0000,0x00ff0000,0x0f000000,0x0f000000,0x0fffff00,0x0fffff00,0x00000000,0x00000000, // w + 0x00000000,0x00000000,0x0f000f00,0x0f000f00,0x00f0f000,0x00f0f000,0x000f0000,0x000f0000,0x000f0000,0x000f0000,0x00f0f000,0x00f0f000,0x0f000f00,0x0f000f00,0x00000000,0x00000000, // x + 0x00000000,0x00000000,0x000fff00,0x000fff00,0xf0f00000,0xf0f00000,0xf0f00000,0xf0f00000,0xf0f00000,0xf0f00000,0xf0f00000,0xf0f00000,0x0fffff00,0x0fffff00,0x00000000,0x00000000, // y + 0x00000000,0x00000000,0x0f000f00,0x0f000f00,0x0ff00f00,0x0ff00f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f0f0f00,0x0f00ff00,0x0f00ff00,0x0f000f00,0x0f000f00,0x00000000,0x00000000, // z + 0x00000000,0x00000000,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x00fffff0,0x00fffff0,0x0fff0fff,0x0fff0fff,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x00000000,0x00000000, // { + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0fffffff,0x0fffffff,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // | + 0x00000000,0x00000000,0x0f00000f,0x0f00000f,0x0f00000f,0x0f00000f,0x0fff0fff,0x0fff0fff,0x00fffff0,0x00fffff0,0x0000f000,0x0000f000,0x0000f000,0x0000f000,0x00000000,0x00000000, // } + 0x00000000,0x00000000,0x000ff000,0x000ff000,0x00000f00,0x00000f00,0x0000f000,0x0000f000,0x000f0000,0x000f0000,0x0000ff00,0x0000ff00,0x00000000,0x00000000,0x00000000,0x00000000 // ~ + }; + } + +} diff --git a/src/devices/Oled/SSD1306/Font_Symbol_16X16.java b/src/devices/Oled/SSD1306/Font_Symbol_16X16.java new file mode 100644 index 0000000..d1611aa --- /dev/null +++ b/src/devices/Oled/SSD1306/Font_Symbol_16X16.java @@ -0,0 +1,211 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +// Source : http://www.rinkydinkelectronics.com/images/fonts/various_symbols.png + +@BA.ShortName("Font_Symbol_16X16") +public class Font_Symbol_16X16 extends FontPack { + + public final char kosong = 32; + public final char plus_minus = 33; + public final char nol_coret = 34; + public final char titik_tiga = 35; + public final char persen_gakjelas = 36; + public final char persen = 37; + public final char ordinal_indicator = 38; + public final char pangkat_dua = 39; + public final char pangkat_tiga = 40; + public final char tidak_samadengan = 41; + public final char pembagi_atasbawah = 42; + public final char pangkat_satu = 43; + public final char yen = 44; + public final char section = 45; + public final char titik = 46; + public final char cent = 47; + public final char nol_lingkaran = 48; + public final char satu_lingkaran = 49; + public final char dua_lingkaran = 50; + public final char tiga_lingkaran = 51; + public final char empat_lingkaran = 52; + public final char lima_lingkaran = 53; + public final char enam_lingkaran = 54; + public final char tujuh_lingkaran = 55; + public final char delapan_lingkaran = 56; + public final char sembilan_lingkaran = 57; + public final char kotak_isi = 58; + public final char lingkaran = 59; + public final char disket_save = 60; + public final char panah_segitiga_atas = 61; + public final char panah_segitiga_bawah = 62; + public final char panah_segitiga_kiri = 63; + public final char kotak_disilang = 64; + public final char kotak_dicentang = 65; + public final char surat = 66; + public final char kotak_kosong = 67; + public final char euro1 = 68; + public final char petir = 69; + public final char satu_per_empat = 70; + public final char satu_per_dua = 71; + public final char tiga_per_empat = 72; + public final char questionmark_kebalik = 73; + public final char n_tilde = 74; + public final char face_happy = 75; + public final char face_sad = 76; + public final char face_neutral = 77; + public final char pirate = 78; + public final char panah_atas = 79; + public final char panah_bawah = 80; + public final char panah_kiri = 81; + public final char panah_kanan = 82; + public final char centang = 83; + public final char panah_segitiga_kanan = 84; + public final char counter_clockwise = 85; + public final char clockwise = 86; + public final char heart = 87; + public final char copyright = 88; + public final char registered = 89; + public final char omega = 90; + public final char gakjelas1 = 91; + public final char infinity = 92; + public final char Nomor = 93; + public final char Caping = 94; + public final char Underscore = 95; + public final char derajat = 96; + public final char poundsterling = 97; + public final char micro = 98; + public final char euro2 = 99; + public final char media_back = 100; + public final char media_play = 101; + public final char media_up = 102; + public final char media_down = 103; + public final char media_rewind = 104; + public final char information = 105; + public final char media_fastforward = 106; + public final char media_previoustrack = 107; + public final char media_nexttrack = 108; + public final char media_pause = 109; + public final char media_stop = 110; + public final char media_record = 111; + public final char speaker = 112; + public final char melody = 113; + public final char loop = 114; + public final char incomingcall = 115; + public final char palu_kuncipas = 116; + public final char diamond = 117; + public final char bibir = 118; + public final char parkir = 119; + public final char icon_ngomong = 120; + public final char pesawat = 121; + public final char pensil = 122; + public final char salib = 123; + public final char arrowhead = 124; + public final char gunting = 125; + public final char bintang = 126; + + public Font_Symbol_16X16() { + super("Font_Symbol_16X16", 16,16,16,16,32,126); + data = new int[] { + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // + 0x0000,0x0000,0x0000,0x0000,0x2080,0x2080,0x2080,0x27f0,0x2080,0x2080,0x2080,0x0000,0x0000,0x0000,0x0000,0x0000, // ! + 0x0000,0x0000,0x47c0,0x6ff0,0x1ff8,0x1878,0x341c,0x331c,0x318c,0x384c,0x3c2c,0x1e18,0x1ff8,0x0ff6,0x03e2,0x0000, // " + 0x0000,0x0000,0x0000,0x0000,0x3800,0x3800,0x0000,0x0000,0x3800,0x3800,0x0000,0x0000,0x3800,0x3800,0x0000,0x0000, // # + 0x0000,0x0070,0x2098,0x18c8,0x0670,0x0380,0x1ce0,0x2630,0x3208,0x1c00,0x0000,0x1c00,0x2600,0x3200,0x1c00,0x0000, // $ + 0x0000,0x00e0,0x01f0,0x2118,0x3108,0x1988,0x0ef8,0x0370,0x1d80,0x3ee0,0x2330,0x2118,0x3108,0x1f00,0x0e00,0x0000, // % + 0x0000,0x0000,0x0000,0x0000,0x0060,0x00f0,0x0098,0x0048,0x00e8,0x00f8,0x0098,0x0000,0x0000,0x0000,0x0000,0x0000, // & + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0180,0x0184,0x01c4,0x0144,0x0164,0x013c,0x0038,0x0000,0x0000,0x0000,0x0000, // ' + 0x0000,0x0000,0x0000,0x0000,0x0100,0x0124,0x0134,0x0154,0x01dc,0x00cc,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ( + 0x0000,0x0000,0x0080,0x0090,0x0c90,0x0f90,0x0ff8,0x01fc,0x009c,0x0090,0x0090,0x0010,0x0000,0x0000,0x0000,0x0000, // ) + 0x0000,0x0000,0x0000,0x0000,0x0300,0x0300,0x0300,0x3300,0x3b00,0x1b60,0x0370,0x0330,0x0300,0x0300,0x0300,0x0000, // * + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0398,0x03f8,0x007c,0x0004,0x0000,0x0000,0x0000,0x0000, // + + 0x0000,0x0000,0x0408,0x04b8,0x04f8,0x34f0,0x3fc0,0x3f00,0x0700,0x0780,0x04d0,0x04f8,0x04b8,0x0018,0x0000,0x0000, // , + 0x0000,0x0000,0x0000,0x0000,0x3380,0x63c0,0x6778,0x6678,0x6eec,0x6ccc,0x3ccc,0x3bcc,0x038c,0x0000,0x0000,0x0000, // - + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0300,0x0780,0x0780,0x0300,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // . + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03c0,0x07e0,0x0ff0,0x3c38,0x0fd8,0x0c3c,0x0418,0x0018,0x0000,0x0000, // / + + 0x0000,0x0000,0x03c0,0x0ff0,0x1ff8,0x1c38,0x300c,0x37ec,0x37ec,0x300c,0x1c38,0x1ff8,0x0ff0,0x03c0,0x0000,0x0000, // 0 + 0x0000,0x0000,0x03c0,0x0ff0,0x1ff8,0x1ff8,0x3ffc,0x3fdc,0x380c,0x380c,0x1ff8,0x1ff8,0x0ff0,0x03c0,0x0000,0x0000, // 1 + 0x0000,0x0000,0x03c0,0x0ff0,0x1ff8,0x1ff8,0x39dc,0x38ec,0x3a6c,0x3b0c,0x1b98,0x1ff8,0x0ff0,0x03c0,0x0000,0x0000, // 2 + 0x0000,0x0000,0x03c0,0x0ff0,0x1ff8,0x1ff8,0x37ec,0x376c,0x376c,0x308c,0x1898,0x1ff8,0x0ff0,0x03c0,0x0000,0x0000, // 3 + 0x0000,0x0000,0x03c0,0x0ff0,0x1ff8,0x1cf8,0x3d3c,0x3ddc,0x300c,0x3dfc,0x1ff8,0x1ff8,0x0ff0,0x03c0,0x0000,0x0000, // 4 + 0x0000,0x0000,0x03c0,0x0ff0,0x1ff8,0x1ff8,0x361c,0x36dc,0x30dc,0x39dc,0x1ff8,0x1ff8,0x0ff0,0x03c0,0x0000,0x0000, // 5 + 0x0000,0x0000,0x03c0,0x0ff0,0x1ff8,0x1c38,0x301c,0x374c,0x376c,0x306c,0x18e8,0x1ff8,0x0ff0,0x03c0,0x0000,0x0000, // 6 + 0x0000,0x0000,0x03c0,0x0ff0,0x1ff8,0x1ff8,0x3bdc,0x39dc,0x3edc,0x3f5c,0x1f98,0x1ff8,0x0ff0,0x03c0,0x0000,0x0000, // 7 + 0x0000,0x0000,0x03c0,0x0ff0,0x1ff8,0x1998,0x372c,0x376c,0x366c,0x36ec,0x1998,0x1ff8,0x0ff0,0x03c0,0x0000,0x0000, // 8 + 0x0000,0x0000,0x03c0,0x0ff0,0x1ff8,0x1f18,0x360c,0x36ec,0x36ec,0x33ec,0x381c,0x1c38,0x1ff8,0x0ff0,0x03c0,0x0000, // 9 + 0x0000,0x0000,0x0000,0x0000,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x0000,0x0000, // : + 0x0000,0x0000,0x03c0,0x0ff0,0x1c38,0x1818,0x300c,0x300c,0x300c,0x300c,0x1818,0x1c38,0x0ff0,0x03c0,0x0000,0x0000, // ; + 0x0000,0x0000,0x1ffc,0x2004,0x21fc,0x3f04,0x2304,0x2304,0x3f04,0x3f04,0x3f04,0x21fc,0x2004,0x3ffc,0x0000,0x0000, // < + 0x0000,0x0000,0x1000,0x1c00,0x1300,0x1080,0x1060,0x1018,0x1004,0x100c,0x1010,0x1060,0x1180,0x1200,0x1c00,0x1000, // = + 0x0000,0x0000,0x0004,0x001c,0x0064,0x0084,0x0304,0x0c04,0x1004,0x1804,0x0404,0x0304,0x00c4,0x0024,0x001c,0x0004, // > + 0x0000,0x0000,0x0000,0x0180,0x0280,0x0240,0x0420,0x0420,0x0810,0x1010,0x1008,0x2004,0x2004,0x7ffe,0x0000,0x0000, // ? + + 0x0000,0x0000,0x3ffc,0x3ffc,0x300c,0x3c0c,0x3eec,0x338c,0x33cc,0x36ec,0x366c,0x300c,0x3ffc,0x3ffc,0x0000,0x0000, // @ + 0x0000,0x0000,0x3ffc,0x3ffc,0x310c,0x318c,0x370c,0x370c,0x318c,0x30cc,0x302c,0x301c,0x3ffc,0x3ffc,0x0002,0x0000, // A + 0x0000,0x0000,0x1fe0,0x1c20,0x1460,0x12a0,0x13a0,0x1120,0x1220,0x1120,0x1320,0x12a0,0x14a0,0x1c60,0x1fe0,0x0000, // B + 0x0000,0x0000,0x3ffc,0x3ffc,0x300c,0x300c,0x300c,0x300c,0x300c,0x300c,0x300c,0x300c,0x3ffc,0x3ffc,0x0000,0x0000, // C + 0x0000,0x0000,0x0040,0x0140,0x07e0,0x0d50,0x1148,0x3144,0x2144,0x2146,0x2146,0x2144,0x2144,0x100c,0x0008,0x0000, // D + 0x0000,0x0000,0x0000,0x0000,0x7800,0x6100,0x39c0,0x2ce0,0x27b8,0x019c,0x008e,0x0046,0x0002,0x0000,0x0000,0x0000, // E + 0x0000,0x0000,0x1188,0x19fc,0x063c,0x0304,0x01c0,0x0060,0x0638,0x078c,0x05c4,0x1cc0,0x1e40,0x0600,0x0000,0x0000, // F + 0x0000,0x0000,0x00c8,0x08f8,0x0c3c,0x0604,0x0180,0x00c0,0x0860,0x0c58,0x0c4c,0x0e44,0x0a40,0x0bc0,0x0180,0x0000, // G + 0x0200,0x0248,0x0268,0x0268,0x23b8,0x3198,0x0c00,0x0600,0x0180,0x0cc0,0x0f70,0x0b98,0x3988,0x3c80,0x0c00,0x0000, // H + 0x0000,0x0000,0x0000,0x0000,0x1c00,0x3e00,0x7e00,0x6700,0x63b0,0x61b8,0x6038,0x3000,0x0000,0x0000,0x0000,0x0000, // I + 0x0000,0x0000,0x1800,0x1fc0,0x1fe0,0x07e0,0x0188,0x00c4,0x1e44,0x1fe8,0x1fe8,0x11cc,0x0000,0x0000,0x0000,0x0000, // J + 0x0000,0x03e0,0x0c18,0x180c,0x1334,0x2432,0x2802,0x2802,0x2802,0x2432,0x1334,0x180c,0x0c18,0x03e0,0x0000,0x0000, // K + 0x0000,0x0000,0x03e0,0x0c18,0x180c,0x1634,0x2132,0x2082,0x2082,0x2082,0x2132,0x1634,0x180c,0x0c18,0x03e0,0x0000, // L + 0x0000,0x03c0,0x0c30,0x1818,0x1208,0x2264,0x2264,0x2204,0x2264,0x2264,0x1208,0x1818,0x0c30,0x03c0,0x0000,0x0000, // M + 0x0000,0x0000,0x0000,0x0000,0x2600,0x2600,0x3438,0x145c,0x09bc,0x09bc,0x145c,0x3438,0x2600,0x0000,0x0000,0x0000, // N + 0x0000,0x0000,0x0380,0x01c0,0x00e0,0x00f0,0x0078,0xfffc,0xfffe,0xfffc,0x0078,0x00f0,0x00e0,0x01c0,0x0380,0x0000, // O + + 0x0000,0x0000,0x0380,0x0700,0x0e00,0x1e00,0x3c00,0x7ffe,0xfffe,0x7ffe,0x3c00,0x1e00,0x0e00,0x0700,0x0380,0x0000, // P + 0x0080,0x01c0,0x03e0,0x07f0,0x0ff8,0x1ffc,0x3dde,0x31c6,0x21c2,0x01c0,0x01c0,0x01c0,0x01c0,0x01c0,0x01c0,0x0000, // Q + 0x0000,0x01c0,0x01c0,0x01c0,0x01c0,0x01c0,0x01c0,0x21c2,0x31c6,0x3dde,0x1ffc,0x0ff8,0x07f0,0x03e0,0x01c0,0x0080, // R + 0x0000,0x0000,0x0080,0x0180,0x0f00,0x0600,0x0380,0x00c0,0x0060,0x0010,0x0008,0x0004,0x0002,0x0000,0x0000,0x0000, // S + 0x0000,0x0000,0x7ffe,0x2004,0x2004,0x1008,0x1010,0x0810,0x0420,0x0420,0x0240,0x0280,0x0180,0x0000,0x0000,0x0000, // T + 0x0000,0x0000,0x03c0,0x0c30,0x1000,0x1000,0x2000,0x2000,0x2000,0x2000,0x103c,0x101c,0x0c3c,0x03e0,0x0000,0x0000, // U + 0x0000,0x0000,0x03e0,0x0c3c,0x101c,0x103c,0x2000,0x2000,0x2000,0x2000,0x1000,0x1000,0x0c30,0x03c0,0x0000,0x0000, // V + 0x0000,0x0000,0x00f8,0x030c,0x0404,0x0804,0x3008,0x6030,0x1008,0x0804,0x0404,0x030c,0x00f8,0x0000,0x0000,0x0000, // W + 0x0000,0x0000,0x07e0,0x0810,0x1008,0x23e4,0x2414,0x2414,0x2414,0x2414,0x2414,0x2224,0x1008,0x0810,0x07e0,0x0000, // X + 0x0000,0x0000,0x07e0,0x0810,0x1008,0x27f4,0x2094,0x2094,0x2194,0x2294,0x2464,0x1008,0x0810,0x07e0,0x0000,0x0000, // Y + 0x0000,0x0000,0x23e0,0x2c10,0x3008,0x3004,0x2004,0x0004,0x2004,0x3004,0x3008,0x2c10,0x23e0,0x0000,0x0000,0x0000, // Z + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0400,0x3ff0,0x2088,0x3078,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // [ + 0x0000,0x0000,0x0000,0x0000,0x0380,0x0440,0x0440,0x0280,0x0380,0x0440,0x0440,0x0440,0x0380,0x0000,0x0000,0x0000, // + 0x0000,0x3ff8,0x0020,0x0040,0x0180,0x0200,0x0400,0x0800,0x3ff8,0x0000,0x13c0,0x1420,0x1420,0x1420,0x13c0,0x0000, // ] + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0010,0x0008,0x0006,0x0003,0x0006,0x0008,0x0010,0x0000,0x0000,0x0000,0x0000, // ^ + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, // _ + + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0038,0x007c,0x004c,0x0044,0x0064,0x007c,0x0038,0x0000, // ` + 0x0000,0x0000,0x0000,0x0000,0x0800,0x1e00,0x1fe0,0x1bf0,0x19f8,0x199c,0x198c,0x198c,0x198c,0x180c,0x0018,0x0000, // a + 0x0000,0x0000,0x0000,0x0000,0x7000,0x7f80,0x07f0,0x0430,0x0600,0x0380,0x07f0,0x0430,0x0300,0x0000,0x0000,0x0000, // b + 0x0000,0x0000,0x0000,0x0160,0x07f8,0x0f7c,0x1966,0x1962,0x1162,0x1162,0x1162,0x1866,0x0c0e,0x0000,0x0000,0x0000, // c + 0x0000,0x0000,0x0000,0x0000,0x0180,0x03c0,0x07e0,0x0ff0,0x1ff8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // d + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1ff8,0x0ff0,0x07e0,0x03c0,0x0180,0x0000,0x0000,0x0000,0x0000,0x0000, // e + 0x0000,0x0000,0x0100,0x0180,0x01c0,0x01e0,0x01f0,0x01f0,0x01e0,0x01c0,0x0180,0x0100,0x0000,0x0000,0x0000,0x0000, // f + 0x0000,0x0000,0x0020,0x0060,0x00e0,0x01e0,0x03e0,0x03e0,0x01e0,0x00e0,0x0060,0x0020,0x0000,0x0000,0x0000,0x0000, // g + 0x0000,0x0000,0x0300,0x0780,0x0fc0,0x1fe0,0x3ff0,0x0300,0x0780,0x0fc0,0x1fe0,0x3ff0,0x0000,0x0000,0x0000,0x0000, // h + 0x0000,0x7ffe,0x4002,0x4002,0x4002,0x4092,0x5fbe,0x5fbe,0x5fbe,0x5f9a,0x4002,0x4002,0x4002,0x4002,0x3ffc,0x0000, // i + 0x0000,0x0000,0x0000,0x0000,0x1ff8,0x0ff0,0x07e0,0x03c0,0x0180,0x1ff8,0x0ff0,0x07e0,0x03c0,0x0180,0x0000,0x0000, // j + 0x0000,0x0000,0x3ff0,0x3ff0,0x0300,0x0780,0x0fc0,0x1fe0,0x3ff0,0x0300,0x0780,0x0fc0,0x1fe0,0x3ff0,0x0000,0x0000, // k + 0x0000,0x0000,0x3ff0,0x1fe0,0x0fc0,0x0780,0x0300,0x3ff0,0x1fe0,0x0fc0,0x0780,0x0300,0x3ff0,0x3ff0,0x0000,0x0000, // l + 0x0000,0x0000,0x0000,0x0000,0x3ff8,0x3ff8,0x0000,0x0000,0x3ff8,0x3ff8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // m + 0x0000,0x0000,0x0000,0x0000,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x0000,0x0000, // n + 0x0000,0x0000,0x0000,0x0000,0x03c0,0x0ff0,0x0ff0,0x1ff8,0x1ff8,0x1ff8,0x1ff8,0x0ff0,0x0ff0,0x03c0,0x0000,0x0000, // o + + 0x0000,0x0000,0x0000,0x0000,0x3ffe,0x1ffc,0x0ff8,0x07f0,0x03e0,0x03e0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // p + 0x0000,0x0000,0x3000,0x3800,0x3800,0x3800,0x1fe0,0x0070,0x0630,0x0738,0x071c,0x070c,0x03fe,0x0000,0x0000,0x0000, // q + 0x0000,0x0000,0x03e0,0x0ffc,0x1e1c,0x081e,0x0000,0x0000,0x0000,0x1e04,0x0e1e,0x0ffc,0x01f0,0x0000,0x0000,0x0000, // r + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1ff8,0x3c3c,0x381c,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // s + 0x0040,0x00e0,0x1078,0x481c,0x582c,0x3c76,0x0ee2,0x07c0,0x0380,0x07e0,0x0ef8,0x1c74,0x3834,0x7020,0x2010,0x0000, // t + 0x00c0,0x01e0,0x03f0,0x07f8,0x0ffc,0x1ffe,0x3fff,0x3fff,0x1ffe,0x0ffc,0x07f8,0x03f0,0x01e0,0x00c0,0x0000,0x0000, // u + 0x0000,0x0000,0x0080,0x0180,0x03c0,0x03e0,0x06e0,0x06c0,0x06c0,0x06e0,0x06e0,0x03e0,0x03c0,0x0180,0x0080,0x0000, // v + 0x0000,0x0000,0x07c0,0x1830,0x3018,0x2008,0x5fe4,0x4224,0x4224,0x4224,0x41c4,0x2008,0x3018,0x1830,0x07c0,0x0000, // w + 0x0000,0x0000,0x21c0,0x3bf0,0x3ff0,0x1ff0,0x0ff8,0x0ff8,0x0ff8,0x0ff8,0x0ff8,0x0ff8,0x0ff8,0x07f8,0x07f0,0x07e0, // x + 0x0080,0x0000,0x03e0,0x01c0,0x0080,0x0080,0x2082,0x388c,0x1ff8,0x17f8,0x07f0,0x0080,0x0080,0x0080,0x0080,0x0080, // y + 0x0070,0x008c,0x01b4,0x02b4,0x02ec,0x0518,0x0528,0x0a50,0x0a60,0x14a0,0x1d40,0x1340,0x1180,0x1600,0x1800,0x0000, // z + 0x0000,0x0000,0x0000,0x0000,0x0030,0x0030,0x0030,0x1ffe,0x1ffe,0x0030,0x0030,0x0030,0x0000,0x0000,0x0000,0x0000, // { + 0x1008,0x1818,0x1430,0x1270,0x09f0,0x08e0,0x04e0,0x04e0,0x04c0,0x02c0,0x02c0,0x0280,0x0180,0x0180,0x0100,0x0000, // | + 0x0000,0x0e38,0x0a28,0x0e38,0x0220,0x0140,0x01c0,0x01c0,0x01c0,0x0360,0x0360,0x0220,0x0630,0x0630,0x0410,0x0410, // } + 0x0000,0x0000,0x0100,0x0280,0x0280,0x02c0,0x06e0,0x0e70,0x781e,0x301c,0x0e70,0x06e0,0x02c0,0x0280,0x0180,0x0100, // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/Font_Symbol_16X32.java b/src/devices/Oled/SSD1306/Font_Symbol_16X32.java new file mode 100644 index 0000000..07dd8bd --- /dev/null +++ b/src/devices/Oled/SSD1306/Font_Symbol_16X32.java @@ -0,0 +1,210 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font_Symbol_16X32") +public class Font_Symbol_16X32 extends FontPack { + + public final char kosong = 32; + public final char plus_minus = 33; + public final char nol_coret = 34; + public final char titik_tiga = 35; + public final char persen_gakjelas = 36; + public final char persen = 37; + public final char ordinal_indicator = 38; + public final char pangkat_dua = 39; + public final char pangkat_tiga = 40; + public final char tidak_samadengan = 41; + public final char pembagi_atasbawah = 42; + public final char akar = 43; + public final char yen = 44; + public final char section = 45; + public final char titik = 46; + public final char cent = 47; + public final char nol_lingkaran = 48; + public final char satu_lingkaran = 49; + public final char dua_lingkaran = 50; + public final char tiga_lingkaran = 51; + public final char empat_lingkaran = 52; + public final char lima_lingkaran = 53; + public final char enam_lingkaran = 54; + public final char tujuh_lingkaran = 55; + public final char delapan_lingkaran = 56; + public final char sembilan_lingkaran = 57; + public final char backspace = 58; + public final char lingkaran = 59; + public final char disket_save = 60; + public final char panah_segitiga_atas = 61; + public final char panah_segitiga_bawah = 62; + public final char panah_segitiga_kiri = 63; + public final char panah_segitiga_kanan = 64; + public final char kotak_disilang = 65; + public final char kotak_dicentang = 66; + public final char surat = 67; + public final char kotak = 68; + public final char euro = 69; + public final char petir = 70; + public final char satu_per_empat = 71; + public final char satu_per_dua = 72; + public final char tiga_per_empat = 73; + public final char questionmark_kebalik = 74; + public final char n_tilde = 75; + public final char face_happy = 76; + public final char face_sad = 77; + public final char face_neutral = 78; + public final char pirate = 79; + public final char panah_bawah = 80; + public final char panah_atas = 81; + public final char panah_kiri = 82; + public final char panah_kanan = 83; + public final char centang = 84; + public final char counter_clockwise = 85; + public final char clockwise = 86; + public final char heart = 87; + public final char copyright = 88; + public final char registered = 89; + public final char omega = 90; + public final char information = 91; + public final char derajat = 92; + public final char poundsterling = 93; + public final char micro = 94; + public final char shuffle = 95; + public final char media_eject = 96; + public final char media_reverse_play = 97; + public final char media_play = 98; + public final char media_up = 99; + public final char media_down = 100; + public final char media_rewind = 101; + public final char media_fastforward = 102; + public final char media_previoustrack = 103; + public final char media_nexttrack = 104; + public final char media_pause = 105; + public final char media_stop = 106; + public final char media_record = 107; + public final char powerbutton = 108; + public final char speaker_mute = 109; + public final char speaker_volumedown = 110; + public final char speaker_volumeup = 111; + public final char melody = 112; + public final char media_loop = 113; + public final char telephone = 114; + public final char sun = 115; + public final char diamond = 116; + public final char airplane = 117; + public final char salib = 118; + public final char gunting = 119; + public final char silang = 120; + public final char bintang = 121; + public final char timer = 122; + public final char zoom_out = 123; + public final char zoom_in = 124; + public final char archieve = 125; + public final char plus = 126; + + public Font_Symbol_16X32() { + super("Font_Symbol_16X32", 16, 32, 16, 32, 32, 126); + data = new int[] { + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // + 0x00000000,0x00000000,0x00183000,0x00183000,0x00183000,0x00183000,0x00183000,0x001bff00,0x001bff00,0x00183000,0x00183000,0x00183000,0x00183000,0x00183000,0x00000000,0x00000000, // ! + 0x00000000,0x00000000,0x0019f000,0x000ffc00,0x000e0600,0x001b0200,0x00198300,0x0010c100,0x00106100,0x00183300,0x000c1b00,0x000e0e00,0x0007fe00,0x0001f300,0x00000000,0x00000000, // " + 0x00000000,0x00000000,0x001c0000,0x001c0000,0x00000000,0x00000000,0x00000000,0x001c0000,0x001c0000,0x00000000,0x00000000,0x00000000,0x001c0000,0x001c0000,0x00000000,0x00000000, // # + 0x00000000,0x00000000,0x00003f00,0x00182100,0x000f3f00,0x0003c000,0x00007000,0x001f9e00,0x00108300,0x001f8000,0x00000000,0x001f8000,0x00108000,0x001f8000,0x00000000,0x00000000, // $ + 0x00000000,0x00000000,0x00001e00,0x00002100,0x00182100,0x000e1e00,0x00038000,0x0000e000,0x00007800,0x000f0c00,0x00108700,0x00108100,0x000f0000,0x00000000,0x00000000,0x00000000, // % + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00003200,0x00004900,0x00004900,0x00002900,0x00007e00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // & + 0x00000000,0x00000000,0x00000000,0x00000000,0x00007818,0x00007c1c,0x00006e0e,0x00006706,0x00006386,0x000061ce,0x000060fc,0x00006078,0x00006030,0x00000000,0x00000000,0x00000000, // ' + 0x00000000,0x00000000,0x00000000,0x00000000,0x00001818,0x0000381c,0x0000700e,0x00006006,0x00006186,0x00006186,0x0000718e,0x00003ffc,0x00001e78,0x00000000,0x00000000,0x00000000, // ( + 0x00000000,0x00000000,0x00000000,0x00031800,0x00031800,0x00031800,0x00031800,0x001fff00,0x001fff00,0x00031800,0x00031800,0x00031800,0x00031800,0x00000000,0x00000000,0x00000000, // ) + 0x00000000,0x00000000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x001cce00,0x001cce00,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x00000000,0x00000000, // * + 0x00000000,0x00000000,0x00002000,0x00002000,0x0000e000,0x000f0000,0x00300000,0x000f0000,0x0000e000,0x00001c00,0x00000300,0x00000100,0x00000100,0x00000100,0x00000000,0x00000000, // + + 0x00000000,0x00000000,0x00000000,0x00009100,0x00009700,0x00009c00,0x0000f800,0x003fd000,0x003fd000,0x0000f800,0x00009c00,0x00009700,0x00009100,0x00000000,0x00000000,0x00000000, // , + 0x00000000,0x00000000,0x00000000,0x00007000,0x0039fe00,0x0073bf00,0x00671980,0x006608c0,0x006418c0,0x003e39c0,0x001f7380,0x0003e000,0x00000000,0x00000000,0x00000000,0x00000000, // - + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0001c000,0x0003e000,0x0007f000,0x0007f000,0x0007f000,0x0003e000,0x0001c000,0x00000000,0x00000000,0x00000000,0x00000000, // . + 0x00000000,0x00000000,0x0001f000,0x0003f800,0x00071c00,0x000c0600,0x00180300,0x00180300,0x007fffc0,0x007fffc0,0x00180300,0x00180300,0x001c0700,0x000c0600,0x00000000,0x00000000, // / + + 0x00000000,0x0007f000,0x001ffc00,0x003ffe00,0x007c1f00,0x00f00780,0x00e7f380,0x00cff980,0x00cff980,0x00e7f380,0x00f00780,0x007c1f00,0x003ffe00,0x001ffc00,0x0007f000,0x00000000, // 0 + 0x00000000,0x0007f000,0x001ffc00,0x003ffe00,0x007fff00,0x00f3cf80,0x00f3c780,0x00f00380,0x00f00380,0x00f00380,0x00f3ff80,0x0073ff00,0x003ffe00,0x001ffc00,0x0007f000,0x00000000, // 1 + 0x00000000,0x0007f000,0x001ffc00,0x003ffe00,0x0073e700,0x00f1e380,0x00f0f380,0x00f07380,0x00f23380,0x00f30380,0x00f38780,0x0073cf00,0x003ffe00,0x001ffc00,0x0007f000,0x00000000, // 2 + 0x00000000,0x0007f000,0x001ffc00,0x003ffe00,0x007fff00,0x00f9e780,0x00f1e380,0x00f3f380,0x00f33380,0x00f33380,0x00f80780,0x007ccf00,0x003ffe00,0x001ffc00,0x0007f000,0x00000000, // 3 + 0x00000000,0x0007f000,0x001ffc00,0x003ffe00,0x007fff00,0x00fc0380,0x00fc0380,0x00fcff80,0x00fcff80,0x00f01f80,0x00f01f80,0x007cff00,0x003cfe00,0x001ffc00,0x0007f000,0x00000000, // 4 + 0x00000000,0x0007f000,0x001ffc00,0x003ffe00,0x007fff00,0x00f38380,0x00f38380,0x00f3b380,0x00f33380,0x00f33380,0x00f87380,0x00787300,0x003ffe00,0x001ffc00,0x0007f000,0x00000000, // 5 + 0x00000000,0x0007f000,0x001ffc00,0x003ffe00,0x007fff00,0x00fc0780,0x00f83380,0x00f7bb80,0x00f7bb80,0x00f7bb80,0x00f87380,0x007cf700,0x003ffe00,0x001ffc00,0x0007f000,0x00000000, // 6 + 0x00000000,0x0007f000,0x001ffc00,0x003ffe00,0x007fff00,0x00f1f380,0x00f0f380,0x00fe7380,0x00ff3380,0x00ff9380,0x00ffc380,0x007fe300,0x003ffe00,0x001ffc00,0x0007f000,0x00000000, // 7 + 0x00000000,0x0007f000,0x001ffc00,0x003ffe00,0x007fff00,0x00f8c780,0x00f00380,0x00f33380,0x00f33380,0x00f33380,0x00f00380,0x0078c700,0x003ffe00,0x001ffc00,0x0007f000,0x00000000, // 8 + 0x00000000,0x0007f000,0x001ffc00,0x003ffe00,0x007fff00,0x00f9c780,0x00f18380,0x00f3b380,0x00f3b380,0x00f3b380,0x00f00380,0x00780700,0x003ffe00,0x001ffc00,0x0007f000,0x00000000, // 9 + 0x00000000,0x00006000,0x0000f000,0x0001f800,0x0003fc00,0x00076e00,0x000e6700,0x000c6300,0x00006000,0x00006000,0x00006000,0x00006000,0x00006000,0x00006000,0x00006000,0x00000000, // : + 0x00000000,0x00000000,0x0003f000,0x0007f800,0x000e1c00,0x001c0e00,0x00180600,0x00180600,0x00180600,0x00180600,0x001c0e00,0x000e1c00,0x0007f800,0x0003f000,0x00000000,0x00000000, // ; + 0x00000000,0x001ffe00,0x00100200,0x0010fe00,0x001ef600,0x0012f600,0x001ef600,0x001ef600,0x001ef600,0x001ef600,0x001ef600,0x0010fe00,0x00100200,0x001ffe00,0x00000000,0x00000000, // < + 0x00000000,0x00000000,0x001c0000,0x001f0000,0x001bc000,0x0018f000,0x00183c00,0x00180e00,0x00183c00,0x0018f000,0x001bc000,0x001f0000,0x001c0000,0x00000000,0x00000000,0x00000000, // = + 0x00000000,0x00000000,0x00000e00,0x00003e00,0x0000f600,0x0003c600,0x000f0600,0x001c0600,0x000f0600,0x0003c600,0x0000f600,0x00003e00,0x00000e00,0x00000000,0x00000000,0x00000000, // > + 0x00000000,0x00000000,0x00004000,0x0000e000,0x0000e000,0x0001b000,0x0001b000,0x00031800,0x00031800,0x00060c00,0x00060c00,0x000c0600,0x000ffe00,0x000ffe00,0x00000000,0x00000000, // ? + + 0x00000000,0x00000000,0x001fff00,0x000ffe00,0x000c0600,0x00060c00,0x00060c00,0x00031800,0x00031800,0x0001b000,0x0001b000,0x0000e000,0x0000e000,0x00004000,0x00000000,0x00000000, // @ + 0x00000000,0x00000000,0x001ffe00,0x00100200,0x00161a00,0x00173a00,0x0011e200,0x0010c200,0x0010c200,0x0011e200,0x00173a00,0x00161a00,0x00100200,0x001ffe00,0x00000000,0x00000000, // A + 0x00000000,0x00000000,0x001ffe00,0x00100200,0x00110200,0x00130200,0x00160200,0x00170200,0x0011c200,0x00106200,0x00103200,0x00100a00,0x00100200,0x001ffe00,0x00000000,0x00000000, // B + 0x00000000,0x0007fc00,0x00060c00,0x00071c00,0x0005b400,0x0004f400,0x0004f400,0x0005f400,0x0005f400,0x0004f400,0x0004f400,0x0005b400,0x00071c00,0x00060c00,0x0007fc00,0x00000000, // C + 0x00000000,0x00000000,0x00000000,0x000ffc00,0x000ffc00,0x000c0c00,0x000c0c00,0x000c0c00,0x000c0c00,0x000c0c00,0x000c0c00,0x000ffc00,0x000ffc00,0x00000000,0x00000000,0x00000000, // D + 0x00000000,0x00000000,0x00024000,0x00024000,0x0007e000,0x001ff800,0x00324c00,0x00624600,0x00c24300,0x00c24300,0x00c24300,0x00c04300,0x00400200,0x00600600,0x00000000,0x00000000, // E + 0x00000000,0x00000000,0x00000000,0x00200000,0x00108000,0x0018c000,0x000ce000,0x0006e000,0x0003b000,0x00039800,0x00010800,0x00000400,0x00000000,0x00000000,0x00000000,0x00000000, // F + 0x00000000,0x00000000,0x00008200,0x0010fe00,0x001c8000,0x00060000,0x00038000,0x0000e000,0x00003000,0x000e1c00,0x000b0600,0x00088200,0x003fc000,0x00080000,0x00000000,0x00000000, // G + 0x00000000,0x00000000,0x00008200,0x0010fe00,0x001c8000,0x00060000,0x00038000,0x0000e000,0x00003000,0x00319c00,0x00388600,0x002c8200,0x00278000,0x00230000,0x00000000,0x00000000, // H + 0x00000000,0x00000000,0x00002200,0x00004100,0x00104900,0x001c4900,0x00063600,0x00038000,0x0000e000,0x000e3000,0x000b1c00,0x00088600,0x003fc200,0x00080000,0x00000000,0x00000000, // I + 0x00000000,0x00000000,0x00000000,0x00000000,0x000c0000,0x001e0000,0x00330000,0x0021ec00,0x0020ec00,0x00300000,0x00180000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // J + 0x00000000,0x00000000,0x00000000,0x00000000,0x003fec00,0x003fe600,0x0000c300,0x00006300,0x00002600,0x00006c00,0x003fcc00,0x003f8600,0x00000000,0x00000000,0x00000000,0x00000000, // K + 0x00000000,0x00000000,0x0007e000,0x00081000,0x00100800,0x00266400,0x00286400,0x00280400,0x00280400,0x00286400,0x00266400,0x00100800,0x00081000,0x0007e000,0x00000000,0x00000000, // L + 0x00000000,0x00000000,0x0007e000,0x00081000,0x00100800,0x00266400,0x00216400,0x00210400,0x00210400,0x00216400,0x00266400,0x00100800,0x00081000,0x0007e000,0x00000000,0x00000000, // M + 0x00000000,0x00000000,0x0007e000,0x00081000,0x00100800,0x00226400,0x00226400,0x00220400,0x00220400,0x00226400,0x00226400,0x00100800,0x00081000,0x0007e000,0x00000000,0x00000000, // N + 0x00000000,0x03838000,0x03838000,0x01831fc0,0x00c63fe0,0x006cf9e0,0x003df9e0,0x0019bfe0,0x0019bfe0,0x003df9e0,0x006cf9e0,0x00c63fe0,0x01831fc0,0x03838000,0x03838000,0x00000000, // O + + 0x00000000,0x00000000,0x00000000,0x00030000,0x00070000,0x000f0000,0x001f0000,0x003fff00,0x003fff00,0x001f0000,0x000f0000,0x00070000,0x00030000,0x00000000,0x00000000,0x00000000, // P + 0x00000000,0x00000000,0x00000000,0x00003000,0x00003800,0x00003c00,0x00003e00,0x003fff00,0x003fff00,0x00003e00,0x00003c00,0x00003800,0x00003000,0x00000000,0x00000000,0x00000000, // Q + 0x00000000,0x0000c000,0x0001e000,0x0003f000,0x0007f800,0x000ffc00,0x000ffc00,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x00000000, // R + 0x00000000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x0000c000,0x000ffc00,0x000ffc00,0x0007f800,0x0003f000,0x0001e000,0x0000c000,0x00000000, // S + 0x00000000,0x00000000,0x00000000,0x00000000,0x00010000,0x00030000,0x00060000,0x00070000,0x0001c000,0x00006000,0x00003000,0x00000800,0x00000000,0x00000000,0x00000000,0x00000000, // T + 0x00000000,0x00000000,0x00000000,0x0003e000,0x0007f000,0x000e1800,0x000c0c00,0x000c0c00,0x003f0c00,0x001e0c00,0x000c1800,0x0000f000,0x0000e000,0x00000000,0x00000000,0x00000000, // U + 0x00000000,0x00000000,0x00000000,0x0007c000,0x000fe000,0x00187000,0x00303000,0x00303000,0x0030fc00,0x00307800,0x00183000,0x000f0000,0x00070000,0x00000000,0x00000000,0x00000000, // V + 0x00000000,0x00000000,0x0000e000,0x0001f000,0x0003f800,0x0007f800,0x000ff800,0x001ff000,0x003fe000,0x001ff000,0x000ff800,0x0007f800,0x0003f800,0x0001f000,0x0000e000,0x00000000, // W + 0x00000000,0x0001e000,0x00061800,0x00180600,0x0021e100,0x0027f900,0x00461880,0x004c0c80,0x004c0c80,0x004c0c80,0x004e1c80,0x00261900,0x00200100,0x00180600,0x00061800,0x0001e000, // X + 0x00000000,0x0001e000,0x00061800,0x00180600,0x00200100,0x00200100,0x004ffc80,0x004ffe80,0x0040c680,0x0040c680,0x004ffc80,0x002fb900,0x00200100,0x00180600,0x00061800,0x0001e000, // Y + 0x00000000,0x18fe0000,0x1bff8000,0x1f83c000,0x1e01e000,0x1c007000,0x00003000,0x00003000,0x00003000,0x00003000,0x1c007000,0x1e01e000,0x1f83c000,0x1bff8000,0x18fe0000,0x00000000, // Z + 0x00000000,0x00ffffc0,0x00800040,0x00800040,0x00b00040,0x00b06640,0x00bfef40,0x00bfef40,0x00bfef40,0x00bfe640,0x00b00040,0x00b00040,0x00800040,0x00800040,0x007fff80,0x00000000, // [ + 0x00000000,0x00000000,0x00000000,0x000003e0,0x000007f0,0x00000ff8,0x00001e3c,0x00001c1c,0x00001c1c,0x00001c1c,0x00001e3c,0x00000ff8,0x000007f0,0x000003e0,0x00000000,0x00000000, // + + 0x00000000,0x00000000,0x00c30000,0x00c30000,0x00e37c00,0x00fffe00,0x00df8700,0x00c30300,0x00c30300,0x00c30300,0x00c30700,0x00c00600,0x00c00000,0x00000000,0x00000000,0x00000000, // ] + 0x00000000,0x00000000,0x00000000,0x18000000,0x1fff8000,0x0fff8000,0x00c00000,0x01800000,0x01800000,0x01800000,0x00c00000,0x00ff8000,0x01ff8000,0x01800000,0x00000000,0x00000000, // ^ + 0x00000000,0x00183000,0x00183000,0x001c3000,0x000e7000,0x00076000,0x00038000,0x0001c000,0x0006e000,0x000e7000,0x001c3000,0x00183000,0x007efc00,0x003c7800,0x00183000,0x00000000, // _ + + 0x00000000,0x001a0000,0x001b0000,0x001b8000,0x001bc000,0x001be000,0x001bf000,0x001bf800,0x001bf800,0x001bf000,0x001be000,0x001bc000,0x001b8000,0x001b0000,0x001a0000,0x00000000, // ` + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00010000,0x00038000,0x0007c000,0x000fe000,0x001ff000,0x003ff800,0x007ffc00,0x00000000,0x00000000,0x00000000,0x00000000, // a + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x007ffc00,0x003ff800,0x001ff000,0x000fe000,0x0007c000,0x00038000,0x00010000,0x00000000,0x00000000,0x00000000,0x00000000, // b + 0x00000000,0x00000000,0x00080000,0x000c0000,0x000e0000,0x000f0000,0x000f8000,0x000fc000,0x000fe000,0x000fc000,0x000f8000,0x000f0000,0x000e0000,0x000c0000,0x00080000,0x00000000, // c + 0x00000000,0x00000000,0x00002000,0x00006000,0x0000e000,0x0001e000,0x0003e000,0x0007e000,0x000fe000,0x0007e000,0x0003e000,0x0001e000,0x0000e000,0x00006000,0x00002000,0x00000000, // d + 0x00000000,0x00000000,0x00000000,0x00030000,0x00078000,0x000fc000,0x001fe000,0x003ff000,0x00030000,0x00078000,0x000fc000,0x001fe000,0x003ff000,0x00000000,0x00000000,0x00000000, // e + 0x00000000,0x00000000,0x00000000,0x003ff000,0x001fe000,0x000fc000,0x00078000,0x00030000,0x003ff000,0x001fe000,0x000fc000,0x00078000,0x00030000,0x00000000,0x00000000,0x00000000, // f + 0x00000000,0x00000000,0x003ff000,0x003ff000,0x00030000,0x00078000,0x000fc000,0x001fe000,0x003ff000,0x00030000,0x00078000,0x000fc000,0x001fe000,0x003ff000,0x00000000,0x00000000, // g + 0x00000000,0x00000000,0x003ff000,0x001fe000,0x000fc000,0x00078000,0x00030000,0x003ff000,0x001fe000,0x000fc000,0x00078000,0x00030000,0x003ff000,0x003ff000,0x00000000,0x00000000, // h + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x003ff800,0x003ff800,0x00000000,0x00000000,0x003ff800,0x003ff800,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // i + 0x00000000,0x00000000,0x00000000,0x003ff000,0x003ff000,0x003ff000,0x003ff000,0x003ff000,0x003ff000,0x003ff000,0x003ff000,0x003ff000,0x003ff000,0x00000000,0x00000000,0x00000000, // j + 0x00000000,0x00000000,0x00000000,0x00078000,0x001fe000,0x001fe000,0x003ff000,0x003ff000,0x003ff000,0x003ff000,0x001fe000,0x001fe000,0x00078000,0x00000000,0x00000000,0x00000000, // k + 0x00000000,0x000fe000,0x001ff000,0x00383800,0x00301800,0x00600000,0x00600000,0x0061fe00,0x0061fe00,0x00600000,0x00600000,0x00301800,0x00383800,0x001ff000,0x000fe000,0x00000000, // l + 0x00000000,0x00000000,0x0007c000,0x00082000,0x00101000,0x002b8800,0x00478400,0x00438400,0x0047c400,0x004fe400,0x00404400,0x00202800,0x00101000,0x00082000,0x0007c000,0x00000000, // m + 0x00000000,0x00000000,0x00000000,0x00000000,0x00038000,0x00038000,0x00038000,0x0007c000,0x000fe000,0x00000000,0x00044000,0x00038000,0x00000000,0x00000000,0x00000000,0x00000000, // n + 0x00000000,0x00038000,0x00038000,0x00038000,0x0007c000,0x000fe000,0x00000000,0x00000000,0x00044000,0x00139000,0x00482400,0x0047c400,0x00301800,0x000fe000,0x00000000,0x00000000, // o + + 0x00000000,0x00600000,0x00f00000,0x00f00000,0x00700000,0x003ffc00,0x00002400,0x00003600,0x00001200,0x00301200,0x00781200,0x00781b00,0x00380900,0x001fff00,0x00000000,0x00000000, // p + 0x00000000,0x0018c000,0x003cf000,0x007e3800,0x00181800,0x00181800,0x00181800,0x00181800,0x00181800,0x00181800,0x00181800,0x00181800,0x001e7e00,0x00073c00,0x00031800,0x00000000, // q + 0x0000a000,0x00f0b000,0x00fcb800,0x00fe3800,0x00ff9800,0x00f3d800,0x00e1f800,0x00ccd800,0x00ccd800,0x00e1f800,0x00f3d800,0x00ff9800,0x00fe3800,0x00fcb800,0x00f0b000,0x0000a000, // r + 0x00000000,0x00008000,0x0001c000,0x00c0f000,0x00e03800,0x00705800,0x0038ec00,0x001dc400,0x000f8000,0x00070000,0x000ff000,0x001df800,0x00399800,0x00718000,0x00e1c000,0x0040c000, // s + 0x00000000,0x00018000,0x0003c000,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x007ffe00,0x003ffc00,0x001ff800,0x000ff000,0x0007e000,0x0003c000,0x00018000,0x00000000, // t + 0x00000000,0x0007e000,0x0003c000,0x00018000,0x00018000,0x00018000,0x00618600,0x007ffe00,0x003ffc00,0x002ff400,0x0007e000,0x0005a000,0x00018000,0x00018000,0x00018000,0x00000000, // u + 0x00000000,0x00000000,0x00000000,0x00000000,0x00006000,0x00006000,0x00006000,0x007ffc00,0x007ffc00,0x00006000,0x00006000,0x00006000,0x00000000,0x00000000,0x00000000,0x00000000, // v + 0x00000000,0x001c7000,0x00145000,0x001c7000,0x00044000,0x00028000,0x00038000,0x00038000,0x00038000,0x0006c000,0x0006c000,0x00044000,0x000c6000,0x000c6000,0x00082000,0x00082000, // w + 0x00000000,0x00300c00,0x00781e00,0x007c3e00,0x003e7c00,0x001ff800,0x000ff000,0x0007e000,0x0007e000,0x000ff000,0x001ff800,0x003e7c00,0x007c3e00,0x00781e00,0x00300c00,0x00000000, // x + 0x00004000,0x0000c000,0x0000c000,0x0061c000,0x003fc000,0x001fe000,0x000ff800,0x000ffe00,0x000ff800,0x001fe000,0x003fc000,0x0061c000,0x0000c000,0x00004000,0x00004000,0x00000000, // y + 0x00000000,0x00000000,0x03e00f80,0x02f81f80,0x02fc3f80,0x02de7680,0x02c7e680,0x02c3c680,0x02c3c680,0x02c7e680,0x02de7680,0x02fc3f80,0x02f81f80,0x03e00f80,0x00000000,0x00000000, // z + 0x00000000,0x0000f000,0x00010800,0x00026400,0x00026400,0x00026400,0x00026400,0x00030800,0x0007f000,0x000e0000,0x001c0000,0x00380000,0x00700000,0x00600000,0x00000000,0x00000000, // { + 0x00000000,0x0000f000,0x00010800,0x00026400,0x0002f400,0x0002f400,0x00026400,0x00030800,0x0007f000,0x000e0000,0x001c0000,0x00380000,0x00700000,0x00600000,0x00000000,0x00000000, // | + 0x00000000,0x003fe800,0x007ff400,0x007ff400,0x007ff400,0x007df400,0x0079f400,0x00700400,0x00700400,0x0079f400,0x007df400,0x007ff400,0x007ff400,0x007ff400,0x003fe800,0x00000000, // } + 0x00000000,0x00000000,0x00078000,0x00078000,0x00078000,0x00078000,0x007ff800,0x007ff800,0x007ff800,0x007ff800,0x00078000,0x00078000,0x00078000,0x00078000,0x00000000,0x00000000 // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/Font_Symbol_32X32.java b/src/devices/Oled/SSD1306/Font_Symbol_32X32.java new file mode 100644 index 0000000..8870110 --- /dev/null +++ b/src/devices/Oled/SSD1306/Font_Symbol_32X32.java @@ -0,0 +1,220 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font_Symbol_32X32") +/** + * OLED Symbol 32 x 32 pixel + * Source : http://www.rinkydinkelectronics.com/images/fonts/Various_Symbols_32x32.png + * + * + */ +public class Font_Symbol_32X32 extends FontPack { + + public final char kosong = 32; + public final char plus_minus = 33; + public final char nol_coret = 34; + public final char titik_tiga = 35; + public final char persen_gakjelas = 36; + public final char persen = 37; + public final char ordinal_indicator = 38; + public final char pangkat_dua = 39; + public final char pangkat_tiga = 40; + public final char tidak_samadengan = 41; + public final char pembagi_atasbawah = 42; + public final char akar = 43; + public final char yen = 44; + public final char section = 45; + public final char titik = 46; + public final char cent = 47; + public final char nol_lingkaran = 48; + public final char satu_lingkaran = 49; + public final char dua_lingkaran = 50; + public final char tiga_lingkaran = 51; + public final char empat_lingkaran = 52; + public final char lima_lingkaran = 53; + public final char enam_lingkaran = 54; + public final char tujuh_lingkaran = 55; + public final char delapan_lingkaran = 56; + public final char sembilan_lingkaran = 57; + public final char backspace = 58; + public final char lingkaran = 59; + public final char disket_save = 60; + public final char panah_segitiga_atas = 61; + public final char panah_segitiga_bawah = 62; + public final char panah_segitiga_kiri = 63; + public final char panah_segitiga_kanan = 64; + public final char kotak_disilang = 65; + public final char kotak_dicentang = 66; + public final char surat = 67; + public final char kotak = 68; + public final char euro = 69; + public final char petir = 70; + public final char satu_per_empat = 71; + public final char satu_per_dua = 72; + public final char tiga_per_empat = 73; + public final char questionmark_kebalik = 74; + public final char n_tilde = 75; + public final char face_happy = 76; + public final char face_sad = 77; + public final char face_neutral = 78; + public final char pirate = 79; + public final char panah_bawah = 80; + public final char panah_atas = 81; + public final char panah_kiri = 82; + public final char panah_kanan = 83; + public final char centang = 84; + public final char counter_clockwise = 85; + public final char clockwise = 86; + public final char heart = 87; + public final char copyright = 88; + public final char registered = 89; + public final char omega = 90; + public final char information = 91; + public final char derajat = 92; + public final char poundsterling = 93; + public final char micro = 94; + public final char shuffle = 95; + public final char media_eject = 96; + public final char media_reverse_play = 97; + public final char media_play = 98; + public final char media_up = 99; + public final char media_down = 100; + public final char media_rewind = 101; + public final char media_fastforward = 102; + public final char media_previoustrack = 103; + public final char media_nexttrack = 104; + public final char media_pause = 105; + public final char media_stop = 106; + public final char media_record = 107; + public final char powerbutton = 108; + public final char speaker_mute = 109; + public final char speaker_volumedown = 110; + public final char speaker_volumeup = 111; + public final char melody = 112; + public final char media_loop = 113; + public final char telephone = 114; + public final char sun = 115; + public final char diamond = 116; + public final char airplane = 117; + public final char salib = 118; + public final char gunting = 119; + public final char silang = 120; + public final char bintang = 121; + public final char timer = 122; + public final char zoom_out = 123; + public final char zoom_in = 124; + public final char archieve = 125; + public final char plus = 126; + + + + /** + * Source : http://www.rinkydinkelectronics.com/images/fonts/Various_Symbols_32x32.png + */ + public Font_Symbol_32X32() { + super("Font_Symbol_32X32", 32, 32, 32, 32, 32, 126); + data = new int[] { + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // + 0x00000000,0x00000000,0x00000000,0x00000000,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e7ffff8,0x1e7ffff8,0x1e7ffff8,0x1e7ffff8,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x1e007800,0x00000000,0x00000000,0x00000000,0x00000000, // ! + 0x00000000,0x00000000,0x00000000,0x1c07e000,0x1e3ffc00,0x1f7ffe00,0x0ff81f80,0x07e007c0,0x03e001c0,0x07f000e0,0x0ff800f0,0x0e7c0070,0x0e3e0070,0x1c1f0038,0x1c0f8038,0x1c07c038,0x1c03e038,0x1c01f038,0x1c00f838,0x0e007c70,0x0e003e70,0x0f001ff0,0x07000fe0,0x038007c0,0x03e007e0,0x01f81ff0,0x007ffef8,0x003ffc78,0x0007e038,0x00000000,0x00000000,0x00000000, // " + 0x00000000,0x00000000,0x00000000,0x00000000,0x1f800000,0x1f800000,0x1f800000,0x1f800000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x1f800000,0x1f800000,0x1f800000,0x1f800000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x1f800000,0x1f800000,0x1f800000,0x1f800000,0x00000000,0x00000000,0x00000000,0x00000000, // # + 0x00000000,0x00000000,0x00000000,0x00001ff0,0x0c007ff8,0x1f006018,0x0fc06018,0x07f07ff8,0x01fc1fe0,0x007e0000,0x001f8000,0x0007e000,0x0001f800,0x00007e00,0x07fc1f80,0x1ffe0fe0,0x180603f0,0x180600f8,0x1ffe0030,0x07f80000,0x00000000,0x00000000,0x07fc0000,0x1ffe0000,0x18060000,0x18060000,0x1ffe0000,0x07f80000,0x00000000,0x00000000,0x00000000,0x00000000, // $ + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00001f80,0x0c003fc0,0x1e006060,0x0f806060,0x07c06060,0x03f06060,0x00fc3fc0,0x003e1f80,0x001f8000,0x0007c000,0x0003f000,0x03f0f800,0x07f87e00,0x0c0c1f80,0x0c0c07c0,0x0c0c03f0,0x0c0c00f0,0x07f80060,0x03f00000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // % + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x001e1800,0x003f1c00,0x00618e00,0x00618600,0x00618600,0x00618600,0x00618600,0x00318600,0x007ffc00,0x007ff800,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // & + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00781800,0x007c1c00,0x006e0e00,0x00670600,0x00638600,0x0061ce00,0x0060fc00,0x00607800,0x00603000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ' + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00181800,0x00381c00,0x00700e00,0x00600600,0x00618600,0x00618600,0x00718e00,0x003ffc00,0x001e7800,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ( + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00781e00,0x00781e00,0x00781e00,0x00781e00,0x00781e00,0x00781e00,0x00781e00,0x00781e00,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x00781e00,0x00781e00,0x00781e00,0x00781e00,0x00781e00,0x00781e00,0x00781e00,0x00781e00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ) + 0x00000000,0x00000000,0x00000000,0x00000000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x07e3c7e0,0x0ff3cff0,0x0c33cc30,0x0c33cc30,0x0ff3cff0,0x07e3c7e0,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x00000000,0x00000000,0x00000000,0x00000000, // * + 0x00000000,0x00000000,0x00000000,0x00000000,0x00003000,0x00003000,0x00003000,0x0001f000,0x001fe000,0x01fe0000,0x1fe00000,0x1e000000,0x1fe00000,0x01fc0000,0x003fc000,0x0007f800,0x00007f00,0x00000ff0,0x000001f8,0x00000018,0x00000018,0x00000018,0x00000018,0x00000018,0x00000018,0x00000018,0x00000018,0x00000018,0x00000000,0x00000000,0x00000000,0x00000000, // + + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000018,0x0071c038,0x0071c0f8,0x0071c3f8,0x0071cfe0,0x0071df80,0x0071fe00,0x0073f800,0x3fffe000,0x3fff8000,0x3fff8000,0x3fffe000,0x0073f800,0x0071fe00,0x0071df80,0x0071cfe0,0x0071c3f8,0x0071c0f8,0x0071c038,0x00000018,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // , + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0003e1c0,0x0707f3e0,0x0f0f3ff0,0x1c1e1e78,0x181c1c38,0x18381c18,0x18301818,0x18703818,0x1c707018,0x1cf8f038,0x0fffe0f0,0x07cfc0e0,0x03878000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // - + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0003c000,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x0003c000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // . + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0007f000,0x001ffc00,0x007fff00,0x00f80780,0x01e003c0,0x01c001c0,0x038000e0,0x038000e0,0x038000e0,0x038000e0,0x1ffffffc,0x1ffffffc,0x1ffffffc,0x038000e0,0x038000e0,0x038000e0,0x03c001e0,0x01f007c0,0x00f00780,0x00700700,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // / + + 0x00000000,0x00000000,0x0007e000,0x003ffc00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x0ffffff0,0x1fc003f8,0x1f8000f8,0x3e00007c,0x3c7ffe3c,0x3cffff3c,0x3cffff3c,0x3cffff3c,0x3cffff3c,0x3cffff3c,0x3c7ffe3c,0x1e000078,0x1f0000f8,0x0fc003f0,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x003ffc00,0x0007e000,0x00000000,0x00000000, // 0 + 0x00000000,0x00000000,0x0007e000,0x003ffc00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x0c3ffff0,0x1c3ff9f8,0x1c3ff8f8,0x3c3ff87c,0x3c3ff83c,0x3c00003c,0x3c00003c,0x3c00003c,0x3c00003c,0x3c3ffffc,0x3c3ffffc,0x1c3ffff8,0x1c3ffff8,0x0c3ffff0,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x003ffc00,0x0007e000,0x00000000,0x00000000, // 1 + 0x00000000,0x00000000,0x0007e000,0x003ffc00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x0ffffff0,0x1c7ff0f8,0x1c3ff078,0x3c1ffc3c,0x3c0ffe3c,0x3c47fe3c,0x3c63fe3c,0x3c71fe3c,0x3c78fe3c,0x3c7c7c3c,0x3c7e387c,0x1c7f00f8,0x1c7f81f8,0x0ffffff0,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x003ffc00,0x0007e000,0x00000000,0x00000000, // 2 + 0x00000000,0x00000000,0x0007e000,0x003ffc00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x0ffffff0,0x1ffffff8,0x1e3ffc78,0x3c3ffc3c,0x3c7ffe3c,0x38ffff1c,0x38fe3f1c,0x38fe3f1c,0x38fe3f1c,0x387e3e3c,0x3c7c1e3c,0x1e000078,0x1f00c0f8,0x0f81e1f0,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x003ffc00,0x0007e000,0x00000000,0x00000000, // 3 + 0x00000000,0x00000000,0x0007e000,0x003ffc00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0fc07ff0,0x0fc03ff0,0x1fc01ff8,0x1fc70ff8,0x3fc787fc,0x3fc7c3fc,0x3fc7e1fc,0x3fc7f0fc,0x3fc7f87c,0x3c00003c,0x3c00003c,0x3c00003c,0x1fc7fff8,0x1fc7fff8,0x0fc7fff0,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x003ffc00,0x0007e000,0x00000000,0x00000000, // 4 + 0x00000000,0x00000000,0x0007e000,0x003ffc00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x0ffffff0,0x1f0f8038,0x1e0f0038,0x3c0f003c,0x3c3f1e3c,0x3c7f1e3c,0x3c7f1e3c,0x3c7f1e3c,0x3c7f1e3c,0x3c3e1e3c,0x3c001e3c,0x1e003e38,0x1f007e38,0x0ffffff0,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x003ffc00,0x0007e000,0x00000000,0x00000000, // 5 + 0x00000000,0x00000000,0x0007e000,0x003ffc00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x0fc003f0,0x1f0000f8,0x1e000078,0x3c3e3e3c,0x3c7f1f1c,0x3cff9f1c,0x3cff9f1c,0x3cff9f1c,0x3cff9f1c,0x3c7f1f1c,0x3c3e1e1c,0x1e003838,0x1f007878,0x0f81f9f0,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x003ffc00,0x0007e000,0x00000000,0x00000000, // 6 + 0x00000000,0x00000000,0x0007e000,0x003ffc00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x0ffffff0,0x1ffffc78,0x1ffffc78,0x3ffffc7c,0x38fffc7c,0x383ffc7c,0x380ffc7c,0x3f01fc7c,0x3fc07c7c,0x3ff81c7c,0x3ffe047c,0x1fff8078,0x1fffe078,0x0ffff070,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x003ffc00,0x0007e000,0x00000000,0x00000000, // 7 + 0x00000000,0x00000000,0x0007e000,0x003ffc00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x0f03e0f0,0x1e018078,0x1c008038,0x38781e1c,0x38fc3f1c,0x39fe7f9c,0x39fe7f9c,0x39fe7f9c,0x38fc3f1c,0x38781e1c,0x3c00803c,0x1e01c078,0x1f03e0f8,0x0ffffff0,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x003ffc00,0x0007e000,0x00000000,0x00000000, // 8 + 0x00000000,0x00000000,0x0007e000,0x003ffc00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x0ffffff0,0x1f1f01f8,0x1e1e00f8,0x3c7c7c7c,0x3cf8fe3c,0x3cf9ff3c,0x3cf9ff3c,0x3cf9ff3c,0x3cf9ff3c,0x3c78fe3c,0x3e3c7c7c,0x1f0000f8,0x1f8001f8,0x0fc003f0,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x003ffc00,0x0007e000,0x00000000,0x00000000, // 9 + 0x00000000,0x00018000,0x0003c000,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x00ffff00,0x01ffff80,0x03ffffc0,0x03ffffe0,0x03ffffe0,0x03ffffe0,0x03ffffe0,0x03fbefe0,0x03f1c7e0,0x03e083e0,0x03c001e0,0x03e003e0,0x03f007e0,0x03f80fe0,0x03f007e0,0x03e003e0,0x03c001e0,0x03e083e0,0x03f1c7e0,0x03fbefe0,0x03ffffe0,0x03ffffe0,0x03ffffe0,0x01ffffc0, // : + 0x00000000,0x00000000,0x00000000,0x0007e000,0x003ffc00,0x007ffe00,0x01f81f80,0x03e007c0,0x038001c0,0x070000e0,0x0f0000f0,0x0e000070,0x0e000070,0x1c000038,0x1c000038,0x1c000038,0x1c000038,0x1c000038,0x1c000038,0x0e000070,0x0e000070,0x0f0000f0,0x070000e0,0x038001c0,0x03e007c0,0x01f81f80,0x007ffe00,0x003ffc00,0x0007e000,0x00000000,0x00000000,0x00000000, // ; + 0x00000000,0x00000000,0x00000000,0x1ffffff8,0x1ffffff8,0x18000018,0x18000018,0x18007ff8,0x18007ff8,0x1ff86678,0x1ff86678,0x1c186678,0x1c186678,0x1ff86678,0x1ff86678,0x1ff86678,0x1ff86678,0x1ff86678,0x1ff86678,0x1ff86678,0x1ff86678,0x1ff86678,0x1ff86678,0x18007ff8,0x18007ff8,0x18000018,0x18000018,0x1ffffff8,0x1ffffff8,0x00000000,0x00000000,0x00000000, // < + 0x00000000,0x00000000,0x18000000,0x1c000000,0x1f000000,0x1fc00000,0x1ff00000,0x1cfc0000,0x1c3f0000,0x1c0fc000,0x1c03f000,0x1c00fc00,0x1c003f00,0x1c000fc0,0x1c0003f0,0x1c0000f8,0x1c0000f8,0x1c0003f0,0x1c000fc0,0x1c003f00,0x1c00fc00,0x1c03f000,0x1c0fc000,0x1c3f0000,0x1cfc0000,0x1ff00000,0x1fc00000,0x1f000000,0x1c000000,0x18000000,0x00000000,0x00000000, // = + 0x00000000,0x00000000,0x00000018,0x00000038,0x000000f8,0x000003f8,0x00000ff8,0x00003f38,0x0000fc38,0x0003f038,0x000fc038,0x003f0038,0x00fc0038,0x03f00038,0x0fc00038,0x1f000038,0x1f000038,0x0fc00038,0x03f00038,0x00fc0038,0x003f0038,0x000fc038,0x0003f038,0x0000fc38,0x00003f38,0x00000ff8,0x000003f8,0x000000f8,0x00000038,0x00000018,0x00000000,0x00000000, // > + 0x00000000,0x00000000,0x00000000,0x00018000,0x0003c000,0x0003c000,0x0007e000,0x0007e000,0x000e7000,0x000e7000,0x001c3800,0x001c3800,0x00381c00,0x00381c00,0x00700e00,0x00700e00,0x00e00700,0x00e00700,0x01c00380,0x01c00380,0x038001c0,0x038001c0,0x070000e0,0x070000e0,0x0e000070,0x0e000070,0x1ffffff8,0x3ffffffc,0x3ffffffc,0x00000000,0x00000000,0x00000000, // ? + + 0x00000000,0x00000000,0x00000000,0x3ffffffc,0x3ffffffc,0x1ffffff8,0x0e000070,0x0e000070,0x070000e0,0x070000e0,0x038001c0,0x038001c0,0x01c00380,0x01c00380,0x00e00700,0x00e00700,0x00700e00,0x00700e00,0x00381c00,0x00381c00,0x001c3800,0x001c3800,0x000e7000,0x000e7000,0x0007e000,0x0007e000,0x0003c000,0x0003c000,0x00018000,0x00000000,0x00000000,0x00000000, // @ + 0x00000000,0x00000000,0x00000000,0x00000000,0x0ffffff0,0x0ffffff0,0x0c000030,0x0dc003b0,0x0de007b0,0x0df00fb0,0x0cf81f30,0x0c7c3e30,0x0c3e7c30,0x0c1e7830,0x0c0ff030,0x0c03c030,0x0c03c030,0x0c0ff030,0x0c1e7830,0x0c3e7c30,0x0c7c3e30,0x0cf81f30,0x0df00fb0,0x0de007b0,0x0dc003b0,0x0c000030,0x0ffffff0,0x0ffffff0,0x00000000,0x00000000,0x00000000,0x00000000, // A + 0x00000000,0x00000000,0x00000000,0x00000000,0x0ffffff0,0x0ffffff0,0x0c000030,0x0c080030,0x0c1c0030,0x0c3c0030,0x0c780030,0x0ce00030,0x0dc00030,0x0df00030,0x0cfc0030,0x0c3f0030,0x0c0f8030,0x0c03c030,0x0c00e030,0x0c007830,0x0c003c30,0x0c000e30,0x0c000730,0x0c0001b0,0x0c0000b0,0x0c000030,0x0ffffff0,0x0ffffff0,0x00000000,0x00000000,0x00000000,0x00000000, // B + 0x00000000,0x00000000,0x01ffff80,0x01ffff80,0x01c00380,0x01e00780,0x01f00f80,0x01b81d80,0x019c3980,0x018e7180,0x0187e180,0x0183c180,0x01838180,0x01870180,0x018e0180,0x019c0180,0x019c0180,0x018e0180,0x01870180,0x01838180,0x0183c180,0x0187e180,0x018e7180,0x019c3980,0x01b81d80,0x01f00f80,0x01e00780,0x01c00380,0x01ffff80,0x01ffff80,0x00000000,0x00000000, // C + 0x00000000,0x00000000,0x00000000,0x00000000,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0e000070,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x00000000,0x00000000,0x00000000,0x00000000, // D + 0x00000000,0x00000000,0x00003800,0x000e3800,0x000e3800,0x000e3800,0x003ffc00,0x00ffff00,0x03ffffc0,0x07ce39e0,0x0f0e38f0,0x0e0e3870,0x1c0e3838,0x1c0e3838,0x1c0e3838,0x1c0e3838,0x1c0e3838,0x1c003838,0x1c003838,0x1c003838,0x1c000038,0x1c000038,0x1e000078,0x0f8001f0,0x078001e0,0x038001c0,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // E + 0x00000000,0x00000000,0x00000000,0x00000000,0x30000000,0x38000000,0x1c000000,0x0e000000,0x0f000000,0x07800000,0x03c00000,0x03e03000,0x01f07c00,0x00f8fe00,0x00fdff80,0x007fffc0,0x003fffe0,0x001ffff8,0x001feffc,0x000fc7fc,0x000783fc,0x000701fc,0x000200fc,0x0000007c,0x0000003c,0x0000001c,0x0000000c,0x00000004,0x00000000,0x00000000,0x00000000,0x00000000, // F + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x000180c0,0x180180e0,0x1c018070,0x0701fff8,0x0381fff8,0x01c18000,0x00f18000,0x00398000,0x001c0000,0x000f0000,0x00038000,0x03c1c000,0x03e0f000,0x03703800,0x031c1c00,0x030e0f00,0x03070380,0x1fff81c0,0x1fff80f0,0x03000038,0x03000018,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // G + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x000180c0,0x180180e0,0x1c018070,0x0701fff8,0x0381fff8,0x01c18000,0x00f18000,0x00398000,0x001c0000,0x000f0000,0x00038000,0x0001c000,0x0000f000,0x00003800,0x1e061c00,0x1f070f00,0x1b838380,0x19c181c0,0x18e180f0,0x18738038,0x183f0018,0x181e0000,0x180c0000,0x00000000,0x00000000,0x00000000,0x00000000, // H + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00006060,0x1800e070,0x1c01c038,0x07018018,0x03818618,0x01c18618,0x00f1c638,0x0038fff0,0x001c79e0,0x000f0000,0x00038000,0x03c1c000,0x03e0f000,0x03703800,0x031c1c00,0x030e0f00,0x03070380,0x1fff81c0,0x1fff80f0,0x03000038,0x03000018,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // I + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00f80000,0x01fc0000,0x03fe0000,0x070700c0,0x0603f9e0,0x0601f9e0,0x0600f9e0,0x060000c0,0x07000000,0x03c00000,0x01c00000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // J + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x1ffff380,0x1ffff3c0,0x1ffff1e0,0x1ffff0f0,0x0003c078,0x0001e038,0x0000f038,0x00007070,0x000070e0,0x000071c0,0x00007380,0x0000f380,0x1ffff3c0,0x1fffe1e0,0x1fffc0f0,0x1fff8070,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // K + 0x00000000,0x00000000,0x001ff800,0x007ffe00,0x01e00780,0x078001c0,0x060000e0,0x0c180070,0x0c38fc30,0x1871fe18,0x18618618,0x30e1860c,0x30c1fe0c,0x30c0fc0c,0x30c0000c,0x30c0000c,0x30c0000c,0x30c0000c,0x30c0fc0c,0x30c1fe0c,0x30e1860c,0x18618618,0x1871fe18,0x0c38fc30,0x0c180070,0x060000e0,0x078001c0,0x01e00780,0x007ffe00,0x001ff800,0x00000000,0x00000000, // L + 0x00000000,0x00000000,0x001ff800,0x007ffe00,0x01e00780,0x078001c0,0x060000e0,0x0cc00070,0x0ce0fc30,0x1871fe18,0x18318618,0x3039860c,0x3019fe0c,0x3018fc0c,0x3018000c,0x3018000c,0x3018000c,0x3018000c,0x3018fc0c,0x3019fe0c,0x3039860c,0x18318618,0x1871fe18,0x0ce0fc30,0x0cc00070,0x060000e0,0x078001c0,0x01e00780,0x007ffe00,0x001ff800,0x00000000,0x00000000, // M + 0x00000000,0x00000000,0x001ff800,0x007ffe00,0x01e00780,0x078001c0,0x060000e0,0x0c300070,0x0c30fc30,0x1831fe18,0x18318618,0x3031860c,0x3031fe0c,0x3030fc0c,0x3030000c,0x3030000c,0x3030000c,0x3030000c,0x3030fc0c,0x3031fe0c,0x3031860c,0x18318618,0x1831fe18,0x0c30fc30,0x0c300070,0x060000e0,0x078001c0,0x01e00780,0x007ffe00,0x001ff800,0x00000000,0x00000000, // N + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00180000,0x0c1b0000,0x6c1f0000,0x7c1f1fc0,0x7c3f3fe0,0x7e7873f0,0x0f70e1f8,0x07f3e1fc,0x07e3f3fc,0x03e0fffc,0x03c3cffc,0x01e3cffc,0x04e0fffc,0x0673f3fc,0x0f73e1f8,0x7e38e1f8,0x7c3873f0,0x7c3f3fe0,0x6c3f1fc0,0x0c1f0000,0x001b0000,0x00180000,0x00000000,0x00000000,0x00000000,0x00000000, // O + + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x000ff800,0x001ff000,0x003fe000,0x007fc000,0x00ff8000,0x01ff0000,0x03fe0000,0x07fc0000,0x0ffffff8,0x1ffffff8,0x3ffffff8,0x3ffffff8,0x1ffffff8,0x0ffffff8,0x07fc0000,0x03fe0000,0x01ff0000,0x00ff8000,0x007fc000,0x003fe000,0x001ff000,0x000ff800,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // P + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x001ff000,0x000ff800,0x0007fc00,0x0003fe00,0x0001ff00,0x0000ff80,0x00007fc0,0x00003fe0,0x1ffffff0,0x1ffffff8,0x1ffffffc,0x1ffffffc,0x1ffffff8,0x1ffffff0,0x00003fe0,0x00007fc0,0x0000ff80,0x0001ff00,0x0003fe00,0x0007fc00,0x000ff800,0x001ff000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // Q + 0x00000000,0x00000000,0x00018000,0x0003c000,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x07ffffe0,0x07f7efe0,0x07e7e7e0,0x07c7e3e0,0x0787e1e0,0x0707e0e0,0x0607e060,0x0407e020,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x00000000,0x00000000,0x00000000, // R + 0x00000000,0x00000000,0x00000000,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x0007e000,0x0407e020,0x0607e060,0x0707e0e0,0x0787e1e0,0x07c7e3e0,0x07e7e7e0,0x07f7efe0,0x07ffffe0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x007ffe00,0x003ffc00,0x001ff800,0x000ff000,0x0007e000,0x0003c000,0x00018000,0x00000000,0x00000000, // S + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00700000,0x01f00000,0x03e00000,0x07c00000,0x0f800000,0x07c00000,0x01e00000,0x00f80000,0x003c0000,0x000f0000,0x00078000,0x0003c000,0x0000e000,0x00007000,0x00003800,0x00001c00,0x00000e00,0x00000700,0x00000380,0x000001c0,0x000000e0,0x00000020,0x00000010,0x00000000,0x00000000,0x00000000,0x00000000, // T + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0007fc00,0x000ffe00,0x001fff00,0x003fff80,0x007c03c0,0x00f801e0,0x00f000f0,0x00f000f0,0x00f000f0,0x00f000f0,0x1fff80f0,0x0fff00f0,0x07fe00f0,0x03fc00f0,0x01f801e0,0x00f003c0,0x0060ff80,0x0000ff00,0x0000fe00,0x0000fc00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // U + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x003fe000,0x007ff000,0x00fff800,0x01fffc00,0x03c03e00,0x07801f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f000f00,0x0f01fff8,0x0f00fff0,0x0f007fe0,0x0f003fc0,0x07801f80,0x03c00f00,0x01ff0600,0x00ff0000,0x007f0000,0x003f0000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // V + 0x00000000,0x00000000,0x00000000,0x00003c00,0x00007e00,0x0000ff00,0x0001ff80,0x0003ffc0,0x0007ffe0,0x000fffe0,0x001fffe0,0x003fffe0,0x007fffc0,0x00ffff80,0x01ffff00,0x03fffe00,0x03fffe00,0x01ffff00,0x00ffff80,0x007fffc0,0x003fffe0,0x001fffe0,0x000fffe0,0x0007ffe0,0x0003ffc0,0x0001ff80,0x0000ff00,0x00007e00,0x00003c00,0x00000000,0x00000000,0x00000000, // W + 0x00000000,0x00000000,0x001ff800,0x007ffe00,0x01ffff80,0x03c003c0,0x078001e0,0x0f0000f0,0x0e000070,0x1c1ff838,0x183ffc18,0x387ffe1c,0x38f00f1c,0x39e0079c,0x39c0039c,0x39c0039c,0x39c0039c,0x39c0039c,0x39c0039c,0x39e0079c,0x38f81f1c,0x18781e18,0x1c381c38,0x0e000070,0x0f0000f0,0x078001e0,0x03c003c0,0x01ffff80,0x007ffe00,0x001ff800,0x00000000,0x00000000, // X + 0x00000000,0x00000000,0x001ff800,0x007ffe00,0x01ffff80,0x03c003c0,0x078001e0,0x0f0000f0,0x0e000070,0x1c000038,0x18000018,0x39ffff1c,0x39ffff9c,0x39ffff9c,0x3803839c,0x3803839c,0x3803839c,0x3807c79c,0x39ffff1c,0x39fefe1c,0x39fc7c1c,0x18000018,0x1c000038,0x0e000070,0x0f0000f0,0x078001e0,0x03c003c0,0x01ffff80,0x007ffe00,0x001ff800,0x00000000,0x00000000, // Y + 0x00000000,0x00000000,0x00000000,0x00000000,0x0e07e000,0x0e1ff800,0x0e7ffe00,0x0effff00,0x0ef81f00,0x0fe00780,0x0fc003c0,0x0fc003c0,0x0f8001c0,0x0f0001e0,0x000001e0,0x000001e0,0x000001e0,0x000001e0,0x0f0001e0,0x0f8001c0,0x0fc003c0,0x0fc003c0,0x0fe00780,0x0ef81f00,0x0effff00,0x0e3ffe00,0x0e1ff800,0x0e07e000,0x00000000,0x00000000,0x00000000,0x00000000, // Z + 0x00000000,0x00000000,0x3ffffffc,0x3ffffffc,0x3000000c,0x3000000c,0x3000000c,0x3000000c,0x3780000c,0x3780000c,0x3780000c,0x3780e38c,0x3780e7cc,0x37ffefec,0x37ffefec,0x37ffefec,0x37ffefec,0x37ffefec,0x37ffe7cc,0x3780038c,0x3780000c,0x3780000c,0x3780000c,0x3780000c,0x3000000c,0x3000000c,0x3000000c,0x3000000c,0x1ffffff8,0x0ffffff0,0x00000000,0x00000000, // [ + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00007c00,0x0000fe00,0x0001ff00,0x0003c780,0x00038380,0x00038380,0x00038380,0x0003c780,0x0001ff00,0x0000fe00,0x00007c00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x1e000000,0x1e000000,0x1e078000,0x1e078000,0x1e078000,0x1fffff80,0x1fffffc0,0x1fffffe0,0x1ffffff0,0x1e0781f8,0x1e0780f8,0x1e078078,0x1e078078,0x1e078078,0x1e078078,0x1e000078,0x1e0001f8,0x1e0001f8,0x1e0001f0,0x1e0001e0,0x1e000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ] + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x1e000000,0x1e000000,0x1e000000,0x1ffffff8,0x0ffffff8,0x07fffff8,0x03fffff8,0x001e0000,0x003c0000,0x00780000,0x00780000,0x00780000,0x00780000,0x003c0000,0x001e0000,0x000ffff8,0x001ffff8,0x003ffff8,0x007ffff8,0x00780000,0x00780000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ^ + 0x00000000,0x00000000,0x01e00780,0x01e00780,0x01e00780,0x01e00780,0x01f00780,0x00f80780,0x007c0f80,0x003e1f00,0x001f1e00,0x000f9c00,0x0007c000,0x0003e000,0x0001f000,0x0000f800,0x000c7c00,0x001e3e00,0x003e1f00,0x007c0f80,0x00f80780,0x01f00780,0x01e00780,0x01e00780,0x1ffe7ff8,0x0ffc3ff0,0x07f81fe0,0x03f00fc0,0x01e00780,0x00c00300,0x00000000,0x00000000, // _ + + 0x00000000,0x00000000,0x00000000,0x079c0000,0x079e0000,0x079f0000,0x079f8000,0x079fc000,0x079fe000,0x079ff000,0x079ff800,0x079ffc00,0x079ffe00,0x079fff00,0x079fff80,0x079fffc0,0x079fffc0,0x079fff80,0x079fff00,0x079ffe00,0x079ffc00,0x079ff800,0x079ff000,0x079fe000,0x079fc000,0x079f8000,0x079f0000,0x079e0000,0x079c0000,0x00000000,0x00000000,0x00000000, // ` + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00018000,0x0003c000,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x1ffffff8,0x3ffffffc,0x3ffffffc,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // a + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x3ffffffc,0x3ffffffc,0x1ffffff8,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x007ffe00,0x003ffc00,0x001ff800,0x000ff000,0x0007e000,0x0003c000,0x00018000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // b + 0x00000000,0x00000000,0x00c00000,0x00e00000,0x00f00000,0x00f80000,0x00fc0000,0x00fe0000,0x00ff0000,0x00ff8000,0x00ffc000,0x00ffe000,0x00fff000,0x00fff800,0x00fffc00,0x00fffe00,0x00fffe00,0x00fffc00,0x00fff800,0x00fff000,0x00ffe000,0x00ffc000,0x00ff8000,0x00ff0000,0x00fe0000,0x00fc0000,0x00f80000,0x00f00000,0x00e00000,0x00c00000,0x00000000,0x00000000, // c + 0x00000000,0x00000000,0x00000600,0x00000e00,0x00001e00,0x00003e00,0x00007e00,0x0000fe00,0x0001fe00,0x0003fe00,0x0007fe00,0x000ffe00,0x001ffe00,0x003ffe00,0x007ffe00,0x00fffe00,0x00fffe00,0x007ffe00,0x003ffe00,0x001ffe00,0x000ffe00,0x0007fe00,0x0003fe00,0x0001fe00,0x0000fe00,0x00007e00,0x00003e00,0x00001e00,0x00000e00,0x00000600,0x00000000,0x00000000, // d + 0x00000000,0x00000000,0x00000000,0x00018000,0x0003c000,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x1ffffff8,0x3ffffffc,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x1ffffff8,0x3ffffffc,0x00000000,0x00000000,0x00000000, // e + 0x00000000,0x00000000,0x00000000,0x3ffffffc,0x1ffffff8,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x007ffe00,0x003ffc00,0x001ff800,0x000ff000,0x0007e000,0x3ffffffc,0x1ffffff8,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x007ffe00,0x003ffc00,0x001ff800,0x000ff000,0x0007e000,0x0003c000,0x00018000,0x00000000,0x00000000,0x00000000, // f + 0x00000000,0x00000000,0x3ffffffc,0x3ffffffc,0x3ffffffc,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x1ffffff8,0x3ffffffc,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x1ffffff8,0x3ffffffc,0x00000000,0x00000000,0x00000000, // g + 0x00000000,0x00000000,0x00000000,0x3ffffffc,0x1ffffff8,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x007ffe00,0x003ffc00,0x001ff800,0x000ff000,0x0007e000,0x3ffffffc,0x1ffffff8,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x007ffe00,0x003ffc00,0x001ff800,0x000ff000,0x0007e000,0x3ffffffc,0x3ffffffc,0x3ffffffc,0x00000000,0x00000000, // h + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x00000000,0x00000000,0x00000000,0x00000000,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // i + 0x00000000,0x00000000,0x00000000,0x00000000,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x00000000,0x00000000,0x00000000,0x00000000, // j + 0x00000000,0x00000000,0x00000000,0x0007e000,0x003ffc00,0x007ffe00,0x01ffff80,0x03ffffc0,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x0ffffff0,0x0ffffff0,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x03ffffc0,0x01ffff80,0x007ffe00,0x003ffc00,0x0007e000,0x00000000,0x00000000,0x00000000, // k + 0x00000000,0x00000000,0x001fc000,0x007ff000,0x00fffc00,0x03fffe00,0x07e03f00,0x07800f00,0x0f000780,0x0f0007c0,0x1e0003c0,0x1e0003c0,0x3c000000,0x3c000000,0x3c03fffc,0x3c03fffc,0x3c03fffc,0x3c03fffc,0x3c000000,0x3c000000,0x1e0003c0,0x1e0003c0,0x0f0007c0,0x0f000780,0x07800f00,0x03e03f00,0x03fffe00,0x00fff800,0x007ff000,0x000fc000,0x00000000,0x00000000, // l + 0x00000000,0x00000000,0x001ff800,0x007ffe00,0x01ffff80,0x07f00fc0,0x07f803e0,0x0fb801f0,0x0e3c0070,0x1e1fe078,0x1c1fe038,0x3c0fe03c,0x380fe01c,0x3807e01c,0x3803e01c,0x3803e01c,0x3807f01c,0x380ff81c,0x381ffc1c,0x383ffe1c,0x3c3ffe3c,0x1c007838,0x1e003878,0x0e003c70,0x0f801df0,0x07c01fe0,0x07f00fc0,0x01ffff80,0x007ffe00,0x001ff800,0x00000000,0x00000000, // m + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0007c000,0x0007c000,0x0007c000,0x0007c000,0x0007c000,0x0007c000,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x007ffe00,0x00000000,0x00181800,0x00181800,0x001c3800,0x000ff000,0x0007e000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // n + 0x00000000,0x00000000,0x0007c000,0x0007c000,0x0007c000,0x0007c000,0x0007c000,0x0007c000,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x007ffe00,0x00000000,0x00181800,0x00181800,0x019c3980,0x018ff180,0x1987e198,0x18c00318,0x18e00718,0x18700e18,0x0c3ffc30,0x0e0ff070,0x070000e0,0x038001c0,0x01e00780,0x007ffe00,0x001ff800,0x00000000,0x00000000, // o + + 0x00000000,0x00000000,0x00000000,0x0c000000,0x1e000000,0x3f000000,0x3f800000,0x3f800000,0x1f800000,0x1f800000,0x0fffff80,0x07ffffc0,0x03ffffc0,0x000071c0,0x000071c0,0x000071e0,0x000038e0,0x000038e0,0x000038e0,0x03003ce0,0x07801c70,0x0fc01c70,0x0fe01c70,0x0fe01e70,0x07e00e38,0x07e00e38,0x03fffe38,0x01fffff8,0x00fffff8,0x00000000,0x00000000,0x00000000, // p + 0x00000000,0x00000000,0x00000000,0x01807800,0x03c07c00,0x07e07e00,0x0ff07f00,0x1ff80f80,0x3ffc07c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03c003c0,0x03e03ffc,0x01f01ff8,0x00fe0ff0,0x007e07e0,0x003e03c0,0x001e0180,0x00000000,0x00000000,0x00000000, // q + 0x00000000,0x3f800e80,0x3ff00ec0,0x3ff80ee0,0x3ffc0ef0,0x3ffe0ef8,0x3fff0efc,0x3fff807e,0x3f87c03e,0x3e01fc3e,0x3c00fe3e,0x38787ffe,0x38fc7ffe,0x30fc3ffe,0x31fe3e3e,0x31fe3c3e,0x31fe3c3e,0x31fe3e3e,0x30fc3ffe,0x38fc7ffe,0x38787ffe,0x3c00fe3e,0x3e01fc3e,0x3f87c03e,0x3fff807e,0x3fff0efc,0x3ffe0ef8,0x3ffc0ef0,0x3ff80ee0,0x3ff00ec0,0x3f800e80,0x00000000, // r + 0x00000000,0x00000000,0x00600c00,0x00701c00,0x00783c00,0x007ffe00,0x00fffe00,0x00f81f00,0x01e00780,0x07c003e0,0x7f8001fe,0x7f0000fe,0x3f03c0fc,0x1e07e078,0x0e0ff070,0x0e0ff070,0x0e0ff070,0x0e0ff070,0x1e07e078,0x3f03c0fc,0x7f0000fe,0x7f8001fe,0x07c003e0,0x01e00780,0x00f81f00,0x00fffe00,0x007ffe00,0x00783c00,0x00701c00,0x00600c00,0x00000000,0x00000000, // s + 0x00000000,0x00000000,0x00018000,0x0003c000,0x0007e000,0x000ff000,0x001ff800,0x003ffc00,0x007ffe00,0x00ffff00,0x01ffff80,0x03ffffc0,0x07ffffe0,0x0ffffff0,0x1ffffff8,0x3ffffffc,0x3ffffffc,0x1ffffff8,0x0ffffff0,0x07ffffe0,0x03ffffc0,0x01ffff80,0x00ffff00,0x007ffe00,0x003ffc00,0x001ff800,0x000ff000,0x0007e000,0x0003c000,0x00018000,0x00000000,0x00000000, // t + 0x00000000,0x00000000,0x00000000,0x003ffc00,0x001ff800,0x000ff000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x3803c01c,0x3c03c03c,0x3f87e1fc,0x1ffffff8,0x0ffffff0,0x0dffffb0,0x0cffff30,0x007ffe00,0x003ffc00,0x0033cc00,0x0033cc00,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x0003c000,0x00018000,0x00000000,0x00000000, // u + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00001f80,0x00001f80,0x00001f80,0x00001f80,0x00001f80,0x00001f80,0x00001f80,0x3ffffffc,0x3ffffffc,0x3ffffffc,0x3ffffffc,0x3ffffffc,0x3ffffffc,0x00001f80,0x00001f80,0x00001f80,0x00001f80,0x00001f80,0x00001f80,0x00001f80,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // v + 0x00000000,0x00000000,0x01f81f80,0x01f81f80,0x01981980,0x01981980,0x01f81f80,0x01f81f80,0x003c3c00,0x001c3800,0x000e7000,0x0007e000,0x0003c000,0x0007e000,0x0007e000,0x0003c000,0x0007f000,0x000e7000,0x001e7800,0x001c3800,0x003c3c00,0x00381c00,0x00700e00,0x00700e00,0x00f00f00,0x00f00f00,0x00e00700,0x00c00300,0x00c00300,0x00800100,0x00000000,0x00000000, // w + 0x00000000,0x00000000,0x00000000,0x1f0000f8,0x1f8001f8,0x1fc003f8,0x1fe007f8,0x1ff00ff8,0x0ff81ff0,0x07fc3fe0,0x03fe7fc0,0x01fe7f80,0x00ffff00,0x007ffe00,0x003ffc00,0x000ff000,0x000ff000,0x003ffc00,0x007ffe00,0x00ffff00,0x01fe7f80,0x03fe7fc0,0x07fc3fe0,0x0ff81ff0,0x1ff00ff8,0x1fe007f8,0x1fc003f8,0x1f8001f8,0x1f0000f8,0x00000000,0x00000000,0x00000000, // x + 0x00000000,0x00000400,0x00000c00,0x00001c00,0x00003c00,0x3e007c00,0x1f80fc00,0x0fe1fc00,0x07f3fc00,0x03fffc00,0x03fffc00,0x01ffff00,0x00ffffc0,0x00fffff0,0x007ffff8,0x007ffffe,0x007ffffe,0x007ffff8,0x00fffff0,0x00ffffc0,0x01ffff00,0x03fffc00,0x03fffc00,0x07f3fc00,0x0fe1fc00,0x1f80fc00,0x3e007c00,0x00003c00,0x00001c00,0x00000c00,0x00000400,0x00000000, // y + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x10000004,0x3b80006e,0x3be001ee,0x3bf007ee,0x3bf80fee,0x3bfc1fee,0x3b9e3fee,0x3b8f7bee,0x3b87f3ee,0x3b83e3ee,0x3b81c3ee,0x3b83e3ee,0x3b87f3ee,0x3b8f7bee,0x3b9e3fee,0x3bfc1fee,0x3bf80fee,0x3bf007ee,0x3be001ee,0x3b80006e,0x30000004,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // z + 0x00000000,0x00000000,0x00003f00,0x0000ffc0,0x0003fff0,0x0003c0f0,0x00078c78,0x00070c38,0x000e0c1c,0x000e0c1c,0x000e0c1c,0x000e0c1c,0x000e0c1c,0x000e0c1c,0x00070c38,0x00038c78,0x0007c0f0,0x000ffff0,0x001f7fc0,0x003e3f00,0x007c0000,0x00f80000,0x01f00000,0x03e00000,0x07c00000,0x0f800000,0x1f000000,0x3e000000,0x3c000000,0x38000000,0x00000000,0x00000000, // { + 0x00000000,0x00000000,0x00003f00,0x0000ffc0,0x0001ffe0,0x0003c0f0,0x00078c78,0x00070c38,0x000e0c1c,0x000e0c1c,0x000effdc,0x000effdc,0x000e0c1c,0x000e0c1c,0x00070c38,0x00038c78,0x0007c0f0,0x000fffe0,0x001f7fc0,0x003e3f00,0x007c0000,0x00f80000,0x01f00000,0x03e00000,0x07c00000,0x0f800000,0x1f000000,0x3e000000,0x3c000000,0x38000000,0x00000000,0x00000000, // | + 0x00000000,0x00000000,0x03fffe70,0x07ffff38,0x0fffff9c,0x1fffff9c,0x1fffff9c,0x1fffff9c,0x1fffff9c,0x1ffdff9c,0x1ff9ff9c,0x1ff1ff9c,0x1fe1ff9c,0x1fc1ff9c,0x1f80001c,0x1f00001c,0x1f00001c,0x1f80001c,0x1fc1ff9c,0x1fe1ff9c,0x1ff1ff9c,0x1ff9ff9c,0x1ffdff9c,0x1fffff9c,0x1fffff9c,0x1fffff9c,0x1fffff9c,0x0fffff9c,0x07ffff38,0x03fffe70,0x00000000,0x00000000, // } + 0x00000000,0x00000000,0x00000000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x1ffffff8,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x0007f000,0x00000000,0x00000000,0x00000000 // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/Font_Ubuntu_24X32.java b/src/devices/Oled/SSD1306/Font_Ubuntu_24X32.java new file mode 100644 index 0000000..b4b062a --- /dev/null +++ b/src/devices/Oled/SSD1306/Font_Ubuntu_24X32.java @@ -0,0 +1,112 @@ +package devices.Oled.SSD1306; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Font_Ubuntu_24X32") +public class Font_Ubuntu_24X32 extends FontPack { + public Font_Ubuntu_24X32() { + super("Font_Ubuntu_24X32", 24, 32, 24, 32, 32, 126); + data = new int[] { + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00e00000,0x01f1fffe,0x01f1fffe,0x01f0fffe,0x00e00000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ! + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000600,0x00000600,0x0000071c,0x000003be,0x000003fe,0x000001fe,0x0000007c,0x00000000,0x00000000,0x00000600,0x00000600,0x0000071c,0x000003be,0x000003fe,0x000001fe,0x0000007c,0x00000000,0x00000000,0x00000000,0x00000000, // " + 0x00000000,0x00000000,0x00000000,0x00000000,0x000e0700,0x030e0700,0x03fe0700,0x03ffc700,0x00ffff00,0x000ffff0,0x000e3ffc,0x000e07fc,0x030e070c,0x03fe0700,0x03ffc700,0x00ffff00,0x000ffff0,0x000e3ffc,0x000e07fc,0x000e070c,0x000e0700,0x00000000,0x00000000,0x00000000, // # + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x03001f00,0x03807f80,0x0700ffc0,0x0700f1c0,0x0701e0e0,0x7f01c0fe,0x7f01c0fe,0x7f03c0fe,0x070380e0,0x070780e0,0x038f00e0,0x03ff01c0,0x01fe00c0,0x00780000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // $ + 0x00000000,0x00000000,0x00000000,0x010003f0,0x01c00ffc,0x01e01c0e,0x00f81806,0x003e1806,0x001f1c0e,0x0007cffc,0x0001f3f0,0x0000fc00,0x003f3e00,0x00ffcf80,0x01c0e3e0,0x018061f0,0x0180607c,0x01c0e01e,0x00ffc00e,0x003f0002,0x00000000,0x00000000,0x00000000,0x00000000, // % + 0x00000000,0x00000000,0x00000000,0x00000000,0x001f0000,0x007f8000,0x00ffe1f0,0x00f0f7f8,0x01e07ffc,0x01c03e1e,0x01c07c0e,0x01c0fc0e,0x01e1ee0e,0x00e3cf1e,0x00f787fc,0x007f03fc,0x003f00f0,0x00fff000,0x01f3f000,0x01c07000,0x01000000,0x00000000,0x00000000,0x00000000, // & + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x0000000e,0x0000001e,0x0000003c,0x00000078,0x00000020,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ' + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x000ff800,0x007fff00,0x01ffffc0,0x07f007f0,0x0f8000f8,0x1e00003e,0x3c00001f,0x7800000f,0xf0000007,0x20000002,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ( + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x20000002,0xf0000007,0x7800000f,0x3c00001e,0x1e00003c,0x0f8000f8,0x07f007f0,0x01ffffc0,0x007fff00,0x000ff800,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ) + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00001000,0x00001c00,0x00043800,0x000e3800,0x001f3800,0x000fb000,0x0003f780,0x00007f80,0x0001f780,0x000fb000,0x001f3800,0x000e3800,0x00043800,0x00001c00,0x00001000,0x00000000,0x00000000,0x00000000,0x00000000, // * + 0x00000000,0x00000000,0x00000000,0x00000000,0x00007000,0x00007000,0x00007000,0x00007000,0x00007000,0x00007000,0x003fffe0,0x003fffe0,0x003fffe0,0x00007000,0x00007000,0x00007000,0x00007000,0x00007000,0x00007000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // + + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x60000000,0x60000000,0x71c00000,0x3be00000,0x1fe00000,0x0fe00000,0x07c00000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // , + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00007000,0x00007000,0x00007000,0x00007000,0x00007000,0x00007000,0x00007000,0x00007000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // - + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x03800000,0x07c00000,0x07c00000,0x07c00000,0x03800000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // . + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0xc0000000,0xf8000000,0xff000000,0x3ff00000,0x07fe0000,0x00ffc000,0x000ff800,0x0001ff80,0x00003ff0,0x000007fe,0x0000007f,0x0000000f,0x00000001,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // / + + 0x00000000,0x00000000,0x00000000,0x00000000,0x0007fe00,0x003fffc0,0x00fffff0,0x01f801f8,0x01e00078,0x03c0703c,0x0380f81c,0x0380f81c,0x0380701c,0x03c0003c,0x01e00078,0x01f801f8,0x00fffff0,0x003fffc0,0x0007fe00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // 0 + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000080,0x038001c0,0x038000c0,0x038000e0,0x03800070,0x03800070,0x03fffffc,0x03fffffc,0x03fffffc,0x03800000,0x03800000,0x03800000,0x03800000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // 1 + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x03c00030,0x03f00078,0x03f80038,0x03bc003c,0x038e001c,0x0387001c,0x0383801c,0x0381c01c,0x0380f03c,0x03807878,0x03803ff8,0x03801ff0,0x038007e0,0x03800000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // 2 + 0x00000000,0x00000000,0x00000000,0x00000000,0x01800018,0x01c00038,0x03800038,0x0380001c,0x0380701c,0x0380701c,0x0380701c,0x0380701c,0x0380701c,0x0380f83c,0x01c0fc78,0x01e1fff8,0x00ffcff0,0x007f87c0,0x003f0000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // 3 + 0x00000000,0x00000000,0x00000000,0x000f0000,0x000fc000,0x000fe000,0x000ff800,0x000e7e00,0x000e1f00,0x000e0780,0x000e03e0,0x000e01f0,0x000e0078,0x000e003c,0x03fffffc,0x03fffffc,0x03fffffc,0x000e0000,0x000e0000,0x000e0000,0x00000000,0x00000000,0x00000000,0x00000000, // 4 + 0x00000000,0x00000000,0x00000000,0x00000000,0x01c00000,0x01c03e00,0x03803ffc,0x03803ffc,0x0380381c,0x0380381c,0x0380381c,0x0380781c,0x0380701c,0x01c0f01c,0x01e1f01c,0x00ffe01c,0x007fc01c,0x003f0000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // 5 + 0x00000000,0x00000000,0x00000000,0x00000000,0x000ff000,0x007ffe00,0x00ffff80,0x01f077c0,0x01c031e0,0x03c038f0,0x03803870,0x03803838,0x03803838,0x03803838,0x03c0781c,0x01e0f01c,0x00fff01c,0x007fe01c,0x001f8000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // 6 + 0x00000000,0x00000000,0x00000000,0x00000000,0x0000001c,0x0000001c,0x0000001c,0x0000001c,0x03c0001c,0x03fc001c,0x03ff801c,0x003fe01c,0x0003f81c,0x00007e1c,0x00001f9c,0x000007dc,0x000001fc,0x0000007c,0x0000003c,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // 7 + 0x00000000,0x00000000,0x00000000,0x00000000,0x003c0000,0x00ff07c0,0x01ff9ff0,0x01e1fff8,0x03c0fc38,0x0380f83c,0x0380701c,0x0380701c,0x0380e01c,0x0380f03c,0x03c1f878,0x01e3dff8,0x01ffcff0,0x00ff87e0,0x003e0000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // 8 + 0x00000000,0x00000000,0x00000000,0x00000000,0x00001f80,0x03807fe0,0x0380fff0,0x0380f078,0x0381e03c,0x03c1c01c,0x01c1c01c,0x01c1c01c,0x01e1c01c,0x00f1c03c,0x0079c038,0x003ee0f8,0x001ffff0,0x000fffe0,0x0001ff00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // 9 + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00e00e00,0x01f01f00,0x01f01f00,0x01f01f00,0x00e00e00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // : + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x30000000,0x30000000,0x38e00e00,0x1df01f00,0x0ff01f00,0x07f01f00,0x03e00e00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ; + 0x00000000,0x00000000,0x00000000,0x00000000,0x0001c000,0x0003c000,0x0003e000,0x0003e000,0x00077000,0x00077000,0x000e3800,0x000e3800,0x001e3c00,0x001c1c00,0x001c1c00,0x00380e00,0x00380e00,0x00780f00,0x00300600,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // < + 0x00000000,0x00000000,0x00000000,0x00000000,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x001c1c00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // = + 0x00000000,0x00000000,0x00000000,0x00000000,0x00300600,0x00780f00,0x00380e00,0x00380e00,0x001c1c00,0x001c1c00,0x001e3c00,0x000e3800,0x000e3800,0x00077000,0x00077000,0x0003e000,0x0003e000,0x0003c000,0x0001c000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // > + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x0000000c,0x0000001c,0x0000000e,0x00e0000e,0x01f1c00e,0x01f1e00e,0x01f1f00e,0x00e0780e,0x00001c1e,0x00000e1c,0x000007fc,0x000003f8,0x000001f0,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ? + + 0x00000000,0x00000000,0x00000000,0x00000000,0x000ffc00,0x00ffff80,0x03ffffe0,0x07f003f8,0x0f80007c,0x1f07e01c,0x1c1ff81e,0x3c3ffc0e,0x383c3c0e,0x38701e0e,0x38700e0e,0x38700e1e,0x38700e3c,0x387ffff8,0x007ffff0,0x003fffc0,0x00000000,0x00000000,0x00000000,0x00000000, // @ + 0x00000000,0x00000000,0x00000000,0x01800000,0x01f80000,0x01ff8000,0x00fff800,0x000fff00,0x0007fff0,0x00071ffe,0x000701fe,0x0007003e,0x0007001e,0x000701fe,0x00070ffe,0x0007fff0,0x000fff00,0x00fff800,0x01ff8000,0x01f80000,0x01800000,0x00000000,0x00000000,0x00000000, // A + 0x00000000,0x00000000,0x00000000,0x00000000,0x03fffffc,0x03fffffc,0x03fffffc,0x0380701c,0x0380701c,0x0380701c,0x0380701c,0x0380701c,0x0380701c,0x03c0783c,0x01c0fc78,0x01e1fff8,0x00ffcff0,0x00ff83e0,0x003f0000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // B + 0x00000000,0x00000000,0x00000000,0x00000000,0x0003fe00,0x000fffc0,0x003ffff0,0x007e01f8,0x00f80078,0x00f0003c,0x01e0001e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x00e0001c,0x00c0000c,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // C + 0x00000000,0x00000000,0x00000000,0x00000000,0x01fffffe,0x01fffffe,0x01fffffe,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x00e0001c,0x00e0001c,0x00f8007c,0x007e01f8,0x003ffff0,0x000fffc0,0x0003ff00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // D + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x01fffffe,0x01fffffe,0x01fffffe,0x01c0380e,0x01c0380e,0x01c0380e,0x01c0380e,0x01c0380e,0x01c0380e,0x01c0380e,0x01c0380e,0x01c0380e,0x01c0000e,0x01c00000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // E + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x01fffffe,0x01fffffe,0x01fffffe,0x0000380e,0x0000380e,0x0000380e,0x0000380e,0x0000380e,0x0000380e,0x0000380e,0x0000380e,0x0000380e,0x0000380e,0x0000000e,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // F + 0x00000000,0x00000000,0x00000000,0x00000000,0x0003fe00,0x000fffc0,0x003ffff0,0x007e01f8,0x00f8007c,0x00f0003c,0x01e0001e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01ffe01e,0x00ffe03c,0x00ffe008,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // G + 0x00000000,0x00000000,0x00000000,0x00000000,0x01fffffe,0x01fffffe,0x01fffffe,0x00003800,0x00003800,0x00003800,0x00003800,0x00003800,0x00003800,0x00003800,0x00003800,0x00003800,0x01fffffe,0x01fffffe,0x01fffffe,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // H + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01fffffe,0x01fffffe,0x01fffffe,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // I + 0x00000000,0x00000000,0x00000000,0x00000000,0x00400000,0x00f00000,0x00e00000,0x01e0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01e0000e,0x00f0000e,0x007ffffe,0x003ffffe,0x001ffffe,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // J + 0x00000000,0x00000000,0x00000000,0x00000000,0x01fffffe,0x01fffffe,0x01fffffe,0x00003800,0x00007c00,0x0000fe00,0x0001ff00,0x0003e780,0x0007c3e0,0x000f81f0,0x003f007c,0x007c003e,0x01f8001e,0x01f00006,0x01c00002,0x01000000,0x00000000,0x00000000,0x00000000,0x00000000, // K + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x01fffffe,0x01fffffe,0x01fffffe,0x01c00000,0x01c00000,0x01c00000,0x01c00000,0x01c00000,0x01c00000,0x01c00000,0x01c00000,0x01c00000,0x01c00000,0x01c00000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // L + 0x00000000,0x00000000,0x00000000,0x00000000,0x01ff8000,0x01fffff8,0x01fffffe,0x0000001e,0x000000fc,0x000007e0,0x00003f80,0x0000fc00,0x0000e000,0x0000fc00,0x00003f80,0x000007e0,0x000000fc,0x0000001e,0x01fffffe,0x01fffffc,0x01ffc000,0x00000000,0x00000000,0x00000000, // M + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x01fffffe,0x01fffffe,0x01fffffe,0x0000007e,0x000001fc,0x000007f0,0x00001fc0,0x00007e00,0x0003f800,0x000fe000,0x003f0000,0x01fc0000,0x01fffffe,0x01fffffe,0x01fffffe,0x00000000,0x00000000,0x00000000,0x00000000, // N + 0x00000000,0x00000000,0x00000000,0x0003ff00,0x001fffe0,0x003ffff0,0x007c00f8,0x00f0003c,0x00e0001c,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x01c0000e,0x00e0001c,0x00f0003c,0x007e01f8,0x003ffff0,0x001fffe0,0x0003ff00,0x00000000,0x00000000,0x00000000,0x00000000, // O + + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x01fffffe,0x01fffffe,0x01fffffe,0x0000e00e,0x0000e00e,0x0000e00e,0x0000e00e,0x0000e00e,0x0000e00e,0x0000701c,0x0000783c,0x00003ff8,0x00001ff0,0x00000fe0,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // P + 0x00000000,0x00000000,0x00000000,0x0003ff00,0x001fffe0,0x003ffff0,0x007c00f8,0x00f0003c,0x01e0001c,0x01c0000e,0x07c0000e,0x0fc0000e,0x1fc0000e,0x3dc0000e,0x39e0001c,0x38f0003c,0x707e01f8,0x703ffff0,0x301fffe0,0x0003ff00,0x00000000,0x00000000,0x00000000,0x00000000, // Q + 0x00000000,0x00000000,0x00000000,0x00000000,0x01fffffe,0x01fffffe,0x01fffffe,0x0000e00e,0x0000e00e,0x0000e00e,0x0000e00e,0x0001e00e,0x0003e00e,0x000ff01c,0x003f783c,0x00fc3ff8,0x01f01ff0,0x01c007e0,0x01000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // R + 0x00000000,0x00000000,0x00000000,0x00000000,0x00600000,0x00f003e0,0x00e007f8,0x01e00ffc,0x01c01e1c,0x01c01c1e,0x01c03c0e,0x01c0380e,0x01c0780e,0x01c0700e,0x01e0f00e,0x00f1e00e,0x00ffe01c,0x007fc00c,0x001f0000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // S + 0x00000000,0x00000000,0x00000000,0x00000000,0x0000000e,0x0000000e,0x0000000e,0x0000000e,0x0000000e,0x0000000e,0x01fffffe,0x01fffffe,0x01fffffe,0x0000000e,0x0000000e,0x0000000e,0x0000000e,0x0000000e,0x0000000e,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // T + 0x00000000,0x00000000,0x00000000,0x00000000,0x000ffffe,0x003ffffe,0x007ffffe,0x00f80000,0x01e00000,0x01c00000,0x01c00000,0x01c00000,0x01c00000,0x01c00000,0x01e00000,0x00f80000,0x007ffffe,0x003ffffe,0x000ffffe,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // U + 0x00000000,0x00000000,0x00000000,0x00000006,0x0000007e,0x000007fe,0x00007ff8,0x0003ff80,0x001ff800,0x01ff8000,0x01fc0000,0x01e00000,0x01fc0000,0x00ffc000,0x001ff800,0x0003ff80,0x00007ff8,0x000007fe,0x0000007e,0x00000006,0x00000000,0x00000000,0x00000000,0x00000000, // V + 0x00000000,0x00000000,0x00000000,0x00000000,0x007ffffe,0x01fffffe,0x01fffffe,0x01f80000,0x003f0000,0x0007e000,0x0000fc00,0x00001c00,0x0000fc00,0x0007e000,0x003f0000,0x01f80000,0x01fffffe,0x01fffffe,0x007ffffe,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // W + 0x00000000,0x00000000,0x00000000,0x01000002,0x01c0000e,0x01f0001e,0x01fc007e,0x007e01f8,0x001f87e0,0x0007cf80,0x0001ff00,0x0000fc00,0x0001ff00,0x0007cf80,0x001f87e0,0x007e01f8,0x01fc007e,0x01f0001e,0x01c0000e,0x01000002,0x00000000,0x00000000,0x00000000,0x00000000, // X + 0x00000000,0x00000000,0x00000000,0x00000002,0x0000001e,0x0000007e,0x000001fc,0x000007f0,0x00001fc0,0x00007f00,0x01fffc00,0x01fff000,0x01fffc00,0x00007e00,0x00001fc0,0x000007f0,0x000001fc,0x0000007e,0x0000001e,0x00000002,0x00000000,0x00000000,0x00000000,0x00000000, // Y + 0x00000000,0x00000000,0x00000000,0x00000000,0x01c00000,0x01f0000e,0x01fc000e,0x01ff000e,0x01df800e,0x01c7e00e,0x01c1f80e,0x01c07c0e,0x01c03f0e,0x01c00f8e,0x01c007ee,0x01c001fe,0x01c0007e,0x01c0003e,0x01c0000e,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // Z + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0xffffffff,0xffffffff,0xffffffff,0xc0000003,0xc0000003,0xc0000003,0xc0000003,0xc0000003,0xc0000003,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // [ + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000003,0x0000000f,0x0000007f,0x000007fe,0x00003ff0,0x0001ff80,0x000ff800,0x00ffc000,0x07fe0000,0x3ff00000,0xff000000,0xf8000000,0xc0000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0xc0000003,0xc0000003,0xc0000003,0xc0000003,0xc0000003,0xc0000003,0xffffffff,0xffffffff,0xffffffff,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ] + 0x00000000,0x00000000,0x00000000,0x00000000,0x00001000,0x00001c00,0x00003f00,0x00001fc0,0x000007f0,0x000001fc,0x0000003e,0x0000000e,0x0000003e,0x000001fc,0x000007f0,0x00001fc0,0x00003f00,0x00001c00,0x00001000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ^ + 0x00000000,0x00000000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x00000000,0x00000000,0x00000000, // _ + + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x000000f8,0x000001fc,0x0000038e,0x00000306,0x00000306,0x00000306,0x0000038e,0x000001fc,0x000000f8,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // ` + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x007c0000,0x00fe0000,0x01ff0e00,0x01c70f00,0x03878700,0x03838700,0x03838700,0x03838700,0x03838700,0x03838f00,0x03839e00,0x03fffe00,0x03fffc00,0x01fff800,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // a + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x01ffffff,0x03ffffff,0x03ffffff,0x03800e00,0x03800700,0x03800700,0x03800700,0x03800700,0x03c00700,0x01c00f00,0x01f03e00,0x00fffc00,0x007ff800,0x001fe000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // b + 0x00000000,0x00000000,0x00000000,0x00000000,0x000fc000,0x003ff000,0x00fffc00,0x00f03c00,0x01e01e00,0x01c00e00,0x03c00f00,0x03800700,0x03800700,0x03800700,0x03800700,0x03800700,0x03800700,0x03c00f00,0x01c00600,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // c + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x001fe000,0x007ff800,0x00fffc00,0x01f03e00,0x01c00f00,0x03c00700,0x03800700,0x03800700,0x03800700,0x03800700,0x03800e00,0x03ffffff,0x01ffffff,0x01ffffff,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // d + 0x00000000,0x00000000,0x00000000,0x00000000,0x000fc000,0x007ff800,0x00fffc00,0x01f3be00,0x01c38e00,0x03c38f00,0x03838700,0x03838700,0x03838700,0x03838700,0x03838f00,0x03839e00,0x01c3fc00,0x01c3f800,0x0003e000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // e + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000700,0x00000700,0x00000700,0x00000700,0x03fffff0,0x03fffffc,0x03fffffe,0x0000071e,0x0000070f,0x00000707,0x00000707,0x00000707,0x00000707,0x00000707,0x0000000e,0x0000000e,0x00000000,0x00000000,0x00000000,0x00000000, // f + 0x00000000,0x00000000,0x00000000,0x00000000,0x0007f000,0x381ffc00,0x783ffe00,0x70781f00,0x70f00700,0x70e00380,0x70e00380,0x70e00380,0x70e00380,0x78e00380,0x3c700380,0x3fffff80,0x1fffff00,0x07ffff00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // g + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x03ffffff,0x03ffffff,0x03ffffff,0x00000700,0x00000700,0x00000700,0x00000700,0x00000700,0x00000f00,0x00001e00,0x03fffe00,0x03fffc00,0x03fff000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // h + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000700,0x00000700,0x00000700,0x00000700,0x0000070c,0x007fff1e,0x01ffff1e,0x01ffff0c,0x03c00000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x01c00000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // i + 0x00000000,0x00000000,0x00000000,0x00000000,0x60000000,0x70000000,0xf0000700,0xe0000700,0xe0000700,0xe0000700,0xe0000700,0xe0000700,0xf000070c,0x7000071e,0x7fffff1e,0x3fffff0c,0x0fffff00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // j + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x03ffffff,0x03ffffff,0x03ffffff,0x00038000,0x0007c000,0x0007e000,0x000ef000,0x001c7800,0x00383c00,0x00f01e00,0x01f00f00,0x03c00700,0x03800300,0x03000100,0x02000000,0x00000000,0x00000000,0x00000000,0x00000000, // k + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000007,0x00000007,0x00000007,0x00000007,0x00000007,0x007fffff,0x01ffffff,0x01ffffff,0x03c00000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x01c00000,0x00000000,0x00000000,0x00000000,0x00000000, // l + 0x00000000,0x00000000,0x00000000,0x00000000,0x03fffe00,0x03ffff00,0x03ffff00,0x00000700,0x00000700,0x00000700,0x00000f00,0x0007fe00,0x0007fe00,0x0007fe00,0x00000700,0x00000700,0x00000f00,0x03ffff00,0x03fffe00,0x03fff800,0x00000000,0x00000000,0x00000000,0x00000000, // m + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x03fffe00,0x03fffe00,0x03ffff00,0x00000700,0x00000700,0x00000700,0x00000700,0x00000700,0x00000f00,0x00001e00,0x03fffe00,0x03fffc00,0x03fff000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // n + 0x00000000,0x00000000,0x00000000,0x00000000,0x000fe000,0x007ff800,0x00fffc00,0x01f03e00,0x01c00e00,0x03800700,0x03800700,0x03800700,0x03800700,0x03800700,0x01c00e00,0x01f03e00,0x00fffc00,0x007ff800,0x001fe000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // o + + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0xfffffe00,0xfffffe00,0xffffff00,0x01c00700,0x03800700,0x03800700,0x03800700,0x03800700,0x03800f00,0x01c00e00,0x01f03e00,0x00fffc00,0x007ff800,0x001fc000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // p + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x001fc000,0x007ff800,0x00fffc00,0x01f03e00,0x01c00e00,0x03800f00,0x03800700,0x03800700,0x03800700,0x03800700,0x01c00700,0xffffff00,0xfffffe00,0xfffffe00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // q + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x03fffe00,0x03fffe00,0x03fffe00,0x00000f00,0x00000700,0x00000700,0x00000700,0x00000700,0x00000700,0x00000700,0x00000700,0x00000f00,0x00000700,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // r + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x01c07800,0x01c0fc00,0x0381fe00,0x0381cf00,0x0383c700,0x03838700,0x03878700,0x03870700,0x03870700,0x03cf0700,0x01fe0f00,0x01fc0e00,0x00780000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // s + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000700,0x00000700,0x00000700,0x00000700,0x007ffff8,0x01fffff8,0x01fffff8,0x03c00700,0x03800700,0x03800700,0x03800700,0x03800700,0x03800700,0x03800700,0x01c00700,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // t + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x003fff00,0x00ffff00,0x01ffff00,0x01e00000,0x03c00000,0x03800000,0x03800000,0x03800000,0x03800000,0x03800000,0x03ffff00,0x01ffff00,0x01ffff00,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // u + 0x00000000,0x00000000,0x00000000,0x00000300,0x00001f00,0x0000ff00,0x0007fe00,0x001ff000,0x007f8000,0x03fc0000,0x03f00000,0x03c00000,0x03f00000,0x03fc0000,0x007f8000,0x001ff000,0x0003fe00,0x0000ff00,0x00001f00,0x00000300,0x00000000,0x00000000,0x00000000,0x00000000, // v + 0x00000000,0x00000000,0x00000000,0x00001f00,0x0007ff00,0x01ffff00,0x03ffc000,0x03c00000,0x01f00000,0x003e0000,0x0007e000,0x0000f000,0x0007e000,0x007e0000,0x03f00000,0x03e00000,0x03ffe000,0x00ffff00,0x0007ff00,0x00001f00,0x00000000,0x00000000,0x00000000,0x00000000, // w + 0x00000000,0x00000000,0x00000000,0x02000100,0x03800300,0x03c00700,0x03e01f00,0x00f83e00,0x007c7800,0x003ff000,0x000fe000,0x0007c000,0x000ff000,0x001ef800,0x007c3e00,0x01f01f00,0x03e00f00,0x03c00300,0x03000100,0x02000000,0x00000000,0x00000000,0x00000000,0x00000000, // x + 0x00000000,0x00000000,0x00000000,0x00000000,0xe0000300,0xe0001f00,0xe000ff00,0xe007fe00,0xe01ff000,0xf07f8000,0x7dfc0000,0x7ff00000,0x1fc00000,0x0ff80000,0x03ff0000,0x007fe000,0x001ffe00,0x0001ff00,0x00003f00,0x00000300,0x00000000,0x00000000,0x00000000,0x00000000, // y + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x03800700,0x03e00700,0x03f00700,0x03fc0700,0x03be0700,0x039f0700,0x0387c700,0x0383e700,0x0380f700,0x03807f00,0x03803f00,0x03801f00,0x03800700,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // z + 0x00000000,0x00000000,0x00000000,0x00000000,0x00018000,0x00018000,0x00018000,0x0003c000,0x1ffffff8,0x7ffe7ffc,0x7ffc3ffc,0xe0000004,0xc0000000,0xc0000000,0xc0000000,0xc0000000,0xc0000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // { + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0xffffffff,0xffffffff,0xffffffff,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, // | + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0xc0000003,0xc0000003,0xc0000003,0xc0000003,0xc0000003,0xe0000007,0x7ffc3ffe,0x7ffe7ffe,0x1ffffff8,0x0003c000,0x00018000,0x00018000,0x00018000,0x00000000,0x00000000,0x00000000, // } + 0x00000000,0x00000000,0x00000000,0x00000000,0x00000030,0x00000078,0x0000001c,0x0000000e,0x0000000e,0x0000000e,0x0000001e,0x0000001c,0x00000038,0x00000078,0x00000070,0x00000070,0x00000070,0x00000038,0x0000001e,0x0000000c,0x00000000,0x00000000,0x00000000,0x00000000 // ~ + }; + } +} diff --git a/src/devices/Oled/SSD1306/OLEDDisplay.java b/src/devices/Oled/SSD1306/OLEDDisplay.java new file mode 100644 index 0000000..d56c735 --- /dev/null +++ b/src/devices/Oled/SSD1306/OLEDDisplay.java @@ -0,0 +1,535 @@ +package devices.Oled.SSD1306; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.util.Arrays; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import jGPIO.I2C.I2C_BUS; +import jGPIO.I2C.I2C_Device; + +@BA.Events(values= { + "log(msg as string)" +}) +@BA.ShortName("SSD1306_OLED") + +//@SuppressWarnings("unused") +public class OLEDDisplay { + /** + * Used to specify the orientation of a display. + * This allows mounting the oled display upright or flipping it. + *

+ * All rotation values are for clockwise rotation. + *

+ */ + protected enum Rotation { + DEG_0, + DEG_90, + DEG_180, + DEG_270 + } + + + private int DISPLAY_WIDTH = 128; + private int DISPLAY_HEIGHT = 64; + private final int MAX_INDEX = (DISPLAY_HEIGHT / 8) * DISPLAY_WIDTH; + + private final byte SSD1306_SETCONTRAST = (byte) 0x81; + private final byte SSD1306_DISPLAYALLON_RESUME = (byte) 0xA4; + + @SuppressWarnings("unused") + private final byte SSD1306_DISPLAYALLON = (byte) 0xA5; + private final byte SSD1306_NORMALDISPLAY = (byte) 0xA6; + private final byte SSD1306_INVERTDISPLAY = (byte) 0xA7; + private final byte SSD1306_DISPLAYOFF = (byte) 0xAE; + private final byte SSD1306_DISPLAYON = (byte) 0xAF; + + private final byte SSD1306_SETDISPLAYOFFSET = (byte) 0xD3; + private final byte SSD1306_SETCOMPINS = (byte) 0xDA; + + private final byte SSD1306_SETVCOMDETECT = (byte) 0xDB; + + private final byte SSD1306_SETDISPLAYCLOCKDIV = (byte) 0xD5; + private final byte SSD1306_SETPRECHARGE = (byte) 0xD9; + + private final byte SSD1306_SETMULTIPLEX = (byte) 0xA8; + + @SuppressWarnings("unused") + private final byte SSD1306_SETLOWCOLUMN = (byte) 0x00; + @SuppressWarnings("unused") + private final byte SSD1306_SETHIGHCOLUMN = (byte) 0x10; + + private final byte SSD1306_SETSTARTLINE = (byte) 0x40; + + private final byte SSD1306_MEMORYMODE = (byte) 0x20; + private final byte SSD1306_COLUMNADDR = (byte) 0x21; + private final byte SSD1306_PAGEADDR = (byte) 0x22; + + @SuppressWarnings("unused") + private final byte SSD1306_COMSCANINC = (byte) 0xC0; + private final byte SSD1306_COMSCANDEC = (byte) 0xC8; + + private final byte SSD1306_SEGREMAP = (byte) 0xA0; + + private final byte SSD1306_CHARGEPUMP = (byte) 0x8D; + + @SuppressWarnings("unused") + private final byte SSD1306_EXTERNALVCC = (byte) 0x1; + @SuppressWarnings("unused") + private final byte SSD1306_SWITCHCAPVCC = (byte) 0x2; + + private final byte SSD1306_ACTIVATE_SCROLL = (byte) 0x2F; + private final byte SSD1306_DEACTIVATE_SCROLL = (byte) 0x2E; + private final byte SSD1306_SET_VERTICAL_SCROLL_AREA = (byte) 0xA3; + private final byte SSD1306_RIGHT_HORIZONTAL_SCROLL = (byte) 0x26; + private final byte SSD1306_LEFT_HORIZONTAL_SCROLL = (byte) 0x27; + private final byte SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = (byte) 0x29; + private final byte SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = (byte) 0x2A; + + private I2C_BUS bus; + private I2C_Device device; + private Rotation rotation; + + private byte[] imageBuffer; + + private BA ba; + private Object caller; + private String eventname; + private final Object Me = this; + private boolean need_log_event = false; + private boolean device_opened = false; + + + /** + * Initialize OLED Display + * @param caller : caller object + * @param eventname : event name + */ + public void Initialize(BA ba, Object caller, String eventname) { + this.ba = ba; + this.caller = caller; + this.eventname = eventname.trim(); + if (this.ba != null) { + if (this.caller != null) { + if (!this.eventname.isEmpty()) { + need_log_event = this.ba.subExists(this.eventname+"_log"); + } + } + } + + + //add shutdown hook that clears the display + //and closes the bus correctly when the software + //if terminated. + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + shutdown(); + } + }); + + + } + + + + /** + * Open + * @param busname : i2c bus name + * @param default_address : if true, will use default address 0x3C. If false, will use alternate address 0x3D + * @param Rotation_Mode : 0, 90, 180, or 270. Default at 0 + * @param Width : width in pixel, usually 128 + * @param Height : height in pixel, usually 64 + * @return true if success + */ + public boolean Open(String busname, boolean default_address, int Rotation_Mode, int Width, int Height) { + if (busname.isEmpty()) { + raise_log("Open failed, busname is empty"); + return false; + } + + if (Width < 1) { + raise_log("Open failed, Width must be positive number"); + return false; + } + + if (Height < 1) { + raise_log("Open failed, Height must be positive number"); + return false; + } + + this.DISPLAY_WIDTH = Width; + this.DISPLAY_HEIGHT = Height; + this.imageBuffer = new byte[(DISPLAY_WIDTH * DISPLAY_HEIGHT) / 8]; + + switch(Rotation_Mode){ + case 90 : + this.rotation = Rotation.DEG_90; + break; + case 180 : + this.rotation = Rotation.DEG_180; + break; + case 270 : + this.rotation = Rotation.DEG_270; + break; + default: + this.rotation = Rotation.DEG_0; + break; + } + + + + bus = new I2C_BUS(busname); + if (bus.Open_Bus()) { + int address = 0x3C; + if (!default_address) address = 0x3D; + + device = new I2C_Device(bus.I2C_Handle(), address); + if (device.OpenDevice_SLAVE()) { + device_opened = true; + + clear(); + init(); + + raise_log("OLEDDisplay opened"); + return true; + } else raise_log("OLEDDisplay failed to create I2C_Device at address "+Bit.ToHexString(address)); + } else raise_log("OLEDDisplay failed to open I2C_BUS at "+busname); + return false; + } + + /** + * Check if OLEDDisplay is opened + * @return true if opened + */ + public boolean getIsOpened() { + return device_opened; + } + + /** + * Sets if the display should be inverted + * @param invert Invert state + */ + public void invertDisplay(boolean invert) { + if (device_opened) { + if (invert) { + writeCommand(SSD1306_INVERTDISPLAY); + } else { + writeCommand(SSD1306_NORMALDISPLAY); + } + } + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, eventname+"_log", false, new Object[] {msg}); + } + + public synchronized void clear() { + if (imageBuffer!=null) Arrays.fill(imageBuffer, (byte) 0x00); + } + + public int getWidth() { + switch (rotation) { + case DEG_90: + case DEG_270: + return DISPLAY_HEIGHT; + case DEG_0: + case DEG_180: + default: + return DISPLAY_WIDTH; + } + } + + public int getHeight() { + switch (rotation) { + case DEG_90: + case DEG_270: + return DISPLAY_WIDTH; + case DEG_0: + case DEG_180: + default: + return DISPLAY_HEIGHT; + } + } + + private boolean writeCommand(byte command) { + if (device!=null) { + if (device.WriteBytes(0, command)>0) { + return true; + } + } + return false; + } + + private boolean writeCommand(int command) { + return writeCommand((byte)command); + } + + private void init() { + if (device_opened) { + writeCommand(SSD1306_DISPLAYOFF); // 0xAE + writeCommand(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 + writeCommand((byte) 0x80); // the suggested ratio 0x80 + writeCommand(SSD1306_SETMULTIPLEX); // 0xA8 + writeCommand((byte) 0x3F); + writeCommand(SSD1306_SETDISPLAYOFFSET); // 0xD3 + writeCommand((byte) 0x0); // no offset + writeCommand((byte) (SSD1306_SETSTARTLINE | 0x0)); // line #0 + writeCommand(SSD1306_CHARGEPUMP); // 0x8D + writeCommand((byte) 0x14); + writeCommand(SSD1306_MEMORYMODE); // 0x20 + writeCommand((byte) 0x00); // 0x0 act like ks0108 + writeCommand((byte) (SSD1306_SEGREMAP | 0x1)); + writeCommand(SSD1306_COMSCANDEC); + writeCommand(SSD1306_SETCOMPINS); // 0xDA + writeCommand((byte) 0x12); + writeCommand(SSD1306_SETCONTRAST); // 0x81 + writeCommand((byte) 0xCF); + writeCommand(SSD1306_SETPRECHARGE); // 0xd9 + writeCommand((byte) 0xF1); + writeCommand(SSD1306_SETVCOMDETECT); // 0xDB + writeCommand((byte) 0x40); + writeCommand(SSD1306_DISPLAYALLON_RESUME); // 0xA4 + writeCommand(SSD1306_NORMALDISPLAY); + + writeCommand(SSD1306_DEACTIVATE_SCROLL); + + writeCommand(SSD1306_DISPLAYON);//--turn on oled panel + } else raise_log("Init failed, device not opened"); + + } + + public synchronized void setPixel(int x, int y, boolean on) { + switch (rotation) { + default: + case DEG_0: + updateImageBuffer(x, y, on); + break; + case DEG_90: + updateImageBuffer(y, getWidth() - x - 1, on); + break; + case DEG_180: + updateImageBuffer(getWidth() - x - 1, getHeight() - y - 1, on); + break; + case DEG_270: + updateImageBuffer(getHeight() - y - 1, x, on); + break; + } + } + + private synchronized void updateImageBuffer(int x, int y, boolean on) { + if (imageBuffer==null) return; + + final int pos = x + (y / 8) * DISPLAY_WIDTH; + if (pos >= 0 && pos < MAX_INDEX) { + + if (on) { + this.imageBuffer[pos] |= (1 << (y & 0x07)); + } else { + this.imageBuffer[pos] &= ~(1 << (y & 0x07)); + } + } + } + + + protected synchronized void drawChar(char c, FontPack font, int x, int y, boolean on) { + font.drawChar(this, c, x, y, on); + } + + + /** + * Draw a String at specific location + * x and y define as top-left of the String + * @param font : Font used for String + * @param string : string value + * @param x : coordinate X, start at 1 + * @param y : coordinate Y, start at 1 + */ + public synchronized void drawString(FontPack font, String string, int x, int y) { + if (font==null) return; + + if (x<1) x = 1; + if (y<1) y = 1; + + int posX = x; + int posY = y; + + + + for (char c : string.toCharArray()) { + if (c == '\n') { + posY += font.getOuterHeight(); + posX = x; + } else { + if (posX >= 0 && posX + font.getWidth() < this.getWidth() + && posY >= 0 && posY + font.getHeight() < this.getHeight()) { + drawChar(c, font, posX, posY, true); + } + posX += font.getOuterWidth(); + } + } + } + +// /** + + + /** + * Draw string at centered location of Y + * Y is defined as top of the string + * @param font : Font used for drawStringCentered + * @param string : string value + * @param y : coordinate Y, start at 1 + */ + public synchronized void drawStringCentered(FontPack font, String string, int y) { + if (font==null) return; + if (y<1) y = 1; + + final int strSizeX = string.length() * font.getOuterWidth(); + final int x = (this.getWidth() - strSizeX) / 2; + drawString(font,string, x, y); + } + + /** + * Draw a Character + * @param font FontPack used + * @param c character to draw + * @param x top left coordinate + * @param y top left coordiante + * @param on if on, then pixel show + */ + public synchronized void drawCharacter(FontPack font, char c, int x, int y, boolean on) { + if (font==null) return; + if (x<1) return; + if (y<1) return; + if (x>this.getWidth()) return; + if (y>this.getHeight()) return; + drawChar(c, font, x, y, on); + } + + public synchronized void clearRect(int x, int y, int width, int height, boolean on) { + for (int posX = x; posX < x + width; ++posX) { + for (int posY = y; posY < y + height; ++posY) { + setPixel(posX, posY, on); + } + } + } + + // Gak guna + @BA.Hide + public void scrollHorizontally(boolean left, int start, int end) { + if (device_opened) { + writeCommand(left ? SSD1306_LEFT_HORIZONTAL_SCROLL : SSD1306_RIGHT_HORIZONTAL_SCROLL); + writeCommand(0); + writeCommand(start); + writeCommand(0); + writeCommand(end); + writeCommand(1); + writeCommand(0xFF); + writeCommand(SSD1306_ACTIVATE_SCROLL); + } + + } + + // gak guna + @BA.Hide + public void scrollDiagonally(boolean left, int start, int end) { + if (device_opened) { + writeCommand(SSD1306_SET_VERTICAL_SCROLL_AREA); + writeCommand(0); + writeCommand(this.getHeight()); + writeCommand(left ? SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL : + SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL); + writeCommand(0); + writeCommand(start); + writeCommand(0); + writeCommand(end); + writeCommand(1); + writeCommand(SSD1306_ACTIVATE_SCROLL); + } + + } + + + // gak guna + @BA.Hide + public void stopScroll() { + if (device_opened) { + writeCommand(SSD1306_DEACTIVATE_SCROLL); + } + + } + + /** + * draws the given image over the current image buffer. The image + * is automatically converted to a binary image (if it not already + * is). + *

+ * Note that the current buffer is not cleared before, so if you + * want the image to completely overwrite the current display + * content you need to call clear() before. + *

+ * + * @param image + * @param x + * @param y + */ + public synchronized void drawImage(BufferedImage image, int x, int y) { + BufferedImage tmpImage = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_BYTE_BINARY); + tmpImage.getGraphics().drawImage(image, x, y, null); + + int index = 0; + int pixelval; + final byte[] pixels = ((DataBufferByte) tmpImage.getRaster().getDataBuffer()).getData(); + for (int posY = 0; posY < DISPLAY_HEIGHT; posY++) { + for (int posX = 0; posX < DISPLAY_WIDTH / 8; posX++) { + for (int bit = 0; bit < 8; bit++) { + pixelval = (byte) ((pixels[index/8] >> (7 - bit)) & 0x01); + setPixel(posX * 8 + bit, posY, pixelval > 0); + index++; + } + } + } + } + + /** + * sends the current buffer to the display + */ + public synchronized void update() { + if (device_opened) { + writeCommand(SSD1306_COLUMNADDR); + writeCommand((byte) 0); // Column start address (0 = reset) + writeCommand((byte) (DISPLAY_WIDTH - 1)); // Column end address (127 = reset) + + writeCommand(SSD1306_PAGEADDR); + writeCommand((byte) 0); // Page start address (0 = reset) + writeCommand((byte) 7); // Page end address + + for (int i = 0; i < ((DISPLAY_WIDTH * DISPLAY_HEIGHT / 8) / 16); i++) { + // send a bunch of data in one xmission + + device.WriteBytes((byte) 0x40, imageBuffer, i * 16, 16); + } + } else raise_log("Unable to Update, device noot opened"); + + } + + private synchronized void shutdown() { + + + clear(); + update(); + + + if (device!=null) { + device.CloseDevice(); + device = null; + } + //now we close the bus + if (bus!=null) { + bus.Close_Bus(); + bus = null; + } + device_opened = false; + } +} diff --git a/src/devices/PCF8574.java b/src/devices/PCF8574.java new file mode 100644 index 0000000..78ee5cd --- /dev/null +++ b/src/devices/PCF8574.java @@ -0,0 +1,151 @@ +package devices; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import jGPIO.I2C.I2C_BUS; +import jGPIO.I2C.I2C_Device; + +@BA.ShortName("PCF8574") +@BA.Events(values= { + "log(msg as string)" +}) +public class PCF8574 { + + private BA bax; + private Object myobject; + private String event; + private boolean need_log_event = false; + + private I2C_BUS i2cbus; + private I2C_Device i2cdev; + + /** + * Initialize a PCF8574. Used in B4J only + * @param caller : caller object + * @param eventname : event name + */ + public void Initialize(BA ba, Object caller, String eventname) { + bax = ba; + myobject = caller; + event = eventname; + if (bax!=null) { + if (caller!=null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + } + } + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + } + + /** + * Open a Slave + * @param bus_name : bus name of I2c + * @param slave_address : slave address of this PCF + * @return true if succcess + */ + public boolean Open(String bus_name, int slave_address) { + if (!jGPIO.jGPIO.IsLinux) return false; + i2cbus = new I2C_BUS(bus_name); + if (i2cbus.Open_Bus()) { + i2cdev = new I2C_Device(i2cbus.I2C_Handle(), slave_address); + if (i2cdev.OpenDevice_SLAVE()) { + raise_log("PCF8574 Address="+slave_address+" is opened"); + return true; + } + } + raise_log("Failed to open PCF8574 Address="+slave_address); + return false; + } + + + /** + * Check associated Slave Address + * @return -1 if no address + */ + public int getSlaveAddress() { + if (i2cdev instanceof I2C_Device) + return i2cdev.SlaveAddress(); + else + return -1; + } + + /** + * Check if I2C to this PCF is opened + * @return true if opened + */ + public boolean getIsOpened() { + if (i2cdev instanceof I2C_Device) { + return i2cdev.IsOpened(); + } else return false; + + } + + /** + * Close PCF8574 + */ + public void Close() { + if (i2cdev instanceof I2C_Device) { + i2cdev.CloseDevice(); + i2cdev = null; + } + + if (i2cbus instanceof I2C_BUS) { + i2cbus.Close_Bus(); + i2cbus = null; + } + } + + /** + * Write a byte to i2C + * @param bb : byte to write + * @return true if success + */ + public synchronized boolean Write(byte bb) { + if (i2cbus instanceof I2C_BUS) { + if (i2cbus.IsOpened()) { + if (i2cdev instanceof I2C_Device) { + if (i2cdev.IsOpened()) { + if (i2cdev.WriteBytes(new byte[] {bb})>0) return true; + + } + } + } + } + + return false; + } + + /** + * Read from I2C. Result in int, but actually value will 0 - 255 (byte size) + * @return -1 if failed + */ + public synchronized int Read() { + if (i2cbus instanceof I2C_BUS) { + if (i2cbus.IsOpened()) { + if (i2cdev instanceof I2C_Device) { + if (i2cdev.IsOpened()) { + byte[] result = i2cdev.ReadBytes(1); + if (result!=null) { + return Bit.And(result[0], 0xFF); + } + + } + } + } + } + return -1; + } + + private void raise_log(String msg) { + if (need_log_event) { + bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg}); + } + } +} diff --git a/src/devices/PZEM004/PZEM004.java b/src/devices/PZEM004/PZEM004.java new file mode 100644 index 0000000..2e9e35d --- /dev/null +++ b/src/devices/PZEM004/PZEM004.java @@ -0,0 +1,243 @@ +package devices.PZEM004; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import jGPIO.SerialPort.SerialComm; +import jGPIO.SerialPort.jSerialPort_Event; + +@BA.ShortName("PZEM004_V3") +@BA.Events(values= { + "log(msg as string)", + "measurement(value as PZEM004_MeasurenmentData)", +}) + +@SuppressWarnings("unused") +public class PZEM004 implements jSerialPort_Event { + private BA bax; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_measurement_event = false; + + private SerialComm sercom; + private boolean inited = false; + + private ExecutorService executor = Executors.newSingleThreadExecutor(); + + + private int txcount = 0; + private int txfail = 0; + private int rxcount = 0; + private int rxfail = 0; + + private final byte[] address = {(byte)0xC0, (byte)0xA8, (byte)0x1, (byte)0x1, (byte)0}; + private final byte[] read_voltage_cmd = {(byte)0xB0, (byte)0x1A}; + + private final byte[] result_voltage_cmd = {(byte)0xA0, (byte) 0x88}; + private final byte[] read_current_cmd = {(byte)0xB1, (byte) 0x1B}; + private final byte[] result_current_cmd = {(byte) 0xA1, (byte) 0xD2}; + private final byte[] read_power_cmd = {(byte) 0xB2, (byte)0x1C}; + private final byte[] result_power_cmd = {(byte) 0xA2, (byte) 0x42}; + private final byte[] read_energy_cmd = {(byte) 0xB3, (byte) 0x1D}; + private final byte[] result_energy_cmd = {(byte) 0xA3, (byte) 0xC9}; + private final byte[] set_address_cmd = {(byte) 0xB4, (byte) 0x1E}; + + + + /** + * Initialize PZEM-004 class + * @param callerobject : callback object + * @param eventname : event name + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + bax = ba; + caller = callerobject; + event = eventname.trim(); + + if (bax!=null) { + if (caller!=null) { + if (!event.isEmpty()) { + if (bax.subExists(event+"_log")) need_log_event = true; + if (bax.subExists(event+"_measurement")) need_measurement_event = true; + + } + } + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + + inited = true; + } + + public void Close() { + if (executor!=null) { + executor.shutdown(); + } + executor = null; + if (sercom!=null) { + sercom.ClosePort(); + } + sercom = null; + } + + /** + * Check if class already initalized + * @return true if initialized + */ + public boolean IsInitialized() { + return inited; + } + + /** + * Set SerialComm for communication + * targetcom must be initialized and opened before + * @param targetcom : SerialComm object + * @return true if targetcom is initialized and opened + */ + public boolean SetSerialComm(SerialComm targetcom) { + if (targetcom!=null) { + if (targetcom.PortIsReady()) { + sercom = targetcom; + // untuk dapetin event log dan newdata + sercom.SetJavaEvent(this); + + byte[] cmd = make_command(set_address_cmd, address); + sercom.Write(cmd); + printbytes("Initial address : ",cmd); + return true; + } + } + return false; + } + + public void Read_Measurement() { + + executor.execute(new serthread()); + + + } + + private byte[] make_command(byte[] cmd, byte[] address) { + byte[] result = new byte[cmd.length+address.length]; + int length = result.length; + + result[0] = cmd[0]; + result[length-1] = cmd[1]; + for(int ii=0;ii0) { + if (idnumber<0xF9) { + slaveid = idnumber; + raise_log("ID set to "+slaveid); + } else raise_log("Invalid ID number "+idnumber+", back to default = 1"); + } else raise_log("Invalid ID number "+idnumber+", back to default = 1"); + + + inited = true; + + // auto close kalau java program closed + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + } + + /** + * Check if inited + * @return true if inited + */ + public boolean IsInitialized() { + return inited; + } + + public void Close() { + if (executor!=null) { + executor.shutdown(); + } + executor = null; + } + + /** + * Set SerialComm for communication + * targetcom must be initialized and opened before + * @param targetcom : SerialComm object + * @return true if targetcom is initialized and opened + */ + public boolean SetSerialComm(SerialComm targetcom) { + if (targetcom!=null) { + if (targetcom instanceof SerialComm) { + if (targetcom.PortIsReady()) { + sercom = targetcom; + // untuk dapetin event log dan newdata + sercom.SetJavaEvent(this); + return true; + } + } + } + raise_invalidserialcom(); + return false; + } + + /** + * Check if SerialComm assigned is ready + * @return true if ready + */ + public boolean SerialComm_Ready() { + if (sercom!=null) { + if (sercom instanceof SerialComm) { + return sercom.PortIsReady(); + } + } + return false; + } + + /** + * Reset Energy (KWH) counter. + * On success, will raise event resetenergy success = true + * on Failed, will raise event resetenergy success = false + */ + public void Reset_Energy() { + byte[] bb = {(byte) slaveid, 0x42,0,0}; + if (executor != null) + executor.execute(new serthread(bb)); + else { + raise_log("Reset_Energy failed, Executor is null"); + raise_resetenergy(false); + } + } + + public void Reset_Energy_SerialComm(SerialComm target ) { + SetSerialComm(target); + Reset_Energy(); + } + + /** + * Get Measurement + * On success, will raise event measurement + * value is PZEM004T_MeasurenmentData class + * On failed, will raise event measurement, value.isvalid = false + */ + public void Read_Measurement() { + byte[] bb = {(byte) slaveid, 4, 0, 0, 0, 10,0,0}; + if (executor != null) + executor.execute(new serthread(bb)); + else { + raise_log("Read_Measurement failed, Executor is null"); + raise_measurement(new PZEM004T_MeasurenmentData()); + } + } + + public void Read_Measurement_SerialComm(SerialComm target) { + SetSerialComm(target); + Read_Measurement(); + } + +// /** +// * Change Slave ID +// * On success, event addresschanged will raised +// * On failed, event addresschanged will raise, with value -1 +// * @param value : 01 - F7 +// */ +// public void Change_Slave_Address(int value) { +// //byte[] bb = {(byte) slaveid, 6, 0, 2, 0, (byte) value,0,0}; +// byte[] bb = {(byte) 0, 6, 0, 2, 0, (byte) value, 0, 0 }; // change address , pakai broadcast address (0) +// if (executor != null) +// executor.execute(new serthread(bb)); +// else { +// raise_log("Change_Slave_Address failed, Executor is null"); +// raise_addresschanged(-1); +// } +// } +// +// public void Change_Slave_Address_SerialComm(int value , SerialComm target) { +// SetSerialComm(target); +// Change_Slave_Address(value); +// } + + /** + * Change power alarm threshold + * On success will raise event alarmpower with value = value inserted in this function + * On failed, will raise event alarmpower with value = -1 + * @param value : value in Wh + */ + public void Set_Power_Alarm(int value) { + byte[] bb = {(byte) slaveid, 0x06, 0, 1, (byte)(value>>8), (byte) value,0,0}; + if (executor != null) + executor.execute(new serthread(bb)); + else { + raise_log("Set_Power_Alarm failed, Executor is null"); + this.raise_alarmpower(-1); + } + + } + + private int CalculateCRC(byte[] datanya, int length) { + int indextable; + int CRCresult = 0xFFFF; + int ii=0; + while(length>0) { + indextable = (datanya[ii] ^ CRCresult) & 0xFF; + ii++; + CRCresult = (CRCresult >> 8) & 0xFFFF; + CRCresult = (CRCresult ^ CRC_Table[indextable]) & 0xFFFF; + length--; + } + return CRCresult; + } + + + + private class serthread implements Runnable{ + private byte[] bb; + private int bblength=0; + serthread(byte[] datanya){ + bb = datanya; + + bblength = datanya.length; + int crc1 = CalculateCRC(datanya, bblength-2); + bb[bblength-2] = (byte) (crc1); + bb[bblength-1] = (byte) (crc1 >> 8); + + if (rxbuffer==null) rxbuffer = ByteBuffer.allocate(100); + rxbuffer.clear(); + } + @Override + public void run() { + if (SerialComm_Ready()) { + synchronized(rxbuffer) { + + if (sercom.Write(bb)>0) { + + // berhasil tulis + if (txcount=5) { // minimal 4 byte == 1 ID, 1 function code, 1 CRC H , 1 CRC L + if (bb[0]==slaveid) { + int cmd = bb[1] & 0xFF; + switch(cmd) { + case 4 : + // read measurement result + PZEM004T_MeasurenmentData value = new PZEM004T_MeasurenmentData(bb); + if (value!=null) { + raise_measurement(value); + } + if (rxcount< Integer.MAX_VALUE) rxcount++; else rxcount = 0; + + + break; + case 6 : + // slave parameter result + if (bb.length>=8) { + //int numbytes = bb[2]; // gak kepake + int reg_address = bb[3]; + int reg_value = Bit.ShiftLeft(bb[4], 8) | Bit.And(bb[5], 0xFF); + switch(reg_address) { + case 1 : + // power alarm threshold + raise_alarmpower(reg_value); + raise_log("Alarm Power Threshold changed to "+reg_value); + if (rxcount< Integer.MAX_VALUE) rxcount++; else rxcount = 0; + + break; + case 2 : + // slave address change + raise_addresschanged(reg_value); + slaveid = (byte) reg_value; + raise_log("Slave Address changed to "+reg_value); + if (rxcount< Integer.MAX_VALUE) rxcount++; else rxcount = 0; + + break; + default: + raise_log("Invalid Register Addres="+reg_address+", Value="+reg_value+", on command 6"); + } + } + break; + case 0x42: + // reset energy result + raise_resetenergy(true); + raise_log("Energy reset success"); + if (rxcount< Integer.MAX_VALUE) rxcount++; else rxcount = 0; + + break; + default: + if (cmd>0x80) { + // error command + cmd = cmd &0x7F; + int code = bb[2]; + raise_log("Command "+cmd+" error, code="+code); + } else { + // unsuported command + raise_log("Unsupported command="+cmd); + } + } + } + } + + } + } + } + + // event from SerialComm + @Override + @BA.Hide + public void Log(String msg) { + raise_log("SerialComm log: "+msg); + } + + + @Override + @BA.Hide + public void newdata(byte[] bb) { + if (bb!=null) { + if (rxbuffer instanceof ByteBuffer) { + rxbuffer.put(bb); + } + } + + } + + +} diff --git a/src/devices/PZEM004/PZEM004T_MeasurenmentData.java b/src/devices/PZEM004/PZEM004T_MeasurenmentData.java new file mode 100644 index 0000000..2325ac2 --- /dev/null +++ b/src/devices/PZEM004/PZEM004T_MeasurenmentData.java @@ -0,0 +1,161 @@ +package devices.PZEM004; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("PZEM004T_MeasurenmentData") +public class PZEM004T_MeasurenmentData { + public double voltage = 0; + public double current = 0; + public double power = 0; + public double energy = 0; + public double frequency = 0; + public double powerfactor = 0; + public boolean alarm_status = false; + private boolean isvalid =false; + + public PZEM004T_MeasurenmentData() { + isvalid = false; + } + + public PZEM004T_MeasurenmentData(byte[] bb){ + if (bb!=null) { + int bblength = bb.length; + int numbytes = bb[2]; + if (bblength == (numbytes+5)) { + // 1 slave id, 1 command, 1 number of bytes, 2 crc == 5 + ByteBuffer bx = ByteBuffer.allocate(numbytes); + bx.put(bb, 3, numbytes); + bx.rewind(); + bx.order(ByteOrder.BIG_ENDIAN); + ShortBuffer sx = bx.asShortBuffer(); + int volt = Bit.And(sx.get(), 0xFFFF); + int cur_L = Bit.And(sx.get(), 0xFFFF); + int cur_H = Bit.And(sx.get(), 0xFFFF); + int pow_L = Bit.And(sx.get(), 0xFFFF); + int pow_H = Bit.And(sx.get(), 0xFFFF); + int ene_L = Bit.And(sx.get(), 0xFFFF); + int ene_H = Bit.And(sx.get(), 0xFFFF); + int freq = Bit.And(sx.get(), 0xFFFF); + int pf = Bit.And(sx.get(), 0xFFFF); + int alm = Bit.And(sx.get(), 0xFFFF); + + voltage = volt / 10.0; + current = ((cur_H << 16) | (cur_L)) /1000.0; + power = ((pow_H << 16) | (pow_L)) / 10.0; + energy = ((ene_H << 16) | (ene_L)); + frequency = freq / 10.0; + powerfactor = pf / 100.0; + alarm_status = (alm == 0 ? false:true); + + isvalid = true; + } + } + + } + + /** + * Get Voltage in String + * @return N/A if not available + */ + public String getVoltageString() { + if (isvalid) { + return voltage+" V"; + } else return "N/A"; + } + + /** + * Get Current in String + * @return N/A if not available + */ + public String getCurrentString() { + if (isvalid) { + return current+" A"; + } else return "N/A"; + } + + /** + * Get Power in String + * @return N/A if not available + */ + public String getPowerString() { + if (isvalid) { + return power+" W"; + } else return "N/A"; + } + + /** + * Get Energy Consumption in String + * @return N/A if not available + */ + public String getEnergyString() { + if (isvalid) { + return energy+" Wh"; + } else return "N/A"; + } + + /** + * Get AC Frequency in String + * @return N/A if not available + */ + public String getFrequencyString() { + if (isvalid) { + return frequency+" Hz"; + } else return "N/A"; + } + + /** + * Get Power Factor in String + * @return N/A if not available + */ + public String getPowerFactorString() { + if (isvalid) { + return String.valueOf(powerfactor); + } else return "N/A"; + } + + /** + * Return Summary in String + */ + public String toString() { + StringBuilder str = new StringBuilder(); + str.append("V=").append(voltage).append(" V,"); + str.append("A=").append(current).append(" A,"); + str.append("P=").append(power).append(" W,"); + str.append("E=").append(energy).append(" Wh,"); + str.append("F=").append(frequency).append(" Hz,"); + str.append("pf=").append(powerfactor).append(","); + str.append("Alarm=").append(alarm_status); + + return str.toString(); + } + + /** + * Check if values is valid + * @return true if valid + */ + public boolean IsValidMeasurement() { + return isvalid; + } + + /** + * Create values in JSON Map + * @return Map value + */ + public Map MakeJsonMap() { + Map result = new Map(); + result.Initialize(); + result.Put("VoltageString", this.getVoltageString()); + result.Put("CurrentString", this.getCurrentString()); + result.Put("PowerString", this.getPowerString()); + result.Put("EnergyString", this.getEnergyString()); + result.Put("FrequencyString", this.getFrequencyString()); + result.Put("PowerFactorString", this.getPowerFactorString()); + return result; + } +} diff --git a/src/devices/PZEM004/PZEM004_MeasurementData.java b/src/devices/PZEM004/PZEM004_MeasurementData.java new file mode 100644 index 0000000..c2f2579 --- /dev/null +++ b/src/devices/PZEM004/PZEM004_MeasurementData.java @@ -0,0 +1,12 @@ +package devices.PZEM004; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("PZEM004_MeasurenmentData") +public class PZEM004_MeasurementData { + + public double voltage = 0; + public double current = 0; + public double power = 0; + public double energy = 0; +} diff --git a/src/devices/PublicIPChecker.java b/src/devices/PublicIPChecker.java new file mode 100644 index 0000000..2b68ac3 --- /dev/null +++ b/src/devices/PublicIPChecker.java @@ -0,0 +1,115 @@ +package devices; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Regex; + +@BA.ShortName("PublicIPChecker") +@BA.Events(values= { + "log(msg as string)", + "publicip(success as boolean, testserver as string, result as string)" +}) + + +// https://stackoverflow.com/questions/2939218/getting-the-external-ip-address-in-java + +public class PublicIPChecker { + + private final String[] servers = { + "http://checkip.amazonaws.com/", + "https://ipv4.icanhazip.com/", + "http://myexternalip.com/raw", + "http://ipecho.net/plain", + "http://bot.whatismyipaddress.com" + }; + + private final String regex_validip = "^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$"; + + private BA bax; + private Object caller; + private String event=""; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_publicip_event = false; + + /** + * Initialize Public IP Checker + * @param callerobject : callback object + * @param eventname : eventname + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + bax = ba; + caller = callerobject; + event = eventname; + if (bax instanceof BA) { + if (caller != null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_publicip_event = bax.subExists(event+"_publicip"); + } + } + } + } + + /** + * Get Public IP + * Will raise event publicip(success as boolean, testserver as string, result as string) + */ + public void GetPublicIP() { + Thread tx = new Thread(new Runnable() { + + @Override + public void run() { + boolean success = false; + String currentserver = ""; + String resultip = ""; + for(int ii=0;ii=1) { + int temp = bb[0] & 0xFF; + if (isON) { + temp &= 0x7F; // CH = 0 + } else { + temp |= 0x80; // CH = 1 + } + byte[] writeback = new byte[] {0, (byte)temp}; + int wrback_result = i2cdev.WriteBytes(writeback); + if (wrback_result==writeback.length) { + return true; + } + } + } + } + } + return false; + } + + /** + * Read Control bytes + * @return true if success + */ + public boolean Read_ControlByte() { + controlbyte = -1; + if (iscorrect) { + byte[] address = new byte[] {0x07}; + if (i2cdev.WriteBytes(address)==1) { + byte[] vv = i2cdev.ReadBytes(1); + if (vv!=null) { + if (vv.length>=1) { + controlbyte = (vv[0] & 0xFF); + return true; + } + } + } + } + return false; + } + + /** + * Get Square Wave Output status + * @return 0 = disabled, 1 = enabled, -1 = no data (call Read_ControlByte function) + */ + public int Get_SquareWaveOutput_status() { + if (controlbyte==-1) return -1; + if ((controlbyte & 0x40)==0) return 0; else return 1; + } + + + /** + * Set Square Wave Output + * @param isON : if true, SQW enabled + * @param freqcode : 0 (default) = 1 Hz, 1 = 4096 Hz, 2 = 8192 Hz, 3 = 32768 Hz + * @return true if success + */ + public boolean Set_SquareWaveOutput(boolean isON, int freqcode) { + if (iscorrect) { + if (controlbyte>=0) { + int temp = controlbyte; + if (isON) { + temp |= 0x10; // SQWE = 1 + temp &= 0xFC; // RS1 = 0, RS0 = 0 + switch(freqcode) { + case 1 : + // RS1 = 0, RS0 = 1 + temp |= 1; + break; + case 2 : + // RS1 = 1 , RS0 = 0 + temp |= 2; + break; + case 3 : + // RS1 = 1, RS0 = 1 + temp |= 3; + break; + } + } else { + temp &= 0xE0; // SQWE = 0, RS1 = 0, RS0 =0 + } + byte[] addr_data = new byte[] {0x07, (byte) temp}; + int write_result = i2cdev.WriteBytes(addr_data); + if (write_result==addr_data.length) { + controlbyte = temp; + return true; + } + } + } + return false; + } + + /** + * DS1307 has internal RAM, sized 56 bytes + * This function write to its RAM + * @param start_address : 0 - 55 + * @param bb : bytes data + * @return true if success + */ + public boolean Write_RAM(int start_address, byte[] bb) { + if (iscorrect) { + byte[] addr_data = new byte[1+bb.length]; + addr_data[0] = (byte) (0x08 + start_address); // RAM start at address 8 + for(int ii=0;ii=1) { + int result = bb[0] & 0x7F; + if ((bb[0] & 0x80)!=0) result = -result; + return result; + } + } + } + } + return -99; + } + + /** + * Check on Status Byte + * @return true if success reading + */ + public boolean Read_StatusByte() { + statusbyte = -1; + if (iscorrect) { + byte[] address = new byte[] {0x0E}; + if (i2cdev.WriteBytes(address)==1) { + byte[] vv = i2cdev.ReadBytes(1); + if (vv!=null) { + if (vv.length>=1) { + statusbyte = (vv[0] & 0xFF); + return true; + } + } + } + } + return false; + } + + /** + * Check if Oscillator is running or stopped + * @return 1 = running, 0 = stopped, -1 = no data (call Read_StatusByte function) + */ + public int Oscillator_is_Running() { + if (statusbyte==-1) return -1; + if ((statusbyte & 0x80)==0) + // running + return 1; + else + // stopped + return 0; + } + + /** + * Check if Osc 32Khz Output is enabled + * @return 1 = enabled, 0 = disabled, -1 = no data (call Read_StatusByte function) + */ + public int Osc32Khz_is_Running() { + if (statusbyte==-1) return -1; + if ((statusbyte & 0x08)==0) { + return 0; + } else { + return 1; + } + } + + + + /** + * Set Osc 32Khz Output is enabled or not + * @param isON : if true, then enabled + * @return true if success + */ + public boolean Set_Osc32Khz_Output(boolean isON) { + if (iscorrect) { + // i2c is opened + if (statusbyte!=-1) { + // statusbyte alread read before + int temp = statusbyte; + if (isON) { + temp |= 8; + } else { + temp &= 0xF7; + } + byte[] address_data = new byte[] {0x0F, (byte) temp}; + int writeresult = i2cdev.WriteBytes(address_data); + if (writeresult==address_data.length) { + statusbyte = temp; + return true; + } + } + } + + + return false; + } + + + /** + * Read Control bytes + * @return true if success + */ + public boolean Read_ControlByte() { + controlbyte = -1; + if (iscorrect) { + byte[] address = new byte[] {0x0F}; + if (i2cdev.WriteBytes(address)==1) { + byte[] vv = i2cdev.ReadBytes(1); + if (vv!=null) { + if (vv.length>=1) { + controlbyte = (vv[0] & 0xFF); + return true; + } + } + } + } + return false; + } + + /** + * Set internal oscillator ON or OFF + * @param isON : if true, then oscillator running + * @return true if success + */ + public boolean Update_ControlByte_OscillatorEnable(boolean isON) { + if (iscorrect) { + if (controlbyte>=0) { + int temp = controlbyte; + if (isON) { + temp &= 0x7F; // if 0, then oscillator on + } else { + temp |= 0x80; // if 1, then oscillator stopped + } + byte[] address_data = new byte[] {0x0F, (byte) temp}; + if (i2cdev.WriteBytes(address_data)==2) { + controlbyte = temp; + return true; + } + } + } + + return false; + } + + /** + * Set SquareWave output ON or OFF + * @param isON : set On + * @param freqcode : 0 = 1 Hz, 1 = 1024 Hz, 2 = 4096 Hz, 3 = 8192 Hz, default is 0 + * @return true if success + */ + public boolean Update_ControlByte_SquareWaveOutput(boolean isON, int freqcode) { + if (iscorrect) { + if (controlbyte>=0) { + int temp = controlbyte; + + if (isON) { + // bit 6 harus 1, bit 2 harus 0 + temp |= 0x40; // bit 6 harus 1 + temp &= 0xFB; // bit 2 harus 0 + temp &= 0xE7; // bit 4 = 0, bit 3 = 0 + switch(freqcode) { + + case 1: // 1024 Hz + // bit 4 = 0, bit 3 = 1 + temp |= 8; + break; + case 2: // 4096 Hz + // bit 4 = 1, bit 3 = 0 + temp |= 0x10; + break; + case 3: // 8192 Hz + // bit 4 = 1, bit 3 = 1 + temp |= 0x18; + break; + + } + } else { + // bit 6 harus 0, bit 2 harus 1 + temp &= 0xBF; // bit 6 harus 0 + temp |= 4; // bit 2 harus 1 + temp &= 0xE7; // bit 4 = 0, bit 3 = 0; + } + + byte[] address_data = new byte[] {0x0F, (byte) temp}; + if (i2cdev.WriteBytes(address_data)==2) { + controlbyte = temp; + return true; + } + } + } + return false; + } + + @Override + ///from I2C_BUS_Event and I2C_Device_Event + public void Log(String msg) { + raise_log(msg); + } + + private void raise_log(String msg) { + if (need_log_event) { + bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg}); + } + } +} diff --git a/src/devices/RTC_I2C/RTC_Data.java b/src/devices/RTC_I2C/RTC_Data.java new file mode 100644 index 0000000..ba807f4 --- /dev/null +++ b/src/devices/RTC_I2C/RTC_Data.java @@ -0,0 +1,467 @@ +package devices.RTC_I2C; + +import java.text.ParseException; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.DateTime; +import jGPIO.mycodes; + +@BA.ShortName("RTC_Data") +public class RTC_Data { + public int second; + public int minute; + public int hour; + public int date; + public int month; + public int year; + public int DayOfWeek; + + private boolean correctdata = false; + private RTC_Data_Event javaevent; + + @BA.Hide + public void SetJavaEvent(RTC_Data_Event event) { + javaevent = event; + } + + private void raise_log(String msg) { + if (javaevent!=null) + { + javaevent.Log(msg); + } + } + + /** + * Initialize RTC_Data values based on current System Clock + */ + public void Initialize_from_Now() { + StringBuilder str = new StringBuilder(); + str.append("RTC_Data Initialize_from_Now :").append(System.lineSeparator()); + long value = DateTime.getNow(); + str.append("Now value = ").append(value).append(System.lineSeparator()); + second = DateTime.GetSecond(value); + str.append("second=").append(second).append(", "); + minute = DateTime.GetMinute(value); + str.append("minute=").append(minute).append(", "); + hour = DateTime.GetHour(value); + str.append("hour=").append(hour).append(", "); + date = DateTime.GetDayOfMonth(value); + str.append("date=").append(date).append(", "); + month = DateTime.GetMonth(value); + str.append("month=").append(month).append(", "); + year = DateTime.GetYear(value); + str.append("year=").append(year).append(", "); + DayOfWeek = DateTime.GetDayOfWeek(value); + str.append("Day Of Week=").append(DayOfWeek).append(", "); + correctdata = true; + raise_log(str.toString()); + System.out.println(str.toString()); + } + + /** + * Get RTC_Data value in DateTime tick + * @return 0 if invalid + */ + public long GetDateTimeTick() { + if (correctdata) { + + String dateformat = DateTime.getDateFormat(); + String timeformat = DateTime.getTimeFormat(); + boolean need_change_date = false; + boolean need_change_time = false; + + if (dateformat!="MM/dd/yyyy") { + DateTime.setDateFormat("MM/dd/yyyy"); // set default date format + need_change_date = true; + + } + if (timeformat!="HH:mm:ss") { + DateTime.setTimeFormat("HH:mm:ss"); // set default time format + need_change_time = true; + + } + + long result; + try { + result = DateTime.DateTimeParse(getDate_in_String(), getTime_in_String()); + } catch (ParseException e) { + result = 0; + raise_log("GetDateTimeTick ParseException, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + } + + if (need_change_date) DateTime.setDateFormat(dateformat); + if (need_change_time) DateTime.setTimeFormat(timeformat); + + return result; + } + raise_log("GetDateTimeTick doesn't contain valid data"); + return 0; + } + + + public void Initialize_from_DS1307(byte[] bb) { + StringBuilder str = new StringBuilder(); + str.append("RTC_Data Initialize_from_DS1307 : ").append(System.lineSeparator()); + + correctdata = false; + if (bb==null) { + str.append("Failed, RTC bytes is null"); + raise_log(str.toString()); + return; + } + if (bb.length<7) { + str.append("Failed, RTC bytes must 7 bytes, current = "+bb.length+" bytes"); + raise_log(str.toString()); + return; + } + + str.append("RTC raw bytes : ").append(mycodes.printbytes(bb)).append(System.lineSeparator()); + + second = ((bb[0] & 0x70)>>4)*10 + (bb[0] & 0x0F); + if ((second<0) || (second>59)) { + str.append(" Failed, contain invalid second=").append(second); + raise_log(str.toString()); + return; + } else { + str.append("second=").append(second).append(", "); + } + + minute = ((bb[1] & 0x70)>>4)*10 + (bb[1] & 0x0F); + if ((minute<0)||(minute>59)) { + str.append(" Failed, contain invalid minute=").append(minute); + raise_log(str.toString()); + return; + } else { + str.append("minute=").append(minute).append(", "); + } + + //detect AM/PM + if ((bb[2] & 0x40)==0) { + // 24H + hour = ((bb[2] & 0x30)>>4)*10 + (bb[2] & 0x0F); + str.append("hour24=").append(hour); + } else { + // 12H, ada AM/PM + if ((bb[2] & 0x20)==0){ + // AM + hour = ((bb[2] & 0x10)>>4)*10 + (bb[2] & 0x0F); + str.append("hourAM=").append(hour); + if (hour==12) hour = 0; // 12AM = jam 00 + } else { + // PM + hour = ((bb[2] & 0x10)>>4)*10 + (bb[2] & 0x0F); + str.append("hourPM=").append(hour); + if (hour!=12) hour+=12; // 1PM = jam 13, tapi jam 12PM ya jam 12 + } + + } + if ((hour<0)|| (hour>23)) { + str.append(" Failed, contain invalid hour=").append(hour); + raise_log(str.toString()); + return; + } else { + str.append(" hour=").append(hour).append(", "); + } + + DayOfWeek = bb[3] & 0x7; + if ((DayOfWeek<1) || (DayOfWeek>7)) { + str.append(" Failed, contain invalid DayOfWeek=").append(DayOfWeek); + raise_log(str.toString()); + return; + } else { + str.append("DayOfWeek=").append(DayOfWeek).append(", "); + } + + date = ((bb[4] & 0x30)>>4) * 10 + (bb[4] & 0x0F); + if ((date<1) || (date>31)) { + str.append(" Failed, contain invalid Date=").append(date); + raise_log(str.toString()); + return; + } else { + str.append(" Date=").append(date).append(", "); + } + + month = ((bb[5] & 0x10)>>4) * 10 + (bb[5] & 0x0F); + if ((month<1)||(month>12)) { + str.append(" Failed, contain invalid Month=").append(month); + raise_log(str.toString()); + return; + } else { + str.append(" Month=").append(month).append(", "); + } + + year = ((bb[6] & 0xF0)>>4) * 10 + (bb[6] & 0x0F); + if ((year<0)||(year>99)) { + str.append(" Failed, contain invalid year=").append(year); + raise_log(str.toString()); + return; + } else { + str.append(" Year=").append(year).append(System.lineSeparator()); + } + year+=2000; + correctdata = true; + raise_log(str.toString()); + } + + @BA.Hide + /** + * Get RTC_Data values in DS1307 data bytes + * @return 1 byte Address(00h) + 7 bytes data + */ + public byte[] Get_DS1307_bytes() { + byte[] result = new byte[7+1]; // 7 bytes data + 1 address; + result[0] = 0; // address + + // temporary value + int XH, XL; + + // second + XH = second / 10; + XL = second % 10; + result[1] = (byte)(((XH << 4) | XL) & 0x7F); // Clock Halt(bit 7) =0, start oscillator + + // minute + XH = minute / 10; + XL = minute % 10; + result[2] = (byte)((XH << 4) | XL); + + // hour + XH = hour / 10; + XL = hour % 10; + result[3] = (byte)(((XH << 4) | XL) & 0x3F); // 24H mode (bit 6 = 0) + + // Day of Week + result[4] = (byte)(DayOfWeek); + + // Date + XH = date / 10; + XL = date % 10; + result[5] = (byte)((XH <<4) | XL); + + // month + XH = month / 10; + XL = month % 10; + result[6] = (byte)((XH << 4) | XL); + + // year + int yy = year - 2000; + XH = yy / 10; + XL = yy % 10; + result[7] = (byte)((XH <<4) | XL); + + return result; + } + + + /** + * Initialize RTC_Data based on DS3231 reading + * Check for IsCorrectData() for examining correct values + * @param bb : 7 bytes of DS3231 data + */ + public void Initialize_from_DS3231(byte[] bb) { + StringBuilder str = new StringBuilder(); + str.append("RTC_Data Initialize_from_DS3231 : ").append(System.lineSeparator()); + + correctdata = false; + if (bb==null) { + str.append("Failed, RTC bytes is null"); + raise_log(str.toString()); + return; + } + if (bb.length<7) { + str.append("Failed, RTC bytes must 7 bytes, current = "+bb.length+" bytes"); + raise_log(str.toString()); + return; + } + + str.append("RTC raw bytes : ").append(mycodes.printbytes(bb)).append(System.lineSeparator()); + + second = ((bb[0] & 0x70)>>4)*10 + (bb[0] & 0x0F); + if ((second<0) || (second>59)) { + str.append(" Failed, contain invalid second=").append(second); + raise_log(str.toString()); + return; + } else { + str.append("second=").append(second).append(", "); + } + + minute = ((bb[1] & 0x70)>>4)*10 + (bb[1] & 0x0F); + if ((minute<0)||(minute>59)) { + str.append(" Failed, contain invalid minute=").append(minute); + raise_log(str.toString()); + return; + } else { + str.append("minute=").append(minute).append(", "); + } + + //detect AM/PM + if ((bb[2] & 0x40)==0) { + // 24H + hour = ((bb[2] & 0x30)>>4)*10 + (bb[2] & 0x0F); + str.append("hour24=").append(hour); + } else { + // 12H, ada AM/PM + if ((bb[2] & 0x20)==0){ + // AM + hour = ((bb[2] & 0x10)>>4)*10 + (bb[2] & 0x0F); + str.append("hourAM=").append(hour); + if (hour==12) hour = 0; // 12AM = jam 00 + } else { + // PM + hour = ((bb[2] & 0x10)>>4)*10 + (bb[2] & 0x0F); + str.append("hourPM=").append(hour); + if (hour!=12) hour+=12; // 1PM = jam 13, tapi jam 12PM ya jam 12 + } + + } + if ((hour<0)|| (hour>23)) { + str.append(" Failed, contain invalid hour=").append(hour); + raise_log(str.toString()); + return; + } else { + str.append(" hour=").append(hour).append(", "); + } + + + DayOfWeek = bb[3] & 0x7; + if ((DayOfWeek<1) || (DayOfWeek>7)) { + str.append(" Failed, contain invalid DayOfWeek=").append(DayOfWeek); + raise_log(str.toString()); + return; + } else { + str.append("DayOfWeek=").append(DayOfWeek).append(", "); + } + + date = ((bb[4] & 0x30)>>4) * 10 + (bb[4] & 0x0F); + if ((date<1) || (date>31)) { + str.append(" Failed, contain invalid Date=").append(date); + raise_log(str.toString()); + return; + } else { + str.append(" Date=").append(date).append(", "); + } + + month = ((bb[5] & 0x10)>>4) * 10 + (bb[5] & 0x0F); + if ((month<1)||(month>12)) { + str.append(" Failed, contain invalid Month=").append(month); + raise_log(str.toString()); + return; + } else { + str.append(" Month=").append(month).append(", "); + } + + year = ((bb[6] & 0xF0)>>4) * 10 + (bb[6] & 0x0F); + if ((year<0)||(year>99)) { + str.append(" Failed, contain invalid year=").append(year); + raise_log(str.toString()); + return; + } else { + str.append(" Year=").append(year).append(System.lineSeparator()); + } + year+=2000; + correctdata = true; + raise_log(str.toString()); + } + + @BA.Hide + /** + * Get RTC_Data values in DS3231 data bytes + * @return 1 byte Address(00h) + 7 bytes data + */ + public byte[] Get_DS3231_bytes() { + byte[] result = new byte[7+1]; // 7 bytes data + 1 address; + result[0] = 0; // address + + // temporary value + int XH, XL; + + // second + XH = second / 10; + XL = second % 10; + result[1] = (byte)((XH << 4) | XL); + + // minute + XH = minute / 10; + XL = minute % 10; + result[2] = (byte)((XH << 4) | XL); + + // hour + XH = hour / 10; + XL = hour % 10; + result[3] = (byte)((XH << 4) | XL); + + // Day of Week + result[4] = (byte)(DayOfWeek); + + // Date + XH = date / 10; + XL = date % 10; + result[5] = (byte)((XH <<4) | XL); + + // month + XH = month / 10; + XL = month % 10; + result[6] = (byte)((XH << 4) | XL); + + // year + int yy = year - 2000; + XH = yy / 10; + XL = yy % 10; + result[7] = (byte)((XH <<4) | XL); + + return result; + } + + /** + * Get RTC_Data Date in String + * @return in format MM/dd/yyyy + */ + public String getDate_in_String() { + if (correctdata) { + StringBuilder datetext = new StringBuilder(); + if (month<10) datetext.append("0"); + datetext.append(month).append("/"); + if (date<10) datetext.append("0"); + datetext.append(date).append("/"); + datetext.append(year); + return datetext.toString(); + } + return ""; + } + + /** + * Get RTC_Data Time in String + * @return in format HH:mm:ss + */ + public String getTime_in_String() { + if (correctdata) { + StringBuilder timetext = new StringBuilder(); + if (hour<10) timetext.append("0"); + timetext.append(hour).append(":"); + if (minute<10) timetext.append("0"); + timetext.append(minute).append(":"); + if (second<10) timetext.append("0"); + timetext.append(second); + return timetext.toString(); + } + return ""; + } + + /** + * Get RTC_Data Date and Time in String + */ + public String toString() { + if (correctdata) { + return getDate_in_String()+" "+getTime_in_String(); + } + return ""; + } + + /** + * Check if Initialization success + * @return true if correct data found + */ + public boolean IsCorrectData() { + return correctdata; + } +} diff --git a/src/devices/RTC_I2C/RTC_Data_Event.java b/src/devices/RTC_I2C/RTC_Data_Event.java new file mode 100644 index 0000000..ca11d11 --- /dev/null +++ b/src/devices/RTC_I2C/RTC_Data_Event.java @@ -0,0 +1,5 @@ +package devices.RTC_I2C; + +public interface RTC_Data_Event { + void Log(String msg); +} diff --git a/src/devices/Relay.java b/src/devices/Relay.java new file mode 100644 index 0000000..e132ea6 --- /dev/null +++ b/src/devices/Relay.java @@ -0,0 +1,136 @@ +package devices; + +import anywheresoftware.b4a.BA; +import jGPIO.jGPIO; +import jGPIO.DigitalOutput.DigitalOutput; +import jGPIO.DigitalOutput.DigitalOutputEvent; + +@BA.ShortName("HardwareRelay") + +/** + * Relay Class + * use DigitalOutput class + * @author rdkartono + * + */ +public class Relay implements DigitalOutputEvent { + private DigitalOutput dox; + private int mypinnumber; + private BA ba; + private boolean initialized = false; + private boolean isON = false; + private boolean activelow = false; + + /** + * Initialize relay Object + * @param pin_number : pin number connected to relay + * @param activelow : if true, logic low will turn ON relay, logic high will turn OFF relay + * @return true if Initialize success + */ + public boolean Initialize(BA bax, int pin_number, boolean activelow) { + this.mypinnumber = pin_number; + this.ba = bax; + this.activelow = activelow; + initialized = false; + if (jGPIO.osname=="") jGPIO.detectOS(); + + if (jGPIO.IsLinux==false) { + return false; + } + + dox = new DigitalOutput(); + dox.SetJavaEvent(this); + dox.Initialize(this.ba, null, "digitalout", mypinnumber, false); + initialized = dox.getIsInitialized(); + if (initialized) { + Turn_OFF(); // turn off relay + } + + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + + return initialized; + } + + public void Close() { + if (dox instanceof DigitalOutput) { + dox.Release(); + dox = null; + } + + initialized = false; + } + + /** + * Check if Initialzied + * @return true if already initialized + */ + public boolean getIsInitialized() { + return initialized; + } + + /** + * Turn ON relay + * @return true if success + */ + public boolean Turn_ON() { + if (initialized) { + boolean result = false; + if (activelow) { + result= dox.SetLow(); + } else { + result= dox.SetHigh(); + } + if (result) isON = true; + return result; + + } + return false; + } + + /** + * Turn OFF relay + * @return true if success + */ + public boolean Turn_OFF() { + if (initialized) { + boolean result = false; + if (activelow) { + result= dox.SetHigh(); + } else { + result= dox.SetLow(); + } + if (result) isON = false; + return result; + } + return false; + } + + /** + * Get Current relay status; + * @return true if Relay ON + */ + public boolean Relay_is_ON() { + return isON; + } + + @Override + @BA.Hide + public void error(int pinnumber, String Msg) { + BA.Log("Relay error, pin="+pinnumber+", Msg = "+Msg); + } + + @Override + @BA.Hide + public void newoutstate(int pinnumber, boolean isOn) { + if (BA.debugMode) { + BA.Log("Relay New Out State, pin="+pinnumber+" Value = "+isOn); + } + + } +} diff --git a/src/devices/ShortPointer.java b/src/devices/ShortPointer.java new file mode 100644 index 0000000..59cf6ca --- /dev/null +++ b/src/devices/ShortPointer.java @@ -0,0 +1,24 @@ +package devices; + +import com.sun.jna.ptr.ShortByReference; + +import anywheresoftware.b4a.keywords.Bit; + +public class ShortPointer extends ShortByReference { + + public ShortPointer() { + super(); + } + + public int toInt() { + return super.getValue() & 0xFFFF; + } + + public String toHex() { + return Bit.ToHexString(toInt()); + } + + public static short ShortValue_FromInt(int value) { + return (short)(value & 0xFFFF); + } +} diff --git a/src/devices/WindowsBattery.java b/src/devices/WindowsBattery.java new file mode 100644 index 0000000..0154917 --- /dev/null +++ b/src/devices/WindowsBattery.java @@ -0,0 +1,71 @@ +package devices; + + +import anywheresoftware.b4a.BA; + +@BA.ShortName("WindowsBattery") +@BA.Events(values={ + "batterychanged (Level As Int, Plugged As Boolean)", + "log(msg As String)" + }) +public class WindowsBattery { + private BA ba; + private String event; + private Object me; + private boolean isInitialized = false; + private boolean need_log_event = false; + private boolean need_batterychanged_event = false; + + + public void Initialize(BA ba, String EventName) { + this.ba = ba; + this.event = EventName.toLowerCase(); + me = this; + check_events(); + isInitialized = true; + + } + + private void check_events() { + if (ba!=null) { + if (event!=null && event.length()>0) { + need_log_event = ba.subExists(event + "_log"); + need_batterychanged_event = ba.subExists(event + "_batterychanged"); + } + } + } + + public boolean IsInitialized() { + return isInitialized; + } + + public boolean CheckStatus() { + if (!isInitialized) { + return false; + } + Kernel32.SYSTEM_POWER_STATUS batteryStatus = new Kernel32.SYSTEM_POWER_STATUS(); + if (Kernel32.INSTANCE.GetSystemPowerStatus(batteryStatus) != 0) { + raise_log(batteryStatus.toString()); + raise_batterychanged(batteryStatus.BatteryLifePercent, batteryStatus.ACLineStatus == 1); + return true; + } + return false; + + } + + private void raise_log(String msg) { + if (need_log_event) { + if (msg!=null && msg.length()>0) { + ba.raiseEventFromDifferentThread(msg, null, 0, event+"_log", false, new Object[] {msg}); + } + } + } + + private void raise_batterychanged(int level, boolean plugged) { + if (need_batterychanged_event) { + ba.raiseEventFromDifferentThread(me, null, 0, event + "_batterychanged", true, + new Object[] { level, plugged }); + } + } +} + diff --git a/src/devices/ups/ica/sin2100c/MegatecProtocol.java b/src/devices/ups/ica/sin2100c/MegatecProtocol.java new file mode 100644 index 0000000..5f5b901 --- /dev/null +++ b/src/devices/ups/ica/sin2100c/MegatecProtocol.java @@ -0,0 +1,460 @@ +package devices.ups.ica.sin2100c; + +import com.fazecast.jSerialComm.SerialPort; + +import com.fazecast.jSerialComm.SerialPortEvent; +import com.fazecast.jSerialComm.SerialPortMessageListenerWithExceptions; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("UPSMegatecProtocol") +@BA.Events(values= { + "log(msg as string)", + "newdata(datanya as Megatec_Data)", + "failedoperation(operationname as string)" + +}) + + +// Source : https://networkupstools.org/protocols/megatec.html +public class MegatecProtocol implements Megatec_Data_Event { + + private BA bax; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_newdata_event = false; + private boolean need_failedoperation_event = false; + + private boolean _inited = false; + private int _txrxfail = 0; + private int _txrxok = 0; + + private SerialPort _serialport; + private Megatec_Data _data = new Megatec_Data(); + + /** + * Initialize UPS Megatec Protocol + * @param callerobject : caller object + * @param eventname : eventname + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + bax = ba; + caller = callerobject; + event = eventname; + if (bax instanceof BA) { + if (caller != null) { + if (event instanceof String) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_newdata_event = bax.subExists(event+"_newdata"); + need_failedoperation_event = bax.subExists(event+"_failedoperation"); + _inited = true; + _data.SetJavaEvent(this); + } + } + } + } + } + + /** + * Check if UPSMegatecProtocol is initalized or not + * @return true if inited + */ + public boolean IsInitialized() { + return _inited; + } + + /** + * Get Serial Port used for communication + * @return empty string if none used + */ + public String getSerialPortUsed() { + return (_serialport instanceof SerialPort ? _serialport.getSystemPortName() : ""); + } + + /** + * Open SerialPort for UPS Data + * @param portname : SerialPort name to use + * @return true if SerialPort is found and can be opened + */ + public boolean Open(String portname) { + + SerialPort[] PortList = SerialPort.getCommPorts(); + if (PortList != null) { + if (PortList.length>0) { + for(SerialPort xx : PortList) { + if (xx.getSystemPortName().equalsIgnoreCase(portname)) { + // sampai sini ketemu + + if (xx.openPort()) { + if (xx.setBaudRate(2400)) { + if (xx.setNumDataBits(8)) { + if (xx.setParity(SerialPort.NO_PARITY)) { + if (xx.setNumStopBits(SerialPort.ONE_STOP_BIT)) { + if (xx.addDataListener(listener)) { + + _serialport = xx; + + return true; + } + } else raise_log("Unable to set StopBit 1 to "+portname); + } else raise_log("Unable to set NO_PARITY to "+portname); + } else raise_log("Unable to set 8Bit to "+portname); + } else raise_log("Unable to set baudrate 2400 to "+portname); + } else raise_log("Unable to open SerialPort "+portname); + } + } + } else raise_log("Start_Scanning can't found SerialPort"); + } else raise_log("Start_Scanning GetCommPorts return null"); + return false; + } + + /** + * Close SerialPort + */ + public void Close() { + if (_serialport instanceof SerialPort) { + _serialport.removeDataListener(); + _serialport.closePort(); + } + _serialport = null; + } + + /** + * Get Measurement using command Q1 + * If there are new data, will raise event newdata(datanya as Megatec_Data) + */ + public void Get_Measurement() { + if (_serialport instanceof SerialPort) { + byte[] cmd = new byte[3]; + cmd[0] = 'Q'; + cmd[1] = '1'; + cmd[2] = 13; + Thread tx = new Thread(new SerialSendExpectReply("Get_Measurement",cmd)); + tx.start(); + } else { + _txrxfail+=1; + } + } + + /** + * Get Measurement using command I + * If there are new data, will raise event newdata(datanya as Megatec_Data) + */ + public void Get_Information() { + if (_serialport instanceof SerialPort) { + byte[] cmd = new byte[2]; + cmd[0] = 'I'; + cmd[1] = 13; + Thread tx = new Thread(new SerialSendExpectReply("Get_Information",cmd)); + tx.start(); + } else { + _txrxfail+=1; + } + } + + /** + * Get UPS Rating using command F + * If there are new data, will raise event newdata(datanya as Megatec_Data) + */ + public void Get_Rating() { + if (_serialport instanceof SerialPort) { + byte[] cmd = new byte[2]; + cmd[0] = 'F'; + cmd[1] = 13; + Thread tx = new Thread(new SerialSendExpectReply("Get_Rating",cmd)); + tx.start(); + } else { + _txrxfail+=1; + } + } + + /** + * Toggle UPS Beep using command Q + */ + public void Toggle_Beep() { + if (_serialport instanceof SerialPort) { + synchronized(_serialport) { + byte[] cmd = new byte[2]; + cmd[0] = 'Q'; + cmd[1] = 13; + if (_serialport.writeBytes(cmd, cmd.length) == cmd.length) { // dont need reply + _txrxok+=1; + return; + } + } + } + _txrxfail+=1; + raise_failedoperation("Toggle_Beep"); + } + + /** + * Cancel Output Shutdown using command C + */ + public void Cancel_Shutdown() { + if (_serialport instanceof SerialPort) { + synchronized(_serialport) { + byte[] cmd = new byte[2]; + cmd[0] = 'C'; + cmd[1] = 13; + if (_serialport.writeBytes(cmd, cmd.length) == cmd.length) { // dont need reply + _txrxok+=1; + return; + }; + } + } + _txrxfail+=1; + raise_failedoperation("Cancel_Shutdown"); + } + + /** + * Shutdown Output after delay in minutes, using command S(delay) + * delay value can vary 0.x minute up to 10 minutes + * @param delay : in minutes + */ + public void Shutdown_Output(float delay) { + if (_serialport instanceof SerialPort) { + synchronized(_serialport) { + if (delay<=0) delay = 0.1f; // minimal 0.1 minute + if (delay>10) delay = 10f; + + byte[] cmd = new byte[4]; + cmd[0] = 'S'; + cmd[3] = 13; + + if (delay<1f) { + cmd[1] = '.'; + cmd[2] = (byte)((int)(delay*10)+0x30); + } else if (delay<10f) { + cmd[1] = '0'; + cmd[2] = (byte)((int)delay+0x30); + } else { + cmd[1] = '1'; + cmd[2] = '0'; + } + + if (_serialport.writeBytes(cmd, cmd.length)==cmd.length) { // dont need reply + _txrxok+=1; + return; + }; + } + + } + + _txrxfail+=1; + raise_failedoperation("Shutdown_Output"); + } + + /** + * Get TX RX OK Counter + * @return number of succesfull transmission + */ + public int getTXRX_OK() { + return _txrxok; + } + + /** + * Get TX RX FAIL counter + * @return number of failed transmission + */ + public int getTXRX_FAIL() { + return _txrxfail; + } + + private class SerialSendExpectReply implements Runnable{ + private final byte[] datanya; + private final boolean validdata; + private final String _operationname; + public SerialSendExpectReply(String operationname, byte[] bytetosend) { + datanya = bytetosend; + _operationname = operationname; + if (datanya!=null) { + if (datanya.length>0) { + if (_serialport instanceof SerialPort) { + if (_serialport.isOpen()) { + validdata = true; + return; + } + } + } + } + validdata = false; + } + + @Override + public void run() { + if (validdata) { + synchronized(_serialport) { + + if (_serialport.writeBytes(datanya, datanya.length) == datanya.length) { + try { + _serialport.wait(5000); + _txrxok += 1; + return; + } catch (InterruptedException e) { + raise_log("Reply for "+_operationname+" was not coming in 5 second interval"); + } + } + + } + } + + _txrxfail+=1; + raise_failedoperation(_operationname); + } + + } + + + SerialPortMessageListenerWithExceptions listener = new SerialPortMessageListenerWithExceptions() { + + @Override + public byte[] getMessageDelimiter() { + byte[] limiter = new byte[1]; + limiter[0] = 13; // CR + return limiter; + } + + @Override + public boolean delimiterIndicatesEndOfMessage() { + return true; // chr 13 (CR) + } + + @Override + public int getListeningEvents() { + int event = SerialPort.LISTENING_EVENT_DATA_RECEIVED; + return event; + } + + @Override + public void serialEvent(SerialPortEvent event) { + if (_serialport instanceof SerialPort) { + synchronized(_serialport) { + _serialport.notifyAll(); // notify , supaya yang wait bisa selesai + } + } + + // sampe sini dapat data + if (event instanceof SerialPortEvent) { + byte[] bb = event.getReceivedData(); + + if (bb!=null) { + if (bb.length>0) { + int bblen = bb.length; + + //TODO : hapus aja ini , buat ngecek doang + String rawstring = new String(bb); + BA.Log("RawString length="+rawstring.length()+" content = "+rawstring); + + if (bb[0]==40) { // kurung buka '(' = 40 + if (bb[bblen-1]==13) { // CR = 13 + String result = new String(bb,1,bblen-2); + BA.Log("Result = "+result); + process_Q1_Result(result); + return; + } + } + + if (bb[0]==35) { // pagar # = 35 + if (bb[bblen-1]==13) { // CR = 13 + String result = new String(bb,1,bblen-2); + BA.Log("Result = "+result); + process_I_F_Result(result); + return; + } + } + } + } + } + + } + + @Override + public void catchException(Exception e) { + raise_log("SerialPort Exception : "+e.getMessage()); + } + }; + + private void process_Q1_Result(String cmd) { + String[] fields = cmd.split(" "); + if (fields != null) { + if (fields.length==8) { + // ada 8 fields + _data.setInputVoltage(Float.valueOf(fields[0])); + _data.setInputFaultVoltage(Float.valueOf(fields[1])); + _data.setOutputVoltage(Float.valueOf(fields[2])); + _data.setOutputCurrentPercentage(Float.valueOf(fields[3])); + _data.setInputFrequency(Float.valueOf(fields[4])); + _data.setBatteryVoltage(Float.valueOf(fields[5])); + _data.setTemperature(Float.valueOf(fields[6])); + + if (fields[7].length()==8) { + int binaryvalue = 0; + if (fields[7].charAt(0)=='1') binaryvalue |= (1<<7); + if (fields[7].charAt(1)=='1') binaryvalue |= (1<<6); + if (fields[7].charAt(2)=='1') binaryvalue |= (1<<5); + if (fields[7].charAt(3)=='1') binaryvalue |= (1<<4); + if (fields[7].charAt(4)=='1') binaryvalue |= (1<<3); + if (fields[7].charAt(5)=='1') binaryvalue |= (1<<2); + if (fields[7].charAt(6)=='1') binaryvalue |= (1<<1); + if (fields[7].charAt(7)=='1') binaryvalue |= (1<<0); + _data.setBinaryStatus(binaryvalue); + } + } + } + } + + private void process_I_F_Result(String cmd) { + String[] fields = cmd.split(" "); + if (fields!=null) { + if (fields.length==3) { + // kemungkinan command I + _data.setCompanyName(fields[0]); + _data.setUPSModel(fields[1]); + _data.setVersion(fields[2]); + + } else if (fields.length==4) { + // kemungkinan command F + _data.setRatingBatteryVoltage(Float.valueOf(fields[0])); + _data.setRatingCurrent(Float.valueOf(fields[1])); + _data.setRatingBatteryVoltage(Float.valueOf(fields[2])); + _data.setRatingFrequency(Float.valueOf(fields[3])); + } + } + } + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_newdata(Megatec_Data datanya) { + if (need_newdata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newdata", false, new Object[] {datanya}); + } + + private void raise_failedoperation(String operationname) { + if (need_failedoperation_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_failedoperation", false, new Object[] {operationname}); + } + + //////////////// Event from Megatec_Data_Event //////////////////// + @Override + @BA.Hide + public void newdatastring(String field, String olddata, String newdata) { + if (_data instanceof Megatec_Data) { + raise_newdata(_data); + } + raise_log("Field "+field+", Old Data="+olddata+", New Data="+newdata); + } + + @Override + @BA.Hide + public void newdatafloat(String field, float olddata, float newdata) { + if (_data instanceof Megatec_Data) { + raise_newdata(_data); + } + raise_log("Field "+field+", Old Data="+olddata+", New Data="+newdata); + } + + /////////////////////////////////////////////////////////////////////// +} diff --git a/src/devices/ups/ica/sin2100c/Megatec_Data.java b/src/devices/ups/ica/sin2100c/Megatec_Data.java new file mode 100644 index 0000000..8ee80d5 --- /dev/null +++ b/src/devices/ups/ica/sin2100c/Megatec_Data.java @@ -0,0 +1,440 @@ +package devices.ups.ica.sin2100c; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("Megatec_Data") +public class Megatec_Data { + // command I + private String _companyname=""; + private String _upsmodel=""; + private String _version=""; + + // command F + private float _ratingvoltage = 0f; + private float _ratingcurrent = 0f; + private float _ratingbatteryvoltage = 0f; + private float _ratingfrequency =0f; + + // command Q1 + private float _inputvoltage = 0f; + private float _inputfaultvoltage = 0f; + private float _outputvoltage = 0f; + private float _outputcurrentpercentage = 0f; + private float _inputfrequency = 0f; + private float _batteryvoltage = 0f; + private float _temperature = 0f; + private int _binarystatus = 0; + private Megatec_Data_Event javaevent = null; + + @BA.Hide + public void SetJavaEvent(Megatec_Data_Event value) { + javaevent = value; + } + + /** + * Will return Map , with following keys : + * battery.charge: + * battery.voltage: + * ups.mfr: + * ups.model: + * ups.status: + * ups.temperature: + * ups.firmware: + * ups.load: + * driver.name: + * input.current.nominal: + * input.frequency: + * input.voltage: + * output.voltage: + * + * @return Map object + */ + public Map MakeMap() { + Map result = new Map(); + float battcharge = (_batteryvoltage / _ratingbatteryvoltage)*100; + result.Put("battery.charge:", battcharge); + result.Put("battery.voltage:", _batteryvoltage); + result.Put("ups.mfr:", _companyname); + result.Put("ups.model:", _upsmodel); + //TODO: kerjain ini + result.Put("ups.status:", "XXX"); + result.Put("ups.temperature:", _temperature); + result.Put("ups.firmware:", _version); + result.Put("ups.load:", _outputcurrentpercentage); + result.Put("driver.name:", "Megatec"); + result.Put("input.current.nominal", _ratingcurrent); + result.Put("input.frequency:", _inputfrequency); + result.Put("input.voltage:", _inputvoltage); + result.Put("output.voltage:", _outputvoltage); + return result; + } + + /** + * Get Manufacturer Name + * @return value in String + */ + public String getCompanyName() { + return _companyname; + } + + @BA.Hide + public void setCompanyName(String value) { + if (value instanceof String) { + if (!value.isEmpty()) { + if (!value.equals(_companyname)) { + raise_string_event("CompanyName",_companyname,value); + } + _companyname = value; + return; + } + } + _companyname = ""; + } + + /** + * Get UPS Model + * @return value in String + */ + public String getUPSModel() { + return _upsmodel; + } + + @BA.Hide + public void setUPSModel(String value) { + if (value instanceof String) { + if (!value.isEmpty()) { + if (!value.equals(_upsmodel)) { + raise_string_event("UPSModel",_upsmodel,value); + } + _upsmodel = value; + return; + } + } + _upsmodel = ""; + } + + /** + * Get Version of UPS + * @return version in String + */ + public String getVersion() { + return _version; + } + + @BA.Hide + public void setVersion(String value) { + if (value instanceof String) { + if (!value.isEmpty()) { + if (!value.equals(_version)) { + raise_string_event("Version",_version,value); + } + _version = value; + return; + } + } + _version = ""; + } + + /** + * Get Rating Voltage + * @return value in Volt AC + */ + public float getRatingVoltage() { + return _ratingvoltage; + } + + @BA.Hide + public void setRatingVoltage(float value) { + if (value<0f) value = 0f; + if (value>999.9f) value = 999.9f; + if (value != _ratingvoltage) { + raise_float_event("RatingVoltage", _ratingvoltage, value); + } + _ratingvoltage = value; + } + + /** + * Get Output Rating Current + * @return value in Amp + */ + public float getRatingCurrent() { + return _ratingcurrent; + } + + @BA.Hide + public void setRatingCurrent(float value) { + if (value<0f) value = 0f; + if (value>999f) value = 999f; + if (value != _ratingcurrent) { + raise_float_event("RatingCurrent", _ratingcurrent, value); + } + _ratingcurrent= value; + } + + /** + * Get Rating Battery Voltage + * @return value in Volt DC + */ + public float getRatingBatteryVoltage() { + return _ratingbatteryvoltage; + } + + @BA.Hide + public void setRatingBatteryVoltage(float value) { + if (value<0f) value = 0f; + if (value>999.9f) value = 999.9f; + if (value != _ratingbatteryvoltage) { + raise_float_event("RatingBatteryVoltage", _ratingbatteryvoltage, value); + } + _ratingbatteryvoltage = value; + } + + /** + * Get Rating Frequency + * @return value in Hz + */ + public float getRatingFrequency() { + return _ratingfrequency; + } + + @BA.Hide + public void setRatingFrequency(float value) { + if (value<0f) value = 0f; + if (value>99.9f) value = 99.9f; + if (value != _ratingfrequency) { + raise_float_event("RatingFrequency", _ratingfrequency, value); + } + _ratingfrequency = value; + } + + /** + * Get Input Voltage + * @return value in Volt AC + */ + public float getInputVoltage() { + return _inputvoltage; + } + + @BA.Hide + public void setInputVoltage(float value) { + if (value<0f) value = 0f; + if (value>999.9f) value = 999.9f; + if (value != _inputvoltage) { + raise_float_event("InputVoltage", _inputvoltage, value); + } + _inputvoltage = value; + } + + /** + * Get Threshold of Input Voltage, where this condition is considered to be Utility Fault + * @return value in Volt AC + */ + public float getInputFaultVoltage() { + return _inputfaultvoltage; + } + + @BA.Hide + public void setInputFaultVoltage(float value) { + if (value<0f) value = 0f; + if (value>999.9f) value = 999.9f; + + if (value != _inputfaultvoltage) { + raise_float_event("InputFaultVoltage", _inputfaultvoltage, value); + } + _inputfaultvoltage = value; + } + + /** + * Get Output Voltage + * @return value in Volt AC + */ + public float getOutputVoltage() { + return _outputvoltage; + } + + @BA.Hide + public void setOutputVoltage(float value) { + if (value<0f) value = 0f; + if (value>999.9f) value = 999.9f; + + if (value != _outputvoltage) { + raise_float_event("OutputVoltage", _outputvoltage, value); + } + _outputvoltage = value; + } + + /** + * Get Output Current load in percent of rating current + * @return value in percent + */ + public float getOutputCurrentPercentage() { + return _outputcurrentpercentage; + } + + @BA.Hide + public void setOutputCurrentPercentage(float value) { + if (value<0f) value = 0f; + if (value>100f) value = 100f; + if (value != _outputcurrentpercentage) { + raise_float_event("OutputCurrentPercentage", _outputcurrentpercentage, value); + } + _outputcurrentpercentage = value; + } + + /** + * Get Utility Frequency + * @return value in Hz + */ + public float getInputFrequency() { + return _inputfrequency; + } + + @BA.Hide + public void setInputFrequency(float value) { + if (value<0f) value = 0f; + if (value>99.9f) value = 99.9f; + if (value != _inputfrequency) { + raise_float_event("InputFrequency", _inputfrequency, value); + } + _inputfrequency = value; + } + + /** + * Get Battery Voltage + * @return value in Volt DC + */ + public float getBatteryVoltage() { + return _batteryvoltage; + } + + @BA.Hide + public void setBatteryVoltage(float value) { + if (value<0f) value = 0f; + if (value>999.9f) value = 999.9f; + if (value != _batteryvoltage) { + raise_float_event("BatteryVoltage", _batteryvoltage, value); + } + _batteryvoltage = value; + } + + /** + * Get UPS Temperature + * @return value in Celcius + */ + public float getTemperature() { + return _temperature; + } + + @BA.Hide + public void setTemperature(float value) { + if (value<0f) value = 0f; + if (value>99.9f) value = 99.9f; + if (value != _temperature) { + raise_float_event("Temperature", _temperature, value); + } + _temperature = value; + } + + @BA.Hide + public void setBinaryStatus(int value) { + value = value & 0xFF; + if (value != _binarystatus) { + raise_float_event("BinaryStatus",_binarystatus, value); + } + _binarystatus = value; + } + + /** + * If true , Utility Electric is failed + * @return true or false + */ + public boolean getUtilityFail() { + return ((_binarystatus & (1<<7))>0); + } + + /** + * If true, Battery is Low + * @return true or false + */ + public boolean getBatteryLow() { + return ((_binarystatus & (1<<6))>0); + } + + /** + * If true, UPS in Bypass stage + * @return true or false + */ + public boolean getUPSIsBypass() { + return ((_binarystatus & (1<<5))>0); + } + + /** + * If true, UPS Buck/Boost in active stage + * This is opposite of UPSIsBypass() + * @return true or false + */ + public boolean getUPSBuckBoostIsActive() { + return ((_binarystatus & (1<<5))==0); + } + + /** + * If true, UPS is Failed + * @return true or false + */ + public boolean getUPSFail() { + return ((_binarystatus & (1<<4))>0); + } + + /** + * If True , UPS in Standby Stage (off power) + * @return true or false + */ + public boolean getUPSIsStandby() { + return ((_binarystatus & (1<<3))>0); + } + + /** + * If true, UPS in ON stage (on power) + * @return true or false + */ + public boolean getUPSIsON() { + return ((_binarystatus & (1<<3))==0); + } + + /** + * If true, UPS in Testing stage + * @return true or false + */ + public boolean getUPSIsTesting() { + return ((_binarystatus & (1<<2))>0); + } + + /** + * If true, UPS in Shutdown stage + * @return true or false + */ + public boolean getUPSIsShutdown() { + return ((_binarystatus & (1<<1))>0); + } + + /** + * if true, Beeper is ON + * @return true or false + */ + public boolean getBeepIsON() { + return ((_binarystatus & 1)>0); + } + + private void raise_string_event(String field, String olddata, String newdata) { + if (javaevent instanceof Megatec_Data_Event) { + javaevent.newdatastring(field, olddata, newdata); + } + } + + private void raise_float_event(String field, float olddata, float newdata) { + if (javaevent instanceof Megatec_Data_Event) { + javaevent.newdatafloat(field, olddata, newdata); + } + } +} diff --git a/src/devices/ups/ica/sin2100c/Megatec_Data_Event.java b/src/devices/ups/ica/sin2100c/Megatec_Data_Event.java new file mode 100644 index 0000000..b48689e --- /dev/null +++ b/src/devices/ups/ica/sin2100c/Megatec_Data_Event.java @@ -0,0 +1,6 @@ +package devices.ups.ica.sin2100c; + +public interface Megatec_Data_Event { + void newdatastring(String field, String olddata, String newdata); + void newdatafloat(String field, float olddata, float newdata); +} diff --git a/src/devices/ups/ica/sin2100c/SIN2100C.java b/src/devices/ups/ica/sin2100c/SIN2100C.java new file mode 100644 index 0000000..861f659 --- /dev/null +++ b/src/devices/ups/ica/sin2100c/SIN2100C.java @@ -0,0 +1,286 @@ +package devices.ups.ica.sin2100c; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import anywheresoftware.b4a.BA; +import jGPIO.jGPIO; + +@BA.ShortName("ICA_SIN2100C") +@BA.Events(values= { + "log(msg as string)" +}) + +/** + * UPS ICA SIN-2100C monitoring + * @author rdkartono + * + * This class need networkupstool (nut) + * to install : + * sudo apt-get update && sudo apt-get upgrade + * sudo apt-get install nut + * + * edit on /etc/nut/ups.conf, to add configuration + * [upsname] + * driver=blazer_usb + * port=auto + * desc="ICA SIN-2100C" + * + * edit on /etc/nut/nut.conf + * MODE=standalone + * + * reboot + * + * try checking with upsc -l + * should show upsname in result + * + * try checking result with upsc upsname + * should show values from ups + * + * to start ups driver : sudo upsdrvctl start + * to stop ups driver : sudo upsdrvctl stop + * to shutdown driver : sudo upsdrvctl shutdown + */ +public class SIN2100C { + private BA ba; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + private boolean opened = false; + private String upsname=""; + + public SIN2100C() { + if (jGPIO.osname.isEmpty()) jGPIO.detectOS(); + } + + /** + * Initialize SIN2100C Object + * @param callerobject : caller object + * @param eventname : eventname + */ + public void Initialize(BA bax, Object callerobject, String eventname) { + opened = false; + ba = bax; + caller = callerobject; + event = eventname.trim(); + if (ba instanceof BA) { + if (caller != null) { + if (!event.isEmpty()) { + need_log_event = ba.subExists(event+"_log"); + } + } + } + + } + + /** + * Open UPS + * @param upsname : ups name, as created in /etc/nut/ups.conf + * @return true if opened + */ + public boolean Open(String upsname) { + opened = false; + this.upsname = ""; + upsname = upsname.trim(); + if (!upsname.isEmpty()) { + if (jGPIO.IsLinux) { + File nut = new File("/etc/nut"); + if (nut.exists()) { + if (nut.isDirectory()) { + File nutconf = new File(nut, "nut.conf"); + if (nutconf.exists()) { + if (nutconf.isFile()) { + File upsconf = new File(nut, "ups.conf"); + if (upsconf.exists()) { + if (upsconf.isFile()) { + try { + Process process = Runtime.getRuntime().exec("upsc -l"); + InputStream inp = process.getInputStream(); + if (inp != null) { + BufferedReader buf = new BufferedReader(new InputStreamReader(inp)); + String rx; + do { + rx = buf.readLine(); + if (rx != null) { + rx = rx.trim(); + if (rx.equalsIgnoreCase(upsname)) { + this.upsname = upsname; + opened = true; + } + } + } while (rx != null); + } + } catch (Exception e) { + raise_log("Unable to run upsc, Msg : "+e.getMessage()); + } + } else { + raise_log("ups.conf is not file"); + } + } else { + raise_log("ups.conf not available"); + } + } else { + raise_log("nut.conf is not file"); + } + } else { + raise_log("nut.conf not available"); + } + } else { + raise_log("Invalid NUT installation"); + } + } else { + raise_log("NUT not installed"); + } + } else { + raise_log("Only support Linux system"); + } + } else { + raise_log("UPS Name is empty string"); + } + + return opened; + } + + /** + * Check if Opened succesfully + * @return true if opened + */ + public boolean IsOpened() { + return opened; + } + + /** + * Check opened ups name + * @return empty string if ups is not opened + */ + public String GetName() { + return upsname; + } + + /** + * Get UPS Status + * @return SIN2100C_Data object . Check IsValidData before using these data + */ + public SIN2100C_Data GetMeasurement() { + if (opened) { + if (!upsname.isEmpty()) { + try { + Process xx = Runtime.getRuntime().exec("upsc "+upsname); + InputStream inp = xx.getInputStream(); + if (inp!=null) { + return new SIN2100C_Data(inp); + } + } catch (IOException e) { + raise_log("Error GetMeasurement, Msg : "+e.getMessage()); + } + + } + } + + return new SIN2100C_Data(); // data asal, invalid + } + + private List printlog(InputStream inp) { + + if (inp instanceof InputStream) { + BufferedReader buf = new BufferedReader(new InputStreamReader(inp)); + List result = new ArrayList(); + while(true) { + String rx; + try { + rx = buf.readLine(); + if (rx instanceof String) { + if (!rx.isEmpty()) { + result.add(rx); + BA.Log(rx); + } + } else break; + } catch (IOException e) { + break; + } + + } + return result; + } + return null; + } + + /** + * Send command upsdrvctl start + * @return true if success + */ + public boolean Start_UPS_Driver() { + if (opened) { + if (!upsname.isEmpty()) { + try { + Process xx = Runtime.getRuntime().exec("upsdrvctl start "+upsname); + InputStream inp = xx.getInputStream(); + if (inp != null) { + printlog(inp); + + return true; + } + } catch(IOException e) { + raise_log("Error Start_UPS_Driver, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Send command upsdrvctl stop + * @return true if success + */ + public boolean Stop_UPS_Driver() { + if (opened) { + if (!upsname.isEmpty()) { + try { + Process xx = Runtime.getRuntime().exec("upsdrvctl stop "+upsname); + InputStream inp = xx.getInputStream(); + if (inp != null) { + printlog(inp); + return true; + } + } catch(IOException e) { + raise_log("Error Stop_UPS_Driver, Msg : "+e.getMessage()); + } + } + } + return false; + } + + /** + * Send command upsdrvctl shutdown + * @return true if success + */ + public boolean Shutdown_UPS() { + if (opened) { + if (!upsname.isEmpty()) { + try { + Process xx = Runtime.getRuntime().exec("upsdrvctl shutdown "+upsname); + InputStream inp = xx.getInputStream(); + if (inp != null) { + printlog(inp); + return true; + } + } catch(IOException e) { + raise_log("Error Shutdown_UPS_Driver, Msg : "+e.getMessage()); + } + } + } + return false; + } + + private void raise_log(String msg) { + if (need_log_event) { + ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + } +} diff --git a/src/devices/ups/ica/sin2100c/SIN2100C_Data.java b/src/devices/ups/ica/sin2100c/SIN2100C_Data.java new file mode 100644 index 0000000..9a2d4ea --- /dev/null +++ b/src/devices/ups/ica/sin2100c/SIN2100C_Data.java @@ -0,0 +1,264 @@ +package devices.ups.ica.sin2100c; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("SIN2100C_Data") +public class SIN2100C_Data { + + + + private boolean isvalid = false; + private Map result; + + public SIN2100C_Data() { + isvalid = false; + result = new Map(); + result.Initialize(); + } + + public SIN2100C_Data(InputStream data) { + isvalid = false; + result = new Map(); + result.Initialize(); + + if (data == null) return; + BufferedReader buf = new BufferedReader(new InputStreamReader(data)); + String rx; + do { + try { + rx = buf.readLine(); + if (rx!=null) { + rx = rx.trim(); + if (!rx.isEmpty()) { + //System.out.println("Found "+rx); + UPSC_Strings ss = new UPSC_Strings(rx); + if (ss.isWanted()) { + if (ss.HasValue()) { + //System.out.println("Put Map, Key="+ss.Key()+", Value="+ss.StringValue()); + result.Put(ss.Key(), ss.StringValue()); + } + } + } + } + } catch (IOException e) { + rx = null; + System.out.println("SIN2100C_Data read error, Msg : "+e.getMessage()); + break; + } + + } while(rx != null); + + if (result.getSize()>0) isvalid = true; + } + + /** + * Check if SIN2100C_Data is valid data + * @return true if valid + */ + public boolean IsValidData() { + return isvalid; + } + + /** + * Get Battery charge in percent + * @return zero if not available + */ + public float getBatteryCharge() { + if (isvalid) { + if (result instanceof Map) { + if (result.IsInitialized()) { + return Float.valueOf((String) result.GetDefault("battery.charge", 0)); + } + } + } + + return 0f; + } + + /** + * Get Battery Charge in Volt DC + * @return zero if not available + */ + public float getBatteryVoltage() { + if (isvalid) { + if (result instanceof Map) { + if (result.IsInitialized()) { + return Float.valueOf((String) result.GetDefault("battery.voltage", 0)); + } + } + } + + return 0f; + } + + /** + * Get internal UPS Temperature in Celcius + * @return zero if not available + */ + public float getUPSTemperature() { + if (isvalid) { + if (result instanceof Map) { + if (result.IsInitialized()) { + return Float.valueOf((String) result.GetDefault("ups.temperature", 0)); + } + } + } + + return 0f; + } + + /** + * Get Input UPS Voltage in Volt AC + * @return zero if not available + */ + public float getInputVoltage() { + if (isvalid) { + if (result instanceof Map) { + if (result.IsInitialized()) { + return Float.valueOf((String) result.GetDefault("input.voltage", 0)); + } + } + } + + return 0f; + } + + /** + * Get Ouput UPS Voltage in Volt AC + * @return zero if not available + */ + public float getOutputVoltage() { + if (isvalid) { + if (result instanceof Map) { + if (result.IsInitialized()) { + return Float.valueOf((String) result.GetDefault("output.voltage", 0)); + } + } + } + + return 0f; + } + + + + /** + * Will return Map , with following keys : + * battery.charge + * battery.voltage + * ups.mfr + * ups.model + * ups.status + * ups.temperature + * ups.firmware + * ups.load + * driver.name + * input.current.nominal + * input.frequency + * input.voltage + * output.voltage + * + * @return Map object + */ + public Map MakeJSONMap() { + return result; + } +} + +class UPSC_Strings{ + private final String[] keys= { + "battery.charge:", + "battery.voltage:", + "ups.mfr:", + "ups.model:", + "ups.status:", + "ups.temperature:", + "ups.firmware:", + "ups.load:", + "driver.name:", + "input.current.nominal:", + "input.frequency:", + "input.voltage:", + "output.voltage:" + }; + + private final String mytext; + private String key=""; + private String value=""; + private float value_number; + private boolean wanted = false; + + public UPSC_Strings(String value) { + mytext = (value != null) ? value.trim() : ""; + if (mytext.isEmpty()) return; // gak ada nilainya + + for(int ii=0;ii invalid value + * @return true if value is empty + */ + public boolean HasValue() { + if (value.isEmpty()) + return false; + else { + return true; + } + } + + /** + * Check if this UPSC_String contain number value --> valid float value + * @return true if value is number + */ + public boolean isNumberValue() { + if (value.isEmpty()) + return false; + else { + try { + Float ff = Float.valueOf(value); + value_number = ff.floatValue(); + return true; + } catch(NumberFormatException e) { + return false; + } + + } + } + + + +} diff --git a/src/gps/AdzanCalculator.java b/src/gps/AdzanCalculator.java new file mode 100644 index 0000000..1c67fd6 --- /dev/null +++ b/src/gps/AdzanCalculator.java @@ -0,0 +1,131 @@ +package gps; + +import java.util.List; + +import anywheresoftware.b4a.BA; +@BA.ShortName("AdzanCalculator") +public class AdzanCalculator { + + private PrayTime pt; + + /** + * Initialize the Adzan Calculator
+ * By default :
+ * the calculation method is Jafari
+ * the Asar Juristic is Shafii
+ * the Higher Latitude Adjustment is Angle Based
+ */ + public void Initialize() { + pt = new PrayTime(); + + pt.setTimeFormat(pt.getTime24()); + pt.setCalcMethod(pt.getJafari()); // Jafari calculation method); + pt.setAsrJuristic(pt.getShafii()); // Ashar Juristic --> Safii , dah betul + pt.setAdjustHighLats(pt.getAngleBased()); // Higher Latitude Adjustment --> Angle Based + int[] offsets = {0, 0, 0, 0, 0, 0, 0}; // {Fajr,Sunrise,Dhuhr,Asr,Sunset,Maghrib,Isha} + pt.tune(offsets); + } + + /** + * Set the calculation
+ * 0 = Jafari
+ * 1 = Karachi
+ * 2 = ISNA
+ * 3 = MWL
+ * 4 = Makkah
+ * 5 = Egypt
+ * 6 = Tehran
+ * 7 = Custom
+ * default = Jafari + * @param method 0 -7 + */ + public void setCalculationMethod(int method) { + switch(method) { + case 0: + pt.setCalcMethod(pt.getJafari()); + break; + case 1: + pt.setCalcMethod(pt.getKarachi()); + break; + case 2: + pt.setCalcMethod(pt.getISNA()); + break; + case 3: + pt.setCalcMethod(pt.getMWL()); + break; + case 4: + pt.setCalcMethod(pt.getMakkah()); + break; + case 5: + pt.setCalcMethod(pt.getEgypt()); + break; + case 6: + pt.setCalcMethod(pt.getTehran()); + break; + case 7: + pt.setCalcMethod(pt.getCustom()); + break; + default: + pt.setCalcMethod(pt.getJafari()); + break; + } + } + + /** + * Set the Asar Juristic
+ * 0 = Shafii
+ * 1 = Hanafi
+ * default = Shafii + * @param juristic 0 - 1 + */ + public void setAsharJuristic(int juristic) { + switch (juristic) { + case 0: + pt.setAsrJuristic(pt.getShafii()); + break; + case 1: + pt.setAsrJuristic(pt.getHanafi()); + break; + default: + pt.setAsrJuristic(pt.getShafii()); + break; + } + } + + /** + * Set the Higher Latitude Adjustment
+ * 0 = None : no adjustment
+ * 1 = MidNight : middle of the night
+ * 2 = OneSeventh : 1/7 of the night
+ * 3 = AngleBased : 1/60 of the night
+ * default = AngleBased + * @param mode + */ + public void setHigherLatitudeAdjustment(int mode) { + switch (mode) { + case 0: + pt.setAdjustHighLats(pt.getNone()); + break; + case 1: + pt.setAdjustHighLats(pt.getMidNight()); + break; + case 2: + pt.setAdjustHighLats(pt.getOneSeventh()); + break; + case 3: + pt.setAdjustHighLats(pt.getAngleBased()); + break; + default: + pt.setAdjustHighLats(pt.getAngleBased()); + break; + } + } + + public List GetPrayerNames(){ + return pt.getTimeNames(); + } + + public List GetPrayerTime(int year, int month, int day, double latitude, double longitude, double TimeZone){ + return pt.getDatePrayerTimes(year, month, day, latitude, longitude, TimeZone); + } +} diff --git a/src/gps/GpsGPGGA.java b/src/gps/GpsGPGGA.java new file mode 100644 index 0000000..fe0419f --- /dev/null +++ b/src/gps/GpsGPGGA.java @@ -0,0 +1,163 @@ +package gps; + +import java.util.ArrayList; +import java.util.List; + +import anywheresoftware.b4a.BA; +import jGPIO.mycodes; +import anywheresoftware.b4a.keywords.Common; + +@BA.ShortName("GpsGPGGA") +public class GpsGPGGA { + + public String invalidmsg = ""; + public byte UTC_Hour = -1; + public byte UTC_Minute = -1; + public byte UTC_Second = -1; + public double Latitude = 0; + public double Longitude = 0; + public double HDOP = -1; + public boolean PositionFixed = false; + public int Satellites = -1; + public double Altitude = -1; + public double GeoID_Height = -1; + + public GpsGPGGA(String msg) { + String[] partB = msg.split(","); // pisahkan konten berdasar koma + if (partB.length<13) { + invalidmsg = "GPGGA Split is less than 13"; + return; + } + + List err = new ArrayList(); + + // waktu hhmmss.sss + byte[] utc = new byte[] {0,0,0}; + if (mycodes.Gps_UTCTime(partB[1],utc)) { + UTC_Hour = utc[0]; + UTC_Minute = utc[1]; + UTC_Second = utc[2]; + } else { + err.add("GPGGA dont have Time"); + } + + // Latitude + double[] result; + result = new double[] {0}; + if (mycodes.Gps_Latitude_formatchange(partB[2], result)) { + Latitude = result[0]; + } else { + err.add("GPGGA Invalid Latitude : "+partB[2]); + } + + // Latitude polarity, N or S + switch(mycodes.Gps_Char(partB[3])) { + case 'N' : + Latitude = mycodes.SetPositive(Latitude); // hasil harus positif + break; + case 'S' : + Latitude = mycodes.SetNegative(Latitude); // hasil harus negatif + break; + default : + Latitude = 0; // tidak jelas + err.add("GPGGA Invalid Latitude Polarity : "+partB[3]); + break; + } + + // Longitude + result = new double[] {0}; + if (mycodes.Gps_Longitude_formatchange(partB[4], result)) { + Longitude = result[0]; + } else { + err.add("GPGGA Invalid Longitude : "+partB[4]); + } + + // Longitude polarity, W or S + + switch(mycodes.Gps_Char(partB[5])) { + case 'W' : + Longitude = mycodes.SetNegative(Longitude); // hasil harus negatif + break; + case 'E' : + Longitude = mycodes.SetPositive(Longitude); // hasil harus positif + break; + default : + Longitude = 0; // tidak jelas + err.add("GPGGA Invalid Longitude Polarity : "+partB[5]); + break; + } + + // Position Fix, 0 / 1 / 2 + try { + int posfix = Integer.parseInt(partB[6]); + if (posfix<1) PositionFixed = false; else PositionFixed = true; + } catch(NumberFormatException e) { + err.add("GPGGA Invalid PositionFix : "+partB[6]); + } + + // Number of satelites + try { + int satfix = Integer.parseInt(partB[7]); + Satellites = satfix; + } catch(NumberFormatException e) { + err.add("GPGGA Invalid Satelite : "+partB[7]); + + } + + // HDOP di index 8 + try { + double hd = Double.parseDouble(partB[8]); + HDOP = hd; + }catch(NumberFormatException e) { + err.add("GPGGA Invalid HDOP : "+partB[8]); + } + + // Altitude , nilai di 9, M di 10 + try { + double alt = Double.parseDouble(partB[9]); + Altitude = alt; + } catch(NumberFormatException e) { + err.add("GPGGA Invalid Altitude : "+partB[9]); + } + + if (mycodes.Gps_Char(partB[10])!='M') { + err.add("GPGGA Altitude M not found"); + } + + // GeoID_height, nilai di 11, M di 12 + try { + double geoid = Double.parseDouble(partB[11]); + GeoID_Height = geoid; + } catch(NumberFormatException e) { + err.add("GPGGA Invalid Altitude : "+partB[11]); + } + + if (mycodes.Gps_Char(partB[12])!='M') { + err.add("GPGGA GeoID M is not found"); + } + + invalidmsg = mycodes.Gps_ErrList_to_String(err); + } + + public boolean isValid() { + if (invalidmsg=="") return true; else return false; + } + + /** + * Get UTC Time String in format hh:mm:ss + * @return UTC Time String in format hh:mm:ss, or empty string if invalid data + */ + public String UTC_Time_hhmmss() { + if (isValid()) { + StringBuilder str = new StringBuilder(); + str.append(Common.NumberFormat(UTC_Hour, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Minute, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Second,2,0)); + return str.toString(); + } else return ""; + } + + +} diff --git a/src/gps/GpsGPGLL.java b/src/gps/GpsGPGLL.java new file mode 100644 index 0000000..a593634 --- /dev/null +++ b/src/gps/GpsGPGLL.java @@ -0,0 +1,121 @@ +package gps; + +import java.util.ArrayList; +import java.util.List; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Common; +import jGPIO.mycodes; + +@BA.ShortName("GpsGPGLL") +public class GpsGPGLL { + public String invalidmsg = ""; + public double Latitude = -1; + public double Longitude = -1; + public byte UTC_Hour = -1; + public byte UTC_Minute = -1; + public byte UTC_Second = -1; + public boolean PositionFixed = false; + public GpsGPGLL(String msg) { + String[] partB = msg.split(","); + if (partB.length<7) { + invalidmsg = "GPGLL split less than 7"; + return; + } + + List err = new ArrayList(); + + double[] result; + // Latitude + result = new double[] {0}; + if (mycodes.Gps_Latitude_formatchange(partB[1], result)) { + Latitude = result[0]; + } else { + err.add("GPGLL Invalid Latitude : "+partB[1]); + } + + // Latitude polarity, N or S + switch(mycodes.Gps_Char(partB[2])) { + case 'N' : + Latitude = mycodes.SetPositive(Latitude); // hasil harus positif + break; + case 'S' : + Latitude = mycodes.SetNegative(Latitude); // hasil harus negatif + break; + default : + Latitude = 0; // gak jelas + err.add("GPGLL Invalid Latitude Polarity : "+partB[2]); + break; + } + + + // Longitude + result = new double[] {0}; + if (mycodes.Gps_Longitude_formatchange(partB[3], result)) { + Longitude = result[0]; + } else { + err.add("GPGLL Invalid Longitude : "+partB[3]); + } + + // Longitude polarity, W or S + switch(mycodes.Gps_Char(partB[4])) { + case 'W' : + Longitude = mycodes.SetNegative(Longitude); // hasil harus negatif + break; + case 'E' : + Longitude = mycodes.SetPositive(Longitude); // hasil harus positif + break; + default : + Longitude = 0; + err.add("GPGLL Invalid Longitude Polarity : "+partB[4]); + break; + } + + // UTC Time + byte[] utc = new byte[] {0,0,0}; + if (mycodes.Gps_UTCTime(partB[5], utc)) { + UTC_Hour = utc[0]; + UTC_Minute = utc[1]; + UTC_Second = utc[2]; + } else { + err.add("GPGLL dont have Time"); + + } + + // Fixed Status + switch(mycodes.Gps_Char(partB[6])) { + case 'A' : + PositionFixed = true; + break; + case 'V' : + PositionFixed = false; + break; + default : + err.add("GPGLL Invalid PositionFix : "+partB[6]); + break; + } + + invalidmsg = mycodes.Gps_ErrList_to_String(err); + + } + + public boolean isValid() { + if (invalidmsg=="") return true; else return false; + } + + /** + * Get UTC Time String in format hh:mm:ss + * @return UTC Time String in format hh:mm:ss, or empty string if invalid data + */ + public String UTC_Time_hhmmss() { + if (isValid()) { + StringBuilder str = new StringBuilder(); + str.append(Common.NumberFormat(UTC_Hour, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Minute, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Second,2,0)); + return str.toString(); + } else return ""; + } +} diff --git a/src/gps/GpsGPGSA.java b/src/gps/GpsGPGSA.java new file mode 100644 index 0000000..dcfecf3 --- /dev/null +++ b/src/gps/GpsGPGSA.java @@ -0,0 +1,98 @@ +package gps; + +import java.util.ArrayList; +import java.util.List; + +import anywheresoftware.b4a.BA; +import jGPIO.mycodes; + +@BA.ShortName("GpsGPGSA") +public class GpsGPGSA { + public String invalidmsg = ""; + public String Mode = ""; + public String LockMode = ""; + public int[] SatelitteID = new int[] {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; + public double PDOP = -1; + public double HDOP = -1; + public double VDOP = -1; + + public GpsGPGSA(String msg) { + String[] partB = msg.split(","); + if (partB.length<18) { + invalidmsg = "GPGSA Split less than 18"; + return; + } + + List err = new ArrayList(); + switch(mycodes.Gps_Char(partB[1])) { + case 'M' : + Mode = "Manual"; + break; + case 'A' : + Mode = "Automatic"; + break; + default : + Mode = ""; + err.add("GPGSA Mode is invalid : "+partB[1]); + break; + } + + int[] intresult = new int[] {0}; + if (mycodes.Gps_Int(partB[2], intresult)) { + switch(intresult[0]) { + case 1 : + LockMode = "No Fix"; + break; + case 2 : + LockMode = "2D"; + break; + case 3 : + LockMode = "3D"; + break; + default : + LockMode = ""; + err.add("GPGSA LockMode is invalid : "+partB[2]); + break; + } + } else { + err.add("GPGSA Lockmode is invalid : "+partB[2]); + } + + // satelite , 12 unit + for (int jj=0;jj<12;jj++) { + if (mycodes.Gps_Int(partB[3+jj], intresult)) { + SatelitteID[jj] = intresult[0]; + } else SatelitteID[jj] = -1; + } + + double[] doubleresult = new double[] {0}; + + // PDOP = 15 + if (mycodes.Gps_Double(partB[15], doubleresult)) { + PDOP = doubleresult[0]; + } else { + err.add("GPGSA Invalid PDOP : "+partB[15]); + } + + // HDOP = 16 + if (mycodes.Gps_Double(partB[16], doubleresult)) { + HDOP = doubleresult[0]; + } else { + err.add("GPGSA Invalid HDOP : "+partB[16]); + } + + // VDOP = 17 + if (mycodes.Gps_Double(partB[17], doubleresult)) { + VDOP = doubleresult[0]; + } else { + err.add("GPGSA Invalid VDOP : "+partB[17]); + + } + + invalidmsg = mycodes.Gps_ErrList_to_String(err); + } + + public boolean isValid() { + if (invalidmsg=="") return true; else return false; + } +} diff --git a/src/gps/GpsGPRMC.java b/src/gps/GpsGPRMC.java new file mode 100644 index 0000000..2460d98 --- /dev/null +++ b/src/gps/GpsGPRMC.java @@ -0,0 +1,194 @@ +package gps; + +import java.util.ArrayList; +import java.util.List; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Common; +import jGPIO.mycodes; + +@BA.ShortName("GpsGPRMC") +public class GpsGPRMC { + public String invalidmsg = ""; + public byte UTC_Hour = -1; + public byte UTC_Minute = -1; + public byte UTC_Second = -1; + public byte UTC_Day = -1; + public byte UTC_Month = -1; + public int UTC_Year = -1; + public boolean PositionFix = false; + public double Latitude = 0; + public double Longitude = 0; + + /** + * In Knot + */ + public double Ground_Speed = 0; + + /** + * In Degree + */ + public double Ground_Course = 0; + + /** + * In Degree + */ + public double Magnetic_Variation = 0; + + + public GpsGPRMC(String msg) { + + String[] partB = msg.split(","); + + if (partB.length<12) { + invalidmsg = "GPRMC Split less than 12"; + return; + } + + List err = new ArrayList(); + + byte[] utctime = new byte[] {0,0,0}; + if (mycodes.Gps_UTCTime(partB[1], utctime)) { + UTC_Hour = utctime[0]; + UTC_Minute = utctime[1]; + UTC_Second = utctime[2]; + } else { + err.add("GPRMC dont have Time") ; + } + + switch(mycodes.Gps_Char(partB[2])) { + case 'A' : + PositionFix = true; + break; + case 'V' : + PositionFix = false; + break; + default : + err.add("GPRMC Invalid Position Fix : "+partB[2]); + break; + } + + double[] result; + result = new double[] {0}; + if (mycodes.Gps_Latitude_formatchange(partB[3], result)) { + Latitude = result[0]; + } else { + err.add("GPRMC Invalid Latitude : "+partB[3]); + } + + switch(mycodes.Gps_Char(partB[4])) { + case 'N' : + Latitude = mycodes.SetPositive(Latitude); //hasil harus positif + break; + case 'S' : + Latitude = mycodes.SetNegative(Latitude); // hasil harus negatif; + break; + default : + Latitude = 0; + err.add("GPRMC Invalid Latitude Polarity : "+partB[4]); + break; + } + + result = new double[] {0}; + if (mycodes.Gps_Longitude_formatchange(partB[5], result)) { + Longitude = result[0]; + } else { + err.add("GPRMC Invalid Longitude : "+partB[5]); + } + + switch(mycodes.Gps_Char(partB[6])) { + case 'W' : + Longitude = mycodes.SetNegative(Longitude); // hasil harus negative + break; + case 'E' : + Longitude = mycodes.SetPositive(Longitude); // hasil positive + break; + default : + err.add("GPRMC Invalid Longitude Polarity : "+partB[6]); + break; + } + + try { + double knot = Double.parseDouble(partB[7]); + Ground_Speed = knot; + } catch( NumberFormatException e) { + err.add("GPRMC invalid Ground Speed "+partB[7]); + Ground_Speed = -1; + } + + try { + double course = Double.parseDouble(partB[8]); + Ground_Course = course; + } catch( NumberFormatException e) { + err.add("GPRMC invalid Ground Course "+partB[8]); + Ground_Course =-1; + } + + byte[] utcdate = new byte[] {0,0,0}; + if (mycodes.Gps_UTCDate(partB[9], utcdate)) { + UTC_Day = utcdate[0]; + UTC_Month = utcdate[1]; + UTC_Year = 2000 + utcdate[2]; + } else { + err.add("GPRMC dont have Date"); + } + + try { + double mag = Double.parseDouble(partB[10]); + Magnetic_Variation = mag; + }catch( NumberFormatException e) { + err.add("GPRMC invalid Magnetic Variation "+partB[10]); + } + + switch(mycodes.Gps_Char(partB[11])) { + case 'E' : + Magnetic_Variation = mycodes.SetPositive(Magnetic_Variation); + break; + case 'W' : + Magnetic_Variation = mycodes.SetNegative(Magnetic_Variation); + break; + default : + err.add("GPRMC invalid Magnetic Polarity : "+partB[11]); + break; + } + + invalidmsg = mycodes.Gps_ErrList_to_String(err); + } + + public boolean isValid() { + if (invalidmsg=="") return true; else return false; + } + + /** + * Get UTC Time String in format hh:mm:ss + * @return UTC Time String in format hh:mm:ss, or empty string if invalid data + */ + public String UTC_Time_hhmmss() { + if (PositionFix) { + StringBuilder str = new StringBuilder(); + str.append(Common.NumberFormat(UTC_Hour, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Minute, 2, 0)); + str.append(":"); + str.append(Common.NumberFormat(UTC_Second,2,0)); + return str.toString(); + } else return ""; + } + + /** + * Get UTC Date String in format dd-MM-yyyy + * @return UTC Time String in format dd-MM-yyyy , or empty string if invalid data + */ + public String UTC_Date_ddMMyyyy() { + if (PositionFix) { + StringBuilder str = new StringBuilder(); + str.append(Common.NumberFormat(UTC_Day, 2, 0)); + str.append("-"); + str.append(Common.NumberFormat(UTC_Month, 2, 0)); + str.append("-"); + str.append(UTC_Year); + return str.toString(); + } else return ""; + } + +} diff --git a/src/gps/GpsGPVTG.java b/src/gps/GpsGPVTG.java new file mode 100644 index 0000000..6087404 --- /dev/null +++ b/src/gps/GpsGPVTG.java @@ -0,0 +1,83 @@ +package gps; + +import java.util.ArrayList; +import java.util.List; + +import anywheresoftware.b4a.BA; +import jGPIO.mycodes; + +@BA.ShortName("GpsGPVTG") +public class GpsGPVTG { + public String invalidmsg = ""; + public double Course_Heading = 0; + public double Magnetic_Heading = 0; + public double Horizontal_Knot_Speed = 0; + public double Horizontal_KM_Speed = 0; + + public GpsGPVTG(String msg) { + String partB[] = msg.split(","); + if (partB.length<8) { + invalidmsg = "GPVTG Split less than 8"; + return; + } + + List err = new ArrayList(); + + double[] result = new double[] {0}; + if (mycodes.Gps_Double(partB[1], result)) { + Course_Heading = result[0]; + } else { + Course_Heading = 0; + err.add("GPVTG invalid Course Heading : "+partB[1]); + } + + if (mycodes.Gps_Char(partB[2])!='T') { + Course_Heading = 0; + err.add("GPVTG Course Heading code is not T"); + } + + + if (mycodes.Gps_Double(partB[3], result)) { + Magnetic_Heading = result[0]; + } else { + Magnetic_Heading = 0; + err.add("GPVTG invalid Magnetic Heading : "+partB[3]); + } + + if (mycodes.Gps_Char(partB[4])!='M') { + Magnetic_Heading = 0; + err.add("GPVTG Magnetic Heading code is not M"); + } + + + if (mycodes.Gps_Double(partB[5], result)) { + Horizontal_Knot_Speed = result[0]; + } else { + Horizontal_Knot_Speed = 0; + err.add("GPVTG Invalid Horizontal Knot Speed : "+partB[5]); + } + + if (mycodes.Gps_Char(partB[6])!='N') { + Horizontal_Knot_Speed = 0; + err.add("GPVTG Horizontal Knot Speed code is not N"); + } + + if (mycodes.Gps_Double(partB[7], result)) { + Horizontal_KM_Speed = result[0]; + } else { + Horizontal_KM_Speed = 0; + err.add("GPVTG Invalid Horizontal KM Speed : "+partB[7]); + } + + if (mycodes.Gps_Char(partB[8])!='K') { + Horizontal_KM_Speed = 0; + err.add("GPVTG Horizontal KM Speed code is not K"); + } + + invalidmsg = mycodes.Gps_ErrList_to_String(err); + } + + public boolean isValid() { + if (invalidmsg=="") return true; else return false; + } +} diff --git a/src/gps/GpsReceiver.java b/src/gps/GpsReceiver.java new file mode 100644 index 0000000..9a7bbc6 --- /dev/null +++ b/src/gps/GpsReceiver.java @@ -0,0 +1,489 @@ +package gps; + +import java.util.ArrayList; +import java.util.List; + +import com.fazecast.jSerialComm.SerialPort; +import com.fazecast.jSerialComm.SerialPortEvent; +import com.fazecast.jSerialComm.SerialPortInvalidPortException; +import com.fazecast.jSerialComm.SerialPortMessageListener; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("GpsReceiver") +@BA.Events(values= { + "log(msg as string)", + "newgpstime(value as string)", + "newgpsdate(value as string)", + "newgpslatitude(value as double)", + "newgpslongitude(value as double)", + + "rawdata(gpsmsg as string)", + "ggadata(data as GpsGPGGA)", + "glldata(data as GpsGPGLL)", +// "gsadata(data as GpsGPGSA)", +// "vtgdata(data as GpsGPVTG)", + "rmcdata(data as GpsGPRMC)" +}) + +//@BA.DependsOn(values = { "jSerialComm-2.6.2" }) + +public class GpsReceiver { + + private BA bax; + private String event = ""; + private Object caller; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_rawdata_event = false; + private boolean need_ggadata_event = false; + private boolean need_newgpstime_event = false; + private boolean need_newgpsdate_event = false; + private boolean need_newgpslatitude_event = false; + private boolean need_newgpslongitude_event = false; + + private boolean need_glldata_event = false; + //private boolean need_gsadata_event = false; // tidak terlalu penting + private boolean need_rmcdata_event = false; + //private boolean need_vtgdata_event = false; // tidak terlalu penting + + private SerialPort myport; + private boolean initialized = false; + private long rxcount = 0; + + private String last_gps_time = ""; + private String last_gps_date = ""; + private double last_gps_latitude = 0; + private double last_gps_longitude = 0; + private boolean gga_fixed = false; + private boolean gll_fixed = false; + private boolean rmc_fixed = false; + private int last_gps_day = 0; + private int last_gps_month = 0; + private int last_gps_year = 0; + private int last_gps_hour = 0; + private int last_gps_minute = 0; + private int last_gps_second = 0; + + + /** + * Initialize GpsReceiver + * @param eventname : eventname string + */ + public void Initialize(BA ba, Object caller, String eventname ) { + bax = ba; + event = eventname; + this.caller = caller; + + if (bax!=null) { + if (this.caller!=null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_rawdata_event = bax.subExists(event+"_rawdata"); + need_ggadata_event = bax.subExists(event+"_ggadata"); + + need_glldata_event = bax.subExists(event+"_glldata"); + //need_gsadata_event = bax.subExists(event+"_gsadata"); + need_rmcdata_event = bax.subExists(event+"_rmcdata"); + //need_vtgdata_event = bax.subExists(event+"_vtgdata"); + + need_newgpstime_event = bax.subExists(event+"_newgpstime"); + need_newgpsdate_event = bax.subExists(event+"_newgpsdate"); + need_newgpslatitude_event = bax.subExists(event+"_newgpslatitude"); + need_newgpslongitude_event = bax.subExists(event+"_newgpslongitude"); + } + } + } + + + if (SerialPort.getVersion()!="") initialized = true; else initialized = false; + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + ClosePort(); + } + }); + } + + + + /** + * Check if GpsReceiver is initialized + * @return true if initialized + */ + public boolean IsInitialized() { + return initialized; + } + + /** + * Get all available port names in this system + * @return List of Serial Port Names + */ + public List GetPortNames(){ + List result = new ArrayList(); + if (initialized) { + for(SerialPort xx:SerialPort.getCommPorts()) { + if (xx != null) { + String portname = xx.getSystemPortName(); + if (portname!="") { + result.add(portname); + } + } + } + } else { + raise_log("GetPortNames failed, SerialPort Library not ready"); + } + return result; + } + + /** + * Close GPS Receiving Port + */ + public void ClosePort() { + if (myport instanceof SerialPort) { + myport.removeDataListener(); + + myport.closePort(); + myport = null; + } + + } + + /** + * How many GPS data received + * @return how many gps data received + */ + public long getRXCounter() { + return rxcount; + } + + public double GetLatitude_rounded(int fractiondigits) { + if (fractiondigits<1) fractiondigits = 1; + if (fractiondigits>4) fractiondigits = 4; + switch(fractiondigits) { + case 1 : + return Math.round(last_gps_latitude * 10.0) / 10.0; + case 2 : + return Math.round(last_gps_latitude * 100.0) / 100.0; + case 3 : + return Math.round(last_gps_latitude * 1000.0) / 1000.0; + default : + return Math.round(last_gps_latitude * 10000.0) / 10000.0; + } + } + + public double GetLongitude_rounded(int fractiondigits) { + if (fractiondigits<1) fractiondigits = 1; + if (fractiondigits>4) fractiondigits = 4; + switch(fractiondigits) { + case 1 : + return Math.round(last_gps_longitude * 10.0) / 10.0; + case 2 : + return Math.round(last_gps_longitude * 100.0) / 100.0; + case 3 : + return Math.round(last_gps_longitude * 1000.0) / 1000.0; + default : + return Math.round(last_gps_longitude * 10000.0) / 10000.0; + } + } + + /** + * Get last GPS Latitude + * @return Latitude value in double + */ + public double getLatitude() { + return last_gps_latitude; + } + + /** + * Get Last GPS Longitude + * @return Longitude value in double + */ + public double getLongitude() { + return last_gps_longitude; + } + + /** + * Get Last GPS Time + * @return time in format hh:mm:ss + */ + public String getTime_hhmmss() { + return last_gps_time; + } + + /** + * Get Last GPS Date + * @return date in format dd-MM-yyyy + */ + public String getDate_ddMMyyyy() { + return last_gps_date; + } + + /** + * Get GPS Fixed status + * @return true if GPS is fixed + */ + public boolean getGPS_Fixed() { + return (gga_fixed || gll_fixed || rmc_fixed); + } + + public int getUTC_Day() { + return last_gps_day; + } + + public int getUTC_Month() { + return last_gps_month; + } + + public int getUTC_Year() { + return last_gps_year; + } + + public int getUTC_Hour() { + return last_gps_hour; + } + + public int getUTC_Minute() { + return last_gps_minute; + } + + public int getUTC_Second() { + return last_gps_second; + } + + private void setUTC_Time(int hour, int minute, int second) { + last_gps_hour = hour; + last_gps_minute = minute; + last_gps_second = second; + } + + private void setUTC_Date(int day, int month, int year) { + last_gps_day = day; + last_gps_month = month; + last_gps_year = year; + } + + /** + * Open Serial Port for Gps Communication + * @param serialname : serialport name + * @param baudrate : default is 9600 + * @return true if success + */ + public boolean OpenPort(String serialname, int baudrate) { + rxcount = 0; + if (baudrate==0) baudrate = 9600; // default baudrate + if (initialized) { + try { + myport = SerialPort.getCommPort(serialname); + if (myport.openPort()) { + myport.setBaudRate(baudrate); + if (myport.addDataListener(new SerialPortMessageListener() { + + @Override + public int getListeningEvents() { + return SerialPort.LISTENING_EVENT_DATA_RECEIVED; + } + + @Override + public void serialEvent(SerialPortEvent arg0) { + if (arg0==null) return; + byte[] bb = arg0.getReceivedData(); + if (bb==null) return; + if (bb.length<1) return; + String msg = new String(bb); + + int indexmulai = msg.indexOf("$GP"); + if (indexmulai<0) return; + + String vv = msg.substring(indexmulai); + if (vv.startsWith("$GPGSV")) return; // ignore GPGSV + raise_rawdata(vv); + process_data(vv); + rxcount+=1; + + } + + @Override + public boolean delimiterIndicatesEndOfMessage() { + // delimiter ada di belakang + return true; + } + + @Override + public byte[] getMessageDelimiter() { + // delimiter adalah CR LF + return new byte[] {(byte) 13, (byte) 10}; + } + + })) + return true; + } + } catch(SerialPortInvalidPortException e) { + raise_exception("Failed to GetCommPort",e); + } + } + + return false; + } + + /** + * Check if SerialPort is opened and ready + * @return true if opened and ready + */ + public boolean PortIsReady() { + if (myport instanceof SerialPort) { + return myport.isOpen(); + } + return false; + } + + private void process_data(String vv) { + String[] gps_split = vv.split("\\*"); + if (gps_split.length!=2) return; + String gpsmsg = gps_split[0]; + + if (gpsmsg.startsWith("$GPGGA")) { + GpsGPGGA datagga = new GpsGPGGA(gpsmsg); + // supaya kalau udah fix, ada UTC Latitude, Longitude, UTC Time , dah bisa raise event + gga_fixed = datagga.PositionFixed; + if (gga_fixed) + { + raise_ggadata(datagga); + String time = datagga.UTC_Time_hhmmss(); + if (last_gps_time.compareTo(time)!=0) { + last_gps_time = time; + setUTC_Time(datagga.UTC_Hour, datagga.UTC_Minute, datagga.UTC_Second); + raise_newgpstime(last_gps_time); + } + if (last_gps_latitude != datagga.Latitude) { + last_gps_latitude = datagga.Latitude; + raise_newgpslatitude(last_gps_latitude); + } + if (last_gps_longitude != datagga.Longitude) { + last_gps_longitude = datagga.Longitude; + raise_newgpslongitude(last_gps_longitude); + } + } + return; + } + + // ini tidak perlu, gak penting + /* + * if (gpsmsg.startsWith("$GPGSA")) { GpsGPGSA datagsa = new GpsGPGSA(gpsmsg); + * if (datagsa.isValid()) { raise_gsadata(datagsa); } else { + * BA.Log(datagsa.invalidmsg); } return; } + */ + + if (gpsmsg.startsWith("$GPGLL")) { + GpsGPGLL datagll = new GpsGPGLL(gpsmsg); + // supaya kalau udah fix, ada UTC Latitude, Longitude, UTC Time, dah bisa raise event + gll_fixed = datagll.PositionFixed; + if (gll_fixed) + { + raise_glldata(datagll); + String time = datagll.UTC_Time_hhmmss(); + if (last_gps_time.compareTo(time)!=0) { + last_gps_time = time; + setUTC_Time(datagll.UTC_Hour, datagll.UTC_Minute, datagll.UTC_Second); + raise_newgpstime(last_gps_time); + } + if (last_gps_latitude != datagll.Latitude) { + last_gps_latitude = datagll.Latitude; + raise_newgpslatitude(last_gps_latitude); + } + if (last_gps_longitude != datagll.Longitude) { + last_gps_longitude = datagll.Longitude; + raise_newgpslongitude(last_gps_longitude); + } + } + return; + } + + if (gpsmsg.startsWith("$GPRMC")) { + GpsGPRMC datarmc = new GpsGPRMC(gpsmsg); + // GPRMC Groud Course, Magnetic Variation, dan Magnetic Polarity sering loss + // jadi tidak pakai isValid, tapi pakai PositionFix aja + rmc_fixed = datarmc.PositionFix; + if (rmc_fixed) + { + raise_rmcdata(datarmc); + String time = datarmc.UTC_Time_hhmmss(); + if (last_gps_time.compareTo(time)!=0) { + last_gps_time = time; + setUTC_Time(datarmc.UTC_Hour, datarmc.UTC_Minute, datarmc.UTC_Second); + raise_newgpstime(last_gps_time); + } + + String date = datarmc.UTC_Date_ddMMyyyy(); + if (last_gps_date.compareTo(date)!=0) { + last_gps_date = date; + setUTC_Date(datarmc.UTC_Day, datarmc.UTC_Month, datarmc.UTC_Year); + raise_newgpsdate(last_gps_date); + } + if (last_gps_latitude != datarmc.Latitude) { + last_gps_latitude = datarmc.Latitude; + raise_newgpslatitude(last_gps_latitude); + } + if (last_gps_longitude != datarmc.Longitude) { + last_gps_longitude = datarmc.Longitude; + raise_newgpslongitude(last_gps_longitude); + } + } + return; + } + + // ini tidak perlu, gak penting + /* + * if (gpsmsg.startsWith("$GPVTG")) { GpsGPVTG datavtg = new GpsGPVTG(gpsmsg); + * if (datavtg.isValid()) { raise_vtgdata(datavtg); } else { + * BA.Log(datavtg.invalidmsg); } return; } + */ + } + + private void raise_exception(String prefix, Exception e) { + raise_log(prefix+", Msg : "+e.getMessage()+", Caused : "+e.getCause()); + } + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_rawdata(String vv) { + if (need_rawdata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_rawdata", false, new Object[] {vv}); + } + + private void raise_ggadata(GpsGPGGA vv) { + if (need_ggadata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_ggadata", false, new Object[] {vv}); + } + + + + private void raise_glldata(GpsGPGLL vv) { + if (need_glldata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_glldata", false, new Object[] {vv}); + } + + + + private void raise_rmcdata(GpsGPRMC vv) { + if (need_rmcdata_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_rmcdata", false, new Object[] {vv}); + } + + + + private void raise_newgpstime(String value) { + if (need_newgpstime_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newgpstime", false, new Object[] {value}); + } + + private void raise_newgpsdate(String value) { + if (need_newgpsdate_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newgpsdate", false, new Object[] {value}); + } + + private void raise_newgpslatitude(double value) { + if (need_newgpslatitude_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newgpslatitude", false, new Object[] {value}); + } + + private void raise_newgpslongitude(double value) { + if (need_newgpslongitude_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newgpslongitude", false, new Object[] {value}); + } +} diff --git a/src/gps/PrayTime.java b/src/gps/PrayTime.java new file mode 100644 index 0000000..d80f74d --- /dev/null +++ b/src/gps/PrayTime.java @@ -0,0 +1,939 @@ +package gps; + + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.TimeZone; + +public class PrayTime { + + // ---------------------- Global Variables -------------------- + private int calcMethod; // caculation method + private int asrJuristic; // Juristic method for Asr + private int dhuhrMinutes; // minutes after mid-day for Dhuhr + private int adjustHighLats; // adjusting method for higher latitudes + private int timeFormat; // time format + private double lat; // latitude + private double lng; // longitude + private double timeZone; // time-zone + private double JDate; // Julian date + // ------------------------------------------------------------ + // Calculation Methods + private int Jafari; // Ithna Ashari + private int Karachi; // University of Islamic Sciences, Karachi + private int ISNA; // Islamic Society of North America (ISNA) + private int MWL; // Muslim World League (MWL) + private int Makkah; // Umm al-Qura, Makkah + private int Egypt; // Egyptian General Authority of Survey + private int Custom; // Custom Setting + private int Tehran; // Institute of Geophysics, University of Tehran + // Juristic Methods + private int Shafii; // Shafii (standard) + private int Hanafi; // Hanafi + // Adjusting Methods for Higher Latitudes + private int None; // No adjustment + private int MidNight; // middle of night + private int OneSeventh; // 1/7th of night + private int AngleBased; // angle/60th of night + // Time Formats + private int Time24; // 24-hour format + private int Time12; // 12-hour format + private int Time12NS; // 12-hour format with no suffix + private int Floating; // floating point number + // Time Names + private ArrayList timeNames; + private String InvalidTime; // The string used for invalid times + // --------------------- Technical Settings -------------------- + private int numIterations; // number of iterations needed to compute times + // ------------------- Calc Method Parameters -------------------- + private HashMap methodParams; + + /* + * this.methodParams[methodNum] = new Array(fa, ms, mv, is, iv); + * + * fa : fajr angle ms : maghrib selector (0 = angle; 1 = minutes after + * sunset) mv : maghrib parameter value (in angle or minutes) is : isha + * selector (0 = angle; 1 = minutes after maghrib) iv : isha parameter value + * (in angle or minutes) + */ + @SuppressWarnings("unused") + private double[] prayerTimesCurrent; + private int[] offsets; + + public PrayTime() { + // Initialize vars + + this.setCalcMethod(0); + this.setAsrJuristic(0); + this.setDhuhrMinutes(0); + this.setAdjustHighLats(1); + this.setTimeFormat(0); + + // Calculation Methods + this.setJafari(0); // Ithna Ashari + this.setKarachi(1); // University of Islamic Sciences, Karachi + this.setISNA(2); // Islamic Society of North America (ISNA) + this.setMWL(3); // Muslim World League (MWL) + this.setMakkah(4); // Umm al-Qura, Makkah + this.setEgypt(5); // Egyptian General Authority of Survey + this.setTehran(6); // Institute of Geophysics, University of Tehran + this.setCustom(7); // Custom Setting + + // Juristic Methods + this.setShafii(0); // Shafii (standard) + this.setHanafi(1); // Hanafi + + // Adjusting Methods for Higher Latitudes + this.setNone(0); // No adjustment + this.setMidNight(1); // middle of night + this.setOneSeventh(2); // 1/7th of night + this.setAngleBased(3); // angle/60th of night + + // Time Formats + this.setTime24(0); // 24-hour format + this.setTime12(1); // 12-hour format + this.setTime12NS(2); // 12-hour format with no suffix + this.setFloating(3); // floating point number + + // Time Names + timeNames = new ArrayList(); + timeNames.add("Fajr"); + timeNames.add("Sunrise"); + timeNames.add("Dhuhr"); + timeNames.add("Asr"); + timeNames.add("Sunset"); + timeNames.add("Maghrib"); + timeNames.add("Isha"); + + InvalidTime = "-----"; // The string used for invalid times + + // --------------------- Technical Settings -------------------- + + this.setNumIterations(1); // number of iterations needed to compute + // times + + // ------------------- Calc Method Parameters -------------------- + + // Tuning offsets {fajr, sunrise, dhuhr, asr, sunset, maghrib, isha} + offsets = new int[7]; + offsets[0] = 0; + offsets[1] = 0; + offsets[2] = 0; + offsets[3] = 0; + offsets[4] = 0; + offsets[5] = 0; + offsets[6] = 0; + + /* + * + * fa : fajr angle ms : maghrib selector (0 = angle; 1 = minutes after + * sunset) mv : maghrib parameter value (in angle or minutes) is : isha + * selector (0 = angle; 1 = minutes after maghrib) iv : isha parameter + * value (in angle or minutes) + */ + methodParams = new HashMap(); + + // Jafari + double[] Jvalues = {16,0,4,0,14}; + methodParams.put(Integer.valueOf(this.getJafari()), Jvalues); + + // Karachi + double[] Kvalues = {18,1,0,0,18}; + methodParams.put(Integer.valueOf(this.getKarachi()), Kvalues); + + // ISNA + double[] Ivalues = {15,1,0,0,15}; + methodParams.put(Integer.valueOf(this.getISNA()), Ivalues); + + // MWL + double[] MWvalues = {18,1,0,0,17}; + methodParams.put(Integer.valueOf(this.getMWL()), MWvalues); + + // Makkah + double[] MKvalues = {18.5,1,0,1,90}; + methodParams.put(Integer.valueOf(this.getMakkah()), MKvalues); + + // Egypt + double[] Evalues = {19.5,1,0,0,17.5}; + methodParams.put(Integer.valueOf(this.getEgypt()), Evalues); + + // Tehran + double[] Tvalues = {17.7,0,4.5,0,14}; + methodParams.put(Integer.valueOf(this.getTehran()), Tvalues); + + // Custom + double[] Cvalues = {20,0,0,0,18}; // fajar = 20 degree, magrib selector = angle, magrib parameter = 0, isha selector = angle, isha parameter = 18 degree + methodParams.put(Integer.valueOf(this.getCustom()), Cvalues); + + } + + // ---------------------- Trigonometric Functions ----------------------- + // range reduce angle in degrees. + private double fixangle(double a) { + + a = a - (360 * (Math.floor(a / 360.0))); + + a = a < 0 ? (a + 360) : a; + + return a; + } + + // range reduce hours to 0..23 + private double fixhour(double a) { + a = a - 24.0 * Math.floor(a / 24.0); + a = a < 0 ? (a + 24) : a; + return a; + } + + // radian to degree + private double radiansToDegrees(double alpha) { + return ((alpha * 180.0) / Math.PI); + } + + // deree to radian + private double DegreesToRadians(double alpha) { + return ((alpha * Math.PI) / 180.0); + } + + // degree sin + private double dsin(double d) { + return (Math.sin(DegreesToRadians(d))); + } + + // degree cos + private double dcos(double d) { + return (Math.cos(DegreesToRadians(d))); + } + + // degree tan + private double dtan(double d) { + return (Math.tan(DegreesToRadians(d))); + } + + // degree arcsin + private double darcsin(double x) { + double val = Math.asin(x); + return radiansToDegrees(val); + } + + // degree arccos + private double darccos(double x) { + double val = Math.acos(x); + return radiansToDegrees(val); + } + + // degree arctan + @SuppressWarnings("unused") + private double darctan(double x) { + double val = Math.atan(x); + return radiansToDegrees(val); + } + + // degree arctan2 + private double darctan2(double y, double x) { + double val = Math.atan2(y, x); + return radiansToDegrees(val); + } + + // degree arccot + private double darccot(double x) { + double val = Math.atan2(1.0, x); + return radiansToDegrees(val); + } + + // ---------------------- Time-Zone Functions ----------------------- + // compute local time-zone for a specific date + @SuppressWarnings("unused") + private double getTimeZone1() { + TimeZone timez = TimeZone.getDefault(); + double hoursDiff = (timez.getRawOffset() / 1000.0) / 3600; + return hoursDiff; + } + + // compute base time-zone of the system + @SuppressWarnings("unused") + private double getBaseTimeZone() { + TimeZone timez = TimeZone.getDefault(); + double hoursDiff = (timez.getRawOffset() / 1000.0) / 3600; + return hoursDiff; + + } + + // detect daylight saving in a given date + @SuppressWarnings("unused") + private double detectDaylightSaving() { + TimeZone timez = TimeZone.getDefault(); + double hoursDiff = timez.getDSTSavings(); + return hoursDiff; + } + + // ---------------------- Julian Date Functions ----------------------- + // calculate julian date from a calendar date + private double julianDate(int year, int month, int day) { + + if (month <= 2) { + year -= 1; + month += 12; + } + double A = Math.floor(year / 100.0); + + double B = 2 - A + Math.floor(A / 4.0); + + double JD = Math.floor(365.25 * (year + 4716)) + + Math.floor(30.6001 * (month + 1)) + day + B - 1524.5; + + return JD; + } + + // convert a calendar date to julian date (second method) + @SuppressWarnings("unused") + private double calcJD(int year, int month, int day) { + double J1970 = 2440588.0; + @SuppressWarnings("deprecation") + Date date = new Date(year, month-1, day); + + double ms = date.getTime(); // # of milliseconds since midnight Jan 1, + // 1970 + double days = Math.floor(ms / (1000.0 * 60.0 * 60.0 * 24.0)); + return J1970 + days - 0.5; + + } + + // ---------------------- Calculation Functions ----------------------- + // References: + // http://www.ummah.net/astronomy/saltime + // http://aa.usno.navy.mil/faq/docs/SunApprox.html + // compute declination angle of sun and equation of time + private double[] sunPosition(double jd) { + + double D = jd - 2451545; + double g = fixangle(357.529 + 0.98560028 * D); + double q = fixangle(280.459 + 0.98564736 * D); + double L = fixangle(q + (1.915 * dsin(g)) + (0.020 * dsin(2 * g))); + + // double R = 1.00014 - 0.01671 * [self dcos:g] - 0.00014 * [self dcos: + // (2*g)]; + double e = 23.439 - (0.00000036 * D); + double d = darcsin(dsin(e) * dsin(L)); + double RA = (darctan2((dcos(e) * dsin(L)), (dcos(L))))/ 15.0; + RA = fixhour(RA); + double EqT = q/15.0 - RA; + double[] sPosition = new double[2]; + sPosition[0] = d; + sPosition[1] = EqT; + + return sPosition; + } + + // compute equation of time + private double equationOfTime(double jd) { + double eq = sunPosition(jd)[1]; + return eq; + } + + // compute declination angle of sun + private double sunDeclination(double jd) { + double d = sunPosition(jd)[0]; + return d; + } + + // compute mid-day (Dhuhr, Zawal) time + private double computeMidDay(double t) { + double T = equationOfTime(this.getJDate() + t); + double Z = fixhour(12 - T); + return Z; + } + + // compute time for a given angle G + private double computeTime(double G, double t) { + + double D = sunDeclination(this.getJDate() + t); + double Z = computeMidDay(t); + double Beg = -dsin(G) - dsin(D) * dsin(this.getLat()); + double Mid = dcos(D) * dcos(this.getLat()); + double V = darccos(Beg/Mid)/15.0; + + return Z + (G > 90 ? -V : V); + } + + // compute the time of Asr + // Shafii: step=1, Hanafi: step=2 + private double computeAsr(double step, double t) { + double D = sunDeclination(this.getJDate() + t); + double G = -darccot(step + dtan(Math.abs(this.getLat() - D))); + return computeTime(G, t); + } + + // ---------------------- Misc Functions ----------------------- + // compute the difference between two times + private double timeDiff(double time1, double time2) { + return fixhour(time2 - time1); + } + + // -------------------- Interface Functions -------------------- + // return prayer times for a given date + public ArrayList getDatePrayerTimes(int year, int month, int day, + double latitude, double longitude, double tZone) { + this.setLat(latitude); + this.setLng(longitude); + this.setTimeZone(tZone); + this.setJDate(julianDate(year, month, day)); + double lonDiff = longitude / (15.0 * 24.0); + this.setJDate(this.getJDate() - lonDiff); + return computeDayTimes(); + } + + // return prayer times for a given date + public ArrayList getPrayerTimes(Calendar date, double latitude, + double longitude, double tZone) { + + int year = date.get(Calendar.YEAR); + int month = date.get(Calendar.MONTH); + int day = date.get(Calendar.DATE); + + return getDatePrayerTimes(year, month+1, day, latitude, longitude, tZone); + } + + // set custom values for calculation parameters + private void setCustomParams(double[] params) { + + for (int i = 0; i < 5; i++) { + if (params[i] == -1) { + params[i] = methodParams.get(this.getCalcMethod())[i]; + methodParams.put(this.getCustom(), params); + } else { + methodParams.get(this.getCustom())[i] = params[i]; + } + } + this.setCalcMethod(this.getCustom()); + } + + // set the angle for calculating Fajr + public void setFajrAngle(double angle) { + double[] params = {angle, -1, -1, -1, -1}; + setCustomParams(params); + } + + // set the angle for calculating Maghrib + public void setMaghribAngle(double angle) { + double[] params = {-1, 0, angle, -1, -1}; + setCustomParams(params); + + } + + // set the angle for calculating Isha + public void setIshaAngle(double angle) { + double[] params = {-1, -1, -1, 0, angle}; + setCustomParams(params); + + } + + // set the minutes after Sunset for calculating Maghrib + public void setMaghribMinutes(double minutes) { + double[] params = {-1, 1, minutes, -1, -1}; + setCustomParams(params); + + } + + // set the minutes after Maghrib for calculating Isha + public void setIshaMinutes(double minutes) { + double[] params = {-1, -1, -1, 1, minutes}; + setCustomParams(params); + + } + + // convert double hours to 24h format + public String floatToTime24(double time) { + + String result; + + if (Double.isNaN(time)) { + return InvalidTime; + } + + time = fixhour(time + 0.5 / 60.0); // add 0.5 minutes to round + int hours = (int)Math.floor(time); + double minutes = Math.floor((time - hours) * 60.0); + + if ((hours >= 0 && hours <= 9) && (minutes >= 0 && minutes <= 9)) { + result = "0" + hours + ":0" + Math.round(minutes); + } else if ((hours >= 0 && hours <= 9)) { + result = "0" + hours + ":" + Math.round(minutes); + } else if ((minutes >= 0 && minutes <= 9)) { + result = hours + ":0" + Math.round(minutes); + } else { + result = hours + ":" + Math.round(minutes); + } + return result; + } + + // convert double hours to 12h format + public String floatToTime12(double time, boolean noSuffix) { + + if (Double.isNaN(time)) { + return InvalidTime; + } + + time = fixhour(time + 0.5 / 60); // add 0.5 minutes to round + int hours = (int)Math.floor(time); + double minutes = Math.floor((time - hours) * 60); + String suffix, result; + if (hours >= 12) { + suffix = "pm"; + } else { + suffix = "am"; + } + hours = ((((hours+ 12) -1) % (12))+ 1); + /*hours = (hours + 12) - 1; + int hrs = (int) hours % 12; + hrs += 1;*/ + if (noSuffix == false) { + if ((hours >= 0 && hours <= 9) && (minutes >= 0 && minutes <= 9)) { + result = "0" + hours + ":0" + Math.round(minutes) + " " + + suffix; + } else if ((hours >= 0 && hours <= 9)) { + result = "0" + hours + ":" + Math.round(minutes) + " " + suffix; + } else if ((minutes >= 0 && minutes <= 9)) { + result = hours + ":0" + Math.round(minutes) + " " + suffix; + } else { + result = hours + ":" + Math.round(minutes) + " " + suffix; + } + + } else { + if ((hours >= 0 && hours <= 9) && (minutes >= 0 && minutes <= 9)) { + result = "0" + hours + ":0" + Math.round(minutes); + } else if ((hours >= 0 && hours <= 9)) { + result = "0" + hours + ":" + Math.round(minutes); + } else if ((minutes >= 0 && minutes <= 9)) { + result = hours + ":0" + Math.round(minutes); + } else { + result = hours + ":" + Math.round(minutes); + } + } + return result; + + } + + // convert double hours to 12h format with no suffix + public String floatToTime12NS(double time) { + return floatToTime12(time, true); + } + + // ---------------------- Compute Prayer Times ----------------------- + // compute prayer times at given julian date + private double[] computeTimes(double[] times) { + + double[] t = dayPortion(times); + + double Fajr = this.computeTime( + 180 - methodParams.get(this.getCalcMethod())[0], t[0]); + + double Sunrise = this.computeTime(180 - 0.833, t[1]); + + double Dhuhr = this.computeMidDay(t[2]); + double Asr = this.computeAsr(1 + this.getAsrJuristic(), t[3]); + double Sunset = this.computeTime(0.833, t[4]); + + double Maghrib = this.computeTime( + methodParams.get(this.getCalcMethod())[2], t[5]); + double Isha = this.computeTime( + methodParams.get(this.getCalcMethod())[4], t[6]); + + double[] CTimes = {Fajr, Sunrise, Dhuhr, Asr, Sunset, Maghrib, Isha}; + + return CTimes; + + } + + // compute prayer times at given julian date + private ArrayList computeDayTimes() { + double[] times = {5, 6, 12, 13, 18, 18, 18}; // default times + + for (int i = 1; i <= this.getNumIterations(); i++) { + times = computeTimes(times); + } + + times = adjustTimes(times); + times = tuneTimes(times); + + return adjustTimesFormat(times); + } + + // adjust times in a prayer time array + private double[] adjustTimes(double[] times) { + for (int i = 0; i < times.length; i++) { + times[i] += this.getTimeZone() - this.getLng() / 15; + } + + times[2] += this.getDhuhrMinutes() / 60; // Dhuhr + if (methodParams.get(this.getCalcMethod())[1] == 1) // Maghrib + { + times[5] = times[4] + methodParams.get(this.getCalcMethod())[2]/ 60; + } + if (methodParams.get(this.getCalcMethod())[3] == 1) // Isha + { + times[6] = times[5] + methodParams.get(this.getCalcMethod())[4]/ 60; + } + + if (this.getAdjustHighLats() != this.getNone()) { + times = adjustHighLatTimes(times); + } + + return times; + } + + // convert times array to given time format + private ArrayList adjustTimesFormat(double[] times) { + + ArrayList result = new ArrayList(); + + if (this.getTimeFormat() == this.getFloating()) { + for (double time : times) { + result.add(String.valueOf(time)); + } + return result; + } + + for (int i = 0; i < 7; i++) { + if (this.getTimeFormat() == this.getTime12()) { + result.add(floatToTime12(times[i], false)); + } else if (this.getTimeFormat() == this.getTime12NS()) { + result.add(floatToTime12(times[i], true)); + } else { + result.add(floatToTime24(times[i])); + } + } + return result; + } + + // adjust Fajr, Isha and Maghrib for locations in higher latitudes + private double[] adjustHighLatTimes(double[] times) { + double nightTime = timeDiff(times[4], times[1]); // sunset to sunrise + + // Adjust Fajr + double FajrDiff = nightPortion(methodParams.get(this.getCalcMethod())[0]) * nightTime; + + if (Double.isNaN(times[0]) || timeDiff(times[0], times[1]) > FajrDiff) { + times[0] = times[1] - FajrDiff; + } + + // Adjust Isha + double IshaAngle = (methodParams.get(this.getCalcMethod())[3] == 0) ? methodParams.get(this.getCalcMethod())[4] : 18; + double IshaDiff = this.nightPortion(IshaAngle) * nightTime; + if (Double.isNaN(times[6]) || this.timeDiff(times[4], times[6]) > IshaDiff) { + times[6] = times[4] + IshaDiff; + } + + // Adjust Maghrib + double MaghribAngle = (methodParams.get(this.getCalcMethod())[1] == 0) ? methodParams.get(this.getCalcMethod())[2] : 4; + double MaghribDiff = nightPortion(MaghribAngle) * nightTime; + if (Double.isNaN(times[5]) || this.timeDiff(times[4], times[5]) > MaghribDiff) { + times[5] = times[4] + MaghribDiff; + } + + return times; + } + + // the night portion used for adjusting times in higher latitudes + private double nightPortion(double angle) { + double calc = 0; + + if (adjustHighLats == AngleBased) + calc = (angle)/60.0; + else if (adjustHighLats == MidNight) + calc = 0.5; + else if (adjustHighLats == OneSeventh) + calc = 0.14286; + + return calc; + } + + // convert hours to day portions + private double[] dayPortion(double[] times) { + for (int i = 0; i < 7; i++) { + times[i] /= 24; + } + return times; + } + + // Tune timings for adjustments + // Set time offsets + public void tune(int[] offsetTimes) { + + for (int i = 0; i < offsetTimes.length; i++) { // offsetTimes length + // should be 7 in order + // of Fajr, Sunrise, + // Dhuhr, Asr, Sunset, + // Maghrib, Isha + this.offsets[i] = offsetTimes[i]; + } + } + + private double[] tuneTimes(double[] times) { + for (int i = 0; i < times.length; i++) { + times[i] = times[i] + this.offsets[i] / 60.0; + } + + return times; + } + + /** + * @param args + */ + /* + * public static void main(String[] args) { double latitude = -37.823689; double + * longitude = 145.121597; double timezone = 10; // Test Prayer times here + * PrayTime prayers = new PrayTime(); + * + * prayers.setTimeFormat(prayers.Time12); prayers.setCalcMethod(prayers.Jafari); + * prayers.setAsrJuristic(prayers.Shafii); + * prayers.setAdjustHighLats(prayers.AngleBased); int[] offsets = {0, 0, 0, 0, + * 0, 0, 0}; // {Fajr,Sunrise,Dhuhr,Asr,Sunset,Maghrib,Isha} + * prayers.tune(offsets); + * + * Date now = new Date(); Calendar cal = Calendar.getInstance(); + * cal.setTime(now); + * + * ArrayList prayerTimes = prayers.getPrayerTimes(cal, latitude, + * longitude, timezone); ArrayList prayerNames = prayers.getTimeNames(); + * + * for (int i = 0; i < prayerTimes.size(); i++) { + * System.out.println(prayerNames.get(i) + " - " + prayerTimes.get(i)); } + * + * } + */ + + public int getCalcMethod() { + return calcMethod; + } + + public void setCalcMethod(int calcMethod) { + this.calcMethod = calcMethod; + } + + public int getAsrJuristic() { + return asrJuristic; + } + + public void setAsrJuristic(int asrJuristic) { + this.asrJuristic = asrJuristic; + } + + public int getDhuhrMinutes() { + return dhuhrMinutes; + } + + public void setDhuhrMinutes(int dhuhrMinutes) { + this.dhuhrMinutes = dhuhrMinutes; + } + + public int getAdjustHighLats() { + return adjustHighLats; + } + + public void setAdjustHighLats(int adjustHighLats) { + this.adjustHighLats = adjustHighLats; + } + + public int getTimeFormat() { + return timeFormat; + } + + public void setTimeFormat(int timeFormat) { + this.timeFormat = timeFormat; + } + + public double getLat() { + return lat; + } + + public void setLat(double lat) { + this.lat = lat; + } + + public double getLng() { + return lng; + } + + public void setLng(double lng) { + this.lng = lng; + } + + public double getTimeZone() { + return timeZone; + } + + public void setTimeZone(double timeZone) { + this.timeZone = timeZone; + } + + public double getJDate() { + return JDate; + } + + public void setJDate(double jDate) { + JDate = jDate; + } + + int getJafari() { + return Jafari; + } + + private void setJafari(int jafari) { + Jafari = jafari; + } + + int getKarachi() { + return Karachi; + } + + private void setKarachi(int karachi) { + Karachi = karachi; + } + + int getISNA() { + return ISNA; + } + + private void setISNA(int iSNA) { + ISNA = iSNA; + } + + int getMWL() { + return MWL; + } + + private void setMWL(int mWL) { + MWL = mWL; + } + + int getMakkah() { + return Makkah; + } + + private void setMakkah(int makkah) { + Makkah = makkah; + } + + int getEgypt() { + return Egypt; + } + + private void setEgypt(int egypt) { + Egypt = egypt; + } + + int getCustom() { + return Custom; + } + + private void setCustom(int custom) { + Custom = custom; + } + + int getTehran() { + return Tehran; + } + + private void setTehran(int tehran) { + Tehran = tehran; + } + + int getShafii() { + return Shafii; + } + + private void setShafii(int shafii) { + Shafii = shafii; + } + + + int getHanafi() { + return Hanafi; + } + + private void setHanafi(int hanafi) { + Hanafi = hanafi; + } + + int getNone() { + return None; + } + + private void setNone(int none) { + None = none; + } + + + int getMidNight() { + return MidNight; + } + + private void setMidNight(int midNight) { + MidNight = midNight; + } + + + int getOneSeventh() { + return OneSeventh; + } + + private void setOneSeventh(int oneSeventh) { + OneSeventh = oneSeventh; + } + + int getAngleBased() { + return AngleBased; + } + + private void setAngleBased(int angleBased) { + AngleBased = angleBased; + } + + + int getTime24() { + return Time24; + } + + private void setTime24(int time24) { + Time24 = time24; + } + + int getTime12() { + return Time12; + } + + private void setTime12(int time12) { + Time12 = time12; + } + + private int getTime12NS() { + return Time12NS; + } + + private void setTime12NS(int time12ns) { + Time12NS = time12ns; + } + + private int getFloating() { + return Floating; + } + + private void setFloating(int floating) { + Floating = floating; + } + + private int getNumIterations() { + return numIterations; + } + + private void setNumIterations(int numIterations) { + this.numIterations = numIterations; + } + + public ArrayList getTimeNames() { + return timeNames; + } +} \ No newline at end of file diff --git a/src/hid/HidDevice.java b/src/hid/HidDevice.java new file mode 100644 index 0000000..42b85df --- /dev/null +++ b/src/hid/HidDevice.java @@ -0,0 +1,202 @@ +package hid; + +import org.hid4java.jna.HidApi; +import org.hid4java.jna.HidDeviceStructure; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("HidDevice") + +public class HidDevice { + private final HidDeviceStructure hidDeviceStructure; + private final int vendorId; + private final int productId; + private final String serialNumber; + + public HidDevice(HidDeviceStructure hidDeviceStructure, int vendorId, int productId, String serialNumber) { + + this.hidDeviceStructure = hidDeviceStructure; + + this.vendorId = vendorId; + this.productId = productId; + this.serialNumber = serialNumber; + } + + /** + * @return A unique device ID made up from vendor ID, product ID and serial number + */ + public String getId() { + + return "" + vendorId + "_" + productId + (serialNumber == null ? "" : "_" + serialNumber); + + } + + /** + * @return The vendor ID + */ + public int getVendorId() { + return vendorId; + } + + /** + * @return The product ID + */ + public int getProductId() { + return productId; + } + + /** + * @return The serial number or null + */ + public String getSerialNumber() { + return serialNumber; + } + + public int write(byte[] message, int packetLength, byte reportId) { + return HidApi.write(hidDeviceStructure, message, packetLength, reportId); + } + + public String getLastErrorMessage() { + return HidApi.getLastErrorMessage(hidDeviceStructure); + } + + /** + *

Set the device handle to be non-blocking

+ * + *

In non-blocking mode calls to hid_read() will return immediately with a value of 0 if there is no data to be read. + * In blocking mode, hid_read() will wait (block) until there is data to read before returning

+ * + *

Non-blocking can be turned on and off at any time

+ * + * @param nonBlocking True if non-blocking mode is required + */ + public void setNonBlocking(boolean nonBlocking) { + HidApi.setNonBlocking(hidDeviceStructure, nonBlocking); + } + + /** + *

Read an Input report from a HID device

+ *

Input reports are returned to the host through the INTERRUPT IN endpoint. The first byte + * will contain the Report number if the device uses numbered reports

+ * + * @param data The buffer to read into + * + * @return The actual number of bytes read and -1 on error. If no packet was available to be read + * and the handle is in non-blocking mode, this function returns 0. + */ + public int read(byte[] data) { + return HidApi.read(hidDeviceStructure, data); + } + + /** + *

Read an Input report from a HID device with timeout

+ * + * @param bytes The buffer to read into + * @param timeoutMillis The number of milliseconds to wait before giving up + * + * @return The actual number of bytes read and -1 on error. If no packet was available to be read within + * the timeout period returns 0. + */ + public int read(byte[] bytes, int timeoutMillis) { + + return HidApi.read(hidDeviceStructure, bytes, timeoutMillis); + + } + + /** + *

Get a feature report from a HID device

+ *

Under the covers the HID library will set the first byte of data[] to the Report ID of the report to be read. + * Upon return, the first byte will still contain the Report ID, and the report data will start in data[1]

+ *

This method handles all the wide string and array manipulation for you

+ * + * @param data The buffer to contain the report + * @param reportId The report ID (or (byte) 0x00) + * + * @return The number of bytes read plus one for the report ID (which has been removed from the first byte), or -1 on error. + */ + public int getFeatureReport(byte[] data, byte reportId) { + return HidApi.getFeatureReport(hidDeviceStructure, data, reportId); + } + + /** + *

Send a Feature report to the device

+ * + *

Under the covers, feature reports are sent over the Control endpoint as a Set_Report transfer. + * The first byte of data[] must contain the Report ID. For devices which only support a single report, + * this must be set to 0x0. The remaining bytes contain the report data

+ *

Since the Report ID is mandatory, calls to hid_send_feature_report() will always contain one more byte than + * the report contains. For example, if a hid report is 16 bytes long, 17 bytes must be passed to + * hid_send_feature_report(): the Report ID (or 0x0, for devices which do not use numbered reports), followed by + * the report data (16 bytes). In this example, the length passed in would be 17

+ * + *

This method handles all the array manipulation for you

+ * + * @param data The feature report data (will be widened and have the report ID pre-pended) + * @param reportId The report ID (or (byte) 0x00) + * + * @return This function returns the actual number of bytes written and -1 on error. + */ + public int sendFeatureReport(byte[] data, byte reportId) { + return HidApi.sendFeatureReport(hidDeviceStructure, data, reportId); + } + + /** + *

Get a string from a HID device, based on its string index

+ * + * @param index The index + * + * @return The string + */ + public String getIndexedString(int index) { + return HidApi.getIndexedString(hidDeviceStructure, index); + } + + /** + *

Close this device

+ */ + public void close() { + HidApi.close(hidDeviceStructure); + } + + + @Override + public String toString() { + return "HidDevice{" + + " vendorId=" + Integer.toHexString(vendorId) + + ", productId=" + Integer.toHexString(productId) + + ", serialNumber='" + serialNumber + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + HidDevice hidDevice = (HidDevice) o; + + if (productId != hidDevice.productId) { + return false; + } + if (vendorId != hidDevice.vendorId) { + return false; + } + if (serialNumber != null ? !serialNumber.equals(hidDevice.serialNumber) : hidDevice.serialNumber != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = vendorId; + result = 31 * result + productId; + result = 31 * result + (serialNumber != null ? serialNumber.hashCode() : 0); + return result; + } +} diff --git a/src/hid/HidDeviceInfo.java b/src/hid/HidDeviceInfo.java new file mode 100644 index 0000000..4d52fa0 --- /dev/null +++ b/src/hid/HidDeviceInfo.java @@ -0,0 +1,212 @@ +package hid; + +import org.hid4java.jna.HidDeviceInfoStructure; + +import com.sun.jna.WString; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("HidDeviceInfo") + +public class HidDeviceInfo { + private String path; + private short vendorId; + private short productId; + private WString serialNumber; + + private short releaseNumber; + private WString manufacturerString; + + private WString productString; + + private short usagePage; + private short usage; + + private int interfaceNumber; + + /** + * @param hidDeviceInfoStructure The HidDeviceInfoStructure providing the data + */ + public HidDeviceInfo(HidDeviceInfoStructure hidDeviceInfoStructure) { + + // Copy the contents across + this.path = hidDeviceInfoStructure.path; + this.vendorId = hidDeviceInfoStructure.vendor_id; + this.productId = hidDeviceInfoStructure.product_id; + this.serialNumber = hidDeviceInfoStructure.serial_number; + this.releaseNumber = hidDeviceInfoStructure.release_number; + this.manufacturerString = hidDeviceInfoStructure.manufacturer_string; + this.productString = hidDeviceInfoStructure.product_string; + this.usagePage = hidDeviceInfoStructure.usage_page; + this.usage = hidDeviceInfoStructure.usage; + this.interfaceNumber = hidDeviceInfoStructure.interface_number; + + } + + /** + * @return The USB path + */ + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + /** + * @return The Vendor ID + */ + public short getVendorId() { + return vendorId; + } + + public void setVendorId(short vendorId) { + this.vendorId = vendorId; + } + + /** + * @return The product ID + */ + public short getProductId() { + return productId; + } + + public void setProductId(short productId) { + this.productId = productId; + } + + /** + * @return The serial number (wide string) + */ + public WString getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(WString serialNumber) { + this.serialNumber = serialNumber; + } + + /** + * @return The release number + */ + public short getReleaseNumber() { + return releaseNumber; + } + + public void setReleaseNumber(short releaseNumber) { + this.releaseNumber = releaseNumber; + } + + /** + * @return The manufacturer string + */ + public WString getManufacturerString() { + return manufacturerString; + } + + public void setManufacturerString(WString manufacturerString) { + this.manufacturerString = manufacturerString; + } + + /** + * @return The product info for this Device/Interface (Windows/Mac only) + */ + public WString getProductString() { + return productString; + } + + public void setProductString(WString productString) { + this.productString = productString; + } + + /** + * @return The usage page for this Device/Interface (Windows/Mac only) + */ + public short getUsagePage() { + return usagePage; + } + + public void setUsagePage(short usagePage) { + this.usagePage = usagePage; + } + + /** + * @return The usage number + */ + public short getUsage() { + return usage; + } + + public void setUsage(short usage) { + this.usage = usage; + } + + /** + * @return The USB interface number + */ + public int getInterfaceNumber() { + return interfaceNumber; + } + + public void setInterfaceNumber(int interfaceNumber) { + this.interfaceNumber = interfaceNumber; + } + + /** + * @return A unique device ID made up from vendor ID, product ID and serial number + */ + public String getId() { + + return "" + vendorId + "_" + productId + (serialNumber == null ? "" : "_" + serialNumber) + "_" + interfaceNumber; + + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + HidDeviceInfo that = (HidDeviceInfo) o; + + if (productId != that.productId) { + return false; + } + if (vendorId != that.vendorId) { + return false; + } + if (serialNumber != null ? !serialNumber.equals(that.serialNumber) : that.serialNumber != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = (int) vendorId; + result = 31 * result + (int) productId; + result = 31 * result + (serialNumber != null ? serialNumber.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "HidDeviceInfo{" + + "path='" + path + '\'' + + ", vendorId=" + Integer.toHexString(vendorId) + + ", productId=" + Integer.toHexString(productId) + + ", serialNumber=" + serialNumber + + ", releaseNumber=" + releaseNumber + + ", manufacturerString=" + manufacturerString + + ", productString=" + productString + + ", usagePage=" + usagePage + + ", usage=" + usage + + ", interfaceNumber=" + interfaceNumber + + '}'; + } +} diff --git a/src/hid/jHID.java b/src/hid/jHID.java new file mode 100644 index 0000000..2a55756 --- /dev/null +++ b/src/hid/jHID.java @@ -0,0 +1,126 @@ +package hid; + +import org.hid4java.jna.HidApi; +import org.hid4java.jna.HidDeviceInfoStructure; +import org.hid4java.jna.HidDeviceStructure; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; +import jGPIO.mycodes; + +@BA.ShortName("jHID") +@BA.Events(values= { + "log(msg as string)" +}) +public class jHID { + + private BA ba; + private String event; + private Object Me; + + private boolean initialized = false; + private boolean need_log_event =false; + + /** + * Initialize jHID + * @param eventname Evantname + */ + public void Initialize(BA ba, String eventname) { + this.ba = ba; + this.event = eventname; + Me = this; + + check_events(); + init_hidapi(); + initialized = true; + } + + /** + * Check if jHID is initialized or not + * @return true if already initialized + */ + public boolean IsInitialized() { + return initialized; + } + + /** + * Get all attached Hid Device + * @return List of HidDeviceInfo object + */ + public List getAttachedDevices() { + return getAttachedDevices(0,0); + } + + /** + * Get all attached Hid Device with specific Vendor and Product ID + * @param vendorid specific vendor id + * @param productid specific product id + * @return List of HidDeviceInfo object + */ + public List getAttachedDevices(final int vendorid, final int productid) { + List result = new List(); + + HidDeviceInfoStructure root = HidApi.enumerateDevices(vendorid, productid); + if (root!=null) { + HidDeviceInfoStructure hid = root; + do { + result.Add(new HidDeviceInfo(hid)); + hid = hid.next(); + } while(hid != null); + + HidApi.freeEnumeration(root); + } + + return result; + } + + /** + * Open a HidDevice + * @param vendorId Specific Vendor ID + * @param productId specific Product Id + * @param serialNumber specific serial Number + * @return null if failed + */ + public HidDevice open(final int vendorId, final int productId, final String serialNumber) { + HidDeviceStructure devstruc = HidApi.open(vendorId, productId, serialNumber); + if (devstruc!=null) { + return new HidDevice(devstruc, vendorId, productId, serialNumber); + } else raise_log("open failed, unable to open HidDeviceStructure"); + return null; + } + + /** + * Open a HidDevice + * @param info HidDeviceInfo object + * @return null if failed + */ + public HidDevice open(HidDeviceInfo info) { + if (info!=null) { + if (mycodes.ValidString(info.getPath())) { + HidDeviceStructure devstruc = HidApi.open(info.getPath()); + if (devstruc != null) { + return new HidDevice(devstruc, info.getVendorId(), info.getProductId(), info.getSerialNumber().toString()); + } else raise_log("open failed, unable to open HidDeviceStructure"); + } else raise_log("open failed, HidDeviceInfo path is invalid"); + } else raise_log("open failed, HidDeviceInfo is null"); + return null; + } + + + + private void init_hidapi() { + HidApi.init(); + } + + private void check_events() { + if (ba!=null) { + if (mycodes.ValidString(event)) { + need_log_event = ba.subExists(event+"_log"); + } + } + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } +} diff --git a/src/infrared/InfraredReceiver.java b/src/infrared/InfraredReceiver.java new file mode 100644 index 0000000..c6ef50e --- /dev/null +++ b/src/infrared/InfraredReceiver.java @@ -0,0 +1,201 @@ +package infrared; + +import com.sun.jna.Platform; + +import anywheresoftware.b4a.BA; +import jGPIO.mycodes; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Arrays; +import java.io.BufferedReader; +@BA.ShortName("InfraredReceiver") +@BA.Events(values= { + "log(msg as string)", + "newvalue(value as int)", + "rawbytes(bb() as byte)" +}) + +public class InfraredReceiver { + + + private BA ba; + private String event; + private Object Me; + private boolean inited = false; + private boolean need_log_event = false; + private boolean need_newvalue_event = false; + private boolean need_rawbytes_event = false; + + private String infrared_event0 = null; + private boolean continue_reading = false; + + public void Initialize(BA ba, String eventname) { + this.ba = ba; + this.event = eventname; + Me = this; + check_events(); + check_device(); + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + System.out.println("InfraredReceiver ShutdownHook started"); + close(); + } + }); + } + + /** + * Open infrared device with specific protocol + * Protocol options : nec, jvc, sony, sanyo, sharp, xmp, lirc, rc-5, rc-6, rc-5-sz + * If dont know, use "all" for protocol for testing, but will not raise event newvalue + * @param protocol : Specific protocol decoding + * @return true if success + */ + public boolean open(final String protocol) { + if (mycodes.ValidString(protocol)) { + if (inited) { + try { + FileInputStream buf = new FileInputStream(infrared_event0); + Thread tt = new Thread(new InputEventReading(protocol,buf)); + tt.start(); + return true; + } catch (FileNotFoundException | SecurityException e) { + raise_log("Open failed, exception = "+e.getMessage()); + } + } else raise_log("Open failed, must call Initialize first"); + } else raise_log("Open failed, protocol invalid"); + return false; + } + + public void close() { + continue_reading = false; + } + + public boolean IsInitialized() { + return inited; + } + + private void check_device() { + inited = false; + infrared_event0 = null; + final String major_start = "MAJOR="; + final String minor_start = "MINOR="; + final String devname_start = "DEVNAME="; + String major = null; + String minor = null; + String devname = null; + if (Platform.isLinux()) { + File rc0 = new File("/sys/class/rc/rc0"); + if (rc0.isDirectory()) { + File uevent_event0 = new File(rc0, "input0/event0/uevent"); + if (uevent_event0.isFile()) { + try(BufferedReader br = new BufferedReader(new FileReader(uevent_event0))) { + ; + String xx = null; + do { + xx = br.readLine(); + if (xx!=null) { + if (xx.startsWith(major_start)) { + major = xx.substring(major_start.length()); + } else if (xx.startsWith(minor_start)) { + minor = xx.substring(minor_start.length()); + } else if (xx.startsWith(devname_start)) { + devname = xx.substring(devname_start.length()); + } + } + } while (xx!=null); + } catch (IOException e) { + raise_log("InfraredReader unable to read inputx/eventx/uevent"); + } + + if (mycodes.ValidString(major)) { + if (mycodes.ValidString(minor)) { + if (mycodes.ValidString(devname)) { + inited = true; + infrared_event0 = "/dev/"+devname; + raise_log(MessageFormat.format("Infrared device found at {0}", infrared_event0)); + } + } + } + } else raise_log("InfraredReceiver not finding inputx/eventx/uevent"); + } else raise_log("InfraredReceiver not finding possible device"); + } else raise_log("InfraredReceiver can only used in Linux based"); + } + + private void check_events() { + if (ba!=null) { + if (mycodes.ValidString(event)) { + need_log_event = ba.subExists(event+"_log"); + need_newvalue_event = ba.subExists(event+"_newvalue"); + need_rawbytes_event = ba.subExists(event+"_rawbytes"); + } + } + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_newvalue(int value) { + if (need_newvalue_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_newvalue", false, new Object[] {value}); + } + + private void raise_rawbytes(byte[] bb) { + if (need_rawbytes_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_rawbytes", false, new Object[] {bb}); + } + + private class InputEventReading implements Runnable{ + private final FileInputStream reader; + private final String protocol; + public InputEventReading(String protocol, FileInputStream br) { + this.reader = br; + this.protocol = protocol; + if (this.reader!=null && mycodes.ValidString(protocol)) { + continue_reading = true; + } + } + @Override + public void run() { + raise_log("InputEventReading started"); + while(continue_reading) { + if (reader==null) break; + byte[] xx = new byte[100]; + try { + int result = reader.read(xx); + if (result>0) { + byte[] raw = Arrays.copyOf(xx, result); + raise_rawbytes(raw); + raise_log(MessageFormat.format("Length = {0}, Hexadecimals = {1}", result, mycodes.Bytes_toHex(raw, " "))); + if (protocol.equalsIgnoreCase("nec")) { + if (result==32 && raw.length==result) { + // scancode ada di byte 12 dan 13, dalam fomat little endian + int value = raw[13] & 0xFF; + value <<= 8; + value |= (raw[12] & 0xFF); + raise_newvalue(value); + } + } + + } + } catch (IOException e) { + raise_log("Reader exception ="+e.getMessage()); + } + + + } + raise_log("InputEventReading finished"); + if (reader!=null) { + try { + reader.close(); + raise_log("Reader closed"); + } catch (IOException e) { + raise_log("Reader Close exception = "+e.getMessage()); + } + } + } + + } +} diff --git a/src/jGPIO/CRC16.java b/src/jGPIO/CRC16.java new file mode 100644 index 0000000..0e80547 --- /dev/null +++ b/src/jGPIO/CRC16.java @@ -0,0 +1,245 @@ +package jGPIO; + +/** + * CRC16_CCITT: Polynomial x16+x12+x5+1 (0x1021), initial value 0x0000, low bit first, high bit after, result is exclusive to 0x0000 + * CRC16_CCITT_FALSE: Polynomial x16+x12+x5+1 (0x1021), initial value 0xFFFF, low bit is after, high bit is first, result is exclusive to 0x0000 + * CRC16_XMODEM: Polynomial x16+x12+x5+1 (0x1021), initial value 0x0000, low bit after, high bit first, result is XOR0 + * CRC16_X25: Polynomial x16+x12+x5+1 (0x1021), initial value 0xffff, low bit first, high bit after, result is XORIF 0xFFFF + * CRC16_MODBUS: Polynomial x16+x15+x2+1 (0x8005), initial value 0xFFFF, low bit first, high bit after, result is X2000000 exclusive OR + * CRC16_IBM: Polynomial x16+x15+x2+1 (0x8005), initial value 0x0000, low bit first, high bit after, result is XOR0 + * CRC16_MAXIM: Polynomial x16+x15+x2+1 (0x8005), initial value 0x0000, low bit first, high bit after, result is XORIF 0xFFFF + * CRC16_USB: Polynomial x16+x15+x2+1 (0x8005), initial value 0xFFFF, low bit first, high bit after, result is XORIF 0xFFFF + *

+ * (1), preset a 16-bit register to hexadecimal FFFF (that is, all 1), call this register a CRC register; + * (2), the first 8-bit binary data (the first byte of the communication information frame) is different from the lower 8 bits of the 16-bit CRC register, the result is placed in the CRC register, the upper eight bits of data are not Change + * (3), shift the contents of the CRC register one bit to the right (toward the low position) to fill the highest bit with 0, and check the shifted out bit after the right shift; + * (4) If the shift bit is 0: repeat step 3 (shift one bit right again); if the shift bit is 1, the CRC register is XORed with the polynomial A001 (1010 0000 0000 0001); + * (5), repeat steps 3 and 4 until the right shift 8 times, so that the entire 8-bit data is processed; + * (6), repeat steps 2 to 5, and process the next byte of the communication information frame; + * (7), after all the bytes of the communication information frame are calculated according to the above steps, the high and low bytes of the obtained 16-bit CRC register are exchanged; + * (8), the final CRC register content is: CRC code. + *

+ * The polynomial 0xA001 in the above calculation step is the result of 0x8005 bitwise reversal. + * 0x8408 is the result of 0x1021 bitwise reversal. + * Online verification tool + * http://www.ip33.com/crc.html + * https://blog.csdn.net/htmlxx/article/details/17369105 + *

+ * Author:Water + * Time:2018/11/19 0019 15:03 + */ +public class CRC16 { + + /** + * CRC16_CCITT: Polynomial x16+x12+x5+1 (0x1021), initial value 0x0000, low bit first, high bit after, result is exclusive to 0x0000 + * 0x8408 is the result of 0x1021 bitwise reversal. + * @param buffer + * @return integer value + */ + public static int CRC16_CCITT(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0x8408; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } +// wCRCin=(wCRCin<<8)|(wCRCin>>8); +// wCRCin &= 0xffff; + return wCRCin ^= 0x0000; + + } + + /** + * CRC-CCITT (0xFFFF) + * CRC16_CCITT_FALSE: Polynomial x16+x12+x5+1 (0x1021), initial value 0xFFFF, low bit is after, high bit is first, result is exclusive to 0x0000 + * + * @param buffer + * @return integer value + */ + public static int CRC16_CCITT_FALSE(byte[] buffer) { + int wCRCin = 0xffff; + int wCPoly = 0x1021; + for (byte b : buffer) { + for (int i = 0; i < 8; i++) { + boolean bit = ((b >> (7 - i) & 1) == 1); + boolean c15 = ((wCRCin >> 15 & 1) == 1); + wCRCin <<= 1; + if (c15 ^ bit) + wCRCin ^= wCPoly; + } + } + wCRCin &= 0xffff; + return wCRCin ^= 0x0000; + } + + /** + * CRC-CCITT (XModem) + * CRC16_XMODEM: Polynomial x16+x12+x5+1 (0x1021), initial value 0x0000, low bit after, high bit first, result is XOR0 + * + * @param buffer + * @return integer value + */ + public static int CRC16_XMODEM(byte[] buffer) { + int wCRCin = 0x0000; // initial value 65535 + int wCPoly = 0x1021; // 0001 0000 0010 0001 (0, 5, 12) + for (byte b : buffer) { + for (int i = 0; i < 8; i++) { + boolean bit = ((b >> (7 - i) & 1) == 1); + boolean c15 = ((wCRCin >> 15 & 1) == 1); + wCRCin <<= 1; + if (c15 ^ bit) + wCRCin ^= wCPoly; + } + } + wCRCin &= 0xffff; + return wCRCin ^= 0x0000; + } + + + /** + * CRC16_X25: Polynomial x16+x12+x5+1 (0x1021), initial value 0xffff, low bit first, high bit after, result is XORIF 0xFFFF + * 0x8408 is the result of 0x1021 bitwise reversal. + * @param buffer + * @return integer value + */ + public static int CRC16_X25(byte[] buffer) { + int wCRCin = 0xffff; + int wCPoly = 0x8408; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } + + /** + * CRC-16 (Modbus) + * CRC16_MODBUS: Polynomial x16+x15+x2+1 (0x8005), initial value 0xFFFF, low bit first, high bit after, result is X2000000 exclusive OR + * 0xA001 is the result of 0x8005 bitwise reversal + * @param buffer + * @return integer value + */ + public static int CRC16_MODBUS(byte[] buffer) { + int wCRCin = 0xffff; + int POLYNOMIAL = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= POLYNOMIAL; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0x0000; + } + + /** + * CRC-16 + * CRC16_IBM: Polynomial x16+x15+x2+1 (0x8005), initial value 0x0000, low bit first, high bit after, result is XOR0 + * 0xA001 is the result of 0x8005 bitwise reversal + * @param buffer + * @return integer value + */ + public static int CRC16_IBM(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0x0000; + } + + /** + * CRC16_MAXIM: Polynomial x16+x15+x2+1 (0x8005), initial value 0x0000, low bit first, high bit after, result is XORIF 0xFFFF + * 0xA001 is the result of 0x8005 bitwise reversal + * @param buffer + * @return integer value + */ + public static int CRC16_MAXIM(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } + + /** + * CRC16_USB: Polynomial x16+x15+x2+1 (0x8005), initial value 0xFFFF, low bit first, high bit after, result is XORIF 0xFFFF + * 0xA001 is the result of 0x8005 bitwise reversal + * @param buffer + * @return integer value + */ + public static int CRC16_USB(byte[] buffer) { + int wCRCin = 0xFFFF; + int wCPoly = 0xa001; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } + + /** + * CRC16_DNP: Polynomial x16+x13+x12+x11+x10+x8+x6+x5+x2+1 (0x3D65), initial value 0x0000, low bit first, high bit after, result is XORIF 0xFFFF + * 0xA6BC is the result of 0x3D65 bitwise reversal + * @param buffer + * @return integer value + */ + public static int CRC16_DNP(byte[] buffer) { + int wCRCin = 0x0000; + int wCPoly = 0xA6BC; + for (byte b : buffer) { + wCRCin ^= ((int) b & 0x00ff); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x0001) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0xffff; + } +} diff --git a/src/jGPIO/DigitalInput/DigitalInput.java b/src/jGPIO/DigitalInput/DigitalInput.java new file mode 100644 index 0000000..4e0e8cf --- /dev/null +++ b/src/jGPIO/DigitalInput/DigitalInput.java @@ -0,0 +1,285 @@ +package jGPIO.DigitalInput; + + +import java.io.IOException; + +import anywheresoftware.b4a.BA; +import jGPIO.jGPIO; +import jGPIO.mycodes; + +@BA.ShortName("DigitalInput") +@BA.Events(values={"error(pinnumber as int, Msg as String)"}) + +public class DigitalInput { + private BA bax; + private String event=""; + private int pinnya=0; + private Object myobject; + + private boolean need_error_event = false; + + private int last_value = -1; + private boolean currentIsON = false; + private boolean initialized = false; + private DigitalInputEvent javaevent; + + /** + * Buat di JAVA coding + */ + public DigitalInput() { + // do nothing here, buat syarat + if (jGPIO.osname=="") jGPIO.detectOS(); + } + + /** + * Buat di JAVA coding + * @param pin_number : pin number + */ + public DigitalInput(int pin_number) { + if (jGPIO.osname=="") jGPIO.detectOS(); + pinnya = pin_number; + if (Setup_Pin()) initialized = true; + } + + /* + * Initialize Digital Input + * Return true if initialization success, or false if failed + */ + public boolean Initialize(BA xx, Object caller, String eventname,int pinnumber){ + bax = xx; + event=eventname; + pinnya = pinnumber; + myobject = caller; + + if (bax!=null) { + if (myobject!=null) { + if (!event.isEmpty()) { + need_error_event = bax.subExists(event+"_error"); + } + } + } + + + + initialized = false; + + if (jGPIO.IsLinux) { + if (Setup_Pin()) { + initialized = true; + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Release(); + } + }); + } else { + initialized = false; + } + } else { + raise_Error("System is not Linux based, or jGPIO function detectOS() has not been called"); + } + + return initialized; + + } + + private boolean Setup_Pin() { + if (mycodes.GPIO_Exist(pinnya)==false) { + // belum ada, coba export dulu + try { + if (mycodes.GPIO_Export(pinnya)==false) { + // gagal export + return false; + } + } catch (IOException e) { + BA.Log("Gagal Export pin "+pinnya+" Untuk DigitalInput"); + } + } + + // sampe sini, harusnya ada + try { + // harus set output dulu + if (mycodes.GPIO_SetDirection(pinnya, true)) { + // pasang value 1 + if (mycodes.GPIO_SetValue(pinnya, true)) { + // ubah jadi input + if (mycodes.GPIO_SetDirection(pinnya, false)) { + BA.Log("DigitalInput Setup_Pin success !!"); + return true; + } else BA.Log("Gagal SetDirection ke Input untuk DigitalInput pin "+pinnya); + } else BA.Log("Gagal SetValue ke high untuk DigitalInput pin "+pinnya); + } else BA.Log("Gagal SetDirection ke Output untuk DigitalInput pin "+pinnya); + + } catch (IOException e) { + BA.Log("Gagal SetDirection pin "+pinnya+", Msg : "+e.getMessage()); + } + return false; + } + + /* + private boolean Setup_Pin() { + if (mycodes.GPIO_Exist(pinnya)==false) { + // not exist, so export first + try { + if (mycodes.GPIO_Export(pinnya)==false) { + // could not export, pin can not use + raise_Error("DigitalInput pin="+pinnya+" GPIO_Export failed"); + return false; + } + } catch (IOException e) { + raise_Error("DigitalInput pin="+pinnya+" GPIO_Export error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + + // check existance after export + if (mycodes.GPIO_Exist(pinnya)==false) { + // still not exist, pin can not use + raise_Error("DigitalInput pin="+pinnya+" GPIO_Exist still failed after GPIO_Export"); + return false; + } + } + + + + // Revision 08/01/2020 + // to set as Input, firstly PIN must be SetDirection as OUT , and SetValue = 1. This need to be done to force internal pull up become 1 + // then SetDirection set as IN + + // force SetDirection to OUT + try { + boolean force1 = mycodes.GPIO_SetDirection(pinnya, true); + if (!force1) { + raise_Error("Failed to Force GPIO_SetDirection=out at pin="+pinnya); + return false; + } + } catch (IOException e1) { + raise_Error("DigitalInput pin="+pinnya+" Force GPIO_SetDirection=out is failed, Msg:"+e1.getMessage()+", Caused:"+e1.getCause()); + return false; + } + + // force SetValue to TRUE + try { + boolean force2 = mycodes.GPIO_SetValue(pinnya, true); + if (!force2) { + raise_Error("Failed to Force GPIO_SetValue=1 at pin="+pinnya); + return false; + } + } catch (IOException e1) { + raise_Error("DigitalInput pin="+pinnya+" Force GPIO_SetValue=1 is failed, Msg:"+e1.getMessage()+", Caused:"+e1.getCause()); + return false; + } + + // now SetDirection to IN + try { + boolean force3 = mycodes.GPIO_SetDirection(pinnya, false); + if (!force3) { + raise_Error("Failed to GPIO_SetDirection=in at pin="+pinnya); + return false; + } + } catch (IOException e1) { + raise_Error("DigitalInput pin="+pinnya+" GPIO_SetDirection=in is failed, Msg:"+e1.getMessage()+", Caused:"+e1.getCause()); + return false; + } + + + + last_value = -1; + try { + last_value = mycodes.GPIO_GetValue(pinnya); + } catch (IOException e) { + raise_Error("DigitalInput pin="+pinnya+" GPIO_GetValue #1 error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + + if (last_value==-1) { + // could not read from this GPIO + raise_Error("DigitalInput pin="+pinnya+" GPIO_GetValue #1 failed"); + return false; + } + + + if (last_value==0) + currentIsON = false; + else + currentIsON = true; + + return true; + } + */ + + @BA.Hide + public void SetJavaEvent(DigitalInputEvent xx) { + javaevent = xx; + } + + public boolean getIsInitialized(){ + return initialized; + } + + + /** + * Read State of GPIO + * @return 0 = Low, 1 = high, -1 = failed reading + */ + public int ReadState(){ + try { + int value = mycodes.GPIO_GetValue(pinnya); + if (value==0) { + currentIsON = false; + } else if (value==1) { + currentIsON = true; + } + + if (last_value != value) { + last_value = value; + if (javaevent!=null) { + if (last_value == 0) { + javaevent.newinstate(pinnya, false); + } else { + javaevent.newinstate(pinnya, true); + } + + } + } + + return value; + } catch (IOException e) { + raise_Error("DigitalInput pin="+pinnya+" ReadState error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return -1; + } + } + + + /* + * Get last ReadState value + * return true if High 1 + * return false if Low 0 + */ + public boolean getIsON(){ + return currentIsON; + } + + /* + * Release control of Digital Output (not controlling anymore) + * return true if success operation + */ + public boolean Release(){ + try { + return mycodes.GPIO_UnExport(pinnya); + } catch (IOException e) { + raise_Error("DigitalInput pin="+pinnya+" Release error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + } + + private void raise_Error(String msg) { + if (need_error_event) { + bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_error", false,new Object[]{pinnya,msg}); + } + if (javaevent!=null) { + javaevent.error(pinnya, msg); + } + + } + +} diff --git a/src/jGPIO/DigitalInput/DigitalInputEvent.java b/src/jGPIO/DigitalInput/DigitalInputEvent.java new file mode 100644 index 0000000..f8f1347 --- /dev/null +++ b/src/jGPIO/DigitalInput/DigitalInputEvent.java @@ -0,0 +1,6 @@ +package jGPIO.DigitalInput; + +public interface DigitalInputEvent { + void error(int pinnumber, String Msg ); + void newinstate(int pinnumber , boolean isOn); +} diff --git a/src/jGPIO/DigitalOutput/DigitalOutput.java b/src/jGPIO/DigitalOutput/DigitalOutput.java new file mode 100644 index 0000000..450cb4d --- /dev/null +++ b/src/jGPIO/DigitalOutput/DigitalOutput.java @@ -0,0 +1,227 @@ +package jGPIO.DigitalOutput; + + +import java.io.IOException; + +import anywheresoftware.b4a.BA; +import jGPIO.jGPIO; +import jGPIO.mycodes; + +@BA.ShortName("DigitalOutput") +@BA.Events(values={"error(pinnumber as int, Msg as String)"}) + +public class DigitalOutput { + private BA bax; + private String event=""; + private int pinnya=0; + private Object myobject; + private boolean need_error_event = false; + + + private boolean initialized = false; + + private DigitalOutputEvent javaevent; + + /** + * Buat di JAVA coding + */ + public DigitalOutput(){ + // do nothing here, cuma buat syarat + if (jGPIO.osname=="") jGPIO.detectOS(); + + } + + /** + * Buat di JAVA coding + * @param pin_number : pin number + * @param initialstate : true --> initial state = high, false --> initial state = low + */ + public DigitalOutput(int pin_number, boolean initialstate){ + pinnya = pin_number; + if (jGPIO.osname=="") jGPIO.detectOS(); + if (Setup_Pin(initialstate)) initialized = true; + } + + /* + * Initialize Digital Output + * Return true if initialization success, or false if failed + */ + public boolean Initialize(BA xx, Object caller, String eventname,int pinnumber, boolean initialstate){ + bax = xx; + event=eventname; + pinnya = pinnumber; + myobject = caller; + + if (bax!=null) { + if(myobject!=null) { + if (!event.isEmpty()) { + need_error_event = bax.subExists(event+"_error"); + } + } + } + + + + initialized = false; + if (jGPIO.IsLinux) { + if (Setup_Pin(initialstate)) { + initialized = true; + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Release(); + } + }); + return true; + + } else { + initialized = false; + } + } else { + raise_Error("System is not Linux based, or jGPIO function detectOS() has not been called"); + } + return false; + + + + } + + @BA.Hide + public void SetJavaEvent(DigitalOutputEvent xx) { + javaevent = xx; + } + + public boolean getIsInitialized(){ + return initialized; + } + + + + + private boolean Setup_Pin(boolean initialstate) { + if (mycodes.GPIO_Exist(pinnya)==false) { + // not exist, so export first + try { + if (mycodes.GPIO_Export(pinnya)==false) { + // could not export, pin can not use + raise_Error("DigitalOutput pin="+pinnya+" GPIO_Export failed"); + return false; + } + } catch (IOException e) { + raise_Error("DigitalOutput pin="+pinnya+" GPIO_Export error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + + // check existance after export + if (mycodes.GPIO_Exist(pinnya)==false) { + // still not exist, pin can not use + raise_Error("DigitalOutput pin="+pinnya+" GPIO_Exist still failed after GPIO_Export"); + return false; + } + } + + // Exported pin exist, now check if direction is correct + String currentdirection; + try { + currentdirection = mycodes.GPIO_GetDirection(pinnya).trim(); + + } catch (IOException e) { + raise_Error("DigitalOutput pin="+pinnya+" GPIO_GetDirection #1 error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + + if (currentdirection.startsWith("out")==false) { + // wrong direction, change direction + try { + if (mycodes.GPIO_SetDirection(pinnya, true)==false) { + // could not change direction, pin can not use + raise_Error("DigitalOutput pin="+pinnya+" GPIO_SetDirection failed"); + return false; + } + } catch (IOException e) { + raise_Error("DigitalOutput pin="+pinnya+" GPIO_SetDirection error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + + // check again after change direction + try { + currentdirection = mycodes.GPIO_GetDirection(pinnya).trim(); + + } catch (IOException e) { + raise_Error("DigitalOutput pin="+pinnya+" GPIO_GetDirection #2 error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + + if (currentdirection.startsWith("out")==false) { + // direction is not changing, pin can not use + raise_Error("DigitalOutput pin="+pinnya+" GPIO_GetDirection still not 'out' after GPIO_SetDirection"); + return false; + } + } + + // pin direction is correct + try { + if (mycodes.GPIO_SetValue(pinnya, initialstate)==false) { + // could not set Value, pin can not use + raise_Error("DigitalOutput pin="+pinnya+" GPIO_SetValue failed"); + return false; + } + } catch (IOException e) { + raise_Error("DigitalOutput pin="+pinnya+" GPIO_SetValue error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + + return true; + } + + + /* + * Set Digital Output to High + * return true if success operation + */ + public boolean SetHigh(){ + try { + return mycodes.GPIO_SetValue(pinnya, true); + } catch (IOException e) { + raise_Error("DigitalOutput pin="+pinnya+" SetHigh error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + } + + /* + * Set Digital Output to Low + * return true if success operation + */ + public boolean SetLow(){ + try { + return mycodes.GPIO_SetValue(pinnya, false); + } catch (IOException e) { + raise_Error("DigitalOutput pin="+pinnya+" SetLow error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + } + + /* + * Release control of Digital Output (not controlling anymore) + * return true if success operation + */ + public boolean Release(){ + try { + return mycodes.GPIO_UnExport(pinnya); + } catch (IOException e) { + raise_Error("DigitalOutput pin="+pinnya+" Release error, Msg:"+e.getMessage()+", Caused:"+e.getCause()); + return false; + } + } + + private void raise_Error(String msg) { + if (need_error_event) { + bax.raiseEventFromDifferentThread(myobject, null, 0, event+"_error", false,new Object[]{pinnya,msg}); + } + if (javaevent!=null) { + javaevent.error(pinnya, msg); + } + + } + +} diff --git a/src/jGPIO/DigitalOutput/DigitalOutputEvent.java b/src/jGPIO/DigitalOutput/DigitalOutputEvent.java new file mode 100644 index 0000000..d28984b --- /dev/null +++ b/src/jGPIO/DigitalOutput/DigitalOutputEvent.java @@ -0,0 +1,6 @@ +package jGPIO.DigitalOutput; + +public interface DigitalOutputEvent { + void error(int pinnumber, String Msg ); + void newoutstate(int pinnumber, boolean isOn); +} diff --git a/src/jGPIO/I2C/I2CException.java b/src/jGPIO/I2C/I2CException.java new file mode 100644 index 0000000..35ba11c --- /dev/null +++ b/src/jGPIO/I2C/I2CException.java @@ -0,0 +1,49 @@ +package jGPIO.I2C; + +import anywheresoftware.b4a.keywords.Bit; + +public class I2CException extends Exception{ + /** + * + */ + private static final long serialVersionUID = -8227969179418888752L; + public final byte bytes[]; + public I2CException(String msg) { + super(msg); + bytes = null; + } + public I2CException(String msg, final byte[] bytes) { + super(msg); + this.bytes = bytes; + } + public I2CException(String msg, byte command) { + super(msg); + this.bytes = new byte[] {command}; + } + + public I2CException(String function, String msg, byte command) { + super("Function="+function+", Message="+msg); + this.bytes = new byte[] {command}; + } + + public I2CException(String function, String msg, byte[] bytes) { + super("Function="+function+", Message="+msg); + this.bytes = bytes; + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append(this.getMessage()); + if (bytes!=null) { + if (bytes.length>0) { + str.append(" [ "); + for(byte xx:bytes) { + str.append(Bit.ToHexString(Bit.And(xx, 0xFF))).append(" "); + } + str.append("]"); + } + } + return str.toString(); + } +} diff --git a/src/jGPIO/I2C/I2C_BUS.java b/src/jGPIO/I2C/I2C_BUS.java new file mode 100644 index 0000000..808ebeb --- /dev/null +++ b/src/jGPIO/I2C/I2C_BUS.java @@ -0,0 +1,127 @@ +package jGPIO.I2C; + +import jGPIO.jGPIO; +import anywheresoftware.b4a.*; + +@BA.Events(values = { + "log(msg as string)" +}) +@BA.ShortName("i2cbus") + +/** + * Source : https://groups.google.com/forum/#!topic/jna-users/9X0bu4tv7Fk + * @author rdkartono + * + */ +public class I2C_BUS { + private final int O_RDWR = 0x00000002; + + private boolean opened = false; + private String i2c_name = ""; + private int i2c_handle = -1; + private I2C_BUS_Event javaevent; + + + private BA ba; + private String event; + private boolean need_log_event = false; + // disediain buat B4X + public I2C_BUS() { + } + + /** + * Initialize I2C Bus + * @param event Eventname + * @param busname bus name, for example /dev/i2c-1 + */ + public void Initialize(BA ba, String event, String busname) { + this.ba = ba; + this.event = event; + this.i2c_name = busname; + check_b4x_event(); + } + + private void check_b4x_event() { + if (ba!=null) { + if (event!=null) { + if (!event.isEmpty()) { + need_log_event = ba.subExists(event+"_log"); + } + } + } + } + + /** + * Open I2C Bus using I2C Name + * @param i2c_name : i2c name, example : /dev/i2c-1 + */ + @BA.Hide + public I2C_BUS(String i2c_name) { + if (i2c_name.isEmpty()) + return; + else + this.i2c_name = i2c_name; + } + + @BA.Hide + public void SetJavaEvent(I2C_BUS_Event ev) { + javaevent = ev; + } + + /** + * Open Bus + * @return true if opened + */ + public boolean Open_Bus() { + opened = false; + if (i2c_name.isEmpty()) return false; + + i2c_handle = jGPIO.libC.open(i2c_name, O_RDWR); + if (i2c_handle<0) { + raise_log("Open_Bus failed"); + opened = false; + } + else { + raise_log("Open_Bus success"); + opened = true; + } + + return opened; + + } + + /** + * Close Bus + */ + public void Close_Bus() { + if (i2c_handle >= 0) { + jGPIO.libC.close(i2c_handle); + i2c_handle = -1; + } + opened = false; + raise_log("Bus Closed"); + } + + /** + * check if I2C bus is opened + * @return true if opened + */ + public boolean IsOpened() { + return opened; + } + + /** + * Get I2C Open handle + * @return -1 if closed + */ + public int I2C_Handle() { + return i2c_handle; + } + + private void raise_log(String msg) { + if (javaevent!=null) { + javaevent.Log(msg); + } + if (need_log_event) ba.raiseEventFromDifferentThread(I2C_BUS.this, null, 0, event+"_log", false, new Object[] {msg}); + } +} diff --git a/src/jGPIO/I2C/I2C_BUS_Event.java b/src/jGPIO/I2C/I2C_BUS_Event.java new file mode 100644 index 0000000..63ea216 --- /dev/null +++ b/src/jGPIO/I2C/I2C_BUS_Event.java @@ -0,0 +1,5 @@ +package jGPIO.I2C; + +public interface I2C_BUS_Event { + void Log(String msg); +} diff --git a/src/jGPIO/I2C/I2C_Device.java b/src/jGPIO/I2C/I2C_Device.java new file mode 100644 index 0000000..10c83db --- /dev/null +++ b/src/jGPIO/I2C/I2C_Device.java @@ -0,0 +1,322 @@ +package jGPIO.I2C; + +import jGPIO.jGPIO; +import anywheresoftware.b4a.*; + +@BA.ShortName("i2cdevice") +@BA.Events(values= { + "log(msg as string)" +}) + +public class I2C_Device { + + private boolean opened = false; + + private int bus_handle = -1; + private int dev_handle = -1; + private int dev_address = -1; + private I2C_Device_Event javaevent; + + private BA ba; + private String event; + private boolean need_log_event = false; + + public void Initialize(BA ba, String event, int bushandle, int address) { + this.ba = ba; + this.event = event; + this.bus_handle = bushandle; + this.dev_address = address; + + if (this.ba!=null) { + if (this.event!=null) { + if (!this.event.isEmpty()) { + need_log_event = this.ba.subExists(event+"_log"); + } + } + } + } + + // buat sediain B4X + public I2C_Device() {} + + /** + * Create a I2C Device (Slave) + * @param bushandle : bus handle , get it from I2C_BUS object + * @param address : slave address to control + */ + @BA.Hide + public I2C_Device(int bushandle, int address) { + opened = false; + if (bushandle < 0) return; + if (address<0) return; + this.bus_handle = bushandle; + this.dev_address = address; + } + + @BA.Hide + public void SetJavaEvent(I2C_Device_Event ev) { + javaevent = ev; + } + + /** + * Open this Device, in I2C_SLAVE mode + * @return true if opened + */ + public boolean OpenDevice_SLAVE() { + opened = false; + if (bus_handle<0) return false; + if (dev_address<0) return false; + dev_handle = jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_SLAVE, dev_address); + if (dev_handle<0) { + raise_log("OpenDevice_SLAVE failed"); + opened = false; + } + else { + raise_log("OpenDevice_SLAVE success"); + opened = true; + } + return opened; + } + + /** + * Open this Device, in I2C_SLAVE_FORCE mode. + * @return true if opened + */ + public boolean OpenDevice_SLAVEFORCE() { + opened = false; + if (bus_handle<0) return false; + if (dev_address<0) return false; + dev_handle = jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_SLAVE_FORCE, dev_address); + + if (dev_handle<0) { + raise_log("OpenDevice_SLAVEFORCE failed"); + opened = false; + } + else { + jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_RETRIES, 3); + jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_TIMEOUT, 3); + raise_log("OpenDevice_SLAVEFORCE success"); + opened = true; + } + return opened; + } + + /** + * Open this Device, in I2C_RDWR mode + * @return true if opened + */ + public boolean OpenDevice_RDWR() { + opened = false; + if (bus_handle<0) return false; + if (dev_address<0) return false; + dev_handle = jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_RDWR, dev_address); + if (dev_handle<0) { + raise_log("OpenDevice_RDWR failed"); + opened = false; + } + else { + raise_log("OpenDevice_RDWR success"); + opened = true; + } + return opened; + } + + /** + * Open this Device, in I2C_TENBIT + * @return true if opened + */ + public boolean OpenDevice_SMBUS() { + opened = false; + if (bus_handle<0) return false; + if (dev_address<0) return false; + dev_handle = jGPIO.libC.ioctl(bus_handle, jGPIO.I2C_SMBUS, dev_address); + if (dev_handle<0) { + raise_log("OpenDevice_SMBUS failed"); + opened = false; + } + else { + raise_log("OpenDevice_SMBUS success"); + opened = true; + } + return opened; + } + + + /** + * Close this Device + */ + public void CloseDevice() { + opened = false; + bus_handle = -1; + dev_handle = -1; + dev_address = -1; + raise_log("Device Closed"); + } + + /** + * Check if slave is opened + * @return true if opened + */ + public boolean IsOpened() { + return opened; + } + + /** + * Get associated Bus handle + * @return -1 if closed + */ + public int BusHandle() { + return this.bus_handle; + } + + /** + * Get associated Slave address + * @return -1 if closed + */ + public int SlaveAddress() { + return this.dev_address; + } + + /** + * Get associated Device Open Handle + * @return -1 if closed + */ + public int DeviceHandle() { + return this.dev_handle; + } + + /** + * Write bytes + * @param bb : data bytes + * @return 0 if write fail + */ + public int WriteBytes(byte[] bb) { + if (bus_handle<0) { + raise_log("WriteBytes failed, Bus Handle closed"); + return 0; + } + if (dev_handle<0) { + raise_log("WriteBytes failed, Device Handle closed"); + return 0; + } + if (dev_address<0) { + raise_log("WriteBytes failed, Device Address invalid"); + return 0; + } + + return jGPIO.libC.write(bus_handle, bb, bb.length); + } + + /** + * Create two bytes of data [register] [data] and send to i2c + * @param register_address register of data + * @param data data to send + * @return 0 if failed + */ + public int WriteRegisterAndData(int register_address, byte data) { + return WriteBytes(register_address, data); + } + + @BA.Hide + public int WriteBytes(int register_address, byte bb) { + byte[] cmd = new byte[2]; + cmd[0] = (byte) register_address; + cmd[1] = bb; + return WriteBytes(cmd); + } + + /** + * Create N+1 bytes of data [register] [data in array] + * @param register_address register of data + * @param data array of data + * @return 0 if failed + */ + public int WriteRegisterAndMoreData(int register_address, byte[] data) { + return WriteBytes(register_address, data); + } + + @BA.Hide + public int WriteBytes(int register_address, byte[] bb ) { + if (bb!=null) { + if (bb.length>0) { + byte[] cmd = new byte[bb.length+1]; + cmd[0] = (byte) register_address; + for(int ii=0;ii=0) { + if (bb.length > (offset+size)) { + byte[] newbyte = new byte[size+1]; + newbyte[0] = (byte) register_address; + for(int ii=0;ii=0) { + if (bb.length > (offset + size)) { + + byte[] newbyte = new byte[size]; + for(int ii=0;ii0) { + byte write_address = (byte)((address & 0x7F)<<1); + byte[] cmd = new byte[] {0x53, write_address, (byte) databyte}; + int writeresult = myport.Write(cmd); + return writeresult; + } + } + return -1; + } + + /** + * Read 1 byte in simple mode, to device without register address, example PCF8574 + * @param address : slave address of the device + * @return -1 = failed, 0 - 255 = read result; + */ + public int Simple_Read(int address) { + if (isopened) { + if (address>0) { + byte read_address = (byte)(((address & 0x7F)<<1)|1); + byte[] cmd = new byte[] {0x53, read_address}; + if (myport.Write(cmd)==2) { + byte[] result = myport.Read(1); + if (result!=null) { + if (result.length>0) { + return (result[0] & 0xFF); + } + } + } + } + + } + return -1; + } + + /** + * Write bytes data in simple mode, to device without register address + * @param address : slave address of the device + * @param databyte : array of bytes to write + * @return -1 = failed, 0 = write failed, positive = write success + */ + public int Block_Write(int address , byte[] databyte) { + if (isopened) { + if (address>0) { + if (databyte!=null) { + if (databyte.length>0) { + byte write_address = (byte)((address & 0x7F)<<1); + byte[] cmd = new byte[3+databyte.length]; // command 1B (0x54), slave (1B), writelength (1B), databytes + cmd[0] = 0x54; + cmd[1] = write_address; + cmd[2] = (byte)databyte.length; + for(int ii=0;ii0) { + if (readcount>0) { + byte read_address = (byte)(((address & 0x7F)<<1)|1); + byte[] cmd = new byte[] {0x54, read_address, (byte) readcount}; + if (myport.Write(cmd)==3) { + byte[] result = myport.Read(readcount); + if (result!=null) { + if (result.length==readcount) { + return result; + } + } + } + } + } + } + return null; + } + + /** + * Write bytes data to device which have 8bit Register address, example RTC + * @param address : slave address of the device + * @param register : register address, where writing start + * @param bb : bytes of data + * @return -1 = failed, 0 = write failed, positive = write success + */ + public int Block_Write_with_Register_L(int address, int register, byte[] bb) { + if (isopened) { + if (address>0) { + if (register>=0) { + if (bb!=null) { + if (bb.length>0) { + byte write_address = (byte)((address & 0x7F)<<1); + byte[] cmd = new byte[4+bb.length]; // command = 0x55, address 1B, register 1B, byte length 1B, data bytes + cmd[0] = 0x55; + cmd[1] = write_address; + cmd[2] = (byte) register; + cmd[3] = (byte)bb.length; + for(int ii=0;ii0) { + if (register>=0) { + if (readcount>0) { + byte read_address = (byte)(((address & 0x7F)<<1)|1); + byte[] cmd = new byte[] {0x55, read_address, (byte) register, (byte) readcount}; + if (myport.Write(cmd)==cmd.length) { + byte[] result = myport.Read(readcount); + if (result!=null) { + if (result.length==readcount) { + return result; + } + } + } + } + } + } + } + return null; + } + + /** + * Write bytes data to device which have 16bit register address, example EEPROM + * @param address : slave address of the device + * @param register : register address (16bit) where writing start + * @param bb : bytes of data + * @return -1 = failed, 0 = write failed, positive = write success + */ + public int Block_Write_with_Register_HL(int address, int register, byte[] bb) { + if (isopened) { + if (address>0) { + if (register>=0) { + if (bb!=null) { + if (bb.length>0) { + byte write_address = (byte)((address & 0x7F)<<1); + + byte[] cmd = new byte[5+bb.length]; // command = 0x56, address 1B, register 2B, byte length 1B, data bytes + cmd[0] = 0x56; + cmd[1] = write_address; + cmd[3] = (byte) (register); + cmd[2] = (byte) (register >> 8); + cmd[4] = (byte)bb.length; + for(int ii=0;ii0) { + if (register>=0) { + if (readcount>0) { + byte read_address = (byte)(((address & 0x7F)<<1)|1); + byte[] cmd = new byte[] {0x56, read_address, (byte) (register>>8), (byte)(register), (byte) readcount}; + if (myport.Write(cmd)==cmd.length) { + byte[] result = myport.Read(readcount); + if (result!=null) { + if (result.length==readcount) { + return result; + } + } + } + } + } + } + } + return null; + } + + + private void raise_log(String msg) { + if (need_log_event) { + ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_Log", false, new Object[] {msg}); + } + } + + @Override + @BA.Hide + // event from SerialComm + public void Log(String msg) { + raise_log(msg); + } + + @Override + @BA.Hide + // event from SerialComm + public void newdata(byte[] bb) { + // TODO belum tau mau ngapain pake event ini + + } +} diff --git a/src/jGPIO/Linux_C_lib.java b/src/jGPIO/Linux_C_lib.java new file mode 100644 index 0000000..cef600a --- /dev/null +++ b/src/jGPIO/Linux_C_lib.java @@ -0,0 +1,71 @@ +package jGPIO; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.NativeLong; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +import anywheresoftware.b4a.BA; + +public interface Linux_C_lib extends com.sun.jna.Library { + public long memcpy(int[] dst, short[] src, long n); + + public int memcpy(int[] dst, short[] src, int n); + + public int pipe(int[] fds); + + public int tcdrain(int fd); + + public int fcntl(int bus_handle, int command, int args); + + public int ioctl(int bus_handle, int command, int args); + + public int ioctl(int bus_handle, int command, int... args); + + public int ioctl(int bus_handle, int command, Pointer args); + + public int open(String path, int flags); + + public int close(int fd); + + public int write(int bus_handle, byte[] buffer, int count); + + public int read(int bus_handle, byte[] buffer, int count); + + public long write(int bus_handle, byte[] buffer, long count); + + public long read(int bus_handle, byte[] buffer, long count); + + public int select(int n, int[] read, int[] write, int[] error, timeval timeout); + + public int poll(int[] fds, int nfds, int timeout); + + public int tcflush(int fd, int qs); + + public void perror(String msg); + + public int tcsendbreak(int fd, int duration); + + static public class timeval extends Structure { + public NativeLong tv_sec; + public NativeLong tv_usec; + + + protected List getFieldOrder() { + return Arrays.asList(// + "tv_sec",// + "tv_usec"// + ); + } + + @BA.Hide public void autoWrite(){} + @BA.Hide public void autoRead(){} + + public timeval(long second, long usecond) { + tv_sec = new NativeLong(second); + tv_usec = new NativeLong(usecond); + } + } +} diff --git a/src/jGPIO/Linux_C_lib_DirectMapping.java b/src/jGPIO/Linux_C_lib_DirectMapping.java new file mode 100644 index 0000000..29b1fe0 --- /dev/null +++ b/src/jGPIO/Linux_C_lib_DirectMapping.java @@ -0,0 +1,56 @@ +package jGPIO; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +public class Linux_C_lib_DirectMapping implements Linux_C_lib { + + native public long memcpy(int[] dst, short[] src, long n); + + native public int memcpy(int[] dst, short[] src, int n); + + native public int pipe(int[] fds); + + native public int tcdrain(int fd); + + native public int fcntl(int bus_handle, int command, int args); + + native public int ioctl(int bus_handle, int command, int args); + + native public int ioctl(int bus_handle, int command, int... args); + + native public int ioctl(int bus_handle, int command, Pointer args); + + native public int open(String path, int flags); + + native public int close(int bus_handle); + + native public int write(int bus_handle, byte[] buffer, int count); + + native public int read(int bus_handle, byte[] buffer, int count); + + native public long write(int bus_handle, byte[] buffer, long count); + + native public long read(int bus_handle, byte[] buffer, long count); + + native public int select(int n, int[] read, int[] write, int[] error, timeval timeout); + + native public int poll(int[] fds, int nfds, int timeout); + + native public int tcflush(int fd, int qs); + + native public void perror(String msg); + + native public int tcsendbreak(int fd, int duration); + + + static { + try { + Native.register("c"); + System.out.println("registered to c library"); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/src/jGPIO/ModbusCRC.java b/src/jGPIO/ModbusCRC.java new file mode 100644 index 0000000..a5ec21e --- /dev/null +++ b/src/jGPIO/ModbusCRC.java @@ -0,0 +1,78 @@ +package jGPIO; + +public class ModbusCRC { + /* Table of CRC values for high-order byte */ + private final static short[] auchCRCHi = { + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 + }; + + /* Table of CRC values for low-order byte */ + private final static short[] auchCRCLo = { + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, + 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, + 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, + 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, + 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, + 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, + 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, + 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, + 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, + 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, + 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, + 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, + 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, + 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, + 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, + 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, + 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, + 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, + 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, + 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, + 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, + 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, + 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, + 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, + 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 + }; + + public static final int[] calculateCRC(byte[] data, int offset, int len) { + int[] crc = {0xFF, 0xFF}; + int nextByte = 0; + int uIndex; /* will index into CRC lookup*/ /* table */ + /* pass through message buffer */ + for (int i = offset; i < len && i < data.length; i++) { + nextByte = 0xFF & ((int) data[i]); + uIndex = crc[0] ^ nextByte; //*puchMsg++; /* calculate the CRC */ + crc[0] = crc[1] ^ auchCRCHi[uIndex]; + crc[1] = auchCRCLo[uIndex]; + } + + return crc; +} +} diff --git a/src/jGPIO/SPI/SPI_BUS.java b/src/jGPIO/SPI/SPI_BUS.java new file mode 100644 index 0000000..509c399 --- /dev/null +++ b/src/jGPIO/SPI/SPI_BUS.java @@ -0,0 +1,185 @@ +package jGPIO.SPI; + +import jGPIO.jGPIO; + +//TODO : belum tau caranya +@SuppressWarnings("unused") +public class SPI_BUS { + private final int O_RDWR = 0x00000002; + + private boolean opened = false; + private String spi_name = ""; + private int spi_handle = -1; + private SPI_BUS_Event javaevent; + + private int mode = 0; + private int speed = 1000000; + private int databits = 8; + private final int min_speed = 500 * 1000; + private final int max_speed = 32*1000*1000; + + /** + * Create SPI_BUS object, with default mode 0, speed 1Mhz + * @param spi_name : valid spi name , example /dev/spidev0.0 + */ + public SPI_BUS(String spi_name) { + + this.spi_name = spi_name; + } + + /** + * Create SPI_BUS Object + * @param spi_name : valid spi_name, example /dev/spidev0.0 + * @param mode : default at mode 0 + * 0 = Clock idle low, data is clocked in on rising edge, output data (change) on falling edge + * 1 = Clock idle low, data is clocked in on falling edge, output data (change) on rising edge + * 2 = Clock idle high, data is clocked in on falling edge, output data (change) on rising edge + * 3 = Clock idle high, data is clocked in on rising, edge output data (change) on falling edge + * @param speed : 500Khz - 32Mhz + */ + public SPI_BUS(String spi_name, int mode, int speed) { + this.spi_name = spi_name; + if ((mode<0) || (mode>3)) mode = 0; + this.mode = mode; + if ((speedmax_speed)) speed = 1000000; + + this.speed = speed; + } + + /** + * Create SPI_BUS Object + * @param spi_name : valid spi_name, example /dev/spidev0.0 + * @param mode : default at mode 0 + * 0 = Clock idle low, data is clocked in on rising edge, output data (change) on falling edge + * 1 = Clock idle low, data is clocked in on falling edge, output data (change) on rising edge + * 2 = Clock idle high, data is clocked in on falling edge, output data (change) on rising edge + * 3 = Clock idle high, data is clocked in on rising, edge output data (change) on falling edge + * @param speed : 500Khz - 32Mhz + * @param databits : default at 8 bits + */ + public SPI_BUS(String spi_name, int mode, int speed, int databits) { + this.spi_name = spi_name; + + if ((mode<0) || (mode>3)) mode = 0; + this.mode = mode; + + if ((speedmax_speed)) speed = 1000000; + this.speed = speed; + + this.databits = databits; + } + + /** + * Set Java Event + * @param event + */ + public void SetJavaEvent(SPI_BUS_Event event) { + javaevent = event; + } + + + /** + * Open BUS + * @return true if success + */ + public boolean Open_Bus() { + opened = false; + if (spi_name=="") return false; + + spi_handle = jGPIO.libC.open(spi_name, O_RDWR); + if (spi_handle<0) { + raise_log("Open_Bus failed"); + opened = false; + } + else { + // initialize mode + + + // initialize speed + + // initialize databits + + raise_log("Open_Bus success"); + opened = true; + } + + return opened; + } + + public void Close_Bus() { + if (spi_handle>=0) { + jGPIO.libC.close(spi_handle); + + } + spi_handle = -1; + opened = false; + this.spi_name=""; + raise_log("SPI BUS closed"); + } + + /** + * Write data + * @param bb : bytes of data + * @return number of bytes written, or 0 if failed + */ + public int Write(byte[] bb) { + if (opened) { + if (spi_handle>=0) { + int writeresult = jGPIO.libC.write(spi_handle, bb, bb.length); + if (writeresult==bb.length) { + raise_log("Write Succesful"); + } else { + raise_log("Write failed, result = "+writeresult); + } + + return writeresult; + } + } + return 0; + } + + /** + * Read bytes + * @param readcount : bytes to read + * @return null if failed + */ + public byte[] Read(int readcount) { + if (opened) { + if (spi_handle>=0) { + byte[] result = new byte[readcount]; + int readresult = jGPIO.libC.read(spi_handle, result, readcount); + if (readresult == readcount) { + raise_log("Read succesful"); + } else { + raise_log("Read failed, result="+readresult); + } + return result; + } + } + return null; + } + + /** + * Check if BUS opened + * @return true if opened + */ + public boolean IsOpened() { + return opened; + } + + /** + * Get SPI Handle + * @return -1 if closed + */ + public int SPI_Handle() { + return spi_handle; + } + + private void raise_log(String msg) { + if (javaevent!=null) { + javaevent.Log(msg); + } + } + + +} diff --git a/src/jGPIO/SPI/SPI_BUS_Event.java b/src/jGPIO/SPI/SPI_BUS_Event.java new file mode 100644 index 0000000..df15eec --- /dev/null +++ b/src/jGPIO/SPI/SPI_BUS_Event.java @@ -0,0 +1,5 @@ +package jGPIO.SPI; + +public interface SPI_BUS_Event { + void Log(String msg); +} diff --git a/src/jGPIO/SPI/SPI_BitBang.java b/src/jGPIO/SPI/SPI_BitBang.java new file mode 100644 index 0000000..e070e9e --- /dev/null +++ b/src/jGPIO/SPI/SPI_BitBang.java @@ -0,0 +1,139 @@ +package jGPIO.SPI; + +import anywheresoftware.b4a.keywords.Bit; +import jGPIO.DigitalInput.DigitalInput; +import jGPIO.DigitalOutput.DigitalOutput; + +public class SPI_BitBang{ + + private DigitalOutput spi_clk = null; + @SuppressWarnings("unused") + private DigitalInput spi_di = null; + private DigitalOutput spi_do = null; + private DigitalOutput spi_cs = null; + + private boolean is_opened = false; + + +/** + * + * @param pin_di : pin for digital input + * @param pin_do : pin for digital output + * @param pin_cs : pin for clock output + * @param pin_clk : pin for chip select output + */ + public SPI_BitBang(int pin_di, int pin_do, int pin_cs, int pin_clk) { + is_opened = false; + + + // CS selalu high (active low) + if (pin_cs>0) spi_cs = new DigitalOutput(pin_cs, true); + // DataInput selalu high waktu awal + if (pin_di>0) spi_di = new DigitalInput(pin_di); + // Data Output selalu high waktu awal + if (pin_do>0) spi_do = new DigitalOutput(pin_do,true); + if (pin_clk>0) spi_clk = new DigitalOutput(pin_clk, false); + is_opened = true; + } + + public boolean setCS(boolean is_enabled) { + if (is_opened) { + if (is_enabled) + spi_cs.SetLow(); + else + spi_cs.SetHigh(); + + + return true; + } + return false; + } + + /** + * Write 8 bits value + * @param value : value + * @return true if success + */ + public boolean Write8(int value) { + if (is_opened) { + + int tmp = Bit.And(value, 0xFF); + + + for(int ii=0;ii<8;ii++) { + if (Bit.And(tmp, 0x80)==0) { + spi_do.SetLow(); + + } else { + spi_do.SetHigh(); + + } + tmp = Bit.And(Bit.ShiftLeft(tmp, 1), 0xFF); + ActivateClock(); + } + + return true; + } + return false; + } + + + + /** + * Write 16 bits value + * @param value + * @return true if success + */ + public boolean Write16(int value) { + if (is_opened) { + + int tmp = Bit.And(value, 0xFFFF); + + for(int ii=0;ii<16;ii++) { + // mulai dari MSB + if (Bit.And(tmp, 0x8000)==0) { + spi_do.SetLow(); + + } else { + spi_do.SetHigh(); + + } + + tmp = Bit.And(Bit.ShiftLeft(tmp, 1), 0xFFFF); + ActivateClock(); + + } + + + return true; + } + + return false; + } + + public boolean IsOpened() { + return is_opened; + } + + private void ActivateClock() { + + spi_clk.SetHigh(); + spi_clk.SetLow(); + } + + public void Close() { + is_opened = false; + if (spi_do instanceof DigitalOutput) { + spi_do.Release(); + spi_do = null; + } + if (spi_clk instanceof DigitalOutput) { + spi_clk.Release(); + spi_clk = null; + } + if (spi_cs instanceof DigitalOutput) { + spi_cs.Release(); + spi_cs = null; + } + } +} diff --git a/src/jGPIO/SerialPort/SerialComm.java b/src/jGPIO/SerialPort/SerialComm.java new file mode 100644 index 0000000..5377fea --- /dev/null +++ b/src/jGPIO/SerialPort/SerialComm.java @@ -0,0 +1,504 @@ +package jGPIO.SerialPort; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.List; + +import com.fazecast.jSerialComm.*; + + +@BA.ShortName("SerialComm") +@BA.Events(values= { + "log(msg as string)", + "newdata(bb() as byte)" + +}) +//@BA.DependsOn(values = { "jSerialComm-2.6.2" }) + +public class SerialComm { + + private BA ba; + private Object myobject; + private String event; + private boolean need_log_event = false; + private boolean need_newdata_event = false; + + + private jSerialPort_Event javaevent; + private boolean isReady = false; + private String version=""; + private long writtencount = 0; + private long readcount = 0; + + private SerialPort myport; + + private String myportname; + + + /** + * Initialize jSerialPort (B4J / B4A) + * @param caller : caller object + * @param eventname : event name + */ + public void Initialize(BA bax, Object caller, String eventname) { + isReady = false; + ba = bax; + event = eventname; + myobject = caller; + need_log_event = ba.subExists(event+"_log"); + need_newdata_event = ba.subExists(event+"_newdata"); + + version = SerialPort.getVersion(); + if (version!="") isReady = true; + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + ClosePort(); + } + }); + } + + /** + * Check Version String of SerialPort Library + * @return version string + */ + public String GetVersion() { + return version; + } + + /** + * Get all available port names in this system + * @return List of Serial Port Names + */ + public List GetPortNames(){ + List result = new List(); + result.Initialize(); + if (isReady) { + for(SerialPort xx:SerialPort.getCommPorts()) { + if (xx != null) { + String portname = xx.getSystemPortName(); + if (portname!="") { + result.Add(portname); + } + } + } + } else { + raise_log("GetPortNames failed, SerialPort Library not ready"); + } + return result; + } + + /** + * Check if SerialComm is Ready to work + * @return true if ready + */ + public boolean PortIsReady() { + if (myport!=null) { + if (myport instanceof SerialPort) { + return myport.isOpen(); + } + } + return false; + + } + + /** + * Open Serial Port, with default value 1000 bytes RX buffer, and 1000 bytes TX Buffer + * @param portname : portname (a member of GetPortNames) + * @return true if port opened + */ + public boolean OpenPort(String portname) { + myportname=""; + writtencount = 0; + readcount = 0; + if (isReady) { + myport = SerialPort.getCommPort(portname); + if (myport instanceof SerialPort) { + if (myport.openPort()) { + + if (myport.addDataListener(new SerialPortDataListener() { + + @Override + public int getListeningEvents() { + return SerialPort.LISTENING_EVENT_DATA_AVAILABLE; + } + + @Override + public void serialEvent(SerialPortEvent event) { + if (event instanceof SerialPortEvent) { + if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) { + int available = myport.bytesAvailable(); + if (available>0) { + byte[] bb = new byte[available]; + int readbytes = myport.readBytes(bb, available); + + raise_newdata(bb); + readcount+= readbytes; + } + } + } + } + + })) { + + } else { + raise_log("Failed to add DataListener to SerialComm"); + } + + + myportname = portname; + raise_log("SerialPort name="+myportname+" is opened"); + return true; + } + } + + } + raise_log("Failed to open SerialPort name="+portname); + return false; + } + + @BA.Hide + /** + * Read serial port + * @param count : maximum bytes wanted to read + * @return array of bytes, or null if failed + */ + public byte[] Read(int count) { + if (isReady) { + if (myport instanceof SerialPort) { + if (myport.isOpen()) { + int available = myport.bytesAvailable(); + if (available>0) { + byte[] bb = new byte[count]; + myport.readBytes(bb, bb.length); + + readcount+= bb.length; + return bb; + } + } + } + } + return null; + } + + /** + * Close Serial Port + */ + public void ClosePort() { + if (isReady) { + if (myport!=null) { + if (myport instanceof SerialPort) { + try { + myport.removeDataListener(); + if (myport.isOpen()) myport.closePort(); + } catch(Exception e) { + raise_log("ClosePort exception, Msg : "+e.getMessage()); + } + } + + myport = null; + raise_log("SerialPort name="+myportname+" is closed"); + } + } + isReady = false; + myportname = ""; + + } + + /** + * Get current used Port Name + * @return port name, or empty string if closed + */ + public String CurrentPortName() { + return myportname; + } + + /** + * Get Baud Rate + * @return -1 if failed + */ + public int GetBaudRate() { + if (isReady) { + if (myport instanceof SerialPort) { + return myport.getBaudRate(); + } + } + return -1; + } + + /** + * Set Baud Rate + * @param value : baud rate value + * @return true if success + */ + public boolean SetBaudRate(int value) { + if (isReady) { + if (myport instanceof SerialPort) { + myport.setBaudRate(value); + return true; + } + } + return false; + } + + /** + * Get Data bits + * @return -1 if failed + */ + public int GetDataBits() { + if (isReady) { + if (myport instanceof SerialPort) { + return myport.getNumDataBits(); + } + } + + return -1; + } + + /** + * Set Data bits + * @param value : data bits value + * @return true if success + */ + public boolean SetDataBits(int value) { + if (isReady) { + if (myport instanceof SerialPort) { + myport.setNumDataBits(value); + return true; + } + } + return false; + } + + /** + * Get / Set Stop bit + * @return -1 = failed, 1 = 1 stop bit, 2 = 2 stop bit, 3 = 1.5 stop bit + */ + public int GetStopBits() { + if (isReady) { + if (myport instanceof SerialPort) { + int value = myport.getNumStopBits(); + switch(value) { + case SerialPort.ONE_STOP_BIT : + return 1; + case SerialPort.ONE_POINT_FIVE_STOP_BITS : + return 3; + case SerialPort.TWO_STOP_BITS : + return 2; + } + } + } + return -1; + } + + /** + * Set Stop bit + * @param stopbitcode : 1 = 1 stop bit, 2 = 2 stop bit, 3 = 1.5 stop bit, other = 1 stop bit + * @return true if success + */ + public boolean SetStopBits(int stopbitcode) { + if (isReady) { + if (myport instanceof SerialPort) { + switch(stopbitcode) { + case 3 : + myport.setNumStopBits(SerialPort.ONE_POINT_FIVE_STOP_BITS); + break; + case 2 : + myport.setNumStopBits(SerialPort.TWO_STOP_BITS); + break; + default : + myport.setNumStopBits(SerialPort.ONE_STOP_BIT); + break; + + } + return true; + } + } + return false; + } + + /** + * Get Parity Code + * @return -1 if failed, 0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space + */ + public int GetParityBits() { + if (isReady) { + if (myport instanceof SerialPort) { + int value = myport.getParity(); + switch(value) { + case SerialPort.ODD_PARITY : + return 1; + case SerialPort.EVEN_PARITY : + return 2; + case SerialPort.MARK_PARITY : + return 3; + case SerialPort.SPACE_PARITY : + return 4; + default : + return 0; + } + } + } + return -1; + } + + /** + * Set Parity + * @param paritycode : 1 = odd, 2 = even, 3 = mark, 4 = space, default (0) = None + * @return true if success + */ + public boolean SetParityBits(int paritycode) { + if (isReady) { + if (myport instanceof SerialPort) { + switch(paritycode) { + case 1 : + myport.setParity(SerialPort.ODD_PARITY); + break; + case 2 : + myport.setParity(SerialPort.EVEN_PARITY); + break; + case 3 : + myport.setParity(SerialPort.MARK_PARITY); + break; + case 4 : + myport.setParity(SerialPort.SPACE_PARITY); + break; + default : + myport.setParity(SerialPort.NO_PARITY); + break; + } + return true; + } + } + return false; + } + + /** + * Get available bytes to read in internal buffer + * @return -1 if failed + */ + public int getAvailableBytesToRead() { + if (isReady) { + if (myport instanceof SerialPort) { + return myport.bytesAvailable(); + } + } + return -1; + } + + /** + * Get number of bytes stil waiting to send in internal buffer + * @return -1 if failed + */ + public int getWaitingBytesToSend() { + if (isReady) { + if (myport instanceof SerialPort) { + return myport.bytesAwaitingWrite(); + } + } + return -1; + } + + /** + * Get Clear-To-Send bit value + * @return true if CTS set + */ + public boolean getCTS() { + if (isReady) { + if (myport instanceof SerialPort) { + return myport.getCTS(); + } + } + return false; + } + + /** + * Set Request-To-Send bit + * @param value : if true, RTS is ON + */ + public void setRTS(boolean value) { + if (isReady) { + if (myport instanceof SerialPort) { + + if (value) + myport.setRTS(); + else + myport.clearRTS(); + } + } + } + + /** + * Write bytes to Serial + * @param bb : bytes to write + * @return number of bytes written to internal buffer, or -1 if failed + */ + public int Write(byte[] bb) { + if (isReady) { + if (myport instanceof SerialPort) { + int writeresult = myport.writeBytes(bb, bb.length); + writtencount+=writeresult; + return writeresult; + } + } + return -1; + } + + /** + * Set Read and Write blocking / timeout. Positive value means block until specific timeout + * @param readblocking_ms : 0 = no read block, positive = milliseconds to block waiting for read + * @param writeblocking_ms : 0 = no write block, positive = milliseconds to block until writing complete + */ + public void SetBlockingMode(int readblocking_ms, int writeblocking_ms) { + if (isReady) { + if (myport instanceof SerialPort) { + int blockvalue = 0; // default is NON_BLOCKING + if (readblocking_ms>0) blockvalue |= SerialPort.TIMEOUT_READ_BLOCKING; + if (writeblocking_ms>0) blockvalue |= SerialPort.TIMEOUT_WRITE_BLOCKING; + + myport.setComPortTimeouts(blockvalue, readblocking_ms, writeblocking_ms); + } + } + } + + /** + * Get how many bytes written since opening + * @return bytes written + */ + public long getBytesWritten() { + return writtencount; + } + + /** + * Get how many bytes read since opening + * @return bytes read + */ + public long getBytesRead() { + return readcount; + } + + @BA.Hide + public void SetJavaEvent(jSerialPort_Event event) { + javaevent = event; + } + + private void raise_log(String msg) { + if (need_log_event) { + ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_log", false, new Object[] {msg}); + } + if (javaevent!=null) { + javaevent.Log(msg); + } + } + + private void raise_newdata(byte[] bb) { + if (need_newdata_event) { + ba.raiseEventFromDifferentThread(myobject, null, 0, event+"_newdata", false, new Object[] {bb}); + } + if (javaevent!=null) { + javaevent.newdata(bb); + } + } + + +} diff --git a/src/jGPIO/SerialPort/jSerialPort_Event.java b/src/jGPIO/SerialPort/jSerialPort_Event.java new file mode 100644 index 0000000..63fc3b7 --- /dev/null +++ b/src/jGPIO/SerialPort/jSerialPort_Event.java @@ -0,0 +1,6 @@ +package jGPIO.SerialPort; + +public interface jSerialPort_Event { + void Log(String msg); + void newdata(byte[] bb); +} diff --git a/src/jGPIO/input_event.java b/src/jGPIO/input_event.java new file mode 100644 index 0000000..3e87435 --- /dev/null +++ b/src/jGPIO/input_event.java @@ -0,0 +1,74 @@ +package jGPIO; + +import com.sun.jna.Structure; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.BA.Hide; + +@BA.ShortName("Linux_Input_Event") + +public class input_event extends Structure { + + public timeval time; + + /** + * 16 bit unsigned + */ + public short type; + + /** + * 16 bit unsigned + */ + public short code; + + /** + * 32 bit signed + */ + public int value; + + public input_event() { + super(); + } + + public input_event(timeval _time, short _type, short _code, int _value) { + super(); + this.time = _time; + this.type = _type; + this.code = _code; + this.value = _value; + } + + protected ByReference newByReference() { + ByReference s = new ByReference(); + s.useMemory(getPointer()); + write(); + s.read(); + return s; + } + protected ByValue newByValue() { + ByValue s = new ByValue(); + s.useMemory(getPointer()); + write(); + s.read(); + return s; + } + + protected input_event newInstance() { + input_event s = new input_event(); + s.useMemory(getPointer()); + write(); + s.read(); + return s; + } + + @Override + @Hide + public void autoWrite() {super.autoWrite();} + + @Override + @Hide + public void autoRead() {super.autoRead();} + + public static class ByReference extends input_event implements com.sun.jna.Structure.ByReference {} + public static class ByValue extends input_event implements com.sun.jna.Structure.ByValue {} +} diff --git a/src/jGPIO/jGPIO.java b/src/jGPIO/jGPIO.java new file mode 100644 index 0000000..99307ba --- /dev/null +++ b/src/jGPIO/jGPIO.java @@ -0,0 +1,361 @@ +package jGPIO; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; + +import com.sun.jna.Native; +import com.sun.jna.Platform; + +import anywheresoftware.b4a.BA; + +@BA.Author("Rudy Darmawan") +@BA.Version(1.14f) + +@BA.ShortName("jGPIO") + + +public class jGPIO { + + @BA.Hide + public static Linux_C_lib_DirectMapping libC = new Linux_C_lib_DirectMapping(); + @BA.Hide + public final static int I2C_RETRIES = 0x701; /* number of times a device address should be polled when not acknowledging */ + @BA.Hide + public final static int I2C_TIMEOUT = 0x702; /* set timeout in units of 10 ms */ + @BA.Hide + public final static int I2C_SLAVE = 0x703; /* Command at ioctl, means : Use this slave address */ + @BA.Hide + public final static int I2C_TENBIT = 0x704; /* 0 for 7 bit addrs, != 0 for 10 bit */ + @BA.Hide + public final static int I2C_FUNCS = 0x705; /* Command at ioctl, means : Get the adapter functionality */ + @BA.Hide + public final static int I2C_SLAVE_FORCE = 0x706; /* Command at ioctl, means : Use this slave address, even if it is already in use by a driver! */ + @BA.Hide + public final static int I2C_RDWR = 0x707; /* Command at ioctl, means : Combined R/W transfer (one stop only) */ + @BA.Hide + public final static int I2C_PEC = 0x708; /* != 0 to use PEC with SMBus */ + @BA.Hide + public final static int I2C_SMBUS = 0x720; /* SMBus transfer */ + + /** + * Detected OS Name + */ + static public String osname = ""; + + /** + * Detected OS Architecture + */ + static public String osarch = ""; + + /** + * Detected OS Version + */ + static public String osversion = ""; + + /** + * Detected default path separator symbol + */ + static public String separator = ""; + + /** + * Detected Java Library Path + */ + static public String javaLibPath = ""; + + /** + * Detected Java Virtual Machine running on this machine + */ + static public String jvm = ""; + + /** + * Detected current Run Directory + */ + static public String rundir = ""; + + /** + * Choosen Library Path inside JAR + */ + static public String nativepath = ""; + + static public boolean IsWindows = false; + static public boolean IsLinux = false; + static public boolean IsMac = false; + static public boolean IsAndroid = false; + + + public static void detectOS() { + osname = System.getProperty("os.name"); + osarch = System.getProperty("os.arch"); + osversion = System.getProperty("os.version"); + separator = System.getProperty("path.separator"); + javaLibPath = System.getProperty("java.library.path"); + jvm = System.getProperty("java.vm.name"); + File xxx = new File(""); + //rundir = System.getProperty("user.dir"); + rundir = xxx.getAbsolutePath(); + + IsAndroid = false; + if (BA.debugMode) { + BA.Log("OS Name : "+osname); + BA.Log("OS Architecture : "+osarch); + BA.Log("OS Version : "+osversion); + BA.Log("JVM Name : "+jvm); + BA.Log("Java Lib path : "+javaLibPath); + BA.Log("Default Separator : "+separator); + } + + + osname = osname.toLowerCase(); + osarch = osarch.toLowerCase(); + jvm = jvm.toLowerCase(); + if (osname.contains("window")) { + if (osarch.contains("86")) { + IsWindows = true; + nativepath = "/lib/win32/"; + BA.Log("System : Windows 32bit"); + } else if (osarch.contains("64")) { + IsWindows = true; + nativepath = "/lib/win64/"; + BA.Log("System : Windows 64bit"); + } else { + BA.Log("System : Unsupported Windows"); + } + } else if (osname.contains("linux")) { + if (Platform.isAndroid()) { + IsAndroid = true; + BA.Log("System : Android (auto extract)"); + + } else { + if (osarch.contains("arm")) { + nativepath = "/lib/raspberry"; + IsLinux = true; + BA.Log("System : Raspberry / ARM"); + } else if (osarch.contains("aarch64")) { + nativepath = "/lib/aarch64"; + IsLinux = true; + BA.Log("System : ARM64"); + } else if (osarch.contains("86")) { + nativepath = "/lib/linux32"; + IsLinux = true; + BA.Log("System : Linux 32bit"); + } else if (osarch.contains("64")) { + nativepath = "/lib/linux64"; + IsLinux = true; + BA.Log("System : Linux 64bit"); + } + } + } else if (osname.contains("mac")) { + nativepath = "/lib/macos"; + IsMac = true; + BA.Log("System : Mac OS"); + } + else { + BA.Log("System : Unsupported OS"); + } + } + + // copy library from JAR to outside + @BA.Hide + protected static boolean copylib(String sourcepath, String libname, String targetpath){ + if (sourcepath=="") { + if (BA.debugMode) BA.Log("copylib "+libname+" failed, sourcepath is empty"); + return false; + } + if (libname=="") { + if (BA.debugMode) BA.Log("copylib failed, libname is empty"); + return false; + } + if (targetpath=="") { + if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is empty"); + return false; + } + File ft = new File(targetpath,""); + if (!ft.exists()) { + if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is not exist"); + return false; + } + if (!ft.isDirectory()) { + if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is not directory"); + return false; + } + if (!ft.canWrite()) { + if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is not writable"); + return false; + } + if (!ft.canRead()) { + if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is not readable"); + return false; + } + if (!ft.canExecute()) { + if (BA.debugMode) BA.Log("copylib "+libname+" failed, targetpath is not executable"); + return false; + } + File ftarget = new File(targetpath,libname); + if (BA.debugMode) BA.Log("copylib targetfile is "+ftarget.getPath()); + try { + File fsource = Native.extractFromResourcePath(sourcepath+libname); + if (BA.debugMode) BA.Log("copylib sourcefile is "+fsource.getPath()); + if (fsource.length()==ftarget.length()) { + if (BA.debugMode) BA.Log("copylib "+libname+" canceled, file already exist"); + return false; + } + if (fsource.renameTo(ftarget)) { + if (BA.debugMode) BA.Log("Copied "+libname+" to "+targetpath); + return true; + } else { + if (BA.debugMode) BA.Log("Failed to copy "+libname+" to "+targetpath); + return false; + } + } catch (IOException e) { + if (BA.debugMode) BA.Log("Failed to extract "+sourcepath+libname+" Msg : "+e.getMessage()); + return false; + } + + }; + + @BA.Hide + public static boolean loadLibrary(String Name) + { + boolean result = true; + try + { + + if (IsAndroid) { + // android + BA.Log("Trying to load Android library " + Name); + System.loadLibrary(Name); + + } else { + // other + File flib = new File(rundir,System.mapLibraryName(Name)); + BA.Log("Trying to load library "+flib.getPath()); + System.load(flib.getPath()); + } + BA.Log("Library " + Name + " loaded"); + } + catch(UnsatisfiedLinkError e) + { + BA.Log("Cannot load library \"" + Name + "\""); + e.printStackTrace(); + result = false; + } + + return result; + } + + @BA.Hide + public static boolean extract_lib(String libname) { + if (libname=="") return false; + String libraryname = System.mapLibraryName(libname); + return copylib(nativepath, libraryname, rundir); + } + + @BA.Hide + public static Method GetMethod_byName(Object classnya, String methodname) { + if (classnya==null) return null; + if (methodname=="") return null; + try { + return classnya.getClass().getMethod(methodname); + } catch (NoSuchMethodException | SecurityException e) { + if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage()); + return null; + } + } + + @BA.Hide + public static Method GetMethod_byName(Object classnya, String methodname, Class param) { + if (classnya==null) return null; + if (methodname=="") return null; + try { + return classnya.getClass().getMethod(methodname, param); + } catch (NoSuchMethodException | SecurityException e) { + if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage()); + return null; + } + + } + + @BA.Hide + public static Method GetMethod_byName(Object classnya, String methodname, Class param1, Class param2) { + if (classnya==null) return null; + if (methodname=="") return null; + try { + return classnya.getClass().getMethod(methodname, param1, param2); + }catch (NoSuchMethodException | SecurityException e) { + if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage()); + return null; + } + + } + + @BA.Hide + public static Method GetMethod_byName(Object classnya, String methodname, Class param1, Class param2, Class param3) { + if (classnya==null) return null; + if (methodname=="") return null; + try { + return classnya.getClass().getMethod(methodname, param1, param2, param3); + }catch (NoSuchMethodException | SecurityException e) { + if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage()); + return null; + } + + } + + @BA.Hide + public static Method GetMethod_byName(Object classnya, String methodname, Class param1, Class param2, Class param3, Class param4) { + if (classnya==null) return null; + if (methodname=="") return null; + try { + return classnya.getClass().getMethod(methodname, param1, param2, param3, param4); + }catch (NoSuchMethodException | SecurityException e) { + if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage()); + return null; + } + + } + + @BA.Hide + public static Method GetMethod_byName(Object classnya, String methodname, Class param1, Class param2, Class param3, Class param4, Class param5) { + if (classnya==null) return null; + if (methodname=="") return null; + try { + return classnya.getClass().getMethod(methodname, param1, param2, param3, param4, param5); + }catch (NoSuchMethodException | SecurityException e) { + if (BA.debugMode) BA.Log("No Method with Name="+methodname+" in "+classnya.getClass().getName()+", Msg :"+e.getMessage()); + return null; + } + + } + + @BA.Hide + public static boolean ExtractJarToRunDir(String jarname, String resourcepath) { + File jarfile = new File(rundir, jarname); + if (jarfile.exists()) { + // sudah ada + BA.Log("File "+jarname+" already exist in "+rundir); + return true; + } else { + // belum ada + try { + File source = Native.extractFromResourcePath(resourcepath); + if (source.renameTo(jarfile)) { + BA.Log("Success extract "+jarname+" to "+jarfile.getAbsolutePath()); + return true; + } else { + BA.Log("Failed extract "+jarname); + return false; + } + } catch (IOException e) { + BA.Log("Unable to extract "+resourcepath+", Msg : "+e.getMessage()); + return false; + } + + + } + } + + + public static void main(String[] args) { + System.out.println("JGPIO main"); + } +} diff --git a/src/jGPIO/mycodes.java b/src/jGPIO/mycodes.java new file mode 100644 index 0000000..92f4db7 --- /dev/null +++ b/src/jGPIO/mycodes.java @@ -0,0 +1,722 @@ +package jGPIO; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import java.util.List; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import anywheresoftware.b4a.keywords.DateTime; + +public class mycodes { + private static final String gpiofolder = "/sys/class/gpio"; + private static final String gpioexportfolder = gpiofolder + "/export"; + private static final String gpiounexportfolder = gpiofolder + "/unexport"; + + + + /** + * Convert Datetime Tick to Date String, on format DD-MM-YYYY + * @param tick : datetime tick in long + * @return empty string if tick less than 1 + */ + public static String Tick_To_DDMMYYYY(long tick) { + if (tick>0) { + StringBuilder str = new StringBuilder(); + int temp; + temp = DateTime.GetDayOfMonth(tick); + if (temp<10) str.append("0"); + str.append(temp); + str.append("-"); + + temp = DateTime.GetMonth(tick); + if (temp<10) str.append("0"); + str.append(temp); + str.append("-"); + + str.append(DateTime.GetYear(tick)); + return str.toString(); + } + return ""; + } + + /** + * Convert Datetime Tick to Time String, on format HH:MM:SS + * @param tick : datetime tick in long + * @return empty string if tick less than 1 + */ + public static String Tick_To_HHMMSS(long tick) { + if (tick>0) { + StringBuilder str = new StringBuilder(); + int temp; + temp = DateTime.GetHour(tick); + if (temp<10) str.append("0"); + str.append(temp); + str.append(":"); + + temp = DateTime.GetMinute(tick); + if (temp<10) str.append("0"); + str.append(temp); + str.append(":"); + + temp = DateTime.GetSecond(tick); + if (temp<10) str.append("0"); + str.append(temp); + return str.toString(); + } + return ""; + } + + /** + * Convert Datetime Tick to String , on format DD-MM-YYYY HH:MM:SS + * @param tick : datetime tick in long + * @return empty string if tick less than 1 + */ + public static String Tick_To_DDMMYYYY_HHMMSS(long tick) { + if (tick>0) { + return Tick_To_DDMMYYYY(tick) + " "+ Tick_To_HHMMSS(tick); + } else return ""; + + } + + /** + * Check if GPIO exist + * + * @param pinnya : pin number + * @return true if exist + */ + public static boolean GPIO_Exist(int pinnya) { + + File ff = new File(gpiofolder + "/" + GetGPIOName(pinnya)); + if (ff != null) { + if (ff.exists()) + return true; + } + return false; + } + + /** + * Export GPIO pin + * + * @param pinnya : pin number + * @return true if success + * @throws IOException + */ + public static boolean GPIO_Export(int pinnya) throws IOException { + File ff = new File(gpioexportfolder); + if (ff != null) { + if (ff.exists()) { + if (ff.canWrite()) { + FileOutputStream fos = new FileOutputStream(ff); + fos.write(String.valueOf(pinnya).getBytes()); + fos.close(); + return true; + } + } + } + return false; + } + + /** + * Un-Export GPIO pin + * + * @param pinnya : pin number + * @return true if success + * @throws IOException + */ + public static boolean GPIO_UnExport(int pinnya) throws IOException { + File ff = new File(gpiounexportfolder); + if (ff != null) { + if (ff.exists()) { + if (ff.canWrite()) { + FileOutputStream fos = new FileOutputStream(ff); + fos.write(String.valueOf(pinnya).getBytes()); + fos.close(); + return true; + } + } + } + return false; + } + + /** + * Get GPIO direction + * + * @param pinnya : pin number + * @return empty string if failed, "in" = Input, "out" = Output + * @throws IOException + */ + public static String GPIO_GetDirection(int pinnya) throws IOException { + File ff = new File(gpiofolder + "/" + GetGPIOName(pinnya) + "/direction"); + if (ff != null) { + if (ff.exists()) { + if (ff.canRead()) { + + FileInputStream fis = new FileInputStream(ff); + + byte[] buf = new byte[5]; + int xx = -1; + int ii = 0; + while (true) { + xx = fis.read(); + if (xx == -1) + break; + if (xx == 13) + continue; // skip CR + if (xx == 10) + continue; // skip LF + buf[ii] = (byte) xx; + if (ii < buf.length - 1) + ii += 1; + + } + fis.close(); + return new String(buf); + } + } + } + return ""; + } + + /** + * Set GPIO direction + * + * @param pinnya : pin number + * @param asOutput : true for Output pin, and false for Input pin + * @return true if success + * @throws IOException + */ + public static boolean GPIO_SetDirection(int pinnya, boolean asOutput) throws IOException { + File ff = new File(gpiofolder + "/" + GetGPIOName(pinnya) + "/direction"); + if (ff != null) { + if (ff.exists()) { + if (ff.canWrite()) { + FileOutputStream fos = new FileOutputStream(ff); + if (asOutput) { + fos.write("out".getBytes()); + } else { + fos.write("in".getBytes()); + } + fos.close(); + return true; + } + } + } + return false; + } + + /** + * Get Value from GPIO + * + * @param pinnya : pin number + * @return 0 = low, 1 = high, -1 = unknown / failed + * @throws IOException + */ + public static int GPIO_GetValue(int pinnya) throws IOException { + int result = -1; + File ff = new File(gpiofolder + "/" + GetGPIOName(pinnya) + "/value"); + if (ff != null) { + if (ff.exists()) { + if (ff.canRead()) { + FileInputStream fis = new FileInputStream(ff); + int vv = fis.read(); + fis.close(); + if (vv == '0') { + result = 0; + } else if (vv == '1') { + result = 1; + } + + } + } + } + return result; + } + + /** + * Set Value to GPIO + * + * @param pinnya : pin number + * @param isON : true = high, false = low + * @return true if success + * @throws IOException + */ + public static boolean GPIO_SetValue(int pinnya, boolean isON) throws IOException { + File ff = new File(gpiofolder + "/" + GetGPIOName(pinnya) + "/value"); + if (ff != null) { + if (ff.exists()) { + if (ff.canWrite()) { + FileOutputStream fos = new FileOutputStream(ff); + if (isON) { + fos.write('1'); + } else { + fos.write('0'); + } + fos.close(); + return true; + } + } + } + return false; + } + + + + /** + * Translate to correct gpio name + * + * @param pinnya : pin number + * @return + */ + private static String GetGPIOName(int pinnya) { + return "gpio" + pinnya; + } + + public static String printbytes(byte[] bb) { + if (bb == null) + return ""; + if (bb.length < 0) + return ""; + StringBuilder result = new StringBuilder(); + for (byte xx : bb) { + if (result.length() > 0) + result.append(" , "); + result.append(Bit.ToHexString(xx & 0xFF)); + } + return result.toString(); + } + + /** + * Change format of GPS Latitude String for ddmm.mmmm to dd.ddddd + * + * @param vv : string in format ddmm.mmmm + * @param result : array double , length 1 , untuk passing hasilnya + * @return false kalau gagal + */ + public static boolean Gps_Latitude_formatchange(String vv, double[] result) { + if (result == null) + return false; + if (result.length < 1) + return false; // gak ada tempatnya + if (vv.length() < 6) + return false; // minimal ddmm.m --> 6 karakter + if (vv.indexOf('.') != 4) + return false; // tidak ada titik nya + + try { + + double dd = Double.parseDouble(vv.substring(0, 2)); // dd + double mm = Double.parseDouble(vv.substring(2)); // mm.mmmm + + result[0] = Gps_Latitude_Round(dd + (mm / 60)); + return true; + } catch (IndexOutOfBoundsException | NumberFormatException | NullPointerException e) { + return false; + } + } + + /** + * Change format of GPS Longitude String from dddmm.mmmm to dd.dddddd + * + * @param vv : String in format dddmm.mmmmm + * @param result : array double, length 1, untuk passing hasil + * @return false kalau gagal + */ + public static boolean Gps_Longitude_formatchange(String vv, double[] result) { + if (result == null) + return false; + if (result.length < 1) + return false; // gak ada tempatnya + if (vv.length() < 7) + return false; // minimal dddmm.m --> 7 karakter + if (vv.indexOf('.') != 5) + return false; // tidak ada titiknya + + try { + double dd = Double.parseDouble(vv.substring(0, 3)); // ddd + double mm = Double.parseDouble(vv.substring(3)); // mm.mmmmm + result[0] = Gps_Longitude_Round(dd + (mm / 60)); + return true; + } catch (IndexOutOfBoundsException | NumberFormatException | NullPointerException e) { + return false; + } + } + + /** + * Convert GPS Time String to array of byte + * + * @param vv : GPS time String (hhmmss.ss or hhmmss) + * @param result : array of byte, length = 3, {hour, minute, second} + * @return true if conversion success + */ + public static boolean Gps_UTCTime(String vv, byte[] result) { + if (result == null) + return false; + if (result.length < 3) + return false; // gak ada tempatnya + if (vv.length() < 6) + return false; // minimal hhmmss + + int indextitik = vv.indexOf("."); // kalau ada desimal second + if (indextitik != -1) { + vv = vv.substring(0, indextitik); // potong aja + } + + try { + result[0] = Byte.parseByte(vv.substring(0, 2)); + result[1] = Byte.parseByte(vv.substring(2, 4)); + result[2] = Byte.parseByte(vv.substring(4)); + return true; + } catch (IndexOutOfBoundsException | NumberFormatException e) { + BA.Log("Error getting GPS_UTCTime, Msg : " + e.getMessage() + ", Caused : " + e.getCause()); + return false; + } + } + + /** + * Convert GPS Date String to array of byte + * + * @param vv : GPS Date String (ddmmyy) + * @param result : array of byte, length = 3, {day, month, year} + * @return true if conversion success + */ + public static boolean Gps_UTCDate(String vv, byte[] result) { + if (result == null) + return false; + if (result.length < 3) + return false; // gak ada tempatnya + if (vv.length() < 6) + return false; // harus ddmmyy + + try { + result[0] = Byte.parseByte(vv.substring(0, 2)); + result[1] = Byte.parseByte(vv.substring(2, 4)); + result[2] = Byte.parseByte(vv.substring(4)); + return true; + } catch (IndexOutOfBoundsException | NumberFormatException e) { + BA.Log("Error getting GPS_UTCDate, Msg : " + e.getMessage() + ", Caused : " + e.getCause()); + return false; + } + } + + /** + * Get Longitude Polarity + * + * @param vv : W = negative, E = positive, other = 0 + * @return 1 / -1 / 0 + */ + public static int Longitude_Polarity(String vv) { + if (vv != null) { + if (vv.length() > 0) { + byte[] xx = vv.getBytes(); + if (xx[0] == 87) // W + return -1; + else if (xx[0] == 69) // E + return 1; + else + return 0; // other + } + } + return 0; // invalid + } + + /** + * Convert GPS String to Double + * + * @param vv : string in format xx.xxxx + * @param result : array of double, length = 1 + * @return true if conversion success + */ + public static boolean Gps_Double(String vv, double[] result) { + if (result == null) + return false; + if (result.length < 1) + return false; + + try { + double xx = Double.parseDouble(vv); + result[0] = xx; + return true; + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Convert GPS String to Integer + * + * @param vv : String in integer + * @param result : array of integer, length = 1 + * @return true if conversion success + */ + public static boolean Gps_Int(String vv, int[] result) { + if (result == null) + return false; + if (result.length < 1) + return false; + try { + int xx = Integer.parseInt(vv); + result[0] = xx; + return true; + } catch (NumberFormatException e) { + return false; + } + + } + + /** + * Get first Char value from a GPS String + * + * @param vv : GPS String + * @return char value + */ + public static char Gps_Char(String vv) { + if (vv == null) + return (char) 0; + if (vv.length() < 1) + return (char) 0; + return vv.charAt(0); + } + + /** + * Force a value become Postive + * + * @param vv : value + * @return positive value from vv + */ + public static double SetPositive(double vv) { + if (vv < 0) + return -vv; + else + return vv; + } + + /** + * Force a value become Negative + * + * @param vv : value + * @return negative value from vv + */ + public static double SetNegative(double vv) { + if (vv > 0) + return -vv; + else + return vv; + } + + /** + * Convert GPS Error List to a String, separated by CRLF + * + * @param err : error list + * @return String + */ + public static String Gps_ErrList_to_String(List err) { + if (err == null) + return ""; + if (err.size() < 1) + return ""; + + StringBuilder str = new StringBuilder(); + + for (String xx : err) { + if (str.length() > 0) + str.append("\r\n"); + str.append(xx.trim()); + } + + return str.toString(); + } + + /** + * Make Latitude fractions to 4 digits + * + * @param vv : Latitude in original + * @return Latitude in dd.dddd + */ + public static double Gps_Latitude_Round(double vv) { + return Math.round(vv * 10000) / 10000.0; + } + + /** + * Make Longitude fractions to 4 digits + * + * @param vv : Longitude in original + * @return Longitude in ddd.dddd + */ + public static double Gps_Longitude_Round(double vv) { + return Math.round(vv * 10000) / 10000.0; + } + + /** + * Make 16bit value from 2 bytes + * @param Hbyte : high byte + * @param Lbyte : low byte + * @return value in 16bit + */ + public static int Make_Int16(byte Hbyte, byte Lbyte) { + int result = Bit.And(Hbyte, 0xFF); + result = result << 8; + result = result + Bit.And(Lbyte, 0xFF); + return result; + } + + /** + * Low byte of a 16bit value + * @param value16bit : value to get + * @return low byte of value16bit + */ + public static byte LowByte(int value16bit) { + return (byte) value16bit; + } + + /** + * low byte of a 16bit value, in unsigned value + * @param value16bit : value to get + * @return low byte of value16bit + */ + public static int LowByte_Unsigned(int value16bit) { + return Bit.And(value16bit, 0xFF); + } + + /** + * High byte of a 16bit value + * @param value16bit value to get + * @return high byte of value16bit + */ + public static byte HighByte(int value16bit) { + return (byte) (value16bit >> 8); + } + + /** + * High byte of a 16bit value, in unsigned value + * @param value16bit : value to get + * @return high byte of value16bit + */ + public static int HighByte_Unsigned(int value16bit) { + int result = Bit.And(value16bit, 0xFF00); + return Bit.ShiftRight(result, 8); + } + + /** + * Check if Value is a valid string + * @param vv value to check + * @return true if valid + */ + public static boolean ValidString(String vv) { + if (vv!=null) { + if (vv.length()>0) { + return true; + } + } + return false; + } + + public static byte[] ToByteArray(int... data) { + if (data!=null) { + if (data.length>0) { + byte[] result = new byte[data.length]; + for(int ii=0;ii0) { + for(byte vv : xx) { + if (mycodes.ValidString(separator)) { + if (str.length()>0) { + str.append(separator); + } + } + int value = vv & 0xFF; + str.append(String.format("%02X", value)); + + } + } + return str.toString(); + } + + public static String Bytes_toHex(String separator, byte... value) { + StringBuilder str = new StringBuilder(); + if (value!=null && value.length>0) { + for(byte vv : value) { + if (mycodes.ValidString(separator)) { + if (str.length()>0) { + str.append(separator); + } + } + int vvv = vv & 0xFF; + str.append(String.format("%02X", vvv)); + } + } + return str.toString(); + } + + public static String Bytes_toString(byte[] xx) { + StringBuilder str = new StringBuilder(); + if (xx!=null && xx.length>0) { + for(byte vv : xx) { + str.append((char) vv); + } + } + return str.toString(); + } + + public static boolean IsByteArray_and_have_values(byte[] xx ) { + if (xx != null) { + if (xx.length>0) { + return true; + } + } + return false; + } + + public static int ToInt(byte xx) { + return xx & 0xFF; + } + + public static byte[] ConcatBytes(byte[]... ba ) { + if (ba!=null) { + if (ba.length>0) { + int length = 0; + for(byte[] xx : ba) { + if (xx!=null) { + if (xx.length>0) { + length+=xx.length; + } + } + } + + if (length>0) { + byte[] result = new byte[length]; + int ii = 0; + for(byte[] xx : ba) { + for(int jj = 0; jj + * C type : __time_t + */ + public int tv_sec; + /** + * Microseconds.
+ * C type : __suseconds_t + */ + public int tv_usec; + + public timeval() { + super(); + } + /** + * @param tv_sec Seconds.
+ * C type : __time_t
+ * @param tv_usec Microseconds.
+ * C type : __suseconds_t + */ + public timeval(int tv_sec, int tv_usec) { + super(); + this.tv_sec = tv_sec; + this.tv_usec = tv_usec; + } + protected ByReference newByReference() { + ByReference s = new ByReference(); + s.useMemory(getPointer()); + write(); + s.read(); + return s; + } + protected ByValue newByValue() { + ByValue s = new ByValue(); + s.useMemory(getPointer()); + write(); + s.read(); + return s; + } + protected timeval newInstance() { + timeval s = new timeval(); + s.useMemory(getPointer()); + write(); + s.read(); + return s; + } + + @Override + @Hide + public void autoWrite() {super.autoWrite();} + + @Override + @Hide + public void autoRead() {super.autoRead();} + + public static class ByReference extends timeval implements com.sun.jna.Structure.ByReference {} + public static class ByValue extends timeval implements com.sun.jna.Structure.ByValue {} +} diff --git a/src/linux-arm/libSecureDongle.so b/src/linux-arm/libSecureDongle.so new file mode 100644 index 0000000..e53de37 Binary files /dev/null and b/src/linux-arm/libSecureDongle.so differ diff --git a/src/linux-arm/libfahw.so b/src/linux-arm/libfahw.so new file mode 100644 index 0000000..26aa75e Binary files /dev/null and b/src/linux-arm/libfahw.so differ diff --git a/src/linux-x86-64/libSecureDongle.so b/src/linux-x86-64/libSecureDongle.so new file mode 100644 index 0000000..52b141b Binary files /dev/null and b/src/linux-x86-64/libSecureDongle.so differ diff --git a/src/linux-x86/libSecureDongle.so b/src/linux-x86/libSecureDongle.so new file mode 100644 index 0000000..ff58ea1 Binary files /dev/null and b/src/linux-x86/libSecureDongle.so differ diff --git a/src/network/jUDPSocket.java b/src/network/jUDPSocket.java new file mode 100644 index 0000000..d80a530 --- /dev/null +++ b/src/network/jUDPSocket.java @@ -0,0 +1,336 @@ +package network; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; + +@BA.ShortName("jUDPSocket") +@BA.Events(values= { + "log(msg as string)", + "socketstatus(isopen as boolean)", + "newdata(bb() as byte, sourceip as string, sourceport as int)", + "txrxstatistic(txcount as int, txfail as int, rxcount as int, rxfail as int)" +}) + +/** + * UDP Socket by me, with more functions + * @author rdkartono + * + */ +public class jUDPSocket { + + private BA ba; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_socketstatus_event = false; + private boolean need_newdata_event = false; + private boolean need_txrxstatistic_event = false; + private DatagramSocket udp; + private boolean isopened = false; + + private int txfail=0, txcount=0, rxfail=0, rxcount=0; + + /** + * Initialize UDP Socket (made by RDK) + * @param callerobject : caller object + * @param eventname : event name + */ + public void Initialize(BA bax, Object callerobject, String eventname) { + ba = bax; + caller = callerobject; + event = eventname.trim(); + if (ba instanceof BA) { + if (caller != null) { + if (!event.isEmpty()) { + need_log_event = ba.subExists(event+"_log"); + need_socketstatus_event = ba.subExists(event+"_socketstatus"); + need_newdata_event = ba.subExists(event+"_newdata"); + need_txrxstatistic_event = ba.subExists(event+"_txrxstatistic"); + } + } + } + } + + /** + * Check if UDP already opened + * @return true if opened + */ + public boolean getUDPIsOpened() { + return isopened; + } + + /** + * Close UDP Communication + * will raise socketstatus event + */ + public void Close() { + isopened = false; + if (udp instanceof DatagramSocket) { + udp.close(); + } + + udp = null; + raise_socketstatus(false); + } + + /** + * Get IPV4 bytes from ip string + * @param ipstring : ip string to translate + * @return 0.0.0.0 if failed + */ + public byte[] Get_IPV4_Bytes(String ipstring) { + if (!ipstring.isEmpty()) { + try { + InetAddress xx = InetAddress.getByName(ipstring); + return xx.getAddress(); + } catch (UnknownHostException e) { + raise_log("Unable to Get_IPV4_Bytes, UnknownHostException Msg : "+e.getMessage()); + } catch(SecurityException e) { + raise_log("Unable to Get_IPV4_Bytes, SecurityException Msg : "+e.getMessage()); + } + } + return new byte[4]; + } + + /** + * Ping a target IP + * @param ipstring : target IP + * @return true if reachable + */ + public boolean PingTo(String ipstring) { + try { + InetAddress target = InetAddress.getByName(ipstring); + return target.isReachable(1000); + } catch (UnknownHostException e) { + raise_log("Unable to PingTo "+ipstring+", UnknownHostException Msg : "+e.getMessage()); + } catch (IOException e) { + raise_log("Unable to PingTo "+ipstring+", IOException Msg : "+e.getMessage()); + } + return false; + } + + /** + * Get Local MAC address + * @return empty string if failed + */ + public String GetLocalMAC() { + + try { + InetAddress local = InetAddress.getLocalHost(); + NetworkInterface ni = NetworkInterface.getByInetAddress(local); + byte[] result = ni.getHardwareAddress(); + if (result!=null) { + if (result.length>0) { + StringBuilder str = new StringBuilder(); + for(byte xx : result) { + if (str.length()>0) str.append(":"); + int xt = Bit.And(xx, 0xFF); // jadiin positif dulu + if (xt<10) str.append("0"); // kalau di bawah 10, tambahin 0 , biar double digit + str.append(Bit.ToHexString(xt)); + } + return str.toString().toUpperCase(); + } + } + } catch (UnknownHostException e) { + raise_log("Unable to GetLocalMAC, UnknownHostException Msg : "+e.getMessage()); + } catch (SocketException e) { + raise_log("Unable to GetLocalMAC, SocketException Msg : "+e.getMessage()); + } catch(NullPointerException e) { + raise_log("Unable to GetLocalMAC, NullPointerException Msg : "+e.getMessage()); + } + return ""; + } + + /** + * Get Local IP Address + * @return empty string failed + */ + public String GetLocalIP() { + try { + InetAddress local = InetAddress.getLocalHost(); + return local.getHostAddress(); + } catch (UnknownHostException e) { + raise_log("Unable to GetLocalIP, UnknownHostException Msg : "+e.getMessage()); + } + + return ""; + } + + /** + * Get Local Port bounded/opened with UDP Socket + * @return -1 if UDP is not opened yet + */ + public int GetLocalPort() { + if (udp instanceof DatagramSocket) { + return udp.getLocalPort(); + } + return -1; + } + + /** + * Open UDP Socket + * will raise socketstatus event + * @param port : port number to listen + * @param maxpackagesize : max packagesize. Default to 1000 bytes + * @return true if port can be opened + */ + public boolean Open(int port, int maxpackagesize) { + if (maxpackagesize<1) maxpackagesize = 1000; + + if (udp instanceof DatagramSocket) Close(); // kalau sudah pernah open, close dulu + try { + udp = new DatagramSocket(port); + Thread tx = new Thread(new udprun(udp, maxpackagesize)); + tx.start(); + raise_socketstatus(true); + return true; + } catch (SocketException e) { + raise_log("Unable to Open UDP at port "+port+", SocketException Msg : "+e.getMessage()); + raise_socketstatus(false); + } catch(SecurityException e) { + raise_log("Unable to Open UDP at port "+port+", SecurityException Msg : "+e.getMessage()); + raise_socketstatus(false); + } + return false; + } + + private class udprun implements Runnable { + private final int maxudpsize; + private final DatagramSocket udp; + + public udprun(DatagramSocket udpsock, int maxsize) { + udp = udpsock; + maxudpsize = maxsize; + isopened = true; + } + + @Override + public void run() { + + while(isopened) { + if (udp instanceof DatagramSocket) { + byte[] bb = new byte[maxudpsize]; + DatagramPacket xx = new DatagramPacket(bb, maxudpsize); + try { + udp.receive(xx); + byte[] sourcedata = xx.getData(); + if (sourcedata.length > xx.getLength()) { + byte[] newdata = new byte[xx.getLength()]; + for(int ii=0;ii0) { + if (bb!=null) { + if (bb.length>0) { + if (udp instanceof DatagramSocket) { + try { + InetAddress t_ip = InetAddress.getByName(targetip); + DatagramPacket pp = new DatagramPacket(bb, bb.length, t_ip, targetport); + udp.send(pp); + if (txcount 0 ? baudrate : 9600; + + try { + myport = SerialPort.getCommPort(_portname); + } catch(SerialPortInvalidPortException e) { + raise_log("Port "+ _portname+" is not accessible, Msg : "+e.getMessage()); + return false; + } + + // sampe sini bisa getCommPort + if (myport.openPort()) { + if (myport.setBaudRate(_baudrate)) { + if (myport.addDataListener(new SerialPortDataListener() { + + @Override + public int getListeningEvents() { + // cuma tertarik kalau ada data masuk + return SerialPort.LISTENING_EVENT_DATA_RECEIVED; + } + + @Override + public void serialEvent(SerialPortEvent event) { + if (event instanceof SerialPortEvent) { + byte[] bb = event.getReceivedData(); + if (bb!=null) { + if (bb.length>0) { + // ada data masuk + raise_rawdatareceived(bb); + + rxcount+= bb.length; + raise_txrxcount(); + + process_received_bytes(bb); + } + } + } + } + + })) + + { + + // success semua + return true; + } + else raise_log("Unable to set DataListener event"); + } else raise_log("Unable to set Baudrate "+ _baudrate); + } else raise_log("Unable to open Port "+ _portname); + return false; + } + + private void process_received_bytes(byte[] bb) { + // TODO : nanti hapus log nya + BA.Log("PTZComPort Received : "+mycodes.printbytes(bb)); + int ID = -1; + Object cam; + if (PelcoP.data_valid(bb)) { + // is PelcoP data + ID = Bit.And(bb[1], 0xFF); + cam = GetCamMapObject(ID); + if (cam instanceof PelcoP) { + ((PelcoP) cam).DataReceivedFromPTZComPort(bb); + } + } else if (PelcoD.data_valid(bb)) { + // is PelcoD data + ID = Bit.And(bb[1], 0xFF); + cam = GetCamMapObject(ID); + if (cam instanceof PelcoD) { + ((PelcoD) cam).DataReceivedFromPTZComPort(bb); + } + } + } + + private Object GetCamMapObject(int ID) { + if (cam_map instanceof Map) { + if (cam_map.IsInitialized()) { + if (ID>0) { + if (cam_map.ContainsKey(ID)){ + return cam_map.Get(ID); + } + } + } + } + return null; + } + + /** + * Check if Serial Port is opened + * @return true if opened + */ + public boolean IsOpened() { + if (_jserialcomm_loaded) { + if (myport instanceof SerialPort) { + return myport.isOpen(); + } + } + return false; + } + + /** + * Get Available Serial Port in system + * @return List of Port Names + */ + public List GetSerialPorts() { + List result = new List(); + result.Initialize(); + + if(_jserialcomm_loaded) { + SerialPort[] xx = SerialPort.getCommPorts(); + if (xx!=null) { + if (xx.length>0) { + for(SerialPort yy : xx) { + result.Add(yy.getSystemPortName()); + } + } + } + } + return result; + } + + + /** + * Send Some Data + * @param bb : data to send + * @return true if success + */ + public synchronized boolean SendData(byte[] bb) { + if (IsOpened()) { + if (bb!=null) { + if (bb.length>0) { + int written = myport.writeBytes(bb, bb.length); + raise_rawdatasent(bb); + if (written == bb.length) { + txcount += written; + raise_txrxcount(); + return true; + } else raise_log("SendData failed, counter mismatch"); + } else raise_log("SendData failed, data is zero length"); + } else raise_log("SendData failed, data is null"); + + } else raise_log("SendData failed, SerialPort not opened"); + + return false; + } + + /** + * Change Databit length + * @param value : data bit length, default to 8 + * @return true if sucess + */ + public boolean Set_DataBits(int value) { + if (IsOpened()) { + if (myport.setNumDataBits(value)) { + return true; + } else raise_log("Set_DataBits failed, value is invalid"); + } else raise_log("Set_DataBits failed, SerialPort not opened"); + + return false; + } + + /** + * Change Stopbit length + * @param value : these value + * 1 = 1 stop bit + * 2 = 1.5 stop bit + * 3 = 2 stop bit + * default to 1 + * @return true if success + */ + public boolean Set_StopBits(int value) { + if (IsOpened()) { + if (value<1) value = 1; + if (value>3) value = 1; + + if (myport.setNumStopBits(value)) { + return true; + } else raise_log("Set_StopBits failed, value is invalid"); + + } else raise_log("Set_StopBits failed, SerialPort not opened"); + + return false; + } + + /** + * Change Parity + * @param value : these value + * 0 = No parity + * 1 = Odd Parity + * 2 = Even Parity + * 3 = Mark Parity + * 4 = Space Parity + * Default to No Parity + * @return true if success + */ + public boolean Set_Parity(int value) { + if (IsOpened()) { + if (value<0) value = 0; + if (value>4) value = 0; + + if (myport.setParity(value)) { + return true; + } else raise_log("Set_Parity failed, value is invalid"); + + } else raise_log("Set_Parity failed, SerialPort not opened"); + + return false; + } + + /** + * Set Flow Control + * @param value : these value and their combination (OR-ed) + * 0 : None + * 0x1 : RTS enable + * 0x10 : CTS enabled + * 0x100 : DSR enabled + * 0x1000 : DTR enabled + * 0x10000 : XON-XOFF IN enabled + * 0x100000 : XON-XOFF OUT enabled + * + * if combine RTS + CTS then (0x1 OR 0x10) = 0x11 + * if combine DSR + DTR then (0x100 OR 0x1000) = 0x1100 + * @return true if success + */ + public boolean Set_FlowControl(int value) { + if (IsOpened()) { + if (value<0) value = 0; + + if (myport.setFlowControl(value)) { + return true; + } else raise_log("Set_FlowControl failed, value is invalid"); + + } else raise_log("Set_FlowControl failed, SerialPort not opened"); + + return false; + } + + private void raise_log(String value) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {value}); + } + + private void raise_rawdatareceived(byte[] bb) { + if (need_rawdatareceived_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_rawdatareceived", false, new Object[] {bb}); + } + + private void raise_txrxcount() { + if (need_txrxcount_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_txrxcount", false, new Object[] {txcount, rxcount}); + } + + private void raise_rawdatasent(byte[] bb) { + if (need_rawdatasent_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_rawdatasent", false, new Object[] {bb}); + } +} diff --git a/src/ptzcamera/PelcoD.java b/src/ptzcamera/PelcoD.java new file mode 100644 index 0000000..6983582 --- /dev/null +++ b/src/ptzcamera/PelcoD.java @@ -0,0 +1,711 @@ +package ptzcamera; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import jGPIO.mycodes; + + +// https://www.epiphan.com/userguides/LUMiO12x/Content/UserGuides/PTZ/3-operation/PELCODcommands.htm +// https://www.commfront.com/pages/pelco-d-protocol-tutorial + +@BA.ShortName("PelcoD") +@BA.Events(values= { + "log(msg as string)", + "newposition(pan as int, tilt as int , zoom as int)" +}) +public class PelcoD implements CamFunctions{ + private BA bax; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_newposition_event = false; + private int _id = 1; + private String _name = "Cam1"; + private PTZComPort comport; + private int _pan_position; + private int _tilt_position; + private int _zoom_position; + + /** + * Initialize PelcoD Camera + * @param callerobject : caller objecgt + * @param eventname : event name + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + bax = ba; + caller = callerobject; + event = eventname.trim(); + if (bax instanceof BA) { + if (caller != null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_newposition_event = bax.subExists(event+"_newposition"); + } + } + } + } + + /** + * Setup Camera identification + * @param ID : Camera ID number, start from 1 (Pelco-D) until 80 + * @param CameraName : some camera name + * @param targetport : PTZComPort used in communication + */ + public void Setup(int ID, String CameraName, PTZComPort targetport) { + if (ID>=1) { + if (ID<=80) { + if (_id != ID) { + _id = ID; + raise_log("Camera ID changed to "+ _id); + } else raise_log("Camera ID already "+ ID); + } else raise_log("Invalid new ID="+ID); + } else raise_log("Invalid new ID="+ID); + + if (!CameraName.isEmpty()) { + if (!CameraName.equals(_name)) { + _name = CameraName; + raise_log("Camera Name changed to "+CameraName); + } else raise_log("Camera Name already "+CameraName); + } else raise_log("Camera Name must not empty"); + + if (targetport instanceof PTZComPort) { + if (targetport.IsOpened()) { + comport = targetport; + raise_log("TargetPort set to "+comport.getPortName()); + comport.AddToMap(this); + } else raise_log("TargetPort is closed"); + } else raise_log("TargetPort is invalid"); + } + + /** + * Get assigned ID + * @return ID + */ + public int getID() { + return _id; + } + + /** + * Get assigned Name + * @return Name + */ + public String getName() { + return _name; + } + + private void raise_log(String value) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {value}); + } + + private void raise_newposition(int pan, int tilt, int zoom) { + if (need_newposition_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newposition", false, new Object[] {pan, tilt, zoom}); + } + + private byte[] prepare_databytes() { + byte[] data = new byte[7]; // Pelco-D data length = 7 + data[0] = (byte) 0xFF; // SYNC byte + data[1] = (byte) _id; // camera ID + return data; + } + + private void make_checksum(byte[] bb) { + if (bb!=null) { + if (bb.length == 7) { + // sum of bytes (byte 2 - 6, byte 1 exclude), then modulo 100 (Decimal code: 256) + int result = 0; + for(int ii=1;ii<7;ii++) { + result = result + Bit.And(0xFF, bb[ii]); + } + bb[6] = (byte)(result % 0x100); + } + } + } + + + @Override + /** + * Set Current Position to Preset Number + * @param presetnumber : start from 1 until 0xFF + * @return true if success + */ + public boolean SetPreset(int presetnumber) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 3; + //data[4] = 0; + data[5] = (byte) presetnumber; + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Go To Preset Number + * @param presetnumber : start from 1 until 0xFF + * @return true if success + */ + public boolean GoToPreset(int presetnumber) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 7; + //data[4] = 0; + data[5] = (byte) presetnumber; + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Start Camera Movement + * @param pan : 0 = no pan, 1 = pan right, -1 = pan left + * @param tilt : 0 = no tilt, 1 = tilt up, -1 = tilt down + * @param zoom : 0 = no zoom, 1 = zoom in, -1 = zoom out + * @param speed : 0 until 0x30 + * @return true if success + */ + public boolean Start_PanTiltZoom(int pan, int tilt, int zoom, int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + + // data[3] = bit 7 6 5 4 3 2 1 0 + // 0 Z-Out Z-In T-Down T-Up P-Left P-Right 0 + byte zoom_mask = 0; + byte tilt_mask = 0; + byte pan_mask = 0; + if (zoom!=0) { + if (zoom>0) + zoom_mask = (byte)(0x20); // zoom IN + else + zoom_mask = (byte)(0x40); // zoom Out + } + if (tilt!=0) { + if (tilt>0) + tilt_mask = (byte)(8); // Tilt Up + else + tilt_mask = (byte)(0x10); // Tilt Down + } + if (pan != 0) { + if (pan>0) + pan_mask = (byte)(2); // pan right + else + pan_mask = (byte)(4); // pan lefts + + } + data[3] = (byte)((zoom_mask | tilt_mask | pan_mask) & 0x7E); // di-AND 7E supaya bit 7 = 0 , bit 0 = 0 + + data[4] = (byte)speed; + data[5] = (byte)speed; + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Clear Preset position + * @param presetnumber : start from 1 until 0xFF + * @return true if success + */ + public boolean ClearPreset(int presetnumber) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 5; + //data[4] = 0; + data[5] = (byte) presetnumber; + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Stop PTZ Movement + */ + public boolean StopMoving() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + // 0 semua, gak ngapa-ngapain + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + /** + * Pan to Left + * @param speed : 0 - 0x3F + * @return true if success + */ + @Override + public boolean Left(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 4; // left + data[4] = (byte) speed; // pan speed + //data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Pan to Right + * @param speed : 0 - 0x3F + * @return true if success + */ + public boolean Right(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 2; // right + data[4] = (byte) speed; // pan speed + //data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Tilt Up + * @param speed : 0 - 0x3F + * @return true if success + */ + public boolean Up(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 8; // Up + //data[4] = 0; + data[5] = (byte) speed; // tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Down(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 0x10; // Down + //data[4] = 0; + data[5] = (byte)speed; // Tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean UpLeft(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 0x0C; // up and left + data[4] = (byte)speed; // pan speed + data[5] = (byte)speed; // tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean UpRight(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 0x0A; // up and right + data[4] = (byte)speed; // pan speed + data[5] = (byte)speed; // tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean DownLeft(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 0x14; // down and left + data[4] = (byte)speed; // pan speed + data[5] = (byte)speed; // tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean DownRight(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 0x12; // down and right + data[4] = (byte)speed; // pan speed + data[5] = (byte)speed; // tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean ZoomIn() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 0x20; // Zoom In + //data[4] = 0; + //data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean ZoomOut() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 0x40; // Zoom Out + //data[4] = 0; + //data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean FocusNear() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 1; // focus near + //data[3] = 0; + //data[4] = 0; + //data[5] = 0; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean FocusFar() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = (byte)0x80; // focus far + //data[3] = 0; + //data[4] = 0; + //data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Get_PanPosition() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 0x51; // request PAN position + //data[4] = 0; + //data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + + // expect result : 0xFF Address 0x00 0x59 Value_High_Byte Value_Low_Byte SUM + } + } + return false; + } + + @Override + public boolean Get_TiltPosition() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 0x53; // request TILT position + //data[4] = 0; + //data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + + // expect result : 0xFF Address 0x00 0x5B Value_High_Byte Value_Low_Byte SUM + } + } + return false; + } + + @Override + public boolean Get_ZoomPosition() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = 0x55; // request ZOOM position + //data[4] = 0; + //data[5] = 0; + + + + make_checksum(data); + return comport.SendData(data); + + // expect result : 0xFF Address 0x00 0x5D Value_High_Byte Value_Low_Byte SUM + } + } + return false; + } + + @Override + public boolean Set_Tracking(boolean ON) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = ON ? (byte)0x65 : (byte)0x67; + //data[4] = 0; + //data[5] = 0; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Trigger_Tracking() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = (byte)0x63; + //data[4] = 0; + //data[5] = 0; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Set_WOL(boolean ON) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = ON ? (byte)0x69 : (byte)0x6B; + //data[4] = 0; + //data[5] = 0; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Read_Profile(int profileID) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = (byte) 0x6D; + //data[4] = 0; + data[5] = (byte)profileID; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Save_Profile(int profileID) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = (byte) 0x6F; + //data[4] = 0; + data[5] = (byte)profileID; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @BA.Hide + public static boolean data_valid(byte[] bb) { + if (bb!=null) { + if (bb.length==7) { + if (Bit.And(bb[0], 0xFF)==0xFF) { + int ck = 0; + for(int ii=0;ii<6;ii++) { + ck = ck + Bit.And(bb[0], 0xFF); + } + + byte mod = (byte)(ck % 0x100); + if (bb[6]==mod) { + return true; + } + } + } + } + return false; + } + + @BA.Hide + public void DataReceivedFromPTZComPort(byte[] bb) { + // sampe sini dah valid, dah dicek pake data_valid + // tinggal pakai aja + + int replycode = Bit.And(bb[3], 0xFF); + int pos = mycodes.Make_Int16(bb[4], bb[5]); + + boolean need_raising_newposition = false; + + switch(replycode) { + case 0x59 : + // pan position + if (pos != _pan_position) { + _pan_position = pos; + need_raising_newposition = true; + } + break; + case 0x5B : + // tilt position + if (pos != _tilt_position) { + _tilt_position = pos; + need_raising_newposition = true; + } + break; + case 0x5D : + // zoom position + if (pos != _zoom_position) { + _zoom_position = pos; + need_raising_newposition = true; + } + break; + } + + if (need_raising_newposition) raise_newposition(_pan_position, _tilt_position, _zoom_position); + } + + /** + * Get Last Pan Position + * @return Pan Position + */ + public int PanPosition() { + return _pan_position; + } + + /** + * Get Last Tilt Position + * @return Tilt Position + */ + public int TiltPosition() { + return _tilt_position; + } + + /** + * Get Last Zoom Position + * @return Zoom Position + */ + public int ZoomPosition() { + return _zoom_position; + } +} diff --git a/src/ptzcamera/PelcoP.java b/src/ptzcamera/PelcoP.java new file mode 100644 index 0000000..f9a73e3 --- /dev/null +++ b/src/ptzcamera/PelcoP.java @@ -0,0 +1,708 @@ +package ptzcamera; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Bit; +import jGPIO.mycodes; + +@BA.ShortName("PelcoP") +@BA.Events(values= { + "log(msg as string)", + "newposition(pan as int, tilt as int , zoom as int)" +}) +public class PelcoP implements CamFunctions { + + private BA bax; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_newposition_event = false; + private int _id = 0; + private String _name = "Cam1"; + private PTZComPort comport; + private int _pan_position; + private int _tilt_position; + private int _zoom_position; + + /** + * Initialize PelcoP Camera + * @param callerobject : caller objecgt + * @param eventname : event name + */ + public void Initialize(BA ba, Object callerobject, String eventname) { + bax = ba; + caller = callerobject; + event = eventname.trim(); + if (bax instanceof BA) { + if (caller != null) { + if (!event.isEmpty()) { + need_log_event = bax.subExists(event+"_log"); + need_newposition_event = bax.subExists(event+"_newposition"); + } + } + } + } + + /** + * Setup Camera identification + * @param ID : ID number, start from 0 (Pelco-P) until 0x7F + * @param CameraName : some camera name + * @param targetport : PTZComPort used in communication + */ + public void Setup(int ID, String CameraName, PTZComPort targetport) { + if (ID>=0) { + if (ID<=0x7F) { + if (_id != ID) { + _id = ID; + raise_log("Camera ID changed to "+ _id); + } else raise_log("Camera ID already "+ ID); + } else raise_log("Invalid new ID="+ID); + } else raise_log("Invalid new ID="+ID); + + if (!CameraName.isEmpty()) { + if (!CameraName.equals(_name)) { + _name = CameraName; + raise_log("Camera Name changed to "+CameraName); + } else raise_log("Camera Name already "+CameraName); + } else raise_log("Camera Name must not empty"); + + if (targetport instanceof PTZComPort) { + if (targetport.IsOpened()) { + comport = targetport; + raise_log("TargetPort set to "+comport.getPortName()); + comport.AddToMap(this); + } else raise_log("TargetPort is closed"); + } else raise_log("TargetPort is invalid"); + } + + /** + * Get assigned ID + * @return ID + */ + public int getID() { + return _id; + } + + /** + * Get assigned Name + * @return Name + */ + public String getName() { + return _name; + } + + private void raise_log(String value) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {value}); + } + + private void raise_newposition(int pan, int tilt, int zoom) { + if (need_newposition_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_newposition", false, new Object[] {pan, tilt, zoom}); + } + + private byte[] prepare_databytes() { + byte[] data = new byte[8]; // Pelco-P data length = 8 + data[0] = (byte) 0xA0; // STX + data[1] = (byte) _id; // camera ID + data[6] = (byte) 0xAF; // ETX + return data; + } + + private void make_checksum(byte[] bb) { + if (bb != null) { + if (bb.length == 8) { + int result = 0; + for(int ii=0;ii<8;ii++) { // byte 1 - 7 di - XOR, jadi checksum + result = Bit.Xor(result, Bit.And(0xFF, bb[ii])); + } + bb[7] = (byte) result; + } + } + } + + @Override + /** + * Set Current Position to Preset Number + * @param presetnumber : start from 1 until 0xFF + * @return true if success + */ + public boolean SetPreset(int presetnumber) { + + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 3; + data[4] = 0; + data[5] = (byte) presetnumber; + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Go To Preset Number + * @param presetnumber : start from 1 until 0xFF + * @return true if success + */ + public boolean GoToPreset(int presetnumber) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 7; + data[4] = 0; + data[5] = (byte) presetnumber; + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Start Camera Movement + * @param pan : 0 = no pan, 1 = pan right, -1 = pan left + * @param tilt : 0 = no tilt, 1 = tilt up, -1 = tilt down + * @param zoom : 0 = no zoom, 1 = zoom in, -1 = zoom out + * @param speed : 0 until 0x30 + * @return true if success + */ + public boolean Start_PanTiltZoom(int pan, int tilt, int zoom, int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + // data[3] : bit 7 6 5 4 3 2 1 0 + // 0 Z-out Z-in T-Down T-Up P-Left P-Right 0 + byte zoom_mask = 0; + byte tilt_mask = 0; + byte pan_mask = 0; + if (zoom!=0) { + if (zoom>0) + zoom_mask = (byte)(0x20); // zoom IN + else + zoom_mask = (byte)(0x40); // zoom Out + } + if (tilt!=0) { + if (tilt>0) + tilt_mask = (byte)(8); // Tilt Up + else + tilt_mask = (byte)(0x10); // Tilt Down + } + if (pan != 0) { + if (pan>0) + pan_mask = (byte)(2); // pan right + else + pan_mask = (byte)(4); // pan lefts + + } + data[3] = (byte)((zoom_mask | tilt_mask | pan_mask) & 0x7E); // di-AND 7E supaya bit 7 = 0 , bit 0 = 0 + + data[4] = (byte) speed; // pan speed + data[5] = (byte) speed; // tilt speed + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Clear Preset position + * @param presetnumber : start from 1 until 0xFF + * @return true if success + */ + public boolean ClearPreset(int presetnumber) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 5; + data[4] = 0; + data[5] = (byte) presetnumber; + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Stop PTZ Movement + */ + public boolean StopMoving() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + // gak ngapa-ngapain di sini + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Pan to Left + * @param speed : 0 - 0x3F + * @return true if success + */ + public boolean Left(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 4; // left + data[4] = (byte) speed; // pan speed + data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + /** + * Pan to Right + * @param speed : 0 - 0x3F + * @return true if success + */ + public boolean Right(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 2; // right + data[4] = (byte) speed; // pan speed + data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Up(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 8; // up + data[4] = 0; + data[5] = (byte) speed; // tilt speed + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Down(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 0x10; // down + data[4] = 0; + data[5] = (byte) speed; // tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean UpLeft(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 0x0C; // up dan left + data[4] = (byte) speed; // pan speed + data[5] = (byte) speed; // tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean UpRight(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 0x0A; // up and right + data[4] = (byte) speed; // pan speed + data[5] = (byte) speed; // tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean DownLeft(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 0x14; // down and left + data[4] = (byte) speed; // pan speed + data[5] = (byte) speed; // tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean DownRight(int speed) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 0x12; // down and right + data[4] = (byte) speed; // pan speed + data[5] = (byte) speed; // tilt speed + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean ZoomIn() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 0x20; // zoom in + data[4] = 0; + data[5] = 0; + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean ZoomOut() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 0x40; // zoom out + data[4] = 0; + data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean FocusNear() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 1; // focus near + data[3] = 0; + data[4] = 0; + data[5] = 0; + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean FocusFar() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = (byte)0x80; // focus far + data[3] = 0; + data[4] = 0; + data[5] = 0; + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Get_PanPosition() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 0x51; // request PAN position + data[4] = 0; + data[5] = 0; + + + make_checksum(data); + return comport.SendData(data); + + // expect result : 0xA0 Address 0x00 0x59 Value_High_Byte Value_Low_Byte 0xAF XOR + } + } + return false; + } + + @Override + public boolean Get_TiltPosition() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 0x53; // request TILT position + data[4] = 0; + data[5] = 0; + + + make_checksum(data); + return comport.SendData(data); + + // expect result : 0xA0 Address 0x00 0x5B Value_High_Byte Value_Low_Byte 0xAF XOR + } + } + return false; + } + + @Override + public boolean Get_ZoomPosition() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + data[2] = 0; + data[3] = 0x55; // request PAN position + data[4] = 0; + data[5] = 0; + + make_checksum(data); + return comport.SendData(data); + + // expect result : 0xA0 Address 0x00 0x5D Value_High_Byte Value_Low_Byte 0xAF XOR + } + } + return false; + } + + @Override + public boolean Set_Tracking(boolean ON) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = ON ? (byte)0x65 : (byte)0x67; + //data[4] = 0; + //data[5] = 0; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Trigger_Tracking() { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = (byte)0x63; + //data[4] = 0; + //data[5] = 0; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Set_WOL(boolean ON) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = ON ? (byte)0x69 : (byte)0x6B; + //data[4] = 0; + //data[5] = 0; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Read_Profile(int profileID) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = (byte) 0x6D; + //data[4] = 0; + data[5] = (byte)profileID; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @Override + public boolean Save_Profile(int profileID) { + if (comport instanceof PTZComPort) { + if (comport.IsOpened()) { + byte[] data = prepare_databytes(); + //data[2] = 0; + data[3] = (byte) 0x6F; + //data[4] = 0; + data[5] = (byte)profileID; + + + + make_checksum(data); + return comport.SendData(data); + } + } + return false; + } + + @BA.Hide + public static boolean data_valid(byte[] bb) { + if (bb!=null) { + if (bb.length==8) { + if (Bit.And(bb[0], 0xFF)==0xA0) { + if (Bit.And(bb[6], 0xFF)==0xAF) { + int ck = 0; + for(int ii=0;ii<7;ii++) { + ck = Bit.Xor(ck, Bit.And(bb[ii], 0xFF)); + } + if (bb[7]==(byte)ck) { + return true; + } + } + } + } + } + return false; + } + + @BA.Hide + public void DataReceivedFromPTZComPort(byte[] bb) { + // sampe sini dah valid, dah dicek pake data_valid + // tinggal pakai aja + + int replycode = Bit.And(bb[3], 0xFF); + int pos = mycodes.Make_Int16(bb[4], bb[5]); + + boolean need_raising_newposition = false; + + switch(replycode) { + case 0x59 : + // pan position + if (pos != _pan_position) { + _pan_position = pos; + need_raising_newposition = true; + } + break; + case 0x5B : + // tilt position + if (pos != _tilt_position) { + _tilt_position = pos; + need_raising_newposition = true; + } + break; + case 0x5D : + // zoom position + if (pos != _zoom_position) { + _zoom_position = pos; + need_raising_newposition = true; + } + break; + } + + if (need_raising_newposition) raise_newposition(_pan_position, _tilt_position, _zoom_position); + } + + /** + * Get Last Pan Position + * @return Pan Position + */ + public int PanPosition() { + return _pan_position; + } + + /** + * Get Last Tilt Position + * @return Tilt Position + */ + public int TiltPosition() { + return _tilt_position; + } + + /** + * Get Last Zoom Position + * @return Zoom Position + */ + public int ZoomPosition() { + return _zoom_position; + } +} diff --git a/src/ptzcamera/ViscaOverIP.java b/src/ptzcamera/ViscaOverIP.java new file mode 100644 index 0000000..5d92c1b --- /dev/null +++ b/src/ptzcamera/ViscaOverIP.java @@ -0,0 +1,1510 @@ +package ptzcamera; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("ViscaOverIP") +@BA.Events(values= { + "log(msg as string)", + "camerapower(success as boolean, id as int, value as boolean)", + "whitebalancemode(success as boolean, id as int, value as string)", + "rgain(success as boolean, id as int, value as int)", + "bgain(success as boolean, id as int, value as int)", + "aemode(success as boolean, id as int, value as string)", + "shutterposition(success as boolean, id as int, value as int)", + "irisposition(success as boolean, id as int, value as int)", + "gainposition(success as boolean, id as int, value as int)", + "brightposition(success as boolean, id as int, value as int)", + "expcompposition(success as boolean, id as int, value as int)", + "focusmode(success as boolean, id as int, value as string)", + "focusposition(success as boolean, id as int, value as int)", + "zoomposition(success as boolean, id as int, value as int)", + "pantiltposition(success as boolean, id as int, panvalue as int, tiltvalue as int)", + "preset(success as boolean, id as int, value as int)", + "trackingstatus(success as boolean, id as int, value as boolean)", + "trackingmode(success as boolean, id as int, value as string)", + "trackingbodysize(success as boolean, id as int, value as string)", + "osdmenu(success as boolean, id as int, value as boolean)", + "tally(success as boolean, id as int, value as boolean)", + "wdrmode(success as boolean, id as int, value as boolean)", + "blcmode(success as boolean, id as int, value as boolean)", + "livefreeze(success as boolean, id as int, value as boolean)", + "presetfreeze(success as boolean, id as int, value as boolean)", + "firmwareversion(success as boolean, id as int, value as string)", + "usbpluggedstatus(success as boolean, id as int, value as boolean)", + "uvcstatus(success as boolean, id as int, value as boolean)" + +}) +public class ViscaOverIP { + private BA ba; + private String event; + private Object Me = this; + + private DatagramSocket myport; + + // event + private boolean need_log_event = false; + private boolean need_camerapower_event = false; + private boolean need_whitebalancemode_event = false; + private boolean need_rgain_event = false; + private boolean need_bgain_event = false; + private boolean need_aemode_event = false; + private boolean need_shutterposition_event = false; + private boolean need_irisposition_event = false; + private boolean need_gainposition_event = false; + private boolean need_brightposition_event = false; + private boolean need_expcompposition_event = false; + private boolean need_focusmode_event = false; + private boolean need_focusposition_event = false; + private boolean need_zoomposition_event = false; + private boolean need_pantiltposition_event = false; + private boolean need_preset_event = false; + private boolean need_trackingstatus_event = false; + private boolean need_trackingmode_event = false; + private boolean need_trackingbodysize_event = false; + private boolean need_osdmenu_event = false; + private boolean need_tally_event = false; + private boolean need_wdrmode_event = false; + private boolean need_blcmode_event = false; + private boolean need_livefreeze_event = false; + private boolean need_presetfreeze_event = false; + private boolean need_firmwareversion_event = false; + private boolean need_usbpluggedstatus_event = false; + private boolean need_uvcstatus_event = false; + + // timeout in miliseconds + private final int read_timeout = 10; + + // sequence number + private Integer sequence_number = 0; + + private final int udp_port = 52381; + + private final Map map = new HashMap(); + + /** + * Initialize Visca PTZ Controller + * @param eventname eventname + */ + public void Initialize(BA bax, String eventname) { + this.ba = bax; + this.event = eventname; + if (ba!=null) { + if (event!=null) { + if (event.isEmpty()==false) { + need_log_event = ba.subExists(event+"_log"); + need_camerapower_event = ba.subExists(event+"_camerapower"); + need_whitebalancemode_event = ba.subExists(event+"_whitebalancemode"); + need_rgain_event = ba.subExists(event+"_rgain"); + need_bgain_event = ba.subExists(event+"_bgain"); + need_aemode_event = ba.subExists(event+"_aemode"); + need_shutterposition_event = ba.subExists(event+"_shutterposition"); + need_irisposition_event = ba.subExists(event+"_irisposition"); + need_gainposition_event = ba.subExists(event+"_gainposition"); + need_brightposition_event = ba.subExists(event+"_brightposition"); + need_expcompposition_event = ba.subExists(event+"_expcompposition"); + need_focusmode_event = ba.subExists(event+"_focusmode"); + need_focusposition_event = ba.subExists(event+"_focusposition"); + need_zoomposition_event = ba.subExists(event+"_zoomposition"); + need_pantiltposition_event = ba.subExists(event+"_pantiltposition"); + need_preset_event = ba.subExists(event+"_preset"); + need_trackingstatus_event = ba.subExists(event+"_trackingstatus"); + need_trackingmode_event = ba.subExists(event+"_trackingmode"); + need_trackingbodysize_event = ba.subExists(event+"_trackingbodysize"); + need_osdmenu_event = ba.subExists(event+"_osdmenu"); + need_tally_event = ba.subExists(event+"_tally"); + need_wdrmode_event = ba.subExists(event+"_wdrmode"); + need_blcmode_event = ba.subExists(event+"_blcmode"); + need_livefreeze_event = ba.subExists(event+"_livefreeze"); + need_presetfreeze_event = ba.subExists(event+"_presetfreeze"); + need_firmwareversion_event = ba.subExists(event+"_firmwareversion"); + need_usbpluggedstatus_event = ba.subExists(event+"_usbpluggedstatus"); + need_uvcstatus_event = ba.subExists(event+"_uvcstatus"); + } + } + } + } + + /** + * Open UDP Port to communicate + * Will open at port 52381 + * @return true if can be opened + */ + public boolean OpenPort() { + try { + myport = new DatagramSocket(udp_port); + myport.setSoTimeout(read_timeout); + return true; + } catch (SocketException e) { + raise_log("Unable to create UDP Socket at 52381, Exception="+e.getMessage()); + } + return false; + } + + + + /** + * Close SerialPort + */ + public void ClosePort() { + if (myport!=null) { + myport.close(); + + myport = null; + } + + } + + /** + * Register ID and IP address for IP Camera + * @param ID value 1 - 7 + * @param ipaddress valid IP address + * @param UdpPort valid UDP Port + * @return true if success + */ + public boolean Register_Device(int ID, String ipaddress, int UdpPort) { + try { + InetAddress ip = InetAddress.getByName(ipaddress); + InetSocketAddress sa = new InetSocketAddress(ip, UdpPort); + InetSocketAddress prev = map.put(ID, sa); + if (prev!=null) { + raise_log("ID="+ID+" replacing "+SocketAddressToString(prev)+" with "+SocketAddressToString(sa)); + } else { + raise_log("Register ID="+ID+" to "+SocketAddressToString(sa)); + } + return true; + } catch (UnknownHostException | IllegalArgumentException e) { + raise_log("Register_Device failed, exception="+e.getMessage()); + return false; + } + + } + + private String SocketAddressToString(InetSocketAddress sa) { + if (sa!=null) { + return sa.getHostString()+":"+sa.getPort(); + } + return ""; + } + + + /** + * Check if serialport is opened + * @return true if opened + */ + public boolean IsOpened() { + if (myport!=null) { + return myport.isClosed()==false; + } + return false; + } + + /** + * Make Camera to ON or OFF + * @param ID value 1 - 7 + * @param toON + *
true = Set Camera to ON + *
false = Set Camera to OFF + * @return true if success + */ + public boolean CameraPower(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0, (toON ? 2 : 3), 0xFF); + } + + /** + * Stop Zooming + * @param ID value 1 -7 + * @return true if success + */ + public boolean Zoom_Stop(int ID) { + return SendData(false,MakeID(ID), 1, 4, 7, 0, 0xFF); + } + + /** + * Zoom Tele + * @param ID value 1 - 7 + * @param speed 0 (low) - 7 (high) + * @return true if success + */ + public boolean Zoom_Tele(int ID, int speed) { + if (speed<0) speed = 0; + if (speed>7) speed = 7; + return SendData(false,MakeID(ID), 1, 4, 7, (0x20+speed), 0xFF); + } + + /** + * Zoom Wide + * @param ID value 1 - 7 + * @param speed 0 (low) - 7 (high) + * @return true if success + */ + public boolean Zoom_Wide(int ID, int speed) { + if (speed<0) speed = 0; + if (speed>7) speed = 7; + return SendData(false,MakeID(ID), 1, 4, 7, (0x30+speed), 0xFF); + } + + /** + * Zoom directly to specific position + * @param ID value 1 - 7 + * @param position + *
0x0000 - 0x6F20 for PTC310 + *
0x0110 - 0x5490 for PTC330 + * @return true if success + */ + public boolean Zoom_Direct(int ID, int position) { + int p = (position & 0xF000) >> 24; + int q = (position & 0x0F00) >> 16; + int r = (position & 0x00F0) >> 8 ; + int s = (position & 0x000F); + return SendData(false,MakeID(ID), 1, 4, 0x47, p, q, r, s, 0xFF); + } + + /** + * Stop Focusing. Call after Focus_Far or Focus_Near + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_Stop(int ID) { + return SendData(false,MakeID(ID), 1, 4, 8, 0, 0xFF); + } + + /** + * Focus to Far. call Focus_Stop if enough + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_Far(int ID) { + return SendData(false,MakeID(ID), 1, 4, 8, 2, 0xFF); + } + + /** + * Focus to Near. Call Focus_Stop if enough + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_Near(int ID) { + return SendData(false,MakeID(ID), 1, 4 ,8, 3, 0xFF); + } + + /** + * Auto Focus + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_Auto(int ID) { + return SendData(false,MakeID(ID), 1, 4, 0x38, 2, 0xFF); + } + + /** + * Manual Focus + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_Manual(int ID) { + return SendData(false,MakeID(ID), 1, 4, 0x38, 3, 0xFF); + } + + /** + * One Push Focus + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_OnePush(int ID) { + return SendData(false,MakeID(ID), 1, 4, 0x18, 1, 0xFF); + } + + /** + * Focus Directly to specific position + * @param ID value 1 - 7 + * @param position + *
0x0000 - 0x6F20 for PTC310 + *
0x0110 - 0x5490 for PTC330 + * @return true if success + */ + public boolean Focus_Direct(int ID, int position) { + + return SendData(false,MakeID(ID), 1, 4, 0x47, GetByteFromInt(position,3), GetByteFromInt(position,2), GetByteFromInt(position,1), GetByteFromInt(position,0), 0xFF); + } + + /** + * Change White Balance Mode + * @param ID value 1 - 7 + * @param mode value 0 - 5, default to 0 + *
0 = Auto + *
1 = Indoor + *
2 = Outdoor + *
3 = One push mode + *
4 = Auto Trace Whitebalance + *
5 = Manual + * @return true if success + */ + public boolean WhiteBalance_Mode(int ID, int mode) { + if (mode<0) mode = 0; + if (mode>5) mode = 0; + return SendData(false,MakeID(ID), 1, 4, 0x35, mode, 0xFF); + } + + /** + * Trigger OnePush at WhiteBalance + * @param ID value 1 - 7 + * @return true if success + */ + public boolean WhiteBalance_OnePush_Trigger(int ID) { + return SendData(false,MakeID(ID), 1, 4, 0x10, 5, 0xFF); + } + + /** + * Increase / Decrease Red Gain + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean RedGain(int ID, boolean toUp) { + return SendData(false,MakeID(ID), 1, 4, 3, (toUp ? 2 : 3), 0xFF); + } + + /** + * Increase / Decrease Blue Gain + * @param ID value 1 - 7 + * @param toUp true =up, false = down + * @return true if success + */ + public boolean BlueGain(int ID, boolean toUp) { + return SendData(false,MakeID(ID), 1, 4, 4, (toUp ? 2 : 3), 0xFF); + } + + /** + * Change Auto Exposure Mode + * @param ID value 1 - 7 + * @param mode 0 - 4 + *
0 = Full Auto, default to 0 + *
1 = Manual + *
2 = Shutter Priority + *
3 = Iris Priority + *
4 = Bright + * @return true if success + */ + public boolean AutoExposure_Mode(int ID, int mode) { + int code = 0; + switch(mode) { + case 1 : + code = 0x3; + break; + case 2 : + code = 0xA; + break; + case 3 : + code = 0xB; + break; + case 4 : + code = 0xD; + default : + code = 0; + break; + } + return SendData(false,MakeID(ID), 1, 4, 0x39, code, 0xFF); + } + + /** + * Increase / Decrease Shutter + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean Shutter(int ID, boolean toUp) { + return SendData(false,MakeID(ID), 1, 4, 0xA, (toUp ? 2 : 3), 0xFF); + } + + /** + * Increase / Decrease Iris + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean Iris(int ID, boolean toUp) { + return SendData(false,MakeID(ID), 1, 4, 0xB, (toUp ? 2 : 3), 0xFF); + } + + /** + * Increase / Decrease Gain + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean Gain(int ID, boolean toUp) { + return SendData(false,MakeID(ID), 1, 4, 0xC, (toUp ? 2 : 3), 0xFF); + } + + /** + * Increase / Decrease Brightness + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean Brightness(int ID, boolean toUp) { + return SendData(false,MakeID(ID), 1, 4, 0xD, (toUp ? 2 : 3), 0xFF); + } + + /** + * Increase / Decrease ExpComp + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean ExpComp(int ID, boolean toUp) { + return SendData(false,MakeID(ID), 1, 4, 0xE, (toUp ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF BackLight + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean BackLight(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0x33, (toON ? 2 : 3), 0xFF); + } + + /** + * Delete Preset Position + * @param ID value 1 - 7 + * @param position 0 - 0xFF + * @return true if success + */ + public boolean Preset_Reset(int ID, int position) { + if (position<0) position = 0; + if (position>0xFF) position = 0xFF; + return SendData(false,MakeID(ID), 1, 4, 0x3F, 0, position, 0xFF); + } + + /** + * Save Preset Position + * @param ID value 1 - 7 + * @param position 0 - 0xFF + * @return true if success + */ + public boolean Preset_Set(int ID, int position) { + if (position<0) position = 0; + if (position>0xFF) position = 0xFF; + return SendData(false,MakeID(ID), 1, 4, 0x3F, 1, position, 0xFF); + } + + /** + * Call saved Preset Position + * @param ID value 1 - 7 + * @param position 0 - 0xFF + * @return true if success + */ + public boolean Preset_Call(int ID, int position) { + if (position<0) position = 0; + if (position>0xFF) position = 0xFF; + return SendData(false,MakeID(ID), 1, 4, 0x3F, 2, position, 0xFF); + } + + /** + * Toggle Show / Hide Menu + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Menu_ShowHide(int ID) { + return SendData(false,MakeID(ID), 1, 6, 6, 0x10, 0xFF); + } + + /** + * Enter to sub-menu at Menu Screen + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Menu_Enter(int ID) { + return SendData(false,MakeID(ID), 1, 0x7E, 1, 2, 0, 1, 0xFF); + } + + /** + * Move Camera Pan and Tilt + * Can move Pan, Tilt, or combination of them + * To stop, give PanMode = 3 and TiltMode = 3 + * @param ID value 1 - 7 + * @param PanMode value 1 - 3, default to 3 + *
1 = Left + *
2 = Right + *
3 = Stop Pan + * @param TiltMode value 1 - 3, default to 3 + *
1 = Up + *
2 = Down + *
3 = Stop Tilt + * @param PanSpeed value 0x1 (low) - 0x18 (high) + * @param TiltSpeed value 0x1 (low) - 0x18 (high) + * @return true if success + */ + public boolean PanTilt(int ID, int PanMode, int TiltMode, int PanSpeed, int TiltSpeed) { + if (PanMode<1) PanMode = 3; + if (PanMode>3) PanMode = 3; + if (TiltMode<1) TiltMode = 3; + if (TiltMode>3) TiltMode = 3; + if (PanSpeed<1) PanSpeed = 1; + if (PanSpeed>0x18) PanSpeed = 0x18; + if (TiltSpeed<1) TiltSpeed = 01; + if (TiltSpeed>0x18) TiltSpeed = 0x18; + return SendData(false,MakeID(ID), 1, 6, 1, PanSpeed, TiltSpeed, PanMode, TiltMode, 0xFF); + } + + /** + * Turn ON / OFF WDR + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean WideDynamicRange(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0x3D, (toON ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF Tally Lamp + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean Tally_Lamp(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 0x7E, 1, 0xA, 0, (toON ? 2 : 3), 0xFF); + } + + /** + * Freeze Picture immediately + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean FreezePicture(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0x62, (toON ? 2 : 3), 0xFF); + } + + + /** + * Freeze Picture when doing preset move + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean FreezePicture_WhenPresetMove(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0x62, (toON ? 0x22 : 0x23), 0xFF); + } + + /** + * Turn ON / OFF Auto Track + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean AutoTracking(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0x7D, (toON ? 2 : 3), 0xFF); + } + + /** + * Set Memory Special + * @param ID value 1 - 7 + * @param preset + *
0x0 - 0xFF = Normal Preset + *
0x5F = Trun(?) on OSD Menu + *
0xA0 = Full Body + *
0xA1 = Upper Body + *
0xA2 = Tracking Point + *
0xA3 = Switch + *
0xA4 = Presenter Mode (firmware v25 or higher) + *
0xA5 = Zone Mode (firmware v25 or higher) + *
0xA6 = Hybrid Mode (firmware v25 or higher) + * @return true if success + */ + public boolean Memory_Special(int ID, int preset) { + return SendData(false,MakeID(ID), 1, 4, 0x3F, 1, preset, 0xFF); + } + + /** + * Move to absolute position + * @param ID value 1 - 7 + * @param PanPosition gak ada keterangan, apakah sama dengan Zoom Position ? + * @param TiltPosition gak ada keterangan, apakah sama dengan Zoom Positon ? + * @param PanSpeed 0x1 - 0x18 + * @param TiltSpeed 0x1 - 0x18 + * @return true if success + */ + public boolean MoveToAbsolutePosition(int ID, int PanPosition, int TiltPosition, int PanSpeed, int TiltSpeed) { + if (PanSpeed < 1) PanSpeed = 1; + if (PanSpeed > 0x18) PanSpeed = 0x18; + if (TiltSpeed < 1) TiltSpeed = 1; + if (TiltSpeed > 0x18) TiltSpeed = 0x18; + return SendData(false,MakeID(ID), 1, 6, 2, PanSpeed, TiltSpeed, GetByteFromInt(PanPosition,3), GetByteFromInt(PanPosition,2), GetByteFromInt(PanPosition,1), GetByteFromInt(PanPosition,0), GetByteFromInt(TiltPosition,3), GetByteFromInt(TiltPosition,2), GetByteFromInt(TiltPosition,1), GetByteFromInt(TiltPosition,0), 0xFF); + } + + /** + * Turn ON / OFF Auto Zoom + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean AutoZoom(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0xA0, (toON ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF Effective Tracking Area + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean EffectiveTrackingArea(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0xA1, (toON ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF RTMP + * @param ID value 1 -7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean RTMP(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0xA2, (toON ? 2 : 3), 0xFF); + } + + /** + * Change Video Mode + * @param ID value 1 - 7 + * @param mode + *
0 = IP+Stream + *
1 = USB Only + *
2 = NDI only + *
3 = Streaming Only + * @return true if success + */ + public boolean VideoMode(int ID, int mode) { + if (mode<0) mode = 0; + if (mode>3) mode = 3; + return SendData(false,MakeID(ID), 1, 4, 0xA3, mode, 0xFF); + } + + /** + * Reboot Camera + * @param ID + * @return true if success + */ + public boolean Reboot(int ID) { + return SendData(false,MakeID(ID), 1, 4, 0xA4, 0xFF); + } + + /** + * Turn ON / OFF Preset Affects PTZ and Focus + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean PresetAffectsPTZandFocus(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0xA5, (toON ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF Relative Zoom Ratio + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean RelativeZoomRatio(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0xA6, (toON ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF Auto Tilt + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean AutoTilt(int ID, boolean toON) { + return SendData(false,MakeID(ID), 1, 4, 0xA7, (toON ? 2 : 3), 0xFF); + } + + /** + * Auto Zoom or Tilt Preset + * @param ID value 1 - 7 + * @param preset 00 - 0xFF + * @return true if success + */ + public boolean AutoZoomTiltPreset(int ID, int preset) { + if (preset<0) preset = 0; + if (preset > 0xFF) preset = 0xFF; + return SendData(false,MakeID(ID),1, 4, 0xA8, preset, 0xFF); + } + + /** + * Get Camera Power + * will raise event camerapower + * @param ID value 1 - 7 + */ + public void Get_CameraPower(int ID) { + if (SendData(true,MakeID(ID),9,4,0,0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result,ID,4)) { + if (result[2]==2) { + raise_camerapower(true, ID, true); + return; + } else if (result[2]==3) { + raise_camerapower(true, ID, false); + return; + } + } + } + raise_camerapower(false, ID, false); + } + + /** + * Get White Balance Mode + * will raise event whitebalancemode + * @param ID value 1 - 7 + */ + public void Get_WhiteBalanceMode(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x35, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID,4)) { + int code = ByteToInt(result[2]); + switch(code) { + case 0 : + raise_whitebalancemode(true, ID, "Auto"); + return; + case 1 : + raise_whitebalancemode(true, ID, "Indoor"); + return; + case 2 : + raise_whitebalancemode(true, ID, "Outdoor"); + return; + case 3 : + raise_whitebalancemode(true, ID, "One Push WB"); + return; + case 4 : + raise_whitebalancemode(true, ID, "ATW"); + return; + case 5 : + raise_whitebalancemode(true, ID, "Manual"); + return; + } + } + } + raise_whitebalancemode(false, ID, ""); + } + + /** + * Get R-Gain + * will raise event rgain + * @param ID value 1 - 7 + */ + public void Get_RGain(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x43, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result,ID,7)) { + raise_rgain(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_rgain(false, ID, 0); + } + + /** + * Get B-Gain + * will raise event bgain + * @param ID value 1 - 7 + */ + public void Get_BGain(int ID) { + if (SendData(true,MakeID(ID), 9,4, 0x44,0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_bgain(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_bgain(false, ID, 0); + } + + /** + * Get Auto Exposure Mode + * will raise event aemode + * @param ID value 1 - 7 + */ + public void Get_AutoExposureMode(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x39, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + int code = ByteToInt(result[2]); + switch(code) { + case 0 : + raise_aemode(true, ID, "Full Auto"); + return; + case 3 : + raise_aemode(true, ID, "Manual"); + return; + case 0xA : + raise_aemode(true, ID, "Shutter Priority"); + return; + case 0xB : + raise_aemode(true, ID, "Iris Priority"); + return; + case 0xD : + raise_aemode(true, ID, "Bright"); + return; + } + } + } + raise_aemode(false, ID, ""); + } + + /** + * Get Shutter position + * will raise event shutterposition + * @param ID value 1 - 7 + */ + public void Get_ShutterPosition(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x4A, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_shutterposition(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_shutterposition(false, ID, 0); + } + + /** + * Get Iris position + * will raise event irisposition + * @param ID value 1 - 7 + */ + public void Get_IrisPosition(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x4B, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_irisposition(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_irisposition(false, ID, 0); + } + + /** + * Get Gain position + * will raise event gainposition + * @param ID value 1 - 7 + */ + public void Get_GainPosition(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x4C, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_gainposition(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_gainposition(false, ID, 0); + } + + /** + * Get Bright position + * will raise event brightposition + * @param ID value 1 - 7 + */ + public void Get_BrightPosition(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x4D, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_brightposition(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_brightposition(false, ID, 0); + } + + /** + * Get Exposure Compensation position + * will raise event expcompposition + * @param ID value 1 - 7 + */ + public void Get_ExpCompPosition(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x4E, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_expcompposition(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_expcompposition(false, ID, 0); + } + + /** + * Get Focus Mode + * will raise event focusmode + * @param ID value 1- 7 + */ + public void Get_FocusMode(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x38, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + if (result[2]==2) { + raise_focusmode(true, ID, "Auto"); + return; + } else if (result[2]==3) { + raise_focusmode(true, ID, "Manual"); + return; + } + } + } + raise_focusmode(false, ID, ""); + } + + /** + * Get Focus Position + * will raise event focusposition + * @param ID value 1 - 7 + */ + public void Get_FocusPosition(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x48, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_focusposition(true, ID, ToInt(result[2], result[3], result[4], result[5]) ); + return; + } + } + raise_focusposition(false, ID, 0); + } + + /** + * Get Zoom Position + * will raise event zoomposition + * @param ID value 1 - 7 + */ + public void Get_ZoomPosition(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x47, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_zoomposition(true, ID, ToInt(result[2], result[3], result[4], result[5]) ); + return; + } + } + raise_zoomposition(false, ID, 0); + } + + /** + * Get Pan and Tilt Position + * will raise event pantiltposition + * @param ID value 1 - 7 + */ + public void Get_PanTiltPosition(int ID) { + if (SendData(true,MakeID(ID), 9, 6, 0x12, 0xFF)) { + byte[] result = ReadData(11); + if (ValidCommandRead(result, ID, 11)) { + raise_pantiltposition(true, ID, ToInt(result[2], result[3], result[4], result[5]), ToInt(result[6], result[7], result[8], result[9]) ); + return; + } + } + raise_pantiltposition(false, ID, 0,0); + } + + /** + * Get current preset + * will raise event preset + * @param ID value 1 - 7 + */ + public void Get_Preset(int ID) { + if (SendData(true,MakeID(ID), 9,4, 0x3F, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_preset(true, ID, ByteToInt(result[2])); + return; + } + } + raise_preset(false, ID, 0); + } + + /** + * Get Tracking Status + * will raise event trackingstatus + * @param ID value 1 - 7 + */ + public void Get_TrackingStatus(int ID) { + if (SendData(true,MakeID(ID), 9,0x36, 0x69, 2, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_trackingstatus(true, ID, ByteToInt(result[2])==1); + return; + + } + } + raise_trackingstatus(false, ID, false); + } + + /** + * Get Tracking Mode + * will raise event trackingmode + * @param ID value 1 - 7 + */ + public void Get_TrackingMode(int ID) { + if (SendData(true,MakeID(ID), 9,0x36, 0x69, 1, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + int code = ByteToInt(result[2]); + switch(code) { + case 1: + raise_trackingmode(true, ID, "Presenter"); + return; + case 2 : + raise_trackingmode(true, ID, "Zone"); + return; + case 3 : + raise_trackingmode(true, ID, "Hybrid"); + return; + } + } + } + raise_trackingmode(false, ID, ""); + } + + /** + * Get Tracking Body Size + * will raise event trackingbodysize + * @param ID value 1 - 7 + */ + public void Get_TrackingBodySize(int ID) { + if (SendData(true,MakeID(ID), 9,0x36, 0x69, 3, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + int code = ByteToInt(result[2]); + switch(code) { + case 1: + raise_trackingbodysize(true, ID, "Full Body"); + return; + case 2 : + raise_trackingbodysize(true, ID, "Upper Body"); + return; + } + } + } + raise_trackingbodysize(false, ID, ""); + } + + /** + * Get if Menu OSD Displayed + * will raise event osdmenu + * @param ID value 1 - 7 + */ + public void Get_OSDMenu(int ID) { + if (SendData(true,MakeID(ID), 9, 0x7E, 4, 0x76, 1, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_osdmenu(true, ID, result[2]==2); + return; + } + } + raise_osdmenu(false, ID, false); + } + + /** + * Get if Tally is ON or OFF + * will raise event tally + * @param ID value 1 - 7 + */ + public void Get_Tally(int ID) { + if (SendData(true,MakeID(ID), 9, 0x7E, 1, 0xA, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_tally(true, ID, result[2]==2); + return; + } + } + raise_tally(false, ID, false); + } + + /** + * Get if WDR Mode is ON or OFF + * will raise event wdrmode + * @param ID value 1 - 7 + */ + public void Get_WDRMode(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x3D, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_wdrmode(true, ID, result[2]==2); + return; + } + } + raise_wdrmode(false, ID, false); + } + + /** + * Get if BackLightCompensation (BLC) Mode is ON or OFF + * will raise event blcmode + * @param ID value 1 - 7 + */ + public void Get_BLCMode(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x33, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_blcmode(true, ID, result[2]==2); + return; + } + } + raise_blcmode(false, ID, false); + } + + /** + * Get if Live Freeze Mode is ON or OFF + * will raise event livefreeze + * @param ID value 1 - 7 + */ + public void Get_LiveFreezeMode(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x62, 1, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_livefreeze(true, ID, result[2]==2); + return; + } + } + raise_livefreeze(false, ID, false); + } + + /** + * Get if Preset Freeze Mode is ON or OFF + * will raise event presetfreeze + * @param ID value 1 - 7 + */ + public void Get_PresetFreezeMode(int ID) { + if (SendData(true,MakeID(ID), 9, 4, 0x62, 2, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_presetfreeze(true, ID, result[2]==2); + return; + } + } + raise_presetfreeze(false, ID, false); + } + + /** + * Get Firmware Version + * will raise event firmwareversion + * @param ID value 1 - 7 + */ + public void Get_FirmwareVersion(int ID) { + if (SendData(true,MakeID(ID), 9, 0x36, 0x69, 4, 0xFF)) { + byte[] result = ReadData(11); + if (ValidCommandRead(result, ID, 11)) { + StringBuilder str = new StringBuilder(); + str.append(ByteToInt(result[2])).append("."); + str.append(ByteToInt(result[3])).append("."); + str.append(ToInt(result[4], result[5], result[6], result[7])).append("."); + str.append(WordToInt(result[8], result[9])); + raise_firmwareversion(true, ID, str.toString()); + return; + } + } + raise_firmwareversion(false, ID, ""); + } + + /** + * Get USB Plugged status + * will raise event usbpluggedstatus + * @param ID value 1 - 7 + */ + public void Get_USBPluggedStatus(int ID) { + if (SendData(true,MakeID(ID), 9, 0x36, 0x69, 5, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_usbpluggedstatus(true, ID, result[2]==1); + return; + } + } + raise_usbpluggedstatus(false, ID, false); + } + + /** + * Get UVC Stream ON or OFF + * will raise event uvcstatus + * @param ID value 1 - 7 + */ + public void Get_UVCStatus(int ID) { + if (SendData(true,MakeID(ID), 9, 0x36, 0x69, 6, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_uvcstatus(true,ID, result[2]==1); + return; + } + } + raise_uvcstatus(false, ID, false); + } + + /** + * Convert Byte to Int + * @param bb byte value + * @return int value + */ + private int ByteToInt(byte bb) { + return (bb & 0xFF); + } + + private int WordToInt(byte bh, byte bl) { + return (ByteToInt(bh) << 8) + ByteToInt(bl); + } + + private int ToInt(byte b3, byte b2, byte b1, byte b0) { + return (ByteToInt(b3)<<24) | (ByteToInt(b2)<<16) | (ByteToInt(b1)<<8) | (ByteToInt(b0)); + } + + /** + * Make ID for sending data + * sending data always 0x8y , with y = ID + * @param ID 1 - 7 + * @return value in 0x8y + */ + private int MakeID(int ID) { + if (ID<1) ID = 1; + if (ID>7) ID = 7; + return (0x80 + ID); + } + + /** + * Make ID for Inquiry data + * getting data always 0xY0, with Y = ID + * @param ID 1 - 7 + * @return value in 0xY0 + */ + private int MakeIDGet(int ID) { + if (ID<1) ID = 1; + if (ID>7) ID = 7; + return (0x10 * ID); + } + + /** + * Send Integer Data to UDP + * @param IsInquiry if true, is a Get command + * @param cmd array of integer + * @return true if can be send + */ + private boolean SendData(boolean IsInquiry, int... cmd ) { + if (cmd!=null) { + if (cmd.length>0) { + // ID dalam format 0x8Y + // dengan Y = 1 - 7 + int ID = cmd[0]; + if (ID>=0x81 && ID<=0x87) { + ID = ID & 0x0F; + InetSocketAddress sa = map.get(ID); + if (sa!=null) { + if (IsOpened()) { + byte[] bytecmd = new byte[cmd.length+8]; + bytecmd[0] = 1; + bytecmd[1] = (byte) (IsInquiry ? 0x10 : 0); + bytecmd[2] = 0; + bytecmd[3] = (byte) cmd.length; + + bytecmd[4] = GetByteFromInt(sequence_number,3); + bytecmd[5] = GetByteFromInt(sequence_number,2); + bytecmd[6] = GetByteFromInt(sequence_number,1); + bytecmd[7] = GetByteFromInt(sequence_number,0); + if (sequence_number>8); + case 2 : + return (byte)((value & 0x0F00)>>16); + case 3 : + return (byte)((value & 0xF000)>>24); + default : + return (byte) (value & 0x000F); + + } + } + + /** + * Read Data from Serial Port + * @param expectedlength number of bytes expected to be read + * @return empty array if failed, or array with expectedlength if success + */ + private byte[] ReadData(int expectedlength) { + if (IsOpened()) { + + byte[] buf = new byte[expectedlength+8]; + DatagramPacket dp = new DatagramPacket(buf, buf.length); + try { + myport.receive(dp); + buf = dp.getData(); + if (buf.length>=(expectedlength+8)) { + if (buf[0]==1) { + if (buf[1]==0x11) { + byte[] result = new byte[expectedlength]; + for(int ii=0;ii=expectedlength) { + // minimal reply itu 4 bytes + int header = ByteToInt(bb[0]); + int footer = ByteToInt(bb[length-1]); + int code = ByteToInt(bb[1]); + if (footer == 0xFF) { + if (code==0x50) { + return (header==MakeIDGet(ID)); + } + } + } + } + return false; + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_camerapower(boolean success, int id, boolean value) { + if (need_camerapower_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_camerapower", false, new Object[] {success, id, value}); + } + + private void raise_whitebalancemode(boolean success, int id, String value) { + if (need_whitebalancemode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_whitebalancemode", false, new Object[] {success, id, value}); + } + + private void raise_rgain(boolean success, int id, int value) { + if (need_rgain_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_rgain", false, new Object[] {success, id, value}); + } + + private void raise_bgain(boolean success, int id, int value) { + if (need_bgain_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_bgain", false, new Object[] {success, id, value}); + } + + private void raise_aemode(boolean success, int id, String value) { + if (need_aemode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_aemode", false, new Object[] {success, id, value}); + } + + private void raise_shutterposition(boolean success, int id, int value) { + if (need_shutterposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_shutterposition", false, new Object[] {success, id, value}); + } + + private void raise_irisposition(boolean success, int id, int value) { + if (need_irisposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_irisposition", false, new Object[] {success, id, value}); + } + + private void raise_gainposition(boolean success, int id, int value) { + if (need_gainposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_gainposition", false, new Object[] {success, id, value}); + } + + private void raise_brightposition(boolean success, int id, int value) { + if (need_brightposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_brightposition", false, new Object[] {success, id, value}); + } + + private void raise_expcompposition(boolean success, int id, int value) { + if (need_expcompposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_expcompposition", false, new Object[] {success, id, value}); + } + + private void raise_focusmode(boolean success, int id, String value) { + if (need_focusmode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_focusmode", false, new Object[] {success, id, value}); + } + + private void raise_focusposition(boolean success, int id, int value) { + if (need_focusposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_focusposition", false, new Object[] {success, id, value}); + } + + private void raise_zoomposition(boolean success, int id, int value) { + if (need_zoomposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_zoomposition", false, new Object[] {success, id, value}); + } + + private void raise_pantiltposition(boolean success, int id, int panvalue, int tiltvalue) { + if (need_pantiltposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_pantiltposition", false, new Object[] {success, id, panvalue, tiltvalue}); + } + + private void raise_preset(boolean success, int id, int value) { + if (need_preset_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_preset", false, new Object[] {success, id, value}); + } + + private void raise_trackingstatus(boolean success, int id, boolean value) { + if (need_trackingstatus_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_trackingstatus", false, new Object[] {success, id, value}); + } + + private void raise_trackingmode(boolean success, int id, String value) { + if (need_trackingmode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_trackingmode", false, new Object[] {success, id, value}); + } + + private void raise_trackingbodysize(boolean success, int id, String value) { + if (need_trackingbodysize_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_trackingbodysize", false, new Object[] {success, id, value}); + } + + private void raise_osdmenu(boolean success, int id, boolean value) { + if (need_osdmenu_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_osdmenu", false, new Object[] {success, id, value}); + } + + private void raise_tally(boolean success, int id, boolean value) { + if (need_tally_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_tally", false, new Object[] {success, id, value}); + } + + private void raise_wdrmode(boolean success, int id, boolean value) { + if (need_wdrmode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_wdrmode", false, new Object[] {success, id, value}); + } + + private void raise_blcmode(boolean success, int id, boolean value) { + if (need_blcmode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_blcmode", false, new Object[] {success, id, value}); + } + + private void raise_livefreeze(boolean success, int id, boolean value) { + if (need_livefreeze_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_livefreeze", false, new Object[] {success, id, value}); + } + + private void raise_presetfreeze(boolean success, int id, boolean value) { + if (need_presetfreeze_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_presetfreeze", false, new Object[] {success, id, value}); + } + + private void raise_firmwareversion(boolean success, int id, String value) { + if (need_firmwareversion_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_firmwareversion", false, new Object[] {success, id, value}); + } + + private void raise_usbpluggedstatus(boolean success, int id, boolean value) { + if (need_usbpluggedstatus_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_usbpluggedstatus", false, new Object[] {success, id, value}); + } + + private void raise_uvcstatus(boolean success, int id, boolean value) { + if (need_uvcstatus_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_uvcstatus", false, new Object[] {success, id, value}); + } +} diff --git a/src/ptzcamera/ViscaPTZ.java b/src/ptzcamera/ViscaPTZ.java new file mode 100644 index 0000000..ac56614 --- /dev/null +++ b/src/ptzcamera/ViscaPTZ.java @@ -0,0 +1,1453 @@ +package ptzcamera; + +import com.fazecast.jSerialComm.SerialPort; + +import com.fazecast.jSerialComm.SerialPortInvalidPortException; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("ViscaPTZ") +@BA.Events(values= { + "log(msg as string)", + "camerapower(success as boolean, id as int, value as boolean)", + "whitebalancemode(success as boolean, id as int, value as string)", + "rgain(success as boolean, id as int, value as int)", + "bgain(success as boolean, id as int, value as int)", + "aemode(success as boolean, id as int, value as string)", + "shutterposition(success as boolean, id as int, value as int)", + "irisposition(success as boolean, id as int, value as int)", + "gainposition(success as boolean, id as int, value as int)", + "brightposition(success as boolean, id as int, value as int)", + "expcompposition(success as boolean, id as int, value as int)", + "focusmode(success as boolean, id as int, value as string)", + "focusposition(success as boolean, id as int, value as int)", + "zoomposition(success as boolean, id as int, value as int)", + "pantiltposition(success as boolean, id as int, panvalue as int, tiltvalue as int)", + "preset(success as boolean, id as int, value as int)", + "trackingstatus(success as boolean, id as int, value as boolean)", + "trackingmode(success as boolean, id as int, value as string)", + "trackingbodysize(success as boolean, id as int, value as string)", + "osdmenu(success as boolean, id as int, value as boolean)", + "tally(success as boolean, id as int, value as boolean)", + "wdrmode(success as boolean, id as int, value as boolean)", + "blcmode(success as boolean, id as int, value as boolean)", + "livefreeze(success as boolean, id as int, value as boolean)", + "presetfreeze(success as boolean, id as int, value as boolean)", + "firmwareversion(success as boolean, id as int, value as string)", + "usbpluggedstatus(success as boolean, id as int, value as boolean)", + "uvcstatus(success as boolean, id as int, value as boolean)" + +}) +public class ViscaPTZ { + private BA ba; + private String event; + private Object Me = this; + private SerialPort myport = null; + + // event + private boolean need_log_event = false; + private boolean need_camerapower_event = false; + private boolean need_whitebalancemode_event = false; + private boolean need_rgain_event = false; + private boolean need_bgain_event = false; + private boolean need_aemode_event = false; + private boolean need_shutterposition_event = false; + private boolean need_irisposition_event = false; + private boolean need_gainposition_event = false; + private boolean need_brightposition_event = false; + private boolean need_expcompposition_event = false; + private boolean need_focusmode_event = false; + private boolean need_focusposition_event = false; + private boolean need_zoomposition_event = false; + private boolean need_pantiltposition_event = false; + private boolean need_preset_event = false; + private boolean need_trackingstatus_event = false; + private boolean need_trackingmode_event = false; + private boolean need_trackingbodysize_event = false; + private boolean need_osdmenu_event = false; + private boolean need_tally_event = false; + private boolean need_wdrmode_event = false; + private boolean need_blcmode_event = false; + private boolean need_livefreeze_event = false; + private boolean need_presetfreeze_event = false; + private boolean need_firmwareversion_event = false; + private boolean need_usbpluggedstatus_event = false; + private boolean need_uvcstatus_event = false; + + // timeout in miliseconds + private final int write_timeout = 10; + private final int read_timeout = 10; + + /** + * Initialize Visca PTZ Controller + * @param eventname eventname + */ + public void Initialize(BA bax, String eventname) { + this.ba = bax; + this.event = eventname; + if (ba!=null) { + if (event!=null) { + if (event.isEmpty()==false) { + need_log_event = ba.subExists(event+"_log"); + need_camerapower_event = ba.subExists(event+"_camerapower"); + need_whitebalancemode_event = ba.subExists(event+"_whitebalancemode"); + need_rgain_event = ba.subExists(event+"_rgain"); + need_bgain_event = ba.subExists(event+"_bgain"); + need_aemode_event = ba.subExists(event+"_aemode"); + need_shutterposition_event = ba.subExists(event+"_shutterposition"); + need_irisposition_event = ba.subExists(event+"_irisposition"); + need_gainposition_event = ba.subExists(event+"_gainposition"); + need_brightposition_event = ba.subExists(event+"_brightposition"); + need_expcompposition_event = ba.subExists(event+"_expcompposition"); + need_focusmode_event = ba.subExists(event+"_focusmode"); + need_focusposition_event = ba.subExists(event+"_focusposition"); + need_zoomposition_event = ba.subExists(event+"_zoomposition"); + need_pantiltposition_event = ba.subExists(event+"_pantiltposition"); + need_preset_event = ba.subExists(event+"_preset"); + need_trackingstatus_event = ba.subExists(event+"_trackingstatus"); + need_trackingmode_event = ba.subExists(event+"_trackingmode"); + need_trackingbodysize_event = ba.subExists(event+"_trackingbodysize"); + need_osdmenu_event = ba.subExists(event+"_osdmenu"); + need_tally_event = ba.subExists(event+"_tally"); + need_wdrmode_event = ba.subExists(event+"_wdrmode"); + need_blcmode_event = ba.subExists(event+"_blcmode"); + need_livefreeze_event = ba.subExists(event+"_livefreeze"); + need_presetfreeze_event = ba.subExists(event+"_presetfreeze"); + need_firmwareversion_event = ba.subExists(event+"_firmwareversion"); + need_usbpluggedstatus_event = ba.subExists(event+"_usbpluggedstatus"); + need_uvcstatus_event = ba.subExists(event+"_uvcstatus"); + } + } + } + } + + /** + * Open serialport + * @param portname port name + * @param baudrate valid value 4800, 9600, 38400. Default to 9600 + * @return true if success + */ + public boolean OpenPort(String portname, int baudrate) { + if (portname!=null) { + if (!portname.isEmpty()) { + boolean correctbaud = false; + if (baudrate==4800) correctbaud = true; + if (baudrate==9600) correctbaud = true; + if (baudrate==38400) correctbaud = true; + if (!correctbaud) baudrate = 9600; + + try { + myport = SerialPort.getCommPort(portname); + myport.setBaudRate(baudrate); + myport.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING | SerialPort.TIMEOUT_WRITE_BLOCKING, read_timeout, write_timeout); + if (myport.openPort()) { + return true; + } + + } catch(SerialPortInvalidPortException e) { + raise_log("OpenPort exception, message="+e.getMessage()); + } + + } + } + return false; + } + + /** + * Close SerialPort + * @return true if success + */ + public boolean ClosePort() { + if (myport!=null) { + myport.removeDataListener(); + boolean result = myport.closePort(); + myport = null; + return result; + } + else return false; + } + + /** + * Check if serialport is opened + * @return true if opened + */ + public boolean IsOpened() { + if (myport!=null) { + return myport.isOpen(); + } + return false; + } + + /** + * Make Camera to ON or OFF + * @param ID value 1 - 7 + * @param toON + *
true = Set Camera to ON + *
false = Set Camera to OFF + * @return true if success + */ + public boolean CameraPower(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0, (toON ? 2 : 3), 0xFF); + } + + /** + * Stop Zooming + * @param ID value 1 -7 + * @return true if success + */ + public boolean Zoom_Stop(int ID) { + return SendData(MakeID(ID), 1, 4, 7, 0, 0xFF); + } + + /** + * Zoom Tele + * @param ID value 1 - 7 + * @param speed 0 (low) - 7 (high) + * @return true if success + */ + public boolean Zoom_Tele(int ID, int speed) { + if (speed<0) speed = 0; + if (speed>7) speed = 7; + return SendData(MakeID(ID), 1, 4, 7, (0x20+speed), 0xFF); + } + + /** + * Zoom Wide + * @param ID value 1 - 7 + * @param speed 0 (low) - 7 (high) + * @return true if success + */ + public boolean Zoom_Wide(int ID, int speed) { + if (speed<0) speed = 0; + if (speed>7) speed = 7; + return SendData(MakeID(ID), 1, 4, 7, (0x30+speed), 0xFF); + } + + /** + * Zoom directly to specific position + * @param ID value 1 - 7 + * @param position + *
0x0000 - 0x6F20 for PTC310 + *
0x0110 - 0x5490 for PTC330 + * @return true if success + */ + public boolean Zoom_Direct(int ID, int position) { + int p = (position & 0xF000) >> 24; + int q = (position & 0x0F00) >> 16; + int r = (position & 0x00F0) >> 8 ; + int s = (position & 0x000F); + return SendData(MakeID(ID), 1, 4, 0x47, p, q, r, s, 0xFF); + } + + /** + * Stop Focusing. Call after Focus_Far or Focus_Near + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_Stop(int ID) { + return SendData(MakeID(ID), 1, 4, 8, 0, 0xFF); + } + + /** + * Focus to Far. call Focus_Stop if enough + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_Far(int ID) { + return SendData(MakeID(ID), 1, 4, 8, 2, 0xFF); + } + + /** + * Focus to Near. Call Focus_Stop if enough + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_Near(int ID) { + return SendData(MakeID(ID), 1, 4 ,8, 3, 0xFF); + } + + /** + * Auto Focus + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_Auto(int ID) { + return SendData(MakeID(ID), 1, 4, 0x38, 2, 0xFF); + } + + /** + * Manual Focus + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_Manual(int ID) { + return SendData(MakeID(ID), 1, 4, 0x38, 3, 0xFF); + } + + /** + * One Push Focus + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Focus_OnePush(int ID) { + return SendData(MakeID(ID), 1, 4, 0x18, 1, 0xFF); + } + + /** + * Focus Directly to specific position + * @param ID value 1 - 7 + * @param position + *
0x0000 - 0x6F20 for PTC310 + *
0x0110 - 0x5490 for PTC330 + * @return true if success + */ + public boolean Focus_Direct(int ID, int position) { + int p = (position & 0xF000) >> 24; + int q = (position & 0x0F00) >> 16; + int r = (position & 0x00F0) >> 8 ; + int s = (position & 0x000F); + return SendData(MakeID(ID), 1, 4, 0x47, p, q, r, s, 0xFF); + } + + /** + * Change White Balance Mode + * @param ID value 1 - 7 + * @param mode value 0 - 5, default to 0 + *
0 = Auto + *
1 = Indoor + *
2 = Outdoor + *
3 = One push mode + *
4 = Auto Trace Whitebalance + *
5 = Manual + * @return true if success + */ + public boolean WhiteBalance_Mode(int ID, int mode) { + if (mode<0) mode = 0; + if (mode>5) mode = 0; + return SendData(MakeID(ID), 1, 4, 0x35, mode, 0xFF); + } + + /** + * Trigger OnePush at WhiteBalance + * @param ID value 1 - 7 + * @return true if success + */ + public boolean WhiteBalance_OnePush_Trigger(int ID) { + return SendData(MakeID(ID), 1, 4, 0x10, 5, 0xFF); + } + + /** + * Increase / Decrease Red Gain + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean RedGain(int ID, boolean toUp) { + return SendData(MakeID(ID), 1, 4, 3, (toUp ? 2 : 3), 0xFF); + } + + /** + * Increase / Decrease Blue Gain + * @param ID value 1 - 7 + * @param toUp true =up, false = down + * @return true if success + */ + public boolean BlueGain(int ID, boolean toUp) { + return SendData(MakeID(ID), 1, 4, 4, (toUp ? 2 : 3), 0xFF); + } + + /** + * Change Auto Exposure Mode + * @param ID value 1 - 7 + * @param mode 0 - 4 + *
0 = Full Auto, default to 0 + *
1 = Manual + *
2 = Shutter Priority + *
3 = Iris Priority + *
4 = Bright + * @return true if success + */ + public boolean AutoExposure_Mode(int ID, int mode) { + int code = 0; + switch(mode) { + case 1 : + code = 0x3; + break; + case 2 : + code = 0xA; + break; + case 3 : + code = 0xB; + break; + case 4 : + code = 0xD; + default : + code = 0; + break; + } + return SendData(MakeID(ID), 1, 4, 0x39, code, 0xFF); + } + + /** + * Increase / Decrease Shutter + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean Shutter(int ID, boolean toUp) { + return SendData(MakeID(ID), 1, 4, 0xA, (toUp ? 2 : 3), 0xFF); + } + + /** + * Increase / Decrease Iris + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean Iris(int ID, boolean toUp) { + return SendData(MakeID(ID), 1, 4, 0xB, (toUp ? 2 : 3), 0xFF); + } + + /** + * Increase / Decrease Gain + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean Gain(int ID, boolean toUp) { + return SendData(MakeID(ID), 1, 4, 0xC, (toUp ? 2 : 3), 0xFF); + } + + /** + * Increase / Decrease Brightness + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean Brightness(int ID, boolean toUp) { + return SendData(MakeID(ID), 1, 4, 0xD, (toUp ? 2 : 3), 0xFF); + } + + /** + * Increase / Decrease ExpComp + * @param ID value 1 - 7 + * @param toUp true = up, false = down + * @return true if success + */ + public boolean ExpComp(int ID, boolean toUp) { + return SendData(MakeID(ID), 1, 4, 0xE, (toUp ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF BackLight + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean BackLight(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0x33, (toON ? 2 : 3), 0xFF); + } + + /** + * Delete Preset Position + * @param ID value 1 - 7 + * @param position 0 - 0xFF + * @return true if success + */ + public boolean Preset_Reset(int ID, int position) { + if (position<0) position = 0; + if (position>0xFF) position = 0xFF; + return SendData(MakeID(ID), 1, 4, 0x3F, 0, position, 0xFF); + } + + /** + * Save Preset Position + * @param ID value 1 - 7 + * @param position 0 - 0xFF + * @return true if success + */ + public boolean Preset_Set(int ID, int position) { + if (position<0) position = 0; + if (position>0xFF) position = 0xFF; + return SendData(MakeID(ID), 1, 4, 0x3F, 1, position, 0xFF); + } + + /** + * Call saved Preset Position + * @param ID value 1 - 7 + * @param position 0 - 0xFF + * @return true if success + */ + public boolean Preset_Call(int ID, int position) { + if (position<0) position = 0; + if (position>0xFF) position = 0xFF; + return SendData(MakeID(ID), 1, 4, 0x3F, 2, position, 0xFF); + } + + /** + * Toggle Show / Hide Menu + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Menu_ShowHide(int ID) { + return SendData(MakeID(ID), 1, 6, 6, 0x10, 0xFF); + } + + /** + * Enter to sub-menu at Menu Screen + * @param ID value 1 - 7 + * @return true if success + */ + public boolean Menu_Enter(int ID) { + return SendData(MakeID(ID), 1, 0x7E, 1, 2, 0, 1, 0xFF); + } + + /** + * Move Camera Pan and Tilt + * Can move Pan, Tilt, or combination of them + * To stop, give PanMode = 3 and TiltMode = 3 + * @param ID value 1 - 7 + * @param PanMode value 1 - 3, default to 3 + *
1 = Left + *
2 = Right + *
3 = Stop Pan + * @param TiltMode value 1 - 3, default to 3 + *
1 = Up + *
2 = Down + *
3 = Stop Tilt + * @param PanSpeed value 0x1 (low) - 0x18 (high) + * @param TiltSpeed value 0x1 (low) - 0x18 (high) + * @return true if success + */ + public boolean PanTilt(int ID, int PanMode, int TiltMode, int PanSpeed, int TiltSpeed) { + if (PanMode<1) PanMode = 3; + if (PanMode>3) PanMode = 3; + if (TiltMode<1) TiltMode = 3; + if (TiltMode>3) TiltMode = 3; + if (PanSpeed<1) PanSpeed = 1; + if (PanSpeed>0x18) PanSpeed = 0x18; + if (TiltSpeed<1) TiltSpeed = 01; + if (TiltSpeed>0x18) TiltSpeed = 0x18; + return SendData(MakeID(ID), 1, 6, 1, PanSpeed, TiltSpeed, PanMode, TiltMode, 0xFF); + } + + /** + * Turn ON / OFF WDR + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean WideDynamicRange(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0x3D, (toON ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF Tally Lamp + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean Tally_Lamp(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 0x7E, 1, 0xA, 0, (toON ? 2 : 3), 0xFF); + } + + /** + * Freeze Picture immediately + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean FreezePicture(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0x62, (toON ? 2 : 3), 0xFF); + } + + + /** + * Freeze Picture when doing preset move + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean FreezePicture_WhenPresetMove(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0x62, (toON ? 0x22 : 0x23), 0xFF); + } + + /** + * Turn ON / OFF Auto Track + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean AutoTracking(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0x7D, (toON ? 2 : 3), 0xFF); + } + + /** + * Set Memory Special + * @param ID value 1 - 7 + * @param preset + *
0x0 - 0xFF = Normal Preset + *
0x5F = Trun(?) on OSD Menu + *
0xA0 = Full Body + *
0xA1 = Upper Body + *
0xA2 = Tracking Point + *
0xA3 = Switch + *
0xA4 = Presenter Mode (firmware v25 or higher) + *
0xA5 = Zone Mode (firmware v25 or higher) + *
0xA6 = Hybrid Mode (firmware v25 or higher) + * @return true if success + */ + public boolean Memory_Special(int ID, int preset) { + return SendData(MakeID(ID), 1, 4, 0x3F, 1, preset, 0xFF); + } + + /** + * Move to absolute position + * @param ID value 1 - 7 + * @param PanPosition gak ada keterangan, apakah sama dengan Zoom Position ? + * @param TiltPosition gak ada keterangan, apakah sama dengan Zoom Positon ? + * @param PanSpeed 0x1 - 0x18 + * @param TiltSpeed 0x1 - 0x18 + * @return true if success + */ + public boolean MoveToAbsolutePosition(int ID, int PanPosition, int TiltPosition, int PanSpeed, int TiltSpeed) { + int p3 = (PanPosition & 0xF000) >> 24; + int p2 = (PanPosition & 0x0F00) >> 16; + int p1 = (PanPosition & 0x00F0) >> 8; + int p0 = (PanPosition & 0x000F); + int t3 = (TiltPosition & 0xF000) >> 24; + int t2 = (TiltPosition & 0x0F00) >> 16; + int t1 = (TiltPosition & 0x00F0) >> 8; + int t0 = (TiltPosition & 0x000F); + if (PanSpeed < 1) PanSpeed = 1; + if (PanSpeed > 0x18) PanSpeed = 0x18; + if (TiltSpeed < 1) TiltSpeed = 1; + if (TiltSpeed > 0x18) TiltSpeed = 0x18; + return SendData(MakeID(ID), 1, 6, 2, PanSpeed, TiltSpeed, p3, p2, p1, p0, t3, t2, t1, t0, 0xFF); + } + + /** + * Turn ON / OFF Auto Zoom + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean AutoZoom(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0xA0, (toON ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF Effective Tracking Area + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean EffectiveTrackingArea(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0xA1, (toON ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF RTMP + * @param ID value 1 -7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean RTMP(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0xA2, (toON ? 2 : 3), 0xFF); + } + + /** + * Change Video Mode + * @param ID value 1 - 7 + * @param mode + *
0 = IP+Stream + *
1 = USB Only + *
2 = NDI only + *
3 = Streaming Only + * @return true if success + */ + public boolean VideoMode(int ID, int mode) { + if (mode<0) mode = 0; + if (mode>3) mode = 3; + return SendData(MakeID(ID), 1, 4, 0xA3, mode, 0xFF); + } + + /** + * Reboot Camera + * @param ID + * @return true if success + */ + public boolean Reboot(int ID) { + return SendData(MakeID(ID), 1, 4, 0xA4, 0xFF); + } + + /** + * Turn ON / OFF Preset Affects PTZ and Focus + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean PresetAffectsPTZandFocus(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0xA5, (toON ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF Relative Zoom Ratio + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean RelativeZoomRatio(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0xA6, (toON ? 2 : 3), 0xFF); + } + + /** + * Turn ON / OFF Auto Tilt + * @param ID value 1 - 7 + * @param toON true = ON, false = OFF + * @return true if success + */ + public boolean AutoTilt(int ID, boolean toON) { + return SendData(MakeID(ID), 1, 4, 0xA7, (toON ? 2 : 3), 0xFF); + } + + /** + * Auto Zoom or Tilt Preset + * @param ID value 1 - 7 + * @param preset 00 - 0xFF + * @return true if success + */ + public boolean AutoZoomTiltPreset(int ID, int preset) { + if (preset<0) preset = 0; + if (preset > 0xFF) preset = 0xFF; + return SendData(MakeID(ID),1, 4, 0xA8, preset, 0xFF); + } + + /** + * Get Camera Power + * will raise event camerapower + * @param ID value 1 - 7 + */ + public void Get_CameraPower(int ID) { + if (SendData(MakeID(ID),9,4,0,0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result,ID,4)) { + if (result[2]==2) { + raise_camerapower(true, ID, true); + return; + } else if (result[2]==3) { + raise_camerapower(true, ID, false); + return; + } + } + } + raise_camerapower(false, ID, false); + } + + /** + * Get White Balance Mode + * will raise event whitebalancemode + * @param ID value 1 - 7 + */ + public void Get_WhiteBalanceMode(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x35, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID,4)) { + int code = ByteToInt(result[2]); + switch(code) { + case 0 : + raise_whitebalancemode(true, ID, "Auto"); + return; + case 1 : + raise_whitebalancemode(true, ID, "Indoor"); + return; + case 2 : + raise_whitebalancemode(true, ID, "Outdoor"); + return; + case 3 : + raise_whitebalancemode(true, ID, "One Push WB"); + return; + case 4 : + raise_whitebalancemode(true, ID, "ATW"); + return; + case 5 : + raise_whitebalancemode(true, ID, "Manual"); + return; + } + } + } + raise_whitebalancemode(false, ID, ""); + } + + /** + * Get R-Gain + * will raise event rgain + * @param ID value 1 - 7 + */ + public void Get_RGain(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x43, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result,ID,7)) { + raise_rgain(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_rgain(false, ID, 0); + } + + /** + * Get B-Gain + * will raise event bgain + * @param ID value 1 - 7 + */ + public void Get_BGain(int ID) { + if (SendData(MakeID(ID), 9,4, 0x44,0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_bgain(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_bgain(false, ID, 0); + } + + /** + * Get Auto Exposure Mode + * will raise event aemode + * @param ID value 1 - 7 + */ + public void Get_AutoExposureMode(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x39, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + int code = ByteToInt(result[2]); + switch(code) { + case 0 : + raise_aemode(true, ID, "Full Auto"); + return; + case 3 : + raise_aemode(true, ID, "Manual"); + return; + case 0xA : + raise_aemode(true, ID, "Shutter Priority"); + return; + case 0xB : + raise_aemode(true, ID, "Iris Priority"); + return; + case 0xD : + raise_aemode(true, ID, "Bright"); + return; + } + } + } + raise_aemode(false, ID, ""); + } + + /** + * Get Shutter position + * will raise event shutterposition + * @param ID value 1 - 7 + */ + public void Get_ShutterPosition(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x4A, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_shutterposition(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_shutterposition(false, ID, 0); + } + + /** + * Get Iris position + * will raise event irisposition + * @param ID value 1 - 7 + */ + public void Get_IrisPosition(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x4B, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_irisposition(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_irisposition(false, ID, 0); + } + + /** + * Get Gain position + * will raise event gainposition + * @param ID value 1 - 7 + */ + public void Get_GainPosition(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x4C, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_gainposition(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_gainposition(false, ID, 0); + } + + /** + * Get Bright position + * will raise event brightposition + * @param ID value 1 - 7 + */ + public void Get_BrightPosition(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x4D, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_brightposition(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_brightposition(false, ID, 0); + } + + /** + * Get Exposure Compensation position + * will raise event expcompposition + * @param ID value 1 - 7 + */ + public void Get_ExpCompPosition(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x4E, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_expcompposition(true, ID, WordToInt(result[4], result[5])); + return; + } + } + raise_expcompposition(false, ID, 0); + } + + /** + * Get Focus Mode + * will raise event focusmode + * @param ID value 1- 7 + */ + public void Get_FocusMode(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x38, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + if (result[2]==2) { + raise_focusmode(true, ID, "Auto"); + return; + } else if (result[2]==3) { + raise_focusmode(true, ID, "Manual"); + return; + } + } + } + raise_focusmode(false, ID, ""); + } + + /** + * Get Focus Position + * will raise event focusposition + * @param ID value 1 - 7 + */ + public void Get_FocusPosition(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x48, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_focusposition(true, ID, ToInt(result[2], result[3], result[4], result[5]) ); + return; + } + } + raise_focusposition(false, ID, 0); + } + + /** + * Get Zoom Position + * will raise event zoomposition + * @param ID value 1 - 7 + */ + public void Get_ZoomPosition(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x47, 0xFF)) { + byte[] result = ReadData(7); + if (ValidCommandRead(result, ID, 7)) { + raise_zoomposition(true, ID, ToInt(result[2], result[3], result[4], result[5]) ); + return; + } + } + raise_zoomposition(false, ID, 0); + } + + /** + * Get Pan and Tilt Position + * will raise event pantiltposition + * @param ID value 1 - 7 + */ + public void Get_PanTiltPosition(int ID) { + if (SendData(MakeID(ID), 9, 6, 0x12, 0xFF)) { + byte[] result = ReadData(11); + if (ValidCommandRead(result, ID, 11)) { + raise_pantiltposition(true, ID, ToInt(result[2], result[3], result[4], result[5]), ToInt(result[6], result[7], result[8], result[9]) ); + return; + } + } + raise_pantiltposition(false, ID, 0,0); + } + + /** + * Get current preset + * will raise event preset + * @param ID value 1 - 7 + */ + public void Get_Preset(int ID) { + if (SendData(MakeID(ID), 9,4, 0x3F, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_preset(true, ID, ByteToInt(result[2])); + return; + } + } + raise_preset(false, ID, 0); + } + + /** + * Get Tracking Status + * will raise event trackingstatus + * @param ID value 1 - 7 + */ + public void Get_TrackingStatus(int ID) { + if (SendData(MakeID(ID), 9,0x36, 0x69, 2, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_trackingstatus(true, ID, ByteToInt(result[2])==1); + return; + + } + } + raise_trackingstatus(false, ID, false); + } + + /** + * Get Tracking Mode + * will raise event trackingmode + * @param ID value 1 - 7 + */ + public void Get_TrackingMode(int ID) { + if (SendData(MakeID(ID), 9,0x36, 0x69, 1, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + int code = ByteToInt(result[2]); + switch(code) { + case 1: + raise_trackingmode(true, ID, "Presenter"); + return; + case 2 : + raise_trackingmode(true, ID, "Zone"); + return; + case 3 : + raise_trackingmode(true, ID, "Hybrid"); + return; + } + } + } + raise_trackingmode(false, ID, ""); + } + + /** + * Get Tracking Body Size + * will raise event trackingbodysize + * @param ID value 1 - 7 + */ + public void Get_TrackingBodySize(int ID) { + if (SendData(MakeID(ID), 9,0x36, 0x69, 3, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + int code = ByteToInt(result[2]); + switch(code) { + case 1: + raise_trackingbodysize(true, ID, "Full Body"); + return; + case 2 : + raise_trackingbodysize(true, ID, "Upper Body"); + return; + } + } + } + raise_trackingbodysize(false, ID, ""); + } + + /** + * Get if Menu OSD Displayed + * will raise event osdmenu + * @param ID value 1 - 7 + */ + public void Get_OSDMenu(int ID) { + if (SendData(MakeID(ID), 9, 0x7E, 4, 0x76, 1, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_osdmenu(true, ID, result[2]==2); + return; + } + } + raise_osdmenu(false, ID, false); + } + + /** + * Get if Tally is ON or OFF + * will raise event tally + * @param ID value 1 - 7 + */ + public void Get_Tally(int ID) { + if (SendData(MakeID(ID), 9, 0x7E, 1, 0xA, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_tally(true, ID, result[2]==2); + return; + } + } + raise_tally(false, ID, false); + } + + /** + * Get if WDR Mode is ON or OFF + * will raise event wdrmode + * @param ID value 1 - 7 + */ + public void Get_WDRMode(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x3D, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_wdrmode(true, ID, result[2]==2); + return; + } + } + raise_wdrmode(false, ID, false); + } + + /** + * Get if BackLightCompensation (BLC) Mode is ON or OFF + * will raise event blcmode + * @param ID value 1 - 7 + */ + public void Get_BLCMode(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x33, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_blcmode(true, ID, result[2]==2); + return; + } + } + raise_blcmode(false, ID, false); + } + + /** + * Get if Live Freeze Mode is ON or OFF + * will raise event livefreeze + * @param ID value 1 - 7 + */ + public void Get_LiveFreezeMode(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x62, 1, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_livefreeze(true, ID, result[2]==2); + return; + } + } + raise_livefreeze(false, ID, false); + } + + /** + * Get if Preset Freeze Mode is ON or OFF + * will raise event presetfreeze + * @param ID value 1 - 7 + */ + public void Get_PresetFreezeMode(int ID) { + if (SendData(MakeID(ID), 9, 4, 0x62, 2, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_presetfreeze(true, ID, result[2]==2); + return; + } + } + raise_presetfreeze(false, ID, false); + } + + /** + * Get Firmware Version + * will raise event firmwareversion + * @param ID value 1 - 7 + */ + public void Get_FirmwareVersion(int ID) { + if (SendData(MakeID(ID), 9, 0x36, 0x69, 4, 0xFF)) { + byte[] result = ReadData(11); + if (ValidCommandRead(result, ID, 11)) { + StringBuilder str = new StringBuilder(); + str.append(ByteToInt(result[2])).append("."); + str.append(ByteToInt(result[3])).append("."); + str.append(ToInt(result[4], result[5], result[6], result[7])).append("."); + str.append(WordToInt(result[8], result[9])); + raise_firmwareversion(true, ID, str.toString()); + return; + } + } + raise_firmwareversion(false, ID, ""); + } + + /** + * Get USB Plugged status + * will raise event usbpluggedstatus + * @param ID value 1 - 7 + */ + public void Get_USBPluggedStatus(int ID) { + if (SendData(MakeID(ID), 9, 0x36, 0x69, 5, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_usbpluggedstatus(true, ID, result[2]==1); + return; + } + } + raise_usbpluggedstatus(false, ID, false); + } + + /** + * Get UVC Stream ON or OFF + * will raise event uvcstatus + * @param ID value 1 - 7 + */ + public void Get_UVCStatus(int ID) { + if (SendData(MakeID(ID), 9, 0x36, 0x69, 6, 0xFF)) { + byte[] result = ReadData(4); + if (ValidCommandRead(result, ID, 4)) { + raise_uvcstatus(true,ID, result[2]==1); + return; + } + } + raise_uvcstatus(false, ID, false); + } + + /** + * Convert Byte to Int + * @param bb byte value + * @return int value + */ + private int ByteToInt(byte bb) { + return (bb & 0xFF); + } + + private int WordToInt(byte bh, byte bl) { + return (ByteToInt(bh) << 8) + ByteToInt(bl); + } + + private int ToInt(byte b3, byte b2, byte b1, byte b0) { + return (ByteToInt(b3)<<24) | (ByteToInt(b2)<<16) | (ByteToInt(b1)<<8) | (ByteToInt(b0)); + } + + /** + * Make ID for sending data + * sending data always 0x8y , with y = ID + * @param ID 1 - 7 + * @return value in 0x8y + */ + private int MakeID(int ID) { + if (ID<1) ID = 1; + if (ID>7) ID = 7; + return (0x80 + ID); + } + + /** + * Make ID for Inquiry data + * getting data always 0xY0, with Y = ID + * @param ID 1 - 7 + * @return value in 0xY0 + */ + private int MakeIDGet(int ID) { + if (ID<1) ID = 1; + if (ID>7) ID = 7; + return (0x10 * ID); + } + + /** + * Send Integer Data to Serial + * @param cmd array of integer + * @return true if can be send + */ + private boolean SendData(int... cmd) { + if (cmd!=null) { + if (cmd.length>0) { + byte[] bytecmd = new byte[cmd.length]; + for(int ii=0;ii0) { + int written= myport.writeBytes(cmd, cmd.length); + return written>0; + } + } + } + return false; + } + + /** + * Read Data from Serial Port + * @param expectedlength number of bytes expected to be read + * @return empty array if failed, or array with expectedlength if success + */ + private byte[] ReadData(int expectedlength) { + if (IsOpened()) { + byte[] result = new byte[expectedlength]; + + int readcount = myport.readBytes(result, expectedlength); + if (readcount>=expectedlength) { + return result; + } + } + return new byte[0]; + } + + /** + * Check if Read Bytes are valid Command + * @param bb array of bytes to check + * @param ID value 1 - 7 + * @param expectedlength expected length of bb + * @return true if valid Command + */ + private boolean ValidCommandRead(byte[] bb, int ID, int expectedlength) { + if (bb!=null) { + int length = bb.length; + if (length>=expectedlength) { + // minimal reply itu 4 bytes + int header = ByteToInt(bb[0]); + int footer = ByteToInt(bb[length-1]); + int code = ByteToInt(bb[1]); + if (footer == 0xFF) { + if (code==0x50) { + return (header==MakeIDGet(ID)); + } + } + } + } + return false; + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_camerapower(boolean success, int id, boolean value) { + if (need_camerapower_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_camerapower", false, new Object[] {success, id, value}); + } + + private void raise_whitebalancemode(boolean success, int id, String value) { + if (need_whitebalancemode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_whitebalancemode", false, new Object[] {success, id, value}); + } + + private void raise_rgain(boolean success, int id, int value) { + if (need_rgain_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_rgain", false, new Object[] {success, id, value}); + } + + private void raise_bgain(boolean success, int id, int value) { + if (need_bgain_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_bgain", false, new Object[] {success, id, value}); + } + + private void raise_aemode(boolean success, int id, String value) { + if (need_aemode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_aemode", false, new Object[] {success, id, value}); + } + + private void raise_shutterposition(boolean success, int id, int value) { + if (need_shutterposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_shutterposition", false, new Object[] {success, id, value}); + } + + private void raise_irisposition(boolean success, int id, int value) { + if (need_irisposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_irisposition", false, new Object[] {success, id, value}); + } + + private void raise_gainposition(boolean success, int id, int value) { + if (need_gainposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_gainposition", false, new Object[] {success, id, value}); + } + + private void raise_brightposition(boolean success, int id, int value) { + if (need_brightposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_brightposition", false, new Object[] {success, id, value}); + } + + private void raise_expcompposition(boolean success, int id, int value) { + if (need_expcompposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_expcompposition", false, new Object[] {success, id, value}); + } + + private void raise_focusmode(boolean success, int id, String value) { + if (need_focusmode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_focusmode", false, new Object[] {success, id, value}); + } + + private void raise_focusposition(boolean success, int id, int value) { + if (need_focusposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_focusposition", false, new Object[] {success, id, value}); + } + + private void raise_zoomposition(boolean success, int id, int value) { + if (need_zoomposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_zoomposition", false, new Object[] {success, id, value}); + } + + private void raise_pantiltposition(boolean success, int id, int panvalue, int tiltvalue) { + if (need_pantiltposition_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_pantiltposition", false, new Object[] {success, id, panvalue, tiltvalue}); + } + + private void raise_preset(boolean success, int id, int value) { + if (need_preset_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_preset", false, new Object[] {success, id, value}); + } + + private void raise_trackingstatus(boolean success, int id, boolean value) { + if (need_trackingstatus_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_trackingstatus", false, new Object[] {success, id, value}); + } + + private void raise_trackingmode(boolean success, int id, String value) { + if (need_trackingmode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_trackingmode", false, new Object[] {success, id, value}); + } + + private void raise_trackingbodysize(boolean success, int id, String value) { + if (need_trackingbodysize_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_trackingbodysize", false, new Object[] {success, id, value}); + } + + private void raise_osdmenu(boolean success, int id, boolean value) { + if (need_osdmenu_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_osdmenu", false, new Object[] {success, id, value}); + } + + private void raise_tally(boolean success, int id, boolean value) { + if (need_tally_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_tally", false, new Object[] {success, id, value}); + } + + private void raise_wdrmode(boolean success, int id, boolean value) { + if (need_wdrmode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_wdrmode", false, new Object[] {success, id, value}); + } + + private void raise_blcmode(boolean success, int id, boolean value) { + if (need_blcmode_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_blcmode", false, new Object[] {success, id, value}); + } + + private void raise_livefreeze(boolean success, int id, boolean value) { + if (need_livefreeze_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_livefreeze", false, new Object[] {success, id, value}); + } + + private void raise_presetfreeze(boolean success, int id, boolean value) { + if (need_presetfreeze_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_presetfreeze", false, new Object[] {success, id, value}); + } + + private void raise_firmwareversion(boolean success, int id, String value) { + if (need_firmwareversion_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_firmwareversion", false, new Object[] {success, id, value}); + } + + private void raise_usbpluggedstatus(boolean success, int id, boolean value) { + if (need_usbpluggedstatus_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_usbpluggedstatus", false, new Object[] {success, id, value}); + } + + private void raise_uvcstatus(boolean success, int id, boolean value) { + if (need_uvcstatus_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_uvcstatus", false, new Object[] {success, id, value}); + } +} diff --git a/src/rfidreader/ACR122Device.java b/src/rfidreader/ACR122Device.java new file mode 100644 index 0000000..e803f7a --- /dev/null +++ b/src/rfidreader/ACR122Device.java @@ -0,0 +1,197 @@ +package rfidreader; + +import java.util.List; + +import javax.smartcardio.Card; +import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; +import javax.smartcardio.CardTerminals; +import javax.smartcardio.TerminalFactory; + + +import anywheresoftware.b4a.BA; +import jGPIO.mycodes; + +@BA.ShortName("ACR122Device") +@BA.Events(values= { + "log(msg as string)", + "cardfound(card as RFIDCardInfo)", + "cardmissing(card as RFIDCardInfo)" +}) + +public class ACR122Device { + + public ACR122Device() { + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + System.out.println("ACR122Device ShutdownHook executed"); + close(); + } + }); + } + private BA ba; + private String event; + private Object Me; + private boolean need_log_event = false; + private boolean need_cardfound_event = false; + private boolean need_cardmissing_event = false; + + private boolean inited = false; + + private CardTerminal terminal; + + + public void Initialize(BA ba, String eventname) { + this.ba = ba; + this.event = eventname; + Me = this; + check_events(); + init_acr(); + + } + + /** + * Check if already initialized + * @return true if initialized + */ + public boolean IsInitialized() { + return inited; + } + + /** + * Initialize ACR122 + * + */ + @BA.Hide + public void init_acr() { + terminal = null; + CardTerminals cts = TerminalFactory.getDefault().terminals(); + if (cts!=null) { + try { + List ll = cts.list(); + ll.forEach((xx)-> { + raise_log("Found "+xx.getName()); + if (xx.getName().contains("ACR122")) { + terminal = xx; + } + }); + + } catch (CardException e) { + raise_log("CardException on CardTerminals list = "+e.getMessage()); + } + + } + + inited = (terminal!=null); + if (inited) { + raise_log("Card Terminal is found and using "+terminal.getName()); + + Thread tt = new Thread(new CardConnect(terminal)); + tt.start(); + + } else { + raise_log("Unable to find Card Terminal"); + } + + } + + /** + * Close ACR122Device + */ + public void close() { + // akan closing CardConnect Runnable + inited = false; + } + + + private class CardConnect implements Runnable{ + + private final CardTerminal terminal; + private String connectprocotol = "*"; + private RFIDCardInfo last_card = null; + public CardConnect(CardTerminal terminal) { + this.terminal = terminal; + } + + @SuppressWarnings("unused") + public CardConnect(CardTerminal terminal, String protocol) { + this.terminal = terminal; + if (mycodes.ValidString(protocol)) { + connectprocotol = protocol; + } + } + @Override + public void run() { + if (terminal==null) return; + raise_log("CardConnect started for "+terminal.getName()+", protocol = "+connectprocotol); + while(inited) { + try { + if (terminal.isCardPresent()) { + + Card card = terminal.connect(connectprocotol); + RFIDCardInfo card_info = new RFIDCardInfo(card); + if (card_info.IsValid()) { + if (last_card==null) { + if (mycodes.ValidString(card_info.getSerialNumber())) { + last_card = card_info; + raise_cardfound(card_info); + } + } else { + if (mycodes.ValidString(card_info.getSerialNumber())) { + if (card_info.getSerialNumber().contentEquals(last_card.getSerialNumber())==false) { + last_card = card_info; + raise_cardfound(card_info); + } + } + } + + } + + } else { + if (last_card!=null) { + raise_cardmissing(last_card); + } + last_card = null; + + } + Thread.sleep(500); + } catch (InterruptedException e) { + raise_log("CardConnect InterruptedException exception = "+e.getMessage()); + close(); + break; + } catch (CardException e) { + raise_log("CardConnect CardException exception = "+e.getMessage()); + } + } + raise_log("CardConnect stopped for "+terminal.getName()); + } + + } + + + + private void check_events() { + if (ba!=null) { + if (mycodes.ValidString(event)) { + need_log_event = ba.subExists(event+"_log"); + need_cardfound_event = ba.subExists(event+"_cardfound"); + need_cardmissing_event = ba.subExists(event+"_cardmissing"); + } + } + } + + + + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_cardfound(RFIDCardInfo card) { + if (need_cardfound_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_cardfound", false, new Object[] {card}); + } + + private void raise_cardmissing(RFIDCardInfo card) { + if (need_cardmissing_event) ba.raiseEventFromDifferentThread(Me, null, 0,event+"_cardmissing", false, new Object[] {card}); + } +} diff --git a/src/rfidreader/HexUtils.java b/src/rfidreader/HexUtils.java new file mode 100644 index 0000000..7948358 --- /dev/null +++ b/src/rfidreader/HexUtils.java @@ -0,0 +1,49 @@ +package rfidreader; + +import java.util.regex.Pattern; + +public final class HexUtils { + /** Regex pattern for hexadecimal strings */ + private static final Pattern HEX_STRING_PATTERN = Pattern.compile("^([0-9A-Fa-f]{2})+$"); + + /** Array of all hexadecimal chars */ + private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray(); + + private HexUtils() { + } + + /** + * @param s a string + * @return true if the provided string is hexadecimal, false otherwise + */ + public static boolean isHexString(String s) { + return (s != null) && HEX_STRING_PATTERN.matcher(s).matches(); + } + + /** + * @param s a hex string + * @return a byte array + */ + public static byte[] hexStringToBytes(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } + + /** + * @param bytes a byte array + * @return a hex string + */ + public static String bytesToHexString(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int i = 0; i < bytes.length; i++) { + int v = bytes[i] & 0xFF; + hexChars[i * 2] = HEX_CHARS[v >>> 4]; + hexChars[i * 2 + 1] = HEX_CHARS[v & 0x0F]; + } + return new String(hexChars); + } +} diff --git a/src/rfidreader/MifareUtils.java b/src/rfidreader/MifareUtils.java new file mode 100644 index 0000000..b442bdc --- /dev/null +++ b/src/rfidreader/MifareUtils.java @@ -0,0 +1,227 @@ +package rfidreader; + +import java.util.Arrays; +import java.util.List; +import java.io.IOException; +import javax.smartcardio.CardException; +import org.nfctools.mf.MfAccess; +import org.nfctools.mf.MfException; +import org.nfctools.mf.MfReaderWriter; +import org.nfctools.mf.block.BlockResolver; +import org.nfctools.mf.block.MfBlock; +import org.nfctools.mf.card.MfCard; +import org.nfctools.mf.classic.Key; +import org.nfctools.mf.classic.MemoryLayout; + +import static rfidreader.HexUtils.bytesToHexString; +import static rfidreader.HexUtils.hexStringToBytes; +import static rfidreader.HexUtils.isHexString; + + +@SuppressWarnings("deprecation") +public final class MifareUtils { + /** Mifare Classic 1K sector count */ + public static final int MIFARE_1K_SECTOR_COUNT = 16; + + /** Mifare Classic 1K block count (per sector) */ + public static final int MIFARE_1K_PER_SECTOR_BLOCK_COUNT = 4; + + /** Common Mifare Classic 1K keys */ + public static final List COMMON_MIFARE_CLASSIC_1K_KEYS = Arrays.asList( + "001122334455", + "000102030405", + "A0A1A2A3A4A5", + "B0B1B2B3B4B5", + "AAAAAAAAAAAA", + "BBBBBBBBBBBB", + "AABBCCDDEEFF", + "FFFFFFFFFFFF" + ); + + private MifareUtils() { + } + + /** + * @param s a string + * @return true if the provided string is a valid Mifare Classic 1K key, false otherwise + */ + public static boolean isValidMifareClassic1KKey(String s) { + return isHexString(s) && (s.length() == 12); + } + + /** + * @param s a string + * @return true if the provided string is a valid Mifare Classic 1K sector index, false otherwise + */ + public static boolean isValidMifareClassic1KSectorIndex(String s) { + try { + int sectorIndex = Integer.parseInt(s); + return sectorIndex >= 0 && sectorIndex < MIFARE_1K_SECTOR_COUNT; + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * @param s a string + * @return true if the provided string is a valid Mifare Classic 1K block index, false otherwise + */ + public static boolean isValidMifareClassic1KBlockIndex(String s) { + try { + int sectorIndex = Integer.parseInt(s); + return sectorIndex >= 0 && sectorIndex < MIFARE_1K_PER_SECTOR_BLOCK_COUNT; + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * Dumps a Mifare Classic 1K card. + * @param reader the reader + * @param card the card + * @param keys the keys to be tested for reading + */ + public static void dumpMifareClassic1KCard(MfReaderWriter reader, MfCard card, List keys) + throws CardException { + for (int sectorIndex = 0; sectorIndex < MIFARE_1K_SECTOR_COUNT; sectorIndex++) { + // For each sector... + for (int blockIndex = 0; blockIndex < MIFARE_1K_PER_SECTOR_BLOCK_COUNT; blockIndex++) { + // For each block... + dumpMifareClassic1KBlock(reader, card, sectorIndex, blockIndex, keys); + } + } + } + + /** + * Write data to a Mifare Classic 1K card. + * @param reader the reader + * @param card the card + * @param sectorId the sector to be written + * @param blockId the block to be written + * @param key the key to be used for writing + * @param dataString the data hex string to be written + */ + public static void writeToMifareClassic1KCard(MfReaderWriter reader, MfCard card, int sectorId, int blockId, String key, String dataString) + throws CardException { + if (!isValidMifareClassic1KKey(key)) { + System.out.println("The key " + key + "is not valid."); + return; + } + if (!isHexString(dataString)) { + System.out.println(dataString + " is not an hex string."); + return; + } + + byte[] keyBytes = hexStringToBytes(key); + // Reading with key A + MfAccess access = new MfAccess(card, sectorId, blockId, Key.A, keyBytes); + String blockData = readMifareClassic1KBlock(reader, access); + if (blockData == null) { + // Reading with key B + access = new MfAccess(card, sectorId, blockId, Key.B, keyBytes); + blockData = readMifareClassic1KBlock(reader, access); + } + System.out.print("Old block data: "); + if (blockData == null) { + // Failed to read block + System.out.println(""); + } else { + // Block read + System.out.println(blockData + " (Key " + access.getKey() + ": " + key + ")"); + + // Writing with same key + boolean written = false; + try { + byte[] data = hexStringToBytes(dataString); + MfBlock block = BlockResolver.resolveBlock(MemoryLayout.CLASSIC_1K, sectorId, blockId, data); + written = writeMifareClassic1KBlock(reader, access, block); + } catch (MfException me) { + System.out.println(me.getMessage()); + } + if (written) { + blockData = readMifareClassic1KBlock(reader, access); + System.out.print("New block data: "); + if (blockData == null) { + // Failed to read block + System.out.println(""); + } else { + // Block read + System.out.println(blockData + " (Key " + access.getKey() + ": " + key + ")"); + } + } + } + } + + /** + * Reads a Mifare Classic 1K block. + * @param reader the reader + * @param access the access + * @return a string representation of the block data, null if the block can't be read + */ + private static String readMifareClassic1KBlock(MfReaderWriter reader, MfAccess access) + throws CardException { + String data = null; + try { + MfBlock block = reader.readBlock(access)[0]; + data = bytesToHexString(block.getData()); + } catch (IOException ioe) { + if (ioe.getCause() instanceof CardException) { + throw (CardException) ioe.getCause(); + } + } + return data; + } + + /** + * Writes a Mifare Classic 1K block. + * @param reader the reader + * @param access the access + * @param block the block to be written + * @return true if the block has been written, false otherwise + */ + private static boolean writeMifareClassic1KBlock(MfReaderWriter reader, MfAccess access, MfBlock block) throws CardException { + boolean written = false; + try { + reader.writeBlock(access, block); + written = true; + } catch (IOException ioe) { + if (ioe.getCause() instanceof CardException) { + throw (CardException) ioe.getCause(); + } + } + return written; + } + + /** + * Dumps Mifare Classic 1K block data. + * @param reader the reader + * @param card the card + * @param sectorId the sector to be read + * @param blockId the block to be read + * @param keys the keys to be tested for reading + */ + private static void dumpMifareClassic1KBlock(MfReaderWriter reader, MfCard card, int sectorId, int blockId, List keys) throws CardException { + System.out.printf("Sector %02d block %02d: ", sectorId, blockId); + for (String key : keys) { + // For each provided key... + if (isValidMifareClassic1KKey(key)) { + byte[] keyBytes = hexStringToBytes(key); + // Reading with key A + MfAccess access = new MfAccess(card, sectorId, blockId, Key.A, keyBytes); + String blockData = readMifareClassic1KBlock(reader, access); + if (blockData == null) { + // Reading with key B + access = new MfAccess(card, sectorId, blockId, Key.B, keyBytes); + blockData = readMifareClassic1KBlock(reader, access); + } + if (blockData != null) { + // Block read + System.out.println(blockData + " (Key " + access.getKey() + ": " + key + ")"); + return; + } + } + } + // All keys tested, failed to read block + System.out.println(""); + } +} diff --git a/src/rfidreader/RFIDCardInfo.java b/src/rfidreader/RFIDCardInfo.java new file mode 100644 index 0000000..45cc8a0 --- /dev/null +++ b/src/rfidreader/RFIDCardInfo.java @@ -0,0 +1,376 @@ +package rfidreader; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import javax.smartcardio.ATR; +import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; + +import anywheresoftware.b4a.BA; +import jGPIO.mycodes; + +@BA.ShortName("RFIDCardInfo") +public class RFIDCardInfo { + private final Card _card; + // RID = Registered Application Provider Identifier + private String RID = ""; + private byte Standard = 0; + private int CardType = 0; + private String RFU = ""; + private String SerialNumber = ""; + private String ATS = ""; + private boolean isvalid = false; + /** + * Initialize RFIDCardInfo + * @param card Card Object + */ + public RFIDCardInfo(Card card) { + this._card = card; + if (_card != null) { + if (mycodes.ValidString(_card.getProtocol())) { + if (mycodes.IsByteArray_and_have_values(card.getATR().getBytes())) { + isvalid = check_valid_atr(card.getATR()); + if (isvalid) { + SerialNumber = get_serialnumber(card.getBasicChannel()); + if (CardType!= 1) { + //TODO yang ketahuan , Mifare 1K gak support ini + ATS = get_ats(card.getBasicChannel()); + } + + + } + } + } + } + } + + /** + * Check if this object already initialized and valid + * @return true if valid + */ + public boolean IsValid() { + return isvalid; + } + + /** + * Get Card Protocol + * @return empty string if invalid + */ + public String getCardProtocol() { + if (isvalid) { + return _card.getProtocol(); + } + return ""; + } + + /** + * Get Card ATR bytes + * @return null if invalid + */ + public byte[] getCardATRBytes() { + if (isvalid) { + return _card.getATR().getBytes(); + } + return null; + } + + /** + * Get Card ATR in String + * @return empty string if invalid + */ + public String getCardATRString() { + String result = ""; + if (isvalid) { + byte[] atr = _card.getATR().getBytes(); + for(byte xx : atr) { + result+=String.format("%02X", xx); + } + + } + return result; + } + + // Source : https://downloads.acs.com.hk/drivers/en/API-ACR122U-2.02.pdf + private boolean check_valid_atr(ATR atr) { + if (atr != null) { + byte[] atrbytes = atr.getBytes(); + try { + if (mycodes.ToInt(atrbytes[0])==0x3B) { + if ((mycodes.ToInt(atrbytes[1]) & 0x80)>0) { + int N = mycodes.ToInt(atrbytes[1]) & 0x0F; + if (atrbytes.length==(5+N)) { + if (mycodes.ToInt(atrbytes[2])==0x80) { + if (mycodes.ToInt(atrbytes[3])==1) { + + // mulai inti + if (mycodes.ToInt(atrbytes[4])==0x80) { + // PDF article 3.1.1 + + if (mycodes.ToInt(atrbytes[5])==0x4F) { + int length = mycodes.ToInt(atrbytes[6]); + if (length>0) { + + byte[] content = Arrays.copyOfRange(atrbytes, 7, 7+length); + + byte[] rid_bytes = Arrays.copyOfRange(content, 0, 5); + RID = mycodes.Bytes_toHex(rid_bytes, ""); + //printlog("RID = "+RID); + Standard = content[5]; + //printlog("Standard = "+Standard); + CardType = mycodes.To16bits(content[6], content[7]); + + + //printlog("Card Name = "+ getCardName()); + + byte[] rfu_bytes = Arrays.copyOfRange(content, 8, 8+4); + RFU = mycodes.Bytes_toHex(rfu_bytes, ""); + //printlog("RFU = "+RFU); + return true; + + } else printlog("check_valid_atr article 3.1.1 failed at length[6]"); + } else printlog("check_valid_atr article 3.1.1 failed at Application Identifier Presence Indicator"); + } + + + } else printlog("check_valid_atr failed at TD2"); + } else printlog("check_valid_atr failed at TD1"); + } else printlog("check_valid_atr failed at length, N = "+N+", length="+atrbytes.length); + } else printlog("check_valid_atr failed at T0"); + } else printlog("check_valid_atr failed at Initial Header"); + } catch(Exception e) { + printlog("check_valid_atr exception = "+e.getMessage()); + } + } + return false; + } + + public String getRFU() { + return RFU; + } + + public String getRID() { + return RID; + } + + public byte getStandard() { + return Standard; + } + + public String getCardName() { + switch(CardType) { + case 1 : return "Mifare 1K"; + case 2 : return "Mifare 4K"; + case 3 : return "Mifare Ultralight"; + case 0x26 : return "Mifare Mini"; + case 0xF004: return "Topaz and Jewel"; + case 0xF011 : return "Felica 212K"; + case 0xF012 : return "Felica 424K"; + default : return "Undefined ["+mycodes.ToHex(CardType)+"]"; + } + } + + public String getSerialNumber() { + return SerialNumber; + } + + public String getATS() { + return ATS; + } + + /** + * Load Authentication Key to Reader + * PDF article 5.1 + * @param keylocation 0 / 1 + * @param key must 6 bytes, if null will default to FF FF FF FF FF FF + * @return false if failed + */ + public boolean Load_Authentication_Keys_To_Reader(byte keylocation, int[] key) { + if (isvalid) { + if (keylocation==0 || keylocation==1) { + if (key==null) key = new int[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + if (key.length!=6) key = new int[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + byte[] cmd = mycodes.ConcatBytes(mycodes.ToByteArray(0xFF, 0x82, 0, keylocation, 6), mycodes.ToByteArray(key)); + printlog("Load_Authentication_Keys_To_Reader will send : "+mycodes.Bytes_toHex(cmd, " ")); + String result = Send(_card.getBasicChannel(), cmd); + return result.endsWith("9000"); + + } else printlog("Load_Authentication_Keys_To_Reader failed, invalid keylocation"); + } else printlog("Load_Authentication_Keys_To_Reader failed, invalid RFIDCardInfo"); + return false; + } + + /** + * Need to authentication before reading data block + * Mifare 1K have 16 sectors, each sector have 4 blocks, each block is 16 bytes + * Mifare 4K have 40 sectors, each sector have 4 blocks, each block is 16 bytes + * Read PDF article 5.2 + * If reading the same sector, no need to re-authenticate + * @param newformat if true, will use 10 bytes format. if false, will use old 6 bytes format + * @param blocknumber : Mifare 1K (00 - 3E), Mifare 4K (00 - FE) + * @param keytype : 0x60 (Type A) or 0x61 (Type B). if invalid, default to Type A + * @param keynumber : 0 or 1, if invalid default to 0 + * @return true if authentication success + */ + public boolean Authentication_To_Card(boolean newformat, int blocknumber, byte keytype, byte keynumber) { + if (keytype<0x60) keytype = 0x60; + if (keytype>0x61) keytype = 0x60; + if (keynumber<0) keynumber = 0; + if (keynumber>1) keynumber = 0; + if (blocknumber<0) blocknumber = 0; + if (CardType==1 && blocknumber > 0x3E) blocknumber = 0x3E; // Mifare 1K + if (CardType==2 && blocknumber > 0xFE) blocknumber = 0xFE; // Mifare 4K + + + byte[] cmd = newformat ? mycodes.ToByteArray(0xFF, 0x88, 0, blocknumber, keytype, keynumber) : mycodes.ToByteArray(0xFF, 0x86, 0, 0, 5, 1, 0, blocknumber, keytype, keynumber); + String result = Send(_card.getBasicChannel(), cmd); + return result.endsWith("9000"); + } + + /** + * Read binary block + * must authenticate byte block using Authentication_To_Card before reading + * if need to read byte block from the same sector, no need to re-authenticate + * Read PDF article 5.3 + * @param blocknumber : Mifare 1K (0 - 3E), Mifare 4K (0 - FE). + * @param bytestoread : Mifare 1K / 4K (0 - 16, invalid = 16), Mifare Ultralight (0 - 4, invalid = 4) + * @return bytes array if success, or null if failed + */ + public byte[] ReadBinaryBlock(int blocknumber, int bytestoread) { + if (blocknumber<0) blocknumber = 0; + if (CardType==1 && blocknumber > 0x3E) blocknumber = 0x3E; + if (CardType==2 && blocknumber > 0xFE) blocknumber = 0xFE; + + // Mifare 1K / 4K + if ((CardType==1 || CardType==2)) { + if (bytestoread < 0) bytestoread = 16; + if (bytestoread > 16) bytestoread = 16; + } + // Mifare Ultralight + if (CardType==3) { + if (bytestoread < 0) bytestoread = 4; + if (bytestoread > 4) bytestoread = 4; + } + byte[] result = Send_ReturnBytes(_card.getBasicChannel(),mycodes.ToByteArray(0xFF, 0xB0, 0, blocknumber, bytestoread)); + if (result!=null) { + int len = result.length; + if (len==bytestoread + 2) { + String resultcode = mycodes.Bytes_toHex("", result[len-2], result[len-1]); + if (resultcode.compareTo("9000")==0) { + return Arrays.copyOfRange(result, 0, bytestoread); + } else printlog("ReadBinaryBlock Operation failed, code="+resultcode); + } else printlog("ReadBinaryBlock result length = "+len+", must be "+(bytestoread+2)); + } else printlog("ReadBinaryBlock result is null"); + return null; + } + + /** + * Write Data to Binary block + * Read PDF article 5.4 + * @param blocknumber : Mifare 1K (0 - 3E), Mifare 4K (0 - FE) + * @param bytestowrite : Mifare 1K/4K (0 - 16), Mifare Ultralight (0 - 4) + * @param datatowrite : array of bytes to write + * @return true if operation success + */ + public boolean WriteBinaryBlock(int blocknumber, int bytestowrite, int... datatowrite) { + if (blocknumber<0) blocknumber = 0; + if (CardType==1 && blocknumber > 0x3E) blocknumber = 0x3E; + if (CardType==2 && blocknumber > 0xFE) blocknumber = 0xFE; + if (CardType==1 || CardType==2) { + //Mifare 1K / 4K + if (bytestowrite < 0) bytestowrite = 16; + if (bytestowrite > 16) bytestowrite = 16; + } + if (CardType==3) { + // Mifare ultralight + if (bytestowrite < 0) bytestowrite =4; + if (bytestowrite > 4) bytestowrite = 4; + } + byte[] cmd = mycodes.ConcatBytes(mycodes.ToByteArray(0xFF, 0xD6, 0, blocknumber, bytestowrite), mycodes.ToByteArray(datatowrite) ); + printlog("WriteBinaryBlock will Send : "+mycodes.Bytes_toHex(" ", cmd)); + String result = Send(_card.getBasicChannel(), cmd ); + return result.endsWith("9000"); + } + + private void printlog(String msg) { + System.out.println(msg); + } + + private String get_serialnumber(CardChannel cc) { + String result = Send(cc, mycodes.ToByteArray(0xFF, 0xCA, 0x00, 0x00, 0x04)); + if (result.endsWith("9000")) { + return result.substring(0, result.length()-4); + } else if (result.endsWith("6300")) { + printlog("get_serialnumber operation failed"); + } else if (result.endsWith("6A81")) { + printlog("get_serialnumber function not supported"); + } + return ""; + } + + private String get_ats(CardChannel cc) { + String result = Send(cc, mycodes.ToByteArray(0xFF, 0xCA, 0x01, 0x00, 0x04)); + if (result.endsWith("9000")) { + return result.substring(0, result.length()-4); + } else if (result.endsWith("6300")) { + printlog("get_ats operation failed"); + } else if (result.endsWith("6A81")) { + printlog("get_ats function not supported"); + } + return ""; + } + + private String Send(CardChannel cc, byte... cmd) { + String result = ""; + if (cc!=null) { + if (cmd!=null && cmd.length>0) { + byte[] baResp = new byte[258]; + ByteBuffer bufCmd = ByteBuffer.wrap(cmd); + ByteBuffer bufResp = ByteBuffer.wrap(baResp); + int output = 0; + try { + output = cc.transmit(bufCmd, bufResp); + + } catch(CardException e) { + printlog("Send exception : "+e.getMessage()); + } + + if (output>0) { + for (int i=0; i< output; i++) { + result+= String.format("%02X", baResp[i]); + } + } + } + + } + return result; + + } + + private byte[] Send_ReturnBytes(CardChannel cc, byte... cmd) { + + if (cc!=null) { + if (cmd!=null && cmd.length>0) { + byte[] baResp = new byte[258]; + ByteBuffer bufCmd = ByteBuffer.wrap(cmd); + ByteBuffer bufResp = ByteBuffer.wrap(baResp); + int output = 0; + try { + output = cc.transmit(bufCmd, bufResp); + + } catch(CardException e) { + printlog("Send exception : "+e.getMessage()); + } + + if (output>0) { + return Arrays.copyOfRange(baResp, 0, output); + } + } + + } + + return null; + } + +} diff --git a/src/syslog/DWM312_Syslog_Consumer.java b/src/syslog/DWM312_Syslog_Consumer.java new file mode 100644 index 0000000..eeb655c --- /dev/null +++ b/src/syslog/DWM312_Syslog_Consumer.java @@ -0,0 +1,390 @@ +package syslog; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.Regex; +import anywheresoftware.b4a.keywords.Regex.MatcherWrapper; + +@BA.ShortName("DWM312_Syslog_Consumer") +@BA.Events(values= { + "log(msg as string)", + "internetconnection(connected as boolean)", + "updatednetworkinfo", + "gsmtime(value as long)", + "changenetwork(previous as IMSI_Data, current as IMSI_Data)" +}) +public class DWM312_Syslog_Consumer implements SyslogEvent { + + private BA ba; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_internetconnection_event = false; + private boolean need_updatednetworkinfo_event = false; + private boolean need_gsmtime_event = false; + private boolean need_changenetwork_event = false; + + private String _identifier = "DWM-312"; + private String _ipaddress = ""; + private String _dns = ""; + private String _sec_dns = ""; + private String _hostaddress = ""; + private String _gwaddress = ""; + private String _ifname = ""; + private String _wantype = ""; + private String _mode = ""; + private String _currentSIM = ""; + private String _wanID = ""; + private IMSI_Data _currentIMSI = new IMSI_Data(""); + + private final String regex_ipaddress = "^<14>MultiWANChk2:\\s*printnode\\[\\d+\\]:\\s*(ipaddr)\\s*(->)\\s*(\\d+.\\d+.\\d+.\\d+)\\s*$"; + private final String regex_dns = "^<14>MultiWANChk2:\\s*printnode\\[\\d+\\]:\\s*(dns)\\s*(->)\\s*(\\d+.\\d+.\\d+.\\d+)\\s*$"; + private final String regex_sec_dns = "^<14>MultiWANChk2:\\s*printnode\\[\\d+\\]:\\s*(sec dns)\\s*(->)\\s*(\\d+.\\d+.\\d+.\\d+)\\s*$"; + private final String regex_hostaddress = "^<14>MultiWANChk2:\\s*printnode\\[\\d+\\]:\\s*(hostaddr)\\s*(->)\\s*(\\d+.\\d+.\\d+.\\d+)\\s*$"; + private final String regex_gwaddress = "^<14>MultiWANChk2:\\s*printnode\\[\\d+\\]:\\s*(gwaddr)\\s*(->)\\s*(\\d+.\\d+.\\d+.\\d+)\\s*$"; + private final String regex_ifname = "^<14>MultiWANChk2:\\s*printnode\\[\\d+\\]:\\s*(ifname)\\s*(->)\\s*(\\d+.\\d+.\\d+.\\d+)\\s*$"; + private final String regex_wantype = "^<14>MultiWANChk2:\\s*printnode\\[\\d+\\]:\\s*(wantype)\\s*(->)\\s*(\\d+.\\d+.\\d+.\\d+)\\s*$"; + private final String regex_mode = "^<14>MultiWANChk2:\\s*printnode\\[\\d+\\]:\\s*(mode)\\s*(->)\\s*(\\d+.\\d+.\\d+.\\d+)\\s*$"; + + private final String regex_connected = "^<14>commander: WAN (\\d+) connected\\s+$"; + private final String regex_disconnected = "^<14>commander: WAN (\\d+) disconnected\\s+$"; + private final String regex_SIM_ACTIVE = "^<14>O3G\\/dualsim_switch: Start dualsim for \\[(\\w+)\\]!!\\s+$"; + private final String regex_SIM_Time = "^<135>udhcpd\\[\\d+\\d]: oldsystemtime=\\d+ newsystemtime=(\\d+)\\s*$"; + private final String regex_IMSI="^<14>O3G\\/mal_daemon_0: previous IMSI\\[\\d*\\], current IMSI\\[(\\d*)\\], change record..\\s*$"; + + public void Initialize(BA bax, Object callerobject, String eventname) { + ba = bax; + caller = callerobject; + event = eventname.trim(); + if (ba instanceof BA) { + if (caller != null) { + if (!event.isEmpty()) { + need_log_event = ba.subExists(event+"_log"); + need_internetconnection_event= ba.subExists(event+"_internetconnection"); + need_updatednetworkinfo_event = ba.subExists(event+"_updatednetworkinfo"); + need_gsmtime_event = ba.subExists(event+"_gsmtime"); + need_changenetwork_event = ba.subExists(event+"_changenetwork"); + } + } + } + } + + // event dari SyslogServer + @BA.Hide + @Override + public void newdata(String fromip, int fromport, byte[] data) { + String cmd = new String(data).trim(); + if (cmd.isEmpty()) return; + + //TODO : nanti hapus ini + raise_log(cmd); + + // info SIM mana yang aktif + //<14>O3G/dualsim_switch: dualsim_switch main:index_3G=0, start[3G1] + //<14>O3G/dualsim_switch: Start dualsim for [3G1]!! + + // buat info jaringan + //<14>MultiWANChk2: printnode[109]: index -> 13 + //<14>MultiWANChk2: printnode[110]: mode -> 0 + //<14>MultiWANChk2: printnode[111]: wantype -> 16 + //<14>MultiWANChk2: printnode[112]: ifname -> usbnet0 + //<14>MultiWANChk2: printnode[113]: gwaddr -> 0.0.0.0 + //<14>MultiWANChk2: printnode[114]: ipaddr -> 10.53.41.49 + //<14>MultiWANChk2: printnode[115]: dns -> 112.215.198.254 + //<14>MultiWANChk2: printnode[116]: sec dns -> 112.215.71.243 + //<14>MultiWANChk2: printnode[117]: hostaddr -> 112.215.198.254 + //<14>MultiWANChk2: printnode[118]: sec_hostaddr-> + //<14>MultiWANChk2: printnode[119]: interval -> 3 + + // info WAN connected / disconnected + //<14>commander: WAN 14 connected + //<14>commander: WAN 14 disconnected + + // info jam GSM + //<135>udhcpd[1841]: oldsystemtime=1356998471 newsystemtime=1598858696 + + // info jaringan + //<14>O3G/mal_daemon_0: previous IMSI[], current IMSI[510113840731039], change record.. + + if (Regex.IsMatch(regex_dns, cmd)) { + String result = Regex.Matcher(regex_dns, cmd).Group(3).trim(); // grup mulai dari 1, value di grup 3 + if (!_dns.equalsIgnoreCase(result)) { + // ada perubahan data + _dns = result; + raise_log("New DNS : "+_dns); + if (complete_network_info()) raise_updatednetworkinfo(); + } + return; + } + + if (Regex.IsMatch(regex_sec_dns, cmd)) { + String result = Regex.Matcher(regex_sec_dns, cmd).Group(3).trim(); // grup mulai dari 1, value di grup 3 + if (!_sec_dns.equalsIgnoreCase(result)) { + // ada perubahan data + _sec_dns = result; + raise_log("New Secondary_DNS : "+ _sec_dns); + if (complete_network_info()) raise_updatednetworkinfo(); + } + } + + if (Regex.IsMatch(regex_gwaddress, cmd)) { + String result = Regex.Matcher(regex_gwaddress, cmd).Group(3).trim(); // grup mulai dari 1, value di grup 3 + if (!_gwaddress.equalsIgnoreCase(result)) { + // ada perubahan data + _gwaddress = result; + raise_log("New Gateway_Address : "+ _gwaddress); + if (complete_network_info()) raise_updatednetworkinfo(); + } + return; + } + + if (Regex.IsMatch(regex_hostaddress, cmd)) { + String result = Regex.Matcher(regex_hostaddress, cmd).Group(3).trim(); // grup mulai dari 1, value di grup 3 + if (!_hostaddress.equalsIgnoreCase(result)) { + // ada perubahan data + _hostaddress = result; + raise_log("New Host_Address : "+ _hostaddress); + if (complete_network_info()) raise_updatednetworkinfo(); + } + return; + } + if (Regex.IsMatch(regex_ifname, cmd)) { + String result = Regex.Matcher(regex_ifname, cmd).Group(3).trim(); // grup mulai dari 1, value di grup 3 + if (!_ifname.equalsIgnoreCase(result)) { + // ada perubahan data + _ifname = result; + raise_log("New Interface_Name : "+ _ifname); + if (complete_network_info()) raise_updatednetworkinfo(); + } + return; + } + if (Regex.IsMatch(regex_ipaddress, cmd)) { + String result = Regex.Matcher(regex_ipaddress, cmd).Group(3).trim(); // grup mulai dari 1, value di grup 3 + if (!_ipaddress.equalsIgnoreCase(result)) { + // ada perubahan data + _ipaddress = result; + raise_log("New IP_Address : "+ _ipaddress); + if (complete_network_info()) raise_updatednetworkinfo(); + } + return; + } + if (Regex.IsMatch(regex_mode, cmd)) { + String result = Regex.Matcher(regex_mode, cmd).Group(3).trim(); // grup mulai dari 1, value di grup 3 + if (!_mode.equalsIgnoreCase(result)) { + // ada perubahan data + _mode = result; + raise_log("New Mode : "+ _mode); + if (complete_network_info()) raise_updatednetworkinfo(); + } + return; + } + if (Regex.IsMatch(regex_wantype, cmd)) { + String result = Regex.Matcher(regex_wantype, cmd).Group(3).trim(); // grup mulai dari 1, value di grup 3 + if (!_wantype.equalsIgnoreCase(result)) { + // ada perubahan data + _wantype = result; + raise_log("New WAN_Type : "+ _wantype); + if (complete_network_info()) raise_updatednetworkinfo(); + } + return; + } + + if (Regex.IsMatch(regex_SIM_ACTIVE, cmd)) { + String result = Regex.Matcher(regex_SIM_ACTIVE, cmd).Group(1).trim(); // grup mulai dari 1, value di grup 1 + if (! _currentSIM.equalsIgnoreCase(result)) { + // ada perubahan data + _currentSIM = result; + raise_log("Selected SIM : "+_currentSIM); + } + return; + } + + if (Regex.IsMatch(regex_connected, cmd)) { + String result = Regex.Matcher(regex_connected, cmd).Group(1).trim(); // grup mulai dari 1, value di grup 1 + if (! _wanID.equalsIgnoreCase(result)) { + // ada perubahan data + _wanID = result; + raise_log("New WAN ID : "+_wanID); + } + raise_internetconnection(true); + return; + } + + if (Regex.IsMatch(regex_disconnected, cmd)) { + _wanID = ""; + raise_log("WAN ID cleared"); + + // clear all network info + _dns = ""; + _gwaddress = ""; + _hostaddress = ""; + _ifname = ""; + _ipaddress = ""; + _mode = ""; + _sec_dns = ""; + _wantype = ""; + + raise_internetconnection(false); + return; + } + + if (Regex.IsMatch(regex_SIM_Time, cmd)) { + String result = Regex.Matcher(regex_SIM_Time, cmd).Group(1).trim(); + try { + long value = Long.parseLong(result); + raise_gsmtime(value); + } catch(NumberFormatException e) { + // do nothing + raise_log("Error Long Parsing System Time = "+result+", Msg : "+e.getMessage()); + } + return; + } + + if (Regex.IsMatch(regex_IMSI, cmd)) { + MatcherWrapper rex = Regex.Matcher(regex_IMSI, cmd); + if (rex instanceof MatcherWrapper) { + IMSI_Data prev = new IMSI_Data(rex.Group(1)); + IMSI_Data current = new IMSI_Data(rex.Group(2)); + _currentIMSI = current; + raise_changenetwork(prev, current); + } + } + + } + + private boolean complete_network_info() { + if (_dns.isEmpty()) return false; + if (_gwaddress.isEmpty()) return false; + if (_hostaddress.isEmpty()) return false; + if (_ifname.isEmpty()) return false; + if (_ipaddress.isEmpty()) return false; + if (_mode.isEmpty()) return false; + if (_sec_dns.isEmpty()) return false; + if (_wantype.isEmpty()) return false; + return true; + } + + /** + * Get Current IMSI Data in use + * @return IMSI_Data + */ + public IMSI_Data getCurrentIMSI() { + return _currentIMSI; + } + + /*** + * Get WAN ID + * @return empty string if not connected + */ + public String getWAN_ID() { + return _wanID; + } + + /** + * Detected Public IP Address + * @return empty string if disconnected + */ + public String getIPAddress() { + return _ipaddress; + } + + /** + * Detected DNS Address + * @return empty string if disconnected + */ + public String getDNS() { + return _dns; + } + + /** + * Detected Secondary DNS Address + * @return empty string if disconnected + */ + public String getSecondaryDNS() { + return _sec_dns; + } + + /** + * Detected Gateway Address + * @return empty string if disconnected + */ + public String getGateway() { + return _gwaddress; + } + + /** + * Detected Host Address + * @return empty string if disconnected + */ + public String getHostAddress() { + return _hostaddress; + } + + /** + * Detected Interface Name + * @return empty string if disconnected + */ + public String getInterfaceName() { + return _ifname; + } + + /** + * Detected Mode + * @return empty string if disconnected + */ + public String getMode() { + return _mode; + } + + /** + * Detected WAN Type + * @return empty string if disconnected + */ + public String getWANType() { + return _wantype; + } + + /** + * Current SIM slot being used + * @return empty string if nothing selected + */ + public String getCurrentSIM() { + return _currentSIM; + } + + /** + * Get / Set Syslog Consumer identification + */ + public String getIdentifier() { + return _identifier; + } + + public void setIdentifier(String value) { + if (value instanceof String) { + value = value.trim(); + if (!value.isEmpty()) { + _identifier = value.trim(); + } + } + } + + private void raise_log(String value) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {value}); + } + + private void raise_internetconnection(boolean value) { + if (need_internetconnection_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_internetconnection", false, new Object[] {value}); + } + + private void raise_updatednetworkinfo() { + if (need_updatednetworkinfo_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_updatednetworkinfo", false, null); + } + + private void raise_gsmtime(long value) { + if (need_gsmtime_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_gsmtime", false, new Object[] {value}); + } + + private void raise_changenetwork(IMSI_Data prev, IMSI_Data current) { + if (need_changenetwork_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_changenetwork", false, new Object[] {prev, current}); + } +} diff --git a/src/syslog/IMSI_Data.java b/src/syslog/IMSI_Data.java new file mode 100644 index 0000000..67e57d3 --- /dev/null +++ b/src/syslog/IMSI_Data.java @@ -0,0 +1,100 @@ +package syslog; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("IMSI_Data") +public class IMSI_Data { + private final String _imsi; // always 15 digit + private final String _mcc; // country code + private final String _mnc; // network code + private final String _msin; // subscriber code + public IMSI_Data() { + _imsi=""; + _mcc=""; + _mnc=""; + _msin=""; + } + + public IMSI_Data(String value) { + _imsi = value; + if (_imsi.length()>=15) { + _mcc = _imsi.substring(0, 3); // 0,1,2 --> 3 digits + _mnc = _imsi.substring(3,5); // 3,4 --> 2 digits + _msin = _imsi.substring(5); // 5++ --> 10 digit + + } else { + _mcc = ""; + _mnc = ""; + _msin = ""; + } + + } + + /** + * Check if IMSI_Data contains valid info + * @return true if valid + */ + public boolean isValid() { + if (_mcc.isEmpty()) return false; + if (_mnc.isEmpty()) return false; + if (_msin.isEmpty()) return false; + return true; + } + + /** + * Check if Indonesian Network, that is MCC = 510 + * @return true if indonesian + */ + public boolean isIndonesian() { + if (_mcc.equals("510")) return true; else return false; + } + + /** + * Get Indonesian Provider Name + * @return Unknown if data invalid + */ + public String ProviderName() { + switch(_mnc) { + case "10" : return "Telkomsel"; + case "11" : return "XL"; + case "01" : return "Indosat"; + case "89" : return "Three"; + case "09" : return "Smartfren"; + case "88" : return "Bolt"; + default: return "Unknown"; + } + } + + /** + * Check if this IMSI is equal with Value + * @param value : other IMSi value + * @return true if equal + */ + public boolean IsEqual(String value) { + return _imsi.equals(value); + } + + /** + * MCC is Mobile Country Code + * @return empty string if invalid + */ + public String getMCC() { + return _mcc; + } + + /** + * MNC is Mobile Network Code + * @return empty string if invalid + */ + public String getMNC() { + return _mnc; + } + + /** + * MSIN is Mobile Subsriber Identification Number + * @return empty string if invalid + */ + public String getMSIN() { + return _msin; + } +} diff --git a/src/syslog/SyslogEvent.java b/src/syslog/SyslogEvent.java new file mode 100644 index 0000000..34ff356 --- /dev/null +++ b/src/syslog/SyslogEvent.java @@ -0,0 +1,5 @@ +package syslog; + +public interface SyslogEvent { + void newdata(String fromip, int fromport, byte[] data); +} diff --git a/src/syslog/SyslogServer.java b/src/syslog/SyslogServer.java new file mode 100644 index 0000000..88a7bf1 --- /dev/null +++ b/src/syslog/SyslogServer.java @@ -0,0 +1,204 @@ +package syslog; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; + +import java.net.SocketException; + + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.objects.collections.Map; + +@BA.ShortName("SyslogServer") +@BA.Events(values= { + "log(msg as string)", + "serverstatus(running as boolean)", + "newdata(fromip as string, fromport as int, bb() as byte)" +}) + +/** + * Syslog Server + * By default, Syslog will listen at UDP Port 514 + * @author rdkartono + * + */ +public class SyslogServer { + + private BA ba; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_serverstatus_event = false; + private boolean need_newdata_event = false; + private int syslogport = 514; + private boolean listening = false; + private Map plugmap = new Map(); + + /** + * Initialize Syslog Server + * @param callerobject : callback object + * @param eventname : event name + */ + public void Initialize(BA bax, Object callerobject, String eventname) { + ba = bax; + caller = callerobject; + event = eventname.trim(); + plugmap.Initialize(); + if (ba instanceof BA) { + if (caller != null) { + if (!event.isEmpty()) { + need_log_event = ba.subExists(event+"_log"); + need_serverstatus_event = ba.subExists(event+"_serverstatus"); + need_newdata_event = ba.subExists(event+"_newdata"); + } + } + } + } + + /** + * Open Syslog Server + * Will raise serverstatus event + * @param port : valid port number (1 - 65535), or default to 514 + * @return true if success + */ + public boolean Open(int port) { + if (port>0) { + if (port < 65535) { + syslogport = port; + } + } + + Close(); + + try { + DatagramSocket udp = new DatagramSocket(syslogport); + Thread tx = new Thread(new ListeningRunnable(udp)); + tx.start(); + return true; + } catch (SocketException | SecurityException e) { + raise_log("Open at Port="+port+" failed, Msg : "+e.getMessage()); + } + raise_serverstatus(false); /// gagal + return false; + } + + private class ListeningRunnable implements Runnable { + private final DatagramSocket udpsock; + public ListeningRunnable(DatagramSocket sock) { + udpsock = sock; + } + @Override + public void run() { + if (udpsock instanceof DatagramSocket) { + listening = true; + raise_serverstatus(true); + while(true) { + if (!listening) break; + if (!(udpsock instanceof DatagramSocket)) break; + if (udpsock.isClosed()) break; + byte[] buf = new byte[1500]; + DatagramPacket p = new DatagramPacket(buf, buf.length); + + try { + udpsock.receive(p); // tungguin di sini sampe dapat data + + } catch (IOException e) { + // gagal receive data + raise_log("Error Receive DatagramPacket, Msg : "+e.getMessage()); + break; + } + + // sampai sini, bisa receive data + if (p instanceof DatagramPacket) { + if (p.getLength()>0) { + String fromip = p.getAddress().getHostAddress(); + int fromport = p.getPort(); + byte[] result = new byte[p.getLength()]; + + // event dari SyslogServer + raise_newdata(fromip, fromport, result); + + // check di plugmap + if (plugmap instanceof Map) { + // plugmap valid + if (plugmap.getSize()>0) { + // ada isinya + for(int ii=0;ii votingcontentmap = new HashMap(); + + private final ExecutorService executor = Executors.newSingleThreadExecutor(); + + /** + * Get Central Volume + *

values :
+ * 1-32
+ * Default

+ * @return string value + */ + public String getCentralVolume() { + if (last_CentralVolume>=1 && last_CentralVolume<=0x20) { + return String.valueOf(last_CentralVolume); + } else return "Default"; + } + + /** + * Get Function Operation Status
+ *

values :
+ * Accepting Speech
+ * Chairman Priority Speech in Progress
+ * Voting in Progress
+ * Installation Testing in Progress
+ * Else
+ * Default

+ * @return string value + */ + public String getFunctionOperationStatus() { + switch(last_FunctionOperationStatus) { + case 0x41 : + return "Accepting Speech"; + case 0x42 : + return "Chairman Priority Speech in Progress"; + case 0x43 : + return "Voting in Progress"; + case 0x44 : + return "Installation Testing in Progress"; + case 0x52 : + return "Else"; + default : + return "Default"; + } + } + + /** + * Get Speech Method
+ *

values :
+ * FIFO
+ * LIFO
+ * 1Fix+LIFO
+ * Centralized allowed
+ * Centralized Prohibited
+ * Default

+ * @return string value + */ + public String getSpeechMethod() { + switch(last_SpeechMethod) { + case 0x41 : + return "FIFO"; + case 0x42 : + return "LIFO"; + case 0x43 : + return "1 Fix + LIFO"; + case 0x44 : + return "Centralized External Control Allowed"; + case 0x45 : + return "Centralized External Control Prohibited"; + default : + return "Default"; + } + } + + /** + * Get Maximum Speakers Limit + *

values :
+ * 1
+ * 2
+ * 3
+ * 4
+ * Default

+ * @return string value + */ + public String getSpeakerNumberLimit() { + switch(last_SpeakerNumberLimit) { + case 0x31 : + return "1"; + case 0x32 : + return "2"; + case 0x33 : + return "3"; + case 0x34 : + return "4"; + default : + return "Default"; + } + } + + /** + * Get Auto Microphone-Off + *

values :
+ * ON
+ * OFF
+ * Default

+ * @return string value + */ + public String getAutoMicOnOFF() { + switch(last_AutoMicOnOFF) { + case 0x59 : + return "ON"; + case 0x4E : + return "OFF"; + default : + return "Default"; + } + } + + /** + * Initialize TS910 Protocol + * @param eventname eventname + */ + public void Initialize(BA ba, String eventname) { + this.bax = ba; + this.event = eventname; + if (bax!=null) { + if (event.isEmpty()==false) { + need_log_event = bax.subExists(event+"_log"); + need_portstatus_event = bax.subExists(event+"_portstatus"); + need_speechfunctionsettingchange_event = bax.subExists(event+"_speechfunctionsettingchange"); + need_functionoperationstatus_event = bax.subExists(event+"_functionoperationstatus"); + need_centralvolume_event = bax.subExists(event+"_centralvolume"); + need_batteryalarm_event = bax.subExists(event+"_batteryalarm"); + need_startspeech_event = bax.subExists(event+"_startspeech"); + need_endspeech_event = bax.subExists(event+"_endspeech"); + need_speechrequest_event = bax.subExists(event+"_speechrequest"); + need_speechrequestcancellation_event = bax.subExists(event+"_speechrequestcancellation"); + need_speechrequestacceptancestatusquery_event = bax.subExists(event+"_speechrequestacceptancestatusquery"); + need_startchairmanpriorityspeech_event = bax.subExists(event+"_startchairmanpriorityspeech"); + need_endchairmanpriorityspeech_event = bax.subExists(event+"_endchairmanpriorityspeech"); + need_startvoting_event = bax.subExists(event+"_startvoting"); + need_endvoting_event = bax.subExists(event+"_endvoting"); + need_votingresult_event = bax.subExists(event+"_votingresult"); + need_individualvolume_event = bax.subExists(event+"_individualvolume"); + need_startspeechvolumeadjustment_event = bax.subExists(event+"_startspeechvolumeadjustment"); + need_speechstatusquery_event = bax.subExists(event+"_speechstatusquery"); + need_installationcheckresult_event = bax.subExists(event+"_installationcheckresult"); + need_errortype2_event = bax.subExists(event+"_errortype2"); + need_errortype3_event = bax.subExists(event+"_errortype3"); + } + } + } + + /** + * Open SerialPort to use + * will raise event portstatus + * @param portname valid serial port name + * @return true if success + */ + public boolean OpenPort(String portname) { + try { + myport = SerialPort.getCommPort(portname); + myport.setBaudRate(9600); + myport.setParity(SerialPort.EVEN_PARITY); + myport.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING | SerialPort.TIMEOUT_WRITE_BLOCKING, Serial_read_timeout, Serial_write_timeout); + if (myport.openPort()) { + raise_portstatus(portname, true); + + serialevent = new eventlistener(); + return true; + } else { + raise_log("Unable to open Port "+portname); + } + } catch(SerialPortInvalidPortException e) { + raise_log("Unable to open Port "+portname+", exception="+e.getMessage()); + myport = null; + } + return false; + } + + /** + * Close SerialPort + * will raise event portstatus + */ + public void ClosePort() { + String portname = ""; + if (myport!=null) { + portname = myport.getDescriptivePortName(); + myport.closePort(); + } + if (serialevent!=null) { + serialevent = null; + } + + raise_portstatus(portname, false); + myport = null; + + } + + /** + * Check if SerialPort is opened + * @return true if opened + */ + public boolean IsOpened() { + if (myport!=null) { + return myport.isOpen(); + } + return false; + } + + /** + * Get SpeechFunctionSetting + * will raise event speechfunctionsettingchange + * @return true if success + */ + public boolean Get_SpeechFunctionSetting() { + Future result = executor.submit(new requestsender("Get_SpeechFunctionSetting",Make_Command_data((byte)0xF3))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Get_SpeechFunctionSetting exception="+e.getMessage()); + return false; + } + } + + /** + * Set SpeechFunctionSetting + * will raise event speechfunctionsettingchange + * @param speechmethod value 0 - 5 + *
0 = Default, based on switch at Central Unit + *
1 = FIFO + *
2 = LIFO + *
3 = 1 Fixed + LIFO + *
4 = Centralized external control allowed + *
5 = Centralized external control prohibited + * @param speakerlimit value = 0 - 4 + *
0 = Default, based on switch at Central Unit + *
1 = 1 + *
2 = 2 + *
3 = 3 + *
4 = 4 + * @param automicoff value 0 - 2 + *
0 = Default, based on switch at Central Unit + *
1 = Yes + *
2 = No + * @return true if success + */ + public boolean Set_SpeechFunctionSetting(int speechmethod, int speakerlimit, int automicoff) { + if (speechmethod<0) speechmethod = 0; + if (speechmethod>5) speechmethod = 5; + if (speakerlimit<0) speakerlimit = 0; + if (speakerlimit>4) speakerlimit = 4; + if (automicoff<0) automicoff = 0; + if (automicoff>2) automicoff = 2; + byte[] cmd = Make_Command_data( + (byte)0xF1, + (byte)(speechmethod>0 ? speechmethod+0x40 : 0), + (byte)(speakerlimit>0 ? speakerlimit+0x30 : 0), + (byte)(automicoff > 0 ? (automicoff == 1 ? 0x59 : 0x4E) : 0) + ); + + Future result = executor.submit(new requestsender("Set_SpeechFunctionSetting",cmd)); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Set_SpeechFunctionSetting exception="+e.getMessage()); + return false; + } + } + + /** + * Get FunctionOperationStatus + * will raise event functionoperationstatus + * @return true if success + */ + public boolean Get_FunctionOperationStatus() { + Future result = executor.submit(new requestsender("Get_FunctionOperationStatus", Make_Command_data((byte)0xF7))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Get_FunctionOperationStatus exception="+e.getMessage()); + return false; + } + } + + /** + * Get Central Volume + * will raise event centralvolume + * @return true if success + */ + public boolean Get_CentralVolume() { + Future result = executor.submit(new requestsender("Get_CentralVolume", Make_Command_data((byte)0xFB))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Get_CentralVolume exception="+e.getMessage()); + return false; + } + } + + /** + * Set Central Volume + * will raise event centralvolume + * @param value 0 - 32 + *
0 = Default, initial value at 16 + *
1-32 = 1 - 32 + * @return true if success + */ + public boolean Set_CentralVolume(int value) { + if (value<0) value = 0; + if (value>32) value = 32; + Future result = executor.submit(new requestsender("Set_CentralVolume", Make_Command_data((byte)0xF9, (byte)value))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Set_CentralVolume exception="+e.getMessage()); + return false; + } + } + + /** + * Start Speech + * will raise event startspeech + * @param unitnumber 1 - 192 + * @return true if success + */ + public boolean Start_Speech(int unitnumber) { + if (unitnumber<1) unitnumber = 1; + if (unitnumber>0xC0) unitnumber = 0xC0; + Future result = executor.submit(new requestsender("Start_Speech", Make_Command_data((byte)0xA1, (byte)unitnumber))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Start_Speech exception="+e.getMessage()); + return false; + } + } + + /** + * End Speech + * will raise event endspeech + * @param unitnumber 1 - 192 + * @return true if success + */ + public boolean End_Speech(int unitnumber) { + if (unitnumber<1) unitnumber = 1; + if (unitnumber>0xC0) unitnumber = 0xC0; + Future result = executor.submit(new requestsender("End_Speech", Make_Command_data((byte)0xA0, (byte)unitnumber))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("End_Speech exception="+e.getMessage()); + return false; + } + } + + /** + * Reject Speech Request from unitnumber + * will raise event speechrequestcancellation + * @param unitnumber 1 - 192 + * @return true if success + */ + public boolean Reject_SpeechRequest(int unitnumber) { + if (unitnumber<1) unitnumber = 1; + if (unitnumber>0xC0) unitnumber = 0xC0; + Future result = executor.submit(new requestsender("Reject_SpeechRequest", Make_Command_data((byte)0xA4, (byte)unitnumber))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Reject_SpeechRequest exception="+e.getMessage()); + return false; + } + } + + /** + * End all Speeches + * will raise event endspeech + * @return true if success + */ + public boolean End_All_Speech() { + Future result = executor.submit(new requestsender("End_All_Speech", Make_Command_data((byte)0xAB))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("End_All_Speech exception="+e.getMessage()); + return false; + } + } + + /** + * Reject all speech requests + * will raise event speechrequestcancellation + * @return true if success + */ + public boolean Reject_All_SpeechRequest() { + Future result = executor.submit(new requestsender("Reject_All_SpeechRequest", Make_Command_data((byte)0xAC))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Reject_All_SpeechRequest exception="+e.getMessage()); + return false; + } + } + + /** + * Get Speech Status + * will raise event speechstatusquery + * @return true if success + */ + public boolean Get_SpeechStatus() { + Future result = executor.submit(new requestsender("Get_SpeechStatus", Make_Command_data((byte)0xA3))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Get_SpeechStatus exception="+e.getMessage()); + return false; + } + } + + /** + * Get Speech Request Acceptance + * will raise event speechrequestacceptancestatusquery + * @return true if success + */ + public boolean Get_SpeechRequestAcceptance() { + Future result = executor.submit(new requestsender("Get_SpeechRequestAcceptance", Make_Command_data((byte)0xA7))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Get_SpeechRequestAcceptance exception="+e.getMessage()); + return false; + } + } + + /** + * Start Voting + * will raise multiple event startvoting + * @return true if success + */ + public boolean Start_Voting() { + Future result = executor.submit(new requestsender("Start_Voting", Make_Command_data((byte)0xC1))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Start_Voting exception="+e.getMessage()); + return false; + } + } + + /** + * End Voting + * will raise event votingresult + * @return true if success + */ + public boolean End_Voting() { + Future result = executor.submit(new requestsender("End_Voting", Make_Command_data((byte)0xC0))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("End_Voting exception="+e.getMessage()); + return false; + } + } + + /** + * Get Final Voting Result + * will raise event votingresult + * @return true if success + */ + public boolean Get_VotingResult() { + Future result = executor.submit(new requestsender("Get_VotingResult", Make_Command_data((byte)0xC3))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Get_VotingResult exception="+e.getMessage()); + return false; + } + } + + /** + * Get Individual Delegate Vote + * will raise multiple event endvoting + * @return true if success + */ + public boolean Get_VotingResponse() { + Future result = executor.submit(new requestsender("Get_VotingResponse", Make_Command_data((byte)0xC2))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Get_VotingResponse exception="+e.getMessage()); + return false; + } + } + + /** + * Get Individual Microphone Input Volume + * will raise event individualvolume + * @param unitnumber 1 - 192 + * @return true if success + */ + public boolean Get_IndividualMicrophoneInputVolume(int unitnumber) { + if (unitnumber<1) unitnumber = 1; + if (unitnumber>0xC0) unitnumber = 0xC0; + Future result = executor.submit(new requestsender("Get_IndividualMicrophoneInputVolume", Make_Command_data((byte)0xF4, (byte)unitnumber))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Get_IndividualMicrophoneInputVolume exception="+e.getMessage()); + return false; + } + + } + + /** + * SEt Individual Microphone Input Volume + * will raise event individualvolume + * @param unitnumber 1- 192 + * @param volume 1 - 32 + * @return true if success + */ + public boolean Set_IndividualMicrophoneInputVolume(int unitnumber, int volume) { + if (unitnumber<1) unitnumber = 1; + if (unitnumber>0xC0) unitnumber = 0xC0; + if (volume<0) volume = 0; + if (volume>32) volume = 32; + Future result = executor.submit(new requestsender("Set_IndividualMicrophoneInputVolume", Make_Command_data((byte)0xF2, (byte)unitnumber,(byte)volume))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Set_IndividualMicrophoneInputVolume exception="+e.getMessage()); + return false; + } + } + + /** + * Start Installation check + * @return true if success + */ + public boolean Start_Installation_Check() { + Future result = executor.submit(new requestsender("Start_Installation_Check", Make_Command_data((byte)0xD1))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("Start_Installation_Check exception="+e.getMessage()); + return false; + } + } + + /** + * End Installation Check + * + * @return true if success + */ + public boolean End_Installation_Check() { + Future result = executor.submit(new requestsender("End_Installation_Check", Make_Command_data((byte)0xD0))); + try { + return result.get(); + } catch (InterruptedException | ExecutionException e) { + raise_log("End_Installation_Check exception="+e.getMessage()); + return false; + } + } + + private class requestsender implements Callable{ + private final byte[] cmd; + private final String description; + + public requestsender(String description, byte[] commandtosend) { + cmd = commandtosend; + this.description = description; + } + + @Override + public Boolean call() throws Exception { + manual_action = true; + Thread.sleep(10); + + // coba send ENQ + if (!SendData(ENQ)) { + raise_log("Unable to Send ENQ at command="+description); + manual_action = false; + return false; + } + + // sampe sini , ENQ terkirim. + // sekarang wait ACK + if (Expecting_ACK("ENQ")==false) { + manual_action = false; + return false; + } + + // sampe sini , terima ACK + // sekarang kirim data + if (!SendData(cmd)) { + raise_log("Unable to Send Command at "+description); + manual_action = false; + return false; + } + + // sampe sini , command terkirim + // sekarang wait ACK + if (Expecting_ACK(description)==false) { + manual_action = false; + return false; + } + + //sampe sini terima ACK + // sekarang kirim EOT + if (!SendData(EOT)) { + raise_log("Unable to Send EOT at command="+description); + manual_action = false; + return false; + } + + // sampai sini, EOT terkirim + // selesai normal + manual_action = false; + return true; + } + + private boolean Expecting_ACK(String after) { + byte[] bb = new byte[1]; + if (myport.readBytes(bb, bb.length) bufdata = new ArrayList<>(); + private int idlecounter = 0; + private final int idlewait = 5; + public eventlistener() { + bufdata.clear(); + } + + @Override + public void run() { + raise_log("SerialPort eventlistener started"); + while(true) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + break; + } + // kalau port tertutup, selesai + if (IsOpened()==false) break; + // kalau ada manual action, pause + if (manual_action==true) { + idlecounter = 0; + continue; + } + + idlecounter++; + if (myport.bytesAvailable()>0) { + // ada yang dibaca + byte[] newdata = new byte[myport.bytesAvailable()]; + myport.readBytes(newdata, newdata.length); + for(byte xx:newdata) bufdata.add(xx); + idlecounter = 0; + } + + if (idlecounter>idlewait) { + // sudah tungguin sampe 10ms gak ada data baru + idlecounter=0; + if (bufdata.size()>0) { + // ada isinya, berarti satu paket data nih .... + Process_Incoming_Bytes(bufdata); + bufdata.clear(); + } + } + } + raise_log("SerialPort eventlistener finished"); + } + + /** + * Process incoming command + * @param bb Byte Array yang sudah stripped out DLE nya + * @return true kalau command valid + */ + private boolean Process_Incoming_Command(byte[] bb) { + int cmdcode = ByteToInt(bb[0]); + int unitnumber = 0; + int channelnumber = 0; + int failuredetection = 0; + int volume=0; + int requestcode = 0; + int reasoncode = 0; + switch(cmdcode) { + case 0xF1 : + // Speech function setting change + case 0xF3 : + // Response to Speech function setting status query + if (bb.length==4) { + + int _speechmethod = ByteToInt(bb[1]); + int _speakernumberlimit = ByteToInt(bb[2]); + int _automiconoff = ByteToInt(bb[3]); + boolean haschanges = false; + if (_speechmethod != last_SpeechMethod) { + last_SpeechMethod = _speechmethod; + haschanges = true; + } + if (_speakernumberlimit != last_SpeakerNumberLimit) { + last_SpeakerNumberLimit = _speakernumberlimit; + haschanges = true; + } + if (_automiconoff != last_AutoMicOnOFF) { + last_AutoMicOnOFF = _automiconoff; + haschanges = true; + } + if (haschanges) { + raise_speechfunctionsettingchange(getSpeechMethod(), getSpeakerNumberLimit(), getAutoMicOnOFF()); + } + return true; + } + break; + case 0xF5 : + // Function operation status + case 0xF7 : + // Respnose to function Operation Status query + if (bb.length==2) { + + int _function = ByteToInt(bb[1]); + if (_function != last_FunctionOperationStatus) { + last_FunctionOperationStatus = _function; + raise_functionoperationstatus(getFunctionOperationStatus()); + } + return true; + } + break; + case 0xF9 : + // Conference unit microphone input volume adjustment + case 0xFB : + // Response to convference unit micrphne input volume adjustment query + if (bb.length==2) { + + int _volume = ByteToInt(bb[1]); + if (_volume != last_CentralVolume) { + last_CentralVolume = _volume; + raise_centralvolume(getCentralVolume()); + } + return true; + } + + break; + case 0xFD : + if (bb.length==2) { + // Battery alarm + unitnumber = ByteToInt(bb[1]); + raise_batteryalarm(getUnitNumber(unitnumber)); + return true; + } + + break; + case 0xA1 : + if (bb.length==3) { + // Start of Speech + unitnumber = ByteToInt(bb[1]); + channelnumber = ByteToInt(bb[2]); + raise_startspeech(getUnitNumber(unitnumber), getChannelNumber(channelnumber)); + return true; + } + break; + case 0xA0 : + if (bb.length==4) { + // End of Speech + unitnumber = ByteToInt(bb[1]); + channelnumber = ByteToInt(bb[2]); + failuredetection = ByteToInt(bb[3]); + raise_endspeech(getUnitNumber(unitnumber), getChannelNumber(channelnumber), getFailureDetection(failuredetection)); + return true; + } + + break; + case 0xA5 : + // Speech request + if (bb.length==2) { + unitnumber = ByteToInt(bb[1]); + raise_speechrequest(getUnitNumber(unitnumber)); + } + break; + case 0xA7 : + + if (bb.length==3) { + // Speech request cancelation + unitnumber = ByteToInt(bb[1]); + failuredetection = ByteToInt(bb[2]); + raise_speechrequestcancellation(getUnitNumber(unitnumber), getFailureDetection(failuredetection)); + return true; + } else if (bb.length==6) { + // Response to speech request acceptance status query + int accepted = ByteToInt(bb[1]); + int unitnumber1 = ByteToInt(bb[2]); + int unitnumber2 = ByteToInt(bb[3]); + int unitnumber3 = ByteToInt(bb[4]); + int unitnumber4 = ByteToInt(bb[5]); + raise_speechrequestacceptancestatusquery(getSpeechRequestAccepted(accepted), getUnitNumber(unitnumber1), getUnitNumber(unitnumber2), getUnitNumber(unitnumber3), getUnitNumber(unitnumber4)); + return true; + } + + break; + case 0xB1 : + if (bb.length==3) { + // start of Chairman priority speech + unitnumber = ByteToInt(bb[1]); + channelnumber = ByteToInt(bb[2]); + raise_startchairmanpriorityspeech(getUnitNumber(unitnumber), getChannelNumber(channelnumber)); + return true; + } + break; + case 0xB0 : + if (bb.length==4) { + // End of Chairman priority speech + unitnumber = ByteToInt(bb[1]); + channelnumber = ByteToInt(bb[2]); + failuredetection = ByteToInt(bb[3]); + raise_endchairmanpriorityspeech(getUnitNumber(unitnumber), getChannelNumber(channelnumber), getFailureDetection(failuredetection)); + return true; + } + break; + case 0xC1 : + if (bb.length==2) { + // Start of Voting + unitnumber = ByteToInt(bb[1]); + votingcontentmap.put(getUnitNumber(unitnumber), getVotingContent(0)); + raise_startvoting(getUnitNumber(unitnumber)); + return true; + } else if (bb.length==1) { + // Start of Installation check test + raise_log("Start of Installation Check"); + return true; + } + + break; + case 0xC2 : + if (bb.length==3) { + // End of Voting + unitnumber = ByteToInt(bb[1]); + int votingcontent = ByteToInt(bb[2]); + votingcontentmap.put(getUnitNumber(unitnumber), getVotingContent(votingcontent)); + raise_endvoting(getUnitNumber(unitnumber), getVotingContent(votingcontent)); + return true; + } + // ada lagi Installation Check Test Response , jumlah byte = 3 + // meragukan !! + break; + case 0xC0 : + if (bb.length==4) { + // Voting result/response + int vote1 = ByteToInt(bb[1]); + int vote2 = ByteToInt(bb[2]); + int vote3 = ByteToInt(bb[3]); + raise_votingresult(getVotingResult(vote1), getVotingResult(vote2), getVotingResult(vote3)); + return true; + } else if (bb.length==1) { + // End of Installation check test + raise_log("End Of Installation Check"); + return true; + } + + break; + case 0xF2 : + if (bb.length==3) { + // Conference Unit Individual Microphone Input Volume Adjsutment + unitnumber = ByteToInt(bb[1]); + volume = ByteToInt(bb[2]); + raise_individualvolume(getUnitNumber(unitnumber), getVolumeValue(volume)); + return true; + } + break; + case 0xA2 : + if (bb.length==4) { + // Volume Adjustment + unitnumber = ByteToInt(bb[1]); + channelnumber = ByteToInt(bb[2]); + volume = ByteToInt(bb[3]); + raise_startspeechvolumeadjustment(getUnitNumber(unitnumber), getChannelNumber(channelnumber), getVolumeValue(volume)); + return true; + } + break; + + case 0xA3 : + if (bb.length==6) { + // Response to speech status query + int chairmanvalue = ByteToInt(bb[1]); + int onch1 = ByteToInt(bb[2]); + int onch2 = ByteToInt(bb[3]); + int onch3 = ByteToInt(bb[4]); + int onch4 = ByteToInt(bb[5]); + raise_speechstatusquery(getChairmanPriorityPerformed(chairmanvalue), getUnitNumber(onch1), getUnitNumber(onch2), getUnitNumber(onch3), getUnitNumber(onch4)); + return true; + } + break; + + + case 0xC3 : + if (bb.length==4) { + // Voting result/response + int vote1 = ByteToInt(bb[1]); + int vote2 = ByteToInt(bb[2]); + int vote3 = ByteToInt(bb[3]); + raise_votingresult(getVotingResult(vote1), getVotingResult(vote2), getVotingResult(vote3)); + return true; + } else if (bb.length==3) { + // Response to isntallation check test result query + int sumofchairman = ByteToInt(bb[1]); + int sumofdelegate = ByteToInt(bb[2]); + raise_installationcheckresult(sumofchairman, sumofdelegate); + return true; + } + + break; + case 0xE2 : + if (bb.length==3) { + // Error response type 2 + requestcode = ByteToInt(bb[1]); + reasoncode = ByteToInt(bb[2]); + raise_errortype2(requestcode, getReasonOfErrors(reasoncode)); + return true; + } + break; + case 0xE3 : + if (bb.length==4) { + // Error response type 3 + requestcode = ByteToInt(bb[1]); + unitnumber = ByteToInt(bb[2]); + reasoncode = ByteToInt(bb[3]); + raise_errortype3(requestcode, getUnitNumber(unitnumber), getReasonOfErrors(reasoncode)); + return true; + } + break; + + } + return false; + } + + /** + * strip out (DLE STX) , (DLE ETX) , (DLE 0x10) + * @param bb array of bytes + * @return array of bytes + */ + private byte[] StripOutDLE(byte[] bb) { + List filtered = new ArrayList<>(); + boolean skipped = false; + for(byte bx : bb) { + if (skipped) { + if (bx==DLE) filtered.add(DLE); // double DLE, masukin yang kedua aja + skipped = false; + continue; + } else { + if (bx==DLE) { + // kalau ketemu DLE , skip byte + skipped = true; + continue; + } else filtered.add(bx); // kalau bukan, masukin dalam array filtered + } + } + + return ListByteToArrayByte(filtered); + } + + /** + * Process Incoming List of Byte + * @param cmd List of Byte + */ + private void Process_Incoming_Bytes(List cmd) { + byte[] bb = ListByteToArrayByte(cmd); + int length = bb.length; + if (length<1) return; + + if (Is_ENQ_data(bb)) { + // ada yang mau kirim data + txbyte.addAndGet(length); + txpacket.addAndGet(1); + // jawabin ACK + SendData(ACK); + + } else if (Is_Valid_Command(bb)) { + // sepaket data + txbyte.addAndGet(length); + txpacket.addAndGet(1); + + if (Process_Incoming_Command(StripOutDLE(bb))) { + SendData(ACK); + } else { + SendData(NAK); + } + + } else if (Is_EOT_data(bb)) { + txbyte.addAndGet(length); + txpacket.addAndGet(1); + + } + } + + /** + * Check if byte array is valid Command
+ * Valid Command always contains [DLE] [STX] [command] [DLE] [ETX] [BCC] + * @param bb array of bytes + * @return true if valid + */ + private boolean Is_Valid_Command(byte... bb) { + if (bb!=null) { + int length = bb.length; + // mesti lebih dari 5 bytes + // DLE STX [xxx] DLE ETX BCC + if (length>5) { + // byte pertama adalah DLE + if (bb[0]==DLE) { + // byte kedua adalah STX + if (bb[1]==STX) { + if (bb[length-3]==DLE) { + if (bb[length-2]==ETX) { + byte bcc = bb[length-1]; + if (CountBCC(bb)==bcc) { + return true; + } + } + } + } + } + } + } + return false; + } + } + + /** + * Convert List to array of byte + * @param ll List to convert + * @return array of byte + */ + private byte[] ListByteToArrayByte(List ll) { + if (ll != null) { + int length = ll.size(); + if (length>0) { + byte[] result = new byte[length]; + for(int ii=0;ii + * Valid Command Form is [DLE] [STX] [cmd] [DLE] [ETX] [BCC] + * @param cmd command bytes + * @return Command form + */ + private byte[] Make_Command_data(byte... cmd) { + byte[] newcommand = new byte[cmd.length+5]; + int length = newcommand.length; + newcommand[0] = DLE; + newcommand[1] = STX; + for(int ii=0;ii5) { + byte result = 0; + // skip DLE dan STX di awal + // skip BCC di paling akhir + // include DLE ETX di akhir + // <--------------------> + // DLE STX [b1] [b2] [b3] DLE ETX BCC + for(int ii=2;ii0) { + rxbyte.addAndGet(written); + rxpacket.addAndGet(1); + } + + } + return false; + } + + /** + * Convert unitnumber integer to UnitNumber Description + * @param unitnumber unit number integer + * @return string value + */ + private String getUnitNumber(int unitnumber) { + if (unitnumber>=1 && unitnumber<=0xC0) { + return "Unit"+unitnumber; + } else if (unitnumber>=0xF0 && unitnumber<=0xF3) { + return "RemoteControl"+(unitnumber-0xF0); + } else if (unitnumber==0xFA) { + return "External Equipment"; + } else if (unitnumber==0xFC) { + return "Central Unit"; + } else return "Not Applicable"; + } + + /** + * Convert channelnumber integer to ChannelNumber Description + * @param channelnumber channel number integer + * @return string value + */ + private String getChannelNumber(int channelnumber) { + if (channelnumber>=0x31 && channelnumber<=34) + return String.valueOf(channelnumber); + else return "No Channel"; + } + + /** + * Convert failuredetection code to Failure Detection Description + * @param code failure code + * @return string value + */ + private String getFailureDetection(int code) { + switch(code) { + case 0x41 : return "Out of Operating Range Detection"; + case 0x4D : return "Auto Mic-Off Detection"; + case 0x52 : return "Power-On Detection"; + default : return "Failure Detection Performed"; + } + } + + /** + * Convert number of accepted speechs to string value + * @param code accepted speech numbers + * @return string value + */ + private String getSpeechRequestAccepted(int code) { + if (code>=0x31 && code<=0x34) + return String.valueOf(code); + else return "Not Accepted"; + } + + /** + * Voting Content + * @param code valid value "1" to "3" + * @return strin value + */ + private String getVotingContent(int code) { + if (code>=0x31 && code<=0x33) + return String.valueOf(code); + else return "No Vote"; + } + + /** + * Voting result to String + * @param code valid value 0 ~ 0xC0 + * @return string value + */ + private String getVotingResult(int code) { + if (code>=0 && code<=0xC0) { + return String.valueOf(code); + } else return "Undefined"; + } + + /** + * Translate reason of error code to string + * @param code reason of error + * @return string value + */ + private String getReasonOfErrors(int code) { + switch(code) { + case 0x43 : return "Reception of Undefined Command"; + case 0x4C : return "Invalid Command Data Length"; + case 0x49 : return "Invalid Information Element"; + case 0x46 : return "Function Status Mismatch"; + case 0x55 : return "Unit Status Mismatch"; + case 0x42 : return "No Idle Channel"; + case 0x51 : return "Cannot Accept Speech Request"; + default : return "Unknown"; + } + } + + /** + * Convert volume value to String value + * @param value 1 - 21 + * @return string value of -10 dB ~ +10 dB + */ + private String getVolumeValue(int value) { + if (value>=0x1 && value <=0x15) { + return (value-11)+" dB"; + } else return "0 dB"; + } + + private String getChairmanPriorityPerformed(int code) { + switch(code) { + case 0x59 : return "Yes"; + case 0x4E : return "No"; + default : return "Unknown"; + } + } + + private void raise_centralvolume(String value) { + if (need_centralvolume_event) bax.raiseEventFromDifferentThread(Me, null,0, event+"_centralvolume", false, new Object[] {value}); + } + + private void raise_log(String msg) { + if (need_log_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + private void raise_portstatus(String portname, boolean isopen) { + if (need_portstatus_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_portstatus", false, new Object[] {portname, isopen}); + } + private void raise_speechfunctionsettingchange(String speechmethod, String speakernumberlimit, String automiconoff) { + if (need_speechfunctionsettingchange_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_speechfunctionsettingchange", false, new Object[] {speechmethod,speakernumberlimit,automiconoff}); + } + + private void raise_functionoperationstatus(String status) { + if (need_functionoperationstatus_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_functionoperationstatus", false, new Object[] {status}); + } + private void raise_batteryalarm(String unitnumber) { + if (need_batteryalarm_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_batteryalarm", false, new Object[] {unitnumber}); + } + + private void raise_startspeech(String unitnumber, String channelnumber) { + if (need_startspeech_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_startspeech", false, new Object[] {unitnumber, channelnumber}); + } + private void raise_endspeech(String unitnumber, String channelnumber, String failuredetection) { + if (need_endspeech_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_endspeech", false, new Object[] {unitnumber, channelnumber, failuredetection}); + } + private void raise_speechrequest(String unitnumber) { + if (need_speechrequest_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_speechrequest", false, new Object[] {unitnumber}); + } + private void raise_speechrequestcancellation(String unitnumber, String failuredetection) { + if (need_speechrequestcancellation_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_speechrequestcancellation", false, new Object[] {unitnumber, failuredetection}); + } + private void raise_speechrequestacceptancestatusquery(String acceptedrequest, String unitnumber1, String unitnumber2, String unitnumber3, String unitnumber4) { + if (need_speechrequestacceptancestatusquery_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_speechrequestacceptancestatusquery", false, new Object[] {acceptedrequest, unitnumber1, unitnumber2, unitnumber3, unitnumber4}); + } + private void raise_startchairmanpriorityspeech(String unitnumber, String channelnumber) { + if (need_startchairmanpriorityspeech_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_startchairmanpriorityspeech", false, new Object[] {unitnumber, channelnumber}); + } + private void raise_endchairmanpriorityspeech(String unitnumber, String channelnumber, String failuredetection) { + if (need_endchairmanpriorityspeech_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_endchairmanpriorityspeech", false, new Object[] {unitnumber, channelnumber, failuredetection}); + } + private void raise_startvoting(String unitnumber) { + if (need_startvoting_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_startvoting", false, new Object[] {unitnumber}); + } + private void raise_endvoting(String unitnumber, String votingcontent) { + if (need_endvoting_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_endvoting", false, new Object[] {unitnumber, votingcontent}); + } + private void raise_votingresult(String vote1, String vote2, String vote3) { + if (need_votingresult_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_votingresult", false, new Object[] {vote1,vote2,vote3}); + } + private void raise_individualvolume(String unitnumber, String volume) { + if (need_individualvolume_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_individualvolume", false, new Object[] {unitnumber, volume}); + } + private void raise_startspeechvolumeadjustment(String unitnumber, String channelnumber, String volume) { + if (need_startspeechvolumeadjustment_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_startspeechvolumeadjustment", false, new Object[] {unitnumber, channelnumber, volume}); + } + private void raise_speechstatusquery(String chairman, String onchannel1, String onchannel2, String onchannel3, String onchannel4) { + if (need_speechstatusquery_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_speechstatusquery", false, new Object[] {chairman, onchannel1, onchannel2, onchannel3, onchannel4}); + } + private void raise_installationcheckresult(int chairmanunits, int delegateunits) { + if (need_installationcheckresult_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_installationcheckresult", false, new Object[] {chairmanunits, delegateunits}); + } + private void raise_errortype2(int commandcode, String reason) { + if (need_errortype2_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_errortype2", false, new Object[] {commandcode, reason}); + } + private void raise_errortype3(int commandcode, String unitnumber, String reason) { + if (need_errortype3_event) bax.raiseEventFromDifferentThread(Me, null, 0, event+"_errortype3", false, new Object[] {commandcode, unitnumber, reason}); + } +} diff --git a/src/waterlevelsensor/HPT604Data.java b/src/waterlevelsensor/HPT604Data.java new file mode 100644 index 0000000..c5d4c24 --- /dev/null +++ b/src/waterlevelsensor/HPT604Data.java @@ -0,0 +1,61 @@ +package waterlevelsensor; + +import anywheresoftware.b4a.BA; +import anywheresoftware.b4a.keywords.DateTime; + +@BA.ShortName("HPT604Data") +public class HPT604Data { + + /** + * Check if this is a valid data + */ + public final boolean valid; + + /** + * when the measurement taken, in datetime.tick + */ + public final long timetick = DateTime.getNow(); + + /** + * Sensor full scale spec, in meters + */ + public final double fullscale; + + /** + * Binary value read from sensor + */ + public final int binaryvalue; + + /** + * Calculated water level, in meters + */ + public final double meters; + + public HPT604Data() { + valid = false; + fullscale = 0; + binaryvalue = 0; + meters = 0; + } + + public HPT604Data(int binary, double fullscalevalue) { + if (binary>0) { + if (fullscalevalue>0) { + binaryvalue = binary; + fullscale = fullscalevalue; + if (fullscale<10) { + meters = binaryvalue / 10000.0; + } else { + meters = binaryvalue / 1000.0; + } + valid = true; + return; + } + } + valid = false; + binaryvalue = 0; + fullscale = 0; + meters = 0; + } + +} diff --git a/src/waterlevelsensor/Holykell_HPT604_Modbus_RTU.java b/src/waterlevelsensor/Holykell_HPT604_Modbus_RTU.java new file mode 100644 index 0000000..9646fd6 --- /dev/null +++ b/src/waterlevelsensor/Holykell_HPT604_Modbus_RTU.java @@ -0,0 +1,259 @@ +package waterlevelsensor; + +import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException; +import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException; +import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException; +import com.intelligt.modbus.jlibmodbus.master.ModbusMaster; +import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory; +import com.intelligt.modbus.jlibmodbus.serial.SerialParameters; +import com.intelligt.modbus.jlibmodbus.serial.SerialPort; +import com.intelligt.modbus.jlibmodbus.serial.SerialPortException; +import com.intelligt.modbus.jlibmodbus.serial.SerialUtils; +import com.intelligt.modbus.jlibmodbus.utils.FrameEvent; +import com.intelligt.modbus.jlibmodbus.utils.FrameEventListener; + +import anywheresoftware.b4a.BA; + +@BA.ShortName("Holykell_HPT604") +@BA.Events(values= { + "log(msg as string)", + "txrxcounter(tx as int, rx as int)", + "sentbytes(bb() as byte)", + "receivedbytes(bb() as byte)", + +}) + +/** + * Holykell HPT604 submersible water level sensor, RS-485 Modbus RTU communication + * @author rdkartono + * + */ +public class Holykell_HPT604_Modbus_RTU { + + private BA ba; + private Object caller; + private String event; + private final Object Me = this; + private boolean need_log_event = false; + private boolean need_txrxcounter_event =false; + private boolean need_sentbytes_event = false; + private boolean need_receivedbytes_event = false; + + + private boolean inited = false; + private SerialParameters sp; + private ModbusMaster modbus; + private int sentcounter = 0; + private int receivedcounter = 0; + + private int devid = 1; // deviceid + private double fullscale = 5.0; // in meters + + /** + * Initialize HPT604 class + * @param callerobject : caller object + * @param eventname : eventname + */ + public void Initialize(BA bax, Object callerobject, String eventname) { + ba = bax; + caller = callerobject; + event = eventname; + if (ba instanceof BA) { + if (caller != null) { + if (event instanceof String) { + if (!event.isEmpty()) { + need_log_event = ba.subExists(event+"_log"); + need_txrxcounter_event = ba.subExists(event+"_txrxcounter"); + need_sentbytes_event = ba.subExists(event+"_sentbytes"); + need_receivedbytes_event = ba.subExists(event+"_receivedbytes"); + + } + } + } + } + + SerialUtils.setSerialPortFactoryJSerialComm(); // use jSerialComm for communication + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Close(); + } + }); + + inited = true; + } + + /** + * Check if already initialized + * @return true if inited + */ + public boolean Initialized() { + return inited; + } + + /** + * Get / Set sensor full scale in meters + * @return full scale + */ + public double getFullScaleMeter() { + return fullscale; + } + + /** + * Get / Set sensor full scale in meters + * @param value value in meters + */ + public void setFullScaleMeter(double value) { + if (value < 0) value = 5.0; + fullscale = value; + } + + /** + * Get / Set assigned Device ID + * @return device ID + */ + public int getDeviceID() { + return devid; + } + + /** + * Get / Set assigned Device ID + * @param value device ID + */ + public void setDeviceID(int value) { + if (value<0) value = 1; + if (value>247) value = 247; + devid = value; + } + + /** + * Open Modbus Connection to HPT604 + * Parameter will be set 9600 bps, 8 bit, 1 stop, parity NONE + * @param serialname : serial port name + * @return true if opened + */ + public boolean OpenSerial(String serialname) { + if ((sp instanceof SerialParameters)==false) { + sp = new SerialParameters(); + } + + sp.setDevice(serialname); + sp.setBaudRate(SerialPort.BaudRate.BAUD_RATE_9600); + + try { + modbus = ModbusMasterFactory.createModbusMasterRTU(sp); + modbus.addListener(modbus_event); + modbus.connect(); + sentcounter = 0; + receivedcounter = 0; + return modbus.isConnected(); + } catch (SerialPortException e) { + raise_log("Unable to open Serial "+serialname+", Msg : "+e.getMessage()); + } catch (ModbusIOException e) { + raise_log("Unable to modbus.connect, Msg : "+e.getMessage()); + } + return false; + } + + /** + * Close Modbus Connection + */ + public void Close() { + if (modbus instanceof ModbusMaster) { + try { + modbus.removeListeners(); + modbus.disconnect(); + } catch (ModbusIOException e) { + raise_log("Exception on Modbus.disconnect, Msg : "+e.getMessage()); + } + } + modbus = null; + } + + /** + * Check if Modbus RTU connected to HPT604 + * @return true if connected + */ + public boolean IsConnected() { + if (modbus instanceof ModbusMaster) { + return modbus.isConnected(); + } + return false; + } + + /** + * Read Sensor Serial Number + * @return -1 if failed to read + */ + public int Read_Sensor_SerialNumber() { + if (IsConnected()) { + try { + int[] result = modbus.readInputRegisters(devid, 3, 1); // function = 4, register address = 3, register length = 1 + if (result.length>0) { + return result[0]; + } + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("Unable to Read Sensor SerialNumber, Msg : "+e.getMessage()); + } + + } + return -1; + } + + /** + * Read WaterLevel Measurement + * Check returned data, on valid boolean , to verify + * @return HPT604Data object + */ + public HPT604Data Read_WaterLevel() { + if (IsConnected()) { + try { + int[] result = modbus.readHoldingRegisters(devid, 0, 1); // function = 3, register address = 0, register length = 1 + if (result.length>0) { + return new HPT604Data(result[0],fullscale); + } + } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) { + raise_log("Unable to Read WaterLevel, Msg : "+e.getMessage()); + } + + } + return new HPT604Data(); // just return random data + } + + private void raise_log(String msg) { + if (need_log_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_log", false, new Object[] {msg}); + } + + private void raise_txrxcounter() { + if (need_txrxcounter_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_txrxcounter", false, new Object[] {sentcounter,receivedcounter}); + } + + private void raise_sentbytes(byte[] bb) { + if (need_sentbytes_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_sentbytes", false, new Object[] {bb}); + } + + private void raise_receivedbytes(byte[] bb) { + if (need_receivedbytes_event) ba.raiseEventFromDifferentThread(Me, null, 0, event+"_receivedbytes", false, new Object[] {bb}); + } + + + + private FrameEventListener modbus_event = new FrameEventListener() { + + @Override + public void frameSentEvent(FrameEvent event) { + if (sentcounter0) { + for(int ii=0;ii0) { + for (int ii=0;ii