Bare bones 328P power_down with interrupt assistance needed

Good day everyone, I'm workin on a POWER_DOWN without a library. All tutorials I have found in regards to waking a bare bones 328P using a LDR fall short in that they all power the LDR ALL THE TIME. Which is a total waste of batteries. Here I have basically 2 interrupts. I thought I had a solution. In Fig. 1 Every 8 seconds the WDT interrupts and turns on the LED for a second then goes back to sleep. The other interrupt is the push button. (falling) BUT! it turns the LED on to as long as your pushing the button. I thought I had a brilliant idea and was going to "ENABLE_LDR". for a second every 8 seconds, from the WDT output that goes to the LED, then read value and wake up or go back to sleep. It will work. HOWEVER Once the DARK threshold is reached and it wakes up the LDR is now powered all the time while awake. Just like in Fig. 1 when you press the button (INTERRUPT). The objective is to have the WDT power the LDR (ENABLE_LDR every 8 seconds for 1000 ms regardless if it is asleep or awake. I am pretty sure the ENABLE_LDR (that LED Fig. 1 at the moment) needs to be OUT of the loop. Will really appreciate any assistance/ideas. The LED in Fig.2 is just so I know that


it's asleep.

NOTE: All extra components, crystal, caps, etc. not shown for ease of reading. There are 2 other questions to follow after this is resolved

#define LED_PIN 4
void setup() {
  pinMode(LED_PIN, OUTPUT);
  
  
  //Save Power by writing all Digital IO LOW - note that pins just need to be tied one way or another, do not damage devices!
  for (int i = 0; i < 20; i++) {
    if(i != 2)//just because the button is hooked up to digital pin 2
    pinMode(i, OUTPUT);
  }
  
  attachInterrupt(0, digitalInterrupt, FALLING); //interrupt for waking up
  
  
  //SETUP WATCHDOG TIMER
WDTCSR = (24);//change enable and WDE - also resets
WDTCSR = (33);//prescalers only - get rid of the WDE and WDCE bit
WDTCSR |= (1<<6);//enable interrupt mode

  //Disable ADC - don't forget to flip back after waking up if using ADC in your application ADCSRA |= (1 << 7);
  ADCSRA &= ~(1 << 7);
  
  //ENABLE SLEEP - this enables the sleep mode
  SMCR |= (1 << 2); //power down mode
  SMCR |= 1;//enable sleep
}

void loop() {

  digitalWrite(LED_PIN, HIGH);
  delay(1000);
  digitalWrite(LED_PIN, LOW);





  //BOD DISABLE - this must be called right before the __asm__ sleep instruction
  MCUCR |= (3 << 5); //set both BODS and BODSE at the same time
  MCUCR = (MCUCR & ~(1 << 5)) | (1 << 6); //then set the BODS bit and clear the BODSE bit at the same time
  __asm__  __volatile__("sleep");//in line assembler to go to sleep

  
}

void digitalInterrupt(){
  //needed for the digital input interrupt
}

ISR(WDT_vect){
  //DON'T FORGET THIS!  Needed for the watch dog timer.  This is called after a watch dog timer timeout - this is the interrupt function called after waking up
}// watchdog interrupt

Have you checked Gammon's guide:
www.gammon.com.au/interrupts

Might give some mroe tips about the sleep and waking on condition aspects of your situation

I thought of that, too. But the one I'll link here:

Nick Gammon on low power

No waste of time either article.

a7

Hi, I have read Nick Gammon stuff on low power. It’s good stuff. however, he uses libraries, i’m not using a library. I’m going off the data sheet on how to put it to sleep and it goes into a very deep sleep. I can also wake it up. The issue is with enable LDR. Every eight seconds, get reading. Stay asleep, or wake up. When it wakes up, run the loop then go back to sleep based on whether it’s day or night. he does the same thing I would like to do with the 328P. He uses an ATtiny 85. It’s called the torch light. But again he is using libraries. It’s not like comparing apples to apples. Not only that, it’s complicated.

Sorry, I can't figure it out what needs to be done and when. Maybe by going step by step.

When only a LED is driven in loop is it working correctly? If you don't press the button, MCU wakes up by WDT interrupt every ~8sec and LED lights up? Can you wake up the MCU if you push the button, and the LED also lights up?

If you are doing stuff on register level, at least you could make it easier for yourself (and every reader) by using bit place definitons.

For example instead of using decimal numbers:

WDTCSR = (33);//prescalers only - get rid of the WDE and WDCE bit
WDTCSR |= (1<<6);//enable interrupt mode

You could use the bit definitions:

WDTCSR = bit(WDIE) | bit(WDP3) | bit(WDP0);

(and make your code as short as possible :slight_smile: )

