Счётчик Гейгера и Народный мониторинг

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

Для этого одним из пользователей Narodmon был куплен кит для сборки счётчика Гейгера и отдельно трубка СБМ-20. Ссылка на магазин: Geiger Counter Radiation Detector DIY Kit Arduino Compatible ver. 3.00 w/o GM Tube. Фото с того же сайта:

И мной уже была написана прошивка к Arduino для работы с данным устройством. На самом деле работает всё довольно просто. Трубка периодически пробивается при пролёте ионизирующей частицы через объём газа. И тем чаще, чем выше уровень излучения. Схема воспринимает эти разряды как импульсы, которые отправляются на ардуину. В коде происходит подсчёт количества импульсов за определённое время, высчитывается количество импульсов за минуту. И полученное число умножается на константу, зависящую от типа трубки (для СБМ-20 = 0,57). В результате получаем уровень излучения в мкР/ч.

Внимание!
Трубка в счётчике Гейгера питается высоким напряжением (> 300 В). Будьте осторожны!

Если найдутся тестеры, то объединю этот код с кодом метеостанции.

// Скетч для Arduino для отправки данных о радиационном фоне на Народный мониторинг
// Версия 1.0 (15.02.2016)
//
// Автор: Гладышев Дмитрий (2016)
//
// Часть кода взята здесь:
// http://www.rhelectronics.net/store/radiation-detector-geiger-counter-diy-kit-second-edition.html
//

/*
The standard unit of radiation dosing in an area is the micro-Sievert/hour (uSv/hr).
For this tube, multiply its CPM by 0.0057 to get the equivalent uSv/hr radiation level.
*/

#include <SPI.h>
#include <Ethernet.h>

bool Debug = false; //режим отладки

//********************************************************************************************
byte mac[] = { 0xDE, 0xAD, 0xBE, 0x00, 0x00, 0x00 }; //MAC-адрес Arduino
#define GEIGER_PIN       2      // пин для подключения счётчика Гейгера
#define GEIGER_INT       0      // прерывание для счётчика Гейгера
#define postingInterval  600000 // интервал между отправками данных в миллисекундах (10 минут)
#define LOG_PERIOD 15000        //Logging period in milliseconds, recommended value 15000-60000.
//********************************************************************************************
#define MAX_PERIOD 60000    //Maximum logging period
IPAddress server(94,19,113,221); // IP сервера народного мониторинга
char macbuf[13];

EthernetClient client;

unsigned long lastConnectionTime = 0;           // время последней передачи данных
boolean lastConnected = false;                  // состояние подключения
int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;
char replyBuffer[160];                          // буфер для отправки

volatile unsigned long counts;             //variable for GM Tube events
unsigned long cpm;                 //variable for CPM
unsigned long mkrh;
unsigned int multiplier;             //variable for calculation CPM in this sketch
unsigned long previousMillis;      //variable for time measurement

void tube_impulse(){               //procedure for capturing events from Geiger Kit
  counts++;
}

void setup() {

  if (Debug)
  {
    Serial.begin(9600);
  }

  // Пробуем подключиться по Ethernet до тех пор пока это не удастся
  do 
  {
    delay(1000);
  } while (Ethernet.begin(mac) == 0);
 
  lastConnectionTime = millis()-postingInterval+15000; //первое соединение через 15 секунд после запуска
  
  counts = 0;
  cpm = 0;
  multiplier = MAX_PERIOD / LOG_PERIOD;      //calculating multiplier, depend on your log period
  
  pinMode(GEIGER_PIN, INPUT);                // set pin INT0 input for capturing GM Tube events
  digitalWrite(GEIGER_PIN, HIGH);            // turn on internal pullup resistors, solder C-INT on the PCB
  
}

