Category Archives: easyIPC

easyIPC Design

I have written a lot about the intentions with easyIPC so it’s time we dig into the details of the protocol. easyIPC will follow the OSI model on a pragmatic level as illustrated below.

Smart SPI, RS-X and CAN-X are layer 2 links. Their only responsibility is to transport generic data between two nodes and maintain that link.

Stream routing is a small layer that will look-up DID & SID, replace them and forward messages either to a different Link or to an application.

Managing Entity is a database maintaining status over the tree of devices and it’s resources. This is the more complex part where we provide plug & play, dynamic resource allocation, redundancy and all the complex features in easyIPC.

The API is a thin layer interfacing easyIPC into various applications and programming languages. On Linux this will be running as a daemon (Service on Windows) with applications connecting as they start/stop. On an embedded device we use a tighter technique written in C/C++.

Watch out for updates on the doc pages.

Smart SPI

Smart SPI is a Layer 1 – 2 protocol using SPI to establish a direct communication link between Raspberry PI and Hat’s. SPI itself is a bit oriented full duplex protocol where the master control the speed by ticking a CLK signal. For every cycle on the clock we transfer a bit on both MOSI and MISO. Using SPI in 1:1 we only need GND, MOSI, MISO and CLK, but a CS (Chip Select) is needed for communication with several devices.

In a classic SPI design we would set a Chip Select pin and communicate with that device, select the next and communicate with that in a sequence.

All easyIPC aware Raspberry PI Hat’s use a technique called “Smart SPI” that implement several tricks to optimize SPI usage.

  • The stream on MOSI include target address allowing 8 Hat’s to read the message and filter out the messages they want to see. This allows Linux to send continuous on SPI without doing a bit-bang on CS pins that would slow down sending.
  • The message stream also include a Chip Select Message in software. As this is read by all Hat’s only the Hat selected will activate MISO. This allows the RPI driver to select MISO independent from MOSI stream.
  • MISO Send algorithm will enable Hat’s in sequence and automatically switch to the next after 1ms or as soon as the Hat report 0 messages. Bit padding is supported on both MOSI and MISO to complete a message.

Start-Up Procedure

The Start-Up procedure consist of a sequence of operations that must be executed as the system is powering on. The Hat’s will at this time not know their own Hat-number and as such not be able to communicate using Smart SPI.

  1. We Set Chip Select to 1 and send Device Identification Request with number “1”. The Hat that see it’s CS enabled will consume this message and set it’s id to 1. All others will ignore the message.
  2. The Hat will respond with a short Device ID Response. This is a short message to tell the master that the device is active. Master will on a slower scheme attempt to send Device Identification request to device numbers that are not active to enable Hat’s at a later point. Device ID Response tell the Master that the device is active and ready to communicate using Smart SPI.

Normal operation

  1. Once a full iteration through all 8 Hat’s are completed we start using Smart SPI. The Master will on regular intervals continue to send Device ID Request to Hat’s that’s not active. If a Hat fail to answer normal messaging it will be moved to Non-Active list until Master can perform a successfully ID request.

Note that the full start-up procedure for easyIPC consist of other sequences, but Smart SPI is a layer 2 link only responsible for sending/receiving between Raspberry PI and the Hat’s. See easyIPC for the higher level start-up procedure.

Smart SPI Message format

Length is a 8 bit unsigned integer that indicate the total length of the message in bytes. Minimum length value is 6 bytes since minimum payload is 1 byte. Payload length = Length-5.

DID (Device ID) is a physical address needed by layer 2 for message transfer. This is always the device ID. MOSI will contain the destination ID, MISO will contain the Source ID.

DID=0 is used for broadcast. Device 1-31 is a physical Device #.

SID (Stream ID) is the ID of a point to point stream. Stream 0 is always the device and layer 2 link. Stream is used for internal addressing on a node.

