В начало | Зарегистрироваться | Заказать наши киты почтой
 
 
 
 

Обмен данными между Android-приложением и nRF52832. Часть 3. Широковещание

📆8 июля 2023   ✒️ayan   🔎340   💬0  

Всем привет! Во второй части статьи был приведён пример коммуникаций между Android и nRF52832 при наличии между ними соединения, т.е. по протоколу GATT. Однако, для случаев, когда достаточно односторонней передачи медленно меняющихся данных, можно ограничиться протоколом GAP и отправлять данные в составе широковещательных пакетов.

Видео-демонстрация проекта


Как видите, отчётливо просматривается запаздывание реакции Android на изменение состояния светодиода и кнопок. Связано это с отсутствием синхронизации между центральным и периферийным устройствами. Поэтому, в широковещательных пакетах предпочтительно передавать данные, значение которых меняется значительно реже сканирования центральным устройством.

Код для nRF52832

Программа с периодом в 300 мс отправляет в эфир широковещательный пакет с информацией о состоянии светодиода (пин P0.17) и двух кнопок (пины P0.13 и P0.14), за управление которыми ответственны библиотеки led
#ifndef LED_H_
#define LED_H_

#include "nrf.h"

#define LED           17

#define LED_OFF       NRF_GPIO->OUT |= (1UL << LED)
#define LED_ON        NRF_GPIO->OUT &= ~(1UL << LED)

#define LED_IS_OFF    (NRF_GPIO->OUT & (1UL << LED))
#define LED_IS_ON     !LED_IS_OFF      

void ledInit();

extern unsigned char ledCounter;

#endif /* LED_H_ */

#include "led.h"

unsigned char ledCounter = 0;

void ledInit() 
{
  NRF_GPIO->PIN_CNF[LED] = (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
  LED_OFF;
}

и buttons
#ifndef BUTTONS_H_
#define BUTTONS_H_

#include "nrf.h"

#define BUTTON_1_PUSHED      (NRF_GPIO->IN & (GPIO_IN_PIN14_High << GPIO_IN_PIN14_Pos)) == 0
#define BUTTON_1_RELEASED    (NRF_GPIO->IN & (GPIO_IN_PIN14_High << GPIO_IN_PIN14_Pos)) != 0
#define BUTTON_2_PUSHED      (NRF_GPIO->IN & (GPIO_IN_PIN13_High << GPIO_IN_PIN13_Pos)) == 0
#define BUTTON_2_RELEASED    (NRF_GPIO->IN & (GPIO_IN_PIN13_High << GPIO_IN_PIN13_Pos)) != 0

void buttonsInit();

#endif

#include "buttons.h"

void buttonsInit()
{
  NRF_GPIO->PIN_CNF[13] = (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) | (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos); 
  NRF_GPIO->PIN_CNF[14] = (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) | (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos);
}

В качестве шаблона main.c принят файл из примера beacon SDK 17. В дополнение к имеющимся данным (тип устройства, длина данных, UUID, RSSI) широковещательный пакет содержит три байта данных о состоянии светодиода и кнопок.
static uint8_t m_beacon_info[APP_BEACON_INFO_LENGTH] =
{
  APP_DEVICE_TYPE,     // Device type.
  APP_ADV_DATA_LENGTH, // Length of the manufacturer specific data.
  APP_BEACON_UUID,     // 128 bit UUID value.
  0,                   // Empty byte
  0, 0, 0,             // Led and Buttons states
  APP_MEASURED_RSSI    // TX power.
};

Кроме того, в функцию timers_init добавлены фрагменты инициализации и включения таймера, который с вышеуказанным периодом вызывает обработчик adv_data_update:
static void timers_init(void)
{
  ret_code_t err_code = app_timer_init();
  APP_ERROR_CHECK(err_code);

  err_code = app_timer_create(&adv_data_update_timer_id, APP_TIMER_MODE_REPEATED, adv_data_update);
  APP_ERROR_CHECK(err_code);  

  err_code = app_timer_start(adv_data_update_timer_id, APP_TIMER_TICKS(ADV_DATA_UPDATE_TIME), NULL);
  APP_ERROR_CHECK(err_code); 
}

А также прописан код самого обработчика, который:
• с периодом в 1.5 с меняет состояние светодиода на противоположное,
• проверяет состояние кнопок,
• приостанавливает широковещание,
• обновляет данные в m_beacon_info,
• возобновляет широковещание.
static void adv_data_update(void * p_context)
{
  ret_code_t err_code;

  sd_ble_gap_adv_stop(m_advertising.adv_handle);

  ledCounter++;
  if(ledCounter == 5)
  {
    ledCounter = 0;
    if(LED_IS_OFF)
    {
      LED_ON;
      NRF_LOG_INFO("led ON")
      m_beacon_info[19] = 1;
    }
    else if(LED_IS_ON)
    {
      LED_OFF;
      m_beacon_info[19] = 0;
      NRF_LOG_INFO("led OFF");
    }        
  }

  if(BUTTON_1_PUSHED)
  {
    m_beacon_info[20] = 1;
    NRF_LOG_INFO("button 1 pushed");
  }
  else if(BUTTON_1_RELEASED)
  {
    m_beacon_info[20] = 0;
    NRF_LOG_INFO("button 1 released");        
  }    

  if(BUTTON_2_PUSHED)
  {
    m_beacon_info[21] = 1;
    NRF_LOG_INFO("button 2 pushed");        
  }
  else if(BUTTON_2_RELEASED)
  {
    m_beacon_info[21] = 0;
    NRF_LOG_INFO("button 2 released");        
  }    

  err_code = ble_advdata_encode(&advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len);
  APP_ERROR_CHECK(err_code);

  err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &m_adv_params);
  APP_ERROR_CHECK(err_code);

  err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);
  APP_ERROR_CHECK(err_code);  
}

