Jump to content
iT4iT.CLUB
Sign in to follow this  
Neon

NRF24L01 на ESP8266

Recommended Posts

в теме про метеостанцию писал про датчик с передатчиком на NRF24L01 и возможность использования с метеостанцией

простой код приемника на esp

// https://aterlux.ru/article/nrf24l01p

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line, для 20x4 - 0x3F

//#include <SPI.h>
//#include "nRF24L01.h"
#include "RF24.h"
int32_t data[5];
RF24 radio(99, 15); // CE, CSN, 99 - это заглушка, чтобы освободить один пин, если передача не предполлагается то можно CE просто подтянуть резистором к питанию, иначе нужно указать вывод

void setup(void)
{
  lcd.init();                  
  lcd.backlight();
  
  radio.begin();
  radio.setAutoAck(true);                // radio.setAutoAck(true);   включение и ли отклучение автоподтверждения
  radio.setChannel(100);
  radio.setDataRate     (RF24_2MBPS);   // скорость обмена RF24_2MBPS, RF24_1MBPS, RF24_250KBPS - максимальная чувствительность на 250 кбитс, NRF24L01 без плюса 250 не умеет
  radio.setPALevel      (RF24_PA_MAX);    // уровень мощности RF24_PA_MIN -18dBm, RF24_PA_LOW -12dBm, RF24_PA_HIGH -6dBM, RF24_PA_MAX 0dBm
  radio.openReadingPipe(1, 0xAABBCCDD11LL); 
  radio.startListening();
}


void loop(void) {
  
  if(radio.available()){                                         // Если в буфере имеются принятые данные, то получаем номер трубы, по которой они пришли, по ссылке на переменную pipe
        radio.read( &data, sizeof(data)); }                      // Читаем данные в массив data и указываем сколько байт читать

     lcd.setCursor(0,0);
     lcd.print("Temp ");
     lcd.print(float(data[3])/100, 2); lcd.print(" "); lcd.print((char)223); lcd.print("C");

     lcd.setCursor(0,1);
     lcd.print("Battery ");
     lcd.print(float(data[0])/1000,3); lcd.print(" v");

     lcd.setCursor(0,2);     // на некоторых 4 строчных дисплеях в 3 и 4 строке ноль сдвинут на 4 символа вправо, поэтому нулевой символ это -4
     lcd.print("Packet N ");
     lcd.print(data[1]);  
        
     delay (100);
}

 

 

код передатчика

 


//  тема на форуме - http://forum.ixbt.com/topic.cgi?id=48:12460&r=lYjQSTixmu#2
//  библиотека для датчика BME280 - https://github.com/sergeyastakhov/BME280 (чтобы не было конфликтов с другими библиотеками, желательно ее разместить в папке скетча)
//  потребление всего модуля в power save - 2.0 мкА(из них NRF24 - 0.9 мкА), 1.1 мкА - atmega168
//  обязательно параллельно батарейке доп емкость около 2200мкф 6.3 в - падение при передаче 10 мВ, ток утечки конденсатора после подформовки менее 100 нА, - http://forum.ixbt.com/topic.cgi?id=48:11468:3073#3073
//  измерение напряжение батарейки с помощью измерения питания atmega
//  еще можно питать атмегу напрямую от лития и измерять ее напряжение, а NRF и BME280 питается от стабилизатора 3.3 вольта с маленькой утечкой, например mcp1700-3302, ток утечки около 1.5 мкА
//  I2C преобразователь уровней не нужен, главное чтобы подтягивающие резисторы I2C питались от 3.3 вольт
// проблема с вотчдогом - http://arduino.ru/forum/apparatnye-voprosy/ne-mogu-vylechit-vatchdog-na-goloi-atmege328r-s-vnutr-taktirovaniem-8-mgts

#include <avr/wdt.h>
#include <avr/sleep.h>
//#include <avr/power.h>