SEQ (Sequence Number) is a 1 byte number that start on 1 and wrap at 255. The value 0 is used for none-sequence. Each side in a Smart SPI need to remember the last messages sent and be able to repeat them on request. The receiver will verify that the SEQ increment with one, and if not issue a MessageMessage Repeat Request. MISO Switch Request also contain the last message received in case the last message was cut short. Sequence is unique per device and only intended to guarantee that all messages are transferred.

MID (Message ID) is actually the first byte in the payload. The meaning of the payload needs only be known by the stream end-points.

Payload is the actual message parameters/data.

CRC is a 2 byte CRC to ensure message integrity.

Device ID Request

Device ID Request is sent to a device that also will be selected by a Chip Select pin. Any device receiving this with CS set to 1 will accept this as it’s ID and respond with Device ID Response. The device will accept the address in the DID field as it’s address

The device ID procedure will send a Device ID Request on MOSI and then start padding with zero’s for a selected response time of x ms. The device needs to respond within this time or it will be marked as inactive and not included in the active iteration.

Master will attempt to re-connect to inactive devices ca once per sec.

Device ID Response

Device IS response is send by a device that receive Device ID request while chip select is active. This indicate that the device have received it’s address and established a link capable of communicating on Smart SPI.

Devices that fail to respond before Chip Select goes 0 (Zero) will be listed as inactive. SPI will always have a number of fixed slots that in theory is max 31, but the current limitation on Hat’s are 8.

Inactive Devices are listed separately and Master will use Idle time to attempt to re-connect to devices. See Device ID Request.

MISO Switch Request

MISO Switch Request is used to switch MISO Sender. All devices will set MISO to 3-state and stop communicating. The device currently sending will finish it’s current message and stop.

The Switch procedure contain a programmatic delay intended for the sending device to detect and abort it’s sending. Once that times out the new device will assume the MISO is available and start sending.

MISO Switch Response

MISO Switch Response is sent after a delay needed to secure that the sending device stops. This is sent from the new device and indicate the current queue length. The next message is the first message in the queue.

Message Repeat Request

A repeat request is issued to repeat 1-2 missing messages. The response will either be messages repeated or a Message Repeat Reject if the sender is unable to repeat the messages.

Message Repeat Reject

Sent in response to a Message Repeat Request if one side is unable to repeat the messages. Can happen if the gap in sequence is to large.

Link Reset

Issued by both sides to reset sequence numbering. Used as a response to errors.

See documentation page for an updated Version of this post…

Universal Motor Controller

I designed this universal motor controller capable on driving DC-, Stepper-, BLDC- and even AC – motors earlier. The design parameters was 12-24V at 15A. This is quite a capable controller, but I did a mistake that limit the controller to 12-20V since I connected the Gate Drivers to the Motor PSU directly. To compensate for this I need to modify the design and implement a separate 12V PSU. As I correct this I also want to consider some additional changes.

I am considering is to replace the RS485 with an isolated RS485 due to the amount of energy involved. The 3rd change is Ethernet on a separate adapter board. Basically I want to copy the modules I use on the Universal Adapter as soon as I have tested them.

I am not sure about Ethernet. Ethernet sound nice due to the functionality, but it is a clumsy, 4-wire 1:1 solution. RS485 is slower, but it is a 2-wire network. It actually makes more sense having dual RS485 to be honest. CAN & Ethernet is easier to deal with using an adapter board- RS485 is considered a lower level of communication than CAN because CAN have protocols like CANopen, J1939 etc. The reality is that if we use RS-X that changes.

What I probably should do at some point is to create a “Ethernet” on top of RS-X by using a dual RS-X connection. But, that is fun for later…

So the modified design will be

  • STM32F405RG MCU, 168Mhz, 32bit M4, 1MbFlash, 192KbSRAM
  • 4 x separate half bridge drivers, 30V @15A
  • Current sensors on all
  • BEMF sensors
  • PSU Voltage Sensor
  • Separate 3.3V supercap to sustain MCU in power dips.
  • 3 x hall sensors
  • 2 x temperature sensors.
  • 1 x resolver
  • 2 x end sensors
  • 1-2 x RS485
  • Adapter board for battery/caps
  • Adapter board for CAN/Ethernet/Wifi

Applications

  • Solenoid driver
  • DC Motor driver
  • Stepper Motor Driver
  • Brushless 3-Phase motor driver

PLC Modular System

I have worked far to long with 19″ cabinets and things you mount from front, so what I am thinking is a micro-version of a rack system. We box each electronic module with a backbone plug and custom front connectors. We then plug them in, wire using standard wiring (that we probably have to create) in front – no wiring in the back.

These boxes are simple and can easily be printed on a 3D printer. They will also allow us to mount more electronics tighter to address the total size.

A classic PLC uses 2 wires for a 24V pulse signal – we can typically standardize these so that we apply standard, plug & play cables and avoid as much custom wiring as possible. The top front is after all for wiring to equipment, not for internal wiring that is already done in the back-plane. I think this can work, but I need to talk it through with professional automation engineers – luckily I have access to them in numbers.

One drawback I can see straight away is vibration. I was planning to make this so small that it could fit mobile Equipment, but mobile Equipment vibrate a lot. We will need an outer box and holding mechanism that tolerate very high vibration – or more correctly reduce vibration.

Plain – Distributed Processing Part 4

A lot of Plain assembly syntax is experimental and will be reviewed as we move forward. I think it is important to get concept ideas out in the open for discussions – we can always optimize keywords and syntax later.

In Distributed Systems we need to synchronizing data between multiple modules and this  creates a need for a distributed database system. The transaction mechanism we created earlier fit straight into this, but I need to review the concept to cover a few loose ends.

use System
Module LedHat
            Object LedGroup
                        Bit Led1
                        Bit Led2
                        Bit Led3
            End

            Interface C LedGroup leds[1..6]
            ...
End

My previous example shows how I can call a function located on a different device. In this New example I want to set the leds directly from 32xIO by sharing data.

use System
use LedHat
Module 32xIO
            Transaction LedHat.leds[1]
                        LedHat.leds[1].Led1=1
            Update
End

The “leds” array is declared as interface allowing other modules to access it, so we can as well just access the array from 32xIO inside a Transaction statement. What happens is that we use DRA to connect between LedHat and 32xIO as previously explained, but we now need to (1) send a message to lock access, (2) make the changes, (3) commit changes. We get two extra messages, but less code this way. This was the easy part!

The system illustrated above is two systems wired as one just to complicate things. 32xIO #1 need to connect to LedHat #1 and 32xIO #2 need to connect to LedHat #2. This creates ambiguity as we by default will connect at random – first request will get first LedHat in list etc.

One way of solving this would be to use different module names – this is possible – but you will end up maintaining separate code for multiple devices depending on how they are used. Let’s try to avoid that as much as possible! In this case I have two “domains”, each with an array of 3 x 6 leds using the same name. And as I don’t want to hard-code the domain name I need something else.

A classic solution would be a complicated configuration, but I do not want that either – I have spent to many hours configuring complex communication systems to walk into that trap.

The concept I want to test out is a “System Diagram” – some kind off high level description of our system with modules, devices and how they connect. With this in place I would only need to tell my module what role it play in that system – lets give it a try:

use LedHat
use 32xIO
System SpiderRobot
            Domain green owner LedHat
                        LedHat greenLedGroup
                        32xIO greenIO
            End
      
            Domain blue owner LedHat
                        Ledhat blueLedGroup
                        32xIO blueIO
            End
End

The example above create a system “SpiderRobot” with two domain’s – “green” and “blue”.

I did consider using xml for this, but decided that this actually is part of our code and should use Plain syntax. I like xml because it is an excellent data storage format that can be edited manually if we need to – but, as xml syntax also can be very cryptic – difficult to read logic – this should not be part of any programming language – IMO!

pd 32xIO SpiderRobot -d blue