1 step at a time. This is for FIG. 1 and the code attached

  1. The 328P is asleep when it is powered up
    (2). Every 8 seconds the WDT interrupts and blinks the LED for 1000ms. then goes back to sleep.
  2. The push button interrupt will also wake up the 328P and the LED will light up as well. As long
    as you press the button the LED will stay on. When you stop pressing the button the LED will
    go off and the WDT resumes line (2)..
  3. Yes it works as previously described.

I am a novice, new to sleep mode, registers, flipping bits and such. Trying to expand my knowledge past blinking lights, servo sweeps, PIRs etc. The code attached is taken directly from the 328P DATA sheet. How to save power and put the 328P into POWER_DOWN_MODE. It's the way I was taught. I'm sure there are short cuts. But this is a whole new world to me so please be patient. Does Part 1 need any further explanation? Thanks, Fred

Interrupts can be tricky.

What happens if you detach button interrupt in digitalInterrupt() and only attach back before putting mcu to sleep again?

Amazed, The tutorial I studied was Fig. 1 and the attached code. It is just an example of how to put a 328P to sleep, to save power and to wake it up with the WDT and/or an external interrupt WITHOUT using a library. It works really well. It just needs some modifications. Here is a link to one of Mr. Gammons projects that inspired me to explore POWER_DOWN with an LDR because all my projects work at night. and they are all battery powered. However the ATtiny 85 is limited for the reasons of it only has a few pins to work with and that it runs at 8MHz.. Some libraries, like the servolib. require 16MHz.. What I would like to learn is how apply the LDR concept used in the Torchlite application on the ATtiny 85 to an ATMEL 328P. It would open up a whole new world of projects I have ideas for and make the completion of some existing ones possible. I have just begun this phase of study and it isn't "tricky" it's REALLY COMPLICATED. If you take a look at the Torchlite project you will have an unquestionable idea of what I would like to learn to do. You may even have some great pointers as well. Thanks, Fred

https://www.gammon.com.au/forum/?id=12769

Questions.

Are you turning on and off a voltage divider that has the LDR as part of it? That's what and all the trick that Nick Gammon is using.

A common trick for things that need so little power you can run them from an output pin, and shut them off totally by setting the pin to INPUT mode.

In your schematic, the LDR is connected to? D2 and "enable LDR" which is nowhere and not mentioned in the code as far as I can see.

Gammon wakes up, energizes the voltage divider and reads the LDR.

a7

Hey Amazed & Alto777, Forget Fig. 2. is it possible that I am approaching this from the wrong perspective? What I mean by that is, Mr. Gammon has already created what I consider a fascinating code for his torch lite project. The more I study it and the more I read his articles it occurred to me that I might be trying to reinvent the wheel so to speak. .

I had this thought. The code is written. Wouldn't it just be easier to modify (bits and ports and such) and just put it on a 328? My end goal is to have a 328 just like Mr.Gammons tiny 85, Just a lot more pins to do a lot more things with?

Just curious........

Alto777, that "Trick" he uses is super cool. PWR LDR, take reading, wake or sleep "WHILE" IT'S ASLEEP! That's AWESOME. I can put it to sleep and wake it up with the WDT or a falling interrupt, but I can't compose the LDR like Mr. Gammon does. Still got a lot of learning to do. But YES, it is REALLY COOL..For battery powered stuff to turn on at night, you couldn't ask for more.

Why not? All you need is an output pin.

Also, in case it isn't obvious, libraries are not magic, they are just code. Furthermore, the source code to any library you've installed is on your machine. Somewhere.

It's nice to curl up with the full data sheet under the umbrella or in front of a roaring fire, but there is no shame in supplementing such research by reading library code to see exactly how the data sheet informs making code that runs, well, whatever it runs.

As for reinventing the wheel, that's a matter of personal choice. How else to learn about wheels?

Low power is addictive. I try not to use libraries also, I like to write as much of the code as close to the "bare metal" as possible. Sometimes it makes sense to just use a stupid library, other times I figure it out without, and sometimes with, careful reading of someone else's solution.

Library code is of variable quality, and some writers really use language features that are kinda advanced, but stuff like running an RTC or shift register IC are simo,e enough so even the most horrific C++ code can be used to glean key facts about a part or module or peripheral.

a7

That's why I went with NO LIBRARY. I have noticed in "SOME" situations there are conflicts between libraries, which is like opening up a can of worms. Worms are only good when fishing! It's complicated stuff and I am venturing out into the unknown to really learn. So you have seen Fig. 2, and you stated "enable LDR, doesn't go anywhere. I Don't know where to put it!. My original concept was to use the WDT or one of the other timers, to power/ENABLE_LDR, take a reading, check "Threshold" Make a decision (is it DAY or NIGHT) go back to sleep, OR, run loop. While asleep. Like Mr. Gammon does. However, it is difficult to look at code using a library and compare it to the one I posted which doesn't use a library. And as mentioned, I'm on a path never traveled. I have the "LOOP" part down pat. If I can overcome this hurdle it would be awesome. Thank you for your input.

