Целью этого проекта Bluetooth LE является считывание данных датчика качества воздуха и отображение их на ЖК-дисплее, подключенном к плате STM32. Веб-браузер считывает данные датчика и передает их на плату STM32 с помощью BleuIO.

1. Введение

Проект основан на STM32 Nucleo-144, который управляет ЖК-дисплеем с помощью BleuIO.

Для этого проекта нам понадобятся два USB-ключа BleuIO, один из которых подключен к плате Nucleo, а другой — к компьютеру, на котором запущен веб-скрипт, и HibouAir — устройство для мониторинга качества воздуха.
Когда ключ BleuIO подключенный к USB-порту платы Nucleo, STM32 распознает его и сразу начнет рекламу. Это позволяет ключу на порту компьютера подключаться к веб-скрипту.

С помощью веб-скрипта на компьютере мы можем сканировать и получать данные датчика качества воздуха от HibouAir. Затем мы отправляем эти данные на ЖК-экран, подключенный к STM32 с помощью Bluetooth.

В этом примере мы использовали плату разработки STM32 Nucleo-144 с микроконтроллером STM32H743ZI (STM32H743ZI с поддержкой micro mbed Development Nucleo-144, серия ARM® Cortex®-M7 MCU, 32-разрядная встроенная оценочная плата). Эта плата разработки имеет USB-хост, к которому мы подключаем ключ BleuIO.

Если вы хотите использовать другую настройку, вам нужно убедиться, что она поддерживает USB-хост, и помнить, что настройка GPIO может отличаться и может потребоваться перенастройка в файле .ioc.

О Кодексе

Исходный код проекта доступен на Github.

https://github.com/smart-sensor-devices-ab/stm32_ble_sensor_lcd.git

Либо клонируйте проект, либо загрузите его в виде zip-файла и разархивируйте его в рабочее пространство STM32CubeIDE.

Если вы загружаете проект в виде zip-файла, вам нужно будет переименовать папку проекта с «stm32_bleuio_lcd-master» на «stm32_bleuio_lcd».

Подключите SDA к PF0 на плате Nucleo, а SCL к PF1.

Затем настройте I2C2 в файле ioc STM32Cube следующим образом. (Обязательно измените частоту скорости I2C на 50 кГц в соответствии с требованиями ЖК-дисплея.)

В функции USBH_CDC_ReceiveCallback в USB_HOST\usb_host.c мы копируем CDC_RX_Buffer во внешнюю переменную с именем dongle_response, доступную из файла main.c.

void USBH_CDC_ReceiveCallback(USBH_HandleTypeDef *phost)
{
  	if(phost == &hUsbHostFS)
  	{
  		// Handles the data recived from the USB CDC host, here just printing it out to UART
  		rx_size = USBH_CDC_GetLastReceivedDataSize(phost);
		HAL_UART_Transmit(&huart3, CDC_RX_Buffer, rx_size, HAL_MAX_DELAY);
		// Copy buffer to external dongle_response buffer
		strcpy((char *)dongle_response, (char *)CDC_RX_Buffer);
		// Reset buffer and restart the callback function to receive more data
		memset(CDC_RX_Buffer,0,RX_BUFF_SIZE);
		USBH_CDC_Receive(phost, CDC_RX_Buffer, RX_BUFF_SIZE);
  	}
  	return;
}

В main.c мы создаем простой интерпретатор, чтобы мы могли реагировать на данные, которые мы получаем от ключа.

void dongle_interpreter(uint8_t * input)
{
	if(strlen((char *)input) != 0)
	{
		if(strstr((char *)input, "\r\nADVERTISING...") != NULL)
		{
			isAdvertising = true;
		}
		if(strstr((char *)input, "\r\nADVERTISING STOPPED") != NULL)
		{
			isAdvertising = false;
		}
		if(strstr((char *)input, "\r\nCONNECTED") != NULL)
		{
			isConnected = true;
			HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_SET);
		}
		if(strstr((char *)input, "\r\nDISCONNECTED") != NULL)
		{
			isConnected = false;
			HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);
		}

		if(strstr((char *)input, "L=0") != NULL)
		{
			isLightBulbOn = false;
			//HAL_GPIO_WritePin(Lightbulb_GPIO_Port, Lightbulb_Pin, GPIO_PIN_RESET);
			lcd_clear();
			writeToDongle((uint8_t*)DONGLE_SEND_LIGHT_OFF);
			uart_buf_len = sprintf(uart_tx_buf, "\r\nClear screen\r\n");
			HAL_UART_Transmit(&huart3, (uint8_t *)uart_tx_buf, uart_buf_len, HAL_MAX_DELAY);
		}
		if(strstr((char *)input, "L=1") != NULL)
		{
				isLightBulbOn = true;
				writeToDongle((uint8_t*)DONGLE_SEND_LIGHT_ON);

				lcd_clear();
				lcd_write(input);
		}
	}
	memset(&dongle_response, 0, RSP_SIZE);
}

