Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. General Discussion
  3. 6/8 Buttons battery remote node

6/8 Buttons battery remote node

Scheduled Pinned Locked Moved General Discussion
53 Posts 7 Posters 7.0k Views 8 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • wesW wes

    I have the same use case (remote control with buttons to control lights/scenes).

    I'm planning to use some cheap RF remotes and connect a RF receiver to my RPI, which hosts both my gateway and controller.

    Not sure if I'll receive the codes via MySensors, or write something that talks directly to the controller (thoughts on pros/cons of each approach welcome).

    Of course this won't be as reliable or flexible as buttons connected to a MySensors node, but it should be enough for my needs.

    Further thoughts welcome :-)

    Nca78N Offline
    Nca78N Offline
    Nca78
    Hardware Contributor
    wrote on last edited by
    #32

    @wes said in 6/8 Buttons battery remote node:

    I have the same use case (remote control with buttons to control lights/scenes).

    I'm planning to use some cheap RF remotes and connect a RF receiver to my RPI, which hosts both my gateway and controller.

    Not sure if I'll receive the codes via MySensors, or write something that talks directly to the controller (thoughts on pros/cons of each approach welcome).

    Of course this won't be as reliable or flexible as buttons connected to a MySensors node, but it should be enough for my needs.

    Further thoughts welcome :-)

    I think you should change the remote controls into MySensors nodes. A bit more work at the beginning but you'll probably save a lot of hair pulling, reliability etc in the long term.

    1 Reply Last reply
    0
    • CarywinC Offline
      CarywinC Offline
      Carywin
      wrote on last edited by
      #33

      You can use pin change interrupts with MySensors sleep loops with an easy hack. Here's the code I use for 4 button battery powered nodes that also report temperature and humidity using DHT a sensor. I run them from 2 AA and so far they've been running for more than 6 months without showing signs of discharge. This could be easily modified for 6 or 8 buttons.

      // MySensors EnviroButtons
      // Temperature and Humidity Sensor with 4 Push Buttons
      // Battery Powered Node
      // Don't forget to change buttons and VCC_CAL for each node
      
      // Cary Wintle - July 2017
      
      // MySensors configuration
      // -----------------------
      #define SN "EnviroButtons"  // Software name
      #define SV "0.4"            // Version number
      //#define MY_DEBUG
      #define MY_NODE_ID 6
      #define MY_RADIO_RFM69
      #define MY_RFM69_NETWORKID 137
      #define MY_RFM69_ENABLE_ENCRYPTION
      #define MY_RFM69_NEW_DRIVER
      #define MY_RFM69_FREQUENCY RFM69_433MHZ
      #define MY_IS_RFM69HW
      #include <MySensors.h>
      
      #include <SPI.h>
      #include <DHT.h>
      
      #define EI_NOTEXTERNAL // External interrupts managed by built-in routines
      #include <EnableInterrupt.h> // Pin-change interrupts
      
      // Set this to the pin you connected the DHT's data pin to
      #define DHT_DATA_PIN 8
      
      // Buttons
      #define BUTTON1_PIN A1 // 5: A1 - 6: A1
      #define BUTTON2_PIN A2 // 5: A3 - 6: A2
      #define BUTTON3_PIN A3 // 5: A2 - 6: A3
      #define BUTTON4_PIN A0 // 5: A0 - 6: A0
      
      // Set this offset if the sensor has a permanent small offset to the real temperatures
      #define SENSOR_TEMP_OFFSET 0
      
      // Sleep time between sensor updates (in milliseconds)
      // Must be >1000ms for DHT22 and >2000ms for DHT11
      static const uint64_t UPDATE_INTERVAL = 120000;
      #define BAT_UPDATE_INTERVAL 720 // 24 hrs - Interval between battery updates (multiples of UPDATE_INTERVAL)
      // VCC Calibration Values
      // Node 5: 1128953L
      // Node 6: 1125300L
      #define VCC_CAL 1125300L
      
      // Force sending an update of the temperature after n sensor reads, so a controller showing the
      // timestamp of the last update doesn't show something like 3 hours in the unlikely case, that
      // the value didn't change since;
      // i.e. the sensor would force sending an update every UPDATE_INTERVAL*FORCE_UPDATE_N_READS [ms]
      static const uint8_t FORCE_UPDATE_N_READS = 30; // After an hour
      
      float lastTemp;
      float lastHum;
      uint8_t nNoUpdatesTemp;
      uint8_t nNoUpdatesHum;
      int cycleCount = BAT_UPDATE_INTERVAL; // Send battery update immediately
      volatile byte B1Int = 0, B2Int = 0, B3Int = 0, B4Int = 0; // Interrupt button flags
      uint32_t now;
      
      MyMessage msgHum(0, V_HUM);
      MyMessage msgTemp(0, V_TEMP);
      MyMessage msgButtons(0, V_SCENE_ON);
      MyMessage msgBattV(0, V_VOLTAGE);
      DHT dht;
      
      void presentation()  
      { 
        // Send the sketch version information to the gateway
        sendSketchInfo(SN,SV);
        // Present the sensor
        present(0, S_CUSTOM, "Temp/Humid/Buttons");
      }
      
      void setup()
      {
        // Setup pins the DHT sensor is on
        digitalWrite(6, LOW);
        pinMode(6, OUTPUT);
        digitalWrite(7, LOW);
        pinMode(7, OUTPUT);
        digitalWrite(9, HIGH);
        pinMode(9, OUTPUT);
      
        sleep(2000);
      
        dht.setup(DHT_DATA_PIN); // Setup the DHT sensor
      
        pinMode(BUTTON1_PIN,INPUT_PULLUP);
        pinMode(BUTTON2_PIN,INPUT_PULLUP);
        pinMode(BUTTON3_PIN,INPUT_PULLUP);
        pinMode(BUTTON4_PIN,INPUT_PULLUP);
        enableInterrupt(BUTTON1_PIN,Button1,FALLING);
        enableInterrupt(BUTTON2_PIN,Button2,FALLING);
        enableInterrupt(BUTTON3_PIN,Button3,FALLING);
        enableInterrupt(BUTTON4_PIN,Button4,FALLING);
      }
      
      void Button1() {
        B1Int++;
        _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
      }
      
      void Button2() {
        B2Int++;
        _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
      }
      
      void Button3() {
        B3Int++;
        _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
      }
      
      void Button4() {
        B4Int++;
        _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
      }
      
      
      void loop() {
        _wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
        // Power up the DHT sensor
        digitalWrite(9, HIGH);
      
        // Process buttons
        if(B1Int > 0) {
          send(msgButtons.set(1));
          B1Int = 0;
        } else if(B2Int > 0) {
          send(msgButtons.set(2));
          B2Int = 0;
        } else if(B3Int > 0) {
          send(msgButtons.set(3));
          B3Int = 0;
        } else if(B4Int > 0) {
          send(msgButtons.set(4));
          B4Int = 0;
        }
      
        // Wait for the DHT sensor to init
        sleep(2000);
        
        // Force reading sensor, so it works also after sleep()
        dht.readSensor(true);
        
        // Get temperature from DHT library
        float temperature = dht.getTemperature();
        if (isnan(temperature)) {
          Serial.println("Failed reading temperature from DHT!");
        } else if (temperature != lastTemp || nNoUpdatesTemp == FORCE_UPDATE_N_READS) {
          // Only send temperature if it changed since the last measurement or if we didn't send an update for n times
          lastTemp = temperature;
          // Reset no updates counter
          nNoUpdatesTemp = 0;
          temperature += SENSOR_TEMP_OFFSET;
          send(msgTemp.set(temperature, 1));
      
          #ifdef MY_DEBUG
          Serial.print("T: ");
          Serial.println(temperature);
          #endif
        } else {
          // Increase no update counter if the temperature stayed the same
          nNoUpdatesTemp++;
        }
      
        // Get humidity from DHT library
        byte humidity = (byte)dht.getHumidity();
        if (humidity != lastHum || nNoUpdatesHum == FORCE_UPDATE_N_READS) {
          // Only send humidity if it changed since the last measurement or if we didn't send an update for n times
          lastHum = humidity;
          // Reset no updates counter
          nNoUpdatesHum = 0;
          send(msgHum.set(humidity, 1));
          
          #ifdef MY_DEBUG
          Serial.print("H: ");
          Serial.println(humidity);
          #endif
        } else {
          // Increase no update counter if the humidity stayed the same
          nNoUpdatesHum++;
        }
      
        if (cycleCount >= BAT_UPDATE_INTERVAL) {
          cycleCount = 0;
          int BatV = readVCC();
          #ifdef MY_DEBUG
          Serial.print("BatVR: ");
          Serial.println(BatV);
          #endif
          float BatVolts = BatV / 1000.0;
          #ifdef MY_DEBUG
          Serial.print("BatV: ");
          Serial.println(BatVolts);
          #endif
          send(msgBattV.set(BatVolts, 2));
          float BatPercent = (BatVolts - 2.8) / 0.6 * 100;
          if(BatPercent > 100) BatPercent = 100;
          #ifdef MY_DEBUG
          Serial.print("Bat%: ");
          Serial.println(BatPercent);
          #endif
          sendBatteryLevel((int)BatPercent);
        }
        cycleCount++;
      
        // Power down the DHT sensor
        digitalWrite(9, LOW);
        
        // Sleep for a while to save energy
        sleep(UPDATE_INTERVAL); 
      }
      
      int readVCC() {
        // Read 1.1V reference against AVcc
        // set the reference to Vcc and the measurement to the internal 1.1V reference
          ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
      
        wait(2); // Wait for Vref to settle
        ADCSRA |= _BV(ADSC); // Start conversion
        while (bit_is_set(ADCSRA,ADSC)); // measuring
      
        uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
        uint8_t high = ADCH; // unlocks both
      
        int result = (high<<8) | low;
      
        #ifdef MY_DEBUG
        Serial.print("R: ");
        Serial.println(result);
        #endif
      
        result = VCC_CAL / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
        return result; // Vcc in millivolts
      }
      
      dbemowskD gohanG 2 Replies Last reply
      0
      • CarywinC Carywin

        You can use pin change interrupts with MySensors sleep loops with an easy hack. Here's the code I use for 4 button battery powered nodes that also report temperature and humidity using DHT a sensor. I run them from 2 AA and so far they've been running for more than 6 months without showing signs of discharge. This could be easily modified for 6 or 8 buttons.

        // MySensors EnviroButtons
        // Temperature and Humidity Sensor with 4 Push Buttons
        // Battery Powered Node
        // Don't forget to change buttons and VCC_CAL for each node
        
        // Cary Wintle - July 2017
        
        // MySensors configuration
        // -----------------------
        #define SN "EnviroButtons"  // Software name
        #define SV "0.4"            // Version number
        //#define MY_DEBUG
        #define MY_NODE_ID 6
        #define MY_RADIO_RFM69
        #define MY_RFM69_NETWORKID 137
        #define MY_RFM69_ENABLE_ENCRYPTION
        #define MY_RFM69_NEW_DRIVER
        #define MY_RFM69_FREQUENCY RFM69_433MHZ
        #define MY_IS_RFM69HW
        #include <MySensors.h>
        
        #include <SPI.h>
        #include <DHT.h>
        
        #define EI_NOTEXTERNAL // External interrupts managed by built-in routines
        #include <EnableInterrupt.h> // Pin-change interrupts
        
        // Set this to the pin you connected the DHT's data pin to
        #define DHT_DATA_PIN 8
        
        // Buttons
        #define BUTTON1_PIN A1 // 5: A1 - 6: A1
        #define BUTTON2_PIN A2 // 5: A3 - 6: A2
        #define BUTTON3_PIN A3 // 5: A2 - 6: A3
        #define BUTTON4_PIN A0 // 5: A0 - 6: A0
        
        // Set this offset if the sensor has a permanent small offset to the real temperatures
        #define SENSOR_TEMP_OFFSET 0
        
        // Sleep time between sensor updates (in milliseconds)
        // Must be >1000ms for DHT22 and >2000ms for DHT11
        static const uint64_t UPDATE_INTERVAL = 120000;
        #define BAT_UPDATE_INTERVAL 720 // 24 hrs - Interval between battery updates (multiples of UPDATE_INTERVAL)
        // VCC Calibration Values
        // Node 5: 1128953L
        // Node 6: 1125300L
        #define VCC_CAL 1125300L
        
        // Force sending an update of the temperature after n sensor reads, so a controller showing the
        // timestamp of the last update doesn't show something like 3 hours in the unlikely case, that
        // the value didn't change since;
        // i.e. the sensor would force sending an update every UPDATE_INTERVAL*FORCE_UPDATE_N_READS [ms]
        static const uint8_t FORCE_UPDATE_N_READS = 30; // After an hour
        
        float lastTemp;
        float lastHum;
        uint8_t nNoUpdatesTemp;
        uint8_t nNoUpdatesHum;
        int cycleCount = BAT_UPDATE_INTERVAL; // Send battery update immediately
        volatile byte B1Int = 0, B2Int = 0, B3Int = 0, B4Int = 0; // Interrupt button flags
        uint32_t now;
        
        MyMessage msgHum(0, V_HUM);
        MyMessage msgTemp(0, V_TEMP);
        MyMessage msgButtons(0, V_SCENE_ON);
        MyMessage msgBattV(0, V_VOLTAGE);
        DHT dht;
        
        void presentation()  
        { 
          // Send the sketch version information to the gateway
          sendSketchInfo(SN,SV);
          // Present the sensor
          present(0, S_CUSTOM, "Temp/Humid/Buttons");
        }
        
        void setup()
        {
          // Setup pins the DHT sensor is on
          digitalWrite(6, LOW);
          pinMode(6, OUTPUT);
          digitalWrite(7, LOW);
          pinMode(7, OUTPUT);
          digitalWrite(9, HIGH);
          pinMode(9, OUTPUT);
        
          sleep(2000);
        
          dht.setup(DHT_DATA_PIN); // Setup the DHT sensor
        
          pinMode(BUTTON1_PIN,INPUT_PULLUP);
          pinMode(BUTTON2_PIN,INPUT_PULLUP);
          pinMode(BUTTON3_PIN,INPUT_PULLUP);
          pinMode(BUTTON4_PIN,INPUT_PULLUP);
          enableInterrupt(BUTTON1_PIN,Button1,FALLING);
          enableInterrupt(BUTTON2_PIN,Button2,FALLING);
          enableInterrupt(BUTTON3_PIN,Button3,FALLING);
          enableInterrupt(BUTTON4_PIN,Button4,FALLING);
        }
        
        void Button1() {
          B1Int++;
          _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
        }
        
        void Button2() {
          B2Int++;
          _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
        }
        
        void Button3() {
          B3Int++;
          _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
        }
        
        void Button4() {
          B4Int++;
          _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
        }
        
        
        void loop() {
          _wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
          // Power up the DHT sensor
          digitalWrite(9, HIGH);
        
          // Process buttons
          if(B1Int > 0) {
            send(msgButtons.set(1));
            B1Int = 0;
          } else if(B2Int > 0) {
            send(msgButtons.set(2));
            B2Int = 0;
          } else if(B3Int > 0) {
            send(msgButtons.set(3));
            B3Int = 0;
          } else if(B4Int > 0) {
            send(msgButtons.set(4));
            B4Int = 0;
          }
        
          // Wait for the DHT sensor to init
          sleep(2000);
          
          // Force reading sensor, so it works also after sleep()
          dht.readSensor(true);
          
          // Get temperature from DHT library
          float temperature = dht.getTemperature();
          if (isnan(temperature)) {
            Serial.println("Failed reading temperature from DHT!");
          } else if (temperature != lastTemp || nNoUpdatesTemp == FORCE_UPDATE_N_READS) {
            // Only send temperature if it changed since the last measurement or if we didn't send an update for n times
            lastTemp = temperature;
            // Reset no updates counter
            nNoUpdatesTemp = 0;
            temperature += SENSOR_TEMP_OFFSET;
            send(msgTemp.set(temperature, 1));
        
            #ifdef MY_DEBUG
            Serial.print("T: ");
            Serial.println(temperature);
            #endif
          } else {
            // Increase no update counter if the temperature stayed the same
            nNoUpdatesTemp++;
          }
        
          // Get humidity from DHT library
          byte humidity = (byte)dht.getHumidity();
          if (humidity != lastHum || nNoUpdatesHum == FORCE_UPDATE_N_READS) {
            // Only send humidity if it changed since the last measurement or if we didn't send an update for n times
            lastHum = humidity;
            // Reset no updates counter
            nNoUpdatesHum = 0;
            send(msgHum.set(humidity, 1));
            
            #ifdef MY_DEBUG
            Serial.print("H: ");
            Serial.println(humidity);
            #endif
          } else {
            // Increase no update counter if the humidity stayed the same
            nNoUpdatesHum++;
          }
        
          if (cycleCount >= BAT_UPDATE_INTERVAL) {
            cycleCount = 0;
            int BatV = readVCC();
            #ifdef MY_DEBUG
            Serial.print("BatVR: ");
            Serial.println(BatV);
            #endif
            float BatVolts = BatV / 1000.0;
            #ifdef MY_DEBUG
            Serial.print("BatV: ");
            Serial.println(BatVolts);
            #endif
            send(msgBattV.set(BatVolts, 2));
            float BatPercent = (BatVolts - 2.8) / 0.6 * 100;
            if(BatPercent > 100) BatPercent = 100;
            #ifdef MY_DEBUG
            Serial.print("Bat%: ");
            Serial.println(BatPercent);
            #endif
            sendBatteryLevel((int)BatPercent);
          }
          cycleCount++;
        
          // Power down the DHT sensor
          digitalWrite(9, LOW);
          
          // Sleep for a while to save energy
          sleep(UPDATE_INTERVAL); 
        }
        
        int readVCC() {
          // Read 1.1V reference against AVcc
          // set the reference to Vcc and the measurement to the internal 1.1V reference
            ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
        
          wait(2); // Wait for Vref to settle
          ADCSRA |= _BV(ADSC); // Start conversion
          while (bit_is_set(ADCSRA,ADSC)); // measuring
        
          uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
          uint8_t high = ADCH; // unlocks both
        
          int result = (high<<8) | low;
        
          #ifdef MY_DEBUG
          Serial.print("R: ");
          Serial.println(result);
          #endif
        
          result = VCC_CAL / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
          return result; // Vcc in millivolts
        }
        
        dbemowskD Offline
        dbemowskD Offline
        dbemowsk
        wrote on last edited by
        #34

        @carywin Certain arduinos such as the pro minis can ONLY do interrupts on pins 2 or 3 from my understanding.

        Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
        Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

        NeverDieN CarywinC 2 Replies Last reply
        0
        • CarywinC Carywin

          You can use pin change interrupts with MySensors sleep loops with an easy hack. Here's the code I use for 4 button battery powered nodes that also report temperature and humidity using DHT a sensor. I run them from 2 AA and so far they've been running for more than 6 months without showing signs of discharge. This could be easily modified for 6 or 8 buttons.

          // MySensors EnviroButtons
          // Temperature and Humidity Sensor with 4 Push Buttons
          // Battery Powered Node
          // Don't forget to change buttons and VCC_CAL for each node
          
          // Cary Wintle - July 2017
          
          // MySensors configuration
          // -----------------------
          #define SN "EnviroButtons"  // Software name
          #define SV "0.4"            // Version number
          //#define MY_DEBUG
          #define MY_NODE_ID 6
          #define MY_RADIO_RFM69
          #define MY_RFM69_NETWORKID 137
          #define MY_RFM69_ENABLE_ENCRYPTION
          #define MY_RFM69_NEW_DRIVER
          #define MY_RFM69_FREQUENCY RFM69_433MHZ
          #define MY_IS_RFM69HW
          #include <MySensors.h>
          
          #include <SPI.h>
          #include <DHT.h>
          
          #define EI_NOTEXTERNAL // External interrupts managed by built-in routines
          #include <EnableInterrupt.h> // Pin-change interrupts
          
          // Set this to the pin you connected the DHT's data pin to
          #define DHT_DATA_PIN 8
          
          // Buttons
          #define BUTTON1_PIN A1 // 5: A1 - 6: A1
          #define BUTTON2_PIN A2 // 5: A3 - 6: A2
          #define BUTTON3_PIN A3 // 5: A2 - 6: A3
          #define BUTTON4_PIN A0 // 5: A0 - 6: A0
          
          // Set this offset if the sensor has a permanent small offset to the real temperatures
          #define SENSOR_TEMP_OFFSET 0
          
          // Sleep time between sensor updates (in milliseconds)
          // Must be >1000ms for DHT22 and >2000ms for DHT11
          static const uint64_t UPDATE_INTERVAL = 120000;
          #define BAT_UPDATE_INTERVAL 720 // 24 hrs - Interval between battery updates (multiples of UPDATE_INTERVAL)
          // VCC Calibration Values
          // Node 5: 1128953L
          // Node 6: 1125300L
          #define VCC_CAL 1125300L
          
          // Force sending an update of the temperature after n sensor reads, so a controller showing the
          // timestamp of the last update doesn't show something like 3 hours in the unlikely case, that
          // the value didn't change since;
          // i.e. the sensor would force sending an update every UPDATE_INTERVAL*FORCE_UPDATE_N_READS [ms]
          static const uint8_t FORCE_UPDATE_N_READS = 30; // After an hour
          
          float lastTemp;
          float lastHum;
          uint8_t nNoUpdatesTemp;
          uint8_t nNoUpdatesHum;
          int cycleCount = BAT_UPDATE_INTERVAL; // Send battery update immediately
          volatile byte B1Int = 0, B2Int = 0, B3Int = 0, B4Int = 0; // Interrupt button flags
          uint32_t now;
          
          MyMessage msgHum(0, V_HUM);
          MyMessage msgTemp(0, V_TEMP);
          MyMessage msgButtons(0, V_SCENE_ON);
          MyMessage msgBattV(0, V_VOLTAGE);
          DHT dht;
          
          void presentation()  
          { 
            // Send the sketch version information to the gateway
            sendSketchInfo(SN,SV);
            // Present the sensor
            present(0, S_CUSTOM, "Temp/Humid/Buttons");
          }
          
          void setup()
          {
            // Setup pins the DHT sensor is on
            digitalWrite(6, LOW);
            pinMode(6, OUTPUT);
            digitalWrite(7, LOW);
            pinMode(7, OUTPUT);
            digitalWrite(9, HIGH);
            pinMode(9, OUTPUT);
          
            sleep(2000);
          
            dht.setup(DHT_DATA_PIN); // Setup the DHT sensor
          
            pinMode(BUTTON1_PIN,INPUT_PULLUP);
            pinMode(BUTTON2_PIN,INPUT_PULLUP);
            pinMode(BUTTON3_PIN,INPUT_PULLUP);
            pinMode(BUTTON4_PIN,INPUT_PULLUP);
            enableInterrupt(BUTTON1_PIN,Button1,FALLING);
            enableInterrupt(BUTTON2_PIN,Button2,FALLING);
            enableInterrupt(BUTTON3_PIN,Button3,FALLING);
            enableInterrupt(BUTTON4_PIN,Button4,FALLING);
          }
          
          void Button1() {
            B1Int++;
            _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
          }
          
          void Button2() {
            B2Int++;
            _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
          }
          
          void Button3() {
            B3Int++;
            _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
          }
          
          void Button4() {
            B4Int++;
            _wokeUpByInterrupt = 0xFE; // Dirty hack to get out of MySensors sleep loop
          }
          
          
          void loop() {
            _wokeUpByInterrupt = INVALID_INTERRUPT_NUM;
            // Power up the DHT sensor
            digitalWrite(9, HIGH);
          
            // Process buttons
            if(B1Int > 0) {
              send(msgButtons.set(1));
              B1Int = 0;
            } else if(B2Int > 0) {
              send(msgButtons.set(2));
              B2Int = 0;
            } else if(B3Int > 0) {
              send(msgButtons.set(3));
              B3Int = 0;
            } else if(B4Int > 0) {
              send(msgButtons.set(4));
              B4Int = 0;
            }
          
            // Wait for the DHT sensor to init
            sleep(2000);
            
            // Force reading sensor, so it works also after sleep()
            dht.readSensor(true);
            
            // Get temperature from DHT library
            float temperature = dht.getTemperature();
            if (isnan(temperature)) {
              Serial.println("Failed reading temperature from DHT!");
            } else if (temperature != lastTemp || nNoUpdatesTemp == FORCE_UPDATE_N_READS) {
              // Only send temperature if it changed since the last measurement or if we didn't send an update for n times
              lastTemp = temperature;
              // Reset no updates counter
              nNoUpdatesTemp = 0;
              temperature += SENSOR_TEMP_OFFSET;
              send(msgTemp.set(temperature, 1));
          
              #ifdef MY_DEBUG
              Serial.print("T: ");
              Serial.println(temperature);
              #endif
            } else {
              // Increase no update counter if the temperature stayed the same
              nNoUpdatesTemp++;
            }
          
            // Get humidity from DHT library
            byte humidity = (byte)dht.getHumidity();
            if (humidity != lastHum || nNoUpdatesHum == FORCE_UPDATE_N_READS) {
              // Only send humidity if it changed since the last measurement or if we didn't send an update for n times
              lastHum = humidity;
              // Reset no updates counter
              nNoUpdatesHum = 0;
              send(msgHum.set(humidity, 1));
              
              #ifdef MY_DEBUG
              Serial.print("H: ");
              Serial.println(humidity);
              #endif
            } else {
              // Increase no update counter if the humidity stayed the same
              nNoUpdatesHum++;
            }
          
            if (cycleCount >= BAT_UPDATE_INTERVAL) {
              cycleCount = 0;
              int BatV = readVCC();
              #ifdef MY_DEBUG
              Serial.print("BatVR: ");
              Serial.println(BatV);
              #endif
              float BatVolts = BatV / 1000.0;
              #ifdef MY_DEBUG
              Serial.print("BatV: ");
              Serial.println(BatVolts);
              #endif
              send(msgBattV.set(BatVolts, 2));
              float BatPercent = (BatVolts - 2.8) / 0.6 * 100;
              if(BatPercent > 100) BatPercent = 100;
              #ifdef MY_DEBUG
              Serial.print("Bat%: ");
              Serial.println(BatPercent);
              #endif
              sendBatteryLevel((int)BatPercent);
            }
            cycleCount++;
          
            // Power down the DHT sensor
            digitalWrite(9, LOW);
            
            // Sleep for a while to save energy
            sleep(UPDATE_INTERVAL); 
          }
          
          int readVCC() {
            // Read 1.1V reference against AVcc
            // set the reference to Vcc and the measurement to the internal 1.1V reference
              ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
          
            wait(2); // Wait for Vref to settle
            ADCSRA |= _BV(ADSC); // Start conversion
            while (bit_is_set(ADCSRA,ADSC)); // measuring
          
            uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
            uint8_t high = ADCH; // unlocks both
          
            int result = (high<<8) | low;
          
            #ifdef MY_DEBUG
            Serial.print("R: ");
            Serial.println(result);
            #endif
          
            result = VCC_CAL / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
            return result; // Vcc in millivolts
          }
          
          gohanG Offline
          gohanG Offline
          gohan
          Mod
          wrote on last edited by
          #35

          @carywin what is the sleep current of your node with this "interrupt hack"?

          CarywinC 1 Reply Last reply
          0
          • NeverDieN Offline
            NeverDieN Offline
            NeverDie
            Hero Member
            wrote on last edited by
            #36

            Here's the PCB I made for the matrix keypad:
            0_1517266378834_matrix_keypad.jpg
            The whole thing fits on a single sided PCB. :)

            1 Reply Last reply
            1
            • dbemowskD dbemowsk

              @carywin Certain arduinos such as the pro minis can ONLY do interrupts on pins 2 or 3 from my understanding.

              NeverDieN Offline
              NeverDieN Offline
              NeverDie
              Hero Member
              wrote on last edited by
              #37

              @dbemowsk said in 6/8 Buttons battery remote node:

              @carywin Certain arduinos such as the pro minis can ONLY do interrupts on pins 2 or 3 from my understanding.

              IIRC, you can do interrupts from other pins too, but each is tied to a separate bank of pins, so you have to do additional testing to determine which specific pin triggered the interrupt. In the case of the voltage divider keypad, that should be no problem.

              NeverDieN CarywinC 2 Replies Last reply
              0
              • NeverDieN NeverDie

                @dbemowsk said in 6/8 Buttons battery remote node:

                @carywin Certain arduinos such as the pro minis can ONLY do interrupts on pins 2 or 3 from my understanding.

                IIRC, you can do interrupts from other pins too, but each is tied to a separate bank of pins, so you have to do additional testing to determine which specific pin triggered the interrupt. In the case of the voltage divider keypad, that should be no problem.

                NeverDieN Offline
                NeverDieN Offline
                NeverDie
                Hero Member
                wrote on last edited by NeverDie
                #38

                Here's the link: https://playground.arduino.cc/Main/PinChangeInterrupt

                So, in this scenario, it would be:
                ISR (PCINT1_vect) pin change interrupt for A0 to A5

                1 Reply Last reply
                0
                • NeverDieN Offline
                  NeverDieN Offline
                  NeverDie
                  Hero Member
                  wrote on last edited by
                  #39

                  Here's a revision of the earlier code so that it uses ONLY ONE PIN (namely, A0) on the Arduino. And yes, this does work on a pro mini too, because that's what I tested it on. :)

                  //  Description:
                  //  Use just A0 to process the 12 button keypad.  
                  //  Button press is detected by interrupt set on A0.
                  //  Which button was pressed is determined by an analog read of A0.
                  
                  // Note: sleep code borrows from Nick Gammon's Schedule J
                  // Interrupt code borrows from https://playground.arduino.cc/Main/PinChangeInterrupt
                  
                  #include <avr/sleep.h>
                  
                  void pciSetup(byte pin)
                  {
                      *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
                      PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
                      PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
                  }
                  
                  ISR (PCINT1_vect) // handle pin change interrupt for A0 to A5 here
                   {
                       //no need to do anything beyond just waking up.
                   }  
                  
                  void setup() {
                    pinMode(A0,INPUT);
                    pciSetup(A0);
                    Serial.begin(115200);
                    Serial.println("Starting...");
                    Serial.flush();
                  }
                  
                  void loop() {
                    uint16_t voltage;
                  
                    set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
                    sleep_enable();
                  
                    // Do not interrupt before we go to sleep, or the
                    // ISR will detach interrupts and we won't wake.
                    noInterrupts ();
                    
                    pciSetup(A0);
                    
                    // turn off brown-out enable in software
                    // BODS must be set to one and BODSE must be set to zero within four clock cycles
                    MCUCR = bit (BODS) | bit (BODSE);
                    // The BODS bit is automatically cleared after three clock cycles
                    MCUCR = bit (BODS); 
                    
                    // We are guaranteed that the sleep_cpu call will be done
                    // as the processor executes the next instruction after
                    // interrupts are turned on.
                    interrupts ();  // one cycle
                    sleep_cpu ();   // one cycle
                  
                    delay(20);  //debounce the button
                    voltage=analogRead(A0);  //throw out this first result
                    voltage=analogRead(A0);
                    if (voltage>700) {
                      Serial.println(voltage);
                      Serial.flush();
                    }
                  }
                  
                  1 Reply Last reply
                  0
                  • gohanG Offline
                    gohanG Offline
                    gohan
                    Mod
                    wrote on last edited by
                    #40

                    How does that integrates with mysensors?

                    NeverDieN 1 Reply Last reply
                    0
                    • gohanG gohan

                      How does that integrates with mysensors?

                      NeverDieN Offline
                      NeverDieN Offline
                      NeverDie
                      Hero Member
                      wrote on last edited by
                      #41

                      @gohan That's your department. :)

                      gohanG 1 Reply Last reply
                      0
                      • NeverDieN NeverDie

                        @gohan That's your department. :)

                        gohanG Offline
                        gohanG Offline
                        gohan
                        Mod
                        wrote on last edited by
                        #42

                        @neverdie well... kind of... I haven't written mysensors' core so I don't know if it will clash with normal sleep management.

                        1 Reply Last reply
                        0
                        • NeverDieN Offline
                          NeverDieN Offline
                          NeverDie
                          Hero Member
                          wrote on last edited by
                          #43

                          Here's an improved sketch that gives you the number (rather than the voltage) of the button that was pressed:

                          //  Description:
                          //  Use just A0 to process the 12 button keypad.  
                          //  Button press is detected by interrupt set on A0.
                          //  Which button was pressed is determined by an analog read of A0.
                          
                          // Note: sleep code borrows from Nick Gammon's Schedule J
                          // Interrupt code borrows from https://playground.arduino.cc/Main/PinChangeInterrupt
                          
                          #include <avr/sleep.h>
                          
                          int key[12][3] = {  //min and max analogRead values for each key in the 3x4 keypad
                                            {0,993,1012},
                                            {1,848,865},
                                            {2,834,847},
                                            {3,800,833},
                                            {4,901,914},
                                            {5,884,900},
                                            {6,866,883},
                                            {7,948,969},
                                            {8,930,947},
                                            {9,915,929},
                                            {10,1013,1023},
                                            {11,970,992}
                                           };
                          
                          void pciSetup(byte pin)
                          {
                              *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
                              PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
                              PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
                          }
                          
                          ISR (PCINT1_vect) // handle pin change interrupt for A0 to A5 here
                           {
                               //no need to do anything beyond just waking up.
                           }  
                          
                           int identifyKey(uint16_t voltage) {
                            int i=0;
                          
                            while ((i<12) && ((voltage<key[i][1]) || (voltage>key[i][2]))) {
                              i++;
                            }
                            return i;
                           }
                          
                          void setup() {
                            pinMode(A0,INPUT);
                            pciSetup(A0);
                            Serial.begin(115200);
                            Serial.println("Starting...");
                            Serial.flush();
                          }
                          
                          void loop() {
                            uint16_t voltage;
                          
                            set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
                            sleep_enable();
                          
                            // Do not interrupt before we go to sleep, or the
                            // ISR will detach interrupts and we won't wake.
                            noInterrupts ();
                            
                            pciSetup(A0);
                            
                            // turn off brown-out enable in software
                            // BODS must be set to one and BODSE must be set to zero within four clock cycles
                            MCUCR = bit (BODS) | bit (BODSE);
                            // The BODS bit is automatically cleared after three clock cycles
                            MCUCR = bit (BODS); 
                            
                            // We are guaranteed that the sleep_cpu call will be done
                            // as the processor executes the next instruction after
                            // interrupts are turned on.
                            interrupts ();  // one cycle
                            sleep_cpu ();   // one cycle
                          
                            delay(20);  //debounce the button
                            voltage=analogRead(A0);  //throw out this first result
                            voltage=analogRead(A0);
                            if (voltage>799) {
                              Serial.println(identifyKey(voltage));
                              Serial.flush();
                            }
                          }
                          
                          1 Reply Last reply
                          0
                          • gohanG gohan

                            @carywin what is the sleep current of your node with this "interrupt hack"?

                            CarywinC Offline
                            CarywinC Offline
                            Carywin
                            wrote on last edited by
                            #44

                            @gohan Sorry I don't have a way to measure current that small, but given that it's already run for months on the original batteries, I'm going to assume it's a "normal" Atmega sleep state

                            1 Reply Last reply
                            0
                            • dbemowskD dbemowsk

                              @carywin Certain arduinos such as the pro minis can ONLY do interrupts on pins 2 or 3 from my understanding.

                              CarywinC Offline
                              CarywinC Offline
                              Carywin
                              wrote on last edited by
                              #45

                              @dbemowsk The Pro Mini does have pin change interrupts on every pin

                              dbemowskD 1 Reply Last reply
                              0
                              • NeverDieN NeverDie

                                @dbemowsk said in 6/8 Buttons battery remote node:

                                @carywin Certain arduinos such as the pro minis can ONLY do interrupts on pins 2 or 3 from my understanding.

                                IIRC, you can do interrupts from other pins too, but each is tied to a separate bank of pins, so you have to do additional testing to determine which specific pin triggered the interrupt. In the case of the voltage divider keypad, that should be no problem.

                                CarywinC Offline
                                CarywinC Offline
                                Carywin
                                wrote on last edited by
                                #46

                                @neverdie This is correct, but this testing is all handled by the EnableInterrupt library

                                1 Reply Last reply
                                0
                                • CarywinC Carywin

                                  @dbemowsk The Pro Mini does have pin change interrupts on every pin

                                  dbemowskD Offline
                                  dbemowskD Offline
                                  dbemowsk
                                  wrote on last edited by
                                  #47

                                  @carywin I stand corrected.

                                  Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
                                  Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

                                  1 Reply Last reply
                                  0
                                  • wesW Offline
                                    wesW Offline
                                    wes
                                    wrote on last edited by wes
                                    #48

                                    @wes said in 6/8 Buttons battery remote node:

                                    I'm planning to use some cheap RF remotes and connect a RF receiver to my RPI, which hosts both my gateway and controller.

                                    I finally received the RF remotes and receiver/decoder from eBay and got them set up to talk directly to my controller - they work pretty well: https://youtu.be/9458-3IiG3Y

                                    I did try a OneButton-type approach on a MySensors node, but had lots of malfunctions with long-presses (e.g. fade up 10% per second whilst the button is depressed), where the the "button pressed" message gets through, but the "button released" message is delayed or dropped.

                                    Blog: https://www.wes.id.au/
                                    Nodes: Arduino Pro Mini ATMega328P 3.3V 8MHz, RFM69 433MHz, Canton Power CE024 0.8-3.3V regulator & single AA battery
                                    Gateway & Controller: Raspberry Pi 3 + Home Assistant

                                    NeverDieN 1 Reply Last reply
                                    0
                                    • wesW wes

                                      @wes said in 6/8 Buttons battery remote node:

                                      I'm planning to use some cheap RF remotes and connect a RF receiver to my RPI, which hosts both my gateway and controller.

                                      I finally received the RF remotes and receiver/decoder from eBay and got them set up to talk directly to my controller - they work pretty well: https://youtu.be/9458-3IiG3Y

                                      I did try a OneButton-type approach on a MySensors node, but had lots of malfunctions with long-presses (e.g. fade up 10% per second whilst the button is depressed), where the the "button pressed" message gets through, but the "button released" message is delayed or dropped.

                                      NeverDieN Offline
                                      NeverDieN Offline
                                      NeverDie
                                      Hero Member
                                      wrote on last edited by
                                      #49

                                      @wes Maybe your resistors don't have a high enough power rating for the current that's running through them? i.e. holding down the button heats them up and therefore changes their resistance? That might explain the slowness to react as expected after the button is released. After release, they cool off and eventually return within their expected resistance tolerance, at which point the expected behavior finally happens.

                                      1 Reply Last reply
                                      0
                                      • gohanG Offline
                                        gohanG Offline
                                        gohan
                                        Mod
                                        wrote on last edited by
                                        #50

                                        @NeverDie do you think a NRF5x would be better solution to make a small compact remote control?

                                        NeverDieN 1 Reply Last reply
                                        0
                                        • gohanG gohan

                                          @NeverDie do you think a NRF5x would be better solution to make a small compact remote control?

                                          NeverDieN Offline
                                          NeverDieN Offline
                                          NeverDie
                                          Hero Member
                                          wrote on last edited by NeverDie
                                          #51

                                          @gohan It's hard to answer a question like that in a vacuum, but in general, I do like nRF5x better than nRF24L01 beause of nRF5x's better link budget. I also like LoRa because of its great range and coverage. I have made remotes using each (and I have made posts about them), and they each have their place.

                                          1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          18

                                          Online

                                          11.7k

                                          Users

                                          11.2k

                                          Topics

                                          113.1k

                                          Posts


                                          Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • MySensors
                                          • OpenHardware.io
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular