Батарейный радио датчик температуры на pro mini + nrf24

pav2000
Offline
Зарегистрирован: 15.12.2014

В качестве освоения режимов потребления МК решил сделать датчик на nrf24.  Также этот датчик позволет мне мониторить проблемные участи дома с выводом информации на головной блок (http://arduino.ru/forum/proekty/udalennyi-monitoring-za-datchikami-na-ma...)

1. Выбор элементной базы.

В качестве контроллера решил использовать pro mini  3.3 вольта 8 мГц.  Сама плата была доработана по "классике" (в сети по этому много инфо) . Аппаратная доработка свелась к трем вещам -  1. Удаление светодиода по питанию, 2. Удаление входного стабилизатора, 3 Удаление  светодиода на 13 ноге. Обычно 3 пункт не описан, но в моем случае используется SPI и удалить для уменьшения потребления все таки нужно.

Тонкости.

Для программирования pro mini я использую конвертор usb uart на ft232. На плате был установлен переключатель 3.3-5 вольт но он только управлял уровнем выходов-входов ft232. Т.к. ft232 имеет встроенный стабилизатор на 3.3 (50мА) я доработал плату (один разрез и один провод кинуть) и теперь при положении 3.3 вольта на pro mini подается питание 3.3 от стабилизатора . ft232.   Зачем ,????????   А затем, что бы nrf24 правильно запитать при отладке.

Выбор датчика.

Использовал ВМ180 т.к. хотел посмотреть как работает датчик давления. Особенность - питание на датчик снимается с 10 ноги контроллера для уменьшение энергопотребления. Т.е. для активации датчика надо записать на 10 ногу 1 и инициализировать датчик. Не факт что это правильное решение. Плюсом такого решения является что в выключенном состянии датчик ничего не потребляет, минусом что увеличивается время в режиме "работа".  Это вопрос требует дальнейшего изучения. Что более энергетически эффективно.

nrf24 - все соединено по классике. Была только одна проблема. Вначале я поставил  плату nrf24 с усилителем. При отладке (питание от ft232) все было хоорошо, при питании от батареи приходилось уменьшать мощность до уровня LOW иначе данны не уходили. Я не верю что батаейка не может отдать необходимый ток, Причина в ином - но в чем не разобрался, Поменял плату на "без усилителя" там можно рабоать на максимальной мощности.   Может кто подскажет?

Элемент питания.  выбор пал на литиевый элемент на основе Li-SOCl2 (литий-тионилхлорида) с напряжением 3.6 воьлта размером 1/2 АА. Емкость обещают 1200 мА ч.

Сразу решил что буду ставить только одну батарейку для простоты монтожа и уменьшения габаритов - т.е это дало диапазон напряжений 3-4 вольта - литевый элемент. Размер 1/2 АА получился при компоновке устройства.

2. Программа

Использовал библиотеки для работы с датчиком и управления режимами потребления контроллера. Здесь все стандартно.

Программа проста и не затейлева.

WDT таймер настроен на 8 секунд он будит МК. Каждое 9 просыпание считывается датчик и копится сумма показаний. Каждое 9*4 просыпание данные передаются в головной блок.  8*9*4=288 секунд около 5 минут.

НО есть единственная тонкость с которой я бился целый вечер. При уходе в режим power_down потребление оставалось на уровне 1.2 мА что не приемлемо.  Долго копался, решения на русскоговорящих форумах не нашел, на англ. есть намеки типа копайте ноги SPI.  Я выяснил что виновата связка  SPI+nrf24. Через выходы течет ток и он влиеет на потребление Решене:

Выключение

//  Выключение чипа nrf24
  radio.powerDown();  // выключаем чип но это не все!!!
  SPI.end();          // выключаем SPI
  PullUp(B,5);        // slk - сделать входом и подтянуть к 3.3 вольтам
  PullUp(B,3);        // mosi - сделать входом и подтянуть к 3.3 вольтам

Включение

 //  Включение чипа nrf24
  SetOutput(B,5);     // slk - выход сделать
  SetOutput(B,3);     // mosi - выход сделать
  SPI.begin();        // старт spi заново
  radio.powerUp();

После этого потребление стало 42 мкА, что совпадает с расчетами (13-15 nrf24 20-25-pro mini).

В режиме сна на батарейке 1200 мА проработает около 28 000 часов (я понимаю что это теория но цифры внушают -))).

Есть еще режим работы  там потребелние колеблится от 4 до 20 мА, но время такого режима мкс.  А передатчик (20 мА) активируется вообще раз в 5 минут.

Так что работать должна я думаю миниум 1 год.

3. Конструктив.

Традиционно корпус напечатан на 3Д принтере (нижняя часть сделана одной деталью). Очень удобно, все отверстия сразу сделаны, только заусецы счистить и можно использовать.

Батарейка приклеена внутри термоклеем, Провода припаяны (использована кислота для уменьшения времени нагрева, не забыть промыть).  Все элемены соеденены проводом . С nrf24 снят разъем (автоматизированный отсос с паяльникм - вещь!!!!). Плата МК и nrf24 склеены между собой термоклеем (сторонами где нет деталей). Выключатель поставлен для того чобы вообще не менять батарейку. Когда не используешь - выключи.

Получилось компактно и просто.

4. Косяки

Периодически теряется связь с датчиком, Выснил что проблема в датчике. Включение выключение датчика помогает. Пока не разобрался. Может чтото отваливается в power_down, надо выяснить.

Для увеличения дальности надо ставить модуль nrf24 с внешней антенной

Конструктивная критика как всегда приветствуется.-))

Исходники файл  temp_sensor.ino:

/* SFE_BMP180 library example sketch
Hardware connections:
- (GND) to GND
+ (VDD) to 3.3V
(WARNING: do not connect + to 5V or the sensor will be damaged!)
You will also need to connect the I2C pins (SCL and SDA) to your
Arduino. The pins are different on different Arduinos:
Any Arduino pins labeled:  SDA  SCL
Uno, Redboard, Pro:        A4   A5
Mega2560, Due:             20   21
Leonardo:                   2    3
*/

#include <SFE_BMP180.h>
#include <Wire.h>
#include "LowPower.h"
#include <SPI.h>
#include "nRF24L01.h"  // Беcпроводной модуль надо использовать библиотеку http://tmrh20.github.io/RF24
#include "RF24.h"      // Беcпроводной модуль используются не стандартные функции https://github.com/TMRh20


// - ОПЦИИ -------------------------------
//#define DEBUG                                   // Отладочную  информацию в ком порт посылает  
//#define DEMO                                    // Признак демонстрации - данные с датчиков генерятся рандом
#define VERSION_DATA "301115"                   // Дата текущей версии  в формате ДДММГГ
#define VERSION      "0.15"                     // Текущая версия прошивки
#define ID            0x31                      // уникально Идентификатор устройства (тип) - старшие 4 бита, вторые (младшие) 4 бита серийный номер устройства
#define LABEL "BM-180"                          // Название блока
#define NRF24_CHANEL  100                       // Номер канала nrf24

// Ноги куда подключаем переферию
#define PIN_CE        7                          // nrf24 ce
#define PIN_CSN       6                          // nrf24 csn

// Макросы для работы с портами  скорость и место
#define SetOutput(port,bit)       DDR ## port |= _BV(bit)
#define SetInput(port,bit)        DDR ## port &= ~_BV(bit)
#define SetBit(port,bit)          PORT ## port |= _BV(bit)
#define ClearBit(port,bit)        PORT ## port &= ~_BV(bit)
#define WritePort(port,bit,value) PORT ## port = (PORT ## port & ~_BV(bit)) | ((value & 1) << bit)
#define ReadPort(port,bit)        (PIN ## port >> bit) & 1
#define PullUp(port,bit)          { SetInput(port,bit); SetBit(port,bit); }
#define Release(port,bit)         { SetInput(port,bit); ClearBit(port,bit); }

// - ВРЕМЕНА ---------------------------------------
#ifdef DEMO                                     // Для демо все быстрее
    #define TIME_WDT SLEEP_2S                   // Период сторожевого таймера
    #define TIME_SCAN_SENSOR 2                  // Время опроса датчиков в 8 секундных интервалах (сторожевого таймера)
    #define TIME_SEND_SENSOR 4                  // Время посылки данных (равно усреднению)
#else   
    #define TIME_WDT SLEEP_8S                   // Период сторожевого таймера
    #define TIME_SCAN_SENSOR 9                  // Время опроса датчиков в 8 секундных интервалах (сторожевого таймера)
    #define TIME_SEND_SENSOR 4                  // Время посылки данных (равно усреднению) 288 секунд почти пять минут
#endif

// АЦП ----------------------------------------
const long ConstADC=1126400;                    // Калибровка встроенного АЦП (встроенный ИОН) по умолчанию 1126400

// Переменные которые сохраняются в памяти без инициализации
int S8_tick __attribute__((section(".noinit")));           // число 8 секундных интервалов с последнего измерения
//int scan_tick __attribute__((section(".noinit")));         // счетчик измерений (опроса датчика)
unsigned long tt __attribute__((section(".noinit")));      // Время пришедшее от головного блока если 0 то время не приходило
// Пакет передаваемый, используется также для хранения УСРЕДНЕННЫХ результатов. 
 struct type_packet_0x30_NRF24                   // Версия 1.1!! адаптация для stm32 Структура передаваемого пакета 32 байта - 32 максимум
    {
        byte id=ID;                             // Идентификатор типа устройства - старшие 4 бита, вторые (младшие) 4 бита серийный номер устройства
        byte error=0;                           // Ошибка блока, пока резерв
        uint16_t  Vcc;                          // Напряжение батареи  Милливольты!!!!!
        int16_t  Temp;                          // Температура датчика  сотые градуса
        uint16_t  relH;                         // Относительная влажность датчика  сотые %
        uint16_t  absH;                         // Абсолютная влажность датчика сотые грамма на куб
        uint16_t  P;                            // Давление  милибары 
        uint16_t count=0;                       // Циклический счетчик пакетов 2 байта хватит на долго - около 3000 часов
        char ver[5] = VERSION;                  // Версия прошивки не более 4 байт + "0" символ 
        char note[13] = LABEL;                  // Примечание не более 12 байт + "0" байт Русские буквы в два раза меньше т.к. UTF-8
    } packet_0x30 __attribute__((section(".noinit")));


struct type_sensors   // структура для усреднения измерений
{      int16_t  num;                           // Число накопленных образцов
       int32_t  Vcc=0;                         // Напряжение батареи  Милливольты!!!!!
       int32_t  Temp=0;                        // Температура датчика  сотые градуса
       int32_t  relH=0;                        // Относительная влажность датчика  сотые %
       int32_t  absH=0;                        // Абсолютная влажность датчика сотые грамма на куб
       int32_t  P=0;                           // Давление  резерв
 } sensor __attribute__((section(".noinit")));

SFE_BMP180 pressure;          //  датчик BMP180
RF24 radio(PIN_CE, PIN_CSN);  // определение управляющих ног

