GSM монитор для дачи

axill
Offline
Зарегистрирован: 05.09.2011

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

Решил собрать маленький проект из имеющейся в запасах GBOARD

Функционал прост:

- двойное питание, для бакапа взял готовый литиевый модуль из трех банок с встроенной защитой, 12.6В номинал, 1800мАЧ. он подключен напрямую к GBOARD и развязан диодом шотки 1N5819 от сетевого блока питания на 12В 1А. Блок питания подстроен на 12.6В. Встроенная защита в батарею позволяет обойтись от сложной схемы переключения и от схемы зарядки. Такая схема проверена в другом устройстве - три года отлично работает. При питании от сети батарея заряжается до нужного напряжения, после чего зарядка отключается встроенной защитой. При пропадании сетевого напряжения питание осуществляется от батареи. Сейчас тестирую - уже 8 часов работает от батареи

- контроль питания сети и напряжения на батарее, для этого установлены резистивные делитили с использованием A7 и A6. Один делитель подключен к сетевому блоку питания до диода, воторой паралельно батареи.

- подключены два DS18B20, один будет в корпусе (с дырками для вентиляции), второй подключаться проводами и выведен на улицу

- текущая информация отображается на дисплее нокия 5110 - две температуры, счетчик отправленных и полученных СМС и звонков, статус питания и напряжение питания

Для удобства взял паячную макетную плату для UNO и подогнав по месту впаял в нее разьемы-мама так, чтобы она одевалась на GBOARD тем самым получая питание 3.3В и пины A0-A7. A0-A4 использованы для дисплея, А5 для датчиков, А6-А7 для контроля питания.

Скетч без изысков, написан по минимуму. Умеет отправлять один тип СМС в котором сообщает о количестве полученных/отправленных СМС, сообщает статус запуска (отправляет при включении), статус питания, напряжение питания, две температуры - по сути дублирует информацию с дисплея. СМС отправляется по событиям или по запросу - 1. если произошло сильное изменение внутренней или внешней температуры по отношению к предыдущему отосланному сообщению 2. если изменился статус питания 3. разово если батарея разрядилась ниже заданного уровня 4. при поступлении звонка или СМС

#include <avr/pgmspace.h>

#define DEBUG

#define  PHONE_NUMBER  "+7xxxxxxxxxx"

#include "U8glib.h"
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SoftwareSerial.h> 
#include <GSM_Shield.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS A5
#define TEMPERATURE_PRECISION 9

#define POWER_SENSE    A7
#define BATTERY_SENSE  A6

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// arrays to hold device addresses
DeviceAddress insideThermometer, outsideThermometer;

U8GLIB_PCD8544 u8g(A3, A4, A1, A0, A2);		// SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9, Reset = 8

GSM gsm;

struct {
  int  temperature_inside;
  int  temperature_outside;
  int  prev_inside;
  int  prev_outside;
  uint8_t  sms_in_count;
  uint8_t  sms_out_count;
  uint16_t start;
  uint8_t  power_status;
  uint8_t  sensors;
} var;

void setup(void) {

#ifdef DEBUG  
  Serial.begin(9600);
  Serial.println("Dom GSM controller started");
#endif

  // Start ds18b20 up the library
  sensors.begin();
  // locate devices on the bus
  var.sensors = sensors.getDeviceCount();

#ifdef DEBUG
  Serial.print("+ found ");
  Serial.print(var.sensors, DEC);
  Serial.println(" temperature sensors.");
  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("! Unable to find address for Device 0"); 
  if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("! Unable to find address for Device 1");
#else 
  sensors.getAddress(insideThermometer, 0); 
  sensors.getAddress(outsideThermometer, 1);
#endif

  // set the resolution to 9 bit
  sensors.setResolution(insideThermometer, TEMPERATURE_PRECISION);
  sensors.setResolution(outsideThermometer, TEMPERATURE_PRECISION);
  
#ifdef DEBUG
  Serial.println("+ init GSM");
#endif

  gsm.TurnOn(9600);          //module power on
  gsm.InitParam(PARAM_SET_1);//configure the module  
  gsm.Echo(0);               //enable AT echo 
  
  var.prev_inside = -99;
}

