У счастливых обладателей Флиппера, прекрасного тамагочи для больших мальчиков и девочек, часто возникает вопрос: что бы ещё к нему подключить? Сегодня расскажу про подключение модуля под названием RadSens для замера радиации. 

Сразу скажу, что если тема измерения радиации для вас интересна, но нет Флиппера, то можно собрать дозиметр в домашних условиях. Для этого предлагаю обратиться к другой статье про использование этого же модуля.

А тем, кто совсем не хочет ничего собирать, но хочет иметь классный попискивающий гаджет, рекомендую прочитать вот эту статью, я начал именно с этого устройства, но потом у меня появился Флиппер.

Что такое RadSens?

RadSens — универсальный дозиметр-радиометр. В качестве чувствительного элемента в модуле используется газоразрядный счётчик Гейгера-Мюллера СБМ-20, применяемый в бытовых и профессиональных дозиметрах. Сделаем небольшое отступление и вспомним, как работают трубки Гейгера-Мюллера и что, собственно, мы ими измеряем.

Чуть-чуть теории о трубках Гейгера-Мюллера

Газоразрядный счётчик Гейгера-Мюллера, как правило, выполняется в виде герметичной трубки, стеклянной или металлической, в которой вместо воздуха находится инертный газ (неон или аргон, или их смесь) под небольшим давлением, с примесью галогенов или спирта. По оси трубки натянута тонкая проволока, а коаксиально с ней расположен металлический цилиндр. И трубка, и проволока являются электродами: трубка — катод, а проволока — анод. 

Когда сквозь трубку пролетает ионизирующая частица, атомы инертного газа, и так находящиеся в электрическом поле большой напряжённости, сталкиваются с этой частицей. Энергии, отданной ею при столкновении, хватает для отрыва электронов от атомов газа. Образующиеся вторичные электроны сами способны образовать новые столкновения, и, таким образом, получается целая лавина электронов и ионов. Под действием электрического поля электроны ускоряются в направлении анода, а положительно заряженные ионы газа — в направлении катода. Возникает электрический ток. В результате падает сопротивление трубки, а вместе с ним и напряжение. Таким образом, мы получаем отрицательный импульс напряжения. Считая импульсы, мы можем оценить число пролетевших частиц. Очень детально весь процесс, история и виды трубок описаны тут

А для желающих глубже погрузиться в тему опасности радиации, а также связанных с ней мифов, есть подробная статья

RadSens

Но вернёмся к RadSens: устройство представляет собой готовый «ардуино-совместимый» модуль. Его «ардуино-совместимость» заключается в том, что вся сложная электрическая обвязка от нас, как пользователей модуля, скрыта, а подсчёт импульсов и вычисление интенсивности излучения делается во встроенном микроконтроллере. Именно с ним нам предстоит общаться по шине i2c.

Фото с сайта производителя модуля: https://climateguard.info/radsens/
Фото с сайта производителя модуля: https://climateguard.info/radsens/

Трубки СБМ-20 производства СФ АО «НИИТФА» соответствуют техническим условиям ТУ-бла-бла-бла… Так как мы с вами не планируем делать сертифицированный дозиметр, то эти самые технические условия нас не интересуют. Нам важно, что трубки стандартизированные и выдают известное количество импульсов на 1 микрорентген, а именно 105 (100-110, если точнее). Это важно, потому что все, кому я показывал модуль, первым делом спрашивали, как я буду его калибровать. Ответ: никак, он откалиброван производителем.

Встроенный контроллер считает импульсы и вычисляет два величины: динамическое и статистическое значение интенсивности излучения. Да, я не ошибся, не статическое, а статистическое значение. Оно измеряется скользящим окном в 500 секунд. Количество импульсов в этом окне переводится через калибровочное значение (те самые 105) в значение интенсивности излучения в микрорентгенах. 

Динамическая величина вычисляется похожим образом, но чуть хитрее: когда контроллер замечает рост скорости импульсов, он уменьшает окно, чтобы показывать наиболее точное значение «мгновенного» излучения. Когда скорость спадает, окно увеличивается, но не более чем до 123 секунд.

