Icono del sitio AranaCorp

Creación de una aplicación BLE para ESP32 con React Native

Vamos a ver cómo crear una aplicación React Native para Adnroid que permita la comunicación BLE (Bluetooth Low Energy) con un ESP32. Usaremos React Native para desarrollar un terminal BLE en Android que pueda comunicarse con un ESP32 NodeMCU o cualquier otro dispositivo compatible.

Hardware

Código de gestión BLE para ESP32

Para probar la aplicación React Native, vamos a utilizar el código de gestión BLE para ESP32.

/*
    Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp
    Ported to Arduino ESP32 by Evandro Copercini
*/

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

BLECharacteristic *pCharacteristic = NULL;

std::string msg;

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string value = pCharacteristic->getValue();

      if (value.length() > 0) {
        Serial.println("*********");
        Serial.print("New value: ");
        for (int i = 0; i < value.length(); i++)
          Serial.print(value[i]);

        Serial.println();
        Serial.println("*********");
      }
    }
};

class MyServerCallbacks: public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    Serial.println("Client connected");
  }
  void onDisconnect(BLEServer* pServer) {
    Serial.println("Client disconnected");
    BLEDevice::startAdvertising(); // needed for reconnection
  }
};

void setup() {
  Serial.begin(115200);

  Serial.println("1- Download and install an BLE Terminal Free");
  Serial.println("2- Scan for BLE devices in the app");
  Serial.println("3- Connect to ESP32BLE");
  Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");

  BLEDevice::init("ESP32BLE");
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());
  
  BLEService *pService = pServer->createService(SERVICE_UUID);

  pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

  pCharacteristic->setCallbacks(new MyCallbacks());

  pCharacteristic->setValue("Hello World");
  pService->start();

  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  pAdvertising->start();

  Serial.print("Server address:");
  Serial.println(BLEDevice::getAddress().toString().c_str());
}

void loop() {
  readSerialPort();

  //Send data to slave
  if(msg!=""){
    pCharacteristic->setValue(msg);
    msg="";
  }
  
  delay(2000);
}

void readSerialPort(){
 while (Serial.available()) {
   delay(10);  
   if (Serial.available() >0) {
     char c = Serial.read();  //gets one byte from serial buffer
     msg += c; //add to String
   }
 }
 Serial.flush(); //clean buffer
}

Añadimos la función BLEServerCallbacks a la gestión del Servidor BLE para detectar la desconexión e iniciar la publicidad para poder reconectar el ESP32.

  pServer->setCallbacks(new MyServerCallbacks());

Aplicación React Native para la gestión de BLE

Para gestionar la comunicación BLE (Bluetooth Low Energy) en el dispositivo Android, utilizamos la biblioteca react-native-ble-manager

npm install react-native-ble-manager --save

Para configurar el proyecto de aplicación, siga el tutorial anterior.

En el archivo App.tsx, para utilizar la biblioteca la importamos utilizando el comando

import BleManager from 'react-native-ble-manager';

Estamos creando un componente funcional que contendrá los elementos que necesitamos para gestionar la comunicación BLE

let serviceid="4fafc201-1fb5-459e-8fcc-c5c9c331914b";
let caracid="beb5483e-36e1-4688-b7f5-ea07361b26a8";