char* formatTemp(char* buf, int temp) {
  sprintf(buf, "%d.%d", temp / 10, abs(temp) % 10);
  return buf;
}

void draw(void) {
  char buf[20];

  // внутренняя температура
  u8g.setFont(u8g_font_fur17);
  u8g.drawStr(0, 17, formatTemp(buf, var.temperature_inside));
  // внешняя температура
  u8g.setFont(u8g_font_04b_03br);
  //u8g.setFont(u8g_font_5x7);
  u8g.setPrintPos(0, 25);
  u8g.print("out ");
  u8g.print(formatTemp(buf, var.temperature_outside));
  
  //u8g.setFont(u8g_font_04b_03br);
  u8g.setPrintPos(0, 48);
  u8g.print("tel:"PHONE_NUMBER);

  u8g.setPrintPos(62, 14);
  sprintf(buf, "In:%02d", var.sms_in_count % 100);
  u8g.print(buf);
  u8g.setPrintPos(56, 20);
  sprintf(buf, "Out:%02d", var.sms_out_count %100);
  u8g.print(buf);
  
  // состояние паитания
  u8g.setPrintPos(56, 5);
  u8g.print((var.power_status)?"220V":"BAT");
}

void loop(void) {
  
  boolean sendstat = false;
  
  // проверяем наличие сетевого напряжения
  int voltage = analogRead(POWER_SENSE);

#ifdef DEBUG
  Serial.print("- voltage ");
  Serial.println(voltage);
#endif

  uint8_t new_power_status = (voltage > 990)?1:0;
  if(var.power_status != new_power_status) {

#ifdef DEBUG
    Serial.println("- power status changed");
#endif

    sendstat = true;
    var.power_status = new_power_status;
  }
  
  // измеряем температуру
  sensors.requestTemperatures();
  var.temperature_inside = (sensors.getTempC(insideThermometer) + 0.05) * 10;
  var.temperature_outside = (sensors.getTempC(outsideThermometer) + 0.05) * 10;

#ifdef DEBUG
  Serial.print("- inside ");
  Serial.println(var.temperature_inside);
  Serial.print("- outside ");
  Serial.println(var.temperature_outside);
#endif

  // проверка на сильные изменения
  if(abs(var.prev_inside - var.temperature_inside) >= 25 || abs(var.prev_outside - var.temperature_outside) >= 100) {

#ifdef DEBUG
    Serial.println("- sending SMS because of temperature shift");
#endif

    var.prev_inside = var.temperature_inside;
    var.prev_outside = var.temperature_outside;
    sendstat = true;
  }
  // проверка входящих SMS
  if(checkSMS() || checkCall()) {
    sendstat=true;
  }
  
  if(sendstat) sendStatus();
  
  // picture loop
  u8g.firstPage();  
  do {
    draw();
  } while( u8g.nextPage() );
  
  // rebuild the picture after some delay
  delay(50);
}

