Беспроводной термодатчик для метеостанции на Arduino

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

Версия с LGT8F328P и LM75A:

Схема подключения:

«Схема» принимающего модуля.
  • Приемник LR43B подключается к выход D2 Arduino или LGT8F328P.

«Схема» передающего модуля, для самых маленьких.
  • Передатчик SYN115 подключается к выход D10 Arduino или LGT8F328P.
  • LM75A подключается к I2C интерфейсу, SDA и SCL микросхемы к A4 и A5 соответственно, входы нужно подтянуть резисторами на 10к к питанию, выходы A0, A1 и A2, (выбор адреса на шине) соединяются с массой.
  • Питание подключается на вход 5V микроконтроллера, никакое другое питание в этот момент, подключено быть не должно.


Код:

Пример для приемника, одинаков для всех вариантов передатчиков и должен работать без изменений. Перевод в градусы цельсия взят из примера работы с LM75 (без А), перевод 9 битный, вне зависимо от разрешения передатчика, шаг отображаемой температуры будет 0,5, это исправимо, но пока так.

Приемник
// receiver.pde
//
// Simple example of how to use VirtualWire to receive messages
// Implements a simplex (one-way) receiver with an Rx-B1 module
//
// See VirtualWire.h for detailed API docs
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2008 Mike McCauley
// $Id: receiver.pde,v 1.3 2009/03/30 00:07:24 mikem Exp $

#include <VirtualWire.h>

 

    /// медленный, экономный память способ
uint8_t crc8(const uint8_t *addr, uint8_t len) {
  uint8_t crc = 0;
  while (len--) {
    uint8_t inbyte = *addr++;
    for (uint8_t i = 8; i; i--) {
      uint8_t mix = (crc ^ inbyte) & 0x01;
      crc >>= 1;
      if (mix) crc ^= 0x8C;
      inbyte >>= 1;
    }
  }
  return crc;
}

//// This table comes from Dallas sample code where it is freely reusable,
//// though Copyright (C) 2000 Dallas Semiconductor Corporation
//static const uint8_t PROGMEM dscrc_table[] = {
//    0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
//  157,195, 33,127,252,162, 64, 30, 95,  1,227,189, 62, 96,130,220,
//   35,125,159,193, 66, 28,254,160,225,191, 93,  3,128,222, 60, 98,
//  190,224,  2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
//   70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89,  7,
//  219,133,103, 57,186,228,  6, 88, 25, 71,165,251,120, 38,196,154,
//  101, 59,217,135,  4, 90,184,230,167,249, 27, 69,198,152,122, 36,
//  248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91,  5,231,185,
//  140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
//   17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
//  175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
//   50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
//  202,148,118, 40,171,245, 23, 73,  8, 86,180,234,105, 55,213,139,
//   87,  9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
//  233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
//  116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};
////
//// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM
//// and the registers. (note: this might better be done without to
//// table, it would probably be smaller and certainly fast enough
//// compared to all those delayMicrosecond() calls. But I got
//// confused, so I use this table from the examples.)
////
//uint8_t crc8(const uint8_t *addr, uint8_t len) {
//  uint8_t crc = 0;
//  while (len--) {
//    crc = pgm_read_byte(dscrc_table + (crc ^ *addr++));
//  }
//  return crc;
//}

void setup()
{
  Serial.begin(9600);	// Debugging only
  Serial.println("setup");

  vw_set_rx_pin(2);
  vw_setup(1024);  // Bits per sec
  vw_rx_start();       // Start the receiver PLL running

}

void loop()
{
  uint8_t msb, lsb = 0; 
  float Grad;
  
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

  if (vw_get_message(buf, &buflen)) // Non-blocking
  {
    digitalWrite(13, true); // Flash a light to show received good message
    // Message with a good checksum received, dump it.
    Serial.print("Got: ");  
      for (int i = 0; i < buflen; i++)    {
        Serial.print(buf[i], HEX);
        Serial.print(" ");
      }
    
    Serial.println("");
    digitalWrite(13, false);

 //// температура                                   
    msb = buf[1];                                  
    lsb = buf[2]; 
  if (msb < 0x80)  {
    Grad = ((msb * 10) + (((lsb & 0x80) >> 7) * 5));
  }
  else  {
    Grad = ((msb * 10) + (((lsb & 0x80) >> 7) * 5));
    Grad = (2555.0 - Grad);
  }
  Grad = Grad / 10;

 
  Serial.print("ID: ");  
  Serial.println( buf[0]);
  Serial.print("Temp: ");  
  Serial.println(Grad);
  Serial.print("crc: ");  
  Serial.print (buf[3]);
  Serial.print(" = ");  
  Serial.println(crc8((uint8_t*)&buf, buflen - 1));
  Serial.println("");
  } 
}

Передатчик LM75A и 8F328P
#include <Wire.h>
#include <PMU.h>
#include <VirtualWire.h>
  
#define LM75_ADDRESS 0x48 
#define LM75_TEMP_REGISTER 0
#define LM75_CONF_REGISTER 1 
#define LM75_CONF_SHUTDOWN  0

