радио пульт с максимальной энергоавтономностью

zert
Offline
Зарегистрирован: 08.07.2016

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

Опишу ситуацию сначала, в своем доме решил для упрощения жизни поставить на насос радио реле, насос используется для накачки воды в различные резервуары(баня и тд). Захотелось сделать обычный пульт на ардуино. Да можно просто вскрыть пульт и припаятся к кнопке если такова одна. На уже имеющемся не хотелось ничего портить паяльником (из личного опыта, из трех пультов смог только на двух сделатьэто, на третьем явно накосячил, только хз, либо из-за того что сместил smd конденсатор, те пульт по выведенной отдельно кнопке то отсылает то нет сигнал(батареи меняю, без разницы), то меняю то нет)

zert
Offline
Зарегистрирован: 08.07.2016

еще ненмого предыстории 

Те работает такой пульт периодично(в несколько недель, то да, то нет). Как я уже сказал, если кнопки 2 то сделать обычный выключатель включатель я не знаю как. да можно выбирать однокнопочный пульт или настравивать отдельно куленный однокнопочный пульт или все сразу купить корреткно. Но я живу в деревне, тут почти ничего нет, закупаясь ардуино и состовляющими, я взял сразу в прозапас. Вот и решил использовать, ардуионо(UNO R3). 

 

Была задача, сделать простой штатный включатель выключатель(по радио), но чтобы все это было полностью автономным. Начал изучать интернет, на готовые варианы. Однозначного ответа я не получил. Сначала натолкнулся на статью https://sites.google.com/site/vanyambauseslinux/arduino/ispolzovanie-preryvanij-arduino , да много чего не понятного, такты какие-то ардуина и куча переферии на ней, прерывания какие-то(до этого у меня за плечами только курс из 5ти уроков Джереми Блюма). Из всего этого меня удивило, что led на ардуине отвечающий за питание, вобщее никак не отключить(все и везде рекомендуют ради примерно 2мА спиливать его, я это еще не делал) и это как-то сыровато(по смыслу).  В итоге в статье , на которую я дал ссылку, имеется подстатья со следующей темой: "Пробуждение Arduino из спящего режима по нажатию кнопки", вроде что нужно 



#include <ve_avr.h>      // Будет использоваться библиотека VEDuino. 
//#include <RemoteSwitch.h> // это скачанная библиотека
//#include <RemoteReceiver.h>

#define LEDPIN 12        // Вывод светодиода
#define BTNPIN 2          // Вывод кнопки
#define LEDPI 13
#define PERIOD 246;
boolean onoff = LOW;
volatile int counnt = 0;      // Переменная счётчика (volatile означает указание компилятору не оптимизировать код её чтения,
                                           // поскольку её значение изменяется внутри обработчика прерывания)

ISR(INT0_vect)      // Функция обработки прерывания INT0
{
   counnt = 25;           // Инициализировать счётчик
}

void setup() {
    // Serial.begin(9600);
    
    pinMode(LEDPI, OUTPUT);
    pinMode(LEDPIN, OUTPUT);         // Вывод светодиода в режим вывода
    pinMode(BTNPIN, INPUT);             // Вывод кнопки в режим ввода
    digitalWrite(LEDPIN, LOW);
    DEV_EXTINTCTRL.setSenseType0(ExtIntControl::RISING_EDGE);    // Прерывание INT0 в режиме переднего фронта (в данном случае при нажатии на кнопку)
    DEV_EXTINTFLAGS.enableInterrupt0();  // Разрешить прерывание INT0
    interrupts();                                 // Разрешить прерывания глобально
}


void loop() {
    //interrupts(); 
    if(counnt==0) {    
    //delay(10);
     digitalWrite(LEDPIN, LOW);        // Выключить светодиод, если счётчик равен 0...
   // Serial.print("VIKl");
   // Serial.println("");
    DEV_SLEEP.setMode(SleepControl::PWR_DOWN);  // Выбор режима сна
    DEV_SLEEP.enableSleep();                    // Разрешить переход в спящий режим
    sleep();                                    // Переход в режим сна
    //Serial.println("");  
  }
  //Serial.print("VIKl");
  else {
    //delay(10);
    delay(10);
    //if (digitalRead(BTNPIN) == HIGH) {
      //Serial.println("");
      while (digitalRead(BTNPIN) == HIGH )  {
      digitalWrite(LEDPIN, HIGH);       // ... иначе включить светодиод,
      
      if (onoff == LOW)
      {
       //Serial.println(" on ");
       //transmit(477522);
      }
      else {
        //Serial.println(" off ");
        //transmit(477495);
      }
            //Serial.println("");
      //delay (500);
      }
    //}
    //else {
      counnt = 0;
      if (onoff == LOW)
      onoff = HIGH;
      else
      onoff = LOW;
  
}
      //delay (1000);
    //}
    //--count;                         // и уменьшить счётчик на 1.
    //delay(10);                          // Подумать 10 милисекунд.
  }
  
/*
void transmit(unsigned long rcode){

unsigned long code = rcode;

unsigned long period = PERIOD;
//for (unsigned long period=1; period <= 469; period++)
//{
  //delay(1000);
  //Serial.print("period: " );
    //Serial.print(  period);
  code |= (unsigned long)period << 23;

code |= 4L << 20; //(|= 4L) цифра перед (L), это (условное число), количества повторов посылаемого сигнала. (соответственно и паузы)

RemoteSwitch::sendTelegram(code, 9); // RF transmitter pin - пин радио передатчика
}
*/

в таком варианте вся балалайка работает(светодиод горит, пока нажата кнопка, отпускаю уходит в сон), смотрел мультиметром(правда он показывал только изменения в сотых А, тысячных увы нет), как видно из кода я пытался прикрутить отправку кодов ридиопульта(все что в коментах), но при инициализации библиотеки #include <RemoteSwitch.h>, появилась ошибка , без понятия, как ее устранить 

ошибка : 

Arduino: 1.6.9 (Windows 10), Плата:"Arduino/Genuino Uno"


C:\Users\zert\AppData\Local\Temp\build494fa2eb5d2721099ba121f2e5c2e728.tmp/core\core.a(WInterrupts.c.o): In function `__vector_1':

C:\Program Files (x86)\Arduino1\hardware\arduino\avr\cores\arduino/WInterrupts.c:309: multiple definition of `__vector_1'

sketch\pultvodabana.ino.cpp.o:C:\Users\zert\Documents\Arduino\pultvodabana/pultvodabana.ino:16: first defined here

collect2.exe: error: ld returned 1 exit status

exit status 1
Ошибка компиляции для платы Arduino/Genuino Uno.
 
 
zert
Offline
Зарегистрирован: 08.07.2016

Я думал, сразу написать о своей проблеме на форуме, но интуиция мне подсказывала, что вообще никто коктрено не поможет, различные манипуляции со скетчем, никак не влияло на ошибку, стало понятно, что конфликт библиотек скорее всего. Решил найти еще приближенные скетчи по данной теме. Наткнулся на видео http://playground.arduino.cc/Learning/arduinoSleepCode , которое ссылалось на статью http://http://playground.arduino.cc/Learning/arduinoSleepCode, где я взял скетч:


#include <avr/sleep.h>
 
/* Sleep Demo Serial
 * -----------------
 * Example code to demonstrate the sleep functions in an Arduino.
 *
 * use a resistor between RX and pin2. By default RX is pulled up to 5V
 * therefore, we can use a sequence of Serial data forcing RX to 0, what
 * will make pin2 go LOW activating INT0 external interrupt, bringing
 * the MCU back to life
 *
 * there is also a time counter that will put the MCU to sleep after 10 secs
 *
 * NOTE: when coming back from POWER-DOWN mode, it takes a bit
 *       until the system is functional at 100%!! (typically <1sec)
 *
 * Copyright (C) 2006 MacSimski 2006-12-30
 * Copyright (C) 2007 D. Cuartielles 2007-07-08 - Mexico DF
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
 
int wakePin = 2;                 // pin used for waking up
int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter
 
void wakeUpNow()        // here the interrupt is handled after wakeup
{
  // execute code here after wake-up before returning to the loop() function
  // timers and code using timers (serial.print and more...) will not work here.
  // we don't really need to execute any special functions here, since we
  // just want the thing to wake up
}
 
void setup()
{
  pinMode(wakePin, INPUT);
 
  Serial.begin(9600);
 
  /* Now it is time to enable an interrupt. In the function call
   * attachInterrupt(A, B, C)
   * A   can be either 0 or 1 for interrupts on pin 2 or 3.  
   *
   * B   Name of a function you want to execute while in interrupt A.
   *
   * C   Trigger mode of the interrupt pin. can be:
   *             LOW        a low level trigger
   *             CHANGE     a change in level trigger
   *             RISING     a rising edge of a level trigger
   *             FALLING    a falling edge of a level trigger
   *
   * In all but the IDLE sleep modes only LOW can be used.
   */
 
  attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
                                      // wakeUpNow when pin 2 gets LOW
}
 
