Z-Uno Tricks & Hacks. Dynamic channels. N-channel relay(switch) sketch.

Discussion about Z-Uno product. Visit http://z-uno.z-wave.me for more details.
Post Reply
p0lyg0n1
Posts: 242
Joined: 04 Aug 2016 07:14

Z-Uno Tricks & Hacks. Dynamic channels. N-channel relay(switch) sketch.

Post by p0lyg0n1 »

Hi,
I'll try to share with community some kind of my test-code/sketches that I collected/wrote during the work on Z-Uno project. It could be helpful for some cases. In most cases I have not time to make a full-size tutorial or a blog posts. So, it will be a brief format of the forum posts. The main target of them is to make the most non evident features of Z-Uno available to the large number of users.
It's a first take.
The first thing I like to talk about is dynamic channels.
It was available from version 2.1.0. There are two publicated examples available that use this approach:
1. http://z-uno.z-wave.me/examples/z-uno-as-a-modem/
2. http://z-uno.z-wave.me/examples/multipl ... e-sensors/
The main advantage is that you can use the same code for a lot of channels instead of making getter/setter for every channel.
Lets look to my code that use this approach to create a multichannel binary switch on all pins of Z-Uno. Here is the code

Code: Select all

/*
  This sketch provide a method to control up to 21 
  switch. You have to configure the number or channels you
  want via parameter #64. Default value is 4. 
  You can use this to control open drain controlling device. 
  For this purpose you have to setup parameter #65 to 0. 
  It switches normal/inverse logic. 
    0 = inverse (open drain) logic, 
    1 = normal logic
  This sketch uses dynamic approach of channel definition
  to decrease code space and demonstrate how to use it.
  See setup() & zunoCallback() functions for details.
  (c) Z-Wave>ME 2017
 */

#define DEFAULT_NUMBER_OF_CHANNELS 4
#define DEFAULT_CHANNEL_LOGIC      1  
byte  number_of_channels = DEFAULT_NUMBER_OF_CHANNELS; // Default
byte  channel_logic      = DEFAULT_CHANNEL_LOGIC;
dword  tmp; 
// pin mapping
byte  pin_mapping[]={0, // ZUNO GPIO #0 
                      1, // ZUNO GPIO #1  
                      2, // ZUNO GPIO #2 
                      // -- WE SKIP UART0, LEAVE IT FOR DEBUG -- 
                      // UNCOMMENT IF NEEDED
                      // 24, // TX0
                      // 25, // RX0
                      // --------------------
                      3, // ZUNO A0
                      4, // ZUNO A1
                      5, // ZUNO A2
                      6, // ZUNO A3
                      7, // ZUNO GPIO #7
                      8, // ZUNO GPIO #8
                      9, // ZUNO GPIO #9
                      10,// ZUNO GPIO #10 
                      11,// ZUNO GPIO #11
                      12,// ZUNO GPIO #12
                      13,// ZUNO PW1
                      14,// ZUNO PW2
                      15,// ZUNO PW3
                      16,// ZUNO PW4
                      17,// ZUNO GPIO 17
                      // -- WE SKIP INT1, IT ALWAY PULLED UP --
                      // IF YOU NEED NC PIN - YOU CAN UNCOMMENT IT
                      // 18, // INT1
                      // --------------------
                      19,
                      20,
                      21,
                      22,
                      // -- WE SKIP BTN
                      // IF YOU NEED NC PIN - YOU CAN UNCOMMENT IT
                      // 23, // BTN
                      // --------------------
                      
                      };
byte pin_states[sizeof(pin_mapping)];

