40 Commits

Author SHA1 Message Date
dandri
81bd43b7fa support for 1367 2024-10-15 18:45:53 +00:00
dandri
4ce363f37d uh huh 2024-10-15 18:02:39 +00:00
dandri
4d24d1d79d Update MainActivity.java 2024-09-16 12:13:53 +00:00
dandri
e52be8e9c8 update 2024-09-16 11:39:33 +00:00
dandri
094d4f05c8 Update gradle-wrapper.properties 2024-09-16 11:21:28 +00:00
dandri
a352e61592 Support for Smarttag HD110 Yellow 2024-09-16 10:51:32 +00:00
dandri
ec66a1bd55 del 2024-04-30 23:29:07 +00:00
dandri
ccc542dd26 Update PricehaxBT_IRDongle_prog.ino 2024-04-30 23:27:23 +00:00
dandri
7be40a0bf4 Update README.md 2024-04-27 10:59:41 +00:00
dandri
40f0aa3c6f Add files via upload 2024-04-26 18:32:46 +00:00
dandri
581184a691 add support for 1603 Yellow 2024-04-26 17:54:34 +00:00
dandri
10f5abd1c6 Update README.md 2023-05-26 14:23:37 +00:00
dandri
02770aa909 Support for 1624 HD M FZ 2023-05-20 20:03:01 +00:00
dandri
330a66d5b0 remove HD300 and add support for new HD 150 and HD 200 2023-05-05 23:09:55 +00:00
dandri
ee57c80db8 fix resolution for HD 300 2023-05-05 18:30:12 +00:00
dandri
e34eb7278f Add support for HD 300 2023-05-05 18:13:39 +00:00
dandri
ee38b97de8 Update README.md 2023-05-04 21:45:44 +00:00
dandri
4ee9a9451a Delete PricehaxBT.apk 2023-05-04 21:42:23 +00:00
dandri
3fccdd32a0 Delete .idea directory 2023-05-04 13:15:33 +00:00
dandri
0c95e04c94 Update version 2023-05-02 12:52:56 +00:00
dandri
0f28b9c155 fix dup 2023-04-30 17:33:04 +00:00
dandri
50be98fe86 Merge branch 'test' 2023-04-30 17:29:52 +00:00
dandri
20a1f0bcee Update MainActivity
fixes
2023-04-30 17:23:13 +00:00
dandri
9e02ac3953 fix
fix Mainactivity
2023-04-30 16:55:03 +00:00
dandri
29f1c6f202 Update README.md 2023-04-30 13:12:03 +00:00
dandri
85e16b72e4 Update README.md 2023-04-30 13:11:26 +00:00
dandri
d787599873 Update README.md 2023-04-29 12:59:08 +00:00
dandri
b7fce4bfbf Update README.md 2023-04-29 11:44:09 +00:00
dandri
a618faaffe Merge branch 'master' of https://github.com/dandri/PricehaxBT 2023-04-28 18:58:27 +00:00
dandri
33d79571b6 Support for 1627 2023-04-28 18:58:23 +00:00
dandri
c856a39c8e Add support for 1243 Continuum HCN
Add support for 1243 Continuum HCN
2023-04-27 19:09:51 +00:00
dandri
0a110abb02 Add support for 1243 Continuum HCN 2023-04-27 19:08:16 +00:00
dandri
c808e0ed4e Delete .github/workflows directory 2023-04-26 21:03:51 +00:00
dandri
d1b40b377e Create gradle.yml 2023-04-26 17:05:50 +00:00
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
20 changed files with 1136 additions and 267 deletions

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

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

329
.idea/caches/deviceStreaming.xml generated Normal file
View File