void sleepNow()         // here we put the arduino to sleep
{
    /* Now is the time to set the sleep mode. In the Atmega8 datasheet
     * http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf on page 35
     * there is a list of sleep modes which explains which clocks and
     * wake up sources are available in which sleep mode.
     *
     * In the avr/sleep.h file, the call names of these sleep modes are to be found:
     *
     * The 5 different modes are:
     *     SLEEP_MODE_IDLE         -the least power savings
     *     SLEEP_MODE_ADC
     *     SLEEP_MODE_PWR_SAVE
     *     SLEEP_MODE_STANDBY
     *     SLEEP_MODE_PWR_DOWN     -the most power savings
     *
     * For now, we want as much power savings as possible, so we
     * choose the according
     * sleep mode: SLEEP_MODE_PWR_DOWN
     *
     */  
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
 
    sleep_enable();          // enables the sleep bit in the mcucr register
                             // so sleep is possible. just a safety pin
 
    /* Now it is time to enable an interrupt. We do it here so an
     * accidentally pushed interrupt button doesn't interrupt
     * our running program. if you want to be able to run
     * interrupt code besides the sleep function, place it in
     * setup() for example.
     *
     * In the function call attachInterrupt(A, B, C)
     * A   can be either 0 or 1 for interrupts on pin 2 or 3.  
     *
     * B   Name of a function you want to execute at interrupt for A.
     *
     * C   Trigger mode of the interrupt pin. can be:
     *             LOW        a low level triggers
     *             CHANGE     a change in level triggers
     *             RISING     a rising edge of a level triggers
     *             FALLING    a falling edge of a level triggers
     *
     * In all but the IDLE sleep modes only LOW can be used.
     */
 
    attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
                                       // wakeUpNow when pin 2 gets LOW
 
    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
 
    sleep_disable();         // first thing after waking from sleep:
                             // disable sleep...
    detachInterrupt(0);      // disables interrupt 0 on pin 2 so the
                             // wakeUpNow code will not be executed
                             // during normal running time.
 
}
 