This is a proposed pd (program download) command. I will review the utility command line later, so this is just a quick & dirty proposal – 32xIO and SpiderRobot are plain assembly (*.pln files). -d blueIO tell the pd that the module can see domain “blue” in addition to global domain.

As the 32xIO now request a LedHat it will only be given access to the LedHat in the same domain. As we start LedHat and 32xIO we also report what domain we are, or that we are unassigned – in the later case we need to be assigned domain visibility & roles by an utility later.

use LedHat
use 32xIO
System SpiderRobot
            Domain green owner LedHat
                        LedHat greenLedGroup
                        32xIO greenIO
            End
         
            Domain blue owner LedHat
                        Ledhat blueLedGroup1
                        Ledhat blueLedGroup2
                        32xIO blueIO1
                        32xIO blueIO2
	     Wire blueIO1 to blueLedGroup1
	     Wire blueIO2 to blueLedGroup2
            End
End

This example complicate our story a bit because I decided to add a 2nd LedHat and 32xIO to domain “blue”. In this case I still do not what LedHat to connect to so we need to “wire” the system manually since auto-wiring will not work anymore.

The added “wire” statement solves this, but I just decided that having two LedHat’s was a bit much, so I want to use only one and let the two 32xIO Hat’s access different led Groups.

use LedHat
use 32xIO
System SpiderRobot
            LedHat greenLedGroup
            32xIO greenIO
            32xIO blueIO
            wire greenIO to leds[1]
            wire blueIO to leds[2] as leds[1]
End

In this case I actually don’t need domain grouping so I just specify my 3 Hat’s and how they are wired together. The wire statement only specify visibility and how we view content. The code does in this case program leds[1] so as I wire blueIO to use leds[2] I also need to specify that this is seen as leds[1].

My concern here is that we introduce too many error scenarios, and as this is a highly experimental concept we need to be open minded for better solutions or loose ends.

Plain – Distributed Processing Part 3

The system I described in part 2 needs to send a message from a module in device #2 to a module in device #5.

This tree is basically the physical address of a resource as seen from RPI1 and RPI2. But, as this address might change with wirings we need a more generic way to program this. The key in this case is that we use the module names are a “resource”.

32xIO and LedHat are both reported as resources in the system during startup. The 32xIO module will due to the “use LedHat” statement request a “LedHat” resource and be sent an address in return. This is part of Dynamic Resource Allocation in easyIPC – a topic we have yet to cover. With an address we need to map a message routing from device#2 to device#5 – this is called a stream in easyIPC. A stream is always 2-ways.

1 The VM “32xIO” (Not the device) will initiate a DRA request. And as part of the request we assign a Stream ID on the device.
2 RPI1 decide to forward the request to RPI2 since this own a request of this type.
3 RPI2 will forward the request to the LedHat device using a managing stream id
4 The LedHat will allocate the resources and send a DRA Responce back with a selected stream ID.
5 RPI2 will set up it’s own Routing between this Stream ID’s and RPI1 and forward the response to RPI1
6 RPI1 will set up its own Routing between the stream ID’s and forward the response to 32xIO.

We have now set up a stream. Any message sent from 32xIO on that stream will be forwarded to the LedHat and wise versa.

DRA will also deal with re-allocating of resources and it is more details to it, but this illustrates how we will  (1) report the modules as resources and (2) connect resource streams in easyIPC.

to be continued in part 4 …

Plain – Distributed Processing Part 2

Our concept of distributed processing is going to need some attention to details in the Assembler/VM design, but it will work. We  now have two mechanisms involved:

  • We have an easy way to synchronize data between several modules
  • We can make on module execute logic on a different module.

The principles of how we do this through easyIPC is easy, but we need to dig into the detailson some some loose ends in our design. One such issue is module addressing in a larger network.

The block diagram above consist 7 devices in a system. We have two RPI’s one for controlling the actuator/sensor sub systems, and one for HMI. The HMI contains a Led Hat where I want to blink Leds from the Servo and stepper controllers.

 LedHat example

