В прошлой статье я пообещал рассказать, о том как подключать CANNY 3 tiny с помощью UART к bluetooth. И поскольку на этих майских особо не разгуляешься, было принято решение провести время с пользой и таки сдержать обещание. Но просто подключить контроллер к Bluetooth адаптеру HC-06, было бы слишком просто для Хабра.
Поэтому мы не просто всё подключим, но еще и напишем для нашей схемы примитивнейшее приложение для Android используя C# и Xamarin.
Если вы любите мониторить «концевики» и герконы, так как это люблю я, милости прошу под кат.

Вот о чем пойдет речь сегодня:
Часть I: Введение
Часть II: Подключение схемы и программа для CANNY 3 tiny
Часть III: Пишем приложение на Xamarin для Android
Часть IV: Заключение
Начну с хорошего, если не считать вставок программного кода на C#, то в этот раз статья будет сравнительно небольшая, потому что основные приемы работы с контроллером мы разобрали раньше. Чтобы не повторяться лишний раз, вот список статей, в которых мы уже разбирали основные приёмы работы с контроллером CANNY:
При подготовке данной статьи я использовал следующее железо: контроллер CANNY 3 tiny, bluetooth адаптер HC-06, концевой выключатель (концевик Trema-модуль), геркон, старые проводные наушники, макетную плату, провода, «крокодилы».
Мы будем собирать систему, которая с помощью мобильного приложения мониторит состояние двух датчиков и при необходимости может в ручном режиме подавать звуковой сигнал.
Все что будет изложено в данной статье, выдумано чисто в учебно-демонстрационных целях. Я просто хотел показать, некоторые приемы работы с контроллером, а также, чтобы купленные в свое время железки хоть как-то отработали свою стоимость.
Несмотря на далекий от реальности характер решаемой задачи мы представим, что делаем систему мониторинга за раздвижной дверью купе. Когда она начнет движение, сработает геркон, а в конце пути даст сигнал концевик. Если нам надо будет привлечь внимание, например, чтобы дверь обратно закрыли, мы подадим «писклявый» сигнал через динамик. Правда динамика у меня нет, но зато есть старые наушники.

Ну и, как всегда, примечание. Настоятельно не рекомендую использовать, материалы данной статьи, как истину в последней инстанции. Многие вещи я сам делал в первый раз, их наверняка можно сделать лучше.
Для начала, чтобы никого в авторских правах не обидеть уточню, что идею подключить контроллер к HC-06, по управлять им через приложение «Serial bluetooth terminal» и некоторые приемы при разработке диаграммы, я позаимствовал с форума, но само собой я их адаптировал под свою задачу.
Схема подключения выглядит следующим образом:

Концевик и геркон подключены к выводам контроллера №6 и №5, наушники к выводу №4 (у него есть ВЧ ШИМ), UART RX – это вывод № 1, UART TX – вывод № 2, вывод № 3 используется для подачи «+5В», вывод «-» — для связи с «землей».
Вот как это выглядит в сборе:

Диаграмму (программу) для СANNY 3 tiny я разрабатывал в CannyLab версии 1.42, возможно в других версиях среды разработки и с другими контроллерами, надо будет внести изменения в диаграмму.
Вот, что получилось:

Блоки связанные с настройкой контроллера и отправкой сообщения по UART, разбирали в прошлой статье.
Разберём подробней два оставшихся.
Блок «Получение сообщения по UART», отвечает за включение сирены(наушников). В принципе он нужен, чтобы разобрать пример, получения сообщения по UART.
Вначале мы проверяем есть ли полученные данные в UART, если есть, то подаем на вход «Е» D-триггера единицу, в таком случае триггер копирует значение со входа “D” в который мы запишем первые два символа из сообщения пришедшего по UART. Я не хотел всё усложнять поэтому дальше мы используем простую схему. Предполагаем, что по UART к нам придет любое число от 00 до 99, переводим это число из символьной формы в числовую (рекомендую почитать как работает блок конвертера у меня с ним был небольшой «затык»). Дальше любое значение ">0" на входе детектора переднего фронта вызывает единичный сигнал, который включит на 5 секунд выход №4, работающий в режиме ВЧ ШИМ.
Вы можете в настройках поиграться с периодом заполнения ВЧ ШИМ, от этого будет зависеть, звук в наушниках.
Перейдем к блоку «Формирование сообщения». Его реализация на первый взгляд может показаться необычной. Объясняется это тем, что я толком не разобрался как работать с программой Serial bluetooth terminal и с аналогичным bluetooth протоколом в Xamarin.
Забегу немножко вперёд и скажу, что я так и не научился гарантированно получать на смартфоне отправленное с контроллера сообщение. Если с проводным UART в прошлой статье все было очевидно, то с Bluetooth на практике вместо отправленного сообщения может прочитаться только его часть и смысл передаваемой команды нарушиться.
Я решил, что самое простое решение — это передавать одно число, которое гарантированно дойдет до адресата без потерь.
В нашем случае мы мониторим дискретное состояние геркона и концевика. То есть у нас всего 4 возможные комбинации: геркон и концевик выключены, включен только кто-то один, оба включены.
Поскольку геркон и концевик дают дискретный сигнал (0/1) нужно как-то их различить. Для этого умножим значение сигнала геркона на 2. Теперь получается, что сумма сигналов даст нам значения от 0 до 3.
Теперь разберем не очевидный вариант с прибавлением к этому значению пятидесяти. Дело в том, что CannyLab передает в UART пару символов, то есть вместо 3 допустим 03, но как я говорил есть риск потери части информации. Например, из значения 01, программа на смартфоне может прочитать только первый «0», а это уже будет ошибка.
Можно было бы заморочиться и преобразовать данные, заменив, например символ «D1» регистра какой-нибудь буквой или пробелом, но я решил сделать проще. Я превратил значение 01 в 51 (02 в 52 и т.д.). Пятерка не несет сигнала и я ее вырезаю на уровне программы для смартфона. Таким образом, у нас всегда гарантированно остается полезная часть сообщения.
Загружаем программу в контроллер, нажимаем «запустить», если все работает как задумано, то HC-06 начнет периодически мигать красным светодиодом.
Далее сопрягаем смартфон с адаптером. Теперь можно проверить работоспособность в приложении «Serial Bluetooth terminal» или любом другом с похожим функционалом.
Запишите адрес Bluetooth адаптера, он нам пригодится в следующей главе.

Как видите данные приходят, в зависимости от состояния датчиков, а если отправить «11», то в наушниках послышится противный писк. Можно было бы на этом и остановиться, но давайте набросаем примитивное приложение.
Программу для контроллера и исходный код программы для смартфона можно скачать с GitHub
Хотелось бы отметить, что вам не обязательно реализовывать всё в железе именно на контроллерах CANNY, вы вполне можете написать программу для Arduino или другого любимого вами контроллера. Изначально я и сам планировал написать дополнительно еще и версию скетча для Arduino, но поскольку убил почти все майские праздники, на подключение CANNY и приложение для смартфона у меня уже просто не осталось сил.
Я знаю, что Xamarin скажем мягко – не самое популярное решение для мобильной разработки. И возможно у вас уже возник вопрос: «Почему я его выбрал?». Ответить на него мне бы хотелось словами из одноименной песни Псоя Короленко:

Честно, нет никаких объективных причин. Просто пару лет назад я учил азы C# и все хотел посмотреть, что такое Xamarin. И теперь из-за «самоизоляции» наконец-то дошли руки.
Ну и еще раз напомню. Я первый раз встречаюсь с Xamarin и это мое первое приложение для Android. Не стоит слепо копировать мой кривой код, если вдруг вы сможете найти более красивое решение, воспользуйтесь им.
При разработке своей программы я опирался на этот материал. Статья не особо разжевана, да еще и на испанском, поэтому я всё-таки счел уместным поделиться с вами своей вариацией на эту тему.
Я собирал программу в Visual studio 2019 community edition.
Первым делом создадим новый пустой проект для Android (Xamarin), как на картинке.

