[contest] 3D printed battery powered Wall Remote Control

  • Contest Winner

    Hello everybody,

    finally I manged to contribute my Project to the MySensors Contest 2015.
    It is a 3d printable Wall Remote control for Home Automation. The Size is 55x55mm, so it fits into many "switch frames" (e.g. Berger, Gira, Merten etc.) The project consists of the 3d printed Parts, the PCB (with Eagle sources), the Sketch, an FHEM example and of cause a detailed documentation which describes howto rebuild this Project on your own. All this is included in the zip file I will provide.

    Because the pdf, where the building is described precisely, is quiet long (20 pages I guess, but many photos) I don't want to put it here in the post. If you are intrested in the Project just have a look at the pdf inside the zip file. There are also more pictures included.


    To finish this project you need the following equipment/skills: 3D printer, SMD soldering capabilites, AVR Programmer (to install bootloader)

    Enough of talking, lets let the pictures speak for themselve:
    WR.jpg Housing_PCB_View_resized.jpg pcb_w_usb2ttl.jpg moved_key_resized.jpg

    A 3d preview of the printer parts can also be found on Thingiverse: http://www.thingiverse.com/thing:692238

  • Mod

    @Dirk_H cool!

  • This post is deleted!

  • Hi downloaded your sketch and i want to use it in the 2.0 mysensors library, but i got problems getting it work at the point of sleep:

    void WallRemote_Sleep() {
      //Custom Function that puts ATMega and NRF24L01 into sleep mode.
      //This was necessary because PinChange Interrupts have been used.
      sensor_node.RF24::powerDown();  //Power Down NRF24
      Serial.flush(); // although there should be nothing in the Serial cue, Let serial prints finish (debug, log etc)
      LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); //power everything down, wake up only by (PinChange) interrupts

    How to call rf24 power down without sensor_node in front of it?

    And i get:

    WallRemote4x.ino: In function 'void WallRemote_Sleep()':
    WallRemote4x:120: error: 'LowPower' was not declared in this scope
    WallRemote4x:120: error: 'ADC_OFF' was not declared in this scope
    WallRemote4x:120: error: 'BOD_OFF' was not declared in this scope
    'LowPower' was not declared in this scope

    After including LowPower:

    Users/Wiebe/Documents/Arduino/libraries/LowPower/LowPower.h:4:6: error: multiple definition of 'enum period_t'
     enum period_t
    In file included from /Users/Wiebe/Documents/Arduino/libraries/MySensors/core/MyHwATMega328.cpp:22:0,
                     from /Users/Wiebe/Documents/Arduino/libraries/MySensors/MySensor.h:69,
                     from WallRemote4x.ino:20:
    /Users/Wiebe/Documents/Arduino/libraries/MySensors/core/MyHwATMega328.h:78:6: error: previous definition here
     enum period_t

  • Contest Winner

    I'd try where the problem is, but I dont even get the MySesors class up and running. Is there already any API Reference/Documentation/Example for MySensors 2.0?

  • Contest Winner

    1st you need to make sure that there is an active interrupt that wakes up the controller, otherwise the microcontroller will sleep forever.

    To solve your above Problem: remove the lines "sensor_node.RF24::powerDown();" and "LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); //power everything down, wake up only by (PinChange) interrupts".

    Replace with "sleep(0xff, 0x00, 0xff, 0x00, 0);"
    Remember the things I mentioned above!

  • Admin

  • Contest Winner

    @hek Thank you very much. I found out most of it the hard way (reading code and die trying). I did not found the document which wold have helped me very much. Is it mentioned in the Github Repo? Maybe you should put a link to it in the readme .md?

  • Admin

    The document has been mentioned a few times here on the forum.

    When 2.0 is released I'll have to update the main site with the new api and examples.

    Just wish codebender would update their backend to more modern compiler supporting the new version (including ESP).

  • Hero Member

    @hek said:

    Just wish codebender would update their backend to more modern compiler

    Is there something we can do to put preasure on codebender? I really miss using codebender since the versions after MySensors 1.5 does not compile.

  • Admin

    Na, I've talked to them a couple of times about it. It's in their backlog.

    Had to tweak the current version on codebender to make it build (not identical to the 1.5.4 on github). Which isn't really sustainable in the long run.

  • Hero Member

    From your answer I now got the impression that I should be able to use codebender so I tried but if I do not have the MySensors Library in my "Personal libraries" All my sketches fail to compile. Then I went to http://www.mysensors.org/build/binary and choose to "Clone and Edit" and also clone the libraries then codebender is working for me again. If I check the Version.h under the lib that got cloned it says "1.5.3".

    So what is the correct way to get the latest version of your tweaked MySensors library for codebender?

    I really appreciate you tweaking and are hoping you keep doing it for the major releases until codebender can get up to speed.

  • Admin

    My guess is you delete your local copy and then re-clone it like you did.

  • hi @Dirk_H
    Great project. I'm looking for a battery powered switch sketch like yours.
    Do you have it updated to MySensors v2.x? I tried to convert it but did not manage to get it working.

     * Copyright (C) 2015 Dirk H. R.
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * version 2 as published by the Free Software Foundation.
     * This Program will implement six child sensors, which present one Push Button each.
     * The Program is meant to be used in conjunction with the Wall Remote Hardware
     * See MySensors for more information
     * Frequency is 3.6864 MHz to ensure operation at low Voltages
    #include <MySensors.h>
    #include <SPI.h>
    #include <PinChangeInt.h> //???
    #define MY_DEBUG
    #define MY_RADIO_NRF24
    #define SKETCH_NAME "Wall Remote"
    #define NodeID 2
    #define BotLeft_CHILD_ID 0			//Child ID for Bottom Left 
    #define MidLeft_CHILD_ID 1			//Child ID forMiddle Left 
    #define TopCornerLeft_CHILD_ID 2	//Child ID for Top Left (Corner Switch)
    #define BotRight_CHILD_ID 3			//Child ID for Bottom Left
    #define MidRight_CHILD_ID 4			//Child ID forMiddle Left
    #define TopCornerRight_CHILD_ID 5	//Child ID for Top Left (Corner Switch)
    #define BotLeft_PIN A1//21			//Digital Input for Bottom Left Pin			PCInt 11
    #define MidLeft_PIN A0//20			//Digital Input for Middle Left Pin			PCInt 10
    #define TopLeft_PIN A2//19			//Digital Input for Top Left (Corner Switch) Pin	PCInt 9
    #define BotRight_PIN 8			//Digital Input for Bottom Right Pin			
    #define MidRight_PIN 5			//Digital Input for Middle Right Pin			
    #define TopRight_PIN 4			//Digital Input for Top Right (Corner Switch) Pin	
    int BATTERY_SENSE_PIN = A6;  // select the input pin for the battery sense point
    int oldBatteryPcnt = 0;
    // Change V_LIGHT if you don't use S_LIGHT in presentation below
    MyMessage msg_BotLeft(BotLeft_CHILD_ID, V_LIGHT);
    MyMessage msg_MidLeft(MidLeft_CHILD_ID, V_LIGHT);
    MyMessage msg_TopLeft(TopCornerLeft_CHILD_ID, V_LIGHT);
    MyMessage msg_BotRight(BotRight_CHILD_ID, V_LIGHT);
    MyMessage msg_MidRight(MidRight_CHILD_ID, V_LIGHT);
    MyMessage msg_TopRight(TopCornerRight_CHILD_ID, V_LIGHT);
    //The BtnVal and the Btn_xx_ID are used to store the current state of the six buttons into a single uint8
    uint8_t BtnVal=0;	//idle/start value is 255 because Buttons get pulled up in idle state
    uint8_t BtnVal_last=0;
    #define Btn_BotLeft_ID 0
    #define Btn_MidLeft_ID 1
    #define Btn_TopLeft_ID 2
    #define Btn_BotRight_ID 3
    #define Btn_MidRight_ID 4
    #define Btn_TopRight_ID 5
    void setup()  
      // Setup the buttons
      pinMode(BotLeft_PIN, INPUT);
      pinMode(MidLeft_PIN, INPUT);
      pinMode(TopLeft_PIN, INPUT);
      pinMode(BotRight_PIN, INPUT);
      pinMode(MidRight_PIN, INPUT);
      pinMode(TopRight_PIN, INPUT);
      // Activate internal pull-ups
      digitalWrite(BotLeft_PIN, HIGH);
      digitalWrite(MidLeft_PIN, HIGH);
      digitalWrite(TopLeft_PIN, HIGH);
      digitalWrite(BotRight_PIN, HIGH);
      digitalWrite(MidRight_PIN, HIGH);
      digitalWrite(TopRight_PIN, HIGH);
      //Use internal 1.1V Reference (for Battery Measurement)
      /*Setup PinChange Interrupt. Unfortunately you must ensure that the "MyGateway.cpp" will not be compiled for this,
      i.e. look into your Arduino libraries\MySensors folder and 
      ---> RENAME THE "MyGateway.cpp" to e.g. "MyGateway.cpp.nolib" <---.
      Of cause you must undo the renameing when you want to compile a MySensors Gateway!
      The reason for this renaming is that the compilter of Arduino compiles also the MyGateway where the PinChange interrupt 
      Vector is also defined, which will lead to an error because it is only allowed to have it one time in the program.
      Serial.begin (115200);
    void presentation()
        // Register Wall Remote buttons (they will be created as child devices)
      // I decided to take the S_Light parameter as i inteded the WallRemote to switch lights,
      // however you can change if you think this is more apropriate, hovwever
      // If S_LIGHT is not used, remember to update variable type you send in. See "msg" above.
      present(BotLeft_CHILD_ID, S_LIGHT);  
      present(MidLeft_CHILD_ID, S_LIGHT);  
      present(TopCornerLeft_CHILD_ID, S_LIGHT); 
      present(BotRight_CHILD_ID, S_LIGHT);
      present(MidRight_CHILD_ID, S_LIGHT);
      present(TopCornerRight_CHILD_ID, S_LIGHT); 
    void WallRemote_Sleep(){
    	//Custom Function that puts ATMega and NRF24L01 into sleep mode.
    	//This was necessary because PinChange Interrupts have been used.
    	sleep(0xff, 0x00, 0xff, 0x00, 0);
    void inline BtnPressHandler(uint8_t Button, uint8_t ValPos){
    	//Handler that just saves state of pressed button into BtnVal variable
    	//assuming that no debouncing is necessary because of wakeup time (16kCykles @ 4MHz = 4ms)
    	if (digitalRead(Button)==LOW) BtnVal |= (1<<ValPos);
    	else BtnVal &= ~(1<<ValPos);
    //The six following ISRs are used to Wakeup the device and to call the Button Handler routine
    void BotLeft_ISR(){
    	BtnPressHandler(BotLeft_PIN, Btn_BotLeft_ID);
    void MidLeft_ISR(){
    	BtnPressHandler(MidLeft_PIN, Btn_MidLeft_ID);
    void TopLeft_ISR(){
    	BtnPressHandler(TopLeft_PIN, Btn_TopLeft_ID);
    void BotRight_ISR(){
    	BtnPressHandler(BotRight_PIN, Btn_BotRight_ID);
    void MidRight_ISR(){
    	BtnPressHandler(MidRight_PIN, Btn_MidRight_ID);
    void TopRight_ISR(){
    	BtnPressHandler(TopRight_PIN, Btn_TopRight_ID);
    void BtnSender(MyMessage Btn_Msg, uint8_t Btn_ID){
    	uint8_t temp=0;
    	temp = (BtnVal & (1<<Btn_ID));
    	if ( (BtnVal_last & (1<<Btn_ID)) != temp){
    		if (temp) send(Btn_Msg.set(HIGH)); 
    		else send(Btn_Msg.set(LOW));
    //loop only checks the ButtonState Variable and transmits changes if necessary
    void loop() 
    	// get the battery Voltage (note: we only get here at restart of the node or after Interrupt from the Buttons)
    	int BatVal = analogRead(BATTERY_SENSE_PIN);
    	// 1M, 470K divider across battery and using internal ADC ref of 1.1V
    	// Sense point is bypassed with 0.1 uF cap to reduce noise at that point
    	// ((1e6+470e3)/470e3)*1.1 = Vmax = 3.44 Volts
    	// 3.44/1023 = Volts per bit = 0.003363075
    	float batteryV  = BatVal * 0.003363075;
    	int batteryPcnt = BatVal / 10;
    	if (oldBatteryPcnt != batteryPcnt) {
    		// Power up radio after sleep
    		oldBatteryPcnt = batteryPcnt;
      //check if something has changed
      if (BtnVal != BtnVal_last){
    	  BtnSender(msg_BotLeft, Btn_BotLeft_ID);
    	  BtnSender(msg_MidLeft, Btn_MidLeft_ID);
    	  BtnSender(msg_TopLeft, Btn_TopLeft_ID);
    	  BtnSender(msg_BotRight, Btn_BotRight_ID);
    	  BtnSender(msg_MidRight, Btn_MidRight_ID);
    	  BtnSender(msg_TopRight, Btn_TopRight_ID);
      BtnVal_last = BtnVal;
      //Go back to sleep (custom function, see above)


  • Mod

    Did you guys managed to have it working with latest mysensors version?

Suggested Topics