use System
Module LedHat
            Object LedGroup
                        Bit Led1
                        Bit Led2
                        Bit Led3
            End
            interface C LedGroup leds[1..6];
            Interface Func SetLedStatus(uint32 group, Bit l1, Bit l2, Bit l3)
                        Transaction leds[group]
                                    leds[group].Led1 = l1
                                    leds[group].Led2 = l2
                                   leds[group].Led3 = l3
                        Update
            End
End

32xIO example

use System
use LedHat
Module 32xIO
            LedHat.SetLedStatus(1,0,0,1)
End

These two code examples are the Plain code I expect to write.

to be continued in part 3…

VM Test Ground

I decided to use the 32 x IO (Servo, Analogue, Digital) as my test ground for the VM – the firmware will in this case include easyIPC (SPI in this case) and a 32 channel programmable IO controller. Each of the channels have capabilities like:

  • Servo with a 14 bit pulse resolution.
  • PWM out with 14 bit resolution. This is a bit-banged PWM that can be used on all channels.
  • Digital Signal Input
  • Digital Signal Output

 Some of the channels will have the following:

  • High resolution/frequency PWM signals
  • Analogue Input
  • Analogue output.

This firmware is an excellent test-ground because it includes highly programmable logic and a hard real-time core on the bit-banging part.

Lets draft some PLAIN Assembly code:

Enum uint32 Mode
            Servo=0
            PWM=1
            DigitalIn=2
            DigitalOut=3
            HRPWM=4
            AnalogueIn=5
            Analogueout=6
End

Object Channel 
            Mode chMode = Mode.Servo
            uint32 position=0
            uint32 frequency = 0
            uint32 duty = 0
            uint32 analogueOut = 0
            uint32 analogueIn = 0
End

Channel Ch1
Channel Ch2
Channel Ch3
Channel Ch4
...

Bit digitalIn[32]
Bit digitalOut[32]

This is a draft of how I want PLAIN Assembly to see the easyIPC objects. Ch1.Mode will be located at register 0x8000, but we can also locate data on selected registers as follows

Channel Ch1 at R(0x8010)

In this example I force Ch1 to be located at Register 0x8010.

The more tricky part is the integration with C/C++ code. The challenge is that the module and data we transfer must match what the C/C++ firmware expects. This last mapping is done by the real-time linker in the firmware that receive the above as a “PLAIN Module Specification”. If it fail it will need to reject the module. In theory it should never fail as the assembler should stop us, but it is always the possibility of firmware version mismatch. This part will need to be strict or we will just dig into loads of debug problems.

Map Channel to C(Channel)
Map digitalIn to C(DigIn)
Map digitalOut to C(DigOut)

Assign Ch1.chMode = Mode.Servo
Assign Ch2.chMode = Mode.DigitalIn
Assign Ch3.chMode = Mode.DigitalIn

Event digitalIn[2]
            Assign Ch1.position = 0
End

Event digitalIn[3]
            Assign Ch1.position = 100
End

This example configure Ch1 as a Servo and Ch2, Ch3 as digital input pins. The Map statements link the objects to associated C/C++ code. The Assembler (and real-time linker) will check these exact names and parameters – parameters must match on name and data type.

The Event statement “digitalIn[2]” declare that any change to bit 2 in the digitalIn will cause a call to this bit of logic. This will be called regardless if it is C/C++ or PLAIN assembly that makes the change.

 I think this will work, but I need to let it mature to catch up missing bits and see if I can optimize this. Notice that while this example is ca 40 lines in assembly we would be talking about ca 7 instructions in the resulting code (+ initialization). The initialization will in this case be nothing since 0 is the default value on all registers. The VM will automatically reset this before execution to avoid that we have random, default values. “Event digitalIn[2]” will generate 2 instructions – an Assign and a Return.

But, keep in mind that this draft is work in progress. I will implement this next to see how it actually works out in real life.

PLAIN – Virtual Machine

