In some cases, a solution may be running a zip client locally. In these cases, perhaps you wish to prevent zip traffic from leaving the gateway.
The quickest solution to keeping zip traffic on the gateway, is to take the default lan configuration, and simply delete the ethernet port from the br-lan, leaving all other configuration constant.
Make sure your zipgateway is configured for local-only mode
#reconfigure the zipgateway package
sudo dpkg-reconfigure zipgateway
On the 'Enable Wireless configuration of Z/IP Gateway' screen, select 'wired'
Remove your LAN interface from /etc/network/interfaces.d/br-lan created by the zipgateway package, for example in ubuntu my setup has ethernet interface 'enp0s3' change this to 'none':
auto br-lan
iface br-lan inet dhcp
bridge_stp on
# bridge_ports enp0s3 # <- The original line installed by zipgateway
bridge_ports none
bridge_fd 2
Restart networking
sudo systemctl restart networking
Restart zipgateway
sudo service zipgateway restart
Because the zipgateway controls ipv6 address assignment, a local zip client should now be able to use ipv6 addresses to control end nodes.
But what if you need IPv4 control? You will need to run a dhcp4 server on your gateway. In this example, I am using isc-dhcp-server
Download a dhcp server
sudo apt update; sudo apt install isc-dhcp-server
Bind the isc-dhcp-server to the br-lan by modifying /etc/default/isc-dhcp-server and adding to the "INTERFACESv4" entry.
INTERFACESv4="br-lan"
Using the bridge as our binding interface gives us an added benifit of a constant interface name. If we were to try and rely on our tap in this case, then if zipgateway changes the tap name we run into problems of our dhcp server failing to bind to the correct interface.
If you're feeling adventurous, you can leave this as "", but be advised, if any interface gets an address in a range you specify in dhcpd.conf (later) you will begin serving dhcp requests on that interface. Probably not what you want.
In order to host dhcp on an interface, that interface needs a static IP address. Let's modify /etc/network/interfaces.d/br-lan to use a static address; Pick your favorite local subnet, here - I've used 192.168.123.X/24; Note that we change the top line to 'static' rather than 'dhcp'
auto br-lan
#iface br-lan inet dhcp # <- the old line we're replacing
iface br-lan inet static
bridge_stp on
# bridge_ports enp0s3
bridge_ports none
bridge_fd 2
#modifications for static ip4 mode:
address 192.168.123.1
broadcast 192.168.123.255
netmask 255.255.255.0
gateway 192.168.123.1
Now that our dhcp server has a static address, let's configure a range for our zip devices...
add the following to /etc/dhcp/dhcpd.conf; Be sure your range matches a subnet that your static address is in
Note that 200-1 = 199 ipv4 addresses; Not enough ipv4 addresses if you were to have the maximum 2^8=255 nodes; If you need ipv4 addresses for every possible node ID, you will want to change your netmask above to 255.255.254.0 and be aware that your static address should also widen its netmask
Next, enable the dhcp server on reboots
sudo systemctl enable isc-dhcp-server
At this point, you should reboot and confirm everything is working. You should now be able to control zip nodes with ipv4 addresses.
The Silicon Labs EFR32 family of IoT microcontrollers are very flexible and can do a ton of cool stuff. However, along with all that flexibility comes a lot of complexity. With that complexity are default settings that work fine for many applications but in some cases you want to dig into the details to come up with an optimal solution. In this post I’ll show how to speed up the wake up time for the Z-Wave ZGM130S chip from a GPIO.
But first – a caveat: This post applies to Z-Wave SDK 7.13.x. Future releases of the SDK may have different methods for sleep/wake and thus may require a different solution.
The Problem
Frequently Listening Routing Slaves (FLiRS) devices like door locks and many thermostats spend most of their time in Energy Mode 2 (EM2) to conserve battery power. Once per second they wake up briefly and listen for a Beam from an always-on device. If there is a beam, the FLiRS device will wakeup and receive the Z-Wave command. This allows battery powered devices to use very little power but still be able to respond to a Z-Wave command within one second. FLiRS devices use more battery power than fully sleeping devices like most sensors which use Hibernate Sleep mode (EM4). To wake every second the ZGM130 has to wake quickly and go right back to sleep to minimize power. The problem with EM4 is that it takes a few tens of milliseconds to wake up as the entire CPU and RAM have to be initialized as they were powered down to save power. For a FLiRS device, it’s more efficient to keep RAM powered but in a low-power state and resume quickly to go right back to sleep if there is no beam. Typically the ZGM130 can wake up in about 500 microseconds from EM2. But in many cases this is still too long of a time to stay awake if there are other interrupts such as UARTs or other sensors.
The scope shot above shows the processing that takes place by default on the ZGM130S. In this case I am using a WSTK to drive the SPI pins of another WSTK running the DoorLockKeyPad sample application. The chip is in EM2 at the start of the trace. When SPISEL signal goes low, the chip wakes up. But it is running on the HFRCO oscillator which is not accurate enough to run the radio but it is stable and usable in just a few microseconds. Thus, the SPI clock and data is captured in the USART using this clock. However, by default the Interrupt Service Routine is blocked waiting for the HFXO to stabilize. The 39MHz HFXO crystal oscillator has the accuracy required for the radio.
The question is what’s going on during this 500usec? The answer is the CPU is just waiting for the HFXO to stabilize. Can we use this time to do some other work? Fortunately, the answer is YES! The challenge is that it takes some understanding and some code which I’ll describe below.
The Solution
There are three functions that do the majority of the sleep processing. These are provided in source code so you can read the code but you should not change it. Instead you’ll provide a callback function to do your processing while the chip is waking up.
Simplified Sleep Processing Code:
SLEEP_Sleep in sleep.c: The main function called to enter sleep
CORE_ENTER_CRITICAL – PRIMASK=1 mask interrupts
DO-WHILE loop
Call enterEMx() – this is where the chip sleeps
Call restoreCallback (return 0 to wake, 1 to sleep)
Call EMU_Restore – waits for HFXO to be ready ~500us
CORE_EXIT_CRITICAL – ISRs will now run
enterEMx() in sleep.c:
sleepCallback called
Call EMU_EnterEM[1-4]
wakeupCallback after returning from EMU_EnterEMx
EMU_EnterEM2 in em_emu.c:
Scales voltage down
Call EMU_EM23PresleepHook()
__WFI – Wait-For-Interrupt instruction – ZGM130 sleeps here
Call EMU_EM23PostsleepHook() ~ 17usec after wakeup
Voltage Scale restored which takes ~20us
The code is in sleep.c in the SDK which has a lot more detail but at a high level this is what you need to know. The important part to understand here is where the “hooks” are and how to use them.
Use Sleep_initEx() to assign:
sleepCallback – called just before sleeping
restoreCallback – Return 0 to wake, 1 to sleep
wakeupCallback – called after waking
Sleep_initEx() input is a pointer to a structure with the three callbacks or NULL if not used
Define the function:
EMU_EM23PresleepHook()
EMU_EM23PostsleepHook()
These are both WEAK functions with nothing in them so if you define them then the compiler will install them
The two EMU_EM23* weak functions are run immediately before/after the Wait-For-Interrupt (WFI) instruction which is where the CPU sleeps. These are very low level functions and while you can use them I recommend using the callbacks from Sleep_initEx().
The SLEEP_initEx() function is the one we want to use and in particular the restoreCallback. The comments around the restoreCallback function talk about restoring the clocks but if the function returns a 0 the chip will wake up and if it returns a 1 then it will immediately go back to sleep which is what we want! You can use the other two hooks if you want but the restoreCallback is the key one since it will immediately put the chip back to sleep if everything is idle.
The key to using ANY of these function is that you CANNOT call ANY FreeRTOS functions! You cannot send any Z-Wave frames or call any Z-Wave function as they all require the RTOS. At this point in the wakeup processing the RTOS is not running! All you can do in these routines is to capture data and quickly decide if everything is idle and to go back to sleep. If there is more processing needed, then return 0 and wait for the event in the RTOS and process the data there. You also don’t want to spend too much time in these routines as it may interfere with the timing of the RTOS. A hundred microseconds is probably fine but longer you should wait for the HFXO.
In ApplicationInit() you will call Sleep_initEx() like this:
const SLEEP_Init_t sleepinit = {NULL, NULL, CheckSPI};
...
ZW_APPLICATION_STATUS ApplicationInit(EResetReason_t eResetReason) {
...
SLEEP_InitEx(&sleepinit); // call checkSPI() upon wakeup from EM2.
...
}
...
uint32_t CheckSPI(SLEEP_EnergyMode_t emode) {
uint32_t retval=0; // wake up by default
if (GPIO_IntGetEnabled() & 0x0000AAAA) { // Check SPI
GPIO_ODD_IRQHandler(); // service the GPIO interrupt
// wait for all the bytes to come in and compute checksum
NVIC->ICPR[0] = NVIC->ICPR[0]; //clear NVIC pending interrupts
if (!SPIDataError && !IsWakeupCausedByRtccTimeout()) {
retval=1; // go back to sleep!
}
}
return(retval); // 0=wakeup, 1=sleep
}
Recall that every second the FLiRS device has to check for a Z-Wave beam which is triggered by the RTCC timer. Thus the check for IsWakeupCausedByRtccTimer ensures that the beaming still works.
This scope shot shows the wake up processing of the ZGM130S:
SPISEL_N SPI chip select signal goes low triggering a GPIO_ODD interrupt
The chip wakes up, the HFRCO begins oscillating
HFRCO begins oscillating in a few microseconds
Once HFRCO is running, the peripherals are functional
SPI data can begin shifting once the HFRCO is running
The default HFRCO frequency is 19MHz but can be increased
Higher frequencies for HFRCO also may need more wait states for the CPU and will use more power
The WFI instruction that put the CPU to sleep is exited here
EMU_EM23PostSleepHook function is called if defined
After returning from PostSleepHook, the VSCALE is returned to full power which takes about 10usec
It is best to wait for the voltage to be powered up to ensure all logic is running at optimal speeds
EMU_EnterEM2 is exited and restoreCallback is called if initialized
This is the function where the ISR should be called to process data
If the data says things are idle and want to go back to sleep, return 1
If more analysis is needed, then return 0
Carefully clear the interrupt bits
First clear the peripheral Interrupt Flags
Then clear the NVIC Interrupt pending register
NVIC->ICPR[n]=NVIC->ICPR[n] where n is 0-1 depending on your interrupt
Make sure there aren’t other reasons to wake up fully
!IsWakeupCausedByRtccTimeout() is the 1s FLiRS interrupt
There may be other reasons to wake up which is application dependent
In this example the SPI data is being fetched from the USART at each toggle of the GPIO
The final toggle shows that the checksum was computed and the data is idle so go back to sleep
The chip returns back to sleep in a few more microseconds
Total processing time of this interrupt is less than 200usec which is a fraction of the time just waiting for the HFXO to stabilize
Much of that time is receiving and processing the SPI data
It is possible to sleep in under 50usec if the check for idle is quicker
If your peripheral processing will take significantly less than 500usec, then it may be more efficient to process the data using the HFRCO and not wait for the HFXO to power up. But if your application needs more processing, then you are probably better off waiting. Each application must make their own calculations to determine the most efficient path.
What About Sleeping Devices?
Fully sleeping devices (EM4 also known as RSS – Routing Sleeping Slaves) have entirely different wake/sleep processing. For sleeping slaves the processor and RAM have to be re-initialized and the chip essentially boots out of reset. All that initialization takes quite a bit of time – a few tens of milliseconds. If your device needs to do a lot of frequent checking of a sensor, then it might make more sense to force it to stay in EM2 by setting a Power Lock to PM_TYPE_PERIPHERAL. For more details on power locks see INS14259 section 7.6. Deciding which way to go is application specific so you have to make the calculations or measurements to find the right balance for your project.
This is a complex posting but I hope I’ve made it clear enough to enable you to optimize your application firmware. Let me know what you think by leaving a comment below or on my blog at drzwave.blog.
This guide uses the DoorlockKeyPad built under 7.12.2 but the same basic concepts apply to any sample app and your project. A generic KBA on upgrading SDKs for Bluetooth projects provides general insights but this document is specific to this Z-Wave release.
Update Simplicity Studio (SS)
From the Launcher perspective, click on the download icon and then click on Update All
Close Simplicity and restart (just to be safe) - SS may ask to do this as well
Click on the download button again, click on Package Manager, click on SDK tab, click on each Update button (keep scrolling down). I recommend restarting SS between each update.
Set the Preferred SDK to the latest release
Plug in a WSTK devkit or select the ZGM130S in the MyProduct window
From the Launcher perspective, click here:
Select the Gecko SDK Suite 7.13.3.0
Now when you want to create a new project it will start with the 7.13.3 release
At this point there are 3 options for upgrading:
Open a new 7.13.3 project and then copy your changes into it
Import/Export parts of your project into a new 7.13.3 project
Manually edit the .cproject and .project files
Far and away the best option is the first one - option A
The advantage of this is that you get all the latest and newest files of the Z-Wave Application Framework (ZAF), emlib, linker scripts, and lots of other stuff
The challenge is that you have to plug all of your changes back into the sample application
If you’ve made most of the changes in files other than the sample application, then the changes are probably easy to install
However, if you’ve made a lot of changes to the sample application source code file it will take more time
Option B or C might be the way to go but at some point it might still be better to go back to option A. Option A is fairly straightforward as the sample apps in the 7.13.3 release already work so just copy in your changes a few at a time and keep recompiling to make sure you haven’t broken something.
If taking option A you’re done! No need to read any further! Create the new sample app and start plugging in your changes
If you’re not taking option A, pick one of the 2 options below. The choice of B or C is up to you and primarily how confident you are in fixing XML files. If you think you can fix an XML file, then use option C otherwise use B.
Option B - Import/Export parts of your project into a new 7.13.3 project:
This option avoids manually editing the XML file but requires importing/exporting several files and using the GUI to fix paths. The advantage however is that you shouldn’t break the XML file which is easily done with manual editing.
Export the project created with Z-Wave 12.2 SDK using [File] > [Export...].
Window->Preferences->SimplicityStudio->SDKs
Remove the 7.1x.xx SDKs from the Simplicity Studio SDK preferences
This does not delete or uninstall the actual SDK, just removes it from the current workspace settings so it will not be used during the project import step
Import the .sls file created with the export. ([File] > [Import...]) above
Right click the project from the IDE perspective
Project Properties [C/C++ Build] > [Board / Part / SDK] and select the Gecko SDK Suite: 7.13.3.0, Flex 2.7.3 from the dropdown list
When it asks to Rebuild Now - click on YES!
The build will fail because the Linker script location has changed from:
Try to Build again but now there are all sorts of problems with basic things like SizeOf.h cannot be found. This is caused by the change in the directory structure for the 7.13 SDK.
In the 7.13.3 project - right click on the project->properties->C/C++ General->Paths and Symbols and click on EXPORT Settings
Export them all to a temporary file - IE:Seven13Paths.xml
Return to the same screen in your project, <CTL-A> to select all the paths then Delete - be sure to delete ALL of them
Then click on Import Settings and select the Seven13Paths.xml file
Build again and you should get a “conflicting types for __Vectors” error
This is caused by a new Device/startup_zgm13[.S,.c] - copy these files from the 7.13.3 project into your project
Build again and you get an error “No rule to make target libZWaveSlave.a”.
Right click properties->C/C++Build/Settings->GNU ARM C Linker->Other objects
If other errors turn up, try doing a Clean Project and exit SS and start it up again
Click Build
OtaInit() function has changed to CC_FirmwareUpdate_Init() which needs a 4th argument which is “true”.
Click Build
The error is: Undefined reference to `FileSystemVerifySet' in DoorLockKeyPad.c
The problem here is that the ZAF has changed how it is checking the state of the file system and using a much easier process of checking a few Tokens in NVM3. Compare the 7.13.3 sample app code and use that process instead of the one from 7.12.2. Basically, delete (or #if 0/#endif) several lines of code.
Click Build
The error is: Undefined Reference to CC_FirmwareUpdate_InvalidateImage
The confusing part of this error is that you can click on this function and it will open the correct file. The problem is that it is not included in the project.
The problem is there are 2 new Linked files - btl_reset_cause_util.[c,h].
Copy via a link into the ZAF_CommandClasses_FirmwareUpdate
The project builds correctly! You may have additional files/directories that need to be cleaned up or corrected. Usually compare what is in the 7.13.3 project with yours to find what’s changed.
Program your project in your device and verify it still works
It is possible that other changes to the code may be required for SmartStart, Inclusion, handling of NVM3, interrupts, peripherals and other features. Expect that it may take a day or so to resolve these problems.
For example, in the DoorLockKeyPad sample application, when a Z-WavePlusInfo_GET is sent, an ASSERT is executed and the CPU takes a hardfault and essentially crashes.
Connect the debugger using an Attach To
Click on Pause and the Debug pane shows the call stack which says we hit an assert within the SDK from handleCommandClassZWavePlusInfo()
Set a breakpoint at the start of this command and restart the debug session and resend the ZWavePlusInfo_GET which should stop the CPU at the start of the handler
Single step thru the code until you find the CPU in the defaultHandler again (you’ll have to click Pause)
The problem is that the Static structure pData is NULL and as a result the ASSERT is triggered:
ASSERT(NULL != pData);
Looking back in the sample app we notice there is a new Init routine for this command class:
CC_ZWavePlusInfo_Init(&CCZWavePlusInfo);
Which also needs a new definition:
SCCZWavePlusInfo CCZWavePlusInfo = { …
Adding these to the source code, recompiling and downloading and testing it now works
There may be several other things that need to be updated or initialized with each release so more testing and debug may be required.
Option C - Manually edit the .cproject and .project XML files:
This option uses a text editor to edit the two XML files SS uses to build your project. XML files are very picky and if you mess up the structure of the document it simply won’t work. So be very careful or go back to option A.
Open a new sample project in the 7.13.3 release using the same sample app you started with originally
Compile it and make sure it builds without error - change APP_FREQ to REGION_US (or your region) if needed
Close Simplicity Studio
The .cproject and .project are XML files which you can edit in a text editor. If you mess up the hierarchy of the file, it can make the files unusable. Editing the files is tricky so be very careful and keep backups of your work.
Rename the .cproject and .project files in your application directory - add .orig or .old to the filename
Copy the .cproject and .project files from the top level of the 7.13.3 sample app into your application directory
Open the two .cproject files in a text editor and diff the two files
Carefully copy the few things you need from your original .cproject file into the 7.13.3 one - typically this is just a few filenames you have added to the project and other customizations
You’ll see that many files are now in different directories
many files now have /protocol/z-wave added or /util removed - all of these changes are necessary now that Z-Wave has been integrated with the other wireless protocols
Open the two .project files in a text editor and diff the two files
Follow the same procedure as with the .cproject file
The project name is the 3rd line of the file - this will certainly need to change
Open SS and select the project
Click on Clean Project
Click on Build
There are likely several errors usually caused by include paths not being correct
Click on the Problems tab and highlight the error
Go to the corresponding line in the 7.13.3 project and find the correct include path
Copy that into your project properties
In my trial SS was not able to find ZAF_common_helper.h
Adding "${StudioSdkPath}/protocol/z-wave/ZAF/ApplicationUtilities/_commonIF" to the C include paths got it past this error
Click Build
Next problem is that UReceiveCmdPayload did not have a corresponding member
In this case the structure has added an extra level of hierarchy so the reference changes from:
Next problem is that __Vectors has conflicting types
This is caused by a new Device/startup_zgm13[.S,.c] - copy these files from the 7.13.3 project into your project
Click Build again
Now the error is: Undefined reference to `FileSystemVerifySet' in DoorLockKeyPad.c
The problem here is that the ZAF has changed how it is checking the state of the file system and using a much easier process of checking a few Tokens in NVM3. Compare the 7.13.3 sample app code and use that process instead of the one from 7.12.2.
Click Build
OtaInit() function has changed to CC_FirmwareUpdate_Init() which needs a 4th argument which is “true”.
Click Build
The project should now build correctly
There are other potential problems with missing include directories, #defines or function names that have changed. The solution is to search the new 7.13.3 project for the same area of code and identify the new directory or function name.
Program your project in your device and verify it still works
It is possible that other changes to the code may be required for SmartStart, Inclusion, handling of NVM3, interrupts, peripherals and other features. Expect that it may take a day or so to resolve these problems.
For example, in the DoorLockKeyPad sample application, when a Z-WavePlusInfo_GET is sent, an ASSERT is executed and the CPU takes a hardfault and essentially crashes.
Connect the debugger using an Attach To
Click on Pause and the Debug pane shows the call stack which says we hit an assert within the SDK from handleCommandClassZWavePlusInfo()
Set a breakpoint at the start of this command and restart the debug session and resend the ZWavePlusInfo_GET which should stop the CPU at the start of the handler
Single step thru the code until you find the CPU in the defaultHandler again (you’ll have to click Pause)
The problem is that the Static structure pData is NULL and as a result the ASSERT is triggered:
ASSERT(NULL != pData);
Looking back in the sample app we notice there is a new Init routine for this command class:
CC_ZWavePlusInfo_Init(&CCZWavePlusInfo);
Which also needs a new definition:
SCCZWavePlusInfo CCZWavePlusInfo = { …
Adding these to the source code, recompiling and downloading and testing it now works
There may be several other things that need to be updated or initialized with each release so more testing and debug may be required.
Another minor issue with the 7.13.3 release is that it is not properly creating the .GBL files for OTA. The fix is simple:
Modify line 46 in \SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v2.7\protocol\z-wave\ZAF\ApplicationUtilities\application_properties.c
from:
.type = 1<<6UL,
to:
.type = 1<<7UL,
My recommendation is to click on this file in the Project Explorer->ZAF_ApplicationUtilites and make the change. When you change the code, this popup comes up:
And you should click on Make A Copy.
When the next minor release comes out this will be fixed and you will then want to delete the copy and go back to the Linked version of the file. I do NOT recommend changing the files in the SDK.
The Z-Ware C API allows for OTA updates of devices. This article provides an example of how to use it.
zwif_fw_info_get() should first be called to find information about the device and then zwif_fw_updt_req() should be called to perform the update. Each of those functions has callbacks that must be implemented.
The example provided is based on the existing bin_switch demo. It finds the first binary switch node and then allows that node to be updated by adding an additional menu option to do so.
Two places in the code were modified from the plain bin_switch demo. Inside the bin_sw_intf_get() function an additional option to find the firmware update interface was added. The callbacks were added and the main function was modified to add menu option 3 to perform the update.
To run the demo make sure you have a firmware image (.gbl or .otz file) for the binary switch end device example in the same directory as the executable.
When bringing up and validating a new Z-Wave 700 hardware platform, Silicon Labs provides a special software tool called RailTest.
RailTest offers all the functionality needed to exercise the RF parts of the new Z-Wave 700 product under development.
Using RailTest, parameters such as:
RF output power
RF frequency
Crystal fine tuning
Z-Wave sensitivity
can easily be measured on the new Z-Wave 700 hardware which is being brought up / validated.
Settings for output power, etc., can be fine-tuned and afterwards incorporated in the final Z-Wave application code, thus ensuring that the performance of the new Z-Wave 700 product will live up to all customer and RF regulatory demands.
RailTest is part of the Z-Wave software distribution; it is a pre-compiled binary file. However, some customer wants to change the default configuration of certain peripheral, such as GPIO. This KB will introduce how to create a RailTest project so that customer can get the source code and configure it.
1 Install FLEX SDK
From Studio Installation manager, choose SDKs, choose Flex in drop down Categories, choose All in Version. Install FLEX SDK 2.5.5.0.
2 Change the preferred SDK to Gecko SDK suite
Connect your ZGM130s to Simplicity Studio, changed the preferred SDK to Gecko SDK suite Flex 2.5.5.0.
After that you will observe RAIL Examples under Getting Started Tab.
3 Create RailTest project
Click the RAIl: RailTest, that will create a RailTest project.
4 Edit railtest_efr32.isc
Open railtest_efr32.isc, select the Plugins tab and check the 'Flash Data, provide API:flash-data'.
5 Generate Project
Press the Generate button to generate the project configuration.
6 Configure project properties
From the Project Explorer windows right click the project and select properties.
From the Properties -> C/C++ Build -> Settings -> Tool Settings tab Select the symbols item from the GNU ARM C Compiler sub menu then create a new symbol RXBUFSIZE=128 in Defined Symbols (-D).
7 Configure the RailTest lib path
From the GNU ARM C Linker sub menu select the Miscellaneous item, check the path for librail_module_efr32xg13_gcc_release.a and librail_config_zgm130s037hgn1_gcc.a is correct, make sure you can find these libraries under the path(In my example, SDK is located under default folders).
We’re all trying to make the Smart Home products smaller and less visible. Using coin cells instead of bulky cylindrical batteries significantly reduces the size of many products. The challenge with making products smaller is that the area available on the PCB for a debug header is in short supply.
Many customers I’ve worked with want to use less PCB real estate which means they come up with a custom set of test points. Typically a jig with spring loaded pins are used to contact to the PCB or more often wires are soldered to the PCB. The problem with this solution is that the jig is large, expensive and fragile. Soldering a cable to a board often results in a fragile connection where the cable can easily break a pin and not be immediately obvious. I’ve spent far too much time trying to figure out why I could program the part a minute ago but now I can’t only to realize the cable has a loose wire.
Unreliable custom cable debug connector
700 Series Debug Header
Fortunately Silicon Labs has a solution for the 700 series – use a 0.05″ spacing 10 pin SMT header. The Mini Simplicity Debug connector is described in AN958. If you have a little room then use the standard SMT header which is 6x6mm. If you are very tight on real estate then put down the pads for the thru-hole version of the connector but hand-solder the thru-hole header to the pads. Using just the pads results in a header only 3x6mm. You can’t tell me you can’t come up with 18sqmm to make the PCB debug reliable!
Either solution requires only a small amount of space on a single side of the PCB. Usually the header pads can be under a coin cell since during debug a power supply is used instead of the battery. This same header can be used for production programming using a jig to contact to the pads. Having a standard and reliable connection to the PCB will save you time during debug and on the production floor.
Reliable tiny debug connector unpopulated in production
Conclusion
No matter how tempted you might be to come up with your own cable/connector/test points, DON’T DO IT! Use the standard Mini Simplicity connector to save you so many headaches during debug. A solid, reliable debug connection is an absolute must otherwise you risk spinning your wheels chasing ghosts that are caused by a flakey connector. Take it from me, I’ve tried to debug just way too many of these over the years and it is not fun.
This article was also published on my blog at DrZWave.blog.
This KB shows an implementation of an interrupt based asynchronous mode configuration of the U(S)ART peripheral for RS-232.
The development kit doesn’t include RS-232 line drivers, so ensure that signal levels are compatible before establishing a communication link between two parties. Connecting the EFR32 UART directly to a PC serial port will damage the EFR32.
In order to implement UART into the Z-Wave framework, we will be using USART API from EMLIB.
We open the UART connection with Baud Rate 115200 bps, and in this example we only enable RX.
Step 6: UART Interrupt handler
In the PRIVATE FUNCTIONS section add a circular buffer and a function USART1_RX_IRQHandler().
/*
* USART1 RX example
* - inspired by AN0045 main.c (https://www.silabs.com/documents/public/example-code/an0045-efm32-uart-rs-232.zip)
*
* WSTK EXP Connector Pin configuration:
* GND : 1
* USART1 RX : 10
* USART1 TX : 13
*
* Baud rate: 115200
*/
/* Declare a circular buffer structure to use for Rx queue */
#define BUFFERSIZE 256
static USART_TypeDef *uart = USART1;
volatile struct circularBuffer
{
uint8_t data[BUFFERSIZE]; /* data buffer */
uint32_t rdI; /* read index */
uint32_t wrI; /* write index */
uint32_t pendingBytes; /* count of how many bytes are not yet handled */
bool overflow; /* buffer overflow indicator */
} rxBuf = { {0}, 0, 0, 0, false };
/*
* USART1 RX interrupt handler
*
* Must follow the naming defined startup_efr32fg13p.c
* (See also: https://www.silabs.com/community/mcu/32-bit/knowledge-base.entry.html/2014/11/19/efm32_isr_naming-Azu3)
*/
void USART1_RX_IRQHandler(void)
{
/* Check for RX data valid interrupt */
if (uart->STATUS & USART_STATUS_RXDATAV)
{
/* Copy data into RX Buffer */
uint8_t rxData = USART_Rx(uart);
rxBuf.data[rxBuf.wrI] = rxData;
rxBuf.wrI = (rxBuf.wrI + 1) % BUFFERSIZE;
rxBuf.pendingBytes++;
/* Flag Rx overflow */
if (rxBuf.pendingBytes > BUFFERSIZE)
{
rxBuf.overflow = true;
}
DPRINTF("%c", rxData); // Print the received character to debug output
//
// Put code here to send event/data to application task
//
/* Clear RXDATAV interrupt */
USART_IntClear(USART1, USART_IF_RXDATAV);
}
}
When an RXDATAV interrupt is received, the Rx interrupt handler copies the incoming data to a receive queue.
UART1_RX_IRQHandler() reacts on the RXDATAV interrupt, meaning that the UART RX buffer contains valid data. When this happens, the incoming byte is copied from the UART RX buffer into the RX queue. The queue write index (wrI) is updated, and the pending byte counter is incremented. The IRQ handler will also disable the TXBL interrupt if the transmit queue becomes empty
Step 7: Build solution and test
Build the solution and flash the new firmware image to the device.
If you enable serial debug output, you are now ready to receive input on the UART RX line.
Step 8: Port and Pin used for RX / TX.
The UART1 pins are defined in extension_board_8029a.h, and the pins are configured as:
In document “DSH14476 - UG381: ZGM130S Zen Gecko Wireless Starter Kit User's Guide” we can map the port and pin to a connection on the development hardware. From section 3.4.1 EXP Header Pinout we learn that:
TX Pin: Connection F3 is connected to EXP Header Pin 13.
RX Pin: Connection C9 is connected to EXP Header Pin 10.
Step 9: Connect a USB to TTL cable
We will be using a USB – TTL cable.
Connect the RX wire from the cable to the TX pin on header 13.
Connect the TX wire from the cable to the RX pin on header 10.
Step 10: Use a terminal program to connect
Use your preferred terminal program, e.g. PuTTY or Tera Term and connect to the virtual COM port for your USB – TTL adapter. Connect with Baud Rate 115200 bps.
Step 11: Run and test the solution.
Everything is now setup and we are ready to start sending data from our terminal program. When our device receive data on UART1, it will print it to the debug log on UART0.
Closing comments
This KB is a quick demonstration in how to add support for U(S)ART in the Z-Wave Framework using the EMLIB library.
This example is not really using the received data, but simply prints it on the debug UART port.
When starting to use the received data, the RX interrupt should invoke ZAF_EventHelperEventEnqueueFromISR() with an event defined in the application, e.g.
EVENT_APP_UART_RX, because it allows the chip to sleep between interrupts.
INS14280 - Getting Started for End Device This document also describes how to setup the Z-Wave region and serial debug, which is not covered by this KB.
INS14259 - Z-Wave Plus V2 Application Framework SDK7
UG381: ZGM130S Zen Gecko Wireless Starter Kit User's Guide
This KB will be using the AppTimer module from the Z-Wave Framework. This module is further documented in document INS14259 - Z-Wave Plus V2 Application Framework SDK7 (available in Simplicity Studio).
All software timers are essentially FreeRTOS timers. Using the AppTimer module, you ensure that timer callback functions are executed in the context of the application task.
Step 1: Continuing on an example
This KB continues on the I2C example. It is suggested to complete that exercise in order to see the full effect of implementing the timer – it is however not required and this guide will instruct how to continue if not using I2C in the relevant steps.
Step 2: SSwTimer instance
To start using application timers, you need a SSwTimer instance. In the PRIVATE DATA section, add a SSwTimer instance.
When the timer fires, we want our callback function to enqueue an event to our state machine. Thus we need to add a new event in the _EVENT_APP_ typdedef. We call this event EVENT_APP_TEMPERATURE.
Step 6: Update the state machine to listen for the event
In the AppStateManager(), find the state called STATE_APP_IDLE. In this state, create a new if-case and perform the temperature measurement if EVENT_APP_TEMPERATURE event is received.
// ACTION: Perform temperature measurement on timer event
if (EVENT_APP_TEMPERATURE)
{
uint32_t rhData;
int32_t tempData;
// Get temperature and humidity reading from on-board sensor
performMeasurements(i2cInit.port, &rhData, &tempData);
DPRINTF("\r\nHumidity: %d", rhData);
DPRINTF("\r\nTemperature: %i\r\n", tempData);
}
Note: In this step, we are calling performMeasurements(). This function is part of the I2C example. You can leave out this function call, and just verify in the debug log that the event is called once every second.
Step 7: Build solution and test
Build the solution and flash the new firmware image to the device.
If you enable serial debug output, you should now see that we get the TemperatureTimerCallback() and that results in a temperature and humidity reading.
INS14280 - Getting Started for End Device This document also describes how to setup the Z-Wave region and serial debug, which is not covered by this KB.
INS14259 - Z-Wave Plus V2 Application Framework SDK7 Refer to section 7.7 for more information on the Timer module.
UG381: ZGM130S Zen Gecko Wireless Starter Kit User's Guide
This KB shows an implementation of a SPI Master application, because most use cases involve connecting a SPI Slave device, e.g. a sensor, to the Z-Wave device.
In order to implement SPI into the Z-Wave framework, we will be using the SPIDRV API from EMDRV. In this KB, we will be implementing the SPI example from the SPIDRV documentation page.
Step 1: Include header files
You need to include the following header files to the search path of the project.
A) Go to project properties => C/C++ General => Paths and Symbols
B) In the 'Includes' tab, add the two include paths listed below to both 'Assembly' and 'GNU C'
C) Apply the settings and click 'Yes' in the dialog to rebuild.
D) Click OK to close the properties window.
The needed include paths are:
spidrv.h
Include path: ${StudioSdkPath}/platform/emdrv/spidrv/inc
Add the following include to the main file of the Z-Wave Sample App you are using.
#include <spidrv.h>
Step 4: Setup for Master
A) In spidrv_config.h (referenced in spidrv.h), locate the define EMDRV_SPIDRV_INCLUDE_SLAVE and place your cursor.
B) Uncomment this line by start typing “/”.
C) This opens a dialog. In this dialog select “Make a Copy”. This will make a copy of the header file and move it to the local project location, because we are now editing a file in the SDK.
D) Finish uncommenting EMDRV_SPIDRV_INCLUDE_SLAVE
// SPIDRV configuration option. Use this define to include the slave part of the SPIDRV API.
//#define EMDRV_SPIDRV_INCLUDE_SLAVE
Step 5: Build solution
Now build the solution. If everything went well when adding include paths and linking to source files, you should not get any build errors.
Step 6: Setup SPI
A) In the PRIVATE DATA section, add the SPI handle
In ApplicationInit(), initialize the SPI driver just before the call to ZW_ApplicationRegisterTask.
Since we will be using the Z-Wave serial debugging interface as well, we will setup SPI to use USART1.
In addition, we will also try to transmit data using a blocking transmit function and using a callback to catch the transfer completion.
// ACTION: Init SPI and send buffer.
uint8_t buffer[10];
SPIDRV_Init_t initData = SPIDRV_MASTER_USART1;
// Initialize a SPI driver instance
SPIDRV_Init( handle, &initData );
// Example: Transmit data using a blocking transmit function
if(SPIDRV_MTransmitB( handle, buffer, 10 ) == 0)
DPRINT("SPI Transfer Blocking Success\r\n");
// Example: Transmit data using a callback to catch transfer completion.
SPIDRV_MTransmit( handle, buffer, 10, TransferComplete );
Step 8: Build solution and test
Build the solution and flash the new firmware image to the device.
If you enable serial debug output, you should now see that we are successfully sending data.
Closing comments
This KB is a quick demonstration in how to add support for SPI in the Z-Wave Framework.
This implementation just sends out data in the ApplicationInit() function, but this should be done in an appropriate place depending on what functionality is needed.
A word on SPI Slave
The SPIDRV from EMDRV can also be setup as a slave. However, this implementation relies on the Real-time Clock Driver (RTCDRV). Because Z-Wave is using FreeRTOS as the underlying OS, it is not possible to use SPIDRV in Slave mode as it conflicts with how FreeRTOS uses the RTC.
As such, if you need to implement SPI Slave functionality in the Z-Wave framework, you need to use the lower-level USART driver from EMLIB.
INS14280 - Getting Started for End Device This document also describes how to setup the Z-Wave region and serial debug, which is not covered by this KB.
INS14259 - Z-Wave Plus V2 Application Framework SDK7
UG381: ZGM130S Zen Gecko Wireless Starter Kit User's Guide
Z-Wave Knowledge Base
Local only Z/IP client, remove the ethernet port from br-lan
In some cases, a solution may be running a zip client locally. In these cases, perhaps you wish to prevent zip traffic from leaving the gateway.
The quickest solution to keeping zip traffic on the gateway, is to take the default lan configuration, and simply delete the ethernet port from the br-lan, leaving all other configuration constant.
Because the zipgateway controls ipv6 address assignment, a local zip client should now be able to use ipv6 addresses to control end nodes.
But what if you need IPv4 control? You will need to run a dhcp4 server on your gateway. In this example, I am using isc-dhcp-server
Bind the isc-dhcp-server to the br-lan by modifying /etc/default/isc-dhcp-server and adding to the "INTERFACESv4" entry.
Using the bridge as our binding interface gives us an added benifit of a constant interface name. If we were to try and rely on our tap in this case, then if zipgateway changes the tap name we run into problems of our dhcp server failing to bind to the correct interface.
If you're feeling adventurous, you can leave this as "", but be advised, if any interface gets an address in a range you specify in dhcpd.conf (later) you will begin serving dhcp requests on that interface. Probably not what you want.
In order to host dhcp on an interface, that interface needs a static IP address. Let's modify /etc/network/interfaces.d/br-lan to use a static address; Pick your favorite local subnet, here - I've used 192.168.123.X/24; Note that we change the top line to 'static' rather than 'dhcp'
Now that our dhcp server has a static address, let's configure a range for our zip devices...
add the following to /etc/dhcp/dhcpd.conf; Be sure your range matches a subnet that your static address is in
Note that 200-1 = 199 ipv4 addresses; Not enough ipv4 addresses if you were to have the maximum 2^8=255 nodes; If you need ipv4 addresses for every possible node ID, you will want to change your netmask above to 255.255.254.0 and be aware that your static address should also widen its netmask
Next, enable the dhcp server on reboots
At this point, you should reboot and confirm everything is working. You should now be able to control zip nodes with ipv4 addresses.
Fast GPIO Wake Up in Z-Wave 700 Series
The Silicon Labs EFR32 family of IoT microcontrollers are very flexible and can do a ton of cool stuff. However, along with all that flexibility comes a lot of complexity. With that complexity are default settings that work fine for many applications but in some cases you want to dig into the details to come up with an optimal solution. In this post I’ll show how to speed up the wake up time for the Z-Wave ZGM130S chip from a GPIO.
But first – a caveat: This post applies to Z-Wave SDK 7.13.x. Future releases of the SDK may have different methods for sleep/wake and thus may require a different solution.
The Problem
Frequently Listening Routing Slaves (FLiRS) devices like door locks and many thermostats spend most of their time in Energy Mode 2 (EM2) to conserve battery power. Once per second they wake up briefly and listen for a Beam from an always-on device. If there is a beam, the FLiRS device will wakeup and receive the Z-Wave command. This allows battery powered devices to use very little power but still be able to respond to a Z-Wave command within one second. FLiRS devices use more battery power than fully sleeping devices like most sensors which use Hibernate Sleep mode (EM4). To wake every second the ZGM130 has to wake quickly and go right back to sleep to minimize power. The problem with EM4 is that it takes a few tens of milliseconds to wake up as the entire CPU and RAM have to be initialized as they were powered down to save power. For a FLiRS device, it’s more efficient to keep RAM powered but in a low-power state and resume quickly to go right back to sleep if there is no beam. Typically the ZGM130 can wake up in about 500 microseconds from EM2. But in many cases this is still too long of a time to stay awake if there are other interrupts such as UARTs or other sensors.
The scope shot above shows the processing that takes place by default on the ZGM130S. In this case I am using a WSTK to drive the SPI pins of another WSTK running the DoorLockKeyPad sample application. The chip is in EM2 at the start of the trace. When SPISEL signal goes low, the chip wakes up. But it is running on the HFRCO oscillator which is not accurate enough to run the radio but it is stable and usable in just a few microseconds. Thus, the SPI clock and data is captured in the USART using this clock. However, by default the Interrupt Service Routine is blocked waiting for the HFXO to stabilize. The 39MHz HFXO crystal oscillator has the accuracy required for the radio.
The question is what’s going on during this 500usec? The answer is the CPU is just waiting for the HFXO to stabilize. Can we use this time to do some other work? Fortunately, the answer is YES! The challenge is that it takes some understanding and some code which I’ll describe below.
The Solution
There are three functions that do the majority of the sleep processing. These are provided in source code so you can read the code but you should not change it. Instead you’ll provide a callback function to do your processing while the chip is waking up.
Simplified Sleep Processing Code:
The code is in sleep.c in the SDK which has a lot more detail but at a high level this is what you need to know. The important part to understand here is where the “hooks” are and how to use them.
The two EMU_EM23* weak functions are run immediately before/after the Wait-For-Interrupt (WFI) instruction which is where the CPU sleeps. These are very low level functions and while you can use them I recommend using the callbacks from Sleep_initEx().
The SLEEP_initEx() function is the one we want to use and in particular the restoreCallback. The comments around the restoreCallback function talk about restoring the clocks but if the function returns a 0 the chip will wake up and if it returns a 1 then it will immediately go back to sleep which is what we want! You can use the other two hooks if you want but the restoreCallback is the key one since it will immediately put the chip back to sleep if everything is idle.
The key to using ANY of these function is that you CANNOT call ANY FreeRTOS functions! You cannot send any Z-Wave frames or call any Z-Wave function as they all require the RTOS. At this point in the wakeup processing the RTOS is not running! All you can do in these routines is to capture data and quickly decide if everything is idle and to go back to sleep. If there is more processing needed, then return 0 and wait for the event in the RTOS and process the data there. You also don’t want to spend too much time in these routines as it may interfere with the timing of the RTOS. A hundred microseconds is probably fine but longer you should wait for the HFXO.
In ApplicationInit() you will call Sleep_initEx() like this:
Recall that every second the FLiRS device has to check for a Z-Wave beam which is triggered by the RTCC timer. Thus the check for IsWakeupCausedByRtccTimer ensures that the beaming still works.
This scope shot shows the wake up processing of the ZGM130S:
If your peripheral processing will take significantly less than 500usec, then it may be more efficient to process the data using the HFRCO and not wait for the HFXO to power up. But if your application needs more processing, then you are probably better off waiting. Each application must make their own calculations to determine the most efficient path.
What About Sleeping Devices?
Fully sleeping devices (EM4 also known as RSS – Routing Sleeping Slaves) have entirely different wake/sleep processing. For sleeping slaves the processor and RAM have to be re-initialized and the chip essentially boots out of reset. All that initialization takes quite a bit of time – a few tens of milliseconds. If your device needs to do a lot of frequent checking of a sensor, then it might make more sense to force it to stay in EM2 by setting a Power Lock to PM_TYPE_PERIPHERAL. For more details on power locks see INS14259 section 7.6. Deciding which way to go is application specific so you have to make the calculations or measurements to find the right balance for your project.
This is a complex posting but I hope I’ve made it clear enough to enable you to optimize your application firmware. Let me know what you think by leaving a comment below or on my blog at drzwave.blog.
Upgrading 700 series project from 7.12.2 to 7.13.3
This guide uses the DoorlockKeyPad built under 7.12.2 but the same basic concepts apply to any sample app and your project. A generic KBA on upgrading SDKs for Bluetooth projects provides general insights but this document is specific to this Z-Wave release.
Another minor issue with the 7.13.3 release is that it is not properly creating the .GBL files for OTA. The fix is simple:
Modify line 46 in \SimplicityStudio\v4\developer\sdks\gecko_sdk_suite\v2.7\protocol\z-wave\ZAF\ApplicationUtilities\application_properties.c
from:
.type = 1<<6UL,
to:
.type = 1<<7UL,
My recommendation is to click on this file in the Project Explorer->ZAF_ApplicationUtilites and make the change. When you change the code, this popup comes up:
And you should click on Make A Copy.
When the next minor release comes out this will be fixed and you will then want to delete the copy and go back to the Linked version of the file. I do NOT recommend changing the files in the SDK.
Z-Ware OTA Firmware Update with the C API
The Z-Ware C API allows for OTA updates of devices. This article provides an example of how to use it.
zwif_fw_info_get() should first be called to find information about the device and then zwif_fw_updt_req() should be called to perform the update. Each of those functions has callbacks that must be implemented.
The example provided is based on the existing bin_switch demo. It finds the first binary switch node and then allows that node to be updated by adding an additional menu option to do so.
Two places in the code were modified from the plain bin_switch demo. Inside the bin_sw_intf_get() function an additional option to find the firmware update interface was added. The callbacks were added and the main function was modified to add menu option 3 to perform the update.
To run the demo make sure you have a firmware image (.gbl or .otz file) for the binary switch end device example in the same directory as the executable.
Z-Wave 700: TX Power calibration/adjustment in RAILTest and Application FW.
The relation between the TX-Power in dB and the raw value used in RAILTest is given by this:
To make the conversion easy the INS14664: Introduction to MaxPowerCalc has been added in Simplicity Studio.
#define 0dBmMeasured // dBm Value measured by raw power setting 24,
// remember to counteract for cables in the setup.
#define wantedOutputPower // The dBm PA output power wanted for the customers application
#define scaledPower (wantedOutputPower - 0dBmMeasured) // intermediate variable for use below
#define rawPASetting (0.000133848*(scaledPower)^5 + 0.001042021*(scaledPower)^4 \
– 0.005109317*(scaledPower)^3 + 0.11773548*(scaledPower)^2 \
+ 3.007063918*(scaledPower) + 24.35849668)
The raw values are used in RAILTest (please make sure to use lower case letters "raw" in RAILTest )
and the settings in the Z-Wave SDK application code are set in "config_rf.h" in dBm:
// The maximum allowed Tx power in deci dBm
#define APP_MAX_TX_POWER 0 // set to 0 dBm by default in the Z-Wave SDK
// The deci dBm output measured at a PA setting of 0dBm (raw value 24)
#define APP_MEASURED_0DBM_TX_POWER 0 //Set to 0 to measure the 0 dBm
// This is set to 33 by default to support the development boards in the Z-Wave SDK
The maximum raw value in RAILTest is limited to 155 and the TX power max is 13dBm on the RFpin
More information can be found in
INS14283-6 Bring-up/test HW development, 4.5.1 How to Adjust the PA Output Power Using RailTest
INS14259-9 Z-Wave Plus V2 Application Framework SDK7, 6.3 Setting Up config_rf.h
Create a RailTest project for ZGM130S
When bringing up and validating a new Z-Wave 700 hardware platform, Silicon Labs provides a special software tool called RailTest.
RailTest offers all the functionality needed to exercise the RF parts of the new Z-Wave 700 product under development.
Using RailTest, parameters such as:
can easily be measured on the new Z-Wave 700 hardware which is being brought up / validated.
Settings for output power, etc., can be fine-tuned and afterwards incorporated in the final Z-Wave application code, thus ensuring that the performance of the new Z-Wave 700 product will live up to all customer and RF regulatory demands.
RailTest is part of the Z-Wave software distribution; it is a pre-compiled binary file. However, some customer wants to change the default configuration of certain peripheral, such as GPIO. This KB will introduce how to create a RailTest project so that customer can get the source code and configure it.
1 Install FLEX SDK
From Studio Installation manager, choose SDKs, choose Flex in drop down Categories, choose All in Version. Install FLEX SDK 2.5.5.0.
2 Change the preferred SDK to Gecko SDK suite
Connect your ZGM130s to Simplicity Studio, changed the preferred SDK to Gecko SDK suite Flex 2.5.5.0.
After that you will observe RAIL Examples under Getting Started Tab.
3 Create RailTest project
Click the RAIl: RailTest, that will create a RailTest project.
4 Edit railtest_efr32.isc
Open railtest_efr32.isc, select the Plugins tab and check the 'Flash Data, provide API:flash-data'.
5 Generate Project
Press the Generate button to generate the project configuration.
6 Configure project properties
From the Project Explorer windows right click the project and select properties.
From the Properties -> C/C++ Build -> Settings -> Tool Settings tab Select the symbols item from the GNU ARM C Compiler sub menu then create a new symbol RXBUFSIZE=128 in Defined Symbols (-D).
7 Configure the RailTest lib path
From the GNU ARM C Linker sub menu select the Miscellaneous item, check the path for librail_module_efr32xg13_gcc_release.a and librail_config_zgm130s037hgn1_gcc.a is correct, make sure you can find these libraries under the path(In my example, SDK is located under default folders).
8 Build RailTest
Now you can build the project and flash the compiled hex to ZGM130S, you will know how to use RailTest in document https://www.silabs.com/documents/login/user-guides/INS14283.pdf.
Use the tiny Mini Simplicity debug connector on your product - avoid unreliable JIGs!
We’re all trying to make the Smart Home products smaller and less visible. Using coin cells instead of bulky cylindrical batteries significantly reduces the size of many products. The challenge with making products smaller is that the area available on the PCB for a debug header is in short supply.
Many customers I’ve worked with want to use less PCB real estate which means they come up with a custom set of test points. Typically a jig with spring loaded pins are used to contact to the PCB or more often wires are soldered to the PCB. The problem with this solution is that the jig is large, expensive and fragile. Soldering a cable to a board often results in a fragile connection where the cable can easily break a pin and not be immediately obvious. I’ve spent far too much time trying to figure out why I could program the part a minute ago but now I can’t only to realize the cable has a loose wire.
700 Series Debug Header
Fortunately Silicon Labs has a solution for the 700 series – use a 0.05″ spacing 10 pin SMT header. The Mini Simplicity Debug connector is described in AN958. If you have a little room then use the standard SMT header which is 6x6mm. If you are very tight on real estate then put down the pads for the thru-hole version of the connector but hand-solder the thru-hole header to the pads. Using just the pads results in a header only 3x6mm. You can’t tell me you can’t come up with 18sqmm to make the PCB debug reliable!
Either solution requires only a small amount of space on a single side of the PCB. Usually the header pads can be under a coin cell since during debug a power supply is used instead of the battery. This same header can be used for production programming using a jig to contact to the pads. Having a standard and reliable connection to the PCB will save you time during debug and on the production floor.
Conclusion
No matter how tempted you might be to come up with your own cable/connector/test points, DON’T DO IT! Use the standard Mini Simplicity connector to save you so many headaches during debug. A solid, reliable debug connection is an absolute must otherwise you risk spinning your wheels chasing ghosts that are caused by a flakey connector. Take it from me, I’ve tried to debug just way too many of these over the years and it is not fun.
This article was also published on my blog at DrZWave.blog.
Z-Wave 700: How to implement UART with interrupt into the Z-Wave Embedded Framework
How to implement interrupt based UART RS-232 into the Z-Wave Embedded Framework
Several peripheral drivers are available (EMDRV and EMLIB) and can be linked to the Z-Wave application. EMDRV exist on top of the lower level EMLIB.
The following resources are great places to look for more information:
Software Documentation (e.g., EMDRV and EMLIB)
32-bit Peripheral Examples (EMLIB)
This KB shows an implementation of an interrupt based asynchronous mode configuration of the U(S)ART peripheral for RS-232.
The development kit doesn’t include RS-232 line drivers, so ensure that signal levels are compatible before establishing a communication link between two parties. Connecting the EFR32 UART directly to a PC serial port will damage the EFR32.
In order to implement UART into the Z-Wave framework, we will be using USART API from EMLIB.
This guide builds on the Application Note AN0045 USART/UART - Asynchronous mode, but implemented into the Z-Wave Framework.
Step 1: Include header files
The include path for EMLIB is already defined in the project, so no actions are required for this step, this just serves a purpose of information.
The needed header file is:
Include path: ${StudioSdkPath}/platform/emlib/inc
Location:
C:\SiliconLabs\SimplicityStudio\v4_ga\developer\sdks\zwave\v7.11.0\platform\emlib\inc
Step 2: Link the needed source files
We need to link the source file to the project.
You need to do the steps above (A-F) for the following source files:
File location:
C:\SiliconLabs\SimplicityStudio\v4_ga\developer\sdks\zwave\v7.11.0\platform\emlib\src
Step 3: Include in Z-Wave framework
Add the following include to the main file of the Z-Wave Sample App you are using.
Step 4: Build solution
Now build the solution. If everything went well when adding include paths and linking to source files, you should not get any build errors.
Step 5: Initialize the UART and setup for interrupt based RX
In ApplicationInit(), initialize the UART driver just before the call to ZW_ApplicationRegisterTask.
We open the UART connection with Baud Rate 115200 bps, and in this example we only enable RX.
Step 6: UART Interrupt handler
In the PRIVATE FUNCTIONS section add a circular buffer and a function USART1_RX_IRQHandler().
When an RXDATAV interrupt is received, the Rx interrupt handler copies the incoming data to a receive queue.
UART1_RX_IRQHandler() reacts on the RXDATAV interrupt, meaning that the UART RX buffer contains valid data. When this happens, the incoming byte is copied from the UART RX buffer into the RX queue. The queue write index (wrI) is updated, and the pending byte counter is incremented. The IRQ handler will also disable the TXBL interrupt if the transmit queue becomes empty
Step 7: Build solution and test
Build the solution and flash the new firmware image to the device.
If you enable serial debug output, you are now ready to receive input on the UART RX line.
Step 8: Port and Pin used for RX / TX.
The UART1 pins are defined in extension_board_8029a.h, and the pins are configured as:
In document “DSH14476 - UG381: ZGM130S Zen Gecko Wireless Starter Kit User's Guide” we can map the port and pin to a connection on the development hardware. From section 3.4.1 EXP Header Pinout we learn that:
TX Pin: Connection F3 is connected to EXP Header Pin 13.
RX Pin: Connection C9 is connected to EXP Header Pin 10.
Step 9: Connect a USB to TTL cable
We will be using a USB – TTL cable.
Connect the RX wire from the cable to the TX pin on header 13.
Connect the TX wire from the cable to the RX pin on header 10.
Step 10: Use a terminal program to connect
Use your preferred terminal program, e.g. PuTTY or Tera Term and connect to the virtual COM port for your USB – TTL adapter. Connect with Baud Rate 115200 bps.
Step 11: Run and test the solution.
Everything is now setup and we are ready to start sending data from our terminal program. When our device receive data on UART1, it will print it to the debug log on UART0.
Closing comments
This KB is a quick demonstration in how to add support for U(S)ART in the Z-Wave Framework using the EMLIB library.
This example is not really using the received data, but simply prints it on the debug UART port.
When starting to use the received data, the RX interrupt should invoke ZAF_EventHelperEventEnqueueFromISR() with an event defined in the application, e.g.
EVENT_APP_UART_RX, because it allows the chip to sleep between interrupts.
The following KB demonstrates how to add a new event and use it in the Z-Wave State Machine.
Z-Wave 700: How to use Software Timers
Z-Wave 700 End-device framework KBs
Z-Wave 700: How to implement I2C into the Z-Wave Embedded Framework
Z-Wave 700: How to use Software Timers
Z-Wave 700: How to implement SPI into the Z-Wave Embedded Framework
Z-Wave 700: How to implement UART with interrupt into the Z-Wave Embedded Framework (this guide)
Linked documentation (found in Simplicity Studio)
This document also describes how to setup the Z-Wave region and serial debug, which is not covered by this KB.
Z-Wave 700: How to use Software Timers
How to use Software Timers
In this KB we will demonstrate an example of using a Software Timer to perform a temperature and humidity sensor reading once every second.
It is documented in KB Z-Wave 700: How to implement I2C into the Z-Wave Embedded Framework how to readout the temperature and humidity sensor reading using I2C.
This KB will be using the AppTimer module from the Z-Wave Framework. This module is further documented in document INS14259 - Z-Wave Plus V2 Application Framework SDK7 (available in Simplicity Studio).
All software timers are essentially FreeRTOS timers. Using the AppTimer module, you ensure that timer callback functions are executed in the context of the application task.
Step 1: Continuing on an example
This KB continues on the I2C example. It is suggested to complete that exercise in order to see the full effect of implementing the timer – it is however not required and this guide will instruct how to continue if not using I2C in the relevant steps.
Step 2: SSwTimer instance
To start using application timers, you need a SSwTimer instance. In the PRIVATE DATA section, add a SSwTimer instance.
Step 3: Initialize timer
In ApplicationTask() register a timer and add a callback. In this example we start the timer and configure it to 1000 ms.
Step 4: Add a new event
When the timer fires, we want our callback function to enqueue an event to our state machine. Thus we need to add a new event in the _EVENT_APP_ typdedef. We call this event EVENT_APP_TEMPERATURE.
Step 5: Create the callback function
In the PRIVATE FUNCTIONS section create the callback function TemperatureTimerCallback() and enqueue the event EVENT_APP_TEMPERATURE when called.
Step 6: Update the state machine to listen for the event
In the AppStateManager(), find the state called STATE_APP_IDLE. In this state, create a new if-case and perform the temperature measurement if EVENT_APP_TEMPERATURE event is received.
Note: In this step, we are calling performMeasurements(). This function is part of the I2C example. You can leave out this function call, and just verify in the debug log that the event is called once every second.
Step 7: Build solution and test
Build the solution and flash the new firmware image to the device.
If you enable serial debug output, you should now see that we get the TemperatureTimerCallback() and that results in a temperature and humidity reading.
Z-Wave 700 End-device framework KBs
Z-Wave 700: How to implement I2C into the Z-Wave Embedded Framework
Z-Wave 700: How to use Software Timers (this guide)
Z-Wave 700: How to implement SPI into the Z-Wave Embedded Framework
Z-Wave 700: How to implement UART with interrupt into the Z-Wave Embedded Framework
Linked documentation (found in Simplicity Studio)
This document also describes how to setup the Z-Wave region and serial debug, which is not covered by this KB.
Refer to section 7.7 for more information on the Timer module.
Z-Wave 700: How to implement SPI into the Z-Wave Embedded Framework
How to implement SPI into the Z-Wave Embedded Framework
Several peripheral drivers are available (EMDRV and EMLIB) and can be linked to the Z-Wave application. EMDRV exist on top of the lower level EMLIB.
The following resources are great places to look for more information:
Software Documentation (e.g., EMDRV and EMLIB)
32-bit Peripheral Examples (EMLIB)
This KB shows an implementation of a SPI Master application, because most use cases involve connecting a SPI Slave device, e.g. a sensor, to the Z-Wave device.
In order to implement SPI into the Z-Wave framework, we will be using the SPIDRV API from EMDRV. In this KB, we will be implementing the SPI example from the SPIDRV documentation page.
Step 1: Include header files
You need to include the following header files to the search path of the project.
The needed include paths are:
Include path: ${StudioSdkPath}/platform/emdrv/spidrv/inc
File location: C:\SiliconLabs\SimplicityStudio\v4_ga\developer\sdks\zwave\v7.11.0\platform\emdrv\spidrv\inc
Include path: ${StudioSdkPath}/platform/emdrv/spidrv/config
File location:
C:\SiliconLabs\SimplicityStudio\v4_ga\developer\sdks\zwave\v7.11.0\platform\emdrv\spidrv\config
Include path: ${StudioSdkPath}/platform/emdrv/dmadrv/inc
File location: C:\SiliconLabs\SimplicityStudio\v4_ga\developer\sdks\zwave\v7.11.0\platform\emdrv\dmadrv\inc
Include path: ${StudioSdkPath}/platform/emdrv/dmadrv/inc
File location: C:\SiliconLabs\SimplicityStudio\v4_ga\developer\sdks\zwave\v7.11.0\platform\emdrv\dmadrv\config
Step 2: Link the needed source files
With the header files added to the project, we need to link the source files.
You need to do the steps above (A-F) for the following source files:
C:\SiliconLabs\SimplicityStudio\v4_ga\developer\sdks\zwave\v7.11.0\platform\emdrv\spidrv\src
C:\SiliconLabs\SimplicityStudio\v4_ga\developer\sdks\zwave\v7.11.0\platform\emdrv\dmadrv\src
C:\SiliconLabs\SimplicityStudio\v4_ga\developer\sdks\zwave\v7.11.0\platform\emlib\src
Step 3: Include in Z-Wave framework
Add the following include to the main file of the Z-Wave Sample App you are using.
Step 4: Setup for Master
Step 5: Build solution
Now build the solution. If everything went well when adding include paths and linking to source files, you should not get any build errors.
Step 6: Setup SPI
Step 7: Initialize SPI and transfer data
In ApplicationInit(), initialize the SPI driver just before the call to ZW_ApplicationRegisterTask.
Since we will be using the Z-Wave serial debugging interface as well, we will setup SPI to use USART1.
In addition, we will also try to transmit data using a blocking transmit function and using a callback to catch the transfer completion.
Step 8: Build solution and test
Build the solution and flash the new firmware image to the device.
If you enable serial debug output, you should now see that we are successfully sending data.
Closing comments
This KB is a quick demonstration in how to add support for SPI in the Z-Wave Framework.
This implementation just sends out data in the ApplicationInit() function, but this should be done in an appropriate place depending on what functionality is needed.
A word on SPI Slave
The SPIDRV from EMDRV can also be setup as a slave. However, this implementation relies on the Real-time Clock Driver (RTCDRV). Because Z-Wave is using FreeRTOS as the underlying OS, it is not possible to use SPIDRV in Slave mode as it conflicts with how FreeRTOS uses the RTC.
As such, if you need to implement SPI Slave functionality in the Z-Wave framework, you need to use the lower-level USART driver from EMLIB.
Z-Wave 700 End-device framework KBs
Z-Wave 700: How to implement I2C into the Z-Wave Embedded Framework
Z-Wave 700: How to use Software Timers
Z-Wave 700: How to implement SPI into the Z-Wave Embedded Framework (this guide)
Z-Wave 700: How to implement UART with interrupt into the Z-Wave Embedded Framework
Linked documentation (found in Simplicity Studio)
This document also describes how to setup the Z-Wave region and serial debug, which is not covered by this KB.