Обе эти величины можно запросить в режиме только чтения у контроллера по определённым регистрам (см. документацию).

Также встроенный контроллер считает импульсы и кладёт их количество в кумулятивный регистр. При считывании этого регистра его значение обнуляется, то есть если модуль находится в активном состоянии (подключен к питанию), он считает суммарное облучение. Этим мы воспользуемся в приложении: будем при запуске считывать количество накопленных модулем импульсов и добавлять к значению с прошлого запуска приложения. Таким образом, даже если приложение не запущено на Флиппере, мы не потеряем ни одного импульса.

Есть ещё несколько регистров, но они нам неинтересны. Разве что регистр калибровки, из которого можно прочитать те самые 105, чтобы пересчитать накопленные импульсы в микрорентгены. Этот регистр, кстати, является R/W-регистром, то есть в него можно записать данные, на случай, если вы замените в RadSens штатную трубку на трубку с другой чувствительностью.

Больше информации о модуле в его официальном репозитории.

Подключаем RadSens к Flipper

С теорией разобрались, переходим к подключению. Как уже было сказано, общение с модулем идёт по шине i2c, а значит нам нужно четыре провода:

  • питание (3,3 В);

  • земля;

  • SCD;

  • SCA.

Все необходимые пины есть на правой колодке корпуса Флиппера. Схема подключения выглядит следующим образом:

В жизни всё не так красиво, как на схеме, у меня получилось вот что:

Уже сейчас модуль можно подключить к Флипперу и посмотреть, как периодически на нём вспыхивает синий светодиод — он сигнализирует о зарегистрированном импульсе. Кстати, индикации питания на модуле нет.

Небольшое отступление: на плате распаян разъём на четыре пина. Но если приглядеться, то в районе разъёма можно увидеть пятое отверстие. Это пин прерывания. Он на короткое время притягивается контроллером модуля к земле сразу после регистрации импульса. Его можно использовать в приложении, например для светодиодной индикации или короткой вибрации, но я не стал его распаивать, а решил эту задачу программным путём, о чём расскажу дальше. 

Делаем корпус

После сборки 3д-принтера меня хлебом не корми, дай для чего-нибудь корпус распечатать. Поэтому после распайки и первичной проверки работы модуля я решил упаковать его в корпус, чтобы можно было таскать в кармане, не боясь оторвать проводочки. Модель нарисовал во Fusion 360, моём любимом редакторе. Готовые STL-файлы можно найти здесь. Фотографии модуля в корпусе:

Да, длинноват получился, можно сделать на модуле RadSens под трубки СБМ-20-1, они немного короче. Или можно поискать детекторы радиации ещё меньше, но помните, что точность детектирования активных частиц напрямую пропорциональна объёмным размерам детектора (проще говоря, можно взять даже маленький сцинтилляционный детектор размером в 1 кубический сантиметр, но большинство частиц пролетит мимо).

На этом хватит аппаратной части, перейдём к программной.

Приложение

Для самых нетерпеливых:

  • Репозиторий тут.

  • Если не знаете, как программировать под Флиппер и собирать fap’ки, то прочитайте эту статью

Для тех, кто любит подробности, продолжу. 

Пока для Флиппера нет официальной документации по методам API. Самый верный способ понять, как писать приложения — изучать исходники встроенных программ, особенно из папки applications/debug, они максимально простые и там есть работа практически со всей периферией. 

В частности, хороший (единственный?) пример работы с i2c лежит в папке с… юнит-тестами. В нём находим интересующие нас методы:

  • furi_hal_i2c_acquire

  • furi_hal_i2c_release

  • furi_hal_i2c_tx

  • furi_hal_i2c_rx

Там же есть примеры вызовов. Я не буду вдаваться в архитектуру приложений для Флиппера, в работу с экраном и кнопками. Это можно прочитать в других статьях, приведу только кусочек кода, который считывает данные с модуля:

Код
bool rad_sens_read_data(RadSensModel* model) {
   furi_hal_i2c_acquire(I2C_BUS);


   uint32_t timeout = furi_ms_to_ticks(100);
   model->connected = false;
   model->verified = false;


   if(furi_hal_i2c_is_device_ready(I2C_BUS, RAD_SENS_ADDRESS, timeout) > 0) {
       model->connected = true;


       uint8_t buffer[4];
       uint8_t device_id = 0;


       buffer[0] = RAD_SENS_ID_RG;
       if(furi_hal_i2c_tx(I2C_BUS, RAD_SENS_ADDRESS, buffer, 1, timeout)) {
           if(furi_hal_i2c_rx(I2C_BUS, (uint8_t)RAD_SENS_ADDRESS, buffer, 1, timeout)) {
               device_id = buffer[0];
           }
       }


       if(device_id == RAD_SENS_ID) {
           model->verified = true;


           // Read dynamic intensity
           buffer[0] = RAD_SENS_DYN_INTENSITY_RG;
           if(furi_hal_i2c_tx(I2C_BUS, RAD_SENS_ADDRESS, buffer, 1, timeout)) {
               if(furi_hal_i2c_rx(I2C_BUS, (uint8_t)RAD_SENS_ADDRESS, buffer, 3, timeout)) {
                   model->dyn_intensity =
                       (((uint32_t)buffer[0] << 16) | ((uint32_t)buffer[1] << 8) |
                        (uint32_t)buffer[2]);
               }
           }


           // Read static intensity
           buffer[0] = RAD_SENS_STAT_INTENSITY_RG;
           if(furi_hal_i2c_tx(I2C_BUS, RAD_SENS_ADDRESS, buffer, 1, timeout)) {
               if(furi_hal_i2c_rx(I2C_BUS, (uint8_t)RAD_SENS_ADDRESS, buffer, 3, timeout)) {
                   model->stat_intensity =
                       (((uint32_t)buffer[0] << 16) | ((uint32_t)buffer[1] << 8) |
                        (uint32_t)buffer[2]);
               }
           }


           // Read impulses
           buffer[0] = RAD_SENS_IMP_CNT_RG;
           if(furi_hal_i2c_tx(I2C_BUS, RAD_SENS_ADDRESS, buffer, 1, timeout)) {
               if(furi_hal_i2c_rx(I2C_BUS, (uint8_t)RAD_SENS_ADDRESS, buffer, 2, timeout)) {
                   model->new_impulse_count = (((uint16_t)buffer[0] << 8) | (uint16_t)buffer[1]);
                   model->impulse_count += model->new_impulse_count;
               }
           }
       }
   }


   furi_hal_i2c_release(I2C_BUS);


   return model->verified;
}

Последовательность такая:

  1. Первым делом «захватываем» шину (I2C_BUS — не встроенная константа, просто скопировать не получится, лучше заглянуть в репозиторий).

  2. Проверяем, что на шине i2c по адресу 0x66 что-то есть.

  3. Считываем из регистра 00 идентификатор устройства, он должен быть равен 0x7D. Это не гарантирует нам, что подключён именно RadSens, но если мы не получили в ответ 0x7D, то модуль точно не подключён.

  4. Считываем динамическое значение.

  5. Считываем статистическое значение.

  6. Считываем количество импульсов, зарегистрированное модулем с момента последнего считывания.

  7. «Отпускаем» шину.

В нашем приложении можно «захватить» шину при запуске и «отпустить» при завершении, но так как на шине i2c может быть несколько устройств, теоретически, может быть другой процесс, который хочет считывать данные с шины, поэтому освобождаем её, когда она нам не нужна.

Все считанные данные сохраняем в модель, которую функция получила в качестве параметра. Из этой модели будем читать данные для отображения на экране.

