From 71bdc4afae2615f7e893446c4817123185e0e0b1 Mon Sep 17 00:00:00 2001 From: rdkartono Date: Wed, 4 Dec 2024 08:59:37 +0700 Subject: [PATCH] first commit --- lib/bananapi/dynamic/libpi4j.so | Bin 0 -> 66128 bytes lib/bananapi/static/libpi4j.so | Bin 0 -> 108108 bytes lib/bananapro/dynamic/libpi4j.so | Bin 0 -> 70280 bytes lib/bananapro/static/libpi4j.so | Bin 0 -> 113208 bytes lib/bpi/dynamic/libpi4j.so | Bin 0 -> 66116 bytes lib/bpi/static/libpi4j.so | Bin 0 -> 135576 bytes lib/hidapi/darwin/libhidapi.dylib | Bin 0 -> 58888 bytes lib/hidapi/linux-aarch64/libhidapi-libusb.so | Bin 0 -> 76048 bytes lib/hidapi/linux-aarch64/libhidapi.so | Bin 0 -> 49920 bytes lib/hidapi/linux-amd64/libhidapi-libusb.so | Bin 0 -> 76376 bytes lib/hidapi/linux-amd64/libhidapi.so | Bin 0 -> 50952 bytes lib/hidapi/linux-arm/libhidapi-libusb.so | Bin 0 -> 56240 bytes lib/hidapi/linux-arm/libhidapi.so | Bin 0 -> 37316 bytes lib/hidapi/linux-armel/libhidapi-libusb.so | Bin 0 -> 56240 bytes lib/hidapi/linux-armel/libhidapi.so | Bin 0 -> 37316 bytes lib/hidapi/linux-x86-64/libhidapi-libusb.so | Bin 0 -> 76376 bytes lib/hidapi/linux-x86-64/libhidapi.so | Bin 0 -> 50952 bytes lib/hidapi/linux-x86/libhidapi-libusb.so | Bin 0 -> 59328 bytes lib/hidapi/linux-x86/libhidapi.so | Bin 0 -> 36052 bytes lib/hidapi/win32-x86-64/hidapi.dll | Bin 0 -> 154400 bytes lib/hidapi/win32-x86/hidapi.dll | Bin 0 -> 118017 bytes lib/nanopi/dynamic/libpi4j.so | Bin 0 -> 70280 bytes lib/nanopi/static/libpi4j.so | Bin 0 -> 102152 bytes lib/odroid/dynamic/libpi4j.so | Bin 0 -> 70280 bytes lib/odroid/static/libpi4j.so | Bin 0 -> 109576 bytes lib/orangepi/dynamic/libpi4j.so | Bin 0 -> 70280 bytes lib/orangepi/static/libpi4j.so | Bin 0 -> 114244 bytes lib/raspberrypi/dynamic/libpi4j.so | Bin 0 -> 66116 bytes src/ModbusWrapper/jModbusCoil.java | 22 + src/ModbusWrapper/jModbusHoldingRegister.java | 22 + src/ModbusWrapper/jModbusMaster.java | 447 +++++ src/ModbusWrapper/jModbusSlave.java | 345 ++++ src/SBC/BasicSBCInfo.java | 1275 ++++++++++++++ src/SBC/NanoPi/Duo2.java | 126 ++ src/SBC/NanoPi/LibFriendlyArmHardware.java | 238 +++ src/SBC/NanoPi/Nanopi_IR_Receiver.java | 114 ++ src/SBC/NanoPi/Neo1_Pin.java | 69 + src/SBC/NanoPi/Neo2_Pin.java | 58 + src/SBC/NetworkMeterInfo.java | 309 ++++ src/SBC/Odroid/H2_Pin.java | 88 + src/SBC/Odroid/N2_Pin.java | 89 + src/SBC/Odroid/XU4_Pin.java | 125 ++ src/SBC/OrangePi/ZeroLTS.java | 50 + src/SBC/RAM_Info.java | 90 + src/SBC/RaspberryPi/Pi3_Pin.java | 263 +++ src/SBC/RockPi/RockPi4B.java | 128 ++ src/SBC/Storage_Info.java | 80 + src/SBC/cpudata.java | 109 ++ src/UVC/Aver_VC520.java | 34 + src/UVC/UVC_Control.java | 68 + src/customsocket/AckRequestWrapper.java | 98 ++ src/customsocket/JsonArray.java | 597 +++++++ src/customsocket/JsonObject.java | 726 ++++++++ src/customsocket/NettySocketWrapper.java | 386 +++++ src/customsocket/SimpleTCPSocket.java | 419 +++++ src/customsocket/SocketIOClientInterface.java | 48 + src/customsocket/SocketIOClientWrapper.java | 428 +++++ src/customsocket/SocketIOServerWrapper.java | 726 ++++++++ src/customsocket/TCPSocket.java | 817 +++++++++ src/customsocket/TCPSocketPrefixMode.java | 418 +++++ src/customsocket/TCPSocketServer.java | 380 +++++ src/customsocket/TcpSocketJavaEvent.java | 8 + src/customsocket/UDPSocket.java | 273 +++ src/devices/AT24C32.java | 219 +++ src/devices/Button.java | 215 +++ src/devices/Buzzer.java | 213 +++ src/devices/DHT/DHT_Data.java | 132 ++ src/devices/DHT/SensorDHT.java | 199 +++ src/devices/FA/Simplex4100/Simplex4100.java | 197 +++ .../FA/Simplex4100/Simplex4100Data.java | 214 +++ src/devices/IntPointer.java | 16 + src/devices/JSecureDongleV2.java | 574 +++++++ src/devices/Kernel32.java | 118 ++ src/devices/LCDDotMatrix/LCD.java | 574 +++++++ src/devices/LCDDotMatrix/LCDString.java | 164 ++ src/devices/LCDDotMatrix/LCDStringEvent.java | 5 + src/devices/LCDSPI/LcdSpi.java | 451 +++++ src/devices/LEDMax7219/Graphic_Max7219.java | 109 ++ src/devices/LEDMax7219/LEDBlock.java | 58 + src/devices/LEDMax7219/LEDMax7219.java | 291 ++++ src/devices/Oled/SSD1306/Font12X16.java | 114 ++ src/devices/Oled/SSD1306/Font16X16.java | 112 ++ src/devices/Oled/SSD1306/Font5X7.java | 112 ++ src/devices/Oled/SSD1306/Font6X8.java | 111 ++ src/devices/Oled/SSD1306/Font8X12.java | 112 ++ src/devices/Oled/SSD1306/Font8X16.java | 114 ++ src/devices/Oled/SSD1306/FontPack.java | 134 ++ .../Oled/SSD1306/Font_Arial_16X16.java | 114 ++ .../Oled/SSD1306/Font_Arial_16X24.java | 113 ++ .../Oled/SSD1306/Font_Dingbats_32X24.java | 211 +++ .../SSD1306/Font_FranklinGothic_16X16.java | 112 ++ .../Oled/SSD1306/Font_Grotesk_16X32.java | 113 ++ .../Oled/SSD1306/Font_Helvetica_16X16.java | 112 ++ .../Oled/SSD1306/Font_Retro_16X32.java | 116 ++ .../Oled/SSD1306/Font_Symbol_16X16.java | 211 +++ .../Oled/SSD1306/Font_Symbol_16X32.java | 210 +++ .../Oled/SSD1306/Font_Symbol_32X32.java | 220 +++ .../Oled/SSD1306/Font_Ubuntu_24X32.java | 112 ++ src/devices/Oled/SSD1306/OLEDDisplay.java | 535 ++++++ src/devices/PCF8574.java | 151 ++ src/devices/PZEM004/PZEM004.java | 243 +++ src/devices/PZEM004/PZEM004T.java | 513 ++++++ .../PZEM004/PZEM004T_MeasurenmentData.java | 161 ++ .../PZEM004/PZEM004_MeasurementData.java | 12 + src/devices/PublicIPChecker.java | 115 ++ src/devices/RTC_I2C/DS1307.java | 301 ++++ src/devices/RTC_I2C/DS3231.java | 356 ++++ src/devices/RTC_I2C/RTC_Data.java | 467 +++++ src/devices/RTC_I2C/RTC_Data_Event.java | 5 + src/devices/Relay.java | 136 ++ src/devices/ShortPointer.java | 24 + src/devices/WindowsBattery.java | 71 + .../ups/ica/sin2100c/MegatecProtocol.java | 460 +++++ .../ups/ica/sin2100c/Megatec_Data.java | 440 +++++ .../ups/ica/sin2100c/Megatec_Data_Event.java | 6 + src/devices/ups/ica/sin2100c/SIN2100C.java | 286 ++++ .../ups/ica/sin2100c/SIN2100C_Data.java | 264 +++ src/gps/AdzanCalculator.java | 131 ++ src/gps/GpsGPGGA.java | 163 ++ src/gps/GpsGPGLL.java | 121 ++ src/gps/GpsGPGSA.java | 98 ++ src/gps/GpsGPRMC.java | 194 +++ src/gps/GpsGPVTG.java | 83 + src/gps/GpsReceiver.java | 489 ++++++ src/gps/PrayTime.java | 939 ++++++++++ src/hid/HidDevice.java | 202 +++ src/hid/HidDeviceInfo.java | 212 +++ src/hid/jHID.java | 126 ++ src/infrared/InfraredReceiver.java | 201 +++ src/jGPIO/CRC16.java | 245 +++ src/jGPIO/DigitalInput/DigitalInput.java | 285 ++++ src/jGPIO/DigitalInput/DigitalInputEvent.java | 6 + src/jGPIO/DigitalOutput/DigitalOutput.java | 227 +++ .../DigitalOutput/DigitalOutputEvent.java | 6 + src/jGPIO/I2C/I2CException.java | 49 + src/jGPIO/I2C/I2C_BUS.java | 127 ++ src/jGPIO/I2C/I2C_BUS_Event.java | 5 + src/jGPIO/I2C/I2C_Device.java | 322 ++++ src/jGPIO/I2C/I2C_Device_Event.java | 5 + src/jGPIO/I2C/USB_I2C.java | 305 ++++ src/jGPIO/Linux_C_lib.java | 71 + src/jGPIO/Linux_C_lib_DirectMapping.java | 56 + src/jGPIO/ModbusCRC.java | 78 + src/jGPIO/SPI/SPI_BUS.java | 185 ++ src/jGPIO/SPI/SPI_BUS_Event.java | 5 + src/jGPIO/SPI/SPI_BitBang.java | 139 ++ src/jGPIO/SerialPort/SerialComm.java | 504 ++++++ src/jGPIO/SerialPort/jSerialPort_Event.java | 6 + src/jGPIO/input_event.java | 74 + src/jGPIO/jGPIO.java | 361 ++++ src/jGPIO/mycodes.java | 722 ++++++++ src/jGPIO/timeval.java | 65 + src/linux-arm/libSecureDongle.so | Bin 0 -> 66169 bytes src/linux-arm/libfahw.so | Bin 0 -> 41268 bytes src/linux-x86-64/libSecureDongle.so | Bin 0 -> 116259 bytes src/linux-x86/libSecureDongle.so | Bin 0 -> 133062 bytes src/network/jUDPSocket.java | 336 ++++ src/ptzcamera/CamFunctions.java | 61 + src/ptzcamera/PTZComPort.java | 404 +++++ src/ptzcamera/PelcoD.java | 711 ++++++++ src/ptzcamera/PelcoP.java | 708 ++++++++ src/ptzcamera/ViscaOverIP.java | 1510 +++++++++++++++++ src/ptzcamera/ViscaPTZ.java | 1453 ++++++++++++++++ src/rfidreader/ACR122Device.java | 197 +++ src/rfidreader/HexUtils.java | 49 + src/rfidreader/MifareUtils.java | 227 +++ src/rfidreader/RFIDCardInfo.java | 376 ++++ src/syslog/DWM312_Syslog_Consumer.java | 390 +++++ src/syslog/IMSI_Data.java | 100 ++ src/syslog/SyslogEvent.java | 5 + src/syslog/SyslogServer.java | 204 +++ src/toaprotocol/TS910.java | 1476 ++++++++++++++++ src/waterlevelsensor/HPT604Data.java | 61 + .../Holykell_HPT604_Modbus_RTU.java | 259 +++ src/waterlevelsensor/Holykell_UE3003.java | 202 +++ src/waterlevelsensor/UE3003Data.java | 63 + src/win32-x86-64/SecureDongle.dll | Bin 0 -> 159168 bytes src/win32-x86/SecureDongle.dll | Bin 0 -> 106432 bytes 178 files changed, 35820 insertions(+) create mode 100644 lib/bananapi/dynamic/libpi4j.so create mode 100644 lib/bananapi/static/libpi4j.so create mode 100644 lib/bananapro/dynamic/libpi4j.so create mode 100644 lib/bananapro/static/libpi4j.so create mode 100644 lib/bpi/dynamic/libpi4j.so create mode 100644 lib/bpi/static/libpi4j.so create mode 100644 lib/hidapi/darwin/libhidapi.dylib create mode 100644 lib/hidapi/linux-aarch64/libhidapi-libusb.so create mode 100644 lib/hidapi/linux-aarch64/libhidapi.so create mode 100644 lib/hidapi/linux-amd64/libhidapi-libusb.so create mode 100644 lib/hidapi/linux-amd64/libhidapi.so create mode 100644 lib/hidapi/linux-arm/libhidapi-libusb.so create mode 100644 lib/hidapi/linux-arm/libhidapi.so create mode 100644 lib/hidapi/linux-armel/libhidapi-libusb.so create mode 100644 lib/hidapi/linux-armel/libhidapi.so create mode 100644 lib/hidapi/linux-x86-64/libhidapi-libusb.so create mode 100644 lib/hidapi/linux-x86-64/libhidapi.so create mode 100644 lib/hidapi/linux-x86/libhidapi-libusb.so create mode 100644 lib/hidapi/linux-x86/libhidapi.so create mode 100644 lib/hidapi/win32-x86-64/hidapi.dll create mode 100644 lib/hidapi/win32-x86/hidapi.dll create mode 100644 lib/nanopi/dynamic/libpi4j.so create mode 100644 lib/nanopi/static/libpi4j.so create mode 100644 lib/odroid/dynamic/libpi4j.so create mode 100644 lib/odroid/static/libpi4j.so create mode 100644 lib/orangepi/dynamic/libpi4j.so create mode 100644 lib/orangepi/static/libpi4j.so create mode 100644 lib/raspberrypi/dynamic/libpi4j.so create mode 100644 src/ModbusWrapper/jModbusCoil.java create mode 100644 src/ModbusWrapper/jModbusHoldingRegister.java create mode 100644 src/ModbusWrapper/jModbusMaster.java create mode 100644 src/ModbusWrapper/jModbusSlave.java create mode 100644 src/SBC/BasicSBCInfo.java create mode 100644 src/SBC/NanoPi/Duo2.java create mode 100644 src/SBC/NanoPi/LibFriendlyArmHardware.java create mode 100644 src/SBC/NanoPi/Nanopi_IR_Receiver.java create mode 100644 src/SBC/NanoPi/Neo1_Pin.java create mode 100644 src/SBC/NanoPi/Neo2_Pin.java create mode 100644 src/SBC/NetworkMeterInfo.java create mode 100644 src/SBC/Odroid/H2_Pin.java create mode 100644 src/SBC/Odroid/N2_Pin.java create mode 100644 src/SBC/Odroid/XU4_Pin.java create mode 100644 src/SBC/OrangePi/ZeroLTS.java create mode 100644 src/SBC/RAM_Info.java create mode 100644 src/SBC/RaspberryPi/Pi3_Pin.java create mode 100644 src/SBC/RockPi/RockPi4B.java create mode 100644 src/SBC/Storage_Info.java create mode 100644 src/SBC/cpudata.java create mode 100644 src/UVC/Aver_VC520.java create mode 100644 src/UVC/UVC_Control.java create mode 100644 src/customsocket/AckRequestWrapper.java create mode 100644 src/customsocket/JsonArray.java create mode 100644 src/customsocket/JsonObject.java create mode 100644 src/customsocket/NettySocketWrapper.java create mode 100644 src/customsocket/SimpleTCPSocket.java create mode 100644 src/customsocket/SocketIOClientInterface.java create mode 100644 src/customsocket/SocketIOClientWrapper.java create mode 100644 src/customsocket/SocketIOServerWrapper.java create mode 100644 src/customsocket/TCPSocket.java create mode 100644 src/customsocket/TCPSocketPrefixMode.java create mode 100644 src/customsocket/TCPSocketServer.java create mode 100644 src/customsocket/TcpSocketJavaEvent.java create mode 100644 src/customsocket/UDPSocket.java create mode 100644 src/devices/AT24C32.java create mode 100644 src/devices/Button.java create mode 100644 src/devices/Buzzer.java create mode 100644 src/devices/DHT/DHT_Data.java create mode 100644 src/devices/DHT/SensorDHT.java create mode 100644 src/devices/FA/Simplex4100/Simplex4100.java create mode 100644 src/devices/FA/Simplex4100/Simplex4100Data.java create mode 100644 src/devices/IntPointer.java create mode 100644 src/devices/JSecureDongleV2.java create mode 100644 src/devices/Kernel32.java create mode 100644 src/devices/LCDDotMatrix/LCD.java create mode 100644 src/devices/LCDDotMatrix/LCDString.java create mode 100644 src/devices/LCDDotMatrix/LCDStringEvent.java create mode 100644 src/devices/LCDSPI/LcdSpi.java create mode 100644 src/devices/LEDMax7219/Graphic_Max7219.java create mode 100644 src/devices/LEDMax7219/LEDBlock.java create mode 100644 src/devices/LEDMax7219/LEDMax7219.java create mode 100644 src/devices/Oled/SSD1306/Font12X16.java create mode 100644 src/devices/Oled/SSD1306/Font16X16.java create mode 100644 src/devices/Oled/SSD1306/Font5X7.java create mode 100644 src/devices/Oled/SSD1306/Font6X8.java create mode 100644 src/devices/Oled/SSD1306/Font8X12.java create mode 100644 src/devices/Oled/SSD1306/Font8X16.java create mode 100644 src/devices/Oled/SSD1306/FontPack.java create mode 100644 src/devices/Oled/SSD1306/Font_Arial_16X16.java create mode 100644 src/devices/Oled/SSD1306/Font_Arial_16X24.java create mode 100644 src/devices/Oled/SSD1306/Font_Dingbats_32X24.java create mode 100644 src/devices/Oled/SSD1306/Font_FranklinGothic_16X16.java create mode 100644 src/devices/Oled/SSD1306/Font_Grotesk_16X32.java create mode 100644 src/devices/Oled/SSD1306/Font_Helvetica_16X16.java create mode 100644 src/devices/Oled/SSD1306/Font_Retro_16X32.java create mode 100644 src/devices/Oled/SSD1306/Font_Symbol_16X16.java create mode 100644 src/devices/Oled/SSD1306/Font_Symbol_16X32.java create mode 100644 src/devices/Oled/SSD1306/Font_Symbol_32X32.java create mode 100644 src/devices/Oled/SSD1306/Font_Ubuntu_24X32.java create mode 100644 src/devices/Oled/SSD1306/OLEDDisplay.java create mode 100644 src/devices/PCF8574.java create mode 100644 src/devices/PZEM004/PZEM004.java create mode 100644 src/devices/PZEM004/PZEM004T.java create mode 100644 src/devices/PZEM004/PZEM004T_MeasurenmentData.java create mode 100644 src/devices/PZEM004/PZEM004_MeasurementData.java create mode 100644 src/devices/PublicIPChecker.java create mode 100644 src/devices/RTC_I2C/DS1307.java create mode 100644 src/devices/RTC_I2C/DS3231.java create mode 100644 src/devices/RTC_I2C/RTC_Data.java create mode 100644 src/devices/RTC_I2C/RTC_Data_Event.java create mode 100644 src/devices/Relay.java create mode 100644 src/devices/ShortPointer.java create mode 100644 src/devices/WindowsBattery.java create mode 100644 src/devices/ups/ica/sin2100c/MegatecProtocol.java create mode 100644 src/devices/ups/ica/sin2100c/Megatec_Data.java create mode 100644 src/devices/ups/ica/sin2100c/Megatec_Data_Event.java create mode 100644 src/devices/ups/ica/sin2100c/SIN2100C.java create mode 100644 src/devices/ups/ica/sin2100c/SIN2100C_Data.java create mode 100644 src/gps/AdzanCalculator.java create mode 100644 src/gps/GpsGPGGA.java create mode 100644 src/gps/GpsGPGLL.java create mode 100644 src/gps/GpsGPGSA.java create mode 100644 src/gps/GpsGPRMC.java create mode 100644 src/gps/GpsGPVTG.java create mode 100644 src/gps/GpsReceiver.java create mode 100644 src/gps/PrayTime.java create mode 100644 src/hid/HidDevice.java create mode 100644 src/hid/HidDeviceInfo.java create mode 100644 src/hid/jHID.java create mode 100644 src/infrared/InfraredReceiver.java create mode 100644 src/jGPIO/CRC16.java create mode 100644 src/jGPIO/DigitalInput/DigitalInput.java create mode 100644 src/jGPIO/DigitalInput/DigitalInputEvent.java create mode 100644 src/jGPIO/DigitalOutput/DigitalOutput.java create mode 100644 src/jGPIO/DigitalOutput/DigitalOutputEvent.java create mode 100644 src/jGPIO/I2C/I2CException.java create mode 100644 src/jGPIO/I2C/I2C_BUS.java create mode 100644 src/jGPIO/I2C/I2C_BUS_Event.java create mode 100644 src/jGPIO/I2C/I2C_Device.java create mode 100644 src/jGPIO/I2C/I2C_Device_Event.java create mode 100644 src/jGPIO/I2C/USB_I2C.java create mode 100644 src/jGPIO/Linux_C_lib.java create mode 100644 src/jGPIO/Linux_C_lib_DirectMapping.java create mode 100644 src/jGPIO/ModbusCRC.java create mode 100644 src/jGPIO/SPI/SPI_BUS.java create mode 100644 src/jGPIO/SPI/SPI_BUS_Event.java create mode 100644 src/jGPIO/SPI/SPI_BitBang.java create mode 100644 src/jGPIO/SerialPort/SerialComm.java create mode 100644 src/jGPIO/SerialPort/jSerialPort_Event.java create mode 100644 src/jGPIO/input_event.java create mode 100644 src/jGPIO/jGPIO.java create mode 100644 src/jGPIO/mycodes.java create mode 100644 src/jGPIO/timeval.java create mode 100644 src/linux-arm/libSecureDongle.so create mode 100644 src/linux-arm/libfahw.so create mode 100644 src/linux-x86-64/libSecureDongle.so create mode 100644 src/linux-x86/libSecureDongle.so create mode 100644 src/network/jUDPSocket.java create mode 100644 src/ptzcamera/CamFunctions.java create mode 100644 src/ptzcamera/PTZComPort.java create mode 100644 src/ptzcamera/PelcoD.java create mode 100644 src/ptzcamera/PelcoP.java create mode 100644 src/ptzcamera/ViscaOverIP.java create mode 100644 src/ptzcamera/ViscaPTZ.java create mode 100644 src/rfidreader/ACR122Device.java create mode 100644 src/rfidreader/HexUtils.java create mode 100644 src/rfidreader/MifareUtils.java create mode 100644 src/rfidreader/RFIDCardInfo.java create mode 100644 src/syslog/DWM312_Syslog_Consumer.java create mode 100644 src/syslog/IMSI_Data.java create mode 100644 src/syslog/SyslogEvent.java create mode 100644 src/syslog/SyslogServer.java create mode 100644 src/toaprotocol/TS910.java create mode 100644 src/waterlevelsensor/HPT604Data.java create mode 100644 src/waterlevelsensor/Holykell_HPT604_Modbus_RTU.java create mode 100644 src/waterlevelsensor/Holykell_UE3003.java create mode 100644 src/waterlevelsensor/UE3003Data.java create mode 100644 src/win32-x86-64/SecureDongle.dll create mode 100644 src/win32-x86/SecureDongle.dll diff --git a/lib/bananapi/dynamic/libpi4j.so b/lib/bananapi/dynamic/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..509c84e537e7bd2cf763f5c2c2e6ad6c4e9ce4dd GIT binary patch literal 66128 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⁢^^qq>&Y!n)q=bD2Zzr$PyW>BL>{B;-7y>)K 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 literal 0 HcmV?d00001 diff --git a/lib/bananapro/dynamic/libpi4j.so b/lib/bananapro/dynamic/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..91dda23b64ef0c909ae0a690cd9c27904babe308 GIT binary patch literal 70280 zcmeFa4Rlq-)&GBP5+DSSi$sk|>rt@=MY+7FsI*?*K@lRvii##72_!Wnw@DCG9vfb& zs2~v$se;^EY;B8*EmpKxqoPu^id9=wv^NR2AR=OYDOOv4pYM6eog5ON|FxdA{_9yD z*05*q*|TTQo;|N;ZqAQJjvEyUg)IL`w{k6EITu^jAYk4RU>R12L6&WGB<*3px}Z|LDs1uD(3+*1rb-ytZ%o zFP|svdP0=Yg7jY~-8ys({VFXw!m@44?vX~5gqD%DW7MJnc}{K&g=2HAv<@qTS$Wn7D|-B&Gy9xq zBf>I|x$zE|0bxcsD_6-{aBl1+F5jCd3IpC9EMlK=?V~ zLBcNyzar@QwF|7Z9`wOOL>?xr^9YXue@j?Tc#QBng8w}3MK%C`@5MI)pCU96o+CU@ z_#@#J!fS*-6W%0jCA>q}M)(^+PlC{-i1%yBTz(yAZk&x)XX3jwJLXwD)9_b~ND_ z!tsP&gl`ePO|S_k5>6tVLO7L>OXx$;a~kk;LSMoegfj_x`T-+eyuSxUA3%H{VG!Xg z!a0P&g!2e`h5#=h3?&RFj3kU9Ttv8-FrF}hFp-c?xRmgH!sUc1gsTX8t_BtmiadNO zu$WLvm`*4o46pdbO&7d8;pT@=KK|AVu0Jbp^MkQ}ethFI#pi!8YstOc6Kk8YU;WKP zujgF%_vb$8{^gQwKl$~nmtT0`t$Qzc|Iyptd;iDb)5bns_teQF^OrAP``bT1vG|iC zIvqdum#=+s|KCOq>DzGY(GPEn{Qk%JuV?=5;ST#>T=Uu`oo=k@v-95%Ow3$!)2=7Z ze;{|*lmE;<=7c>ZzkF}#nMd9-xTM>^%DW}jzT2xRYt?y=&0R5d?$8ar)>u_%cRIW0 z4}SgL;;fU7P7B>X?Y+0|>G|H^E>}Fab!6_B+h2WY|2eO`+W6tO3%x-Vu<7J^x6c6%e68Tq2{+cgwf%*Q(RbXL)p@V}=CO@8cK%!J+`pbX|4*M+?t5w8 zSz}Jw(|y2ALvJYgxO>xP}yS-4IlZ|?w)_UVaf6r`>vgQX37QoUli3p`@4TkJMri~$sM09I(6KoW0&kMsoZtpvlrJK z``Ny?y1&)9C$aUhBcJ)jWyjXMec~D0jw~6qbM9Gldj90yvgl7Pto`Mq*_UQC-GuDR zKfe3ATYw6;5nj3ehIa)QdtvLOOXvUVh-3CF0l%GaN71Y$cL4uZ_2chMe{lTClNSH; z-qPMxPKdDf3-2wuJM%KpCw z{)+G%{ov^6X-6FS=+yoXf!Dn@Yus93|7~e&9tQR~^_s6A0X|Bw9vQTA9dPDl)-h)< z{_MP;PI;xzHJx6&VCIoaPTJVL!ykuy`p^4U_4;yh_Wd>AiH*IZN-Gu_ob}LuZ|8qd!8G-h> zF(B`(0Doh^-njvL-xtttSfKuY2ydJW^(0wevc zuIxQy>}$OI(a$uD0K|LxR&%nZw-SOJfrPycZB=$}o|Fg7TKJxwYq=&zLIg<6u9?}a*_wx@t4*hRq?@7H>-c0#7 z_Vx9Dn{v13rR+12^nFHs$Iw6P7N8S)DF1KBK4ep>J&u=uP(Q!CnV|LK-MyLkZy=%e zIiEk>hNts$>J z?V0l<_S~L5yEDGcf!Wt@OfS}0cc<$6dlI%)xaUkN?<>ZWE!U;=dz|*oH`@0`?9uyX z0Q=no^Xhi}gn$lHzo zjPcT6CjVq3|1YR-U+TNYOTU?P%Sit-c%gysrN4}uO`q`ie-L6k!rwQ5%byp+pK0*F z4PIv8tMQL@*Pw%!KS6rTNWT+2Y~VYx}f(;I@IEiea{0 zm-3GXNY6FWKL=lE;J0Too*MmS8;(^UOSQ+>@YfssE7Pgpm8teP7XK_W#;0GyA2Imn zVUNsf+%r7+-=m}tHqv)v-(^2>&(xIuIO$O%eJkVZWY7Ez(0Ne?}5K;#y{2G(&uK%o1K@M zUtWiCSwTwP2P%m6>Fx2)q`ek^(+p1jVfb%7-LS{LS3*`D>8j7qu-^j1eh>Ac zJp4^hnDgdEjK{qhk4wO%&&%|$^<}B~XmJkuQeVIPY4GP5^HT@p^}ZXKUj92W@TaR& z`lr+0D-3)0M!zWYtIwZBdfZ4qR^>C^ETUf0=MR)W8+*|$9lQtp4;uWhcSk?`(Ql6u z=C61?b@TG)z`xGmKLLD$fqw_Q!NAV|-)!K6z_%Fq`QTd(d>DAbz(1pYxfQAP$k`}X z2kz@X0YA$${QD8?xzMoZ?bNfbW&S(Lnu~v~F#PGp?u=j5cZz3^e%im@;GS#CUN_-y zuNdQD1LbAF?2m69vCn6>QYSBeIr5g#7-KyAOPWw#4F?WL3diYev_aXQzW^&U1MSbH&eUHLE`G&ndM&HTkyU^pm5W{8T zzrOsj2-<1b`yCSYzV7?;QC1%5dyVwV!1o#W)!4p9`LA;QLsQ?3IKU4kOZkk{Lje<$PNzPTy+>uHbF_;QprA&0(f=-Zq2Tub|xdHFkIuR`oK z*~1$G`DcC1^kjr1z)y}c+keyt*XjFG+uJkP)%ML*jZ&qvbUCA4?glm9vE ztpxV-%lkEv$wvPEwCCVSDf|3}^g<)Oiu%y6>@6o>fpVeMVJfK}~H@b!|a`RZw18UTYOh z16T!P^DizaDXlJ@US3mMTAhFK@QSL+()^;S6{T)k+bIRbvx_94sG|IO7%#lGV4^2s zctuf7O=*o)L|WB!ii*vwEui%B+N$b;Sb1gJ3B^Sf6;q3fuPG>2_CxUe;My67;FwWb zTUJ$KU08H&Q9*Il49Xq!gMuGamKO|lv>Gd=F7|XPtyE?6$}7KLR57!3cv(^9^wQy; zI##T>ju@gqlE-sDLmRD7}gi9-H zX2xPw)wQK16Jn*+MM_DrOfRh+dTmj8g|r`bU2W;`s+pCwR(Vx%ZABY3pHy01UQ|&~ z6)UZ@rWIGhQ(KILqT1SOt9aUsqHB;pi&#x*ZFx`AI)i{^18JU->5;qWrN3Rd4u+_LCVcT>ovKrL?%Ls{Pbi)#VHl z?ONAK9yW8@w9@MKnVjLKVC+~+kwdZ_oFTu;D-FA~bj}eBSK~@6r`MKQ-mvVt@7PMl z|LU2sTB|fxRZ-y&c^*O!bz`%Nm^8=2de9L!)0#cd~f zL-2uhx@<(gJ2bYP)ZS>NY@U{F_=QsOgZ%OtrHno8Fwnsd&cF;lJ&<=+Iiu_JSb4$d zSb0@}i{+J1Dy^Lvvs%)$7X$4E6)~?&Sz!~Kg73BwS+!^J?)l~;)DN#8WJA9?ZRh1u|Icy$AME^puJeC?FPodMznihYo3p>0wZEIU|JiN^S4MwV zNPkz#09VQYuXI<+09VQYSIPia$^ci&09VQYSIPia%0O4jKv&8@uM)15fv%K+u9ShU zl!304fv%K+u9QKpltHePL9UcRo-JG{gIpBht_TxD<4+H`fXzAwFebAD^|{KA;0R7Lr#oQ=I}Vpey_#zLCdG_Gq@glcyib# zYCCfn;@I{TUvp4CriQ$tk`dKRrU&KITH}BrkuEc>yrQ&VTzTcp*`pM5mgoUN2XfWa zR=Z1$*2Bqxj3wpOrNy;lCk)RYcSxZ#W)#H^%`~$Twh@k4YledyGLNCeq}uA4#kDi5 z+qIRuBq*qHmQd`cg8PaV*KEZ|Gaax|X`6GDvm*#;te=RxOQ?35 zFu;4T**m~`&>6ffUXFGKw`c8k_?d?eU+@1yzN!g(`{KtHmlRYKvj=CF(vAim%b2Pe z?RHL&Wq3tt`~AY>$*ZaVa zZDet^J7}))r8TT0*lrWKuUSkv6|X!NS#RN7f;u>h?sE~+Ul zp3&}bp*mbzTV7G%Ds4^kPMfFAI@C7a$u-lfY;y*HRpY7Zn$m@|vWbVX-9# zPT|^2ZEz@NYJK?3NS1|1IG%Bs(*L^*j-O2OUpO36d)V2T;6u|(ONKhunOOS6nXKO1 z9WPs~f~^cwC|aacdS)$xFP z8qk{7=F6#;0aI$V$oEb&DRL5TI)X2!9I^gEW=ZNTR4aPh2b}E`=baw5Q(kM;jN{F= z61>CBit?#$S$XAW)>K&rI->MiQFx`ojs|)$*qLWr1*NJ9?{KEpFskW|r5V@TODBal zA5PqZ0BiKPvBQQJ3^;R;`=(SsedUF+_rFOKRgatMbNEy@&HrCA{1i9tW3GhOG&jwp z1#_q5wvPEy1^u|sA=nnpPj~a^x6$AH`@d{~pK%XCcOLX)mzVR_Kh4?!O^<#Le+)3w zgE8)d2_Nxn$U5pX?i&d1x2$o3|K`4lAV1vA7v#RrWWg`FHz3IUi9&}SvPuN`L2Q}e zbKH3le2x1cg70#NLhvc>g9yIFy#&Ec-2V`Kfjb?7Teu%1_!sU~2)@kyL&32jYnkA1 z?otXyL)J<`E8E8wM~#83MT}==3a;3Ke<09_zL$@1i5>% zSMWvd#R>kI`%Hpw^S1y5-^1|O|ERlZ7r~9Ri{NJNdGD=PR9?JxMfkTphd zIqfg_L)u@EA7JMT+97MQAU~|0BFGP@3kA7AULtrZ?Jvj=uqy=LrydO<>*#x_hamT@ zvIV(UlOxFelU{-wEXx+;zEN+%B=^_^@wdK$TyTj9a$jbUAXile3vw}Lh#(#w6 z1i5E3Mv(hf;{@ppd4k-h$rt3F)MUYT@MA&l2^9)*ai~O4_ksnvP*WjD2Z;%C-=|ja z3H(^_4*Xc~So~PSJQF__?1vu<{vJOTjNr$D{qbYL0r;`tK>S!xGwCzHv+!fVv+-lW zb7+6T!L+~NxwOCFd9=UaUfN&qe9N*0htU3laoS(-yR^UH_h^5?+h~8m3uu4ADD5ve zC1gbfhtmFn!)SlO;k3Wt2-;t8B<(LaiuM;AP5TRuq5TEN(*A-M(*A-M(f)$B(Efts zXn(^>WJZ zF_^S4b9H)TsiPCcXXQdmw^r{s?LYfJ+`NDPhrPfxZm0&{@yk31@te zcH0>rW#rW-Ly>s16^%D$Z@IY1j?^VPSn$YqFXH9T%Xlx&yE{5 z?CE&Jf{)mi?8>y3C2yl1;-3M3#~YS?lyS|nkLt5J>`C6XVbACr2Jd2LIeW|ErnK9h+P!zlQ#(7}w&0^h)NxS*f%>M`FW=G0 zjyGn8z&SaRNx?A==$qo@{*r^k??`o|| zS~mH@$ZDuhTDL9!XwcG?$#XqkE3!11hKy`4{~OqC2sSIE9dfYSU_uPLN?v`!U30O$ zd2%88riJ2-Y2ms=*XVsa=Hg4Ch6M>bZzVcj+RTcKU0A$1M%!lJu=*qU$AC!uw;_AM zj?rD#?->`xhw@e@Gb4+VvQvh=DA`qdhvUtoyQ~|Z(Phy`84dUC=wRQ6A1z8WVRKFe z9C@s2J?OS;Z`yefde))y7~tfvd}d;^{GF}glk!*jM%vJM^rJg=EEqaJ;kOAkdM84< z=xL!lcXXmnEqud{EJ&(6`KIdO!i4%*XRAa#ZCMrx@3;uK2~1yDfn+jWL0t}diw1rU0Zna@|xK4Wr?Q)e&F*g9O$<3dPiU6 zBg?YpC)v(Aym8x;*KlWY1w2k(`dFI1B(dp|mC3WveL)~^p|$#`u>1s{j8NAy%k6LP z{~J4C=Y9CYWRLc?NBb1o7?1X*N5e;VslTePzUI+BfR=%+hcK z`Brae8wgVfpAq&B$JgrPjf?8&4>tZ$wR6nr7 zOOxsLiXDpv-a(&P>hvkuX7Bu`cCMe1YIoH~b#eMeG~O7^V@xZIH+INl++dvOK-{9= zs}1;ji;XR98_sL`r}{H#9c?#lFgnE;vov;>VFSsOEcuV*T2WW7WSMe0&~EL?@ynIm z*Z;kLSLA;|?(dPin?1GmV``fWdvQ}Z%)CYWhpbq0hPCP_*)C)6s_nJH;D!M(ID%#f)L`=GsYjCh41v9j$ny zd|7r~an$PMc&|L!iKCXRGLYyJfLX&k@t>-`^Ibj0P& z{oy+iU%II4<;^y6w%0E$hu#1mM{Dm~)a~+Sjuf+&1H0A7)8Rd?GdfDfC0AU=qv&drl|HS6BPnOiL$7eWx*o8TFaV)Tl zBZOTyS(i6+6q@xkI&CJ-wm<6&>DT@8=Km014$MT4_wc7NaqPsnC7Wc~@x}$&tCQD6 z{*-*()4_@+etQFB;bg{?U-r8tQNp|sI`6sWNJL?N^Rrf6LV8a|r-41J4<%y+GNgZ@ zwfK4F%QqyuFtYfqjh@VZBTsxe@G0$g9{p?Rh4iTs>b$gb=S0Z5yzvF}tv{~Qp3lDpOo#2=>ZqSz;g`h={@n#0s7tTFHVjjy(_lvbpCVARwCY9(h#TJHD9`I)D_;76Mssc)bOWdC3RMR zSrA`*Bu6e;vgy_%U7Hq>wg5SzbwrO%9(@7%uOQ8r_u*IjKU|x(Iyu>({T-Ux<@&!d ze%<`k&a@%VHHW-ep+g-cf_M*%z5$D2n0y8wp)AE1me=z+dX z&YF_`oJBcD&cA@$GB@C=2wH+;gymjNw3ns9v7*U7cf{N~5tO+y#oo~A9=l6DdP zPa9@c0!IP|0Fi%O2%Ww~dL6Pi_Bc?d51{)xZO0yI@Xmy`tPS4x4c@om?Fw%UK41Sg zWzT^AQ^}LphOCzjSuc?97G(A9@A`2TeRl7E_Gk5gwj4k2PCdF)j}QL?PQ5yizLWIM zq#pr23B4<@8MwQCb+QrI3H)!sG~j!{4B)#!#?$7vft`VW1vW4q`}(gzj}GY1I(OF~ z^m~vrwaWs=MvciD$5uhtIH^1<;ID+Y541Y*l2`oyfYuvY3|g8;`v}?z&1iQXy6*p1O6KeB$UM)G zd6pq_jVCh{kh#l{xv?FYrx-GAL*_zHrp6Of&$kSjzi3Bh*pS)9kQwu2rehaV=KF@s zrR~W43c7F4FW~pvrwp0eON70)k9lq1k#RcRX8xkBv&pOW5-o-wIBR#-1X0qud1~&C)g1^&$tTsu znfBtQOl0o$>YIiCeE%S2{=q2o38TzoC{OPa&C^& zH@q_Cv*#kWulhj1XYY5)bA5Id{JxF-@+!POmKO2ak9tYI>_eH!C&}Zk^H_(np0L>u z()XHtImaVESLFu!-U8@;-}{lHXT13n#tMxI_94oYo@SYGbTP~P59w`{Io&98YM{)m zyhoBv+pA|?u*|g9Wj3}`rp5uko+ATg)*qtG;y{^pM(Z*+wNvI9Mwz*RGRGXE%u#_d zoi|`k_n=@JeWk%-r)Ot<*UuP0puVr|; z;%`}T#+AKupV}#3qR%IX1p3y?&^3N_BL55M=ktFby5^4|b*cFy6P~A`>&!>|Ps0B$ z{b6vFIq!SSh14M(J+&V!(|8x7&%&D+z|3Qxp9!Ek8G$iW6aS#ptUteUk}}Hf?>}I?mFN zJ4bej(yn!k*YIlmAHqH))5>eK>f(=fiY#pE#9k+T%f$&!h-l-*P4$s3$@F;qnatSy zhFc0RdN!07e=dVPUoZ9wx7b%EL-tGCJ6TsW$~QXLtcj?D>fa6doKQA%wvg3Cd zT3})HB4-^Xo{P|3?XPo(QPRgdgOD#Z&~DE8L)4XLZ@H*RXMy684=7IykNuNmedKud zy^A?3JGHot{Ei?T-2m7Mmnsi>ylg>?UJ==Ukx5VeRlvr^t^Tzumlrve8}rd|@HF($6;{Uow5a`4;)qe@9~fe-Lk=tPp30 zva$9(Qz&zjb$oILY1-HRST?sVZDf!0QXcwkAuT;pzvE5h>g?TFlOb!c_IGyuj;*%n z^i$Sqr$O%xU43?8n0go1b1uBxS!0^#_6w=M#yYL7ME8Arp?!REV}i5@^af-m7AK>V zIAf2xZIC8@WXr!TYdK}+HEW%@5FOt{=2r4M=l)^M6-%8lMCWOPhOJBv^7AmS_~-wd zw;Z2bh2K0BxiYztycv;acgQxX|3lRO52R_WneUzT_V#Qv1RK4gvY1aYBlD8-XMa5E z>+v27ug;S*t_WH6_@w+Y!W|Ip0jJP8;%Sx@1!i(TXcL2X3Foy|Ltc~4ud`|Y$q}6$ z(YE2GJ3`na!@iTX`+Ua#DbBmAkX6@YQ-8`hPJEe>JCa?)bDPu-oZG+NI^8|X4_Pi; zcNy=BC_kokAi8TVdHkRIKP+irf6%amIU}#x*P)Jg9_jYV9U;B*h|Ei5CgcNGw|eI> zD8V^s9`k}VZ$}1xeMERA?=n{IutIZr{}yk47+ZwUQS+bwJ|p7kkc|ztql@&=KIbm< zn2arC2kGIz)2NH02k+T3hR#o9dheK+=ikx0L+AY)?=#+@9Jfst(3Wk#)6o9tK|WNX z`m1cMg}c)C|F6B%_$77>zSCIa(ad)mt38_ePU9Yrrgs_Rm?W7I)p6-~%;&d#fI*gVvXh|A*s^!#JO> z*ZPvauD;;RYmvL0Gxpd`M3Od&_2$v!tOg3 zt(8KIS&#m6|E~4@Q*)E}vfVdB4tak{VF3YI%=dsKNcioo}=}RA{ zkJ|CZqsim*cDyX3LGm(4vnZ=OWpyCG`R-l)(0O+ro}c_V=~K7|Y+(nJN9Q3v&w20+ zZi7el^?CNdlidan>*5xk58>H6(Jf2sW$8H!nje3{h|f3TKlviXvzoZ_#XR_u!S{hj zYXoZiSSedlzWTZFi@r$VlJT_154}Z)w}|)RJ?5E=H+#vWu}5RVAlAp3n$M!lRe5^H zf0r}o3}(*xEW)@`2#xtoK71y$?V`m*yW64lfhIpwo9I29|K3{fI{mqC1oNN#jkagZ z()>o=2J(cLx6E56f4%o!RAZygd@TIbm*vlE@{7IH-^}Niqw~zDrMYEEB93i*-iSxL z*Pz)RZ8;IY7Uz~g`q1CIx;1@;2|3ivJHgTQYC ze-1nWxCUqg?+2a;#GboO0^SEa8MqSI8+Z@!6yOTrcYxS)*Qvl|z+B)`U>{&T@HF7< zz|(wUss30nw%Bs@#_Jz+iJA;QlIs|hOzdIxqZVLo9Ffo0Y%uIuia zPADXB)p!@zmU*vY?c#`L7fY^PXA`(0j{Pm#&Z6Bde1^}%(O<*7XVtrBjor>%zH!0GGA@WaQoFupSyFf&P-kz#2g5Z+AZB)wqx(mr*=O5`TkuS zJi6M|r#}IGjYn6z`}E&JulMNk3!nZF^f?|~e&f@B0lmzl%ddR;Pod{~borf6zZZJc zqsuRS`d!fbdUW}%PhSE($D_-yefllXGd#Nd-lxxpzL)o!vV;1CPyZk2+dR7ZjZdEi zeX~bbzw+rngudRRtKa$bYoNzHy85M0FNMCyqpRQg^aAL!J-Yg}Prn>`iAPtz_vw?M z=XrFE3qJi~=tDfZ#tol78hWlr*SO--heFTx=o)u?`uWf;kFIgar=JCVH+x9gP2-kN z?+1OWN7uOK)B8Z*auk+{{7k&D-ps)1k8aI9VG0+!!bd9S%y$AGKkFIgo zr+0x~=+QMU`}7Xb$9Z&(+dh5&XRIwey2f>%{w4I@9$n+UPyZCU_SUk4<^`Yr5%jN_ zpGDWa;nR0OclOrMHLv*eM(A&P{F--s`n%8@Ji6v3pZ*r~2R*vRQ=k4C^kp7h^O;Zo zBlJ3tuKCWVKL@?Sqia6&=}$qQ?9o}9w9p@iKEk7GKK1z@g&y(fns0sjuc7zitUz-* z@ei|!YwQVow4X!!vU{RgG|Qv?6q@!2qV4tO`IQDOe*(X+VxFJP9A81ueE->5{1$?N zGXlLHyf<(eFc-K2I09Hg*hst(xE`1f+yI;mWRYr>0oMVy67mSI5XKO;5XKP_gvo@> zgb{>=gkI#S0J6BYW)m{O=K#9`Yk?WSuYobbKEh{&1q2)VBH&&^8Da1MWPQDV|Mmzv zu@<)p|G;+GzOl0h@Alv|9&Gy5P49{?i!U3xzjpn~vN?s^Gm0tC_nSLK<2|f~c+XDE@fivBUzgmyBduXk z(-DbziLTc78#^TC?Z{+L#@W-Y>Gr(D-sn?1zou@Q6Lp@N!J2pl>(2u6iDWE;?i49xvNmwQ8D^ztwVQu zQno4pk6DlWU4AZK6x@4}yYKI{&fTjzdwp!7HRk8=chb9wnBGm?+;Zk_agQjop8Lnx zrZeSqD!e7Zc-lA`er|?it9;K!E1WT&F}u|{c&7+9sn;1HXN7j^J%RZAGw@zpc;9ea z+xkhjSEXB|&d`Z99eSI3S)N`e{x9ey8-Mf<*Y2MH)#pV^_k2;fMV~*(Ya1U=lJ4`? z81aqBaQY51dNuH#8lUVf{OPjb(h(_`_*0rWYI*_nBZxx=zJsdRfj z;|e~;dyr=O=A)UB_|McA2E*F{8)*%qeicWbu=;4(`%>N+ zGO03!|R`{jQ9xW?(1p&bzVK6_Z+N~8j0KUnlc*dx2IW? z8Z#Q^ZfESh^$5lny+26r@s}MT`*!5#H>US^en$t&Obh>IM_T0ergUg&;pcax+kbg6 z-G2T>){~7Hwrh`jse@mR^FD)eRGvlsWCOR%JG?UM0%g8sl=*^J=37RYFHq)Nl=;Go znj<<=remw&?i@kAoApjZHviox`#+Rky7vg3a!2wGqb~7!_>Ba1X$*y%l3y{`NN@2; z*9vsci2gXC^+?Db<-7w7h2PkoVelt7^D*mNpf;m!??89jHIH_!r(FwOxoLIGT~52+ zm6XrP7CYr5(bW9U7s8$K^;_83)pbPEE$I4>fUccw>T6+Z~F$NjjEf@!Bl1^<->Pn7um=^r=5X~Bu_RHlzhoPP5Rcw8+HEej}e!8eM-2- zUVODBZS5y+A4~vm_u?Bp=<_f4^3MPcZs2!4w)IuyOt2bOu+hmhh^tE?-Y+fQ8aqa}vCh1mu#&U$+mm79nfC3xx4J#K z{*qT4kB+{|?>FMj5oCOQ3hQy?IBVU8B?-%uqx{O7j!flKek-!gk(CWSC-Q1iduQ3^ z6=Z~*c69F~^ugY10<_-H9t_YrCj` zz1V|yvAp-r()()BJD>-5Z=2s%8a9;wah{a*I(hTSn?_$yUh&=uO+0EVz4sO^-AiBO zrG>0`dYYB!xeY&%z0_9t`L3r(*Lys@TU?L6a|ne5zdu&9R@NMmR*1}Kym5#pFQ5L# zSk}y$_N^M5o`l|e>FQ+XdTd|EJr8JaqPw8hqPM6X@w}!ybhM7-w<)$;_p#(-uibbr zQ2l6z!^aw!edTAw^{)R6@@TA*taIR#JjH9p8_R3DN;1&}wX^sYT=PXh#@=R?B^{%{ z`Q)qbfo<$~<7RBEHHdG&M@XAZn)w%P`37$e?@`q@gA4CW zYMz&TwX3Kcr4Mv_f)xMUi13Kb6t`2vRm)}6_T1lG5FX^as>32J6e<5vw zm#>47?`G0oBkdEQugA}cPoYfN%F#u#(B(388Dr?e+2*csG*%uVeiOej1dizqPUuBg zMtJaKH@pd~CG>{254fE$o3QgFe3ZVWIladB(Y$!$GIUU1I1>L<8)Q?q)(i5Na?;kL zhvL$2`^Th3^O`uT&5A?USTFh-_@wt2z#4Clw}5oNU9f{wuM?3SbLzF+vAujjePM83 zYhMse`v+f^3d%Ld@h>!F9cS2f0UFLT>s=bhsS%Ebp9n@Oh~%1hsH^dz6^ zDZ20T-QYESUSqv4%SJy1r>$rQv#rvjtd%`IM|$H4O?#`_O}?bIEms>c280;DIA71w zoGjhXB3-{j@Z0i-;w#K+;#}St3%&!?STGu>u|V%3G!}5=lvPLGDeB+0+rK+RT-(b| zYe^g7^zXZ!I%+H!EM9nm?f+AFZG%^KQGX7$zi93qlX$c51H0){8iUO7;5XDkzA_oP z)VGCq6YUW!dmcRN9RG0j{G0s4lS#c=`u8ei>Rp?DAFJ~4vzD~wq&1ONOPcz=(v??z ze+g;2NBJet_tl4qkE6_>uNI)oU_%%E{$;D}w)+s^2tt^!fUxWYH;85vz6M`G$Rxzn zpYvM#ahx*c$J*1YeQe6wzQ?6yvtIH2Y!2nCJ>{43iSyvAaCEvW*)8hQ4>eY6PUQE& zyEZyy)+eiheNDN@(Hc|oPeOjSC*SEKDqj7g0NrT>jbj^E@tL}_hv+?7vd#-kV+obYm!6W

