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
AWIA

AWI

@AWI
Hero Member
About
Posts
1.1k
Topics
36
Shares
0
Groups
3
Followers
14
Following
1

Posts

Recent Best Controversial

  • Double Micro (nano) Ampere meter
    AWIA AWI

    A very low current (double) stand alone uA meter to tune MySensors battery nodes..
    0_1493111992919_upload-0091f8f7-8b1a-4d26-8c5d-f66cffe3408c
    I de' MySensor'ized this project because it is more usefull on the workbench for measuring the very low currents of MySensors battery nodes. I own several Ampere meters including the famous µCurrent. Many of these are not accurate enough or need a lot of wiring and additional equipment (µCurrent). I just wanted something simple and stand alone
    Using the low cost HX711 weight scale 24 bit AD converter a sub 10€ cost double µA meter was born. Some characteristics to fit MySensors projects:

    • range channel A: ± 20mA 5½-6½ digit µA
    • range channel B: ±40mA 5½-6½ digit µA
    • burden voltage 1µV/1µA (internal resistance 1Ω)
    • 'patch panel' on the connectors.
    • easy calibration.

    In comparison with the µCurrent and a standard multimeter in uA range
    0_1493113275783_upload-2a33c4be-759a-46c6-af8f-57862ecf4e3c

    The internals:
    0_1493113146906_upload-9152b445-76e8-44e6-8625-530f33a80d20

    Although it cannot compare in accuracy with the µCurrent (in combination with a good multimeter) it is more than useable and accurate to do some serious MySensors tuning.

    If there is some interest I will try to publish a decent built instruction on Openhardware.io.

    My Project

  • nRf24L01+ connection quality meter
    AWIA AWI

    A little frustrated by radio's showing inconsistent behaviour I built a simple connection quality meter with a leftover of a previous project.

    0_1464549715094_upload-8cefcb84-2528-42e3-98e1-792b63fc0b50

    Not too complicated ;-) a nano, I2C lcd display, radio adapter board (guarantees a stable power supply for the radio) and the radio to be tested.

    0_1464549601260_upload-0bca4a04-80b0-4f70-9263-cef5d75e7702

    a little hot glue and it fits nicely in the box. The radio can be swapped on the outside. Power comes in from a power bank connected to the nano USB so that I can walk around the house.

    0_1464549797943_upload-328cbdd6-bd88-417c-9bbb-0160b1a2d55a

    The sketch sends values from 0..99 to a gateway with "ack" enabled. The LCD shows the "failed" sends and the number of not acknowledged messages. i.e. the messages which did not arrive at the gateway or did not make it on their return flight.

    It now takes less than a minute to sort the radio's :bowtie:

    My Project

  • Wall mounted 'mood light'
    AWIA AWI

    (there is a sketch for MySensors v2 available)
    I received my 110v-230v AC to Mysensors PCB board last week en spent a few hours this weekend making 2 wall mounted mood lights.
    upload-12d13e93-9d8f-4244-b1a4-2e74d014587c

    I looks a little cheap from the backside but very luxurious on the wall ( < €20 total) and fully "MySensored".

    upload-cbbe7065-68b3-4108-b6c1-ebffc7cbdddb

    I did not use the relais on the board (and discarded some of the safety measures :blush: ). Very basic: a cut WS2812b strip, a few wires.
    Case is 12 mm MDF Board painted with Chalk paint (no primer needed, ready in half an hour ;-))

    Basic sketch with FastLED library (or Adafruit) with V_RBG.

    upload-1c215841-3f7f-4e6a-a856-f8d6f6435818

    upload-c7e90b81-b8b1-46b8-843d-b8c8007cd521 upload-1b906742-96fc-4b41-a24e-6fa302921d10

    My Project

  • "Weathercock" / weather comfort station
    AWIA AWI

    0_1462545364186_upload-42f07aaa-ce36-4d9a-8758-c0f43003456d

    I couldn't wait to share my latest project... The weather is getting better and I wanted to have a simple display (and later on a more ' techie' one) to display if it is comfortable to go outside. The rooster (and ring) show the wind direction and the rest of the leds show if anything is in range. green == comfortable ; red == extreme (for my standards) but it smoothly displays all the colors in between (rainbow).

    Another (soon to be published) project I am working on is a solar powered weather sensor station with alternative wind sensor.. At this stage I am getting data from the controller (Domoticz) in a kind of proprietary V_TEXT form.

    The final version will get it's data directy from a MySensors sensor station... and a better crafted rooster. :blush:

    A beginners project.. Main components: stepper with driver ; WS2812 8 led ring & 5 separate leds and a MySensors arduino node (JModule in this case). All powered by a 5V usb supply.
    0_1462546054383_upload-df75e5d1-7600-4b72-8a12-29adea470c7f

    If anyone is interested I will publish the details on openhardware.

    Doing the startup animation to show it's capabilities..

    https://www.youtube.com/watch?v=6_DqHB5I_n0

    0_1462545448665_upload-df1a6b48-7c5c-4986-8378-fef9aedd33d1

    My Project

  • Wall mounted 'mood light' v2
    AWIA AWI

    I made a few changes to my Wall mounted 'mood light' and translated it to MySensors v2.
    0_1474615675050_upload-71b7410c-eae1-43a3-abf8-d074aff5058a

    The sketch shows an example of using a Domoticz selector switch attached to a dimmer (V_PERCENTAGE) to switch between the different light patterns (line 413). I'm pretty sure you can use this trick for more controllers.
    0_1474615943349_upload-f05ed149-c991-4e54-84df-ef1585d32ed9

    /*
     PROJECT: MySensors / RGB light NEOPIXEL
     PROGRAMMER: AWI
     DATE: october 10, 2015/ last update: september 20, 2016
     FILE: AWI_Wall_LIght_x.ino
     LICENSE: Public domain
    
     Hardware: Nano and MySensors 2.0, Wall light 16 WS2812B leds (neopixel)
    		
     Special:
    	uses Fastled library with NeoPixel (great & fast RBG/HSV universal library) 			https://github.com/FastLED/FastLED
    	
     SUMMARY:
    	
    	Different patterns and brightness settings
    	
    	Button switches on/off and cycles through all Color patterns on long press
    	
     Remarks:
    	Fixed node-id
    	
     Change log:
     20160915 - Updated to MySensors 2.0
     20160920 - Changed state change to dimmer i.s.o. switch()
    
    */
    //****  MySensors *****
    // Enable debug prints to serial monitor
    #define MY_DEBUG 
    #define MY_RADIO_NRF24										// Enable and select radio type attached
    //#define MY_RF24_CHANNEL 80								// radio channel, default = 76
    
    #define MY_NODE_ID 62
    #define NODE_TXT "W 62"										// Text to add to sensor name
    
    // #define MY_RF24_CE_PIN 7									// Ceech board, 3.3v (7,8)  (pin default 9,10)
    // #define MY_RF24_CS_PIN 8
    
    // helpers
    #define LOCAL_DEBUG											// enable if print wanted 
    
    #ifdef LOCAL_DEBUG
    #define Sprint(a) (Serial.print(a))							// macro as substitute for print, enable if no print wanted
    #define Sprintln(a) (Serial.println(a))						// macro as substitute for println
    #else
    #define Sprint(a)										
    #define Sprintln(a)
    #endif
    
    
    
    #include <SPI.h>											// My Sensors
    #include <MySensors.h>
    
    #include <FastLED.h>										// https://github.com/FastLED/FastLED
    #include "Button.h"											// https://github.com/JChristensen/Button
    
    const int stripPin = 5 ;									// pin where 2812 LED strip is connected
    const int buttonPin = 4 ;									// push button
    const int numPixel = 16 ;									// set to number of pixels (x top / y bottom)
    
    const int RGB_LightChild = 0 ;								// Child Id's, standard light child on/off/ dim
    const int RGB_RGBChild = 1 ;								// RGB light child (on/off/dim/color, if controller supports V_RBG))
    const int RGB_SolidColorChild = 2 ;							// when set, node reads Color text from ColorTextChild
    const int RGB_TextColorChild = 3 ;							// Holds Text value for color (custom colors from controller)
    const int RGB_AlarmPatternChild = 4 ;						// Switches to alarm status
    const int RGB_NextPatternChild = 5 ;						// Move to next pattern when set
    
    CRGB leds[numPixel];
    
    // Kelving colors: Light & daylight (in Fastled reference only)
    /// 1900 Kelvin Candle=0xFF9329 /* 1900 K, 255, 147, 41 */,
    /// 2600 Kelvin Tungsten40W=0xFFC58F /* 2600 K, 255, 197, 143 */,
    /// 2850 Kelvin Tungsten100W=0xFFD6AA /* 2850 K, 255, 214, 170 */,
    /// 3200 Kelvin	Halogen=0xFFF1E0 /* 3200 K, 255, 241, 224 */,
    /// 5200 Kelvin CarbonArc=0xFFFAF4 /* 5200 K, 255, 250, 244 */,
    /// 5400 Kelvin HighNoonSun=0xFFFFFB /* 5400 K, 255, 255, 251 */,
    /// 6000 Kelvin DirectSunlight=0xFFFFFF /* 6000 K, 255, 255, 255 */,
    /// 7000 Kelvin OvercastSky=0xC9E2FF /* 7000 K, 201, 226, 255 */,
    /// 20000 Kelvin ClearBlueSky=0x409CFF /* 20000 K, 64, 156, 255 */
    
    char setRGBvalue[] = "FFC58F";		 						// Controller sent RGB value, default tungsten40W
    uint16_t curBrightness = 0x7F, setBrightness = 0x7F ;		// Brightness globals (actualBrightness)
    unsigned long updateBrightnessDelay, lastBrightnessUpdate ; // Brightness timers
    int RGBonoff ;												// OnOff flag
    
    enum { pSolid, pOff, pOn, pAlarm, pFire, pFire2, pCandle, pCircle, pSinelon, pRainbow}  ;	// Pattern states (stored in int for convenience)
    const int lastPatternIdx = pRainbow + 1 ;					// use last pattern for patterncount
    int curPattern = pSolid ;									// current pattern
    int setPattern = pSolid ;									// set pattern (controller)
    unsigned long updatePatternDelay, lastPatternUpdate ;		// Pattern timers
    
    unsigned long idleTimer = millis() ;						// return to idle timer
    const unsigned long idleTime = 10000UL;					// return to idle after 10 secs
    
    const unsigned long dimTime = 1000UL;						// dim period
    
    const unsigned long heartbeatInterval = 1 * 60UL * 1000UL ;	// heartbeatinterval, just to let the controller know I am alive
    unsigned long heartbeatCounter = 0 ;
    
    MyMessage lightRGBMsg(RGB_LightChild,  V_RGB);				// standard messages, light
    MyMessage lightdimmerMsG(RGB_LightChild ,V_DIMMER);	
    MyMessage lightOnOffMessage(RGB_LightChild, V_STATUS);
    
    Button myBtn(buttonPin, true, true, 20);					//Declare the button (pin, pull_up, invert, debounce_ms)
    
    // Simple state machine for button state
    enum {sIdle, sBrightness, sPattern} ;						 // simple state machine for button press
    int State ;
    
    void setup() {
    	FastLED.addLeds<WS2812B, stripPin, GRB >(leds, numPixel); 	// initialize led strip (NEOPIXEL =WS...)
    	for(int i = 0 ; i < 6 ; i++) {							// get color value from EEPROM (6 char)
    		setRGBvalue[i] = loadState(i) ;
    		}
    	setLightPattern(pSolid, 0) ;							// default controller Solid 
    	FastLED.show();
    	State = sIdle ;											// Initial state
    	//randomSeed(analogRead(0));
    }
    
    
    
    void presentation(){
    // MySensors
    	sendSketchInfo("AWI RGB Wall " NODE_TXT, "2.0");
    	present(RGB_RGBChild, S_RGB_LIGHT, "RGB Wall RGB " NODE_TXT);// present to controller
    	present(RGB_LightChild, S_LIGHT, "RGB Wall Light " NODE_TXT);
    	present(RGB_SolidColorChild, S_LIGHT, "RGB Set Solid color (text) " NODE_TXT);
    	present(RGB_TextColorChild, S_INFO, "RGB Wall textcolor " NODE_TXT);	
    	present(RGB_AlarmPatternChild, S_BINARY, "RGB Wall Alarm " NODE_TXT);
    	present(RGB_NextPatternChild, S_DIMMER, "RGB Wall Pattern " NODE_TXT);
    }
    
    
    
    // read button and act accordingly
    // short press: on/off
    // longer press: set patterns with following short press
    // long press: set brightness increase 
    void loop() {
    	myBtn.read();               							//Read the button (only read)
    	unsigned long now = millis(); 							// loop timer reference
    	switch (State) {
    		case sIdle:											// default state, browse through patterns
    			if (myBtn.wasReleased()){						// light on/ off in idle
    				RGBonoff = !RGBonoff ;						// invert light state
    				setLightBrightness((RGBonoff == 1)?setBrightness:0, dimTime);
    				send(lightOnOffMessage.set(RGBonoff));	// and update controller	
    			} else if (myBtn.pressedFor(800)){				// move to Pattern update state with long press
    				idleTimer = now ;							// return to idle after ...
    				State = sPattern ;
    			}
    			break ;
    		case sPattern:										// entered after long press
    			if (myBtn.pressedFor(4000)){					// when press even longer move to Brightness update
    				State = sBrightness ;
    			} else if (myBtn.wasPressed()){
    				setPattern = (setPattern + 1) % lastPatternIdx ;  // increase pattern and wrap
    				setLightPattern((setPattern), 500 );
    				idleTimer = now ;
    			} else if ( now > idleTime + idleTimer  ){		// return to idle after ...
    				State = sIdle ;
    			}
    			break ;
    		case sBrightness:									// entered after looong press
    			if (myBtn.wasPressed()){							// if pressed again increase brightness
    				setLightBrightness((curBrightness+0x1F) % 0xFF, 0) ; // increase brightness and wrap (0..0xFF)
    				idleTimer = now ;
    			} else if ( now > idleTime + idleTimer  ){		// return to idle after ...
    				State = sIdle ;
    			}
    			break ;
    		default :
    			State = sIdle ;
    			break ;
    		}
    	updateLightBrightness();								// update Brightness if time
    	updateLightPattern();									// update Pattern if time
    	if ( now > heartbeatCounter  + heartbeatInterval){		// heartbeat every hour
    	    sendHeartbeat();
    		heartbeatCounter = now ; 
    		}
    	}
    
    // Sets the light brightness, takes value and time (ms) as input
    void setLightBrightness(int newBrightness, unsigned long updateTime){
    	// global: curBrightness, actualBrightness, updateBrightnessDelay
    	updateBrightnessDelay = updateTime / 0xFF ;				// delay = time / max steps
    	curBrightness = newBrightness ;							// set curBrightness to new value, rest is done in update
    	}	
     
    // Update the light brightness if time
    void updateLightBrightness(){
    	// global: curBrightness, actualBrightness, updateBrightnessDelay, lastBrightnessUpdate ;
    	static byte actualBrightness ;							// store real brightness state for slow dim
    	unsigned long now = millis() ;
    	if (now > lastBrightnessUpdate + updateBrightnessDelay){// check if time for update
    		if ( actualBrightness > curBrightness) {
    			FastLED.setBrightness( actualBrightness-- );
    			FastLED.show();
    		} else if ( actualBrightness < curBrightness){
    			FastLED.setBrightness( actualBrightness++ );
    			FastLED.show();
    			}
    		lastBrightnessUpdate = now ;
    		}
    	}
    
    // **** Pattern routines *****
    // Sets and initializes the light pattern if nescessary
    void setLightPattern( int newPattern, unsigned long updateDelay){
    	// global: curPattern, updatePatternDelay
    	static int lastPattern = pSolid ;						// last pattern for pOn / pOff virtual patterns
    	if (newPattern == pOff) lastPattern = curPattern ;		// remember last pattern
    	if (newPattern == pOn) curPattern = lastPattern ;		// only for pOn switch to last pattern
    	else curPattern = newPattern ;
    	updatePatternDelay = updateDelay ;						// delay for next pattern update, can be changed in pattern 
    	switch(curPattern){
    		case pSolid:										//  solid is set value in all pixels (and on)
    			for(int i = 0 ; i < numPixel ; i++) leds[i] = strtol( setRGBvalue, NULL, 16);
    			setLightBrightness(setBrightness, dimTime) ;	// slow dim to on
    			FastLED.show();
    			break ;
    		case pOn:											//  On is set Brightness in all pixels
    			setLightBrightness(setBrightness, dimTime) ;		// slow dim to on
    			FastLED.show();
    			break ;
    		case pOff:											//  off state all pixels off (add dim and pOn)
    			setLightBrightness(0, dimTime) ; 					// slow dim to off
    			FastLED.show();
    			break ;
    		case pCircle:										//  all pixels off
    			for(int i = 0 ; i < numPixel ; i++) leds[i] = 0 ;
    			for(int i = 0 ; i < 2 ; i++){
    				leds[i] = strtol( setRGBvalue, NULL, 16) ; // 1 pixel on
    				}
    			FastLED.show();
    			break ;
    		default :
    			setLightBrightness(setBrightness, dimTime) ;	// slow dim to on
    			FastLED.show();
    			break ;
    			}
    	}	
    
    // Update the light pattern when time for it
    void updateLightPattern(){
    	// global: curPattern, updatePatternDelay, lastPatternUpdate
    	unsigned long now = millis() ;
    	if (now > lastPatternUpdate + updatePatternDelay){		// check if time for update
    		switch (curPattern) {
    			case pAlarm:									// flash light
    				patternAlarm();
    				break ;
    			case pFire:										// wild fire
    				patternFire();
    				break ;
    			case pFire2:									// cosy fire
    				patternFire2();
    				break ;
    			case pCandle:									// flame
    				patternCandle();
    				break ;
    			case pCircle:									// flame
    				patternCircle();
    				break ;
    			case pRainbow:									// rotating rainbow
    				patternRainbow();
    				break ;
    			case pSinelon:									// rotating rainbow
    				patternSinelon();
    				break ;
    			case pSolid:									// do nothing fall through
    			case pOff:
    			case pOn:
    			default	:										// def	
    				break ;
    			}
    		lastPatternUpdate = now ;
    		}
    	}
    
    // Define the different patterns
    // Alarm - intermittent white and red color, full intensity, intermittent top & bottom half
    void patternAlarm() {
        static boolean topBot ;									// indicates direction for next entry
    	const CRGB colorTop = CRGB(0xFF, 0, 0 );				// red color
    	const CRGB colorBottom = CRGB(0xFF, 0xFF, 0xFF );		// white color
    	FastLED.setBrightness(0xFF);							// set the strip brightness to max for Alarm
    	for(int i=0; i <= (numPixel / 2 - 1) ; i++) {			// for half of strip size
    		leds[i] = topBot?colorTop:colorBottom ;	
    		leds[i+ (numPixel/2)] = topBot?colorBottom:colorTop ;
    		}
    	topBot = !topBot ;										// switch direction
    	FastLED.show();
    	}
    
    // Simulate fire with red color, varying number of leds intensity & tempo
    void patternFire() {
        byte numberLeds = random(0,numPixel);					// start number and end of led's for flickering
        int lum = ((random(100,255) * curBrightness)) / 0xFF ;	// set brightness and scale
        CRGB color = CRGB(200, random(70,230),0 );				// get red color with varying green
        for(int i=0; i <= numberLeds; i++) {
          leds[i] = color ;
          FastLED.setBrightness(lum);							// set the strip brightness
          FastLED.show();
          wait(random(0,10));									// (blocking, need to be changed)
        }
        updatePatternDelay = 100 ; 
    }
    
    // Simulate fire with red color and varying intensity & tempo
    void patternFire2() {
        CRGB color = CRGB(200, random(100,150),0);				// get red color with varying green
        for (byte p=0; p < numPixel; p++) {
          leds[p] = color;
        }
        FastLED.setBrightness((random(50,255) * curBrightness)/ 0xFF );	// set Brightness and scale
        FastLED.show();
        updatePatternDelay = random(20,300);					// variable delay
    }
    
    // Simulate candle based on fire with red color, varying number of leds intensity & tempo
    void patternCandle() {
        byte numberLeds = random(0,numPixel);					// start number and end of led's for flickering
        byte lum = ((random(100, 255) * curBrightness)/ 0xFF);	// set brightness
        CRGB color = CRGB(200, random(90,130),0 );				// get red color with varying green
        for(int i=0; i <= numberLeds; i++) {
          leds[i] = color ;
          FastLED.setBrightness(lum);							// set the strip brightness
          FastLED.show();
          wait(random(5,10));									// (blocking, need to be changed)
        }
        updatePatternDelay = 100 ; 
    }
    
    // a colored dot sweeping back and forth, with fading trails, adapted from Fastled sinelon
    void patternSinelon()
    {
    	fadeToBlackBy( leds, numPixel, 10);						// fade all leds a small amount 
    	int pos = beatsin8(25,0,numPixel);						// get a new position for the led (BPM = 13, min, max, )
    	leds[pos] += strtol( setRGBvalue, NULL, 16);
    	FastLED.show();
    	updatePatternDelay = 2 ;
    }
    
    
    // Rotate all Leds with current content and trail
    void patternCircle() {
    	static int currentLed ;									// indicated current led to light
    	// CRGB tempLed = leds[0];					 			// temporary variable for color
    	fadeToBlackBy( leds, numPixel, 128);					// fade all leds for trail..
    	leds[currentLed] = strtol( setRGBvalue, NULL, 16);		// set to current color
    	currentLed = (currentLed + 1) % numPixel ;				// wrap
        FastLED.show();
        updatePatternDelay = 100 ; 
    }
    
    void patternRainbow() {
    	static uint16_t hue ;									// starting color
    	FastLED.clear();
    	// for(hue=10; hue<255*3; hue++) {
    	hue = (hue+1) % 0xFF ;									// incerease hue and wrap
    	fill_rainbow( leds, numPixel , hue /*static hue value */, 1);// set a rainbow from hue to last in stepsize 1
    	FastLED.show();
    	updatePatternDelay = 100 ;
    	}
    
    // Incoming messages from MySensors
    void receive(const MyMessage &message) {
    	int ID = message.sensor;
    	Serial.print("Sensor: ");
    	Serial.println(ID);
    	switch (ID){
    		case RGB_LightChild:								// same behaviour as RGB child/ fall through
    		case RGB_RGBChild:									// if controller can handle V_RGB
    			if (message.type == V_RGB) {					// check for RGB type
    				strcpy(setRGBvalue, message.getString());	// get the payload
    				setLightPattern(pSolid, 0);					// and set solid pattern 
    			} else if (message.type == V_DIMMER) {			// if DIMMER type, adjust brightness
    				setBrightness = map(message.getInt(), 0, 100, 0, 255);
    				setLightBrightness(setBrightness, dimTime) ;
    			} else if (message.type == V_STATUS) {			// if on/off type, toggle brightness
    				RGBonoff = message.getInt();
    				setLightBrightness((RGBonoff == 1)?setBrightness:0, dimTime);
    			}
    			break ;
    		case RGB_SolidColorChild:							// request color from controller
    			if (message.type == V_STATUS) {					// if get color from text child
    				request(RGB_TextColorChild, V_TEXT);
    				setLightPattern(pSolid, 0);					// and set solid pattern (if not alre)
    				}
    			break ;
    		case RGB_TextColorChild:							// Text color from controller
    			if (message.type == V_TEXT) {					// if get color from text child
    				strcpy(setRGBvalue, message.getString());	// get the payload
    				for(int i = 0 ; i < 6 ; i++) {				// save color value to EEPROM (6 char)
    					saveState(i, setRGBvalue[i]) ;}			// Save to EEPROM
    				}
    			break ;
    		case RGB_AlarmPatternChild:							// set Alarm pattern
    			if (message.type == V_STATUS) {					// if get color from text child
    				if (message.getInt() == 1){
    					setLightPattern(pAlarm, 500);			// set slow alarm pattern
    				} else {
    					setLightPattern(setPattern, 0);			// and reset pattern
    					FastLED.setBrightness(setBrightness);
    					}
    				}
    			break ;
    		case RGB_NextPatternChild:							// next pattern
    			if (message.type == V_PERCENTAGE) {				//  Percentage indicates the pattern
    				setPattern = map(message.getInt(), 0, 100, 0, 15) % lastPatternIdx  ; // mapper dimmer value to state 0..9  and wrap
    				setLightPattern((setPattern), 500 );
    				Sprint("Pattern: ") ; Sprintln(setPattern) ;
    			} else if (message.type == V_STATUS){			// if off switch pattern to default == 0
    				setPattern = 0  ;
    				setLightPattern((setPattern), 500 );
    				Sprint("Pattern: ") ; Sprintln(setPattern) ;
    			}
    			break ;
    		}
        FastLED.show();
    	dispRGBstat();
     	}
    // debug	
    // display the status of all RGB: controller, requested, real
    void dispRGBstat(void){
        Serial.print(" Color: "); Serial.print(setRGBvalue); 
        Serial.print(" Brightness: "); Serial.println(setBrightness);
    	}
    
    
    My Project

  • Using the "Selector switch" in Domoticz (tutorial)
    AWIA AWI

    Domoticz features a special switch type named "Selector switch" . This switch type can serve many MySensors purposes. In this walk through it is used for selecting different light patterns in my Wall mounted 'mood light'.
    A "button set"
    0_1474613749068_upload-5c2bdf83-e135-4b01-a842-f8bf16b2c468

    or "select menu"
    0_1474613812167_upload-ff85302a-8b41-4033-8654-f3d85a0a2351

    Essentially it is (or can be) a switch which controls another device. In this case I use it to control a dimmer. Let's start.

    1. Create a virtual hardware device (if you have not already done so before)

    0_1474614096927_upload-fdbc81ca-84db-4f95-8a4c-36d9434bde72

    1. Create a new "Virtual Sensor" with type "Selector Switch" by pressing the "Create Virtual Sensors" button.
      0_1474614259118_upload-dfadaa31-681c-4100-9f65-7e9b77705850
      you can also use the "Manual Light/Switch" button in the "Switches" tab.
      0_1474614203400_upload-fc866b82-9544-43ac-ad46-280721ae59eb

    This will create a new Virtual selector switch.
    0_1474614396110_upload-9189aef8-4502-43ce-bbb5-386dc84d6cb1

    I will use this to control a dimmer device which output is used to set the light patterns in the mood light
    0_1474614481046_upload-803f08fb-dda1-4142-8677-d9fb706ad9bd

    1. In the devices tab find the "Dimmer you want the Selector attached to (in this case RGB Wall Pattern W 62" with index 971
      0_1474614623251_upload-a4eb98fa-e59f-4915-a4aa-a970f8ab57c6

    "Edit" the newly created Selector switch and for each of the selector states attach a command to be executed.

    The commands here can be pretty much anything but here I used the Domoticz JSON command to set the Dimmer to one of the 16 possible states (Yes, a dimmer can only asume 16 states). You need to put your own domoticz URL/IP and port number in there. "Idx" is the dimmer device index from the previous step.

    A maximum of 10 switch values can be assigned
    :exclamation: don't forget to save :sweat:

    0_1474614939249_upload-825741bf-c2ee-4538-ad15-7881f12fc4ca
    I deliberately did not set "level 1" , because this translates to "0" and is associated with the off state in my case (room light)

    for the copycats (don't forget to include your own values):

    http://192.168.2.110:8080/json.htm?type=command&param=switchlight&idx=971&switchcmd=Set%20Level&level=0
    

    That's it. Now when you press a selector button it switches the dimmer value.

    In your sketch you can translate the dimmer values to differenct actions. Have a look at the Wall light sketch for an example.

    Domoticz

  • USB ISP programmer with Small AA sensor node
    AWIA AWI

    Programming a bare ATMEGA can be quite cumbersome. Using a cheap USB ISP programmer and the Arduino IDE simplifies things.

    0_1456997858419_upload-6532ca59-67fd-4ee5-a524-206a11ab024c

    I equipped The multi purpose Slim 2AA Battery Node with a ZIF socket and resonator (only needed if the bare ATMEGA328 is pre programmed to need an external crystal) and voila: a €6 USB ISP programmer for your MySensors nodes!

    0_1456998326251_upload-309e3269-313f-42dc-9139-0731e45111c7
    0_1456997605485_upload-9a7eedf6-d78e-4c14-ad63-02b624761a87

    (notice that I mounted the components on the back side of the board, in fact most are not actually needed)

    Select the right programmer in the Arduino IDE and you can set fuses and upload you preferred bootloader.

    My Project

  • Remote debug messages with V_TEXT
    AWIA AWI

    Sometimes I want to have debug information from my nodes while these are not connected through serial. I.e. to discover "random" freezes of an outside weather node. So I rewrote a familiar macro

    #define LOCAL_DEBUG
    #ifdef LOCAL_DEBUG
    	#define Sprint(a) (Serial.print(a))						// macro as substitute for print, enable if no print wanted
    	#define Sprintln(a) (Serial.println(a))					// macro as substitute for println
    #else														// no print
    	#define Sprint(a)
    	#define Sprintln(a)
    #endif
    

    to include "remote debugging" by using "sprintf()" (formatted print). It is a (working) first attempt with limitations. Suggestions for improvement are more than welcome. The debug information is sent with V_TEXT and needs to be handled by the controller or a "logging node" (I will publish one soon)

    // Helper for Debug:  1 = Serial debug output ; 2 = V_TEXT remote output ; else no debug
    // Use Formats described in fprint() : http://www.cplusplus.com/reference/cstdio/printf/  
    // Example: 	Printf("Temp %2d Hum %2d\n", temperature, humidity);
    // warning: max print size < 24 ; float = NOT supported in Arduino, you need to convert it yourself, ie. dtostrf(Temperature, 5, 2, tempBuf)
    #define _DEBUG 2												// 0 = no output ; 1 = Serial debug output ; 2 = V_TEXT remote output 
    #if _DEBUG == 1													// Serial output
    	char printBuf[24] ;											// need temporary buffer
    	#define Printf(...) { sprintf(printBuf, __VA_ARGS__) ; Serial.print(printBuf);}	// macro to substitute Printf()
    #elif _DEBUG == 2												// if remote debug you need to define a child and present it to the controller
    	#define DEBUG_CHILD_ID 10									// Child id of V_TEXT
    	MyMessage debugMsg(DEBUG_CHILD_ID, V_TEXT);					// get the debug message ready
    	char printBuf[24] ;											// need temporary buffer
    	#define Printf(...) { sprintf(printBuf, __VA_ARGS__) ; send(debugMsg.set(printBuf)); }	// macro to substitute Printf()						
    #else															// No debug wanted
    	#define Printf(...)										
    #endif
    
    Development

  • Scrolling Text sensor node with new V_TEXT
    AWIA AWI

    Another experiment for the V_TEXT variable/ sensor. Including brightness & speed setting and special Alarm function.
    upload-3aa973f9-d6cf-41a5-8b59-2241311896b0
    A scrolling display to let the family now that the dog needs walking... nothing more than 1 nano, 8 (or whatever number) of displays with max7219 controller upload-f2290f64-a15d-48c9-8420-ab65e481a492 a radio and a few cables
    upload-4c163020-edac-4bc5-b3bf-50f1f3cccd24
    Total cost: €18 (with 8 displays)

    in Domoticz:
    upload-868a0c0c-a44d-480f-940c-e0378716ebd3
    Built around the very versatile "Parola" driver library

    (I will upload a video when I know how to .. ;-))
    https://www.youtube.com/watch?v=gCKTm427_Zc

    /*
     PROJECT: MySensors / Scrolling Text display
     PROGRAMMER: AWI
     DATE: september 12, 2015/ last update: september 12, 2015
     FILE: AWI_scroll_MAX.ino
     LICENSE: Public domain
    
     Hardware: tbd ..Ceech - ATmega328p board w/ NRF24l01
    	and MySensors 1.5 ()
    		
    Special:
    	Need to use MySensors development edition 
    	
    SUMMARY:
    	3 S_xx devices for scrolling text
    	- Displays a scrolling text from a "V_TEXT" variable
     	- additional dimmer sensor for controlling display brightness
    	- 'Alarm' switch. If "On", overrides the text display to display ALARM message
    	- You can also input messages from the Serial monitor (testing)
    	Uses excellent MD_MAX72XX library 
     Remarks:
    	Fixed node-id
    */
    
    // Use the MD_MAX72XX library to scroll text on the display with the use of the callback function 
    // to control what is scrolled on the display text.
    // You need to set the used display in the library MD_MAX72XX.h
    // User can enter text on the serial monitor and this will display as a
    // Speed for the display is controlled by a pot on SPEED_IN analog in.
    #include <MD_MAX72xx.h>						// multipurpose library for MAX72xx diaplay driver  https://parola.codeplex.com/
    #include <MySensor.h>          				// Mysensor network
    #include <SPI.h>
    #include <Time.h>   						//http://playground.arduino.cc/Code/Time
    #define	USE_POT_CONTROL	0					// enable Scroll speed  potentiometer
    #define	PRINT_CALLBACK	0
    
    // Macro to simplify serial print
    #define	PRINT(s, v)	{ Serial.print(F(s)); Serial.print(v); }
    
    // Define the number of devices we have in the chain and the hardware interface
    // need to be adapted to your setup
    const int MAX_DEVICES = 8 ;					// number of 8x8 displays
    const int CLK_PIN = 7 ; 					// SPI like clock
    const int DATA_PIN = 8 ;					// SPI like data
    const int CS_PIN = 6 ; 						// SPI like select
    
    // Parola is able to use SPI hardware interface, not testted in combination with MySensors
    // MD_MAX72XX mx = MD_MAX72XX(CS_PIN, MAX_DEVICES);
    // now use Arbitrary pins
    MD_MAX72XX mx = MD_MAX72XX(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);	// instantiate one display chain
    
    // Scrolling parameters, you can attach normal potentiometer to A5, Vcc, Gnd
    #if USE_POT_CONTROL
    const int SPEED_IN = A5 ;
    #else
    const int SCROLL_DELAY = 20 ;				// in milliseconds
    #endif // USE_POT_CONTROL
    
    const int CHAR_SPACING = 1 ;				// pixels between characters
    
    // MySensors constants & variables
    const byte nodeId = 51 ;					// MySensors fixed node id
    const byte messageCHILD = 8 ;				// Text from ControllerLCD
    const byte brightnessChild = 9 ;			// Brightness of display
    const byte alarmChild = 10 ;				// Display Alarm text (overrides normal text)
    boolean timeReceived = false ;				// Flag to indicate time received
    // Display constants & variables
    byte textBrightness = 1 ;					// brightness of display (between 0 - MAX_INTENSITY (0xF)
    byte textOnOff = true ;						// textOnOff = ! shutdown
    boolean textAlarm = false ;					// Alarm (switch S_BINARY)
    // Global message buffers shared by MySensors and Scrolling functions
    const int BUF_SIZE = 25 ;					// max payload for MySensors(NRF24l01)
    char curMessage[BUF_SIZE];					// current displayed message
    char newMessage[BUF_SIZE];					// next message to be displayed if available
    bool newMessageAvailable = false ;			// new message available flag
    uint16_t	scrollDelay;					// in milliseconds
    
    // *** Definition and initialisation
    // define the MySensor network (1.5)
    MyTransportNRF24 transport(9,10); 			// Sensoduino (8,7) Ceech board, 3.3v (7,8)  (pin default 9,10)
    MySensor gw(transport);  
    // Initialize messages for sensor network
    MyMessage textMsg(messageCHILD, V_TEXT);	// message for Sending Text to Controller
    
    
    /* MD_MAX72XX functions: can be found in the documentation for the library,
     * no need to customtize callback & scroll routines (unless you want to...)
    */
    uint8_t scrollDataSource(uint8_t dev, MD_MAX72XX::transformType_t t)
    // Callback function for data that is required for scrolling into the display
    {
      static char		*p = curMessage;
      static uint8_t	state = 0;
      static uint8_t	curLen, showLen;
      static uint8_t	cBuf[8];
      uint8_t colData;
    
      // finite state machine to control what we do on the callback
      switch(state)
      {
        case 0:	// Load the next character from the font table
          showLen = mx.getChar(*p++, sizeof(cBuf)/sizeof(cBuf[0]), cBuf);
          curLen = 0;
          state++;
    
          // if we reached end of message, reset the message pointer
          if (*p == '\0')
          {
            p = curMessage;			// reset the pointer to start of message
            if (newMessageAvailable)	// there is a new message waiting
            {
              strcpy(curMessage, newMessage);	// copy it in
              newMessageAvailable = false;
            }
          }
          // !! deliberately fall through to next state to start displaying
    
        case 1:	// display the next part of the character
          colData = cBuf[curLen++];
          if (curLen == showLen)				// end of character insert interchar space
          {
            showLen = CHAR_SPACING;
            curLen = 0;
            state = 2;
          }
          break;
    
        case 2:	// display inter-character spacing (blank column)
          colData = 0;
          curLen++;
          if (curLen == showLen)
            state = 0;
          break;
    
        default:
          state = 0;
      }
    
      return(colData);
    }
    
    // Callback (not used here)
    void scrollDataSink(uint8_t dev, MD_MAX72XX::transformType_t t, uint8_t col){
    // Callback function for data that is being scrolled off the display
    #if PRINT_CALLBACK
    	Serial.print("\n cb ");
    	Serial.print(dev);
    	Serial.print(' ');
    	Serial.print(t);
    	Serial.print(' ');
    	Serial.println(col);
    #endif
    }
    
    // non-blocking text display to be used in loop (call frequently)
    void scrollText(void){
    	static uint32_t	prevTime = 0;
    	if (millis()-prevTime >= scrollDelay){					// Is it time to scroll the text?
    		mx.transform(MD_MAX72XX::TSL);						// scroll along - the callback will load all the data
    		prevTime = millis();								// starting point for next time
    		}
    	}
    
    // sets the scroll delay (read from potentiometer if enabled)
    uint16_t getScrollDelay(void){
    #if USE_POT_CONTROL
    	uint16_t t = analogRead(SPEED_IN);
    	t = map(t, 0, 1023, 25, 250);
    	return(t);
    #else
    	return(SCROLL_DELAY);
    #endif
    	}
    
    void setup(){
    	// set up the display first
    	mx.begin();												// initialize display chain					
    	mx.setShiftDataInCallback(scrollDataSource);			// define function to get the scrolldata (returned as int8)
    	//mx.setShiftDataOutCallback(scrollDataSink); 			// not used
    	mx.control(MD_MAX72XX::INTENSITY, 0x01);
    #if USE_POT_CONTROL											// initialize speed potentiometer if enabled
    	pinMode(SPEED_IN, INPUT);
    #else
    	scrollDelay = SCROLL_DELAY;
    #endif
    
    	strcpy(curMessage, "I \x03 MySensors ");				// first message on display
    	newMessage[0] = '\0';									// new message initialized to empty
    	// Setup MySensors 
       //Serial in Sensor network = 115200
        gw.begin(incomingMessage, nodeId); 						// this node is 51 fixed 
        //Send the sensor node sketch version information to the gateway
        gw.sendSketchInfo("AWI Scroll MAX 51", "1.1");
     	gw.present(messageCHILD, S_INFO, "Text line Scroll");	// new S_type 20150905 (not know by domoticz)
     	gw.present(alarmChild, S_BINARY, "Alarm display");		// to display alarm text
    	gw.present(brightnessChild, S_DIMMER, "Text brightness"); // new S_type 20150905 (not know by domoticz)
        //gw.send(textMsg.set("-"));							// initialize the V_TEXT at controller for sensor to none (trick for Domoticz)
    	gw.request(messageCHILD, V_TEXT, 0); 					// request value from controller
    	// Initializations
        gw.requestTime(receiveTime);							// get the time from controller (handled by receiveTime)
    	}
    
    // loop only uses non-blocking functions
    void loop() {
    	gw.process();
    	static unsigned long lastUpdate ;						// Static hold the last update time
    	unsigned long now = millis();
    	scrollDelay = getScrollDelay();							// update scroll delay from potentiometer
    	readSerial();
        // Check for new conditions & ask for new information from controller every 10 seconds
        if (now-lastUpdate > 10000){
    		if (textAlarm){										// if alarmstatus: override all text and set max intensity
    			strcpy(curMessage, "   ALARM   ");
    			mx.control(MD_MAX72XX::INTENSITY, MAX_INTENSITY);	// set current brightness
    			mx.control(MD_MAX72XX::SHUTDOWN, false) ;
    		} else {											// standard (non Alarm actions)
    			mx.control(MD_MAX72XX::INTENSITY, textBrightness);	// set current brightness
    			mx.control(MD_MAX72XX::SHUTDOWN, textOnOff) ;
    			gw.request(messageCHILD, V_TEXT, 0); 				// request new value from controller
    		}
    		lastUpdate = now;
    	}
    	scrollText();
    }
     
    // This is called when a new time value was received
    void receiveTime(unsigned long controllerTime) {
        Serial.print("Time value received: ");
        Serial.println(controllerTime);
        setTime(controllerTime); 								// time from controller
        timeReceived = true;
    	} 
    
    // This is called when a message is received 
    void incomingMessage(const MyMessage &message) {
    	Serial.print("Message: "); Serial.print(message.sensor); Serial.print(", Message: "); Serial.println(message.getString());
    	if (message.sensor == messageCHILD){
    		if (message.type==V_TEXT) {						// Text content
    		strcpy(newMessage, message.getString());		// copy it in
     			newMessageAvailable = true ;
    		}
    	} else if (message.sensor == alarmChild) {
    		if (message.type==V_STATUS) {					// True/ false content
     			textAlarm = message.getBool()?true:false ;	// set alarmflag
    			Serial.print("TextAlarm: ");
    			Serial.println(textAlarm);
    		}
    	} else if (message.sensor == brightnessChild){
    		if (message.type==V_PERCENTAGE) {				// Level 0..100  content
     			textBrightness = map(message.getInt(),0, 100, 0, MAX_INTENSITY ) ;	// map to brightnesslevel 
    			Serial.print("TextBrightness: ");
    			Serial.println(textBrightness);
    		} else if (message.type==V_STATUS) {			// Display on/off
     			textOnOff = message.getBool()?false:true ;		// set shutdown/ !on/off
    			Serial.print("Text on/off: ");
    			Serial.println(textOnOff);
    		}
    	}
    }
    
    	
    // Testing purposes: input routine character buffer. Reads serial characters in buffer newMessage.
    // sets flag newMessageAvailable to true if completed
    void readSerial(void)
    {
      static uint8_t	putIndex = 0;
    
      while (Serial.available())
      {
        newMessage[putIndex] = (char)Serial.read();
        if ((newMessage[putIndex] == '\n') || (putIndex >= BUF_SIZE-3))	// end of message character or full buffer
        {
          // put in a message separator and end the string
          newMessage[putIndex++] = ' ';
          newMessage[putIndex] = '\0';
          // restart the index for next filling spree and flag we have a message waiting
          putIndex = 0;
          newMessageAvailable = true;
        }
        else
          // Just save the next char in next location
          newMessage[putIndex++];
      }
    }
    
    
    
    
    My Project

  • nRf24L01+ connection quality meter
    AWIA AWI

    The sketch for the connection quality meter.

    /*
     PROJECT: MySensors / Quality of radio transmission 
     PROGRAMMER: AWI (MySensors libraries)
     DATE: 20160529/ last update: 20160530
     FILE: AWI_Send.ino
     LICENSE: Public domain
    
     Hardware: ATMega328p board w/ NRF24l01
    	and MySensors 2.0 (Development)
    	
    Special:
    	
    	
    Summary:
    	Sends a radio message with counter each  x time to determine fault ratio with receiver
    Remarks:
    	Fixed node-id & communication channel to other fixed node
    	
    Change log:
    20160530 - added moving average on fail/ miss count 
    */
    
    
    //****  MySensors *****
    // Enable debug prints to serial monitor
    #define MY_DEBUG 
    #define MY_RADIO_NRF24									// Enable and select radio type attached
    #define MY_RF24_CHANNEL 80								// radio channel, default = 76
    
    #define MY_NODE_ID 250
    #define NODE_TXT "Q 250"								// Text to add to sensor name
    
    // #define MY_RF24_CE_PIN 7								// Ceech board, 3.3v (7,8)  (pin default 9,10)
    // #define MY_RF24_CS_PIN 8
    #define DESTINATION_NODE 0								// receiving fixed node id (default 0 = gateway)
    
    #include <SPI.h>
    #include <MySensor.h>  
    
    // display
    #include <Wire.h>											// I2C
    #include <LiquidCrystal_I2C.h>								// LCD display with I2C interface
    
    
    // helpers
    #define LOCAL_DEBUG
    
    #ifdef LOCAL_DEBUG
    #define Sprint(a) (Serial.print(a))						// macro as substitute for print, enable if no print wanted
    #define Sprintln(a) (Serial.println(a))					// macro as substitute for println
    #else
    #define Sprint(a)										// enable if no print wanted -or- 
    #define Sprintln(a)										// enable if no print wanted
    #endif
    
    
    // MySensors sensor
    #define counterChild 0
    
    // send constants and variables
    int messageCounter = 0 ; 
    const int messageCounterMax = 100 ; 					// maximum message counter value 
    const unsigned counterUpdateDelay = 100 ;				// send every x ms and sleep in between
    
    // receive constants and variables
    boolean failStore[messageCounterMax] ;					// moving average stores & pointers
    int failStorePointer = 0 ;
    boolean missedStore[messageCounterMax] ;
    int missedStorePointer = 0 ;
    int newMessage = 0 ;
    int lastMessage = -1 ;
    int missedMessageCounter = 0 ; 							// total number of messages in range (messageCounterMax)
    int failMessageCounter = 0 ; 							// total number of messages in range (messageCounterMax)
    
    // Loop delays
    const unsigned long displayInterval = 1000UL ;			// display update in ms
    unsigned long lastDisplayUpdate = 0 ;					// last update for loop timers
    
    // standard messages
    MyMessage counterMsg(counterChild, V_PERCENTAGE);		// Send value
    
    // ***** LCD
    LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
    
    void setup() {
    	Wire.begin();  // I2C
        // ** LCD display **
        // LCD 2 lines * 16 char.
        lcd.begin(16, 2);
        lcd.setBacklight(HIGH);
        lcd.setCursor(0, 0);
    	lcd.print("AWI Quality nRF24");
    
    	for(int i= 0 ; i <  messageCounterMax ; i++){		// init stores for moving averages
    		failStore[i] = true ;
    		missedStore[i] = true ;
     	}
    	missedStorePointer = failStorePointer = 0 ;
        delay(1000);
    }
    
    void presentation(){
    // MySensors
    	present(counterChild, S_DIMMER, "Quality counter " NODE_TXT) ;  // counter uses percentage from dimmer value
    }
    
    
    void loop() {
    	// Sprint("count:") ; Sprintln(messageCounter) ;
    	LCD_local_display();
    	missedStore[failStorePointer] = false  ; 			// set slot to false (ack message needs to set) ; 
    	boolean succes = failStore[failStorePointer] = send(counterMsg.setDestination(DESTINATION_NODE).set(failStorePointer), true);  // send to destination with ack
    	if (!succes){
    		failMessageCounter++ ; 
    		Sprint("Fail on message: ") ; Sprint(failStorePointer) ;
    		Sprint(" # ") ; Sprintln(failMessageCounter);
    	}
    	failStorePointer++ ;
    	if(failStorePointer >= messageCounterMax){
    		failStorePointer =  0	;						// wrap counter
    	}
    	wait(counterUpdateDelay) ;							// wait for things to settle and ack's to arrive
    }
    
    void receive(const MyMessage &message) {  				// Expect few types of messages from controller
    	newMessage = message.getInt();						// get received value
    	switch (message.type){
    		case V_PERCENTAGE:
    			missedStore[newMessage] = true ;			// set corresponding flag to received.
    			if (newMessage > lastMessage){				// number of messages missed from lastMessage (kind of, faulty at wrap)
    				Sprint("Missed messages: ") ; Sprintln( newMessage - lastMessage - 1) ;
    				missedMessageCounter += newMessage - lastMessage - 1 ;
    			}
    			lastMessage = newMessage ;
    			break ;
    		default: break ;
    	}
    }
    
    
    // calculate number of false values in array 
    // takes a lot of time, but who cares...
    int getCount(boolean countArray[], int size){
    	int falseCount = 0 ;
    	for (int i = 0 ; i < size ; i++){
    		falseCount += countArray[i]?0:1 ;
    	}
    	return falseCount ;
    }
    
    void LCD_local_display(void){
    /* prints all available variables on LCD display with units
    */
    	
        char buf[17]; 											// buffer for max 16 char display
        lcd.setCursor(0, 0);
        snprintf(buf, sizeof buf, "Failed:%4d %3d%%", failMessageCounter, getCount(failStore, messageCounterMax));
        lcd.print(buf);
        lcd.setCursor(0, 1);
        snprintf(buf, sizeof buf, "Missed:%4d %3d%%", missedMessageCounter, getCount(missedStore, messageCounterMax));
    	lcd.print(buf);
    }
    
    
    My Project

  • RS485 Stress test
    AWIA AWI

    Just a little experiment on how well the wired RS485 communication is doing. I wired up 4 nano's with RS485, in parallel on the bus, Baud rate to 19200. One node is a gateway the other three are firing an integer every 500-700 ms (random). The gateway catches the values and simply measures missed to received messages.

    Rather messy, but hey it's wired :-)
    0_1475940887069_upload-7fcbe85a-d054-401c-8cf4-bfde2436d4ca 0_1475940940839_upload-d36f3e4e-33bb-4ec3-8c0a-9ff11afab087

    Error rate is around 5% ! Probably bus collisions ... (I will dig deeper ... and Yes CAN bus would be better, but this can be resolved )

    The Sketches:
    Simple adaption of the example motion sketch

    // MySensors 
    #define MY_DEBUG 												// Enable MySensors debug to serial
    #define MY_PARENT_NODE_ID 0										// define if fixed parent
    #define MY_PARENT_NODE_IS_STATIC
    #undef MY_REGISTRATION_FEATURE									// sketch moves on if no registration
    #define MY_NODE_ID 12											// fixed node number
    #define NODE_TXT "RS485 test 12"								// Text to add to sensor name
    
    // Enable RS485 transport layer
    #define MY_RS485
    
    // Define this to enables DE-pin management on defined pin 
    #define MY_RS485_DE_PIN 2
    
    // Set RS485 baud rate to use
    #define MY_RS485_BAUD_RATE 19200
    
    #include <MySensors.h>
    
    unsigned long SLEEP_TIME = 500; 								// Sleep time between reports (in milliseconds)
    #define CHILD_ID 1   											// Id of the sensor child
    
    // Initialize message
    MyMessage msg(CHILD_ID, V_PERCENTAGE);
    int messageCounter = 0 ;										// Count the outgoing messages for validation (0..99)
    
    
    void setup()  
    {  
    }
    
    void presentation()  {
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo("AWI " NODE_TXT, "1.0");
    
      // Register all sensors to gw (they will be created as child devices)
      present(CHILD_ID, S_PERCENTAGE);
    }
    
    void loop()     
    {     
       	Serial.print(NODE_TXT " message: " );
    	Serial.println(messageCounter);
    	send(msg.set(messageCounter)); 								// Send message to gw 
    	messageCounter = ++messageCounter % 100 ;					// wrap
    	// Send update every SLEEP_TIME
    	sleep(random(SLEEP_TIME, SLEEP_TIME + 200));
    }
    

    Adapted Gateway sketch

    
    // Enable RS485 transport layer
    #define MY_RS485
    
    // Define this to enables DE-pin management on defined pin
    #define MY_RS485_DE_PIN 2
    
    // Set RS485 baud rate to use
    #define MY_RS485_BAUD_RATE 19200
    
    // Enable serial gateway
    #define MY_GATEWAY_SERIAL
    
    
    // Enable inclusion mode
    #define MY_INCLUSION_MODE_FEATURE
    // Enable Inclusion mode button on gateway
    #define MY_INCLUSION_BUTTON_FEATURE
    // Set inclusion mode duration (in seconds)
    #define MY_INCLUSION_MODE_DURATION 60
    // Digital pin used for inclusion mode button
    #define MY_INCLUSION_MODE_BUTTON_PIN  3
    
    // Set blinking period
    #define MY_DEFAULT_LED_BLINK_PERIOD 300
    
    // Flash leds on rx/tx/err
    #define MY_DEFAULT_ERR_LED_PIN 4  // Error led pin
    #define MY_DEFAULT_RX_LED_PIN  5  // Receive led pin
    #define MY_DEFAULT_TX_LED_PIN  6  // the PCB, on board LED
    
    #include <MySensors.h>
    
    const int maxNodes = 20 ;								// maximum number of Nodes for test. Nodes should be numbered from 1..
    int lastValues[20] ; 									// temporary storage of last sent values (index is node number)
    int messageCounter = 0 ;								// total messages
    int messageErrorCounter = 0 ;							// number of errors
    
    void setup() {
      // Setup locally attached sensors
    }
    
    void presentation() {
     // Present locally attached sensors
    }
    
    void loop() {
      // Send locally attached sensor data here
    }
    
    void receive(const MyMessage &message) {  				// messages from node or controller
    	char printBuf[40] ;
    	lastValues[message.sender] = ++lastValues[message.sender] % 100 ; // increase and wrap last value, should be new
    	messageCounter++ ;
    	if (message.getInt() == lastValues[message.sender]){
    		sprintf(printBuf, "Message node: %d %d  OK\n", message.sender, message.getInt());
    	} else{
    		sprintf(printBuf, "Message node: %d %d  Error\n", message.sender, message.getInt());
    		lastValues[message.sender] = message.getInt() ;	// correct to last
    		messageErrorCounter++ ;
    	}
    	Serial.print(printBuf) ;
    	sprintf(printBuf, "Messages: %d Errors: %d\n", messageCounter, messageErrorCounter);
    	Serial.print(printBuf) ;
    }
    
    My Project

  • NRF24l01 1.27mm Arduino Pro Mini Adapter board
    AWIA AWI

    Not really a project, not my own design but I think this adapter board can be of use to many. The board was published by Kauz on www.thinkering.de.
    0_1487515383182_upload-9af3827a-74e8-4744-a799-2fdcf8f2e521

    I breadboard a lot of pro-mini circuits to be MySensor'ized at a later stage and have been using the jModule frequently upto now, The jModule includes a 3.3V LDO and does the job well. However this adapter has the advantage that it breaks out all the pro-mini pin's in their original spot and keeps most of the form factor (you need to use a 3.3V pro-mini).

    A few pictures:

    The components
    0_1487515596830_upload-14a7811c-934f-4ad2-b2aa-23f2c71488f2 The yellow part is a smd tantalum 4.7uF capacitor. I had the smd nRF24l01 already soldered on the board (takes a little exercise).

    Soldered with pin headers. The author soldered it "trough-hole" without headers.
    0_1487515869578_upload-4626267c-7169-4475-9aa7-e11e40db09190_1487516051581_upload-0e1db919-3304-4dc2-881b-9c0c2e3b9bdf You need wires from the board to Vcc and Gnd.

    Comparison with jModule:
    0_1487515950101_upload-9d222b3f-c342-426e-9e08-8fda9201a10e 0_1487515980034_upload-c1a4776d-8746-4b15-9324-3df09369f6dd

    Ready for 4 new MySensors projects :-)
    0_1487516135469_upload-baffb796-a7dc-436b-a138-5f6d2d0ca9e3

    My Project

  • The examples on the website need love
    AWIA AWI

    Sounds like a succes story. A few remarks...

    • I started in a similar way and soon find out it is and should be a learning experience.

    • plug and play is not very satisfying. A good amount of trial and error is the learning experience. "wasting a sunday on MySensors" is a contradiction in my view.

    • examples are just a good start and function most of the time. The community need to take care of maintaining them. As I read you already did a good job here :point_up:

    • MySensors is typically a low power sensor thingy. Using sleep() is very useful for most sensors (nor actuators)

    • I hope you got infected and help building a great learning experience for beginners and more advanced users.

    General Discussion

  • Power/ Usage sensor - multi channel - local display
    AWIA AWI

    It is a power meter device which displays: current power, daily usage and total usage of upto 12 S0 meters and sends all the information through the MySensors network. The current sketch displays power used and generated from the different channels and uses a rotary encoder to browse the displays and adjust the (total values).upload-47e80693-5e06-4b90-9e6b-c435212fc038 (also added message communication to the display from the controller (V_TEXT).
    With reference to my earlier project I reworked the whole thing to be more reliable and added functionality. The basic concept is the same:

    1 Arduino doing the time critical measurement and counting of the pulses and time, sending the measurements via a one-wire serial in JSON
    upload-e7fcad43-2ae0-4762-8c25-7d1bc3c71e2b
    including "patch panel" for connection to the S0 meters.
    upload-d07ff26c-a941-4923-9324-7b7842627af2

    Another arduino reading the JSON string and performing the tasks like display and sending the values over the MySensors network.
    upload-bbac86e4-758d-4794-8740-05e22b54be8d
    The grey and white wire connect to the "measurement" Arduino.

    some displays:
    upload-762b5e1e-dada-4314-ae95-7d6fa6c1b2aa
    adjusting the values

    upload-b1afc999-309b-4b5a-9170-c4998e2c7627
    reset the daily values

    My Project

  • Request for contribution - controller selection matrix
    AWIA AWI

    Following up on a the need for guidance in several posts by @stefaanv , by @samppa we started a simple Controller selection matrix.
    The purpose of the matrix is to compare the different controllers on MySensors specific features (not on the generic ones) to get global idea on which controller to choose (or test) for your MySensors network.
    Contribution to this tool is very much appreciated, so if you know the peculiarities of one or more controllers please help us filling the matrix or make comments on this post.

    The sheet includes a small selection of popular controllers, let's make it complete!

    The (editable) Controller Selection Matrix

    The list of controllers bold is (partly) filled by a few volunteers, are you the next one?:

    Ago Control
    Calaos
    Domoticz
    DomotiGa
    FHEM
    Freedomotic
    Home Assistant
    Homeseer
    Homey
    HoMIDoM
    Indigo Domotics
    ioBroker
    Jeedom
    MajorDoMo
    MyController.org
    MyNodes.NET
    Node Red
    OpenHAB
    openLuup
    PiDome
    pimatic
    Vera

    Controllers

  • Slim Node scene controller/ keypad
    AWIA AWI

    Inspired by the Battery button sensor, a look at "how low can you go"

    0_1455376405391_upload-51c797dd-68ab-4248-b99c-860b1c4c704b

    Not much more than a standard 16 key touchpad and the ever popular slim node.

    Have it sleep on interrupt only (no timer delay)

    DVint = sleep(DV_int, FALLING, 0);  // sleep, node wakes up on key interrupt  (sleep returns int number)
    

    and it will last for years on a coin cell. i.e.
    250mAh battery ; 50 keypresses a day ; results in > 7 year battery life!

    Now thinking on which housing to use.. I wish I had the skills for something like this :disappointed: . 0_1455377221330_upload-934ee488-5c34-466a-8224-f9c3b8eef5d5

    My Project

  • Slim Node scene controller/ keypad
    AWIA AWI

    Completed the design... not a beauty but functional. Expected lifetime with the battery > 50 yrs (2.2 uA, completely theoretical. Batteries aren't of that quality )

    0_1455566548551_upload-2e761d10-068c-47f0-b8a3-69293e0f16af 0_1455566654262_upload-7d7d7ec0-bf07-4d2e-a154-ef803959bfd3 0_1455566600056_upload-7136a779-06eb-4a1e-b6d9-c092af1fa87e. The bi-color LED was added to show the on/off state when toggling a scene. (and because @GertSanders likes flashing led's :grinning: )

    My Project

  • RFID Card reader - Wiegand
    AWIA AWI

    An universal RFID reader using the Wiegand protocol.
    0_1478440526897_upload-fed74ddb-acd8-40b9-8431-c4e2ffd93fdd

    a few features:

    • Can add/ delete/ list rfid codes (with hardcoded master rfid tag)
    • 4 digit 7 segment display
    • Store rfid codes in EEPROM (AVR) (be aware - limited room available)
    • Reports status log to MySensors controller
    • Stores Rfid tags in local memory and presents them as switches to the controller
      ○ Switch on - enable / switch off - disable
    • Communicates via RS485 for security (not necessarily)

    A nice commercial waterproof sealed wiegand rfid reader with buzzer and led can be found for a few bucks. The Wiegand protocol is a well established standard for card reader communication and can be interfaced easily with an Arduino. The interface of this type uses 5V logic but the reader needs 9-12V to function.
    0_1478440971635_upload-1c0ad37e-9999-4453-b720-94bd31802d55 The back side shows the wiring: Wiegand wires are D0 and D1. I also use the LED and BEEP to get some feedback to the user. (the WG26/WG34 can be used to switch between 26 and 34 bit protocol, the "door bell" wires are not even there :confused: )

    A few pictures of hardware. All pretty straightforward connections.
    0_1478441720110_upload-afbf23c8-c964-42b4-864a-7be340b94e7a

    There is a 7-segment display which has a simple interface and a lot of functionality.
    0_1478441768013_upload-e1bd62ba-bb00-42c7-b1b9-2844818a0d7a
    Connected to a RS485 test setup.

    0_1478441948792_upload-15b743bf-7835-4995-a286-ec0a0568f9de 0_1478441975194_upload-c63fd66a-0f41-4a8c-a838-31180d047a92 0_1478442059886_upload-86ede3d5-0303-40bf-b9c4-2cba19295c26 0_1478442105543_upload-cdbcead9-07aa-4359-b246-bc1836a29098 0_1478442141295_upload-a817f39a-64ce-4114-b517-16486ea6ed4d
    And the display showing the presented rfid tag 1 & 2, inclusion mode, deletion mode and error.

    The whole project was meant to play with a FiniteStateMachine machine library/ class, which worked out well (for me :smirk: ). This class lets you define "states" for which you create entry, update and exit functions. Worth a try... You can find the sketch and libraries on git. All libraries used are put in the sketch folder.

    more reading in the main sketch

    /*
     PROJECT: MySensors / Wiegand card reader with display
     PROGRAMMER: AWI
     DATE: 20160920 last update: 20161020
     FILE: AWI_Cardreader.ino
     LICENSE: Public domain
    
     Hardware: ATMega328p board w/ RS485
    	and MySensors 2.0 
    		
    Special:
    	
    Summary:
    	Wiegand reader - universal card reader
    	4 7-segment display
    	RS485 interface with MySensors (wired for security, cardID's are sent to controller)
    	
    	1. A local database with Card id's is kept in the node. Card id 0 is the "master master" card. With the master card you can:
    	- Open the door lock (just present it)
    	- Include other cards (present it twice and present the new card)
    	- Delete cards (present it three times, present the card to be deleted and confirm with master card)
    	- Browse the cards (present it four times), the display shows the card indexes with their status.
    
    	2. Any registered card opens the lock for a short time. A non registered card shows "Err"
    	
    	3. A new (included) card is presented to the controller as a switch with "On" status and a node-id which is the same as the card index. The CardID is added as text
    	
    	4. A card deletion will set the corresponding switch to off (to be implemented = switching a card to off in the controller will delete the card)
    	
    	5. The card "browse" function will (re)"present" all the activated cards (again) to the controller
    	
    	
    Remarks:
    	Fixed node-id
    	State machine based on FiniteStateMachine library
    	
    Change log:
    20160920 - created
    20161020 - updated to include MySensors V_TEXT status log. Log should be kept by controller
    20161023 - clean & comment code
    */
    #define MY_NODE_ID 10
    #define NODE_TXT "Cardreader 10"					// Text to add to sensor name
    //#define MY_PARENT_NODE_ID 3						// fixed parent to controller when 0 (else comment out = AUTO)
    #define MY_DEBUG 									// Enable MySensors debug to serial
    #define MY_PARENT_NODE_ID 0							// define if fixed parent
    #define MY_PARENT_NODE_IS_STATIC
    #undef MY_REGISTRATION_FEATURE						// sketch moves on if no registration
    #define MY_TRANSPORT_DONT_CARE_MODE					// transport connection to Gateway not essential/ needs to work without
    
    // Enable RS485 transport layer
    #define MY_RS485
    #define MY_RS485_DE_PIN 4							// Enables DE-pin management 
    #define MY_RS485_BAUD_RATE 19200					// Set RS485 baud rate (max determined by AltsoftSerial)
    // or use radio:
    //#define MY_RADIO_NRF24							// Enable and select radio type attached
    //#define MY_BAUD_RATE 9600
    
    #include <SPI.h>
    #include <MySensors.h>
    
    #include "CardDB.h"									// AWI: local lib to store cards
    #include "Wiegand.h"								// Wiegand protocol lib https://github.com/monkeyboard/Wiegand-Protocol-Library-for-Arduino
    #include "FiniteStateMachine.h"						// FiniteStateMachine https://github.com/gusgonnet/particle-fsm/tree/master/firmware
    #include "LedFlash.h"								// AWI: non blocking class for flexible LED/ buzzer 
    #include "SevenSegmentTM1637.h"						// 4 digit 7 segment display https://github.com/bremme/arduino-tm1637
    
    // helpers
    #define LOCAL_DEBUG
    #ifdef LOCAL_DEBUG
    	#define Sprint(...) (Serial.print( __VA_ARGS__))				// macro's as substitute for print,println 
    	#define Sprintln(...) (Serial.println( __VA_ARGS__))
    #else
    	#define Sprint(...)
    	#define Sprintln(...)
    #endif
    //** door lock 
    const byte DOORLOCK = 5 ;
    //** LedFlash lib used for the Buzzer and Led on the cardreader */
    const byte LED_PIN = 6;												// status led
    const byte BEEP_PIN = 7 ; 											// beep
    //** LED Display connections (library serial protocol)
    const byte PIN_CLK = A4;   											// define CLK pin (any digital pin)
    const byte PIN_DIO = A5;   											// define DIO pin (any digital pin)
    //** MySensors children
    const byte CARD_CHILD = 0 ; 										// MySensors master card child (rest of cards are dynamic)
    const byte CARD_ID_CHILD = 1 ; 										// MySensors card id/ log sensor 
    
    const unsigned long MASTERCARD = xxxxxx ;							// Hardcoded MASTERCARD, insert your master card number
    
    // Instantiate library objects
    SevenSegmentTM1637    display(PIN_CLK, PIN_DIO);					// LED display
    LedFlash statusLed(LED_PIN,true, 50, 400);							// status led (active on, flash on 50ms/ period 400ms )
    LedFlash statusBeep(BEEP_PIN,true, 2, 400);							// buzzer (active on, flash on 2ms/ period 400ms )
    CardDB cardDB ; 													// EEPROM database routines
    WIEGAND wg;															// instantiate Wiegand
    
    // state machine definitions (&routines need to be defined)
    FState idleState( &idleEnter, &idleUpdate, NULL );  				// Idle state (doe not need exit routine)
    FState delayState( &delayEnter, &delayUpdate, &delayExit);	  		// delay after invalid card
    FState unlockState( &unlockEnter, &unlockUpdate, &unlockExit);  	// Unlocks after valid card
    FState includeState( &includeEnter, &includeUpdate, &includeExit);  // waiting for inclusion of new card
    FState deleteState( &deleteEnter, &deleteUpdate, &deleteExit);  	// deletion of card
    FState confirmState( &confirmEnter, &confirmUpdate, &confirmExit);  // confirmation of deletion
    FState browseState( &browseEnter, &browseUpdate, &browseExit);  	// browse cards
    FiniteStateMachine stateMachine(idleState) ; 						//initialize state machine, start in state: noop
    
    const unsigned long idleTime = 2000UL ;								// delay to return to idle
    unsigned long browseTimer = millis() ;								// timer for browsing
    const unsigned long browseTime = 800UL ;							// detay for browsing
    
    unsigned long heartbeat = 60000UL ;									// heartbeat every hour
    unsigned long lastHeartbeat = millis() ; 
    
    unsigned long lastUpdate = millis(); 								// timer value
    
    
    unsigned long lastCardID = 0 ;										// holds last card value for inclusion / deletion
    int curCard = 0 ;													// Used as a browse pointer and temp store for deletion/ inclusion
    bool newCard = false ;												// global to indicate new card is available
    
    // MySensor messages
    MyMessage cardStatusMsg(0,V_STATUS);								// Each card id has its own "Switch", which is presented at inclusion
    MyMessage cardIdMsg(0,V_TEXT);										// Each card id has its own identifier, sent a text to controller 
    
    
    void setup() {
    	pinMode(DOORLOCK, OUTPUT) ;										// doorlock connection
    	display.begin();												// initializes the display
    	display.setBacklight(10);										// set the brightness to x %
    	display.print("INIT");											// display INIT on the display
    	wait(1000) ;
    	wg.begin();														// activate wiegand
    	lastUpdate = millis() ;
    	cardDB.initDB();												// ONLY in the first run to clear the EEPROM store (comment later)
    	cardDB.writeCardIdx(0, MASTERCARD);								// MASTER card (HARD CODED)
    	cardDB.setCardTypeIdx(0, CardDB::masterCard) ;					// write to 0 index in database
    	Sprint("EEPROM: ");
    	Sprintln(EEPROM_LOCAL_CONFIG_ADDRESS, HEX) ;
    }
    
    void presentation(){
    	sendSketchInfo("AWI " NODE_TXT, "1.2");							// Sketch version to gateway and Controller
    	presentCard(CARD_CHILD) ;										// present the master card (index == 0)
    	present(CARD_ID_CHILD, S_INFO, "SwitchID " NODE_TXT);			// present the log child
    }
    
    void loop() {
        unsigned long now = millis();									// timer value for "once in a while" events
        if (now-lastUpdate > 5000UL) {									// change after 5 seconds
    		//cardDB.printDB() ;										// only for debug
    		lastUpdate = now;
    	}
    	newCard = wg.available() ;
    	if(newCard){
    		Sprint("Wiegand HEX = ");
    		Sprint(wg.getCode(),HEX);
    		Sprint(", DECIMAL = ");
    		Sprint(wg.getCode());
    		Sprint(", Type W ");
    		Sprintln(wg.getWiegandType());
    		//sendLog(wg.getCode(), "presented");
    	}
    	stateMachine.update();											// check and update non blocking
    	statusLed.update() ;
    	statusBeep.update() ;
    	}
    
    	
    /* 	State machine
    **	Each state can have functions for enter/ update / exit (defined earlier)
    */
    
    //** ILDE tate **//
    void idleEnter() {Sprintln(" idle enter") ;
    	statusLed.off() ;
    	statusBeep.off() ;
    	display.clear();													// emptdisplay
    }
    void idleUpdate(){
    	if (newCard){
    		curCard = cardDB.readCard(wg.getCode()) ;						
    		if(curCard != cardDB.maxCards){									// card found
    			if(cardDB.readCardTypeIdx(curCard) == CardDB::masterCard || cardDB.readCardTypeIdx(curCard) == CardDB::idCard) { // only open if id or master card
    				stateMachine.transitionTo(unlockState);
    			} else if (cardDB.readCardTypeIdx(curCard) == CardDB::delCard){// card found but deleted
    				display.print("Errd");
    				sendLog(wg.getCode(), "Deleted Card");
    				stateMachine.transitionTo(delayState);
    			}
     		} else {														// card not found
    			display.print("Err");
    			sendLog(wg.getCode(), "Unknown Card");
    			stateMachine.transitionTo(delayState);
    		}
    	}
    }
    
    //** DELAY state **//
    void delayEnter() {	Sprintln(" delay enter") ;
    }
    void delayUpdate() {
    	if (stateMachine.timeInCurrentState() > idleTime){
    		Sprintln(" to idle") ; stateMachine.transitionTo(idleState);
    	}
    }
    void delayExit(){Sprintln(" delay exit") ;}
    
    //** UNLOCK state **//
    void unlockEnter() {Sprintln(" unlock enter") ;
    	display.print(curCard);												// display Card index (curCard) on the display
    	Sprintln("Door unlocked");
    	sendLog(wg.getCode(), "Unlocked");
    	lockDoor(false) ; 													// Unlock the door
    	send(cardStatusMsg.setSensor(curCard).set(1));						// send update for sensor (card) to show its usage.
    }
    void unlockUpdate() {
    	if (stateMachine.timeInCurrentState() > idleTime){
    		Sprintln(" to idle") ;
    		stateMachine.transitionTo(idleState);
    		}
    	if (newCard){														// new tag
    		if(cardDB.readCardType(wg.getCode()) == CardDB::masterCard){	// master card, prepare for inclusion
    			Sprintln(" to include") ;
    			stateMachine.transitionTo(includeState);
    		}
    	}
    }
    void unlockExit(){Sprintln(" unlock exit") ;
    	lockDoor(true) ; 													// Lock the door
    	Sprintln("Door locked");
    }
    
    //** INCLUDE state
    void includeEnter() {
    	Sprintln(" include enter") ;
    	display.print("Incl");
    	statusLed.flash() ;
    	statusBeep.flash() ;
    };
    void includeUpdate(){
    	if (stateMachine.timeInCurrentState() > idleTime){
    		Sprintln(" to idle") ;
    		stateMachine.transitionTo(idleState);
    	}
    	if (newCard){														// new tag
    		curCard = cardDB.readCard(wg.getCode()) ;
    		if (curCard != cardDB.maxCards){								// known card
    			if(cardDB.readCardTypeIdx(curCard) == CardDB::masterCard){	//  master card, goto delete state
    				Sprintln(" to delete") ;
    				stateMachine.transitionTo(deleteState);
    			} else {													// known other card, so only change card type
    				cardDB.setCardTypeIdx(curCard, CardDB::idCard) ;					
    				display.clear(); display.print(curCard);
    				sendLog(wg.getCode(), "re-included");
    				presentCard(curCard); 									// present the new card as a switch and switch on
    				Sprintln(" to delay") ; stateMachine.transitionTo(delayState); // delay only to extend display time
    			}
    		} else {														// unknown card, so add in empty spot
    			curCard = cardDB.writeCard(wg.getCode()) ;
    			if(curCard == cardDB.maxCards){								//  if maxCards, database full
    				display.clear(); display.print("Full") ;
    				sendLog(wg.getCode(), "DB full");
    				Sprintln(" to delay") ; stateMachine.transitionTo(delayState); // delay only to extend display time
    			} else {													// include 
    				display.clear(); display.print(curCard);
    				sendLog(wg.getCode(), "included");
    				presentCard(curCard); 									// present the new card as a switch and switch on
    				Sprintln(" to delay") ; stateMachine.transitionTo(delayState); // delay only to extend display time
    			}
    		}
    	}
    }
    void includeExit(){
    	Sprintln(" include exit") ;
    	statusLed.off() ;
    	statusBeep.off() ;
    	}
    
    //** DELETE state
    void deleteEnter() {
    	Sprintln(" delete enter") ;
    	display.print("Del");
    	statusLed.flash() ;
    	statusBeep.flash() ;
    };
    void deleteUpdate(){
    	if (stateMachine.timeInCurrentState() > idleTime){
    		Sprintln(" to idle") ; stateMachine.transitionTo(idleState);
    	}
    	if (newCard) {
    		if(cardDB.readCardType(wg.getCode()) == CardDB::masterCard){	//  master card, goto Browse
    			Sprintln(" to browse") ;
    			stateMachine.transitionTo(browseState);
    		}else {	
    			lastCardID = wg.getCode() ;									// store code for deletion
    			Sprintln(" to Confirm") ; stateMachine.transitionTo(confirmState);
    		}
    	}
    }
    void deleteExit() {	Sprintln(" delete exit") ;}
    
    //** CONFIRM state
    void confirmEnter() { Sprintln(" confirm enter") ;
    	display.print("Conf");
    	statusLed.flash() ;
    	statusBeep.flash() ;
    }
    void confirmUpdate(){
    	if (stateMachine.timeInCurrentState() > idleTime){
    		Sprintln(" to idle") ;
    		stateMachine.transitionTo(idleState);
    	}
    	if (newCard) {
    		if(cardDB.readCardType(wg.getCode()) == CardDB::masterCard){	//  master card means confirmed
    			Sprint("Delete Card: "); Sprintln(lastCardID) ;
    			cardDB.deleteCard(lastCardID);								// delete card (lib takes care of presence)
    			sendLog(wg.getCode(), "deleted");
    			send(cardStatusMsg.setSensor(lastCardID).set(0));			// switch controller status to "off"
    			Sprintln(" to idle") ; stateMachine.transitionTo(idleState);
    		}
    	}
    }
    void confirmExit() {Sprintln(" confirm exit") ;}
    
    //** BROWSE state
    void browseEnter() {Sprintln(" browse enter") ;
    	display.print("Brws");
    	curCard = 0 ;
    	browseTimer = millis() ;
    	statusLed.off() ;
    	statusBeep.off() ;
    
    }
    void browseUpdate(){
    	unsigned long now = millis() ;
    	if (newCard) {
    		if(cardDB.readCardType(wg.getCode()) == CardDB::masterCard){	//  master card, return to IDLE
    			Sprintln(" to idle") ; stateMachine.transitionTo(idleState);
    		}
    	}
    	if (now >= browseTimer + browseTime){
    		Sprint(" browse id: ") ; Sprintln(curCard);
    		browseTimer = now ;
    		display.clear();
    		display.print(curCard);
    		display.setCursor(0,2) ;
    		if(cardDB.readCardTypeIdx(curCard)== CardDB::masterCard){ 
    			display.print("ma");
    			presentCard(curCard); 									// present the cards again when browsing (to sync controller)
    		} else if(cardDB.readCardTypeIdx(curCard)== CardDB::idCard){ 
    			display.print("id");
    			presentCard(curCard);
    		} else if(cardDB.readCardTypeIdx(curCard)== CardDB::delCard){ 
    			display.print("dl");
    			presentCard(curCard);
    		} else if(cardDB.readCardTypeIdx(curCard)== CardDB::noCard){ display.print("no");}
    		if (++curCard > cardDB.maxCards){								// idle if end of database
    			Sprintln(" to idle") ; stateMachine.transitionTo(idleState);
    		}
    	}
    }
    void browseExit(){Sprintln(" browse exit") ;}
    //** end of stae machine **//
    
    // lock / unlock the door
    void lockDoor(bool doorState){
    	digitalWrite(DOORLOCK, doorState?HIGH:LOW) ;						// Adapt for active low of any other door unlock
    }
    
    // sends a log message to the controller containing a message and cardID
    void sendLog(unsigned long cardID, const char logMessage[]){
    	char tmpBuf[26] ;													// temporary store for message
    	sprintf(tmpBuf, "Card %8lu %-10s", cardID, logMessage);				// sends unsigned long and message (be aware of message length)
    	Sprintln(tmpBuf) ;
    	send(cardIdMsg.setSensor(CARD_ID_CHILD).set(tmpBuf));
    }
    
    // present a (new) card to the controller by presenting it and switch it to state (master, id = On, deleted = Off)
    void presentCard(byte cardIdx){
    	char tmpBuf[26] ;													// temporary store for message
    	sprintf(tmpBuf, "CardId %8lu", cardDB.readCardIdIdx(cardIdx));		// convert the cardID to text
    	Sprintln(tmpBuf) ;
    	present(cardIdx, S_BINARY, tmpBuf) ;								// present the (new) card (idx == child) to controller
    	wait(50) ;															// give it some time to settle
    	send(cardStatusMsg.setSensor(cardIdx).set(cardDB.readCardTypeIdx(cardIdx)==CardDB::delCard?0:1)); // switch according to type
    }
    
    // Handle incoming messages, remote card i.e. disable/ enable
    void receive(const MyMessage &message) {  								// Expect few types of messages from controller
    	if (message.type == V_STATUS){										// Switch "off" messages are handled as deletions
    		if (message.sensor < cardDB.maxCards && message.sensor > 0){	// take care of non existing sensors and master
    			cardDB.setCardTypeIdx( message.sensor, message.getBool()?CardDB::idCard:CardDB::delCard) ;	// set type according to payload
    		}
    	}
    }
    
    My Project

  • Amazing progress...
    AWIA AWI

    I have been away/ ill for only a few months and slowly returning to the forum... I almost feel like a stranger ;-)

    and so much progress:

    • missed a great gathering in the Netherlands :-(
    • recreated web site! (I have to find my way again) :ok_hand: @hek
    • a whole new core (nothing but progress, and some learning to do) :two_hearts: @tekka & core team
    • unbelievable community support. (:thumbsup: @mfalkvidd, @sundberg84 and many (new) others)
    • mysensors workshops (:clap: @TheoL )
    • etc.
    General Discussion

  • LCD Clock and Text sensor node with new V_TEXT
    AWIA AWI

    With good cooperation of @hek and @GizMoCuz (Domoticz) a TEXT sensor is introduced. It is currently in development stage but I hope will make it to production soon. The V_TEXT variable makes simple text message transfer possible intra-node and between node and controller.

    Below a first experiment. More to follow!

    "Sensor Node" My sensorboard (can be any board or just a nano with wires):
    upload-15e45402-6839-4ff9-a32d-9fabbd1f3f35

    "LCD Display" (standard 2x16 char, I2C):
    upload-1fd2549d-5e44-4896-832b-ce642fc2e84c

    start the sketch (end of post). Activated sensor in Domoticz:
    upload-e588beb4-52a2-49ea-ad93-14fc634b99bf

    You can update the text in Domoticz with JSON (or many other ways). The last part is the text

    http://192.168.2.20:8080/json.htm?type=command&param=udevice&idx=369&nvalue=0&svalue=I %01 MySensors
    

    and on the LCD
    upload-e907075a-f500-46a2-9fbc-cebb2d3c4107

    I'm happy....

    If you like to experiment: get the development version of MySensors or just add the V_TEXT as described in the example sketch:

    /*
     PROJECT: MySensors / LCD display for text from controller
     PROGRAMMER: AWI
     DATE: september 8, 2015/ last update: september 8, 2015
     FILE: AWI_LCD_49.ino
     LICENSE: Public domain
    
     Hardware: tbd ..MYS Ceech - ATmega328p board w/ NRF24l01
    	and MySensors 1.5 ()
    		
    Special:
    	MySensors - Development (as of sept 2015)
    	
    SUMMARY:
    	display time & 2 lines of text from Controller Text sensors
    */
    
    
    #include <MySensor.h>          				// Mysensor network
    #include <SPI.h>
    #include <LiquidCrystal_I2C.h>
    #include <Time.h>   						//http://playground.arduino.cc/Code/Time
    #include <Wire.h>		         			
    
    // uncomment if you are using the DEVELOPMENT version with V_TEXT & V_INFO defined (better to use development MyMessage.h)
    //const byte V_TEXT = 47 ;					// values taken from development edition MyMessage.h
    //const byte S_INFO = 36 ;
    
    const byte nodeId = 49 ;					// MySensors fixed node id
    const byte LCD1_CHILD = 8 ;					// LCD line 1
    const byte LCD2_CHILD = 9 ;					// LCD line 2
    
    char lastLCD1[21] = "Line1 - first   ";		// define & init before first receive
    char lastLCD2[21] = "Line2 - second  ";
    
    boolean timeReceived = false ;
    // timers for loop delays
    unsigned long lastUpdate=0, lastRequest=0, lastDisplay=0;
    int display_no = 0 ; 						// current display
    
    // *** Definition and initialisation
    // define the MySensor network
    MyTransportNRF24 transport(9,10); 			// Sensoduino (8,7) Ceech board, 3.3v (7,8)  (pin default 9,10)
    MySensor gw(transport);  
    
    // Initialize messages for sensor network
    MyMessage textMsg(0, V_TEXT);
    //                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
    LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
    
    // OPTIONAL: Custom characters for display - Units)
    byte heart[8] = { B00000, B01010, B11111, B11111, B01110, B00100, B00000, B00000};
    
    void setup(void){
        //Serial in Sensor network = 115200
        gw.begin(incomingMessage, nodeId); 					// this node is 49 fixed 
        //Send the sensor node sketch version information to the gateway
        gw.sendSketchInfo("AWI_LCD text 49", "1.1");
     	gw.present(LCD1_CHILD, S_INFO, "LCD line1");		// new S_type 20150905 (not know by domoticz)
        gw.present(LCD2_CHILD, S_INFO, "LCD_line2");
    	gw.send(textMsg.setSensor(LCD1_CHILD).set("-"));	// initialize the V_TEXT at controller for sensor to none (trick for Domoticz)
    	gw.send(textMsg.setSensor(LCD2_CHILD).set("-"));		
    
    	// Initializations
        gw.requestTime(receiveTime);						// get the time from controller (handled by receiveTime)
        
        // ** LCD display **
    	Wire.begin();										// I2C
        lcd.begin(16, 2);							    	// LCD 2 lines * 16 char.
        lcd.setBacklight(HIGH);
        lcd.setCursor(0, 0);
    	lcd.createChar(1, heart);
    
        // lcd.write(byte(0)); // write units
    }
    
    void loop(void){
        // timer for loop delays
        unsigned long now = millis();
        gw.process() ;
        // If no time has been received yet, request it every 10 second from controller
        // When time has been received, request update every hour
    	if ((!timeReceived && (now-lastRequest > 10*1000)) ||	
    		(now-lastRequest > 3600000UL)){					// request update every hour to keep in sync
           // Request time from controller. 
           Serial.println("requesting time");
           timeReceived = false;
           gw.requestTime(receiveTime);  
           lastRequest = now;
    		}
        // Change display and update sensors every 5 seconds
        if (now-lastDisplay > 5000){
    		lastDisplay = now;
    		gw.request(LCD1_CHILD, V_TEXT, 0); 					// request new values from controller
    		gw.request(LCD2_CHILD, V_TEXT, 0); 					// request new values from controller
    		// change display 
    		display_no++;
    			if (display_no >= 2){ // wrap for number of different displays for second line
    			display_no = 0;
    			}
    		}
        // Update display every second
        if (now-lastUpdate > 1000) {
    		LCD_local_display();
    		lastUpdate = now;
    		}
    	}
    
    // This is called when a new time value was received
    void receiveTime(unsigned long controllerTime) {
        Serial.print("Time value received: ");
        Serial.println(controllerTime);
        setTime(controllerTime); 							// time from controller
        timeReceived = true;
    	}
    
    // This is called when a message is received 
    void incomingMessage(const MyMessage &message) {
    	if (message.type==V_TEXT) {							// Text messages only
         // Write some debug info
         Serial.print("Message: "); Serial.print(message.sensor); Serial.print(", Message: "); Serial.println(message.getString());
    	if (message.sensor == LCD1_CHILD ) {
    		snprintf(lastLCD1, sizeof(lastLCD1), "%16s", message.getString());	// load text into LCD string
    		}
    	else if (message.sensor == LCD2_CHILD){
    		snprintf(lastLCD2, sizeof(lastLCD2), "%16s", message.getString());
    		}
    	} 
    }
    
    void LCD_local_display(void){
    // take car of LCD display information
        char buf[17]; // temp buffer for max 16 char display
        // start with location & time on first line
        snprintf(buf, sizeof buf, "%02d:%02d:%02d %02d-%02d", hour(), minute(), second(), day(), month());
        lcd.setCursor(0, 0);
        lcd.print(buf);
        lcd.setCursor(0, 1);
        if (display_no == 0){
          lcd.print(lastLCD1); 							// second line is text value from controller
        } else { 										// display == 2
          lcd.print(lastLCD2);
        }
    }
    
    My Project
  • Login

  • Don't have an account? Register

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