void loop()
{
  // display information about the counter
  Serial.print("Awake for ");
  Serial.print(count);
  Serial.println("sec");
  count++;
  delay(1000);                           // waits for a second
 
  // compute the serial input
  if (Serial.available()) {
    int val = Serial.read();
    if (val == 'S') {
      Serial.println("Serial: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep
                      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
    }
    if (val == 'A') {
      Serial.println("Hola Caracola"); // classic dummy message
    }
  }
 
  // check if it should go to sleep because of time
  if (count >= 10) {
      Serial.println("Timer: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep
                      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
  }
}
 
переделал я его в следующий код 
#include <avr/sleep.h>
#include <RemoteSwitch.h>
#define PERIOD 246;
 
int wakePin = 2;                 // pin used for waking up
int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter
int dd = 0; 
void wakeUpNow()        // here the interrupt is handled after wakeup
{
  
}
 
void setup()
{
  pinMode(wakePin, INPUT);
  pinMode(13, OUTPUT);
   digitalWrite(13, LOW);
  
  attachInterrupt(0, wakeUpNow, HIGH); // use interrupt 0 (pin 2) and run function
                                      // wakeUpNow when pin 2 gets LOW
}
 
void sleepNow()         // here we put the arduino to sleep
{
    
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
 
    sleep_enable();          // enables the sleep bit in the mcucr register
                             // so sleep is possible. just a safety pin
 
   
    attachInterrupt(0,wakeUpNow,HIGH); // use interrupt 0 (pin 2) and run function
                                       // wakeUpNow when pin 2 gets LOW
 
    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
 
    sleep_disable();         // first thing after waking from sleep:
                             // disable sleep...
    detachInterrupt(0);      // disables interrupt 0 on pin 2 so the
                             // wakeUpNow code will not be executed
                             // during normal running time.
                             
}
 
void loop()
{
  /* if (dd == 1)
      {
             transmit(477522); dd =0;
      }
      else {
                transmit(477495); dd = 1;
      }
    
   */ /*
   if (dd == 1)
      {
             transmit(366776); dd =0;
      }
      else {
                //transmit(477495)
                transmit(366776);
                 dd = 1;
      }
  */
  
  if (dd == 1)
      {
             transmit(51786); dd =0;
      }
      else if (count > 0) {
                transmit(51759); dd = 1;
      }
  //delay(1000);                           // waits for a second

     
      delay(100);     // this delay is needed, the sleep
                      //function will provoke a Serial error otherwise!!
      count++;
      sleepNow();     // sleep function called here
  //}
}
 
void transmit(unsigned long rcode){

unsigned long code = rcode;

unsigned long period = PERIOD;
//for (unsigned long period=1; period <= 469; period++)
//{
  //delay(1000);
  //Serial.print("period: " );
    //Serial.print(  period);
  code |= (unsigned long)period << 23;

code |= 4L << 20; //(|= 4L) цифра перед (L), это (условное число), количества повторов посылаемого сигнала. (соответственно и паузы)

RemoteSwitch::sendTelegram(code, 9); // RF transmitter pin - пин радио передатчика
}

данный код работает хорошо, нажал на кнопку отправился код на радиореле и ардуина ушла в сон, нажал повторно, ардуиона послала другой код. Те получил и экономию энергии и отправку на реле, осталось только прикрутить чтобы на втором пине, прерывание работало при 0 и при 1 , те подал 5ть вольт на второй пин, отправился один код и сон наступил сразу, разомкнул выключатель подал 0 на пин2 и чтобы послался другой код и наступил опять сон, над этим я еще думаю. В рабочем коде я использовал тактовую кнопку, вот не ясно для выключателя(включателя) нужен ли будет резистор стягивающий, наверное нет(хотя я хз насчет дребезга в данном случае). Но не это меня пока волнует, как достигнуть еще большей экономии энергии , так как в статье откуда я взял код, потребление энергии под 10 мА, при всей экономии.

zert
Offline
Зарегистрирован: 08.07.2016

Нашел статью, как можно перевести на потребление 6 мкА http://robotosha.ru/arduino/arduino-interrupts.html

а именно 


#include <avr/sleep.h>
#include <avr/wdt.h>
 
#define LED 13
 
// процедура обработки прерывания по нажатию кнопки
void wake ()                            
{
 wdt_disable();  // отключаем сторожевой таймер
}
 
// прерывание сторожевого таймера
ISR (WDT_vect) 
{
 wake ();
}
 
void myWatchdogEnable (const byte interval) 
{ 
 noInterrupts (); 
 
 MCUSR = 0;                          // сбрасываем различные флаги
 WDTCSR |= 0b00011000;               // устанавливаем WDCE, WDE
 WDTCSR =  0b01000000 | interval;    // устанавливаем WDIE, и соответсвующую задержку
 wdt_reset();
  
 byte adcsra_save = ADCSRA;
 ADCSRA = 0;  // запрещаем работу АЦП
 power_all_disable ();   // выключаем все модули
 set_sleep_mode (SLEEP_MODE_PWR_DOWN);   // устанавливаем режим сна
 sleep_enable();
 attachInterrupt (0, wake, LOW);   // позволяем заземлить pin 2 для выхода из сна
 interrupts ();
 sleep_cpu ();            // переходим в сон и ожидаем прерывание
 detachInterrupt (0);     // останавливаем прерывание LOW
  
 ADCSRA = adcsra_save;  // останавливаем понижение питания
 power_all_enable ();   // включаем все модули
}
 
void setup ()
{
 digitalWrite (2, HIGH);    // кнопка с подтягивающим резистором
}
 
void loop()
{
 pinMode (LED, OUTPUT);
 digitalWrite (LED, HIGH);
 delay (5000);
 digitalWrite (LED, LOW);
 delay (5000);
 
 // шаблон битов sleep:
 //  1 секунда:  0b000110
 //  2 секунды: 0b000111
 //  4 секунды: 0b100000
 //  8 секунд: 0b100001
 
  // засыпаем на 8 секунд
  myWatchdogEnable (0b100001);  // 8 секунд
}
«Голая» плата, то есть без регулятора напряжения, USB-интерфейса и прочего, с запущенным на ней приведенным выше кодом, потребляет:

Со светящимся светодиодом — 19.5 мА
При включенном светодиоде — 16.5 мА
В режиме сна — 6 мкА
Использование сторожевого таймера в комбинации с режимом сна может сберечь значительное количество энергии во время периодов, когда процессор может быть не нужен.

Это также иллюстрирует, что можно выйти из режима сна двумя различными способами. Один из них — нажатие клавиши (то есть заземление пина D2), другой — это периодическое просыпание (каждые 8 секунд), хотя вы можете сделать это каждые 1, 2, 4 или 8 секунд (возможно, даже короче, но для этого нужно подробнее изучить техническое руководство на микроконтроллер).

После того, как процессор проснулся, возможно, потребуется некоторое время, чтобы стабилизировать свой таймер. Например, вы можете увидеть «мусор» на последовательном порту, пока он синхронизируется. Если это проблема есть, вы можете установить короткую задержку после выхода из режима сна.

Замечание по-поводу определения пониженного напряжения: полученные выше цифры получены с отключенным определением пониженного напряжения (brownout detection). Для создания опорного напряжения для определения пониженного напряжения требуется небольшой ток. Если его включить, то в режиме сна будет потребляться около 70 мкА (вместо 6 мкА). Одним из способов выключить определение пониженного напряжения является использование компилятора avrdude из командной строки:


avrdude -c usbtiny -p m328p -U efuse:w:0x07:m
1
avrdude -c usbtiny -p m328p -U efuse:w:0x07:m
При помощи параметров устанавливается efuse (дополнительные фьюзы) равным 7, что отключает определение пониженного напряжения. Здесь предполагается, что используется программатор USBtinyISP.

 

осталось тока срастить что значит «Голая» плата, то есть без регулятора напряжения, сколько напряжения подавать на ардуино и как сделать функционал на обычый включатель выключатель