@@ -0,0 +1,329 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceStreaming">
<option name="deviceSelectionList">
<list>
<PersistentDeviceSelectionData>
<option name="api" value="27" />
<option name="brand" value="DOCOMO" />
<option name="codename" value="F01L" />
<option name="id" value="F01L" />
<option name="manufacturer" value="FUJITSU" />
<option name="name" value="F-01L" />
<option name="screenDensity" value="360" />
<option name="screenX" value="720" />
<option name="screenY" value="1280" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="28" />
<option name="brand" value="DOCOMO" />
<option name="codename" value="SH-01L" />
<option name="id" value="SH-01L" />
<option name="manufacturer" value="SHARP" />
<option name="name" value="AQUOS sense2 SH-01L" />
<option name="screenDensity" value="480" />
<option name="screenX" value="1080" />
<option name="screenY" value="2160" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="Lenovo" />
<option name="codename" value="TB370FU" />
<option name="id" value="TB370FU" />
<option name="manufacturer" value="Lenovo" />
<option name="name" value="Tab P12" />
<option name="screenDensity" value="340" />
<option name="screenX" value="1840" />
<option name="screenY" value="2944" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="samsung" />
<option name="codename" value="a51" />
<option name="id" value="a51" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy A51" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="akita" />
<option name="id" value="akita" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
<option name="codename" value="b0q" />
<option name="id" value="b0q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S22 Ultra" />
<option name="screenDensity" value="600" />
<option name="screenX" value="1440" />
<option name="screenY" value="3088" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="32" />
<option name="brand" value="google" />
<option name="codename" value="bluejay" />
<option name="id" value="bluejay" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 6a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="caiman" />
<option name="id" value="caiman" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 9 Pro" />
<option name="screenDensity" value="360" />
<option name="screenX" value="960" />
<option name="screenY" value="2142" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="comet" />
<option name="id" value="comet" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 9 Pro Fold" />
<option name="screenDensity" value="390" />
<option name="screenX" value="2076" />
<option name="screenY" value="2152" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="29" />
<option name="brand" value="samsung" />
<option name="codename" value="crownqlteue" />
<option name="id" value="crownqlteue" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Note9" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2220" />
<option name="screenY" value="1080" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="dm3q" />
<option name="id" value="dm3q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S23 Ultra" />
<option name="screenDensity" value="600" />
<option name="screenX" value="1440" />
<option name="screenY" value="3088" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="e1q" />
<option name="id" value="e1q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S24" />
<option name="screenDensity" value="480" />
<option name="screenX" value="1080" />
<option name="screenY" value="2340" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="felix" />
<option name="id" value="felix" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Fold" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2208" />
<option name="screenY" value="1840" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="felix" />
<option name="id" value="felix" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Fold" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2208" />
<option name="screenY" value="1840" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="felix_camera" />
<option name="id" value="felix_camera" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Fold (Camera-enabled)" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2208" />
<option name="screenY" value="1840" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
<option name="codename" value="gts8uwifi" />
<option name="id" value="gts8uwifi" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Tab S8 Ultra" />
<option name="screenDensity" value="320" />
<option name="screenX" value="1848" />
<option name="screenY" value="2960" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="husky" />
<option name="id" value="husky" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8 Pro" />
<option name="screenDensity" value="390" />
<option name="screenX" value="1008" />
<option name="screenY" value="2244" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="motorola" />
<option name="codename" value="java" />
<option name="id" value="java" />
<option name="manufacturer" value="Motorola" />
<option name="name" value="G20" />
<option name="screenDensity" value="280" />
<option name="screenX" value="720" />
<option name="screenY" value="1600" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="komodo" />
<option name="id" value="komodo" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 9 Pro XL" />
<option name="screenDensity" value="360" />
<option name="screenX" value="1008" />
<option name="screenY" value="2244" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="lynx" />
<option name="id" value="lynx" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 7a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="google" />
<option name="codename" value="oriole" />
<option name="id" value="oriole" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 6" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="panther" />
<option name="id" value="panther" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 7" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="q5q" />
<option name="id" value="q5q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Z Fold5" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1812" />
<option name="screenY" value="2176" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="google" />
<option name="codename" value="r11" />
<option name="id" value="r11" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Watch" />
<option name="screenDensity" value="320" />
<option name="screenX" value="384" />
<option name="screenY" value="384" />
<option name="type" value="WEAR_OS" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="google" />
<option name="codename" value="redfin" />
<option name="id" value="redfin" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 5" />
<option name="screenDensity" value="440" />
<option name="screenX" value="1080" />
<option name="screenY" value="2340" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="shiba" />
<option name="id" value="shiba" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="tangorpro" />
<option name="id" value="tangorpro" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Tablet" />
<option name="screenDensity" value="320" />
<option name="screenX" value="1600" />
<option name="screenY" value="2560" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="tokay" />
<option name="id" value="tokay" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 9" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2424" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="29" />
<option name="brand" value="samsung" />
<option name="codename" value="x1q" />
<option name="id" value="x1q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S20" />
<option name="screenDensity" value="480" />
<option name="screenX" value="1440" />
<option name="screenY" value="3200" />
</PersistentDeviceSelectionData>
</list>
</option>
</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,20 +1,44 @@
# Pricehax BT
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.
**Disclaimer:** Only for educational purposes and fun. 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 will not change, you have been warned.
**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.
Videos about this project are archived in this [playlist](https://www.youtube.com/playlist?list=PLhEz48id1qqD27sRc73mDFfBpu_RcLxfZ) (most in French).
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 the AT commands, change the name of the HC-05/HC-06 module to "PRICEHAX TX V3" and its baudrate 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 ;-)
- Download and install the [Android app](https://github.com/dandri/PricehaxBT/releases/latest), pair the dongle and enjoy ;-)
## Android app changelog
### Version 1.4
Added support for:
- HD 1603 Yellow
- HD 150
- HD 200
- 1624 Freezer
- 1628 (B407047372716287)
- 1639 (A406048236716396)
- 1243 Continuum E4 HCW (F45324224927123434)
- 1627 (D4611412853816278)
### 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**
@@ -57,11 +81,12 @@ Videos about this project are archived in this [playlist](https://www.youtube.co
- Fixed some bugs and app crashes
## Notes
- I didn't write the app, I only 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 only Bluetooth communication, the original communication using audio has been disabled (not the goal of this repo)
- 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...
#
Copyright (c) Furrtek 2014 & david4599 2019 - 2022
Copyright (c) Furrtek 2014 & david4599 2019 - 2022

View File

@@ -6,8 +6,8 @@ android {
applicationId "org.furrtek.pricehaxbt"
minSdkVersion 15
targetSdkVersion 28
versionCode 20
versionName "1.2"
versionCode 21
versionName "1.4"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -16,6 +16,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
namespace 'org.furrtek.pricehaxbt'
}
dependencies {

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,5 +1,6 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.furrtek.pricehaxbt" platformBuildVersionCode="23" platformBuildVersionName="6.0-2438415">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
platformBuildVersionCode="23" platformBuildVersionName="6.0-2438415">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

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;
@@ -87,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;
@@ -135,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) {
@@ -144,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);
}
};
@@ -161,6 +167,7 @@ public class MainActivity extends Activity {
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();
@@ -214,12 +221,12 @@ public class MainActivity extends Activity {
MainActivity.this.ESLType = 1;
break;
case 1242:
case 1243:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (Continuum E4 HCN FZ)");
MainActivity.this.plBitDef = 0;
MainActivity.this.ESLType = 1;
break;
case 1217:
case 1265:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (Continuum E5 HCS)");
@@ -273,13 +280,7 @@ public class MainActivity extends Activity {
MainActivity.this.hi = 152;
MainActivity.this.ESLType = 2;
break;
case 1339:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD S Red 152x152) EXPERIMENTAL");
MainActivity.this.wi = 152;
MainActivity.this.hi = 152;
MainActivity.this.ESLType = 2;
MainActivity.this.ESLTypeColor = true;
break;
case 1318:
@@ -296,6 +297,7 @@ public class MainActivity extends Activity {
MainActivity.this.ESLTypeColor = true;
break;
case 1324:
case 1624:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD M FZ 208x112) EXPERIMENTAL");
MainActivity.this.wi = 208;
MainActivity.this.hi = 112;
@@ -303,6 +305,7 @@ public class MainActivity extends Activity {
break;
case 1315:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD L 296x128) EXPERIMENTAL");
MainActivity.this.wi = 296;
@@ -318,6 +321,17 @@ public class MainActivity extends Activity {
MainActivity.this.ESLType = 2;
MainActivity.this.ESLTypeColor = true;
break;
case 1627:
case 1628:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD L Red 296x128) EXPERIMENTAL");
MainActivity.this.wi = 296;
MainActivity.this.hi = 128;
MainActivity.this.ESLType = 2;
MainActivity.this.ESLTypeColor = true;
break;
case 1603:
case 1344:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD L Yellow 296x128) EXPERIMENTAL");
MainActivity.this.wi = 296;
@@ -356,7 +370,21 @@ public class MainActivity extends Activity {
MainActivity.this.ESLType = 2;
MainActivity.this.ESLTypeColor = true;
break;
case 1381:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD110 Yellow 400x300) EXPERIMENTAL");
MainActivity.this.wi = 400;
MainActivity.this.hi = 300;
MainActivity.this.ESLType = 2;
MainActivity.this.ESLTypeColor = true;
break;
case 1339:
case 1639:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD S Red 152x152) EXPERIMENTAL");
MainActivity.this.wi = 152;
MainActivity.this.hi = 152;
MainActivity.this.ESLType = 2;
MainActivity.this.ESLTypeColor = true;
break;
case 1351:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD150 648x480) EXPERIMENTAL");
@@ -367,6 +395,7 @@ public class MainActivity extends Activity {
case 1371: // 2021 revision of the SmartTag HD150 Red 648x480 - black housing
case 1353:
case 1354:
case 1334: // Seems to be a new version of HD150
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD150 Red 648x480)");
MainActivity.this.wi = 648;
MainActivity.this.hi = 480;
@@ -382,6 +411,8 @@ public class MainActivity extends Activity {
MainActivity.this.ESLType = 2;
break;
case 1340:
case 1367:
case 1358: // Seems to be a new version of HD200
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (SmartTag HD200 Red 800x480) EXPERIMENTAL");
MainActivity.this.wi = 800;
MainActivity.this.hi = 480;
@@ -396,6 +427,9 @@ public class MainActivity extends Activity {
MainActivity.this.ESLTypeColor = true;
break;
default:
MainActivity.this.scaneitype.setText("Type: " + MainActivity.this.PLType + " (incompatible)");
MainActivity.this.ESLType = -1;
@@ -763,21 +797,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;
@@ -791,7 +832,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};
@@ -817,7 +858,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);
}
@@ -831,25 +872,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() {
@@ -857,7 +898,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);
}
@@ -887,7 +928,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);
}
@@ -896,11 +937,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>();
@@ -908,27 +950,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;
}
@@ -943,8 +989,14 @@ public class MainActivity extends Activity {
}
});
return hexlist;
}
private void sendImage(List<Byte> hexlist, int imagePart) {
MainActivity mainActivity;
sendPingCode();
if (!threadRunning) return;
@@ -962,20 +1014,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);
@@ -1005,10 +1050,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;
@@ -1023,8 +1069,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 {
@@ -1043,26 +1095,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() {
@@ -1078,15 +1111,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() {
@@ -1100,11 +1141,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() {
@@ -1122,13 +1164,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();
@@ -1142,25 +1186,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);
}
}
});
}
@@ -1249,8 +1299,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);
}
@@ -1348,6 +1398,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;
@@ -1363,15 +1416,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);
}
}
});
@@ -1430,6 +1492,26 @@ public class MainActivity extends Activity {
}
}
});
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
@@ -1484,12 +1566,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();
@@ -1503,9 +1588,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);
}
@@ -1522,9 +1607,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);
}
}
@@ -1543,14 +1628,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();
}
@@ -1647,9 +1729,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) {
@@ -1707,7 +1792,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");
@@ -1753,21 +1841,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);
}
@@ -1778,7 +1870,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();
}
@@ -1795,7 +1887,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])});
@@ -1835,7 +1927,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();
@@ -1899,7 +1991,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();
}
@@ -1946,6 +2038,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 (20)"
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