void sendStatus() {
  char buf[64];
  var.sms_out_count++;
  sprintf(buf, "%s%d/%d: power=%s, inside=%d.%d outside=%d.%d", 
    (var.start)?"":"restarted:",
    var.sms_in_count, var.sms_out_count, 
    (var.power_status)?"220V":"BAT",
    var.temperature_inside/10, abs(var.temperature_inside)%10,
    var.temperature_outside/10, abs(var.temperature_outside)%10);
  gsm.SendSMS(PHONE_NUMBER, buf);
  var.start = 1;
}

 boolean checkSMS()  //Check if there is an sms 'type_sms'
 {
     char pos_sms_rx;  //Received SMS position     
     pos_sms_rx=gsm.IsSMSPresent(SMS_UNREAD);
     if (pos_sms_rx!=0)
     {
       //Read text/number/position of sms
       char number_incoming[20];
       char sms_rx[34]; //Received text SMS
       gsm.GetSMS(pos_sms_rx,number_incoming,sms_rx,32);

#ifdef DEBUG       
       Serial.print("Received SMS from ");
       Serial.print(number_incoming);
       Serial.print("(sim position: ");
       Serial.print(word(pos_sms_rx));
       Serial.println(")");
       Serial.println(sms_rx);
#endif

       int error=gsm.DeleteSMS(pos_sms_rx);

#ifdef DEBUG
       if (error==1)Serial.println("SMS deleted"); else Serial.println("SMS not deleted");
#endif

       var.sms_in_count++;
       return true;
     }
     return false;
 }

 boolean checkCall()  //Check status call if this is available
 {     
     char number_incoming[20];
     int call=gsm.CallStatus();
     switch (call)
     {    
       case CALL_NONE:
         //Serial.println("no call");
         break;
       case CALL_INCOM_VOICE:
         gsm.CallStatusWithAuth(number_incoming,0,0);        
#ifdef DEBUG
         Serial.print("incoming voice call from ");     
         Serial.println(number_incoming);
#endif
         gsm.HangUp();
         var.sms_in_count++;
         return true;
         break;
       case CALL_ACTIVE_VOICE:
#ifdef DEBUG
         Serial.println("active voice call");    
#endif
         break;
       case CALL_NO_RESPONSE:
#ifdef DEBUG
         Serial.println("no response");
#endif
         break;
     }
     return false;
 }
 

 

axill
Offline
Зарегистрирован: 05.09.2011

добавка

GBOARD скомутирован на софтверный Serial к GSM модулю. Железный сериал использован для програмирования и отладки. Под дисплей тоже впаян разъем на макетную плату. Вообще все что нужно впаяно в эту плату, к плате подключены блок питания, батарея, питание GBOARD и внешний датчик температуры

и фото

gena
Offline
Зарегистрирован: 04.11.2012

  Не плохо бы приложить используемые библиотеки - не компилируется.

axill
Offline
Зарегистрирован: 05.09.2011

не знаю как тут файлы приложить

библиотеки общедостурпные

ug8lib https://code.google.com/p/u8glib/

GSM_board ftp://imall.iteadstudio.com/IM120411004_GBoard/Documents/Lib_IM120411004...

http://www.arduino.cc/playground/Learning/OneWire

http://milesburton.com/index.php?title=Dallas_Temperature_Control_Library

NewSoftwareSerial кажется есть в дистрибутиве или тут http://sundial.org/arduino/index.php/newsoftserial/

axill
Offline
Зарегистрирован: 05.09.2011

в дополнение - от батареи устройство отработало чуть больше 12 часов и это без принятия специальных мер по экономии, пока меня устроит. Подсветку дисплея резистором 680 ом подключил к сетевому блоку питания перед 1N5819, тем самым сделал ее автоматическое отключение при пропаже питания и она не расходует батарею

но нужно смотреть. На моем модуле вход подсветки это плюс, но на некоторых модулях там минус

gena
Offline
Зарегистрирован: 04.11.2012

  Интересуюсь потому, что сам сейчас прорабатываю "проект" GSM термометра (четыре точки измерения), с перспективой превращения этого затем в GSM сигнализацию. Поскольку радиомодемы SIM900 уже умеют различать входящие DTMF сигналы и воспрозводить звуковые файлы (до ста звуковых файлов), планирую получать ответ голосом о температуре (состоянии охраняемого объекта) по запросу с мобильного, а не через SMS или GPRS (у меня на мобильном  голосовая связь бесплатная). Т.к. пользоваться готовыми библиотеками только учусь (незнание английского), то чужие коды рассматриваю как примеры, и по ним пытаюсь ликвидировать свои незнания. На данный момент ищу (придумываю свой) алгоритм включения SIM900 после пропадания питания.

axill
Offline
Зарегистрирован: 05.09.2011

дерзайте, идея хорошая

я доработал скетч, теперь он умеет принимать текстовое сообщение по радио используя вставленный в GBOARD модуль NRF24L01 и отправляет полученный текст по СМС и отображает на дисплее