Пора вернуться к «хитрости» с индикацией частиц. Поскольку линию прерывания я не распаял, я эмулирую нужное количество импульсов светодиодом и вибрацией по значению, считанному в пункте 6. Если модуль опрашивается каждую секунду, то ожидаем от него 0, 1, 2 или 3 зарегистрированные частицы. В зависимости от их количества включаем светодиод и вибрацию на 25 мс соответствующее количество раз с промежутком в 250 мс (тут было бы правильнее 225 мс, но используем предопределённые константы из API уведомлений). Теоретически, за секунду может быть зарегистрировано больше трёх импульсов. На практике, в «чистой» среде больше двух импульсов в секунду я не встречал. И не планирую перемещаться туда, где такая ситуация возможна =)

В результате на экране Флиппера мы видим следующее:

В верхней строке статуса видим информацию о том, что RadSens подключён. Если в момент работы приложения модуль был отключён, то приложение уведомит об этом. Крупными цифрами показываем «текущий» уровень радиации. Это значение, вычисленное на основе динамической интенсивности.

Чуть ниже выводятся статистическое значение интенсивности и количество зарегистрированных с первого запуска программы импульсов. При завершении приложения количество накопленных импульсов будет сохранено в памяти Флиппера, а при последующем запуске считано и сложено с количеством импульсов, которое накопилось в модуле, пока мы не считывали данные. Кстати, этот счётчик всего 16 битный. За время моего пользования модулем он переполнился дважды. Нужно поменять переменную счётчика на 32-битное целое, но руки пока не дошли.

Последний, но не по важности, элемент — это индикатор батарейки. Модуль потребляет достаточно много (около 60 мА по замерам самого Флиппера, хотя в документации заявлено меньше), поэтому полезно иметь индикацию. Контур батарейки скопирован из репозитория Флиппера, так что он повторяет батарейку на главном экране. Заполнение рисуется обычным прямоугольником в зависимости от уровня заряда.

В строке статуса есть ещё один индикатор, который не виден на скриншоте — индикатор виброрежима. Для его включения нужно нажать кнопку вверх на джойстике устройства. Тогда на каждый зарегистрированный импульс Флиппер издаст короткий бзз.

Строим график

Смотреть на меняющиеся циферки прикольно, но ещё интереснее смотреть на их динамику. Конечно же, сразу в голову приходит идея построить график. Экран Флиппера не очень большой, шириной 128 точек, поэтому график должен быть компактный. Данные мы снимаем каждую секунду. Напрашивается вариант отображать график значений за последние две минуты, а на оставшихся 8 пикселях расположить подписи. 

С осью значений сложнее. Я долго думал, какой выбрать масштаб. Высота экрана 64 пикселя, но верхние 8 пикселей занимает строка статуса. Условно будем считать, что у нас в наличии 60 точек. Так как среднее значение фонового излучения у меня дома около 10, сначала я выбрал масштаб 20 точек на 10 мкР. Поэтому на шкале было две отметки: «10» и «20», хотя сетка графика шла выше, почти до 30. Но фиксированный масштаб — это плохо. При низких значениях график будет сливаться с осью, при высоких — уедет за экран. Поэтому во втором подходе к экрану с графиком я добавил автомасштабирование. Приложение выбирает масштаб исходя из самого большого значения. Базовый вариант — 20 точек на 5мкР, а дальше увеличивается кратно степеням двойки: 20 точек на 10м кР, 20 точек на 20 мкР и т.д.

Технически график реализован через кольцевой буфер (обычный массив со смещающимся индексом начала данных) на 120 значений, в который записывается текущее значение радиации. Выглядит вот так:

Переключаться между двумя экранами можно стрелками влево-вправо.

Заключение

Теперь я таскаю RadSens в рюкзаке и могу при желании измерить текущий радиоактивный фон. Пока что из интересного могу рассказать, что в перегонах метро он падает в два раза, до 5 мкР. Говорят, что фон сильно выше в летящем самолете, но мне пока не довелось убедиться в этом лично.

После обсуждения модуля и приложения в официальном Telegram-чате Флиппера мне задали вопрос: 