@@ -5,7 +5,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
classpath 'com.android.tools.build:gradle:8.7.1'
}
}

View File

@@ -6,6 +6,9 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=false
android.nonTransitiveRClass=false
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit

View File

@@ -1,6 +1,6 @@
#Thu Aug 08 15:05:08 CEST 2019
#Mon Sep 16 10:50:38 GMT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

View File

@@ -1,34 +1,145 @@
/*
* Pricehax BT IR dongle (for Pricehax Version 1.2 BT (20))
* Pricehax BT IR dongle (for Pricehax Version 1.3+ BT (21+))
* furrtek 2014
* david4599 2019 - 2022
* david4599 2019 - 2024
*/
#define F_CPU 16000000L
#define NOP __asm__ __volatile__ ("nop\n\t")
#define BT_RXPIN 10
#define BT_TXPIN 12
#define LEDPIN 2
#define F_CPU 16000000UL
#define NOP __asm__ __volatile__ ("nop")
// Arduino Mega (digital pin 2 = physical pin PE4)
#if (defined(ARDUINO_AVR_MEGA) || \
defined(ARDUINO_AVR_MEGA1280) || \
defined(ARDUINO_AVR_MEGA2560) || \
defined(__AVR_ATmega1280__) || \
defined(__AVR_ATmega1281__) || \
defined(__AVR_ATmega2560__) || \
defined(__AVR_ATmega2561__))
#define SET_PORT_OUTPUT() (DDRE = 1 << 4)
#define SET_LED_LOW() (PORTE &= ~(1 << 4))
#define LED_TOGGLE() (PORTE ^= (1 << 4))
// Arduino Uno, Nano and some others (digital pin 2 = physical pin PD2)
#else
#define SET_PORT_OUTPUT() (DDRD = 1 << 2)
#define SET_LED_LOW() (PORTD &= ~(1 << 2))
#define LED_TOGGLE() (PORTD ^= (1 << 2))
#endif
#define BT_RX_PIN 10
#define BT_TX_PIN 12
#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_RXPIN, BT_TXPIN);
boolean sendframe = false;
uint8_t b, cnt, data[54], datalength;
uint16_t s, sym_count, r, repeat = 0, result, poly;
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
};
void CRCCalc() {
result = 0x8408;
poly = 0x8408;
// 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 (int i = 0; i < datalength-2; i++) {
result ^= data[i];
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 (int j = 0; j < 8; j++) {
for (uint8_t j = 0; j < 8; j++) {
if (result & 1) {
result >>= 1;
result ^= poly;
@@ -38,91 +149,212 @@ void CRCCalc() {
}
}
}
if (gDataTemp[gDataLengthTemp - 2] != (uint8_t) result)
return false;
if (gDataTemp[gDataLengthTemp - 1] != (uint8_t) (result >> 8))
return false;
return true;
}
*/
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;
}
}
// 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;
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);
// Reset the temp data array
memset(gDataTemp, 0, sizeof(gDataTemp));
while (BT.available()) {
if (cnt == 0) { // Read header data
if (counter == 0) { // Read header data
gModePP16Temp = false;
// Get the number of times the frame will be transmitted
repeat = (byte) BT.read() << 8;
repeat |= (byte) BT.read();
gRepeatTemp = (uint8_t) BT.read() << 8;
gRepeatTemp |= (uint8_t) BT.read();
// Get the data length of the frame
datalength = (byte) BT.read();
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 (cnt == datalength) { // Read unneeded bytes after the end of the frame
while (BT.available()) {
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();
}
break;
return isPhxChecksumValid(checksum);
}
data[cnt] = (byte) BT.read(); // Get the next byte of the frame
cnt++;
gDataTemp[counter] = (uint8_t) BT.read(); // Get the next byte of the frame
counter++;
}
sendframe = true;
}
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++){
LED_TOGGLE();
NOP;
NOP;
LED_TOGGLE();
NOP;
}
}
void IRSend() { // Send a frame following the IR protocol
sym_count = datalength << 2;
// 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 (r = 0; r < (unsigned int) repeat; r++) {
for (s = 0; s < sym_count; s++) {
if ((s & 3) == 0) {
b = data[s >> 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];
}
burst();
sendPPMBurst(PULSES_PP4C);
switch(b & 3) {
switch(currentByte & 3) {
case 0:
_delay_us(56);
_delay_us(gPP4C[0]);
break;
case 1:
_delay_us(237);
_delay_us(gPP4C[1]);
break;
case 2:
_delay_us(117);
_delay_us(gPP4C[2]);
break;
case 3:
_delay_us(178);
_delay_us(gPP4C[3]);
break;
}
b >>= 2;
// Switch to the next symbol
currentByte >>= 2;
}
burst();
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);
}
@@ -131,31 +363,55 @@ void IRSend() { // Send a frame following the IR protocol
void setup() {
DDRD |= (1 << LEDPIN); // Define led pin as output
PORTD &= (0 << LEDPIN); // Set led pin to low state
SET_PORT_OUTPUT();
SET_LED_LOW();
BT.begin(57600);
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()) {
getData();
CRCCalc();
if (data[datalength-2] != (uint8_t) result || data[datalength-1] != (uint8_t) (result >> 8)) {
BT.write("1");
sendframe = false;
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 (sendframe) {
IRSend();
BT.write("0"); // Send an answer to Pricehax once the frame is transmitted
sendframe = false;
}
if (gSendFrame) {
if (gModePP16)
sendPP16Frame();
else
sendPP4CFrame();
_delay_ms(1);
if (!gSentOk) {
BT.write("0"); // Send an OK message to Pricehax once the frame is transmitted
gSentOk = true;
gSendFrame = false;
}
gDataLengthTemp = 0;
}
}