Languages like Java C# etc all use a virtual machine. This is a software package that need to start and interpret some kind assembly code. Languages like C/C++ compile into native assembly code that is much faster, but also tied to the hardware it run’s on.

A virtual machine have the advantage that we extend the platform with build in features made available in C/C++. This high level code occupy less space than binary code and executes under our control. The drawback is that it executes slower than native code, but I plan to challenge that by introducing very high level assembly instructions.

I would like to create a native assembler at some point, but for now I want to play around with a Virtual Machine design.

VM Block

The diagram above show how we link things together in the firmware. We use easyIPC protocols to communicate, RTOS and HAL + other modules tom access electronics and do complex stuff. The VM executes logic that control what we do.

Instruction Format

Our VM needs it’s own assembly language so we can use a 32 bit design as indicated above. A 8 bit Op-Code + 4 bit Category code leaves 16 pages of 256 instructions. Category 0 is the build in one, but other categories can be used for extended instructions. Length is the added number of 32 bit register, meaning an instruction can be 32 x 16 bits long.

Instruction Move

This show the MOVE R1, R2 that will copy content of R1 into R2. As this is an array of 31 parameters we can extend this to MOVE R1,R2,R3;R4 etc that copies R1 into R2,R3,R4. Op Code 0 is NOP (No Operation).

VM Tables

This diagram illustrate the internal core of the VM. We have a table holding 32 bit instructions stored in Flash. A Registers table that is located in SRAM for generic use in our logic, and an object table that use address 0x8000 to 0xFFFF to access easyIPC mapped content. Notice that the total register size is 64Kb to make the most of a 16 bit parameter. Stack is located in SRAM at 0x7FFF and up. Each PLAIN Module have it’s own, private stack.

PLAIN contains a set of registers known as easyIPC object registers located between 8000h and FFFFh. These are accessed the same way as stack and generic registers, but are wired to other software or hardware. If these registers are changed events will be generated to allow PLAIN or C/C++ to process changes.

The PLAIN specification allows an application to organize it’s own layout of these objects to guarantee that an application will execute correctly. The issue is that registers for selected hardware need to be located somewhere, and with various hardware/software combinations you end up with far too many registers for the world to be unique. To solve this the Assembler will create a map where all software and hardware is mapped correctly with PLAIN logic. This result in a map that follows the application. The run-time engine will need to map and resolve these calls as the PLAIN application is loaded.

This is work in progress so expect changes as we implement this. The design draft above is based on a proof of concept implementation done about a year ago.

Introducing PLAIN

One of the challenges with a distributed system consisting of multiple Raspberry PI’s, multiple Hat’s and multiple RS-X connected devices is how to control it all. At the end we are running a complex, distributed system and coding logic on this level using a classic programming language like C/C++ is not straight forward. C/C++ is unbeatable dealing with low level electronics, performance and algorithms, but it is a bit clumsy to write higher level logic involving distributed logic. I would like to keep C/C++ to deal with low level things and HOW we do things, but I need something better to describe the logic and WHAT we want done. Having used years with various programming languages, PLC and CNC machinery I basically want to create something new that does things a bit differently.

I decided some time ago to name this PLAINProgramming Language for Automation Industry – basically a language/Logic concept to control robotics on distributed systems. We can evolve this a bit as we move forward, but I have a few key requirements:

  • Generic in nature.
  • Extendable and able to use C/C++ modules.
  • Deal with distributed, high level logic.
  • Support a future graphical CASE tool.
  • State-engine and event based Logic at core.
  • Easy to read top-down text, no cryptic syntax.
  • Easy to learn – read Logic in 5 min – yet powerfully to support Professionals.
  • Embeddable into other solutions.
  • Real-Time Centric.

The previous work on easyIPC fits straight into this work as is establish a plug & play communication network using multiple techniques. What I lack is an Ethernet based protocol, but I will return to this later.

At this point I am planning a language specification, compiler and Virtual Machine. The specification will need to evolve as we move forward on try & fail. Actually I want to start with the Virtual Machine.