17 Commits

Author SHA1 Message Date
dandri
4499bfbf9f Update MainActivity.java 2023-04-21 14:25:47 +00:00
dandri
1a84c610ba Add support for 1628 2023-04-20 15:09:54 +00:00
dandri
5b673317cc Add files via upload
Add support for 1628
2023-04-20 15:08:45 +00:00
david4599
f337b93d25 Updated readme 2022-09-09 19:03:51 +02:00
david4599
615d7a2909 Updated arduino dongle code to support PP16 IR protocol + refactoring 2022-09-09 18:57:55 +02:00
david4599
07c5ece3aa Pricehax BT V1.3 (21) Released
Added PP16 IR protocol support (not full speed)

Updated camera autofocus handling

Improved Bluetooth transmission reliability: basic checksum of the whole data (including Pricehax header) instead of using the PPM frame CRC

Adjusted page changing repeat timings for PP16

Fixed bug that was sometimes stopping the image transmission just after the wake-up frame

Fixed camera preview rotation

Fixed waiting time when sending an image in 2 parts
2022-09-09 18:55:49 +02:00
david4599
d33ef3e10a Added Version 1.2 (20) in changelog 2022-08-27 19:34:35 +02:00
david4599
cf7ec44431 Fixed version bump 2022-08-27 19:07:18 +02:00
david4599
5782df9f38 Changed version in comment 2022-08-20 18:01:48 +02:00
david4599
607680fe40 Put the ino file in a folder with the proper name otherwise the Arduino IDE will complain and automatically create the folder which will affect the git local repo 2022-08-20 17:58:40 +02:00
david4599
b59400eb22 Fixed MainActivity.java 2022-08-19 19:53:51 +02:00
david4599
05efc0e52b Updated readme 2022-08-19 17:50:08 +02:00
david4599
22f00ef100 Added checksum verification of the barcode 2022-08-19 17:40:03 +02:00
david4599
d026ecf2c7 Fixed bad resolution in ST HD T Red type string 2022-08-19 17:10:35 +02:00
david4599
517a3ef667 Added 1371 ESL type and removed experimental string from ST HD L Red type 2022-08-19 17:06:23 +02:00
david4599
4fa5487e9c Merge pull request #2 from themillerdave/adding_newer_model_st_1370
Update to MainActivity.java adding new hardware type id 1370
2022-08-19 16:56:40 +02:00
Dave Miller
d9f2e28c8f Update MainActivity.java
New hardware type ID 1370, 2021 model - SmartTag HD L 296x128 in a black housing
2022-08-18 07:19:42 -04:00
18 changed files with 900 additions and 361 deletions

3
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

14
.idea/PricehaxBT.iml generated Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android" name="Android">
<configuration />
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Android API 26 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/PricehaxBT.iml" filepath="$PROJECT_DIR$/.idea/PricehaxBT.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

BIN
PricehaxBT.apk Normal file

Binary file not shown.

View File

