diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/ESP32_BLUETOOTH_SERIAL_ARDUINO_IDE/ESP32_BLUETOOTH_SERIAL_ARDUINO_IDE.ino b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/ESP32_BLUETOOTH_SERIAL_ARDUINO_IDE/ESP32_BLUETOOTH_SERIAL_ARDUINO_IDE.ino
new file mode 100644
index 0000000..790563a
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/ESP32_BLUETOOTH_SERIAL_ARDUINO_IDE/ESP32_BLUETOOTH_SERIAL_ARDUINO_IDE.ino
@@ -0,0 +1,30 @@
+//https://github.com/espressif/arduino-esp32/blob/master/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino
+//This example code is in the Public Domain (or CC0 licensed, at your option.)
+//By Evandro Copercini - 2018
+//
+//This example creates a bridge between Serial and Classical Bluetooth (SPP)
+//and also demonstrate that SerialBT have the same functionalities of a normal Serial
+
+#include "BluetoothSerial.h"
+
+#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
+#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
+#endif
+
+BluetoothSerial SerialBT;
+
+void setup() {
+ Serial.begin(115200);
+ SerialBT.begin("ESP32_CLASSIC_BT"); //Bluetooth device name
+ Serial.println("The device started, now you can pair it with bluetooth!");
+}
+
+void loop() {
+ if (Serial.available()) {
+ SerialBT.write(Serial.read());
+ }
+ if (SerialBT.available()) {
+ Serial.write(SerialBT.read());
+ }
+ delay(20);
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/.gitignore b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/.gitignore
new file mode 100644
index 0000000..f3c2053
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/.gitignore
@@ -0,0 +1,44 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Exceptions to above rules.
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/.metadata b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/.metadata
new file mode 100644
index 0000000..834408e
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: 45da0f63af4fbc60b920c3a13d58201408c3c8f4
+ channel: master
+
+project_type: app
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/README.md b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/README.md
new file mode 100644
index 0000000..59f2c84
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/README.md
@@ -0,0 +1,16 @@
+# androidbluetoothserialapp
+
+A new Flutter application.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
+
+For help getting started with Flutter, view our
+[online documentation](https://flutter.dev/docs), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/.gitignore b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/.gitignore
new file mode 100644
index 0000000..bc2100d
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/.gitignore
@@ -0,0 +1,7 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/build.gradle b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/build.gradle
new file mode 100644
index 0000000..4d89725
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/build.gradle
@@ -0,0 +1,54 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 28
+
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "com.thatproject.androidbluetoothserialapp"
+ minSdkVersion 18
+ targetSdkVersion 28
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/debug/AndroidManifest.xml b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..a5838fd
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/AndroidManifest.xml b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..94e1a80
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/java/com/thatproject/androidbluetoothserialapp/MainActivity.java b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/java/com/thatproject/androidbluetoothserialapp/MainActivity.java
new file mode 100644
index 0000000..5edc7e7
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/java/com/thatproject/androidbluetoothserialapp/MainActivity.java
@@ -0,0 +1,6 @@
+package com.thatproject.androidbluetoothserialapp;
+
+import io.flutter.embedding.android.FlutterActivity;
+
+public class MainActivity extends FlutterActivity {
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/drawable/launch_background.xml b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/values/styles.xml b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..1f83a33
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/profile/AndroidManifest.xml b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..a5838fd
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/build.gradle b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/build.gradle
new file mode 100644
index 0000000..e0d7ae2
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.0'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/gradle.properties b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/gradle.properties
new file mode 100644
index 0000000..38c8d45
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.enableR8=true
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/gradle/wrapper/gradle-wrapper.properties b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..296b146
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/settings.gradle b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/settings.gradle
new file mode 100644
index 0000000..d3b6a40
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/android/settings.gradle
@@ -0,0 +1,15 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/.gitignore b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/.gitignore
new file mode 100644
index 0000000..e96ef60
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/.gitignore
@@ -0,0 +1,32 @@
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Flutter/AppFrameworkInfo.plist b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..6b4c0f7
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 8.0
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Flutter/Debug.xcconfig b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..e8efba1
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Flutter/Release.xcconfig b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..399e934
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Podfile b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Podfile
new file mode 100644
index 0000000..5a69b89
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Podfile
@@ -0,0 +1,84 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '9.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def parse_KV_file(file, separator='=')
+ file_abs_path = File.expand_path(file)
+ if !File.exists? file_abs_path
+ return [];
+ end
+ generated_key_values = {}
+ skip_line_start_symbols = ["#", "/"]
+ File.foreach(file_abs_path) do |line|
+ next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
+ plugin = line.split(pattern=separator)
+ if plugin.length == 2
+ podname = plugin[0].strip()
+ path = plugin[1].strip()
+ podpath = File.expand_path("#{path}", file_abs_path)
+ generated_key_values[podname] = podpath
+ else
+ puts "Invalid plugin specification: #{line}"
+ end
+ end
+ generated_key_values
+end
+
+target 'Runner' do
+ # Flutter Pod
+
+ copied_flutter_dir = File.join(__dir__, 'Flutter')
+ copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
+ copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
+ unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
+ # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
+ # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
+ # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
+
+ generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+ generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
+ cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
+
+ unless File.exist?(copied_framework_path)
+ FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
+ end
+ unless File.exist?(copied_podspec_path)
+ FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
+ end
+ end
+
+ # Keep pod path relative so it can be checked into Podfile.lock.
+ pod 'Flutter', :path => 'Flutter'
+
+ # Plugin Pods
+
+ # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
+ # referring to absolute paths on developers' machines.
+ system('rm -rf .symlinks')
+ system('mkdir -p .symlinks/plugins')
+ plugin_pods = parse_KV_file('../.flutter-plugins')
+ plugin_pods.each do |name, path|
+ symlink = File.join('.symlinks', 'plugins', name)
+ File.symlink(path, symlink)
+ pod name, :path => File.join(symlink, 'ios')
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ target.build_configurations.each do |config|
+ config.build_settings['ENABLE_BITCODE'] = 'NO'
+ end
+ end
+end
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.pbxproj b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..d77e695
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,496 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
+ 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
+ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
+ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 97C146F11CF9000F007C117D /* Supporting Files */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ 97C146F11CF9000F007C117D /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146F21CF9000F007C117D /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1020;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
+ 97C146F31CF9000F007C117D /* main.m in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.thatproject.androidbluetoothserialapp;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.thatproject.androidbluetoothserialapp;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.thatproject.androidbluetoothserialapp;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..a28140c
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcworkspace/contents.xcworkspacedata b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/AppDelegate.h b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/AppDelegate.h
new file mode 100644
index 0000000..36e21bb
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/AppDelegate.h
@@ -0,0 +1,6 @@
+#import
+#import
+
+@interface AppDelegate : FlutterAppDelegate
+
+@end
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/AppDelegate.m b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/AppDelegate.m
new file mode 100644
index 0000000..70e8393
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/AppDelegate.m
@@ -0,0 +1,13 @@
+#import "AppDelegate.h"
+#import "GeneratedPluginRegistrant.h"
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application
+ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ [GeneratedPluginRegistrant registerWithRegistry:self];
+ // Override point for customization after application launch.
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
+}
+
+@end
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d36b1fa
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..dc9ada4
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..28c6bf0
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..2ccbfd9
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..f091b6b
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..4cde121
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..d0ef06e
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..dcdc230
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..2ccbfd9
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..c8f9ed8
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..a6d6b86
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..a6d6b86
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..75b2d16
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..c4df70d
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..6a84f41
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..d0e1f58
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Base.lproj/Main.storyboard b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Info.plist b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Info.plist
new file mode 100644
index 0000000..1e2e219
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ androidbluetoothserialapp
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/main.m b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/main.m
new file mode 100644
index 0000000..dff6597
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/ios/Runner/main.m
@@ -0,0 +1,9 @@
+#import
+#import
+#import "AppDelegate.h"
+
+int main(int argc, char* argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/BackgroundCollectedPage.dart b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/BackgroundCollectedPage.dart
new file mode 100755
index 0000000..298cc16
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/BackgroundCollectedPage.dart
@@ -0,0 +1,145 @@
+import 'package:flutter/material.dart';
+
+import './BackgroundCollectingTask.dart';
+import './helpers/LineChart.dart';
+import './helpers/PaintStyle.dart';
+
+class BackgroundCollectedPage extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ final BackgroundCollectingTask task =
+ BackgroundCollectingTask.of(context, rebuildOnChange: true);
+
+ // Arguments shift is needed for timestamps as miliseconds in double could loose precision.
+ final int argumentsShift =
+ task.samples.first.timestamp.millisecondsSinceEpoch;
+
+ final Duration showDuration =
+ Duration(hours: 2); // @TODO . show duration should be configurable
+ final Iterable lastSamples = task.getLastOf(showDuration);
+
+ final Iterable arguments = lastSamples.map((sample) {
+ return (sample.timestamp.millisecondsSinceEpoch - argumentsShift)
+ .toDouble();
+ });
+
+ // Step for argument labels
+ final Duration argumentsStep =
+ Duration(minutes: 15); // @TODO . step duration should be configurable
+
+ // Find first timestamp floored to step before
+ final DateTime beginningArguments = lastSamples.first.timestamp;
+ DateTime beginningArgumentsStep = DateTime(beginningArguments.year,
+ beginningArguments.month, beginningArguments.day);
+ while (beginningArgumentsStep.isBefore(beginningArguments)) {
+ beginningArgumentsStep = beginningArgumentsStep.add(argumentsStep);
+ }
+ beginningArgumentsStep = beginningArgumentsStep.subtract(argumentsStep);
+ final DateTime endingArguments = lastSamples.last.timestamp;
+
+ // Generate list of timestamps of labels
+ final Iterable argumentsLabelsTimestamps = () sync* {
+ DateTime timestamp = beginningArgumentsStep;
+ yield timestamp;
+ while (timestamp.isBefore(endingArguments)) {
+ timestamp = timestamp.add(argumentsStep);
+ yield timestamp;
+ }
+ }();
+
+ // Map strings for labels
+ final Iterable argumentsLabels =
+ argumentsLabelsTimestamps.map((timestamp) {
+ return LabelEntry(
+ (timestamp.millisecondsSinceEpoch - argumentsShift).toDouble(),
+ ((timestamp.hour <= 9 ? '0' : '') +
+ timestamp.hour.toString() +
+ ':' +
+ (timestamp.minute <= 9 ? '0' : '') +
+ timestamp.minute.toString()));
+ });
+
+ return Scaffold(
+ appBar: AppBar(
+ title: Text('Collected data'),
+ actions: [
+ // Progress circle
+ (task.inProgress
+ ? FittedBox(
+ child: Container(
+ margin: new EdgeInsets.all(16.0),
+ child: CircularProgressIndicator(
+ valueColor:
+ AlwaysStoppedAnimation(Colors.white))))
+ : Container(/* Dummy */)),
+ // Start/stop buttons
+ (task.inProgress
+ ? IconButton(icon: Icon(Icons.pause), onPressed: task.pause)
+ : IconButton(
+ icon: Icon(Icons.play_arrow), onPressed: task.reasume)),
+ ],
+ ),
+ body: ListView(
+ children: [
+ Divider(),
+ ListTile(
+ leading: const Icon(Icons.brightness_7),
+ title: const Text('Temperatures'),
+ subtitle: const Text('In Celsius'),
+ ),
+ LineChart(
+ constraints: const BoxConstraints.expand(height: 350),
+ arguments: arguments,
+ argumentsLabels: argumentsLabels,
+ values: [
+ lastSamples.map((sample) => sample.temperature1),
+ lastSamples.map((sample) => sample.temperature2),
+ ],
+ verticalLinesStyle: const PaintStyle(color: Colors.grey),
+ additionalMinimalHorizontalLabelsInterval: 0,
+ additionalMinimalVerticalLablesInterval: 0,
+ seriesPointsStyles: [
+ null,
+ null,
+ //const PaintStyle(style: PaintingStyle.stroke, strokeWidth: 1.7*3, color: Colors.indigo, strokeCap: StrokeCap.round),
+ ],
+ seriesLinesStyles: [
+ const PaintStyle(
+ style: PaintingStyle.stroke,
+ strokeWidth: 1.7,
+ color: Colors.indigoAccent),
+ const PaintStyle(
+ style: PaintingStyle.stroke,
+ strokeWidth: 1.7,
+ color: Colors.redAccent),
+ ],
+ ),
+ Divider(),
+ ListTile(
+ leading: const Icon(Icons.filter_vintage),
+ title: const Text('Water pH level'),
+ ),
+ LineChart(
+ constraints: const BoxConstraints.expand(height: 200),
+ arguments: arguments,
+ argumentsLabels: argumentsLabels,
+ values: [
+ lastSamples.map((sample) => sample.waterpHlevel),
+ ],
+ verticalLinesStyle: const PaintStyle(color: Colors.grey),
+ additionalMinimalHorizontalLabelsInterval: 0,
+ additionalMinimalVerticalLablesInterval: 0,
+ seriesPointsStyles: [
+ null,
+ ],
+ seriesLinesStyles: [
+ const PaintStyle(
+ style: PaintingStyle.stroke,
+ strokeWidth: 1.7,
+ color: Colors.greenAccent),
+ ],
+ ),
+ ],
+ ));
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/BackgroundCollectingTask.dart b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/BackgroundCollectingTask.dart
new file mode 100755
index 0000000..9cc73d2
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/BackgroundCollectingTask.dart
@@ -0,0 +1,123 @@
+import 'dart:convert';
+import 'package:flutter/material.dart';
+import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
+import 'package:scoped_model/scoped_model.dart';
+
+class DataSample {
+ double temperature1;
+ double temperature2;
+ double waterpHlevel;
+ DateTime timestamp;
+
+ DataSample({
+ this.temperature1,
+ this.temperature2,
+ this.waterpHlevel,
+ this.timestamp,
+ });
+}
+
+class BackgroundCollectingTask extends Model {
+ static BackgroundCollectingTask of(
+ BuildContext context, {
+ bool rebuildOnChange = false,
+ }) =>
+ ScopedModel.of(
+ context,
+ rebuildOnChange: rebuildOnChange,
+ );
+
+ final BluetoothConnection _connection;
+ List _buffer = List();
+
+ // @TODO , Such sample collection in real code should be delegated
+ // (via `Stream` preferably) and then saved for later
+ // displaying on chart (or even stright prepare for displaying).
+ // @TODO ? should be shrinked at some point, endless colleting data would cause memory shortage.
+ List samples = List();
+
+ bool inProgress;
+
+ BackgroundCollectingTask._fromConnection(this._connection) {
+ _connection.input.listen((data) {
+ _buffer += data;
+
+ while (true) {
+ // If there is a sample, and it is full sent
+ int index = _buffer.indexOf('t'.codeUnitAt(0));
+ if (index >= 0 && _buffer.length - index >= 7) {
+ final DataSample sample = DataSample(
+ temperature1: (_buffer[index + 1] + _buffer[index + 2] / 100),
+ temperature2: (_buffer[index + 3] + _buffer[index + 4] / 100),
+ waterpHlevel: (_buffer[index + 5] + _buffer[index + 6] / 100),
+ timestamp: DateTime.now());
+ _buffer.removeRange(0, index + 7);
+
+ samples.add(sample);
+ notifyListeners(); // Note: It shouldn't be invoked very often - in this example data comes at every second, but if there would be more data, it should update (including repaint of graphs) in some fixed interval instead of after every sample.
+ //print("${sample.timestamp.toString()} -> ${sample.temperature1} / ${sample.temperature2}");
+ }
+ // Otherwise break
+ else {
+ break;
+ }
+ }
+ }).onDone(() {
+ inProgress = false;
+ notifyListeners();
+ });
+ }
+
+ static Future connect(
+ BluetoothDevice server) async {
+ final BluetoothConnection connection =
+ await BluetoothConnection.toAddress(server.address);
+ return BackgroundCollectingTask._fromConnection(connection);
+ }
+
+ void dispose() {
+ _connection.dispose();
+ }
+
+ Future start() async {
+ inProgress = true;
+ _buffer.clear();
+ samples.clear();
+ notifyListeners();
+ _connection.output.add(ascii.encode('start'));
+ await _connection.output.allSent;
+ }
+
+ Future cancel() async {
+ inProgress = false;
+ notifyListeners();
+ _connection.output.add(ascii.encode('stop'));
+ await _connection.finish();
+ }
+
+ Future pause() async {
+ inProgress = false;
+ notifyListeners();
+ _connection.output.add(ascii.encode('stop'));
+ await _connection.output.allSent;
+ }
+
+ Future reasume() async {
+ inProgress = true;
+ notifyListeners();
+ _connection.output.add(ascii.encode('start'));
+ await _connection.output.allSent;
+ }
+
+ Iterable getLastOf(Duration duration) {
+ DateTime startingTime = DateTime.now().subtract(duration);
+ int i = samples.length;
+ do {
+ i -= 1;
+ if (i <= 0) {
+ break;
+ }
+ } while (samples[i].timestamp.isAfter(startingTime));
+ return samples.getRange(i, samples.length);
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/BluetoothDeviceListEntry.dart b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/BluetoothDeviceListEntry.dart
new file mode 100755
index 0000000..ff9781b
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/BluetoothDeviceListEntry.dart
@@ -0,0 +1,73 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
+
+class BluetoothDeviceListEntry extends ListTile {
+ BluetoothDeviceListEntry({
+ @required BluetoothDevice device,
+ int rssi,
+ GestureTapCallback onTap,
+ GestureLongPressCallback onLongPress,
+ bool enabled = true,
+ }) : super(
+ onTap: onTap,
+ onLongPress: onLongPress,
+ enabled: enabled,
+ leading:
+ Icon(Icons.devices), // @TODO . !BluetoothClass! class aware icon
+ title: Text(device.name ?? "Unknown device"),
+ subtitle: Text(device.address.toString()),
+ trailing: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ rssi != null
+ ? Container(
+ margin: new EdgeInsets.all(8.0),
+ child: DefaultTextStyle(
+ style: _computeTextStyle(rssi),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(rssi.toString()),
+ Text('dBm'),
+ ],
+ ),
+ ),
+ )
+ : Container(width: 0, height: 0),
+ device.isConnected
+ ? Icon(Icons.import_export)
+ : Container(width: 0, height: 0),
+ device.isBonded
+ ? Icon(Icons.link)
+ : Container(width: 0, height: 0),
+ ],
+ ),
+ );
+
+ static TextStyle _computeTextStyle(int rssi) {
+ /**/ if (rssi >= -35)
+ return TextStyle(color: Colors.greenAccent[700]);
+ else if (rssi >= -45)
+ return TextStyle(
+ color: Color.lerp(
+ Colors.greenAccent[700], Colors.lightGreen, -(rssi + 35) / 10));
+ else if (rssi >= -55)
+ return TextStyle(
+ color: Color.lerp(
+ Colors.lightGreen, Colors.lime[600], -(rssi + 45) / 10));
+ else if (rssi >= -65)
+ return TextStyle(
+ color: Color.lerp(Colors.lime[600], Colors.amber, -(rssi + 55) / 10));
+ else if (rssi >= -75)
+ return TextStyle(
+ color: Color.lerp(
+ Colors.amber, Colors.deepOrangeAccent, -(rssi + 65) / 10));
+ else if (rssi >= -85)
+ return TextStyle(
+ color: Color.lerp(
+ Colors.deepOrangeAccent, Colors.redAccent, -(rssi + 75) / 10));
+ else
+ /*code symetry*/
+ return TextStyle(color: Colors.redAccent);
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/ChatPage.dart b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/ChatPage.dart
new file mode 100755
index 0000000..596f81f
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/ChatPage.dart
@@ -0,0 +1,237 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:typed_data';
+import 'package:flutter/material.dart';
+import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
+
+class ChatPage extends StatefulWidget {
+ final BluetoothDevice server;
+
+ const ChatPage({this.server});
+
+ @override
+ _ChatPage createState() => new _ChatPage();
+}
+
+class _Message {
+ int whom;
+ String text;
+
+ _Message(this.whom, this.text);
+}
+
+class _ChatPage extends State {
+ static final clientID = 0;
+ BluetoothConnection connection;
+
+ List<_Message> messages = List<_Message>();
+ String _messageBuffer = '';
+
+ final TextEditingController textEditingController =
+ new TextEditingController();
+ final ScrollController listScrollController = new ScrollController();
+
+ bool isConnecting = true;
+ bool get isConnected => connection != null && connection.isConnected;
+
+ bool isDisconnecting = false;
+
+ @override
+ void initState() {
+ super.initState();
+
+ BluetoothConnection.toAddress(widget.server.address).then((_connection) {
+ print('Connected to the device');
+ connection = _connection;
+ setState(() {
+ isConnecting = false;
+ isDisconnecting = false;
+ });
+
+ connection.input.listen(_onDataReceived).onDone(() {
+ // Example: Detect which side closed the connection
+ // There should be `isDisconnecting` flag to show are we are (locally)
+ // in middle of disconnecting process, should be set before calling
+ // `dispose`, `finish` or `close`, which all causes to disconnect.
+ // If we except the disconnection, `onDone` should be fired as result.
+ // If we didn't except this (no flag set), it means closing by remote.
+ if (isDisconnecting) {
+ print('Disconnecting locally!');
+ } else {
+ print('Disconnected remotely!');
+ }
+ if (this.mounted) {
+ setState(() {});
+ }
+ });
+ }).catchError((error) {
+ print('Cannot connect, exception occured');
+ print(error);
+ });
+ }
+
+ @override
+ void dispose() {
+ // Avoid memory leak (`setState` after dispose) and disconnect
+ if (isConnected) {
+ isDisconnecting = true;
+ connection.dispose();
+ connection = null;
+ }
+
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final List list = messages.map((_message) {
+ return Row(
+ children: [
+ Container(
+ child: Text(
+ (text) {
+ return text == '/shrug' ? '¯\\_(ツ)_/¯' : text;
+ }(_message.text.trim()),
+ style: TextStyle(color: Colors.white)),
+ padding: EdgeInsets.all(12.0),
+ margin: EdgeInsets.only(bottom: 8.0, left: 8.0, right: 8.0),
+ width: 222.0,
+ decoration: BoxDecoration(
+ color:
+ _message.whom == clientID ? Colors.blueAccent : Colors.grey,
+ borderRadius: BorderRadius.circular(7.0)),
+ ),
+ ],
+ mainAxisAlignment: _message.whom == clientID
+ ? MainAxisAlignment.end
+ : MainAxisAlignment.start,
+ );
+ }).toList();
+
+ return Scaffold(
+ appBar: AppBar(
+ title: (isConnecting
+ ? Text('Connecting chat to ' + widget.server.name + '...')
+ : isConnected
+ ? Text('Live chat with ' + widget.server.name)
+ : Text('Chat log with ' + widget.server.name))),
+ body: SafeArea(
+ child: Column(
+ children: [
+ Flexible(
+ child: ListView(
+ padding: const EdgeInsets.all(12.0),
+ controller: listScrollController,
+ children: list),
+ ),
+ Row(
+ children: [
+ Flexible(
+ child: Container(
+ margin: const EdgeInsets.only(left: 16.0),
+ child: TextField(
+ style: const TextStyle(fontSize: 15.0),
+ controller: textEditingController,
+ decoration: InputDecoration.collapsed(
+ hintText: isConnecting
+ ? 'Wait until connected...'
+ : isConnected
+ ? 'Type your message...'
+ : 'Chat got disconnected',
+ hintStyle: const TextStyle(color: Colors.grey),
+ ),
+ enabled: isConnected,
+ ),
+ ),
+ ),
+ Container(
+ margin: const EdgeInsets.all(8.0),
+ child: IconButton(
+ icon: const Icon(Icons.send),
+ onPressed: isConnected
+ ? () => _sendMessage(textEditingController.text)
+ : null),
+ ),
+ ],
+ )
+ ],
+ ),
+ ),
+ );
+ }
+
+ void _onDataReceived(Uint8List data) {
+ // Allocate buffer for parsed data
+ int backspacesCounter = 0;
+ data.forEach((byte) {
+ if (byte == 8 || byte == 127) {
+ backspacesCounter++;
+ }
+ });
+ Uint8List buffer = Uint8List(data.length - backspacesCounter);
+ int bufferIndex = buffer.length;
+
+ // Apply backspace control character
+ backspacesCounter = 0;
+ for (int i = data.length - 1; i >= 0; i--) {
+ if (data[i] == 8 || data[i] == 127) {
+ backspacesCounter++;
+ } else {
+ if (backspacesCounter > 0) {
+ backspacesCounter--;
+ } else {
+ buffer[--bufferIndex] = data[i];
+ }
+ }
+ }
+
+ // Create message if there is new line character
+ String dataString = String.fromCharCodes(buffer);
+ int index = buffer.indexOf(13);
+ if (~index != 0) {
+ setState(() {
+ messages.add(
+ _Message(
+ 1,
+ backspacesCounter > 0
+ ? _messageBuffer.substring(
+ 0, _messageBuffer.length - backspacesCounter)
+ : _messageBuffer + dataString.substring(0, index),
+ ),
+ );
+ _messageBuffer = dataString.substring(index);
+ });
+ } else {
+ _messageBuffer = (backspacesCounter > 0
+ ? _messageBuffer.substring(
+ 0, _messageBuffer.length - backspacesCounter)
+ : _messageBuffer + dataString);
+ }
+ }
+
+ void _sendMessage(String text) async {
+ text = text.trim();
+ textEditingController.clear();
+
+ if (text.length > 0) {
+ try {
+ connection.output.add(utf8.encode(text + "\r\n"));
+ await connection.output.allSent;
+
+ setState(() {
+ messages.add(_Message(clientID, text));
+ });
+
+ Future.delayed(Duration(milliseconds: 333)).then((_) {
+ listScrollController.animateTo(
+ listScrollController.position.maxScrollExtent,
+ duration: Duration(milliseconds: 333),
+ curve: Curves.easeOut);
+ });
+ } catch (e) {
+ // Ignore error, but notify state
+ setState(() {});
+ }
+ }
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/DiscoveryPage.dart b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/DiscoveryPage.dart
new file mode 100755
index 0000000..d96a6b5
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/DiscoveryPage.dart
@@ -0,0 +1,153 @@
+import 'dart:async';
+import 'package:flutter/material.dart';
+import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
+
+import './BluetoothDeviceListEntry.dart';
+
+class DiscoveryPage extends StatefulWidget {
+ /// If true, discovery starts on page start, otherwise user must press action button.
+ final bool start;
+
+ const DiscoveryPage({this.start = true});
+
+ @override
+ _DiscoveryPage createState() => new _DiscoveryPage();
+}
+
+class _DiscoveryPage extends State {
+ StreamSubscription _streamSubscription;
+ List results = List();
+ bool isDiscovering;
+
+ _DiscoveryPage();
+
+ @override
+ void initState() {
+ super.initState();
+
+ isDiscovering = widget.start;
+ if (isDiscovering) {
+ _startDiscovery();
+ }
+ }
+
+ void _restartDiscovery() {
+ setState(() {
+ results.clear();
+ isDiscovering = true;
+ });
+
+ _startDiscovery();
+ }
+
+ void _startDiscovery() {
+ _streamSubscription =
+ FlutterBluetoothSerial.instance.startDiscovery().listen((r) {
+ setState(() {
+ results.add(r);
+ });
+ });
+
+ _streamSubscription.onDone(() {
+ setState(() {
+ isDiscovering = false;
+ });
+ });
+ }
+
+ // @TODO . One day there should be `_pairDevice` on long tap on something... ;)
+
+ @override
+ void dispose() {
+ // Avoid memory leak (`setState` after dispose) and cancel discovery
+ _streamSubscription?.cancel();
+
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: isDiscovering
+ ? Text('Discovering devices')
+ : Text('Discovered devices'),
+ actions: [
+ isDiscovering
+ ? FittedBox(
+ child: Container(
+ margin: new EdgeInsets.all(16.0),
+ child: CircularProgressIndicator(
+ valueColor: AlwaysStoppedAnimation(Colors.white),
+ ),
+ ),
+ )
+ : IconButton(
+ icon: Icon(Icons.replay),
+ onPressed: _restartDiscovery,
+ )
+ ],
+ ),
+ body: ListView.builder(
+ itemCount: results.length,
+ itemBuilder: (BuildContext context, index) {
+ BluetoothDiscoveryResult result = results[index];
+ return BluetoothDeviceListEntry(
+ device: result.device,
+ rssi: result.rssi,
+ onTap: () {
+ Navigator.of(context).pop(result.device);
+ },
+ onLongPress: () async {
+ try {
+ bool bonded = false;
+ if (result.device.isBonded) {
+ print('Unbonding from ${result.device.address}...');
+ await FlutterBluetoothSerial.instance
+ .removeDeviceBondWithAddress(result.device.address);
+ print('Unbonding from ${result.device.address} has succed');
+ } else {
+ print('Bonding with ${result.device.address}...');
+ bonded = await FlutterBluetoothSerial.instance
+ .bondDeviceAtAddress(result.device.address);
+ print(
+ 'Bonding with ${result.device.address} has ${bonded ? 'succed' : 'failed'}.');
+ }
+ setState(() {
+ results[results.indexOf(result)] = BluetoothDiscoveryResult(
+ device: BluetoothDevice(
+ name: result.device.name ?? '',
+ address: result.device.address,
+ type: result.device.type,
+ bondState: bonded
+ ? BluetoothBondState.bonded
+ : BluetoothBondState.none,
+ ),
+ rssi: result.rssi);
+ });
+ } catch (ex) {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ title: const Text('Error occured while bonding'),
+ content: Text("${ex.toString()}"),
+ actions: [
+ new FlatButton(
+ child: new Text("Close"),
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ ],
+ );
+ },
+ );
+ }
+ },
+ );
+ },
+ ),
+ );
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/MainPage.dart b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/MainPage.dart
new file mode 100755
index 0000000..87aa67b
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/MainPage.dart
@@ -0,0 +1,358 @@
+import 'dart:async';
+import 'package:flutter/material.dart';
+import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
+import 'package:scoped_model/scoped_model.dart';
+
+import './DiscoveryPage.dart';
+import './SelectBondedDevicePage.dart';
+import './ChatPage.dart';
+import './BackgroundCollectingTask.dart';
+import './BackgroundCollectedPage.dart';
+
+// import './helpers/LineChart.dart';
+
+class MainPage extends StatefulWidget {
+ @override
+ _MainPage createState() => new _MainPage();
+}
+
+class _MainPage extends State {
+ BluetoothState _bluetoothState = BluetoothState.UNKNOWN;
+
+ String _address = "...";
+ String _name = "...";
+
+ Timer _discoverableTimeoutTimer;
+ int _discoverableTimeoutSecondsLeft = 0;
+
+ BackgroundCollectingTask _collectingTask;
+
+ bool _autoAcceptPairingRequests = false;
+
+ @override
+ void initState() {
+ super.initState();
+
+ // Get current state
+ FlutterBluetoothSerial.instance.state.then((state) {
+ setState(() {
+ _bluetoothState = state;
+ });
+ });
+
+ Future.doWhile(() async {
+ // Wait if adapter not enabled
+ if (await FlutterBluetoothSerial.instance.isEnabled) {
+ return false;
+ }
+ await Future.delayed(Duration(milliseconds: 0xDD));
+ return true;
+ }).then((_) {
+ // Update the address field
+ FlutterBluetoothSerial.instance.address.then((address) {
+ setState(() {
+ _address = address;
+ });
+ });
+ });
+
+ FlutterBluetoothSerial.instance.name.then((name) {
+ setState(() {
+ _name = name;
+ });
+ });
+
+ // Listen for futher state changes
+ FlutterBluetoothSerial.instance
+ .onStateChanged()
+ .listen((BluetoothState state) {
+ setState(() {
+ _bluetoothState = state;
+
+ // Discoverable mode is disabled when Bluetooth gets disabled
+ _discoverableTimeoutTimer = null;
+ _discoverableTimeoutSecondsLeft = 0;
+ });
+ });
+ }
+
+ @override
+ void dispose() {
+ FlutterBluetoothSerial.instance.setPairingRequestHandler(null);
+ _collectingTask?.dispose();
+ _discoverableTimeoutTimer?.cancel();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Flutter Bluetooth Serial'),
+ ),
+ body: Container(
+ child: ListView(
+ children: [
+ Divider(),
+ ListTile(title: const Text('General')),
+ SwitchListTile(
+ title: const Text('Enable Bluetooth'),
+ value: _bluetoothState.isEnabled,
+ onChanged: (bool value) {
+ // Do the request and update with the true value then
+ future() async {
+ // async lambda seems to not working
+ if (value)
+ await FlutterBluetoothSerial.instance.requestEnable();
+ else
+ await FlutterBluetoothSerial.instance.requestDisable();
+ }
+
+ future().then((_) {
+ setState(() {});
+ });
+ },
+ ),
+ ListTile(
+ title: const Text('Bluetooth status'),
+ subtitle: Text(_bluetoothState.toString()),
+ trailing: RaisedButton(
+ child: const Text('Settings'),
+ onPressed: () {
+ FlutterBluetoothSerial.instance.openSettings();
+ },
+ ),
+ ),
+ ListTile(
+ title: const Text('Local adapter address'),
+ subtitle: Text(_address),
+ ),
+ ListTile(
+ title: const Text('Local adapter name'),
+ subtitle: Text(_name),
+ onLongPress: null,
+ ),
+ ListTile(
+ title: _discoverableTimeoutSecondsLeft == 0
+ ? const Text("Discoverable")
+ : Text(
+ "Discoverable for ${_discoverableTimeoutSecondsLeft}s"),
+ subtitle: const Text("PsychoX-Luna"),
+ trailing: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Checkbox(
+ value: _discoverableTimeoutSecondsLeft != 0,
+ onChanged: null,
+ ),
+ IconButton(
+ icon: const Icon(Icons.edit),
+ onPressed: null,
+ ),
+ IconButton(
+ icon: const Icon(Icons.refresh),
+ onPressed: () async {
+ print('Discoverable requested');
+ final int timeout = await FlutterBluetoothSerial.instance
+ .requestDiscoverable(60);
+ if (timeout < 0) {
+ print('Discoverable mode denied');
+ } else {
+ print(
+ 'Discoverable mode acquired for $timeout seconds');
+ }
+ setState(() {
+ _discoverableTimeoutTimer?.cancel();
+ _discoverableTimeoutSecondsLeft = timeout;
+ _discoverableTimeoutTimer =
+ Timer.periodic(Duration(seconds: 1), (Timer timer) {
+ setState(() {
+ if (_discoverableTimeoutSecondsLeft < 0) {
+ FlutterBluetoothSerial.instance.isDiscoverable
+ .then((isDiscoverable) {
+ if (isDiscoverable) {
+ print(
+ "Discoverable after timeout... might be infinity timeout :F");
+ _discoverableTimeoutSecondsLeft += 1;
+ }
+ });
+ timer.cancel();
+ _discoverableTimeoutSecondsLeft = 0;
+ } else {
+ _discoverableTimeoutSecondsLeft -= 1;
+ }
+ });
+ });
+ });
+ },
+ )
+ ],
+ ),
+ ),
+ Divider(),
+ ListTile(title: const Text('Devices discovery and connection')),
+ SwitchListTile(
+ title: const Text('Auto-try specific pin when pairing'),
+ subtitle: const Text('Pin 1234'),
+ value: _autoAcceptPairingRequests,
+ onChanged: (bool value) {
+ setState(() {
+ _autoAcceptPairingRequests = value;
+ });
+ if (value) {
+ FlutterBluetoothSerial.instance.setPairingRequestHandler(
+ (BluetoothPairingRequest request) {
+ print("Trying to auto-pair with Pin 1234");
+ if (request.pairingVariant == PairingVariant.Pin) {
+ return Future.value("1234");
+ }
+ return null;
+ });
+ } else {
+ FlutterBluetoothSerial.instance
+ .setPairingRequestHandler(null);
+ }
+ },
+ ),
+ ListTile(
+ title: RaisedButton(
+ child: const Text('Explore discovered devices'),
+ onPressed: () async {
+ final BluetoothDevice selectedDevice =
+ await Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) {
+ return DiscoveryPage();
+ },
+ ),
+ );
+
+ if (selectedDevice != null) {
+ print('Discovery -> selected ' + selectedDevice.address);
+ } else {
+ print('Discovery -> no device selected');
+ }
+ }),
+ ),
+ ListTile(
+ title: RaisedButton(
+ child: const Text('Connect to paired device to chat'),
+ onPressed: () async {
+ final BluetoothDevice selectedDevice =
+ await Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) {
+ return SelectBondedDevicePage(checkAvailability: false);
+ },
+ ),
+ );
+
+ if (selectedDevice != null) {
+ print('Connect -> selected ' + selectedDevice.address);
+ _startChat(context, selectedDevice);
+ } else {
+ print('Connect -> no device selected');
+ }
+ },
+ ),
+ ),
+ Divider(),
+ ListTile(title: const Text('Multiple connections example')),
+ ListTile(
+ title: RaisedButton(
+ child: ((_collectingTask != null && _collectingTask.inProgress)
+ ? const Text('Disconnect and stop background collecting')
+ : const Text('Connect to start background collecting')),
+ onPressed: () async {
+ if (_collectingTask != null && _collectingTask.inProgress) {
+ await _collectingTask.cancel();
+ setState(() {
+ /* Update for `_collectingTask.inProgress` */
+ });
+ } else {
+ final BluetoothDevice selectedDevice =
+ await Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) {
+ return SelectBondedDevicePage(
+ checkAvailability: false);
+ },
+ ),
+ );
+
+ if (selectedDevice != null) {
+ await _startBackgroundTask(context, selectedDevice);
+ setState(() {
+ /* Update for `_collectingTask.inProgress` */
+ });
+ }
+ }
+ },
+ ),
+ ),
+ ListTile(
+ title: RaisedButton(
+ child: const Text('View background collected data'),
+ onPressed: (_collectingTask != null)
+ ? () {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) {
+ return ScopedModel(
+ model: _collectingTask,
+ child: BackgroundCollectedPage(),
+ );
+ },
+ ),
+ );
+ }
+ : null,
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ void _startChat(BuildContext context, BluetoothDevice server) {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) {
+ return ChatPage(server: server);
+ },
+ ),
+ );
+ }
+
+ Future _startBackgroundTask(
+ BuildContext context,
+ BluetoothDevice server,
+ ) async {
+ try {
+ _collectingTask = await BackgroundCollectingTask.connect(server);
+ await _collectingTask.start();
+ } catch (ex) {
+ if (_collectingTask != null) {
+ _collectingTask.cancel();
+ }
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ title: const Text('Error occured while connecting'),
+ content: Text("${ex.toString()}"),
+ actions: [
+ new FlatButton(
+ child: new Text("Close"),
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ ],
+ );
+ },
+ );
+ }
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/SelectBondedDevicePage.dart b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/SelectBondedDevicePage.dart
new file mode 100755
index 0000000..29584bc
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/SelectBondedDevicePage.dart
@@ -0,0 +1,144 @@
+import 'dart:async';
+import 'package:flutter/material.dart';
+import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
+
+import './BluetoothDeviceListEntry.dart';
+
+class SelectBondedDevicePage extends StatefulWidget {
+ /// If true, on page start there is performed discovery upon the bonded devices.
+ /// Then, if they are not avaliable, they would be disabled from the selection.
+ final bool checkAvailability;
+
+ const SelectBondedDevicePage({this.checkAvailability = true});
+
+ @override
+ _SelectBondedDevicePage createState() => new _SelectBondedDevicePage();
+}
+
+enum _DeviceAvailability {
+ no,
+ maybe,
+ yes,
+}
+
+class _DeviceWithAvailability extends BluetoothDevice {
+ BluetoothDevice device;
+ _DeviceAvailability availability;
+ int rssi;
+
+ _DeviceWithAvailability(this.device, this.availability, [this.rssi]);
+}
+
+class _SelectBondedDevicePage extends State {
+ List<_DeviceWithAvailability> devices = List<_DeviceWithAvailability>();
+
+ // Availability
+ StreamSubscription _discoveryStreamSubscription;
+ bool _isDiscovering;
+
+ _SelectBondedDevicePage();
+
+ @override
+ void initState() {
+ super.initState();
+
+ _isDiscovering = widget.checkAvailability;
+
+ if (_isDiscovering) {
+ _startDiscovery();
+ }
+
+ // Setup a list of the bonded devices
+ FlutterBluetoothSerial.instance
+ .getBondedDevices()
+ .then((List bondedDevices) {
+ setState(() {
+ devices = bondedDevices
+ .map(
+ (device) => _DeviceWithAvailability(
+ device,
+ widget.checkAvailability
+ ? _DeviceAvailability.maybe
+ : _DeviceAvailability.yes,
+ ),
+ )
+ .toList();
+ });
+ });
+ }
+
+ void _restartDiscovery() {
+ setState(() {
+ _isDiscovering = true;
+ });
+
+ _startDiscovery();
+ }
+
+ void _startDiscovery() {
+ _discoveryStreamSubscription =
+ FlutterBluetoothSerial.instance.startDiscovery().listen((r) {
+ setState(() {
+ Iterator i = devices.iterator;
+ while (i.moveNext()) {
+ var _device = i.current;
+ if (_device.device == r.device) {
+ _device.availability = _DeviceAvailability.yes;
+ _device.rssi = r.rssi;
+ }
+ }
+ });
+ });
+
+ _discoveryStreamSubscription.onDone(() {
+ setState(() {
+ _isDiscovering = false;
+ });
+ });
+ }
+
+ @override
+ void dispose() {
+ // Avoid memory leak (`setState` after dispose) and cancel discovery
+ _discoveryStreamSubscription?.cancel();
+
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ List list = devices
+ .map((_device) => BluetoothDeviceListEntry(
+ device: _device.device,
+ rssi: _device.rssi,
+ enabled: _device.availability == _DeviceAvailability.yes,
+ onTap: () {
+ Navigator.of(context).pop(_device.device);
+ },
+ ))
+ .toList();
+ return Scaffold(
+ appBar: AppBar(
+ title: Text('Select device'),
+ actions: [
+ _isDiscovering
+ ? FittedBox(
+ child: Container(
+ margin: new EdgeInsets.all(16.0),
+ child: CircularProgressIndicator(
+ valueColor: AlwaysStoppedAnimation(
+ Colors.white,
+ ),
+ ),
+ ),
+ )
+ : IconButton(
+ icon: Icon(Icons.replay),
+ onPressed: _restartDiscovery,
+ )
+ ],
+ ),
+ body: ListView(children: list),
+ );
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/helpers/LineChart.dart b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/helpers/LineChart.dart
new file mode 100755
index 0000000..281001e
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/helpers/LineChart.dart
@@ -0,0 +1,600 @@
+/// @name LineChart
+/// @version 0.0.5
+/// @description Simple line chart widget
+/// @author Patryk "PsychoX" Ludwikowski
+/// @license MIT License (see https://mit-license.org/)
+
+import 'package:flutter/material.dart';
+import 'dart:ui' as ui;
+import 'dart:math' as math show min, max;
+
+import './PaintStyle.dart';
+
+class LabelEntry {
+ final double value;
+ final String label;
+
+ LabelEntry(this.value, this.label);
+}
+
+/// Widget that allows to show data on line chart.
+///
+/// All arguments, values and labels data should be sorted!
+/// Since both the arguments and the values must be `double` type,
+/// be aware of the precision.
+class LineChart extends StatelessWidget {
+ /// Constraints for the line chart.
+ final BoxConstraints constraints;
+
+ // @TODO ? Both `_LineChartPainter` and `LineChart` have most the same fields.
+ // `LineChart` is just mainly passing them to the painter. Shouldn't there be
+ // only one class containing these data? Some `LineChartData` forged inside here
+ // and then passed and used by the painter? :thinking:
+
+ /// Padding around main drawng area. Necessary for displaying labels (around the chart).
+ final EdgeInsets padding;
+
+ /* Arguments */
+ /// Collection of doubles as arguments.
+ final Iterable arguments;
+
+ /// Mappings of strings for doubles arguments, which allow to specify custom
+ /// strings as labels for certain arguments.
+ final Iterable argumentsLabels;
+
+ /* Values */
+ /// Collection of data series as collections of next values on corresponding arguments.
+ final Iterable> values;
+
+ /// Mappings of string for doubles values, which allow to specify custom
+ /// string as labels for certain values.
+ final Iterable valuesLabels;
+
+ /* Labels & lines styles */
+ /// Style of horizontal lines labels
+ final TextStyle horizontalLabelsTextStyle;
+
+ /// Style of vertical lines labels
+ final TextStyle verticalLabelsTextStyle;
+
+ /// Defines style of horizontal lines. Might be null in order to prevent lines from drawing.
+ final Paint horizontalLinesPaint;
+
+ /// Defines style of vertical lines. Might be null in order to prevent lines from drawing.
+ final Paint verticalLinesPaint;
+
+ // @TODO . expose it
+ final bool snapToLeftLabel = false;
+ final bool snapToTopLabel = true;
+ final bool snapToRightLabel = false;
+ final bool snapToBottomLabel = true;
+
+ /* Series points & lines styles */
+ /// List of paint styles for series values points.
+ ///
+ /// On whole list null would use predefined set of styles.
+ /// On list entry null there will be no points for certain series.
+ final List seriesPointsPaints;
+
+ /// List of paint styles for lines between next series points.
+ ///
+ /// On null there will be no lines.
+ final List seriesLinesPaints;
+
+ final double additionalMinimalHorizontalLabelsInterval;
+ final double additionalMinimalVerticalLablesInterval;
+
+ LineChart({
+ @required this.constraints,
+ this.padding = const EdgeInsets.fromLTRB(32, 12, 20, 28),
+ this.arguments,
+ this.argumentsLabels,
+ this.values,
+ this.valuesLabels,
+ this.horizontalLabelsTextStyle,
+ this.verticalLabelsTextStyle,
+ PaintStyle horizontalLinesStyle = const PaintStyle(color: Colors.grey),
+ PaintStyle verticalLinesStyle, // null for default
+
+ this.additionalMinimalHorizontalLabelsInterval = 8,
+ this.additionalMinimalVerticalLablesInterval = 8,
+ Iterable
+ seriesPointsStyles, // null would use predefined set of styles
+ Iterable seriesLinesStyles, // null for default
+ }) : horizontalLinesPaint = horizontalLinesStyle?.toPaint(),
+ verticalLinesPaint = verticalLinesStyle?.toPaint(),
+ seriesPointsPaints = _prepareSeriesPointsPaints(seriesPointsStyles),
+ seriesLinesPaints = _prepareSeriesLinesPaints(seriesLinesStyles) {
+ if (seriesPointsStyles.length < values.length &&
+ 12 /* default paints */ < values.length) {
+ throw "Too few `seriesPointsPaintStyle`s! Try define more or limit number of displayed series";
+ }
+ if (seriesLinesStyles.length < values.length) {
+ throw "Too few `seriesLinesStyles`s! Try define more or limit number of displayed series";
+ }
+ }
+
+ static Iterable _prepareSeriesPointsPaints(
+ Iterable seriesPointsStyles) {
+ if (seriesPointsStyles == null) {
+ // Default paint for points
+ return List.unmodifiable([
+ PaintStyle(strokeWidth: 1.7, color: Colors.blue).toPaint(),
+ PaintStyle(strokeWidth: 1.7, color: Colors.red).toPaint(),
+ PaintStyle(strokeWidth: 1.7, color: Colors.yellow).toPaint(),
+ PaintStyle(strokeWidth: 1.7, color: Colors.green).toPaint(),
+
+ PaintStyle(strokeWidth: 1.7, color: Colors.purple).toPaint(),
+ PaintStyle(strokeWidth: 1.7, color: Colors.deepOrange).toPaint(),
+ PaintStyle(strokeWidth: 1.7, color: Colors.brown).toPaint(),
+ PaintStyle(strokeWidth: 1.7, color: Colors.lime).toPaint(),
+
+ PaintStyle(strokeWidth: 1.7, color: Colors.indigo).toPaint(),
+ PaintStyle(strokeWidth: 1.7, color: Colors.pink).toPaint(),
+ PaintStyle(strokeWidth: 1.7, color: Colors.amber).toPaint(),
+ PaintStyle(strokeWidth: 1.7, color: Colors.teal).toPaint(),
+
+ // For more, user should specify them :F
+ ]);
+ } else {
+ return seriesPointsStyles.map((style) => style?.toPaint()).toList();
+ }
+ }
+
+ static Iterable _prepareSeriesLinesPaints(
+ Iterable seriesLinesStyles) {
+ if (seriesLinesStyles == null) {
+ return null;
+ } else {
+ return seriesLinesStyles.map((style) => style.toPaint()).toList();
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return ConstrainedBox(
+ constraints: this.constraints,
+ child: CustomPaint(
+ painter: _LineChartPainter(
+ padding: padding,
+ arguments: arguments,
+ argumentsLabels: argumentsLabels,
+ values: values,
+ valuesLabels: valuesLabels,
+ horizontalLabelsTextStyle:
+ horizontalLabelsTextStyle ?? Theme.of(context).textTheme.caption,
+ verticalLabelsTextStyle:
+ verticalLabelsTextStyle ?? Theme.of(context).textTheme.caption,
+ horizontalLinesPaint: horizontalLinesPaint,
+ verticalLinesPaint: verticalLinesPaint,
+ additionalMinimalHorizontalLabelsInterval:
+ additionalMinimalHorizontalLabelsInterval,
+ additionalMinimalVerticalLablesInterval:
+ additionalMinimalVerticalLablesInterval,
+ seriesPointsPaints: seriesPointsPaints,
+ seriesLinesPaints: seriesLinesPaints,
+ )));
+ }
+}
+
+class _LineChartPainter extends CustomPainter {
+ /// Padding around main drawng area. Necessary for displaying labels (around the chart).
+ final EdgeInsets padding;
+
+ /* Arguments */
+ /// Collection of doubles as arguments.
+ final Iterable arguments;
+
+ /// Mappings of strings for doubles arguments, which allow to specify custom
+ /// strings as labels for certain arguments.
+ final Iterable argumentsLabels;
+
+ /* Values */
+ /// Collection of data series as collections of next values on corresponding arguments.
+ final Iterable> values;
+
+ /// Mappings of string for doubles values, which allow to specify custom
+ /// string as labels for certain values.
+ final Iterable valuesLabels;
+
+ /* Labels & lines styles */
+ /// Style of horizontal lines labels
+ final TextStyle horizontalLabelsTextStyle;
+
+ /// Style of vertical lines labels
+ final TextStyle verticalLabelsTextStyle;
+
+ /// Defines style of horizontal lines. Might be null in order to prevent lines from drawing.
+ final Paint horizontalLinesPaint;
+
+ /// Defines style of vertical lines. Might be null in order to prevent lines from drawing.
+ final Paint verticalLinesPaint;
+
+ // @TODO . expose it
+ final bool snapToLeftLabel = false;
+ final bool snapToTopLabel = true;
+ final bool snapToRightLabel = false;
+ final bool snapToBottomLabel = true;
+
+ /* Series points & lines styles */
+ /// Collection of paint styles for series values points.
+ ///
+ /// On whole argument null would use predefined set of styles.
+ /// On collection entry null there will be no points for certain series.
+ final Iterable seriesPointsPaints;
+
+ /// Collection of paint styles for lines between next series points.
+ ///
+ /// On null there will be no lines.
+ final Iterable seriesLinesPaints;
+
+ /* Runtime */
+ /// Minimal allowed interval between horizontal lines. Calculated from font size.
+ final double minimalHorizontalLabelsInterval;
+
+ /// Maximal value of all data series values
+ double maxValue = -double.maxFinite;
+
+ /// Minimal value of all data series values
+ double minValue = double.maxFinite;
+
+ double _minimalHorizontalRatio = 0;
+ double _minimalVerticalRatio = 0;
+
+ /// Creates `_LineChartPainter` (`CustomPainter`) with given data and styling.
+ _LineChartPainter({
+ this.padding = const EdgeInsets.fromLTRB(40, 8, 8, 32),
+ this.arguments,
+ this.argumentsLabels,
+ this.values,
+ this.valuesLabels,
+ @required this.horizontalLabelsTextStyle,
+ @required this.verticalLabelsTextStyle,
+ @required this.horizontalLinesPaint,
+ @required this.verticalLinesPaint,
+ double additionalMinimalHorizontalLabelsInterval = 8,
+ double additionalMinimalVerticalLablesInterval = 8,
+ @required this.seriesPointsPaints,
+ @required this.seriesLinesPaints,
+ }) : this.minimalHorizontalLabelsInterval =
+ horizontalLabelsTextStyle.fontSize +
+ additionalMinimalHorizontalLabelsInterval {
+ // Find max & min values of data to be show
+ for (Iterable series in values) {
+ for (double value in series) {
+ if (value > maxValue) {
+ maxValue = value;
+ } else if (value < minValue) {
+ minValue = value;
+ }
+ }
+ }
+
+ if (valuesLabels != null) {
+ // Find minimal vertical ratio to fit all provided values labels
+ Iterator entry = valuesLabels.iterator;
+ entry.moveNext();
+ double lastValue = entry.current.value;
+
+ while (entry.moveNext()) {
+ final double goodRatio =
+ minimalHorizontalLabelsInterval / (entry.current.value - lastValue);
+ if (goodRatio > _minimalVerticalRatio) {
+ _minimalVerticalRatio = goodRatio;
+ }
+
+ lastValue = entry.current.value;
+ }
+ }
+
+ if (argumentsLabels != null) {
+ // Find minimal horizontal ratio to fit all provided arguments labels
+ Iterator entry = argumentsLabels.iterator;
+ entry.moveNext();
+ double lastValue = entry.current.value;
+ double lastWidth =
+ _getLabelTextPainter(entry.current.label, verticalLabelsTextStyle)
+ .width;
+
+ while (entry.moveNext()) {
+ final double nextValue = entry.current.value;
+ final double nextWidth =
+ _getLabelTextPainter(entry.current.label, verticalLabelsTextStyle)
+ .width;
+
+ final double goodRatio = ((lastWidth + nextWidth) / 2 +
+ additionalMinimalVerticalLablesInterval) /
+ (nextValue - lastValue);
+ if (goodRatio > _minimalHorizontalRatio) {
+ _minimalHorizontalRatio = goodRatio;
+ }
+
+ lastValue = nextValue;
+ lastWidth = nextWidth;
+ }
+ }
+ }
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ final double width = size.width - padding.left - padding.right;
+ final double height = size.height - padding.top - padding.bottom;
+
+ /* Horizontal lines with labels */
+ double valuesOffset = 0; // @TODO ? could be used in future for scrolling
+ double verticalRatio;
+
+ {
+ Iterable labels;
+
+ // If no labels provided - generate them!
+ if (valuesLabels == null) {
+ final double optimalStepValue =
+ _calculateOptimalStepValue(maxValue - minValue, height);
+ int stepsNumber = 1;
+
+ // Find bottom line value
+ double bottomValue = 0;
+ if (minValue > 0) {
+ while (bottomValue < minValue) {
+ bottomValue += optimalStepValue;
+ }
+ bottomValue -= optimalStepValue;
+ } else {
+ while (bottomValue > minValue) {
+ bottomValue -= optimalStepValue;
+ }
+ }
+ valuesOffset = bottomValue;
+
+ // Find top line value
+ double topValue = bottomValue;
+ while (topValue < maxValue) {
+ topValue += optimalStepValue;
+ stepsNumber += 1;
+ }
+
+ // Set labels iterable from prepared generator
+ Iterable generator(double optimalStepValue, int stepsNumber,
+ [double value = 0.0]) sync* {
+ //double value = _bottomValue;
+ for (int i = 0; i < stepsNumber; i++) {
+ yield LabelEntry(
+ value,
+ value
+ .toString()); // @TODO , choose better precision based on optimal step value while parsing to string
+ value += optimalStepValue;
+ }
+ }
+
+ labels = generator(optimalStepValue, stepsNumber, bottomValue);
+
+ if (!snapToTopLabel) {
+ topValue = maxValue;
+ }
+ if (!snapToBottomLabel) {
+ bottomValue = valuesOffset = minValue;
+ }
+
+ // Calculate vertical ratio of pixels per value
+ // Note: There is no empty space already
+ verticalRatio = height / (topValue - bottomValue);
+ }
+ // If labels provided - use them
+ else {
+ // Set labels iterable as the provided list
+ labels = valuesLabels;
+
+ // Use minimal visible value as offset.
+ // Note: `minValue` is calculated in constructor and includes miniaml labels values.
+ valuesOffset = minValue;
+
+ // Calculate vertical ratio of pixels per value
+ // Note: `_minimalVerticalRatio` is calculated in constructor
+ final double topValue = snapToTopLabel
+ ? math.max(maxValue, valuesLabels.last.value)
+ : maxValue;
+ final double bottomValue = snapToBottomLabel
+ ? math.min(minValue, valuesLabels.first.value)
+ : minValue;
+ final double noEmptySpaceRatio = height / (topValue - bottomValue);
+ verticalRatio = math.max(_minimalVerticalRatio, noEmptySpaceRatio);
+ }
+
+ // Draw the horizontal lines and labels
+ for (LabelEntry tuple in labels) {
+ if (tuple.value < valuesOffset) continue;
+ final double yOffset = (size.height -
+ padding.bottom -
+ (tuple.value - valuesOffset) * verticalRatio);
+ if (yOffset < padding.top) break;
+
+ // Draw line
+ if (horizontalLinesPaint != null) {
+ canvas.drawLine(
+ Offset(padding.left, yOffset),
+ Offset(size.width - padding.right, yOffset),
+ horizontalLinesPaint);
+ }
+
+ // Draw label
+ TextPainter(
+ text: TextSpan(text: tuple.label, style: horizontalLabelsTextStyle),
+ textAlign: TextAlign.right,
+ textDirection: TextDirection.ltr)
+ ..layout(minWidth: padding.left - 4)
+ ..paint(canvas,
+ Offset(0, yOffset - horizontalLabelsTextStyle.fontSize / 2 - 1));
+ }
+ }
+
+ /* Vertical lines with labels */
+ double argumentsOffset = 0;
+ final double xOffsetLimit = size.width - padding.right;
+ double horizontalRatio;
+
+ {
+ Iterable labels;
+
+ // If no labels provided - generate them!
+ if (argumentsLabels == null) {
+ throw "not implemented";
+ // @TODO . after few hot days of thinking about the problem for 1-2 hour a day, I just gave up.
+ // The hardest in the problem is that there must be trade-off between space for labels and max lines,
+ // but keep in mind that the label values should be in some human-readable steps (0.5, 10, 0.02...).
+ }
+ // If labels provided - use them
+ else {
+ // Set labels iterable as the provided list
+ labels = argumentsLabels;
+
+ // Use first visible argument as arguments offset
+ argumentsOffset = argumentsLabels.first.value;
+
+ if (!snapToLeftLabel) {
+ argumentsOffset = arguments.first;
+ }
+
+ // Calculate vertical ratio of pixels per value
+ // Note: `_minimalHorizontalRatio` is calculated in constructor
+ final double leftMost = snapToLeftLabel
+ ? math.min(arguments.first, argumentsLabels.first.value)
+ : arguments.first;
+ final double rightMost = snapToRightLabel
+ ? math.max(arguments.last, argumentsLabels.last.value)
+ : arguments.last;
+ final double noEmptySpaceRatio = width / (rightMost - leftMost);
+ horizontalRatio = math.max(_minimalHorizontalRatio, noEmptySpaceRatio);
+ }
+
+ // Draw the vertical lines and labels
+ for (LabelEntry tuple in labels) {
+ if (tuple.value < argumentsOffset) continue;
+ final double xOffset =
+ padding.left + (tuple.value - argumentsOffset) * horizontalRatio;
+ if (xOffset > xOffsetLimit) break;
+
+ // Draw line
+ if (verticalLinesPaint != null) {
+ canvas.drawLine(
+ Offset(xOffset, padding.top),
+ Offset(xOffset, size.height - padding.bottom),
+ verticalLinesPaint);
+ }
+
+ // Draw label
+ final TextPainter textPainter = TextPainter(
+ text: TextSpan(text: tuple.label, style: verticalLabelsTextStyle),
+ textDirection: TextDirection.ltr)
+ ..layout();
+ textPainter.paint(
+ canvas,
+ Offset(xOffset - textPainter.width / 2,
+ size.height - verticalLabelsTextStyle.fontSize - 8));
+ }
+ }
+
+ /* Points and lines between subsequent */
+ Iterator> series = values.iterator;
+ Iterator linesPaints = seriesLinesPaints == null
+ ? [].iterator
+ : seriesLinesPaints.iterator;
+ Iterator pointsPaints = seriesPointsPaints.iterator;
+ while (series.moveNext()) {
+ List points = [];
+ Iterator value = series.current.iterator;
+ Iterator argument = arguments.iterator;
+ while (value.moveNext()) {
+ argument.moveNext();
+ if (value.current == null || value.current == double.nan) continue;
+
+ if (argument.current < argumentsOffset) continue;
+ final double xOffset = padding.left +
+ (argument.current - argumentsOffset) * horizontalRatio;
+ if (xOffset > xOffsetLimit) break;
+
+ if (value.current < valuesOffset) continue;
+ final double yOffset = size.height -
+ padding.bottom -
+ (value.current - valuesOffset) * verticalRatio;
+ if (yOffset < padding.top) continue;
+
+ points.add(Offset(xOffset, yOffset));
+ }
+
+ // Lines
+ if (linesPaints.moveNext() && linesPaints.current != null) {
+ canvas.drawPath(Path()..addPolygon(points, false), linesPaints.current);
+ }
+
+ // Points
+ if (pointsPaints.moveNext() && pointsPaints.current != null) {
+ canvas.drawPoints(ui.PointMode.points, points, pointsPaints.current);
+ }
+ }
+ }
+
+ @override
+ bool shouldRepaint(_LineChartPainter old) =>
+ (this.arguments != old.arguments ||
+ this.values != old.values ||
+ this.argumentsLabels != old.argumentsLabels ||
+ this.valuesLabels != old.valuesLabels ||
+ this.seriesPointsPaints != old.seriesPointsPaints ||
+ this.seriesLinesPaints != old.seriesLinesPaints ||
+ this.horizontalLabelsTextStyle != old.horizontalLabelsTextStyle ||
+ this.verticalLabelsTextStyle != old.verticalLabelsTextStyle ||
+ this.padding != old.padding //
+ );
+
+ // ..., 0.01, 0.02, 0.05, 0.1, [0.125], 0.2, [0.25], 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, ...
+ double _calculateOptimalStepValue(double valueRange, double height) {
+ final int maxSteps = height ~/ minimalHorizontalLabelsInterval;
+ if (maxSteps <= 0) {
+ throw "invalid max lines!";
+ }
+ double interval = valueRange / maxSteps;
+ if (interval > 1) {
+ int zeros = 0;
+ while (interval >= 10) {
+ interval = interval / 10;
+ zeros += 1;
+ }
+ /**/ if (interval <= 1) {
+ interval = 1;
+ } else if (interval <= 2) {
+ interval = 2;
+ } else if (interval <= 5) {
+ interval = 5;
+ }
+ for (; zeros-- != 0;) {
+ interval *= 10;
+ }
+ } else {
+ // @TODO ! not working at all for lower :C
+ int zeros = 0;
+ while (interval < 0) {
+ interval = interval * 10;
+ zeros += 1;
+ }
+ /**/ if (interval <= 1) {
+ interval = 1;
+ } else if (interval <= 2) {
+ interval = 2;
+ } else if (interval <= 5) {
+ interval = 5;
+ }
+ for (; zeros-- != 0;) {
+ interval /= 10;
+ }
+ }
+ return interval;
+ }
+
+ TextPainter _getLabelTextPainter(String text, TextStyle style) {
+ return TextPainter(
+ text: TextSpan(text: text, style: style),
+ textDirection: TextDirection.ltr)
+ ..layout();
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/helpers/PaintStyle.dart b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/helpers/PaintStyle.dart
new file mode 100755
index 0000000..179828e
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/helpers/PaintStyle.dart
@@ -0,0 +1,263 @@
+import 'dart:ui';
+
+/// A description of the style to use when drawing on a [Canvas].
+///
+/// Most APIs on [Canvas] take a [Paint] object to describe the style
+/// to use for that operation. [PaintStyle] allows to be const
+/// constructed and later in runtime forged into the [Paint] object.
+class PaintStyle {
+ /// Whether to apply anti-aliasing to lines and images drawn on the
+ /// canvas.
+ ///
+ /// Defaults to true.
+ final bool isAntiAlias;
+
+ // Must be kept in sync with the default in paint.cc.
+ static const int _kColorDefault = 0xFF000000;
+
+ /// The color to use when stroking or filling a shape.
+ ///
+ /// Defaults to opaque black.
+ ///
+ /// See also:
+ ///
+ /// * [style], which controls whether to stroke or fill (or both).
+ /// * [colorFilter], which overrides [color].
+ /// * [shader], which overrides [color] with more elaborate effects.
+ ///
+ /// This color is not used when compositing. To colorize a layer, use
+ /// [colorFilter].
+ final Color color;
+
+ // Must be kept in sync with the default in paint.cc.
+ static final int _kBlendModeDefault = BlendMode.srcOver.index;
+
+ /// A blend mode to apply when a shape is drawn or a layer is composited.
+ ///
+ /// The source colors are from the shape being drawn (e.g. from
+ /// [Canvas.drawPath]) or layer being composited (the graphics that were drawn
+ /// between the [Canvas.saveLayer] and [Canvas.restore] calls), after applying
+ /// the [colorFilter], if any.
+ ///
+ /// The destination colors are from the background onto which the shape or
+ /// layer is being composited.
+ ///
+ /// Defaults to [BlendMode.srcOver].
+ ///
+ /// See also:
+ ///
+ /// * [Canvas.saveLayer], which uses its [Paint]'s [blendMode] to composite
+ /// the layer when [restore] is called.
+ /// * [BlendMode], which discusses the user of [saveLayer] with [blendMode].
+ final BlendMode blendMode;
+
+ /// Whether to paint inside shapes, the edges of shapes, or both.
+ ///
+ /// Defaults to [PaintingStyle.fill].
+ final PaintingStyle style;
+
+ /// How wide to make edges drawn when [style] is set to
+ /// [PaintingStyle.stroke]. The width is given in logical pixels measured in
+ /// the direction orthogonal to the direction of the path.
+ ///
+ /// Defaults to 0.0, which correspond to a hairline width.
+ final double strokeWidth;
+
+ /// The kind of finish to place on the end of lines drawn when
+ /// [style] is set to [PaintingStyle.stroke].
+ ///
+ /// Defaults to [StrokeCap.butt], i.e. no caps.
+ final StrokeCap strokeCap;
+
+ /// The kind of finish to place on the joins between segments.
+ ///
+ /// This applies to paths drawn when [style] is set to [PaintingStyle.stroke],
+ /// It does not apply to points drawn as lines with [Canvas.drawPoints].
+ ///
+ /// Defaults to [StrokeJoin.miter], i.e. sharp corners.
+ ///
+ /// Some examples of joins:
+ ///
+ /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4}
+ ///
+ /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4}
+ ///
+ /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4}
+ ///
+ /// The centers of the line segments are colored in the diagrams above to
+ /// highlight the joins, but in normal usage the join is the same color as the
+ /// line.
+ ///
+ /// See also:
+ ///
+ /// * [strokeMiterLimit] to control when miters are replaced by bevels when
+ /// this is set to [StrokeJoin.miter].
+ /// * [strokeCap] to control what is drawn at the ends of the stroke.
+ /// * [StrokeJoin] for the definitive list of stroke joins.
+ final StrokeJoin strokeJoin;
+
+ // Must be kept in sync with the default in paint.cc.
+ static const double _kStrokeMiterLimitDefault = 4.0;
+
+ /// The limit for miters to be drawn on segments when the join is set to
+ /// [StrokeJoin.miter] and the [style] is set to [PaintingStyle.stroke]. If
+ /// this limit is exceeded, then a [StrokeJoin.bevel] join will be drawn
+ /// instead. This may cause some 'popping' of the corners of a path if the
+ /// angle between line segments is animated, as seen in the diagrams below.
+ ///
+ /// This limit is expressed as a limit on the length of the miter.
+ ///
+ /// Defaults to 4.0. Using zero as a limit will cause a [StrokeJoin.bevel]
+ /// join to be used all the time.
+ ///
+ /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_0_join.mp4}
+ ///
+ /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4}
+ ///
+ /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_6_join.mp4}
+ ///
+ /// The centers of the line segments are colored in the diagrams above to
+ /// highlight the joins, but in normal usage the join is the same color as the
+ /// line.
+ ///
+ /// See also:
+ ///
+ /// * [strokeJoin] to control the kind of finish to place on the joins
+ /// between segments.
+ /// * [strokeCap] to control what is drawn at the ends of the stroke.
+ final double strokeMiterLimit;
+
+ /// A mask filter (for example, a blur) to apply to a shape after it has been
+ /// drawn but before it has been composited into the image.
+ ///
+ /// See [MaskFilter] for details.
+ final MaskFilter maskFilter;
+
+ /// Controls the performance vs quality trade-off to use when applying
+ /// filters, such as [maskFilter], or when drawing images, as with
+ /// [Canvas.drawImageRect] or [Canvas.drawImageNine].
+ ///
+ /// Defaults to [FilterQuality.none].
+ // TODO(ianh): verify that the image drawing methods actually respect this
+ final FilterQuality filterQuality;
+
+ /// The shader to use when stroking or filling a shape.
+ ///
+ /// When this is null, the [color] is used instead.
+ ///
+ /// See also:
+ ///
+ /// * [Gradient], a shader that paints a color gradient.
+ /// * [ImageShader], a shader that tiles an [Image].
+ /// * [colorFilter], which overrides [shader].
+ /// * [color], which is used if [shader] and [colorFilter] are null.
+ final Shader shader;
+
+ /// A color filter to apply when a shape is drawn or when a layer is
+ /// composited.
+ ///
+ /// See [ColorFilter] for details.
+ ///
+ /// When a shape is being drawn, [colorFilter] overrides [color] and [shader].
+ final ColorFilter colorFilter;
+
+ /// Whether the colors of the image are inverted when drawn.
+ ///
+ /// Inverting the colors of an image applies a new color filter that will
+ /// be composed with any user provided color filters. This is primarily
+ /// used for implementing smart invert on iOS.
+ final bool invertColors;
+
+ const PaintStyle({
+ this.isAntiAlias = true,
+ this.color = const Color(_kColorDefault),
+ this.blendMode = BlendMode.srcOver,
+ this.style = PaintingStyle.fill,
+ this.strokeWidth = 0.0,
+ this.strokeCap = StrokeCap.butt,
+ this.strokeJoin = StrokeJoin.miter,
+ this.strokeMiterLimit = 4.0,
+ this.maskFilter, // null
+ this.filterQuality = FilterQuality.none,
+ this.shader, // null
+ this.colorFilter, // null
+ this.invertColors = false,
+ });
+
+ @override
+ String toString() {
+ final StringBuffer result = StringBuffer();
+ String semicolon = '';
+ result.write('PaintStyle(');
+ if (style == PaintingStyle.stroke) {
+ result.write('$style');
+ if (strokeWidth != 0.0)
+ result.write(' ${strokeWidth.toStringAsFixed(1)}');
+ else
+ result.write(' hairline');
+ if (strokeCap != StrokeCap.butt) result.write(' $strokeCap');
+ if (strokeJoin == StrokeJoin.miter) {
+ if (strokeMiterLimit != _kStrokeMiterLimitDefault)
+ result.write(
+ ' $strokeJoin up to ${strokeMiterLimit.toStringAsFixed(1)}');
+ } else {
+ result.write(' $strokeJoin');
+ }
+ semicolon = '; ';
+ }
+ if (isAntiAlias != true) {
+ result.write('${semicolon}antialias off');
+ semicolon = '; ';
+ }
+ if (color != const Color(_kColorDefault)) {
+ if (color != null)
+ result.write('$semicolon$color');
+ else
+ result.write('${semicolon}no color');
+ semicolon = '; ';
+ }
+ if (blendMode.index != _kBlendModeDefault) {
+ result.write('$semicolon$blendMode');
+ semicolon = '; ';
+ }
+ if (colorFilter != null) {
+ result.write('${semicolon}colorFilter: $colorFilter');
+ semicolon = '; ';
+ }
+ if (maskFilter != null) {
+ result.write('${semicolon}maskFilter: $maskFilter');
+ semicolon = '; ';
+ }
+ if (filterQuality != FilterQuality.none) {
+ result.write('${semicolon}filterQuality: $filterQuality');
+ semicolon = '; ';
+ }
+ if (shader != null) {
+ result.write('${semicolon}shader: $shader');
+ semicolon = '; ';
+ }
+ if (invertColors) result.write('${semicolon}invert: $invertColors');
+ result.write(')');
+ return result.toString();
+ }
+
+ Paint toPaint() {
+ Paint paint = Paint();
+ if (this.isAntiAlias != true) paint.isAntiAlias = this.isAntiAlias;
+ if (this.color != const Color(_kColorDefault)) paint.color = this.color;
+ if (this.blendMode != BlendMode.srcOver) paint.blendMode = this.blendMode;
+ if (this.style != PaintingStyle.fill) paint.style = this.style;
+ if (this.strokeWidth != 0.0) paint.strokeWidth = this.strokeWidth;
+ if (this.strokeCap != StrokeCap.butt) paint.strokeCap = this.strokeCap;
+ if (this.strokeJoin != StrokeJoin.miter) paint.strokeJoin = this.strokeJoin;
+ if (this.strokeMiterLimit != 4.0)
+ paint.strokeMiterLimit = this.strokeMiterLimit;
+ if (this.maskFilter != null) paint.maskFilter = this.maskFilter;
+ if (this.filterQuality != FilterQuality.none)
+ paint.filterQuality = this.filterQuality;
+ if (this.shader != null) paint.shader = this.shader;
+ if (this.colorFilter != null) paint.colorFilter = this.colorFilter;
+ if (this.invertColors != false) paint.invertColors = this.invertColors;
+ return paint;
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/main.dart b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/main.dart
new file mode 100755
index 0000000..72b4904
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/lib/main.dart
@@ -0,0 +1,15 @@
+//flutter_bluetooth_serial_example
+//https://github.com/edufolly/flutter_bluetooth_serial/tree/master/example
+
+import 'package:flutter/material.dart';
+
+import './MainPage.dart';
+
+void main() => runApp(new ExampleApplication());
+
+class ExampleApplication extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(home: MainPage());
+ }
+}
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/pubspec.lock b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/pubspec.lock
new file mode 100644
index 0000000..a5520e2
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/pubspec.lock
@@ -0,0 +1,160 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ async:
+ dependency: transitive
+ description:
+ name: async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.4.1"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ charcode:
+ dependency: transitive
+ description:
+ name: charcode
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.3"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.1"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.14.12"
+ cupertino_icons:
+ dependency: "direct main"
+ description:
+ name: cupertino_icons
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.3"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_bluetooth_serial:
+ dependency: "direct main"
+ description:
+ name: flutter_bluetooth_serial
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.2.2"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.12.6"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.8"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.7.0"
+ scoped_model:
+ dependency: "direct main"
+ description:
+ name: scoped_model
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.1"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.7.0"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.9.3"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.0"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.5"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.2.15"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.6"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.8"
+sdks:
+ dart: ">=2.7.0 <3.0.0"
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/pubspec.yaml b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/pubspec.yaml
new file mode 100644
index 0000000..621d6ce
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/pubspec.yaml
@@ -0,0 +1,78 @@
+name: androidbluetoothserialapp
+description: A new Flutter application.
+
+# The following line prevents the package from being accidentally published to
+# pub.dev using `pub publish`. This is preferred for private packages.
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+# The following defines the version and build number for your application.
+# A version number is three numbers separated by dots, like 1.2.43
+# followed by an optional build number separated by a +.
+# Both the version and the builder number may be overridden in flutter
+# build by specifying --build-name and --build-number, respectively.
+# In Android, build-name is used as versionName while build-number used as versionCode.
+# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
+# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
+# Read more about iOS versioning at
+# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.7.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^0.1.3
+ flutter_bluetooth_serial: ^0.2.2
+ scoped_model: ^1.0.1
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware.
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/assets-and-images/#from-packages
+
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.dev/custom-fonts/#from-packages
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/favicon.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/favicon.png
new file mode 100644
index 0000000..8aaa46a
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/favicon.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/icons/Icon-192.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/icons/Icon-192.png
new file mode 100644
index 0000000..b749bfe
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/icons/Icon-192.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/icons/Icon-512.png b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/icons/Icon-512.png
new file mode 100644
index 0000000..88cfd48
Binary files /dev/null and b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/icons/Icon-512.png differ
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/index.html b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/index.html
new file mode 100644
index 0000000..a84c5e3
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ androidbluetoothserialapp
+
+
+
+
+
+
+
+
diff --git a/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/manifest.json b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/manifest.json
new file mode 100644
index 0000000..4951838
--- /dev/null
+++ b/ESP32_BT_CLASSIC/ESP32_BLUETOOTH_SERIAL_DEMO/android_bluetooth_serial_app/web/manifest.json
@@ -0,0 +1,23 @@
+{
+ "name": "androidbluetoothserialapp",
+ "short_name": "androidbluetoothserialapp",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "A new Flutter application.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ]
+}