Я вносил изменения только в три файла, целиком их можно просмотреть на GitHub, а тут разберем только важные части:
AndroidManifest.xml
В стандартный шаблон добавлены 2 разрешения:
activity_main.xml
Был убран контейнер по умолчанию (RelativeLayout). Вместо него был добавлен, контейнер LinearLayout просто потому, что он проще. В данном контейнере все элементы выравниваются по вертикали, растягиваясь на всю ширину экрана.
Любому человеку немного знакомому с HTML вёрсткой или XML не составит труда разобраться со структурой пользовательского интерфейса. У нас есть три доступных только для чтения текстовых поля (TextView), один переключатель(Switch), который работает по сути как чекбокс и одна самая обычная кнопка(Button). Элементы можно разместить на форме путем перетаскивания из конструктора, а в окне свойств или в коде задать им более удобные Id, текстовые заглушки и другие параметры.
Осталось описать логику программы.
MainActivity.cs
Ниже под спойлером код целиком для удобства
Теперь по частям.
Блоки с подключением пространств имен, объявлением класса и т.п. я пропущу.
Создаём переменные с которыми позже свяжем элементы пользовательского интерфейса:
Дальше идет, код из примера на который я опирался. Переменные (поля) необходимые для работы тех или иных методов.
Нам здесь важно вбить адрес вашего модуля HC-06 в поле address.
Поскольку это мой первый опыт разработки для смартфонов в том числе работы с Xamarin, я решил ничего не усложнять поэтому адрес устройства, как и в исходном примере, жестко зафиксирован. Если хотите вы можете посмотреть эту статью, там похоже реализован перебор доступных Bluetooth устройств.
Идем дальше.
Этот метод создается автоматически, наша задача связать в нем объекты UI с полями класса, а также привязать обработчики для реакций на события (
Проверяем подключение по Bluetooth:
Включение сирены. По сути просто отправка символов «11» в контроллер:
Проверка состояния переключателя (если смещен в право значит включён):
При включении начинаем соединение с Bluetooth.
Реализация непосредственно подключения:
А вот один из самых важных методов — непосредственно считывание данных:
Многие элементы метода я оставил как в примере, как я понимаю вначале создается соединение с потоком данных, если в буфере с прочитанными данными, что-то есть то оно считывается в переменную
Дальше мы убираем из считанного сообщения все символы кроме цифр
Ну а после уже все просто в зависимости от того какое число нам пришло, выводим тот или иной статус в UI.
Эти два метода я кардинально не менял:
Как я понимаю в данном блоке кода реализованы отправка сообщения и обработчик события на зарос разрешения доступа к Bluetooth.
Ну вот и все осталось настроить подключение смартфона для отладки приложения.
Как ни странно, но все работает:

Вот как работа программы выглядит в натуре:

Вот таким был мой первый опыт разработки приложений для смартфона на Android.
Хотелось бы отметить, что VS 2019 и Xamarin на моем стареньком компьютере работают очень медленно.
При первой сборке проекта, я реально успел съесть еще этих мягких французских булок да выпить чаю. Также само приложение получилось откровенно убогое, переключатель «вкл/выкл» работает очень не удобно, но с другой стороны учитывая, что я лишь немного знаком с базовыми приёмами разработки для .NET, а также то, что я вообще не программист, я смог даже не проходя целиком ни одного туториала и ни одного урока, за день набросать свое первое приложение. Поэтому порог вхождения для создания элементарного приложения получается достаточно низкий.
Я так понимаю, контроллеры CANNY можно использовать при тюнинге автомобилей, особенно отечественных так, что вполне можно сделать какую-нибудь «фичу» и написать к ней приложение для смартфона. Главное помнить, что при питании от бортовой сети автомобиля на выходах контроллера будет тоже напряжение что и на входе (например, 12 В вместо 5 В). Не забудьте защитить Bluetooth адаптер, чтобы он ненароком не вышел из строя.
Статья оказалась для меня очень трудоёмкой, надеюсь, что все было не зря и она вам понравиться.
Поэтому мы не просто всё подключим, но еще и напишем для нашей схемы примитивнейшее приложение для Android используя C# и Xamarin.
Если вы любите мониторить «концевики» и герконы, так как это люблю я, милости прошу под кат.

Вот о чем пойдет речь сегодня:
Часть I: Введение
Часть II: Подключение схемы и программа для CANNY 3 tiny
Часть III: Пишем приложение на Xamarin для Android
Часть IV: Заключение
Часть I: Введение
Начну с хорошего, если не считать вставок программного кода на C#, то в этот раз статья будет сравнительно небольшая, потому что основные приемы работы с контроллером мы разобрали раньше. Чтобы не повторяться лишний раз, вот список статей, в которых мы уже разбирали основные приёмы работы с контроллером CANNY:
- «Раз, два, три – ёлочка гори!» или мой первый взгляд на контроллер CANNY 3 tiny — в этой статье мы разбирали что из себя представляет контроллер, а также азы работы в среде разработки CannyLab.
- «У Предназначения масса обличий...» или автоматизируем управление автолампой с помощью CANNY 3 tiny и фоторезистора — в этой статье мы разбирали работу с USB Virtual COM-port, подключение датчиков к АЦП, а также высокочастотный ШИМ на выходах контроллера.
- «Как зеницу ока...» или делаем простенькую охранную систему на базе микроконтроллера (CANNY или Arduino) и Raspberry PI — в этой статье мы разбирали работу с UART, а также повторили ранее пройденное.
При подготовке данной статьи я использовал следующее железо: контроллер CANNY 3 tiny, bluetooth адаптер HC-06, концевой выключатель (концевик Trema-модуль), геркон, старые проводные наушники, макетную плату, провода, «крокодилы».
Мы будем собирать систему, которая с помощью мобильного приложения мониторит состояние двух датчиков и при необходимости может в ручном режиме подавать звуковой сигнал.
Все что будет изложено в данной статье, выдумано чисто в учебно-демонстрационных целях. Я просто хотел показать, некоторые приемы работы с контроллером, а также, чтобы купленные в свое время железки хоть как-то отработали свою стоимость.
Несмотря на далекий от реальности характер решаемой задачи мы представим, что делаем систему мониторинга за раздвижной дверью купе. Когда она начнет движение, сработает геркон, а в конце пути даст сигнал концевик. Если нам надо будет привлечь внимание, например, чтобы дверь обратно закрыли, мы подадим «писклявый» сигнал через динамик. Правда динамика у меня нет, но зато есть старые наушники.

Ну и, как всегда, примечание. Настоятельно не рекомендую использовать, материалы данной статьи, как истину в последней инстанции. Многие вещи я сам делал в первый раз, их наверняка можно сделать лучше.
Часть II: Подключение схемы и программа для CANNY 3 tiny
Для начала, чтобы никого в авторских правах не обидеть уточню, что идею подключить контроллер к HC-06, по управлять им через приложение «Serial bluetooth terminal» и некоторые приемы при разработке диаграммы, я позаимствовал с форума, но само собой я их адаптировал под свою задачу.
Схема подключения выглядит следующим образом:

Концевик и геркон подключены к выводам контроллера №6 и №5, наушники к выводу №4 (у него есть ВЧ ШИМ), UART RX – это вывод № 1, UART TX – вывод № 2, вывод № 3 используется для подачи «+5В», вывод «-» — для связи с «землей».
Вот как это выглядит в сборе:

Диаграмму (программу) для СANNY 3 tiny я разрабатывал в CannyLab версии 1.42, возможно в других версиях среды разработки и с другими контроллерами, надо будет внести изменения в диаграмму.
Вот, что получилось:

Блоки связанные с настройкой контроллера и отправкой сообщения по UART, разбирали в прошлой статье.
Разберём подробней два оставшихся.
Блок «Получение сообщения по UART», отвечает за включение сирены(наушников). В принципе он нужен, чтобы разобрать пример, получения сообщения по UART.
Вначале мы проверяем есть ли полученные данные в UART, если есть, то подаем на вход «Е» D-триггера единицу, в таком случае триггер копирует значение со входа “D” в который мы запишем первые два символа из сообщения пришедшего по UART. Я не хотел всё усложнять поэтому дальше мы используем простую схему. Предполагаем, что по UART к нам придет любое число от 00 до 99, переводим это число из символьной формы в числовую (рекомендую почитать как работает блок конвертера у меня с ним был небольшой «затык»). Дальше любое значение ">0" на входе детектора переднего фронта вызывает единичный сигнал, который включит на 5 секунд выход №4, работающий в режиме ВЧ ШИМ.
Вы можете в настройках поиграться с периодом заполнения ВЧ ШИМ, от этого будет зависеть, звук в наушниках.
Перейдем к блоку «Формирование сообщения». Его реализация на первый взгляд может показаться необычной. Объясняется это тем, что я толком не разобрался как работать с программой Serial bluetooth terminal и с аналогичным bluetooth протоколом в Xamarin.
Забегу немножко вперёд и скажу, что я так и не научился гарантированно получать на смартфоне отправленное с контроллера сообщение. Если с проводным UART в прошлой статье все было очевидно, то с Bluetooth на практике вместо отправленного сообщения может прочитаться только его часть и смысл передаваемой команды нарушиться.
Я решил, что самое простое решение — это передавать одно число, которое гарантированно дойдет до адресата без потерь.
В нашем случае мы мониторим дискретное состояние геркона и концевика. То есть у нас всего 4 возможные комбинации: геркон и концевик выключены, включен только кто-то один, оба включены.
Поскольку геркон и концевик дают дискретный сигнал (0/1) нужно как-то их различить. Для этого умножим значение сигнала геркона на 2. Теперь получается, что сумма сигналов даст нам значения от 0 до 3.
Теперь разберем не очевидный вариант с прибавлением к этому значению пятидесяти. Дело в том, что CannyLab передает в UART пару символов, то есть вместо 3 допустим 03, но как я говорил есть риск потери части информации. Например, из значения 01, программа на смартфоне может прочитать только первый «0», а это уже будет ошибка.
Можно было бы заморочиться и преобразовать данные, заменив, например символ «D1» регистра какой-нибудь буквой или пробелом, но я решил сделать проще. Я превратил значение 01 в 51 (02 в 52 и т.д.). Пятерка не несет сигнала и я ее вырезаю на уровне программы для смартфона. Таким образом, у нас всегда гарантированно остается полезная часть сообщения.
Загружаем программу в контроллер, нажимаем «запустить», если все работает как задумано, то HC-06 начнет периодически мигать красным светодиодом.
Далее сопрягаем смартфон с адаптером. Теперь можно проверить работоспособность в приложении «Serial Bluetooth terminal» или любом другом с похожим функционалом.
Запишите адрес Bluetooth адаптера, он нам пригодится в следующей главе.

Как видите данные приходят, в зависимости от состояния датчиков, а если отправить «11», то в наушниках послышится противный писк. Можно было бы на этом и остановиться, но давайте набросаем примитивное приложение.
Программу для контроллера и исходный код программы для смартфона можно скачать с GitHub
Хотелось бы отметить, что вам не обязательно реализовывать всё в железе именно на контроллерах CANNY, вы вполне можете написать программу для Arduino или другого любимого вами контроллера. Изначально я и сам планировал написать дополнительно еще и версию скетча для Arduino, но поскольку убил почти все майские праздники, на подключение CANNY и приложение для смартфона у меня уже просто не осталось сил.
Часть III: Пишем приложение на Xamarin для Android
Я знаю, что Xamarin скажем мягко – не самое популярное решение для мобильной разработки. И возможно у вас уже возник вопрос: «Почему я его выбрал?». Ответить на него мне бы хотелось словами из одноименной песни Псоя Короленко:

Честно, нет никаких объективных причин. Просто пару лет назад я учил азы C# и все хотел посмотреть, что такое Xamarin. И теперь из-за «самоизоляции» наконец-то дошли руки.
Ну и еще раз напомню. Я первый раз встречаюсь с Xamarin и это мое первое приложение для Android. Не стоит слепо копировать мой кривой код, если вдруг вы сможете найти более красивое решение, воспользуйтесь им.
При разработке своей программы я опирался на этот материал. Статья не особо разжевана, да еще и на испанском, поэтому я всё-таки счел уместным поделиться с вами своей вариацией на эту тему.
Я собирал программу в Visual studio 2019 community edition.
Первым делом создадим новый пустой проект для Android (Xamarin), как на картинке.

Я вносил изменения только в три файла, целиком их можно просмотреть на GitHub, а тут разберем только важные части:
AndroidManifest.xml
В стандартный шаблон добавлены 2 разрешения:
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
activity_main.xml
Был убран контейнер по умолчанию (RelativeLayout). Вместо него был добавлен, контейнер LinearLayout просто потому, что он проще. В данном контейнере все элементы выравниваются по вертикали, растягиваясь на всю ширину экрана.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:minWidth="25px" android:minHeight="25px" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/linearLayout1"> <TextView android:text="Reed switch status - undefined" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/rSwitch" android:textSize="12pt" /> <TextView android:text="End sensor status - undefined" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/EndSensor" android:textSize="12pt" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/startSiren" android:text="Send signal to siren" /> <Switch android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/bltSwitch" android:checked="false" android:showText="true" android:text="Connect bluetooth" /> <TextView android:text="status" android:textAppearance="?android:attr/textAppearanceSmall" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/status" /> </LinearLayout>
Любому человеку немного знакомому с HTML вёрсткой или XML не составит труда разобраться со структурой пользовательского интерфейса. У нас есть три доступных только для чтения текстовых поля (TextView), один переключатель(Switch), который работает по сути как чекбокс и одна самая обычная кнопка(Button). Элементы можно разместить на форме путем перетаскивания из конструктора, а в окне свойств или в коде задать им более удобные Id, текстовые заглушки и другие параметры.
Осталось описать логику программы.
MainActivity.cs
Ниже под спойлером код целиком для удобства
Полный код MainActivity.cs
// based on http://alejandroruizvarela.blogspot.com/2014/01/bluetooth-arduino-xamarinandroid.html // for this article https://habr.com/ru/post/500454/ // based on http://alejandroruizvarela.blogspot.com/2014/01/bluetooth-arduino-xamarinandroid.html // for this article https://habr.com/ru/post/500454/ using Android.App; using Android.OS; using Android.Support.V7.App; using Android.Runtime; using Android.Widget; using System.Linq; using System; using System.IO; using Java.Util; using Android.Bluetooth; using System.Threading.Tasks; namespace _6.Canny_Xanarin_Bluetooth_Android { [Activity(Label = "Control Canny 3 tiny via bluetooth", Theme = "@style/AppTheme", MainLauncher = true)] public class MainActivity : AppCompatActivity { Button startSiren; TextView rSwitch; TextView EndSensor; Switch bltSwitch; TextView status; private Java.Lang.String dataToSend; private BluetoothAdapter mBluetoothAdapter = null; private BluetoothSocket btSocket = null; private Stream outStream = null; // don't forget change addres to your device: private static string address = "98:D3:91:F9:6C:F6"; // MY_UUID can be saved as is private static UUID MY_UUID = UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"); private Stream inStream = null; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); // Set our view from the "main" layout resource SetContentView(Resource.Layout.activity_main); startSiren = FindViewById<Button>(Resource.Id.startSiren); rSwitch = FindViewById<TextView>(Resource.Id.rSwitch); EndSensor = FindViewById<TextView>(Resource.Id.EndSensor); status = FindViewById<TextView>(Resource.Id.status); bltSwitch = FindViewById<Switch>(Resource.Id.bltSwitch); startSiren.Click += startSiren_ClickOnButtonClicked; bltSwitch.CheckedChange += bltSwitch_HandleCheckedChange; CheckBt(); } private void CheckBt() { mBluetoothAdapter = BluetoothAdapter.DefaultAdapter; if (!mBluetoothAdapter.Enable()) { Toast.MakeText(this, "Bluetooth Off", ToastLength.Short).Show(); } if (mBluetoothAdapter == null) { Toast.MakeText(this, "Bluetooth does not exist or is busy", ToastLength.Short) .Show(); } } void startSiren_ClickOnButtonClicked(object sender, EventArgs e) { if (bltSwitch.Checked) { try { dataToSend = new Java.Lang.String("11"); writeData(dataToSend); System.Console.WriteLine("Send signal to siren"); } catch (System.Exception execept) { System.Console.WriteLine("Error when send data" + execept.Message); } } else status.Text = "bluetooth not connected"; } void bltSwitch_HandleCheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e) { if (e.IsChecked) { Connect(); } else { status.Text = "bluetooth not connected"; if (btSocket.IsConnected) { try { btSocket.Close(); System.Console.WriteLine("Connection closed"); } catch (System.Exception ex) { Console.WriteLine(ex.Message); } } } } public void Connect() { BluetoothDevice device = mBluetoothAdapter.GetRemoteDevice(address); System.Console.WriteLine("Connection in progress" + device); mBluetoothAdapter.CancelDiscovery(); try { btSocket = device.CreateRfcommSocketToServiceRecord(MY_UUID); btSocket.Connect(); System.Console.WriteLine("Correct Connection"); status.Text = "Correct Connection to bluetooth"; } catch (System.Exception e) { Console.WriteLine(e.Message); try { btSocket.Close(); System.Console.WriteLine("Connection closed"); } catch (System.Exception) { System.Console.WriteLine("Impossible to connect"); status.Text = "Impossible to connect"; } System.Console.WriteLine("Socket Created"); } beginListenForData(); } public void beginListenForData() { try { inStream = btSocket.InputStream; } catch (System.IO.IOException ex) { Console.WriteLine(ex.Message); } Task.Factory.StartNew(() => { byte[] buffer = new byte[1024]; int bytes; while (true) { try { bytes = inStream.Read(buffer, 0, 1024); System.Console.WriteLine("bytes " + bytes.ToString()); if (bytes > 0) { RunOnUiThread(() => { string valor = System.Text.Encoding.ASCII.GetString(buffer).Replace("5",String.Empty); // transform string for deleate all symbols except 1-4(command from canny). string command = new string(valor.Where(char.IsDigit).ToArray()); if (command.Length > 0) { status.Text="data successfully readed"; System.Console.WriteLine("command " + command); switch (Int32.Parse(command)) { case 0: rSwitch.Text = "reed switch - disconnected "; EndSensor.Text = "end sensor - not pressed "; break; case 1: rSwitch.Text = "reed switch - disconnected "; EndSensor.Text = "end sensor - pressed "; break; case 2: rSwitch.Text = "reed switch - connected "; EndSensor.Text = "end sensor - not pressed "; break; case 3: rSwitch.Text = "reed switch - connected "; EndSensor.Text = "end sensor - pressed "; break; } } }); } } catch (Java.IO.IOException) { RunOnUiThread(() => { EndSensor.Text = "End sensor status - undefined"; rSwitch.Text = "Reed switch status - undefined "; }); break; } } }); } private void writeData(Java.Lang.String data) { try { outStream = btSocket.OutputStream; } catch (System.Exception e) { System.Console.WriteLine("Error with OutputStream when write to Serial port" + e.Message); } Java.Lang.String message = data; byte[] msgBuffer = message.GetBytes(); try { outStream.Write(msgBuffer, 0, msgBuffer.Length); System.Console.WriteLine("Message sent"); } catch (System.Exception e) { System.Console.WriteLine("Error with when write message to Serial port" + e.Message); status.Text = "Error with when write message to Serial port"; } } public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) { Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults); base.OnRequestPermissionsResult(requestCode, permissions, grantResults); } } }
Теперь по частям.
Блоки с подключением пространств имен, объявлением класса и т.п. я пропущу.
Создаём переменные с которыми позже свяжем элементы пользовательского интерфейса:
Button startSiren; TextView rSwitch; TextView EndSensor; Switch bltSwitch; TextView status;
Дальше идет, код из примера на который я опирался. Переменные (поля) необходимые для работы тех или иных методов.
private Java.Lang.String dataToSend; private BluetoothAdapter mBluetoothAdapter = null; private BluetoothSocket btSocket = null; private Stream outStream = null; // don't forget change addres to your device: private static string address = "98:D3:91:F9:6C:F6"; // MY_UUID can be saved as is private static UUID MY_UUID = UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"); private Stream inStream = null;
Нам здесь важно вбить адрес вашего модуля HC-06 в поле address.
Поскольку это мой первый опыт разработки для смартфонов в том числе работы с Xamarin, я решил ничего не усложнять поэтому адрес устройства, как и в исходном примере, жестко зафиксирован. Если хотите вы можете посмотреть эту статью, там похоже реализован перебор доступных Bluetooth устройств.
Идем дальше.
protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); // Set our view from the "main" layout resource SetContentView(Resource.Layout.activity_main); startSiren = FindViewById<Button>(Resource.Id.startSiren); rSwitch = FindViewById<TextView>(Resource.Id.rSwitch); EndSensor = FindViewById<TextView>(Resource.Id.EndSensor); status = FindViewById<TextView>(Resource.Id.status); bltSwitch = FindViewById<Switch>(Resource.Id.bltSwitch); startSiren.Click += startSiren_ClickOnButtonClicked; bltSwitch.CheckedChange += bltSwitch_HandleCheckedChange; CheckBt(); }
Этот метод создается автоматически, наша задача связать в нем объекты UI с полями класса, а также привязать обработчики для реакций на события (
startSiren.Click и bltSwitch.CheckedChange).Проверяем подключение по Bluetooth:
private void CheckBt() { mBluetoothAdapter = BluetoothAdapter.DefaultAdapter; if (!mBluetoothAdapter.Enable()) { Toast.MakeText(this, "Bluetooth Off", ToastLength.Short).Show(); } if (mBluetoothAdapter == null) { Toast.MakeText(this, "Bluetooth does not exist or is busy", ToastLength.Short) .Show(); } }
Включение сирены. По сути просто отправка символов «11» в контроллер:
void startSiren_ClickOnButtonClicked(object sender, EventArgs e) { if (bltSwitch.Checked) { try { dataToSend = new Java.Lang.String("11"); writeData(dataToSend); System.Console.WriteLine("Send signal to siren"); } catch (System.Exception execept) { System.Console.WriteLine("Error when send data" + execept.Message); } } else status.Text = "bluetooth not connected"; }
Проверка состояния переключателя (если смещен в право значит включён):
void bltSwitch_HandleCheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e) { if (e.IsChecked) { Connect(); } else { if (btSocket.IsConnected) { try { btSocket.Close(); System.Console.WriteLine("Connection closed"); } catch (System.Exception ex) { Console.WriteLine(ex.Message); } } } }
При включении начинаем соединение с Bluetooth.
Реализация непосредственно подключения:
public void Connect() { BluetoothDevice device = mBluetoothAdapter.GetRemoteDevice(address); System.Console.WriteLine("Connection in progress" + device); mBluetoothAdapter.CancelDiscovery(); try { btSocket = device.CreateRfcommSocketToServiceRecord(MY_UUID); btSocket.Connect(); System.Console.WriteLine("Correct Connection"); status.Text = "Correct Connection to bluetooth"; } catch (System.Exception e) { Console.WriteLine(e.Message); try { btSocket.Close(); System.Console.WriteLine("Connection closed"); } catch (System.Exception) { System.Console.WriteLine("Impossible to connect"); status.Text = "Impossible to connect"; } System.Console.WriteLine("Socket Created"); } beginListenForData(); }
А вот один из самых важных методов — непосредственно считывание данных:
public void beginListenForData() { try { inStream = btSocket.InputStream; } catch (System.IO.IOException ex) { Console.WriteLine(ex.Message); } Task.Factory.StartNew(() => { byte[] buffer = new byte[1024]; int bytes; while (true) { try { bytes = inStream.Read(buffer, 0, 1024); System.Console.WriteLine("bytes " + bytes.ToString()); if (bytes > 0) { RunOnUiThread(() => { string valor = System.Text.Encoding.ASCII.GetString(buffer).Replace("5",String.Empty); // transform string for deleate all symbols except 1-4(command from canny). string command = new string(valor.Where(char.IsDigit).ToArray()); if (command.Length > 0) { status.Text="data successfully readed"; System.Console.WriteLine("command " + command); switch (Int32.Parse(command)) { case 0: rSwitch.Text = "reed switch - disconnected "; EndSensor.Text = "end sensor - not pressed "; break; case 1: rSwitch.Text = "reed switch - disconnected "; EndSensor.Text = "end sensor - pressed "; break; case 2: rSwitch.Text = "reed switch - connected "; EndSensor.Text = "end sensor - not pressed "; break; case 3: rSwitch.Text = "reed switch - connected "; EndSensor.Text = "end sensor - pressed "; break; } } }); } } catch (Java.IO.IOException) { RunOnUiThread(() => { EndSensor.Text = "End sensor status - undefined"; rSwitch.Text = "Reed switch status - undefined "; }); break; } } }); }
Многие элементы метода я оставил как в примере, как я понимаю вначале создается соединение с потоком данных, если в буфере с прочитанными данными, что-то есть то оно считывается в переменную
valor. При этом как я и обещал, цифру «5» мы просто удалим.Дальше мы убираем из считанного сообщения все символы кроме цифр
string command = new string(valor.Where(char.IsDigit).ToArray());Ну а после уже все просто в зависимости от того какое число нам пришло, выводим тот или иной статус в UI.
Эти два метода я кардинально не менял:
private void writeData(Java.Lang.String data) { try { outStream = btSocket.OutputStream; } catch (System.Exception e) { System.Console.WriteLine("Error with OutputStream when write to Serial port" + e.Message); } Java.Lang.String message = data; byte[] msgBuffer = message.GetBytes(); try { outStream.Write(msgBuffer, 0, msgBuffer.Length); System.Console.WriteLine("Message sent"); } catch (System.Exception e) { System.Console.WriteLine("Error with when write message to Serial port" + e.Message); status.Text = "Error with when write message to Serial port"; } } public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) { Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults); base.OnRequestPermissionsResult(requestCode, permissions, grantResults); } } }
Как я понимаю в данном блоке кода реализованы отправка сообщения и обработчик события на зарос разрешения доступа к Bluetooth.
Ну вот и все осталось настроить подключение смартфона для отладки приложения.
Как ни странно, но все работает:

Часть IV: Заключение
Вот как работа программы выглядит в натуре:

Вот таким был мой первый опыт разработки приложений для смартфона на Android.
Хотелось бы отметить, что VS 2019 и Xamarin на моем стареньком компьютере работают очень медленно.
При первой сборке проекта, я реально успел съесть еще этих мягких французских булок да выпить чаю. Также само приложение получилось откровенно убогое, переключатель «вкл/выкл» работает очень не удобно, но с другой стороны учитывая, что я лишь немного знаком с базовыми приёмами разработки для .NET, а также то, что я вообще не программист, я смог даже не проходя целиком ни одного туториала и ни одного урока, за день набросать свое первое приложение. Поэтому порог вхождения для создания элементарного приложения получается достаточно низкий.
Я так понимаю, контроллеры CANNY можно использовать при тюнинге автомобилей, особенно отечественных так, что вполне можно сделать какую-нибудь «фичу» и написать к ней приложение для смартфона. Главное помнить, что при питании от бортовой сети автомобиля на выходах контроллера будет тоже напряжение что и на входе (например, 12 В вместо 5 В). Не забудьте защитить Bluetooth адаптер, чтобы он ненароком не вышел из строя.
Статья оказалась для меня очень трудоёмкой, надеюсь, что все было не зря и она вам понравиться.