#include "RF24.h"
RF24 radio(9, 10);  // CE_PIN, CSN_PIN
byte count = 0;     // счетчик количества просыпаний
int32_t data[5];    // на данный момент используется int32_t чтобы иметь возможность получить большие числа счетчика передач, для тестирования продолжительности работы от батарейки,
                    // потом можно обычный int
                    // data 0 - данные АЦП или пересчитанное напряжение питания c тремя знаками(в милливольтах, на стороне приемника переменная float деленная на 1000
                    // data 1 - счетчик отправленных пакетов, сбрасывается при снятии питания
                    // data 2 - P атмсомферное в мм, можно умножить на 10 если нужны десятые, тогда на стороне приемника делить на 10 и float
                    // data 3 - Т температура умноженное на 100 на стороне приемника float делить на 100
                    // data 4 - H влажность умноженная на 100
              
#include "BME280.h" // при таком написании компилятор ищет библиотеку в первую очередь в папке скетча
using namespace BME280; 
class CustomI2CProtocol : public I2CProtocol {
  public:
    virtual void init() {
      I2CProtocol::init();
      Wire.setClock(400000);  // частота работы I2C, 400 кГц - максимум для BME280, уже чувствительна к качеству и длине линии
    }                         // реальная частота I2C зависит от регистра и строки в wire.cpp - TWBR = ((F_CPU / frequency) - 16) / 2 и при частоте 8 МГц она ниже, около 250 кГц
                              // https://www.avrfreaks.net/forum/twi-clock-divider?name=PNphpBB2&file=viewtopic&t=79562
}; 
BME280Sensor bme280 = BME280Sensor(new CustomI2CProtocol()); //BME280Sensor bme280 = BME280Sensor(new I2CProtocol()) - I2C работает на частоте по умолчанию - 100 кГц 

ISR (TIMER2_OVF_vect) {                             // из прерывания по таймеру 2 нельзя выходить слишком быстро, поэтому инкрементирование переменной расположим здесь
  count++;
  }
   
void setup() {
  MCUSR = 0;                                        // необходимо при использовании вотчдога для сброса контроллера при зависании на выпонении кода
  wdt_disable();                                    // иначе при первом сбросе вотчдогом, будет установлено минимальное время вотчдога 0.15 с, и мк зависнет на этом цикле
  PORTC |= (1 << PC1);                              // включение светодиода, для индикации запуска мк
  delay(10);

  DDRB = 0b11000001;                                // настраиваем неиспользуемые выводы(и порты) на выход
  DDRC = 0b11111111;
  DDRD = 0b11111111;
  
  //clock_prescale_set(clock_div_2);                  // деление тактовой частоты  для получения 4 МГц для работы atmega168 вплоть до 1.8 вольта
  bme280.init(false);
  bme280.setHumidityMode(Over_1);                   // установка режима измерения влажности, остальные при запуске измерения
  //bme280.setMode(Forced, Over_1, Over_1, Over_1);   // Forced - однократный режим измрения, все оверсемплинги(T, P, Hum) = 1

  PRR = (1<<PRTIM1) | (1<<PRUSART0);                // отключаем неиспользуемую периферию, (1<<PRTIM2) - работает в асинхронном режиме
                                                    // (1<<PRTIM0) - так не работает передача, видимо count использует этот таймер
                                                    // (1<<PRSPI), (1<<PRTWI)- не отключаем,  SPI - для NRF24 и TWI(по другому I2C) - для BME280
  radio.begin();
  radio.setAutoAck(true);                    // подтверждение приема, true - включено, false - отключено
  radio.setRetries   (2, 1);                 // количество попыток передачи если включен режим автоподтверждения приема, первая цифра - задержка между передачами, 1 = 250 мкс, вторая - количество попыток передачи
  radio.setChannel(100);                     // номер канала, можно перед выбором просканировать эфир чтобы выяснить менее зашумленные каналы(использовать скетч scanner из библ. RF24)
  radio.setDataRate     (RF24_2MBPS);        // скорость обмена RF24_2MBPS, RF24_1MBPS, RF24_250KBPS - максимальная чувствительность на 250 кбитс
  radio.setPALevel      (RF24_PA_MAX);       // уровень мощности RF24_PA_MIN -18dBm, RF24_PA_LOW -12dBm, RF24_PA_HIGH -6dBM, RF24_PA_MAX 0dBm
  radio.openWritingPipe (0xAABBCCDD11LL);    // уникальный номер передатчика, на приемнике должен быть такой же
  
    ADCSRA = 0;                              // отключаем АЦП
    ACSR |= (1<<ACD);                        // отключаем компаратор
    ADMUX =  _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);            // настраиваем АЦП на измерение собственного напряжения питания, опорное 1.1 вольт
    DIDR0 = 1;                                                          // отключаем цифровой вход выводов АЦП, он тоже немного потребляет

    ASSR |= (1<<AS2);                         // Асинхронный режим
    TCCR2A = (0<<WGM21)|(0<<WGM20);           //режим normal
    TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20);   // clk/1024
    TCNT2 = 0x00;                             // начальное значение таймера
 // OCR2A = 0x00; OCR2B = 0x00;
    TIMSK2 |= (1 << TOIE2);                   // разрешаем прерывание по переполнению Т2  
    
    SMCR = _BV(SM1) | _BV(SM0);               // Power_Save,  set_sleep_mode(SLEEP_MODE_PWR_SAVE),  set_sleep_mode (SLEEP_MODE_PWR_DOWN) - выбор режима сна
    radio.powerDown();   

    PORTC &= ~_BV(PC1);                       // выключение светодиода 
}                             

