Looking for help on battery powered product - budget available

Hi all,

For the creation of a product I need some help. I have done the basics but can’t get it finished.
Hopefully somebody here is willing to help me. There is only coding to do, wiring and electronics are fixed. There is some budget available.

The concept is easy, a motion detector detects a person, sends data(as Tx) through RF to a receiver which will turn on an LED. Once the LED is on, the system needs to check every X seconds (short-term) if the situation has changed. Once no-one is detect during X-seconds(mid-term) the interval to check the room should be changed back to X-seconds (longer term)

The most difficult part is to make it consume as less power as possible. In the best situation the transmitter and receiver should work for a year on 2x AA. For the transmitter I’m on the right track, but it can be done better, sure. The receiver however is a very hungry.

Next thing is also I should be able to add a maximum of 6 transmitters to the receiver. All the transmitters send their own message.
So in the maximum situation we would have 6 transmitters and 1 receiver. If more is possible, that would be great (I’ve read there is maximum of 6 when there is one base only)
All values should be stored in variables to make it easily changeable.

For the RF communication I use the nRF24L01+ transceiver.
The code runs on a bare atMega328P, the PIR is a Panasonic EKMB sensor (2uA)

I’m thinking to add a RTC since the motion sensor does not need to check during nights, so that would save some more power. Ordered them already if necessary.

I was hoping perhaps to schedule some hours with someone and use TeamViewer to collaborate.
There is some budget available.

I’m aiming to get this done before Thursday next week.
If anyone is interested or wants to know more, please reply or send me a PM so we can get in touch through mail.

My code so far is here

Tx

#include <LowPower.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(9, 10); // CE, CSN  

const byte ledPin = 8;
//const byte sensor = 4;
const byte interrupt_pin = 2;

boolean sensor_state = 0;

const byte address[6] = "00001";     //Byte of array representing the address. This is the address where we will send the data. This should be same on the receiving side.
volatile byte state = LOW;

char dataToSend[10] = "Message 0";
char txNum = '0';

unsigned long currentMillis;
unsigned long prevMillis;
unsigned long txIntervalMillis = 500; // send once per second

unsigned long interval = 30000; // 30s


void wake(){
  // cancel sleep as a precaution
  sleep_disable();
  // precautionary while we do other stuff
  detachInterrupt (0);
}  // end of wake

void setup() {
  Serial.begin(9600);

  Serial.println("SimpleTx Starting");
 // pinMode(sensor, INPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(interrupt_pin, INPUT); 
  digitalWrite(2, HIGH);

    for (int i = 0; i < 20; i++) {
    if(i != 2)//just because the button is hooked up to digital pin 2
    pinMode(i, OUTPUT);
  }

  clock_prescale_set(clock_div_256);

  //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
  
  //DISABLE BOD
  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

  //SET CLOCK PROCESSOR
  CLKPR = 0x80;
  CLKPR = 0x01;

  radio.begin();                  //Starting the Wireless communication
  radio.openWritingPipe(address); //Setting the address where we will send the data
  radio.setPALevel(RF24_PA_MAX);  //You can set it as minimum or maximum depending on the distance between the transmitter and receiver.
  radio.stopListening();          //This sets the module as transmitter

  //Show that device is ON
  digitalWrite(ledPin, HIGH);
  delay (2000);
  digitalWrite(ledPin, LOW);
}

void loop() {

  ADCSRA = 0;

  
  // the interrupt must be attached each loop
  attachInterrupt(digitalPinToInterrupt(interrupt_pin),interrupt_routine, RISING);
  LowPower.powerDown(SLEEP_FOREVER,ADC_OFF,BOD_OFF); // sleep until interrupt
  detachInterrupt(digitalPinToInterrupt(interrupt_pin)); // remove interrupt

  sensor_state = digitalRead(interrupt_pin);
  currentMillis = millis();
  
  if (sensor_state==HIGH){
    digitalWrite(ledPin,HIGH); //debug only
    const char text[] = "Toilet 1 - HEREN - BEZET";    //debug only 
    radio.write(&text, sizeof(text));                  //Sending the message to receiver
    digitalWrite(interrupt_pin, LOW);
    delay(5000);

  }
    if (state==HIGH){
    state = LOW;
    digitalWrite(ledPin,LOW);  //debug only    
    digitalWrite(interrupt_pin, LOW); 
    const char text[] = "Toilet 1 - HEREN - VRIJ";     //debug only 
    radio.write(&text, sizeof(text));                  //Sending the message to receiver 
    }
    radio.write(&sensor_state, sizeof(sensor_state));  //Sending the message to receiver 
 
    delay(interval);
}