#define TXPIN 10              // нога OOK радиомодуля 
#define LED 13                // светодиод на плате, мигает при передачи
#define SETIDPIN 7            // перемычка выбор id

uint8_t ID = 0;
uint8_t oldmsb = 0;
uint8_t Send[4];

// This table comes from Dallas sample code where it is freely reusable,
// though Copyright (C) 2000 Dallas Semiconductor Corporation
static const uint8_t PROGMEM dscrc_table[] = {
    0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
  157,195, 33,127,252,162, 64, 30, 95,  1,227,189, 62, 96,130,220,
   35,125,159,193, 66, 28,254,160,225,191, 93,  3,128,222, 60, 98,
  190,224,  2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
   70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89,  7,
  219,133,103, 57,186,228,  6, 88, 25, 71,165,251,120, 38,196,154,
  101, 59,217,135,  4, 90,184,230,167,249, 27, 69,198,152,122, 36,
  248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91,  5,231,185,
  140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
   17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
  175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
   50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
  202,148,118, 40,171,245, 23, 73,  8, 86,180,234,105, 55,213,139,
   87,  9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
  233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
  116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};
//
// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM
// and the registers. (note: this might better be done without to
// table, it would probably be smaller and certainly fast enough
// compared to all those delayMicrosecond() calls. But I got
// confused, so I use this table from the examples.)
//
uint8_t crc8(const uint8_t *addr, uint8_t len) {
  uint8_t crc = 0;
  while (len--) {
    crc = pgm_read_byte(dscrc_table + (crc ^ *addr++));
  }
  return crc;
}

void shutDownLM75(boolean val) {           /// 1 выкл, 0 вкл
  Wire.beginTransmission(LM75_ADDRESS);
  Wire.write(LM75_CONF_REGISTER);
  Wire.write(val << LM75_CONF_SHUTDOWN);
  Wire.endTransmission();  
}
 
void setup()
{
  Wire.begin(); 
   vw_set_tx_pin(TXPIN);
   vw_set_ptt_inverted(false);                 // false! иначе передатчик останется включенным
   vw_setup(1024);                             // Bits per sec

  pinMode(LED, OUTPUT); 
  digitalWrite(LED, LOW);   
   
  pinMode(SETIDPIN, INPUT_PULLUP);             // читаем состояние ноги 
  ID = digitalRead(SETIDPIN);  
  pinMode(SETIDPIN, INPUT);                   // исключаем подтягивающий резистор
 
}

void loop(){
  
  shutDownLM75(0);                            // включаем термометр
  PMU.sleep(PM_POFFS0, SLEEP_256MS);          // ждем пока он очухается (можно SLEEP_128MS)
                                        // читаем регистры LM75
  uint8_t msb, lsb = 0;   
  Wire.beginTransmission(LM75_ADDRESS);
  Wire.write(LM75_TEMP_REGISTER);
  Wire.endTransmission(LM75_ADDRESS);
  Wire.requestFrom(LM75_ADDRESS, 2);
//  while(Wire.available() < 2);                  /// тут может все поламаться
  msb = Wire.read();
  lsb = Wire.read();

                                         // если температура изменилась 
  if (oldmsb != msb) {                             // смотрим в старший бит 
     oldmsb = msb;                                 // сохраняем для следующей проверки 
                                                   // заполняем массив 
     Send[0] = ID;                                    
     Send[1] = msb;                                  
     Send[2] = lsb;
     Send[3] = crc8((uint8_t*)&Send, sizeof(Send) - 1);
                                                // отправляем 
digitalWrite(LED, HIGH);  
     vw_send((uint8_t *)Send, sizeof(Send));
     vw_wait_tx();                                // Wait until the whole message is gone
digitalWrite(LED, LOW);
   
    shutDownLM75(1);                              // выключаем градусник 
//    PMU.sleep(PM_POWERDOWN, SLEEP_1S);            
    PMU.sleep(PM_POWERDOWN, SLEEP_32S);           // вообще все выключаем на 32 сек 
  }
  else {                                          /// иначе спим дальше 
     shutDownLM75(1);                              // выключаем градусник
//     PMU.sleep(PM_POWERDOWN, SLEEP_1S);  
     PMU.sleep(PM_POWERDOWN, SLEEP_32S);
    
  }

  
//  if (msb < 0x80)
//  {
//    Grad = ((msb * 10) + (((lsb & 0x80) >> 7) * 5));
//  }
//  else
//  {
//    Grad = ((msb * 10) + (((lsb & 0x80) >> 7) * 5));
//    Grad = (2555.0 - Grad);
//  }
//
//  Grad = Grad / 10;
 
 
}

Видео:


Версия датчика с LGT8F328P и TMP100 \ TMP102:

TMP100 \ TMP102 схожие по характеристикам термодатчики с низким энергопотреблением, имеют аналогичные LM75A регистры с температурой и могут его заменить без особых изменений в коде. Датчики TMP100 и TMP102 имеют одинаковые регистры, код передатчика ниже работает с обоими без изменений, описание датчиков на странице тут и тут. TMP102 имеет корпус SOT563 сложный для ручного монтажа, но доступен в виде модуля, у TMP100 более привычный корпус SOT23-6.