такой простой механизм позволит пристыковать к устройству по радио любые датчики и информировать по СМС.

#define GSM_MONITOR 0x1000000000LL
#define RADIO_CHECK	0x0101

typedef struct {
	uint16_t check;
	char	text[16];	
} radio_msg;

#include <avr/pgmspace.h>

//#define DEBUG

#define  PHONE_NUMBER  "+7xxxxxxxxxx"

#include "U8glib.h"
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SoftwareSerial.h> 
#include <GSM_Shield.h>

#include <SPI.h>
#include "RF24.h"

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS A5
#define TEMPERATURE_PRECISION 9

#define POWER_SENSE    A7
#define BATTERY_SENSE  A6

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// arrays to hold device addresses
DeviceAddress insideThermometer, outsideThermometer;

U8GLIB_PCD8544 u8g(A3, A4, A1, A0, A2);		// SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9, Reset = 8

GSM gsm;
RF24 radio(8,9);

struct {
  int  temperature_inside;
  int  temperature_outside;
  int  prev_inside;
  int  prev_outside;
  uint8_t  sms_in_count;
  uint8_t  sms_out_count;
  uint8_t  radio_count;
  uint16_t start;
  uint8_t  power_status;
  uint8_t  sensors;
  uint16_t  battery;
  boolean   battery_notified;
  char      radio_text[18];
} var;

void setup(void) {

#ifdef DEBUG  
  Serial.begin(9600);
  Serial.println("Dom GSM controller started");
#endif

  // Start ds18b20 up the library
  sensors.begin();
  // locate devices on the bus
  var.sensors = sensors.getDeviceCount();
  
  radio.begin();
  radio.openReadingPipe(1, GSM_MONITOR);
  radio.startListening();

#ifdef DEBUG
  Serial.print("+ found ");
  Serial.print(var.sensors, DEC);
  Serial.println(" temperature sensors.");
  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("! Unable to find address for Device 0"); 
  if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("! Unable to find address for Device 1");
#else 
  sensors.getAddress(insideThermometer, 0); 
  sensors.getAddress(outsideThermometer, 1);
#endif

  // set the resolution to 9 bit
  sensors.setResolution(insideThermometer, TEMPERATURE_PRECISION);
  sensors.setResolution(outsideThermometer, TEMPERATURE_PRECISION);
  
#ifdef DEBUG
  Serial.println("+ init GSM");
#endif

  gsm.TurnOn(9600);          //module power on
  gsm.InitParam(PARAM_SET_1);//configure the module  
  gsm.Echo(0);               //enable AT echo 
  
  //analogReference(INTERNAL);
  var.prev_inside = -99;
  var.battery_notified = false;
  var.radio_text[0] = '\0';
}

char* formatTemp(char* buf, int temp) {
  sprintf(buf, "%d.%d", temp / 10, abs(temp) % 10);
  return buf;
}

void draw(void) {
  char buf[24];

  // внутренняя температура
  u8g.setFont(u8g_font_fur17);
  u8g.drawStr(0, 17, formatTemp(buf, var.temperature_inside));
  // внешняя температура
  u8g.setFont(u8g_font_04b_03br);
  //u8g.setFont(u8g_font_5x7);
  u8g.setPrintPos(0, 25);
  u8g.print("out ");
  u8g.print(formatTemp(buf, var.temperature_outside));
  
  //u8g.setFont(u8g_font_04b_03br);
  u8g.setPrintPos(0, 42);
  u8g.print("tel:"PHONE_NUMBER);

  u8g.setPrintPos(0, 48);
  sprintf(buf, "In:% 3d Out:% 3d R: %d", var.sms_in_count, var.sms_out_count, var.radio_count);
  u8g.print(buf);
  
  // состояние паитания
  u8g.setPrintPos(56, 5);
  u8g.print((var.power_status)?"220V":"BAT");
  u8g.setPrintPos(56, 11);
  sprintf(buf, "%d.%01dV", var.battery / 10, var.battery % 10);
  u8g.print(buf);
  
  // radio text
  u8g.setPrintPos(0, 34);
  u8g.print(var.radio_text);
}