MiKhgfj6mAOUfLAWcC+SSUP2!?-zYEb*QC8k8e@M;+67)(I%Cdw(l?M! znT-i=Euebb56>Ft+Dl0$udH{i02hzHw$K_!ym!M}XYd}*IPvZG+4HlvIF`H#{6X{l ztJq@*a#Ws$><-ATBfW21vc2_42lzBLYYg)DF1~E}mGqTt`PELwGJjnrKbP)h@Vxn7 zjB)!d*7=_9A>ti5rvs+H#Qj69zufXTpGCgv;oE3D<N7vVaM~1}+z`04^YCESf@y60)HWVUxV`c;3Ab77?~X>kEuR-^3zl z0r&zzLI&}1z-6Ro1GfQRA(Rl-vQhEp^I68)>n!S^zBq<)O*VQA+}!uXKBA7;T7IRr zUjVK-dzAXF*Z1k8&c5efr6G&o^=4^Kyc1a^%Io-p+lMdp%Igl#xIo%?FHOFueNlw; zR{h^g$Cl|>@3>jsIL7f5I-B>%{Mj171J95uP$5pVstQM!u)X*Vfz}gU6h^ zwWi;g!;YIaUr2}%@(6CNlV5k2LY@rOZ7DMR zwTEnxe>D9FsNnxrzG9+E|qzdyli470Ldw(VTl;*2o?vYg~WMuB=tbuCayG zr+$ZitE}@Rowu@v&C>hmbnb!cx8VLgaDIo8)eE}%oz2;Kx>dg;Y$@ISy`l2^cj3aw zRzHyqy3zJ}C)5`j&2ya2>GjT2zel&iI)7i9>;-Pcc?VIwGO71_E%}k{-+z08J`tz= z)$Wf|Kh^U!wUrfbcHRN<`;#2WLB53_sSHPcbg?79MYiY`bZ@e{%%DkHccsHY%g4r;C$@K z{098zlKF|Q4a;|QxMWcx+@L#_Q*^KYo%AR^jea}P+x@#W%XegO&--%e3OyY8F=w7} z?_RkN6Ar#P*DYJ+Mkx3H;vMe@cF`V4XMz6TtvTJJnRmRuIZcG z-T@xHL)RH?9(_h*&P(u^^dTO7Q-JP|6Muldn|A0WzPtqed`aR7XgWhuU6x_@4TNoK z2SV7Z*IDpL$NxcRLET|M|6RJrIhTGMYOoVlxM*0&zNCpgEB6=I(Vq&3g?PtP#TkE^ z)uWMlC#z?_s>U9iDW%!b#0hqF(>!}#SYrr(t3|MJZud!zz{K1OhT9=89wI*X2#@|f z=w3WTd~P?7{$1!^JVbnMSC2jfx)%=-pWDTwpAX%OhltP3^62M5_u?Vqb31$VbD?|j z5b?R09(^!$FCHR3x06Rd2f7yz5ue-9qn{1ki-(BM?cmYRg6_pb#OG#s^g+Di zbdNp|x)%=-pPS~<2SE4YA>wmG9=$(wFCHR3mv(XN9D(k|L&P;6a>_r8E3F6Zc!E8(N&EvjA58J(HK&CV&!vUpzh!Oh_{W#b(dqcnO#NLq{AQ_@ zo=E3!{%CX!W;f#}CaP`~8u2?t!1;?qiRfIB~+n^KCwqU|(BW ztq-Btl~uKN%U86|v@DCSllRlt$@}qj@_q*ju?|E2fw^qwvlC8X)A$T`2?ZU79DN|f z%UNB@H;PO7#<*QxV~@Xd+&HOf<&GWE=lj4|+uuq*oHFzoi}GUI{RD=6>Vc}C*|zZ9 zKBwBnRWmC}oJvhCwf%bO<3|1Taie~zkC=~Aoku!fD7KvR@~VF3*UVFS4*p5=OPw#n z+qG4;z6)d5mX+G>=lX1Yu%=%LU$-ePJ=4y0in32WrKAsk`K~1opCp}DKAkTB`kAKj zbrQRzw5GVaJXTv(O%cj2!u%VZZu=#d->6usN^R>iDlSWP)J9OMtttxTwe72Ku zbXytmjUqmJh@Ml6X7aK0TE~A*DY5(6{P$h^lxbF~UVJyj&MmLxBj|m63(2fbR#(GT zN+COtx61i~`e9~nt4YY&?^ za_B{NP5E?nHaeft-&?DavYGstkKWr1TsGgUp=TZ3Z%5=$JS<+m$8^|o#rxmL9e$Yl zI&vrd7y6EEuWBiKN#^hohvsv>o@MJ}KnHCI+R+_<85&Dx#Pm%s@#`CBZsuv#RWlB! z9S=7L97_Lt=0|!0V>y@^8-mOvIF!sRt<-iai~Q@?$|4bY0*$sIf9m)SXx}l z_k)M?^}(q{e5CHs@&gNofXZjKDYm`Ef^QH1gH(JEy2Td0O2(JI9T%;fIb&*RwOu;9 zxU{sS#=dyy-p_gw#DE4uZQ*1hrbSP197zFSO?SV&_3lXL-pNtzu6Aj4gnvMN@p5* zbE$iK%gckxZQFilTK#IStLf)7ML)H6KkvJM2hdx-{D&U}^C;sfybx@g-p~Egv_8Do zZr*;Se4wwr{45M-mMU*Gp&Tj9m;PGEmV-Oaf8{A9S22R6*2@!TR@Ro!C>=>}V5C*) z-dd-19=k<7+Y@2UqMX#)*jZ0I#kjVy%PU#pPAe)dJSA`O|LA!o}J2!suHL1&a@|$IU6-LX{SoUseSEJ-JFVf@=vwd z{G6)7Pd(FVlhpb^>nYbYWF&2p)VAy&nAO$7=d)p|WfHO7D%SYcJJ1)g4qikbVyEWx zM84LjdU*SC_cM_a>$iA~%@)13j6E+snAHTU2;?5bNI`$B)4=oJ&G5$gK&vsgMam&$w<@u%VfYaF4?Ys- zj=jP^56}=>I=qa1xG}jOyxwh$$}QE>DL@+{+nd<2xnGtJTgFWoI$~sMYCq`t%R%x5 z)*80EqLaT=R27+LDs7b6+VBVDJgB}09*P`TwA}2Q=Nk)rV}Wlh@Qnq&vA{PL_{IX? zSl}BAd}D!cEbxs5zOleJ7Wn^(1)}5myFSjT5WmYsr#ak77rf1bKLc{7*c!(La_&=E z+g4f@*Ke(ir$ZLk)U6Bx*OjeIa9ADu^Z)Fx%0Wm6@P%B`;_jlgFH@J9Eb9}PxTj@t zNy*~67k^+V#2q%yq7cM2DXRy8>y_5Fp>Dk50?XoehR)v!;_jh^80$a#_g9UBz8@%E z`8(28*AKBQ{yspJBNN);JYjV9pUKqkoA8Yd{#zgL{pTBaXbZ%i3#rfUA3c2ddECAz zs)e$U*s2+7ELX; zLMIXJaJJR4V`ivxC@byAjO@@+p<~i>LdS-VOY0RnDb=f6>-3q2raL1X_cA@kbY}!| z$0)I+yCah`cDcAlIm^-*qbJ=N*%%WUgIruA9Pe*haBl4JXbjVn?u=UQ80zAk9lCtY z#hq6SR*p9gr#mC0JJ!1C8iDyO(Mks(Pa`om1|RY9Bb>x^{i=lhR^|8Tv$1rLEM-EV)sj%JNE z8rZjgeL&uZ310DDdu}#x$t%ojoj%*ZmA=)$RiEt!?&!-8(cQ=Omq7-u^eqA0?>}0} z@b_<99$)|M0scXgd=^*!U;~%@MFuYY>J41!+XHyXeZIVI*1R^#-x=WVn(yFW(#`fV_9Cx1&Pa^+_lxY8#ZxbjaiaHX#`aHX#^aHU&Q^q(GAeujZ7 zJ>S5Uf3krq{Y?W`dcwe!K88O^;y$kYd;?ed1_M|AO$M&?$klFcU%$ZyuJknquKa5a zT`>7Z$fpuQza|ujT%S`?&en8@SRJIB)00;g0VO_qp8ae=9qM+fvfyH1DAgJ2Cnou2Cnov16O)|0Dp58 zMCa*dz0`(&%L4rC4BWAgflL0I2CnpN2Cnpd2Cj5#hW^vz+OL;^E4{aYEC2RN@#=By zV^z7BpMT4>DcsRdmwcSZm0$mJAMa*0w^3hfwx8ZbKeTFj{P`l!xq&Tm7wh;o(g)A+ zvvjcrwvoQbNWZ9!^z0i``AgeKA7P}=Z6m$RNWZU*^aV!x@7hRD%uSWA-vj!OUw>Ai1G;Y!b5n8KxRUjtYAW&>CH76Vs$!>#=1JZ|})8MxBd-{vKI{cn?j zE4_Dp>-4yRD}Bz=*6HgETSHQ@i~O4BYqES62G+yIA-8QJ~UQzP~@UtRt*? z^80uQT_4-d^a=y-O@57s z+QJ-3{{E8hJx2nE1@Nzq@l)ON&nrmZKFdA-RQ`G3E12&xVH17}xW+4;p$cCKzS&6U z58t*tDsKb$1|$7x@G|CCU*6xrwO+3EJR_elPU=;p37xwh1Jx7tQ^MpLlKT5zge`b34V(?V^XIsm_YpI`K-gDqu zZ%Qqd|3~mT1K$EZ$LLT00AEmu9Xl1zsOZ$-CA#VpDz3Ctd4}Y>$5e z_+X>FOTqm|{L{b}8uDg9ne*d^1+;8d*bTr1U@L2w$4?v8*R@TH{X!#;-Hs zUtx?d!@+A`PK`Gs1O9wPKz@btqpyu0tNzOa>AwK4Gvq%JNPihz`{&-Msr;V{XFm1Y zBc}!1$C+!5ip58RV6`^T9u(|M++%_|B@7 zKgTl zJi>V4xBt(<*Bkx)4e&Zc-p0Uul1@hL&wP810@rz|Z;x_tov-J5`us=*c@L^vOTgns z`z#0FV&JQkegk6wysFO|;0;FlJK+8!dAq@Nz96#rGw3hce=bOKas6!`{}F#5@Pr{R z5=j3dfWLN(pIh=QYj`023h*(8{pN$m4E)yt{*B;ze=r$Y()V5PxY6E^1jdIBN7Md{ z2OHqm_;VciIz#^x!Tm@2ogToy8^A9I592&J@T&Y-f%MzK8_+-MjbHbJ>wSx_@ACov zzk-|d{~gEs+PL=GM!MdI;IdA6UkNWy`Sa1(cgXD2_)yZVb^h~CXpIkpU+<6dJ$WAo z%DaqoonJ5V+P@N9?@N6CD+A-*Qqpza+t-u#TkuV@QvUoN_!eXS?~LK{jrm=FKhl3> z|FcNnbQ^OB1G4OUYA-+4wa1--`b~r1v|lCoTFO&4YnWZTQ!P;0uiTs8fLdYSJ?e{u1T)&f~QHkAeG- z>@yEM*Wh0W9x?FS!5bJ4{qbZY_$H(NJHhq-aUHT`pUmU%RU`iq;F+|ipZ_%Qg+~9% z#UB16ef0Mrb$+P%vj?L{B{NT`Z;zH4{ND?-_sgVD zHq!qLuJ={76s!7tqWsJ!g&zJjxc^AscWobY^*aLp*ZVR*|C!)A-}CE#RiJ;=fN#Do zmA(pmnbE$d2j;&Gr0;u$^$*2r{CN$04*ulJ{~FwXWV$XmuHGN`{@M?G$R^*uM_HGE zFEr{u89c|puLh4YK44PE9=8PcH|GZC&xU}$KPA83&#mDYZ{-eG{eW-tgc~tg03*3KH{uKfJ$B-T~*2^=% zXB*|!g6n)*f0IuBa^p$9G}j;Qm3)jB1}Oa*@P$VEzA1SIz7u?~AwS%`wY-l>*L{P* zAy=OMMz7AVdwb=d1+M!B>%8)2fxl^tpZ9^U!5@5oS;hLoe^mc|f%Vuc@az114QbN< zpWxdJc_(1seRERshRNQ@@9pu&z;*unpoiZEuJdf)9xtg-?@{yx(j$hyJ`&L9E5@6O zd)SY_D}6pvdER3KJMo|1NBjNnRPg*)Q~v&L229?g^3J!>#jy8x1NsajU5jhqUSq*` z(qDb~vjgpOE$MTN_FSa=H?XDvNZ%*GZ9`rIxZX$k@*g>l{*S+m@%XPc>L=CyUu#$M z8#z+Ni;n{<@i9XD0SJxuHl2^o3|Z}ElFnqNN6Ac-WEeQrI&OF39k=bZ-T6S`078i4 z9{B?hr#*2&NJt<7;s8R38xlxJoIrb+1LDl@S6^MOwtKP&D;2k0uU=KX_v+Pq-W6=#kjuv zorVAX16=d@)>nXw^b~9Jx?0Q9v$gX$UW=;PJ-jAAzrJ^#&$(&tV!HNVZ7?u@UD;r4$oHayw7)I(sFX znM72mJgEUODdc%poJE#??RYf;4$(!m% zOsi;?$jN**yFn4}N&>QJfuaV>KrxFKhc5X@Ndwi98^O_wmcmbfuP?;IhV>qs}4+@uc(q$wfMCx^s$Yb96Q9%{v7* zyEDtQ^ZsK5Haja2H>)%ESyFf2CxwpEyf^PflV-h{z5F=9YxGHla&nSh*69#JJkY;n z{VFYacU>aXT_b7|2w<19iOlkA07|)kymR>fN~#f!>> zp^_X!>O~c$b@K4xXwIaq@ zknh?Gxt3WxIVx%u&qOz9n@xRm4b6jiA`qUv6wyLd(3n+?(KwG%bbDT&+)z`+*AaCN zLK%L$ML5;9O(DmphjO9hz%i=0)94J8Y1icS2SG-5!HHKwcOHrgaR}wC0OyKIQbRL7 z6|`kk*Zo)+=tIi!IDl|;IVn@=I&?HveL6&(6m;sWu4UasRRigrG8~T2wB=?)bxE}O z6v20%BT`Q{0MU@>JC}$(#{M(p)xFrpEasxfaVazF{k;fwRTe7nDgB-2@OGHaUWOb6X$VQcRe?36%IHwv1 zEpQY~_As)v;@{X<34T?k5Qx))W%~g8C2x1E>2+V9hTZD2xu7Bp^<=BrN-uCez?`+G z1d!3glqqTH^XQu4UlnzLu(;7~FP3-AcivUOS zMs-UyP;+l<7}V`Lc>*JohNP^`4@H&$+UpOweOm0Qkg%#(TfO5cY*eq17!yL7PO8x% zJ^NcbLVLhLgzT+~%2z@PPdGfheqboksG(iv7we&~lACg#WiQITd{n^mDKgkxKvL8t zf}NP#*!J+Tn?ZdmEc6uR-Kpl@w7l7 zz0`Ow!)<|L|AUQn4CaU#jPfGBg&X)Qu820O8sfMIca80bDV*AC7 zE7u-+ZJIf1*w$o8R3Tz1CSzCu=}KdV-qubAe^;JE%AK3gQ$b6`K89bjVmdR8!R!!& zI?7>P%sbk^?y$tj{|?IyvOqgu_h=qQ|LbXOmq-SJjH zZT5@G^*5;10S7Il>~)#<4xY#zQYq4IQd);Cq-vQcM$4J`XD171UrRX1ZucTdTd>ND8>-?r_(jT> zz+6U`!s^U+(;QaX&{(KdCN$1k5_@1NvC1?8GXjT*{J5o$gJrJzllv`Q4f8k-@{RWT zMns>VcP{d4kWA;Iui$hd|<_Zq4-^C|D(F1uQqFvC8sS#l%#NO1*93wk<@XeR{S`8Tf$EKq1$R#ms9dB24<^QV^*MvZzZIN}<& zF$cCnXJbJbUl%@3wEfuGOV1&zmyLkVBB7^D+H8ehiff9KA`@4?&CBJnpyuAT+R^L! zTCp!2a?6&j_3m{pFn1O6tx9)BQvgh!?5Jlg%6&RC4;%DqImVe<9SmY?AluPj3;aG=pe?xcl&{d8XA1PI4eBADV5|rj17G%z8Av$r^Vn%5m>X~a8 z4t2-TmR5tUp-uEqLc5+vb)&!#U0C_U{u#6VgbJnh-$bP+DahN$aQgmSO7UI)9)1ND zlUc=6#0n=^*TvrDShYgXQ<0LeS&Hu(XrE`evC!xdzHyfcwNIh1OF=u;jG)+TY**7-Q#{Pak21=lduqubD3ZT$M@l;d{@GN|WjU{5Rb9^{heDKch?s@Z>TNAl` z{nJSQHi`Sh>|wWN4RGT>h-pVS#;RiaI|^dUkU9v*w2j2q0SL!9drTixj3V)`>KY`whC^a6>o`1qT#u$aDz->i?gc;+!d z;`VXEF-8_si3^L5aNM_r?M@1A7iY%KVq#oC!f|_^g}cOW;^&r2?!jW>zD|1(j>iW` z7S6_X`vnV`gK$jPw&&m&ql)P_*gI$swvEU4kSspFo5p1Gm~ZR-p@G}PnK6f$uDR*U z9E2mD56zi$;k(gH76)#*-`M7dH>Ncm7^CU4zg*;5IF|bf(w7VzV?lkc2kxhUVf~~l z-?{#L2OP4*&yYwi!tq_{JAiwQ-?oiBv;V(BviQ_H)3{Mu<|2=9Out34aC}$0{B<|| zeh)a}B?|7@z6RX2x4+xc{7(koI?g-@x21oo|E!DYFG#>us+;)t5#a9G3!DkVgJ<%> x9)AVg>$i2h%eRopKHNc~ECZiA{_rn+nD=Y$(&6KlO&@Ohg-?lpF2Eeze*w&m&xHU0 literal 0 HcmV?d00001 diff --git a/lib/bananapro/static/libpi4j.so b/lib/bananapro/static/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..9d7da101a23a098360bff31c9fd2d167e516c5ea GIT binary patch literal 113208 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 X=Ab-3epX&UH~G(E!B6vKxpe;zL%q$1 literal 0 HcmV?d00001 diff --git a/lib/bpi/dynamic/libpi4j.so b/lib/bpi/dynamic/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..039bf7571bcfc184ed6996a07189337013221f07 GIT binary patch literal 66116 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 z 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 literal 0 HcmV?d00001 diff --git a/lib/bpi/static/libpi4j.so b/lib/bpi/static/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..41750648af05486a0d5ff05317b25d9b6c63b7a8 GIT binary patch literal 135576 zcmeF4e_Yg6_W$2AfFh!fNkwYg_@kn-8kLGRcN>2tCfcM}R<=NZLLy9nh3VF)R9KRv zrdGI4Wt)|im6esXR90?l<(9Q>Sqp&6BxmF4$&-tYTnE~99l$K(6`=ll4udwtz| z?!D)pd+xdC{&>IN)6JtZE^;^=hW!^~q!^NFE;5W%FuR-6Fk+3kRKsm_BCn6p+swDi z3)WuaFhiV4beQ=`B@QF0#A$|Qrx}LlLWBD9=P=-CxXE{uFCj!)g5<}lun)AuK;jbY zyi3F7c_a_{llm^gMDq3a_dt^%^`&ouNrV`}mBn`Z;6(BRghYbN>5VsB$-!77-stD792g)Y!SE(@8ot2mrquZ4t^J{R>0C_T?BEE) zIozmBiCYqQ)xB_$V{c--*M00U&b4r&@5X?1!##GQ+n@?+@QyItZm&UA?_EyU0=MDy zjxoGX8?oNljg)p&4m8%gUGeb)gTwn7sVlPw8eX~(o9d4B8r_}o#_1#8Kf!IRH_mss zx1VXa9Pw^%z%b?+{g26X8~uzLn(QABqj={CV|eymfz&{}Gba56!$=R*#cnqOC-h8r zWyE!HySu0*VBku7Jyk zcU3Km?-YnNI;}JUhBL$4Y20b9I5cf|8BeEkyF~dL_<%9U$j(TgH*dcCF6Y4h@h*qM zm=9kH8vPxe=G9zj7{@vWJe_c|7cCp>V=R*-KQ_QW@kZcQ;pif>rqb|0lD{h^-u3bQ zof03Ql*{Pit`ATU*^|76qXy0az$vyMf0GH95iTcOLAZ)Am5@!yCCFbMA)jz9foWv; z3G#Oxp^Q*Ym_?`{%qG}>bBX+wa1+5)AUKb33qk*@BC(KgheljP9AVAByA&<~?^bae zmfI02`>>|A=DA{zgJb_HSl%9X2KhaCh#WVpM2%D)Ds#B@)ssF5q1;4BFJAW;akG@gdYj}2|p9$?^nWa5^-RRgcw3BL3~64p(~*~ zLH>Gxy$DH!;|R%w-h@981`tjmoIyB?a5iBuVJN}=JD2!*D*Z?B0>YnF`a;l47)cnV zXi{%9@i8hr792;&AdDwWAovKGgvo@f2vZ4J1o^{t8@Ysh!ZgBk!VE$Yp_t$&Tt_G) z+(4)x=znumdM5(5#;ah;KM5ZDEJuR zal#Xd_O!wc;B$oM2^$G75#(=^LMvq$FO#k#yheC~@HSy9;opS!3EK%D5;JLk=X>UU@%lGCo?k!wtJuaxmjvFsA>-=_ON?>l3l5%f z`TS|ABU~f@lG^#nVXb$ca-(z7=ZjaI|Iz%n&baKMiScXVAAYX>i}(ADTyoK~t0zxb zxU}rPH@Y>G9(qRK8*T{x_fI*^Fa7%3o3*z*dc(ioJb7}?m!E!kZ?Dh%=l|!j6UzSk zHRYZ;cBA8$e@*?%Ln)UBH}#17=L=o-_c(p-nER*9YfSpAvz;|rz_t?#xy<)?xTTa2rEY)_j%=%vObb631IXx;9&eylC( z=f0_K?vtkut&0txLkJ*k-oyTAJDf8KD*%r8lQ+WYSdhV4t6*r(>t zf4RBgf=}*zd8QvzWqE;blY&%7s*%m8vMae3*Vl)c=I*im#>(+=%x}+ z{aeeg{W0nIlgf`-+&?_+<{7^6r%pPrdfy)(N*VFuz1#0e+5cgW51+bj+MxQ~f5dojYRmhkJ5rUVEAB=IR04uXKH|?9M0B7QFNQt~`ga_oi2KPdjzSfnEN4j{jy}?v5a-ZidOtn?s=qIKcGFE=;tsyGX3@H5-am1E?(-9? zf4uKO*Sk-jweZ3p2ETMm=IzHlvGcX0cb|5jx$~GgdH=lTz;~;XM|w^k|8ar$o-N