Передатчик LGT8F328P и TMP100 TMP102
////// датчик температуры с OOK радиомодулем и батарейным питанием
/////  TMP100 и 8F328P    

#include <Wire.h>
#include <PMU.h>
#include <VirtualWire.h>
  
#define TMP100_ADDRESS 0x48 
#define TMP100_TEMP_REGISTER 0
#define TMP100_CONF_REGISTER 1  
#define TMP100_SETRESOLUTION 10       // 9 - 12 bit

#define TXPIN 10              // нога OOK радиомодуля 
#define LED 13                // светодиод на плате, мигает при передачи
#define SETIDPIN 7            // перемычка выбор id

uint8_t ID = 0;
uint8_t oldmsb = 0;
uint8_t Send[4];

// This table comes from Dallas sample code where it is freely reusable,
// though Copyright (C) 2000 Dallas Semiconductor Corporation
static const uint8_t PROGMEM dscrc_table[] = {
    0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
  157,195, 33,127,252,162, 64, 30, 95,  1,227,189, 62, 96,130,220,
   35,125,159,193, 66, 28,254,160,225,191, 93,  3,128,222, 60, 98,
  190,224,  2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
   70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89,  7,
  219,133,103, 57,186,228,  6, 88, 25, 71,165,251,120, 38,196,154,
  101, 59,217,135,  4, 90,184,230,167,249, 27, 69,198,152,122, 36,
  248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91,  5,231,185,
  140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
   17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
  175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
   50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
  202,148,118, 40,171,245, 23, 73,  8, 86,180,234,105, 55,213,139,
   87,  9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
  233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
  116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};
//
// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM
// and the registers. (note: this might better be done without to
// table, it would probably be smaller and certainly fast enough
// compared to all those delayMicrosecond() calls. But I got
// confused, so I use this table from the examples.)
//
uint8_t crc8(const uint8_t *addr, uint8_t len) {
  uint8_t crc = 0;
  while (len--) {
    crc = pgm_read_byte(dscrc_table + (crc ^ *addr++));
  }
  return crc;
}

void shutDownTMP100(boolean val) {           /// 1 выкл, 0 вкл
  Wire.beginTransmission(TMP100_ADDRESS);
  Wire.write(TMP100_CONF_REGISTER);
  Wire.write((TMP100_SETRESOLUTION - 9) << 5 | val);
  Wire.endTransmission();   
  
}
 
void setup()
{
//   Serial.begin(9600);
  Wire.begin(); 
   vw_set_tx_pin(TXPIN);
   vw_set_ptt_inverted(false);                 // false! иначе передатчик останется включенным
   vw_setup(1024);                             // Bits per sec

  pinMode(LED, OUTPUT); 
  digitalWrite(LED, LOW);   
   
  pinMode(SETIDPIN, INPUT_PULLUP);             // читаем состояние ноги 
  ID = digitalRead(SETIDPIN);  
  pinMode(SETIDPIN, INPUT);                   // исключаем подтягивающий резистор
 
}

void loop(){
  
  shutDownTMP100(0);                            // включаем термометр
  PMU.sleep(PM_POFFS0, SLEEP_256MS);          // ждем пока он очухается (можно SLEEP_128MS)
                                          // читаем регистры 
  uint8_t msb, lsb = 0;   
  Wire.beginTransmission(TMP100_ADDRESS );
  Wire.write(TMP100_TEMP_REGISTER);
  Wire.endTransmission(TMP100_ADDRESS);
  Wire.requestFrom(TMP100_ADDRESS, 2);
//  while(Wire.available() < 2);                  /// тут может все поламаться
  msb = Wire.read();
  lsb = Wire.read();

                                         // если температура изменилась 
   if (oldmsb != msb) {                             // смотрим в старший бит  
     oldmsb = msb;                                 // сохраняем для следующей проверки 
                                                   // заполняем массив 
     Send[0] = ID;                                    
     Send[1] = msb;                                  
     Send[2] = lsb;
     Send[3] = crc8((uint8_t*)&Send, sizeof(Send) - 1);
                                                // отправляем 
digitalWrite(LED, HIGH);   
     vw_send((uint8_t *)Send, sizeof(Send));
     vw_wait_tx();                                // Wait until the whole message is gone
digitalWrite(LED, LOW);
   
    shutDownTMP100(1);                              // выключаем градусник 
//     PMU.sleep(PM_POWERDOWN, SLEEP_1S);            
     PMU.sleep(PM_POWERDOWN, SLEEP_32S);           // вообще все выключаем на 32 сек  
  }
  else {                                          /// иначе спим дальше 
     shutDownTMP100(1);                              // выключаем градусник
//      PMU.sleep(PM_POWERDOWN, SLEEP_1S);  
      PMU.sleep(PM_POWERDOWN, SLEEP_32S); 
  }
  
 
//  float Grad;
//  int TemperatureSum = ((msb << 8) | lsb) >> 4;
//
//  Serial.println(TemperatureSum*0.0625);
//  //delay(1000); 
 
 
}