void loop(void) {
  
  boolean sendstat = false;
  
  // проверяем наличие сетевого напряжения
  int voltage = analogRead(POWER_SENSE);

#ifdef DEBUG
  Serial.print("- voltage ");
  Serial.println(voltage);
#endif

  uint8_t new_power_status = (voltage > 990)?1:0;
  if(var.power_status != new_power_status) {

#ifdef DEBUG
    Serial.println("- power status changed");
#endif

    sendstat = true;
    var.power_status = new_power_status;
    var.battery_notified = false;
  }

  // проверяем питающее напряжение
  uint32_t v = analogRead(BATTERY_SENSE);
  v *= 33 * 430;
  v /= 1023;
  v += 50;
  v /= 100;
  var.battery = v;

  if(var.battery < 90 && !var.battery_notified) {
    sendstat = true;
    var.battery_notified = true;
  }

#ifdef DEBUG
  Serial.print("- source voltage ");
  Serial.println(var.battery);
#endif
  
  // измеряем температуру
  sensors.requestTemperatures();
  var.temperature_inside = (sensors.getTempC(insideThermometer) + 0.05) * 10;
  var.temperature_outside = (sensors.getTempC(outsideThermometer) + 0.05) * 10;

#ifdef DEBUG
  Serial.print("- inside ");
  Serial.println(var.temperature_inside);
  Serial.print("- outside ");
  Serial.println(var.temperature_outside);
#endif

  // проверка на сильные изменения
  if(abs(var.prev_inside - var.temperature_inside) >= 25 || abs(var.prev_outside - var.temperature_outside) >= 100) {

#ifdef DEBUG
    Serial.println("- sending SMS because of temperature shift");
#endif

    var.prev_inside = var.temperature_inside;
    var.prev_outside = var.temperature_outside;
    sendstat = true;
  }
  // проверка входящих SMS
  if(checkSMS() || checkCall()) {
    sendstat=true;
  }
  
  if(sendstat) sendStatus();

  if ( radio.available() ) {
      radio_msg msg;
      bool done = false;
      
      while (!done)
      {
        // Fetch the payload, and see if this was the last one.
        done = radio.read(&msg, 18);
        
        if(msg.check == RADIO_CHECK) {
          strncpy(var.radio_text, msg.text, 16);
          var.radio_count++;
          
          char buf[24];
          sprintf(buf, "%d:%s", var.radio_count, var.radio_text);
          gsm.SendSMS(PHONE_NUMBER, buf);
        }
      }
  } 
  
  // picture loop
  u8g.firstPage();  
  do {
    draw();
  } while( u8g.nextPage() );
  
  // rebuild the picture after some delay
  delay(50);
}

void sendStatus() {
  char buf[70];
  var.sms_out_count++;
  sprintf(buf, "%s%d/%d: power=%s, %d.%01dV, inside=%d.%d outside=%d.%d", 
    (var.start)?"":"restarted:",
    var.sms_in_count, var.sms_out_count, 
    (var.power_status)?"220V":"BAT",
    var.battery / 10, var.battery % 10,
    var.temperature_inside/10, abs(var.temperature_inside)%10,
    var.temperature_outside/10, abs(var.temperature_outside)%10);
  gsm.SendSMS(PHONE_NUMBER, buf);
  var.start = 1;
}

 boolean checkSMS()  //Check if there is an sms 'type_sms'
 {
     char pos_sms_rx;  //Received SMS position     
     pos_sms_rx=gsm.IsSMSPresent(SMS_UNREAD);
     if (pos_sms_rx!=0)
     {
       //Read text/number/position of sms
       char number_incoming[20];
       char sms_rx[34]; //Received text SMS
       gsm.GetSMS(pos_sms_rx,number_incoming,sms_rx,32);

#ifdef DEBUG       
       Serial.print("Received SMS from ");
       Serial.print(number_incoming);
       Serial.print("(sim position: ");
       Serial.print(word(pos_sms_rx));
       Serial.println(")");
       Serial.println(sms_rx);
#endif

       int error=gsm.DeleteSMS(pos_sms_rx);

#ifdef DEBUG
       if (error==1)Serial.println("SMS deleted"); else Serial.println("SMS not deleted");
#endif

       var.sms_in_count++;
       return true;
     }
     return false;
 }

 boolean checkCall()  //Check status call if this is available
 {     
     char number_incoming[20];
     int call=gsm.CallStatus();
     switch (call)
     {    
       case CALL_NONE:
         //Serial.println("no call");
         break;
       case CALL_INCOM_VOICE:
         gsm.CallStatusWithAuth(number_incoming,0,0);        
#ifdef DEBUG
         Serial.print("incoming voice call from ");     
         Serial.println(number_incoming);
#endif
         gsm.HangUp();
         var.sms_in_count++;
         return true;
         break;
       case CALL_ACTIVE_VOICE:
#ifdef DEBUG
         Serial.println("active voice call");    
#endif
         break;
       case CALL_NO_RESPONSE:
#ifdef DEBUG
         Serial.println("no response");
#endif
         break;
     }
     return false;
 }
 

