Bit of a follow-up of a thread I started earlier on home assistant.
TL;DR: what is the spec of the SPI transmitted values for the neuron models?
Since my posts there, I've been reading a bit about the way home assistant does its magic and it has a lot of built-in support for asynchronously polling IO -- which is pretty much what I'd like to do. The next step I thought of was to have a direct way to read those values -- which appear to already be available through the spidev overlay; part of the solution provided by you in the neuron modbus TCP server
I already tried reading the spidev directly through a python library called spidev, which is quite simple really (example from neuron L303)
>>> import spidev >>> spi = spidev.SpiDev() >>> spi.open(0, 1) >>> spi.readbytes(10) [250, 0, 85, 14, 182, 10, 250, 255, 255, 0]
My question now: is there any spec of how the spi device puts those values on the spi line? How should I interpret these values? I can probably start looking into how the modbus TCP server interprets these values, but it's probably easier to ask here first :)
The specification is ModBus RTU, with a custom header. For details see: http://www.simplymodbus.ca/FAQ.htm
In practice all the Neuron TCP server does is to repack the messages from ModBus TCP to a customised ModBus RTU. The overhead associated is therefore minuscule, albeit it does exist.
It should also be noted that if you do wish to use the device directly regardless, you may have an easier time using our OSS image from downloads.unipi.technology, which uses a custom driver to get around certain problems associated with spidev. That way you will have a SYSFS interface along with a documented message structure. spidev is only used in EVOK for legacy reasons and ease of installation, both Mervis and CODESYS use the kernel driver instead.
The IO is not asynchronous in any case. The primary reason is that there is only a single communication channel back towards the primary RPi board, and so there is no convenient way to inform it of changes as they happen. The number of interrupt lines which would be required is considerably more than the number of GPIOs provided by the RPi itself. At any rate, the maximum supported polling speed of 12MHz is more than sufficient for a fast response time.
Another note - the response you read is simply a response to a request sent by the Neuron TCP server, there are no messages originating from the device, only replies. Indeed the HW devices involved are not designed for the subsidiary SOCs to act as SPI masters, only slaves. Therefore they are not capable of transmitting any information by themselves.
The way SPI works is that the same number of bytes is transmitted both ways, it's not possible to send one-way messages using standard SPI (but see below).
Note that the MOSI/MISO lines are used simultaneously. It is technically possible to use one of several non-duplex SPI schemes, but they are not commonly supported by all hardware (including some of the hardware components we use) and in practice would only limit the bandwidth available on the device.
For further information you may benefit from reading the source code for the driver section responsible for SPI parsing, unipi_spi.c and unipi_spi.h
Thanks for the quick and very extensive reply!
Follow-up question: with the modbus TCP server / evok, I know it's possible to change the behavior of how I/O should be interpreted, e.g. the toggle or direct switch functionality. How is this initiated? Is this an interpretation that happens on the "server side" (pi-side) or is this some behavior that actually gets set on the SPI slave device? Does the server side also keep the SPI slave register state locally in its memory?
As a more generic question, what kind of software is running on the SPI slaves? Probably something like a single program that handles the modbus RTU interfacing?
Besides the SPI interfacing, are the standard GPIO also still used in the 24V pins? Are they also made available via the neuron TCP server?
About the asynchronous behavior: SPI does seem indeed inherently synchronous. It might be possible to have the slave devices initiatie interrupts, but most docs I've read about it seem to indicate it's indeed not that common + I probably wouldn't know how to do it in practice :). In that respect, I am beginning to doubt whether there's much to gain by bypassing the modbus TCP server ...
After giving it some more thought and playing around with the setup, my main motivation really is that my (home asssistant-based) integration still feels rather slow, so I was looking on how to improve on that.
From what I gather here however, there's probably more to gain by improving the way the front-end queries (polls) the modbus TCP server -- so I should probably focus more on that instead.
Apologies for the somewhat delayed reply, I was away for a few days of personal time off. Basically you are right - the SPI slaves are 48Mhz STM SOCs, which run an embedded/os-less program. Depending on the application there may be some caching on the side of the client, but when using pure ModBus by default all there is is a couple of mutexes used to serialise the requests.
In terms of the program it has a well-defined interface, which is available at our download site - section Modbus register maps. All the state is stored inside the SOCs, and can optionally be stored in one of two kinds of non-volatile (flash) memory on the chip. This has a number of advantages, namely higher reliability and orders of magnitude faster startup to the saved state than the Raspberry Pi itself has. In order to handle all the various functionality the program inside the SoCs has to be actually quite complex, running into tens of thousands of lines of code each. (every functionality in the SoC has a separate program, though they have much of the internal structure in common)
The Raspberry Pi only acts as a communication interface, and therefore has no connection to the I/Os - in fact there are several layers of isolation between it and the connectors.
With all that said - if you have trouble with sluggishness, have you disabled input debouncing? That alone will introduce 30ms latency.
Martijn Hemeryck last edited by Martijn Hemeryck
Thanks for the reply.
Since my last posts, I have been fiddling around with the setup and noticed that when I directly poll the modbus tcp server, the updated state is made available almost instantaneously.
My issue is really on the polling side of the home assistant visualization I am using. I have now started a project to do the polling in a separate server, that then pushes out any changes in the form of an event via MQTT, see https://github.com/mhemeryck/unipusher . It currently works as a proof of concept for a single toggle switch, I am now looking into how I can make it more scalable to all kinds of inputs / outputs. I'll get back here if this ever works out :)
EDIT: given it's not really unipi specific, I decided to rename it to modbridge, see https://github.com/mhemeryck/modbridge