wbOvsmH#!;*(46d$r(|3({{J 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

kg{=Z$If5VHt(JpR_y(^2Hn4UN9u!6wtT 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 literal 0 HcmV?d00001 diff --git a/lib/hidapi/darwin/libhidapi.dylib b/lib/hidapi/darwin/libhidapi.dylib new file mode 100644 index 0000000000000000000000000000000000000000..91389aef6d2c74356276d3d862a97e458adf7e65 GIT binary patch literal 58888 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 G@ca+mU1dQ4 literal 0 HcmV?d00001 diff --git a/lib/hidapi/linux-aarch64/libhidapi-libusb.so b/lib/hidapi/linux-aarch64/libhidapi-libusb.so new file mode 100644 index 0000000000000000000000000000000000000000..39990766e84341d9043bf5ad9e1bb2ff13a9de88 GIT binary patch literal 76048 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�v?Hhv-J~>vm1T>a|@b#l0 zLou}(-1;kGMLaNat3)Tpx9P-UbVlNLc8-mYv?jrag&eF=A8kZ?el;>8_ekgyZk`$^ zjMgD2VYQS(CFN{No(D?Kr?ExA8L3Nr-^2Ve#WTaPMIv?L>FEg4BpA?C-G8pl31 zVOx_(ax5;dhRDzNM?O~uDYp&($&a15O`Gs3vdPc)Ow50aeR4dBjCk6L2mDc~*PrjB zn15M`sXxnMF2AsTHpb8QR?P2Ge$iiKP=7_6F#z%%GD6F;^%ur=3mA+T0Eu8zYkxYjsLVSzHdA-IK5O}4EB8H?!kaOq%xM0{(puIEG<5#sdD=Du243U(t!<{M4&%md|`cLHUn;+ZDLy zby{#i`CSG4>)Krjd!M8JLX|R6Thg6pIR0D3IUrqv|5xKb?T7P+_($3vK%Y8Z;`o1@ Ya-a_D$?yw@eE#+hM>&vUNIjbW1uy5eDgXcg literal 0 HcmV?d00001 diff --git a/lib/hidapi/linux-amd64/libhidapi.so b/lib/hidapi/linux-amd64/libhidapi.so new file mode 100644 index 0000000000000000000000000000000000000000..ef1a11fa529b76dd782d09c289420fdc0aa82aac GIT binary patch literal 50952 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%0AsH*c4s;c*UYy z8HZ)JvQYOXTdOtd?i^%Xtsc9aon`-m9vNNP&sAl=Q8_AmSb=|xkfNpZYDBMx+ZDsj zD%b0eJqj;W(|@VTH>tcs<$7t@rEtAM(F?xl`lIW4TrnJsW>h%8r@^G{h^|+bM$ScQ zf}F4NGL^4Td4y9!U#Tfzfa;e)D%ar>&0nI*?^StNf*K z?JB=f<;&C^o82nc_n7nzCVe+4`c_i3{`>zw%D<{^vAm}8=vyz*_1%yxDc@K5PL=;! zBq_vi=6e7RsAei%Xcfe2&aUmDf&v4^Gn)H z+g1MWLJN0J(p5lyBWMl6jCq=@Rkz%aC3((QYGWr`W zap|WRH)Th2^Zrq)u_G=$%{VkR9)G%Va9lk407LKVCe;qpr6qP zeRv=AGy9->`=F1Aq4SE6V0>^gx}ZM@CUy^x_VX~%U2)=vEhT+#R9xjKy1l^}aHb+0 zro*4KgQ*;cX~g#X6`)hUy)PY^k*>pG#)+Swf*!A3_ku3$=SKmM^re-TPST@4DfLH1 zkN)P?n~EO&Stb}G^hAGujN2ShfAqJPrh;z5Iqg0#G~&x5;!Ik6`)3M%jIT+GQDt0G z#Op5>$MeH7Rd0*WOBOpd2zu-|Y6TsiHi;SUTa=#Yaj{*{W5?Hjfj$tejQ$YyUO~6b zuz{!gpuep2M1QgJ4Mi_h^VK}1>1ZE%l5suo+O!=d+h`ffb zRs3BeC1sH+;Ad?HY9>eii$3VwV2t+Uj*-mOO6Ijb_}^3f(HlV|;d&OY-fYm*5r@(J zI7#rMzedXn)fL&8rRdnpMI^O?G0GJ^`nyfsnJQkr{MuYR`p*PC_Ik9p5B|e_(D|!0 zp8kGVYR04I^+D%Pz4|*z=&SE?&JBx)?_#~B}E5woUw`M$h_^ns!=~8hrK*?MWy4NAE!Q1-K z^RqtaPbvM;n2ubYV^kwY>QVlHm|I1RPWB(RP}zOc%wF_A=p}-lNbMWTm9;3%7Bz6G-bvlkY@ZGixNsNvdRb3nb087Qh+=v#o#9RvdSVnSfHKOn{YMTZZ2(>Y7j+-UR0xn|$Rr>LqsPi>if%Fn#D@X+pz#gl5+WFsp26$;Ax>V6sL(kD@>^xSDZ<*w8_~iM zeBHaZ`{}m-Kn~wd=q(qRw>Gf2PJXjsR%3M#-^GwIr~)}oFM$gm9&ucj?5RS&fzQb| z?$nD=5Erc8b_N19?bVzSsvGe(ckePIC%AqMUb9DzFcSG{4K+1~>T>W&nH+w4rgkFU zj2GDI>fmZ4r*?f4BxR0IK8O;16>%Z~l;O)G)f7-r3qBr@BPP8ZfH|x1U7VZ{zd4b^ z>pqy8#q^m|w_084SJy&`#=wg{mI~yBU&?9t(i$+Mvg$SXv>uhS5>PlJ=LktGLDpbd z0b1S4Hh#FHx@i?UMWN!$LAq$=N_=!cmxyl~sS^0eCgY18imEg@mQw#mAny-hX@+|s zDrrFFS(>hLwG6hYypMDOd);d>eZN-cZz)bJ^F-3?N`;E=)AFvfme==nb-oAdUMALS z;?VN?_gNum)UD+oQhz$CbNyfuEFw{dGx>qw-5=Y2eVuN?tcp-;dXMK~%s++y8?od41nr=O5WMOMtQB zO!oj`6}o-zP5@q_M~PG3e(s?J`5AI&(sDWvqtGee;`0jj7N5w({3;=N)^=;! zdjP0Qw_o4C*rViiJ(@qt9OFY2kd~_%RIcSGM$xsr&c8&thMlZ0`6L#tH<~vEglHNi z6K6{Kg`;Jz&(XZ6_90)sT1uSI9j_Eb%a7_qzIcrk(0{nlSN(ZPKDwP>wMvQTN4UCv zolond{>M47B5_v+XVP+-Hn$J?zuzS}x^!SDfoOT+dhXFWnYZkbSgv|-au(2OICMPe m>lf$Os49fu?%#;ac#(?}M~beW(|bJm;A2u~Nt8eoYWy!ds2FJg literal 0 HcmV?d00001 diff --git a/lib/hidapi/linux-arm/libhidapi-libusb.so b/lib/hidapi/linux-arm/libhidapi-libusb.so new file mode 100644 index 0000000000000000000000000000000000000000..bf2b6ae39fce0274753298d701e554d24c2a703c GIT binary patch literal 56240 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

Z?M>Hu`(s+Z>rio; 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 HbWZ*+^qk3H literal 0 HcmV?d00001 diff --git a/lib/hidapi/linux-arm/libhidapi.so b/lib/hidapi/linux-arm/libhidapi.so new file mode 100644 index 0000000000000000000000000000000000000000..8aad408764b4d73ec46cc13bd66580b082cf300a GIT binary patch literal 37316 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 literal 0 HcmV?d00001 diff --git a/lib/hidapi/linux-armel/libhidapi-libusb.so b/lib/hidapi/linux-armel/libhidapi-libusb.so new file mode 100644 index 0000000000000000000000000000000000000000..4e5117ac183addf2f1642afe881d7aba908bebd8 GIT binary patch literal 56240 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 Hos<6yammM$ literal 0 HcmV?d00001 diff --git a/lib/hidapi/linux-armel/libhidapi.so b/lib/hidapi/linux-armel/libhidapi.so new file mode 100644 index 0000000000000000000000000000000000000000..8aad408764b4d73ec46cc13bd66580b082cf300a GIT binary patch literal 37316 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c773a3b451332978d6fa4870dc01a1d920db0ea3 GIT binary patch literal 76376 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�v?Hhv-J~>vm1T>a|@b#l0 zLou}(-1;kGMLaNat3)Tpx9P-UbVlNLc8-mYv?jrag&eF=A8kZ?el;>8_ekgyZk`$^ zjMgD2VYQS(CFN{No(D?Kr?ExA8L3Nr-^2Ve#WTaPMIv?L>FEg4BpA?C-G8pl31 zVOx_(ax5;dhRDzNM?O~uDYp&($&a15O`Gs3vdPc)Ow50aeR4dBjCk6L2mDc~*PrjB zn15M`sXxnMF2AsTHpb8QR?P2Ge$iiKP=7_6F#z%%GD6F;^%ur=3mA+T0Eu8zYkxYjsLVSzHdA-IK5O}4EB8H?!kaOq%xM0{(puIEG<5#sdD=Du243U(t!<{M4&%md|`cLHUn;+ZDLy zby{#i`CSG4>)Krjd!M8JLX|R6Thg6pIR0D3IUrqv|5xKb?T7P+_($3vK%Y8Z;`o1@ Ya-a_D$?yw@eE#+hM>&vUNIjbW1uy5eDgXcg literal 0 HcmV?d00001 diff --git a/lib/hidapi/linux-x86-64/libhidapi.so b/lib/hidapi/linux-x86-64/libhidapi.so new file mode 100644 index 0000000000000000000000000000000000000000..ef1a11fa529b76dd782d09c289420fdc0aa82aac GIT binary patch literal 50952 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%0AsH*c4s;c*UYy z8HZ)JvQYOXTdOtd?i^%Xtsc9aon`-m9vNNP&sAl=Q8_AmSb=|xkfNpZYDBMx+ZDsj zD%b0eJqj;W(|@VTH>tcs<$7t@rEtAM(F?xl`lIW4TrnJsW>h%8r@^G{h^|+bM$ScQ zf}F4NGL^4Td4y9!U#Tfzfa;e)D%ar>&0nI*?^StNf*K z?JB=f<;&C^o82nc_n7nzCVe+4`c_i3{`>zw%D<{^vAm}8=vyz*_1%yxDc@K5PL=;! zBq_vi=6e7RsAei%Xcfe2&aUmDf&v4^Gn)H z+g1MWLJN0J(p5lyBWMl6jCq=@Rkz%aC3((QYGWr`W zap|WRH)Th2^Zrq)u_G=$%{VkR9)G%Va9lk407LKVCe;qpr6qP zeRv=AGy9->`=F1Aq4SE6V0>^gx}ZM@CUy^x_VX~%U2)=vEhT+#R9xjKy1l^}aHb+0 zro*4KgQ*;cX~g#X6`)hUy)PY^k*>pG#)+Swf*!A3_ku3$=SKmM^re-TPST@4DfLH1 zkN)P?n~EO&Stb}G^hAGujN2ShfAqJPrh;z5Iqg0#G~&x5;!Ik6`)3M%jIT+GQDt0G z#Op5>$MeH7Rd0*WOBOpd2zu-|Y6TsiHi;SUTa=#Yaj{*{W5?Hjfj$tejQ$YyUO~6b zuz{!gpuep2M1QgJ4Mi_h^VK}1>1ZE%l5suo+O!=d+h`ffb zRs3BeC1sH+;Ad?HY9>eii$3VwV2t+Uj*-mOO6Ijb_}^3f(HlV|;d&OY-fYm*5r@(J zI7#rMzedXn)fL&8rRdnpMI^O?G0GJ^`nyfsnJQkr{MuYR`p*PC_Ik9p5B|e_(D|!0 zp8kGVYR04I^+D%Pz4|*z=&SE?&JBx)?_#~B}E5woUw`M$h_^ns!=~8hrK*?MWy4NAE!Q1-K z^RqtaPbvM;n2ubYV^kwY>QVlHm|I1RPWB(RP}zOc%wF_A=p}-lNbMWTm9;3%7Bz6G-bvlkY@ZGixNsNvdRb3nb087Qh+=v#o#9RvdSVnSfHKOn{YMTZZ2(>Y7j+-UR0xn|$Rr>LqsPi>if%Fn#D@X+pz#gl5+WFsp26$;Ax>V6sL(kD@>^xSDZ<*w8_~iM zeBHaZ`{}m-Kn~wd=q(qRw>Gf2PJXjsR%3M#-^GwIr~)}oFM$gm9&ucj?5RS&fzQb| z?$nD=5Erc8b_N19?bVzSsvGe(ckePIC%AqMUb9DzFcSG{4K+1~>T>W&nH+w4rgkFU zj2GDI>fmZ4r*?f4BxR0IK8O;16>%Z~l;O)G)f7-r3qBr@BPP8ZfH|x1U7VZ{zd4b^ z>pqy8#q^m|w_084SJy&`#=wg{mI~yBU&?9t(i$+Mvg$SXv>uhS5>PlJ=LktGLDpbd z0b1S4Hh#FHx@i?UMWN!$LAq$=N_=!cmxyl~sS^0eCgY18imEg@mQw#mAny-hX@+|s zDrrFFS(>hLwG6hYypMDOd);d>eZN-cZz)bJ^F-3?N`;E=)AFvfme==nb-oAdUMALS z;?VN?_gNum)UD+oQhz$CbNyfuEFw{dGx>qw-5=Y2eVuN?tcp-;dXMK~%s++y8?od41nr=O5WMOMtQB zO!oj`6}o-zP5@q_M~PG3e(s?J`5AI&(sDWvqtGee;`0jj7N5w({3;=N)^=;! zdjP0Qw_o4C*rViiJ(@qt9OFY2kd~_%RIcSGM$xsr&c8&thMlZ0`6L#tH<~vEglHNi z6K6{Kg`;Jz&(XZ6_90)sT1uSI9j_Eb%a7_qzIcrk(0{nlSN(ZPKDwP>wMvQTN4UCv zolond{>M47B5_v+XVP+-Hn$J?zuzS}x^!SDfoOT+dhXFWnYZkbSgv|-au(2OICMPe m>lf$Os49fu?%#;ac#(?}M~beW(|bJm;A2u~Nt8eoYWy!ds2FJg literal 0 HcmV?d00001 diff --git a/lib/hidapi/linux-x86/libhidapi-libusb.so b/lib/hidapi/linux-x86/libhidapi-libusb.so new file mode 100644 index 0000000000000000000000000000000000000000..7de1e574a39be8eda16b7b9df08a385ed631f33e GIT binary patch literal 59328 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 literal 0 HcmV?d00001 diff --git a/lib/hidapi/linux-x86/libhidapi.so b/lib/hidapi/linux-x86/libhidapi.so new file mode 100644 index 0000000000000000000000000000000000000000..513f9930050e129da5a6704ec74ecb7863034a59 GIT binary patch literal 36052 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 literal 0 HcmV?d00001 diff --git a/lib/hidapi/win32-x86-64/hidapi.dll b/lib/hidapi/win32-x86-64/hidapi.dll new file mode 100644 index 0000000000000000000000000000000000000000..070b39ced57f3d5e7f9d24c9ace9a9d439e94fd8 GIT binary patch literal 154400 zcmeFa31C#!^*?^!doz>C0vUurSRFBFKnMvCNmwM41tu_oAqfap9YQi$G+UDy7HexX zppIi|P;AxG8kf2+Xcus)4WNSBDn+X(YOyXIL@R2mxYYbV=iK{dCIM}~{r!LcU;F#} z;LW+`+;i_ecRTmqbMLz;JF8n5LI?}qzCIzg1JYB#zu)}R0q?;lK0H|bGV%8(ZPz@% zKdG|5q21Zq)>7A2)#R+MYHn@`I9L0fZNX+|L$kAZNrkhirN*B%Xi!pyirQNuM9rl` zMa!>m_K6oYanvfr5pjw$S=YyiEHEv{n*@1=0y-$rjC%TIw=U%-)GC0`(*bA`qWn>v z^Cg6n$ucZCIg%)dQo4m`peTBf7SfA8tAv z3i#Ir;Bm(k{E�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%0AGnuBIB9KFL(m}{ST%3Q*2f6Y~Lr2}AkGLGh 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 V0cqIbCYIwo;*w%t(h^{T{|9$_GK&BJ literal 0 HcmV?d00001 diff --git a/lib/hidapi/win32-x86/hidapi.dll b/lib/hidapi/win32-x86/hidapi.dll new file mode 100644 index 0000000000000000000000000000000000000000..e9a055b8467fd29b228cbb2cf9851e31bfcd0325 GIT binary patch literal 118017 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�asCX5ZYdIy$q%r&>=I@Jazg1; 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%0A;{ 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

jyg1-^OY=HAN@IKL(q=y_{-2( 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 literal 0 HcmV?d00001 diff --git a/lib/nanopi/static/libpi4j.so b/lib/nanopi/static/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..aa4cd94db9804a2d18656e820083b91812fd308c GIT binary patch literal 102152 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 literal 0 HcmV?d00001 diff --git a/lib/odroid/dynamic/libpi4j.so b/lib/odroid/dynamic/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..91dda23b64ef0c909ae0a690cd9c27904babe308 GIT binary patch literal 70280 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 literal 0 HcmV?d00001 diff --git a/lib/odroid/static/libpi4j.so b/lib/odroid/static/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..198c7da9b00492da0e76ea23864ac423c9363007 GIT binary patch literal 109576 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� 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 literal 0 HcmV?d00001 diff --git a/lib/orangepi/dynamic/libpi4j.so b/lib/orangepi/dynamic/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..91dda23b64ef0c909ae0a690cd9c27904babe308 GIT binary patch literal 70280 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 literal 0 HcmV?d00001 diff --git a/lib/orangepi/static/libpi4j.so b/lib/orangepi/static/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..4ec0b5c7ac211e0a1097686069e96e26f37dbf98 GIT binary patch literal 114244 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 JxoFvZ|2GZKFvkD@ literal 0 HcmV?d00001 diff --git a/lib/raspberrypi/dynamic/libpi4j.so b/lib/raspberrypi/dynamic/libpi4j.so new file mode 100644 index 0000000000000000000000000000000000000000..039bf7571bcfc184ed6996a07189337013221f07 GIT binary patch literal 66116 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 z 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e53de37a958ca823611acc5f825d037c906833f8 GIT binary patch literal 66169 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 aMcWk`mMU*3Xd<5p2W?;S6!IUH@BaV|cJO=v literal 0 HcmV?d00001 diff --git a/src/linux-x86-64/libSecureDongle.so b/src/linux-x86-64/libSecureDongle.so new file mode 100644 index 0000000000000000000000000000000000000000..52b141b8f422832f074fd3dc6745d421a54e3707 GIT binary patch literal 116259 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 ST9mibh$TWsYZ{BaqyGRFFOlg0 literal 0 HcmV?d00001 diff --git a/src/linux-x86/libSecureDongle.so b/src/linux-x86/libSecureDongle.so new file mode 100644 index 0000000000000000000000000000000000000000..ff58ea189308c0b64c42354004a9176745808e26 GIT binary patch literal 133062 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

}e*bvcyc%k-jV?+nys@iKP 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 { + 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;ii5e<&`f=gjWs3J_5 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⁡xA?8u?B%CZ^ii8Og>LfgQT)6L%uv5Yg31dGN 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 GS@B;U*+IMj literal 0 HcmV?d00001 diff --git a/src/win32-x86/SecureDongle.dll b/src/win32-x86/SecureDongle.dll new file mode 100644 index 0000000000000000000000000000000000000000..cd1a1a6ea8ad7346f3dc21be631fb033c81c76b3 GIT binary patch literal 106432 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