6/8 Buttons battery remote node
-
@neverdie said in 6/8 Buttons battery remote node:
But so does a one-time calibration. Since writing the above, I've warmed up to the idea. At least for my purposes, it's not that big a deal.
What about resistance variation due to temperature changes ? :)
@nca78 said in 6/8 Buttons battery remote node:
@neverdie said in 6/8 Buttons battery remote node:
But so does a one-time calibration. Since writing the above, I've warmed up to the idea. At least for my purposes, it's not that big a deal.
What about resistance variation due to temperature changes ? :)
I hadn't really thought about that. Should I? In my case (12 buttons on a remote control) I'm assuming the resistors stay more or less room temperature.
-
@nca78 said in 6/8 Buttons battery remote node:
@neverdie said in 6/8 Buttons battery remote node:
But so does a one-time calibration. Since writing the above, I've warmed up to the idea. At least for my purposes, it's not that big a deal.
What about resistance variation due to temperature changes ? :)
I hadn't really thought about that. Should I? In my case (12 buttons on a remote control) I'm assuming the resistors stay more or less room temperature.
@neverdie said in 6/8 Buttons battery remote node:
I hadn't really thought about that. Should I? In my case (12 buttons on a remote control) I'm assuming the resistors stay more or less room temperature.
It would depend on the quality of your resistors, and if your room temperature varies. Cheap carbon film resistors can have relatively wide temperature coefficient variation range, I see over 1000ppm/°C between min and max of range for some "branded" resistors on Arrow.com so it could be worse with cheap aliexpress versions. So if you're unlucky you could get resistors in the two opposite parts of the range. With 27 units on average on a 1024 max value you have an average 27000ppm margin, so you should be ok if your keyboard stays inside, but it could become a problem if it's outside, a 30°C variation between summer and winter could in theory give your wrong results.
But I suppose I'm really nitpicking here, you would need to be really unlucky with your resistors :D
-
Since values are a bit far apart, I could make a range of values to be considered a specific key pressed.
-
-
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 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 :-)
@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.
-
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 } -
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 } -
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 } -
Here's the PCB I made for the matrix keypad:

The whole thing fits on a single sided PCB. :) -
@carywin Certain arduinos such as the pro minis can ONLY do interrupts on pins 2 or 3 from my understanding.
@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.
-
@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.
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 -
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(); } } -
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(); } } -
@carywin Certain arduinos such as the pro minis can ONLY do interrupts on pins 2 or 3 from my understanding.