├── GPS_APP
├── app
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── drawable
│ │ │ │ │ ├── btn_pressed.xml
│ │ │ │ │ ├── btn_unpressed.xml
│ │ │ │ │ ├── side_nav_bar.xml
│ │ │ │ │ ├── ic_check_black_24dp.xml
│ │ │ │ │ ├── ic_file_download_black_24dp.xml
│ │ │ │ │ ├── ic_fiber_manual_record_black_24dp.xml
│ │ │ │ │ ├── ic_dashboard_black_24dp.xml
│ │ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ │ ├── ic_close_black_24dp.xml
│ │ │ │ │ ├── ic_map_marker_24dp.xml
│ │ │ │ │ ├── ic_textsms_black_24dp.xml
│ │ │ │ │ ├── ic_location_on_black_24dp.xml
│ │ │ │ │ ├── ic_bluetooth_black_24dp.xml
│ │ │ │ │ ├── ic_refresh_black_24dp.xml
│ │ │ │ │ ├── status_off.xml
│ │ │ │ │ ├── ic_do_not_disturb_alt_black_24dp.xml
│ │ │ │ │ ├── status_on.xml
│ │ │ │ │ ├── ic_link_black_24dp.xml
│ │ │ │ │ ├── ic_sync_black_24dp.xml
│ │ │ │ │ ├── ic_gps_not_fixed_black_24dp.xml
│ │ │ │ │ ├── ic_map_black_24dp.xml
│ │ │ │ │ ├── ic_assignment_black_24dp.xml
│ │ │ │ │ ├── info_boarder.xml
│ │ │ │ │ ├── ic_gps_fixed_black_24dp.xml
│ │ │ │ │ ├── ic_location_off_black_24dp.xml
│ │ │ │ │ ├── ic_bluetooth_searching_black_24dp.xml
│ │ │ │ │ ├── action_button.xml
│ │ │ │ │ ├── ic_gps_off_black_24dp.xml
│ │ │ │ │ ├── ic_sync_disabled_black_24dp.xml
│ │ │ │ │ └── ic_settings_black_24dp.xml
│ │ │ │ ├── values-v21
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ ├── menu
│ │ │ │ │ ├── ble_menu.xml
│ │ │ │ │ ├── main_menu.xml
│ │ │ │ │ ├── nav_header_main.xml
│ │ │ │ │ ├── activity_main_drawer.xml
│ │ │ │ │ └── app_bar_main.xml
│ │ │ │ ├── layout
│ │ │ │ │ ├── fragment_home_second.xml
│ │ │ │ │ ├── fragment_setting.xml
│ │ │ │ │ ├── content_main.xml
│ │ │ │ │ ├── activity_main.xml
│ │ │ │ │ ├── activity_ble.xml
│ │ │ │ │ ├── fragment_logs.xml
│ │ │ │ │ ├── ble_cells.xml
│ │ │ │ │ ├── fragment_map.xml
│ │ │ │ │ └── fragment_home.xml
│ │ │ │ ├── values
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ ├── styles.xml
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ └── colors.xml
│ │ │ │ ├── drawable-v24
│ │ │ │ │ └── ic_launcher_foreground.xml
│ │ │ │ └── navigation
│ │ │ │ │ └── mobile_navigation.xml
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── gps
│ │ │ │ │ ├── objects
│ │ │ │ │ ├── GlobalApp.kt
│ │ │ │ │ ├── SqliteDB.kt
│ │ │ │ │ └── BLEDevice.kt
│ │ │ │ │ ├── ui
│ │ │ │ │ ├── home
│ │ │ │ │ │ ├── HomeViewModel.kt
│ │ │ │ │ │ ├── HomeSecondFragment.kt
│ │ │ │ │ │ └── HomeFragment.kt
│ │ │ │ │ ├── map
│ │ │ │ │ │ ├── MapViewModel.kt
│ │ │ │ │ │ └── MapFragment.kt
│ │ │ │ │ ├── logs
│ │ │ │ │ │ ├── LogsViewModel.kt
│ │ │ │ │ │ └── LogsFragment.kt
│ │ │ │ │ └── setting
│ │ │ │ │ │ ├── SettingViewModel.kt
│ │ │ │ │ │ └── SettingFragment.kt
│ │ │ │ │ ├── BLEScanAdapter.kt
│ │ │ │ │ ├── MainActivity.kt
│ │ │ │ │ └── BLEActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── gps
│ │ │ │ └── ExampleUnitTest.kt
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── gps
│ │ │ └── ExampleInstrumentedTest.kt
│ ├── proguard-rules.pro
│ └── build.gradle
├── settings.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradlew.bat
└── gradlew
├── .gitignore
├── Assets
└── images
│ ├── icon
│ └── icon.png
│ ├── UI_Mockups
│ ├── Maps Mockup.png
│ ├── UI Mockup.pptx
│ ├── List_example.png
│ ├── Dashboard Mockup.png
│ ├── Activity_Load_Order.png
│ └── Dashboard Buttons Mockup.png
│ └── schematics
│ ├── schematic.png
│ ├── schematic.vsdx
│ ├── schematic_b.png
│ ├── ESP32-Pinout.jpg
│ ├── NEO-6M-GPS-Module-Pinout.png
│ └── Micro-SD-TF-Card-Module-Pinout-SPI.png
├── Test_Code
├── show_bytes.py
├── GPS
│ ├── .theia
│ │ └── launch.json
│ └── GPS.ino
├── Hardware_Serial
│ └── Hardware_Serial.ino
├── SD_Test
│ └── SD_Test.ino
├── BLE_Server
│ └── BLE_Server.ino
└── SD_ESP_Test
│ └── SD_ESP_Test.ino
├── README.md
└── GPS_Firmware
└── Tiny_GPS_Logger
└── Tiny_GPS_Logger.ino
/GPS_APP/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/GPS_APP/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='GPS'
2 | include ':app'
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/
2 | target/
3 | .vscode/
4 | .idea/
5 | *.sql
6 | git_commit.py
7 | google_maps_api.xml
--------------------------------------------------------------------------------
/Assets/images/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/icon/icon.png
--------------------------------------------------------------------------------
/Assets/images/UI_Mockups/Maps Mockup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/UI_Mockups/Maps Mockup.png
--------------------------------------------------------------------------------
/Assets/images/UI_Mockups/UI Mockup.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/UI_Mockups/UI Mockup.pptx
--------------------------------------------------------------------------------
/Assets/images/schematics/schematic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/schematics/schematic.png
--------------------------------------------------------------------------------
/Assets/images/schematics/schematic.vsdx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/schematics/schematic.vsdx
--------------------------------------------------------------------------------
/Assets/images/schematics/schematic_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/schematics/schematic_b.png
--------------------------------------------------------------------------------
/Assets/images/UI_Mockups/List_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/UI_Mockups/List_example.png
--------------------------------------------------------------------------------
/Assets/images/schematics/ESP32-Pinout.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/schematics/ESP32-Pinout.jpg
--------------------------------------------------------------------------------
/GPS_APP/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/Assets/images/UI_Mockups/Dashboard Mockup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/UI_Mockups/Dashboard Mockup.png
--------------------------------------------------------------------------------
/Assets/images/UI_Mockups/Activity_Load_Order.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/UI_Mockups/Activity_Load_Order.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Assets/images/UI_Mockups/Dashboard Buttons Mockup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/UI_Mockups/Dashboard Buttons Mockup.png
--------------------------------------------------------------------------------
/Assets/images/schematics/NEO-6M-GPS-Module-Pinout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/schematics/NEO-6M-GPS-Module-Pinout.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/GPS_APP/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/Assets/images/schematics/Micro-SD-TF-Card-Module-Pinout-SPI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liuyal/Tiny_GPS_Logger/HEAD/Assets/images/schematics/Micro-SD-TF-Card-Module-Pinout-SPI.png
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/objects/GlobalApp.kt:
--------------------------------------------------------------------------------
1 | package com.gps.objects
2 |
3 | class GlobalApp {
4 | companion object {
5 | var BLE: BLEDevice? = null
6 | }
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/Test_Code/show_bytes.py:
--------------------------------------------------------------------------------
1 | import shutil
2 |
3 | total, used, free = shutil.disk_usage("/")
4 |
5 | print("Total: %d B" % (total))
6 | print("Used: %d B" % (used))
7 | print("Free: %d B" % (free))
--------------------------------------------------------------------------------
/Test_Code/GPS/.theia/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | "version": "0.2.0",
5 | "configurations": [
6 |
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/GPS_APP/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/GPS_APP/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jun 17 10:44:10 PDT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/btn_pressed.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/btn_unpressed.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Test_Code/Hardware_Serial/Hardware_Serial.ino:
--------------------------------------------------------------------------------
1 |
2 |
3 | #define RXD2 16
4 | #define TXD2 17
5 |
6 | void setup() {
7 | Serial.begin(115200);
8 | Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
9 |
10 |
11 | }
12 |
13 | void loop() {
14 | while (Serial2.available()) {
15 | Serial.print(char(Serial2.read()));
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/menu/ble_menu.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_check_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_file_download_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_fiber_manual_record_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_dashboard_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/ui/home/HomeViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gps.ui.home
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 |
7 | class HomeViewModel : ViewModel() {
8 | private val _text = MutableLiveData().apply {
9 | value = "HOME Fragment"
10 | }
11 | val text: LiveData = _text
12 | }
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/ui/map/MapViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gps.ui.map
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 |
7 | class MapViewModel : ViewModel() {
8 |
9 | private val _text = MutableLiveData().apply {
10 | value = "Maps Fragment"
11 | }
12 | val text: LiveData = _text
13 | }
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/ui/logs/LogsViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gps.ui.logs
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 |
7 | class LogsViewModel : ViewModel() {
8 |
9 | private val _text = MutableLiveData().apply {
10 | value = "Logs Fragment"
11 | }
12 | val text: LiveData = _text
13 | }
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/menu/main_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/ui/setting/SettingViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.gps.ui.setting
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 |
7 | class SettingViewModel : ViewModel() {
8 |
9 | private val _text = MutableLiveData().apply {
10 | value = "Setting Fragment"
11 | }
12 | val text: LiveData = _text
13 | }
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_close_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/layout/fragment_home_second.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_map_marker_24dp.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/test/java/com/example/gps/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.gps
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_textsms_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/layout/fragment_setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_location_on_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_bluetooth_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 8dp
6 | 176dp
7 | 16dp
8 |
9 | 25dp
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_refresh_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/status_off.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 | -
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_do_not_disturb_alt_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/status_on.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 | -
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_link_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_sync_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_gps_not_fixed_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/ui/home/HomeSecondFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gps.ui.home
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import com.gps.R
9 |
10 | class HomeSecondFragment : Fragment() {
11 |
12 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
13 | val view = inflater.inflate(R.layout.fragment_home_second, container, false)
14 | return view
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_map_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_assignment_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/info_boarder.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_gps_fixed_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_location_off_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlin_version = '1.4.10'
4 | repositories {
5 | google()
6 | jcenter()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.0.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | mavenCentral()
19 | maven { url 'https://jitpack.io' }
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_bluetooth_searching_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/menu/nav_header_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/action_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
12 |
16 |
20 |
21 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/androidTest/java/com/example/gps/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.gps
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.gps", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_gps_off_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_sync_disabled_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/GPS_APP/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable/ic_settings_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/menu/activity_main_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
25 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/ui/logs/LogsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gps.ui.logs
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.Fragment
9 | import androidx.lifecycle.ViewModelProvider
10 | import com.gps.R
11 |
12 | class LogsFragment : Fragment() {
13 |
14 | private lateinit var logsViewModel: LogsViewModel
15 |
16 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
17 | logsViewModel = ViewModelProvider(this).get(LogsViewModel::class.java)
18 | val root = inflater.inflate(R.layout.fragment_logs, container, false)
19 |
20 |
21 |
22 |
23 |
24 |
25 | return root
26 | }
27 |
28 | override fun onStart() {
29 | super.onStart()
30 | Log.e("LOGS", "Start L Fragment")
31 | }
32 |
33 | override fun onStop() {
34 | super.onStop()
35 | Log.e("LOGS", "Stopped L Fragment")
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/ui/setting/SettingFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gps.ui.setting
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.Fragment
9 | import androidx.lifecycle.ViewModelProvider
10 | import com.gps.R
11 |
12 | class SettingFragment : Fragment() {
13 |
14 | private lateinit var settingViewModel: SettingViewModel
15 |
16 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
17 | settingViewModel = ViewModelProvider(this).get(SettingViewModel::class.java)
18 | val root = inflater.inflate(R.layout.fragment_setting, container, false)
19 |
20 |
21 |
22 |
23 |
24 | return root
25 | }
26 |
27 | override fun onStart() {
28 | super.onStart()
29 | Log.e("Setting", "Start map Fragment")
30 | }
31 |
32 | override fun onStop() {
33 | super.onStop()
34 | Log.e("Setting", "Stopped map Fragment")
35 | }
36 |
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/menu/app_bar_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/GPS_APP/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/layout/activity_ble.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
19 |
20 |
31 |
32 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/layout/fragment_logs.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
14 |
15 |
21 |
22 |
28 |
29 |
35 |
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
22 |
23 |
26 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/navigation/mobile_navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
17 |
18 |
19 |
24 |
27 |
30 |
31 |
32 |
37 |
38 |
43 |
44 |
49 |
50 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/BLEScanAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.gps
2 |
3 | import android.bluetooth.BluetoothDevice
4 | import android.bluetooth.le.ScanResult
5 | import android.view.LayoutInflater
6 | import android.view.ViewGroup
7 | import android.widget.TextView
8 | import androidx.recyclerview.widget.RecyclerView
9 | import androidx.recyclerview.widget.RecyclerView.ViewHolder
10 | import kotlinx.android.synthetic.main.ble_cells.view.*
11 |
12 | class BLEScanAdapter(private val devices : ArrayList, private val results: ArrayList, private val listener: (BluetoothDevice) -> Unit) : RecyclerView.Adapter() {
13 |
14 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
15 | return CustomViewHolder(LayoutInflater.from(parent.context), parent)
16 | }
17 |
18 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
19 | (holder as CustomViewHolder).bind(devices[position], results[position], listener)
20 | }
21 |
22 | override fun getItemCount(): Int = devices.size
23 |
24 | override fun getItemViewType(position: Int): Int = position
25 |
26 | class CustomViewHolder(inflater: LayoutInflater, parent: ViewGroup) : RecyclerView.ViewHolder(inflater.inflate(R.layout.ble_cells, parent, false)) {
27 |
28 | fun bind(device: BluetoothDevice, result: ScanResult, clickListener: (BluetoothDevice) -> Unit) {
29 |
30 | val name: TextView? = itemView.findViewById(R.id.ble_name_label)
31 | val mac: TextView? = itemView.findViewById(R.id.ble_mac_label)
32 | val status: TextView? = itemView.findViewById(R.id.ble_db_label)
33 | val rssi = "RSSI: " + result.rssi.toString() + "dbm"
34 |
35 | itemView.connect_btn.setOnClickListener { clickListener(device) }
36 |
37 | if (device.name != null && device.name.isNotEmpty()) name?.text = device.name
38 | else name?.text = "N/A"
39 | mac?.text = device.address
40 | status?.text = rssi
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Test_Code/SD_Test/SD_Test.ino:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 |
5 | Sd2Card card;
6 | SdVolume volume;
7 | SdFile root;
8 | File myFile;
9 |
10 | const int CS = 4;
11 |
12 | void setup() {
13 |
14 | Serial.begin(115200);
15 | GET_SD_INFO();
16 |
17 | if (!SD.begin(CS)) {
18 | Serial.println("\nSD Init Failed!");
19 | return;
20 | }
21 |
22 | Serial.println("\nSD Initialization Success!");
23 | }
24 |
25 | void GET_SD_INFO() {
26 |
27 | Serial.println("Mounting SD Card...");
28 | if (!card.init(SPI_HALF_SPEED, CS)) {
29 | Serial.println("SD Mount Failed!");
30 | return;
31 | }
32 |
33 | Serial.print("SD Mount Success!\n\nCard type:\t\t");
34 |
35 | switch (card.type()) {
36 | case SD_CARD_TYPE_SD1:
37 | Serial.println("SD1");
38 | break;
39 | case SD_CARD_TYPE_SD2:
40 | Serial.println("SD2");
41 | break;
42 | case SD_CARD_TYPE_SDHC:
43 | Serial.println("SDHC");
44 | break;
45 | default:
46 | Serial.println("Unknown");
47 | }
48 |
49 | if (!volume.init(card)) {
50 | Serial.println("Could not find FAT16/FAT32 partition.");
51 | while (1);
52 | }
53 |
54 | Serial.print("Clusters:\t\t" + volume.clusterCount());
55 | Serial.print("Blocks x Cluster:\t");
56 | Serial.println(volume.blocksPerCluster());
57 | Serial.print("Total Blocks:\t\t");
58 | Serial.println(volume.blocksPerCluster() * volume.clusterCount());
59 | uint32_t volumesize;
60 | Serial.print("\nVolume type is:\t\tFAT");
61 | Serial.println(volume.fatType(), DEC);
62 | volumesize = volume.blocksPerCluster();
63 | volumesize *= volume.clusterCount();
64 | volumesize /= 2;
65 | Serial.println("Volume size (Kb):\t" + String(volumesize));
66 | Serial.println("Volume size (Mb):\t" + String((float)volumesize / 1024.0));
67 | Serial.println("Volume size (Gb):\t" + String((float)volumesize / (1024.0 * 1024.0)));
68 | Serial.println("\nList Files (name, date, and size in bytes): ");
69 | root.openRoot(volume);
70 | root.ls(LS_R | LS_DATE | LS_SIZE);
71 | }
72 |
73 | void loop() {
74 |
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/GPS_APP/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/layout/ble_cells.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
18 |
19 |
30 |
31 |
41 |
42 |
58 |
59 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | GPS
3 | Open navigation drawer
4 | Close navigation drawer
5 | Android Studio
6 | android.studio@android.com
7 | Navigation header
8 | Scan for Devices
9 | BLE Settings
10 |
11 |
12 | Dashboard
13 | Map
14 | Logs
15 | Settings
16 |
17 |
18 | SCAN
19 | STOP
20 | N/A
21 | 00:00:00:00:00:00
22 | NOT BONDED
23 | RSSI: 0dBm
24 | CONNECT
25 |
26 |
27 | Home Second
28 | OK
29 | Title
30 | Dialog
31 | DISCONNECT
32 |
33 |
34 | 00000000
35 | 00:00:00
36 | 0
37 | 000.0000
38 | 000.0000
39 |
40 | %1$s%2$s%3$s
41 | %1$s:%2$s:%3$s
42 |
43 |
44 | DEVICE:
45 | MAC:
46 | CONNECTED:
47 | GPS:
48 | GPS FIX:
49 | LOGGING:
50 | GPS DATE:
51 | GPS TIME:
52 | SATELLITES:
53 | LATITUDE:
54 | LONGITUDE:
55 |
56 |
57 | UPDATE
58 |
59 |
60 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/objects/SqliteDB.kt:
--------------------------------------------------------------------------------
1 | package com.gps.objects
2 |
3 | import android.content.ContentValues
4 | import android.content.Context
5 | import android.database.Cursor
6 | import android.database.sqlite.SQLiteDatabase
7 | import android.database.sqlite.SQLiteOpenHelper
8 |
9 | class SqliteDB(context: Context, factory: SQLiteDatabase.CursorFactory?) : SQLiteOpenHelper(context, DATABASE_NAME, factory, DATABASE_VERSION) {
10 |
11 | companion object {
12 | private const val DATABASE_VERSION = 1
13 | private const val DATABASE_NAME = "bleGPS.db"
14 | const val macTable: String = "macTable"
15 | const val macColumn: String = "mac"
16 | const val GPSDataTable: String = "GPSDataTable"
17 | const val GPSDataColumn: String = "data"
18 | }
19 |
20 | override fun onCreate(db: SQLiteDatabase) {
21 | db.execSQL("CREATE TABLE IF NOT EXISTS $macTable($macColumn varchar(18) PRIMARY KEY)")
22 | db.execSQL("CREATE TABLE IF NOT EXISTS $GPSDataTable($GPSDataColumn varchar(80) PRIMARY KEY)")
23 | }
24 |
25 | override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
26 | db.execSQL("DROP TABLE IF EXISTS $macTable")
27 | db.execSQL("DROP TABLE IF EXISTS $GPSDataTable")
28 | onCreate(db)
29 | }
30 |
31 | private fun createTable(table: String) {
32 | val db = this.writableDatabase
33 | if (table == macTable) {
34 | db.execSQL("CREATE TABLE IF NOT EXISTS $macTable($macColumn varchar(18) PRIMARY KEY)")
35 | } else if (table == GPSDataTable) {
36 | db.execSQL("CREATE TABLE IF NOT EXISTS $GPSDataTable($GPSDataColumn varchar(80) PRIMARY KEY)")
37 | }
38 | }
39 |
40 | fun clearTable(table: String): Boolean {
41 | val db = this.writableDatabase
42 | try {
43 | db.execSQL("DROP TABLE IF EXISTS $table")
44 | this.createTable(table)
45 | } catch (e: Throwable) {
46 | this.createTable(table)
47 | }
48 | return true
49 | }
50 |
51 | fun insertDB(table: String, columns: ArrayList, data: ArrayList) {
52 | val values: ArrayList = ArrayList()
53 | val db = this.writableDatabase
54 | for (i in 0 until data.size) {
55 | val node = ContentValues()
56 | node.put(columns[i], data[i])
57 | values.add(node)
58 | }
59 | for (i in 0 until data.size) {
60 | db.insert(table, null, values[i])
61 | db.close()
62 | }
63 | }
64 |
65 | fun selectFromDB(table: String): Cursor? {
66 | val db = this.readableDatabase
67 | return db.rawQuery("SELECT * FROM $table", null)
68 | }
69 | }
--------------------------------------------------------------------------------
/Test_Code/BLE_Server/BLE_Server.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | // See the following for generating UUIDs:
7 | // https://www.uuidgenerator.net/
8 | #define SERVICE_UUID "000ffdf4-68d9-4e48-a89a-219e581f0d64"
9 | #define CHARACTERISTIC_UUID "44a80b83-c605-4406-8e50-fc42f03b6d38"
10 |
11 | BLEServer* pServer = NULL;
12 | BLEService* pService = NULL;
13 | BLECharacteristic* pCharacteristic = NULL;
14 |
15 | bool deviceConnected = false;
16 |
17 | class ServerCallbacks: public BLEServerCallbacks {
18 | void onConnect(BLEServer* pServer) {
19 | deviceConnected = true;
20 | Serial.println("BLE Device Connected");
21 | };
22 | void onDisconnect(BLEServer* pServer) {
23 | deviceConnected = false;
24 | Serial.println("BLE Device Disconnect");
25 | }
26 | };
27 |
28 | class BLE_Callbacks: public BLECharacteristicCallbacks {
29 | void onWrite(BLECharacteristic *pCharacteristic) {
30 | std::string value = pCharacteristic->getValue();
31 | if (value.length() > 0) {
32 | Serial.print("Value: ");
33 | for (int i = 0; i < value.length(); i++) Serial.print(value[i], HEX);
34 | Serial.println();
35 | }
36 | }
37 | };
38 |
39 | void setup() {
40 | Serial.begin(115200);
41 | Serial.println("SERVICE UUID: " + String(SERVICE_UUID));
42 | Serial.println("CHARACTERISTIC UUID: " + String(CHARACTERISTIC_UUID));
43 | Serial.println("Starting BLE Server...");
44 | BLEDevice::init("ESP32_BLE");
45 | pServer = BLEDevice::createServer();
46 | pServer->setCallbacks(new ServerCallbacks());
47 | pService = pServer->createService(SERVICE_UUID);
48 | pCharacteristic = pService->createCharacteristic(
49 | CHARACTERISTIC_UUID,
50 | BLECharacteristic::PROPERTY_READ |
51 | BLECharacteristic::PROPERTY_WRITE |
52 | BLECharacteristic::PROPERTY_NOTIFY |
53 | BLECharacteristic::PROPERTY_INDICATE
54 | );
55 | pCharacteristic->addDescriptor(new BLE2902());
56 | pCharacteristic->setCallbacks(new BLE_Callbacks());
57 | pService->start();
58 | BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
59 | pAdvertising->addServiceUUID(SERVICE_UUID);
60 | pAdvertising->setScanResponse(true);
61 | pAdvertising->setMinPreferred(0x06);
62 | pAdvertising->setMinPreferred(0x12);
63 | pServer->getAdvertising()->start();
64 | Serial.println("Characteristic Defined!");
65 | }
66 |
67 | void loop() {
68 |
69 | int txValue = random(1, 20);
70 | char txString[8];
71 | dtostrf(txValue, 1, 2, txString);
72 | pCharacteristic->setValue(txString);
73 | pCharacteristic->notify();
74 |
75 | delay(2000);
76 | }
77 |
--------------------------------------------------------------------------------
/GPS_APP/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 29
7 | buildToolsVersion "29.0.3"
8 | defaultConfig {
9 | applicationId "com.gps"
10 | minSdkVersion 21
11 | targetSdkVersion 29
12 | versionCode 1
13 | versionName "1.0"
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 |
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | targetCompatibility JavaVersion.VERSION_1_8
26 | }
27 | kotlinOptions {
28 | jvmTarget = "1.8"
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.2.1'
34 | implementation 'info.androidhive:fontawesome:0.0.5'
35 | implementation 'jp.wasabeef:recyclerview-animators:3.0.0'
36 |
37 | implementation 'com.google.android.material:material:1.3.0-alpha03'
38 | implementation 'com.pusher:pusher-java-client:2.2.5'
39 | implementation 'com.google.android.gms:play-services-maps:17.0.0'
40 | implementation 'com.squareup.retrofit2:retrofit:2.9.0'
41 | implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
42 |
43 | implementation 'com.github.traex.rippleeffect:library:1.3'
44 | implementation 'com.github.hajiyevelnur92:intentanimation:1.0'
45 | implementation 'com.chauthai.overscroll:overscroll-bouncy:0.1.1'
46 | implementation 'androidx.appcompat:appcompat:1.2.0'
47 | implementation 'androidx.core:core-ktx:1.3.2'
48 | implementation 'androidx.cardview:cardview:1.0.0'
49 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
50 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
51 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
52 | implementation 'androidx.navigation:navigation-fragment:2.3.0'
53 | implementation 'androidx.navigation:navigation-ui:2.3.0'
54 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
55 | implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
56 | implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
57 |
58 | implementation fileTree(dir: 'libs', include: ['*.jar'])
59 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
60 |
61 | testImplementation 'junit:junit:4.13'
62 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
63 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
64 | }
65 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/layout/fragment_map.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
20 |
21 |
27 |
28 |
34 |
35 |
36 |
42 |
43 |
44 |
45 |
46 |
53 |
54 |
62 |
63 |
64 |
65 |
74 |
75 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.gps
2 |
3 | import android.bluetooth.BluetoothAdapter
4 | import android.content.ContextWrapper
5 | import android.content.Intent
6 | import android.os.Bundle
7 | import android.util.Log
8 | import android.view.Menu
9 | import android.widget.Toast
10 | import androidx.appcompat.app.AppCompatActivity
11 | import androidx.appcompat.widget.Toolbar
12 | import androidx.drawerlayout.widget.DrawerLayout
13 | import androidx.navigation.findNavController
14 | import androidx.navigation.ui.AppBarConfiguration
15 | import androidx.navigation.ui.navigateUp
16 | import androidx.navigation.ui.setupActionBarWithNavController
17 | import androidx.navigation.ui.setupWithNavController
18 | import com.google.android.material.navigation.NavigationView
19 | import com.gps.objects.BLEDevice
20 | import com.gps.objects.GlobalApp
21 | import maes.tech.intentanim.CustomIntent
22 |
23 | class MainActivity : AppCompatActivity(){
24 |
25 | private lateinit var appBarConfiguration: AppBarConfiguration
26 |
27 | override fun onCreate(savedInstanceState: Bundle?) {
28 | super.onCreate(savedInstanceState)
29 | setContentView(R.layout.activity_main)
30 | val toolbar: Toolbar = findViewById(R.id.toolbar)
31 | setSupportActionBar(toolbar)
32 | supportActionBar?.elevation = 0F
33 | toolbar.setOnMenuItemClickListener {
34 | when (it.itemId) {
35 | R.id.action_settings -> {
36 | val intent = Intent(this, BLEActivity::class.java)
37 | startActivity(intent)
38 | CustomIntent.customType(this, "left-to-right")
39 | }
40 | }
41 | true
42 | }
43 | if (this.checkBTon() && GlobalApp.BLE == null) {
44 | GlobalApp.BLE = BLEDevice(this, applicationContext as ContextWrapper)
45 | GlobalApp.BLE!!.initialize()
46 | } else if (this.checkBTon()) {
47 | GlobalApp.BLE?.context = this
48 | GlobalApp.BLE?.applicationContext = applicationContext as ContextWrapper
49 | }
50 | val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
51 | val navView: NavigationView = findViewById(R.id.nav_view)
52 | val navController = findNavController(R.id.nav_host_fragment)
53 | appBarConfiguration = AppBarConfiguration(setOf(R.id.nav_home, R.id.nav_map, R.id.nav_logs, R.id.nav_setting), drawerLayout)
54 | setupActionBarWithNavController(navController, appBarConfiguration)
55 | navView.setupWithNavController(navController)
56 | }
57 |
58 | override fun onStop() {
59 | super.onStop()
60 | try {
61 | GlobalApp.BLE?.close()
62 | } catch (e: Throwable) {
63 | Log.e("MAIN", "GATT Closing ERROR")
64 | }
65 | }
66 |
67 | override fun onCreateOptionsMenu(menu: Menu): Boolean {
68 | menuInflater.inflate(R.menu.main_menu, menu)
69 | return true
70 | }
71 |
72 | override fun onSupportNavigateUp(): Boolean {
73 | val navController = findNavController(R.id.nav_host_fragment)
74 | return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
75 | }
76 |
77 | fun checkBTon(): Boolean {
78 | if (BluetoothAdapter.getDefaultAdapter() == null) {
79 | Toast.makeText(applicationContext, "BlueTooth is not supported!", Toast.LENGTH_SHORT).show()
80 | } else if (!BluetoothAdapter.getDefaultAdapter().isEnabled) {
81 | Toast.makeText(applicationContext, "BlueTooth is not enabled!", Toast.LENGTH_SHORT).show()
82 | } else if (BluetoothAdapter.getDefaultAdapter().isEnabled) {
83 | return true
84 | }
85 | return false
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Test_Code/GPS/GPS.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "SD.h"
4 | #include "FS.h"
5 | #include "TinyGPS++.h"
6 |
7 | #define DEBUG true
8 |
9 | #if DEBUG
10 | #define DEBUG_PRINT(x) Serial.print(x);
11 | #else
12 | #define DEBUG_PRINT(x) \
13 | do { \
14 | } while (0);
15 | #endif
16 |
17 | #if DEBUG
18 | #define DEBUG_PRINT_LN(x) Serial.println(x);
19 | #else
20 | #define DEBUG_PRINT_LN(x) \
21 | do { \
22 | } while (0);
23 | #endif
24 |
25 | #define LED_PIN 22
26 | #define RXD2 16
27 | #define TXD2 17
28 |
29 | TinyGPSPlus gps;
30 |
31 | const int CS_PIN = 5;
32 | int nFiles = 0;
33 |
34 | /**********************************************************************
35 | SDCard Functions
36 | **********************************************************************/
37 |
38 | void SD_INIT() {
39 |
40 | DEBUG_PRINT_LN("Initializing SD Card...");
41 |
42 | if (!SD.begin(CS_PIN)) {
43 | DEBUG_PRINT_LN("SD Card Initialization Failed!");
44 | return;
45 | }
46 |
47 | DEBUG_PRINT_LN("-------SD Card Info-------");
48 |
49 | uint8_t cardType = SD.cardType();
50 | uint64_t bytes = SD.totalBytes();
51 | uint64_t used_bytes = SD.usedBytes();
52 |
53 | if (cardType == CARD_MMC) {
54 | DEBUG_PRINT_LN("SD Card Type:\tMMC");
55 | } else if (cardType == CARD_SD) {
56 | DEBUG_PRINT_LN("SD Card Type:\tSDSC");
57 | } else if (cardType == CARD_SDHC) {
58 | DEBUG_PRINT_LN("SD Card Type:\tSDHC");
59 | } else if (cardType == CARD_NONE) {
60 | DEBUG_PRINT_LN("SD Card Type:\tNo SD Card Attached");
61 | } else {
62 | DEBUG_PRINT_LN("UNKNOWN");
63 | }
64 |
65 | DEBUG_PRINT("Used(MB):\t");
66 | DEBUG_PRINT_LN((float)used_bytes / (1000 * 1000));
67 | DEBUG_PRINT("Used(GB):\t");
68 | DEBUG_PRINT_LN((float)used_bytes / (1000 * 1000 * 1000));
69 | DEBUG_PRINT("Volume(MB):\t");
70 | DEBUG_PRINT_LN((float)bytes / (1000 * 1000));
71 | DEBUG_PRINT("Volume(GB):\t");
72 | DEBUG_PRINT_LN((float)bytes / (1000 * 1000 * 1000));
73 |
74 | nFiles = countFiles(SD, "/", 0);
75 |
76 | DEBUG_PRINT("Files Count:\t");
77 | DEBUG_PRINT_LN(nFiles);
78 | DEBUG_PRINT_LN("--------------------------");
79 | }
80 |
81 | int countFiles(fs::FS &fs, String dirname, uint8_t levels) {
82 |
83 | int count = 0;
84 | File root = fs.open(dirname);
85 | if (!root || !root.isDirectory()) {
86 | return -1;
87 | }
88 | File file = root.openNextFile();
89 |
90 | while (file) {
91 | if (file.isDirectory()) {
92 | if (levels) countFiles(fs, file.name(), levels - 1);
93 | } else count += 1;
94 |
95 | file = root.openNextFile();
96 | }
97 |
98 | return count;
99 | }
100 |
101 |
102 | void appendFile(fs::FS &fs, String path, String message) {
103 |
104 | File file = fs.open(path, FILE_APPEND);
105 | if (!file) return;
106 | file.print(message);
107 | file.close();
108 | }
109 |
110 |
111 | /**********************************************************************
112 | Main
113 | **********************************************************************/
114 |
115 | void setup() {
116 |
117 | // LED
118 | pinMode(LED_PIN, OUTPUT);
119 |
120 | // Initialize Serial
121 | Serial.begin(115200);
122 |
123 | // Initialize HW Serial to NEO
124 | Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
125 |
126 | // Initialize SD Card
127 | SD_INIT();
128 |
129 | DEBUG_PRINT_LN("Initialization Complete!");
130 | }
131 |
132 | void loop() {
133 |
134 | String gnss_data = "";
135 |
136 | // Read from NEO serial
137 | while (Serial2.available()) {
138 | int raw_data = Serial2.read();
139 | gps.encode(raw_data);
140 | gnss_data += (char)raw_data;
141 | }
142 |
143 | // // Check fix and log to file
144 | // if (gps.location.isValid()) {
145 | // digitalWrite(LED_PIN, HIGH);
146 | // appendFile(SD, "/" + String(nFiles) + ".log", gnss_data);
147 |
148 | // } else {
149 | // digitalWrite(LED_PIN, LOW);
150 | // DEBUG_PRINT_LN("No GNSS FIX...");
151 | // }
152 |
153 | DEBUG_PRINT(gnss_data)
154 | delay(1000);
155 | appendFile(SD, "/" + String(nFiles) + ".log", gnss_data);
156 | }
157 |
--------------------------------------------------------------------------------
/Test_Code/SD_ESP_Test/SD_ESP_Test.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include "SD.h"
3 | #include "FS.h"
4 |
5 | File myFile;
6 | const int CS = 5;
7 |
8 | void setup() {
9 | Serial.begin(115200);
10 | SD_INIT();
11 | }
12 |
13 | void SD_INIT() {
14 | Serial.println("\nInitializing SD Card...");
15 | if (!SD.begin(CS)) {
16 | Serial.println("Initialization Failed!");
17 | return;
18 | }
19 | Serial.println("Initialization Success!");
20 | Serial.println("-------SD Card Info-------");
21 | Serial.print("SD Card Type:\t");
22 | uint8_t cardType = SD.cardType();
23 | if (cardType == CARD_MMC) Serial.println("MMC");
24 | else if (cardType == CARD_SD) Serial.println("SDSC");
25 | else if (cardType == CARD_SDHC) Serial.println("SDHC");
26 | else if (cardType == CARD_NONE) Serial.println("No SD Card Attached");
27 | else Serial.println("UNKNOWN");
28 | uint64_t bytes = SD.totalBytes();
29 | uint64_t used_bytes = SD.usedBytes();
30 | Serial.println("Volume(KB):\t" + String((float)bytes / (1000)));
31 | Serial.println("Volume(MB):\t" + String((float)bytes / (1000 * 1000)));
32 | Serial.println("Volume(GB):\t" + String((float)bytes / (1000 * 1000 * 1000)));
33 | Serial.println("Used(KB):\t" + String((float)used_bytes / (1000)));
34 | Serial.println("Used(MB):\t" + String((float)used_bytes / (1000 * 1000)));
35 | Serial.println("Used(GB):\t" + String((float)used_bytes / (1000 * 1000 * 1000)));
36 | Serial.println("--------------------------\n");
37 | }
38 |
39 | int listDir(fs::FS &fs, String dirname, uint8_t levels) {
40 | int count = 0;
41 | Serial.println("Listing directory: " + dirname + "\n");
42 | File root = fs.open(dirname);
43 | if (!root || !root.isDirectory()) {
44 | Serial.println("Failed to open directory\n");
45 | return -1;
46 | }
47 | File file = root.openNextFile();
48 | while (file) {
49 | if (file.isDirectory()) {
50 | Serial.println(" DIR : " + String(file.name()) + "\n");
51 | if (levels) listDir(fs, file.name(), levels - 1);
52 | }
53 | else {
54 | count += 1;
55 | Serial.println(" FILE: " + String(file.name()) + " SIZE: " + String(file.size()) + "\n");
56 | }
57 | file = root.openNextFile();
58 | }
59 | return count;
60 | }
61 |
62 | void createDir(fs::FS &fs, String path) {
63 | if (SD.exists(path)) {
64 | Serial.println("Dir " + path + " Exists...\n");
65 | return;
66 | }
67 | Serial.println("Creating Dir: " + path + "\n");
68 | if (fs.mkdir(path)) Serial.println("Dir created\n");
69 | else Serial.println("mkdir failed\n");
70 | }
71 |
72 | void removeDir(fs::FS &fs, String path) {
73 | Serial.println("Removing Dir: " + path + "\n");
74 | if (fs.rmdir(path))Serial.println("Dir removed\n");
75 | else Serial.println("rmdir failed\n");
76 | }
77 |
78 | void readFile(fs::FS &fs, String path) {
79 | Serial.println("Reading file: " + path + "\n");
80 | File file = fs.open(path);
81 | if (!file) {
82 | Serial.println("Failed to open file for reading\n");
83 | return;
84 | }
85 | Serial.println("Read from file: ");
86 | while (file.available()) Serial.write(file.read());
87 | file.close();
88 | }
89 |
90 | void writeFile(fs::FS &fs, String path, String message) {
91 | Serial.println("Writing file: " + path + "\n");
92 | File file = fs.open(path, FILE_WRITE);
93 | if (!file) {
94 | Serial.println("Failed to open file for writing\n");
95 | return;
96 | }
97 | if (file.print(message)) Serial.println("File written\n");
98 | else Serial.println("Write failed\n");
99 | file.close();
100 | }
101 |
102 | void appendFile(fs::FS &fs, String path, String message) {
103 | Serial.println("Append to file: " + path + " ");
104 | File file = fs.open(path, FILE_APPEND);
105 | if (!file) {
106 | Serial.println("Failed to open file\n");
107 | return;
108 | }
109 | if (file.print(message)) Serial.println("| Data appended\n");
110 | else Serial.println("Append failed\n");
111 | file.close();
112 | }
113 |
114 | void renameFile(fs::FS &fs, String path1, String path2) {
115 | Serial.println("Renaming file " + path1 + " to " + path2 + "\n");
116 | if (fs.rename(path1, path2)) Serial.println("File renamed\n");
117 | else Serial.println("Rename failed\n");
118 | }
119 |
120 | void deleteFile(fs::FS &fs, String path) {
121 | Serial.println("Deleting file: " + path + "\n");
122 | if (fs.remove(path))Serial.println("File deleted\n");
123 | else Serial.println("Delete failed\n");
124 | }
125 |
126 | void loop() {
127 |
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/GPS_APP/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tiny GPS Logger
2 |
3 | Portable GPS logging device built with ESP32, NEO-6M, and Catalex Micro SD Card Module.
4 |
5 | ## Hardware Schematic & Pin Connection
6 |
7 | 
8 |
9 | | ESP32 | SD Card Module |
10 | | --- | --- |
11 | | Pin 5 | CS |
12 | | Pin 18 | SCK |
13 | | Pin 23 | MOSI |
14 | | Pin 19 | MISO |
15 | | 5V | VCC |
16 | | GND | GND |
17 |
18 | | ESP32 | NEO-6M |
19 | | --- | --- |
20 | | 3V3 | VCC |
21 | | Pin 17 | RX |
22 | | Pin 16 | TX |
23 | | GND | GND |
24 |
25 | ## Android App Guide
26 |
27 | - TBA
28 | - USE OF BUILT-IN INTERNAL PHONE GPS
29 |
30 | ### Android [Remote adb debug](https://stackoverflow.com/questions/4893953/run-install-debug-android-applications-over-wi-fi)
31 |
32 | 1. Connect the device via USB and make sure debugging is working;
33 | 2. `adb tcpip 5555` This makes the device to start listening for connections on port 5555;
34 | 3. Look up the device IP address with `adb shell netcfg` or `adb shell ifconfig` with 6.0 and higher;
35 | 4. Disconnect the USB now;
36 | 5. `adb connect :5555`. This connects to the server we set up on the device on step 2;
37 | 6. Now you have a device over the network with which you can debug as usual.
38 |
39 | - To switch the server back to the USB mode, run `adb usb`, which will put the server on your phone back to the USB mode.
40 | - If you have more than one device, you can specify the device with the -s option: `adb -s :5555 usb`
41 | - To find the IP address of the device: run `adb shell` and then `netcfg`.
42 | - To find the IP address while using OSX run the command adb shell ip route.
43 |
44 | ## BLE Instruction Codes
45 |
46 | | Hex Code | Function | Description |
47 | | :---: | --- | --- |
48 | | 0x00 | None | Code Not Assigned |
49 | | 0x01 | Get device status | Get current GPS device statue code (see GPS Status Flags) |
50 | | 0x02 | Toggle GPS on | Turn GPS location service on |
51 | | 0x03 | Toggle GPS off | Turn GPS location service off |
52 | | 0x04 | Toggle logging on | Turn GPS sentences logging on |
53 | | 0x05 | Toggle logging off | Turn GPS sentences logging off |
54 | | 0x06 | Get GPS data | Get current GPS location data if GPS has fix |
55 | | 0x07 | List Files | List all current log files on GPS SD card |
56 | | 0x08 | Read File | Read log file from GPS SD card |
57 | | 0x09 | Get SD Card Status | Get SD card usage information |
58 | | 0x0a | Reboot | Reboot GPS device |
59 | | 0x0b | Reset | Reset GPS device configurations and all status flags |
60 |
61 | ## GPS Status Flags
62 |
63 | #### 4 Bit GPS Status Flag System
64 |
65 | | Bit Index | Function | Description |
66 | | :---: | --- | --- |
67 | | 0 | BLE Connection | True if BLE service is connected to BLE client |
68 | | 1 | GPS On/Off Status | True if GPS service is enabled on device |
69 | | 2 | GPS Has Fix | True if GPS has location fix |
70 | | 3 | Logging Status | True if GPS sentences logging is enabled |
71 |
72 | ## Resource Links
73 |
74 | #### GPS Module
75 | - [NMEA Sentences](https://www.gpsinformation.org/dale/nmea.htm)
76 | - [Tiny GPS++ Library](http://arduiniana.org/libraries/tinygpsplus/)
77 | - [Guide to NEO-6M GPS Module](https://randomnerdtutorials.com/guide-to-neo-6m-gps-module-with-arduino/)
78 | - [Interface ublox NEO-6M GPS Module](https://lastminuteengineers.com/neo6m-gps-arduino-tutorial/)
79 | - [NEO-6M DataSheet](https://www.u-blox.com/sites/default/files/products/documents/NEO-6_DataSheet_%28GPS.G6-HW-09005%29.pdf)
80 | - [NEO-6M Product Summary](https://www.u-blox.com/sites/default/files/products/documents/NEO-6_ProductSummary_%28GPS.G6-HW-09003%29.pdf)
81 | - [NEOGPS Repo](https://github.com/SlashDevin/NeoGPS/tree/master/examples)
82 | - [LoRaTracker GPS Tutorial](https://github.com/LoRaTracker/GPSTutorial)
83 | -
84 | #### ESP32
85 | - [ESP32 DataSheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf)
86 | - [ESP32 Wiki](http://arduinoinfo.mywikis.net/wiki/Esp32)
87 |
88 | #### SD Card Module
89 | - [ESP32 Logging to MicroSD Card](https://randomnerdtutorials.com/esp32-data-logging-temperature-to-microsd-card/)
90 | - [Interfacing Micro SD Card Module](https://lastminuteengineers.com/arduino-micro-sd-card-module-tutorial/)
91 | - [Arduino SD Library](https://www.arduino.cc/en/reference/SD)
92 |
93 | #### Bluetooth Low Energy
94 | - [ESP32 BLE Guide](https://randomnerdtutorials.com/esp32-bluetooth-low-energy-ble-arduino-ide/)
95 | - [Characteristic with Multiple Descriptors](https://github.com/espressif/arduino-esp32/issues/1038)
96 | - [Indication & Notification](https://community.nxp.com/docs/DOC-328525)
97 | - [BLE introduction: Notify or Indicate ](https://www.onethesis.com/2015/11/21/ble-introduction-notify-or-indicate/)
98 | - [UUID Generator](https://www.uuidgenerator.net/)
99 | - [ASCII TO HEX](https://www.asciitohex.com/)
100 |
101 | #### Android App
102 | - [Android Developers: Bluetooth](https://developer.android.com/reference/kotlin/android/bluetooth/package-summary)
103 | - [Android Developers: Bluetooth GATT](https://developer.android.com/reference/android/bluetooth/BluetoothGatt)
104 | - [Android Developers: BLE Basics](https://developer.android.com/guide/topics/connectivity/bluetooth-le)
105 | - [GATT Services & Characteristics](https://www.oreilly.com/library/view/getting-started-with/9781491900550/ch04.html)
106 | - [Android CardView Example](https://stacktips.com/tutorials/android/android-cardview-example)
107 |
108 | #### Google Map API
109 | - [Android Developers: Map Object](https://developers.google.com/maps/documentation/android-sdk/map)
110 | - [Android Developers: Map View](https://developers.google.com/android/reference/com/google/android/gms/maps/MapView)
111 | - [Android Developers: Map Controls and Gestures](https://developers.google.com/maps/documentation/android-sdk/controls)
112 | - [Android Developers: Map Markers](https://developers.google.com/maps/documentation/javascript/markers)
113 | - [Android Developers: Map MarkerOptions](https://developers.google.com/android/reference/com/google/android/gms/maps/model/MarkerOptions)
114 | - [Android Developers: Map Path](https://developers.google.com/maps/documentation/android-sdk/polygon-tutorial)
115 | - [Mapbox API Offline maps](https://docs.mapbox.com/android/maps/overview/)
116 | - [Offline maps help](https://stackoverflow.com/questions/38169034/use-google-maps-offline-maps-cache-in-google-maps-android-api)
117 | - [Real-Time Map using Kotlin](https://pusher.com/tutorials/realtime-map-kotlin)
118 | - [Real-Time Map using Kotlin Repo](https://github.com/neoighodaro/realtime-map-example-kotlin)
119 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #282828
5 | #282828
6 | #282828
7 |
8 | #00000000
9 |
10 | #555555
11 | #FFFFFF
12 | #FFFFF0
13 | #FFFFE0
14 | #FFFF00
15 | #FFFAFA
16 | #FFFAF0
17 | #FFFACD
18 | #FFF8DC
19 | #FFF5EE
20 | #FFF0F5
21 | #FFEFD5
22 | #FFEBCD
23 | #FFE4E1
24 | #FFE4C4
25 | #FFE4B5
26 | #FFDEAD
27 | #FFDAB9
28 | #FFD700
29 | #FFC0CB
30 | #FFB6C1
31 | #FFA500
32 | #FFA07A
33 | #FF8C00
34 | #FF7F50
35 | #FF69B4
36 | #FF6347
37 | #FF4500
38 | #FF1493
39 | #FF00FF
40 | #FF00FF
41 | #FF0000
42 | #FDF5E6
43 | #FAFAD2
44 | #FAF0E6
45 | #FAEBD7
46 | #FA8072
47 | #F8F8FF
48 | #F5FFFA
49 | #F5F5F5
50 | #F5F5DC
51 | #F5DEB3
52 | #F4A460
53 | #F0FFFF
54 | #F0FFF0
55 | #F0F8FF
56 | #F0E68C
57 | #F08080
58 | #EEE8AA
59 | #EE82EE
60 | #E9967A
61 | #E6E6FA
62 | #E0FFFF
63 | #DEB887
64 | #DDA0DD
65 | #DCDCDC
66 | #DC143C
67 | #DB7093
68 | #DAA520
69 | #DA70D6
70 | #D8BFD8
71 | #D3D3D3
72 | #D2B48C
73 | #D2691E
74 | #CD853F
75 | #CD5C5C
76 | #C71585
77 | #C0C0C0
78 | #BDB76B
79 | #BC8F8F
80 | #BA55D3
81 | #B8860B
82 | #B22222
83 | #B0E0E6
84 | #B0C4DE
85 | #AFEEEE
86 | #ADFF2F
87 | #ADD8E6
88 | #A9A9A9
89 | #A52A2A
90 | #A0522D
91 | #9ACD32
92 | #9932CC
93 | #98FB98
94 | #9400D3
95 | #9370DB
96 | #90EE90
97 | #8FBC8F
98 | #8B4513
99 | #8B008B
100 | #8B0000
101 | #8A2BE2
102 | #87CEFA
103 | #87CEEB
104 | #808080
105 | #808000
106 | #800080
107 | #800000
108 | #7FFFD4
109 | #7FFF00
110 | #7CFC00
111 | #7B68EE
112 | #778899
113 | #708090
114 | #6B8E23
115 | #6A5ACD
116 | #696969
117 | #66CDAA
118 | #6495ED
119 | #5F9EA0
120 | #556B2F
121 | #4B0082
122 | #48D1CC
123 | #483D8B
124 | #4682B4
125 | #4169E1
126 | #40E0D0
127 | #3CB371
128 | #32CD32
129 | #2F4F4F
130 | #2E8B57
131 | #228B22
132 | #20B2AA
133 | #1E90FF
134 | #191970
135 | #00FFFF
136 | #00FFFF
137 | #00FF7F
138 | #00FF00
139 | #00FA9A
140 | #00CED1
141 | #00BFFF
142 | #008B8B
143 | #008080
144 | #008000
145 | #006400
146 | #0000FF
147 | #0000CD
148 | #00008B
149 | #000080
150 | #000000
151 |
152 |
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/BLEActivity.kt:
--------------------------------------------------------------------------------
1 | package com.gps
2 |
3 | import android.app.AlertDialog
4 | import android.bluetooth.BluetoothAdapter
5 | import android.bluetooth.BluetoothDevice
6 | import android.bluetooth.BluetoothManager
7 | import android.bluetooth.le.BluetoothLeScanner
8 | import android.bluetooth.le.ScanCallback
9 | import android.bluetooth.le.ScanResult
10 | import android.content.Context
11 | import android.content.ContextWrapper
12 | import android.content.pm.PackageManager
13 | import android.os.Bundle
14 | import android.util.Log
15 | import android.view.Menu
16 | import android.view.MenuItem
17 | import android.view.View
18 | import android.widget.ProgressBar
19 | import android.widget.Toast
20 | import androidx.annotation.UiThread
21 | import androidx.appcompat.app.AppCompatActivity
22 | import androidx.recyclerview.widget.DividerItemDecoration
23 | import androidx.recyclerview.widget.LinearLayoutManager
24 | import com.gps.objects.GlobalApp
25 | import jp.wasabeef.recyclerview.animators.SlideInLeftAnimator
26 | import kotlinx.android.synthetic.main.activity_ble.*
27 | import maes.tech.intentanim.CustomIntent
28 | import java.util.*
29 | import kotlin.collections.ArrayList
30 |
31 | class BLEActivity : AppCompatActivity() {
32 |
33 | private var scanFlag: Boolean = false
34 | private var deviceList: ArrayList = ArrayList()
35 | private var resultsList: ArrayList = ArrayList()
36 |
37 | private var progressBar: ProgressBar? = null
38 |
39 | private val bleScanner = object : ScanCallback() {
40 | override fun onScanResult(callbackType: Int, result: ScanResult?) {
41 | if (result != null) {
42 | if (result.device !in deviceList) {
43 | deviceList.add(result.device)
44 | resultsList.add(result)
45 | select_device_list.adapter?.notifyItemInserted(deviceList.size - 1)
46 | }
47 | if (result.device in deviceList) {
48 | resultsList[deviceList.indexOf(result.device)] = result
49 | select_device_list.adapter?.notifyItemChanged(deviceList.indexOf(result.device))
50 | }
51 | }
52 | Log.d("BLEACT", "SCAN: ${result?.device?.address} - ${result?.device?.name}")
53 | }
54 | }
55 |
56 | private val bluetoothLeScanner: BluetoothLeScanner
57 | get() {
58 | GlobalApp.BLE?.bleManager = applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
59 | GlobalApp.BLE?.bleAdapter = GlobalApp.BLE?.bleManager!!.adapter
60 | return GlobalApp.BLE?.bleAdapter!!.bluetoothLeScanner
61 | }
62 |
63 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
64 | when (requestCode) {
65 | 1 -> when (grantResults) {
66 | intArrayOf(PackageManager.PERMISSION_GRANTED) -> {
67 | bluetoothLeScanner.startScan(bleScanner)
68 | }
69 | }
70 | else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
71 | }
72 | }
73 |
74 | override fun onCreate(savedInstanceState: Bundle?) {
75 | super.onCreate(savedInstanceState)
76 | setContentView(R.layout.activity_ble)
77 | supportActionBar!!.setDisplayHomeAsUpEnabled(true)
78 | supportActionBar!!.elevation = 0F
79 | GlobalApp.BLE?.context = this
80 | GlobalApp.BLE?.applicationContext = applicationContext as ContextWrapper
81 | select_device_list.layoutManager = LinearLayoutManager(select_device_list.context)
82 | select_device_list.adapter = BLEScanAdapter(deviceList, resultsList) { partItem: BluetoothDevice -> partItemClicked(partItem) }
83 | select_device_list.itemAnimator = SlideInLeftAnimator()
84 | select_device_list.itemAnimator?.addDuration = 200
85 | select_device_list.addItemDecoration(DividerItemDecoration(select_device_list.context, DividerItemDecoration.VERTICAL))
86 | }
87 |
88 | override fun onStop() {
89 | super.onStop()
90 | this.scanFlag = false
91 | if (BluetoothAdapter.getDefaultAdapter() != null && BluetoothAdapter.getDefaultAdapter().isEnabled) {
92 | bluetoothLeScanner.stopScan(bleScanner)
93 | }
94 | }
95 |
96 | override fun onCreateOptionsMenu(menu: Menu?): Boolean {
97 | menuInflater.inflate(R.menu.ble_menu, menu)
98 | return super.onCreateOptionsMenu(menu)
99 | }
100 |
101 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
102 | if (BluetoothAdapter.getDefaultAdapter() == null) {
103 | Toast.makeText(applicationContext, "BlueTooth is not supported!", Toast.LENGTH_SHORT).show()
104 | return false
105 | } else if (!BluetoothAdapter.getDefaultAdapter().isEnabled) {
106 | Toast.makeText(applicationContext, "BlueTooth is not enabled!", Toast.LENGTH_SHORT).show()
107 | return false
108 | } else {
109 | GlobalApp.BLE?.initialize()
110 | GlobalApp.BLE?.disconnect()
111 | GlobalApp.BLE?.close()
112 | if (item.itemId == R.id.scan_btn) {
113 | if (this.scanFlag) {
114 | this.bluetoothLeScanner.stopScan(bleScanner)
115 | item.title = getString(R.string.scan)
116 | } else {
117 | this.deviceList.removeAll(deviceList)
118 | this.resultsList.removeAll(resultsList)
119 | select_device_list.adapter?.notifyDataSetChanged()
120 | this.bluetoothLeScanner.startScan(bleScanner)
121 | item.title = getString(R.string.stop_scan)
122 | }
123 | this.scanFlag = !this.scanFlag
124 | }
125 | return super.onOptionsItemSelected(item)
126 | }
127 | }
128 |
129 | @UiThread
130 | private fun partItemClicked(partItem: BluetoothDevice) {
131 | var isConnected = false
132 | val index = deviceList.indexOf(partItem)
133 | val serviceUUID: UUID? = resultsList[index].scanRecord?.serviceUuids?.get(0)?.uuid
134 | progressBar = findViewById(R.id.progress_circularBar)
135 |
136 | Thread(Runnable {
137 | this.runOnUiThread { progressBar?.visibility = View.VISIBLE }
138 |
139 | try {
140 | this.bluetoothLeScanner.stopScan(this.bleScanner)
141 | isConnected = GlobalApp.BLE?.connect(partItem.address.toString())!!
142 | } catch (e: Throwable) {
143 | e.printStackTrace()
144 | }
145 | Thread.sleep(1000)
146 | if (isConnected && serviceUUID != null) {
147 | GlobalApp.BLE?.device = partItem
148 | GlobalApp.BLE?.scanResult = this.resultsList[index]
149 | this.runOnUiThread { createDialog(this, "Success!", "Connected To BLE Device!", "OK") }
150 | } else if (this.scanFlag) {
151 | this.runOnUiThread { Toast.makeText(applicationContext, "Invalid Device!", Toast.LENGTH_SHORT).show() }
152 | this.bluetoothLeScanner.startScan(bleScanner)
153 | } else {
154 | this.runOnUiThread { Toast.makeText(applicationContext, "Invalid Device!", Toast.LENGTH_SHORT).show() }
155 | }
156 | this.runOnUiThread { progressBar?.visibility = View.GONE }
157 | }).start()
158 | }
159 |
160 | @UiThread
161 | private fun createDialog(c: Context, title: String, msg: String, button: String) {
162 | val builder = AlertDialog.Builder(c)
163 | builder.setTitle(title)
164 | builder.setMessage(msg)
165 | builder.setPositiveButton(button) { _, _ ->
166 | builder.create().dismiss()
167 | onSupportNavigateUp()
168 | }
169 | builder.create().show()
170 | }
171 |
172 | override fun onSupportNavigateUp(): Boolean {
173 | onBackPressed()
174 | supportFragmentManager.popBackStack()
175 | CustomIntent.customType(this, "right-to-left")
176 | return true
177 | }
178 | }
179 |
180 |
181 | // https://stackoverflow.com/questions/32759785/is-it-possible-to-allocate-these-folders-in-another-place
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/ui/map/MapFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gps.ui.map
2 |
3 | import android.content.Context
4 | import android.content.ContextWrapper
5 | import android.graphics.Bitmap
6 | import android.graphics.Canvas
7 | import android.os.Bundle
8 | import android.util.Log
9 | import android.view.LayoutInflater
10 | import android.view.View
11 | import android.view.ViewGroup
12 | import android.widget.Toast
13 | import androidx.core.content.ContextCompat
14 | import androidx.fragment.app.Fragment
15 | import androidx.lifecycle.ViewModelProvider
16 | import com.google.android.gms.maps.CameraUpdateFactory
17 | import com.google.android.gms.maps.GoogleMap
18 | import com.google.android.gms.maps.OnMapReadyCallback
19 | import com.google.android.gms.maps.model.*
20 | import com.gps.MainActivity
21 | import com.gps.R
22 | import com.gps.objects.*
23 | import kotlinx.android.synthetic.main.fragment_map.view.*
24 | import kotlinx.android.synthetic.main.fragment_map.view.statusBar1
25 | import kotlinx.android.synthetic.main.fragment_map.view.statusBar2
26 | import kotlinx.android.synthetic.main.fragment_map.view.statusBar3
27 | import kotlinx.android.synthetic.main.fragment_map.view.statusBar4
28 |
29 | class MapFragment : Fragment(), OnMapReadyCallback {
30 |
31 | private lateinit var mapViewModel: MapViewModel
32 |
33 | private var mapCheckThread: Thread? = null
34 |
35 | private lateinit var googleMap: GoogleMap
36 | private lateinit var markerOptions: MarkerOptions
37 | private lateinit var marker: Marker
38 | private lateinit var cameraPosition: CameraPosition
39 | private var defaultLatitude = 49.279793
40 | private var defaultLongitude = -123.115669
41 |
42 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
43 | mapViewModel = ViewModelProvider(this).get(MapViewModel::class.java)
44 | val view = inflater.inflate(R.layout.fragment_map, container, false)
45 |
46 | view?.mapView?.onCreate(savedInstanceState)
47 | view?.mapView?.getMapAsync(this)
48 | this.createMaps()
49 |
50 | view.toggleButton.setOnClickListener {
51 | if (this.mapCheckThread != null && this.mapCheckThread!!.isAlive) this.mapCheckThread?.interrupt()
52 | this.mapCheckThread = null
53 | this.mapCheckThread = Thread(Runnable { this.updateMapTask() })
54 | this.mapCheckThread!!.start()
55 | }
56 |
57 | return view
58 | }
59 |
60 | override fun onStart() {
61 | super.onStart()
62 | this.bleLinkCheck()
63 | view?.mapView?.onStart()
64 | Log.d("MAP", "Start m Fragment")
65 | }
66 |
67 | override fun onResume() {
68 | super.onResume()
69 | view?.mapView?.onResume()
70 | Log.d("MAP", "onResume m Fragment")
71 | }
72 |
73 | override fun onPause() {
74 | view?.mapView?.onResume()
75 | Log.d("MAP", "Pause m Fragment")
76 | super.onPause()
77 | }
78 |
79 | override fun onStop() {
80 | super.onStop()
81 | view?.mapView?.onStop()
82 | if (this.mapCheckThread != null && this.mapCheckThread?.isAlive!!) this.mapCheckThread?.interrupt()
83 | this.mapCheckThread = null
84 | Log.d("MAP", "Stopped m Fragment")
85 | }
86 |
87 | override fun onDestroy() {
88 | view?.mapView?.onDestroy()
89 | super.onDestroy()
90 | }
91 |
92 | override fun onLowMemory() {
93 | super.onLowMemory()
94 | view?.mapView?.onLowMemory()
95 | }
96 |
97 | override fun onMapReady(googleMap: GoogleMap?) {
98 | this.googleMap = googleMap!!
99 | this.googleMap.uiSettings.isZoomControlsEnabled = true
100 | this.googleMap.uiSettings.isCompassEnabled = true
101 | this.googleMap.uiSettings.isMapToolbarEnabled = true
102 | this.googleMap.uiSettings.isScrollGesturesEnabled = true
103 | this.googleMap.uiSettings.isTiltGesturesEnabled = true
104 | this.googleMap.uiSettings.isRotateGesturesEnabled = true
105 | this.marker = googleMap.addMarker(this.markerOptions)
106 | googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(this.cameraPosition))
107 | googleMap.mapType = GoogleMap.MAP_TYPE_NORMAL
108 | }
109 |
110 | private fun createMaps() {
111 | this.markerOptions = MarkerOptions()
112 | val bitmapDescriptor = bitmapDescriptorFromVector(activity as Context, R.drawable.ic_map_marker_24dp)
113 | this.markerOptions.icon(bitmapDescriptor)
114 | this.markerOptions.position(LatLng(defaultLatitude, defaultLongitude))
115 | this.cameraPosition = CameraPosition.Builder().target(LatLng(defaultLatitude, defaultLongitude)).zoom(10f).build()
116 | }
117 |
118 | private fun bitmapDescriptorFromVector(context: Context, vectorResId: Int): BitmapDescriptor? {
119 | return ContextCompat.getDrawable(context, vectorResId)?.run {
120 | setBounds(0, 0, intrinsicWidth, intrinsicHeight)
121 | val bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
122 | draw(Canvas(bitmap))
123 | BitmapDescriptorFactory.fromBitmap(bitmap)
124 | }
125 | }
126 |
127 | private fun drawPath() {
128 |
129 | }
130 |
131 | private fun updateMaps() {
132 | if (GlobalApp.BLE?.gpsData != null && GlobalApp.BLE?.gpsData != "") {
133 | var gpsData = GlobalApp.BLE?.gpsData!!
134 | gpsData = gpsData.substring(gpsData.indexOf('[') + 1, gpsData.indexOf(']'))
135 | val gpdDataList = gpsData.split(',') as ArrayList
136 | this.defaultLatitude = gpdDataList[LOCATION_LAT_INDEX].toDouble()
137 | this.defaultLongitude = gpdDataList[LOCATION_LNG_INDEX].toDouble()
138 | marker.position = LatLng(defaultLatitude, defaultLongitude)
139 | cameraPosition = CameraPosition.Builder().target(LatLng(defaultLatitude, defaultLongitude)).zoom(15f).build()
140 | googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
141 | }
142 | }
143 |
144 | private fun updateMapTask() {
145 | val delay = 5 // TODO: configurable delay (in settings fragment)
146 | try {
147 | this.connectionHandler()
148 | while (true) {
149 | if (GlobalApp.BLE?.connectionState == STATE_CONNECTED || GlobalApp.BLE?.bleGATT != null) {
150 | GlobalApp.BLE?.fetchDeviceStatus()
151 | if (GlobalApp.BLE?.gpsStatusFlags?.get(GPS_FIX_FLAG_INDEX)!!) GlobalApp.BLE?.fetchGPSData()
152 | else GlobalApp.BLE?.gpsData = ""
153 | activity?.runOnUiThread { updateMaps() }
154 | Thread.sleep((delay * TIME_OUT).toLong())
155 | Log.d("MAP", "UPDATING MAPS")
156 | } else throw IllegalArgumentException("NO GPS FIX")
157 | }
158 | } catch (e: Throwable) {
159 | Log.e("MAP", "MAP UPDATE ERROR")
160 | }
161 | }
162 |
163 | private fun connectionCheck(): Boolean {
164 | if (GlobalApp.BLE?.bleAddress == null || GlobalApp.BLE?.bleAddress == "") {
165 | Toast.makeText(activity, "No Device Paired", Toast.LENGTH_SHORT).show()
166 | return false
167 | }
168 | val start = System.currentTimeMillis()
169 | while (GlobalApp.BLE?.connectionState != STATE_CONNECTED || GlobalApp.BLE?.bleGATT == null) {
170 | if (System.currentTimeMillis() - start > 10 * TIME_OUT) return false
171 | GlobalApp.BLE?.connect(GlobalApp.BLE?.bleAddress!!)!!
172 | Thread.sleep(250)
173 | }
174 | Thread.sleep(1000)
175 | return true
176 | }
177 |
178 | private fun connectionHandler() {
179 | try {
180 | Log.d("MAP", "Connection Handler Starting")
181 | val connected = connectionCheck()
182 | if (GlobalApp.BLE?.connectionState == STATE_CONNECTED || connected) {
183 | Thread.sleep(1000)
184 | GlobalApp.BLE?.fetchDeviceStatus()
185 | Thread.sleep(1000)
186 | activity?.runOnUiThread { this.updateUIStatusBar() }
187 | } else Log.d("MAP", "Unable to Connect to Device")
188 | Log.d("MAP", "Connection Handler Complete")
189 | } catch (e: Throwable) {
190 | Log.e("MAP", "Connection Handler Error")
191 | }
192 | }
193 |
194 | private fun updateUIStatusBar() {
195 | val statusBarArray: ArrayList? = ArrayList(0)
196 | statusBarArray?.add(view?.statusBar1!!)
197 | statusBarArray?.add(view?.statusBar2!!)
198 | statusBarArray?.add(view?.statusBar3!!)
199 | statusBarArray?.add(view?.statusBar4!!)
200 | for (i in 0 until NUMBER_OF_FLAGS) {
201 | val view: View? = statusBarArray?.get(i)
202 | if (statusBarArray != null && GlobalApp.BLE?.gpsStatusFlags?.get(i)!!) {
203 | if (view != null) {
204 | statusBarArray[i].background = ContextCompat.getDrawable(requireContext(), R.drawable.status_on)
205 | }
206 | } else if (!GlobalApp.BLE?.gpsStatusFlags?.get(i)!!) {
207 | if (view != null) {
208 | statusBarArray[i].background = ContextCompat.getDrawable(requireContext(), R.drawable.status_off)
209 | }
210 | }
211 | }
212 | }
213 |
214 | private fun bleLinkCheck() {
215 | val main = activity as MainActivity?
216 | if (main != null && main.checkBTon() && GlobalApp.BLE?.connectionState == STATE_CONNECTED) {
217 | if (GlobalApp.BLE == null) {
218 | GlobalApp.BLE = BLEDevice(main as Context, activity as ContextWrapper)
219 | GlobalApp.BLE!!.initialize()
220 | }
221 | if (this.mapCheckThread != null && this.mapCheckThread!!.isAlive) this.mapCheckThread!!.interrupt()
222 | this.mapCheckThread = null
223 | this.mapCheckThread = Thread(Runnable { this.connectionHandler() })
224 | this.mapCheckThread!!.start()
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/objects/BLEDevice.kt:
--------------------------------------------------------------------------------
1 | package com.gps.objects
2 |
3 | import android.bluetooth.*
4 | import android.bluetooth.le.ScanResult
5 | import android.content.Context
6 | import android.content.ContextWrapper
7 | import android.database.Cursor
8 | import android.util.Log
9 | import android.widget.Toast
10 | import java.util.*
11 |
12 | //https://developer.android.com/reference/android/bluetooth/BluetoothGatt
13 |
14 | const val GPS_CHECK_CODE: Byte = 0x00
15 | const val GET_GPS_STATUS_CODE: Byte = 0x01
16 | const val SET_GPS_ON_CODE: Byte = 0x02
17 | const val SET_GPS_OFF_CODE: Byte = 0x03
18 | const val SET_GPG_LOGGING_ON_CODE: Byte = 0x04
19 | const val SET_GPG_LOGGING_OFF_CODE: Byte = 0x05
20 | const val GET_GPS_DATA_CODE: Byte = 0x06
21 | const val LIST_LOG_FILES_CODE: Byte = 0x07
22 | const val READ_LOG_FILE_CODE: Byte = 0x08
23 | const val GET_SDCARD_STATUS_CODE: Byte = 0x09
24 | const val GPS_REBOOT_CODE: Byte = 0x0a
25 | const val GPS_RESET_CODE: Byte = 0x0b
26 |
27 | const val NUMBER_OF_FLAGS: Int = 4
28 | const val GPS_CONNECTION_FLAG_INDEX: Int = 0
29 | const val GPS_ON_FLAG_INDEX: Int = 1
30 | const val GPS_FIX_FLAG_INDEX: Int = 2
31 | const val GPS_LOGGING_FLAG_INDEX: Int = 3
32 |
33 | const val LOCATION_IS_VALID_INDEX: Int = 0
34 | const val LOCATION_IS_UPDATED_INDEX: Int = 1
35 | const val LOCATION_AGE_INDEX: Int = 2
36 | const val LOCATION_LAT_INDEX: Int = 3
37 | const val LOCATION_LNG_INDEX: Int = 4
38 | const val DATE_YEAR_INDEX: Int = 5
39 | const val DATE_MONTH_INDEX: Int = 6
40 | const val DATE_DAY_INDEX: Int = 7
41 | const val TIME_HOUR_INDEX: Int = 8
42 | const val TIME_MINUTE_INDEX: Int = 9
43 | const val TIME_SECOND_INDEX: Int = 10
44 | const val SATELLITES_VALUE_INDEX: Int = 11
45 | const val SPEED_KMPH_INDEX: Int = 12
46 | const val COURSE_DEG_INDEX: Int = 13
47 | const val ALTITUDE_METERS_INDEX: Int = 14
48 | const val HDOP_VALUE_INDEX: Int = 15
49 |
50 | const val STATE_DISCONNECTED: Int = 0
51 | const val STATE_CONNECTING: Int = 1
52 | const val STATE_CONNECTED: Int = 2
53 |
54 | const val TIME_OUT: Int = 1000
55 | const val DEVICE_CODE_NAME: String = "ESP32_GPS"
56 | const val oneByte: Byte = 1
57 |
58 | val SERVICE_UUID: UUID = UUID.fromString("000ffdf4-68d9-4e48-a89a-219e581f0d64")
59 | val CHARACTERISTIC_UUID: UUID = UUID.fromString("44a80b83-c605-4406-8e50-fc42f03b6d38")
60 |
61 | class BLEDevice(c: Context, var applicationContext: ContextWrapper) {
62 |
63 | var context: Context = c
64 | var connectionState = STATE_DISCONNECTED
65 |
66 | var bleManager: BluetoothManager? = null
67 | var bleAdapter: BluetoothAdapter? = null
68 | var bleGATT: BluetoothGatt? = null
69 |
70 | var device: BluetoothDevice? = null
71 | var scanResult: ScanResult? = null
72 | var bleAddress: String? = null
73 | var gpsData: String? = null
74 | var transactionSuccess: Boolean = false
75 | var gpsStatusFlags: BooleanArray? = BooleanArray(NUMBER_OF_FLAGS)
76 |
77 | private var service: BluetoothGattService? = null
78 | private var characteristic: BluetoothGattCharacteristic? = null
79 | private var dbHandler: SqliteDB? = null
80 |
81 | private val mGattCallback = object : BluetoothGattCallback() {
82 |
83 | override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
84 | super.onConnectionStateChange(gatt, status, newState)
85 | if (newState == BluetoothProfile.STATE_CONNECTED) {
86 | connectionState = STATE_CONNECTED
87 | Log.d("BLE", "STATE_CONNECTED")
88 | } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
89 | connectionState = STATE_DISCONNECTED
90 | Log.d("BLE", "STATE_DISCONNECTED")
91 | }
92 | }
93 |
94 | override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
95 | super.onServicesDiscovered(gatt, status)
96 | if (status == BluetoothGatt.GATT_SUCCESS) {
97 | Log.d("BLE", "onServicesDiscovered received: $status")
98 | } else {
99 | Log.d("BLE", "onServicesDiscovered received: $status")
100 | }
101 | }
102 |
103 | override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
104 | super.onCharacteristicChanged(gatt, characteristic)
105 | Log.d("BLE", "onCharacteristicChanged received")
106 | }
107 |
108 | override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
109 | super.onCharacteristicRead(gatt, characteristic, status)
110 | if (status == BluetoothGatt.GATT_SUCCESS) {
111 | Log.d("BLE", "onCharacteristicRead received: $status")
112 | transactionSuccess = true
113 | }
114 | }
115 |
116 | override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
117 | super.onCharacteristicWrite(gatt, characteristic, status)
118 | if (status == BluetoothGatt.GATT_SUCCESS) {
119 | Log.d("BLE", "onCharacteristicWrite received: $status")
120 | transactionSuccess = true
121 | }
122 | }
123 |
124 | override fun onDescriptorRead(gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int) {
125 | super.onDescriptorRead(gatt, descriptor, status)
126 | if (status == BluetoothGatt.GATT_SUCCESS) {
127 | Log.d("BLE", "onDescriptorRead received: $status")
128 | transactionSuccess = true
129 | }
130 | }
131 |
132 | override fun onDescriptorWrite(gatt: BluetoothGatt?, descriptor: BluetoothGattDescriptor?, status: Int) {
133 | super.onDescriptorWrite(gatt, descriptor, status)
134 | if (status == BluetoothGatt.GATT_SUCCESS) {
135 | Log.d("BLE", "onDescriptorWrite received: $status")
136 | transactionSuccess = true
137 | }
138 | }
139 |
140 | override fun onPhyRead(gatt: BluetoothGatt?, txPhy: Int, rxPhy: Int, status: Int) {
141 | super.onPhyRead(gatt, txPhy, rxPhy, status)
142 | if (status == BluetoothGatt.GATT_SUCCESS) {
143 | Log.d("BLE", "onPhyRead received: $status")
144 | }
145 | }
146 |
147 | override fun onPhyUpdate(gatt: BluetoothGatt?, txPhy: Int, rxPhy: Int, status: Int) {
148 | super.onPhyUpdate(gatt, txPhy, rxPhy, status)
149 | if (status == BluetoothGatt.GATT_SUCCESS) {
150 | Log.d("BLE", "onPhyUpdate received: $status")
151 | }
152 | }
153 |
154 | override fun onReliableWriteCompleted(gatt: BluetoothGatt?, status: Int) {
155 | super.onReliableWriteCompleted(gatt, status)
156 | if (status == BluetoothGatt.GATT_SUCCESS) {
157 | Log.d("BLE", "onReliableWriteCompleted received: $status")
158 | }
159 | }
160 |
161 | override fun onReadRemoteRssi(gatt: BluetoothGatt?, rssi: Int, status: Int) {
162 | super.onReadRemoteRssi(gatt, rssi, status)
163 | if (status == BluetoothGatt.GATT_SUCCESS) {
164 | Log.d("BLE", "onReadRemoteRssi received: $status, RSSI: $rssi")
165 | }
166 | }
167 |
168 | override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
169 | super.onMtuChanged(gatt, mtu, status)
170 | if (status == BluetoothGatt.GATT_SUCCESS) {
171 | Log.d("BLE", "onMtuChanged received: $status, MTU: $mtu")
172 | }
173 | }
174 | }
175 |
176 | fun initialize(): Boolean {
177 | if (this.bleManager == null) {
178 | this.bleManager = this.applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
179 | if (this.bleManager == null) return false
180 | }
181 | this.bleAdapter = this.bleManager!!.adapter
182 | if (this.bleAdapter == null) return false
183 | dbHandler = SqliteDB(context, null)
184 | this.gpsStatusFlags?.fill(false, 0, NUMBER_OF_FLAGS)
185 | this.loadDBMAC()
186 | return true
187 | }
188 |
189 | fun connect(address: String?): Boolean {
190 | val mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
191 | if (mBluetoothAdapter == null || address == null || !mBluetoothAdapter.isEnabled) return false
192 | if (this.bleAddress != null && address == this.bleAddress && this.bleGATT != null) {
193 | return if (this.bleGATT!!.connect()) {
194 | this.connectionState = STATE_CONNECTING
195 | true
196 | } else false
197 | }
198 | if (mBluetoothAdapter.getRemoteDevice(address) == null) return false
199 | this.bleGATT = mBluetoothAdapter.getRemoteDevice(address).connectGatt(context, false, mGattCallback)
200 | this.bleAddress = address
201 | this.connectionState = STATE_CONNECTING
202 | val checkA = serviceChecks()
203 | val checkB = gpsTagCheck()
204 | if (!checkA || !checkB) return false
205 | updateDBMAC(address)
206 | return true
207 | }
208 |
209 | fun disconnect() {
210 | this.gpsStatusFlags?.fill(false, 0, NUMBER_OF_FLAGS)
211 | this.gpsData = ""
212 | if (this.bleAdapter == null || this.bleGATT == null) return
213 | this.bleGATT!!.disconnect()
214 | }
215 |
216 | fun close() {
217 | if (this.bleGATT == null) return
218 | this.bleGATT!!.close()
219 | this.bleGATT = null
220 | }
221 |
222 | private fun updateDBMAC(address: String?) {
223 | dbHandler?.clearTable(SqliteDB.macTable)
224 | if (address != null) {
225 | dbHandler?.insertDB(SqliteDB.macTable, arrayListOf(SqliteDB.macColumn), arrayListOf(address))
226 | }
227 | }
228 |
229 | private fun loadDBMAC(): String? {
230 | return try {
231 | val cursor: Cursor? = dbHandler?.selectFromDB(SqliteDB.macTable)
232 | cursor!!.moveToFirst()
233 | this.bleAddress = cursor.getString(cursor.getColumnIndex(SqliteDB.macColumn))
234 | this.bleAddress!!
235 | } catch (e: Throwable) {
236 | this.bleAddress = ""
237 | this.bleAddress
238 | }
239 | }
240 |
241 | private fun serviceChecks(timeout: Boolean = true): Boolean {
242 | val start = System.currentTimeMillis()
243 | var serviceList = GlobalApp.BLE!!.bleGATT?.services
244 | var foundService = false
245 | var foundCharacteristics = false
246 | while (serviceList != null && serviceList.size < 1 && !foundService && !foundCharacteristics) {
247 | GlobalApp.BLE!!.bleGATT?.discoverServices()
248 | serviceList = GlobalApp.BLE!!.bleGATT?.services
249 | if (serviceList != null) for (item in serviceList) {
250 | if (item.uuid == SERVICE_UUID) {
251 | foundService = true
252 | if (item.characteristics != null) for (characterItem in item.characteristics) {
253 | foundCharacteristics = characterItem.uuid == CHARACTERISTIC_UUID
254 | }
255 | } else foundService = false
256 | }
257 | if (timeout && System.currentTimeMillis() - start > 3 * TIME_OUT) break
258 | }
259 | if (serviceList == null || serviceList.size < 1) return false
260 | for (serviceItem in serviceList) {
261 | if (serviceItem.uuid == SERVICE_UUID) {
262 | this.service = serviceItem
263 | if (serviceItem.characteristics != null) for (characterItem in serviceItem.characteristics) {
264 | if (characterItem.uuid == CHARACTERISTIC_UUID) {
265 | this.characteristic = characterItem
266 | break
267 | }
268 | }
269 | }
270 | }
271 | if (this.service == null || this.characteristic == null) return false
272 | return true
273 | }
274 |
275 | private fun gpsTagCheck(): Boolean {
276 | writeValue(byteArrayOf(GPS_CHECK_CODE))
277 | val returnVal = readValue()
278 | if (returnVal != null && returnVal.toString(Charsets.UTF_8).contains(DEVICE_CODE_NAME, ignoreCase = true)) {
279 | Log.d("BLE", returnVal.contentToString())
280 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
281 | } else return false
282 | return true
283 | }
284 |
285 | private fun writeValue(value: ByteArray): Boolean {
286 | this.transactionSuccess = false
287 | val serviceCheck: Boolean = if (this.service == null || this.characteristic == null) {
288 | GlobalApp.BLE?.serviceChecks()!!
289 | } else true
290 | if (serviceCheck) {
291 | this.characteristic!!.value = value
292 | GlobalApp.BLE?.bleGATT?.writeCharacteristic(this.characteristic)
293 | } else return false
294 | val start = System.currentTimeMillis()
295 | while (!this.transactionSuccess) {
296 | if (System.currentTimeMillis() - start > 5 * TIME_OUT) return false
297 | }
298 | this.transactionSuccess = false
299 | return true
300 | }
301 |
302 | private fun readValue(): ByteArray? {
303 | this.transactionSuccess = false
304 | val serviceCheck: Boolean = if (this.service == null || this.characteristic == null) {
305 | GlobalApp.BLE?.serviceChecks()!!
306 | } else true
307 | if (serviceCheck) {
308 | GlobalApp.BLE?.bleGATT?.readCharacteristic(this.characteristic)
309 | } else return null
310 | val start = System.currentTimeMillis()
311 | while (!this.transactionSuccess) {
312 | if (System.currentTimeMillis() - start > 5 * TIME_OUT) return null
313 | }
314 | this.transactionSuccess = false
315 | return this.characteristic?.value
316 | }
317 |
318 | private fun Int.toBoolean(): Boolean {
319 | return this == 1
320 | }
321 |
322 | fun fetchDeviceStatus(): Boolean {
323 | writeValue(byteArrayOf(GET_GPS_STATUS_CODE))
324 | val returnVal = readValue()
325 | this.gpsStatusFlags?.fill(false, 0, NUMBER_OF_FLAGS)
326 | if (returnVal != null && returnVal.size == NUMBER_OF_FLAGS) {
327 | this.gpsStatusFlags?.set(GPS_CONNECTION_FLAG_INDEX, returnVal[GPS_CONNECTION_FLAG_INDEX].toInt().toBoolean())
328 | this.gpsStatusFlags?.set(GPS_FIX_FLAG_INDEX, returnVal[GPS_FIX_FLAG_INDEX].toInt().toBoolean())
329 | this.gpsStatusFlags?.set(GPS_ON_FLAG_INDEX, returnVal[GPS_ON_FLAG_INDEX].toInt().toBoolean())
330 | this.gpsStatusFlags?.set(GPS_LOGGING_FLAG_INDEX, returnVal[GPS_LOGGING_FLAG_INDEX].toInt().toBoolean())
331 | Log.d("BLE", returnVal.contentToString())
332 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
333 | } else return false
334 | return true
335 | }
336 |
337 | fun GPSon(): Boolean {
338 | writeValue(byteArrayOf(SET_GPS_ON_CODE))
339 | val returnVal = readValue()
340 | if (returnVal != null && returnVal[0] == oneByte && returnVal[1] == oneByte) {
341 | Log.d("BLE", returnVal.contentToString())
342 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
343 | } else return false
344 | return true
345 | }
346 |
347 | fun GPSoff(): Boolean {
348 | writeValue(byteArrayOf(SET_GPS_OFF_CODE))
349 | val returnVal = readValue()
350 | if (returnVal != null && returnVal[0] == oneByte && returnVal[1] == oneByte) {
351 | Log.d("BLE", returnVal.contentToString())
352 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
353 | } else return false
354 | return true
355 | }
356 |
357 | fun GPSloggingOn(): Boolean {
358 | writeValue(byteArrayOf(SET_GPG_LOGGING_ON_CODE))
359 | val returnVal = readValue()
360 | if (returnVal != null && returnVal[0] == oneByte && returnVal[1] == oneByte) {
361 | Log.d("BLE", returnVal.contentToString())
362 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
363 | } else return false
364 | return true
365 | }
366 |
367 | fun GPSloggingOff(): Boolean {
368 | writeValue(byteArrayOf(SET_GPG_LOGGING_OFF_CODE))
369 | val returnVal = readValue()
370 | if (returnVal != null && returnVal[0] == oneByte && returnVal[1] == oneByte) {
371 | Log.d("BLE", returnVal.contentToString())
372 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
373 | } else return false
374 | return true
375 | }
376 |
377 | fun fetchGPSData(): String {
378 | writeValue(byteArrayOf(GET_GPS_DATA_CODE))
379 | val returnVal = readValue()
380 | return if (returnVal != null) {
381 | Log.d("BLE", returnVal.contentToString())
382 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
383 | this.gpsData = returnVal.toString(Charsets.UTF_8)
384 | returnVal.toString(Charsets.UTF_8)
385 | } else {
386 | this.gpsData = ""
387 | ""
388 | }
389 | }
390 |
391 | fun listLogFiles(): String {
392 | writeValue(byteArrayOf(LIST_LOG_FILES_CODE))
393 | val returnVal = readValue()
394 | if (returnVal != null) {
395 | Log.d("BLE", returnVal.contentToString())
396 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
397 | } else return ""
398 | return returnVal.toString(Charsets.UTF_8)
399 | }
400 |
401 | fun readLogFile(index: Int): String {
402 | val indexByte: Byte = index.toByte()
403 | writeValue(byteArrayOf(READ_LOG_FILE_CODE, indexByte))
404 | val returnVal = readValue()
405 |
406 | return returnVal?.toString(Charsets.UTF_8) ?: ""
407 | }
408 |
409 | fun SDCardStatus(): String {
410 | writeValue(byteArrayOf(GET_SDCARD_STATUS_CODE))
411 | val returnVal = readValue()
412 | if (returnVal != null) {
413 | Log.d("BLE", returnVal.contentToString())
414 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
415 | } else return ""
416 | return returnVal.toString(Charsets.UTF_8)
417 | }
418 |
419 | fun GPSReboot(): Boolean {
420 | writeValue(byteArrayOf(GPS_REBOOT_CODE))
421 | val returnVal = readValue()
422 | if (returnVal != null && returnVal[0] == oneByte && returnVal[1] == oneByte) {
423 | Log.d("BLE", returnVal.contentToString())
424 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
425 | } else return false
426 | return true
427 | }
428 |
429 | fun GPSReset(): Boolean {
430 | writeValue(byteArrayOf(GPS_RESET_CODE))
431 | val returnVal = readValue()
432 | if (returnVal != null && returnVal[0] == oneByte && returnVal[1] == oneByte) {
433 | Log.d("BLE", returnVal.contentToString())
434 | Log.d("BLE", returnVal.toString(Charsets.UTF_8))
435 | } else return false
436 | return true
437 | }
438 | }
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
21 |
22 |
28 |
29 |
35 |
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
55 |
56 |
60 |
61 |
76 |
77 |
93 |
94 |
110 |
111 |
127 |
128 |
143 |
144 |
145 |
146 |
147 |
148 |
160 |
161 |
168 |
169 |
179 |
180 |
191 |
192 |
204 |
205 |
217 |
218 |
219 |
220 |
227 |
228 |
238 |
239 |
247 |
248 |
259 |
260 |
268 |
269 |
280 |
281 |
289 |
290 |
291 |
303 |
304 |
312 |
313 |
314 |
315 |
316 |
323 |
324 |
334 |
335 |
346 |
347 |
359 |
360 |
371 |
372 |
373 |
374 |
375 |
381 |
382 |
383 |
393 |
394 |
405 |
406 |
417 |
418 |
429 |
430 |
441 |
442 |
453 |
454 |
455 |
456 |
457 |
458 |
467 |
468 |
469 |
--------------------------------------------------------------------------------
/GPS_APP/app/src/main/java/com/gps/ui/home/HomeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.gps.ui.home
2 |
3 | import android.content.Context
4 | import android.content.ContextWrapper
5 | import android.graphics.Color
6 | import android.os.Bundle
7 | import android.util.Log
8 | import android.view.LayoutInflater
9 | import android.view.View
10 | import android.view.ViewGroup
11 | import android.widget.TextView
12 | import android.widget.Toast
13 | import androidx.appcompat.widget.AppCompatImageView
14 | import androidx.core.content.ContextCompat
15 | import androidx.fragment.app.Fragment
16 | import androidx.lifecycle.ViewModelProvider
17 | import com.gps.MainActivity
18 | import com.gps.R
19 | import com.gps.objects.*
20 | import kotlinx.android.synthetic.main.fragment_home.view.*
21 |
22 | class HomeFragment : Fragment() {
23 |
24 | private lateinit var homeViewModel: HomeViewModel
25 |
26 | private var autoCheckRunning: Boolean = false
27 | private var statueCheckThread: Thread? = null
28 |
29 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
30 | homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java)
31 | val view = inflater.inflate(R.layout.fragment_home, container, false)
32 |
33 | view.action_button_A.setOnClickListener {
34 | val main = activity as MainActivity?
35 | if (main != null && main.checkBTon() && GlobalApp.BLE?.connectionState == STATE_CONNECTED) {
36 | if (this.statueCheckThread != null && this.statueCheckThread?.isAlive!!) this.statueCheckThread?.interrupt()
37 | this.statueCheckThread = null
38 | this.disconnectionUIHandler()
39 | view?.action_button_A?.setColorFilter(Color.WHITE)
40 | } else if (main != null && main.checkBTon()) {
41 | if (GlobalApp.BLE == null) {
42 | GlobalApp.BLE = BLEDevice(main as Context, activity as ContextWrapper)
43 | GlobalApp.BLE!!.initialize()
44 | }
45 | if (this.statueCheckThread != null && this.statueCheckThread?.isAlive!!) this.statueCheckThread?.interrupt()
46 | this.statueCheckThread = null
47 | this.statueCheckThread = Thread(Runnable { this.connectionUIHandler() })
48 | this.statueCheckThread!!.start()
49 | }
50 | }
51 |
52 | view.action_button_B.setOnClickListener {
53 | if (this.statueCheckThread != null && this.statueCheckThread?.isAlive!!) this.statueCheckThread?.interrupt()
54 | this.statueCheckThread = null
55 | this.statueCheckThread = Thread(Runnable { this.toggleGPS() })
56 | this.statueCheckThread!!.start()
57 | }
58 |
59 | view.action_button_D.setOnClickListener {
60 | if (this.statueCheckThread != null && this.statueCheckThread?.isAlive!!) this.statueCheckThread?.interrupt()
61 | this.statueCheckThread = null
62 | this.statueCheckThread = Thread(Runnable { this.toggleLogging() })
63 | this.statueCheckThread!!.start()
64 | }
65 |
66 | view.action_button_E.setOnClickListener {
67 | val main = activity as MainActivity?
68 | if (main != null && main.checkBTon() && !autoCheckRunning) {
69 | autoCheckRunning = true
70 | if (this.statueCheckThread != null && this.statueCheckThread?.isAlive!!) this.statueCheckThread?.interrupt()
71 | this.statueCheckThread = null
72 | this.statueCheckThread = Thread(Runnable { this.checkTaskLoop() })
73 | this.statueCheckThread!!.start()
74 | } else if (main != null && main.checkBTon() && this.statueCheckThread != null && this.statueCheckThread?.isAlive!!) {
75 | autoCheckRunning = false
76 | this.statueCheckThread?.interrupt()
77 | this.statueCheckThread = null
78 | view?.action_button_E?.setImageResource(R.drawable.ic_sync_disabled_black_24dp)
79 | view?.action_button_E?.setColorFilter(Color.WHITE)
80 | }
81 | }
82 | return view
83 | }
84 |
85 | override fun onStart() {
86 | super.onStart()
87 | this.updateUIDeviceInfo()
88 | Log.d("HOME", "Start h Fragment")
89 | }
90 |
91 | override fun onResume() {
92 | super.onResume()
93 | this.bleLinkCheck()
94 | Log.d("HOME", "onResume h Fragment")
95 | }
96 |
97 | override fun onPause() {
98 | super.onPause()
99 | Log.d("HOME", "Pause h Fragment")
100 | }
101 |
102 | override fun onStop() {
103 | super.onStop()
104 | if (this.statueCheckThread != null && this.statueCheckThread?.isAlive!!) this.statueCheckThread?.interrupt()
105 | this.statueCheckThread = null
106 | Log.d("HOME", "Stopped h Fragment")
107 | }
108 |
109 | private fun connectionCheck(): Boolean {
110 | if (GlobalApp.BLE?.bleAddress == null || GlobalApp.BLE?.bleAddress == "") {
111 | activity?.runOnUiThread { this.updateUIDeviceInfo() }
112 | activity?.runOnUiThread { this.disconnectionUIHandler() }
113 | Toast.makeText(activity, "No Device Paired", Toast.LENGTH_SHORT).show()
114 | return false
115 | }
116 | val start = System.currentTimeMillis()
117 | while (GlobalApp.BLE?.connectionState != STATE_CONNECTED || GlobalApp.BLE?.bleGATT == null) {
118 | if (System.currentTimeMillis() - start > 10 * TIME_OUT) return false
119 | GlobalApp.BLE?.connect(GlobalApp.BLE?.bleAddress!!)!!
120 | Thread.sleep(250)
121 | }
122 | activity?.runOnUiThread { this.updateUIDeviceInfo() }
123 | Thread.sleep(1000)
124 | return true
125 | }
126 |
127 | private fun updateUIAll() {
128 | this.updateUIStatusBar()
129 | this.updateUIStatusButtons()
130 | this.updateUIStatusInfo()
131 | this.updateUICoordinateInfo()
132 | }
133 |
134 | private fun toggleGPS() {
135 | try {
136 | Log.d("HOME", "Toggled GPS")
137 | activity?.runOnUiThread { view?.status_progress_bar?.visibility = View.VISIBLE }
138 | val connected = connectionCheck()
139 | if (GlobalApp.BLE?.connectionState == STATE_CONNECTED || connected) {
140 | if (GlobalApp.BLE?.gpsStatusFlags?.get(GPS_ON_FLAG_INDEX)!!) {
141 | if (!GlobalApp.BLE?.GPSoff()!!) throw IllegalArgumentException("GPS off Failed")
142 | } else {
143 | if (!GlobalApp.BLE?.GPSon()!!) throw IllegalArgumentException("GPS on Failed")
144 | GlobalApp.BLE?.fetchGPSData()
145 | }
146 | GlobalApp.BLE?.fetchDeviceStatus()
147 | activity?.runOnUiThread { this.updateUIAll() }
148 | }
149 | activity?.runOnUiThread { view?.status_progress_bar?.visibility = View.GONE }
150 | if (this.autoCheckRunning) this.checkTaskLoop()
151 | Log.d("HOME", "Toggled GPS " + GlobalApp.BLE?.gpsStatusFlags?.get(GPS_ON_FLAG_INDEX).toString())
152 | } catch (e: Throwable) {
153 | Log.e("HOME", "TOGGLE GPS ERROR")
154 | }
155 | }
156 |
157 | private fun toggleLogging() {
158 | try {
159 | Log.d("HOME", "Toggled GPS Logging")
160 | activity?.runOnUiThread { view?.status_progress_bar?.visibility = View.VISIBLE }
161 | val connected = connectionCheck()
162 | if (GlobalApp.BLE?.connectionState == STATE_CONNECTED || connected) {
163 | if (GlobalApp.BLE?.gpsStatusFlags?.get(GPS_LOGGING_FLAG_INDEX)!!) {
164 | if (!GlobalApp.BLE?.GPSloggingOff()!!) throw IllegalArgumentException("GPS Logging off Failed")
165 | } else {
166 | if (!GlobalApp.BLE?.GPSloggingOn()!!) throw IllegalArgumentException("GPS Logging on Failed")
167 | GlobalApp.BLE?.fetchGPSData()
168 | }
169 | GlobalApp.BLE?.fetchDeviceStatus()
170 | activity?.runOnUiThread { this.updateUIAll() }
171 | }
172 | activity?.runOnUiThread { view?.status_progress_bar?.visibility = View.GONE }
173 | if (this.autoCheckRunning) this.checkTaskLoop()
174 | Log.d("HOME", "Toggled GPS Logging " + GlobalApp.BLE?.gpsStatusFlags?.get(GPS_LOGGING_FLAG_INDEX).toString())
175 | } catch (e: Throwable) {
176 | Log.e("HOME", "TOGGLE GPS Logging ERROR")
177 | }
178 | }
179 |
180 | private fun checkTaskLoop() {
181 | try {
182 | Log.d("HOME", "STATUS CHECK START")
183 | activity?.runOnUiThread { view?.status_progress_bar?.visibility = View.VISIBLE }
184 | if (!connectionCheck()) {
185 | view?.status_progress_bar?.visibility = View.GONE
186 | return
187 | }
188 | val delay = 5 // TODO: configurable delay (in settings fragment)
189 | while (true) {
190 | try {
191 | if (GlobalApp.BLE?.connectionState == STATE_CONNECTED || GlobalApp.BLE?.bleGATT != null) {
192 | GlobalApp.BLE?.fetchDeviceStatus()
193 | if (GlobalApp.BLE?.gpsStatusFlags?.get(GPS_FIX_FLAG_INDEX)!!) GlobalApp.BLE?.fetchGPSData()
194 | else GlobalApp.BLE?.gpsData = ""
195 | activity?.runOnUiThread { this.updateUIAll() }
196 | activity?.runOnUiThread { view?.status_progress_bar?.visibility = View.GONE }
197 | Thread.sleep((delay * TIME_OUT).toLong())
198 | } else throw IllegalArgumentException()
199 | } catch (e: Throwable) {
200 | break
201 | }
202 | }
203 | Log.d("HOME", "STATUS CHECK STOPPED")
204 | } catch (e: Throwable) {
205 | Log.e("HOME", "STATUS CHECK ERROR")
206 | }
207 | }
208 |
209 | private fun connectionUIHandler() {
210 | try {
211 | Log.d("HOME", "Connection UI Handler Starting")
212 | activity?.runOnUiThread { view?.status_progress_bar?.visibility = View.VISIBLE }
213 | val connected = connectionCheck()
214 | if (GlobalApp.BLE?.connectionState == STATE_CONNECTED || connected) {
215 | GlobalApp.BLE?.fetchDeviceStatus()
216 | activity?.runOnUiThread { this.updateUIAll() }
217 | } else {
218 | activity?.runOnUiThread { this.disconnectionUIHandler() }
219 | Log.d("HOME", "Unable to Connect to Device")
220 | }
221 | activity?.runOnUiThread { view?.status_progress_bar?.visibility = View.GONE }
222 | Log.d("HOME", "Connection UI Handler Complete")
223 | } catch (e: Throwable) {
224 | Log.e("MAP", "Connection Handler Error")
225 | }
226 | }
227 |
228 | private fun disconnectionUIHandler() {
229 | try {
230 | this.autoCheckRunning = false
231 | view?.status_progress_bar?.visibility = View.GONE
232 | GlobalApp.BLE?.disconnect()
233 | GlobalApp.BLE?.gpsStatusFlags?.fill(false, 0, NUMBER_OF_FLAGS)
234 | GlobalApp.BLE?.gpsData = ""
235 | this.updateUIAll()
236 | } catch (e: Throwable) {
237 | Log.e("HOME", "Disconnection UI Update Error")
238 | }
239 | }
240 |
241 | private fun updateUIStatusBar() {
242 | try {
243 | val statusBarArray: ArrayList? = ArrayList(0)
244 | statusBarArray?.add(view?.statusBar1!!)
245 | statusBarArray?.add(view?.statusBar2!!)
246 | statusBarArray?.add(view?.statusBar3!!)
247 | statusBarArray?.add(view?.statusBar4!!)
248 | for (i in 0 until NUMBER_OF_FLAGS) {
249 | val view: View? = statusBarArray?.get(i)
250 | if (statusBarArray != null && GlobalApp.BLE?.gpsStatusFlags?.get(i)!!) {
251 | if (view != null) {
252 | statusBarArray[i].background = ContextCompat.getDrawable(requireContext(), R.drawable.status_on)
253 | }
254 | } else if (!GlobalApp.BLE?.gpsStatusFlags?.get(i)!!) {
255 | if (view != null) {
256 | statusBarArray[i].background = ContextCompat.getDrawable(requireContext(), R.drawable.status_off)
257 | }
258 | }
259 | }
260 | } catch (e: Throwable) {
261 | Log.e("HOME", "Update UI Status Bar Error")
262 | }
263 | }
264 |
265 | private fun updateUIStatusButtons() {
266 | try {
267 | if (GlobalApp.BLE?.connectionState == STATE_CONNECTED) {
268 | view?.action_button_A?.setImageResource(R.drawable.ic_link_black_24dp)
269 | view?.action_button_A?.setColorFilter(Color.GREEN)
270 | } else if (GlobalApp.BLE?.connectionState != STATE_CONNECTED) {
271 | view?.action_button_A?.setImageResource(R.drawable.ic_link_black_24dp)
272 | view?.action_button_A?.setColorFilter(Color.WHITE)
273 | }
274 |
275 | if (GlobalApp.BLE?.gpsStatusFlags?.get(GPS_FIX_FLAG_INDEX)!!) {
276 | view?.action_button_C?.setImageResource(R.drawable.ic_gps_fixed_black_24dp)
277 | view?.action_button_C?.setColorFilter(Color.GREEN)
278 | } else if (!GlobalApp.BLE?.gpsStatusFlags?.get(GPS_FIX_FLAG_INDEX)!!) {
279 | view?.action_button_C?.setImageResource(R.drawable.ic_gps_not_fixed_black_24dp)
280 | view?.action_button_C?.setColorFilter(Color.WHITE)
281 | }
282 |
283 | if (GlobalApp.BLE?.gpsStatusFlags?.get(GPS_ON_FLAG_INDEX)!!) {
284 | view?.action_button_B?.setImageResource(R.drawable.ic_location_on_black_24dp)
285 | view?.action_button_B?.setColorFilter(Color.GREEN)
286 | } else if (!GlobalApp.BLE?.gpsStatusFlags?.get(GPS_ON_FLAG_INDEX)!!) {
287 | view?.action_button_B?.setImageResource(R.drawable.ic_location_off_black_24dp)
288 | view?.action_button_C?.setImageResource(R.drawable.ic_gps_off_black_24dp)
289 | view?.action_button_B?.setColorFilter(Color.WHITE)
290 | view?.action_button_C?.setColorFilter(Color.WHITE)
291 | }
292 |
293 | if (GlobalApp.BLE?.gpsStatusFlags?.get(GPS_LOGGING_FLAG_INDEX)!!) {
294 | view?.action_button_D?.setImageResource(R.drawable.ic_fiber_manual_record_black_24dp)
295 | view?.action_button_D?.setColorFilter(Color.RED)
296 | } else if (!GlobalApp.BLE?.gpsStatusFlags?.get(GPS_LOGGING_FLAG_INDEX)!!) {
297 | view?.action_button_D?.setImageResource(R.drawable.ic_fiber_manual_record_black_24dp)
298 | view?.action_button_D?.setColorFilter(Color.WHITE)
299 | }
300 |
301 | if (this.autoCheckRunning) {
302 | view?.action_button_E?.setImageResource(R.drawable.ic_sync_black_24dp)
303 | view?.action_button_E?.setColorFilter(Color.GREEN)
304 | } else if (!this.autoCheckRunning) {
305 | view?.action_button_E?.setImageResource(R.drawable.ic_sync_disabled_black_24dp)
306 | view?.action_button_E?.setColorFilter(Color.WHITE)
307 | }
308 |
309 | } catch (e: Throwable) {
310 | Log.e("HOME", "Update UI Status Button Error")
311 | }
312 | }
313 |
314 | private fun updateUIDeviceInfo() {
315 | try {
316 | val macLabel: TextView? = view?.mac_text_label
317 | val deviceLabel: TextView? = view?.device_text_label
318 | val macAddress = GlobalApp.BLE?.bleAddress
319 | if (macAddress != "" && macLabel != null && deviceLabel != null) {
320 | deviceLabel.text = DEVICE_CODE_NAME
321 | macLabel.text = macAddress
322 | } else {
323 | GlobalApp.BLE?.initialize()
324 | if (macLabel != null && deviceLabel != null) {
325 | deviceLabel.text = getString(R.string.initial_ble_name)
326 | macLabel.text = getString(R.string.initial_ble_mac)
327 | }
328 | }
329 | } catch (e: Throwable) {
330 | Log.e("HOME", "Update Device Info Error")
331 | }
332 | }
333 |
334 | private fun updateUIStatusInfo() {
335 | try {
336 | val iconArray: ArrayList? = ArrayList(0)
337 | iconArray?.add(view?.connected_text_label!!)
338 | iconArray?.add(view?.gps_on_text_label!!)
339 | iconArray?.add(view?.fix_text_label!!)
340 | iconArray?.add(view?.log_text_label!!)
341 | for (i in 0 until NUMBER_OF_FLAGS) {
342 | val icon: AppCompatImageView? = iconArray?.get(i)
343 | if (GlobalApp.BLE?.gpsStatusFlags?.get(i)!!) {
344 | if (icon != null) {
345 | iconArray[i].setImageResource(R.drawable.ic_check_black_24dp)
346 | iconArray[i].setColorFilter(Color.parseColor("#00b250"))
347 | }
348 | } else if (iconArray != null && !GlobalApp.BLE?.gpsStatusFlags?.get(i)!!) {
349 | if (icon != null) {
350 | iconArray[i].setImageResource(R.drawable.ic_close_black_24dp)
351 | iconArray[i].setColorFilter(Color.argb(255, 255, 0, 0))
352 | }
353 | }
354 | }
355 | } catch (e: Throwable) {
356 | Log.e("HOME", "Update UI Status Info Error")
357 | }
358 | }
359 |
360 | private fun updateUICoordinateInfo() {
361 | try {
362 | val gpsDate: TextView? = view?.date_text_label
363 | val gpsTime: TextView? = view?.time_text_label
364 | val satellite: TextView? = view?.sat_text_label
365 | val latitude: TextView? = view?.lat_text_label
366 | val longitude: TextView? = view?.long_text_label
367 | var gpsData = GlobalApp.BLE?.gpsData
368 | if (gpsData != null && gpsData != "") {
369 | gpsData = gpsData.substring(gpsData.indexOf('[') + 1, gpsData.indexOf(']'))
370 | val gpdDataList = gpsData.split(',') as ArrayList
371 | if (gpdDataList[DATE_MONTH_INDEX].toInt() < 10) gpdDataList[DATE_MONTH_INDEX] = "0" + gpdDataList[DATE_MONTH_INDEX]
372 | if (gpdDataList[DATE_DAY_INDEX].toInt() < 10) gpdDataList[DATE_DAY_INDEX] = "0" + gpdDataList[DATE_DAY_INDEX]
373 | if (gpdDataList[TIME_HOUR_INDEX].toInt() < 10) gpdDataList[TIME_HOUR_INDEX] = "0" + gpdDataList[TIME_HOUR_INDEX]
374 | if (gpdDataList[TIME_MINUTE_INDEX].toInt() < 10) gpdDataList[TIME_MINUTE_INDEX] = "0" + gpdDataList[TIME_MINUTE_INDEX]
375 | if (gpdDataList[TIME_SECOND_INDEX].toInt() < 10) gpdDataList[TIME_SECOND_INDEX] = "0" + gpdDataList[TIME_SECOND_INDEX]
376 | gpsDate?.text = getString(R.string.gpsDate, gpdDataList[DATE_YEAR_INDEX], gpdDataList[DATE_MONTH_INDEX], gpdDataList[DATE_DAY_INDEX])
377 | gpsTime?.text = getString(R.string.gpsTime, gpdDataList[TIME_HOUR_INDEX], gpdDataList[TIME_MINUTE_INDEX], gpdDataList[TIME_SECOND_INDEX])
378 | satellite?.text = gpdDataList[SATELLITES_VALUE_INDEX]
379 | latitude?.text = gpdDataList[LOCATION_LAT_INDEX]
380 | longitude?.text = gpdDataList[LOCATION_LNG_INDEX]
381 | } else if (gpsData == null || gpsData == "") {
382 | gpsDate?.text = getString(R.string.init_date)
383 | gpsTime?.text = getString(R.string.init_time)
384 | satellite?.text = getString(R.string.init_sat)
385 | latitude?.text = getString(R.string.init_lat)
386 | longitude?.text = getString(R.string.init_long)
387 | }
388 | } catch (e: Throwable) {
389 | Log.e("HOME", "Update UI Coordinate Info Error")
390 | }
391 | }
392 |
393 | private fun bleLinkCheck() {
394 | val main = activity as MainActivity?
395 | if (main != null && main.checkBTon() && GlobalApp.BLE?.connectionState == STATE_CONNECTED) {
396 | if (GlobalApp.BLE == null) {
397 | GlobalApp.BLE = BLEDevice(main as Context, activity as ContextWrapper)
398 | GlobalApp.BLE!!.initialize()
399 | }
400 | if (this.statueCheckThread != null && this.statueCheckThread?.isAlive!!) this.statueCheckThread?.interrupt()
401 | this.statueCheckThread = null
402 | this.statueCheckThread = Thread(Runnable { this.connectionUIHandler() })
403 | this.statueCheckThread!!.start()
404 | }
405 | }
406 | }
407 |
--------------------------------------------------------------------------------
/GPS_Firmware/Tiny_GPS_Logger/Tiny_GPS_Logger.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include "SD.h"
7 | #include "FS.h"
8 | #include "TinyGPS++.h"
9 |
10 | #define DEBUG true
11 |
12 | #define RXD2 16
13 | #define TXD2 17
14 |
15 | // TODO: Get proper UUIDs
16 | #define SERVICE_UUID "000ffdf4-68d9-4e48-a89a-219e581f0d64"
17 | #define CHARACTERISTIC_UUID "44a80b83-c605-4406-8e50-fc42f03b6d38"
18 |
19 | BLEServer *pServer = NULL;
20 | BLEService *pService = NULL;
21 | BLECharacteristic *pCharacteristic = NULL;
22 |
23 | const int NUMBER_OF_FLAGS = 4;
24 | const int GPS_CONNECTION_FLAG_INDEX = 0;
25 | const int GPS_ON_FLAG_INDEX = 1;
26 | const int GPS_FIX_FLAG_INDEX = 2;
27 | const int GPS_LOGGING_FLAG_INDEX = 3;
28 |
29 | bool statusFlags[NUMBER_OF_FLAGS];
30 |
31 | bool SERIAL_PRINT_FLAG = false;
32 |
33 | const int CS = 5;
34 | int log_counter = 0;
35 | int nfiles = 0;
36 | String gnss_dir = "GNSS_LOGS";
37 | String log_buffer = "";
38 |
39 | #if DEBUG
40 | #include
41 | #include
42 | const char *WIFI_SSID = "";
43 | const char *WIFI_PWD = "";
44 | const int WIFI_TIMEOUT = 10 * 1000;
45 | const int UDP_PORT = 9996;
46 | IPAddress HOST_IP(192, 168, 1, 80);
47 | WiFiUDP Udp;
48 | #endif
49 |
50 | TinyGPSPlus gps;
51 |
52 | void Serial_Print(String msg) {
53 | #if DEBUG
54 | Serial.print(msg);
55 | if (WiFi.status() == WL_CONNECTED) {
56 | Udp.beginPacket(HOST_IP, UDP_PORT);
57 | Udp.printf((msg).c_str());
58 | Udp.endPacket();
59 | }
60 | #endif
61 | }
62 |
63 | void setup() {
64 | #if DEBUG
65 | Serial.begin(115200);
66 | #endif
67 | Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
68 | EEPROM.begin(64);
69 | Serial_Print("\n\r*************** GPS_ON ***************\n\r");
70 | BLE_INIT();
71 | SD_INIT();
72 | listDir(SD, "/", 0);
73 | createDir(SD, "/" + gnss_dir);
74 | listDir(SD, "/" + gnss_dir, 0);
75 | for (int i = 0; i < NUMBER_OF_FLAGS; i++) statusFlags[i] = false;
76 | if (EEPROM.read(GPS_ON_FLAG_INDEX) == 0x01) statusFlags[GPS_ON_FLAG_INDEX] = true;
77 | if (EEPROM.read(GPS_LOGGING_FLAG_INDEX) == 0x01) statusFlags[GPS_LOGGING_FLAG_INDEX] = true;
78 | #if DEBUG
79 | WIFI_INIT();
80 | #endif
81 | Serial_Print("\n\r\n\r[SETUP COMPLETE]\n\r\n\r");
82 | }
83 |
84 | /**********************************************************************
85 | BLE Functions
86 | **********************************************************************/
87 |
88 | class ServerCallbacks : public BLEServerCallbacks {
89 | void onConnect(BLEServer *pServer) {
90 | statusFlags[GPS_CONNECTION_FLAG_INDEX] = true;
91 | Serial_Print("BLE Device Connected!\n\r");
92 | };
93 | void onDisconnect(BLEServer *pServer) {
94 | statusFlags[GPS_CONNECTION_FLAG_INDEX] = false;
95 | Serial_Print("BLE Device Disconnect!\n\r");
96 | }
97 | };
98 |
99 | class BLE_Callbacks : public BLECharacteristicCallbacks {
100 | void onWrite(BLECharacteristic *pCharacteristic) {
101 | std::string value = pCharacteristic->getValue();
102 | if (value.length() > 0) {
103 | Serial_Print("BLE Code: ");
104 | for (int i = 0; i < value.length(); i++) Serial.print(value[i], HEX);
105 | Serial_Print("\n\r");
106 |
107 | if (value[0] == 0x00) {
108 | String packet = "[ESP32_GPS]";
109 | byte buf[packet.length() + 1];
110 | packet.getBytes(buf, sizeof(buf));
111 | pCharacteristic->setValue(buf, sizeof(buf));
112 | pCharacteristic->indicate();
113 | Serial_Print(packet + "\n\r");
114 | } else if (value[0] == 0x01) {
115 | byte buf[NUMBER_OF_FLAGS];
116 | buf[GPS_CONNECTION_FLAG_INDEX] = statusFlags[GPS_CONNECTION_FLAG_INDEX] ? 0x01 : 0x00;
117 | buf[GPS_FIX_FLAG_INDEX] = statusFlags[GPS_FIX_FLAG_INDEX] ? 0x01 : 0x00;
118 | buf[GPS_ON_FLAG_INDEX] = statusFlags[GPS_ON_FLAG_INDEX] ? 0x01 : 0x00;
119 | buf[GPS_LOGGING_FLAG_INDEX] = statusFlags[GPS_LOGGING_FLAG_INDEX] ? 0x01 : 0x00;
120 | pCharacteristic->setValue(buf, sizeof(buf));
121 | pCharacteristic->indicate();
122 | Serial_Print("Status: " + String(buf[0]) + String(buf[1]) + String(buf[2]) + String(buf[3]) + "\n\r");
123 | } else if (value[0] == 0x02) {
124 | Serial_Print("[start_gps]\n\r");
125 | statusFlags[GPS_ON_FLAG_INDEX] = true;
126 | EEPROM.write(GPS_ON_FLAG_INDEX, 0x01);
127 | EEPROM.commit();
128 | byte buf[2] = { 0x01, 0x01 };
129 | pCharacteristic->setValue(buf, sizeof(buf));
130 | pCharacteristic->indicate();
131 | } else if (value[0] == 0x03) {
132 | Serial_Print("[end_gps]\n\r");
133 | statusFlags[GPS_ON_FLAG_INDEX] = false;
134 | EEPROM.write(GPS_ON_FLAG_INDEX, 0x00);
135 | EEPROM.commit();
136 | byte buf[2] = { 0x01, 0x01 };
137 | pCharacteristic->setValue(buf, sizeof(buf));
138 | pCharacteristic->indicate();
139 | } else if (value[0] == 0x04) {
140 | Serial_Print("[start_logging]\n\r");
141 | statusFlags[GPS_LOGGING_FLAG_INDEX] = true;
142 | EEPROM.write(GPS_LOGGING_FLAG_INDEX, 0x01);
143 | EEPROM.commit();
144 | byte buf[2] = { 0x01, 0x01 };
145 | pCharacteristic->setValue(buf, sizeof(buf));
146 | pCharacteristic->indicate();
147 | } else if (value[0] == 0x05) {
148 | Serial_Print("[end_logging]\n\r");
149 | statusFlags[GPS_LOGGING_FLAG_INDEX] = false;
150 | EEPROM.write(GPS_LOGGING_FLAG_INDEX, 0x00);
151 | EEPROM.commit();
152 | byte buf[2] = { 0x01, 0x01 };
153 | pCharacteristic->setValue(buf, sizeof(buf));
154 | pCharacteristic->indicate();
155 | } else if (value[0] == 0x06) {
156 | String gps_valid = String(gps.location.isValid()) + "," + String(gps.location.isUpdated()) + "," + String(gps.location.age());
157 | String gps_data_a = String(gps.location.lat(), 7) + "," + String(gps.location.lng(), 7);
158 | String gps_data_b = String(gps.date.year()) + "," + String(gps.date.month()) + "," + String(gps.date.day());
159 | String gps_data_c = String(gps.time.hour()) + "," + String(gps.time.minute()) + "," + String(gps.time.second());
160 | String gps_data_d = String(gps.satellites.value()) + "," + String(gps.speed.kmph()) + "," + String(gps.course.deg());
161 | String gps_data_e = String(gps.altitude.meters()) + "," + String(gps.hdop.value());
162 | String packet = "[" + gps_valid + "," + gps_data_a + "," + gps_data_b + "," + gps_data_c + "," + gps_data_d + "," + gps_data_e + "]";
163 | byte buf[packet.length() + 1];
164 | packet.getBytes(buf, sizeof(buf));
165 | pCharacteristic->setValue(buf, sizeof(buf));
166 | pCharacteristic->indicate();
167 | Serial_Print(packet + "\n\r");
168 | } else if (value[0] == 0x07) {
169 | String listing = "[" + listDir(SD, "/" + gnss_dir, 0) + "]";
170 | byte buf[listing.length() + 1];
171 | listing.getBytes(buf, sizeof(buf));
172 | pCharacteristic->setValue(buf, sizeof(buf));
173 | pCharacteristic->indicate();
174 | Serial_Print(listing);
175 | } else if (value[0] == 0x08) {
176 | String text = readFile(SD, "/" + gnss_dir + "/GPS_" + int(value[1]) + ".log");
177 | // BLOCKED
178 | } else if (value[0] == 0x09) {
179 | uint64_t bytes = SD.totalBytes();
180 | uint64_t used_bytes = SD.usedBytes();
181 | uint32_t bytes_low = bytes % 0xFFFFFFFF;
182 | uint32_t bytes_high = (bytes >> 32) % 0xFFFFFFFF;
183 | uint32_t used_bytes_low = used_bytes % 0xFFFFFFFF;
184 | uint32_t used_bytes_high = (used_bytes >> 32) % 0xFFFFFFFF;
185 | String sdcard = "[" + String(bytes_high) + String(bytes_low) + "," + String(used_bytes_high) + String(used_bytes_low) + "]";
186 | byte buf[sdcard.length() + 1];
187 | sdcard.getBytes(buf, sizeof(buf));
188 | pCharacteristic->setValue(buf, sizeof(buf));
189 | pCharacteristic->indicate();
190 | Serial_Print(sdcard + "\n\r");
191 | } else if (value[0] == 0x0a) {
192 | Serial_Print("[rebooting]\n\r");
193 | byte buf[2] = { 0x01, 0x01 };
194 | pCharacteristic->setValue(buf, sizeof(buf));
195 | pCharacteristic->indicate();
196 | delay(2000);
197 | ESP.restart();
198 | } else if (value[0] == 0x0b) {
199 | Serial_Print("[system_reset]\n\r");
200 | removeDir(SD, "/" + gnss_dir);
201 | createDir(SD, "/" + gnss_dir);
202 | listDir(SD, "/" + gnss_dir, 0);
203 | statusFlags[GPS_ON_FLAG_INDEX] = false;
204 | statusFlags[GPS_LOGGING_FLAG_INDEX] = false;
205 | EEPROM.write(GPS_ON_FLAG_INDEX, 0x00);
206 | EEPROM.write(GPS_LOGGING_FLAG_INDEX, 0x00);
207 | EEPROM.commit();
208 | byte buf[2] = { 0x01, 0x01 };
209 | pCharacteristic->setValue(buf, sizeof(buf));
210 | pCharacteristic->indicate();
211 | Serial_Print("[reset_complete]\n\r");
212 | }
213 | }
214 | }
215 | };
216 |
217 | void BLE_INIT() {
218 | Serial_Print("\n\rInitializing BLE...\n\r");
219 | Serial_Print("SERVICE UUID: \t\r" + String(SERVICE_UUID) + "\n\r");
220 | Serial_Print("CHARACTERISTIC UUID: \r" + String(CHARACTERISTIC_UUID) + "\n\r");
221 | Serial_Print("Starting BLE Server...\n\r");
222 | BLEDevice::init("ESP32_GPS_LOGGER");
223 | pServer = BLEDevice::createServer();
224 | pServer->setCallbacks(new ServerCallbacks());
225 | pService = pServer->createService(SERVICE_UUID);
226 | pCharacteristic = pService->createCharacteristic(
227 | CHARACTERISTIC_UUID,
228 | BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE);
229 | pCharacteristic->setCallbacks(new BLE_Callbacks());
230 | pService->start();
231 | BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
232 | pAdvertising->addServiceUUID(SERVICE_UUID);
233 | pAdvertising->setScanResponse(true);
234 | pAdvertising->setMinPreferred(0x06);
235 | pAdvertising->setMinPreferred(0x12);
236 | pServer->getAdvertising()->start();
237 | Serial_Print("GATT Service Defined!\n\r");
238 | Serial_Print("GATT Characteristic Defined!\n\r");
239 | }
240 |
241 |
242 | /**********************************************************************
243 | SDCard Functions
244 | **********************************************************************/
245 |
246 | void SD_INIT() {
247 | Serial_Print("\n\rInitializing SD Card...\n\r");
248 | if (!SD.begin(CS)) {
249 | Serial_Print("Initialization Failed!\n\r");
250 | delay(1000);
251 | return;
252 | }
253 | Serial_Print("Initialization Success!\n\r");
254 | Serial_Print("-------SD Card Info-------\n\r");
255 | Serial_Print("SD Card Type:\t");
256 | uint8_t cardType = SD.cardType();
257 | uint64_t bytes = SD.totalBytes();
258 | uint64_t used_bytes = SD.usedBytes();
259 | if (cardType == CARD_MMC) Serial_Print("MMC\n\r");
260 | else if (cardType == CARD_SD) Serial_Print("SDSC\n\r");
261 | else if (cardType == CARD_SDHC) Serial_Print("SDHC\n\r");
262 | else if (cardType == CARD_NONE) Serial_Print("No SD Card Attached\n\r");
263 | else Serial_Print("UNKNOWN\n\r");
264 |
265 | Serial_Print("Volume(MB):\t" + String((float)bytes / (1000 * 1000)) + "\n\r");
266 | Serial_Print("Volume(GB):\t" + String((float)bytes / (1000 * 1000 * 1000)) + "\n\r");
267 | Serial_Print("Used(MB):\t" + String((float)used_bytes / (1000 * 1000)) + "\n\r");
268 | Serial_Print("Used(GB):\t" + String((float)used_bytes / (1000 * 1000 * 1000)) + "\n\r");
269 | Serial_Print("--------------------------\n\r");
270 | }
271 |
272 | String listDir(fs::FS &fs, String dirname, uint8_t levels) {
273 | String listing = "";
274 | int count = 0;
275 | Serial_Print("Listing directory: " + dirname + "\n\r");
276 | File root = fs.open(dirname);
277 | if (!root || !root.isDirectory()) {
278 | Serial_Print("Failed to open directory\n\r");
279 | return "-1,";
280 | }
281 | File file = root.openNextFile();
282 | while (file) {
283 | if (file.isDirectory()) {
284 | Serial_Print(" DIR : " + String(file.name()) + "\n\r");
285 | if (levels) listDir(fs, file.name(), levels - 1);
286 | } else {
287 | count += 1;
288 | Serial_Print(" FILE: " + String(file.name()) + " SIZE: " + String(file.size()) + "\n\r");
289 | listing = listing + "," + String(file.name()) + "," + String(file.size());
290 | }
291 | file = root.openNextFile();
292 | }
293 | nfiles = count;
294 | return String(count) + listing;
295 | }
296 |
297 | void createDir(fs::FS &fs, String path) {
298 | if (SD.exists(path)) {
299 | Serial_Print("Dir " + path + " Exists...\n\r");
300 | return;
301 | }
302 | Serial_Print("Creating Dir: " + path + "\n\r");
303 | if (fs.mkdir(path)) Serial_Print("Dir created\n\r");
304 | else Serial_Print("mkdir failed\n\r");
305 | }
306 |
307 | void removeDir(fs::FS &fs, String path) {
308 | Serial_Print("Removing Dir: " + path + "\n\r");
309 | File root = fs.open(path);
310 | if (!root || !root.isDirectory()) {
311 | Serial_Print("Failed to open directory\n\r");
312 | return;
313 | }
314 | File file = root.openNextFile();
315 | while (file) {
316 | if (!file.isDirectory()) deleteFile(SD, file.name());
317 | file = root.openNextFile();
318 | }
319 | if (fs.rmdir(path)) Serial_Print("Dir removed\n\r");
320 | else Serial_Print("rmdir failed\n\r");
321 | }
322 |
323 | void appendFile(fs::FS &fs, String path, String message) {
324 | File file = fs.open(path, FILE_APPEND);
325 | if (!file) return;
326 | file.print(message);
327 | file.close();
328 | }
329 |
330 | String readFile(fs::FS &fs, String path) {
331 | String text = "";
332 | Serial_Print("Reading file: " + path + "\n\r");
333 | File file = fs.open(path);
334 | if (!file) {
335 | return "Failed to open file for reading\n\r";
336 | }
337 | while (file.available()) {
338 | text = text + (char)file.read();
339 | }
340 | file.close();
341 | return text;
342 | }
343 |
344 | void deleteFile(fs::FS &fs, String path) {
345 | Serial_Print("Deleting file: " + path + "\n\r");
346 | if (fs.remove(path)) Serial_Print("File deleted\n\r");
347 | else Serial_Print("Delete failed\n\r");
348 | }
349 |
350 |
351 | /**********************************************************************
352 | WIFI Functions
353 | **********************************************************************/
354 |
355 | #if DEBUG
356 | void WIFI_INIT() {
357 | WiFi.begin(WIFI_SSID, WIFI_PWD);
358 | Serial_Print("\n\rConnecting to WiFi");
359 | unsigned long start_wait = millis();
360 | while (WiFi.status() != WL_CONNECTED && millis() - start_wait <= WIFI_TIMEOUT) {
361 | Serial_Print(".");
362 | delay(500);
363 | }
364 | if (WiFi.status() == WL_CONNECTED) {
365 | Serial_Print("\n\rConnected to: \t" + String(WIFI_SSID));
366 | Serial_Print("\n\rGateway IP: \t" + WiFi.gatewayIP().toString());
367 | Serial_Print("\n\rHost IP: \t" + HOST_IP.toString());
368 | Serial_Print("\n\rLocal IP: \t" + WiFi.localIP().toString());
369 | Serial_Print("\n\rUDP port: \t" + String(UDP_PORT));
370 | Udp.begin(UDP_PORT);
371 | } else Serial_Print("Unable to Connect to WiFi.");
372 | }
373 |
374 | String UDP_listen() {
375 | char incomingPacket[255];
376 | String packet;
377 | if (WiFi.status() == WL_CONNECTED) {
378 | int packetSize = Udp.parsePacket();
379 | if (packetSize) {
380 | int len = Udp.read(incomingPacket, 255);
381 | if (len > 0) incomingPacket[len] = 0;
382 | Serial_Print("Received from: " + String(Udp.remoteIP().toString()) + ":" + String(Udp.remotePort()) + "\n\r");
383 | Serial_Print("UDP Packet Contents: [" + String(packetSize) + " bytes] " + String(incomingPacket));
384 | } else return "";
385 | } else return "";
386 | return String(incomingPacket);
387 | }
388 | #endif
389 |
390 |
391 | /**********************************************************************
392 | CMD Functions
393 | **********************************************************************/
394 |
395 | void CMD_EVENT() {
396 | String receivedChars;
397 | while (Serial.available() > 0) receivedChars = Serial.readString();
398 |
399 | #if DEBUG
400 | String input = UDP_listen();
401 | if (input != "") receivedChars = input;
402 | #endif
403 |
404 | if (receivedChars.indexOf("status") >= 0) {
405 | byte buf[NUMBER_OF_FLAGS];
406 | buf[GPS_CONNECTION_FLAG_INDEX] = statusFlags[GPS_CONNECTION_FLAG_INDEX] ? 0x01 : 0x00;
407 | buf[GPS_FIX_FLAG_INDEX] = statusFlags[GPS_FIX_FLAG_INDEX] ? 0x01 : 0x00;
408 | buf[GPS_ON_FLAG_INDEX] = statusFlags[GPS_ON_FLAG_INDEX] ? 0x01 : 0x00;
409 | buf[GPS_LOGGING_FLAG_INDEX] = statusFlags[GPS_LOGGING_FLAG_INDEX] ? 0x01 : 0x00;
410 | Serial_Print("Status: " + String(buf[0]) + String(buf[1]) + String(buf[2]) + String(buf[3]) + "\n\r");
411 | } else if (receivedChars.indexOf("gps") >= 0) {
412 | if (!statusFlags[GPS_ON_FLAG_INDEX]) {
413 | Serial_Print("[start_gps]\n\r");
414 | statusFlags[GPS_ON_FLAG_INDEX] = true;
415 | EEPROM.write(GPS_ON_FLAG_INDEX, 0x01);
416 | EEPROM.commit();
417 | } else {
418 | Serial_Print("[end_gps]\n\r");
419 | statusFlags[GPS_ON_FLAG_INDEX] = false;
420 | EEPROM.write(GPS_ON_FLAG_INDEX, 0x00);
421 | EEPROM.commit();
422 | }
423 | } else if (receivedChars.indexOf("log") >= 0) {
424 | if (!statusFlags[GPS_LOGGING_FLAG_INDEX]) {
425 | Serial_Print("[start_logging]\n\r");
426 | statusFlags[GPS_LOGGING_FLAG_INDEX] = true;
427 | EEPROM.write(GPS_LOGGING_FLAG_INDEX, 0x01);
428 | EEPROM.commit();
429 | } else {
430 | Serial_Print("[end_logging]\n\r");
431 | statusFlags[GPS_LOGGING_FLAG_INDEX] = false;
432 | EEPROM.write(GPS_LOGGING_FLAG_INDEX, 0x00);
433 | EEPROM.commit();
434 | }
435 | } else if (receivedChars.indexOf("print") >= 0) {
436 | if (!SERIAL_PRINT_FLAG) {
437 | Serial_Print("[start_printing]\n\r");
438 | SERIAL_PRINT_FLAG = true;
439 | } else {
440 | Serial_Print("[end_printing]\n\r");
441 | SERIAL_PRINT_FLAG = false;
442 | }
443 | } else if (receivedChars.indexOf("data") >= 0) {
444 | String gps_valid = String(gps.location.isValid()) + "," + String(gps.location.isUpdated()) + "," + String(gps.location.age());
445 | String gps_data_a = String(gps.location.lat(), 7) + "," + String(gps.location.lng(), 7);
446 | String gps_data_b = String(gps.date.year()) + "," + String(gps.date.month()) + "," + String(gps.date.day());
447 | String gps_data_c = String(gps.time.hour()) + "," + String(gps.time.minute()) + "," + String(gps.time.second());
448 | String gps_data_d = String(gps.satellites.value()) + "," + String(gps.speed.kmph()) + "," + String(gps.course.deg());
449 | String gps_data_e = String(gps.altitude.meters()) + "," + String(gps.hdop.value());
450 | String packet = "[" + gps_valid + "," + gps_data_a + "," + gps_data_b + "," + gps_data_c + "," + gps_data_d + "," + gps_data_e + "]";
451 | Serial_Print(packet + "\n\r");
452 | } else if (receivedChars.indexOf("list") >= 0) {
453 | listDir(SD, "/" + gnss_dir, 0);
454 | } else if (receivedChars.indexOf("read|") >= 0) {
455 | String index = receivedChars.substring(receivedChars.indexOf("|") + 1, receivedChars.indexOf("]"));
456 | Serial_Print(readFile(SD, "/" + gnss_dir + "/GPS_" + String(index.toInt()) + ".log"));
457 | } else if (receivedChars.indexOf("sdcard") >= 0) {
458 | uint64_t bytes = SD.totalBytes();
459 | uint64_t used_bytes = SD.usedBytes();
460 | uint32_t bytes_low = bytes % 0xFFFFFFFF;
461 | uint32_t bytes_high = (bytes >> 32) % 0xFFFFFFFF;
462 | uint32_t used_bytes_low = used_bytes % 0xFFFFFFFF;
463 | uint32_t used_bytes_high = (used_bytes >> 32) % 0xFFFFFFFF;
464 | String sdcard = "[" + String(bytes_high) + String(bytes_low) + "," + String(used_bytes_high) + String(used_bytes_low) + "]";
465 | Serial_Print(sdcard);
466 | } else if (receivedChars.indexOf("reboot") >= 0) {
467 | Serial_Print("[rebooting]\n\r");
468 | delay(2000);
469 | ESP.restart();
470 | } else if (receivedChars.indexOf("reset") >= 0) {
471 | Serial_Print("[system_reset]\n\r");
472 | removeDir(SD, "/" + gnss_dir);
473 | createDir(SD, "/" + gnss_dir);
474 | listDir(SD, "/" + gnss_dir, 0);
475 | statusFlags[GPS_ON_FLAG_INDEX] = false;
476 | statusFlags[GPS_LOGGING_FLAG_INDEX] = false;
477 | EEPROM.write(GPS_ON_FLAG_INDEX, 0x00);
478 | EEPROM.write(GPS_LOGGING_FLAG_INDEX, 0x00);
479 | EEPROM.commit();
480 | Serial_Print("[reset_complete]\n\r");
481 | }
482 | }
483 |
484 | void loop() {
485 |
486 | String gnss_data = "";
487 |
488 | if (statusFlags[GPS_ON_FLAG_INDEX]) {
489 | while (Serial2.available()) {
490 | int raw_data = Serial2.read();
491 | gps.encode(raw_data);
492 | gnss_data = String(gnss_data + String((char)raw_data));
493 | }
494 |
495 | if (gps.location.age() > 10000) statusFlags[GPS_FIX_FLAG_INDEX] = false;
496 | else statusFlags[GPS_FIX_FLAG_INDEX] = true;
497 |
498 | if (SERIAL_PRINT_FLAG && gnss_data != "") Serial_Print(gnss_data);
499 |
500 |
501 | log_buffer = log_buffer + gnss_data;
502 | if (statusFlags[GPS_LOGGING_FLAG_INDEX] && log_counter % 1000 == 0 && log_counter != 0) {
503 | appendFile(SD, "/" + gnss_dir + "/GPS_" + String(nfiles) + ".log", log_buffer);
504 | log_buffer = "";
505 | log_counter = 0;
506 | }
507 | } else statusFlags[GPS_FIX_FLAG_INDEX] = false;
508 | log_counter++;
509 |
510 | CMD_EVENT();
511 | }
512 |
--------------------------------------------------------------------------------