Основная функция инициализирует устройство и запускает широковещание:
int main(void)
{
  log_init();
  ledInit();
  buttonsInit();
  timers_init();
  power_management_init();
  ble_stack_init();
  advertising_init();
  advertising_start();

  while(1)
  {
    idle_state_handle();
  }
}

Android-приложение

В манифесте прописаны разрешения на работу с Bluetooth.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

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

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.Gap"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

В xml-файле определены параметры:
• кнопки сканирования BLE-устройств,
• текстовых полей, отражающих состояния светодиода и кнопок nRF52832.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/ledTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="led off"
        android:textSize="30dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.492"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.442" />

    <TextView
        android:id="@+id/button1TextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="button 1 is released"
        android:textSize="30dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.496"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.587" />

    <TextView
        android:id="@+id/button2TextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="button 2 is released"
        android:textSize="30dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.496"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.753" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="scan"
        android:text="Scan"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.495"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.279" />

</androidx.constraintlayout.widget.ConstraintLayout>

Java-код:
а) По нажатию кнопки сканирует BLE-устройства.
    public void scan(View view) {
        if (scanner != null) {
            if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
            }
            scanner.startScan(filters, scanSettings, scanCallback);
            Toast.makeText(this, "scanning...", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(this, "could not get scanner object", Toast.LENGTH_LONG).show();
        }
    }

б) Отражает в текстовых полях актуальные данные по состоянию светодиода и кнопок.
    private final ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            device = result.getDevice();
            Toast.makeText(MainActivity.this, "device with address" + "\n" + device.getAddress() + "\n" + "found", Toast.LENGTH_LONG).show();
            if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
            }
            byte[] data = result.getScanRecord().getManufacturerSpecificData(89);
            if(data[19] == 1) {
                ledTextView.setText("led on");
            }
            else if(data[19] == 0) {
                ledTextView.setText("led off");
            }
            if(data[20] == 1) {
                button1TextView.setText("button 1 is pressed");
            }
            else if(data[20] == 0) {
                button1TextView.setText("button 1 is released");
            }
            if(data[21] == 1) {
                button2TextView.setText("button2 is pressed");
            }
            else if(data[21] == 0) {
                button2TextView.setText("button2 is released");
            }
        }
    };


Файлы

🎁Исходники android.zip  10.64 Mb ⇣ 11
🎁Исходники nrf52832.zip  50.36 Kb ⇣ 8

Спасибо за внимание!
 

Читательское голосование

Нравится

Статью одобрили 8 читателей.

Для участия в голосовании зарегистрируйтесь и войдите на сайт с вашими логином и паролем.
 

Поделись с друзьями!

 

 

Связанные материалы

 

Схема на Датагоре. Новая статья Обмен данными между Android-приложением и nRF52832. Часть 2. BLE... Приветствую вас, уважаемые датагорцы! Сегодня — обмен данными между Android-приложением и nRF52832...
Схема на Датагоре. Новая статья Обмен данными между Android-приложением и nRF52832. Часть 1. Serial... Здравствуйте! Представляю вашему вниманию проект обмена данными между Android-приложением и SoC...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 6. Протоколы обмена данными I2C и SPI... В проекте из предыдущей части нашей ассемблерной эпопеи мы подключали к микроконтроллеру светодиод...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 4. Система адресации памяти, назначение выводов, тактирование и прерывания МК... Привет датагорцам! Сегодня мы остановимся на следующих вопросах касательно рассматриваемых нами...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 5. Периферия МК.... Сегодня мы рассмотрим работу следующих модулей периферии: • порта ввода-вывода, • таймера •...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 2. Шаблонные файлы и инструкции МК... В предыдущей части статьи мы провели подготовительную работу и вкратце разобрали принципы работы...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 1. Начало пути... Приветствую всех сограждан и читателей журнала Датагор! Пользуясь кучей времени, предоставленной...
Схема на Датагоре. Новая статья Программирование микроконтроллеров на языке C. Часть 2... Добрый день, уважаемые камрады-датагорцы! Сегодня, рассмотрев некоторые общие моменты, мы займёмся...
Схема на Датагоре. Новая статья Ассемблер для микроконтроллера с нуля. Часть 7. Компиляция, отладка, загрузка... Привет датагорцам и гостям нашего кибер-города! В предыдущих частях материала по Ассемблеру...
Схема на Датагоре. Новая статья Обмен данными между Java-приложением и МК. Часть 1. По проводу, USB-UART... Приветствую всех жителей и гостей кибер-города Датагор! Работа устройства на базе микроконтроллера...
Схема на Датагоре. Новая статья Визуализация для микроконтроллера. Часть 3. TFT дисплей 2.8" (240х320) на ILI9341... Битва за урожай закончена, можно продолжить повествование. Полноцветный TFT-дисплей 240×320 ILI9341...
Схема на Датагоре. Новая статья Цифровое телевидение, ч.1... Говорят про это много, но техническую сторону вопроса освещают нечасто. Поскольку работа моя...
 

Комментарии, вопросы, ответы, дополнения, отзывы

 

Добавить комментарий, вопрос, отзыв 💬

Камрады, будьте дружелюбны, соблюдайте правила!

  • Смайлы и люди
    Животные и природа
    Еда и напитки
    Активность
    Путешествия и места
    Предметы
    Символы
    Флаги
 
 
В начало | Зарегистрироваться | Заказать наши киты почтой