Мы помещаем функцию интерпретатора внутрь основного цикла.

/* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    MX_USB_HOST_Process();
    /* USER CODE BEGIN 3 */
    // Simple handler for uart input
    handleUartInput(uartStatus);
    // Inteprets the dongle data
    dongle_interpreter(dongle_response);
	// Starts advertising as soon as the Dongle is ready.
	if(!isAdvertising && !isConnected && isBleuIOReady)
	{
		HAL_Delay(200);
		writeToDongle((uint8_t*)DONGLE_CMD_AT_ADVSTART);
		isAdvertising = true;
	}
  }
  /* USER CODE END 3 */

Использование примера проекта

Что нам понадобится

Импорт в качестве существующего проекта

В STM32CubeIDE выберите «Файл»> «Импорт…».

Затем выберите «Общие»> «Существующие проекты» в рабочей области, затем нажмите «Далее»

Убедитесь, что вы выбрали свое рабочее пространство в «Выбрать корневой каталог:»

Вы должны увидеть проект «stm32_bleuio_SHT85_example», отметьте его и нажмите «Готово».

Запуск примера

Загрузите код в STM32 и запустите пример. USB-ключ, подключенный к STM32, начнет рекламу автоматически.

Отправка данных датчика на ЖК-экран из веб-браузера

Подключите ключ BleuIO к компьютеру. Запустите веб-скрипт для подключения к другому ключу BleuIO на STM32. Теперь вы можете отправлять данные датчика на ЖК-экран.

Для работы этого скрипта нам нужно

Создайте простой HTML-файл с именем index.html, который будет служить интерфейсом скрипта. Этот HTML-файл содержит несколько кнопок, которые помогают подключаться, считывать рекламные данные с HibouAir для получения данных датчика качества воздуха и отправлять эти данные на ЖК-экран, подключенный к stm32.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
      crossorigin="anonymous"
    />
    <title>Bluetooth LE Air quality sensor data to LCD screen</title>
  </head>
  <body class="mt-5">
    <div class="container mt-5">
      <img
        src="https://www.bleuio.com/blog/wp-content/themes/bleuio/images/logo.png"
      />
      <h1 class="mb-5">Bluetooth LE Air quality sensor data to LCD screen</h1>
      <div class="row">
        <div class="col-md-4 pt-5">
          <button class="btn btn-success mb-2" id="connect">Connect</button>
          <form method="post" id="sendDataForm" name="sendMsgForm" hidden>
            <div class="mb-3">
              <label for="sensorID" class="form-label">Sensor ID</label>
              <input
                type="text"
                class="form-control"
                name="sensorID"
                id="sensorID"
                required
                maxlength="60"
                value="0578E0"
              />
            </div>
            <button type="submit" class="btn btn-primary">Get Data</button>
          </form>
          <br />
          <button class="btn btn-danger" id="clearScreen" disabled>
            Clear screen
          </button>
        </div>
        <div class="col-md-8">
          <img src="air_quality_lcd.jpg" class="img-fluid" />
        </div>
      </div>
    </div>
    <script src="script.js"></script>
  </body>
</html>

Создайте файл js с именем script.js и включите его в конец файла HTML. Этот js-файл использует js-библиотеку BleuIO для написания AT-команд и связи с другим ключом.

