SNAPpy Scripting Tips¶
The following are some helpful tips (sometimes learned from painful lessons) for developing custom SNAPpy scripts:
Beware of Case Sensitivity¶
In SNAPpy (as with Python), identifiers are case sensitive - foo
is not the same as Foo
.
SNAPpy is a dynamically-typed language, so it is perfectly legal to create a new variable on-the-fly. In the following
SNAPpy code snippet two variables are created, and foo
still has the original value of 2
:
foo = 2
Foo = "The Larch"
Case sensitivity applies to function names as well as variable names:
linkQuality = getlq() # ERROR! Unless you have defined your own function
linkQuality = getLq() # Probably what you want
Beware of Accidental Local Variables¶
In SNAPpy (as with Python), all functions can read global variables, but you need to use the “global” keyword in your functions if you want to write to them:
count = 4 # create global count and set it to 4
def bumpCount():
count = count + 1 # global count will still equal 4
def bumpCountTry2():
global count # needed to avoid creating a local version of count
count = count + 1 # will actually increment global count
Don’t Cut Yourself Off (Packet Serial)¶
SNAPstack talks to its “bridge” (directly connected) device using a packet serial protocol. SNAPpy scripts can change both the UART and Packet Serial settings.
This means you can be talking to a device via SNAPstack and then upload a script into that device that starts using that same serial port – or even just the same SNAP engine pins – for some other function (for example, for printing script text output or as an externally triggered sleep interrupt). SNAPstack will no longer be able to communicate with that node serially.
Serial Output Takes Time¶
In the following example, there is likely not enough time for the text to make it all the way out of the device
(particularly at slower baud rates) before the sleep()
command shuts off the device:
def goodNightMessage():
print("imagine a very long and important message here")
sleep(...) # sleep parameters vary per platform
One possible solution would be to invoke the sleep()
function from the HOOK_100MS
hook
event. First, create a new global by adding it to the top of your script:
goodNightCountDown = 0
Then, change the goodNightMessage()
function to:
def goodNightMessage():
global goodNightCountDown
print("imagine a very long and important message here")
goodNightCountDown = 500 # actual number of milliseconds may vary
Finally, add this logic to the handler for HOOK_100MS
@setHook(HOOK_100MS)
def callEvery100ms(tick):
global goodNightCountDown
if goodNightCountDown != 0:
if goodNightCountDown <= 100: # timebase is 100 ms
goodNightCountDown = 0
sleep(...) # sleep parameters vary per platform
else:
goodNightCountDown -= 100
SNAP Engines Do Not Have a Lot of RAM¶
SNAPpy scripts should avoid generating a flood of text output all at once, because there is nowhere to buffer the
output and the excess text will be truncated. Instead, generate the composite output in small pieces (for example, one
line at a time), triggering the next step of the process using the HOOK_STDOUT
event.
SNAPpy Numbers Are Integers¶
2/3 = 0
in SNAPpy. As in all fixed-point systems, you can work around this by scaling your internal calculations
up by a factor of 10, 100, etc. You then scale your final result down before presenting it to the user.
SNAPpy integers are 16-bit numbers and have a numeric range of -32768 to +32767. Remember that 32767 + 1 = -32768
,
and be careful that any intermediate math computations do not exceed this range, as the resulting overflow value will
be incorrect.
A side-effect of SNAPpy integers being signed is that negative numbers shifted right are still negative, because
the sign bit is preserved. You might expect 0x8000 >> 1 = 0x4000
, but it is 0xC000
. You can use a bitwise and
operator (&
) to clear the sign bit after a shift:
myInt = myInt >> 1
myInt = myInt & 0x7FFF
Pay Attention to Script Output¶
Any SNAPpy script errors that occur can be printed to the previously configured STDOUT destination, such as serial port 1. If your script is not behaving as expected, be sure and check the output for any errors that may be reported.
Don’t Define Functions Twice¶
In SNAPpy (as with Python), defining a function that already exists counts as a re-definition of that function. Any script code that used to invoke the old function will now be invoking the replacement function instead. Using meaningful function names will help alleviate this.
SNAPpy Has Limited Dynamic Memory¶
Functions that manipulate strings (concatenation, slicing, subscripting, chr()) all pull from a small pool of dynamic (reusable) string buffers.
You still do not have unlimited string space and can run out if you try to keep too many strings. See each platform’s section in the SNAP Reference Manual for a breakdown of how many string buffers are available and what size those buffers are.
Use the Supported Form of Import¶
In SNAPpy scripts you should use the form:
from moduleName import *
from synapse.moduleName import *
from moduleName import specificFunction
Be Careful Using Multicast RPC¶
If all devices hear the question at the same time, they will all answer at the same time. If you have more than a few devices, you will need to coordinate their responses if you poll them via a multicast RPC call. Here are some possible solutions:
NV18 - Collision Avoidance inserts some random delay (up to 20 ms) when responding to multicast requests, to assist in overcoming this.
NV16 - Carrier Sense and NV17 - Collision Detect help ensure you do not have too many devices talking at the same time.
A directed multicast message has a built-in delay factor. By providing an empty string for the destination addresses, it will behave the same as a multicast message; however, you will still be able to take advantage of the new built-in delay feature.
Application-level control of when your device responds to a request.
Recovering an Unresponsive Node¶
As with any programming language, there are going to be ways you can put your devices into a state where they do not respond. Setting a device to spend all of its time asleep, having an endless loop in a script, enabling encryption with a mistyped key, or turning off the radio and disconnecting the UARTs are all very effective ways to make your SNAP devices unresponsive.