This build introduces a lot of changes, new cool features and fixes.
You can get it from our test repo: https://z-uno.z-wave.me/files/z-uno/pac ... index.json. For more infor check https://z-uno.z-wave.me/install
What's new:
New features and changes
- New style of channels mapped to a variable directly - very easy to use. Old syntax with getter/setter is working too for more flexible handling
- Fast pins can be used on other pins than 9-16
- New format of dynamic channels - much more flexible
- New channel mapping to channel 0
- Do not follow the new channel mapping to channel 0: if both Switch binary and Switch Multilevel are only present outside of channels some controllers (like Fibaro Home Center) will show only Switch Multilevel. To solve this use macro ZUNO_DISABLE(NO_MULTIBINARY_SWITCH0);
- System events are now reported to the sketch too - you can handle inclusion start/stop, firmware update start, system faults
- New system 10ms timer
- Easier handlers of external reports predefined for most cases
- Configuration parameters interface changed a bit to use less space and be easier to use
- New thermostat modes
- Possibility to change Product Type Id and Prodyuct Id
- Autoinclusion (NWI) added - Z-Uno will automatically include on power on if not in network (mains powered only)
- Bootloader upgrade do not reset the frequency to default
- S0 Security mode set via Z-Wave
- You can now select which S2 security level to use: Unauthenticated, Authenticated or Access
- You can now write to the EEPROM/NVM using USB - easy way to pre-load some specific data to the EEPROM to be used in the sketch (like fonts for the e-ink screen or some conversion table
- Most of Z-Wave Command Classes available for the user moved into user code space to free more space - you can even contribute to them via GitHub
- New ZUNO_Buttons library for easier buttons handling
- New ZUNO_LEDS library allows easy control of LEDs
- New ZUNO_LCD library to work with LCDs
- New ZUNO_Stepper library for step motors
- New ZUNO_IAQCore library for air quality sensors
- Z-Uno boots from INT1 2.5 times faster: 57 ms from interrupt to enter in setup()
- Bootloader/sketch upload via USB is now faster
- Rescue mode is not shown as red LED blink instead of green
- Maximum size of user code in S2 mode rised up to 12 kB
- No need to set delay() in the loop - there is now an intrinsic delay(20). You can always disable it using a macro: ZUNO_ENABLE(NO_LOOP_MINIMALDELAY);
- Fixed send queue filling problem with too many reports
- Wrong Wakeup report fixed
- Fixed Z-Wave Plus Info for FLiRS
- Fixed wakeups in FLiRS mode and sleeps on low INT1
- Fixed speed of SoftwareSerial, OneWire, DHT when USB is connected
- Fixed Thermostat unsolicited report
- Fixed OTA confirmation with param #20
- Fixed negative temperature in ZUNO_DHT
- Fixed memory overflow in ZUNO_DS18B20 autodetect on number of sensors greater than defined in the library
- Fixed zunoFastPWMSet
New style of channels mapped to a variable directly
In addition to the getter/setter concept you can now use direct mapping of channel values to a variable. This simplifies the code and is much better in most cases (this is not working for thermostats and switchcolor)
Have a look on the following example with SwitchMultilevel:
Getter/setter concept:
Code: Select all
byte dimmerValue = 100;
ZUNO_SETUP_CHANNELS(ZUNO_SWITCH_MULTILEVEL(getSwitchMultilevelValue, setSwitchMultilevelValue));
...
void setSwitchMultilevelValue(byte newValue) {
dimmerValue = newValue;
}
byte getSwitchMultilevelValue(void) {
return dimmerValue;
}
Code: Select all
ZUNO_SETUP_CHANNELS(ZUNO_SWITCH_MULTILEVEL(dimmerValue, NULL));
byte dimmerValue = 100;
...
With the new direct mapping concept you can detect new value set using function zunoIsChannelUpdated(CH_NUM) that returns True if new value was set via Z-Wave.
For the example above:
Code: Select all
void loop() {
if (zunoIsChannelUpdated(1)) {
// Do something on new value received via Z-WAve
}
}
Those allows to either remove some features from the sketch or add them for dynamic channels.
For dynamic channels you need to define every user Command Class that might be used.
Other common cases:
- For a binary sensor you can suppress SensorBinary or Notification using ZUNO_DISABLE(WITH_CC_SENSOR_BINARY). By default both are in.
- Any user macro that is used in the library (for debugging):
example 1: ZUNO_ENABLE(MY_LIBRARY_DEBUG);
example 2: ZUNO_ENABLE(MY_LIBRARY_DEBUG MY_LIBRARY_DEBUG_LEVEL=1); - System log to SerialL ZUNO_ENABLE(LOGGING_EVENTS LOGGING_UART=Serial);
- Disable Service LEDs: ZUNO_DISABLE(SERVICE_LEDS); - to save battry and remove unwanted blinks
Macro ZUNO_REMAP_SPINS(portName), where portName:
- FAST_P0 - (default) pins 9-16, for I2C: SCL=9 SDA=10
- FAST_P1 - pins 17-23, for I2C: SCL=17 SDA=18
- FAST_P2 - pins 0-2, for I2C: SCL=0 SDA=2
- FAST_P3 - pins 3-6, for I2C: SCL=3 SDA=4
Now you need to define the max number of dynamic channels that can appear in Z-Uno during runtime. Do it with macro ZUNO_DYNAMIC_CHANNELS(NUM_CHANNELS). This will save memory if you don't need all 32
In runtime you can get it in variable g_channels_data.
Values of channels are referred via:
- for one byte channels like SwitchBinary/SwitchMultilevel/SensorBinary/FlowStop/Siren/Doorlock/Blinds: g_channels_data[CH_NUMBER].bParam
- for Meter/SensorMultilevel depending on the size of the value in the channel: g_channels_data[CH_NUMBER].bParam, g_channels_data[CH_NUMBER].wParam or g_channels_data[CH_NUMBER].dwParam
- for SwitchColor: g_channels_data[CH_NUMBER].buffParam[color], color is SWITCH_COLOR_COMPONENT_* constant
- for Thermostat: g_channels_data[CH_NUMBER].thermoParam.mode, g_channels_data[CH_NUMBER].thermoParam.temperature, i = 0 for Heat, i = 1 for Cool (for Auto, otherwise only [0] is used)
You can check if new value arrived using zunoIsChannelUpdated()
Sources are much more readable now. See our GitHub: https://github.com/Z-Wave-Me/Z-Uno-Test ... annel_2.15
New channel mapping to channel 0
If the channel appears only once, it will not be inside MultiChannel, but rather on the root (channel 0) device only. Additional channels are not created anymore for that case.
In case of more than one channel of the same type channel 0 is mapped to the first (as before).
All SensorMultilevel of different types are aggregated in the one channel. If more than one SensorMultilevel of the same type is defined, a new channel will be created.
This will make mostly non-channeled devices in many cases and simplify Z-Uno representation in most controllers.
If both Switch binary and Switch Multilevel are only present outside of channels some controllers (like Fibaro Home Center) will show only Switch Multilevel. To solve this use macro ZUNO_DISABLE(NO_MULTIBINARY_SWITCH0);
System events
In 2.1.4 system events were logged only to the Serial0 or via Z-Wave configuration parameter #48. Now you can also catch them in the code.
- Inclusion start/stop to notify the user
- Firmware update start to notify the user
- Z-Uno boot and go into sleep to make special handling
- System faults like stack overflow or blocked packets due to the policy to debug
Code: Select all
ZUNO_SETUP_SYSEVENT_HANDLER(sys_event);
void sys_event(byte event, byte param) {
switch(event) {
case ZUNO_LOGGING_EVENT_LEARNSTART:
// … <ACTIONS>
break;
case ZUNO_LOGGING_EVENT_LEARNCOMPLETE:
// … <ACTIONS>
break;
case ZUNO_LOGGING_EVENT_FWAUTH:
// … <ACTIONS>
break;
case ZUNO_LOGGING_EVENT_STACKOVERFLOW:
// … OOOPS! Error handling
break;
}
}
New system 10ms timer
10 ms time for easier async handling of buttons, LEDs and encoders
Code: Select all
ZUNO_SETUP_SYSTIMER_HANDLER(sys_timer);
void sys_timer(){
digitalToggle(13);
}
New style of handlers for each type of report:
Code: Select all
ZUNO_REPORTS_HANDLER(Battery, BatteryReport);
ZUNO_REPORTS_HANDLER(SensorMultilevel, SensorMultilevelReport);
ZUNO_REPORTS_HANDLER(SensorBinary, SensorBinaryReport);
ZUNO_REPORTS_HANDLER(Notification, NotificationReport);
ZUNO_REPORTS_HANDLER(SwitchMultilevel, SwitchMultilevelReport);
ZUNO_REPORTS_HANDLER(SwitchBinary, SwitchBinaryReport);
ZUNO_REPORTS_HANDLER(Meter, MeterReport);
ZUNO_REPORTS_HANDLER(Basic, BasicReport);
// dbg
#define LOGGER_SERIAL Serial0
ZUNO_ENABLE(LOGGING_DBG LOGGING_EVENTS);
void logReportTitle(char * text) {
LOGGER_SERIAL.print(millis());
LOGGER_SERIAL.print(" ");
LOGGER_SERIAL.print(text);
LOGGER_SERIAL.print(" NODE:");
LOGGER_SERIAL.print(REPORT_NODE_ID());
LOGGER_SERIAL.print(" VALUE:");
}
void BatteryReport() {
logReportTitle("BATTERY");
LOGGER_SERIAL.println(REPORT_BATTERY_VALUE(), HEX);
}
void SensorMultilevelReport() {
logReportTitle("SENSOR MULTILEVEL");
switch(REPORT_SENSOR_MULTILEVEL_SIZE()) {
case 1:
LOGGER_SERIAL.fixPrint(int(REPORT_SENSOR_MULTILEVEL_VALUE_1B()), REPORT_SENSOR_MULTILEVEL_PRECISION());
break;
case 2:
LOGGER_SERIAL.fixPrint(int(REPORT_SENSOR_MULTILEVEL_VALUE_2B()), REPORT_SENSOR_MULTILEVEL_PRECISION());
break;
case 4:
LOGGER_SERIAL.fixPrint(long(REPORT_SENSOR_MULTILEVEL_VALUE_4B()), REPORT_SENSOR_MULTILEVEL_PRECISION());
break;
}
LOGGER_SERIAL.print(" TYPE:");
LOGGER_SERIAL.print(REPORT_SENSOR_MULTILEVEL_TYPE(), HEX);
LOGGER_SERIAL.print(" SCALE:");
LOGGER_SERIAL.println(REPORT_SENSOR_MULTILEVEL_SCALE(), HEX);
}
void SensorBinaryReport() {
logReportTitle("SENSOR BINARY");
LOGGER_SERIAL.print(REPORT_SENSOR_BINARY_VALUE(), HEX);
LOGGER_SERIAL.print(" TYPE:");
LOGGER_SERIAL.println(REPORT_SENSOR_BINARY_TYPE());
}
void SwitchBinaryReport() {
logReportTitle("SWITCH BINARY");
LOGGER_SERIAL.println(REPORT_SWITCH_BINARY_VALUE(), HEX);
}
void SwitchMultilevelReport() {
logReportTitle("SWITCH MULTILEVEL");
LOGGER_SERIAL.println(REPORT_SWITCH_MULTILEVEL_VALUE(), HEX);
}
void NotificationReport() {
logReportTitle("NOTIFICATION ");
LOGGER_SERIAL.print(REPORT_NOTIFICATION_STATUS(), HEX);
LOGGER_SERIAL.print(" TYPE:");
LOGGER_SERIAL.print(REPORT_NOTIFICATION_TYPE(), HEX);
LOGGER_SERIAL.print(" EVENT:");
LOGGER_SERIAL.print(REPORT_NOTIFICATION_EVENT(), HEX);
LOGGER_SERIAL.print(" PARAM:");
LOGGER_SERIAL.println(REPORT_NOTIFICATION_EVENT_PARAM(0), HEX);
}
void MeterReport() {
logReportTitle("METER");
switch(REPORT_METER_SIZE()){
case 1:
LOGGER_SERIAL.fixPrint(int(REPORT_METER_VALUE_1B()), REPORT_METER_PRECISION());
break;
case 2:
LOGGER_SERIAL.fixPrint(int(REPORT_METER_VALUE_2B()), REPORT_METER_PRECISION());
break;
case 4:
LOGGER_SERIAL.fixPrint(long(REPORT_METER_VALUE_4B()), REPORT_METER_PRECISION());
break;
}
LOGGER_SERIAL.print(" TYPE:");
LOGGER_SERIAL.print(REPORT_METER_TYPE(), HEX);
LOGGER_SERIAL.print(" SCALE:");
LOGGER_SERIAL.println(REPORT_METER_SCALE(), HEX);
}
void BasicReport() {
logReportTitle("BASIC");
LOGGER_SERIAL.println(REPORT_BASIC_VALUE(), HEX);
}
void setup() {
LOGGER_SERIAL.begin(115200);
LOGGER_SERIAL.print("START");
}
void loop() {
delay(1000);
}
In 2.1.4 and earlier it was:
Code: Select all
ZUNO_SETUP_CFGPARAMETER_HANDLER(config_parameter_changed);
void config_parameter_changed(byte param, word * value){
// …
}
void setup() {
word tmp;
byte my_param;
// loading of the parameter
// Prototype: void zunoLoadCFGParam(byte num, word * value);
zunoLoadCFGParam(MY_CFG_PARAM_NUM,&tmp);
my_param = tmp;
// saving of the parameter
// Prototype: void zunoSaveCFGParam(byte num, word * value);
tmp = my_param;
zunoSaveCFGParam(MY_CFG_PARAM_NUM, &tmp);
}
Code: Select all
ZUNO_SETUP_CFGPARAMETER_HANDLER(config_parameter_changed);
void config_parameter_changed(byte param, word value){
// …
}
void setup() {
// loading of the parameter
// Prototype: word zunoLoadCFGParam(byte num);
uint8_t my_param = zunoLoadCFGParam(MY_CFG_PARAM_NUM);
// saving of the parameter
// Prototype: zunoSaveCFGParam(byte num, word value);
zunoSaveCFGParam(MY_CFG_PARAM_NUM, my_param);
}
- THERMOSTAT_MODE_FURNACE
- THERMOSTAT_MODE_DRY
- THERMOSTAT_MODE_MOIST
- THERMOSTAT_MODE_FULL_POWER
- THERMOSTAT_MODE_AUTO changed to THERMOSTAT_MODE_AUTO_CHANGEOVER
Before 2.1.5 all Z-Uno sketches were detected ProductType/ProductId 0x0110/0x0001. This lead to problems in some controllers that had a template for specific devices - two different Z-Uno were detected using the same UI template.
From 2.1.5 there are more options for ProductType/ProductId:
- 0x0110/0x0001 - default
- 0x0111/0x0000-0xffff - user defined Product ID - use this option if you want to make own templates for skethes for your controllers - it is not recommended to share them with the community as other people can use same Type/Id. Example: for Id 0xAABB
Code: Select all
ZUNO_SETUP_PRODUCT_ID(0xAA, 0xBB);
- 0x0112/(automatic) - Z-Uno autogenerated Product Id based on channels types - use this if you are making a template that you want to share with other people. Sketches with same sequence of channels will share same template and hence will look in the same way. Only channel types and their order is used to generate the automatic Id. Example:
Code: Select all
ZUNO_SETUP_PRODUCT_AUTO();
- Custom ManufacturerId/ProductType/ProductId - used only in agreement with Z-Wave.Me team and requires full Z-Wave certification - for own device manufacturing based on Z-Uno Module.
Before the S0 security mode was possible to change using Arduino IDE. Now it can be changed using parameter #7 (1 - enabled, 0 - disabled). Re-inclusion required.
You can now select which S2 security level to use: Unauthenticated, Authenticated or Access
Example:
Code: Select all
ZUNO_SETUP_S2ACCESS(SKETCH_FLAG_S2_UNAUTHENTICATED_BIT|SKETCH_FLAG_S2_AUTHENTICATED_BIT);
- SKETCH_FLAG_S2_UNAUTHENTICATED_BIT
- SKETCH_FLAG_S2_AUTHENTICATED_BIT
- SKETCH_FLAG_S2_ACCESS_BIT
- read all user date to file
Code: Select all
compiler dumpNVM -d <Z-Uno port> -f <file.hex/bin>
- write all user data from file
Code: Select all
compiler restoreNVM -d <Z-Uno port> -f <file.hex/bin>
- write all user data from file to address 0x1000 and upper - then you can access it from the code using
Code: Select all
compiler restoreNVM -d <Z-Uno port> -a 1000 -f <file.hex/bin>
Code: Select all
EEPROM.read(0x1000)
This allowed us to free more space for your code. And you can even contribute to this part now - fork us on GitHub
https://github.com/Z-Wave-Me/Z-Uno-Core ... pport.ucxt
New ZUNO_Buttons library for easier buttons handling
You can use this library with handling in the loop() or with system 10ms timer
Example with handling in the loop() - might be problematic if the sketch is doing a lot of work in the loop().
Code: Select all
#include <ZUNO_Buttons.h>
#define BTN_PIN 0
PinButton btn1(BTN_PIN, true);
void setup() {
Serial.begin(115200);
pinMode(BTN_PIN, INPUT_PULLUP);
}
void loop() {
btn1.update();
btn1.startDispatch();
if(btn1.isSingleClick()){
Serial.println("Single click");
}
if(btn1.isLongClick()){
Serial.println("Long click");
}
if(btn1.isDoubleClick()){
Serial.println("Double click");
}
btn1.endDispatch();
delay(20);
}
Code: Select all
#include <ZUNO_Buttons.h>
#define BTN_PIN 0
PinButton btn1(BTN_PIN, true);
ZUNO_SETUP_SYSTIMER_HANDLER(sys_timer);
void sys_timer(){
btn1.update();
}
void setup(){
Serial.begin(115200);
pinMode(BTN_PIN, INPUT_PULLUP);
}
void loop() {
btn1.startDispatch();
if (btn1.isSingleClick()) {
Serial.println("Single click");
}
if (btn1.isLongClick()) {
Serial.println("Long click");
}
if (btn1.isDoubleClick()) {
Serial.println("Double click");
}
btn1.endDispatch();
delay(20);
}
Example:
Code: Select all
#include <ZUNO_LEDS.h>
enum {
LED_MODE_NWI,
LED_MODE_HEARTBEAT,
LED_MODE_CLOSED,
LED_MODE_FWAUTH,
LED_MAX_MODES
};
LedMode ValveLEDModes[] = {
{8, 0xAAAAAAAA},
{32, 0x00000003},
{8, 0xF000F000},
{8, 0x00A00A00}
};
ZUnoLED led1(VALVE_LED_PIN, ValveLEDModes, LED_MAX_MODES);
void sys_timer(){
led1.tick();
}
void sys_event(byte event, byte param) {
switch(event) {
case ZUNO_LOGGING_EVENT_LEARNSTART:
led1.setCustomMode(LED_MODE_NWI);
break;
case ZUNO_LOGGING_EVENT_LEARNCOMPLETE:
led1.off();
break;
}
}
void setup() {
led1.setInverted(TRUE);
}
void loop() {
}
Появилась официальная библиотека ZUNO_LCD.
This library looks pretty much like Serial.
Code: Select all
#include <ZUNO_LCD.h>
LCD_I2C lcd(20,4, 0x27);
void setup() {
lcd.begin();
lcd.print("Z-Uno LCD TEST");
}
void loop() {
lcd.gotoXY(2, 1);
lcd.print("Time: ");
lcd.print(millis());
delay(100);
}
Examples on the forum.
New ZUNO_IAQCore library for air quality sensors
Examples on the forum. Thanks to @michap for porting it.