и пример скетча для отправк-и сообщения

#include <SPI.h>
#include "RF24.h"
#include <string.h>

#define GSM_MONITOR 0x1000000000LL
#define RADIO_CHECK	0x0101

RF24 radio(9,10);

typedef struct {
	uint16_t check;
	char	text[16];	
} radio_msg;

void setup()
{
	Serial.begin(9600);
	
	radio.begin();
	radio.openWritingPipe(GSM_MONITOR);
	
	Serial.println("Board was started\n");
}

uint8_t count = 0;

void loop()
{
	char buf[48];

	radio_msg msg;
	uint8_t	payload;
	
	msg.check = RADIO_CHECK;
	sprintf(msg.text, "test%03d", count++);
	payload = sizeof(msg);
	
	radio.write(&msg, payload);
	
	sprintf(buf, "- text '%s' was send, payload=%d \n", msg.text, payload);
	Serial.print(buf);
	
	delay(10000);
}

 

axill
Offline
Зарегистрирован: 05.09.2011

а вот и готовое устройство, в выходные буду устанавливать

gena
Offline
Зарегистрирован: 04.11.2012

 А можно немного поподробнее про "готовый литиевый модуль из трех банок с встроенной защитой, 12.6В" - модель. А как понижаете напряжение до 5 В? Надеюсь импульсным блочком (на фото не видно).

axill
Offline
Зарегистрирован: 05.09.2011

импульсный преобразователь стоит на GBOARD

батарея не такая, но такого же типа http://www.buyincoins.com/item/8236.html#.VCzpQeffDx4

на фото у меня это черная коробочка

axill
Offline
Зарегистрирован: 05.09.2011

установил

п.с. кстати, зашел в личный кабинет МТС по симке установленной в мой девайс и обнаружил кучу платных подписок типа курса валют. Судя по дате подписки это произошло когда я отлаживал скетч. Очень странно. Берегите ваши деньги))  Не понятно как это произошло, видимо МТС умудрился любое действие кроме смс и звонка трактовать как подпись на сервисы)))

axill
Offline
Зарегистрирован: 05.09.2011

сделал на макетке радио сенсор как приставка к монитору. Эта штука будет мониторить запуск генератора, напряжение на его батарее и две температуры, скорее всего одна уличная, вторая в боксе с генератором

думал прикрутить все это к ардуине, но потом понял, что проще взять макетку и проводами спаять используя голый atmega328. Поставил туда LCD1602, радиомодуль NRF24L01, датчик сетевого напряжения на оптопаре PC817. Питание от аккумулятора геренатора, пришлось поставить два линейных стабилизатора, один на 5В (LCD не работает от 3В) и второй на 3.3В. Так же делитель на резисторах для замера напряжения на батарее.

Получая сообщения такого типа, где alert - это любой текст от любого радио датчика:

0/5: power=BAT, 12.0V, inside=27.0 outside=27.0 alert='BAT=10.5V'

а так новый сенсор