// the setup function runs once, when you press reset or power the board
void setup() {
  byte i;
  // Loading configuration data...
  zunoLoadCFGParam(64,&tmp); // Here we can use dword, but byte is cheaper
  number_of_channels = tmp;
  zunoLoadCFGParam(65,&tmp); // Here we can use dword, but byte is cheaper
  channel_logic = tmp;
 
  // Check if value of param is valid
  if(number_of_channels > sizeof(pin_mapping) || number_of_channels == 0)
  {
    number_of_channels = DEFAULT_NUMBER_OF_CHANNELS;
  }
  if((channel_logic != 0) && (channel_logic != 1))
  {
    channel_logic = DEFAULT_CHANNEL_LOGIC;
  }
  // we use "off" by default
  // you can use EEPROM library to load/save default states
  memset(pin_states, 0, number_of_channels);
  // Dynamic config...
  ZUNO_START_CONFIG();
  for(i=0;i<number_of_channels;i++)
  {
    // add Z-Wave channel
    ZUNO_ADD_CHANNEL(ZUNO_SWITCH_BINARY_CHANNEL_NUMBER, 0, 0)
    // set pin to output
    pinMode(pin_mapping[i], OUTPUT);
    digitalWrite(pin_mapping[i],1); //channel_logic ? pin_states[i]: !(pin_states[i])); 

  }
  ZUNO_COMMIT_CONFIG();
}
// the loop function runs over and over again forever
void loop() {
  // We don't need loop here...
  // All logic is located in zunoCallback
}
void zunoCallback(void)
{
    // See callback_data variable 
    // We use zero based index of the channel instead of 
    // typical Getter/Setter index of Z-Uno. 
    // See enum ZUNO_CHANNEL*_GETTER/ZUNO_CHANNEL*_SETTER in ZUNO_Definitions.h
    byte index = (callback_data.type & 0x03F);
    index >>= 1;
    // Configuration parameter was changed?
    if(callback_data.type & CONFIG_DATA_FLAG)
    {
        if(callback_data.type & SETTER_BIT)
        {
          switch(index)
          {
              case 0:
              number_of_channels = callback_data.param.bParam;
              if(number_of_channels > sizeof(pin_mapping) || number_of_channels == 0)
                number_of_channels = DEFAULT_NUMBER_OF_CHANNELS;
              break;
              case 1:
              channel_logic =  callback_data.param.bParam;
              if((channel_logic != 0) && (channel_logic != 1))
                channel_logic = DEFAULT_CHANNEL_LOGIC;
              break;
              default:
              break;
          }  
          zunoSaveCFGParam(64+index,&(callback_data.param.dwParam)); 
        }
        else
        {
          zunoLoadCFGParam(64+index,&(callback_data.param.dwParam));  
        }
        return;
    }
    // It's a device channel event
    if(callback_data.type & SETTER_BIT)
    {
       pin_states[index] =  (callback_data.param.bParam != 0);
       digitalWrite(pin_mapping[index], channel_logic ? pin_states[index]: !(pin_states[index])); 
    }
    else
    {
       callback_data.param.bParam = pin_states[index];
    }
    
}
The main thing here is the function "zunoCallback". It's the function receives all the data about Z-Wave that Z-Uno gets from the network. In non-dynamical sketches this function will be generated using your ZUNO_SETUP_CHANNELS macro by uCxx translator. Lets take a look to the most simplest variant of zunoCallback. You can see this for example in the RadioBlink.ino(see built-in examples for Z-Uno) if you will go to the build directory. There you could find file "_RadioBlink_ino.cpp" that was generated from "RadioBlink.ino". The content of it will be like this:

Code: Select all

#include "Arduino.h"
void setup();

BYTE getSwitchMultilevelValue();

void setSwitchMultilevelValue(BYTE newValue);

void loop();

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.

  Z-Uno has an on-board LED you can control. It is attached to digital pin 13.
  LED_BUILTIN is an internal Define which can be used to access this LED.
  This example demonstrates the simple blink programm, where the blink interval can be changed from the Z-Wave controller.
  More information on http:

 */


byte dimmerValue=100;


// ZUNO_SETUP_CHANNELS(ZUNO_SIREN(getSwitchMultilevelValue,setSwitchMultilevelValue));


void setup() {
  
  pinMode(LED_BUILTIN, OUTPUT);
  dimmerValue = 100;
}


void loop() {
  digitalWrite(LED_BUILTIN, HIGH); 
  delay(dimmerValue*10);           
  digitalWrite(LED_BUILTIN, LOW);  
  delay(dimmerValue*10);           
}


void setSwitchMultilevelValue(byte newValue) {
  
  dimmerValue = newValue;
}


byte getSwitchMultilevelValue(void) {
  
  return dimmerValue;
}

//  *********** uCxx section **********

// zunoCallback() was generated automatically by uCxx 

void zunoCallback()
{
	switch(callback_data->type)
	{
		case ZUNO_CHANNEL1_GETTER:
			callback_data->param.bParam = getSwitchMultilevelValue();
			break;
		case ZUNO_CHANNEL1_SETTER:
			setSwitchMultilevelValue(callback_data->param.bParam);
			break;
		default: 
			break;
	}
}


// __zuno_autosetup() was generated automatically by uCxx 

void __zuno_autosetup()
{
	ZUNO_START_CONFIG();
	ZUNO_ADD_CHANNEL(ZUNO_SIREN_CHANNEL_NUMBER, 0, 0);
	ZUNO_COMMIT_CONFIG();
}

//  *********************************
The code inside the zunoCallback() looks simply, doesn't it? :)
If we compare it with the code of n-channel switch some of things will become evident (you can take a look to core-files for more information about channel constants and etc). The main advantage of n-channel switch code listed bellow is size. It needs only 1,5k of code for 26-channel relay. It sounds good, doesn't it? You can use the same approach to reduce & simplify your code for projects when you need a number of the same channels. Another advantage of these code is setup of number of channels from Z-Wave controller without reflashing of the sketch.
sega66
Posts: 38
Joined: 30 Jul 2017 19:31

Re: Z-Uno Tricks & Hacks. Dynamic channels. N-channel relay(switch) sketch.

Post by sega66 »

Thank You!
This is a great example! If can be - continue
Post Reply