const BluetoothBLETerminal = () =>  {
 const [devices, setDevices] = useState<any[]>([]);
 const [paired, setPaired] = useState<any[]>([]);
 const [selectedDevice, setSelectedDevice] = useState<Peripheral>();
 const [messageToSend, setMessageToSend] = useState("");
 const [receivedMessage, setReceivedMessage] = useState("");
 const [isConnected, setIsConnected] = useState(false);
 const [intervalId, setIntervalId] = useState<NodeJS.Timer>();
 const [isScanning, setIsScanning] = useState(false);

N.B.: es posible crear un componente derivado de ReactNative.Components

Gestión de permisos

Para descubrir y conectarte a dispositivos Bluetooth, necesitas al menos 3 permisos:

N.B.: estos permisos dependen de la versión y el SO utilizados

Estas son las etiquetas que hay que añadir al archivo AndroidManifest.xml

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.BLUETOOTH" />
  <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

En el archivo App.tsx, creamos la función requestBluetoothPermission()

    if (Platform.OS === 'android' && Platform.Version >= 23) {
  
        PermissionsAndroid.requestMultiple([
          PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
          PermissionsAndroid.PERMISSIONS.LIGAÇÃO_DE_DISTRIBUIÇÃO,
          PermissionsAndroid.PERMISSIONS.LOCALIZAÇÃO_DE_ACESSO,
        ]).then(result => {
          if (
            (result['android.permission.BLUETOOTH_SCAN'] &&
            result['android.permission.LIGAÇÃO_DE_DISTRIBUIÇÃO'] &&
            result['android.permission.LOCALIZAÇÃO_DE_ACESSO'] === 'granted')
            ||
            (result['android.permission.BLUETOOTH_SCAN'] &&
            result['android.permission.LIGAÇÃO_DE_DISTRIBUIÇÃO'] &&
            result['android.permission.LOCALIZAÇÃO_DE_ACESSO'] === 'never_ask_again')
          ) {
            console.log('User accepted');
          } else {
            console.log('User refused');        }
        });
    }

Función de gestión BLE

Las funciones para gestionar Bluetooth LE son las siguientes:

N.B.: En este ejemplo, estamos escribiendo y leyendo de la misma característica. Por lo tanto, leemos el valor registrado pulsando la tecla

const checkBluetoothEnabled = async () => {
   try {
         // turn on bluetooth if it is not on
   BleManager.enableBluetooth().then(() => {
     console.log('Bluetooth is turned on!');
   });
     
   } catch (error) {
     console.error('BLE is not available on this device.');
   }
 }
 
 const startScan = () => {
  if (!isScanning) {
    BleManager.scan([], 5, true)
      .then(() => {
        console.log('Scanning...');
        setIsScanning(true);
      })
      .catch(error => {
        console.error(error);
      });
  }
};

 const startDeviceDiscovery = async () => {

  BleManager.getBondedPeripherals().then((bondedPeripheralsArray) => {
    // Each peripheral in returned array will have id and name properties
    console.log("Bonded peripherals: " + bondedPeripheralsArray.length);
    setPaired(bondedPeripheralsArray);
  });

  /*BleManager.getDiscoveredPeripherals().then((peripheralsArray) => {
    // Success code
    console.log("Discovered peripherals: " + peripheralsArray.length);
  });*/
 }

 const connectToDevice = async (device: Peripheral) => {
 BleManager.connect(device.id)
     .then(() => {
     // Success code
     console.log("Connected");
     setSelectedDevice(device);
     setIsConnected(true);
     BleManager.retrieveServices(device.id).then(
       (deviceInfo) => {
       // Success code
       console.log("Device info:", deviceInfo);
       }
     );


     })
     .catch((error) => {
     // Failure code
     console.log(error);
     });
 }


const sendMessage = async () => {
 if(selectedDevice && isConnected){
   try {

    const buffer = Buffer.from(messageToSend);
    BleManager.write(
      selectedDevice.id,
      serviceid,
      caracid,
      buffer.toJSON().data
    ).then(() => {
      // Success code
      console.log("Write: " + buffer.toJSON().data);
    })
    .catch((error) => {
      // Failure code
      console.log(error);
    });
     
   } catch (error) {
     console.error('Error sending message:', error);
   }
 }
}


const readData = async () => {  
 if (selectedDevice && isConnected) {
    BleManager.read(
      selectedDevice.id,
      serviceid,
      caracid
    )
      .then((readData) => {
        // Success code
        console.log("Read: " + readData);
        const message = Buffer.from(readData);
        //const sensorData = buffer.readUInt8(1, true);
        if(receivedMessage.length>300){
          setReceivedMessage(""); //reset received message if length higher than 300
        }
        setReceivedMessage(receivedMessage => receivedMessage + message +"\n" );
        console.log("receivedMessage length",receivedMessage.length)
      })
      .catch((error) => {
        // Failure code
        console.log("Error reading message:",error);
      });
 }
}

 // disconnect from device
 const disconnectFromDevice = (device: Peripheral) => {
   BleManager.disconnect(device.id)
   .then(() => {
        setSelectedDevice(undefined);
        setIsConnected(false);
        setReceivedMessage("");
        clearInterval(intervalId);
        console.log("Disconnected from device");
   })
   .catch((error) => {
     // Failure code
     console.log("Error disconnecting:",error);
   });
 };

La función de representación en pantalla

Para la visualización, hemos decidido ponerlo todo en la misma pantalla. Habrá :

    return (
      <View>
      <Text
            style={{
              fontSize: 30,
              textAlign: 'center',
              borderBottomWidth: 1,
            }}>
            AC Bluetooth Terminal
          </Text>
        <ScrollView>

          {!isConnected && (
          <>
          {/*
          <Text>Available Devices:</Text>
          {devices.map((device) => (
            <Button
              key={device.id}
              title={device.name || 'Unnamed Device'}
              onPress={() => this.connectToDevice(device)}
            />
          ))}
          */}
          <Text>Paired Devices:</Text>
          {paired.map((pair: BluetoothDevice) => (
                      <View
                      style={{
                        flexDirection: 'row',
                        justifyContent: 'space-between',
                        marginBottom: 2,
                      }}>
                      <View style={styles.deviceItem}>
                        <Text style={styles.deviceName}>{pair.name}</Text>
                        <Text style={styles.deviceInfo}>{pair.id}</Text>
                      </View>
                      <TouchableOpacity
                        onPress={() =>
                          isConnected
                            ?  disconnect()
                            :  connectToDevice(pair)
                        }
                        style={styles.deviceButton}>
                        <Text
                          style={[
                            styles.scanButtonText,
                            {fontWeight: 'bold', fontSize: 12},
                          ]}>
                          {isConnected ? 'Disconnect' : 'Connect'}
                        </Text>
                      </TouchableOpacity>
                    </View>
          ))}
          </>  
          )}
          {selectedDevice && isConnected && (
            <>
              <View
                      style={{
                        flexDirection: 'row',
                        justifyContent: 'space-between',
                        margin: 5,
                      }}>
                      <View style={styles.deviceItem}>
                        <Text style={styles.deviceName}>{selectedDevice.name}</Text>
                        <Text style={styles.deviceInfo}>{selectedDevice.id}</Text>
                      </View>
                      <TouchableOpacity
                        onPress={() =>
                          isConnected
                            ?  disconnect()
                            :  connectToDevice(selectedDevice)
                        }
                        style={styles.deviceButton}>
                        <Text
                          style={styles.scanButtonText}>
                          {isConnected ? 'Disconnect' : 'Connect'}
                        </Text>
                      </TouchableOpacity>
                    </View>

          <View
              style={{
                flexDirection: 'row',
                justifyContent: 'space-between',
                margin: 5,
              }}>        
              <TextInput
                style={{
                  backgroundColor: '#888888',
                  margin: 2,
                  borderRadius: 15,
                  flex:3,
                  }}
                placeholder="Enter a message"
                value={messageToSend}
                onChangeText={(text) => setMessageToSend(text )}
              />
              <TouchableOpacity
                        onPress={() => sendMessage()
                        }
                        style={[styles.sendButton]}>
                        <Text
                          style={[
                            styles.scanButtonText,
                          ]}>
                          SEND
                        </Text>
                      </TouchableOpacity>
        </View>
              <Text>Received Message:</Text>
              <TextInput
              editable = {false}
              multiline
              numberOfLines={20}
              maxLength={100}
                style={{
                  backgroundColor: '#333333',
                  margin: 10,
                  borderRadius: 2,
                  borderWidth: 1,
                  borderColor: '#EEEEEE',
                  textAlignVertical: 'top',
                  }} >
                    {receivedMessage}
              </TextInput>
              
            </>
          )}
        </ScrollView>
      </View>
    );

Resultados

Como el emparejamiento no está gestionado por la aplicación, el ESP32 debe estar emparejado antes de utilizar la aplicación. Una vez que el código se ha cargado en el ESP32, puede iniciar la aplicación en el teléfono mediante el comando

npx react-native start

Código completo de la aplicación React Native

/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* https://github.com/innoveit/react-native-ble-manager
* https://blog.logrocket.com/using-react-native-ble-manager-mobile-app/
*/



import React, {useState, useEffect} from 'react';
import {   
  StyleSheet,
  Dimensions,
  View, 
  ScrollView, 
  Text,
  TextInput,
  PermissionsAndroid,
  TouchableOpacity,
  Platform} from 'react-native';
import BleManager,{Peripheral} from 'react-native-ble-manager';
import { Buffer } from 'buffer';

let serviceid="4fafc201-1fb5-459e-8fcc-c5c9c331914b";
let caracid="beb5483e-36e1-4688-b7f5-ea07361b26a8";

const BluetoothBLETerminal = () =&gt;  {
 const [devices, setDevices] = useState&lt;any[]&gt;([]);
 const [paired, setPaired] = useState&lt;any[]&gt;([]);
 const [selectedDevice, setSelectedDevice] = useState&lt;Peripheral&gt;();
 const [messageToSend, setMessageToSend] = useState("");
 const [receivedMessage, setReceivedMessage] = useState("");
 const [isConnected, setIsConnected] = useState(false);
 const [intervalId, setIntervalId] = useState&lt;NodeJS.Timer&gt;();
 const [isScanning, setIsScanning] = useState(false);

 const checkBluetoothEnabled = async () =&gt; {
   try {
         // turn on bluetooth if it is not on
   BleManager.enableBluetooth().then(() =&gt; {
     console.log('Bluetooth is turned on!');
   });
     
   } catch (error) {
     console.error('BLE is not available on this device.');
   }
 }
 
 const startScan = () =&gt; {
  if (!isScanning) {
    BleManager.scan([], 5, true)
      .then(() =&gt; {
        console.log('Scanning...');
        setIsScanning(true);
      })
      .catch(error =&gt; {
        console.error(error);
      });
  }
};

 const startDeviceDiscovery = async () =&gt; {

  BleManager.getBondedPeripherals().then((bondedPeripheralsArray) =&gt; {
    // Each peripheral in returned array will have id and name properties
    console.log("Bonded peripherals: " + bondedPeripheralsArray.length);
    setPaired(bondedPeripheralsArray);
  });

  /*BleManager.getDiscoveredPeripherals().then((peripheralsArray) =&gt; {
    // Success code
    console.log("Discovered peripherals: " + peripheralsArray.length);
  });*/
 }

 const connectToDevice = async (device: Peripheral) =&gt; {
 BleManager.connect(device.id)
     .then(() =&gt; {
     // Success code
     console.log("Connected");
     setSelectedDevice(device);
     setIsConnected(true);
     BleManager.retrieveServices(device.id).then(
       (deviceInfo) =&gt; {
       // Success code
       console.log("Device info:", deviceInfo);
       }
     );


     })
     .catch((error) =&gt; {
     // Failure code
     console.log(error);
     });
 }


const sendMessage = async () =&gt; {
 if(selectedDevice &amp;&amp; isConnected){
   try {

    const buffer = Buffer.from(messageToSend);
    BleManager.write(
      selectedDevice.id,
      serviceid,
      caracid,
      buffer.toJSON().data
    ).then(() =&gt; {
      // Success code
      console.log("Write: " + buffer.toJSON().data);
    })
    .catch((error) =&gt; {
      // Failure code
      console.log(error);
    });
     
   } catch (error) {
     console.error('Error sending message:', error);
   }
 }
}


const readData = async () =&gt; {  
 if (selectedDevice &amp;&amp; isConnected) {
    BleManager.read(
      selectedDevice.id,
      serviceid,
      caracid
    )
      .then((readData) =&gt; {
        // Success code
        console.log("Read: " + readData);
        const message = Buffer.from(readData);
        //const sensorData = buffer.readUInt8(1, true);
        if(receivedMessage.length&gt;300){
          setReceivedMessage("");
        }
        setReceivedMessage(receivedMessage =&gt; receivedMessage + message +"\n" );
        console.log("receivedMessage length",receivedMessage.length)
      })
      .catch((error) =&gt; {
        // Failure code
        console.log("Error reading message:",error);
      });
 }
}

/*useEffect(() =&gt; {
 let intervalId: string | number | NodeJS.Timer | undefined;
 if (selectedDevice &amp;&amp; isConnected) {
   intervalId = setInterval(() =&gt; readData(), 100);
   setIntervalId(intervalId);
 }
 return () =&gt; {
   clearInterval(intervalId);
 };
}, [isConnected,selectedDevice]);*/

 // disconnect from device
 const disconnectFromDevice = (device: Peripheral) =&gt; {
   BleManager.disconnect(device.id)
   .then(() =&gt; {
        setSelectedDevice(undefined);
        setIsConnected(false);
        setReceivedMessage("");
        clearInterval(intervalId);
        console.log("Disconnected from device");
   })
   .catch((error) =&gt; {
     // Failure code
     console.log("Error disconnecting:",error);
   });
   
   /*BleManager.removeBond(peripheral.id)
     .then(() =&gt; {
       peripheral.connected = false;
       peripherals.set(peripheral.id, peripheral);
       setConnectedDevices(Array.from(peripherals.values()));
       setDiscoveredDevices(Array.from(peripherals.values()));
       Alert.alert(`Disconnected from ${peripheral.name}`);
     })
     .catch(() =&gt; {
       console.log('fail to remove the bond');
     });*/



 };
 
 
 useEffect(() =&gt; {

    checkBluetoothEnabled();

    if (Platform.OS === 'android' &amp;&amp; Platform.Version &gt;= 23) {
  
        PermissionsAndroid.requestMultiple([
          PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
          PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
          PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
        ]).then(result =&gt; {
          if (
            (result['android.permission.BLUETOOTH_SCAN'] &amp;&amp;
            result['android.permission.BLUETOOTH_CONNECT'] &amp;&amp;
            result['android.permission.ACCESS_FINE_LOCATION'] === 'granted')
            ||
            (result['android.permission.BLUETOOTH_SCAN'] &amp;&amp;
            result['android.permission.BLUETOOTH_CONNECT'] &amp;&amp;
            result['android.permission.ACCESS_FINE_LOCATION'] === 'never_ask_again')
          ) {
            console.log('User accepted');
          } else {
            console.log('User refused');        }
        });

    }

    BleManager.start({showAlert: false}).then(() =&gt; {
      console.log('BleManager initialized');
      startDeviceDiscovery();
    }).catch((error) =&gt; {
      // Failure code
      console.log("Error requesting permission:",error);
    });
   
BleManager.checkState().then((state) =&gt;
   console.log(`current BLE state = '${state}'.`)
 );

 BleManager.getConnectedPeripherals([]).then((peripheralsArray) =&gt; {
   // Success code
   console.log("Connected peripherals: " + peripheralsArray.length);
 });

 BleManager.getBondedPeripherals().then((bondedPeripheralsArray) =&gt; {
   // Each peripheral in returned array will have id and name properties
   console.log("Bonded peripherals: " + bondedPeripheralsArray.length);
   //setBoundedDevices(bondedPeripheralsArray);
 });

 BleManager.getDiscoveredPeripherals().then((peripheralsArray) =&gt; {
   // Success code
   console.log("Discovered peripherals: " + peripheralsArray.length);
 });

 /*let stopDiscoverListener = BleManagerEmitter.addListener(
   'BleManagerDiscoverPeripheral',
   peripheral =&gt; {
     peripherals.set(peripheral.id, peripheral);
   },
 );*/

 /*let stopConnectListener = BleManagerEmitter.addListener(
   'BleManagerConnectPeripheral',
   peripheral =&gt; {
     console.log('BleManagerConnectPeripheral:', peripheral);
     peripherals.set(peripheral.id, peripheral);
     setConnectedDevices(Array.from(peripherals.values()));
   },
 );*/

 /*let stopScanListener = BleManagerEmitter.addListener(
   'BleManagerStopScan',
   () =&gt; {
     setIsScanning(false);
     console.log('scan stopped');
     BleManager.getDiscoveredPeripherals().then((peripheralsArray) =&gt; {
       // Success code
       console.log("Discovered peripherals: " + peripheralsArray.length);
       for (let i = 0; i &lt; peripheralsArray.length; i++) {
         let peripheral = peripheralsArray[i];
         console.log("item:", peripheral);
         //peripheral.connected = true;
         peripherals.set(peripheral.id, peripheral);
         setDiscoveredDevices(peripheralsArray);
       }

       
     });
   },
 );*/

 return () =&gt; {
   /*stopDiscoverListener.remove();
   stopConnectListener.remove();
   stopScanListener.remove();*/
 };
 }, [])


   return (
     &lt;View style={[styles.mainBody]}&gt;
     &lt;Text
           style={{
             fontSize: 30,
             textAlign: 'center',
             borderBottomWidth: 1,
           }}&gt;
           AC BLE Terminal
         &lt;/Text&gt;
       &lt;ScrollView&gt;

         {!isConnected &amp;&amp; (
         &lt;&gt;

        &lt;TouchableOpacity
                       onPress={() =&gt; startDeviceDiscovery()
                       }
                       style={[styles.deviceButton]}&gt;
                       &lt;Text
                         style={[
                           styles.scanButtonText,
                         ]}&gt;
                         SCAN
                       &lt;/Text&gt;
              &lt;/TouchableOpacity&gt;

         {/*
         &lt;Text&gt;Available Devices:&lt;/Text&gt;
         {devices.map((device) =&gt; (
           &lt;Button
             key={device.id}
             title={device.name || 'Unnamed Device'}
             onPress={() =&gt; this.connectToDevice(device)}
           /&gt;
         ))}
         */}
         &lt;Text&gt;Paired Devices:&lt;/Text&gt;
         {paired.map((pair,i) =&gt; (
                     &lt;View key={i}
                     style={{
                       flexDirection: 'row',
                       justifyContent: 'space-between',
                       marginBottom: 2,
                     }}&gt;
                     &lt;View style={styles.deviceItem}&gt;
                       &lt;Text style={styles.deviceName}&gt;{pair.name}&lt;/Text&gt;
                       &lt;Text style={styles.deviceInfo}&gt;{pair.id}, rssi: {pair.rssi}&lt;/Text&gt;
                     &lt;/View&gt;
                     &lt;TouchableOpacity
                       onPress={() =&gt;
                         isConnected
                           ?  disconnectFromDevice(pair)
                           :  connectToDevice(pair)
                       }
                       style={styles.deviceButton}&gt;
                       &lt;Text
                         style={[
                           styles.scanButtonText,
                           {fontWeight: 'bold', fontSize: 12},
                         ]}&gt;
                         {isConnected ? 'Disconnect' : 'Connect'}
                       &lt;/Text&gt;
                     &lt;/TouchableOpacity&gt;
                   &lt;/View&gt;
         ))}
         &lt;/&gt;  
         )}
         {selectedDevice &amp;&amp; isConnected &amp;&amp; (
           &lt;&gt;
             &lt;View
                     style={{
                       flexDirection: 'row',
                       justifyContent: 'space-between',
                       margin: 5,
                     }}&gt;
                     &lt;View style={styles.deviceItem}&gt;
                       &lt;Text style={styles.deviceName}&gt;{selectedDevice.name}&lt;/Text&gt;
                       &lt;Text style={styles.deviceInfo}&gt;{selectedDevice.id}, rssi: {selectedDevice.rssi}&lt;/Text&gt;
                     &lt;/View&gt;
                     &lt;TouchableOpacity
                       onPress={() =&gt;
                         isConnected
                           ?  disconnectFromDevice(selectedDevice)
                           :  connectToDevice(selectedDevice)
                       }
                       style={styles.deviceButton}&gt;
                       &lt;Text
                         style={styles.scanButtonText}&gt;
                         {isConnected ? 'Disconnect' : 'Connect'}
                       &lt;/Text&gt;
                     &lt;/TouchableOpacity&gt;
                   &lt;/View&gt;

         &lt;View
             style={{
               flexDirection: 'row',
               justifyContent: 'space-between',
               margin: 5,
             }}&gt;        
             &lt;TextInput
               style={{
                 backgroundColor: '#888888',
                 margin: 2,
                 borderRadius: 15,
                 flex:3,
                 }}
               placeholder="Enter a message"
               value={messageToSend}
               onChangeText={(text) =&gt; setMessageToSend(text)}
             /&gt;
             &lt;TouchableOpacity
                       onPress={() =&gt; sendMessage()
                       }
                       style={[styles.sendButton]}&gt;
                       &lt;Text
                         style={[
                           styles.scanButtonText,
                         ]}&gt;
                         SEND
                       &lt;/Text&gt;
                     &lt;/TouchableOpacity&gt;
       &lt;/View&gt;
       &lt;View
             style={{
               flexDirection: 'row',
               justifyContent: 'space-between',
               margin: 5,
             }}&gt;
             &lt;Text style={{textAlignVertical: 'center'}}&gt;Received Message:&lt;/Text&gt;
             &lt;TouchableOpacity
                       onPress={() =&gt; readData()
                       }
                       style={[styles.deviceButton]}&gt;
                       &lt;Text
                         style={[
                           styles.scanButtonText,
                         ]}&gt;
                         READ
                       &lt;/Text&gt;
              &lt;/TouchableOpacity&gt;
        &lt;/View&gt;
             &lt;TextInput
             editable = {false}
             multiline
             numberOfLines={20}
             maxLength={300}
               style={{
                 backgroundColor: '#333333',
                 margin: 10,
                 borderRadius: 2,
                 borderWidth: 1,
                 borderColor: '#EEEEEE',
                 textAlignVertical: 'top',
                 }} &gt;
                   {receivedMessage}
             &lt;/TextInput&gt;
             
           &lt;/&gt;
         )}
       &lt;/ScrollView&gt;
     &lt;/View&gt;
   );

};//end of component

//https://medium.com/supercharges-mobile-product-guide/reactive-styles-in-react-native-79a41fbdc404
export const theme = {
  smallPhone: 0,
  phone: 290,
  tablet: 750,
  }

const windowHeight = Dimensions.get('window').height;
const styles = StyleSheet.create({
 mainBody: {
   flex: 1,
   justifyContent: 'center',
   height: windowHeight,

   ...Platform.select ({
    ios: {
      fontFamily: "Arial",
    },
    
    android: {
      fontFamily: "Roboto",

    },
  }),
 },

 scanButtonText: {
   color: 'white',
   fontWeight: 'bold',
   fontSize: 12,
   textAlign: 'center',
 },
 noDevicesText: {
   textAlign: 'center',
   marginTop: 10,
   fontStyle: 'italic',
 },
 deviceItem: {
   marginBottom: 2,
 },
 deviceName: {
   fontSize: 14,
   fontWeight: 'bold',
 },
 deviceInfo: {
   fontSize: 8,
 },
 deviceButton: {
   backgroundColor: '#2196F3',
   padding: 10,
   borderRadius: 10,
   margin: 2,
   paddingHorizontal: 20,
 },
 sendButton: {
  backgroundColor: '#2196F3',
  padding: 15,
  borderRadius: 15,
  margin: 2,
  paddingHorizontal: 20,
 },
});

export default BluetoothBLETerminal;

Fuentes

Salir de la versión móvil