@@ -1,17 +1,41 @@
# Pricehax BT
Working prototype of the ESL hack project of furrtek (https://github.com/furrtek/PrecIR) using Pricehax BT app, Arduino Nano board and HC-06 (or HC-05) module for bluetooth communication.
Prototype of the ESL hack project done by Furrtek (https://github.com/furrtek/PrecIR) using the Pricehax BT Android app, Arduino Nano board and HC-06 (or HC-05) module for Bluetooth communication.
Videos about this project are archived in this [playlist](https://www.youtube.com/playlist?list=PLhEz48id1qqD27sRc73mDFfBpu_RcLxfZ) (most in French).
**Disclaimer:** For educational purposes and fun only. Both Furrtek and I decline all responsability for any kind of issues related to an illegal use of this repo. Be smart, the prices in the store's database won't change, you have been warned.
Furrtek's videos of this project are archived in this [playlist](https://www.youtube.com/playlist?list=PLhEz48id1qqD27sRc73mDFfBpu_RcLxfZ) (most in French, auto-translated subtitles are enabled but they are far from perfect).
<img src="PricehaxBT.jpg" width="640" alt="PricehaxBT">
## Build
- Create the dongle on breadboard following the schematic (or order the pcb and solder the components)
- Using AT commands, change the name of the HC-05/HC-06 module to "PRICEHAX TX V3" and its baud rate to 57600bps (115200bps and more will not work because of the SoftwareSerial library usage that produces errors at those rates)
- Using the AT commands, change the name of the HC-05/HC-06 module to "PRICEHAX TX V3" and its baudrate to 115200bps
- Program the arduino nano with the included sketch
- Download and install the [Android app](https://github.com/david4599/PricehaxBT/releases/latest), pair the dongle and enjoy ;-)
## Android app changelog
### Version 1.3 (21)
**Features**
- Added PP16 IR Protocol support (not full speed, see [Notes](https://github.com/david4599/PricehaxBT#notes)): [speed comparison video](https://youtu.be/DFfLOQh_ERs)
- Updated camera autofocus handling by using continuous focus modes if available
- Improved Bluetooth transmission reliability: the whole data (Pricehax header + PPM frame) is sent to the dongle with a basic checksum instead of using the PPM frame CRC that won't verify the header
**Fixes**
- Fixed bug that was sometimes stopping the image transmission just after the wake-up frame
- Fixed camera preview rotation in reverse landscape and reverse portrait orientations
- Fixed waiting time when sending an image in 2 parts (due to 64kB limit)
### Version 1.2 (20)
**Features**
- Added 1370 and 1371 ESL types (ST HD L Red and ST HD150 Red black housing versions)
- Added checksum verification of the ESL barcode
**Fixes**
- Fixed SmartTag HD T definition in information label
### Version 1.2 (19)
**Features**
@@ -20,12 +44,12 @@ Videos about this project are archived in this [playlist](https://www.youtube.co
### Version 1.1 (18)
**Features**
- Red (and yellow?) ESLs are supported: https://youtu.be/0PFMIiDluDw
- Red (yellow not tested) ESLs are supported: https://youtu.be/0PFMIiDluDw
- Improved Bluetooth transmission reliability: comparison of received checksum and calculated checksum by the dongle
- Added the possibility to stop current image send
- Added the possibility to stop sending the current image
**Fixes**
- Fixed ST HD150 and ST HD200 definition
- Fixed ST HD150 and ST HD200 definitions
### Version 1.1 (17)
@@ -34,25 +58,23 @@ Videos about this project are archived in this [playlist](https://www.youtube.co
- Display debug infos for 24h
- Hide debug infos feature added
- Ability to blink the green LED on ST ESLs (not working yet on some): https://youtu.be/b0Rn40alxQg
- Start autofocus by touching the preview screen on "PLID Scan" tab
- Start autofocus by touching the preview screen on "PLID Scan" tab (not working sometimes?)
- ESL types added (mainly graphic ESLs, not tested on the most of them but it should work)
- Automatically choose sending compressed or raw data to graphic ESLs
- Ability to force not compressed data sending to graphic ESLs
- The number of repeats of frames for graphic ESLs can be chosen (speed transmission vs reliability)
- The dongle can be manually connected or disconnected in "Config" page
- Ability to force sending uncompressed data to graphic ESLs
- The number of frames repeats for graphic ESLs can be chosen (speed transmission vs reliability)
- The dongle can be manually connected or disconnected in "Config" tab
**Fixes**
- Fixed some bugs and app crashes
## Notes
- Android app sources are included
- I didn't write the app, I only decompiled sources of the apk, imported them on Android Studio and rebuilt the app
- The app supports only Bluetooth communication
- Furrtek had already code Bluetooth feature in the original app, I just made some changes for that feature works with my dongle
- I am not a programming or electronic expert, so maybe I made some mistakes on coding or making the schematic...
- The implemented PP4C and PP16 protocols are not transmitting data at their maximum speeds. In theory, they should be around 10kbps for PP4C and 38kbps for PP16 (in reality, they are measured at 9kbps and 31kbps). Pricehax BT seems to do only 6kbps and 11kbps if my calculations are correct
- I didn't write the app, I just decompiled the sources from the apk (that's why the code is a bit of a mess). Then, I imported them on Android Studio and fixed the decompilation errors preventing the re-compilation
- The app supports Bluetooth communication only, the original communication using audio has been disabled (not the goal of this repo)
- Furrtek did code the Bluetooth feature in the original app, I just made some changes so the feature works with my dongle
- I am not a programming or electronic expert, so the code and the schematic might be better...
#
Both furrtek and me decline all responsability for any kind of issues related by an illegal use of this project.
Copyright (c) furrtek 2014 - david4599 2019
Copyright (c) Furrtek 2014 & david4599 2019 - 2022

View File

@@ -6,8 +6,8 @@ android {
applicationId "org.furrtek.pricehaxbt"
minSdkVersion 15
targetSdkVersion 28
versionCode 19
versionName "1.2"
versionCode 21
versionName "1.3"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {

View File

@@ -0,0 +1,8 @@
## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Thu Apr 20 13:49:00 GMT 2023
sdk.dir=C\:\\Users\\dandri\\AppData\\Local\\Android\\Sdk

View File

@@ -1,10 +1,13 @@
package org.furrtek.pricehaxbt;
import android.app.Activity;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PreviewCallback;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
@@ -18,29 +21,50 @@ public class CameraPreview extends SurfaceView implements Callback {
private Camera mCamera;
private SurfaceHolder mHolder = getHolder();
private PreviewCallback previewCallback;
private boolean oldAutoFocusMode;
private Context context;
private int cameraId;
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
checkOrientation(newConfig);
}
private void checkOrientation(Configuration newConfig) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
this.mCamera.setDisplayOrientation(90);
} else {
this.mCamera.setDisplayOrientation(0);
}
setCameraDisplayOrientation((Activity) this.context, this.cameraId, this.mCamera);
}
public CameraPreview(Context context, Camera camera, PreviewCallback previewCb, AutoFocusCallback autoFocusCb) {
super(context);
this.context = context;
this.mCamera = camera;
this.previewCallback = previewCb;
this.autoFocusCallback = autoFocusCb;
this.mHolder.addCallback(this);
this.mHolder.setType(3);
this.cameraId = getBackCameraId();
// The old mode uses the onAutoFocus function and makes a timer to try to focus periodically.
// The drawback is the hunting of the camera.
// This can be "smoothed" by using either FOCUS_MODE_CONTINUOUS_PICTURE or FOCUS_MODE_CONTINUOUS_VIDEO parameters instead (if available).
Camera.Parameters params = this.mCamera.getParameters();
this.oldAutoFocusMode = false;
if (params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
else if (params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
else {
this.oldAutoFocusMode = true;
}
this.mCamera.setParameters(params);
}
public boolean isOldAutoFocusMode() {
return this.oldAutoFocusMode;
}
public void surfaceCreated(SurfaceHolder holder) {
@@ -63,16 +87,61 @@ public class CameraPreview extends SurfaceView implements Callback {
} catch (Exception e) {
}
try {
if (getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
this.mCamera.setDisplayOrientation(90);
}
setCameraDisplayOrientation((Activity) this.context, this.cameraId, this.mCamera);
this.mCamera.setPreviewDisplay(this.mHolder);
this.mCamera.setPreviewCallback(this.previewCallback);
this.mCamera.startPreview();
this.mCamera.autoFocus(this.autoFocusCallback);
if (this.oldAutoFocusMode) {
this.mCamera.autoFocus(this.autoFocusCallback);
}
} catch (Exception e2) {
Log.d("DBG", "Error starting camera preview: " + e2.getMessage());
}
}
}
// Adapted from Android SDK API sample code
// https://github.com/Miserlou/Android-SDK-Samples/blob/master/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.java
private int getBackCameraId() {
int numberOfCameras = Camera.getNumberOfCameras();
CameraInfo cameraInfo = new CameraInfo();
for (int i = 0; i < numberOfCameras; i++) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
return i;
}
}
return 0;
}
// Sample code from Android Developer documentation
// https://developer.android.com/reference/android/hardware/Camera#setDisplayOrientation%28int%29
public static void setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
}

View File

@@ -26,7 +26,6 @@ import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.v4.media.TransportMediator;
import android.support.v4.view.MotionEventCompat;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -42,6 +41,7 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.Spinner;
@@ -54,6 +54,7 @@ import android.widget.ToggleButton;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -86,7 +87,8 @@ public class MainActivity extends Activity {
Integer PLType;
Activity at = this;
int donglever;
int rawmode;
boolean rawmode_forced;
boolean rawmode_enabled;
Handler handler = new Handler();
int hi;
InputStream imageStream = null;
@@ -134,6 +136,9 @@ public class MainActivity extends Activity {
private Camera mCamera;
private CameraPreview mPreview;
private boolean previewing = true;
private boolean pp16Mode = true;
private boolean oldAutoFocusMode = false;
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (MainActivity.this.previewing && MainActivity.this.tabHost.getCurrentTab() == 1 && MainActivity.this.mCamera != null) {
@@ -143,6 +148,8 @@ public class MainActivity extends Activity {
};
AutoFocusCallback autoFocusCB = new AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
// postDelayed() will call doAutoFocus() and onAutoFocus() will be called again from mCamera.autoFocus().
// This will create a timer that will readjust the focus every second.
MainActivity.this.autoFocusHandler.postDelayed(MainActivity.this.doAutoFocus, 1000);
}
};
@@ -152,19 +159,40 @@ public class MainActivity extends Activity {
Image barcode = new Image(size.width, size.height, "Y800");
barcode.setData(data);
if (MainActivity.this.scanner.scanImage(barcode) != 0) {
MainActivity.this.previewing = false;
MainActivity.this.mCamera.setPreviewCallback(null);
MainActivity.this.mCamera.stopPreview();
Iterator it = MainActivity.this.scanner.getResults().iterator();
while (it.hasNext()) {
String PLBarcode = ((Symbol) it.next()).getData();
setESLBarcode(PLBarcode);
if (setESLBarcode(PLBarcode)) {
MainActivity.this.previewing = false;
MainActivity.this.mCamera.setPreviewCallback(null);
MainActivity.this.mCamera.stopPreview();
MainActivity.this.scanButton.setText("Scan another ESL barcode?");
}
else {
Toast.makeText(MainActivity.this, "The scanned barcode is invalid!", Toast.LENGTH_SHORT).show();
}
}
}
}
};
private void setESLBarcode(String PLBarcode) {
private boolean isBarcodeChecksumValid(String barcode) {
byte[] barcodeBytes = barcode.getBytes(Charset.forName("UTF-8"));
int checksum = 0;
for (int i = 0; i < barcodeBytes.length - 1; i++) {
checksum += (int) barcodeBytes[i];
}
checksum %= 10;
return checksum == barcodeBytes[barcodeBytes.length - 1] - '0';
}
private boolean setESLBarcode(String PLBarcode) {
if (!isBarcodeChecksumValid(PLBarcode)) return false;
MainActivity.this.plID = (long) ((Integer.parseInt(PLBarcode.substring(2, 7)) << 16) + Integer.parseInt(PLBarcode.substring(7, 12)));
String PLSerial = Long.toHexString(MainActivity.this.plID);
MainActivity.this.PLType = Integer.valueOf(Integer.parseInt(PLBarcode.substring(12, 16)));
@@ -200,6 +228,13 @@ public class MainActivity extends Activity {
case 1217:
case 1628:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (Support for 1628 296x128) EXPERIMENTAL");
MainActivity.this.wi = 296;
MainActivity.this.hi = 128;
MainActivity.this.ESLType = 2;
MainActivity.this.ESLTypeColor = true;
break;
case 1265:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (Continuum E5 HCS)");
MainActivity.this.plBitDef = 2;
@@ -260,6 +295,13 @@ public class MainActivity extends Activity {
MainActivity.this.ESLTypeColor = true;
break;
case 1639:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD 1639 152x152) EXPERIMENTAL");
MainActivity.this.wi = 152;
MainActivity.this.hi = 152;
MainActivity.this.ESLType = 2;
MainActivity.this.ESLTypeColor = true;
break;
case 1318:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD M 208x112)");
@@ -288,8 +330,10 @@ public class MainActivity extends Activity {
MainActivity.this.hi = 128;
MainActivity.this.ESLType = 2;
break;
case 1370: // 2021 revision of the SmartTag HD L Red 296x128 - black housing
case 1328:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD L Red 296x128) EXPERIMENTAL");
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD L Red 296x128)");
MainActivity.this.wi = 296;
MainActivity.this.hi = 128;
MainActivity.this.ESLType = 2;
@@ -305,14 +349,14 @@ public class MainActivity extends Activity {
case 1348:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD T Red 296x128) EXPERIMENTAL");
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD T Red 264x176) EXPERIMENTAL");
MainActivity.this.wi = 264;
MainActivity.this.hi = 176;
MainActivity.this.ESLType = 2;
MainActivity.this.ESLTypeColor = true;
break;
case 1349:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD T Yellow 296x128) EXPERIMENTAL");
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD T Yellow 264x176) EXPERIMENTAL");
MainActivity.this.wi = 264;
MainActivity.this.hi = 176;
MainActivity.this.ESLType = 2;
@@ -341,6 +385,7 @@ public class MainActivity extends Activity {
MainActivity.this.hi = 480;
MainActivity.this.ESLType = 2;
break;
case 1371: // 2021 revision of the SmartTag HD150 Red 648x480 - black housing
case 1353:
case 1354:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD150 Red 648x480)");
@@ -378,6 +423,7 @@ public class MainActivity extends Activity {
break;
}
MainActivity.this.barcodeScanned = true;
return true;
}
private boolean repeatMode = false;
@@ -738,21 +784,28 @@ public class MainActivity extends Activity {
MainActivity.this.txtworkh.setText("Waking up ESL...");
}
});
PP4C.sendPP4C(MainActivity.this.at.getApplicationContext(), pingcode, 32, MainActivity.this.donglever, 50, MainActivity.audioTrack, 250);
int wakeupRepeat = 250;
if (MainActivity.this.pp16Mode) wakeupRepeat *= 2;
PPM.sendPPM(this, pingcode, MainActivity.this.pp16Mode, 32, MainActivity.this.donglever, 50, MainActivity.audioTrack, wakeupRepeat);
if (MainActivity.this.donglever == 2) {
SystemClock.sleep(2500);
} else if (MainActivity.this.donglever == 1) {
SystemClock.sleep(1800);
PP4C.sendPP4C(MainActivity.this.at.getApplicationContext(), pingcode, 32, MainActivity.this.donglever, 35, MainActivity.audioTrack, 0);
PPM.sendPPM(this, pingcode, MainActivity.this.pp16Mode, 32, MainActivity.this.donglever, 35, MainActivity.audioTrack, 0);
SystemClock.sleep(1800);
PP4C.sendPP4C(MainActivity.this.at.getApplicationContext(), pingcode, 32, MainActivity.this.donglever, 35, MainActivity.audioTrack, 0);
PPM.sendPPM(this, pingcode, MainActivity.this.pp16Mode, 32, MainActivity.this.donglever, 35, MainActivity.audioTrack, 0);
SystemClock.sleep(1800);
}
}
// Part:
// 0 = entire image
// 1 = 1st half of the image (top)
// 2 = 2nd half of the image (bottom)
private void sendStartCode(int part) {
byte[] startcode = new byte[54];
startcode[0] = (byte) -123;
@@ -766,7 +819,7 @@ public class MainActivity extends Activity {
startcode[8] = (byte) 0;
startcode[9] = (byte) 5;
startcode[10] = (byte) (datalen >> 8);
startcode[11] = (byte) (datalen & MotionEventCompat.ACTION_MASK);
startcode[11] = (byte) (datalen & 0xFF);
byte[] bArr = new byte[20];
if (part == 0) {
bArr = new byte[]{(byte) 0, (byte) compression_type, (byte) 2, (byte) (MainActivity.this.wi >> 8), (byte) (MainActivity.this.wi & 255), (byte) (MainActivity.this.hi >> 8), (byte) (MainActivity.this.hi & 255), (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) -120, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0};
@@ -792,7 +845,7 @@ public class MainActivity extends Activity {
MainActivity.this.txtworkh.setText("Start frame...");
}
});
PP4C.sendPP4C(MainActivity.this.at.getApplicationContext(), startcode, 34, MainActivity.this.donglever, 6, MainActivity.audioTrack, 10);
PPM.sendPPM(this, startcode, MainActivity.this.pp16Mode, 34, MainActivity.this.donglever, 6, MainActivity.audioTrack, 10);
if (MainActivity.this.donglever == 2) {
SystemClock.sleep(1000);
}
@@ -806,25 +859,25 @@ public class MainActivity extends Activity {
private void sendFrame(List<Byte> hexlist, int numframe) {
byte[] startcode = new byte[54];
startcode[0] = (byte) -123;
startcode[1] = (byte) ((int) (MainActivity.this.plID & 255));
startcode[2] = (byte) ((int) (MainActivity.this.plID >> 8));
startcode[3] = (byte) ((int) (MainActivity.this.plID >> 16));
startcode[4] = (byte) ((int) (MainActivity.this.plID >> 24));
startcode[5] = (byte) 52;
startcode[6] = (byte) 0;
startcode[7] = (byte) 0;
startcode[8] = (byte) 0;
startcode[9] = (byte) 32;
startcode[10] = (byte) (numframe >> 8);
startcode[11] = (byte) (numframe & MotionEventCompat.ACTION_MASK);
byte[] framecode = new byte[54];
framecode[0] = (byte) -123;
framecode[1] = (byte) ((int) (MainActivity.this.plID & 255));
framecode[2] = (byte) ((int) (MainActivity.this.plID >> 8));
framecode[3] = (byte) ((int) (MainActivity.this.plID >> 16));
framecode[4] = (byte) ((int) (MainActivity.this.plID >> 24));
framecode[5] = (byte) 52;
framecode[6] = (byte) 0;
framecode[7] = (byte) 0;
framecode[8] = (byte) 0;
framecode[9] = (byte) 32;
framecode[10] = (byte) (numframe >> 8);
framecode[11] = (byte) (numframe & 0xFF);
for (int cp = 0; cp < 40; cp++) {
startcode[cp + 12] = ((Byte) hexlist.get((numframe * 40) + cp)).byteValue();
framecode[cp + 12] = ((Byte) hexlist.get((numframe * 40) + cp)).byteValue();
}
byte[] FrameCRC = CRCCalc.GetCRC(startcode, 52);
startcode[52] = FrameCRC[0];
startcode[53] = FrameCRC[1];
byte[] FrameCRC = CRCCalc.GetCRC(framecode, 52);
framecode[52] = FrameCRC[0];
framecode[53] = FrameCRC[1];
if (!threadRunning) return;
MainActivity.this.runOnUiThread(new Runnable() {
@@ -832,7 +885,7 @@ public class MainActivity extends Activity {
MainActivity.this.txtworkh.setText("Data frame " + MainActivity.this.y + "/" + MainActivity.this.ymax);
}
});
PP4C.sendPP4C(MainActivity.this.at.getApplicationContext(), startcode, 54, MainActivity.this.donglever, 1, MainActivity.audioTrack, MainActivity.this.nbRepeatFrame);
PPM.sendPPM(this, framecode, MainActivity.this.pp16Mode, 54, MainActivity.this.donglever, 1, MainActivity.audioTrack, MainActivity.this.nbRepeatFrame);
if (MainActivity.this.donglever == 2) {
SystemClock.sleep(550);
}
@@ -862,7 +915,7 @@ public class MainActivity extends Activity {
MainActivity.this.txtworkh.setText("Verify frame...");
}
});
PP4C.sendPP4C(MainActivity.this.at.getApplicationContext(), vercode, 30, MainActivity.this.donglever, 10, MainActivity.audioTrack, 50);
PPM.sendPPM(this, vercode, MainActivity.this.pp16Mode, 30, MainActivity.this.donglever, 10, MainActivity.audioTrack, 50);
if (MainActivity.this.donglever != 3) {
SystemClock.sleep(2000);
}
@@ -871,11 +924,12 @@ public class MainActivity extends Activity {
private void sendImage(Bitmap image, int size_raw, int imagePart) {
MainActivity mainActivity;
private List<Byte> convertImage(Bitmap image, int size_raw) {
int size_compressed;
List<Byte> hexlist;
MainActivity.this.rawbitstream = new byte[size_raw];
MainActivity.this.idx = 0;
MainActivity.this.compressed = new ArrayList<Integer>();
@@ -883,27 +937,31 @@ public class MainActivity extends Activity {
StringBuilder bstr_compressed = new StringBuilder();
// Add black/white colors first
convertMonochrome(image, false);
if (!threadRunning) return;
if (!threadRunning) return null;
// Then add red color if compatible ESL
if (MainActivity.this.ESLTypeColor) {
convertMonochrome(image, true);
if (!threadRunning) return;
if (!threadRunning) return null;
}
RLECompress();
if (!threadRunning) return;
if (!threadRunning) return null;
Hexadecimalifying(bstr_raw, bstr_compressed);
size_compressed = bstr_compressed.toString().length();
if (size_compressed < size_raw && rawmode == 0) { // Compressed data mode
if (size_compressed < size_raw && !rawmode_forced) { // Compressed data mode
hexlist = createCompressedHexList(bstr_compressed);
rawmode_enabled = false;
}
else { // raw data mode
hexlist = createRawHexList(bstr_raw);
rawmode_enabled = true;
}
@@ -918,8 +976,14 @@ public class MainActivity extends Activity {
}
});
return hexlist;
}
private void sendImage(List<Byte> hexlist, int imagePart) {
MainActivity mainActivity;
sendPingCode();
if (!threadRunning) return;
@@ -937,20 +1001,13 @@ public class MainActivity extends Activity {
}
sendVerifCode();
if (!threadRunning) return;
}
public void convertImage() {
public void convertAndSendImage() {
new Thread(new Runnable() {
public void run() {
MainActivity mainActivity;
MainActivity.this.txtworkh = (TextView) MainActivity.this.findViewById(R.id.txtwork);
MainActivity.this.pgb = (ProgressBar) MainActivity.this.findViewById(R.id.pgb1);
MainActivity.this.imgbmp = (ImageView) MainActivity.this.findViewById(R.id.imgvbmp);
@@ -980,10 +1037,11 @@ public class MainActivity extends Activity {
StringBuilder bstr_compressed = new StringBuilder();
// Add black/white colors first
convertMonochrome(scaledimage, false);
if (!threadRunning) return;
// Then add red color if compatible ESL
if (MainActivity.this.ESLTypeColor) {
convertMonochrome(scaledimage, true);
if (!threadRunning) return;
@@ -998,8 +1056,14 @@ public class MainActivity extends Activity {
size_compressed = bstr_compressed.toString().length();
if ((size_compressed/8) <= 65535 && rawmode == 0 || ((size_raw/8) <= 65535 && rawmode == 1)) {
if (size_compressed < size_raw && rawmode == 0) {
// It seems an image cannot be sent if its length (compressed or not) is greater than 64kB.
// This is because the "datalen" field (bytes 10 and 11) of the start code is only 2 bytes (unless we can have more?)
// A workaround is to split the image e.g. in half and send the parts separately.
// So the image has to be converted and compressed a first time to know if we need to split it.
// If so, we split it, convert and compress the parts again and send them.
if ((size_compressed/8) <= 65535 && !rawmode_forced || ((size_raw/8) <= 65535 && rawmode_forced)) {
if (size_compressed < size_raw && !rawmode_forced) {
hexlist = createCompressedHexList(bstr_compressed);
}
else {
@@ -1018,26 +1082,7 @@ public class MainActivity extends Activity {
}
});
sendPingCode();
if (!threadRunning) return;
sendStartCode(0);
if (!threadRunning) return;
MainActivity.this.ymax = padded_datalen / 40;
MainActivity.this.y = 0;
while (MainActivity.this.y < MainActivity.this.ymax) {
sendFrame(hexlist, MainActivity.this.y);
if (!threadRunning) return;
mainActivity = MainActivity.this;
mainActivity.y++;
}
sendVerifCode();
if (!threadRunning) return;
sendImage(hexlist, 0);
}
else {
MainActivity.this.handler.post(new Runnable() {
@@ -1053,15 +1098,23 @@ public class MainActivity extends Activity {
e22.printStackTrace();
}
MainActivity.this.scaledimagepart1=Bitmap.createBitmap(MainActivity.this.scaledimage, 0,0, w, h/2);
MainActivity.this.scaledimagepart2=Bitmap.createBitmap(MainActivity.this.scaledimage, 0,h/2, w, h/2);
MainActivity.this.scaledimagepart1 = Bitmap.createBitmap(MainActivity.this.scaledimage, 0,0, w, h / 2);
MainActivity.this.scaledimagepart2 = Bitmap.createBitmap(MainActivity.this.scaledimage, 0,h / 2, w, h / 2);
MainActivity.this.rawbitstream = new byte[size_raw/2];
sendImage(scaledimagepart1, (size_raw/2), 1);
MainActivity.this.rawbitstream = new byte[size_raw / 2];
hexlist = convertImage(scaledimagepart1, (size_raw / 2));
sendImage(hexlist, 1);
if (!threadRunning) return;
for (int i = 45; i > 0; i--) {
// We need to wait because the second part cannot be sent while the ESL is refreshing the first part
int timer = 35;
// If the first part was sent compressed, we have to wait even more since decompression takes a lot of time
if (!rawmode_enabled) timer = 50;
for (int i = timer; i > 0; i--) {
final int sec = i;
MainActivity.this.handler.post(new Runnable() {
public void run() {
@@ -1075,11 +1128,12 @@ public class MainActivity extends Activity {
}
}
MainActivity.this.rawbitstream = new byte[size_raw/2];
sendImage(scaledimagepart2, size_raw/2, 2);
if (!threadRunning) return;
}
MainActivity.this.rawbitstream = new byte[size_raw / 2];
hexlist = convertImage(scaledimagepart2, size_raw / 2);
sendImage(hexlist, 2);
}
if (!threadRunning) return;
MainActivity.this.handler.post(new Runnable() {
@@ -1097,13 +1151,15 @@ public class MainActivity extends Activity {
}).start();
}
public boolean isSendImageThreadRunning() {
return threadRunning;
}
public void setScanPreview() {
this.autoFocusHandler = new Handler();
this.mCamera = getCameraInstance();
Parameters params = this.mCamera.getParameters();
params.setPreviewSize(640, 480);
this.mCamera.setParameters(params);
this.mPreview = new CameraPreview(this, this.mCamera, this.previewCb, this.autoFocusCB);
this.oldAutoFocusMode = this.mPreview.isOldAutoFocusMode();
this.preview = (FrameLayout) findViewById(R.id.cameraPreview);
this.preview.addView(this.mPreview);
this.scanner = new ImageScanner();
@@ -1117,25 +1173,31 @@ public class MainActivity extends Activity {
this.scanButton = (Button) findViewById(R.id.scan_button);
this.scanButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String scanBarcodeString = "Scan ESL barcode";
if (MainActivity.this.mCamera == null) {
MainActivity.this.previewing = false;
MainActivity.this.mCamera = MainActivity.getCameraInstance();
MainActivity.this.mCamera.setPreviewCallback(MainActivity.this.previewCb);
MainActivity.this.mCamera.startPreview();
MainActivity.this.previewing = true;
MainActivity.this.mCamera.autoFocus(MainActivity.this.autoFocusCB);
((FrameLayout) MainActivity.this.findViewById(R.id.cameraPreview)).addView(MainActivity.this.mPreview);
}
if (MainActivity.this.barcodeScanned) {
MainActivity.this.barcodeScanned = false;
MainActivity.this.scaneibarcode.setText("Scan ESL barcode");
MainActivity.this.mCamera.setPreviewCallback(MainActivity.this.previewCb);
MainActivity.this.mCamera.startPreview();
MainActivity.this.previewing = true;
MainActivity.this.mCamera.autoFocus(MainActivity.this.autoFocusCB);
MainActivity.this.scaneibarcode.setText(scanBarcodeString);
MainActivity.this.ESLType = 0;
}
MainActivity.this.scanButton.setText(scanBarcodeString);
MainActivity.this.mCamera.setPreviewCallback(MainActivity.this.previewCb);
MainActivity.this.mCamera.startPreview();
MainActivity.this.previewing = true;
if (MainActivity.this.oldAutoFocusMode) {
MainActivity.this.mCamera.autoFocus(MainActivity.this.autoFocusCB);
}
}
});
}
@@ -1224,8 +1286,8 @@ public class MainActivity extends Activity {
((TextView) MainActivity.this.findViewById(R.id.tv_nb_repeat)).setText("Frames repeated " + (nbRepeatFrame) + " time" + (p) + " (Lower is faster but less reliable !)");
nbRepeatControl.setProgress(nbRepeatFrame - 1);
this.rawmode = this.settings.getInt("rawmode", 0);
if (this.rawmode == 1) {
this.rawmode_forced = this.settings.getBoolean("rawmode_forced", false);
if (this.rawmode_forced) {
((CheckBox) findViewById(R.id.force_raw_sending_mode)).setChecked(true);
}
@@ -1323,6 +1385,9 @@ public class MainActivity extends Activity {
public void onClick(View v) {
Button btnsend = (Button) v;
String buttonText = btnsend.getText().toString();
String cancelText = "Cancel now";
if (buttonText.equals("Send image")) {
if (!isDonglePaired()) {
return;
@@ -1338,15 +1403,24 @@ public class MainActivity extends Activity {
}
MainActivity.this.threadRunning = true;
btnsend.setText("Stop send");
convertImage();
btnsend.setText(cancelText);
convertAndSendImage();
}
else if (buttonText.equals("Stop send")) {
else if (buttonText.equals(cancelText)) {
MainActivity.this.threadRunning = false;
//SystemClock.sleep(200);
btnsend.setText("Send image");
MainActivity.this.txtworkh.setText("Stopped successfully !");
MainActivity.this.pgb.setProgress(100);
new Timer().schedule(new TimerTask() {
public void run() {
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
MainActivity.this.btnsendimg.setText("Send image");
MainActivity.this.txtworkh.setText("Transmission cancelled !");
MainActivity.this.pgb.setProgress(100);
}
});
}
}, 200);
}
}
});
@@ -1396,19 +1470,35 @@ public class MainActivity extends Activity {
String barcode = barcodeedittext.getText().toString().toUpperCase();
if (barcode.length() == 17) {
if (Character.isLetter(barcode.charAt(0)) && TextUtils.isDigitsOnly(barcode.substring(1, 17))) {
setESLBarcode(barcode);
if (!setESLBarcode(barcode)) {
Toast.makeText(MainActivity.this, "The barcode is invalid!", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(MainActivity.this, "The barcode entered is invalid!", Toast.LENGTH_SHORT).show();
}
}
else {
Toast.makeText(MainActivity.this, "The barcode entered is not the right length!", Toast.LENGTH_SHORT).show();
Toast.makeText(MainActivity.this, "The barcode is not 17 characters long!", Toast.LENGTH_SHORT).show();
}
}
});
RadioGroup radppmversion = findViewById(R.id.ppmversion);
radppmversion.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
MainActivity.this.pp16Mode = checkedId == R.id.radpp16;
Editor editor = getSharedPreferences("Pricehax", 0).edit();
editor.putBoolean("pp16Mode", MainActivity.this.pp16Mode);
editor.commit();
}
});
radppmversion.check(R.id.radpp16);
this.pp16Mode = this.settings.getBoolean("pp16Mode", true);
if (!this.pp16Mode) {
radppmversion.check(R.id.radpp4c);
}
}
@Override
@@ -1463,12 +1553,15 @@ public class MainActivity extends Activity {
this.autoFocusHandler = new Handler();
this.mCamera = getCameraInstance();
Parameters params = this.mCamera.getParameters();
params.setPreviewSize(640, 480);
this.mCamera.setParameters(params);
this.mPreview = new CameraPreview(this, this.mCamera, this.previewCb, this.autoFocusCB);
this.oldAutoFocusMode = this.mPreview.isOldAutoFocusMode();
this.preview = (FrameLayout) findViewById(R.id.cameraPreview);
this.preview.addView(this.mPreview);
this.mCamera.autoFocus(MainActivity.this.autoFocusCB);
if (this.oldAutoFocusMode) {
this.mCamera.autoFocus(this.autoFocusCB);
}
}
catch (RuntimeException e) {
e.printStackTrace();
@@ -1482,9 +1575,9 @@ public class MainActivity extends Activity {
this.autoFocusHandler = new Handler();
this.mCamera = getCameraInstance();
Parameters params = this.mCamera.getParameters();
params.setPreviewSize(640, 480);
this.mCamera.setParameters(params);
this.mPreview = new CameraPreview(this, this.mCamera, this.previewCb, this.autoFocusCB);
this.oldAutoFocusMode = this.mPreview.isOldAutoFocusMode();
this.preview = (FrameLayout) findViewById(R.id.cameraPreview);
this.preview.addView(this.mPreview);
}
@@ -1501,9 +1594,9 @@ public class MainActivity extends Activity {
}
}
public void doAutoFocusOnTouch(View view) {
if (this.previewing == true) {
this.mCamera.autoFocus(MainActivity.this.autoFocusCB);
public void doOldAutoFocusOnTouch(View view) {
if (this.previewing && this.oldAutoFocusMode) {
this.mCamera.autoFocus(this.autoFocusCB);
}
}
@@ -1522,14 +1615,11 @@ public class MainActivity extends Activity {
}
public void setrawmode(View view) {
if (((CheckBox) findViewById(getResources().getIdentifier("force_raw_sending_mode", "id", getPackageName()))).isChecked()) {
this.rawmode = 1;
} else {
this.rawmode = 0;
}
CheckBox cbForceRawMode = findViewById(getResources().getIdentifier("force_raw_sending_mode", "id", getPackageName()));
this.rawmode_forced = cbForceRawMode.isChecked();
Editor editor = MainActivity.this.getSharedPreferences("Pricehax", 0).edit();
editor.putInt("rawmode", this.rawmode);
editor.putBoolean("rawmode_forced", this.rawmode_forced);
editor.commit();
}
@@ -1626,9 +1716,12 @@ public class MainActivity extends Activity {
return;
}
this.repeatModeDM = ((CheckBox) findViewById(R.id.chkrepeatdm)).isChecked();
int timerPeriod = 4500;
int timerPeriod = 3900;
if (MainActivity.this.pp16Mode) timerPeriod = 3600;
// Shorter timer period for green led flash
if (((RadioButton) findViewById(R.id.raddur10)).isChecked() || ((RadioButton) findViewById(R.id.raddur11)).isChecked()) {
timerPeriod = 2000;
timerPeriod = 1500;
}
if (this.repeatModeDM) {
if (this.timer != null) {
@@ -1686,7 +1779,10 @@ public class MainActivity extends Activity {
byte[] hcode = new byte[18];
int dispDuration = 0;
int dispDMDuration = 0;
int nbrepeat = 400;
if (MainActivity.this.pp16Mode) nbrepeat *= 2;
EditText metxtPage = (EditText) findViewById(R.id.etxtPage);
if (metxtPage.getText() == null) {
metxtPage.setText("0");
@@ -1732,21 +1828,25 @@ public class MainActivity extends Activity {
hcode[6] = (byte) 243;
dispDMDuration = (dispDMPage[0] & 15);
} else if (dispDuration == 10) {
hcode[1] = (byte) ((int) (MainActivity.this.plID & 255));
hcode[2] = (byte) ((int) (MainActivity.this.plID >> 8));
hcode[3] = (byte) ((int) (MainActivity.this.plID >> 16));
hcode[4] = (byte) ((int) (MainActivity.this.plID >> 24));
//hcode[1] = (byte) ((int) (MainActivity.this.plID & 255));
//hcode[2] = (byte) ((int) (MainActivity.this.plID >> 8));
//hcode[3] = (byte) ((int) (MainActivity.this.plID >> 16));
//hcode[4] = (byte) ((int) (MainActivity.this.plID >> 24));
hcode[6] = (byte) 73;
dispDMDuration = (dispDMPage[0] & 15);
nbrepeat = 150;
if (MainActivity.this.pp16Mode) nbrepeat *= 2;
} else if (dispDuration == 11) {
hcode[1] = (byte) ((int) (MainActivity.this.plID & 255));
hcode[2] = (byte) ((int) (MainActivity.this.plID >> 8));
hcode[3] = (byte) ((int) (MainActivity.this.plID >> 16));
hcode[4] = (byte) ((int) (MainActivity.this.plID >> 24));
//hcode[1] = (byte) ((int) (MainActivity.this.plID & 255));
//hcode[2] = (byte) ((int) (MainActivity.this.plID >> 8));
//hcode[3] = (byte) ((int) (MainActivity.this.plID >> 16));
//hcode[4] = (byte) ((int) (MainActivity.this.plID >> 24));
hcode[6] = (byte) 201;
dispDMDuration = (dispDMPage[0] & 15);
nbrepeat = 150;
if (MainActivity.this.pp16Mode) nbrepeat *= 2;
} else {
hcode[6] = (byte) (((dispDMPage[0] & 15) << 3) | 1);
}
@@ -1757,7 +1857,7 @@ public class MainActivity extends Activity {
byte[] FrameCRC = CRCCalc.GetCRC(hcode, 11);
hcode[11] = FrameCRC[0];
hcode[12] = FrameCRC[1];
PP4C.sendPP4C(getApplicationContext(), hcode, 13, MainActivity.this.donglever, 60, audioTrack, nbrepeat);
PPM.sendPPM(MainActivity.this, hcode, MainActivity.this.pp16Mode, 13, MainActivity.this.donglever, 60, audioTrack, nbrepeat);
}
}).start();
}
@@ -1774,7 +1874,7 @@ public class MainActivity extends Activity {
byte[] FrameCRC = CRCCalc.GetCRC(hcode, 28);
hcode[28] = FrameCRC[0];
hcode[29] = FrameCRC[1];
PP4C.sendPP4C(getApplicationContext(), hcode, 30, this.donglever, 60, audioTrack, 0);
PPM.sendPPM(MainActivity.this, hcode, MainActivity.this.pp16Mode, 30, this.donglever, 60, audioTrack, 0);
plHexString = "";
for (int cp = 0; cp < 30; cp++) {
plHexString = plHexString + String.format("%02X", new Object[]{Byte.valueOf(hcode[cp])});
@@ -1814,7 +1914,7 @@ public class MainActivity extends Activity {
byte[] FrameCRC = CRCCalc.GetCRC(hcode, 9);
hcode[9] = FrameCRC[0];
hcode[10] = FrameCRC[1];
PP4C.sendPP4C(getApplicationContext(), hcode, 11, MainActivity.this.donglever, 60, audioTrack, 100);
PPM.sendPPM(MainActivity.this, hcode, false, 11, MainActivity.this.donglever, 60, audioTrack, 100);
}
}
}).start();
@@ -1878,7 +1978,7 @@ public class MainActivity extends Activity {
((TextView) findViewById(R.id.label_dbgbs)).setText("Segment bitstream: " + plHexString);
}
});
PP4C.sendPP4C(getApplicationContext(), hcode, 43, MainActivity.this.donglever, 15, audioTrack, 100);
PPM.sendPPM(MainActivity.this, hcode, false, 43, MainActivity.this.donglever, 15, audioTrack, 100);
}
}).start();
}
@@ -1925,6 +2025,6 @@ public class MainActivity extends Activity {
plHexString = plHexString + String.format("%02X", new Object[]{Byte.valueOf(hcode[cp])});
}
((TextView) findViewById(R.id.label_dbgbs)).setText("Data update: " + plHexString);
PP4C.sendPP4C(getApplicationContext(), hcode, 43, this.donglever, 15, audioTrack, 0);
PPM.sendPPM(MainActivity.this, hcode, false, 43, this.donglever, 15, audioTrack, 0);
}
}

View File

@@ -1,31 +1,44 @@
package org.furrtek.pricehaxbt;
import android.content.Context;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.AudioTrack.OnPlaybackPositionUpdateListener;
import android.support.v4.view.MotionEventCompat;
import android.util.Log;
import java.io.IOException;
public class PP4C {
public class PPM {
static AudioManager mAudioManager;
static int origVolume;
static double[] sample = new double[48000];
private static void sendData(byte[] tmp) {
Log.d("BT SEND", "SENDING DATA...");
private static void sendData(MainActivity mainActivity, byte[] tmp) {
//Log.d("BT SEND", "SENDING DATA...");
try {
byte[] buffer = new byte[128];
int timeout = 20;
int timeoutRead = 2000;
do {
MainActivity.outStream.write(tmp);
do {
} while (MainActivity.inStream.available() <= 0);
int res = MainActivity.inStream.read(buffer);
for (int i = timeoutRead; i > 0; i--) {
if (MainActivity.inStream.available() > 0) {
break;
}
if (!mainActivity.isSendImageThreadRunning()) {
return;
}
try {
Thread.sleep(1);
} catch (InterruptedException e22) {
e22.printStackTrace();
}
}
MainActivity.inStream.read(buffer);
timeout--;
} while (buffer[0] == (byte) 49 && timeout >= 0);
@@ -36,25 +49,40 @@ public class PP4C {
}
}
static void sendPP4C(Context context, byte[] hcode, int length, int donglever, int rpt, AudioTrack audioTrack, int nbRepeatFrame) {
static void sendPPM(MainActivity mainActivity, byte[] hcode, boolean pp16Mode, int length, int donglever, int rpt, AudioTrack audioTrack, int nbRepeatFrame) {
double[] pcode = new double[256];
byte[] generatedSnd = new byte[96000];
if (donglever == 3) {
int cp;
byte[] btdata = new byte[58];
btdata[0] = (byte) -86;
byte[] btdata = new byte[60];
btdata[0] = (byte) 170; // The first 4 bytes are only used by the dongle, they are not part of the PPM protocol
if (pp16Mode) {
btdata[0] = (byte) 171;
}
btdata[1] = (byte) (nbRepeatFrame >> 8);
btdata[2] = (byte) (nbRepeatFrame & MotionEventCompat.ACTION_MASK);
btdata[2] = (byte) (nbRepeatFrame & 0xFF);
btdata[3] = (byte) length;
for (cp = 0; cp < length; cp++) {
btdata[cp + 4] = hcode[cp];
}
String plHexString = "";
for (cp = 0; cp < btdata.length; cp++) {
plHexString = plHexString + String.format("%02X", new Object[]{Byte.valueOf(btdata[cp])});
int basicChecksum = 0;
for(byte i = 0; i < 4 + length; i++){
basicChecksum += (btdata[i] & 0xFF); // " & 0xFF" is here to convert to unsigned byte because btdata[i] is a signed byte
}
sendData(btdata);
Log.d("DATA", "SENT " + plHexString);
// Should not overflow since the max possible checksum should be less than 16384 (if all bytes are 255, 255 * 64 = 16320)
btdata[4 + length] = (byte) (basicChecksum >> 8);
btdata[4 + length + 1] = (byte) (basicChecksum & 0xFF);
//String plHexString = "";
//for (cp = 0; cp < btdata.length; cp++) {
// plHexString = plHexString + String.format("%02X", new Object[]{Byte.valueOf(btdata[cp])});
//}
sendData(mainActivity, btdata);
//Log.d("DATA", "SENT " + plHexString);
}
if (donglever < 3) {
int i;
@@ -78,7 +106,7 @@ public class PP4C {
pcode[(i * 4) + 3] = (double) ((hcode[i] >> 6) & 3);
}
}
mAudioManager = (AudioManager) context.getSystemService("audio");
mAudioManager = (AudioManager) mainActivity.at.getApplicationContext().getSystemService("audio");
origVolume = mAudioManager.getStreamVolume(3);
mAudioManager.setStreamVolume(3, (int) (((double) mAudioManager.getStreamMaxVolume(3)) * (((double) (((float) MainActivity.transmitVolume) / 100.0f)) + 0.6d)), 0);
for (int r = 0; r < 48000; r++) {
@@ -165,7 +193,7 @@ public class PP4C {
} catch (IllegalStateException e) {
e.printStackTrace();
}
PP4C.mAudioManager.setStreamVolume(3, PP4C.origVolume, 0);
PPM.mAudioManager.setStreamVolume(3, PPM.origVolume, 0);
}
public void onPeriodicNotification(AudioTrack track) {

View File

@@ -112,7 +112,7 @@
android:id="@id/raddur7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:layout_marginBottom="40dp"
android:text="1h30"
android:textColor="@color/textcolor" />
@@ -120,7 +120,7 @@
android:id="@id/raddur8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Display debug infos"
android:text="Show debug infos *"
android:textColor="@color/textcolor"
android:textSize="12.0sp" />
@@ -129,7 +129,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:text="Hide debug infos"
android:text="Hide debug infos *"
android:textColor="@color/textcolor"
android:textSize="12.0sp" />
@@ -137,7 +137,7 @@
android:id="@id/raddur10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SmartTag LED low flash *"
android:text="SmartTag LED weak flash **"
android:textColor="@color/textcolor"
android:textSize="12.0sp" />
@@ -148,7 +148,7 @@
android:layout_below="@id/chkrepeatdm"
android:layout_alignStart="@id/radgDurations"
android:layout_alignLeft="@id/radgDurations"
android:text="SmartTag LED high flash *"
android:text="SmartTag LED strong flash **"
android:textColor="@color/textcolor"
android:textSize="12.0sp" />
</RadioGroup>
@@ -207,14 +207,25 @@
android:textColor="@color/textcolor" />
<TextView
android:id="@id/txtvledflash"
android:id="@id/txtdebug"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/radgDurations"
android:layout_alignLeft="@id/txtvPageDur"
android:layout_alignRight="@id/button2"
android:layout_marginTop="16.0dip"
android:text="* Not working yet on some SmartTag ESLs"
android:text="* DotMatrix ESLs only"
android:textColor="@color/textcolor"
android:textSize="11.0sp" />
<TextView
android:id="@id/txtvledflash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/txtdebug"
android:layout_alignLeft="@id/txtvPageDur"
android:layout_alignRight="@id/button2"
android:text="** Not working yet on some SmartTag ESLs"
android:textColor="@color/textcolor"
android:textSize="11.0sp" />
@@ -226,7 +237,7 @@
android:layout_alignLeft="@id/txtvPageDur"
android:layout_alignRight="@id/button2"
android:layout_marginTop="6.0dip"
android:text="Use the 'Page number' field to define the number of times the LED will flash (0 = always)"
android:text="Use the 'Page number' field to define the number of times the LED will flash (between 1 and 9, 0 = always)"
android:textColor="@color/textcolor"
android:textSize="11.0sp" />
</RelativeLayout>

View File

@@ -17,11 +17,11 @@
android:layout_height="192.0dip"
android:layout_centerHorizontal="true"
android:layout_marginTop="8.0dip"
android:onClick="doAutoFocusOnTouch"/>
android:onClick="doOldAutoFocusOnTouch"/>
<Button
android:id="@id/scan_button"
android:layout_width="200.0dip"
android:layout_width="264.0dip"
android:layout_height="50.0dip"
android:layout_below="@id/cameraPreview"
android:layout_centerHorizontal="true"

View File

@@ -33,14 +33,50 @@
android:layout_alignParentLeft="true"
android:layout_marginLeft="8.0dip"
android:layout_marginTop="8.0dip"
android:layout_marginBottom="16.0dip"
android:text="Segment bitstream: "
android:textColor="@color/textcolor" />
<TextView
android:id="@+id/tvppmversion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/label_dbgbs"
android:layout_centerHorizontal="true"
android:text="PPM mode for DotMatrix ESLs:"
android:textColor="@color/textcolor" />
<RadioGroup
android:id="@+id/ppmversion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tvppmversion"
android:layout_marginTop="8.0dip"
android:layout_marginBottom="32.0dip"
android:layout_centerHorizontal="true">
<RadioButton
android:id="@+id/radpp16"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="PP16 (faster)"
android:textColor="@color/textcolor" />
<RadioButton
android:id="@+id/radpp4c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PP4C"
android:textColor="@color/textcolor" />
</RadioGroup>
<CheckBox
android:id="@id/chk_donglever"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/label_dbgbs"
android:layout_below="@id/ppmversion"
android:layout_centerHorizontal="true"
android:layout_marginTop="25.0dip"
android:checked="true"
@@ -117,7 +153,7 @@
android:id="@+id/btnPairBT"
android:layout_below="@id/tvbtt"
android:layout_centerHorizontal="true"
android:layout_marginTop="32dp"
android:layout_marginTop="16dp"
android:onClick="pairBTTransmitter"
android:textColor="@color/textcolor" />
</RelativeLayout>

View File

@@ -82,7 +82,7 @@
android:layout_centerHorizontal="true"
android:layout_marginTop="48.0dip"
android:gravity="center_horizontal"
android:text="Version 1.2 BT (19)"
android:text="Version 1.3 BT (21)"
android:textColor="@color/textcolor"
android:textSize="15.0sp" />

View File

@@ -127,4 +127,5 @@
<item type="id" name="txtwork" />
<item type="id" name="force_raw_sending_mode" />
<item type="id" name="action_settings" />
<item type="id" name="txtdebug" />
</resources>

View File

@@ -1,161 +0,0 @@
/*
* Pricehax BT IR dongle (for Pricehax Version 1.1 BT (18.0))
* furrtek 2014
* david4599 2019
*/
#define F_CPU 16000000L
#define NOP __asm__ __volatile__ ("nop\n\t")
#define BT_RXPIN 10
#define BT_TXPIN 12
#define LEDPIN 2
#include <SoftwareSerial.h>
SoftwareSerial BT(BT_RXPIN, BT_TXPIN);
boolean sendframe = false;
uint8_t b, cnt, data[54], datalength;
uint16_t s, sym_count, r, repeat = 0, result, poly;
void CRCCalc() {
result = 0x8408;
poly = 0x8408;
for (int i = 0; i < datalength-2; i++) {
result ^= data[i];
for (int j = 0; j < 8; j++) {
if (result & 1) {
result >>= 1;
result ^= poly;
}
else {
result >>= 1;
}
}
}
}
void burst() { // Create 40us burst at around 1.25MHz
for(int i = 0; i < 50; i++){
PORTD ^= (1 << LEDPIN);
NOP;
NOP;
PORTD ^= (1 << LEDPIN);
NOP;
}
}
void getData() { // Get frames from Pricehax sent over Bluetooth
if (BT.read() == 170) { // First byte sent is an ID to avoid unwanted values sent from the bluetooth module itself
cnt = 0;
// Reset data array
memset(data, 0, sizeof(data));
_delay_ms(1);
while (BT.available()) {
if (cnt == 0) { // Read header data
// Get the number of times the frame will be transmitted
repeat = (byte) BT.read() << 8;
repeat |= (byte) BT.read();
// Get the data length of the frame
datalength = (byte) BT.read();
}
if (cnt == datalength) { // Read unneeded bytes after the end of the frame
while (BT.available()) {
BT.read();
}
break;
}
data[cnt] = (byte) BT.read(); // Get the next byte of the frame
cnt++;
}
sendframe = true;
}
}
void IRSend() { // Send a frame following the IR protocol
sym_count = datalength << 2;
for (r = 0; r < (unsigned int) repeat; r++) {
for (s = 0; s < sym_count; s++) {
if ((s & 3) == 0) {
b = data[s >> 2];
}
burst();
switch(b & 3) {
case 0:
_delay_us(56);
break;
case 1:
_delay_us(237);
break;
case 2:
_delay_us(117);
break;
case 3:
_delay_us(178);
break;
}
b >>= 2;
}
burst();
_delay_ms(2);
}
}
void setup() {
DDRD |= (1 << LEDPIN); // Define led pin as output
PORTD &= (0 << LEDPIN); // Set led pin to low state
BT.begin(57600);
}
void loop() {
if (BT.available()) {
getData();
CRCCalc();
if (data[datalength-2] != (uint8_t) result || data[datalength-1] != (uint8_t) (result >> 8)) {
BT.write("1");
sendframe = false;
}
}
if (sendframe) {
IRSend();
BT.write("0"); // Send an answer to Pricehax once the frame is transmitted
sendframe = false;
}
_delay_ms(1);
}

View File

@@ -0,0 +1,394 @@
/*
* Pricehax BT IR dongle (for Pricehax Version 1.3 BT (21))
* furrtek 2014
* david4599 2019 - 2022
*/
#define F_CPU 16000000UL
#define NOP __asm__ __volatile__ ("nop")
#define BT_RX_PIN 10
#define BT_TX_PIN 12
#define LED_PIN 2
#define PULSES_PP4C 50
#define PULSES_PP16 26
#define FRAME_MAX_LENGTH 58
// Specific to Pricehax
#define PHX_CODE_PP4C 170
#define PHX_CODE_PP16 171
#include <SoftwareSerial.h>
SoftwareSerial BT(BT_RX_PIN, BT_TX_PIN);
uint8_t gData[FRAME_MAX_LENGTH], gDataLength;
uint16_t gRepeat = 0;
boolean gModePP16 = false, gSendFrame = false, gSentOk = false;
uint8_t gDataTemp[FRAME_MAX_LENGTH], gDataLengthTemp;
uint16_t gRepeatTemp = 0;
boolean gModePP16Temp = false;
// Mandatory header for PP16 protocol, not included in the frame CRC16
const uint8_t gPP16Header[4] = {
0x00,
0x00,
0x00,
0x40
};
// The timings offsets (especially for PP16) may need to be adjusted again if another uC is used
const uint8_t gOffPP4C = -5;
const uint8_t gOffPP16 = -5;
// PP4C symbols timings
const uint8_t gPP4C[4] = {
61 + gOffPP4C,
244 + gOffPP4C,
122 + gOffPP4C,
183 + gOffPP4C
};
// PP16 symbols timings
const uint8_t gPP16[16] = {
27 + gOffPP16,
51 + gOffPP16,
35 + gOffPP16,
43 + gOffPP16,
147 + gOffPP16,
123 + gOffPP16,
139 + gOffPP16,
131 + gOffPP16,
83 + gOffPP16,
59 + gOffPP16,
75 + gOffPP16,
67 + gOffPP16,
91 + gOffPP16,
115 + gOffPP16,
99 + gOffPP16,
107 + gOffPP16
};
// Check the whole Bluetooth data with a basic checksum implementation
boolean isPhxChecksumValid(uint16_t checksumToVerify) {
uint8_t phxCode = PHX_CODE_PP4C, length = gDataLengthTemp, start = 0;
uint16_t calcChecksum = 0;
if (gModePP16Temp) {
// Skip the PP16 header
phxCode = PHX_CODE_PP16;
length = gDataLengthTemp - 4;
start = 4;
}
// Add frame parameters to the checksum
calcChecksum += phxCode;
calcChecksum += (uint8_t) (gRepeatTemp >> 8);
calcChecksum += (uint8_t) (gRepeatTemp & 0xFF);
calcChecksum += length;
for (uint8_t i = start; i < 4 + length; i++) {
calcChecksum += gDataTemp[i];
}
return calcChecksum == checksumToVerify;
}
/*
// Check the PPM frame CRC16 (replaced by isPhxChecksumValid())
boolean isCRCValid() {
uint16_t result = 0x8408, poly = 0x8408, offset = 0;
// Skip the PP16 header
if (gModePP16Temp)
offset = 4;
for (uint16_t i = offset; i < gDataLengthTemp - 2; i++) {
result ^= gDataTemp[i];
for (uint8_t j = 0; j < 8; j++) {
if (result & 1) {
result >>= 1;
result ^= poly;
}
else {
result >>= 1;
}
}
}
if (gDataTemp[gDataLengthTemp - 2] != (uint8_t) result)
return false;
if (gDataTemp[gDataLengthTemp - 1] != (uint8_t) (result >> 8))
return false;
return true;
}
*/
// Get a frame from Pricehax sent over Bluetooth
boolean getFrame() {
uint8_t phxCode, counter;
uint16_t checksum;
// First byte sent is an ID to avoid unwanted values sent from the Bluetooth module itself
phxCode = BT.read();
if (phxCode == PHX_CODE_PP4C || phxCode == PHX_CODE_PP16) {
counter = 0;
// Reset the temp data array
memset(gDataTemp, 0, sizeof(gDataTemp));
while (BT.available()) {
if (counter == 0) { // Read header data
gModePP16Temp = false;
// Get the number of times the frame will be transmitted
gRepeatTemp = (uint8_t) BT.read() << 8;
gRepeatTemp |= (uint8_t) BT.read();
// Get the data length of the frame
gDataLengthTemp = (uint8_t) BT.read();
if (phxCode == PHX_CODE_PP16) {
// Add the PP16 header
gModePP16Temp = true;
memcpy(gDataTemp, gPP16Header, sizeof(gPP16Header));
gDataLengthTemp += 4;
counter += 4;
}
}
if (counter == gDataLengthTemp) {
// Get the basic Pricehax checksum
checksum = (uint8_t) BT.read() << 8;
checksum |= (uint8_t) BT.read();
while (BT.available()) { // Read unneeded bytes after the end of the frame
BT.read();
}
return isPhxChecksumValid(checksum);
}
gDataTemp[counter] = (uint8_t) BT.read(); // Get the next byte of the frame
counter++;
}
}
return false;
}
// Create a burst (40us for PP4C, 21us for PP16) at around 1.25MHz (16MHz/13 = 1.23MHz)
// The number of assembly instructions of the whole loop once compiled is critical
// If the function is changed, the timings will need to be adjusted by adding or removing NOPs
void sendPPMBurst(uint8_t pulses) {
for(uint8_t i = 0; i < pulses; i++){
PORTD ^= (1 << LED_PIN);
NOP;
NOP;
PORTD ^= (1 << LED_PIN);
NOP;
}
}
// Send a frame using the PP4C protocol
void sendPP4CFrame() {
uint8_t currentByte;
uint16_t repeat, symNumber, symCount;
// Get the number of PP4C symbols the frame contains
symCount = gDataLength << 2;
for (repeat = 0; repeat < gRepeat; repeat++) {
cli(); // Stop all interrupts to avoid added delay that will interfere with the PPM signal
for (symNumber = 0; symNumber < symCount; symNumber++) {
// Switch to the next byte once the 4 2-bit symbols of the current byte are transmitted
if ((symNumber & 3) == 0) {
currentByte = gData[symNumber >> 2];
}
sendPPMBurst(PULSES_PP4C);
switch(currentByte & 3) {
case 0:
_delay_us(gPP4C[0]);
break;
case 1:
_delay_us(gPP4C[1]);
break;
case 2:
_delay_us(gPP4C[2]);
break;
case 3:
_delay_us(gPP4C[3]);
break;
}
// Switch to the next symbol
currentByte >>= 2;
}
sendPPMBurst(PULSES_PP4C);
sei(); // Allow interrupts
_delay_ms(2);
}
}
// Send a frame using the PP16 protocol
void sendPP16Frame() {
uint8_t currentByte;
uint16_t repeat, symNumber, symCount;
// Get the number of PP16 symbols the frame contains
symCount = gDataLength << 1;
for (repeat = 0; repeat < gRepeat; repeat++) {
cli(); // Stop all interrupts to avoid added delay that will interfere with the PPM signal
for (symNumber = 0; symNumber < symCount; symNumber++) {
// Switch to the next byte once the 2 4-bit symbols of the current byte are transmitted
if ((symNumber & 1) == 0) {
currentByte = gData[symNumber >> 1];
}
sendPPMBurst(PULSES_PP16);
switch(currentByte & 15) {
case 0:
_delay_us(gPP16[0]);
break;
case 1:
_delay_us(gPP16[1]);
break;
case 2:
_delay_us(gPP16[2]);
break;
case 3:
_delay_us(gPP16[3]);
break;
case 4:
_delay_us(gPP16[4]);
break;
case 5:
_delay_us(gPP16[5]);
break;
case 6:
_delay_us(gPP16[6]);
break;
case 7:
_delay_us(gPP16[7]);
break;
case 8:
_delay_us(gPP16[8]);
break;
case 9:
_delay_us(gPP16[9]);
break;
case 10:
_delay_us(gPP16[10]);
break;
case 11:
_delay_us(gPP16[11]);
break;
case 12:
_delay_us(gPP16[12]);
break;
case 13:
_delay_us(gPP16[13]);
break;
case 14:
_delay_us(gPP16[14]);
break;
case 15:
_delay_us(gPP16[15]);
break;
}
// Switch to the next symbol
currentByte >>= 4;
}
sendPPMBurst(PULSES_PP16);
sei(); // Allow interrupts
_delay_ms(2);
}
}
void setup() {
DDRD |= (1 << LED_PIN); // Define led pin as output
PORTD &= (0 << LED_PIN); // Set led pin to low state
cli(); // Stop all interrupts
// Set bit TOIE0 in the TIMSK0 register to zero to disable timer0 interrupt
// This was causing randomly and unwanted 5-6us added delay to _delay_us() due to timer overflow
TIMSK0 &= ~(1 << TOIE0);
sei(); // Allow interrupts
BT.begin(115200);
}
void loop() {
if (BT.available()) {
if (getFrame()) {
// Set new PPM frame parameters
memcpy(gData, gDataTemp, sizeof(gDataTemp));
gDataLength = gDataLengthTemp;
gRepeat = gRepeatTemp;
gModePP16 = gModePP16Temp;
gSendFrame = true;
gSentOk = false;
}
else {
// Read all garbage data the Bluetooth module itself may send
while (BT.available()) {
BT.read();
}
BT.write("1"); // Send an error message to Pricehax if the frame is not properly transmitted
gSendFrame = false;
}
}
if (gSendFrame) {
if (gModePP16)
sendPP16Frame();
else
sendPP4CFrame();
if (!gSentOk) {
BT.write("0"); // Send an OK message to Pricehax once the frame is transmitted
gSentOk = true;
gSendFrame = false;
}
gDataLengthTemp = 0;
}
}