import * as my_dongle from 'bleuio'
import 'regenerator-runtime/runtime'
const dongleToConnect='[0]40:48:FD:E5:2F:17'
//const sensorID = '0578E0'
document.getElementById('connect').addEventListener('click', function(){
  my_dongle.at_connect()
  document.getElementById("clearScreen").disabled=false;
  document.getElementById("connect").disabled=true;
  document.getElementById("sendDataForm").hidden=false;
})
document.getElementById("sendDataForm").addEventListener("submit", function(event){
    event.preventDefault()
    const sensorID = document.getElementById('sensorID').value
    getSensorData(sensorID)
    setInterval(function () {getSensorData(sensorID)}, 10000);
   
  });
  const getSensorData =((sensorID)=>{
    my_dongle.ati().then((data)=>{
        //make central if not
        if(JSON.stringify(data).includes("Peripheral")){
            console.log('peripheral')
            my_dongle.at_dual().then((x)=>{
                console.log('central now')
            })
        }        
    })
    .then(()=>{
        // connect to dongle
        my_dongle.at_getconn().then((y)=>{
            if(JSON.stringify(y).includes(dongleToConnect)){
                console.log('already connected')
            }else{
                my_dongle.at_gapconnect(dongleToConnect).then(()=>{
                    console.log('connected successfully')
                })
            }
        })
        .then(async()=>{
           return my_dongle.at_findscandata(sensorID,6).then((sd)=>{
                console.log('scandata',sd)
                let advData = sd[sd.length - 1].split(" ").pop()
                let positionOfID= advData.indexOf(sensorID);
                let tempHex = advData.substring(positionOfID+14, positionOfID+18)
                let temp = parseInt('0x'+tempHex.match(/../g).reverse().join(''))/10;
                let co2Hex = advData.substring(positionOfID+38, positionOfID+42)
                let co2 = parseInt('0x'+co2Hex);
                //console.log(temp,co2)
                return {
                    'CO2' :co2,
                    'Temp' :temp,                       
                  }
            })
        })
        .then((x)=>{
            console.log(x.CO2)
            console.log(x.Temp)
            var theVal = "L=1 SENSOR ID "+sensorID+"    TEMPERATURE " + x.Temp + ' °c    CO2 '+ x.CO2+' ppm';
            console.log('Message Send 1 ')
            // send command to show data
            my_dongle.at_spssend(theVal).then(()=>{
                console.log('Message Send '+theVal)
            })
        })
        
    })
})
document.getElementById('clearScreen').addEventListener('click', function(){
    my_dongle.ati().then((data)=>{
        //make central if not
        if(JSON.stringify(data).includes("Peripheral")){
            console.log('peripheral')
            my_dongle.at_central().then((x)=>{
                console.log('central now')
            })
        }
    })
    .then(()=>{
        // connect to dongle
        my_dongle.at_getconn().then((y)=>{
            if(JSON.stringify(y).includes(dongleToConnect)){
                console.log('already connected')
            }else{
                my_dongle.at_gapconnect(dongleToConnect).then(()=>{
                    console.log('connected successfully')
                })
            }
        })
        .then(()=>{
            // send command to clear the screen
            my_dongle.at_spssend('L=0').then(()=>{
                console.log('Screen Cleared')
            })
        })
        
    })
})

В скрипте есть кнопка для подключения к COM порту на компьютере. Имеется текстовое поле, в котором вы можете написать идентификатор датчика устройства контроля качества воздуха. После подключения скрипт попытается получить объявленные данные от датчика и преобразовать их в значимые данные. После этого он отправит эти данные на плату STM32, которые затем отобразятся на ЖК-экране.

Чтобы подключиться к ключу BleuIO на STM32, убедитесь, что STM32 включен и к нему подключен ключ BleuIO.

Получить MAC-адрес

Следуйте инструкциям, чтобы получить MAC-адрес ключа, подключенного к STM32.

- Open this site https://bleuio.com/web_terminal.html and click connect to dongle.
- Select the appropriate port to connect.
- Once it says connected, type ATI. This will show dongle information and current status.
- If the dongle is on peripheral role, set it to central by typing AT+CENTRAL
- Now do a gap scan by typing AT+GAPSCAN
- Once you see your dongle on the list ,stop the scan by pressing control+c
- Copy the ID and paste it into the script (script.js) line #4

Запустите веб-скрипт

Вам понадобится веб-бандер. Вы можете использовать parcel.js

После установки пакета js перейдите в корневой каталог веб-скрипта и введите "parcel index.html". Это запустит вашу среду разработки.

Откройте скрипт в браузере. Для этого примера мы открыли https://localhost:1234

Вы можете легко подключиться к ключу и увидеть данные о качестве воздуха на ЖК-экране. Ответ отобразится на экране консоли браузера.

Веб-скрипт выглядит так

Выход

Сообщение отобразится на ЖК-экране.