Подключение Arduino к отечественному проекту Народного мониторинга

Хочу рассказать ещё об одном сервисе для удалённого мониторинга температуры, преимущественно за окном. На этот раз проект отечественный, что не может не радовать.

Внимание! Существует вторая версия данного устройства, доступная по этой ссылке: Метеостанция 2.0

Последнее обновление: 16 июля 2014 (v1.3)
Исправлена ошибка с дробными показаниями температуры

6 октября 2013 (v1.2)
При наличии нескольких термодатчиков данные передаются на сервер одновременно со всех, а не по очереди, как было раньше.

25 февраля 2013 (v1.1)
Исправлена ошибка с округлением отрицательной температуры до десятых долей градуса.

Немного о проекте

Сервис располагается по адресу http://narodmon.ru

Проект начался с обсуждения темы «Народный мониторинг температуры (vs прогноз) в различных городах. Нужен ли?» на Хабрахабре. На тот момент целью проекта являлось лишь информирование пользователей в Интернет о реальной погоде в данный момент времени в определенном месте, где есть участники данного проекта. C дальнейшим ростом аудитории разработчиков электронных устройств для работы с данным проектом, а также пополнением базы пользователей, возникла необходимость расширить рамки проекта с целью передачи сбора показаний с датчиков различного типа с более универсальным протоколом с крайне низкими требованиями к каналу передачи данных вплоть до GPRS/EDGE/3G/UMTS.

Собираем устройство

Нам понадобятся следующие компоненты:

  • Микроконтроллер Arduino или Freeduino (с ATmega328);
  • Ethernet-shield;
  • Цифровой термодатчик DS18B20;
  • Резистор 4.7 кОм;

Все компоненты соединяются между собой также как и в предыдущей статье: Arduino: цифровой термометр с web-мониторингом. Немного повторюсь:

Термодатчик подключается по шине 1-wire по схеме с паразитным питанием. При этом, можно использовать несколько таких датчиков (все они соединяются параллельно двумя проводами) и считывать с каждого отдельную температуру. Подключать можно к любому цифровому входу микроконтроллера, кроме 10, 11, 12 и 13, которые используются для нужд Ethernet Shield.

Прошивка

Теперь заливаем в Arduino следующий скетч:

// Скетч для Arduino для отправки данных о температуре на Народный мониторинг.
// Версия 1.3 (16.07.2014)
//
// Автор: Гладышев Дмитрий (2012-2014)
// http://student-proger.ru/2012/06/podklyuchenie-arduino-k-otechestvennomu-proektu-narodnogo-monitoringa/

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

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

//****************************************************************************************
byte mac[] = {  0xDE, 0xAD, 0xBE, 0x00, 0x00, 0x00 }; //MAC-адрес Arduino
OneWire ds(2); //пин подключения термодатчика
const unsigned long postingInterval = 600000;  // интервал между отправками данных в миллисекундах (10 минут)
//****************************************************************************************

char server[] = "narodmon.ru";

EthernetClient client;

unsigned long lastConnectionTime = 0;           // время последней передачи данных
boolean lastConnected = false;                  // состояние подключения

int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;

char replyBuffer[160];

int CountSensors;