void interrupt_routine(){
     state = HIGH;
}
/*
void send() {

    digitalWrite(ledPin, HIGH);
    bool rslt;
    const char text[] = "Toilet 1 - HEREN - BEZET";    //debug only 
    rslt = radio.write( &text, sizeof(text) );
        // Always use sizeof() as it gives the size as the number of bytes.
        // For example if dataToSend was an int sizeof() would correctly return 2

    Serial.print("Data Sent: ");
    Serial.println(text);
    if (rslt) {
        Serial.println("  Acknowledge received");
        digitalWrite(ledPin, LOW);
        updateMessage();
    }
    else {
        digitalWrite(ledPin, LOW);
        Serial.println("  Tx failed");
    }
}

//================

void updateMessage() {
        // so you can see that new data is being sent
    txNum += 1;
    if (txNum > '9') {
        txNum = '0';
    }
    dataToSend[8] = txNum;
}

*/

Rx

#include <SPI.h>
#include <nRF24L01.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <RF24.h>
#include <LowPower.h>

RF24 radio(9, 10); // CE, CSN



const byte address[6] = "00001";
boolean sensor_state = 0;   //
const int led_pin = 4;
const int pause = 7500; //pause time 7500ms

char dataReceived[10] = "Message 0"; // this must match dataToSend in the TX
bool newData = false;

void setup() {
Serial.begin(9600); 
pinMode(led_pin, OUTPUT);

    for (int i = 0; i < 20; i++) {
    if(i != 2)//just because the button is hooked up to digital pin 2
    pinMode(i, OUTPUT);
  }

  clock_prescale_set(clock_div_256);

  //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
  
  //DISABLE BOD
  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

  //SET CLOCK PROCESSOR
  CLKPR = 0x80;
  CLKPR = 0x01;

radio.begin();
radio.openReadingPipe(0, address);   //Setting the address at which we will receive the data
radio.setPALevel(RF24_PA_MIN);       //You can set this as minimum or maximum depending on the distance between the transmitter and receiver.
radio.setDataRate( RF24_250KBPS );
radio.startListening();
}

void loop(){

    getData();
    showData();
}

//============

void getData() {
    if ( radio.available() ) {
        char text[32] = "";
        radio.read( &text, sizeof(text) );
        radio.read(&sensor_state, sizeof(sensor_state));    //Reading the data
        newData = true;
    }
        
    if(sensor_state == HIGH)
{
digitalWrite(led_pin, LOW);
radio.startListening();
Serial.println("received");
  }
}

//================

void showData() {
    if (newData == true) {
        Serial.print("Data received ");
        Serial.println(dataReceived);
        newData = false;
    }
}

/*
{
if (radio.available())              //Looking for the data.
{
char text[32] = "";                 //Saving the incoming data
radio.read(&text, sizeof(text));    //Reading the data
radio.read(&sensor_state, sizeof(sensor_state));    //Reading the data
if(sensor_state == HIGH)
{
digitalWrite(led_pin, LOW);
radio.startListening();
Serial.println(text);
delay(100); 
}
else
{
digitalWrite(led_pin, HIGH);
Serial.println(text);}
}
delay(1000);

radio.powerDown(); //put nRF24L01 into power down mode
delay(7500);
radio.startListening();
}
*/

Those sensors have an amazingly low power consumption, well below the self discharge rate for normal alkaline cells.

  1. I don’t see any need to lower the activity of the TX devices at night. If they are woken by an interrupt, and there is no interrupt, they stay in sleep mode.

  2. Your power consumption problem is going to be the RX devices. In order to receive a transmission, they must be active at the time the transmitter is sending a signal. In practice, that means that they must be in active receive mode the whole time, or you have to develop a complex synchronization method so that the receiver knows when to wakeup to receive the transmissions. You can get an RTC to wakeup an MCU (the MCU internal oscillator may not be accurate enough for such a synchronization). You can get the RX and TX parts to agree a synchronization schedule, handling also synchronization failures and re-synchronizations. If you achieve this level of synchronization, you can then exceed the normal limit on the number of TX parts an RX part can listen to, because the RX part can then change channel / pipe / address etc. also according to a schedule. However, it is not a trivial task. Easiest is clearly to have the receiver part powered via a mains adapter.

  3. You also have a problem with your project time scales.

EDIT

  1. You probably also want to think about a pairing mechanism because, on anything other than a small scale operation, you don't want to be setting channel / pipe / address etc. programmatically. DIP switches could be an alternative and still be configurable by the end user.