• Fighting for power in sleep mode

    andrey.malyshenko08/13/2020 at 21:20 0 comments

    So idea of using this device in wild is to have it sleeping most of the time, then it should wake once in a while, do measuremrnt, send it out and go back to sleep. Therefore battery life will depend heavily on the power consumption in sleep mode, since wakup cycle can be as rare as once per few hours.

    So for the first test i want to ensure that when PW_ON pin is low, all my peripherals are off and not drawing current. And here it is, not even a microamp when PW_ON is zero. That's a good start.

    Next test I don't care for any of the periferials, they will be off in the sleep mode, now i need to make sure MCU draw as less current as possible. 

    Okay so here is the first attempt to use sleep mode

    My fuses are

    board_fuses.efuse = 0xFF
    board_fuses.hfuse = 0xDE
    board_fuses.lfuse = 0xF1

    BOD set on 1.8V

    main.h

    #include <avr/io.h>
    
    #define SLEEP_16ms 0
    #define SLEEP_32ms 1
    #define SLEEP_64ms 2
    #define SLEEP_128ms 3
    #define SLEEP_256ms 4
    #define SLEEP_512ms 5
    #define SLEEP_1024ms 6
    #define SLEEP_2048ms 7
    #define SLEEP_4096ms 8
    #define SLEEP_8192ms 9
    
    #define PIN_PWR_SW PINB3
    #define PIN_PWR_SW_ _BV(PINB3)
    
    void setup_watchdog(int mode);
    void sleep(int mode);

    main.c

    #include <avr/sleep.h>
    #include <avr/wdt.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    #include "main.h"
    #include <Arduino.h>
    
    void setup() {
      DDRB = DDRB | PIN_PWR_SW_;
      PORTB = 0x00;
    }
    
    void loop() {
      PORTB |= PIN_PWR_SW_;
      _delay_ms(1024);
      PORTB &= ~PIN_PWR_SW_;
      sleep(SLEEP_2048ms);
    }
    
    // =============== SLEEP MODE ===============
    
    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif
    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #endif
    
    void sleep(int mode) {
      setup_watchdog(mode);
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
      sleep_enable();
      sleep_mode();                        // System actually sleeps here
      sleep_disable();                     // System continues execution here when watchdog timed out
    }
    
    void setup_watchdog(int ii) {
      uint8_t bb = ii & 7;
      if (ii > 7) 
        bb |= (1 << 5);
      bb |= (1 << WDCE);
      int ww = bb;
    
      MCUSR &= ~(1 << WDRF);
      // start timed sequence
      WDTCR |= (1 << WDCE) | (1 << WDE);
      // set new watchdog timeout value
      WDTCR = bb;
      WDTCR |= _BV(WDIE);
    }

    This gives me two figures, (A) active mcu, soil sensor, nrf and (B) all off, mcu sleeping. Once again case (A) is non optimised, for now i case only for case (B).

    (A) Active: 8mA, 

    (B) Sleep: 0,23mA

    Good start.

    Now by handbook i'll disable ADC during sleep mode:

    void sleep(int mode) {
      setup_watchdog(mode);
      cbi(ADCSRA, ADEN);                   // switch Analog to Digitalconverter OFF
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
      sleep_enable();
      sleep_mode();                        // System actually sleeps here
      sleep_disable();                     // System continues execution here when watchdog timed out
      sbi(ADCSRA, ADEN);                   // switch Analog to Digitalconverter ON
    }

    (A) Active: 8mA, 

    (B) Sleep: 24uA

    That's 10 times improvement compared to pervious setup.

    Now there is a hint to disable BOD during sleep

    I'll try to do it softwarewise

    void sleep(int mode) {
      setup_watchdog(mode);
      cbi(ADCSRA, ADEN);                   // switch Analog to Digitalconverter OFF
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
      sleep_enable();
      cli();                               // Disable Interrupts
      sleep_bod_disable();                 // Disable BOD
      sei();
      sleep_mode();                        // System actually sleeps here
      sleep_disable();                     // System continues execution here when watchdog timed out
      sbi(ADCSRA, ADEN);                   // switch Analog to Digitalconverter ON
    }

     Unfortunately makes no difference on current consumption, does Attiny85 supports it anyway?

    So let's disable it completely

    New fuses

    board_fuses.efuse = 0xFF
    board_fuses.hfuse = 0xDF
    board_fuses.lfuse = 0xF1

    And now i'm down to

    (A) Active: 8mA, 

    (B) Sleep: 4,5uA

    So another 5 times imprevement.

    I tried also to set all pins to input mode, but this made no difference as well.

    Now that's really the best I've managed so far. Datasheet says it should be under 1uA, and possibly as low as 0.2uA in my conditions, but i'm failing to get the same result. So if you know what I'm missing here, please help me out. Could it be the crappy multimeter...

    Read more »

  • PCBs arrived

    andrey.malyshenko08/12/2020 at 22:12 0 comments

    Rev. A PCBs just arrived. As you would expect I've messed up pinout for nrf module. But still plan to do series of tests, and hopefully finalize fimware based on this design. 

    Next task would be a fight for power consumption, therefore i took some time to prepare tooling for that. 

    I've assmbled INA219 based power meter additionaly to standard multimeter tool. Later has better resolution for sleep mode currents (talking few uA here), first one i plan to use to capture consumption on longer runs. 

    I replaced 0.1 Ohm resistor with 1 Ohm to have 10 times better resolution, so instead of ±800uA it should be ±80uA. It is not quite enough for sleep mode capturing, but should give good enough overview on total power consumption on longer run.

    Here is the firmware for it

    #define SERIAL_SPEED 115200
    
    #define DISPLAY_CONTRAST 120
    #define DISPLAY_PIN_RST    3
    #define DISPLAY_PIN_CE     4
    #define DISPLAY_PIN_DC     5
    
    #include <Arduino.h>
    #include <Wire.h>
    #include <SPI.h>
    #include "main.h"
    
    #include <Adafruit_GFX.h>
    #include <Adafruit_PCD8544.h>
    Adafruit_PCD8544 display = Adafruit_PCD8544(DISPLAY_PIN_DC, DISPLAY_PIN_CE, DISPLAY_PIN_RST);
    
    #include <Adafruit_INA219.h>
    Adafruit_INA219 ina219;
    
    void setup()
    {
      Serial.begin(SERIAL_SPEED);
      while (!Serial)
        _delay_ms(1);
    
      display.begin();
      display.setContrast(DISPLAY_CONTRAST);
      display.setTextSize(0);
      display.setTextColor(BLACK);
    
      if (!ina219.begin())
      {
        Serial.println("Failed to find INA219 chip");
        while (1)
          _delay_ms(10);
      }
      ina219.setCalibration_16V_400mA();
    }
    
    uint32_t last_measurement = 0;
    
    void loop()
    {
      uint32_t started = millis();
      update_power_display();
      Serial.println();
      // _delay_ms(100);
    }
    
    float total_mA_ms  = 0.0;
    
    void update_power_display() {
      // Read voltage and current from INA219.
      float shuntvoltage = ina219.getShuntVoltage_mV();
      float busvoltage = ina219.getBusVoltage_V();
      float current_mA = ina219.getCurrent_mA() / 10.;
      if (current_mA < 0)
        current_mA = 0;
    
      // Compute load voltage, power, and milliamp-hours.
      float loadvoltage = busvoltage + (shuntvoltage / 1000);
      float power_mW = loadvoltage * current_mA;
    
      display.clearDisplay();
    
      display.setCursor(0, 0);
      display.setTextColor(BLACK);
      display.print(F("VCC, V= ")); 
      display.setTextColor(WHITE, BLACK);
      display.println(loadvoltage); 
      Serial.print(loadvoltage);
      Serial.print('\t');
    
      display.setTextColor(BLACK);
      display.print(F("CUR,mA= ")); 
      display.setTextColor(WHITE, BLACK);
      display.println(current_mA); 
      Serial.print(current_mA);
      Serial.print('\t');
    
      display.setTextColor(BLACK);
      display.print(F("PWR,mW= ")); 
      display.setTextColor(WHITE, BLACK);
      display.println(power_mW); 
      Serial.print(power_mW);
      Serial.print('\t');
    
      uint32_t passed_ms = millis() - last_measurement;
      Serial.print(F("+"));
      Serial.print(passed_ms);
      Serial.print('\t');
      last_measurement = millis();
    
      uint32_t total_sec = last_measurement / 1000.0;
      uint32_t sec = total_sec % 60;
      uint32_t min = (total_sec - sec) / 60;
      display.setTextColor(BLACK);
      display.print(F("TIME  = ")); 
      display.setTextColor(WHITE, BLACK);
      display.print(min); 
      display.setTextColor(BLACK);
      display.print(F(":")); 
      display.setTextColor(WHITE, BLACK);
      if (sec < 10) display.print(F("0")); 
      display.println(sec); 
      Serial.print(total_sec);
      Serial.print('\t');
    
      total_mA_ms += current_mA * passed_ms;
      float total_mAs = total_mA_ms / 1000.; // 3600.0;
    
      display.setTextColor(BLACK);
      display.print(F("CP,mAs= ")); 
      display.setTextColor(WHITE, BLACK);
      display.println(total_mAs); 
      Serial.print(total_mAs);
      Serial.print('\t');
    
      // Serial.println();
      display.display(); 
    }

    Started with library example I tried to reduce time between measurements to possible minimum, since total power use is calculated based on assumption that current doesn't change between measurements. Currently i have ~33 ms between probes, will see later on if this would be enough. Basically it should be at least one order of magnitude lower than wakup time of my MCU....

    Read more »

  • 3-pin communication is working

    andrey.malyshenko07/12/2020 at 21:09 0 comments

    With previos step done i'm ready to assebmle final 3-pin setup

    Using schematics taken from here, and recommendation from here i replaced resistor with 20K, and cap with 10nF. 

    No need to change library, it is already adjusted to work with 3-pin config, only need to initialize it with same pin numbers for CE and CSN, like this

    #define NRF_CE    3
    #define NRF_CSN   3
    RF24 radio(NRF_CE, NRF_CSN);

    Actual pin 3  will not be used by library and it is still available for Serial.

    Pin 4 is connected to Analog soil moisture sensor and i'm ready to send out actual data.

    Image is not much different from previous one, but in fact numbers are actual sensor readings.

    Next step is to play around with energy saving and sleep modes. I still have couple more weeks before PCBs should arrive.

  • 4-pin connection is working fine

    andrey.malyshenko07/12/2020 at 20:19 0 comments

    As mentioned before i started with simplest possible 5-pin connection between attiny85 and nrf24L01 and moving forward.

    Today I have 4-pin configuration working, with nrf's CE pin connected to Vcc. What is more important, now when i have pin 3 of attiny85 for my disposal, i started Serial output there. So now transmission and Serial working at the same time and i'm able to debug anything!

    I'm using SoftwareSerial library and often you'll see it is not working with other libs since tiny85 only have 2 timers and limited interrupts, but this time it works perfectly and i'm surprized to get away with it this easy.

    On the background is my "reciever" that i've assembled yesterday. It helps me quickly check if transmission is working (oled is black otherwise). And treminal is connected to tiny85 now.

  • Test rig assembly in progress

    andrey.malyshenko07/11/2020 at 20:56 0 comments

    I'm adding schematics with a bit of explanation

    Now to details.

    Attiny85 5 pin configuration

    Normally to connect NRF24L01 module you need 5 pins, 3 SPI + 2 control pins (CE,CSN). In that case module will work in it's perfect conditions, power consumption would be on minimum (since chip will be idling most of the time) and i have no more pins to use. 

    Attiny85 4 pin configuration

    However plan is to connect external sensor, therefore i need to free up some space. Step one - CE pin will be tied to Vcc, and Vcc will be switched on and off  when i need it via mosfet. This way i win no extra pins, but i have ability to switch on and of power to the soil moisture sensor and radio module at the same time using single pin. 

    Attiny85 3 pin configuration

    Using this article and this library it is possible to win one more pin using multiplexing (in other words using single pin to simulate two, CSN and SPI_CLK in our case. However this setup is not that trivial to start and before going this way i'll need to start with 5 pin config and progress slowly to 3 pin.

    Atmel328P receiver node

    To test any configuration i need to have second node in constant monitoring mode. Here i'm not limited to attiny85 and I'd prefer to have something with Serial and even a oled screen. So here it is:

    I slightly modified starter ping-pong example from RF24 library to add OLED printing and now i'm able to listen Attiny85 in 5-pin configuration from another breadboard.

    To be continued...