#define ALTITUDE 1655.0 // Altitude of SparkFun's HQ in Boulder, CO. in meters

void setup()
{  int volt=0;
   S8_tick=0;
   tt=0;
   reset_sum();
   // 1. Проверка питания  мигание светодиода показывает напряжение батареи
  
  volt=readVcc();
 /* if (volt>3300) LedBlink(4, 40);
   else if (volt>3000) LedBlink(3, 40);
    else if (volt>2700) LedBlink(2, 40);
     else if (volt>2400) LedBlink(1, 40);
 */
   #ifdef  DEBUG  
    Serial.begin(115200); 
    Serial.print(F("\n\n1. Version: "));
    Serial.print(F(VERSION));
    Serial.print(F(" "));
    Serial.println(F(VERSION_DATA));
    
    Serial.print(F("2. Battery voltage: "));
    Serial.println(((float)volt)/1000.0,2);
    Serial.print(F("3. NRF24 init . . .     "));
    Serial.flush();
  # endif 
  
 // 2.Инициализация радиомодудя
 char buf[10];
   if (setRadio()==true)     // радио проинициализировано удачно 
    {                
       #ifdef  DEBUG  
       Serial.println(F("OK"));
       // внимание функция read_register сделана в библиотеке публичной
       sprintf(buf,"%02x",radio.read_register(CONFIG));    Serial.print(F("  3.1 Register CONFIG:     0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(SETUP_AW));  Serial.print(F("  3.2 Register SETUP_AW:   0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(SETUP_RETR));Serial.print(F("  3.3 Register SETUP_RETR: 0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(RF_CH));     Serial.print(F("  3.4 Register RF_CH:      0x")); Serial.println(buf); 
       sprintf(buf,"%02x",radio.read_register(RF_SETUP));  Serial.print(F("  3.5 Register RF_SETUP:   0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(NRF_STATUS));Serial.print(F("  3.6 Register NRF_STATUS: 0x")); Serial.println(buf);
       #endif 
       radio.powerDown();  // Выключить радиомодуль
    }
  else  // радио не работает
    {
       #ifdef  DEBUG  
       Serial.println(F("FAIL"));
       #endif 
    } 
   
  // 3. Инициализация сенсора
  pinMode(10, OUTPUT);  
  digitalWrite(10, HIGH);  
  delay(10);
  if (pressure.begin())
  { 
    #ifdef  DEBUG  
    Serial.println(F("4. BMP180 init OK\n"));
    Serial.flush();
    #endif 
  }  
  else
  {
    #ifdef  DEBUG  
    Serial.print(F("4. BMP180 init FAIL, error:"));
    Serial.println(pressure.getError());
    Serial.print(F("\n"));
    Serial.flush();
   #endif
   }
   digitalWrite(10, LOW);  
   pinMode(10, INPUT);  
   // Ошибки датчика ВМР180 связанные с чтением шины i2c  
   // 0 = Success
   // 1 = Data too long to fit in transmit buffer
   // 2 = Received NACK on transmit of address
   // 3 = Received NACK on transmit of data
   // 4 = Other error
   packet_0x30.error=pressure.getError()-'0'; // Ошибка в виде символа переводим в число 
   
   // Для проверки связи сразу посылаем пакет
   measurement();
   packet_0x30.Vcc=sensor.Vcc;
   packet_0x30.Temp=sensor.Temp;
   packet_0x30.relH=sensor.relH;
   packet_0x30.absH=sensor.absH;
   packet_0x30.P=sensor.P/10;
   reset_sum(); 
   sendRadio();  // Послать даннные  
 
}

void loop()
{
   
  if (S8_tick>=TIME_SCAN_SENSOR-1)   // Пора опрашивать датчик
  {
   #ifdef  DEBUG  
    Serial.print(F("PowerUp and scan sensor"));
//    Serial.flush();
   #endif  
   S8_tick=0;
  // сюда вставить функцию измерения и накопления суммы
   measurement();
  } 
  else 
  {
   #ifdef  DEBUG  
    Serial.println(F("PowerUp"));
    Serial.flush();
   #endif  
  S8_tick++; 
  } 
  
 if (sensor.num>=TIME_SEND_SENSOR)   // Пора посылать данные
  {
   // Усреднение 
   packet_0x30.Vcc=sensor.Vcc/TIME_SEND_SENSOR;
   packet_0x30.Temp=sensor.Temp/TIME_SEND_SENSOR;
   packet_0x30.relH=sensor.relH/TIME_SEND_SENSOR;
   packet_0x30.absH=sensor.absH/TIME_SEND_SENSOR;
   packet_0x30.P=(sensor.P/TIME_SEND_SENSOR)/10;
   reset_sum();
   sendRadio();  // Послать даннные
   } 
   LowPower.powerDown(TIME_WDT, ADC_OFF, BOD_OFF);   // СПАТЬ  
}

// Чтение напряжения питания ----------------------------------------------
long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = ConstADC / result; // Back-calculate AVcc in mV
  return result;
  
} 

//  Установка радиомодуля  
bool setRadio(void)
{

//   if (radio.begin()==false) return false;  // Если модуль не стартует то выходим 
   radio.begin();
   radio.setDataRate(RF24_250KBPS);         // выбор скорости RF24_250KBPS RF24_1MBPS RF24_2MBPS
   radio.setPALevel(RF24_PA_MAX);           // выходная мощность передатчика RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX,
   radio.setChannel(NRF24_CHANEL);          // тут установка канала
   radio.setCRCLength(RF24_CRC_16);         // использовать контрольную сумму в 16 бит
   radio.setAutoAck(true);                  // выключить аппаратное потверждение
// radio.enableDynamicPayloads();           // разрешить Dynamic Payloads
   radio.enableAckPayload();                // Разрешить ответ приемника AckPayload
   radio.setRetries(50,10);                 // Количество пауза и количество повторов 
  // Рекомендуют первые 2-4 байта адреса устанавливать в E7 или 18 он проще детектируется чипом
  radio.openWritingPipe(0xE7E7E7E7E1LL);    // передатчик 
  radio.openReadingPipe(1,0xE7E7E7E7D2LL);  // приемник
  radio.startListening();
  radio.powerDown();
  return true;
 }
//  Посылка пакета 
bool sendRadio(void)
{ 
   bool send_packet_ok=false;
  //  Включение чипа nrf24
  SetOutput(B,5);     // slk - выход сделать
  SetOutput(B,3);     // mosi - выход сделать
  SPI.begin();        // старт spi заново
  radio.powerUp();
  
  radio.stopListening();     // Остановить приемник перейти в режим передачи
  delay(2);                  // дать время на переключение
  cli();
  radio.writeBlocking(&packet_0x30,sizeof(packet_0x30),200);  // Writes 1 payload to the buffers
  send_packet_ok=radio.txStandBy();
  if ( radio.isAckPayloadAvailable() )            // Ждем получения -- основной блок передает текущее время
           radio.read(&tt,sizeof(tt));            //... и имеем переменную tt с временем от приемника.
  sei(); 
      
  #ifdef  DEBUG  
    if (send_packet_ok==true) { Serial.println(F("Packet sending ++++++++++")); LedBlink(1, 10);}
    else                        Serial.println(F("Packet NOT sending -----------"));
    Serial.flush();
   #endif   
   
  //  Выключение чипа nrf24
  radio.powerDown();  // выключаем чип но это не все!!!
  SPI.end();          // выключаем SPI
  PullUp(B,5);        // slk - сделать входом и подтянуть к 3.3 вольтам
  PullUp(B,3);        // mosi - сделать входом и подтянуть к 3.3 вольтам

  packet_0x30.count++;                           // при переполнении сам сбросится
 } 
// Помигать светодиодом на 5 ноге N раз длительность t мсек
void LedBlink(int n, int t)
{
  int i;
  SetOutput(D,5);                   //  5 ногу на вывод
  WritePort(D,5,LOW);               // Погасить светодиод
  for(i=1;i<=n;i++)
  {
     WritePort(D,5,HIGH);         // зажечь светодиод  
     delay(t);
     WritePort(D,5,LOW);          // Погасить светодиод
     if ((n>1)&&(i<n)) delay(500);  // если один раз или последний раз то задержка не нужна
     
  }
 SetInput(D,5);                   //  5 ногу на вход 
}

void reset_sum(void)  // Сброс счетчиков накоплений
{  sensor.num=0;
   sensor.Vcc=0;
   sensor.Temp=0;
   sensor.relH=0;
   sensor.absH=0;
   sensor.P=0;  }

// Измерение датчика
bool measurement(void)
{
  char status;
  double T,P,p0,a;
  packet_0x30.error=0;
  
  pinMode(10, OUTPUT);  
  digitalWrite(10, HIGH);  
  delay(10);
  pressure.begin();
  
  status = pressure.startTemperature();
  if (status != 0)
  {
    delay(status);
    status = pressure.getTemperature(T);
    packet_0x30.error=pressure.getError();  // Запомнить ошибку 
    if (status != 0)
    {
      #ifdef  DEBUG  
      Serial.print(F(" Temperature: "));
      Serial.print(T,2);
      #endif
       
      status = pressure.startPressure(3);
      if (status != 0)
      {
        delay(status);
        status = pressure.getPressure(P,T);
        packet_0x30.error=packet_0x30.error+8*(pressure.getError());  // Запомнить ошибку 
        if (status != 0)
        {;
          #ifdef  DEBUG 
          Serial.print(F(" C absolute pressure: "));
          Serial.print(P,2);
          Serial.println(F(" mb"));
          Serial.flush();
          #endif
         }
        else  
        {;
        #ifdef  DEBUG  
        Serial.println("error retrieving pressure measurement\n"); 
        #endif
        }
      }
      else 
      {;
      #ifdef  DEBUG 
      Serial.println("error starting pressure measurement\n"); 
      #endif
      }
    }
    else 
    {;
    #ifdef  DEBUG 
    Serial.println("error retrieving temperature measurement\n"); 
    #endif
    }
  }
  else 
  {;
  #ifdef  DEBUG 
  Serial.println("error starting temperature measurement\n");  
  #endif
  }
   digitalWrite(10, LOW);  
   pinMode(10, INPUT); 
if (packet_0x30.error==0)  // Если нет ошибок усредняем
{
   sensor.num++;
   sensor.Vcc=sensor.Vcc+readVcc();
   sensor.Temp=sensor.Temp+T*100;
   sensor.P=sensor.P+P*10;  
}

} 


Фото для понимания конструктива

Уф много букв.....

Кто дочитал до конца Молодец -))

a5021
Offline
Зарегистрирован: 07.07.2013

Зачем толстый кондер сверху?

yucan
Offline
Зарегистрирован: 20.04.2015

Электролит, по питанию... Акумулятор понравился, компактный

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

yucan, "nrf24 - все соединено по классике" Это как? Я прочёл, но так и не понял как вы запитали NRF24. И вполне возможно, что  "Периодически теряется связь с датчиком, Выснил что проблема в датчике. Включение выключение датчика помогает." есть как раз следствие неправильного подключения.

toc
Offline
Зарегистрирован: 09.02.2013

pav2000, спасибо, интересно.

> это дало диапазон напряжений 3-4 вольта

Уточните в даташите. По-моему, для nrf24l01+ максимальное допустимое напряжение питания 3.6 В. Вы рискуете давая 4.

yucan
Offline
Зарегистрирован: 20.04.2015

dimax пишет:

yucan, "nrf24 - все соединено по классике" Это как? Я прочёл, но так и не понял как вы запитали NRF24. И вполне возможно, что  "Периодически теряется связь с датчиком, Выснил что проблема в датчике. Включение выключение датчика помогает." есть как раз следствие неправильного подключения.

Это  пишет автор темы...

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

yucan, да проглядел)

toc
Offline
Зарегистрирован: 09.02.2013

pav2000, я использую следующий способ определения наличия радио модуля и вам предлагаю заменить 254 строку на

return radio.getChannel() == NRF24_CHANEL;

хотя, лучше это проверить раньше, сразу после setChannel

pav2000
Offline
Зарегистрирован: 15.12.2014

По питанию nrf24.

Питание nrf24  соеденино с  Vcc и с "+" батареи напрямую.  По питанию стоит кондесатор 470 мкф. Сейчас напряжение питания 3.54 вольта и находится  в допустимых приделах. Надо еще керамику припаять на плату nrf24 1-2 мкф для спокойствия. Хотя может и здесь проблема  т.е. 3.6 "китайские" могут быть меньше 3.54 "европейским"

Дипапазон 3-4 вольта был мною установлен только при ВЫБОРЕ батарии, т.е я решил не брать 1.5 вольта, 6 вольт. И выбрал батарею с напряжеинем (максимальным) 3.6 вольта помня об nrf24.

Сейчас моя идея о потери связи связана с тем что nrf24 не успевает просыпаться а я уже шлю команды. Поставил (увеличил) задержку  - пока блок работает без сбоев более 20 часов.

Спасибо конечно "return radio.getChannel() == NRF24_CHANEL;" .

Но вставлять ее тогда  надо не в функцию SetRadio  которая устанавливает настройки nrf24 и вызавается один раз при старте (вероятность что будет все ОК приближается к 100%). Ставить ее на в SendRadio - функцию отправки пакета и делать механизм повтороной (например до 5 раз) отправки (возможно и инициализации nrf24) пакета.

О . . .   Пока писал пришла шикарно-богатая идея.   В ЕЕПРОМ писать лог ошибок что бы потом разобраться что происходит.  Тем более часики есть. Обязательно прикручу.

 

a5021
Offline
Зарегистрирован: 07.07.2013

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

pav2000
Offline
Зарегистрирован: 15.12.2014

a5021 пишет:

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

Все примерно так, но есть нюансы. Есть еще внутреннее сопротивление источника. Оно влияет на выходное напряжение. 

Сначала конденсатора вообще не было, и стояла плата nrf24 с усилителем. Передача вообще не работала. При этом при питании от ft232 с его внутреннем источником в 50 мА было все ок. Почему так для меня загадка.

Добавление конденсатора и уменьшение выходной мощности решили (уровень low) проблему. При замене на радиомодуль без усилителя я конденсатор оставил (при этом уровень мощности установлен максимальный).

Nrf24 очень требовательна к питанию.

Это похоже на шаманство но без него не работало.

Может без него не корректно сброс работает? Я не знаю.

spa-sam
Offline
Зарегистрирован: 14.12.2012

По поводу Nrf24 и той или иной неработоспособности, в одном из форумов очень неплохо обсуждается и проблема остаётся.

http://forum.mysensors.org/topic/1664/which-are-the-best-nrf24l01-module...

a5021
Offline
Зарегистрирован: 07.07.2013

pav2000 пишет:
Есть еще внутреннее сопротивление источника. Оно влияет на выходное напряжение.
Верное утверждение, которое, однако, никак не объясняет смысла установки кондера параллельно батарейке.

Конденсатор по питанию ставят для подавления помех, сглаживания пульсаций и для "размазывания" короткоимульсного характера потребления значительных токов. Это не все случаи, но наиболее частые. В вашей ситуации батарея 14250 имеет рабочий ток на порядок (в десять раз) превышающий максимальный ток потребления NRF24L01 и при этом ее внутреннее сопротивление настолько мало, что она будет самым замечательным образом гасить все самые мощные помехи, пришедшие снаружи.

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

Ivanii
Offline
Зарегистрирован: 11.10.2015

a5021 пишет:
Кондер имел бы смысл, если бы устройство работало у вас от часовой батарейки, которая не в состоянии обеспечивать рабочих токов для передатчика. Тогда, кондер бы заряжался в промежутках между работой передатчика и отдавал большие токи, когда в них возникала потребность. На вашей схеме в таком режиме он не будет работать, скорее всего, никогда.

На фото как раз "часовая" батарея, из опыта емкостей по питанию очень часто не хватает в связи с неправельным расчетом или их усыханием.

a5021
Offline
Зарегистрирован: 07.07.2013

Читаем мануал от ROBITON ER14250 в подразделе MAIN APPLICATION:

- Utility mettering

- Alarms and security devices

- Memory back-up

- Tracking systems

- Automotive electronics

- Professional electronics

Вы видите здесь "часы" ? Я нет. Зато часы указаны в батарейках серии, скажем, CRxxxx. Вы уже начинаете догадываться, что должно называться часовой батарейкой?

Какое "усыхание" у литиевой батареи ? Очнитесь.

pav2000
Offline
Зарегистрирован: 15.12.2014

a5021 пишет:

Конденсатор по питанию ставят для подавления помех, сглаживания пульсаций и для "размазывания" короткоимульсного характера потребления значительных токов. Это не все случаи, но наиболее частые. В вашей ситуации батарея 14250 имеет рабочий ток на порядок (в десять раз) превышающий максимальный ток потребления NRF24L01 и при этом ее внутреннее сопротивление настолько мало, что она будет самым замечательным образом гасить все самые мощные помехи, пришедшие снаружи.

Полностью согласен. НО без кондесатора nrf24 с усилителем (ток до 100 ма) отказывалась работать. Я тоже думал в начале что все ок уж батарейка справится с током. 

Как это объяснить. Установка кондесатора и уменьшение выходной мощности исправили это дело.

Это конкретный случай. 

Возможно батарейка не соответвует заявленным параметрам.

Ivanii
Offline
Зарегистрирован: 11.10.2015

a5021 пишет:
Вы видите здесь "часы" ? Я нет. Зато часы указаны в батарейках серии, скажем, CRxxxx. Вы уже начинаете догадываться, что должно называться часовой батарейкой?

Какое "усыхание" у литиевой батареи ? Очнитесь.

Часть даташита с токами религия не позволила прочитать?

CR2032 в разы более устойчива к импульсным нагрузкам, 20 мА 15 с против 50 мА 0,1 с у ER14250.

Сохнут электролиты.

pav2000
Offline
Зарегистрирован: 15.12.2014

Доработал код.

1. Увеличил время включения nrf24.  После этого потерь связи почти нет, но еще изучаю этот вопрос.

2. Поставил механизм записи ошибок в еепром и возможность их вывода в последовательный порт. Внимание в режиме отладки необходимо для запуска блока подать команду в последовательный порт, без этого он будет ждать команду.

Для ловли ошибок надо  сначала загрузить прошивку с опцией // #define DEBUG. Прошика будет работать  и накапливать ошибки. Для просмотра раскоментировать  // #define DEBUG и по последовательному порту можно будет посмотреть накопленные ошибки.

Правильное время лога выставляется ТОЛЬКО при связи с головным блоком.

/* SFE_BMP180 library example sketch
Hardware connections:
- (GND) to GND
+ (VDD) to 3.3V
(WARNING: do not connect + to 5V or the sensor will be damaged!)
You will also need to connect the I2C pins (SCL and SDA) to your
Arduino. The pins are different on different Arduinos:
Any Arduino pins labeled:  SDA  SCL
Uno, Redboard, Pro:        A4   A5
Mega2560, Due:             20   21
Leonardo:                   2    3
*/

#include <SFE_BMP180.h>
#include <Wire.h>
#include "LowPower.h"
#include <SPI.h>
#include "nRF24L01.h"  // Беcпроводной модуль надо использовать библиотеку http://tmrh20.github.io/RF24
#include "RF24.h"      // Беcпроводной модуль используются не стандартные функции https://github.com/TMRh20
#include "stmTime.h"   // Time library - https://github.com/PaulStoffregen/Time

// - ОПЦИИ  при сборке окончательной версии все отключить-----------------------------
#define LOG                                     // Признак записи лога в еепром
#define DEBUG                                   // Отладочную  информацию в ком порт посылает  и есть меню управления логом должен быть включен #define LOG 
#define DEMO                                    // Признак демонстрации - данные с датчиков генерятся рандом и быстрее

// Константы
#define VERSION_DATA "091215"                   // Дата текущей версии  в формате ДДММГГ
#define VERSION      "0.20"                     // Текущая версия прошивки
#define ID            0x31                      // уникально Идентификатор устройства (тип) - старшие 4 бита, вторые (младшие) 4 бита серийный номер устройства
#define LABEL "BM-180"                          // Название блока
#define NRF24_CHANEL  100                       // Номер канала nrf24

// Макросы для работы с портами  скорость и место
#define SetOutput(port,bit)       DDR ## port |= _BV(bit)
#define SetInput(port,bit)        DDR ## port &= ~_BV(bit)
#define SetBit(port,bit)          PORT ## port |= _BV(bit)
#define ClearBit(port,bit)        PORT ## port &= ~_BV(bit)
#define WritePort(port,bit,value) PORT ## port = (PORT ## port & ~_BV(bit)) | ((value & 1) << bit)
#define ReadPort(port,bit)        (PIN ## port >> bit) & 1
#define PullUp(port,bit)          { SetInput(port,bit); SetBit(port,bit); }
#define Release(port,bit)         { SetInput(port,bit); ClearBit(port,bit); }

// Ноги куда подключаем переферию
#define PIN_CE          7                       // nrf24 ce
#define PIN_CSN         6                       // nrf24 csn
#define PIN_VCC_BMP180  10                      // Нога питания ВМР180 использую SetOutput(B,2)
#define PIN_SDA_BMP180  A4                      // Нога SDA ВМР180 
#define PIN_SCL_BMP180  A5                      // Нога SCL ВМР180 


// - ВРЕМЕНА ---------------------------------------
#ifdef DEMO                                     // Для демо все быстрее
    #define TIME_WDT SLEEP_2S                   // Период сторожевого таймера
    #define TIME_SCAN_SENSOR 2                  // Время опроса датчиков в 8 секундных интервалах (сторожевого таймера)
    #define TIME_SEND_SENSOR 4                  // Время посылки данных (равно усреднению)
#else   
    #define TIME_WDT SLEEP_8S                   // Период сторожевого таймера
    #define TIME_SCAN_SENSOR 9                  // Время опроса датчиков в 8 секундных интервалах (сторожевого таймера)
    #define TIME_SEND_SENSOR 4                  // Время посылки данных (равно усреднению) 288 секунд почти пять минут
#endif

// АЦП ----------------------------------------
const long ConstADC=1126400;                    // Калибровка встроенного АЦП (встроенный ИОН) по умолчанию 1126400

// Переменные которые сохраняются в памяти без инициализации
int S8_tick __attribute__((section(".noinit")));           // число 8 секундных интервалов с последнего измерения
unsigned long tt __attribute__((section(".noinit")));      // Время пришедшее от головного блока если 0 то время не приходило
// Пакет передаваемый, используется также для хранения УСРЕДНЕННЫХ результатов. 
 struct type_packet_0x30_NRF24                  // Версия 1.1!! адаптация для stm32 Структура передаваемого пакета 32 байта - 32 максимум
    {
        byte id=ID;                             // Идентификатор типа устройства - старшие 4 бита, вторые (младшие) 4 бита серийный номер устройства
        byte error=0;                           // Ошибка блока, пока резерв
        uint16_t  Vcc;                          // Напряжение батареи  Милливольты!!!!!
        int16_t  Temp;                          // Температура датчика  сотые градуса
        uint16_t  relH;                         // Относительная влажность датчика  сотые %
        uint16_t  absH;                         // Абсолютная влажность датчика сотые грамма на куб
        uint16_t  P;                            // Давление  милибары 
        uint16_t count=0;                       // Циклический счетчик пакетов 2 байта хватит на долго - около 3000 часов
        char ver[5] = VERSION;                  // Версия прошивки не более 4 байт + "0" символ 
        char note[13] = LABEL;                  // Примечание не более 12 байт + "0" байт Русские буквы в два раза меньше т.к. UTF-8
    } packet_0x30 __attribute__((section(".noinit")));


struct type_sensors   // структура для усреднения измерений
{      int16_t  num;                           // Число накопленных образцов
       int32_t  Vcc=0;                         // Напряжение батареи  Милливольты!!!!!
       int32_t  Temp=0;                        // Температура датчика  сотые градуса
       int32_t  relH=0;                        // Относительная влажность датчика  сотые %
       int32_t  absH=0;                        // Абсолютная влажность датчика сотые грамма на куб
       int32_t  P=0;                           // Давление  мбары
 } sensor __attribute__((section(".noinit")));

#ifdef LOG    // Структуры для записи лога
      #define NUM_LOG 80    // длина лога в записях!!!
      // Коды ошибок для записи в лог
      // Возвращаемые при инииализации датчика
      #define ERR_SENSOR_B1  1                 // error Data too long to fit in transmit buffer 
      #define ERR_SENSOR_B2  2                 // error Received NACK on transmit of address
      #define ERR_SENSOR_B3  3                 // error Received NACK on transmit of data
      #define ERR_SENSOR_B4  4                        // error Other error
      // Возвращаемые приработе датчика
      #define ERR_SENSOR_PR  5                 // error retrieving pressure measurement
      #define ERR_SENSOR_PS  6                 // error starting pressure measurement
      #define ERR_SENSOR_TR  7                 // error retrieving temperature measurement
      #define ERR_SENSOR_TS  8                 // error starting temperature measurement
      // Возвращаемые радио модулем ошибки
      #define ERR_NRF24_ASK  9                 // ASK не получен - обычно идет после ERR_NRF24_SEND
      #define ERR_NRF24_SEND 10                // Пакет не отправлен
      
     
      struct type_event     // Cтруктура для хранения одного события лога размер 5 байт.
      {
        unsigned long t;    // время последнего события 
        uint8_t err;        // коды ошибок
      };
      
      struct type_logs   // структура для хранения лога
      {
       bool flag_save=false;        // Признак необходимости записи в лог
       uint8_t  count=0;            // Указатель на последнюю запись в event
       uint16_t err_sensor=0;       // Общее число ошибок чтения датчика
       uint16_t err_nrf24=0;        // Общее число ошибок передачи
       unsigned long last_err=0;    // время последней ошибки
       unsigned long first_err=0;   // время первой  ошибки
       type_event  event[NUM_LOG];  // лог где встречается ошибка на каждую передачу одно число
      };
      
      type_logs logRAM __attribute__((section(".noinit"))); // Рабочая копия  в памяти
      type_logs logEEPROM EEMEM ;     // Копия в eeprom - туда пишем 
#endif 

SFE_BMP180 pressure;                //  датчик BMP180
RF24 radio(PIN_CE, PIN_CSN);        // определение управляющих ног радиомодуля

void setup()
{  char ch;
   S8_tick=0;
   tt=0;
   
   reset_sum();
   #ifdef LOG
     readEeprom();   // Чтение лога из еепром в память
   #endif
   // 1. Проверка питания  
    #ifdef  DEBUG  
    Serial.begin(115200); 
    Serial.flush();
    Serial.print(F("\n1. Version: "));
    Serial.print(F(VERSION));
    Serial.print(F(" "));
    Serial.println(F(VERSION_DATA));
    Serial.print(F("2. Battery voltage: "));
    Serial.println(((float)readVcc())/1000.0,2);
    Serial.print(F("3. NRF24 init . . .  "));
    Serial.flush();
  # endif 
  
 // 2.Инициализация радиомодудя
   char buf[10];
   if (setRadio()==true)     // радио проинициализировано удачно 
    {                
       #ifdef  DEBUG  
       Serial.println(F("OK"));
       // внимание функция read_register сделана в библиотеке публичной
       sprintf(buf,"%02x",radio.read_register(CONFIG));    Serial.print(F("  3.1 Register CONFIG:     0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(SETUP_AW));  Serial.print(F("  3.2 Register SETUP_AW:   0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(SETUP_RETR));Serial.print(F("  3.3 Register SETUP_RETR: 0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(RF_CH));     Serial.print(F("  3.4 Register RF_CH:      0x")); Serial.println(buf); 
       sprintf(buf,"%02x",radio.read_register(RF_SETUP));  Serial.print(F("  3.5 Register RF_SETUP:   0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(NRF_STATUS));Serial.print(F("  3.6 Register NRF_STATUS: 0x")); Serial.println(buf);
       #endif 
   //    radio.powerDown();  // Выключить радиомодуль
    }
  else  // радио не работает
    {
       #ifdef  DEBUG  
       Serial.println(F("FAIL"));
       #endif 
    } 
    
   // 3. Проверка сенсора ВМР180 
   Serial.print(F("4.  BMP180 init . . . "));
   if ( BMP180_ON()==true)
        { ;
          #ifdef  DEBUG  
          Serial.print(F(" OK\n"));
          Serial.flush();
          #endif
        }  
        else
        {  ;
          #ifdef  DEBUG 
          Serial.print(F(" FAIL, error:"));
          Serial.print(pressure.getError());
          Serial.print(F("\n"));
          Serial.flush();
           #endif
        } 
    BMP180_OFF();
   
  
   #ifdef  DEBUG 
   while(1)   // ДИАЛОГ ДЛЯ работы с логом
    { Serial.flush();
      Serial.println(F("\nThe available LOG commands: "));
      Serial.println(F("V - View log "));
      Serial.println(F("C - Clear log "));
      Serial.println(F("R - Clear log and RUN measurement"));
      Serial.println(F("G - RUN measurement"));
      Serial.print(F("Command? "));
 
      while(Serial.available()==0) {;} // Ждем команду
        ch=Serial.read();              // читаем первый байт
        Serial.print(F("'"));
        Serial.print(ch);
        Serial.print(F("'"));
        
        switch (ch)  // разбор команды
         {
          case 'V':
          case 'v':
            Serial.print(F("\n")); view_log();   break;
          case 'C':
          case 'c':
     //       Serial.print(F("\n")); clear_log();  break;
          case 'R':  
          case 'r':       
            Serial.print(F("\n")); clear_log();  break;
          case 'G':  
          case 'g':  
            Serial.print(F("\n"));  break;
          default: Serial.print(F(" unknown command.\n")); break; 
          } 
    if ((ch=='R')||(ch=='r')||(ch=='G')||(ch=='g')) break;          // Выход из цикла  
    while(Serial.available()>0) Serial.read();  // очистка входного буфера для новой итерации
    } 
    #endif
   // Ошибки датчика ВМР180 связанные с чтением шины i2c  возвращается СИМВОЛ!!! не число
   // 0 = Success
   // 1 = Data too long to fit in transmit buffer
   // 2 = Received NACK on transmit of address
   // 3 = Received NACK on transmit of data
   // 4 = Other error
   packet_0x30.error=pressure.getError()-'0'; // Ошибка в виде символа переводим в число 
   
   // Для проверки связи сразу посылаем пакет
   measurement();
   packet_0x30.Vcc=sensor.Vcc;
   packet_0x30.Temp=sensor.Temp;
   packet_0x30.relH=sensor.relH;
   packet_0x30.absH=sensor.absH;
   packet_0x30.P=sensor.P/10;
   sendRadio();  // Послать даннные  
   reset_sum(); 
   #ifdef  DEBUG  
    Serial.println(F("Start LOOP . . ."));
    Serial.flush();
   #endif  
}

void loop()
{
   
  if (S8_tick>=TIME_SCAN_SENSOR-1)   // Пора ли опрашивать датчик
  {
   #ifdef  DEBUG  
    Serial.print(F("PowerUp and scan sensor "));
   #endif  
   S8_tick=0;
   measurement();   // измерение
  } 
  else              
  {
    S8_tick++;         // добавляем счетчик
   #ifdef  DEBUG  
    Serial.print(F("PowerUp "));
    Serial.println(S8_tick);
    Serial.flush();
   #endif  
   } 
  
 if (sensor.num>=TIME_SEND_SENSOR)   // Пора посылать данные
  {
   // Усреднение 
   packet_0x30.Vcc=sensor.Vcc/TIME_SEND_SENSOR;
   packet_0x30.Temp=sensor.Temp/TIME_SEND_SENSOR;
   packet_0x30.relH=sensor.relH/TIME_SEND_SENSOR;
   packet_0x30.absH=sensor.absH/TIME_SEND_SENSOR;
   packet_0x30.P=(sensor.P/TIME_SEND_SENSOR)/10;
   reset_sum();
   sendRadio();  // Послать даннные
   } 
   
   LowPower.powerDown(TIME_WDT, ADC_OFF, BOD_OFF);   // СПАТЬ  
   tt=tt+8;           // Прошло 8 секунд только не для демо
}

// Чтение напряжения питания ----------------------------------------------
long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = ConstADC / result; // Back-calculate AVcc in mV
  return result;
  
} 

//  Установка радиомодуля  -----------------------------------------------
bool setRadio(void)
{

   if (radio.begin()==false) radio.begin();  // Если модуль не стартует делаем еще попытку 
 //  radio.begin();
   radio.setDataRate(RF24_250KBPS);         // выбор скорости RF24_250KBPS RF24_1MBPS RF24_2MBPS
   radio.setPALevel(RF24_PA_MAX);           // выходная мощность передатчика RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX,
   radio.setChannel(NRF24_CHANEL);          // тут установка канала
   radio.setCRCLength(RF24_CRC_16);         // использовать контрольную сумму в 16 бит
   radio.setAutoAck(true);                  // выключить аппаратное потверждение
// radio.enableDynamicPayloads();           // разрешить Dynamic Payloads
   radio.enableAckPayload();                // Разрешить ответ приемника AckPayload
   radio.setRetries(50,10);                 // Количество пауза и количество повторов 
  // Рекомендуют первые 2-4 байта адреса устанавливать в E7 или 18 он проще детектируется чипом
  radio.openWritingPipe(0xE7E7E7E7E1LL);    // передатчик 
  radio.openReadingPipe(1,0xE7E7E7E7D2LL);  // приемник
  radio.startListening();
  radio.powerDown();
  return true;
 }
 
//  Посылка пакета ----------------------------------------
bool sendRadio(void)
{  unsigned long t=0;
   bool send_packet_ok=false;
  //  Включение чипа nrf24
  SetOutput(B,5);     // slk - выход сделать
  SetOutput(B,3);     // mosi - выход сделать
  SPI.begin();        // старт spi заново
  radio.powerUp();
  delay(3);               
  radio.stopListening();     // Остановить приемник перейти в режим передачи
  delay(2);                  // дать время на переключение
  cli();
  radio.writeBlocking(&packet_0x30,sizeof(packet_0x30),200);  // Writes 1 payload to the buffers
  send_packet_ok=radio.txStandBy();
  if ( radio.isAckPayloadAvailable() )            // Ждем получения -- основной блок передает текущее время
           radio.read(&tt,sizeof(t));             //... и имеем переменную tt с временем от приемника.
  #ifdef LOG         
      else save_err(ERR_NRF24_ASK);               // ответ не получен  запишем ошибку
  #endif      
  sei(); 
  if (t>0)  tt=t;                                 // Если пришел ответ обновить время
  #ifdef  DEBUG  
    if (send_packet_ok==true)  Serial.println(F("Packet sending ++++++++++"));
    else                       Serial.println(F("Packet NOT sending -----------"));
    Serial.flush();
   #endif   
   
   #ifdef LOG         
      if (send_packet_ok==false)  save_err(ERR_NRF24_SEND); //  пакет  не послан запишем ошибку
   #endif 
    
  //  Выключение чипа nrf24
  radio.powerDown();  // выключаем чип но это не все!!!
  SPI.end();          // выключаем SPI
  PullUp(B,5);        // slk - сделать входом и подтянуть к 3.3 вольтам
  PullUp(B,3);        // mosi - сделать входом и подтянуть к 3.3 вольтам
  packet_0x30.count++;   // при переполнении сам сбросится
 } 
 
void reset_sum(void)  // Сброс счетчиков накоплений
{  sensor.num=0;
   sensor.Vcc=0;
   sensor.Temp=0;
   sensor.relH=0;
   sensor.absH=0;
   sensor.P=0;  
}

// Измерение датчика -----------------------------------------------
bool measurement(void)
{
  char status;
  double T,P,p0,a;
  packet_0x30.error=0;
  
  if (BMP180_ON()==false) return false; // датчик не работает выходим
  status = pressure.startTemperature();
  if (status != 0)
  {
    delay(status);
    status = pressure.getTemperature(T);
    packet_0x30.error=pressure.getError();  // Запомнить ошибку 
    if (status != 0)
    {
      #ifdef  DEBUG  
      Serial.print(F("Temperature: "));
      Serial.print(T,2);
      #endif
       
      status = pressure.startPressure(3);
      if (status != 0)
      {
        delay(status);
        status = pressure.getPressure(P,T);
        packet_0x30.error=packet_0x30.error+8*(pressure.getError());  // Запомнить ошибку 
        if (status != 0)
        {;
          #ifdef  DEBUG 
          Serial.print(F(" C absolute pressure: "));
          Serial.print(P,2);
          Serial.println(F(" mb"));
          Serial.flush();
          #endif
         }
        else  
        {;
        #ifdef  DEBUG  
          Serial.println("error retrieving pressure measurement\n"); 
        #endif
        #ifdef LOG         
           save_err(ERR_SENSOR_PR); //  Ошибка чтения датчика
        #endif 
        }
      }
      else 
      {;
      #ifdef  DEBUG 
        Serial.println("error starting pressure measurement\n"); 
      #endif
      #ifdef LOG         
           save_err(ERR_SENSOR_PS); //  Ошибка чтения датчика
      #endif 
      }
    }
    else 
    {;
    #ifdef  DEBUG 
     Serial.println("error retrieving temperature measurement\n"); 
    #endif
    #ifdef LOG         
        save_err(ERR_SENSOR_TR); //  Ошибка чтения датчика
    #endif 
    }
  }
  else 
  {;
  #ifdef  DEBUG 
    Serial.println("error starting temperature measurement\n"); 
  #endif
  #ifdef LOG         
      save_err(ERR_SENSOR_TS);  //  Ошибка чтения датчика
  #endif 
  }
 BMP180_OFF();
if (packet_0x30.error==0)  // Если нет ошибок усредняем и копим сумму
{
   sensor.num++;
   sensor.Vcc=sensor.Vcc+readVcc();
   sensor.Temp=sensor.Temp+T*100;
   sensor.P=sensor.P+P*10;  
   return true;
}
return false;
} 

// Включение датчика BMP180 ----------------------------------
bool BMP180_ON(void)    
{   SetOutput(B,2);       // Подать питание
    SetBit(B,2); 
    //delay(5);
    if(pressure.begin()!=1) // Инициализация BMP180 
    {
     #ifdef LOG   
     save_err(pressure.getError()-'0'); // Если надо запишим ошибку 
     #endif  
     return false;
    } 
    else  return true;
 } 
 
// Выключение датчика BMP180 ---------------------------------
void BMP180_OFF(void)  
{
  PullUp(B,2);  
} 

#ifdef LOG  
// Запись счетчиков в Eeprom --------------------------------------------------
void writeEeprom()
{ 
  if (logRAM.flag_save==true)  // Если не было изменений то выходим без записи
  {
    logRAM.flag_save=false;              //  Сборосить флаг записи
    cli();   
    eeprom_write_block((const void*)&logRAM, (void*) &logEEPROM, sizeof(logRAM)); 
    sei();
  }

}
// Чтение счетчиков из Eeprom --------------------------------------------------
void readEeprom()
{
cli(); 
   eeprom_read_block((void*)&logRAM, (const void*) &logEEPROM, sizeof(logRAM)); 
sei();
}

// Запись ошибки в лог -----------------------------------------------
void save_err(int code)
{
 if (code<ERR_NRF24_ASK) logRAM.err_sensor++; else  logRAM.err_nrf24++;  // добавление счетчиков ошибок
 if (logRAM.first_err==0) logRAM.first_err=tt;
 logRAM.last_err=tt;
 logRAM.event[logRAM.count].t=tt;
 logRAM.event[logRAM.count].err=code;
 logRAM.count++;                               // счетчик записей увеличиваем
 if (logRAM.count==NUM_LOG) logRAM.count=0;    // делаем кольцевой буфер
 logRAM.flag_save=true;                        // Признак необходимости записи
 writeEeprom();                                // Запись в еепром
} 
#endif

#ifdef DEBUG
// Очистка лога  и запись его в еепром -----------------------------------------------
void clear_log(void)
{   int i;
    Serial.print(F("Erase logs . . . "));
    logRAM.err_sensor=0;
    logRAM.err_nrf24=0;
    logRAM.last_err=0;
    logRAM.first_err=0;
    logRAM.count=0;
    logRAM.flag_save=true;
    for (i=0;i<NUM_LOG;i++)
      {
      logRAM.event[i].t=0;
      logRAM.event[i].err=0;
      } 
    writeEeprom();
    Serial.println(F(" OK"));
//    Serial.flush();    
} 
// Вывод лога на экран -----------------------------------------------
void view_log(void)
{   int i, x;
    Serial.println(F("\n- LOG  ----------"));
    Serial.print(F("Number error read sensor: ")); Serial.println(logRAM.err_sensor);
    Serial.print(F("Number error nrf24: ")); Serial.println(logRAM.err_nrf24);
    Serial.print(F("Time first error: "));// Serial.println(logRAM.first_err);
      Serial.print(day(logRAM.first_err)); Serial.print(F("/"));
      Serial.print(month(logRAM.first_err)); Serial.print(F("/")); 
      Serial.print(year(logRAM.first_err)); Serial.print(F(" "));
      Serial.print(hour(logRAM.first_err)); Serial.print(F(":"));
      Serial.print(minute(logRAM.first_err)); Serial.print(F(":"));
      Serial.println(second(logRAM.first_err));
    Serial.print(F("Time last error: "));      //Serial.println(logRAM.last_err); 
      Serial.print(day(logRAM.last_err)); Serial.print(F("/"));
      Serial.print(month(logRAM.last_err)); Serial.print(F("/")); 
      Serial.print(year(logRAM.last_err)); Serial.print(F(" "));
      Serial.print(hour(logRAM.last_err)); Serial.print(F(":"));
      Serial.print(minute(logRAM.last_err)); Serial.print(F(":"));
      Serial.println(second(logRAM.last_err));  
    Serial.print(F("Count log event: ")); Serial.println(logRAM.count);
    Serial.println(F("Detals logs: "));
    if ((logRAM.count<NUM_LOG)&&(logRAM.err_sensor+logRAM.err_nrf24<=NUM_LOG)) x=logRAM.count; else x=NUM_LOG;  // если мало ошибок то выводим не весь лог
      for (i=0;i<x;i++)
   //     { Serial.print(i); Serial.print(F(". time:")); Serial.print(logRAM.event[i].t); Serial.print(F(" err:")); Serial.println(logRAM.event[i].err,HEX);}
    {  // дешефровка времени
      Serial.print(i); Serial.print(F(". Time:"));
      Serial.print(day(logRAM.event[i].t)); Serial.print(F("/"));
      Serial.print(month(logRAM.event[i].t)); Serial.print(F("/")); 
      Serial.print(year(logRAM.event[i].t)); Serial.print(F(" "));
      Serial.print(hour(logRAM.event[i].t)); Serial.print(F(":"));
      Serial.print(minute(logRAM.event[i].t)); Serial.print(F(":"));
      Serial.print(second(logRAM.event[i].t)); Serial.print(F(" "));
      Serial.print(F(" err:")); Serial.println(logRAM.event[i].err,HEX);
    } 
    
    if (logRAM.count==0)  Serial.println(F("Logs empty . . ."));
     
}
 #endif

 

a5021
Offline
Зарегистрирован: 07.07.2013

Ivanii пишет:
Часть даташита с токами религия не позволила прочитать?

Я вам могу задать тот же вопрос.

Цитата:
CR2032 в разы более устойчива к импульсным нагрузкам, 20 мА 15 с против 50 мА 0,1 с у ER14250

Вот даташит по часовым батарейкам Panasonic. Если мне не изменяет зрение, то рекомендованный ток потребления там указан в 200 микроампер и никаких имульсных значений не приводится вовсе. Зато есть график, на котором дается падение выдаваемого напряжения в зависимости от нагрузки. Так нагрузив батарейку током 2.5ма, напряжение упадет до 2.7в. Как-то я не очень разделяю ваш оптимизм насчет 20ма в течение 15с.

Ivanii
Offline
Зарегистрирован: 11.10.2015

a5021 пишет:
Я вам могу задать тот же вопрос.

Вот даташит по часовым батарейкам Panasonic. Если мне не изменяет зрение, то рекомендованный ток потребления там указан в 200 микроампер и никаких имульсных значений не приводится вовсе. Зато есть график, на котором дается падение выдаваемого напряжения в зависимости от нагрузки. Так нагрузив батарейку током 2.5ма, напряжение упадет до 2.7в. Как-то я не очень разделяю ваш оптимизм насчет 20ма в течение 15с.

Этот режим используется в пультах д.у., даташиты с его описанием находятся по строке поиска "CR2032 pulse load" форум крив и не позволяет ничего вставить.

pav2000
Offline
Зарегистрирован: 15.12.2014

Подобрал времена, включения nrf24, теперь связь не теряется. Проверял трое суток, все ок.

Для увеличения дальности припаял кусок провода (83 мм)  к печатной антенне (гуглил инет). Дальность возрасла где то в два раза. Теперь уверенный прием по всей квартире.

Последняя версия исходника:

/* SFE_BMP180 library example sketch
Hardware connections:
- (GND) to GND
+ (VDD) to 3.3V
(WARNING: do not connect + to 5V or the sensor will be damaged!)
You will also need to connect the I2C pins (SCL and SDA) to your
Arduino. The pins are different on different Arduinos:
Any Arduino pins labeled:  SDA  SCL
Uno, Redboard, Pro:        A4   A5
Mega2560, Due:             20   21
Leonardo:                   2    3
*/

#include <SFE_BMP180.h>
#include <Wire.h>
#include "LowPower.h"
#include <SPI.h>
#include "nRF24L01.h"  // Беcпроводной модуль надо использовать библиотеку http://tmrh20.github.io/RF24
#include "RF24.h"      // Беcпроводной модуль используются не стандартные функции https://github.com/TMRh20
#include "stmTime.h"   // Time library - https://github.com/PaulStoffregen/Time

// - ОПЦИИ  при сборке окончательной версии все отключить-----------------------------
//#define LOG                                     // Признак записи лога в еепром
//#define DEBUG                                   // Отладочную  информацию в ком порт посылает  и есть меню управления логом должен быть включен #define LOG 
//#define DEMO                                    // Признак демонстрации - данные с датчиков генерятся рандом и быстрее

// Константы
#define VERSION_DATA "121215"                   // Дата текущей версии  в формате ДДММГГ
#define VERSION      "0.25"                     // Текущая версия прошивки
#define ID            0x31                      // уникально Идентификатор устройства (тип) - старшие 4 бита, вторые (младшие) 4 бита серийный номер устройства
#define LABEL "BM-180"                          // Название блока
#define NRF24_CHANEL  100                       // Номер канала nrf24

// Макросы для работы с портами  скорость и место
#define SetOutput(port,bit)       DDR ## port |= _BV(bit)
#define SetInput(port,bit)        DDR ## port &= ~_BV(bit)
#define SetBit(port,bit)          PORT ## port |= _BV(bit)
#define ClearBit(port,bit)        PORT ## port &= ~_BV(bit)
#define WritePort(port,bit,value) PORT ## port = (PORT ## port & ~_BV(bit)) | ((value & 1) << bit)
#define ReadPort(port,bit)        (PIN ## port >> bit) & 1
#define PullUp(port,bit)          { SetInput(port,bit); SetBit(port,bit); }
#define Release(port,bit)         { SetInput(port,bit); ClearBit(port,bit); }

// Ноги куда подключаем переферию
#define PIN_CE          7                       // nrf24 ce
#define PIN_CSN         6                       // nrf24 csn
#define PIN_VCC_BMP180  10                      // Нога питания ВМР180 использую SetOutput(B,2)
#define PIN_SDA_BMP180  A4                      // Нога SDA ВМР180 
#define PIN_SCL_BMP180  A5                      // Нога SCL ВМР180 


// - ВРЕМЕНА ---------------------------------------
#ifdef DEMO                                     // Для демо все быстрее
    #define TIME_WDT SLEEP_2S                   // Период сторожевого таймера
    #define TIME_SCAN_SENSOR 2                  // Время опроса датчиков в 8 секундных интервалах (сторожевого таймера)
    #define TIME_SEND_SENSOR 4                  // Время посылки данных (равно усреднению)
#else   
    #define TIME_WDT SLEEP_8S                   // Период сторожевого таймера
    #define TIME_SCAN_SENSOR 9                  // Время опроса датчиков в 8 секундных интервалах (сторожевого таймера)
    #define TIME_SEND_SENSOR 4                  // Время посылки данных (равно усреднению) 288 секунд почти пять минут
#endif

// АЦП ----------------------------------------
const long ConstADC=1126400;                    // Калибровка встроенного АЦП (встроенный ИОН) по умолчанию 1126400

// Переменные которые сохраняются в памяти без инициализации
int S8_tick __attribute__((section(".noinit")));           // число 8 секундных интервалов с последнего измерения
unsigned long tt __attribute__((section(".noinit")));      // Время пришедшее от головного блока если 0 то время не приходило
// Пакет передаваемый, используется также для хранения УСРЕДНЕННЫХ результатов. 
 struct type_packet_0x30_NRF24                  // Версия 1.1!! адаптация для stm32 Структура передаваемого пакета 32 байта - 32 максимум
    {
        byte id=ID;                             // Идентификатор типа устройства - старшие 4 бита, вторые (младшие) 4 бита серийный номер устройства
        byte error=0;                           // Ошибка блока, пока резерв
        uint16_t  Vcc;                          // Напряжение батареи  Милливольты!!!!!
        int16_t  Temp;                          // Температура датчика  сотые градуса
        uint16_t  relH;                         // Относительная влажность датчика  сотые %
        uint16_t  absH;                         // Абсолютная влажность датчика сотые грамма на куб
        uint16_t  P;                            // Давление  милибары 
        uint16_t count=0;                       // Циклический счетчик пакетов 2 байта хватит на долго - около 3000 часов
        char ver[5] = VERSION;                  // Версия прошивки не более 4 байт + "0" символ 
        char note[13] = LABEL;                  // Примечание не более 12 байт + "0" байт Русские буквы в два раза меньше т.к. UTF-8
    } packet_0x30 __attribute__((section(".noinit")));


struct type_sensors   // структура для усреднения измерений
{      int16_t  num;                           // Число накопленных образцов
       int32_t  Vcc=0;                         // Напряжение батареи  Милливольты!!!!!
       int32_t  Temp=0;                        // Температура датчика  сотые градуса
       int32_t  relH=0;                        // Относительная влажность датчика  сотые %
       int32_t  absH=0;                        // Абсолютная влажность датчика сотые грамма на куб
       int32_t  P=0;                           // Давление  мбары
 } sensor __attribute__((section(".noinit")));

#ifdef LOG    // Структуры для записи лога
      #define NUM_LOG 80    // длина лога в записях!!!
      // Коды ошибок для записи в лог
      // Возвращаемые при инииализации датчика
      #define ERR_SENSOR_B1  1                 // error Data too long to fit in transmit buffer 
      #define ERR_SENSOR_B2  2                 // error Received NACK on transmit of address
      #define ERR_SENSOR_B3  3                 // error Received NACK on transmit of data
      #define ERR_SENSOR_B4  4                        // error Other error
      // Возвращаемые приработе датчика
      #define ERR_SENSOR_PR  5                 // error retrieving pressure measurement
      #define ERR_SENSOR_PS  6                 // error starting pressure measurement
      #define ERR_SENSOR_TR  7                 // error retrieving temperature measurement
      #define ERR_SENSOR_TS  8                 // error starting temperature measurement
      // Возвращаемые радио модулем ошибки
      #define ERR_NRF24_ASK  9                 // ASK не получен - обычно идет после ERR_NRF24_SEND
      #define ERR_NRF24_SEND 10                // Пакет не отправлен
     
      struct type_event     // Cтруктура для хранения одного события лога размер 5 байт.
      {
        unsigned long t;    // время последнего события 
        uint8_t err;        // коды ошибок
      };
      
      struct type_logs   // структура для хранения лога
      {
       bool flag_save=false;        // Признак необходимости записи в лог
       uint8_t  count=0;            // Указатель на последнюю запись в event
       uint16_t err_sensor=0;       // Общее число ошибок чтения датчика
       uint16_t err_nrf24=0;        // Общее число ошибок передачи
       unsigned long last_err=0;    // время последней ошибки
       unsigned long first_err=0;   // время первой  ошибки
       type_event  event[NUM_LOG];  // лог где встречается ошибка на каждую передачу одно число
      };
      
      type_logs logRAM __attribute__((section(".noinit"))); // Рабочая копия  в памяти
      type_logs logEEPROM EEMEM ;     // Копия в eeprom - туда пишем 
#endif 

SFE_BMP180 pressure;                //  датчик BMP180
RF24 radio(PIN_CE, PIN_CSN);        // определение управляющих ног радиомодуля

void setup()
{  char ch;
   S8_tick=0;
   tt=0;
   
   reset_sum();
   #ifdef LOG
     readEeprom();   // Чтение лога из еепром в память
   #endif
   // 1. Проверка питания  
    #ifdef  DEBUG  
    Serial.begin(115200); 
    Serial.flush();
    Serial.print(F("\n1. Version: "));
    Serial.print(F(VERSION));
    Serial.print(F(" "));
    Serial.println(F(VERSION_DATA));
    Serial.print(F("2. Battery voltage: "));
    Serial.println(((float)readVcc())/1000.0,2);
    Serial.print(F("3. NRF24 init . . .  "));
    Serial.flush();
  # endif 
  
 // 2.Инициализация радиомодудя
   char buf[10];
   if (setRadio()==true)     // радио проинициализировано удачно 
    {                
       #ifdef  DEBUG  
       Serial.println(F("OK"));
       // внимание функция read_register сделана в библиотеке публичной
       sprintf(buf,"%02x",radio.read_register(CONFIG));    Serial.print(F("  3.1 Register CONFIG:     0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(SETUP_AW));  Serial.print(F("  3.2 Register SETUP_AW:   0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(SETUP_RETR));Serial.print(F("  3.3 Register SETUP_RETR: 0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(RF_CH));     Serial.print(F("  3.4 Register RF_CH:      0x")); Serial.println(buf); 
       sprintf(buf,"%02x",radio.read_register(RF_SETUP));  Serial.print(F("  3.5 Register RF_SETUP:   0x")); Serial.println(buf);
       sprintf(buf,"%02x",radio.read_register(NRF_STATUS));Serial.print(F("  3.6 Register NRF_STATUS: 0x")); Serial.println(buf);
       #endif 
   //    radio.powerDown();  // Выключить радиомодуль
    }
  else  // радио не работает
    {
       #ifdef  DEBUG  
       Serial.println(F("FAIL"));
       #endif 
    } 
    
   // 3. Проверка сенсора ВМР180 
   Serial.print(F("4.  BMP180 init . . . "));
   if ( BMP180_ON()==true)
        { ;
          #ifdef  DEBUG  
          Serial.print(F(" OK\n"));
          Serial.flush();
          #endif
        }  
        else
        {  ;
          #ifdef  DEBUG 
          Serial.print(F(" FAIL, error:"));
          Serial.print(pressure.getError());
          Serial.print(F("\n"));
          Serial.flush();
           #endif
        } 
    BMP180_OFF();
   
  
   #ifdef  DEBUG 
   while(1)   // ДИАЛОГ ДЛЯ работы с логом
    { Serial.flush();
      Serial.println(F("\nThe available LOG commands: "));
      Serial.println(F("V - View log "));
      Serial.println(F("C - Clear log "));
      Serial.println(F("R - Clear log and RUN measurement"));
      Serial.println(F("G - RUN measurement"));
      Serial.print(F("Command? "));
 
      while(Serial.available()==0) {;} // Ждем команду
        ch=Serial.read();              // читаем первый байт
        Serial.print(F("'"));
        Serial.print(ch);
        Serial.print(F("'"));
        
        switch (ch)  // разбор команды
         {
          case 'V':
          case 'v':
            Serial.print(F("\n")); view_log();   break;
          case 'C':
          case 'c':
     //       Serial.print(F("\n")); clear_log();  break;
          case 'R':  
          case 'r':       
            Serial.print(F("\n")); clear_log();  break;
          case 'G':  
          case 'g':  
            Serial.print(F("\n"));  break;
          default: Serial.print(F(" unknown command.\n")); break; 
          } 
    if ((ch=='R')||(ch=='r')||(ch=='G')||(ch=='g')) break;          // Выход из цикла  
    while(Serial.available()>0) Serial.read();  // очистка входного буфера для новой итерации
    } 
    #endif
   // Ошибки датчика ВМР180 связанные с чтением шины i2c  возвращается СИМВОЛ!!! не число
   // 0 = Success
   // 1 = Data too long to fit in transmit buffer
   // 2 = Received NACK on transmit of address
   // 3 = Received NACK on transmit of data
   // 4 = Other error
   packet_0x30.error=pressure.getError()-'0'; // Ошибка в виде символа переводим в число 
   
   // Для проверки связи сразу посылаем пакет
   measurement();
   packet_0x30.Vcc=sensor.Vcc;
   packet_0x30.Temp=sensor.Temp;
   packet_0x30.relH=sensor.relH;
   packet_0x30.absH=sensor.absH;
   packet_0x30.P=sensor.P/10;
   sendRadio();  // Послать даннные  
   reset_sum(); 
   #ifdef  DEBUG  
    Serial.println(F("Start LOOP . . ."));
    Serial.flush();
   #endif  
}

void loop()
{
  if (S8_tick>=TIME_SCAN_SENSOR-1)   // Пора ли опрашивать датчик
  {
   #ifdef  DEBUG  
    Serial.print(F("PowerUp and scan sensor "));
   #endif  
   S8_tick=0;
   measurement();   // измерение
  } 
  else              
  {
    S8_tick++;         // добавляем счетчик
   #ifdef  DEBUG  
    Serial.print(F("PowerUp "));
    Serial.println(S8_tick);
    Serial.flush();
   #endif  
   } 
  
 if (sensor.num>=TIME_SEND_SENSOR)   // Пора посылать данные
  {
   // Усреднение 
   packet_0x30.Vcc=sensor.Vcc/TIME_SEND_SENSOR;
   packet_0x30.Temp=sensor.Temp/TIME_SEND_SENSOR;
   packet_0x30.relH=sensor.relH/TIME_SEND_SENSOR;
   packet_0x30.absH=sensor.absH/TIME_SEND_SENSOR;
   packet_0x30.P=(sensor.P/TIME_SEND_SENSOR)/10;
   reset_sum();
   sendRadio();  // Послать даннные
   } 
   
   LowPower.powerDown(TIME_WDT, ADC_OFF, BOD_OFF);   // СПАТЬ  
   tt=tt+8;           // Прошло 8 секунд только не для демо
}

// Чтение напряжения питания ----------------------------------------------
long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = ConstADC / result; // Back-calculate AVcc in mV
  return result;
  
} 

//  Установка радиомодуля  -----------------------------------------------
bool setRadio(void)
{

   if (radio.begin()==false) radio.begin();  // Если модуль не стартует делаем еще попытку 
 //  radio.begin();
   radio.setDataRate(RF24_250KBPS);         // выбор скорости RF24_250KBPS RF24_1MBPS RF24_2MBPS
   radio.setPALevel(RF24_PA_MAX);           // выходная мощность передатчика RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX,
   radio.setChannel(NRF24_CHANEL);          // тут установка канала
   radio.setCRCLength(RF24_CRC_16);         // использовать контрольную сумму в 16 бит
   radio.setAutoAck(true);                  // выключить аппаратное потверждение
// radio.enableDynamicPayloads();           // разрешить Dynamic Payloads
   radio.enableAckPayload();                // Разрешить ответ приемника AckPayload
   radio.setRetries(50,10);                 // Количество пауза и количество повторов 
  // Рекомендуют первые 2-4 байта адреса устанавливать в E7 или 18 он проще детектируется чипом
  radio.openWritingPipe(0xE7E7E7E7E1LL);    // передатчик 
  radio.openReadingPipe(1,0xE7E7E7E7D2LL);  // приемник
  radio.startListening();
  radio.powerDown();
  return true;
 }
 
//  Посылка пакета ----------------------------------------
bool sendRadio(void)
{  unsigned long t=0;
   bool send_packet_ok=false;
  //  Включение чипа nrf24
  SetOutput(B,5);     // slk - выход сделать
  SetOutput(B,3);     // mosi - выход сделать
  SPI.begin();        // старт spi заново
  radio.powerUp();
  delay(1);               
  radio.stopListening();     // Остановить приемник перейти в режим передачи
  delay(2);                  // дать время на переключение
  cli();
  radio.writeBlocking(&packet_0x30,sizeof(packet_0x30),200);  // Writes 1 payload to the buffers
  send_packet_ok=radio.txStandBy();
  if ( radio.isAckPayloadAvailable() )            // Ждем получения -- основной блок передает текущее время
           radio.read(&tt,sizeof(t));             //... и имеем переменную tt с временем от приемника.
  #ifdef LOG         
      else save_err(ERR_NRF24_ASK);               // ответ не получен  запишем ошибку
  #endif      
  sei(); 
  if (t>0)  tt=t;                                 // Если пришел ответ обновить время
  #ifdef  DEBUG  
    if (send_packet_ok==true)  Serial.println(F("Packet sending ++++++++++"));
    else                       Serial.println(F("Packet NOT sending -----------"));
    Serial.flush();
   #endif   
   
   #ifdef LOG         
      if (send_packet_ok==false)  save_err(ERR_NRF24_SEND); //  пакет  не послан запишем ошибку
   #endif 
    
  //  Выключение чипа nrf24
  radio.powerDown();  // выключаем чип но это не все!!!
  SPI.end();          // выключаем SPI
  PullUp(B,5);        // slk - сделать входом и подтянуть к 3.3 вольтам
  PullUp(B,3);        // mosi - сделать входом и подтянуть к 3.3 вольтам
  packet_0x30.count++;   // при переполнении сам сбросится
 } 
 
void reset_sum(void)  // Сброс счетчиков накоплений
{  sensor.num=0;
   sensor.Vcc=0;
   sensor.Temp=0;
   sensor.relH=0;
   sensor.absH=0;
   sensor.P=0;  
}

// Измерение датчика -----------------------------------------------
bool measurement(void)
{
  char status;
  double T,P,p0,a;
  packet_0x30.error=0;
  
  if (BMP180_ON()==false) return false; // датчик не работает выходим
  status = pressure.startTemperature();
  if (status != 0)
  {
    delay(status);
    status = pressure.getTemperature(T);
    packet_0x30.error=pressure.getError();  // Запомнить ошибку 
    if (status != 0)
    {
      #ifdef  DEBUG  
      Serial.print(F("Temperature: "));
      Serial.print(T,2);
      #endif
       
      status = pressure.startPressure(3);
      if (status != 0)
      {
        delay(status);
        status = pressure.getPressure(P,T);
        packet_0x30.error=packet_0x30.error+8*(pressure.getError());  // Запомнить ошибку 
        if (status != 0)
        {;
          #ifdef  DEBUG 
          Serial.print(F(" C absolute pressure: "));
          Serial.print(P,2);
          Serial.println(F(" mb"));
          Serial.flush();
          #endif
         }
        else  
        {;
        #ifdef  DEBUG  
          Serial.println("error retrieving pressure measurement\n"); 
        #endif
        #ifdef LOG         
           save_err(ERR_SENSOR_PR); //  Ошибка чтения датчика
        #endif 
        }
      }
      else 
      {;
      #ifdef  DEBUG 
        Serial.println("error starting pressure measurement\n"); 
      #endif
      #ifdef LOG         
           save_err(ERR_SENSOR_PS); //  Ошибка чтения датчика
      #endif 
      }
    }
    else 
    {;
    #ifdef  DEBUG 
     Serial.println("error retrieving temperature measurement\n"); 
    #endif
    #ifdef LOG         
        save_err(ERR_SENSOR_TR); //  Ошибка чтения датчика
    #endif 
    }
  }
  else 
  {;
  #ifdef  DEBUG 
    Serial.println("error starting temperature measurement\n"); 
  #endif
  #ifdef LOG         
      save_err(ERR_SENSOR_TS);  //  Ошибка чтения датчика
  #endif 
  }
 BMP180_OFF();
if (packet_0x30.error==0)  // Если нет ошибок усредняем и копим сумму
{
   sensor.num++;
   sensor.Vcc=sensor.Vcc+readVcc();
   sensor.Temp=sensor.Temp+T*100;
   sensor.P=sensor.P+P*10;  
   return true;
}
return false;
} 

// Включение датчика BMP180 ----------------------------------
bool BMP180_ON(void)    
{   SetOutput(B,2);       // Подать питание
    SetBit(B,2); 
    //delay(5);
    if(pressure.begin()!=1) // Инициализация BMP180 
    {
     #ifdef LOG   
     save_err(pressure.getError()-'0'); // Если надо запишим ошибку 
     #endif  
     return false;
    } 
    else  return true;
 } 
 
// Выключение датчика BMP180 ---------------------------------
void BMP180_OFF(void)  
{
  PullUp(B,2);  
} 

#ifdef LOG  
// Запись счетчиков в Eeprom --------------------------------------------------
void writeEeprom()
{ 
  if (logRAM.flag_save==true)  // Если не было изменений то выходим без записи
  {
    logRAM.flag_save=false;              //  Сборосить флаг записи
    cli();   
    eeprom_write_block((const void*)&logRAM, (void*) &logEEPROM, sizeof(logRAM)); 
    sei();
  }

}
// Чтение счетчиков из Eeprom --------------------------------------------------
void readEeprom()
{
cli(); 
   eeprom_read_block((void*)&logRAM, (const void*) &logEEPROM, sizeof(logRAM)); 
sei();
}

// Запись ошибки в лог -----------------------------------------------
void save_err(int code)
{
 if (code<ERR_NRF24_ASK) logRAM.err_sensor++; else  logRAM.err_nrf24++;  // добавление счетчиков ошибок
 if (logRAM.first_err==0) logRAM.first_err=tt;
 logRAM.last_err=tt;
 logRAM.event[logRAM.count].t=tt;
 logRAM.event[logRAM.count].err=code;
 logRAM.count++;                               // счетчик записей увеличиваем
 if (logRAM.count==NUM_LOG) logRAM.count=0;    // делаем кольцевой буфер
 logRAM.flag_save=true;                        // Признак необходимости записи
 writeEeprom();                                // Запись в еепром
} 
#endif

#ifdef DEBUG
// Очистка лога  и запись его в еепром -----------------------------------------------
void clear_log(void)
{   int i;
    Serial.print(F("Erase logs . . . "));
    logRAM.err_sensor=0;
    logRAM.err_nrf24=0;
    logRAM.last_err=0;
    logRAM.first_err=0;
    logRAM.count=0;
    logRAM.flag_save=true;
    for (i=0;i<NUM_LOG;i++)
      {
      logRAM.event[i].t=0;
      logRAM.event[i].err=0;
      } 
    writeEeprom();
    Serial.println(F(" OK"));
//    Serial.flush();    
} 
// Вывод лога на экран -----------------------------------------------
void view_log(void)
{   int i, x;
    Serial.println(F("\n- LOG  ----------"));
    Serial.print(F("Number error read sensor: ")); Serial.println(logRAM.err_sensor);
    Serial.print(F("Number error nrf24: ")); Serial.println(logRAM.err_nrf24);
    Serial.print(F("Time first error: "));// Serial.println(logRAM.first_err);
      Serial.print(day(logRAM.first_err)); Serial.print(F("/"));
      Serial.print(month(logRAM.first_err)); Serial.print(F("/")); 
      Serial.print(year(logRAM.first_err)); Serial.print(F(" "));
      Serial.print(hour(logRAM.first_err)); Serial.print(F(":"));
      Serial.print(minute(logRAM.first_err)); Serial.print(F(":"));
      Serial.println(second(logRAM.first_err));
    Serial.print(F("Time last error: "));      //Serial.println(logRAM.last_err); 
      Serial.print(day(logRAM.last_err)); Serial.print(F("/"));
      Serial.print(month(logRAM.last_err)); Serial.print(F("/")); 
      Serial.print(year(logRAM.last_err)); Serial.print(F(" "));
      Serial.print(hour(logRAM.last_err)); Serial.print(F(":"));
      Serial.print(minute(logRAM.last_err)); Serial.print(F(":"));
      Serial.println(second(logRAM.last_err));  
    Serial.print(F("Count log event: ")); Serial.println(logRAM.count);
    Serial.println(F("Detals logs: "));
    if ((logRAM.count<NUM_LOG)&&(logRAM.err_sensor+logRAM.err_nrf24<=NUM_LOG)) x=logRAM.count; else x=NUM_LOG;  // если мало ошибок то выводим не весь лог
      for (i=0;i<x;i++)
   //     { Serial.print(i); Serial.print(F(". time:")); Serial.print(logRAM.event[i].t); Serial.print(F(" err:")); Serial.println(logRAM.event[i].err,HEX);}
    {  // дешефровка времени
      Serial.print(i); Serial.print(F(". Time:"));
      Serial.print(day(logRAM.event[i].t)); Serial.print(F("/"));
      Serial.print(month(logRAM.event[i].t)); Serial.print(F("/")); 
      Serial.print(year(logRAM.event[i].t)); Serial.print(F(" "));
      Serial.print(hour(logRAM.event[i].t)); Serial.print(F(":"));
      Serial.print(minute(logRAM.event[i].t)); Serial.print(F(":"));
      Serial.print(second(logRAM.event[i].t)); Serial.print(F(" "));
      Serial.print(F(" err:")); Serial.println(logRAM.event[i].err,HEX);
    } 
    
    if (logRAM.count==0)  Serial.println(F("Logs empty . . ."));
     
}
 #endif

Фото блока с "антенной"

Прием данных на удаленный блок

 

 

Ivanii
Offline
Зарегистрирован: 11.10.2015

Очень интересная тема, собираюсь делать сеть регистраторов, типа метиостанция, питание будет сетевое с резервированием банками 18650 и передачей на ПК для анализа.

Toto_G
Offline
Зарегистрирован: 20.11.2015

  Хорошая тема, для себя тоже многое  найду.

Хотел бы по поводу питания. Этот робитон, есть мнение, не справляется с токами включения. По самому робитону даташита не нашёл, сайт их висит, тот что давал человек выше в этой теме, на аналог - http://www.evebattery.ru/datas/menu/eve/er14250_eve.pdf - 15мА - максимальный! продолжительный ток. При это задуманный производителем нормальный ток отдачи таких батареек - 2 мА. 

  То есть 100мА (нрф с усилителем) она никак не вытягивает, 20мА (обычная нрф) - с трудом.  Потому и понадобились вам чуднЫе конденсаторы и пляски с пролжительностями включения и тд.

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

pav2000, учитесь кроме кода и схемы рисовать, а то моветон получается какой то..

edgi
Offline
Зарегистрирован: 02.09.2014

pav2000, времени прошло прилично со дня создания. Мне интересно сколько батарейка прожила? год хотя бы выдержала?

udavst
udavst аватар
Offline
Зарегистрирован: 29.11.2013

:) Примерно в то же время, создания топика, делал себе термометр на улицу. Сначала было на 27МГц, плохо было, не дальнобойно, не всё доходило и тп. Сделал на nrf, заработало сразу, передавало данные каждые 4 минуты, использовал 2ААА батарейки, через полгода батарейки сели, поставил новые - сразу сели, потом обнаружился паучёк умерший на плате датчика, а тк питание датчику я не снимал, он и начал жрать. Тогда переделал датчик, ну в смысле больше прошивку, полёт нормальный пока, напряжение на батарейках упало за месяц на 0.04v с учетом, что на улице мороз был.

Ну температура разная - всё в разное время делалось.

Ну и кривой но простой и рабочий код. Эх, спойлеров тут нет.

//-----mini(prog)-------------aka Oregon-----------------------------
#include "LowPower.h"
#include <SPI.h>                       
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(9,10);                       //ce-9 ,csn-10
#include "i2c_SI7021.h"
SI7021 si7021;
int16_t datadat[5]={79, 0, 0, 0, 0}; //данные 0-"o" (79 - ID передатчика), 1-количество передач, 2-температура, 3-влажность, 4-батарейка
float temp; 
bool debug=1;
//======================================================================================================= 
void setup() {
if (debug) Serial.begin(2400);
if (debug) Serial.print ("Setup..");
for (temp = 0; temp <= A7; temp++)  // для экономии энергии кладём аналоговые выходы
   { pinMode (temp, OUTPUT);     digitalWrite (temp, LOW);  }
ADCSRA &= ~(1 << ADEN);            //для экономии энергии oтключаем ADC
pinMode(8, OUTPUT);  digitalWrite(8, HIGH); //питание на ногу включения датчика и радио
si7021.initialize();
radio.begin(); 
radio.setChannel(42);            // Указываем канал передачи данных (от 0 до 127), 5 - значит передача данных осуществляется на частоте 2,405 ГГц
radio.setPayloadSize(32);
radio.setDataRate(RF24_250KBPS); // скорость обмена данными RF24_250KBPS, RF24_1MBPS или RF24_2MBPS
radio.setPALevel (RF24_PA_MAX);  // Указываем мощность передатчика (RF24_PA_MIN=-18dBm, RF24_PA_LOW=-12dBm, RF24_PA_HIGH=-6dBm, RF24_PA_MAX=0dBm)
radio.openWritingPipe(000000041);     // открыть канал на отправку
radio.stopListening();           // приём остановить
delay(1000);     
if (debug) Serial.println ("...end");
} 
//======================================================================================================= 
void loop() {
  datadat[1]++;
  si7021.getHumidity(temp);        datadat[4]=temp*10;       //влажность   *10
  si7021.getTemperature(temp);     datadat[3]=temp*10;       //температура *10
  si7021.triggerMeasurement();                                
  temp=(float)(1.1*16368)/Vbg();   datadat[2]=temp*100;      //батарея     *100              
if (debug) {Serial.print("Bat:"); Serial.print(temp,3); 
            Serial.print(" To.txt="); Serial.print((datadat[3]/10));  
            Serial.print(" Ho.txt="); Serial.print(datadat[4]/10); 
            Serial.print(" #: "); Serial.println(String(datadat[1]));}
  if (!radio.write(&datadat, sizeof(datadat)))               //2 попытки передачи
      if (!radio.write(&datadat, sizeof(datadat))) 
           if (debug) Serial.println (" Send error");
if (debug) Serial.print (" Sleep... ");
radio.powerDown();                                           //отключение радиомодуля
digitalWrite(8, LOW);                                        //отключение дачика
delay(200);
int i=0; while(i<120) {i++; LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); } //сон 8x120 (16 мин, на самом деле 18.1 мин)
if (debug) Serial.println ("...Wake Up!");
digitalWrite(8, HIGH);                          //включение датчика
radio.powerUp();                                //включение радиомодуля
delay(400);
}
//-------------------------------------------------------------------------------------------------------

int Vbg() {  
ADMUX = (1<<REFS0)|(0<<REFS1)|(1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(0<<MUX0);
long buffersamp=0;
for (int n=0x0; n<=0xff; n++ ) {
ADCSRA = 0xc7;
while (bit_is_set(ADCSRA,ADSC));
buffersamp += ADC; }
buffersamp >>=4; //16368 full scale 14bit
ADCSRA &= ~(1 << ADEN);  // отключаем АЦП
return buffersamp;
 }

 

bulat943
Offline
Зарегистрирован: 19.09.2016

Можно выложить #include "LowPower.h"?

Нашел!