Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
BartEB

BartE

@BartE
Contest Winner
About
Posts
196
Topics
14
Shares
0
Groups
2
Followers
1
Following
0

Posts

Recent Best Controversial

  • RFID Garage door opener
    BartEB BartE

    This sketch is to open a Garage door with an mifare RIFD-tag

    For an Arduino Nano v3 wiring :

    • rf24l01+ as descibed on the MySensors website
    • MFRC522 reader/writer as described above EXCEPT for pin D9 and D10: connect RST i.s.o. pin D9 to pin D7 and connect SDA(SS) i.s.o. pin D10 to pin D8
    • LED with 470ohm resistor between GMD and pin A3
    • push button between GND and pin D5
    • 5v relays coil between GND and pin D4 -> switch pins of the relays should be connected in parallel with your garage door push button.

    Features:

    • This project can record up to 18 RFID-"tags"
    • These card IDs are stored in to EEPROM by keeping them next to the RFID reader when the system in program mode. (Slow blinking LED) When a card is accepted as new card the LED will blink fast for a short time.
    • To keep the master-tags (choose your own) next to the RFID reader when pressing the push button. To clear all cards (except the master card) press the push button in program mode for 6 seconds. The LED will now fast blink for a couple of seconds.
    • Your garage your can be opened by keep a registered RFID tag next to the reader or by clicking the open icon on lock node presented by this plugin.
    • By by-passing the alarm node presented by this plug in the RFID will be temporarily disabled.
    • When an incorrect (not registered) RFID tag is scanned the alarm is triggered to detect illegal scan attempts
      RFIDSketch.png

    Used RFID reader can be found here:

    The Sketch code:

    /*
     * ----------------------------------------------------------------------------
     * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid
     * for further details and other examples.
     * 
     * NOTE: The library file MFRC522.h has a lot of useful info. Please read it.
     * 
     * Released into the public domain.
     * ----------------------------------------------------------------------------
     * Example sketch/program showing how to read data from a PICC (that is: a RFID
     * Tag or Card) using a MFRC522 based RFID Reader on the Arduino SPI interface.
     * 
     * When the Arduino and the MFRC522 module are connected (see the pin layout
     * below), load this sketch into Arduino IDE then verify/compile and upload it.
     * To see the output: use Tools, Serial Monitor of the IDE (hit Ctrl+Shft+M).
     * When you present a PICC (that is: a RFID Tag or Card) at reading distance
     * of the MFRC522 Reader/PCD, the serial output will show the ID/UID, type and
     * any data blocks it can read. Note: you may see "Timeout in communication"
     * messages when removing the PICC from reading distance too early.
     * 
     * If your reader supports it, this sketch/program will read all the PICCs
     * presented (that is: multiple tag reading). So if you stack two or more
     * PICCs on top of each other and present them to the reader, it will first
     * output all details of the first and then the next PICC. Note that this
     * may take some time as all data blocks are dumped, so keep the PICCs at
     * reading distance until complete.
     * 
     * Typical pin layout used:
     * -----------------------------------------------------------------------------------------
     *             MFRC522      Arduino       Arduino   Arduino    Arduino          Arduino
     *             Reader/PCD   Uno           Mega      Nano v3    Leonardo/Micro   Pro Micro
     * Signal      Pin          Pin           Pin       Pin        Pin              Pin
     * -----------------------------------------------------------------------------------------
     * RST/Reset   RST          9             5         D9         RESET/ICSP-5     RST
     * SPI SS      SDA(SS)      10            53        D10        10               10
     * SPI MOSI    MOSI         11 / ICSP-4   51        D11        ICSP-4           16
     * SPI MISO    MISO         12 / ICSP-1   50        D12        ICSP-1           14
     * SPI SCK     SCK          13 / ICSP-3   52        D13        ICSP-3           15
     */
    
    /*
     RFID Garagedoor opener by Bart Eversdijk
     
     This sketch is to open a Garage door with an mifare RIFD-tag 
      
      For an Arduino Nano v3
      Connection wiring :
        - nrf24l01+ as descibed on the MySensors website
        - MFRC522 reader/writer as described above EXCEPT for pin D9 and D10:  connect RST i.s.o. pin D9 to pin D7 and connect SDA(SS) i.s.o. pin D10 to pin D8 
        - LED with 470ohm resistor between GMD and pin A3 
        - push button between GND and pin D5
        - 5v relays coil between GND and  pin D4 -> switch pins of the relays should be connected in parallel with your garage door push button. 
      
      Features:
      This project can record up to 18 RFID-"tags"
      These card IDs are stored in to EEPROM by keeping them next to the RFID reader when the system in program mode. (Slow blinking LED) When a card is accepted as new card the LED will blink fast for a short time.
      To keep the master-tags (choose your own) next to the RFID reader when pressing the push button. To clear all cards (except the master card) press the push button in program mode for 6 seconds. The LED will now fast blink for a couple of seconds.
      
      Your garage your can be opened by keep a registered RFID tag next to the reader or by clicking the open icon on lock node presented by this plugin.
      By by-passing the alarm node presented by this plug in the RFID will be temporarily disabled.
      When an incorrect (not registered) RFID tag is scanned the alarm is triggered to detect illegal scan attempts   
    
    */
    
    #include <SPI.h>
    #include <MFRC522.h>
    #include <MySensor.h> 
    #include <Bounce2.h>
    
    #define RF_INIT_DELAY   125
    #define ONE_SEC         1000
    #define MAX_CARDS       18
    #define PROG_WAIT       10
    #define HEARTBEAT       10
    #define BAUD            115200
    
    /*Pin definitions*/
    #define LED_PIN         A3
    #define GARAGEPIN       4
    #define SWITCH_PIN      5
    #define RST_PIN		7		//  MFRC 
    #define SS_PIN		8		//  MFRC 
    
    MFRC522      mfrc522(SS_PIN, RST_PIN);	// Create MFRC522 instance
    MFRC522::Uid olduid;
    MFRC522::Uid masterkey = { 10, {0,0,0,0, 0,0,0,0, 0,0 },  0 };
    
    byte       countValidCards  = 0;
    MFRC522::Uid validCards[MAX_CARDS];
    
    void       ShowCardData(MFRC522::Uid* uid);
    bool       sameUid(MFRC522::Uid* old, MFRC522::Uid* check);
    void       copyUid(MFRC522::Uid* src, MFRC522::Uid* dest);
    bool       isValidCard(MFRC522::Uid* uid);
    int        releasecnt = 0;
    
    #define    CHILD_ID_ALARM    1
    #define    CHILD_ID_LOCK     2
    MySensor   gw;
    Bounce     debouncer = Bounce();
    
    int        oldSwitchValue=-1;
    int        switchValue = 0;
    long       timer = -1;
    bool       programmode = false;
    bool       ledon;
    int        programTimer = 0;
    bool       armed = true;
    unsigned long lastTime = 0;
    
    MyMessage  lockMsg(CHILD_ID_LOCK,          V_LOCK_STATUS);
    MyMessage  lockArmMsg(CHILD_ID_ALARM,      V_ARMED);
    MyMessage  wrongMsg(CHILD_ID_ALARM,        V_TRIPPED);
    
    void setup() {
    	Serial.begin(BAUD);		// Initialize serial communications with the PC
            pinMode(GARAGEPIN, OUTPUT);     // Initialise in/output ports
            
            // Make sure MFRC will be disabled on the SPI bus
            pinMode(RST_PIN, OUTPUT);     
            digitalWrite(RST_PIN, LOW);
            pinMode(SS_PIN, OUTPUT);     
            digitalWrite(SS_PIN, LOW);
    
            pinMode(LED_PIN, OUTPUT);
            digitalWrite(LED_PIN, LOW);
            // Setup the button
            pinMode(SWITCH_PIN, INPUT);
            // Activate internal pull-up
            digitalWrite(SWITCH_PIN, HIGH);
    
            // After setting up the button, setup debouncer
            debouncer.attach(SWITCH_PIN);
            debouncer.interval(5);
    
            // Init mysensors library
            gw.begin(incomingMessage, 5);
            gw.sendSketchInfo("RFID Garage", "1.1"); delay(RF_INIT_DELAY);
            
            // Register all sensors to gw (they will be created as child devices)
            gw.present(CHILD_ID_LOCK, S_LOCK);      delay(RF_INIT_DELAY);
            gw.present(CHILD_ID_ALARM, S_MOTION);   delay(RF_INIT_DELAY);
    
            recallEeprom();
            
            // Init MFRC RFID sensor
    	SPI.begin();			// Init SPI bus
    	mfrc522.PCD_Init();		// Init MFRC522
    	ShowReaderDetails();	        // Show details of PCD - MFRC522 Card Reader details
    
            gw.send(lockArmMsg.set(armed));
            Serial.println(F("Init done..."));
    }
    
    void loop() {
            timer++;
            delay(HEARTBEAT);
            gw.process();
            debouncer.update();
    
            // Get the update value
            int switchValue = debouncer.read();
            if (switchValue != oldSwitchValue) {
              // Send in the new value
              Serial.print (F("Switch "));
              Serial.println (switchValue);
              
              if (switchValue && programmode) {
                   lastTime     = millis() / 1000;
              }
              
              if (!switchValue && programmode && lastTime > 0) {
                   if ( (millis() / 1000) - lastTime > 3) {
                        Serial.println(F("Reset all cards"));  
                        countValidCards  = 0;
                        blinkFast(50);
                   } else {
                      Serial.println(F("Program off"));  
                      digitalWrite(LED_PIN, LOW);
                      programmode = false;
                      
                      storeEeprom();
                   }
              }
              
              if (!switchValue)   {
                  programTimer = 0;
              }
              oldSwitchValue = switchValue;
            }
            
            if (programmode && ((timer % (ONE_SEC / HEARTBEAT)) == 0 ))  {
                ledon = !ledon;
                digitalWrite(LED_PIN, ledon);
                programTimer++;
    
                // Stop program mode after 20 sec inactivity
                if (programTimer > PROG_WAIT)  {
                   programmode = false;
                   digitalWrite(LED_PIN, false);
                   Serial.println(F("Program expired"));  
                }
            }
            
            if ((timer % (200 / HEARTBEAT)) == 0 )   {
                 // Look for new cards
                 if ( ! mfrc522.PICC_IsNewCardPresent()) {
                      if (releasecnt > 0)   {
                          releasecnt--;
                          if (!releasecnt)  {
                              olduid.size = 0;
                              Serial.println(F("release"));
                          }
                        }
        		return;
        	    }
                releasecnt = 5;
        
        	   // Select one of the cards
        	   if ( ! mfrc522.PICC_ReadCardSerial()) {
        		return;
        	   }
        
               // Dump debug info about the card; PICC_HaltA() is automatically called
        	   //mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
               if (!olduid.size || !sameUid(&(mfrc522.uid), &olduid))  {
                    ShowCardData(&(mfrc522.uid));
                    copyUid(&(mfrc522.uid), &olduid);
                    if ( isValidCard(&olduid) )   {
                         OpenDoor(programmode);
                    } else  {
                      
                       if (sameUid(&(mfrc522.uid), &masterkey)) {
                           // Only switch in program mode when mastercard is found AND the program button is pressed
                           if (switchValue)  {
                             Serial.println(F("Program mode"));
                             programmode = true;
                             programTimer = 0;
                             lastTime     = 0;
                           }
                       } else {
                           if (programmode) {
                             Serial.println(F("new card"));
                             programTimer = 0;
                             
                             if (countValidCards < MAX_CARDS)
                             {
                                // Add card to list...
                                copyUid(&(mfrc522.uid), &validCards[countValidCards]);
                                countValidCards++;
                                blinkFast(15);
                             }
                           } else {
                             Serial.println(F("Invalid card"));
                             if (armed) {
                                gw.send(wrongMsg.set(1));
                                delay(2000);
                                gw.send(wrongMsg.set(0));
                             }
                           }
                       }
                    }
               }
            }
    }
    
    void ShowCardData(MFRC522::Uid* uid) {
            Serial.print(F("Card UID:"));
    	for (byte i = 0; i < uid->size; i++) {
    		if(uid->uidByte[i] < 0x10) {
    			Serial.print(F(" 0"));
    		} else {
    			Serial.print(F(" "));
                    }
    		Serial.print(uid->uidByte[i], HEX);
    	} 
    	Serial.println();
    }
    
    void copyUid(MFRC522::Uid* src, MFRC522::Uid* dest)
    {
        dest->size = src->size;
        dest->sak  = src->sak;
        
        for (byte i = 0; i < src->size; i++) {
      	dest->uidByte[i] = src->uidByte[i];
        }
    }
    
    bool sameUid(MFRC522::Uid* old, MFRC522::Uid* check)
    {
        if (old->size != check->size) {
           return false;
        }
        for (byte i = 0; i < old->size; i++) {
      	if (old->uidByte[i] != check->uidByte[i]) {
                return false;
            }
        }
        return true;
    }
    
    bool isValidCard(MFRC522::Uid* uid)
    {
          for (byte i = 0; i < countValidCards; i++)  {
              if (validCards[i].size != uid->size)  {
                  break;
              }
              for (int j = 0; j < uid->size; j++) {
                  if (validCards[i].uidByte[j] != uid->uidByte[j])  {
                      break;
                  }
                  if (j == (uid->size - 1)) {  
                      return true;
                  }
              }
          }
          return false;
    }
    
    
    void storeEeprom()
    {
        byte address = 0;
        gw.saveState(address++, countValidCards);
        
        for (byte i = 0; i < countValidCards; i++) {
           gw.saveState(address++, validCards[i].size);
           for (byte j = 0; j < 10; j++) {
              gw.saveState(address++, validCards[i].uidByte[j]);
           } 
        }
    }
    
    void recallEeprom()
    {
        byte address = 0;
        
        countValidCards = gw.loadState(address++);
        if (countValidCards > MAX_CARDS) {
           Serial.println(F("Not a valid EEPROM reading set to default"));
           countValidCards = 0;
           storeEeprom();
           return;
        }
        
        for (byte i = 0; i < countValidCards; i++)  {
           validCards[i].size = gw.loadState(address++);
           for (byte j = 0; j < 10; j++)  {
              validCards[i].uidByte[j] = gw.loadState(address++);
           } 
        }
      
    }
    
    void blinkFast(int times)
    {
        for (int i = 0; i < times; i++) { 
           ledon = !ledon;
           digitalWrite(LED_PIN, ledon);
           delay(100);
        }
    }
    
    void OpenDoor(bool fakeOpen)
    {
        Serial.println(F("Open door!"));
        gw.send(lockMsg.set(false));
        
        if (!fakeOpen) { 
          digitalWrite(LED_PIN, HIGH);
          digitalWrite(GARAGEPIN, HIGH); 
        }
        delay(1000);
        
        if (!fakeOpen) { 
          digitalWrite(GARAGEPIN, LOW); 
          digitalWrite(LED_PIN, LOW);
        }
        
        gw.send(lockMsg.set(true));
    }
    
    void ShowReaderDetails() {
    	// Get the MFRC522 software version
    	byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
    	Serial.print(F("MFRC522 Software Version: 0x"));
    	Serial.print(v, HEX);
    	if (v == 0x91) {
    		Serial.print(F(" = v1.0"));
            } else if (v == 0x92) {
    		Serial.print(F(" = v2.0"));
            } else {
    		Serial.print(F(" (unknown)"));
            }
    	Serial.println("");
    	
            // When 0x00 or 0xFF is returned, communication probably failed
    	if ((v == 0x00) || (v == 0xFF)) {
    		Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?"));
    	}
    }
    
    
    void incomingMessage(const MyMessage &message) 
    {
       if (message.type == V_LOCK_STATUS) {
         // Change relay state
         if (!message.getBool())  {
            OpenDoor(false);
         }
      
         // Write some debug info
         Serial.print(F("Lock status: "));
         Serial.println(message.getBool());
       }
       else 
       {
         if (message.type == V_ARMED)  {
           // Change relay state
           armed = message.getBool();
      
           // Write some debug info
           Serial.print(F("Arm status: "));
           Serial.println(message.getBool());
         }
         else
         {
           // Write some debug info
           Serial.print(F("Incoming msg type: "));
           Serial.print(message.type);
           Serial.print(F(" id: "));
           Serial.print(message.sensor);
           Serial.print(F(" content: "));
           Serial.println(message.getInt());
         }
       }
    }
    
    My Project

  • IR Record and Playback module
    BartEB BartE

    On special request i've created a IR (InfraRed remote) record and playback sensor.
    This is based on the example IR sensor but now one can record a IR - signal from an remote and let your controller send the IR code on request. http://www.mysensors.org/build/ir

    Up to 10 codes can be recorded and playback

    Please note this plugin relies on a message type which is not yet available in the MySensors core (a pull request has been made)
    This Arduino library https://github.com/z3t0/Arduino-IRremote/releases is used for decoding and encoding:

    /**
     * The MySensors Arduino library handles the wireless radio link and protocol
     * between your home built sensors/actuators and HA controller of choice.
     * The sensors forms a self healing radio network with optional repeaters. Each
     * repeater and gateway builds a routing tables in EEPROM which keeps track of the
     * network topology allowing messages to be routed to nodes.
     *
     * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
     * Copyright (C) 2013-2015 Sensnology AB
     * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
     *
     * Documentation: http://www.mysensors.org
     * Support Forum: http://forum.mysensors.org
     *
     * 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.
     *
     *******************************
     *
     * REVISION HISTORY
     * Version 1.0 - Changed for MySensors usage by Bart Eversdijk
     * 
     * DESCRIPTION
     *
     * IRrecord: record and play back IR signals as a minimal 
     * An IR detector/demodulator must be connected to the input RECV_PIN.
     * An IR LED must be connected to the output PWM pin 3.
     * A button must be connected to the input BUTTON_PIN; this is the
     * send button.
     * A visible LED can be connected to STATUS_PIN to provide status.
     *
     * The logic is:
     * If the button is pressed, send the IR code.
     * If an IR code is received, record it.
     *
     * Version 0.11 September, 2009
     * Copyright 2009 Ken Shirriff
     * http://arcfn.com
     */
    
    #include <MySensor.h>
    #include <SPI.h>
    #include <IRremote.h>  // https://github.com/z3t0/Arduino-IRremote/releases
    
    int RECV_PIN     = 8;
    #define NODEID   5
    #define CHILD_1  2  // childId
    
    #define MY_RAWBUF  50
    const char * TYPE2STRING[] = {
    		"UNKONWN",
    		"RC5",
    		"RC6",
    		"NEC",
    		"Sony",
    		"Panasonic",
    		"JVC",
    		"SAMSUNG",
    		"Whynter",
    		"AIWA RC T501",
    		"LG",
    		"Sanyo",
    		"Mitsubishi",
    		"Dish",
    		"Sharp",
    		"Denon"
    };
    #define Type2String(x)   TYPE2STRING[x < 0 ? 0 : x]
    #define AddrTxt          F(" addres: 0x")
    #define ValueTxt         F(" value: 0x")
    #define NATxt            F(" - not implemented/found")
    
    // Raw or unknown codes requires an Arduino with a larger memory like a MEGA and some changes to store in EEPROM (now max 255 bytes)
    // #define IR_SUPPORT_UNKNOWN_CODES
    typedef union
    {
      struct
      {
        decode_type_t type;            // The type of code
        unsigned long value;           // The data bits if type is not raw
        int           len;             // The length of the code in bits
        unsigned int  address;         // Used by Panasonic & Sharp [16-bits]
      } code;
    #ifdef IR_SUPPORT_UNKNOWN_CODES      
      struct
      {
        decode_type_t type;             // The type of code
        unsigned int  codes[MY_RAWBUF];
        byte          count;           // The number of interval samples
      } raw;
    #endif
    } IRCode;
    
    #define           MAX_IR_CODES     10
    IRCode            StoredIRCodes[MAX_IR_CODES];
    
    IRrecv            irrecv(RECV_PIN);
    IRsend            irsend;
    decode_results    ircode;
    
    
    #define           NO_PROG_MODE 0xFF
    byte              progModeId       = NO_PROG_MODE;
    
    MySensor gw;
    MyMessage msgIrReceive(CHILD_1, V_IR_RECEIVE);
    MyMessage msgIrRecord(CHILD_1, V_IR_RECORD); 
    
    void setup()  
    {  
      gw.begin(incomingMessage, NODEID);
    
      // Send the sketch version information to the gateway and Controller
      gw.sendSketchInfo("IR Rec/Playback", "1.0");
    
      // Register a sensors to gw. Use binary light for test purposes.
      gw.present(CHILD_1, S_IR);
    
      // Tell MYS Controller that we're NOT recording
      gw.send(msgIrRecord.set(0));
      
      Serial.println(F("Recall EEPROM settings"));
      recallEeprom(sizeof(StoredIRCodes), (byte *)&StoredIRCodes);
    
      // Start the ir receiver
      irrecv.enableIRIn(); 
      
      Serial.println(F("Init done..."));
    }
    
    void loop() 
    {
      gw.process();
      
      if (irrecv.decode(&ircode)) {
          dump(&ircode);
          if (progModeId != NO_PROG_MODE) {
             // If we are in PROG mode (Recording) store the new IR code and end PROG mode
             if (storeRCCode(progModeId)) {
                Serial.print(F("Stored "));
              
                // If sucessfull RC decode and storage --> also update the EEPROM
                storeEeprom(sizeof(StoredIRCodes), (byte *)&StoredIRCodes);
                progModeId = NO_PROG_MODE;
               
                // Tell MYS Controller that we're done recording
                gw.send(msgIrRecord.set(0));
             }
          } else {
             // If we are in Playback mode just tell the MYS Controll we did receive an IR code
             if (ircode.decode_type != UNKNOWN) {
                 if (ircode.value != REPEAT) {
                   // Look if we found a stored preset 0 => not found
                   byte num = lookUpPresetCode(&ircode);
                   if (num) {
                       // Send IR decode result to the MYS Controller
                       Serial.print(F("Found code for preset #"));
                       Serial.println(num);
                       gw.send(msgIrReceive.set(num));
                   }
                 }
             }
        }
        // Wait a while before receive next IR-code
        delay(500);
        
        // Start receiving again
        irrecv.resume();
      }
    }
    
    void incomingMessage(const MyMessage &message) {
        //Serial.print(F("New message: "));
        //Serial.println(message.type);
       
       if (message.type == V_IR_RECORD) { // IR_RECORD V_VAR1
          // Get IR record requets for index : paramvalue
          progModeId = message.getByte() % MAX_IR_CODES;
          
          // Tell MYS Controller that we're now in recording mode
          gw.send(msgIrRecord.set(1));
          
          Serial.print(F("Record new IR for: "));
          Serial.println(progModeId);
       }
      
       if (message.type == V_IR_SEND) {
          // Send an IR code from offset: paramvalue - no check for legal value
          Serial.print(F("Send IR preset: "));
          Serial.print(message.getByte());
          sendRCCode(message.getByte() % MAX_IR_CODES);
       }
    
       // Start receiving ir again...
       irrecv.enableIRIn(); 
    }
    
    
    byte lookUpPresetCode (decode_results *ircode)
    {
        for (byte index = 0; index < MAX_IR_CODES; index++)
        {
          if ( StoredIRCodes[index].code.type  == ircode->decode_type &&
               StoredIRCodes[index].code.value == ircode->value       &&
               StoredIRCodes[index].code.len   == ircode->bits)      {
              // The preset number starts with 1 so the last is stored as 0 -> fix this when looking up the correct index
              return (index == 0) ? MAX_IR_CODES : index;
          }  
        }
        // not found so return 0
        return 0;
    }
        
    // Stores the code for later playback
    bool storeRCCode(byte index) {
    
      if (ircode.decode_type == UNKNOWN) {
    #ifdef IR_SUPPORT_UNKNOWN_CODES  
          Serial.println(F("Received unknown code, saving as raw"));
          // To store raw codes:
          // Drop first value (gap)
          // As of v1.3 of IRLib global values are already in microseconds rather than ticks
          // They have also been adjusted for overreporting/underreporting of marks and spaces
          byte rawCount = min(ircode.rawlen - 1, MY_RAWBUF);
          for (int i = 1; i <= rawCount; i++) {
            StoredIRCodes[index].raw.codes[i - 1] = ircode.rawbuf[i]; // Drop the first value
          };
          return true;
    #else 
          return false;
        }
    #endif
    
      if (ircode.value == REPEAT) {
          // Don't record a NEC repeat value as that's useless.
          Serial.println(F("repeat; ignoring."));
          return false;
       }
       StoredIRCodes[index].code.type      = ircode.decode_type;
       StoredIRCodes[index].code.value     = ircode.value;
       StoredIRCodes[index].code.address   = ircode.address;      // Used by Panasonic & Sharp [16-bits]
       StoredIRCodes[index].code.len       = ircode.bits;
       Serial.print(F(" value: 0x"));
       Serial.println(ircode.value, HEX);
       return true;
    }
    
    void sendRCCode(byte index) {
    
    #ifdef IR_SUPPORT_UNKNOWN_CODES  
       if(StoredIRCodes[index].code.type == UNKNOWN) {
          // Assume 38 KHz
          irsend.sendRaw(StoredIRCodes[index].raw.codes, StoredIRCodes[index].raw.count, 38);
          Serial.println(F("Sent raw"));
          return;
       }
    #endif
    
       Serial.print(F(" - sent "));
       Serial.print(Type2String(StoredIRCodes[index].code.type));
       if (StoredIRCodes[index].code.type == RC5) {
           // For RC5 and RC6 there is a toggle bit for each succesor IR code sent alway toggle this bit
           StoredIRCodes[index].code.value ^= 0x0800;
           irsend.sendRC5(StoredIRCodes[index].code.value, StoredIRCodes[index].code.len);
        } 
        else if (StoredIRCodes[index].code.type == RC6) {
           // For RC5 and RC6 there is a toggle bit for each succesor IR code sent alway toggle this bit
           StoredIRCodes[index].code.value ^= 0x10000;
           irsend.sendRC6(StoredIRCodes[index].code.value, StoredIRCodes[index].code.len);
       }
       else if (StoredIRCodes[index].code.type == NEC) {
           irsend.sendNEC(StoredIRCodes[index].code.value, StoredIRCodes[index].code.len);
        } 
        else if (StoredIRCodes[index].code.type == SONY) {
           irsend.sendSony(StoredIRCodes[index].code.value, StoredIRCodes[index].code.len);
        } 
        else if (StoredIRCodes[index].code.type == PANASONIC) {
           irsend.sendPanasonic(StoredIRCodes[index].code.address, StoredIRCodes[index].code.value);
           Serial.print(AddrTxt);
           Serial.println(StoredIRCodes[index].code.address, HEX);
        }
        else if (StoredIRCodes[index].code.type == JVC) {
           irsend.sendJVC(StoredIRCodes[index].code.value, StoredIRCodes[index].code.len, false);
        }
        else if (StoredIRCodes[index].code.type == SAMSUNG) {
           irsend.sendSAMSUNG(StoredIRCodes[index].code.value, StoredIRCodes[index].code.len);
        }
        else if (StoredIRCodes[index].code.type == WHYNTER) {
           irsend.sendWhynter(StoredIRCodes[index].code.value, StoredIRCodes[index].code.len);
        }
        else if (StoredIRCodes[index].code.type == AIWA_RC_T501) {
           irsend.sendAiwaRCT501(StoredIRCodes[index].code.value);
        }
        else if (StoredIRCodes[index].code.type == LG || StoredIRCodes[index].code.type == SANYO || StoredIRCodes[index].code.type == MITSUBISHI) {
           Serial.println(NATxt);
           return;
        }
        else if (StoredIRCodes[index].code.type == DISH) {
           irsend.sendDISH(StoredIRCodes[index].code.value, StoredIRCodes[index].code.len);
        }
        else if (StoredIRCodes[index].code.type == SHARP) {
           irsend.sendSharp(StoredIRCodes[index].code.address, StoredIRCodes[index].code.value);
           Serial.print(AddrTxt);
           Serial.println(StoredIRCodes[index].code.address, HEX);
        }
        else if (StoredIRCodes[index].code.type == DENON) {
           irsend.sendDenon(StoredIRCodes[index].code.value, StoredIRCodes[index].code.len);
        }
        else {
          // No valid IR type, found it does not make sense to broadcast
          Serial.println(NATxt);
          return; 
        }
        Serial.print(" ");
        Serial.println(StoredIRCodes[index].code.value, HEX);
    }    
    
    // Dumps out the decode_results structure.
    void dump(decode_results *results) {
        int count = results->rawlen;
        
        Serial.print(F("Received : "));
        Serial.print(results->decode_type, DEC);
        Serial.print(F(" "));
        Serial.print(Type2String(results->decode_type));
      
        if (results->decode_type == PANASONIC) {	
          Serial.print(AddrTxt);
          Serial.print(results->address,HEX);
          Serial.print(ValueTxt);
        }
        Serial.print(F(" "));
        Serial.print(results->value, HEX);
        Serial.print(F(" ("));
        Serial.print(results->bits, DEC);
        Serial.println(F(" bits)"));
      
        if (results->decode_type == UNKNOWN) {
          Serial.print(F("Raw ("));
          Serial.print(count, DEC);
          Serial.print(F("): "));
      
          for (int i = 0; i < count; i++) {
            if ((i % 2) == 1) {
              Serial.print(results->rawbuf[i]*USECPERTICK, DEC);
            } 
            else {
              Serial.print(-(int)results->rawbuf[i]*USECPERTICK, DEC);
            }
            Serial.print(" ");
          }
          Serial.println("");
        }
    }
    
    // Store IR record struct in EEPROM   
    void storeEeprom(byte len, byte *buf)
    {
        gw.saveState(0, len);
        for (byte i = 1; i < min(len, 100); i++, buf++)
        {
           gw.saveState(i, *buf);
        }
    }
    
    void recallEeprom(byte len, byte *buf)
    {
        if (gw.loadState(0) != len)
        {
           Serial.print(F("Corrupt EEPROM preset values and Clear EEPROM"));
           for (byte i = 1; i < min(len, 100); i++, buf++)
           {
               *buf = 0;
               storeEeprom(len, buf);
           }
           return;
        }
        for (byte i = 1; i < min(len, 100); i++, buf++)
        {
           *buf = gw.loadState(i);
        }
    }   
        
    
    My Project

  • Running ATmega328P on internal 8MHz clock
    BartEB BartE

    Playing around a little bit with GertSanders 50 by 30 mm PCB i was wondering if this board also could run without the external clock (8MHz crystal). Saving not only a couple of components but even better save battery lifetime. It appears the ATmega328 uses less power when running on the internal clock.

    It turned out to be a bigger puzzle than expected i was not able to find a straight answer nor a ready-mate solution. Fortunately i did found a couple partial solution and will try describe the way it worked out for my setup.

    It all starts with a bare ATmega328P chip, they are sold in basically 2 flavors. One with-out a bootloader (see Wikipedia) (or with a non Arduino) they are programmed to run on the 1 MHz internal clock. The second flavor is shipped with a pre-programmed Arduino R3 bootloader these will expect a 16 MHz external clock, (but an 8 MHz crystal will do).

    The clock settings are made by so-called fuses, a better name would be internal dip-switches since a fuse sounds like one-time only programming , while the ATmega's fuses can be re-programmed over and over again. So for both flavors we need change theses fuses and for the first flavor we also need to program a bootloader.

    On the Arduino website we can read how to program a bootloader by connecting it to an Arduio UNO (see here) BUT in this case the programming it selves did not worked out. So wiring is OK the program sequence not:

    • For the empty 328p's this wiring will do (so no Crystal) https://www.arduino.cc/en/uploads/Tutorial/SimpleBreadboardAVR.png

    • For the one with an bootloader one needs to wire the 328P like this: https://www.arduino.cc/en/uploads/Tutorial/BreadboardAVR.png

    A better way to program the Arduino is using the sketch from Bill Westfield which can be found here: https://github.com/WestfW/OptiLoader So download this sketch into the Arduino UNO and when wired liked described above the new Atmega is programmed with a fresh bootloader.

    The last step is the set the correct fuses so the interval 8 MHz clock is used. This turned-out to be not so easy to figure out . There are 3 fuse bytes which need to be set. The "Low", "High" and "Extented" , in the end i did run this page: http://www.engbedded.com/fusecalc/. Where you can selected the appropriate configuration and the site will "calculate" the correct byte's (and vice versa).

    The setting which does work for me are:

    • Low := 0xE2
    • High := 0xDE
    • Ext. := 0xFD

    To set these fuse bytes in the Atmega one needs to change Bill's OptiLoader script around line 730 you can find
    this peace of code:

    const image_t PROGMEM image_328p = {
      {
        "optiboot_atmega328.hex"    }
      ,
      {
        "atmega328P"    }
      ,
      0x950F,				/* Signature bytes for 328P */
      {
         0x3F,0xFF,0xDE,0x05,0   }
      ,
      {
        0x2F,0,0,0,0     }
    

    Once change to this the correct fuse byte's are used

    const image_t PROGMEM image_328p = {
      {
        "optiboot_atmega328.hex"    }
      ,
      {
        "atmega328P"    }
      ,
      0x950F,				/* Signature bytes for 328P */
      {
        // 16 MHz external crystal
    //    0x3F,0xFF,0xDE,0x05,0   
        // 8 MHz internal crystal
          0x3F,0xE2,0xDE,0xFD,0  
    }
      ,
      {
        0x2F,0,0,0,0   
      }
    

    So in short:

    • Connect your new Atmega to an Arduino UNO
    • Download Bill Westfield skecth WITH fuse modifaction into the UNO
    • Give a reset and look at your terminal if the bootloader and fuse where set correctly

    Once the correct bootloader is in. You can place the ATmega328 in GertSanders PCB and program a sketch in in with these type of FTDI USB serial convertors: http://www.ebay.com/itm/320907184616?rmvSB=true.
    Set the Ardiuno IDE to:

    • Tools -> Board -> Arduino Pro or Pro Mini
    • Tools -> Processor -> ATmega328 (3.3V 8 MHz)
    • Tools -> Board -> ArduinoISP

    And have fun with it...

    Hardware

  • Single button operated Led Strip Dimmer
    BartEB BartE

    Since i wanted to integrate a dimming led strip in my existing house electrical system i did create the following projected.

    It had to:

    • fit in my electrical wiring (so need to pull additional wires)
    • single button operation (standard house switch)
    • full MySensors dimming and control features

    During development i discovered that (in my situation) the standard LED strip dimmer did not reach 12 volt @ 100% dim level (digitalWrite(pin, 255)) so a had to added a extra NPN transistor to reach the full 12 volt an thus maximum light. "Down site" of this extra transistor an inverted dim control so digitalWrite(pin, 255) will give no light and digitalWrite(pin, 0) 100%.

    To make use of the standard in-wall switch i added a relay with a 230v coil controlling a 5 volt input pin a to signal the Arduino

    This is the electrical prototype scheme:

    LedStripSwitch.png

    It operates like this:

    • Just toggling the switch will light the LED-(strip) smooth to 100% level or back to 0% when turning the light off.
    • By a short ON-OFF switch-pulse the LED will dim to the last set dim level when it was OFF or dim to 0% when the LED was on.
    • Setting a new target dim level can be done by keeping the switch on until it reaches the desired dim-level and the switch OFF again.
    • Now the LED will stay on keeping the dim level
    • In all situations your home automation controller will be informed on the changing situations and off course can override the switch situation

    This sketch controls 2 LED-(strips) but can be easily extended by adding a LEDS entry to the led array (on line 67) the amount of leds is limited by the amount of PWM pins on your Arduino this is 6 (pin#3,5,6, 9,10,11), but 3 of these PWN pins (9.10,11) are occupied by the radio (NRF24L01), were pin 9 of the radio can be remapped
    For the switch input both analog (Ax) and digital (Dx) pins can be used

    The dim level can be set linear i.s.o. logarithmic (for your eyes this will look more linear)

    /**
     * REVISION HISTORY
     * Version 1.0 - February 15, 2014 - Bruce Lacey
     * Version 1.1 - August 13, 2014 - Converted to 1.4 (hek) 
     * Version 1.2 - Januari 2016 - Bart Eversdijk
     *
     * DESCRIPTION
     * This sketch provides a Dimmable LED Light using PWM and based on Henrik Ekblad 
     * <henrik.ekblad@gmail.com> Vera Arduino Sensor project.  
     * Developed by Bruce Lacey, inspired by Hek's MySensor's example sketches.
     * 
     * The circuit uses a MOSFET for Pulse-Wave-Modulation to dim the attached LED or LED strip.  
     * The MOSFET Gate pin is connected to Arduino pin 3 (LED_PIN), the MOSFET Drain pin is connected
     * to the LED negative terminal and the MOSFET Source pin is connected to ground.  
     *
     * V1.2 Additions:
     * control by normal ON/OFF switch
     * The sketch is now optimized for integration in an existing house wiring situation, Where a standard light switch can be used to control
     * the dimmer. Just toggling the switch will light the LED-(strip) smooth to 100% level or back to 0% when turning the light off.
     * By a short ON-OFF switch-pulse the LED will dim to the last set dim level (when it was OFF) or dim to 0% when the LED was on.
     * Setting a new target dim level can be done by keeping the switch on until it reaches the desired dim-level and the switch OFF again. 
     * Now the LED will stay on keeping the dim level. 
     * In all situations your home automation controller will be informed on the changing situations and off course can override the switch situation 
     * 
     * This sketch controls 2 LED-(strips) but can be easily extended by adding a LEDS entry to the led array (on line 70)
     * The dim level can be set linear i.s.o. logarithmic (for your eyes this will look more linear)
     *
     * http://www.mysensors.org/build/dimmer
     */
    
    #include <MySensor.h> 
    #include <SPI.h>
    #include <Bounce2.h>
    #include <math.h>
    
    #define LED1_PIN    6  // Arduino pin attached to MOSFET Gate pin
    #define SW1_PIN     4
    #define LED2_PIN    3  // Arduino pin attached to MOSFET Gate pin
    #define SW2_PIN     2
    
    #define MYS_INIT_DELAY 500
    
    #define FADE_DELAY 10  // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
    enum DIMCURVES {
      DIM_LINEAR = 0,        // Normal linear curve
      DIM_LINEAR_INV,        // Inverted linear curve
      DIM_LOGARITHMIC,       // Normal logarithmic curve
      DIM_LOGARITHMIC_INV,   // Inverted logarithmic curve
      DIM_CUSTOM             // Define your own dimming curve
    };
    
    struct LEDS {
        int       currentLevel; // Current dim level
        int       toLevel;      // Level to dim to
        Bounce    debouncer;
        int       LedPin;
        int       SwitchPin;
        byte      switchValue;
        int       savedLevel;   // level to dim to when on is pressed
        int       switchCount;
        bool      ignoreNextSw; // if true ignore next OFF switch (was overriden by controller in ON state)
        DIMCURVES dimcurve;     // Set the dim curve mode (linear, logarithmic, inverted, custom) 
        MyMessage dimmerMsg;
        MyMessage lightMsg;
    };
    
    LEDS led[] = {
              {0, 0, Bounce(), LED1_PIN, SW1_PIN, 0, 100, 0, false, DIM_LINEAR_INV, MyMessage(0, V_DIMMER), MyMessage(0, V_LIGHT)},
              {0, 0, Bounce(), LED2_PIN, SW2_PIN, 0, 100, 0, false, DIM_CUSTOM,     MyMessage(1, V_DIMMER), MyMessage(1, V_LIGHT)} 
            };
    #define MAXLED (sizeof(led)/sizeof(LEDS))
    
    MySensor gw;
    
    /***
     * Dimmable LED initialization method
     */
    void setup()  
    { 
       // Switch off all leds
       for (byte id = 0; id < MAXLED; id++) {
           pinMode(led[id].LedPin, OUTPUT);
           setLedLevel(id);
       }
       
       gw.begin( incomingMessage );
      
       // Register the LED Dimmable Light with the gateway
       for (byte id = 0; id < MAXLED; id++) {
           pinMode(led[id].SwitchPin, INPUT);
           // Activate internal pull-up
           digitalWrite(led[id].SwitchPin, HIGH); 
            
           gw.present( id, S_DIMMER );
           delay( MYS_INIT_DELAY );
    
           // Pull the gateway's current dim level - restore light level upon sendor node power-up
           gw.request( id, V_DIMMER );
           delay( MYS_INIT_DELAY );
           
           // After setting up the button, setup debouncer
           led[id].debouncer.attach(led[id].SwitchPin);
           led[id].debouncer.interval(5);
       }
        
       gw.sendSketchInfo("1.2", "LedStripSwitch");
    }
    
    /***
     *  Dimmable LED main processing loop 
     */
    void loop() 
    {
         gw.process();
         
         for (byte id = 0; id < MAXLED; id++) {
           // If target level is not reached fade a little bit more
            if (led[id].currentLevel != led[id].toLevel)  {
               led[id].currentLevel += ((led[id].toLevel - led[id].currentLevel ) < 0 ? -1 : 1);
               setLedLevel(id);
            }
    
            // Check debounced button  
            led[id].debouncer.update();
            byte switchValue = led[id].debouncer.read() ? 0 : 1; // Inverted signal 
            
            // Button change detected
            if (led[id].switchValue != switchValue) {
               Serial.print (F("Switch "));
               Serial.println (switchValue);
               led[id].switchValue = switchValue;
               
               // If key released switch on when off or off when on --> when we where fading (above 100 steps) this is the end state
               // When we just left the button (> 500) we now turning the lights off again
               if (!switchValue && !led[id].ignoreNextSw) {
                  if (led[id].switchCount <= 100 || led[id].switchCount > 500) {
                      led[id].toLevel = (led[id].currentLevel ? 0 : led[id].savedLevel);
                  } else {
                      led[id].savedLevel = led[id].toLevel; // Store new saved level
                  }
                  
                  // Inform the gateway of the current DimmableLED's SwitchPower1 and LoadLevelStatus value...
                  gw.send(led[id].lightMsg.set(led[id].toLevel > 0 ? 1 : 0));
                  gw.send(led[id].dimmerMsg.set(led[id].toLevel) );
               }
               led[id].ignoreNextSw = false;
               led[id].switchCount = 0;
            } else if (switchValue && led[id].switchCount <= 500) {
               // Keep counting until we reached 500 (@ 500 we asume we are in switch ON / OFF mode)
               led[id].switchCount++;
               
               // So this in not just a switch on (or off) but a new target led level key press
               if (led[id].switchCount > 100) {
                  // Smooth led level increment, until the user found his/here desired dim lever
                  if ((led[id].switchCount % 3) == 0) {
                      // Stop increasing led level @ 100%
                      if (led[id].currentLevel < 100) {
                          if (led[id].currentLevel == 99) {
                              // Inform the gateway we've reached 100%
                              gw.send(led[id].lightMsg.set(1));
                              gw.send(led[id].dimmerMsg.set(100));
                          }
                          led[id].currentLevel++;
                          led[id].toLevel = led[id].currentLevel;
                          setLedLevel(id);
                      } 
                  }
               }
            } 
         }  
         
         // Wait FADE_DELAY ms to smooth the dim level adjustments
         gw.wait(FADE_DELAY);
    }
    
    void incomingMessage(const MyMessage &message) {
       if (message.type == V_LIGHT || message.type == V_DIMMER) {
         byte id = (message.sensor % MAXLED);
          
          // Retrieve the power or dim level from the incoming request message
          // if this is a V_LIGHT variable update [0 == off, 1 == on] use savedLevel
          int requestedLevel = ( message.type == V_LIGHT ? led[id].savedLevel * atoi( message.data )  : atoi( message.data ) );
          if (requestedLevel > 0) {
             // Store as lastLevel
             led[id].savedLevel = requestedLevel;
          }
          // Make sure the new level is between 0 - 100     
          led[id].toLevel = (requestedLevel >= 0 ? min(requestedLevel, 100) : 0);
          
          Serial.print(F("Changing node: "));
          Serial.print( id );
          Serial.print(F(", from: "));
          Serial.print( led[id].currentLevel );
          Serial.print(F("%, to: ")); 
          Serial.print( requestedLevel );
          Serial.println(F("%")); 
          
          // Inform the gateway of the current DimmableLED's SwitchPower1 and LoadLevelStatus value...
          gw.send(led[id].lightMsg.set(requestedLevel > 0 ? 1 : 0));
          gw.send(led[id].dimmerMsg.set(requestedLevel) );
          
          // Ignore next OFF switch when switch is ON and controller switches LED to OFF state)
          led[id].ignoreNextSw = (led[id].toLevel == 0 && led[id].SwitchPin);
       }
    }
    
    
    void setLedLevel(byte id)
    {
       // Convert  level 0% - 100% to logathimic OR linear PWM range of 0 to 255 
       switch (led[id].dimcurve)
       {
          case DIM_LINEAR:
             // Normal linear curve
             analogWrite(led[id].LedPin, (int)(led[id].currentLevel * 2.5));
             break;
             
          case DIM_LINEAR_INV:
             // Inverted linear curve
             analogWrite(led[id].LedPin, 255 - (int)(led[id].currentLevel * 2.5));
             break;
             
          case DIM_LOGARITHMIC:
             // Normal logarithmic curve
             analogWrite(led[id].LedPin, fscale( 0, 100, 0, 255, led[id].currentLevel, -2));
             break;
    
          case DIM_LOGARITHMIC_INV:
             // Inverted logarithmic curve
             analogWrite(led[id].LedPin, 255 - fscale( 0, 100, 0, 255, led[id].currentLevel, -2));
             break;
             
          case DIM_CUSTOM:
            analogWrite(led[id].LedPin, 255 - led[id].currentLevel);
            break;
       }
    }
    
    /* fscale
     Floating Point Autoscale Function V0.1
     Paul Badger 2007
     Modified from code by Greg Shakar
     */
    float fscale( float originalMin, float originalMax, float newBegin, float newEnd, float inputValue, float curve) 
    {
      float   OriginalRange = 0;
      float   NewRange = 0;
      float   zeroRefCurVal = 0;
      float   normalizedCurVal = 0;
      float   rangedValue = 0;
      boolean invFlag = 0;
    
     // condition curve parameter
      // limit range
     if (curve > 10) curve = 10;
      if (curve < -10) curve = -10;
    
      curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output 
      curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function
    
      // Check for out of range inputValues
      if (inputValue < originalMin) {
        inputValue = originalMin;
      }
      if (inputValue > originalMax) {
        inputValue = originalMax;
      }
    
      // Zero Refference the values
      OriginalRange = originalMax - originalMin;
    
      if (newEnd > newBegin){ 
        NewRange = newEnd - newBegin;
      } else {
        NewRange = newBegin - newEnd; 
        invFlag = 1;
      }
    
      zeroRefCurVal = inputValue - originalMin;
      normalizedCurVal  =  zeroRefCurVal / OriginalRange;   // normalize to 0 - 1 float
    
      // Check for originalMin > originalMax  - the math for all other cases i.e. negative numbers seems to work out fine 
      if (originalMin > originalMax ) {
        return 0;
      }
    
      if (invFlag == 0) {
        rangedValue =  (pow(normalizedCurVal, curve) * NewRange) + newBegin;
      } else { // invert the ranges   
        rangedValue =  newBegin - (pow(normalizedCurVal, curve) * NewRange); 
      }
    
      return rangedValue;
    }
    
    My Project

  • 💬 MySensors @Eindhoven Maker Fair 2017 2-3 September
    BartEB BartE

    oke some pics..

    Queue to get in :-)
    0_1504359522596_IMG_7507.JPG

    The booth
    0_1504359562210_IMG_7509.JPG

    0_1504359650015_IMG_7510.JPG
    0_1504359679592_IMG_7512.JPG

    Announcements

  • Eprom erase Jumper
    BartEB BartE

    HI @kr0815 , what you can do is add a piece of code which checks for a jumper presence on start-up if so just erase your EEPROM.

    An input pin will become LOW when the jumper connects this pin to GND and will become HIGH when the jumper is removed (stable high by activating the interval pull up resistor)

    This jumper check code can be something like this:

        pinMode(CLEAR_EEPROM_JUMPER, INPUT);           // set pin to input
        digitalWrite(CLEAR_EEPROM_JUMPER, HIGH);       // turn on internal pull up resistors
        delay(50);                                     // Wait to stabilize the input pin
        // If jumper is placed and thus the input pin is LOW a clean EEPROM is required
        if (digitalRead(CLEAR_EEPROM_JUMPER) == LOW) {
            Serial.begin(BAUD_RATE);
            Serial.println(F("Clearing EEPROM. Please wait..."));
            for (int i=0;i<512;i++) {
                EEPROM.write(i, 0xff);
            }
            Serial.println(F("Clearing done. Continue with normal init..."));
        }
    

    A very simple plug in would look something like this:

    #include <MySensor.h>
    #include <SPI.h>
    #include <EEPROM.h>  
    
    // Clear EEPROM jumper: when placed this pin should be connected to GND 
    #define CLEAR_EEPROM_JUMPER  6
    
    MySensor gw;
    
    void setup()  
    { 
        pinMode(CLEAR_EEPROM_JUMPER, INPUT);           // set pin to input
        digitalWrite(CLEAR_EEPROM_JUMPER, HIGH);       // turn on internal pull up resistors
        delay(50);                                     // Wait to stabalize the input pin
        // If jumper is placed and thus the input pin is LOW a clean EEPROM is required
        if (digitalRead(CLEAR_EEPROM_JUMPER) == LOW) {
            Serial.begin(BAUD_RATE);
            Serial.println(F("Clearing EEPROM. Please wait..."));
            for (int i=0;i<512;i++) {
                EEPROM.write(i, 0xff);
            }
            Serial.println(F("Clearing done. Continue with normal init..."));
        }
        
        // place normal init here
        gw.begin();
    }
    
    void loop()      
    { 
        // Normal sensor stuff here
        gw.process();
    } 
    

    Just add this check to all your plugin code

    Feature Requests

  • Combined MySensor Gateway with 433Mhz transmitter (HomeEasy/Klik-Aan-Klik-uit)
    BartEB BartE

    In Holland there is an affordable popular brand for home automation called Klik-aan-klik-uit. These module are also sold in other countries as different brands like HomeEasy, more can be found here There are several (Aurdio) projects which decode en encode this RF protocol.

    To be able to controll my HomeAutomation network with an Klik-aan-klik-uit (AYCT-104) remote controller i fitted my MySensor gateway with an RF 433Mhz receiver (and transmitter). The transmitter / receiver are of type : this type
    I used the decoding software from (Randy Simons http://randysimons.nl/) which i modified so it can receive both the new
    style and on style protocol messages. Read this thread for more background information.

    How to connect:

    • Take a standard MySensors gateway
    • And addtional connect the both the RF receiver and tansmitter ground and Vcc to the GND and 5V pins of your Ardiuno
    • Connect the RF receiver data pin to Arduino pin 2 (interrupt 0 pin)
    • Connect the RF transmitter data pin to Arduino pin 7

    The recieved RC commands are sent to my Vera controller next to the MySensor messages. These commands have this format "rf;<number>" to be able to distinguish between MySensor commands and HomeEasy command i changed the MySensor lua-core file.

    Other changes to the MySensor controller core are:

    • Each received command is shown on the inclusion count area, the see which command can be used
    • A scene controller is added on first inclusion, which can be use to activate scnenes where the scene number is the recieved command.
    • The settings tab of primary MySensor device has a number of field where devices (Light) can be controlled directly on RF reception. The first column is the Vera device ID, the second column the RF code the switch on the device, the third column the RF code to switch off the column

    Rf-gateway.jpg

    The arduino code:
    mySensorsRemoteGW.zip

    The modified MySensors Vera code:
    Vera_RFGateway.zip

    My Project

  • My MySensors RGBW plug in has an init problem
    BartEB BartE

    I've created a RGB(W) led strip MySensors controller which interacts with the RGB color wheel plug in from MiOS Marketplace.

    The problem i have with it is that the the Arduino plug in does not initialize with MiOS plug in as "slave" plug in.
    It is made by vosmont and can be found here and i've added MyS support (see below)

    If someone can help me by fixing the init problem it would be really nice, but so far.

    i did make a small work around (tested with UI5 Vera 3):
    1 make sure both Arduino and the MiOS RGB controller module are installed.
    2 start MySensors inclusion
    3 reset MySensors RGB module
    4 Two devices will (should) be detected and appear. (a MYS controller and a RGB color wheel)
    5 Remember the RBV modules 'altid' (wrench-icon, Advanced-tab), this looks like "6;1"
    6 Remember the Device ID of the MYS Arduindo master plug in
    7 Now delete the RGB color wheel (trash can icon)
    8 Create a new device (APPS -> Develop Apps -> Create device)
    9 Description field: <give a usefull name> like RGB strip
    Upnp Device Filename field : D_RGBController1.xml
    No parent device !!!
    10 Press the Create device button
    11 Press the reload button, wait for the system becomes stable
    12 Change the setting of te RGB controller (wrench-icon, Settings-tab)
    13 Select: MySensors RGBW
    Arduino plugin Id: <enter ID remembered and step 6>
    RGB Node altid: <enter ID remembered and step 5>
    14 Press save button
    15 Now the color wheel can be used

    rgbcolor.png

    To be able to control MyS RGB modules the following coded is added to L_RGBController1.lua after line 1004 (before Color transition management)

    -- MySensor RGB(W)
    RGBDeviceTypes["MYS-RGBW"] = {
    	getParameters = function (lul_device)
    		return {
    			name = "MySensors RGBW",
    			settings = {
    				{ variable = "ArduinoId", name = "Arduino plugin Id", type = "string" },
    				{ variable = "RadioId",   name = "RGB Node altid",    type = "string" }
    			}
    		}
    	end,
    
    	getColorChannelNames = function (lul_device)
    		return {"red", "green", "blue", "warmWhite"}
    	end,
    
    	getAnimationProgramNames = function()
    		return {
    			"Rainbow slow",
    			"Rainbow medium",
    			"Rainbow fast",
    			"Random slow colors",
    			"Random medium colors",
    			"Random fast colors",
    			"RGB fade slow colors",
    			"RGB fade medium colors",
    			"RGB fade fast colors",
    			"Multicolor fade slow colors",
    			"Multicolor fade medium colors",
    			"Multicolor fade fast colors",
    			"Current color flash slow colors",
    			"Current color flash medium colors",
    			"Current color flash fast colors",
    		}
    	end,
    
    	init = function (lul_device)
    		debug("MYS-RGBW.init", "Init for device #" .. tostring(lul_device))
    		pluginParams.rgbArduinoId = tonumber(getVariableOrInit(lul_device, SID.RGB_CONTROLLER, "ArduinoId", ""))
    		pluginParams.rgbRadioId = getVariableOrInit(lul_device, SID.RGB_CONTROLLER, "RadioId", "")
    		return true
    	end,
    
    	setStatus = function (lul_device, newTargetValue)
    		debug("MYS.setStatus", "Set status '" .. tostring(newTargetValue) .. "' for device #" .. tostring(lul_device))
    		if (tostring(newTargetValue) == "1") then
    			debug("MYS.setStatus", "Switches on ")
    			local formerColor = luup.variable_get(SID.RGB_CONTROLLER, "Color", lul_device):gsub("#","")
    			luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "RGBW", value = formerColor, radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    			luup.variable_set(SID.SWITCH, "Status", "1", lul_device)
    		else
    			debug("MYS.setStatus", "Switches off")
                            luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "LIGHT", value = "0", radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    			luup.variable_set(SID.SWITCH, "Status", "0", lul_device)
    		end
    
    	end,
    
    	setColor = function (lul_device, color)
    		colorString = tostring(color)
    		debug("MYS.setColor", "Set RGBW color #" .. colorString .. " for device #" .. tostring(lul_device))
                    luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "RGBW", value = colorString, radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    	end,
    
    	startAnimationProgram = function (lul_device, programId, programName)
    		if ((programName ~= nil) and (programName ~= "")) then
    			--debug("MYS.startAnimationProgram", "Start animation program '" .. programName .. "'")
    			mode = 0
    			if string.match(programName, "Random") then
    				mode = 0x01
    			end
    			if string.match(programName, "RGB fade") then
    				mode = 0x02	
    			end
    			if string.match(programName, "Multicolor fade") then
    				mode = 0x03	
    			end
    			if string.match(programName, "Current color flash") then
    				mode = 0x04
    			end
    			if string.match(programName, "slow") then
    				mode = 0x10 + mode
    			end
    			if string.match(programName, "medium") then
    				mode = 0x20 + mode
    			end
    			if string.match(programName, "fast") then
    				mode = 0x30 + mode
    			end
    			debug("MYS.startAnimationProgram", "Start animation program '" .. programName .. "' " .. tostring(mode))
    			luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "VAR_1", value = tostring(mode), radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    		else
    			debug("MYS.startAnimationProgram", "Stop animation program")
    			luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "VAR_1", value = "00", radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    			setColorTarget(lul_device, "")
    		end
    	end
    }
    
    

    And this is the MyS RGB color strip plugin

    // RBG led strip plug in.
    // by Bart Eversdijk (c) 2015.
    #include <MySensor.h>
    #include <SPI.h>
    
    // If not defined RGB is used otherwise RGBW
    #define RGBW 1
    
    #define RED   3  // Arduino PWM pin for Red
    #define GREEN 5 // Arduino PWM pin for Green
    #define BLUE  6  // Arduino PWM pin for Blue
    #define WHITE 7  // Arduino PWM pin for White (ilumination)
    
    
    enum ANIMATIOMODES {RAINBOW=0,RANDONMIZE,FADERGB,FADEMULTICOLR,FLASHCOLOR,LAST_PROGRAM};
    
    byte FADE_RGB_COLOR_MODES[]   = {0b0010,0b0011,0b0100,0b0101,0b1000,0b1001, 0xFF};
    byte FADE_MULTI_COLOR_MODES[] = {0b0010,0b0011,0b0110,0b0111,0b0100,0b0101,0b1100,0b1101,0b1000,0b1001,0b1010,0b1011,0xFF};
    
    #ifdef RGBW
      #define NODENAME "RGBW node"
      #define NODETYPE S_RGBW_LIGHT
      byte rgb_pins[] = {RED, GREEN, BLUE, WHITE};
    #else
      #define NODENAME "RGB node"
      #define NODETYPE S_RGB_LIGHT
      byte rgb_pins[]   = {RED, GREEN, BLUE};
    #endif
    
    byte ledOffValues[] = {0, 0, 0, 0};
    byte rgb_values[]   = {0, 0, 0, 0};
    
    #define NODEID 2  // Assigning the node id
    #define SUBID  1  // sensor number needed in the custom devices set up
    
    void incomingMessage(const MyMessage &message);
    #define NUM_OF_COLORS sizeof(rgb_pins)
    int speedtable[] = { 0, 100, 50, 2 };
    #define NUM_OF_SPEEDS sizeof(speedtable)
    
    struct
    {
       byte values[4];
       byte speedsetting;
       byte mode;
       bool status;
    } rgb = { {0,0,0,0}, 0, RAINBOW, false};
    
    bool    flashOn      = true;
    int     syscounter   = 0;
    int     lastUpdate   = 0;
    bool    newSetting   = false;
    
    MySensor gw; 
    
    void setup()
    {
        // Initialize library and add callback for incoming messages
        gw.begin(incomingMessage, AUTO /*NODEID*/);
        // Send the sketch version information to the gateway and Controller
        gw.sendSketchInfo(NODENAME, "1.1");
        // Register the sensor to gw
        gw.present(SUBID, NODETYPE);
    
        // Set the rgb(w) pins in output mode
        for (int i = 0; i < NUM_OF_COLORS; i++) {
            pinMode(rgb_pins[i], OUTPUT);
        }
        recallEeprom();
        setLedValues(rgb.values, true);
        
        Serial.println("Init done");
    }
    
    void loop()
    {
        // Alway process incoming messages whenever possible
        gw.process();
    
        if (speedtable[rgb.speedsetting] > 0) {
            if ((syscounter % speedtable[rgb.speedsetting]) == 0) {
               switch (rgb.mode)
               {
                   case RAINBOW:
                     animateRainbowStep();
                     break;
    
                   case FADERGB:
                     animateFadeColorStep(FADE_RGB_COLOR_MODES);
                     break;
    
                   case FADEMULTICOLR:
                     animateFadeColorStep(FADE_MULTI_COLOR_MODES);
                     break;
                 
                  case FLASHCOLOR:
                     setLedValues(flashOn ? ledOffValues : rgb.values, false);
                     flashOn = !flashOn;
                     break;
                                 
                   case RANDONMIZE:
                     long number = random(0, 0xFFFFFF);
                     rgb_values[0] = number >> 16 & 0xFF ;
                     rgb_values[1] = number >> 8 & 0xFF ;
                     rgb_values[2] = number & 0xFF;
                     setLedValues(rgb_values, false);
                     break;
               }
            }
            delay(rgb.mode == RANDONMIZE || rgb.mode == FLASHCOLOR ? 50 : 1);
        }
        if (newSetting && (lastUpdate + 30000 < syscounter)) {   
            // Wait for a couple of seconds be fore actual storing the current setting in to EEPROM
            // This will save the EEPROM's life time, when playing around with colors
            Serial.println(" Store EERPOM");
            storeEeprom();
            newSetting = false;
        } 
        delay(1);
        syscounter++;
    }
    
    void animateRainbowStep()
    {    
        static float counter = 0;
        float        pi      = 3.14159; 
        counter++;
        rgb_values[0] = map(sin(counter/100         )*1000,-1000,1000,0,255);
        rgb_values[1] = map(sin(counter/100 + pi*2/3)*1000,-1000,1000,0,255);
        rgb_values[2] = map(sin(counter/100 + pi*4/3)*1000,-1000,1000,0,255);
        setLedValues(rgb_values, false);
    }
    
    void animateFadeColorStep(byte *modes)
    {    
        static int modecnt = 0;
        if (updateRGBValues(modes[modecnt] >> 1, (modes[modecnt] & 0x1) == 0x1)) { 
            modecnt = (modes[modecnt+1] == 0xFF ? 0 : modecnt + 1);
        }
    }
    
    bool updateRGBValues(byte mode, bool down)
    {
        bool endReached = false;
        for (byte i = 0; i < 3; i++) {
            if (((mode >> i) & 0x1) == 0x1) {
               rgb_values[i] += (down ? -1 : 1);
               endReached    |= (down && (rgb_values[i] == 0x00)) || (!down && (rgb_values[i] == 0xFF));
            }
        }
        setLedValues(rgb_values, false);
        return endReached;
    }
    
    
    void incomingMessage(const MyMessage &message) {
        if (message.type == V_RGB || message.type == V_RGBW) {
    	// starting to process the hex code
            String hexstring = message.getString();
    
            long number;
    #ifdef RGBW
            char white[3];
            white[0] = hexstring[6];
            white[1] = hexstring[7];
            white[2] = 0;
            number = (long) strtol( &white[0], NULL, 16);
            rgb.values[3] = number & 0xFF;
    #endif        
            hexstring[6] = 0;
            number = (long) strtol( &hexstring[0], NULL, 16);
            rgb.values[0] = number >> 16 & 0xFF ;
            rgb.values[1] = number >> 8 & 0xFF ;
            rgb.values[2] = number & 0xFF;
            
            rgb.speedsetting = 0;
            setLedValues(rgb.values, true);
            lastUpdate = syscounter;
            newSetting = true;
        }
        
        if (message.type == V_STATUS) {
          if (message.getBool()) {
              Serial.println("ON: Switch to last known color values");
              setLedValues(rgb_values, true);
          } else {
             Serial.println("OFF: Switch colors off");
             setLedValues(ledOffValues, true);
          }
          rgb.speedsetting = 0;
          rgb.status = message.getBool();
          lastUpdate = syscounter;
          newSetting = true;
        }
        
        if (message.type == V_VAR1) {
           Serial.println("Set speed and program value"); 
           byte newsetting = message.getByte();
           rgb.speedsetting = (newsetting >> 4) & 0x0F;
           byte newmode = newsetting & 0x0F;
    
           if (newmode != rgb.mode) {
               for (byte i = 0; i < NUM_OF_COLORS; i++) {
                   rgb_values[i] = 0;
               }
               rgb.mode = newmode;
           }
           if (rgb.speedsetting > 0) {
             rgb.status = true;
           }
           lastUpdate = syscounter;
           newSetting = true;
          
           Serial.print("Data 0x");
           Serial.print(newsetting, HEX);
           Serial.print(" speed:");
           Serial.print(rgb.speedsetting);
           Serial.print(" mode:");
           Serial.println(rgb.mode);
        }
    }
    
    void setLedValues(byte *rgb, bool show)
    {
        for (int i = 0; i < NUM_OF_COLORS; i++) {
            analogWrite(rgb_pins[i], rgb[i]);
        }  
     
        if (show) {
          Serial.print("Red: " );
          Serial.print(rgb[0], HEX);
          Serial.print("  Green: " );
          Serial.print(rgb[1], HEX);
          Serial.print("  Blue: " );
          Serial.print(rgb[2], HEX);
     #ifdef RGBW
            Serial.print("  White is " );
            Serial.print(rgb[3], HEX);
     #endif
          Serial.println();
        }
    }
    
    void storeEeprom()
    {
        byte address = 0;
        byte *p = (byte *)&(rgb);
        for (byte i = 0; i < sizeof(rgb); i++) {
           gw.saveState(address++, p[i]);
        }
    }
    
    void recallEeprom()
    {
        byte address = 0;
        byte *p = (byte *)&(rgb);
        for (byte i = 0; i < sizeof(rgb); i++) {
           p[i] = gw.loadState(address++);
        }
    }
    

    Adding a reference to the MiOS forum about this plug in:
    http://forum.micasaverde.com/index.php/topic,32613.0.html

    Vera

  • Looking for LED 7-segment cover plate
    BartEB BartE

    @mfalkvidd thx for the tip i use the a eds bag with a semi transparent red foil

    @niccodemi the bezel shop you suggested does not ship bezel large enough and for the box i did use this box http://www.ebay.com/itm/201180688204

    This is the end result of my MySensor based clock radio
    (Unfortunately finished to late for the contest)

    0_1459280782340_clockradio.png

    General Discussion

  • How to find out if message was successfully delivered?
    BartEB BartE

    Hi @arraWX

    When you set the ack boolean to true while sending a message the gateway will return the message with the ACK bit set.

    So you have to handle this message with code like this

    setup ()
    {
       ... do stuff
    
       gw.begin(incomingMessage, AUTO, true);
    
      ... do more stuff
    }
    
    
    void incomingMessage(const MyMessage &message) {
      if (message.isAck()) {
          Serial.println("This is an ack from gateway");
          if (message.sensor == YOUR_KWH_SENSOR_ID) {
                 toggleLed();
          }
      }
    }
    
    Development

  • IR Record and Playback module
    BartEB BartE

    Adding some Vera screenshots
    upload-89cc6ee3-ce20-44d0-a53c-79305c023d6c
    upload-1d15bc07-5e6d-46ce-87df-cae8adbb5159

    My Project

  • Looking for LED 7-segment cover plate
    BartEB BartE

    @mfalkvidd said:

    Very cool, I love the big red button. Where did you find it?

    I did found it i my box with electronic parts :) i once got a number of these switches of a friend of mine.

    BTW the clock radio has a led strip connected emulating a Wakeup light, shows the out door temperature on request, since it is MYS connected the alarm can be triggered via my HA controller and the most important feature it switches on my coffee machine upon wake up time.

    I will post a detailed description in the comming days,

    General Discussion

  • Multi RF433 Switches Node
    BartEB BartE

    @OliverDog Sorry my (beginners) fault the array's are counting from 0 to 8 and the sensor numbers from 1 till 9

    This should work:

    #define MY_DEBUG 
    #define MY_RADIO_NRF24
    #include <SPI.h>
    #include <MySensors.h>
    #include <livolo.h>
    
    #define CHILD_ID 1
    
    Livolo livolo(8);
    
    bool initValSent[]   = {false, false, false, false, false, false, false, false, false};
    int livoloSensCode[] = {120, 70, 1, 2, 3, 4, 5, 6, 7}; // Set correct livolo codes here
    
    MyMessage msg(CHILD_ID, V_STATUS);
    
    void setup() {
      
    }
    
    void presentation()  {   
      sendSketchInfo("Livolo", "1.0");
      for (int sensor=1; sensor<=9; sensor++) { 
        // (presents each sensor, S_BINARY type)
        present(sensor, S_BINARY);
      }
    }
    
    void loop() {
      for (int sensor=1; sensor<=9; sensor++) { 
        if (!initValSent[sensor-1]) {
          msg.setSensor(sensor);
          send(msg.set(false)); 
          Serial.print("Requesting initial value for sensor: ");
          Serial.println(sensor);
          request(sensor, V_STATUS);
          wait(2000, C_SET, V_STATUS);
        }
      }
    }
    
    void receive(const MyMessage &message) {
      switch (message.type) {
        case V_LIGHT:
           if (!initValSent[message.sensor-1]) {
              Serial.print("Receiving initial value from controller for sensor: ");
              initValSent[message.sensor-1] = true;
           } else {
              Serial.print("Receiving new value for sensor: ");
              livolo.sendButton(6400, livoloSensCode[message.sensor-1]); // coded for each sensor 
           }  
           Serial.println(message.sensor);  
           break;
      }
    }
    
    
    Development

  • RelayWithButtonActuator (6 channel)
    BartEB BartE

    @vampircik i see that with some help from korttoma you manage to find the correct pins setting. @korttoma thx for your help.
    korttoma is right about the pin 11, i should have added this link with more background info

    The sketch is designed for push buttons and not for toggle switches and will respond when releasing a button press.
    In your movie i see that switching on and off via MySensors (mouse clicks) works but the button presses not (always) only switching off.

    I found an error in my sketch on line 131

    // Store new switch value
    switches[id].currentStatus = (switchValue ? 0 : 1);
    
    

    the correct line should be

    // Toggle relay status
    switches[id].currentStatus = (switches[id].currentStatus ? 0 : 1);
    
    

    note: I've updated the sketch in the original post as well

    My Project

  • IR Record and Playback module
    BartEB BartE

    This sketch has been updated to MySensors 2.0 and added example library https://www.mysensors.org/build/ir

    My Project

  • Humidity+Relay with button node
    BartEB BartE

    @thecko said:

    The problem is in the "state" variable, you use the state variable to toggle but never save changed value

    so change this code snippet

       debouncer.update();
       int value = debouncer.read();
       if (value != oldValue && value==0) {
           gw.send(msg.set(state?false:true), true); // Send new state and request ack back
       }
       oldValue = value;
    

    to

       debouncer.update();
       int value = debouncer.read();
       if (value != oldValue && value==0) {
           gw.send(msg.set(gw.loadState(1)?false:true), true); // Send new state and request ack back
       }
       oldValue = value;
    

    and remove the state variable at all.

    Even better also remove the "oldValue" and "value" variables (the bounce2 library keeps the old value for you and debouncer.update(); returns true when the value was toggled )

       if (debouncer.update() && debouncer.read() == 0) {
           gw.send(msg.set(gw.loadState(1)?false:true), true); // Send new state and request ack back
       }
    
    Development

  • How can I control a servo with Mysensors? Automatic cat feeder
    BartEB BartE

    @chuckconnors funny project!

    Arduino has a servo library take a look here

    Your sketch can be the relay sketch like mfalkvidd says (use pin 6 i.s.o. 9 because 9 is used by the radio)

    Servo myservo;  // create servo object to control a servo
    
    void setup()  {
       ...
       myservo.attach(6);  // attaches the servo on pin 6 to the servo object
       ...
    }
    

    And in the message handler

    if (message.getBool()){
        // Open door 90 degrees
        myservo.write(90);
    } else {
        // Close door 0 degrees
        myservo.write(0);
    }
    
    General Discussion

  • IR Record and Playback module
    BartEB BartE

    @pete1450 i've added your feature request in version 1.1 of my plug in sketch You can add up to 240 RC HEX commands, from line 111, if you use an Arduino with enough memory. 40 will do with an Arduino UNO

    /**
     * The MySensors Arduino library handles the wireless radio link and protocol
     * between your home built sensors/actuators and HA controller of choice.
     * The sensors forms a self healing radio network with optional repeaters. Each
     * repeater and gateway builds a routing tables in EEPROM which keeps track of the
     * network topology allowing messages to be routed to nodes.
     *
     * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
     * Copyright (C) 2013-2015 Sensnology AB
     * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
     *
     * Documentation: http://www.mysensors.org
     * Support Forum: http://forum.mysensors.org
     *
     * 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.
     *
     *******************************
     *
     * REVISION HISTORY
     * Version 1.0 - Changed for MySensors useage by Bart Eversdijk
     * Version 1.1 - Added option to record manual presets up to 240
     * 
     * DESCRIPTION
     *
     * IRrecord: record and play back IR signals as a minimal 
     * An IR detector/demodulator must be connected to the input RECV_PIN.
     * An IR LED must be connected to the output PWM pin 3.
     * A button must be connected to the input BUTTON_PIN; this is the
     * send button.
     * A visible LED can be connected to STATUS_PIN to provide status.
     *
     * The logic is:
     * If the button is pressed, send the IR code.
     * If an IR code is received, record it.
     *
     * Version 0.11 September, 2009
     * Copyright 2009 Ken Shirriff
     * http://arcfn.com
     */
    
    #include <MySensor.h>
    #include <SPI.h>
    #include <IRremote.h>  // https://github.com/z3t0/Arduino-IRremote/releases
    
    int RECV_PIN     = 8;
    #define NODEID   5
    //#define NODEID   NULL
    #define CHILD_1  2  // childId
    
    #define MY_RAWBUF  50
    const char * TYPE2STRING[] = {
    		"UNKONWN",
    		"RC5",
    		"RC6",
    		"NEC",
    		"Sony",
    		"Panasonic",
    		"JVC",
    		"SAMSUNG",
    		"Whynter",
    		"AIWA RC T501",
    		"LG",
    		"Sanyo",
    		"Mitsubishi",
    		"Dish",
    		"Sharp",
    		"Denon"
    };
    #define Type2String(x)   TYPE2STRING[x < 0 ? 0 : x]
    #define AddrTxt          F(" addres: 0x")
    #define ValueTxt         F(" value: 0x")
    #define NATxt            F(" - not implemented/found")
    
    // Raw or unknown codes requires an Arduino with a larger memory like a MEGA and some changes to store in EEPROM (now max 255 bytes)
    // #define IR_SUPPORT_UNKNOWN_CODES
    typedef union
    {
      struct
      {
        decode_type_t type;            // The type of code
        unsigned long value;           // The data bits if type is not raw
        int           len;             // The length of the code in bits
        unsigned int  address;         // Used by Panasonic & Sharp [16-bits]
      } code;
    #ifdef IR_SUPPORT_UNKNOWN_CODES      
      struct
      {
        decode_type_t type;             // The type of code
        unsigned int  codes[MY_RAWBUF];
        byte          count;           // The number of interval samples
      } raw;
    #endif
    } IRCode;
    
    #define           MAX_STORED_IR_CODES     10
    IRCode            StoredIRCodes[MAX_STORED_IR_CODES];
    
    IRrecv            irrecv(RECV_PIN);
    IRsend            irsend;
    decode_results    ircode;
    
    #define           NO_PROG_MODE 0xFF
    byte              progModeId       = NO_PROG_MODE;
    
    // Manual Preset IR values
    // VERA call: luup.call_action("urn:schemas-arduino-cc:serviceId:ArduinoIr1", "SendIrCode", {Index=15}, <device number>)
    // One can add up to 240 preset codes (if your memory lasts) to see to correct data connect the Arduino with this plug in and
    // look at the serial monitor while pressing the desired RC button
    IRCode PresetIRCodes[] = {
        { { RC5, 0x01,       12, 0 } },  // 11 - RC5 key "1" 
        { { RC5, 0x02,       12, 0 } },  // 12 - RC5 key "2"
        { { RC5, 0x03,       12, 0 } },  // 13 - RC5 key "3"
        { { NEC, 0xFF30CF,   32, 0 } },  // 14 - NEC key "1"
        { { NEC, 0xFF18E7,   32, 0 } },  // 15 - NEC key "2"
        { { NEC, 0xFF7A85,   32, 0 } },  // 16 - NEC key "3"
        { { NEC, 0xFF10EF,   32, 0 } },  // 17 - NEC key "4"
        { { NEC, 0xFF38C7,   32, 0 } },  // 18 - NEC key "5"
        { { RC6, 0x800F2401, 36, 0 } },  // 19 - RC6 key "1" MicroSoft Mulitmedia RC
        { { RC6, 0x800F2402, 36, 0 } }   // 20 - RC6 key "2" MicroSoft Mulitmedia RC
    };
    #define MAX_PRESET_IR_CODES  (sizeof(PresetIRCodes)/sizeof(IRCode))
    #define MAX_IR_CODES (MAX_STORED_IR_CODES + MAX_PRESET_IR_CODES)
    
    MySensor gw;
    MyMessage msgIrReceive(CHILD_1, V_IR_RECEIVE);
    MyMessage msgIrRecord(CHILD_1, V_IR_RECORD); 
    
    void setup()  
    {  
      gw.begin(incomingMessage, NODEID);
    
      // Send the sketch version information to the gateway and Controller
      gw.sendSketchInfo("IR Rec/Playback", "1.1");
    
      // Register a sensors to gw. Use binary light for test purposes.
      gw.present(CHILD_1, S_IR);
    
      // Tell MYS Controller that we're NOT recording
      gw.send(msgIrRecord.set(0));
      
      Serial.println(F("Recall EEPROM settings"));
      recallEeprom(sizeof(StoredIRCodes), (byte *)&StoredIRCodes);
    
      // Start the ir receiver
      irrecv.enableIRIn(); 
      
      Serial.println(F("Init done..."));
    }
    
    void loop() 
    {
      gw.process();
      
      if (irrecv.decode(&ircode)) {
          dump(&ircode);
          if (progModeId != NO_PROG_MODE) {
             // If we are in PROG mode (Recording) store the new IR code and end PROG mode
             if (storeRCCode(progModeId)) {
                Serial.print(F("Stored "));
              
                // If sucessfull RC decode and storage --> also update the EEPROM
                storeEeprom(sizeof(StoredIRCodes), (byte *)&StoredIRCodes);
                progModeId = NO_PROG_MODE;
               
                // Tell MYS Controller that we're done recording
                gw.send(msgIrRecord.set(0));
             }
          } else {
             // If we are in Playback mode just tell the MYS Controll we did receive an IR code
             if (ircode.decode_type != UNKNOWN) {
                 if (ircode.value != REPEAT) {
                   // Look if we found a stored preset 0 => not found
                   byte num = lookUpPresetCode(&ircode);
                   if (num) {
                       // Send IR decode result to the MYS Controller
                       Serial.print(F("Found code for preset #"));
                       Serial.println(num);
                       gw.send(msgIrReceive.set(num));
                   }
                 }
             }
        }
        // Wait a while before receive next IR-code
        delay(500);
        
        // Start receiving again
        irrecv.resume();
      }
    }
    
    void incomingMessage(const MyMessage &message) {
        //Serial.print(F("New message: "));
        //Serial.println(message.type);
       
       if (message.type == V_IR_RECORD) { // IR_RECORD V_VAR1
          // Get IR record requets for index : paramvalue
          progModeId = message.getByte() % MAX_STORED_IR_CODES;
          
          // Tell MYS Controller that we're now in recording mode
          gw.send(msgIrRecord.set(1));
          
          Serial.print(F("Record new IR for: "));
          Serial.println(progModeId);
       }
      
       if (message.type == V_IR_SEND) {
          // Send an IR code from offset: paramvalue - no check for legal value
          Serial.print(F("Send IR preset: "));
          byte code = message.getByte() % MAX_IR_CODES;
          if (code == 0) {
            code = MAX_IR_CODES;
          }
          Serial.print(code);
          sendRCCode(code);
       }
    
       // Start receiving ir again...
       irrecv.enableIRIn(); 
    }
    
    
    byte lookUpPresetCode (decode_results *ircode)
    {
        // Get rit of the RC5/6 toggle bit when looking up
        if (ircode->decode_type == RC5)  {
            ircode->value = ircode->value & 0x7FF;
        }
        if (ircode->decode_type == RC6)  {
            ircode->value = ircode->value & 0xFFFF7FFF;
        }
        for (byte index = 0; index < MAX_STORED_IR_CODES; index++) {
          if ( StoredIRCodes[index].code.type  == ircode->decode_type &&
               StoredIRCodes[index].code.value == ircode->value       &&
               StoredIRCodes[index].code.len   == ircode->bits)      {
              // The preset number starts with 1 so the last is stored as 0 -> fix this when looking up the correct index
              return (index == 0) ? MAX_STORED_IR_CODES : index;
          }  
        }
        
        for (byte index = 0; index < MAX_PRESET_IR_CODES; index++) {
          if ( PresetIRCodes[index].code.type  == ircode->decode_type &&
               PresetIRCodes[index].code.value == ircode->value       &&
               PresetIRCodes[index].code.len   == ircode->bits)      {
              // The preset number starts with 1 so the last is stored as 0 -> fix this when looking up the correct index
              return ((index == 0) ? MAX_PRESET_IR_CODES : index) + MAX_STORED_IR_CODES;
          }  
        }
        // not found so return 0
        return 0;
    }
        
    // Stores the code for later playback
    bool storeRCCode(byte index) {
    
      if (ircode.decode_type == UNKNOWN) {
    #ifdef IR_SUPPORT_UNKNOWN_CODES  
          Serial.println(F("Received unknown code, saving as raw"));
          // To store raw codes:
          // Drop first value (gap)
          // As of v1.3 of IRLib global values are already in microseconds rather than ticks
          // They have also been adjusted for overreporting/underreporting of marks and spaces
          byte rawCount = min(ircode.rawlen - 1, MY_RAWBUF);
          for (int i = 1; i <= rawCount; i++) {
            StoredIRCodes[index].raw.codes[i - 1] = ircode.rawbuf[i]; // Drop the first value
          };
          return true;
    #else 
          return false;
        }
    #endif
    
       if (ircode.value == REPEAT) {
           // Don't record a NEC repeat value as that's useless.
           Serial.println(F("repeat; ignoring."));
           return false;
       }
       // Get rit of the toggle bit when storing RC5/6 
       if (ircode.decode_type == RC5)  {
            ircode.value = ircode.value & 0x07FF;
       }
       if (ircode.decode_type == RC6)  {
            ircode.value = ircode.value & 0xFFFF7FFF;
       }
    
       StoredIRCodes[index].code.type      = ircode.decode_type;
       StoredIRCodes[index].code.value     = ircode.value;
       StoredIRCodes[index].code.address   = ircode.address;      // Used by Panasonic & Sharp [16-bits]
       StoredIRCodes[index].code.len       = ircode.bits;
       Serial.print(F(" value: 0x"));
       Serial.println(ircode.value, HEX);
       return true;
    }
    
    void sendRCCode(byte index) {
       IRCode *pIr = ((index <= MAX_STORED_IR_CODES) ? &StoredIRCodes[index % MAX_STORED_IR_CODES] : &PresetIRCodes[index - MAX_STORED_IR_CODES - 1]);
       
    #ifdef IR_SUPPORT_UNKNOWN_CODES  
       if(pIr->code.type == UNKNOWN) {
          // Assume 38 KHz
          irsend.sendRaw(pIr->raw.codes, pIr->raw.count, 38);
          Serial.println(F("Sent raw"));
          return;
       }
    #endif
    
       Serial.print(F(" - sent "));
       Serial.print(Type2String(pIr->code.type));
       if (pIr->code.type == RC5) {
           // For RC5 and RC6 there is a toggle bit for each succesor IR code sent alway toggle this bit, needs to repeat the command 3 times with 100 mS pause
           pIr->code.value ^= 0x0800;
           for (byte i=0; i < 3; i++) {
             if (i > 0) { delay(100); } 
             irsend.sendRC5(pIr->code.value, pIr->code.len);
           }
        } 
        else if (pIr->code.type == RC6) {
           // For RC5 and RC6 there is a toggle bit for each succesor IR code sent alway toggle this bit, needs to repeat the command 3 times with 100 mS pause
           if (pIr->code.len == 20) {
                  pIr->code.value ^= 0x10000;
           }
           for (byte i=0; i < 3; i++) {
             if (i > 0) { delay(100); } 
             irsend.sendRC6(pIr->code.value, pIr->code.len);
           }
       }
       else if (pIr->code.type == NEC) {
           irsend.sendNEC(pIr->code.value, pIr->code.len);
        } 
        else if (pIr->code.type == SONY) {
           irsend.sendSony(pIr->code.value, pIr->code.len);
        } 
        else if (pIr->code.type == PANASONIC) {
           irsend.sendPanasonic(pIr->code.address, pIr->code.value);
           Serial.print(AddrTxt);
           Serial.println(pIr->code.address, HEX);
        }
        else if (pIr->code.type == JVC) {
           irsend.sendJVC(pIr->code.value, pIr->code.len, false);
        }
        else if (pIr->code.type == SAMSUNG) {
           irsend.sendSAMSUNG(pIr->code.value, pIr->code.len);
        }
        else if (pIr->code.type == WHYNTER) {
           irsend.sendWhynter(pIr->code.value, pIr->code.len);
        }
        else if (pIr->code.type == AIWA_RC_T501) {
           irsend.sendAiwaRCT501(pIr->code.value);
        }
        else if (pIr->code.type == LG || pIr->code.type == SANYO || pIr->code.type == MITSUBISHI) {
           Serial.println(NATxt);
           return;
        }
        else if (pIr->code.type == DISH) {
          // need to repeat the command 4 times with 100 mS pause
          for (byte i=0; i < 4; i++) {
             if (i > 0) { delay(100); } 
               irsend.sendDISH(pIr->code.value, pIr->code.len);
          }
        }
        else if (pIr->code.type == SHARP) {
           irsend.sendSharp(pIr->code.address, pIr->code.value);
           Serial.print(AddrTxt);
           Serial.println(pIr->code.address, HEX);
        }
        else if (pIr->code.type == DENON) {
           irsend.sendDenon(pIr->code.value, pIr->code.len);
        }
        else {
          // No valid IR type, found it does not make sense to broadcast
          Serial.println(NATxt);
          return; 
        }
        Serial.print(" ");
        Serial.println(pIr->code.value, HEX);
    }    
    
    // Dumps out the decode_results structure.
    void dump(decode_results *results) {
        int count = results->rawlen;
        
        Serial.print(F("Received : "));
        Serial.print(results->decode_type, DEC);
        Serial.print(F(" "));
        Serial.print(Type2String(results->decode_type));
      
        if (results->decode_type == PANASONIC) {	
          Serial.print(AddrTxt);
          Serial.print(results->address,HEX);
          Serial.print(ValueTxt);
        }
        Serial.print(F(" "));
        Serial.print(results->value, HEX);
        Serial.print(F(" ("));
        Serial.print(results->bits, DEC);
        Serial.println(F(" bits)"));
      
        if (results->decode_type == UNKNOWN) {
          Serial.print(F("Raw ("));
          Serial.print(count, DEC);
          Serial.print(F("): "));
      
          for (int i = 0; i < count; i++) {
            if ((i % 2) == 1) {
              Serial.print(results->rawbuf[i]*USECPERTICK, DEC);
            } else {
              Serial.print(-(int)results->rawbuf[i]*USECPERTICK, DEC);
            }
            Serial.print(" ");
          }
          Serial.println("");
        }
    }
    
    // Store IR record struct in EEPROM   
    void storeEeprom(byte len, byte *buf)
    {
        gw.saveState(0, len);
        for (byte i = 1; i < min(len, 100); i++, buf++) {
           gw.saveState(i, *buf);
        }
    }
    
    void recallEeprom(byte len, byte *buf)
    {
        if (gw.loadState(0) != len) {
           Serial.print(F("Corrupt EEPROM preset values and Clear EEPROM"));
           for (byte i = 1; i < min(len, 100); i++, buf++) {
               *buf = 0;
               storeEeprom(len, buf);
           }
           return;
        }
        for (byte i = 1; i < min(len, 100); i++, buf++) {
           *buf = gw.loadState(i);
        }
    }
    

    I will update the Vera code as well in git
    With Vera this luup code controlls the extra presets:
    luup.call_action("urn:schemas-arduino-cc:serviceId:ArduinoIr1", "SendIrCode", {Index=15}, <device number>)

    Vera UI5 Screen shot:
    upload-0bb76cf0-b984-4b30-b9b3-cfb03ffe9648

    My Project

  • NeoPixel( S_RGB_LIGHT) with Vera 3 (UI 5) - SOLVED
    BartEB BartE

    @ar91 I had simulair problems with the RGB-plugin from vosmont. See this forum post .

    Vera has an issue with child-parent controlled plugins. The init procedure is called twice in specific cases, and in this case your MSY plugin in not able to open the serial port twice (which is logical). The solution is delete the MYS generated plug-in tile, in your screen capture the left RGB tile. (which is a child plug-in) and use a manual created plug-in tile (with Vera create device) which has no child-parent relation.

    Let this manual created plugin control your RGB device by configuring the correct MYS- ID and device node ID. By following this procedure:

    • Change the setting of te RGB controller (wrench-icon, Settings-tab)
    • Select: MySensors RGBW
    • Arduino plugin Id: <enter MyS GW device ID >
    • RGB Node altid: <enter node ID in your example 62>
    • Press save button
    Troubleshooting

  • UI5 Mysensors library 2.0 gateway issues
    BartEB BartE

    @hek andf @martinhjelmare i did some debugging this evening and came to the following findings

    My previous comment on start-up behavior was based on a stand alone GW (so disconnected from the Vera Controller)
    And i found out that the Library version is NOT pushed by the gateway but requested by the controller on plugin startup.

    And this part (on GW side is working) --> Sending "0;0;0;3;0;2;Get Version" to the GW is being answered with "0;255;3;0;2;2.0.0"

    So it must be the Vera plugin and then i found this line in my Vera box

    02      07/30/16 23:23:43.694   luup_log:64: Arduino: Incoming internal command '0;255;3;0;2;2.0.0' discarded for child: nil <0x6c14>
    

    So the MySensors v2.0.0 GW responses with alt-id 0;255 i.s.o 0;0
    By updating this line https://github.com/mysensors/Vera/blob/development/L_Arduino.lua#L370 by... auhm what is already in the current git version it works.

    So the final conclusion: just update the Vera plugin with the development branch and it works...

    Vera
  • Login

  • Don't have an account? Register

  • Login or register to search.
  • First post
    Last post
0
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular