💬 MyMultisensors
-
Does anybody know of a neat way to get this board to operate below 2.7V?
From some reading it seems like the Atmega328 chip doesn't like running a crystal oscillator below 2.7V, but should be happy running from internal clock source. It seems like this might cause problems with libraries or serial comms though. Does anyone have experience with this or can point me towards a bootloader that might work?
So far I've had a couple of these sensors fail after 2 months or so with battery volts around 2.65V. I was hoping for at least 12 months from a CR2450, but some of these sensors are sending >150 messages a day so I'd understand if it was shorter than that. Still, 2 months is not enough I think, and I hope I can tweak something to get it to work down to at least 2.0V, which might squeeze some more time out of it.
-
Does anybody know of a neat way to get this board to operate below 2.7V?
From some reading it seems like the Atmega328 chip doesn't like running a crystal oscillator below 2.7V, but should be happy running from internal clock source. It seems like this might cause problems with libraries or serial comms though. Does anyone have experience with this or can point me towards a bootloader that might work?
So far I've had a couple of these sensors fail after 2 months or so with battery volts around 2.65V. I was hoping for at least 12 months from a CR2450, but some of these sensors are sending >150 messages a day so I'd understand if it was shorter than that. Still, 2 months is not enough I think, and I hope I can tweak something to get it to work down to at least 2.0V, which might squeeze some more time out of it.
@Carywin hello.
By default the BOD is set at 2.7V so the atmega will enter a boot loop when reaching this voltage.
You need to update the fuses to set BOD at 1.8V or remove it (but it's better to keep one, o
In some rare case it could write data in the wrong place and mess with the bootloader code).
It's also necessary to update fuses if you want to use internal oscillator, it is less precise but you just need to use a lower baud rate when transferring sketches or debugging and you will be fine.
I also suggest you run at 1MHz as long as you have no heavy computing to do. From it humble experience with CR batteries I always have better battery life at 1MHz compared to 8MHz.Look for tutorials about updating bootloader, there's a topic somewhere here called "various optiboot bootloader's" with précompilés bootloaders at different frequencies.
You just need to put the file in the right directory after updating you boards.txt file and you can write bootloader from arduino interface. Fuses will be updated at the same time.To write bootloader don't worry about a programmer, just go the easy way with Arduino ISP sketch (in examples menu) on a nano, connect as explained in the many tutorials on the web (but use 3.3V for VCC to protect your radio), select "Arduino as ISP" as programmer and you're good to go :)
-
I usually use the MySensors Sensebender bootloader. It's 1.8V BOD and internal 8Mhz.
else like nca78 said there is this topic:
https://www.openhardware.io/view/33/Various-bootloader-files-based-on-Optiboot-62 -
Okay I played around with this tonight and had some struggles. I couldn't get serial uploading working on any of the Gert Sanders bootloaders, so I had to use Arduino as ISP and Upload via Programmer.
However since I have encryption enabled, I need the AES key in EEPROM before my nodes will work.
So I had to enable the fuse that prevents EEPROM being erased when programming.I experimented with using the 1 MHz oscillator option, but the sketch didn't run properly. It was sending 5-8 copies of every message at a very slow rate.
So now I'm trying the 8 MHz internal oscillator with BOD at 1.8 V to see if that works at a lower voltage than the crystal.
If anyone knows if MySensors works at 1 MHz, or what might cause it to send multiple copies of the same message, speak up please!
-
Okay I played around with this tonight and had some struggles. I couldn't get serial uploading working on any of the Gert Sanders bootloaders, so I had to use Arduino as ISP and Upload via Programmer.
However since I have encryption enabled, I need the AES key in EEPROM before my nodes will work.
So I had to enable the fuse that prevents EEPROM being erased when programming.I experimented with using the 1 MHz oscillator option, but the sketch didn't run properly. It was sending 5-8 copies of every message at a very slow rate.
So now I'm trying the 8 MHz internal oscillator with BOD at 1.8 V to see if that works at a lower voltage than the crystal.
If anyone knows if MySensors works at 1 MHz, or what might cause it to send multiple copies of the same message, speak up please!
-
@scalz Can you please enlighten me re PIR? There are two inputs D6 and D7. I cannot understand why and how to manage them in a sketch? Previously, I only used one digital input.
@alexsh1
You need to use pinchange interrupts. I won't reinvent a howto, there are multiple on google, like the one from Gammon here
https://gammon.com.au/forum/?id=11488&reply=6#reply6pinchange can only detect toggle. it's up to you to detect the pin state in the interrupt routine.
Mysensors sleep() doesn't handle pinchange. so in this case, just use sleep(ms). and test for the irq flag when it wakes upIn a previous post, I extracted and showed you the few functions needed for this. I thought it was enough documented!
ISR (PCINT1_vect) { if (digitalRead(AMBIANT_LIGHT_PIN)) irqLight = false; else irqLight = true; } ISR (PCINT2_vect) { if((PIND & (1 << PIND6)) == 0x40 ) { myPirSensor.pirhCount++; myPirSensor.irqPir = true; } if((PIND & (1 << PIND7)) == 0x80 ) { myPirSensor.pirlCount++; myPirSensor.irqPir = true; } } /* ====================================================================== Function: pirIntEnable Purpose : Enable pin change for PIR interrupt Input : - Output : - Comments: ====================================================================== */ void pirIntEnable() { // Enable pin change for D6, D7 PCMSK2 |= bit (PCINT22); PCMSK2 |= bit (PCINT23); PCIFR |= bit (PCIF2); // clear any outstanding interrupts PCICR |= bit (PCIE2); // enable pin change interrupts for D0 to D7 } /* ====================================================================== Function: pirIntDisable Purpose : Disable pin change for PIR interrupt Input : - Output : - Comments: ====================================================================== */ void pirIntDisable() { // Disable pin change for D6, D7 PCICR ^= bit (PCIE2); // disable pin change interrupts for D0 to D7 } /* ====================================================================== Function: lightIntEnable Purpose : Enable pin change for OPT3001 interrupt Input : - Output : - Comments: ====================================================================== */ void lightIntDisable() { PCMSK1 |= bit (PCINT9); PCIFR |= bit (PCIF1); // clear any outstanding interrupts PCICR |= bit (PCIE1); // enable pin change interrupts for A0 to A5 } /* ====================================================================== Function: Light_IntDisable Purpose : Disable pin change for OPT3001 interrupt Input : - Output : - Comments: ====================================================================== */ void lightIntDisable() { PCICR ^= bit (PCIE1); // disable pin change interrupts }So if we add this, in the MySensors Motion example, as a very basic example, this should look like this:
(untested, no time, but it should be close or maybe working)// Enable debug prints // #define MY_DEBUG // Enable and select radio type attached #define MY_RADIO_NRF24 //#define MY_RADIO_NRF5_ESB //#define MY_RADIO_RFM69 //#define MY_RADIO_RFM95 #include <MySensors.h> uint32_t SLEEP_TIME = 120000; // Sleep time between reports (in milliseconds) #define PIR_SETTLE_TIME 300000 #define PIR_INT_PINH 6 // The digital input you attached your motion sensor. #define PIR_INT_PINL 7 // The digital input you attached your motion sensor. #define CHILD_ID 1 // Id of the sensor child // Initialize motion message MyMessage msg(CHILD_ID, V_TRIPPED); volatile bool irqPirHigh = false; /* ====================================================================== Pin change Interrupt Service Routine for D0 to D7 ====================================================================== */ ISR (PCINT2_vect) { // Pin change interrupt! // if one of the PIR pins is HIGH, we have a pulse if((PIND & (1 << PIND6)) == 0x40 || (PIND & (1 << PIND7)) == 0x80 ) { irqPirHigh = true; } } /* ====================================================================== Function: pirIntEnable Purpose : Enable pin change for PIR interrupt Comments: ====================================================================== */ void pirIntEnable() { // Enable pin change for D6, D7 PCMSK2 |= bit (PCINT22); PCMSK2 |= bit (PCINT23); PCIFR |= bit (PCIF2); // clear any outstanding interrupts PCICR |= bit (PCIE2); // enable pin change interrupts for D0 to D7 } /* ====================================================================== Function: pirIntDisable Purpose : Disable pin change for PIR interrupt Comments: ====================================================================== */ void pirIntDisable() { // Disable pin change for D6, D7 PCICR ^= bit (PCIE2); // disable pin change interrupts for D0 to D7 } /* ====================================================================== Function: before Purpose : set pin states Comments: before setup Mysensors init ====================================================================== */ void before() { hwDigitalWrite(PIR_INT_PINH, LOW); hwPinMode(PIR_INT_PINH, INPUT); // sets the motion sensor digital pinH as input hwDigitalWrite(PIR_INT_PINH, LOW); hwPinMode(PIR_INT_PINL, INPUT); // sets the motion sensor digital pinL as input } void setup() { // do setup stuff like waiting for pir to settle, send states at init etc. sleep(PIR_SETTLE_TIME); // enable pin change interrupt to enable PIR/motion detection pirIntEnable(); } void presentation() { // Send the sketch version information to the gateway and Controller sendSketchInfo("Motion Sensor", "1.0"); // Register all sensors to gw (they will be created as child devices) present(CHILD_ID, S_MOTION); } void loop() { // irq ? if (irqPirHigh) { // We got a HIGH pulse on PIR pins!! // increment a pulse counter, etc. // test pulse counter then send or not send(msg.set("1")); irqPirHigh = false; } else { // increment timer counters, to reset motion state etc // do stuff } // Sleep until timer or a pin change interrupt sleep(SLEEP_TIME); Serial.println("Wake up!"); }Remember it detect pin change, so it will wake up at each pin change state.
I can't make this example more noob and simple. then add all your variables for states, timers, improve power consumption etc, as you wish.Or, it's perhaps easier to use the example from carywinn above. he posted his sketch which use a lib to handle pinchange. (on my side I don't need a lib for this, and it also saves memory).
I hope it's clear about pinchange, so I'm done at explaining it ;)
-
@alexsh1
You need to use pinchange interrupts. I won't reinvent a howto, there are multiple on google, like the one from Gammon here
https://gammon.com.au/forum/?id=11488&reply=6#reply6pinchange can only detect toggle. it's up to you to detect the pin state in the interrupt routine.
Mysensors sleep() doesn't handle pinchange. so in this case, just use sleep(ms). and test for the irq flag when it wakes upIn a previous post, I extracted and showed you the few functions needed for this. I thought it was enough documented!
ISR (PCINT1_vect) { if (digitalRead(AMBIANT_LIGHT_PIN)) irqLight = false; else irqLight = true; } ISR (PCINT2_vect) { if((PIND & (1 << PIND6)) == 0x40 ) { myPirSensor.pirhCount++; myPirSensor.irqPir = true; } if((PIND & (1 << PIND7)) == 0x80 ) { myPirSensor.pirlCount++; myPirSensor.irqPir = true; } } /* ====================================================================== Function: pirIntEnable Purpose : Enable pin change for PIR interrupt Input : - Output : - Comments: ====================================================================== */ void pirIntEnable() { // Enable pin change for D6, D7 PCMSK2 |= bit (PCINT22); PCMSK2 |= bit (PCINT23); PCIFR |= bit (PCIF2); // clear any outstanding interrupts PCICR |= bit (PCIE2); // enable pin change interrupts for D0 to D7 } /* ====================================================================== Function: pirIntDisable Purpose : Disable pin change for PIR interrupt Input : - Output : - Comments: ====================================================================== */ void pirIntDisable() { // Disable pin change for D6, D7 PCICR ^= bit (PCIE2); // disable pin change interrupts for D0 to D7 } /* ====================================================================== Function: lightIntEnable Purpose : Enable pin change for OPT3001 interrupt Input : - Output : - Comments: ====================================================================== */ void lightIntDisable() { PCMSK1 |= bit (PCINT9); PCIFR |= bit (PCIF1); // clear any outstanding interrupts PCICR |= bit (PCIE1); // enable pin change interrupts for A0 to A5 } /* ====================================================================== Function: Light_IntDisable Purpose : Disable pin change for OPT3001 interrupt Input : - Output : - Comments: ====================================================================== */ void lightIntDisable() { PCICR ^= bit (PCIE1); // disable pin change interrupts }So if we add this, in the MySensors Motion example, as a very basic example, this should look like this:
(untested, no time, but it should be close or maybe working)// Enable debug prints // #define MY_DEBUG // Enable and select radio type attached #define MY_RADIO_NRF24 //#define MY_RADIO_NRF5_ESB //#define MY_RADIO_RFM69 //#define MY_RADIO_RFM95 #include <MySensors.h> uint32_t SLEEP_TIME = 120000; // Sleep time between reports (in milliseconds) #define PIR_SETTLE_TIME 300000 #define PIR_INT_PINH 6 // The digital input you attached your motion sensor. #define PIR_INT_PINL 7 // The digital input you attached your motion sensor. #define CHILD_ID 1 // Id of the sensor child // Initialize motion message MyMessage msg(CHILD_ID, V_TRIPPED); volatile bool irqPirHigh = false; /* ====================================================================== Pin change Interrupt Service Routine for D0 to D7 ====================================================================== */ ISR (PCINT2_vect) { // Pin change interrupt! // if one of the PIR pins is HIGH, we have a pulse if((PIND & (1 << PIND6)) == 0x40 || (PIND & (1 << PIND7)) == 0x80 ) { irqPirHigh = true; } } /* ====================================================================== Function: pirIntEnable Purpose : Enable pin change for PIR interrupt Comments: ====================================================================== */ void pirIntEnable() { // Enable pin change for D6, D7 PCMSK2 |= bit (PCINT22); PCMSK2 |= bit (PCINT23); PCIFR |= bit (PCIF2); // clear any outstanding interrupts PCICR |= bit (PCIE2); // enable pin change interrupts for D0 to D7 } /* ====================================================================== Function: pirIntDisable Purpose : Disable pin change for PIR interrupt Comments: ====================================================================== */ void pirIntDisable() { // Disable pin change for D6, D7 PCICR ^= bit (PCIE2); // disable pin change interrupts for D0 to D7 } /* ====================================================================== Function: before Purpose : set pin states Comments: before setup Mysensors init ====================================================================== */ void before() { hwDigitalWrite(PIR_INT_PINH, LOW); hwPinMode(PIR_INT_PINH, INPUT); // sets the motion sensor digital pinH as input hwDigitalWrite(PIR_INT_PINH, LOW); hwPinMode(PIR_INT_PINL, INPUT); // sets the motion sensor digital pinL as input } void setup() { // do setup stuff like waiting for pir to settle, send states at init etc. sleep(PIR_SETTLE_TIME); // enable pin change interrupt to enable PIR/motion detection pirIntEnable(); } void presentation() { // Send the sketch version information to the gateway and Controller sendSketchInfo("Motion Sensor", "1.0"); // Register all sensors to gw (they will be created as child devices) present(CHILD_ID, S_MOTION); } void loop() { // irq ? if (irqPirHigh) { // We got a HIGH pulse on PIR pins!! // increment a pulse counter, etc. // test pulse counter then send or not send(msg.set("1")); irqPirHigh = false; } else { // increment timer counters, to reset motion state etc // do stuff } // Sleep until timer or a pin change interrupt sleep(SLEEP_TIME); Serial.println("Wake up!"); }Remember it detect pin change, so it will wake up at each pin change state.
I can't make this example more noob and simple. then add all your variables for states, timers, improve power consumption etc, as you wish.Or, it's perhaps easier to use the example from carywinn above. he posted his sketch which use a lib to handle pinchange. (on my side I don't need a lib for this, and it also saves memory).
I hope it's clear about pinchange, so I'm done at explaining it ;)
@scalz said in 💬 MyMultisensors:
uint32_t SLEEP_TIME = 120000; // Sleep time between reports (in milliseconds)
You are a star - I must admit that setting it up is a bit more fiddly. This is the first time I come across pinchange interrupts and excellent link you provided. Thank you
-
I will gladly design a nice 3D printable case if someone can donate a board. I see that there are multiple battery options, so I would design a case that would fit all battery options.
@dbemowsk Thanks for volunteering!
I can probably order new boards (they come in batches of 3) from oshpark and send you one or two. They are 1.93 x 1.00 inch (49.0 x 25.4 mm).
Unfortunately, the boards I have with CR2450 battery holders and these are extremely fragile to be posted (they are soldered to little pads may come off easily). -
@scalz said in 💬 MyMultisensors:
uint32_t SLEEP_TIME = 120000; // Sleep time between reports (in milliseconds)
You are a star - I must admit that setting it up is a bit more fiddly. This is the first time I come across pinchange interrupts and excellent link you provided. Thank you
@scalz I did just notice that I have about 30-32uA sleep consumption, which is too high. This is very consistent through 3 sensors. Any ideas why I have such high consumption? Obviously, there is a chance my multimeter is not giving me correct reading, but so far my Brymen BM869S has been very much spot on!
-
@alexsh1 looks weird, because there is nothing special on the board which could consumes like that, all parts are ultra low power..I tested mine with uCurrent gold device.
so I would say if it's consistent on 3sensors boards, it could be:- software
- bad caps, out of specs parts..(I usually order my parts at Mouser, arrow etc..)
what do you get when using sketch J from gammon for example + shutdown sensors in case, no serial connected.
-
@alexsh1 looks weird, because there is nothing special on the board which could consumes like that, all parts are ultra low power..I tested mine with uCurrent gold device.
so I would say if it's consistent on 3sensors boards, it could be:- software
- bad caps, out of specs parts..(I usually order my parts at Mouser, arrow etc..)
what do you get when using sketch J from gammon for example + shutdown sensors in case, no serial connected.
-
@alexsh1
so you get 1.5mA with sketch J but 35uA with your code ?? something is wrong..you may have forgotten to shutdown some peripherals.
with sketch J, you need to shutdown everything on your board in case peripherals are not well initialized. So that implies you include libs for radio etc in sketch J. Then disconnect your ftdi for power consumption tests. the PIR sensor itself won't consume more than 2-3uA, I tested it too.
On other side, sketch J is just a basic test, because almost same code is used in MySensors lib for sleep()Sometimes I read people saying they have x uA power consumption for 328p standalone in deep sleep mode, but this is strange because here, on different 328p design, I have always been able to confirm the current consumption from the datasheet which is nA range. But I always used uCurrent..and reliable capa (X7R etc), hopefully I never got one capa extra leaking so far.
There is no magics, especially for this board, you should get what datasheets say..
When I made this board, I started by soldering only 328p circuit and checked power consumption, then tested step by step additional peripheral. -
@alexsh1
so you get 1.5mA with sketch J but 35uA with your code ?? something is wrong..you may have forgotten to shutdown some peripherals.
with sketch J, you need to shutdown everything on your board in case peripherals are not well initialized. So that implies you include libs for radio etc in sketch J. Then disconnect your ftdi for power consumption tests. the PIR sensor itself won't consume more than 2-3uA, I tested it too.
On other side, sketch J is just a basic test, because almost same code is used in MySensors lib for sleep()Sometimes I read people saying they have x uA power consumption for 328p standalone in deep sleep mode, but this is strange because here, on different 328p design, I have always been able to confirm the current consumption from the datasheet which is nA range. But I always used uCurrent..and reliable capa (X7R etc), hopefully I never got one capa extra leaking so far.
There is no magics, especially for this board, you should get what datasheets say..
When I made this board, I started by soldering only 328p circuit and checked power consumption, then tested step by step additional peripheral. -
@alexsh1
where did you source your passive parts like capa, especially the bigger like 100uF ? (for curiosity)
I guess you sourced others ic like opamp and comparator from a known source. -
oki maybe try removing some of the big capa to see if it improves, use a simpler sketch where you just powerdown everything. there is no reason you don't get the low power consumption.
regarding sketch J, yes, for sure you got 1.5ma because others peripherals were not initialized/shutdown