improv-wifi / sdk-ble-js

JavaScript SDK to add Improv Wi-Fi over BLE to your website.
https://www.improv-wifi.com
Apache License 2.0
46 stars 8 forks source link

Stuck on "Provisioning" #212

Closed Fristi closed 1 year ago

Fristi commented 1 year ago

Hi,

When I look in my GATT server console I see a client connected, but no RPC messages being received.

I'm writing a Rust GATT version of Improv. I'm using a ESP32 + IDF + Bluedroid

The setup works when I connect with Lightblue and use the following hex string 011e0c4d79576972656c6573734150106d7973656375726570617373776f7264. I see the GATT server picks up the message.

I've also managed to connect and successfully send the byte string message like this:

<html>
    <head>
        <script>

            const fromHexString = (hexString) =>
                Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));

            function handleConnect() {

                const service = "00467768-6228-2272-4663-277478268000";

                navigator.bluetooth.requestDevice({ filters: [{ services: [service] }] })
                    .then(device => device.gatt.connect())
                    .then(server => {
                        return server.getPrimaryService(service);
                    })
                    .then(service => {
                        return service.getCharacteristic("00467768-6228-2272-4663-277478268003");
                    })
                    .then(characteristic => {
                        const byteString = "011e0c4d79576972656c6573734150106d7973656375726570617373776f7264";
                        const bytes = fromHexString(byteString);
                        characteristic.writeValue(bytes);
                    })
                    .catch(error => { console.error(error); });
            }
        </script>
    </head>
    <body>
        <button onclick="handleConnect()">Connect</button>
    </body>
</html>

GATT server:

fn main() -> ! {
    // It is necessary to call this function once. Otherwise some patches to the runtime
    // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
    esp_idf_sys::link_patches();
    // Bind the log crate to the ESP Logging facilities
    esp_idf_svc::log::EspLogger::initialize_default();

    let state = Arc::new(RwLock::new(State::Authorized));
    let state_read = state.clone();
    let error = Arc::new(RwLock::new(Error::None));
    let error_read = error.clone();

    let current_state = Characteristic::new(BleUuid::from_uuid128_string(IMPROV_STATUS_UUID))
        .name("Current state")
        .permissions(AttributePermissions::new().read())
        .properties(CharacteristicProperties::new().read().notify())
        .show_name()
        .on_read(move |_| {
            let s = state_read.read().unwrap();
            let ss = *s;
            vec![ss.into()]
        })
        .build();

    let error_state = Characteristic::new(BleUuid::from_uuid128_string(IMPROV_ERROR_UUID))
        .name("Error state")
        .permissions(AttributePermissions::new().read())
        .properties(CharacteristicProperties::new().read().notify())
        .show_name()
        .on_read(move |_| {
            let s = error_read.read().unwrap();
            let ss = *s;
            vec![ss.into()]
        })
        .build();

    let rpc_command = Characteristic::new(BleUuid::from_uuid128_string(IMPROV_RPC_COMMAND_UUID))
        .name("RPC command handler")
        .permissions(AttributePermissions::new().write())
        .properties(CharacteristicProperties::new().write())
        .on_write(move |bytes, _| {
            match ImprovCommand::from_bytes(bytes.as_slice()) {
                Ok(ImprovCommand::WifiSettings { ssid, password }) => {
                    info!("Got ssid and password: {} {}", ssid, password);
                    *state.write().unwrap() = State::Provisioning;
                    *error.write().unwrap() = Error::None;
                    match wifi(&ssid, &password) {
                        Ok(_) => {
                            *state.write().unwrap() = State::Provisioned;
                            info!("Connected")
                        },
                        Err(err) => {
                            info!("Error {}", err);
                            *error.write().unwrap() = Error::UnableToConnect;
                            *state.write().unwrap() = State::Authorized;
                            return ()
                        }
                    }
                    return ()
                }
                Ok(cmd) => {
                    info!("Command not processed {:?}", cmd);
                    return ()
                }
                Err(err) => {
                    info!("Error {:?}", err);
                    return ()
                }
            }
        })
        .show_name()
        .build();

    let rpc_result = Characteristic::new(BleUuid::from_uuid128_string(IMPROV_RPC_RESULT_UUID))
        .name("RPC result")
        .permissions(AttributePermissions::new().read())
        .properties(CharacteristicProperties::new().read().notify())
        .show_name()
        .build();

    let capabilities = Characteristic::new(BleUuid::from_uuid128_string(IMPROV_CAPABILITIES_UUID))
        .name("Capabilities")
        .permissions(AttributePermissions::new().read())
        .properties(CharacteristicProperties::new().read())
        .show_name()
        .set_value([0x00])
        .build();

    let service = Service::new(BleUuid::from_uuid128_string(IMPROV_SERVICE_UUID))
        .name("Improv Service")
        .primary()
        .characteristic(&rpc_command)
        .characteristic(&rpc_result)
        .characteristic(&current_state)
        .characteristic(&error_state)
        .characteristic(&capabilities)
        .build();

    let profile = Profile::new(0x0001)
        .name("Default Profile")
        .service(&service)
        .build();

    GLOBAL_GATT_SERVER
        .lock()
        .unwrap()
        .profile(profile)
        .device_name("Improve onboarding")
        .appearance(bluedroid::utilities::Appearance::GenericComputer)
        .advertise_service(&service)
        .start();

    loop {
        std::thread::sleep(Duration::from_millis(100));
    }
}
Fristi commented 1 year ago

Closing due inactivity