В целом, получилась отличная игрушка, которая показывает что-то магическое, что мы не видим. Надеюсь, вам понравилось, вы вдохновитесь этой статьёй и соберёте модуль для Флиппера. А если и не соберёте, всё равно спасибо что дочитали до конца =)

Комментарии (17)


  1. Markscheider
    00.00.0000 00:00
    +3

    Эх, вот бы альфу еще мерять!
    В сторону соответствующих детекторов (слюдяные "Беты") не смотрели?


  1. velipre_xella
    00.00.0000 00:00
    +1

    Возможно, стоит добавить, что дозиметр только про гамма-излучение. Хотя, конечно, доводилось встречать людей, кот-е гамма-дозиметрами радиацию при рентгеновских дефектоскопах измеряли.


    1. red_dragon
      00.00.0000 00:00

      C чего это, дозиметр только про γ-излучение?


  1. kulhaker478
    00.00.0000 00:00
    +1

    Раз уж соединение сделано проводами, то почему бы не разместить модуль сзади Флиппера (один фиг при работе с дозиметром ключи доступа не особо

    С мини версией модуля - самое то получится, особенно для повседневного таскания в рюкзаке :)


  1. red_dragon
    00.00.0000 00:00

    А схема модуля где?


    1. kulhaker478
      00.00.0000 00:00

      У производителя, но делиться они не будут (да и не сильно надо)


  1. Polaris99
    00.00.0000 00:00
    +6

    Астрологи провозгласили неделю Флиппера? Зачем Флиппер в данном конкретном случае? Что дает использование тула за 300 евро в задаче для ардуино нано плюс китайский экранчик за 5 евро (причем гораздо более выского качества)? Извините, но это как из пушки по воробьям.


    1. vGimly
      00.00.0000 00:00

      "Можно, но в кармане у меня валялся только флиппер".

      https://pikabu.ru/story/net_produktov_1360678
      «Что делать, если к вам пришли гости, а в доме нет продуктов?»
      — «Пошлите кухарку в погреб, пускай она нарежет холодной буженины, лососины,....


    1. kulhaker478
      00.00.0000 00:00
      +1

      При наличии Флиппера - почему бы и нет. Другое дело, что об этом в статье не обозначено, аудиторая тут додумывать не любит


      1. VadimBal Автор
        00.00.0000 00:00
        +2

        В самом первом абзаце написано, что это статья для тех, у кого есть Флиппер.

        В самом втором написано, что если Флиппера нет, то есть ссылка на проект, где этот же модуль используется с ардуинкой.

        В самом третьем есть ссылка на статью, где можно вообще самому не собирать.

        Так что все обозначено =)


    1. VadimBal Автор
      00.00.0000 00:00

      Как уже ответили другие пользователи: Флиппер тут потому, что он у меня есть. Использую я его не только для измерения радиации, но это одно из применений. Делаю для него и другие расширения.


  1. Akr0n
    00.00.0000 00:00

    Жаль, что сам датчик стоит совсем не дёшево. А, собственно, дешёвых и показывающих не погоду на Марсе нет.


    1. VadimBal Автор
      00.00.0000 00:00

      Модуль в сборке продается в Амперкоте за ~4500р. Там же есть голый модуль без трубки за ~1500р, а трубку можно взять на Авито за 500р - это самый дешевый вариант измерить радиацию.


  1. FVVSHARK
    00.00.0000 00:00

    А давно ли вы измеряли радиацию у себя дома?

    В любое время под рукой имеется ГРАЧ Дозиметр ДКГ-03Д, жаль не знаю как его подцепить к какой нить ESP32 и через MQTT в Home Assistant закинул бы)


  1. Eraminel01
    00.00.0000 00:00
    +1

    Мама, я в телике!)


    1. VadimBal Автор
      00.00.0000 00:00

      Я ждал этот коммент =)


  1. tormozedison
    00.00.0000 00:00

    Флиппер здесь лишний, тут ресурсов Ардубоя с десятикратным запасом хватит.