void loop()
{
  //Если вдруг нам случайно приходят откуда-то какие-то данные,
  //то просто читаем их и игнорируем, чтобы очистить буфер
  if (client.available())
  {
    client.read();
  }

  if (!client.connected() && lastConnected)
  {
    if (Debug)
    {
      Serial.println();
      Serial.println("disconnecting.");
    }
    client.stop();
  }

  //если не подключены и прошло определённое время, то делаем замер,
  //переподключаемся и отправляем данные
  if (!client.connected() && (millis() - lastConnectionTime > postingInterval)) 
  {
    //формирование HTTP-запроса
    memset(replyBuffer, 0, sizeof(replyBuffer));
    strcpy(replyBuffer,"ID=");
 
    memset(macbuf, 0, sizeof(macbuf));
    //Конвертируем MAC-адрес
    for (int k=0; k<6; k++)
    {
      int b1=mac[k]/16;
      int b2=mac[k]%16;
      char c1[2],c2[2];
 
      if (b1>9) c1[0]=(char)(b1-10)+'A';
      else c1[0] = (char)(b1) + '0';
      if (b2>9) c2[0]=(char)(b2-10)+'A';
      else c2[0] = (char)(b2) + '0';
 
      c1[1]='\0';
      c2[1]='\0';
 
      strcat(macbuf,c1);
      strcat(macbuf,c2);
    }
    strcat(replyBuffer, macbuf);
 
    unsigned long currentMillis = millis();
    
    counts = 0;
    attachInterrupt(GEIGER_INT, tube_impulse, FALLING);  //define external interrupts
    while (millis()-currentMillis < LOG_PERIOD)
    {
    }
    detachInterrupt(GEIGER_INT);
    cpm = counts * multiplier;
    mkrh = round((float)cpm*0.57); // CPM --> мкР/ч

    char temp[8];
    itoa(mkrh, temp);
    strcat(replyBuffer, "&");
    strcat(replyBuffer, "R1=");
    strcat(replyBuffer, temp);

    strcat(replyBuffer,'\0');

    if (Debug)
    {
      Serial.println(replyBuffer);
      Serial.print("Content-Length: ");
      Serial.println(len(replyBuffer));
    }

    //отправляем запрос
    httpRequest();
  }
  //храним последнее состояние подключения
  lastConnected = client.connected();
}

//Функция отправки данных
void httpRequest() 
{
  if (client.connect(server, 80))
  {
    if (Debug)
    {
      Serial.println("connecting...");
    }
    // отправляем HTTP POST запрос:
    client.println("POST http://narodmon.ru/post.php HTTP/1.0");
    client.println("Host: narodmon.ru");
    client.println("Content-Type: application/x-www-form-urlencoded");
    client.print("Content-Length: ");
    client.println(len(replyBuffer));
    client.println();
    client.println(replyBuffer);
    client.println();
 
    lastConnectionTime = millis();
  } 
  else
  {
    if (Debug)
    {
      Serial.println("connection failed");
      Serial.println("disconnecting.");
    }
    client.stop();
  }
}

//Функция определения длины строки
int len(char *buf)
{
  int i=0; 
  do
  {
    i++;
  } while (buf[i]!='\0');
  return i;
}
 
//Функция переворота строки
void reverse(char s[])
{
  int i, j;
  char c;
 
  for (i = 0, j = strlen(s)-1; i<j; i++, j--) 
  {
    c = s[i];
    s[i] = s[j];
    s[j] = c;
  }
}
 
//Функция конвертирования числа в символьный массив
void itoa(int n, char s[])
{
  int i, sign;
 
  if ((sign = n) < 0)       /* записываем знак */
    n = -n;                 /* делаем n положительным числом */
  i = 0;
  do {                      /* генерируем цифры в обратном порядке */
    s[i++] = n % 10 + '0';  /* берем следующую цифру */
  } while ((n /= 10) > 0);  /* удаляем */
  if (sign < 0)
    s[i++] = '-';
  s[i] = '\0';
  reverse(s);
}

В строках 21-25:

mac — MAC-адрес Arduino. Это ваш уникальный идентификатор на сайте народного мониторинга. Поэтому для защиты от совпадений рекомендую использовать MAC-адрес вашего компьютера/роутера/телефона с изменённым последним байтом (чтобы не было коллизии внутри локальной сети).

GEIGER_PIN — пин подключения счётчика. Не изменяйте, если не знаете что делаете.

GEIGER_INT — номер прерывания. Не изменяйте, если не знаете что делаете.

postingInterval — интервал отправки данных в миллисекундах.

LOG_PERIOD — время замера в миллисекундах. Максимум 60000 мс.

Файлы

Скачать скетч
Работа проверялась с Arduino IDE v1.0.5-r2.
Предупреждение!
Автор не несёт ответственности за возможную порчу оборудования. Всё, что вы делаете — вы делаете на свой страх и риск!