void loop() {
    if ( count >= 8 ) {        // передавая один раз в 64 секунды = 8
      radio_();
      TCNT2 = 0x00;
      }   
    while(ASSR&(1<<TCN2UB)){;} //wait to TCNT update!!! 
    TCNT2 = 0x00;                                    
    sleep_enable();  //SMCR |= _BV(SE); 
    sleep_cpu ();                                             
}

void radio_() {
  wdt_enable(WDTO_1S); 
  radio.powerUp();
  data[1]++;
  
  ADCSRA |= _BV(ADEN);                            // включаем АЦП
   
  Measurement measurement = bme280.readMeasurement();                     // опрос датчика и расчет параметров  
  data[2] = (measurement.getPressure32()*1000/133322);
  data[3] = (measurement.getTemperature()*100);
  data[4] = (measurement.getHumidity()*100);
  bme280.setMode(Forced, Over_1, Over_1);           // запуск однократного измерения и установка оверсемплингов T, P, без влажности
  //bme280.setMode(Forced, Over_1, Over_1, Over_1); // запуск однократного измерения и установка оверсемплингов T, P, Hum
    
  ADCSRA |= _BV(ADSC);                            // запуск конверсии               
  while (bit_is_set(ADCSRA,ADSC));                // измерение
  uint8_t low  = ADCL;                            // сначала читаем ADCL - это расблокирует чтение ADCH  
  uint8_t high = ADCH;                           
  int volt = (high<<8) | low;                     // данные АЦП
  data[0] = (1135494/volt);                       // 1135494 подобрано эксперементально и в общем случае равно 1100 милливольт опорное * 1024, но опорное не всегда 1100 мВ
    
  ADCSRA = 0;                                     // отключаем АЦП          
  
  // ******************************************** // код до этого места примерно 1700 мкс, убираем в файле RF24.cpp задержку delay(5) и добавляем перед отправкой данных 
                                                  // задержку в 2.3 мс, чтобы в сумме задежка получилась 4 мс, это минимальная задержка от команды powerUp() до write()
                                                  // при которой стабильно работает большой модуль на RF24, smd модуль работает и при меньшей задержки, т.к. разные кварцы
                   
  clock_prescale_set(clock_div_256);              // на время задержки снижаем частоту контроллера до минимально возможной, для снижения энергопотребления                                                                  
  delayMicroseconds(10);                          // на этой частоте для получения 2.3 миллисекунд нужно указать задержку в 256 раз меньше ~ 9 мкс                                                 
                                                  // в этом режиме потребляет только atmega, которая работает на частоте 1/256 от номанальной
  clock_prescale_set(clock_div_1);                // перед отправкой данных снова устанавливаем исходную частоту работы контроллера                                                
  radio.write(&data, sizeof(data));             
  radio.powerDown();                         
  count = 0;
  wdt_disable();
} 

 

Edited by Neon
  • Like 1

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...