void setup() {

    if (Debug)
    {
      Serial.begin(9600);
    }
   
    delay(1000);
    // Ethernet connection:
    if (Ethernet.begin(mac) == 0) 
    {
      if (Debug)
      {
        Serial.println("Failed to configure Ethernet using DHCP");
      }
      // ничего не делаем
      for(;;);
    }
    // секунда для инициализации Ethernet
    delay(1000);

    //Узнаём количество термодатчиков
    CountSensors = DsCount();
    if (Debug)
    {
      Serial.print("Found ");
      Serial.print(CountSensors);
      Serial.println(" sensors."); 
    }
    lastConnectionTime = millis()-postingInterval+15000; //первое соединение через 15 секунд после запуска
}

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=");

    //Конвертируем 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(replyBuffer,c1);
      strcat(replyBuffer,c2);
    }

    //Сбрасываем поиск датчиков (кол-во нам уже известно)
    ds.reset_search();
    //Теперь в цикле опрашиваем все датчики сразу

    for (int j=0; j<CountSensors; j++)
    {

      byte i;
      byte present = 0;
      byte data[12];
      byte addr[8];

      if ( !ds.search(addr)) 
      {
        ds.reset_search();
        return;
      }

      ds.reset();
      ds.select(addr);
      ds.write(0x44,1);

      delay(1000);

      present = ds.reset();
      ds.select(addr);    
      ds.write(0xBE);

      for ( i = 0; i < 9; i++) // we need 9 bytes
      {
        data[i] = ds.read();
      }

      LowByte = data[0];
      HighByte = data[1];
      TReading = (HighByte << 8) + LowByte;
      SignBit = TReading & 0x8000;  // test most sig bit
      if (SignBit) // negative
      {
        TReading = (TReading ^ 0xffff) + 1; // 2's comp
      }
      Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

      Whole = Tc_100 / 100;  // separate off the whole and fractional portions
      Fract = Tc_100 % 100;

      char temp[3];

      itos(Whole,temp);
      strcat(replyBuffer,"&");

      //конвертируем адрес термодатчика
      for (int k=7; k>=0; k--)
      {
        int b1=addr[k]/16;
        int b2=addr[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(replyBuffer,c1);
        strcat(replyBuffer,c2);
      }
      strcat(replyBuffer,"=");
      if (SignBit) //если температура отрицательная, добавляем знак минуса
      {
        strcat(replyBuffer,"-");
      }
      strcat(replyBuffer,temp);
      strcat(replyBuffer,".");
	  if (Fract<10)
	  {
		  strcat(replyBuffer,"0");
	  }
      itos(Fract,temp);
      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...");
    }
    // send the HTTP POST request:
    client.println("POST http://narodmon.ru/post.php HTTP/1.0");
    client.println("Host: narodmon.ru");
    //client.println("User-Agent: arduino-ethernet");
    //client.println("Connection: close");
    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 DsCount()
{
  int count=0;
  bool thatsall = false;
  byte addr[8];
  do
  {
    if ( !ds.search(addr))
    {
      ds.reset_search();
      thatsall = true;
    }
    count++;
  } while(!thatsall);
  return (count-1);
}

int len(char *buf)
{
  int i=0; 
  do
  {
    i++;
  } while (buf[i]!='\0');
  return i;
}

void itos(int n, char bufp[3]) //int to string
{
  char buf[3]={'0','0','\0'}; 
  int i = 1;

  while (n > 0) {
    buf[i] = (n % 10)+48;
    i--;
    n /= 10;
  }

  for (i=0; i<3; i++)
    bufp[i]=buf[i];
}

Обратите внимание на следующие строки:

//****************************************************************************************
byte mac[] = {  0xDE, 0xAD, 0xBE, 0x00, 0x00, 0x00 }; //MAC-адрес Arduino
OneWire ds(2); //пин подключения термодатчика
const unsigned long postingInterval = 600000;  // интервал между отправками данных в миллисекундах (10 минут)
//****************************************************************************************

Здесь необходимо будет придумать MAC-адрес для Arduino (рекомендую взять для этого MAC-адрес вашего компьютера), указать номер входа, к которому подключен термодатчик, а также частоту обновления информации. По MAC-адресу устройство будет идентифицироваться в проекте, поэтому он должен быть уникальным.

Теперь регистрация на сайте

Заходим на сайт, жмём кнопочку «Вход», затем «Я тут впервые». Вводим свой E-Mail и ждём пока на почту свалится письмо с паролем.


Входим в систему.

Теперь нажимаем кнопку «Мои датчики», затем «Добавить». Вводим MAC-адрес, который указан в прошивке. Теперь система должна отобразить подключенные термодатчики и температуру на них. Можно указать местоположение термометра на карте и дать подписи датчикам.

Щёлкнув по пузырьку с температурой на карте, можно просмотреть дополнительную информацию:


Также можно просмотреть график изменения параметра:


и отчёт о переданных числовых данных:

Файлы
Скетч v1.3
Скетч v1.2
Скетч v1.1
Библиотеки:
Ethernet
OneWire
SPI

Работа проверялась с Arduino IDE v1.0.1

Предупреждение!
Автор не несёт ответственности за возможную порчу оборудования. Всё, что вы делаете — вы делаете на свой страх и риск!