Yes, it would be.

Have you tried compiling for 328P? Mr. Gammon kind of prepared the code in case of not ATtiny85 is used, but as you mentioned, pins and other related stuff has to be figured out for 328P.

#if defined(__AVR_ATtiny85__)  
  ADMUX = (port & 0x07);  // AVcc   
#else   
  ADMUX = bit (REFS0) | (port & 0x07);  // AVcc   
#endif

Step by step. Can you use LED pin, as it is in the original code?
(Forget the comments in Gammon's code)

const byte LED = 0; 
const byte LDR_ENABLE = 3; 
const byte LDR_READ = 1; 

LED pin is controlled with Arduino functions (digitalWrite) so 0 is D0.
Pinout for 328P:

//                  +-\/-+
//            PC6  1|    |28  PC5 (AI 5)
//      (D 0) PD0  2|    |27  PC4 (AI 4)
//      (D 1) PD1  3|    |26  PC3 (AI 3)
//      (D 2) PD2  4|    |25  PC2 (AI 2)
// PWM+ (D 3) PD3  5|    |24  PC1 (AI 1)
//      (D 4) PD4  6|    |23  PC0 (AI 0)
//            VCC  7|    |22  GND
//            GND  8|    |21  AREF
//            PB6  9|    |20  AVCC
//            PB7 10|    |19  PB5 (D 13)
// PWM+ (D 5) PD5 11|    |18  PB4 (D 12)
// PWM+ (D 6) PD6 12|    |17  PB3 (D 11) PWM
//      (D 7) PD7 13|    |16  PB2 (D 10) PWM
//      (D 8) PB0 14|    |15  PB1 (D 9) PWM
//                  +----+

Happy 4th. I started to work on it but grabbed the data sheet instead. Hard to believe it takes 600 pages to explain the chip. So I'm going to take it 1 line at a time. I understand the concept, but it's going to take me awhile. Great learning experience though!

Having trouble with this, what does it mean, what does it do? THANKS!!!

#if defined(__AVR_ATmega328P__) 
  ADMUX = (port & 0x07);  // AVcc   
#else   
  ADMUX = bit (REFS0) | (port & 0x07);  // AVcc   
#endif

You mean the conditional inclusion? You can check this link: Preprocessor directives.

#if defined(__AVR_ATmega328P__) 
#else 
#endif

Basically if you compile the code for ATmega328p, the line between #if and #else will be compiled, the other line between #else and #end will be ignored. If you compile for any other mcu, it will be the other way around.

my bad, originally it was

#if defined(AVR_ATtiny85) //so I am going to REDEFINE this to a 328_
ADMUX = (port & 0x07); // AVcc
#else
ADMUX = bit (REFS0) | (port & 0x07); // AVcc
#endif


I'm changing mcu,

[quote="amazed, post:17, topic:1143633"]
Basically if you compile the code for ATmega328p, the line between `#if` and `#else` will be compiled, the other line between `#else` and `#end` will be ignored. If you compile for any other mcu, it will be the other way around.
[/quote]

NOT confusing at all LOL. I realize this is complicated stuff. It isn't written inside the loop. It appears that the directions to enable the LDR are written "inside" the mcu and.... if the ref value does not = threshold simply go back to sleep. maybe I should do it 1 line at a time? Lets start with this one.

ADMUX = (port & 0x07);  // AVcc PLEASE EXPAND UPON THIS 1 LINE


THANKS

I assume you're getting this from Gammon's getReading() function.

That one line you are asking about, only makes sense in the function's body, because port is a passed parameter to the function.

float getReading (byte port)
{
  //some code here

  ADMUX = (port & 0x07);

  //further code here
}

ADMUX is the ADC Multiplexer Selection Register (17.13.1 in the manual).
Because port variable is bitwise AND with 0x07 hex number which is 0b00000111 in binary only the lowest three bits (MUX2, MUX1 and MUX0.) can be set to logical 1, depending on the passed port variable's value.

Because REFS2, REFS1 and REFS0 can only be logical zero after the bitwise AND with 0x07, it assures that always AVcc is selected as reference.

SO, i am persistent and I will learn this. can I write this to any mcu and it will understand? IT SAYS, .......EMPTY_ADC INTERRUPT (ADC vect). Given the info is for a 328P. Does it know where the (ADC vect) is? will it understand the command? or is it even MORE complicated? I have to ask a lot of questions. this is NOT a simple as what color shirt are you wearing.......How to make this ******* work?`

`
// when ADC completed, take an interrupt
EMPTY_INTERRUPT (ADC_vect);

// Take an ADC reading in sleep mode (ADC)
float getReading (byte port)
{
power_adc_enable() ;
ADCSRA = bit (ADEN) | bit (ADIF); // enable ADC, turn off any pending interrupt