orbital-systems / react-native-esp-idf-provisioning Goto Github PK
View Code? Open in Web Editor NEWESP IDF provisioning and custom data library for react-native
License: MIT License
ESP IDF provisioning and custom data library for react-native
License: MIT License
const espResponse = await device.provision(ssid, password);
never resolves, whether there is an error or success. It does send the credentials.
I noticed this when testing yesterday. They should be defined as getters instead of statically defined in JS land because they are always undefined
until the device is connected. Also, I don't think they make much sense in SoftAP mode.
If we rewrite ESPDevice to a factory class returning subclasses based on input params for transport
and security
, we can get cleaner method calls to connect
(instead of passing null
for unused params):
// security 0, ble
device.connect();
// security 0, softAP
device.connect(softAPPassword);
// ... etc
We currently do not have a device to test SoftAP provisioning with so any help appreciated!
Our app follows the example app. searchESPDevices works, we can see list of devices.
On a found device, we call await espDevice.connect(proofOfPossession);
Then call await device.scanWifiList();
This always fails with message Failed to create session
.
Logcat show onCharacteristicWrite, status : 4
And stack trace
java.lang.Exception: Write to BLE failed
at com.espressif.provisioning.transport.BLETransport$1.onCharacteristicWrite(BLETransport.java:377)
at android.bluetooth.BluetoothGatt$1$7.run(BluetoothGatt.java:562)
at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:959)
at android.bluetooth.BluetoothGatt.-$$Nest$mrunOrQueueCallback(Unknown Source:0)
at android.bluetooth.BluetoothGatt$1.onCharacteristicWrite(BluetoothGatt.java:557)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:279)
at android.os.Binder.execTransactInternal(Binder.java:1351)
at android.os.Binder.execTransact(Binder.java:1310)
I found that the android native property ESPDevice.proofOfPossession
is always an empty string which make it unable to create BLE session with the device, hence the error above.
The reason is:
onPeripheralFound
, whenever a device is found (via searchESPDevices
), it is added to the espDevices
cache. But it that time, there is no PoP, thus no thing is set. Here is the line.createESPDevice
, if the device exist in the espDevices
cache, it returns immediately without filling the Pop. And the result is that PoP is never used.I made a patch and it seems to work for my case. Not know if there is any other cases. I don't check the property android.bluetooth.BluetoothDevice.uuids
because it always null in my case: the bluetooth device is not null by its uuids is null.
fix_proofOfPossession_is_never_set_patch.txt
It's difficult to unit test this as practically all code needs a real device to test with.
Suggestions on methodology are welcome.
when building iOS in release mode (to submit to appstore), we got the following error
The Swift pod `react-native-esp-idf-provisioning` depends upon `glog`, which does not define modules. To opt into those targets generating module maps (which is necessary to import them from Swift when building as static libraries), you may set `use_modular_headers!` globally in your Podfile, or specify `:modular_headers => true` for particular dependencies.
Are we missing anything causing the error?
The following code
const espDevice = new ESPDevice({
name,
transport: ESPTransport.ble,
security: ESPSecurity.secure,
});
... results in...
Argument of type '{ name: string; transport: ESPTransport.ble; security: ESPSecurity.secure; }' is not assignable to parameter of type 'ESPDeviceInterface'.
Property 'connected' is missing in type '{ name: string; transport: ESPTransport.ble; security: ESPSecurity.secure; }' but required in type 'ESPDeviceInterface'.ts(2345)
A few suggested changes to the README:
Missing arguments in example code
In the example in the README, this is shown:
const devices = ESPProvisionManager.searchESPDevices('prefix');
However, running this causes the following error:
Argument 2 (NSInteger) of EspIdfProvisioning.searchESPDevices must not be null
At least 2 arguments must be passed to searchESPDevices() for it to work.
Undocumented iOS permissions.
It is undocumented that the app.json for the module must provide a string for NSBluetoothAlwaysUsageDescription. Failing to add it causes this error:
This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSBluetoothAlwaysUsageDescription key with a string value explaining to the user how the app uses this data.
This error is a bit tough to find because it doesn't appear in the react native or expo loggers, only the xcode debug panel.
Merged and released in version 2.1.1 for iOS and 2.0.14 for android.
This line fails with "No bluetooth device found with given prefix":
The BLUETOOTH_CONNECT
permission does not exist on Android <= 30, so this will always fail.
In fact permission checks seem a bit inconsistent in general. 🤔
Sidenote: It seems you do not need to grant permissions on lower android version, but maybe the permission checks are usefull to ensure bluetooth is enabled in general?
When installing this package with the new architecture, it errors out with UnsupportedGenericParserError
. Apparently it is a known limitation with turbo modules and types imported from another file, see facebook/react-native#38769 (comment)
Solution is to copy the types to the turbomodule spec..
Thank you for creating this library. It maybe helpful for adoption to explain why one should choose this library compared to similar ones. Some examples:
Both our app and the example app in this repo could't pass the wifi scan after connecting to a device. In the example app, we can scan for the list of devices; we can select a device; but after selecting BLE, Secure1 and entering PoP, we're unable to scan Wifi.
The error message that toasted on the screen was Failed to create session
The native log in logcat was onCharacteristicWrite, status : 4
The native stack trace was
java.lang.Exception: Write to BLE failed
at com.espressif.provisioning.transport.BLETransport$1.onCharacteristicWrite(BLETransport.java:377)
at android.bluetooth.BluetoothGatt$1$7.run(BluetoothGatt.java:562)
at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:959)
at android.bluetooth.BluetoothGatt.-$$Nest$mrunOrQueueCallback(Unknown Source:0)
at android.bluetooth.BluetoothGatt$1.onCharacteristicWrite(BluetoothGatt.java:557)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:279)
at android.os.Binder.execTransactInternal(Binder.java:1351)
at android.os.Binder.execTransact(Binder.java:1310)
After much of debugging, I found the cause at file NativeEspIdfProvisioning.ts
, method createESPDevice
. The argument proofOfPossesion
is misspelled (missing one s), here is it. While its koltlin counterpart in class EspIdfProvisioningModule
is proofOfPossession
. That make the PoP always passed to the native code as empty string which cause the session creation fails.
Could you help fix this and pump a new version? Otherwise, the plugin won't work with any secured BLE devices
Hi, I'm back, sorry.
Not 100% sure if this is an issue for this package, but I want to share what I am facing and maybe hear your thoughts on this.
Here you reject the promise when an error occurs in scanWifiList
:
react-native-esp-idf-provisioning/ios/EspIdfProvisioning.swift
Lines 130 to 134 in 3bb75be
Which is great, but apparently that does not always mean it's the end.
The espressif sample app does not handle errors, it just ignores them and waits for another call of the completionHandler, as you can see in this issue. And with my device, which always returns a malformedProtobuf
error before resolving this means the scan always rejects prematurely.
For testing purposes I just updated the above handler like this:
if error != nil {
- reject("error", error?.description, nil)
- invoked = true
return
}
And voila, results are coming in! Feels dirty, though.
Another possible solution would be to not use a promise, but a subscription-like strategy, so we can receive errors and still a successful response afterwards. This seems to be what the sample app is doing as well, keep the connection open and just update whenever results come in.
The error and success do seem to follow up very closely, so maybe the error will not even be visible to end users if you choose to output them.
(That would be a breaking change, I suppose.)
This is an edge-case bug, but I found out that if I
I get error 133 on writing characteristic and immediately after I get disconnected from GATT with status code 19, which indicates that the device initiated the disconnection for some reason.
I found out that if I call disconnectDevice() in createESPDevice when the bond state of bluetoothDevice is BOND_NONE, this error does not occur anymore and scanning works again. Will fix in PR
When calling device.scanWifiList
(or sendData
) on a connected device that requires a username the following error occurs:
java.lang.IllegalArgumentException: The user identity 'I' must not be null or empty
at com.espressif.provisioning.srp6a.SRP6ClientSession.step1
at com.espressif.provisioning.security.Security2.<init>(Security2.java:84)
at com.espressif.provisioning.ESPDevice.initSession(ESPDevice.java:640)
at com.espressif.provisioning.ESPDevice.scanNetworks(ESPDevice.java:503)
at com.espidfprovisioning.EspIdfProvisioningModule.scanWifiList
at java.lang.reflect.Method.invoke(Native Method)
at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.
at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.
at com.facebook.jni.NativeRunnable.run(Native Method)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run
at java.lang.Thread.run(Thread.java:1012)
This is caused by the espDevices in EspIdfProvisioningModule
not retaining the username used to connect to the device.
I'm not sure if it is as simple as that, but this is what I think should work does seem to work in my local setup:
espDevice.userName = username
espDevices[deviceName]?.userName = username
Thank you for the time and effort you put into this package. I will continue debugging and let you know if I find anything substantial. 🙏
Hi,
I'm trying to get a simple implementation of this library working:
import {
ESPProvisionManager,
ESPDevice,
ESPTransport,
ESPSecurity,
} from '@orbital-systems/react-native-esp-idf-provisioning';
const ConnectToPedalAccessPoint = async () => {
try {
let prefix = '';
let transport = ESPTransport.ble;
const devices = ESPProvisionManager.searchESPDevices(prefix, transport);
console.log('devices', devices);
} catch (err) {
console.error(err);
}
}
await ConnectToPedalAccessPoint();
Running this returns an empty object
{"_h": 0, "_i": 0, "_j": null, "_k": null}
and produces the following error:
XPC connection invalid
Any suggestions on resolving this? I'd like to start with list all nearby BLE devices regardless of prefix. However, I've also tried this with values of prefix
that I know have matching nearby devices.
Hello,
I've successfully used this library to find and connect to an ESP32 device. However, I'm not able to scan for a wifi list using the device connection. It returns the error:
[Error: Request for returning Wi-Fi network list failed with error: The operation couldn’t be completed. (SwiftProtobuf.BinaryDecodingError error 3.)]
Very simple implemenation:
# This line works. Confirmed connection by monitoring ESP32 output.
await device.connect(proofOfPosession, null, username);
# This line produces an error.
const espWifiList = await device.scanWifiList();
Solve this by doing BLE search?
They implement the security check in the example app after connecting (https://github.com/espressif/esp-idf-provisioning-android/blob/master/app/src/main/java/com/espressif/ui/activities/AddDeviceActivity.java#L699-L753) but not in the library itself. So if you try to connect to an ESP device configured with security level 1 or 2 using ESPSecurity.unsecure it will not give you an error until later, which can be very confusing/frustrating.
Hi,
I run the example application and when I provision the wifi data I get {"status": "success"}
response but nothing happens: the device doesn't try to connect to wifi and is no more visible in the devices screen. To reappear and make it connectable via ble, I have to disconnect it from the power supply.
Where am I wrong?
Thanks, Vincenzo
Not sure if it makes sense getting it at all.
In an app project which uses Android Gradle plugin 8.2.0, it fails to build with the following error at this plugin
> Task :gradle-plugin:inspectClassesForKotlinIC UP-TO-DATE
FAILURE: Build completed with 2 failures.
1: Task failed with an exception.
-----------
* Where:
Build file '/..../node_modules/@orbital-systems/react-native-esp-idf-provisioning/android/build.gradle' line: 1
* What went wrong:
A problem occurred evaluating project ':orbital-systems_react-native-esp-idf-provisioning'.
> Failed to apply plugin 'kotlin-android'.
> Extension of type 'JavaPluginExtension' does not exist. Currently registered extension types: [ExtraPropertiesExtension, KotlinAndroidProjectExtension, KotlinTestsRegistry]
* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
* Exception is:
org.gradle.api.GradleScriptException: A problem occurred evaluating project ':orbital-systems_react-native-esp-idf-provisioning'.
at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:93)
at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.lambda$apply$0(DefaultScriptPluginFactory.java:135)
at org.gradle.configuration.ProjectScriptTarget.addConfiguration(ProjectScriptTarget.java:79)
at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:138)
at org.gradle.configuration.BuildOperationScriptPlugin$1.run(BuildOperationScriptPlugin.java:65)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
at org.gradle.configuration.BuildOperationScriptPlugin.lambda$apply$0(BuildOperationScriptPlugin.java:62)
at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:44)
at org.gradle.configuration.BuildOperationScriptPlugin.apply(BuildOperationScriptPlugin.java:62)
a...
...
==============================================================================
2: Task failed with an exception.
-----------
* What went wrong:
A problem occurred configuring project ':orbital-systems_react-native-esp-idf-provisioning'.
> 'kotlin-android' plugin requires one of the Android Gradle plugins.
Please apply one of the following plugins to ':orbital-systems_react-native-esp-idf-provisioning' project:
- com.android.application
- com.android.library
- com.android.dynamic-feature
- com.android.test
- com.android.instantapp
- com.android.feature
* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
...
The reason is at file android/build.gradle
. It currently looks like
apply plugin: "kotlin-android"
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.2.1"
}
}
def isNewArchitectureEnabled() {
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
}
apply plugin: "com.android.library"
.....
The line apply plugin: "kotlin-android"
is on the top, then, on the way down, there is the apply plugin: "com.android.library"
.
If I move the apply plugin: "com.android.library"
on the very first line, the build succeed
apply plugin: "com.android.library"
apply plugin: "kotlin-android"
... then the rest of the file
I'm not sure what proguard does, but it interferes with this library and causes error when creating a session (e.g. before scan wifi list).
Hello react-native-esp-idf-provisioning team,
I would like to extend my deepest gratitude for developing such a niche yet incredibly useful toolkit for React Native developers interested in working with ESP32 devices.
I have successfully used the connect(pop, null, username) method to establish a connection to an ESP32 device, which worked flawlessly. The logs displayed on the ESP32 terminal were as expected, including security2 connection initiation and public key creation, among others.
However, when attempting to provide an alternative connection method in my app through the use of connect(pop) (without the second and third parameters), I noticed that the ESP32 terminal did not display the same logs as when connecting with the connect(pop, null, username) method. Specifically, I didn't observe the logs related to security2 connection initiation or public key creation.
This led me to wonder if this behavior is related to the security settings configured within the connect() API. Could it be possible that the connect(pop) method currently only supports secure1 connections? Or is there another explanation for the difference in log output when using connect(pop) compared to connect(pop, null, username)?
This is my react native code
const search = async () => {
let prefix = 'PROV_'
let transport = ESPTransport.ble
let security = ESPSecurity.secure2
try {
const devices = await ESPProvisionManager.searchESPDevices(
prefix,
transport,
security,
)
setDevice(devices[0])
console.log(devices[0])
} catch (e) {
console.log(e)
}
}
const handlePressConnection = async () => {
console.log('Pressed')
setIsLoading(true)
try {
const newDevice = new ESPDevice({
name: device.name,
transport: ESPTransport.ble,
security: ESPSecurity.secure2,
})
await newDevice.connect(pop)
console.log('connected')
await newDevice.scanWifiList().then((res) => {
dispatch(setWifiList(res))
})
setIsLoading(false)
navigation.navigate('WifiListScreen')
} catch (e) {
setIsLoading(false)
console.log(e)
}
}
I could not see console.log('connected') on handlepressConnection method.
Thanks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.