Raspberry for distributed control

A b s t r a c t and I n t r o d u c t i o n

This technical report is about installing and using Raspberry Pi machines as little distributed ser­vers especially for embedded process control applications, often in co-operation with other little machines in the same (W)LAN. That means: With the applications in question we always control external devices. And we do that almost always via Pi's I/O ports. Additionally we use MQTT and Modbus devices (screw terminals A & B in Fig. 1) as well and sometimes VoIP telephones (by Asterisk).

This paper’s predecessor “Raspberry for remote services” [33] was first published 2016 as a laboratory handbook for process control set ups with then Pi 2. Until July 2024, it was continuously used, updated and expanded based on experiences with Pi0 and 3..5 on a wide spectrum of real control applications, some of them in 24/7 use for years.

Over those years, some modules, libraries and tools we investigated and reported on originally turn out to be unfit to our real time requirements. Hence, in October 2024, I decided to remove topics unused in the last 4..6 years, update and restrict to newer PiOS and tools. The chapters were re-ordered under a new title as [31]. For omitted topics still see [33].

Fig. 1: “Raspi 4 with I/O

Target machines are Raspberry Pi Zeros and 3..4s, and very seldom Pi 1s or 2s.

The operating systems will be a Raspberry Pi OS lite. a Debian/Ubuntu derivative. Pi OS per se is no real time OS. Nevertheless, obeying some caveats described here, the lite variant may well be used in applications where max. latencies of 200 µs are tolerable.

With careful C programming we can implement PLC like applications with 1 ms as fastest cycle based on a Pi OS lite. Under some conditions I/O programs in Java ([32]) are feasible.

Motivation and goals

One might use a Raspberry 4 (or even 3, now better 5) as a little low performance PC, neverthe­less well outperf­orming last century's early PCs. This is not our topic here.

Our Raspberries will be configured as a device for special purposes, often embedded and with some process IO. Distributed co-operating Pis will communicate over a private protected (W)LAN. Workstations, PCs etc. on that LAN will be used for (human) observation and control.

Monitor, keyboard and mouse will hardly ever be present on our process control Pis. Mainten­ance and installation jobs can be made via (putty) remote access.

Following that route we will have a headless controllers that

● boots quickly and robustly at power on

● starts all its applications and/or services automatically

● runs 24/7 and has good real time performance

● fits, connects and re-connects robustly in its LAN and/or WLAN

● connects to process I/O via GPIO, SPI, RS232/445, private LAN, 1-wire or other

● provides administrative access remotely via ssl/ssh/putty and optionally
human operation and observation via Web interfaces as web server

● provides access to files via ftp (FileZilla, WinSCP)

● decent comfortable cross platform working environment using Eclipse and GCC tools.

Looking up those points we find us quite near the properties we must have in a “real” server; com­pare the Fujitsu Siemens RAID examples in [29] (notwithstanding the latter being a 1000 times heavier). Hence, some solutions and procedures may look astonishingly alike.

On the content

In Part I we describe the basic installation and commissioning with Raspberries.

Part II deals with process IO communication and real time services. We look at hardware interfaces, GPIO handling, timing, latencies, threading and cyclic tasks.

In Part III we deal with additional hardware and some exemplary real use cases.

Using names

Names and addresses used here (and in [27..31]) are not fictitious. This helps bringing examples, really working and proven, of commands, files, outputs etc. – without errors introduced by obfus­cating. Of course, you'll have to adapt IP addresses, names numbers etc. to your environ­ment and needs, even when this will not always be explicitly mentioned.

For References and Abbreviations please see the Appendix (page 69)., Refer to [29] for some Linux and server basics, abbreviations and else, also used here.

Prof. Dr.-Ing. Albrecht Weinert is founder and director of weinert-automation. He developed safety critical and highly available automation systems with Siemens AG and was Professor of Computer Science at Bochum University of Applied Sciences. a-weinert.de Some final experiments may bring few updates, soon. Rev. 59 (svn) 01.12.2024 Photos by A. Weinert

Table of Content

A b s t r a c t and I n t r o d u c t i o n	1

Motivation and goals	1

On the content	2

P A R T I The headless Pi	5

Preparing the SD-card	5

SD-card: save and restore	6

The size problem	7

First commissioning	7

Enable WLAN – or disable it	9

Force IPv4	9

Getting a decent directory listing	9

Update the Pi OS	10

The keyring problem	10

User interfaces	11

putty	12

ssh – the putty alternative	13

Apache 2.4	14

Installation	14

A cause of trouble	17

Installing PHP 8	18

Enabling file access	19

SFTP – FileZilla	20

SFTP – WinSCP	20

Cross-compile C for Raspberry from a powerful workstation	21

Eclipse	25

Command line – make project	25

Eclipse – troubles and hints	26

Part I's results	27

P A R T I I I/O and process communication	28

The pigpio library	28

Other GPIO libraries	30

Other protocols for process IO(, also)	30

Modbus	31

libmodbus	32

MQTT	35

MOSQUITTO	36

libmosquitto	37

Part II's results	37

P A R T III Process Periphery and control	38

Using the GPIOs	38

.An application as service	46

rc.local	46

cron	46

Mimic a service – start stop restart enable disable	47

Making a library	48

Process IO hardware	48

LEDs and buttons – direct IO	49

Speakers and beepers	50

Relays	51

Power transistors	52

Communication hardware – RS485	53

PoE – power over Ethernet	55

Communication	56

1-wire	56

Real time	57

Absolute timing	58

Latency and accuracy	58

Cycles and threads	59

Threading and synchronizing	59

Co-operating applications	60

Shared memory	61

Semaphore sets	62

Watchdog	62

GCI programs	63

Data exchange with AJAX & JSON	65

Getting Web data – cURL	65

Asterisk	66

The result – and where we are	67

A p p e n d i x	69

Miscellaneous commands	69

Abbreviations	73

References	74

P A R T I	The headless Pi

The OS images mostly used are variants of Pi OS, formerly known as Raspbian.
For servers and process control with real time requirements we strongly recommend*) the “lite” variant without GUI nor graphical tools. Standard Linuxes call that a “server distribution”.
Note *): For “real” real time process control with SPS type 10ms or even 1ms cycles its a must.

Warning: GUIs with pointing devices do definitely spoil Pi OS’s real time performance essential for our control applications. Hence, our Pis with “lite” OS come without keyboard, mouse and monitor, keyboard. Computers without such devices are called “headless”

Part I’s basic installation gives us a Pi with fixed IP address(es) we can handle, control and observe remotely. So we can put it to its (embedded) dwelling and connect to its (process) IO.

Preparing the SD-card

Raspberry's OS and the file system usually reside on a µSD card -- without the “µ” for very old Pis not used here. New Pi OSs can also be on a SSD-drive; this features we did not (yet) use on our embedded systems. On one Pi4 installation we added a 2T SSD with an USB-adapter as extra storage for a SMB service (as a tiny NAS).

On embedded real time systems use a SD-card of suitable size and good speed – scrapping here for a long running server application with logs and critical timing is wrong. On the other hand too large cards can be annoying**) when reading, writing and checking images.
Note **): 64GB with 200/140MB/s would take about 14 minutes. Slower cards get in range of hours.

To prepare Raspberry's OS and the file system we use Raspberry Pi Imager (figure 2) to choose a suitable Pi OS version, make the basic settings and write that image to the card. Formatting will be done in the procecc. Just put the SD-card in an up-to-date USB-adapter and start the imager.

Fig. 2: rpi-imager.exe

In the first step (figure 2) you chose the Raspberry type, the OS and the adapters drive, in our example Pi3b, Pi Os lite 64bit and G:. Do check the drive chosen to be absolute sure it is the SD card in question and you don’t need any data there.
In the next step edit the settings: name, WLAN, user:password, SSH and no telemetry.

When having all checked and the appropriate settings, start the formatting and imaging. It will take some time. When ready, un-mount the device (if the exemplary G: is still present), remove the µSD card from the PC and put it in the target Raspberry.

But before going on it is essential to be able to handle saving and restoring our Pi’s SD cards. There are pitfalls.

SD-card: save and restore

For the basic installation we prepared the [µ]SD card with rpi-imager.exe. We put the card into the Pi in question, logged in and did the basic settings. We may have started installing pro­grammes and transferring data files. But pause: All our work so far resides in the tiny µSD card.
If it is damaged electrically or crucial parts are spoiled by a program violating its memory bounds all work might be lost.

Fig. 3: Diskmager

The only rescue can be saving total images in a (big) file on a PC or disk station and when needed from there to another SD card of equal size (and speed). On a Windows PC we can do this with the “Disc Imager” (figure 3): C:\util\ImageWriter\Win32DiskImager.exe

To save the actual state
●	shutdown and un-power the Raspberry,
●	remove the card and insert it to your laptop/PC
If there’s no slot use an actual USB adapter. Old ones may have problems with nowadays cards.

To save as .img file with Win32DiskImager chose a target file and click “Read” (German: “Lesen”), see figure 3. The target file will usually not exist, just chose an appropriate name.

The Win32DiskImager.exe works on Windows 11. But, alas, there are limitations:
●	must be started as admin! (One has to accept it.)
And occasionally – but then very annoying – we had the following effects
●	it had to be shutdown before every next operation (even with the same card)
●	sometimes the SD card must have been present before the start
●	typing or copying the file name , D:\temp\test.img in the example fig. 3, into
the text field may fail. In that case click the blue folder icon, find the best fitting file
in the opening explorer window and then edit the line “Image-Datei” (image file).

To write the saved image to another SD-card, the same size as the original should work.
A bigger one is always usable, and may be the rescue if Win32DiskImager complains on size.

Warning:	When hitting the size problem and ticking “write anyway” Win32DiskImager
crashes leaving the target unusable. Even when reporting a normal end we
had cards never booting in the Pi

Caveats for Win32DiskImager:
●	Be extremely careful with “Write”.
Double check the drive letter. Win32DiskImager can kill your system (as admin).
●	Be patient 64GByte µSD Read – Write – Verify may take 1h20min with two interactions.

The size problem

Most probably, its cause is no two [µ]SD-card types having the same size.

●	Do not go ahead when getting complaints on the operation planned.
Click exit – especially when hitting the size problem, see Fig 4.

Fig. 4: The size problem

If you go ahead on a some percent size misfit the card will not work in the Pi even if “Verify” says OK. Do not believe in "The extra space does not appear to contain data" in Fig 4, even when here the 64GByte source µSD was 95% empty and the missing size is less than ½%.

For the size problem Win32DiskImager is not to be blamed. We used dd with the very same cards on an Ubuntu laptop. It finished the read and write operations (taking ages) without crashing but complaining on the non fitting size afterwards [sic!]. The card didn’t work in the Pi either.
Win32DiskImager would have saved you an hour (when hearing the warning).

Writing an image file read from one SD card to another smaller one did always fail – at least we never enjoyed it otherwise.
A bigger size as destination will work. But you will not persevere with than in the long run.

Tip 1: If you want e.g. some µSD cards for saving n working states, and having m for experimental improvements and r for reserve, do have/buy n+m+r cards of same make and size. Requiring same speed was not necessary in out lot of ScanDisk 32G, as all had 124735488 sectors (see Fig 4), We did a complete re-install as we could not get other 32G µSD cards with 125493136 sectors.
Tip 2: Choose a reasonable size but do not think big, 128G when 16G would be enough, e.g..
Consider each SD card image file will be as large as the card with Win32DiskImager.

First commissioning

These are first steps to bring in a new OS with a freshly prepared [µ]SD card:
●	take the not powered target Raspberry and put the card in
●	check your DNS server for your new Pi's IP addresses. In a distributed control system
we strongly recommend fixed LAN and/or WLAN IP adresses.
Tell your DNS server, router, Fritzbox or else to act this way
●	connect power (usually by USB 5V 2A..4A)
●	On the PC fire up putty or the ssh program and connect to that IP

When all went well, you'll be prompted to log-in (with user:password you choose in the imager).

Putty will warn you on a security breach; do accept the ssh fingerprint.

With working (W)LAN and in a network with a DHCP server you should check the Raspberry's usable IP address(es) by ifconfig.

In case of success you have an empty working Pi with one user. Congratulations.
“Empty” in our case means just the programs and libraries belonging to the lite version choosen.

A Note on the nano editor and on putty

With a “headless” Pi putty will will most often use remote console and the standard nano our text file editor. nanos’ handling, alas, is in no way standard, but it is easy to learn and one has to.

When trying to navigate with (numeric pad) arrow keys one may get an “unbound key” error instead of a cursor movement. Here is not nano but putty the culprit.
On the putty icon do Ctrl + right-click to bring up the context menu . Then
●	Select "Change Settings..."
●	Select Terminal → Features in the left-hand navigation tree
●	Check "Disable application keypad mode"

And when at putty change the colours to a high contrast dark on light setting. Otherwise nano’s syntax highlighting will display some text items in almost the same color and brightness as the background. (The programmers of nano must be able to edit unreadable text.)

Hint: If you use ssh user@thePi in the cmd.exe (instead of putty), a nano editor used there gives much less colour troubles.

Have more than one user

Formerly Pi OSs came with a pre-definded user:password = “pi:raspberry”. It is most strongly recommended not to use this combination well known by all hackers. Cases were reported of well meaning software (updates) eliminating that user even when it had another password. If the murdered user one was only one the machine is practically unusable. We had to mourn several accounts pi, too. Fiddling with the µSD may get you a one time log in. If that does not work, one falls back to a saved image of the machine saved or a complete re-install. Due to those expe­ri­ences we recommend a) not to use pi (we use sweet now) as main account and b) have an additional users (pius e.g.) with sudo privileges (resp. same groups) to have still putty and ftp:

sudo useradd -u 1001 -g 1000 -c "sweet's substitute" -m pius

ls -la /home/pius

sudo passwd pius

sudo usermod -a -G sudo pius

sudo usermod -a -G adm pius

sudo usermod -a -G dialout pius

sudo usermod -a -G cdrom pius

sudo usermod -a -G audio pius

sudo usermod -a -G video pius

sudo usermod -a -G plugdev pius

sudo usermod -a -G netdev pius

sudo usermod -a -G games pius

sudo usermod -a -G users pius

sudo usermod -a -G input pius

sudo usermod -a -G render pius

sudo usermod -a -G netdev pius

sudo usermod -a -G spi pius

sudo usermod -a -G i2c pius

sudo usermod -a -G lpadmin pius

id pius

id sweet

The last two command allow an easy check of both users belonging to the same groups.

Note again: Traumatised by the to early death of several user accounts "pi" we strongly recommend having
two sudo users (sweet & pius never pi). Feel free to add an extra standard user to play with.

Note2:	Besides being better known to hackers than to new users, "raspberry" is and never was a good	password (in case of any language mismatch in settings).
Rule:	Avoid passwords (and names, also) typed on keys misplaced on	US keyboards nor special ones
on a French or German keyboards (é è ä Ö ß, e.g.).
Hint: If in trouble typing raspberrz might succeed, when rasperry fails.
Note3:	That keyboard (layout) hassle won’t occur with headless access – provided your workstation / PC
by itself has no such troubles.

Enable WLAN – or disable it

The Pi3, Zero, 4 and 5 have WLAN on board, Pi1 has not. The build in WLAN's only disadvantage is the antenna being fixed to the board. Encasements with good protection, shielding and heatsinks may render it unusable. While LAN (with DHCP) is working out of the box, WLAN will require some settings. This is best done by command line, as well described in [53]. On the other hand, when wanting a known WLAN do the setting in the Raspberry Pi Imager.

In other cases or problems a most useful command is listing visible WLANs by:

sudo iwlist wlan0 scan

In doubt if the WLAN is wlan0 use ifconfig. In our case it didn't see 5 GHz cells. The Pi3 (not 3+) supports 2.4 GHz only, even if the Broadcom chip should do both. From the iwlist scan output we chose a WLAN in acceptable quality (Micha42Netz in the example here) to connect to.

Cell 01 - Address: 5C:49:79:6C:03:81

Frequency: 2.462 GHz (Channel 11)

Quality=42/70 Signal level=-68 dBm

Encryption key:on

ESSID:"Micha42Netz"

Bit Rates:1 Mb/s; 2 Mb/s; 5.5 Mb/s; 11 Mb/s; .....

Mode:Master

IE: IEEE 802.11i/WPA2 Version 1

Group Cipher : CCMP

Pairwise Ciphers (1) : CCMP

Authentication Suites (1) : PSK

sudo ifconfig wlan0 down/up disables/enable the WLAN. To disable it permanently put @reboot sleep 10; ifconfig wlan0 down into the crontab file (sudo crontab -e).

Force IPv4

Even when all your LAN addresses are IPv4, you may notice wlan0 using IPv6 only, even when the access points live in the same LAN and the DHCP server distributes IPv4 to all. Add the line

net.ipv6.conf.all.disable_ipv6 = 1

net.ipv6.conf.default.disable_ipv6 = 1

net.ipv6.conf.lo.disable_ipv6 = 1

to /etc/sysctl.conf by sudo nano /etc/sysctl.conf .

Getting a decent directory listing

The commands dir, ls and even variants ls -la etc. produce unsatisfactory output to put it mildly. Iit’s just ugly and information hiding. Hence type
nano ~/.bash_aliases	and add the following three lines:

alias dir='ls -lAh --time-style=long-iso'

alias diR='ls -lARh --time-style=long-iso'

alias sudo='sudo '

Those lines could have been put into .bashrc also, but .bash_aliases can be copied or transferred to other user accounts with less risc of spoiling other individual settings, e.g. by:

sudo cp ~/.bash_aliases /home/pius

sudo chown pius:sweet /home/pius/.bash_aliases

Changes in .bash_aliases and .bashrc will have an effect at next login.

Update the Pi OS

Having the very basic configurations done you should have a first thorough update:

sudo apt-get update # <-\

sudo apt-get upgrade # \_ loop until stable

sudo apt autoremove

sudo apt-get dist-upgrade

After commissioning and running productive (24/7 server) dist-upgrade should be handled with care. Instead of apt-get … you may get additional results by apt ….

While using sudo here you might see a warning like:

raspbian/dists/bookworm/InRelease:

Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg),

see the DEPRECATION section in apt-key(8) for details.

If so and if you want to clear problem now go ahead with “The keyring problem”.
It’s not urgent. So you could do it later and skip the topic for now.

The keyring problem

To solve the problem, type: sudo apt-key list to get

Warning: apt-key is deprecated.

Manage keyring files in trusted.gpg.d instead (see apt-key(8)).

/etc/apt/trusted.gpg

--------------------

pub rsa2048 2012-04-01 [SC]

A0DA 38D0 D76E 8B5D 6388 7281 9165 938D 90FD DD2E

uid [unknown] Mike Thompson (Raspberry Pi Debian armhf ARMv6+VFP) <mpthompson@gmail.com>

sub rsa2048 2012-04-01 [E]

/etc/apt/trusted.gpg.d/raspberrypi-archive-stable.gpg

-----------------------------------------------------

pub rsa2048 2012-06-17 [SC]

CF8A 1AF5 02A2 AA2D 763B AE7E 82B1 2992 7FA3 303E

uid [ unknown] Raspberry Pi Archive Signing Key

sub rsa2048 2012-06-17 [E]

-------------------------------------------------------------------------

For the (two in our case) keys listed, generate this command for the first:

sudo apt-key export 90FDDD2E | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/mikethom-key.gpg

Here 90FDDD2E are the last 8 hex digits (no space here!) in the long hex line. The file name is derived (freely) from the text. Check the result by:

sudo apt-key list

ls -lAh --time-style=long-iso /etc/apt/trusted.gpg.d

-rw-r--r-- 1 root root 1.2K 2024-10-25 17:11 mikethom-key.gpg

-rw-r--r-- 1 root root 1.2K 2024-07-04 02:05 raspberrypi-archive-stable.gpg

Checking “before and after” gives a hint to the background:
Once upon a time, all keys lived happy together in one file /etc/apt/trusted.gpg. Now this is deprecated. PiOS prefers to find each key in a separate file in /etc/apt/trusted.gpg. When a key needed isn’t found there it inspects the past’s one file – and will complain even if found.

User interfaces

As stated and verified, we can do real time process control with precise 1ms cycle time with a Raspberry Pi 3 and Raspbians / PiOSs from Jessie on – as long as we do not install an OS with graphical HMI.

In other words: Take the lite variants, only.

As long as we use the little machine as headless server all is well – and installing graphics spoils all real time cyclic process control endeavour. Linuxes on other OSs with graphics do this by dozens of busy extra services all running around. Adding graphics to a non graphical OS like Unix/Linux means adding a lot of foreign objects in various variants.

The standard remote access to our headless little machine is putty or ssh.

And yes: It is possible to write a console program (to be started by and usable via putty/ssh) that waits on a signal, gets data via shared memory and displays them on the console. (An example is hometersConsol.c.) It is also possible to get input from such console program and put the values and commands via shared memory to the process control application.

This "remote HMI by putty as shared memory coupled application" works perfectly reliable – but looks as stone age as the nano editor. It is quite natural to want this functionality in a web interface, and this at least*) in the same local (W)LAN where the headless process control machine can be “puttyed”.
Note *): One should specify “only” instead of “at least” for safety and security.

Graphics just by being installed im­pedes even modest real time applications. The detrimental effect needs nobody doing anything locally nor X-remote. On the other hand an Apache 2.4 with static web pages, pages with JavaScript and CGI, directory listings serving files has no [sic!] measurable detrimental effect on said real time performance. Your web design may even play with boxes, tables, pictures etc.. And we had no problems with 5 active web clients at the same time in the LAN or even remote via VPN. But one should not exaggerate the number of active web clients.

At first sight this may seem astonishing. But this approach, used judiciously, only transfers small amounts of data – partly static and cacheable – and puts all rendering and graphical toils to the workstation, laptop, tablet, telephone or whatever web-client. And in case of VPN the extra communication and encryption work is done by the router (FritzBox) and the remote client.

Hence, we use Apache 2.4 – on our process control Raspberrys – for remote HMI and putty or ssh for administrative work.

putty

With ssl enabled, from a workstation or laptop in the same network connect to your Raspberry with ssh using putty(.exe). And making a good putty configuration is well worth the trouble. Think of nice and readable colours, a good font, window title etc.. When satisfied give the configuration a recog­nisable name, like e.g. rasp61. Best use the Raspberry's hostname. To re-use the configuration command

putty -load meterPi -l sweet

Change the “sweet” when wanting another user account or omit the “-l name” option to interactively entering the user. Optionally make an icon with the command. When just entering putty you get a command window

Fig. 5: Putty’s command window

If you want to copy, modify and reuse such configuration you may use the putty GUI by
a) select a saved session and click [Load] (or just click on the session) a sessoload, (
b) optionally modify settings
c) optionally [save], optionally after typing a new session name.
d) [open] connect (Without modifications you may just double click on the session name.)

To save and transfer configurations across workstation borders, find them on Linux at
~/.putty/sessions/
and on Windows in the registry at

[HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\<ses.name>]

To list your putty session setting type

reg query HKCU\Software\SimonTatham\PuTTY\Sessions /s | grep Sessions

and get something like:

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\growPi

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\meterpi

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\pi4ast

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\pi4java

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\pi4nas

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\*** pi *** (manymore)

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\ubu1u1

Should a pure Windows user ask (what) where is grep, one answer is here:

D:\temp>where grep.exe

C:\util\msys32\usr\bin\grep.exe

Here is an example of a session's entry - far from complete:

D:\temp>reg query HKCU\Software\SimonTatham\PuTTY\Sessions\pi4ast

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions\pi4ast

Present REG_DWORD 0x1

HostName REG_SZ 192.168.178.144

LogType REG_DWORD 0x0

LogFileClash REG_DWORD 0x0

LogFlush REG_DWORD 0x1

SSHLogOmitPasswords REG_DWORD 0x1

SSHLogOmitData REG_DWORD 0x0

Protocol REG_SZ ssh

PortNumber REG_DWORD 0x16

CloseOnExit REG_DWORD 0x1

WarnOnClose REG_DWORD 0x1

PingInterval REG_DWORD 0x0

PingIntervalSecs REG_DWORD 0x0

TCPNoDelay REG_DWORD 0x1

TCPKeepalives REG_DWORD 0x0

TerminalType REG_SZ xterm

TerminalSpeed REG_SZ 38400,38400

ApplicationKeypad REG_DWORD 0x0

....ANSIColour REG_DWORD 0x1

Xterm256Colour REG_DWORD 0x1

BoldAsColour REG_DWORD 0x0

Colour0 REG_SZ 0,0,0

Colour1 REG_SZ 0,0,0

Colour2 REG_SZ 192,192,192

Colour3 REG_SZ 255,255,255

To save a) all putty settings or b) a single session open the registry editor, navigate to that branch

a) HKCU\Software\SimonTatham\PuTTY
b) HKCU\Software\SimonTatham\PuTTY\Sessions\pi4ast

ssh – the putty alternative

On Windows you can connect the command shell (usually cmd.exe ) with a Pi by

ssh sweet@meterpi && REM parameter is user at machine (name or IP)

The presentation usually outperforms putty. nano will show no unreadable light-yellow on hellgelb colours. Ending the ssh session with logout returns to the Windows shell.

A disadvantage is the shell instance (cmd.exe) being the terminal and hence out of use until the session ends. On the other hand it is possible to scroll back to the closed “ssh terminals”.

In seldom cases you might also be annoyed by the following ssh behaviour:
1.) At every start it complains on not to be able to create directory /home/winGuy/.ssh.
2.) At every session start it asks for the acceptance of the Pi’s fingerprint. Putty does this on
the very first session, only.

It seems the stupid thing is trying to store the Pi’s fingerprint on the Pi itself under the account of the windows user, not existing there. It’s otally grazy. By the way providing the wanted directory with all rights to everyone on Windows and Pi is no good idea – and it does not help. If like me, haunted by that look for ssh.exe:

D:\temp>where ssh.exe

C:\util\msys32\usr\bin\ssh.exe

C:\Windows\System32\OpenSSH\ssh.exe

This shows me having two ssh.exe, a Windows one and a ported but badly adapted Linux tool – the latter being on a better place on the PATH with good reasons in my case. Remedies are:

a) call C:\Windows\System32\OpenSSH\ssh.exe [parameters] directly every time
b) delete C:\Windows\System32\OpenSSH\ssh.exe
c) put @C:\Windows\System32\OpenSSH\ssh.exe %* in a new file C:\bat\ssh.bat

I recommend (and took) c):

D:\eclipCe18-12WS\rasProject_01>type C:\bat\ssh.bat

@C:\Windows\System32\OpenSSH\ssh.exe %*

Apache 2.4

As said on our Pis a web server’s main purpose is to provide a HMI for process control, delivering log and documentation files. All this is fixed content set/changed via FTP.
For the HMI communication and (dynamic) process control pages we use CGI, AJAX, JASON and JavaScript (see below), but not PHP except perhaps in one homeopathic dose.

And we are on a protected, non public LAN. Hence, we need neither domains, virtual hosts nor https. We address those Pis by their name or fixed IP address, provided by the own DNS / DHCP. Hence we can (should) live with a very simple Apache setting with full access for the apache account.

Installation

Apache 2.4 is (apt-get) provided in a reasonable way since the Raspian Stretch distribution. Installing Apache starts by

sudo apt update

sudo apt upgrade

sudo apt install apache2 -y

In this state the web server delivers only the “Apache2 Debian Default Page” (by the configuration 000-default; see /etc/apache2/sites-available/). According to our Pi’s name we want a site “meterpi” (in all lower case) en lieu de “default”. We prepare it by:

dir /etc/apache2/sites-available/

sudo mkdir /var/www/meterpi

sudo chgrp sweet /var/www/meterpi

sudo chown sweet:sweet /var/www/meterpi

dir /var/www/

sudo chgrp sweet /etc/apache2/sites-available/

sudo chmod g+rwx /etc/apache2/sites-available/

dir /etc/apache2/

We left Apache’s main configuration file untouched, except stripping most comments to get a better overview:

# /etc/apache2/apache2.conf 20.11.2024 Albrecht Weinert

# changes: strip most comments see them in the original file

# This is Apache's main configuration file on meterpi

DefaultRuntimeDir ${APACHE_RUN_DIR}

PidFile ${APACHE_PID_FILE}

Timeout 300

KeepAlive On

MaxKeepAliveRequests 100

KeepAliveTimeout 5

User ${APACHE_RUN_USER}

Group ${APACHE_RUN_GROUP}

HostnameLookups Off

ErrorLog ${APACHE_LOG_DIR}/error.log

LogLevel warn

IncludeOptional mods-enabled/*.load

IncludeOptional mods-enabled/*.conf

Include ports.conf

<Directory />

Options FollowSymLinks

AllowOverride None

Require all denied

</Directory>

<Directory /usr/share> ## not used so far 220.11.2024

AllowOverride None

Require all granted

</Directory>

AccessFileName .htaccess

<FilesMatch "^\.ht">

Require all denied

</FilesMatch>

LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined

LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined

LogFormat "%h %l %u %t \"%r\" %>s %O" common

LogFormat "%{Referer}i -> %U" referer

LogFormat "%{User-agent}i" agent

IncludeOptional conf-enabled/*.conf

IncludeOptional sites-enabled/*.conf

The reason for this compact overview will get clear in "A cause of trouble" below.

By

nano /etc/apache2/sites-available/meterpi.conf

we made a site configuration file according to our Pi’s name:

# /etc/apache2/sites-available/meterpi.conf 20.11.2024 17:30

# configuration for http://meterpi

<VirtualHost *:80>

ServerAdmin webmaster@localhost

DocumentRoot /var/www/meterpi

CustomLog ${APACHE_LOG_DIR}/access.log combined

HeaderName /logs/logsHeader.html

ReadmeName /logs/logsFooter.html

IndexOptions SuppressHTMLPreamble FancyIndexing FoldersFirst
IconsAreLinks HTMLtable

IndexIgnore logsHeader.html logsFooter.html

<Directory /var/www >

AllowOverride All

Require all granted

Options +Indexes +FollowSymLinks

</Directory>

## /var/www/meterpi/index.html

## /server

## /icons

## /incude

## /cgi .--> /var/log/apache2/

## /logs/apache2 ___/

## /^\ ## This link was the other was round

## /home/sweet/logs ## for years.See "Cause of troubles"

<Directory "var/www/meterpi/cgi >

Options ExecCGI

SetHandler cgi-script

</Directory>

<Directory /var/www/meterpi/logs >

AllowOverride All

Require all granted

Options +Indexes +FollowSymLinks

</Directory>

Alias "/icons/" "/var/www/meterpi/icons/"

</VirtualHost>

And, finally, we switch the Apache configuration by

sudo a2dissite 000-default

sudo a2ensite meterpi

sudo service apache2 stop & sudo service apache2 start

Hint: Never forget the two last commands after a change in Apache’s configuration. The web service may take a minute to stop and start. Doing

sudo systemctl reload apache2

is much faster, but we had cases where this procedure caused problems.

With this configuration all went well and .htaccess files did their work, like e.g. (shortened):

# /var/www/meterpi/server/.htaccess

# $Revision: 237 $ $Date: 2024-11-20 18:11:02 +0100 (Mi, 20 Nov 2024) $

IndexIgnore logsHeader.html logsFooter.html

IndexOptions +SuppressHTMLPreamble

AddDescription "minimal server info by PHP" hello.php

AddDescription "show most things PHP knows" info.php

and

# /var/www/meterpi/logs/.htaccess

# $Revision: 237 $ $Date: 2024-11-20 18:31:02 +0100 (Mi, 20 Nov 2024) $

IndexIgnore logsHeader.html logsFooter.html

IndexOptions +SuppressHTMLPreamble

ReadmeName /logs/logsFooter.html

HeaderName /logs/logsHeader.html

AddDescription "Web server log files" apache2

AddDescription "page view log" apache2/access.log* ## Note*)

AddDescription "http server errors" apache2/error.log* ##

AddDescription "Ereignisse und Werte" .txt

AddDescription "Kamerabilder" img

AddDescription "Türkamera Schnappschuss" cam

AddDescription "Ereignisse, Änderungen" meterpiDiary

## ... and more

Note*):	There is a symbolic link
/var/www/meterpi/logs/apache2 to /var/log/apache2/.

As we use the web server for process observation and control we like to see all relevant logs by http://meterpi/logs and in the directories shown there.

With the simple configuration indicated here we got everything working, look and feel like it did for about 6 years in a 24/7 process control job.

Alas, this a long story told from from its short (happy) end.

We started as we did many times with great success before: We just copied all our Apache data and configuration files from an equal or similar system to their respective new locations – and expected all or at least all most all would work.

Well – almost nothing did.

A cause of trouble

In the previous Installation (Raspbian Stretch/Buster running 24/7 for some years) we had ~/var/www as document root and all automated update activities are programmed to manipulate this document tree. (~/ was /home/pi and is /home/sweet/ now.) The automated updates respectively first provisions worked at once but the web server did not.

Apache seemed not to read most of its configured directories and not to follow symbolic links. Most .conf files and all .htaccess had no effect as one ugly directory listing demonstrated.

Internet research brought no clear causes or remedies. Some recommendations had nasty side effects but not the healing. There were speculations on new options like SymLinksIfOwnerMatch which should be turned of on instead of FollowSymLinks. Most tips didn’t help. And no trustworthy information on the background was found. The only clear info was:

● There are many fellow sufferers.

● From Jessie + then Apache to Bookworm + current Apache
Apache’s behaviour changed drastically.

Nightmarish hours of fruitless experiments and refined searches lead me this impression:

● Apache does its start up work as root and gets its basic files as such. (Not new, never mind).

● Apache delivers web content and read the relevant files as user www-data. (Not new, too)

● Apaches reading of these files is restricted to /var/www (+ subdirs).
and to /var/log (+ subdirs)

● The second one only works after putting user www-data additionally in the group adm by
sudo adduser www-data adm

● The access to those (two) directory trees can be further restricted by file access rights
and directives – but never expanded.

These restrictions and perhaps more are obviously hardcoded in Bookworm or Apache. And,alas, there is no option +OneSiteClosedShopFewUsers to bring back the old freedom.

One rule is

● When the user www-data can’t enter a directory or can’t read a file forget all fiddling with
directives, symbolic links etc. This file will never be part of your web site.

The accesses needed, wanted, planned shall be tested as user www-data.
● If a test fails in spite of sufficient access rights for www-data or its groups,
abandon all hope and change your structure.

For those tests you need no running Apache server, it might well be stopped.
But I had to do something absurd for those tests:

● log in as user www-data

I never ever thought of it – and others didn’t either and it never was provided:
www-data has no (known) password and no shell. To mimic it by

sudo su www-data

is not sufficient. You might either give www-data a password and a shell which I didn’t fearing side effects. The other way to the shell needed are su options.

sudo su -l www-data -s /bin/bash

will do the trick. And to repeat the important rule

● If you as this user www-data can’t read a file needed for your web site and if you can’t
change that quickly, easily and without unwanted side effects move that file elsewhere.

My new “world view” on Apache and said rules lead to (quite simple imho) structure and conf­iguration files described above. This “world view”, gained by painful experience may partly be not correct or missing important aspects. Corrections are welcome. Bus as hypothesis it did work well.

Installing PHP 8

In our process control projects we did all server side dynamic content generation, including AJAX with GCI and used C for GCI programs. Doing so, one stays consistently in one language and our cross compiling workstations with IDEs etc.
Hence we never (!) use PHP for its original purpose of generating complex pages from databases on embedded Pis.

But for completeness and for tiny task as displaying the Pi’s name in same static html page used on several Pis (see below) we do the basic installation by:

sudo apt install php8.2 libapache2-mod-php8.2

As of Oct. 24, 8.2 seems the latest version for PiOS.

Check the PHP installation by

php -v

PHP 8.2.7 (cli) (built: Jul 20 2023 18:02:54) (NTS)

Copyright (c) The PHP Group

Zend Engine v4.2.7, Copyright (c) Zend Technologies

with Zend OPcache v8.2.7, Copyright (c), by Zend Technologies

To get Apache acquainted with PHP we made (by nano) a file /var/www/meterpi/server/hello.php :

<?php

echo "The server <b>", gethostname(), "</b> is online at ";

echo date("H:i:s"), " (UTC) on ", date("Y-m-d") ,".<br /><hr />";

echo "<small>(A. Weinert, 26.10.2024)</small>";

?>

It will show the Pi’s name and date and time (UTC not local) by http://meterpi/server/hello.php :

The server meterPi is online at 16:13:36 (UTC) on 2024-10-26.

_____________________________________________________________________________________________________

(A. Weinert, 26.10.2024)

Pi’s name and date and time (UTC not local) are server site data here put on the html page by Apache via PHP at every (re-) load. Loading this page is as fast as a purely static one and hurting a real time process was never observed by us. But . . .
Now make a small addon to hello.php as /var/www/meterpi/server/info.php :

<?php

echo "The server <b>", gethostname(), "</b> is online at ";

echo date("H:i:s"), " on ", date("Y-m-d") ,".<br /><hr /><br />";

phpinfo();

echo "You are on <b>", gethostname(), "</b>.";

echo " &nbsp; &nbsp; &nbsp; <small>(A. Weinert, 16.11.2024)</small>";

?>

Browsing to this file by http://meterpi/server/info.php provides a lot of information – more as one should show to everybody or as can be printed here. And we saw loading the page the very first time took more than 40s giving the impression of a dead web server. Anyway, be cautious with server side work on your tiny “Apache on Pi” site.

Enabling file access

For administrative and programming work remote file access from workstations is essential. Accessing headless / GUI-less systems that way, you'll get decent editors and (on Windows) an operational clipboard handling worth the name.

Most Linux distributions for Raspberry, like Raspbian, will not have a FTP server. But almost all will have SSH. This offers remote file access by protocol sftp. SSHFS, FileZilla and WinSCP *) can handle that as clients. Hence, no FTP server installation on Raspbian will be needed.
Note *: Windows' ftp.exe can't – but on Windows you may have the excellent WinSCP.

SFTP – FileZilla

You really should have FileZilla on your Ubuntu and Windows workstations. (You may even have FileZilla on a graphical Raspbian.) On Linuxes install it by:

sudo apt-get update ## *)

sudo apt-get upgrade

sudo apt-get install filezilla

On Windows download and install the newest Filezilla.

Now set up FileZilla as client: Open FileZilla, go to site manager, make a new site and name it accordingly, say “raspi61”.

In general setting do: host = 192.168.89.61; protocol: sftp login: normal, sweet, 0123.
In “advanced” tick “bypass proxy”, when your workstation and Pis share a local network.

That's it. Connect should work.

Tipp: If getting “connection refused”: If all else – WinSCP, SFTP,… – works with your Pi but not Fillezilla, try
server manager → … → Advanced → Server type = Unix instead of “autodetect (default)”.
This made Fillezilla shows fingerprints and ask if OK instead just refuse the connection.

For every other Pi just copy and modify IP and name. Enjoy
●	disconnect and re-connect working perfectly
●	comfortable view/modify integration with text editor set (best Editpad on Windows)
●	see and explore all files from root (/) on, not just /home/sweet/

The last point allows to comfortably view /etc/apt/sources.list (with Editpad on Windows) and copy its content without ado by clipboard.

Modifications in the editor on the (automatic) local copy will be possible, but in FilleZilla's mirroring the changes back will fail for files with root:root permissions only. But we always can see, work on, copy, use clipboard etc. on multiple windows in a comfortable environment.

One workaround for modifying system files is opening a putty connection in parallel with FileZilla and transfer system files to be comfortably worked on to and (sudo) from a user working directory.

SFTP – WinSCP

WinSCP is one of the best FTP client programs – not for giving us the n + 21st graphical FTP but as a most powerful command line program and for its automated batch processing capabilities.

To install it, download the .zip file of a portable build, unpack it and move the three files

19.04.2017 14:44 282.960 WinSCP.com ; console, only, recommended

19.04.2017 14:44 18.905.808 WinSCP.exe.. ; console and GUI

29.05.2017 18:40 14.497 WinSCP.ini

to a path directory (C:\util\ in our case).

WinSCP.com will put you to the command line client. Play with it starting with 'help', 'open', 'close' and 'exit'. Some find WinSCP a bit bitchy on first encounter. If so, don't give up – once you master it you won't miss it any more.

winscp.com /script=progTransWin

will transfer two programs from the actual directory to a (Raspberry Pi) target machine by the WinSCP script progTransWin. Scripts can be parametrised and in the end used in a general­ised transfer recipe in a (Eclipse) C make project; see the downloadable examples given in Part II.

# transfer two programs to the target machine

# Copyright 2017 Albrecht Weinert weinert-automation.de

# $Revision: 2 $ ($Date: 2017-05-29 18:37:55 +0200 (Mo, 29 Mai 2017) $)

open sftp://sweet:0123@192.168.89.67

cd bin

option batch continue

option confirm off

put gnBlinkSimple -preservetime -permissions=775

put rdGnSimpleBlink -preservetime -permissions=755

exit

Listing 1: WinSCP script “progTransWin” for batch transfer of two programs.

Note: Looking at the Part II example's enhanced script and the make file you'll notice again that we found no Linux/Ubuntu equivalent. No free FTP command line tool for Linux with WinSCP's flexibility, features and professional quality seems to exits.
We tend to blame our incapacity to search for such Linux programs. On the other hand we even found expert comments saying WinSCP is keeping them with Windows.
What we got as nearest to WinSCP's elegance – and have often used for automated FTP transfers from Linux servers in the past – is LFTP. On the other hand recurring fingerprint/certificate refusals with sftp:// and parametrising with make variables may drive you nuts – all this a no topic with WinSCP.

On Windows 10 a single file may be transferred by command line:

scp notes.txt sweet@pi61:/home/sweet/notes/

As there’s no option to give scp the password that command is hardly usable for batch processing. But therefore we are quite happy with WinSCP scritps.

Cross-compile C for Raspberry from a powerful workstation

In the very basic example (Listing 2) we use the piogpio(d) library ([61] ..[63]). The reasons for it and the installation are described at the beginning of Part II “I/O and process communication” where it belongs. Install it now or skip the example until done.

/** (Listing 2) \file gnBlinkSimple.c (2024-11-12)

A very first program for Raspberry's GPIO pins

Copyright (c) 2024 Albrecht Weinert

weinert-automation.de a-weinert.de

This is a most simple GPIO pin output program using pigpio(d).

Compile on Pi by: g++ gnBlinkSimple.c -o gnBlinkSimple -lpigpiod_if2
and run by: ./gnBlinkSimple

Compile, build and transfer on Windows by:

arm-linux-gnueabihf-gcc -I./include -c -o gnBlinkSimple.o gnBlinkSimple.c

arm-linux-gnueabihf-gcc -I. gnBlinkSimple.o --output gnBlinkSimple.elf -lpigpiod_if2 -lpthread

cp gnBlinkSimple.elf gnBlinkSimple

winscp.com /script=progTransWin /parameter sweet:0123 targetPi bin gnBlinkSimple

*/

#include <stdio.h>

#include <pigpio.h>

#include <pigpiod_if2.h> // set_pull_up_down set_mode gpio_write

#include <unistd.h>

// 40 pin con | GPIO number excerpt from include/arch/config_raspberry_03.h

#define PIN37 26

#define LEDgn PIN37 // LED gn 1 active is attached to Pin 37

#define ON 1 // true On An marche go. // exc. from include/baseTyCo.h

#define OFF 0 // false Off Aus arret stop halt.

#define uMS 1000 // 1ms is 1000 us

int thePi; // the Pi handle for pigpoid

int main(int argc, char* argv[]){

printf(

"\n Blink green LED on Pin 37 \n"

"gnBlinkSimple.c R.01 12.12.2024 Albrecht Weinert \n\n");

int blinkS = 3600; // blink for 1h

thePi = pigpio_start(NULL, NULL); // connect to pigpio daemon(local, 8888)

if (thePi < 0) { // initialise pigpio daemon failed

printf(

"\ncan't initialise IO handling (piGpioD) \n"

"gnBlinkSimple.c Error / return code 99 \n\n");

return 99;

} // can't initialise piGpioD (the essential gpIO library)

set_mode(thePi, LEDgn, PI_OUTPUT);

while(--blinkS) { // green

gpio_write(thePi, LEDgn, ON);

usleep(500 * uMS); // 500ms gn

gpio_write(thePi, LEDgn, OFF);

usleep(500 * uMS); // 500ms dark

} // while endless 1 s loop

printf(

"\nstopped blinking the LED on Pin 37 \n"

"gnBlinkSimple.c end (return code 0) \n\n");

return 0;

} // main()

Listing 2: gnBlinkSimple.c

Also with a headless Pi, it is possible to transfer C-sources of GPIO programs (like the example gnBlinkSimple above, listing 2, page 21) to the Pi or even type/edit them there by putty/ssh & nano. Then one can translate and build them on the Pi with command (given also in the comment):

g++ gnBlinkSimple.c -o gnBlinkSimple -lpigpiod_if2 -lpthread

Transfer the executable binary to a directory in the path (~/bin e.g.) and run it. This gives a first impression and shows a base for going further to useful and greater projects. But now its also time to pause and re-think tooling and deployment:
●	Very few of us like to develop software on a GUI-less Raspberry with local tools
and stone age editors (nano being the best of them).
●	Or when adding the GUI, only few would like to handle IDEs, version control,
documentation generators and all else on a Raspberry (besides from having killed
even modest real time capabilities).
●	At least this will be true for those of us using all this and more
in all comfort and speed on their powerful Windows (or Ubuntu) workstations.

The pre-condition to develop C software for Raspberries on Windows is being able to cross-compile and -link. To get this going we download a “Prebuilt Windows Toolchain for Raspberry P” from http://gnutoolchains.com/raspberry/, e.g.:

23.05.2017 17:06 773.928.611 raspberry-gcc4.9.2-r4.exe

Run this (or a newer version) to

●	install at C:\util\WinRaspi

●	make no links for duplicate files
(we're on Windows, links exist and do work there, but no one will expect seeing one)
●	add C:\util\WinRaspi\bin\ to the path or let installer do it
It makes sense, even if no one likes extending the PATH by every install.

To test this best make an empty directory,
●	have our .c source (gnBlinkSimple.c, listing 2) in a working directoy and
●	cd there.

Then use the Windows commands given in the listing. Hint: In such “one source file only” case without need for extra information compile and build may be shortened to:

arm-linux-gnueabihf-g++.exe gnBlinkSimple.c -o gnBlinkSimple
-lpigpiod_if2 -lpthread

You'll get the compiled and linked runnable gnBlinkSimple in no time.
Transfer it to the Raspberry, cd there, make it executable if not so and start by

chmod 755 gnBlinkSimple

gnBlinkSimple &

It works! A program made on a Windows PC does GPIO on a Raspberry. Well, it just blinks one LED for one hour – but this is the entry.

arm-linux-gnueabihf-g++ and arm-linux-gnueabihf-gcc are the equivalent to plain g++ and gcc we used on the Raspberries.

Note on the prefix arm-linux-gnueabihf-:

These confusing prefixes are used for cross tool-chains: They are unique prefixes for the target processor (architecture) and specific library sets. When setting up a new cross compile project in your GCC enabled Eclipse you will be asked for a tool path and a tool prefix; here it would be:
C:\util\WinRaspi\bin and “arm-linux-gnueabihf-”, the latter standing for
architecture-noVendor-system-applicationBinaryInterface.

Note on the g++ or gcc choice:

Both gcc and g++ are GNU compilers respectively tool-chain drivers, doing almost the same. g++ treats .c files as C++ source while gcc expects and handles plain C. In the case of our IO example, listing 2, both work and both produce an executable of almost the same length and content.

To round up our (Windows) cross-compile tool chain we should also have a make file under­standing at least make clean and make all. See more in chapter – make project (p. 25).

A note on Windows:

Evidently, we prefer Windows on powerful development workstations – with Java, Eclipse, Open­Office, GNU tools, SVN etc.. We have all liberty of open tools, while enjoying Windows' comfort and professionalism: domain and network file system integration, decent powerful explorer (not changing its name with every upgrade) with tool integration e.g. for SVN (tortoise), decent text editors (Editpad), common clipboard support and so on. And (almost) all just works fine.

With Ubuntu, more than once, dragging files to shells suddenly stopped them working or changed its be­haviour. Unclear, pure text or no clipboard sup­port is a good recipe to drive Linux users mad. Experiencing regular total crashes or loosing tools on upgrades of Ubuntu and derivatives, one is constantly shooed back to Windows.

Well this happiness with Windows plus open tools did last up to Windows 7 professional respec­ti­vely Windows Server 2008 R2 enterprise. Windows 10, at its beginning, was brought first dis­appoint­ments: It was slow (on hard­ware very happy with its predecessors) and at first unreliable.

The less controllable updates / upgrades did render installed tools inoperable. Nevertheless, even when finally forced to “upgrade” to W10 professional and other hardware, we stayed there. The good news is: All our tools (Eclipse, Gnu, SVN, Git, Java, doxygen, AVR, …) were kept working and even the Explorer was still SVN aware *). In between, we accepted the impertinence of being forced to Windows 11 and partly new hardware and licences.

On the other hand, almost all what we describe for Windows can be (and was) done on a powerful Ubuntu workstation, too. And yes: Most Windows have their "drive nuts" potential, for example, the dangerous faking directory and file names, when accessing the file system graphically, like showing the fake name like “Programme” (German) for “Program Files”, which won’t work in tools and scripts.

But the comfort, stability and usability balance is positive. Hence use Windows, Ubuntu or what ever you like as your cross-development platform.

Note *): If the svn icons in the explorer are gone type regedit.exe . Goto
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\
CurrentVersion\Explorer\ShellIconOverlayIdentifiers
and kill all entries not needed – in my case all not beginning with Tortoise.
Background: On updates Windows has the habit of floating this node with icons of applications the user should install and surrender to bust stubbornly will not. Additionally there is a low limit of the number of entries the explorer will consider when building a file list. but

Eclipse

Now (cross-) developing on a powerful workstation, we want, the comfort of a powerful IDE. Evidently, our choice is Eclipse, used since years for Java projects, Web, AVR C and much more.

Put your sources, like listing 2, page 21, for example in Eclipse's project folder. Best make the folder and the copies before making the new cross-C project. Set tool path and a tool prefix to:

C:\util\WinRaspi\bin	and	arm-linux-gnueabihf-

and enjoy Eclipse's support and comfort.

Well, a little work on make files and project setting will be unavoidable.

A reliable source of trouble are Eclipse's automatically generated make files, which notoriously fail. Before stepping into (great!) configuration trouble to get Eclipse’s taste of make to work, drop the generated make files and write an own makefile which Eclipse would use with targets all and clean.

Thereby you get

+	immense flexibility considering targets, devices and else
which in the end will often be needed,
+	building and cleaning automatically by scripts
or by just running make by command line without starting or even needing the IDE
▼	involved with the ill syntax and semantic of the make tool.

So we make the appropriate changes in project → settings C/C++ → build and add a makefile to the project. And while we're at it we put the exemplary project in a SVN repository.

Command line – make project

See at the browser or get (by svn checkout https://...) all makefiles, sources etc. at
https://weinert-automation.de/svn/rasProject_01/
You'll need to login (guest:guest). In this paper ([31]) there will be no listing for

26.05.2017 15:27 9.521 makefile

26.05.2017 15:27 1.163 make_raspberry_01_settings.mk

26.05.2017 15:27 1.165 make_raspberry_02_settings.mk

26.05.2017 15:27 1.116 make_raspberry_03_settings.mk

29.05.2017 18:37 770 progTransWin ..... and some more

The makefile and its includes do work in our Eclipse C make project via project build and clean.

As postulated above you can use the makefile with more functionality and in automated batch files directly (without Eclipse). Be in the project/source directory, say D:\eclipCeWsOx\rasProject_01:

make help

make help_comm

make clean all

make PROGRAM=gnBlinkSimple clean all

make PROGRAM=gnBlinkSimple TARGET=thePi progapp

The last two commands generate our littleRaspberry IO program gnBlinkSimple.
And by the command displayed by the last make command, we do the transfer.

winscp.com /script=progTransWin /parameter sweet:0123 192.168.178.87 bin gnBlinkSimple

As already said: We stay with the "old" command line make and do not let Eclipse take over with Eclipse make projects.

Eclipse – troubles and hints

SVN client chaos

With development projects we still prefer Subversion (SVN) over Git. On commit you can get a source file (any plain text file) marked with the revision and date of this individual file’s last changes. You won’t get this feature with git push.

One trouble with Eclipse is putting in a decent SVN client. SubClipse made the least trouble. And there were problems of different client versions with in-compatibly different structures of local working copies. You may well have two or more SVN clients on your workstation: command line, TortoiseSVN (explorer plug-in), SubClipse (eclipse plug in) etc, see “our configuration”.

In the last years those compatibility troubles vanished – may be the success of GIT brought the yearly changes in SVN protocols and internal data structures to a welcome rest. Anyway, if you have a stable compatible state between all your SVN servers and clients – do not change a thing.

Our current configuration:
●	eclipse.exe	Eclipse IDE for C/C++ Developers Version: 2018-12 (4.10.0) [sic!]
●	Subclipse	1.10.13 + Subversion Client Adapter	1.10.3	tigris.org
●	svn.exe	SVN 1.8.10-SlikSvn-1.8.10-X64 Aug 2014
●	dto. Tortoise	svn, version 1.14.3 (r1914484) Dec. 2023
●	arm-linux-gnueabihf-gcc.exe	(GCC) 4.9.2
●	make.exe	GNU Make 4.3	Built for i686-pc-msys (2020)
●	doxygen.exe	1.9.5 (2f6875a5ca481a69a6f32650c77a667f87d25e88)
●	pdflatex.exe	pdfTeX 3.141592653-2.6-1.40.26 (TeX Live 2024)

Not young any more, but … “running” – and running well.

Eclipse marking non-existent errors

This problem hits mainly, but not only, cross-compile, cross-build projects:
While “make all” directly on the shell or indirectly in Eclipse by “build project” goes with zero errors and warnings – and yields a usable result – Eclipse marks the sources with non-existent errors and warnings and loads of red and yellow marks …. This problem haunted us for a long while in the past but went away beginning with Eclipse CDT IDE Oxygen, 4.7.0, June 2017. if still affected read this subchapter in [33].

The tip “do not let Eclipse make your make files” seems still valid. And additionally: Make your main makefile “makefile” having valid defaults for all parameters. It must build the main or the actually worked on part of your project without extra parameters.

But before getting too enthusiastic on Oxygen: For the AVR projects in the same workspace up­dating to Oxygen (with AVR plug-in) led to a not repairable (not voodooable) catastrophic failure with > 1000 false errors.

Note 1: To emphasize the last point, projects used successfully for years got a useless IDE by just upgrading Eclipse. Eclipse versions good for one target may fail on others.

Note 2: An IDE for C or any other language marking false errors and warning is worse than none. When marking errors or warnings, correctness and consistency with the target tool chain has the absolute top priority. Speed – wrong answers fast – is less than secondary.

Cross tooling summary

Now we can successfully
●	cross-compile/cross-develop with
●	GNU-tools,
●	using Eclipse (Oxygen) with make, or
●	make (alone / on shell or automated batch)
●	bringing all under version control, here SVN
●	having all on Windows (or almost all on Ubuntu)
for our Raspberries and even
●	upload the program just build from Windows to the Raspberry,
almost *) automated by make.
So, we can go on to utilise Raspberry Pis in a professional development environment.

Note *) on almost: At any point in past make.exe stopped being able to run winscp.com
scripts. That is probably not make’s fault but a security “improvement” of
winscp.com which slipped through our precaution “Never change a
running …”. The only remediy was: Let make print instead of execute the
command and copy it to cmd.exe by hand.
Tipps to get rid of this disability welcome.

Part I's results

We can install a usable OS and tools on Raspberries. We provided all basic tools and set up (human) communication and file transfer. We can save and restore the current complete µSD card state of our ”little servers”.

And we know how not to run into the dreadful “size problem”.

The basic installation gave us a Pi with fixed IP address(es) we can handle, control and observe remotely. We may have put it to its destined location and/or connected it to its (process) IO.

Hence we could do the “program – install – test” cycles to get it to its “real” work.

Therefore we will establish the means for I/O and process communication in Part II. The most basic one will be the using of general purpose IO pins (GPIO) of our Raspberry. We may have prepared this already for sake of the very basic example gnBlinkSimple (listing 2, page 21).

Otherwise let’s do it now and perhaps come back to gnBlinkSimple.c.
Note: This example is in Part I instead of II to demonstrate local and cross building.

Experience with our zoo of embedded Pis showed that having very similar and good
●	accesses
●	tools / services
●	drivers & libraries
●	look and feel
is a great help in the long run. Doing the basics right and re-usable pays.

P A R T I I	I/O and process communication

The pigpio library

As said above, pigpio is our preferred approach to Raspberry IO and the only one we use for real control applications.

Other IO libraries considered before do burden the production code with the trouble to
○	initialise the GPIO (memory) usage,
○	adapt to changing (virtual) addresses as well as
○	the fighting with access rights
or in a certain sense worse
○	giving users the rights for standard IO with all libraries and approaches.

No decent OS offering any protection will let user code do IO, as did former Raspbians. With them accessing GPIO required sudo. In between the standard GPIO usages (read write) can be made accessible without sudo, but more settings or alternate functions may not.
Note: Standard IO without sudo works, when a user is in group gpio.

The pigpio library uses a different approach. It defines a server or daemon which does all initialisa­tions and has control over all functions of the GPIOs used. This server has to be started with sudo to run forever in background. Programs doing (process) IO just communicate with the daemon by

●	socket	(as in basic example gnBlinkSimple, listing 2, page 21) and
as in all our control applications) or by
●	pipe	(never used here).

Both approaches need no sudo. In the case of socket the control program and the GPIO pins may be on different Raspberries on the same network or one control program can use multiple Pi’s IO.

To tell it the other way round, the pigiod daemon or server can be used remotely or locally by other programs using the socket or pipe approach. And, not really surprising after all, pigpio forces its daemon's singleton property. No program or operator can start another instance on the same machine when the daemon (pigpiod) is running, already.

Even when a pigpio daemon (piGpioD) is forced to be a singleton, nevertheless, one should make program's usage of pigpiod singleton, too. Additionally, when using only piGpioD one should exclude all other approaches from sudo-less IO if not yet so. On newer Raspbians this usually means removing standard users from group gpio by

sudo deluser pius gpio

But now let's go.

To get and install pigpio (see also [61]) do:

wget https://github.com/joan2937/pigpio/archive/master.zip

unzip master.zip

cd pigpio-master

## make

sudo make install #

sudo ldconfig -v # may be necessary, but seems included in make

Then do all the tests provided **) as recommended in [61] by

sudo ./x_pigpio # check C I/F see note *)

sudo pigpiod # start daemon with default sample rate 5 µs etc.

./x_pigpiod_if2 # check C I/F to daemon

./x_pigpio.py # check Python I/F to daemon

./x_pigs # check pigs I/F to daemon (1 fail, Cbs, wavef.)

./x_pipe # check pipe I/F to daemon
cd ~ # ready return to user's directory

Note *): Put a 1-active LED at pin 22 = GPIO25 to see some test steps working. And see also
that the tests forget to set pin 22 inactive (by making it an input). The LED stays on.

As said in [61]: if a few tests fail and many more are passed; this is OK. We saw some irrelev­ant test fails on Pi3s with Jessy, Stretch but with later OS on several Pi types4 (almost) all tests pass.
N.b.: With 10 µs sample rate some test will will fail saying “half the expected number of yxz”.

Please find and see the working examples rdGnPiGpioBlink and rdGnPiGpioDBlink in the SVN repository mentioned above on page 25.

And when using piGpioD on a Raspberry it's recommendable to start it at boot. Best use:

sudo crontab -e # sudo here(!) but not at program to add; see note

and add one line at the end:

@reboot /usr/local/bin/pigpiod -s 10

Starting pigpiod without parameters uses default settings: 5 µs sample rate, PCM clock 400Hz, port 8888, both interfaces (socket, pipe) enabled; -s 10 sets 10 µs sampling; -p might change port.

Note:	You must put sudo in front of crontab -e when adding or editing a task requiring sudo.
Do not prepend the task command with sudo. The crontab command suggests your editing a single system configuration file directly. That's an illusion. Every user and sudo seem has own cron editor settings and files. Getting this (sudo) business wrong is the source of notorious “My crontab (@boot) setting isn't .” complaints.

Note also:	cron tasks might be started without having your environment and path settings.
Hence, always use the full path to the “real” executable (like /usr/local/bin/pigpiod e.g.).

A last note:	When cross-compiling / cross-building it may happen (after first time using a feature) linker ending with not being able to satisfy externals.
In this case the workstation tool chain may have other / outdated libraries.

23.06.2017 15:15 256.624 libpigpio.so

23.06.2017 15:15 65.128 libpigpiod_if.so

23.06.2017 15:15 75.624 libpigpiod_if2.so

Copy those files from the Raspberry's /usr/local/lib to your workstation's
C:\util\WinRaspi\arm-linux-gnueabihf\sysroot\usr\lib (by ftp).

piGpioD does solve the GPIO sudo hassle. Additionally, Joan N.N.'s pigpio library has a lot of other rich and useful features, like PWM on every pin, a substitute for digital input interrupts and much more. (By the way: Do not use interrupts!)

This library's socket approach with all good points brings one disadvantage:
A (binary) single write to one pin takes 98 µs on slow Pis. Making more than 4 such IO calls in a 1 ms cycle is, hence, a no go. This sounds harder than it is in reality. There are several remedies:

a)	Anyway, programming style not endlessly repeating the same IO operation, like
turning on a relay already on, is adequate.

b)	Use pigpio's bulk / bank functions when feasible. With one call you can affect a set of
I/O registers in one bank.

c)	gpiod knows function which it can perform with 10µs resolution (in our setting above,
5µs is default) by itself as server/daemon. PWM with individual frequencies and rations
on every pin being the most modest example.

d)	You may have programmable waveforms, samplings UART outputs on every pin etc. pp.

You don’t have to use all this, but bear it in mind, when hitting any limit.

The pigpio site (http://abyz.co.uk/rpi/pigpio/pdif2.html) offers no offline .pdf document. The very good on-line documentation allows being .pdf-printed (60 pages, links partly working).

The resume for piGpioD:

●	It is our choice and
●	service proofed several in real time process IO applications running 24/7 over 6 years
●	worked on all Pi 0, 3..5
●	good base for pure Java process IO applications without any extra libs; see [34].

Other GPIO libraries

Years ago we considered, installed and tried wiringPi, bcm2835 and a derivative, see [33] if interested. Download and installation described there still seems to work.

wiringPi tries to cover the range of Raspberry Pi1, 2 and 3 with its diverse variants as well as some alter­nate Pi lookalikes. Additionally wiringPi knows a wide selec­tion of extension boards or so called “shields” considered then popular by the authors. Well this "serve all" approach is doomed to fail under other than the assumed "what is all" conditions.

Probably as consequence of this “cover everything” approach wiringPi introduces indirection re­spectively abstraction layers away from from the µP's (BCM2835, BCM2836, BCM2837) GPIO numbers or (virtualised) IO register addresses ([56]). Wisely, pigpiod does nothing of that sort. This “feature” (addressing Pin numbers instead of GPIO register numbers) is easily added by some simple Pi type specific macros, as e.g. include\arch\config_raspberry_04.h (and 00, 03, 05):

#define PIN37 26 //!< GPIO 26

#define PIN38 20 //!< GPIO 20

#define PIN40 21 //!< GPIO 21

And you can easily add a next abstraction/marco layer to name process signals and put all “wiring” definitions in one place:

#define LEDgn PIN37 //!< The green LED to blink

Implementing those abstraction layers by marcos does no harm at runtime on real time behaviour, as C handles all macros completely at compile time.

And as pigpiod transparently adds functions as PWM and UART input on every IO pin you may think in pins from the hardware IO perspective.

Re-consider our example gnBlinkSimple, listing 2, page 21. With pigpio(d) you may dim the LED on pin 37 with PWM by just changing the line
gpio_write(thePi, LEDgn, ON);
to
set_PWM_dutycycle(thePi, LEDgn, 128); // 128 = half power, 50% PWM
Nota bene: LEDgn → PIN37 → GPIO 26 is none of Pi’s four outputs with PWM !

The bcm2835 library, as the name suggests, just handles and refers to the µP's GPIO. As the name does not suggest, it is also usable for BCM2837 and the Pi3 (and others).
Note: Regrettably a lot of bcm2835 documentation stops between 2012 and 2014 with the Pi1, leaving the rest to forums and speculation. Having switched totally to pigpiod (2017 - 2020), we haven't investigated other libraries further.

Resumme: With pigpio(d) we have total control over binary input and output devices like switches, LEDs optocouplers, power transistors, relais, RC servos and much else.

Other protocols for process IO(, also)

Other devices we can use by special protocols (Modbus, MQTT, …) over serial connection (RS232, RS485, 1wire, …) including also USB and Ethernet.

For Raspberries, the natural way to communicate with companions on the same process control tier, with servers on higher tiers or with HMI systems is IP via LAN or WLAN (best private).
Note: For micro-controllers, like e.g. most Atmel AVR boards, this is not their “natural” way.

(W)LAN and TCP/IP communication is an integral part of Raspberries and most available OSs, including Raspbian lite and the like. A whole bunch of protocols, applications and libraries is avail­able – from start or after a bit of apt-getting. This includes ftp, http, SCP, SSH, Telnet, rlogin and more.

For process control communication (mostly of IO values and usually time critical) one might be tempted to write own binary protocols. On base of the GCC socket library (sys/socket.h etc., [63]), this can be done. Most often it's wiser to use a standard protocol, like Modbus or MQTT.

Modbus

Modbus [65], [66] is an industry standard protocol to communicate process IO data. It is in wide spread since its origins in 1979 by the PLC manufacturer Modicon (now Schneider). Until today it is supported by most small automation systems or PLCs. It started as a P2P RS323 link and was later extended to multi-slave/server by RS485 and also by TCP/IP. Like many beloved program­ming languages and protocols of such age, Modbus has a bundle of architectural flaws the worst of which were mistakes already in 1979:

▼	Modbus has no layer concept and mixes physics, transport and application in an unfortu­nate way. The standard makes inadequate references to concrete devices and their addressing idiosyncrasies. As none of those has any effect to the protocol and its telegrams, these references to “4xxx registers” and the like are a rich source of confusion. A lot of secondary literature just dwells on what belongs to the standard and what was meant by "only for a 984A/B/X machine".

▼	Modbus has no data type concept worth the name.
One type is "coil" [sic!] = copper wire for relays. This is a boolean forced to one bit. Modbus insists to transfer thousand of bits (sorry "coils") starting at arbitrary odd bit addresses aligned to bytes – meaning RS232/485 bytes since 1979 and also TCP bytes a bit later. This shifting nonsense is a burden to implement and test as well as for runtime performance.

The only other Modbus data type is called "register" which is just a16 bit something. (Above trans­port Modbus knows no "byte"). Even when the application "thinks" in bytes, this “register” approach brings the full computational load and risk of errors by endianess handling – without doing any good for applications' or devices' 32 or 64 bit data.

For the serial interfaces RS232/485 – still in wide use for small controllers – Modbus won't use UART parity supplemented by a simple checksum. The standard requires a complex CRC tele­gram checksum. This overkill is, again, overcharging some controllers even with a clever double look-up algorithm, requiring low baud-rates just to reduce the telegram and CRC load. Serial Mod­bus has no control flow concept, but a set of pause and time-out requirements forcing modern buf­fered UARTs to low gear and requiring extra programming. Some newer Modbus imple­mentations ignore these requirements by implement­ing "full speed" – the better ones at least document­ing or advertising it. But beware: Other stations might just fail or intentionally reject to communicate with such non-conformer – and, alas, quite rightly so.

One last point: Modbus by itself has no security measures. This can and must be handled by using protected networks, only. Serial RS232/485 lines should always fall in that category (by physics). For the preferred TCP/IP use protected private LANs (or tunnels). On the other hand:
If devices have TCP/IP do use other communication protocols if you can influence it.

Resume: As it is a widespread industry standard we should use Modbus on TCP on our Raspberry servers. With the GCC libraries and the Ethernet port we have all infrastructure at hand.

Serial (RTU) Modbus mostly via RS485 have to be used when devices of high quality lack other communication means. The most prominent example are smart industry quality one and three phase smart meters, like Eastron SDM630 to name just one. Besides an unsigned “impulses per kWh” output they communicate by RS485 Modbus (as slave) only. Some allow up to 38,4 Kbaud but we saw seldom stable communication above 9,6 KBaud (the default) on a RS485 bus.

As some solar inverters want to have such smart house meter in some modes, many inverters do have RS485 modbus. – sometimes as their only communication interface. Some allow other modes than being the sole master over the houses smart meter (Growatt nm00-S e.g.) and are thus possible clients for our Pis.

Raspberries need extra hardware (like MAX485 modules, cf. Figure 1 page 1) to have RS485. On the other hand there are many process IO devices around with good value for money featuring RS485 Modbus interface, like said smart meters and inverters. Linking those to a Raspberry opens a rich field of applications with professional process IO. So let's do serial “Modbus RTU”.

libmodbus

Implementing a minimal Modbus subset (class 0, TCP IP e.g.) is relat­ively easy. Going to higher classes or other interfaces will get hard work. But even with small sub­sets it is re-inventing the wheel, considering age and wide use of the protocol. But looking for a reliable and conforming Modbus library for Raspberry (Linux) was harder than expected.

In the end we used Stéphane Raimbault's libmodbus. It is
+	function code complete
+	including even exotic function codes 17/11, which is
▼ implemented in a useless way reporting fixed constants and always “PLC run”
+	available and tested on many platforms
+	in wide spread use. It has
+	implemented all interfaces, TCP/IP and serial,
+	using an (▼ inconsistent) abstraction layer concept for the interfaces. So the application
software would be less affected should one ever switch from RS485 to TCP.

▼	The library is a “heavy malloc user”, which gives bad feelings in connection with
real time and process control.
Trying remedies by too naïve replacing malloced structure with fixed static ones
may lead to funny conflicts with Raspberry's MMU (ARM, Linux activated).
▼	The well and comfortable working modbus_receive() modbus_reply() server function pair
offers no (call back) interface to handle output just put and input just requested. Time
permitting you'd have to bring all possible input before and get all possible output after
receive().
▼	The linked multi-stage (C) pointing leads to horrible readability.
▼	There is no real documentation of the library's functions, the data structures,
nor on semantics and architectural ideas.
▼	Besides some typos there are a few bugs inhibiting cross-compilation and Eclipse
(F3) look-up. One Eclipse / compiler troubling example: In

static int response_exception(modbus_t *ctx, sft_t *sft,

int exception_code, uint8_t *rsp, ..... const char * template, ...)

the name “template” is considered as keyword misuse by some tools – rename it “tempel”, e.g.

On the other hand, this little list may sound much worse than it is meant. Monsieur Raimbault's library just works – and quite well so. To resume: With libmodbus we do have the complete sources of an excellent library to work with – and to learn from.

Installation

Do the usual update/upgrade exercise. The installation needs git-core; if not installed yet do:

sudo apt-get install git-core

Then do:

sudo apt-get install -y autoconf libtool

git clone https://github.com/stephane/libmodbus/

cd libmodbus/

dir

./autogen.sh ## *)

./configure --prefix=/usr/local

libmodbus 3.1.10

::::::::: *)

sudo make install

dir /usr/local/lib

cd src/

dir

dir .libs/ ## here are the .so files

dir .deps/ ## what the hell are .Plo files?

cd ~/libmodbus/tests/

./unit-test-server & ## best note the process number **)

./unit-test-client

Note *): All installation scripts take considerable time and produce lots of output, be patient.

Note **): Normally a test-server started in background would end, when the test-client disconnects. If this fails do

killall lt-unit-test-server ## Yes, the process' name differs

Due to a bug in the test-servers they work only with clients on the same machine; change the source accordingly and re-build.

A note on "just do this!" to install libmodbus

Well, due to lacking any respectable background documentation on libmodbus, this "just do!"
was our biggest mental hurdle at first encounter years ago.

To begin with, why should I use libmodbus at all?
Because it's good and, when not fitting, adaptable.
What does the above installation – many, many pages of scripts! – do with my system?
Gives you some 7 files needed.
What is the – never before used – libtool and why would I need it?
To understand get [64] and read it. You would not need it, It's just a help for
the project owner to serve many targets.

So the short answer to "Should I do this complicated installation?" is: When wanting libmodbus, "Yes, do it". Be courageous or make a backup before.

In the end you need the following files – and transferring those from your installation Raspberry to another one does the installation job:

●	the sources of the library and the tests if you like

●	the include files. Put the includes to /usr/local/include/ ***)

-rw-r--r-- 1 root staff 11155 2017-08-04 14:34 modbus.h

-rw-r--r-- 1 root staff 2124 2017-08-04 14:34 modbus-version.h

-rw-r--r-- 1 root staff 1199 2017-08-04 14:34 modbus-rtu.h

-rw-r--r-- 1 root staff 1373 2017-08-04 14:34 modbus-tcp.h

-rw-r--r-- 1 root staff 3430 2017-08-04 14:34 modbus-private.h

-rw-r--r-- 1 root staff 7690 2017-08-08 09:20 config.h

-rw-r--r-- 1 root staff 1627 2017-08-04 14:34 modbus-rtu-private.h

-rw-r--r-- 1 root staff 1247 2017-08-04 14:34 modbus-tcp-private.h

●	the library files. Put them (all 6) to /usr/local/lib/

lrwxrwxrwx root staff 2017-07-21 libmodbus.so -> libmodbus.so.5.1.0

lrwxrwxrwx root staff 2017-07-21 libmodbus.so.5 -> libmodbus.so.5.1.0

-rwxr-xr-x root staff 123408 2017-07-21 14:32 libmodbus.so.5.1.0

***) Version .10 has another set of include files in /usr/local/include/modbus/

-rw-r--r-- 1 root root 12960 2024-10-19 13:19 modbus.h

-rw-r--r-- 1 root root 1195 2024-10-19 13:19 modbus-rtu.h

-rw-r--r-- 1 root root 1360 2024-10-19 13:19 modbus-tcp.h

-rw-r--r-- 1 root root 2205 2024-10-19 13:19 modbus-version.h

Do not forget to say sudo ldconfig afterwards. Otherwise you might get incomprehensible errors when running (cross-build) Modbus applications on that machine or when linking there.

Just to run a cross-build Modbus program on another Raspberry an unlinked libmodbus.so ftp-transferred there should suffice. after copying it to /usr/local/lib/:

sudo cp libmodbus.so /usr/local/lib/ ## to where it belongs

sudo ln -s /usr/local/lib/libmodbus.so /usr/local/lib/libmodbus.so.5

sudo ldconfig ## what ever it does, never forget

But it turned out that for what ever reasons programs required libmodbus.so.5 even when made with -lmodbus.

Compile and cross-build

After a successful installation or transfer one can locally compile:

cd ~/ibmodbus/src/tests ## or where ever the .c source is

gcc modApp.c -o version -lmodbus

./modApp

To be able to cross-compile, cross-make and cross-build from our (Windows) workstation – and to use Eclipse there – we have to get the include files to
C:\util\WinRaspi\arm-linux-gnueabihf\sysroot\usr\include

22.07.2017 11:22 1.199 modbus-rtu.h

22.07.2017 11:22 1.373 modbus-tcp.h

22.07.2017 11:22 2.114 modbus-version.h

22.07.2017 11:22 10.912 modbus.h

One may also take

22.07.2017 21:37 3.405 modbus-private.h

22.07.2017 22:04 5.829 config.h

22.07.2017 11:22 1.627 modbus-rtu-private.h

22.07.2017 11:22 1.247 modbus-tcp-private.h

Those more “private” .h files would be needed to (re-) build the library itself and when digging a bit deeper like building and using their data structures. It is no fault to take them from start.

And we need the get the ("un-linked") file libmodbus.so (~120kB)
and put it to	C:\util\WinRaspi\arm-linux-gnueabihf\sysroot\usr\lib

In a (test) project we let see Eclipse the library sources primarily to have "Open declaration / F3". And we let them make by a suitable make file. While make (from command line or by Eclipse's build project) is no problem for all library sources Eclipse's CDT complains:

Warning "Code Analysis Problem"

No break at the end of case	modbus.c	/rasProject_02/modbus	line 439

The well commented case fall through is OK. To get also Eclipse to 0 warnings one has to add a
"//no break" comment.

Cross-compiling example:

arm-linux-gnueabihf-gcc -c -I. -DPLATFORM=raspberry_03 -g -Wa,-adhlns=bau/tests/version.lst tests/version.c -o bau/tests/version.o

Cross-build example:

arm-linux-gnueabihf-gcc -I. -DPLATFORM=raspberry_03 -g -Wa,-adhlns=bau/tests/version.lst tests/version.c -lmodbus -o au/tests/version

libmodbus application and experience

Have a look at the SVN project (rasPexLab) or check it out. And see the examples

●	keysModTCP.c	a Modbus server doing real process IO, a key matrix
(12 keys), 3 LEDs and one beeper (in an 1ms cycle) for

●	keysModClient.c	a Modbus client using the input and controlling the output
(in an 100 ms cycle); both Modbus over TCP.

●	weRasp\weModbus.c, include\weModbus.h	support for Modbus (RS485)
devices, especially smart meters in home automation projects.

MQTT

MQTT ([67]) is a TCP based lightweight M2M messaging protocol claiming to be simple, low net­work bandwidth and small code footprint. Notwithstanding those claims, at least the "simplicity" might be slightly doubted.

MQTT is quite successful and widely used in the world of IoT and home automation. Most sources and the protocol are open. The protocol specification is managed by OASIS. To use it, one has to have at least one broker service running on a machine reachable by all clients via TCP/IP. This machine may very well be a Raspberry Pi doing other process control work.

A client program producing events might publish those to the broker. The event message must be assigned to a "topic". Topics are to be organised hierarchically in a tree, e.g.

labExp/sweetHome/meters/alarms

labExp/sweetHome/hmi/actuators

The publisher might chose several service quality levels from "once or lost", "at least once or duplicated" to "just once" when delivering a message to the broker.

The broker would run by default with no security or encryption. It might be configured with an own user/password base for access to ports and topics, for requiring client certificates and for using TLS. Before jumping to those features in a closed, secured (W)LAN, one should consider these requirements hitting all clients at the site. Think twice before burdening our small Raspberry and ESPxy devices with complicated protocol extensions. But beware: This "have no fear" only holds until we open our private process control LAN to someone else.

A client program wanting to react respectively listen to events has to subscribe to one or more topics. Besides specific topics, like the two examples above, single level (+) and multi-level (#, at the end, only) wildcards can be used:

labExp/sweetHome/+/alarms

labExp/sweetHome/hmi/#

In a certain sense, we have the Listener respectively Observer pattern implemented by the MQTT broker to which clients (programs) can publish or subscribe. A client program may very well have both the publisher and the subscriber role.

A client may publish one special "last will" or "testament" (LWT) message. This message will be stored by the broker and pushed to subscribers, only, after this client failed or died. Criteria for death are network and MQTT protocol errors and time outs.

MQTT products

There are a lot of MQTT enabled products with good price performance ratio on the market:
●	ESP8266 WiFi modules with MQTT
●	single to quadruple power (relay) switches, optionally
●	230V AC	or	●	24V
●	DIN rail mounted	●	wall plug-in
●	touch wall switches
●	lamps / bulbs and a lot more.

As did Modbus with smart meters, pumps etc., MQTT opens another range of products as sensors and actuators for our process control Raspberries.

MOSQUITTO

A Raspberry Pi 3 doing a 24/7 process control task in >= 20 ms cycles may very well execute some extra services, like an Apache 2.4 server for HMI (see page 14) and a MQTT broker, when used judiciously and almost only related to the process control task.

Mosquitto is an open source implementation of a MQTT broker etc. by Eclipse. It seems to be the most used implementation for small and embedded systems, notwithstanding the lack of findable respectively usable documentation and manuals.

To install Mosquitto do:

sudo apt-get update && sudo apt-get upgrade

sudo apt-get install mosquitto

sudo service --status-all | grep mosq

The installation command brings the broker and starts it as service. The broker service can be switched off and on by:

sudo service mosquitto stop

sudo service mosquitto start

If we want the broker alone (on an extra Raspberry, e.g.) we would stop here. For having the client (test) program, the library and the include file we do:

sudo apt-get install mosquitto-clients

sudo apt-get install libmosquitto-dev

sudo ldconfig

To test our new broker we utilise the command-line clients, just installed:

mosquitto_sub -d -t labExp/sweetHome/#

-t precedes the topic(s) we subscribe to, and -d brings some extra (debug) messages, helping to get acquainted with the protocol. To see the broker and our (one) running subscriber working we publish a fitting message by command-line, best in another putty console:

mosquitto_pub -d -t labExp/sweetHome/alarm -m "bathtub overflow 3"

If all went well, our subscriber sees the event, immediately. When sending an off topic message

mosquitto_pub -d -t hello/world -m "Hello 11"

nothing should happen.
Note: Besides mosquitto_pub and mosquitto_sub, there's a third command-line tool mosquitto_passwd.

libmosquitto

By some searching and grepping (due to lack of documentation) one can see, we have just one library and one include file:

lrwxrwxrwx 1 root root 17 2017-05-29 07:21 libmosquitto.so	-> libmosquitto.so.1

-rw-r--r-- 1 root root 46960 2017-05-30 00:32 libmosquitto.so.1

-rw-r--r-- 1 root root 54155 2017-05-29 07:21 mosquitto.h

in /usr/lib/ respectively /usr/include/.

Compile and cross-build

To cross-compile and -build on the workstation we put the include file to
C:\util\WinRaspi\arm-linux-gnueabihf\sysroot\usr\include

and the library both as libmosquitto.so.1 as well as as "un-linked" (copied) libmosquitto.so to

C:\util\WinRaspi\arm-linux-gnueabihf\sysroot\usr\lib

That's it. As first test we make a simple publisher example (with accompanying make include) in Eclipse and having the make tool build it and ftp it to the target Raspberry, as usual. With this proof of feasibility – and when having understood the API ([69]), we can add MQTT functions (public and/or subscribe) to our C process control software. And we can make own small (ESP8266 based, e.g.) MQTT devices.

Part II's results

Since Part I we can install a usable OS and tools on Raspberries. Having provided all basic tools and having set up communication we can administer Pis with some comfort remotely from both Ubuntu and Windows. And we can save and restore the current state of our ”little servers”, sometimes a bit spoiled by the “size problem”.

Now we just added libraries to control process I/0 of many sorts in real time.
Experiences and measurements show: Comparing Raspian lite=GUI-less with with the GUI variant we see with the GUI variant

▼	39+ more services/processes running immediately after reboot

▼	2..5 s delay when typing on putty after a short pause.
On a GUI-less/lite we always saw an immediate reaction as we type.

And when it comes to even modest real time requirements with PLC like cyclic 1 ms tasks (threads) we experience no problems with Pi3 and OS lite, while the GUI variant reproducibly always and totally fails in latencies and many other aspects:

▼	The GUI variant showed just slightly worse latency results (sometimes
max. latency above 200µs compared to always below 200µs on lite).
This seems not so bad for the non lite graphical OS. But:

▲	Loads (ping etc.) have no measurable effect on the lite Pi OS variants
▼	Such loads, on the other hand, significantly increase he latency on the GUI variant.
And the latency literally “explodes” by just moving the mouse.

Hence we must state: A standard PC like GUI is making the Raspberry unsuitable for any em­bed­ded / realtime/ server work. This resembles our experiences in the large with “real” servers ([29]).

P A R T III Process Periphery and control

Fig. 6: Raspberry Pi's 40 pin two-row IO connector

Using the GPIOs

Our first goal is to use Raspberry's GPIO pins in an own program, preferably written in C. In any case, we want our programs cross-compiled (cross-made, cross-build) from comfortable work­stations. For a min­imal proof of concept we had the example gnBlinkSimple in Part I, see listing 2, page 21. If skipped, do it now!

A Pi 1 Model A, see figure 7, would also be sufficient for those experiments, but it has no Pin 37. The equivalent of figure 6 would end at Pin26.

Fig. 7 :Minimal I/O

As hinted above, we handle the IO differences between the Pi types in include files. For a Pi1 (fig. 7) it would be include/arch/config_raspberry_01.h (Listing 3, page 39), and for Pi 0,3,4 and 5 include/arch/config_raspberry_03.h (shortened, Listing 4, page 40) is valid.

** @file config_raspberry_01.h (Listing 3)

* Configuration settings for Raspberry Pi1 (Models A)

* This file contains some platform (type) specific definitions. These

* settings influence the compilation and build process. Most of those

* settings can not be changed later at runtime.

* Since end 2017 we mostly use Pi3 ++; see config_raspberry_03.h.

\code

Copyright (c) 2019 Albrecht Weinert

weinert-automation.de a-weinert.de

* / / /\

* / /___ / \ |

* \ /____\ /____\ | _|__

* \ /\ / \ / \| |

* \/ \/ \__/ \__/|_ \endcode

Revision history \code

Rev. $Revision: 271 $ $Date: 2024-11-15 19:32:54 +0100 (So, 29 Okt 2023) $

Rev. 209 10.07.2019 : stdUARTpath

Rev. 231 13.08.2020 : two digit PIN0x and GPIO2pin added

\endcode */

#ifndef CONFIG_PLATFORM_H

# define CONFIG_PLATFORM_H raspberry_01

#else

# error "more than one platform specific config_platform.h file"

#endif

// by using Joan NN's pigpio(d) we don't need those address values

//#define ARM_PERI_BASE 0x20000000

//#define ARM_GPIO_BASE 0x20000000

//#define ARM_PERI_SIZE 0x01000000

// 26 pin con |GPIO number

#define PIN03 0 //!< SDA0 (GPIO 02 on other Pis)

#define PIN05 1 //!< SCL0 (GPIO 03 on other Pis)

#define PIN07 4

#define PIN08 14 //TXDI

#define PIN10 15 //RXDI

#define PIN11 17

#define PIN12 18

#define PIN13 21 //!< GPIO 21 (GPIO 27 on other Pis)

#define PIN15 22

#define PIN16 23

#define PIN18 24

#define PIN19 10 // MOSI | S

#define PIN21 9 // MISO | P

#define PIN22 25

#define PIN23 11 // SCLK | I

#define PIN24 8 // CE0 /

#define PIN26 7 // CE1 /

// 6 9 14 20 25 : gnd

// 1 17 : 3.3V

// 2 4 : 5V

/** GPIO [0..39] to pin number (1..40) lookup list.

*

* 0 means has no pin on the 40 pin connector; see also ::gpio2pin

*/

// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,

#define GPIO2pin 3, 5, 0, 0, 7, 0, 0, 0,24,21,19,23, 0, 0, 8,10, 0,11,12, 0,\

0,13,15,16,18,22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

/** Pin number [0..26] to GPIO number lookup list.

*

* 0..56: GPIO number; 95: 5V pin; 93: 3.3V; 90: Ground 0V; <br />

* 99 means not existent. N.b.: [1..26] are: valid pin numbers, only.

* @see ::pin2gpio

*/

// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,

#define PIN2gpio 99,93,95, 0,95, 1,90, 4,14,90,15,17,18,21,90,22,23,93,24,10,\

90, 9,25,11, 8,90, 7,99,99,99,99,99,99,99,99,99,99,99,99,99,\

99,99,99,99

// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,

// 0..56(?) GPIO; 95: 5V; 93: 3.3V; 90: Ground 0V;

// 99: not existent; [1..40]: valid pin number/index; [1..26]: existing pins

/** /def stdUARTpath

* Pi's standard UART.

*

* It is the one on the Pins 8 (GPIO14) for Tx and 10 (GPIO15) for Rx.

*/

#define stdUARTpath "/dev/ttyS0"

Listing 3: include file config_raspberry_01.h

** @files config_raspberry_03.h config_raspberry_04.h (Listing 4)

* Differences to config_raspberry_01.h (Listing 3 page 39)
:::::::::::::::::::::

// 40 pin con|GPIO number

#define PIN03 2 //!< GPIO 02 SDA1 (GPIO 0 on Pi1)

#define PIN05 3 //!< GPIO 03 SCL (GPIO 1 on Pi1)

#define PIN07 4 //!< GPIO 04 GPCLK0

#define PIN08 14 //!< GPIO 14 TXDI

#define PIN10 15 //!< GPIO 15 RXDI

#define PIN11 17 //!< GPIO 17

#define PIN12 18 //!< GPIO 18

#define PIN13 27 //!< GPIO 27 (GPIO 21 on Pi1)

#define PIN15 22 //!< GPIO 22

#define PIN16 23 //!< GPIO 23

#define PIN18 24 //!< GPIO 14

#define PIN19 10 //!< GPIO 10 SPI.MOSI | S

#define PIN21 9 //!< GPIO 09 MSPI.ISO | P

#define PIN22 25 //!< GPIO 25

#define PIN23 11 //!< GPIO 11 SPI.SCLK | I

#define PIN24 8 //!< GPIO 08 SPI.CE0 / .

#define PIN26 7 //!< GPIO 07 SPI.CE1 / .

#define PIN27 0 //!< SDA0

#define PIN28 1 //!< SCL0

#define PIN29 5 //!< GPIO 05

#define PIN31 6 //!< GPIO 06

#define PIN32 12 //!< GPIO 12

#define PIN33 13 //!< GPIO 13

#define PIN35 19 //!< GPIO 19

#define PIN36 16 //!< GPIO 16

#define PIN37 26 //!< GPIO 26

#define PIN38 20 //!< GPIO 20

#define PIN40 21 //!< GPIO 21

// 6 9 14 20 25 30 34 39 : gnd

// 1 17 : 3.3V

// 2 4 : 5V

// 27 28: ID_SD/SC (HAT board & EEPROM interface) or GPIO 0 and 1

/** GPIO [0..39] to pin number (1..40) lookup list.

* 0 means has no pin on the 40 pin connector; see also ::gpio2pin

*/

// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,

#define GPIO2pin 27,28, 3, 5, 7,29,31,26,24,21,19,23,32,33, 8,10,36,11,12,35,\

38,40,15,16,18,22,37,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

/** Pin number [0..43] to GPIO number lookup list.

* 0..56: GPIO number; 95: 5V pin; 93: 3.3V; 90: Ground 0V; <br />

* 99 means not existent. N.b.: [1..40] are: valid pin numbers, only.

* @see ::pin2gpio

*/

// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,

#define PIN2gpio 99,93,95, 2,95, 3,90, 4,14,90,15,17,18,27,90,22,23,93,24,10,\

90, 9,25,11, 8,90, 7, 0, 1, 5,90, 6,12,13,90,19,16,26,20,90,\

21,99,99,99

// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,

// 0..56(?) GPIO; 95: 5V; 93: 3.3V; 90: Ground 0V;

// 99: not existent; [1..40]: valid pin number/index

Listing 4: include files config_raspberry_03.h, 04h; Differences to config_raspberry_01.h, only

Hint:	With some #define magic (in arch/arch/config.h) those include files can be selected by a variable PLATFORM in the make process. To get that useful “gimmick” and more, check out the project. Without those you’ll have to control the make process by hand and make the sources less common by hard coding some definitions.

A look at rdGnBlink.c (listing 5, page 42) will make these point clear.

/** @file rdGnBlink.c (Listing 5)

A second program for Raspberry's GPIO pins

Copyright (c) 2024 Albrecht Weinert
weinert-automation.de a-weinert.de

This is a simple GPIO pin output program using pigpio(d), still.But it has

features qualifying it as a process control service. It locks gpio, it can

run endlessly and it cleans up when ended. Hence it may be the germ cell

of (simple) real time control applications.

Compile on Pi by: gcc rdGnBlink.c -o rdGnBlink -lpigpiod_if2

Compile on Windows by:\code

arm-linux-gnueabihf-gcc -DF_CPU=1200000000 -DPLATFORM=raspberry_03 -DMCU=BCM2837
-DTARGET=Pi4all -I./include -c -o rdGnBlink.o rdGnBlink.c

arm-linux-gnueabihf-gcc -DF_CPU=1200000000 -DPLATFORM=raspberry_03 -DMCU=BCM2837
-DTARGET=Pi4all -I./include -c -o weRasp/weLockWatch.o weRasp/weLockWatch.c

arm-linux-gnueabihf-gcc -DF_CPU=1200000000 -DPLATFORM=raspberry_03 -DMCU=BCM2837
-DTARGET=Pi4all -I./include -c -o weRasp/sysBasic.o weRasp/sysBasic.c

cp rdGnBlink.elf rdGnBlink

winscp.com /script=progTransWin /parameter sweet:0123 targetPi bin gnBlinkSimple
\endcode

or on Windows simply by\code

make PROGRAM=rdGnBlink TARGET=pi4play clean all

make PROGRAM=rdGnBlink TARGET=pi4play FTPuser=sweet:0123 progapp \endcode

*/

#include <stdlib.h>

#include <stdio.h>

#include <pigpio.h>

#include <pigpiod_if2.h> // set_pull_up_down set_mode gpio_write

#include <unistd.h>

#include <signal.h> // signal SIGTERM SIGINT

#include "weLockWatch.h"

#include "arch/config.h" // evaluates PLATFORM

#define LEDgn PIN13 // green LED 1 active is attached to Pin 13

#define LEDrd PIN11 // red LED 1-active is Pin 13

#define ON 1 // true On An marche go.

#define OFF 0 // false Off Aus arret stop halt.

#define uMS 1000 // 1ms is 1000 us

int thePi; // the Pi handle for pigpoid

//--------- basic configuration and names -----------------------------

char const prgNamPure[] = "redGnBlink";

// ....0123456789x1234567

char const prgSVNrev[] = "$Revision: 271 $ ";

//........ ..........0123456789x123456789v123456789t123456789q

char const prgSVNdat[] = "$Date: 2024-11-15 10:52:04 +0100 (So, 29 Okt 2023) $";

// 0123456789x123456789v12345

static void onSign(int s){

if (s == SIGINT) { printf(

"\n rdGnBlink (%s) is shutting down normally \n", lckPiGpioPth);

exit(0); // cntl C terminates normally

} else { printf(

"\n rdGnBlink (%s) was terminated, code : %d \n", lckPiGpioPth, s);

exit(s);

}

} // onSign(int)

static void onExit(){

set_mode(thePi, LEDgn, PI_INPUT); // release red LED pin

set_mode(thePi, LEDrd, PI_INPUT); // green LED pin

closeLock();

} // onExit(int, void*)

/* Relative delays for the specified number of milliseconds */

void delay(unsigned int millis){

usleep(500 * uMS);

} // delay(unsigned int)

int main(int argc, char* argv[]){

printf(

"\n Blink green and red LEDs on Pins 13 and 11 \n"

"rdGnBlink.c R.01 15.11.2024 Albrecht Weinert \n\n");

if (openLock(lckPiGpioPth, ON)) return retCode; // no lock/singleton

thePi = pigpio_start(NULL, NULL); // connect pigpio daemon(local, 8888)

if (thePi < 0) { // initialise pigpio daemon failed

printf("\ncan't initialise IO handling (piGpioD) \n"

"rdGnBlink.c Error / return code 99 \n\n");

return 99;

} // can't initialise piGpioD (the essential gpIO library)

atexit(onExit); // register exit hook

signal(SIGTERM, onSign); // signal hook

signal(SIGABRT, onSign);

signal(SIGINT, onSign);

signal(SIGQUIT, onSign);

set_mode(thePi, LEDgn, PI_OUTPUT);

set_mode(thePi, LEDrd, PI_OUTPUT);

while(1) { // red green

gpio_write(thePi, LEDrd,ON);

delay(200); // 200 ms red

gpio_write(thePi, LEDgn, ON);

delay(100); // 100 ms both

gpio_write(thePi, LEDrd, OFF);

delay(100); // 100 ms green

gpio_write(thePi, LEDgn, OFF);

delay(200); // 200 ms dark

} // while endless // loop 600 ms

} // main()

Listing 5: rdGnBlink.c, the second more professional gpio program

Listing 5 is still is a simple GPIO output program using pigpio(d). Compared to he example gnBlinkSimple, see listing 2, page 21, the visible difference is its

a)	blinking with two LEDs instead of one.

gnBlinkSimple was self-contained, while rrdGnBlink (listing 5, page 42)

b)	uses extra sources, includes and make files.

This is purely technical and makes the sources flexible and re-usable.
Additionally rdGnBlink has

c)	features qualifying it as a process control service. It locks gpio, it can run endlessly and
it cleans up when ended.

The real decisive difference is c). Now it may be time to explain why rdGnBlink.c (listing 5) seems so long and may look com­plicated – it's neither. A minimal “hello output” with exactly the same 600 ms loop, two LEDs blinking would be 16 non comment lines easily excerpted from listing 5.

But according to this paper's title our requirements are a bit higher.

As the germ cell of of a full grown 24/7 process control / IO application we at least want it to

●	clean up and leave a specified, controlled output state when finishing or being killed and
●	prevent more than one instance of such well behaving *) control program running.
●	A watchdog is easily added (and should be, see page 62 below).
Note *):
“Well behaving” means: All programs using GPIO (i.e. pigdiod daemon) shall get a certain lock or die making a daemon user be a “singleton”.	Nb.: Holding lock by itself requires a watchdog.

Comparing listing 2 and 5 shows “process control” part implementing the external behaviour being essentially the same. The complication by those two/three – minimal by the way – requirements won’t grow substantially when getting to a full grown process control program. (And we put in extra sources and include files.)

Using the simple version (listing 2, page 21), it will easily get evident or demonstrated that run­ning two in­stances of the program gnBlinkSimple would spoil the timing behaviour on the process outputs. These multiple starts happen quite easily, often by implementing automatic starts (boot, cron, etc.) forgetting one already did so. Additionally well meaning users tend to start control applications without noticing or checking their state.

The Unix style solution in listing 5 is to use a fixed lock file that has to exist. Before entering any process control part including its initialisation, it is tried to lock that file. If it can't be locked or if it does not exist the program terminates. As a welcome side effect we can delete the lock file to prevent all future starts – those by hand as well as the automatic ones.

Of course, the lock file must be unlocked when program ends – no matter why or how the program was terminated or killed. Hence, we best implement a clean up and put the unlock there.

This clean up, we need anyway – so it's no extra complication for the unlock. When controlling process outputs it is essential to bring them in a specified state when the program ends, no matter how and why. In listing 5 (find signal() and atExit()) this is done by catching the relevant signals (interrupts) as well as the program end and putting the clean-up in the registered hooks. In our case we release (and de-energise) our outputs, which most often is the adequate procedure.

If this is not adequate the design of the process IO attached is wrong. If the outputs ar de-energised i.e. high impedance state by (our making them inputs) or by turning the (µ) controller of no actor, relais etc. should come in an active (moving, energised, …) state.

Turning off power or making outputs high impedance must lead to a “safe state” as golden rule.

Having just a “singleton” GPIO user would not mean that an application may not observe the GPIO states (if well written and behaving). gpioList shows a snapshot of gpio 0..31 for an exemplary controler running 24/7 on a Pi3 just by:

sweet@meterPi:~ $ gpioList

Pin | GPIO | Mode | freq | duty | range

-----|------|------|------|------|------

27 | 00 H | in | 400 | -92 | 255

28 | 01 H | in | 400 | -92 | 255

03 | 02 H | out | 400 | -92 | 255

05 | 03 H | in | 400 | -92 | 255

07 | 04 H | in | 400 | -92 | 255

29 | 05 L | out | 400 | -92 | 255

31 | 06 H | out | 400 | -92 | 255

26 | 07 H | out | 400 | -92 | 255

24 | 08 H | in | 400 | -92 | 255

21 | 09 L | in | 400 | -92 | 255

19 | 10 L | in | 400 | -92 | 255

23 | 11 L | in | 400 | -92 | 255

32 | 12 L | out | 400 | -92 | 255

33 | 13 L | out | 1000 | 118 | 1000

08 | 14 H | alt2 | 400 | -92 | 255

10 | 15 H | alt2 | 400 | -92 | 255

36 | 16 L | out | 400 | -92 | 255

11 | 17 H | out | 400 | -92 | 255

12 | 18 H | out | 400 | -92 | 255

35 | 19 H | in | 400 | -92 | 255

38 | 20 L | out | 400 | -92 | 255

40 | 21 L | out | 400 | 48 | 255

15 | 22 H | out | 400 | -92 | 255

16 | 23 H | out | 400 | -92 | 255

18 | 24 H | out | 400 | -92 | 255

22 | 25 H | out | 400 | -92 | 255

37 | 26 L | out | 400 | -92 | 255

13 | 27 H | out | 400 | -92 | 255

| 28 H | alt0 | 400 | -92 | 255

| 29 L | alt0 | 400 | -92 | 255

| 30 L | alt5 | 400 | -92 | 255

| 31 L | alt5 | 400 | -92 | 255

-----|------|------|------|------|------

Pin | GPIO | Mode | freq | duty | range

Low/High^ no PWM=-92

gpio 4	(pin 7) is used for 1wire which can’t be seen here; we just see input.

gpio 13 (pin 33) generates the 1kHz (1000 Hz) 50% duty cycle CP (control pilot)
signal waiting for an electrical car wanting AC one or three phase loading *).

gpio 14 and 15 (pins 8, 10) are a UART, “alt2”, controlling two smart meters via RS485 Modbus.

gpio 28 ..31 do not end in IO pins. On a compute module the are usable.

All others are are used as inputs or outputs or they are not used here (and listed as “in).

Note *): The loading circuit amplifies this to a ±12V rectangular signal with 1 kOhm impedance.
The car would load the positive half of the ±12V with a resistor to show its presence,
. . . and so on in the “analog protocol”. The duty cycle 118 / 255 is not 50%. The -10 here
compensates the asymmetries introduced here by optocouplers and the analogue circuit.

.An application as service

When using the Raspberry as server – or device – we usually want applications (process control, web server etc.) start automatically when powering up without a user having to login.

rc.local

One way is putting the starting command at the end of /etc/rc.local.
For rdGnBlink.c, listing 5, page 42, put there:

/home/sweet/bin/rdGnBlink &

Our small program will be started at the end of the boot process. Do not forget the & at the end. It puts the thing in the background goes on with the script.

To stop a program started this way, best determine the process ID number and kill it:

ps aux | grep rdGnBlink

sudo kill TheProcessNumberProvided

The process ID number is the second word in each line given by ps aux, hence lookup by:

ps aux | grep rdGnBlink | awk '{print $2}'

If valiant enough replace the number lookup by direct kill using this:

ps aux | grep rdGnBlink | awk '{print $2}' | xargs kill

or (if available) a "mass murderer" command

sudo killall rdGnBlink # also works on most Pi OSs

The last one the most simple one and is preferrable if used by hand. The others find in listing 6, page 47.

cron

The cron service knows an event “@reboot”. Add an @reboot line by

crontab -e

@reboot /home/sweet/bin/rdGnBlink

This works without & at the end at the cost of an extra shell process.

ps aux | grep rdGnBlink

reveals the extra (avoidable) shell process process, the can be killed.

pi 526 1912 388 ? 16:53 0:00 /bin/sh -c /home/sweet/bin/rdGnBlink

pi 528 4008 1696 ? 16:53 0:00 /home/sweet/bin/rdGnBlink

pi 818 4776 1916 S+ 17:16 0:00 grep --color=auto rdGnBlink

As more complicated programs require other services (gpiod e.g.), DHCP settled etc. one would like to start them a certain time (35 s in example) after reboot:

@reboot sleep 35 && /home/sweet/bin/hometersControl &

The cron service is probably running by default, but logging may not always be enabled. If some­thing is doubtful or wrong when using cron, the first look would go to its log file, /var/log/cron.log by default. You might wish to enable cron logging by:

sudo nano /etc/rsyslog.conf

and put in or uncomment this line (found under # rules #):

cron.* /var/log/cron.log

In our above example cron would log e.g.

May 26 08:53:49 rasp67 CRON[499]: pi CMD (/home/sweet/bin/rdGnBlink)

It shows our program's start time and command, but only the shell's pId.

Mimic a service – start stop restart enable disable

With a bash script (listing 47 below) rdGnBlinkCntl we control our exemplary “process control” program (listing 5: rdGnBlink.c, page 42). Make the script by

cd ~/bin & touch rdGnBlinkCntl

chmod 755 rdGnBlinkCntl

and work on it via FileZilla and editpad.exe to have this:

##!/bin/bash listing 6

showRdGnBlinkCntlVers () { echo "

# /usr/local/bin/rdGnBlinkCntl resp. ~/bin/rdGnBlinkCntl

# control the rdGnBlink service V.59 30.11.2024

# (c) 2017 Albrecht Weinert weinert-automation.de

"; }

rdGnBlinkCntlHelp () { echo "

# call by: rdGnBlinkCntl command

# commands are:

# start | stop: start or stop rdGnBlink

# restart: stop and then start

# version: show version info

"; }

if [ "X--help" == "X$1" -o "X" == "X$1" ]; then

showRdGnBlinkCntlVers ; rdGnBlinkCntlHelp ; exit 0

fi

if [ "--version" == "$1" -o "version" == "$1" ]; then

showRdGnBlinkCntlVers ; exit 0

fi

progPath=/home/sweet/bin/rdGnBlink

searchPt=rdGnBlink

stop() {

ps aux | grep -m 1 ${searchPt} | awk '{print $2}' | xargs -t kill

}

start() {

${progPath} &

pid=$!

sleep .3

if ps -p $pid > /dev/null; then

echo "rdGnBlink started with PId ${pid}"

exit 0

fi

exit 99

}

if [ "start" == "$1" ]; then start; fi

if [ "stop" == "$1" ]; then stop; exit 0; fi

if [ "restart" == "$1" ]; then stop; sleep 2;

start

fi

rdGnBlinkCntlHelp

Listing 6: Script rdGnBlinkCntl to control rdGnBlink (listing5) as service.

Making a library

Having common utility functions, variables and values in extra .c and .h files, let's see how to make a library from them, when not wanting to link them to every executable in question.

In a key matrix example ([33]) once we had three sources

●	keysPiGpioTest.c	main program
●	weRasp/sysUtil.c, include/sysUtil.h	utilities and cyclic task execution support
●	weRasp/weGPIOd.c. include/weGPIOd.h	IO support for using the gpio library

The usual (and no bad) way is to translate all three .c files to .o files best organised in the makefile and link all three .o files to the executable keysPiGpioTest.
With this procedure you may
●	change every source before cross-build. The good point is
●	get one monolithic (mid-sized) executable you may transfer to and
●	run on every Pi where a pigpiod is installed and the daemon is is running.

On the other hand you may be tempted to make one (or more) of the utilities a library – in our exemplary case sysUtil.c. Doing so you may
●	separate stable utility and runtime code from the more volatile application sources
●	keep the source code away from the application programmers.

Then you translate one source less and link the extra library with the -lsysUtil option. The library libsysUtil has to be present on the cross-build workstation as well as on every Pi where an application linked against it must run. To make the library on the workstation do:

arm-linux-gnueabihf-gcc -Wall -DMCU=BCM2837 -I./include -shared -o weRasp/libSysUtil.so -fPIC weRasp/sysUtil.c

copy weRasp\libSysUtil.so C:\util\WinRaspi\arm-linux-gnueabihf\sysroot\usr\lib\

Transfer the library libSysUtil.so to the Raspberry to a directory remotely (ftp) accessible.
There do:

sudo cp libsysUtil.so /usr/local/lib/

sudo chmod +x /usr/local/lib/libSysUtil.so

sudo ldconfig

Tzhis is put here for completeness. As long as as the cross make / cross build chain works we see no real advantage in building libraries and to have keeping track of their versions. If you want give a customer functionality without the sources this is one way.

Process IO hardware

In our introductory examples (rdGnBlink, listing5, page 42) the process IO is directly con­nected to the IO pins of Raspberry's µP. This might be feasible to a certain extend when all sensors and actuators are nearby in a closed encasement. Figure 8 shows a professional key matrix, LEDs and a piezo beeper as one suitable example for directly attached process IO. The break-out board is just a test harness and helps to connect a logic analyser – it is the black box with the green LED on the left of Figure 8.

In the production version the Pi is fixed to the back of the key matrix. When WLAN is sufficient, we now use a Zero in that place.

In all other cases and when controlling power beyond 48 mW, or distant actuators and sensors, interface and protecting circuitry is mandatory. Many offers are found, often under the genus “shield”. But beware of useless or outright dangerous devices.

Fig. 8: Direct attached IO

LEDs and buttons – direct IO

As soon as process IO signals go beyond the borders of a Raspberry, protective circuitry is man­datory. Low power LEDs, piezo speakers and buttons within the same protective encase­ment are an acceptable exception.

A real use case had three LEDs and a 12 keys matrix EOZ Clavier S.series, 12 touches. Imagine the hardware set-up as figure 8 without the breadboard and logic analyser in in a flush-mounted wall box.

The key matrix was directly attached to 7 GPIO, see listing for the concrete set-up. Single or multiple key presses were determined by a common algorithm. Its seven steps according to the seven scan lines defined in the keyMatrix structure were put in the 1ms cycle.

#define ROW123 PIN37

#define ROW456 PIN35

#define ROW789 PIN31

#define ROWa0h PIN33

#define COL147a PIN40

#define COL2580 PIN38

#define COL369h PIN36

#define NoCols 3 // number of columns

#define NoRows 4 // number of rows in key matrix

static keyMatrix thePad = {

.noCols = NoCols, .noRows = NoRows,

.colRow = {COL147a, COL2580, COL369h, // noCols + noRows,

ROW123, ROW456, ROW789, ROWa0h},

.keyVal= { // [ rowInd * noCols + colInd ]

'1', '2', '3',

'4', '5', '6',

'7', '8', '9',

'*', '0', '#' },

}; // thePad

Listing 7: Defines and structure for exemplary12 keys matrix EOZ Clavier S.series

The device's IO was controlled by a next tier system via Modbus. This general approach of using Raspberries as TCP/IP attached remote subsystems calls for a PoE solution.

LEDs directly driven by GPIO pins, of course, need a resistor of about 270 Ω and the pad drive strength can be reduced down to 8 mA.

For all the Pi GPIO pins it is essential to keep the voltage (Upin) in the range:

0V <= Upin <= Ub <= 3.3V

where Ub is the processors actual supply voltage (Pins 1 & 17). For binary inputs best use contacts (as in fig. 8, page 49) or open collector/drain transistors respectively optocouplers to ground (pins6, 9 &c.). By enabling the input pin’s pull up resistor you need no extra components.

Speakers and beepers

Piezo speakers should get a series resistor of e.g. 47 Ω, too. The drive strength may be set to 2 mA. Without resistor, one may observe spikes when applying voltage to the speaker, generated by its mechanical (resonance) vibrations. Magnetic speakers must be 200 Ω, with series resistors, when directly attached to one or two (push-pull) output pins.

For generating a tone one may use the gpio library's ability of “PWM at every pin, by setting the desired frequency and turning the tone on by PW=50 % and off by 0 %:

#define PIEPS PIN12

set_PWM_frequency(thePi, PIEPS, 400);

set_PWM_dutycycle(thePi, PIEPS, 128); // PW 50% → tone

:::::

set_PWM_dutycycle(thePi, PIEPS, 0); // PW 0% → silent (or output off)

Using speakers directly controlled by GPIO brings two problems
●	generating the tone frequency by software either directly or by hardware or library
PWM may eat resources or may bring problems with seldom used and hardly tested
library functions,
●	depending on the surrounding and the speaker the tones may be just or hardly audible.

The last point may be healed by amplification instead of using a GPIO as power source – in simple cases one n-channel MOSFET alone may do the job.

Piezo buzzers, on the other hand, contain a fixed frequency generator circuitry and a piezo speak­er of fitting resonance. With low electrical power – as deliverable from a GPIO – they can be quite loud. This solves both problems. On the other hand one has the fixed frequency, usually in the 2..3 kHz range. Nevertheless most type can very well by "on-off-modulated" up to about 600 Hz, allowing some distinguishable sound effects.

Relays

Relays are one way to handle remote actors, high power and to provide insulation.
But, you hardly find 3V relays with >= 200 Ω coil resistance. And even if so
●	the contact load of those very low power relays to control a three phase
motor switch or three pole contactor and
●	you need protective (diode) circuitry for Raspberry GPIO forbidding simple
direct attachment in the end.

For switching some "real" power by GPIO one has to use either
●	solid state relays with opto-coupler isolated control input or
●	use transistor circuitry to switch relay coils,
●	ideally isolated by optocoupler input.

Fig. 9: Eight relays module 10A
The break out board is used for attaching test equipment
(logic analyser). It is omitted in production system , of course.

The latter solution comes quite handy in (semi-) professional modules, like in figure 8, page 49. Figure 9 shows a module with eight 5 V relays, each well controllable by Raspberry's GPIO. The separate 5 V supply for the relays may come from a separate source, or also from Raspberry's 5 V power supply including PoE. So in the end one has 11 short female / female pin to pin connections: Gnd, 3.3 V, 5 V and up to eight GPIOs between the Raspberry's 40 pin connector and the relay module (omitting, of course, the experimental break out board used in figure 9).

Figure 9 shows an eight relays module with 10 A changeover contact 250 V or 30 V=. Obviously it has one status LED per relay (which is quite nice) powered by the 3.3 V side (which is OK). These LEDs are red, which is the same same mistake as with Raspberry's power LED:
In all professional process control standards red means error/fault.

On the other hand: Replacing Raspberry's red SMD LED "power and else OK" by a green one isn't difficult.

Besides this “red light sin” and besides being "no name and no documentation" the module is more than OK for less then 10 €. As the controller side and the 5 V relay supply have common ground (in most exemplars), the opto-couplers seem nice overkill (no circuit diagram available). And as a sur­prise in this undocumented case the 8 control inputs are low-active.

Similar higher power relay modules, best with 12 V coil supply, are also available and have been used in production systems.

Power transistors

N-channel power MOS-FETs or npn Darlington transistors or Darlington arrays may be used as (open drain, open collector) N-switches. The transistors would have to be placed near the Raspberry and can be connected directly (gate, array input) or via a fitting resistor (base) with a GPIO pin. (Load M and Raspberry's Gnd have to be the same than.)

Depending on the transistor / array type and a suitable supply and grounding layout, the loads may be in the range of 400mA ..10A and up to 60V. The loads and or their supplies can be placed quite separated from the Raspberry.

Note: There are (probably Chinese, totally un­documented) single power MOS-FET modules offering a red LED to signal on (nice, except the red) but featuring no current or temperature pro­tection at all. Under certain circumstances, this might be acceptable. Nevertheless, most power MOS-FETs, as the IRF520, need >= 4 V gate voltage to switch fully on. That a 3.3 V powered µC cannot deliver!	Fig. secret: Chinese module's circuit diagram

Replacing the power MOS-FET by a small signal one (if sufficient) heals the switching problem. And the 100 Ohm gate to source resistor and the LED can / should be omitted or made larger if gate on voltage is critical.

Communication hardware – RS485

As the Raspberry Pi3 has 4 USB, Ethernet, WLAN and with some extra software/configuration even Bluetooth, there's seldom need for extra communication modules. One exception may be Modbus (see page 31) over RS485 often found in quite interesting equipment, like heat pumps, gas ovens and "smart" power meters, to name just a few.
Note 1: Still, some industrial sectors are stuck at the communication hardware and protocols of the 1970ies.

Many DIN rail-mounted one or three phase energy meters come with a so called S0 bus. That's just an opto-coupler or switch output giving 300..1000 (as configured) 100 ms closures per kWh. These can be observed by Raspberry's GPIO input with pull-up and counted allowing seldom energy consumption updates and seldom and coarse non-equidistant average power samples.
Note 2: The so-called S0-"bus" is a good example of transferring century old technology's solutions without any cogitation to a new one: It's just counting and stopwatching the Ferraris wheel – but without being able to see the energy flow direction / sign of power.

Better electronic meters offer a real bus connection, usually at a slightly higher price. And, usually in this and some other fields, that will be Modbus over RS485. In the exemplary case of power meters this gives precise and actual measurements of voltage, current, frequency, active and reactive power, overall consumption and often much more.
Note 3: Despite note 1's crtiticism, using Modbus instead of the so called S0-protocoll is a big progress.

In our context, a process control set-up with those devices would be a RS485 bus with the Rasp­berry as Modbus master / client and one or many of those devices as Modbus slave / server.

RS485 is a serial two wire 5 V (3 V sufficient), push pull serial link, half-duplex, standard UART bytes + start and stop bits. To get the RS485 two wire interface one can
●	use an USB to RS485 stick or
●	attach a TTL to RS485 converter to Raspberries standard serial link:
UART: TxD = GPIO14 = pin8; RxD = GPIO15 = pin10

The USB stick solution on a Pi is commonly reported to bring driver problems and, if finally brought to work, causing resource conflicts and system crashes. The reason is Raspberry's USB link already being overused / misused for other build-in USB to XYZ converters.

Hence, one would stick with Raspberry's standard UART (pins 8 and 10). Besides having to deal with RS485 and Modbus related tasks, often, the sheer simple usage of this standard UART is more difficult than it should really be. This is partly due to no documentation at all or, worse, misleading documents and examples as not for which Pi variant they are applicable.

In short for the Pi3:
Enable serial interface and disable serial console by raspi-config or add

enable_uart=1

in /boot/firmware/config.txt (and reboot). This will bring Pins 8 (GPIO14) and 10 (GPIO15) as TX and RX in UART (i.e. ALT0) mode.

Do use /dev/ttyS0 – or the link /dev/serial0. Using other devices (like the notorious /dev/ttyAMA0) with Pi3 will not work and, sometimes worse, will make the application or at least the thread grind to halt. When using RS485 for Modbus with Stéphane Raimbault's libmodbus a Modbus master will have do do something indicated here:

modbus_t * ctx; //!< Pointer to libmodbus rtu structure

ctx = modbus_new_rtu("/dev/serial0\0",9600, 'N', 8, 1);

modbus_connect(ctx);

while (onDuty){

modbus_set_slave(ctx, 1);

modbus_read_input_registers(ctx, …);

} // onDuty

modbus_close(ctx);

modbus_free(ctx);

Fig. 10: RS485
module with automatic DE (for MAX485), i.e. transmit enable

When having the UART working, the TTL to RS485 solution as such is quite simple. In a minimal form, a MAX485 IC and three resistors would do it. Little modules of that simple kind are offered for less than one Euro. Do not confuse those with the much better module shown in figure 10 .

Problem with the minimalistic “extra output for TE” solution (not figure 10) is:
One is left alone with the "transmit enable" signal = "DE" on MAX885.

As we have logically a one wire half-duplex bus, a sender must
●	apply	"transmit enable"	before the first start bit of its (Modbus) telegram and
●	remove "transmit enable"	very shortly after the telegram's last stop bit.
Note:	“Receive enable” might be the inverse of "transmit enable" or always on.

The suggesting idea of using an extra GPIO pin to DE is naive:
●	we would have to modify the drivers respectively the Modbus library at every point,
where the sending of a telegram would start.
●	And while getting the start (even if error prone and killed by library updates) would
be feasible, hitting the telegram's end usually evolves to a not solvable problem.

In the end, pure hardware solutions generating the DE signal from TxD are the most simple ones, and, when well made, totally reliable. There are modules of that kind, just translating RxD/TxD to/from RS485-A&B; see figure 10. Besides doing the TTL to RS845 transmission line (A&B) translation by a MAX485 IC (with auto DE) for about 10 €, it brings all covered GPIO and supply pins to extra connectors. Hence other (process) IO can still easily be attached.
Note:	This should be taken for granted but is not. There are “shields” with the 40 pin connector
using supply and two IO – and burying all else.

Figure 10, page 54, shows this "GPIO forwarding" for a relays module as example.

The Plus side of this module ("joy-it") is
●	the reliable DE automatism and
●	forwarding the Raspberry pins unused for RS485 to an own pin header
supplying an eight relay module and serving eight GPIO (outputs) to it

The (big) minus is
▼	having only two screw clamps "A" and "B". One always needs "Gnd", too!
Remark: In all our Modbus applications we used the module shown in figure 10. A version with
two sets of “A, B, Gnd” screw clamps instead of the never used sub-D would have been ideal.

Fig. 11: Power over Ethernet splitter 5V, 2A

PoE – power over Ethernet

Switches, a key matrix in our example, low power LEDs and speakers / beepers and the Pi camera are peripherals one may use without extras hardware interfaces, often called shields. A Raspberry with such direct attached IO is put at its place of action and often controlled via LAN. Then getting the 5 V, 1.3 A is an extra complication, often underestimated. IEEE 802.3af Ethernet switches can provide power over Ethernet (36..57 V, up to 25 W). An on site step down converter then provides the 5V.

IEEE 802af splitters to "LAN without power" and "5V on microUSB", see figure 11, page 55, are on purchase for 10 €. We have some of those in 24/7 use without any problems. And do not use non IEEE “solutions” without converter to feed the 5 V from the other end of the LAN cable.

Raspberry Pi3+ (the “+” is new) and the Pi4 promise PoE, but it has not. It requires a special extra module, as expensive as a Pi 3, and an extra patch cable from the bulky module to the Pi. Hence, the P4 PoE solution is in no way better and more expensive than the figure 11 appoach.
Note: Modules of the kind show in the figure 11 are also available with a USB-C power supply connector for the Pi 4 and5 as well as 12V output for other purposes.

Communication

For Raspberries, the natural way to communicate with companions on the same process control tier, with servers on higher tiers or with HMI systems is IP via LAN or WLAN (best private).
Note: For micro-controllers, like e.g. most Atmel AVR boards, this is not their “natural” way.

Protocols

(W)LAN and TCP/IP communication is an integral part of Raspberries and most available OSs, including Raspbian lite and the like. A whole bunch of protocols, applications and libraries is avail­able – from start or after a bit of apt-getting. This includes ftp, http, SCP, SSH, Telnet, rlogin and more.

For process control communication (mostly of IO values and usually time critical) one might be tempted to write own binary protocols. On base of the GCC socket library (sys/socket.h etc., [63]), this can be done. Often it's wiser to use a standard protocol, like Modbus or MQTT.

1-wire

1-wire is a bus and protocol specification allowing several IO devices attached to one IO pin. For one 1-wire bus on a Raspberry the default is GPIO04 (Pin7). Another pin may be configured as well as multiple 1-wire buses. But it is good practice to stick to the default, when feasible.

Fig. 12: Robust 1-wire temperature sensor
-40...25°C , stainless steel
wires black: ground; red: +3.3V
optional suply; yellow: signal

1-wire devices do have a Vcc pin for 3..5V; 5V is allowed even when the bus is limited to 3.3V. By providing a 4K7 pull up resistor to 3.3V one may even omit the Vcc wire to the devices; they can live from the "One" bus wire alone. This feature is called "parasite power" and done by feeding an internal Vcc buffer capacitor by both the external Vcc pin and the I/O bus pin via decoupling diodes.
Available 1-wire ICs respectively devices are
●	DS1822 thermometer -40°C..+125°C; 1mK resolution, accuracy 2K;
the unique factory 64-Bit registration number starts with 0x28
●	DS18B20 thermometer -40°C..+125°C; 1mK resolution, accuracy 0.5K, see fig.12 0x28
●	DS2430 small EEPROM	(0x14)
●	DS2408 8 very small power I/O pins;	input or open drain	(0x29)
●	DS2413 2 very small power I/O pins; input or open drain	(0x3A)

For process I/O with Raspberries the last two, "digital IO via 1-wire", are not attractive. Better use GPIO directly or have a ESP8266 module do it via MQTT. And here would be no use case for an extra EEPROM as we can hibernate data on the SD-card in the OS's file system.

With Raspberry's lack of analogue input, the 1-wire temperature sensors in all their variants may come very useful. One can have the cheap naked IC as well as a device with 1..5 m cable sealed in a stainless steel tube fitting in the standard thermometer inlets of industrial tanks. When heating water with surplus solar power, e.g., one should read the temperature for safety and comfort limits.

1-wire devices are accessed by Linux's device as file approach, the behaviour being implemented in kernel modules (.so files). From the application's point of view this approach is orthogonal to the "use I/O pins directly or driver software approach". To make it happen, i.e. have the files and the kernel modules for 1-wire + thermometer available, we have to (sudo) modify two configuration files. In /boot/config.txt (old Raspbian) or in /boot/firmware/config.txt add

dtoverlay=w1-gpio # default GPio4-pin7 otherwise:dtoverlay=w1-gpio,gpiopin=x

And in /etc/modules add

w1_gpio

w1_therm

After re-boot one 1-wire is ready on the default pin *1). When a sensor is connected find it by
dir /sys/bus/w1/devices

lrwxrwxrwx 2024-10-19 w1_bus_master1	-> ../../../devices/w1_bus_master1

lrwxrwxrwx 2024-10-19 28-0113173ecfcc	-> ../../../devices/w1_bus_master1/28-0113173ecfcc

lrwxrwxrwx 2024-10-19 28-0113175b35bf	-> ../../../devices/w1_bus_master1/28-0113175b35bf

lrwxrwxrwx 2024-10-19 28-6fb4d443009b	-> ../../../devices/w1_bus_master1/28-6fb4d443009b

Here 28-0113173ecfcc points to a thermometer's directory named its unique serial number. We recommend to resolve the symbolic links *2) down to the thermo­meter's file w1_slave for a real time application.

A read of this "file" by code or command
cat /sys/devices/w1_bus_master1/28-0113173ecfcc/w1_slave
reveals an actual measurement as a two line text:

57 03 4b 46 7f ff 0c 10 42 : crc=42 YES

57 03 4b 46 7f ff 0c 10 42 t=53437

If the first line ends with YES the last number in the second line is the temperature in m°C, that is 53.4°C in the example. If the file isn't readable or if YES is not on its (fixed) position consider it an error.
Note *1 : If a GPIO is thus assigned to a kernel modul, never touch, configure, read nor write it directly.
The pigpio(d) library / daemon promises not to to so when not explicitly requested by user code.
Note *2 : Do not let time critical code follow symlinks. It's quite a task and probably not optimised away.

Real time

Every server doing work for others and every system doing process IO does so under timing re­quirements. These requirements should be well defined, best clearly documented with con­sistent numbers and units. In some fields of application and communities much of it may be considered as implicitly clear, be it by the industry's standards or by properties of the usual standard products seen as not disput­able *).

If someone with PLC background says “someThingX is done in the 1ms cycle” following is implied:

●	there is a trigger event by the underlying runtime every 1ms “exactly” … well ±
±	accuracy	(timing oscillator / source, short time)
±	jitter	(variability of delays/latency)

●	there are 86.400.000 such trigger events per day “exactly” … well ±
±	accuracy	(long time; can sometimes be made better than short time value)
+	exactly 0 or exactly 1000 events per leap second	(Google, Oracle & others: 0)

●	“someThingX” will run at every such trigger event “exactly” … well ±
+	0..max delay/latency (meaning max. .. later but never ever too early)

We have “real time” when all those times and intervals are specified to the application's require­ments and “hard real time” when one occurred violation is to be considered as application failure. While the absolute numbers are important the “hard” property has, per se, nothing to do with speed. You may replace 1ms by 1s in our mental example and relax other requirements but con­sider not having (86.400 + number of leap seconds) runs per day as a total failure.
It may be astonish­ing how “hard” that can be in some environments.

Wanting PLC / cycles and with a Raspberry Pi3 / Raspbian lite doing just process IO and related communication we can well implement 1ms cycles. Going faster might work but would be daring.
Note *) on common process IO standards: The same is true for hardware, electrical signals, maximum ratings of IO, EMC etc. Most sold IO extensions, shields etc. are totally inadequate in this respect.

Absolute timing

The approach in our very first LED blink examples (listing 2, page 21, e.g.)
do something
delay (relative from now)
do something
delay (relative from now)
… and so on
never gives an exact timing. The basic recipe to start the “do something” at exact intervals relative to one start point in time is to use absolute time steps as supported by listing 6's functions.

/* Absolute timer delay for the specified number of µs

*

* @return sleep's return value if of interest (0: uninterrupted)

*/

int timeStep(timTv * timer, unsigned int micros){

timeAddNs(timer, (long)(micros) * 1000); // timer += micros

return clock_nanosleep(CLOCK_ABS, TIMER_ABSTIME, timer, NULL);

} // delay(unsigned int)

/* Absolute timer initialisation

*

* This function initialises the time structure provided.

* @param timer the time structure to be used (never NULL!)

*/

void timeInit(timTv * timer){

clock_gettime(CLOCK_ABS, timer);

} // timeInit(timTv *)

Listing 6: Absolute time steps; excerpt from weRasp/sysUtil.c

This is also the base idea to get PLC like cyclic tasks. Here the execution time of “do something” including all jitter and latencies and considering it's CPU/core usage must be shorter than the (next) timing step.

Latency and accuracy

Of course, it is of no avail to offer 1ms cycles for process control, when the runtime's latency (in the sense of delays on signals) is in the same order of magnitude or worse. One common command line tool to check latency is cyclictest. Get it by:

sudo apt-get install rt-tests

and run it by e.g.

sudo cyclictest -l100000 -m -n -a0 -t1 -p99 -i400 -h400 -q

After some patience on a Raspberry Pi3 with process and some communication load we get a typical result like

# Total: 0001000000

# Min Latencies: 00009 # observed 4...12µs

# Avg Latencies: 00012 # observed 11...13µs

# Max Latencies: 00062 # observed 70..118µs

# Histogram Overflows: 00000

The tool and its options as well as the interpretation of the results are non-trivial. Anyway, running the same test again will give comparable results on different run-times and loads. Experimental outcomes are:

●	A well configured (specialised, single purpose, single use) PiOS lite on a Pi3
is suitable for hard real time process control and 1 ms cycles.
●	A graphical (non lite) OS in all our use cases never was and never will be.
●	Using interrupts may also overthrow predictable / acceptable latencies and kill
inter thread co-operation. Refrain from using them.

The accuracy of the 1ms cycle may very well measured by precise logic analyser observing outputs by the 1ms cycle and derived longer ones (n * 100ms) over a long period. In one case we observed an hour being 144 ms too long (~3.5 s/d). By (excerpt)

absNanos1ms = 1000000 + vcoCorrNs;

while(commonRun) { // timing loop in main thread

timeAddNs(&cyc1msEnd, absNanos1ms); // 1 ms time step

clock_nanosleep(CLOCK_ABS, TIMER_ABSTIME, &cyc1msEnd, NULL);

if (++msTo100Cnt >= 100) {

with the signed byte vcoCorrNs set to -40 we improved the accuracy by 2 orders of magnitude. This correction by a calculated fixed value worked very well over many days of uninterrupted use. So we can conclude this Pi3's quartz stability being excellent while the accuracy could be better.

Of course, this “hand-made” correction value is no practical solution for wide use. vcoCorrNs should be automatically determined by a phase locked loop (PLL) “voltage controlled oscillator” (VCO) algorithm against a precise time source, like NTP (available) or DCF77 (extra hardware).
Note: In the (latest) Jessie distribution CLOCK_REALTIME and ABS_MONOTIME's clock will be NTP tuned. Own inventions as with earlier Jessies (or libs) aren't required any longer.

Cycles and threads

For PLC like cyclic execution we offer an 1 ms, 10ms, 20 ms, 100 ms and 1 s cycle by library and run time support organising the manager (supplied) as one thread. The cyclic tasks as well as other event triggered ones will have be supplied as user threads.
Note: In a minimal runtime for AVR µControl­lers we based a similar solution
(proven 24/7 since over 9 years) on Adam Dunkel's protothreads.
As protothreads are well suited for GCC, they could have been used too. But with full grown Linux runtime on a multi-core processor it's strongly recommended to use the runtime's own threading (pthreads) and signalling system instead. See sysUtil.c and sysUtil.h.

Threading and synchronizing

As most process control must handle multiple asynchronous tasks we utilise Linux threads for
●	several cyclic tasks
●	a central task for organising all timing, i.e. organising
●	1ms and 100ms cycles and multiples of it as well as
●	time and date
●	all else tasks the application in question requires.

The main() method will have to parse arguments, provide all necessary resources and make and start all all those threads. It may serve as one of the threads – or just sit suspended waiting on the other threads end, as show in this excerpt:

int main(int argc, char * * argv){

::::::

for (;;) {

ret = getopt_long (argc, argv, "h?v", longOptions, &optIndex);

:::::

} // for over options

::::

if (theCyclistStart(290)) { ::::: } // start the central timing thread

on_exit(onExit, NULL); // register exit hook

signal(SIGTERM, onSignalStop); // register signal hook

:::

signal(SIGQUIT, onSignalStop);

::::

ret = pthread_create(&threadRS485Mod, NULL, rs485ModThread, (void*) NULL);

::::

ret = pthread_create(&threadRelays, NULL, relaysThread, (void*) NULL);

:::

if (theCyclistWaitEnd()) { ::::::: } // wait for the other threads to end

:::::

} // .main(int, char * *)

For full details see the project files sysUtil.c, sysUtil.h &c..

Preventing two or more threads to enter critical code – critical mostly by using common variables / structures or other resources – is done by a common mutex guarding the critical code:

int ret = pthread_mutex_lock(&comCycMutex); // under lock

:::::: // the critical code

int ret2 = pthread_mutex_unlock(&comCycMutex); // release lock

Under such mutex lock a thread may wait (optionally with timeout) for a signal from another thread. The wait call implies an unlock and re-lock for the (necessarily) used mutex:

int ret = pthread_mutex_lock(&comCycMutex); // under lock

:::::: // there may be optinal critical code before and after the wait call

ret = pthread_cond_wait(&cycTask->cond, &comCycMutex); // unlock / lock

int ret2 = pthread_mutex_unlock(&comCycMutex); // release lock

Under the same mutex lock another thread may send the signal to one or all waiting threads:

int ret = pthread_mutex_lock(&comCycMutex); // under lock

ret r= pthread_cond_broadcast(&cykTask->cond); // wake threads; unlock / lock

int ret2 = pthread_mutex_unlock(&comCycMutex); // release lock

Using this schema detailed in the example, threading and the necessary locking, synchronising and signalling is almost as easy as with Java. Limitations with Raspian are no pthread_yield() and no working thread priorities. The first, if needed, may be substituted by sched_yield() and the priorities by fine granular organising of the process control tasks.

Co-operating applications

Besides the cyclic and organisational tasks (threads) a process control application may have to handle HMI output, output to files (log, error, CSV etc.) and much more. Except for very small and simple applications it seems logical to move some (or all) tasks not belonging to core (real time) process control and process IO to other programs on the same computer.

On the minus or costs side are
●	inter process communication
and implicitly or explicitly
●	inter process synchronisation.

On the plus side we see
●	the core process control / IO program getting smaller and
●	kept as simple as possible,
●	having less or ideally no start parameters (start options)
●	being quite suitable to be started as background service (on boot)
and hence as depending on less resources (files, HMI and else)
●	reliably running 24/7.

Shared memory

To get to this separation of tasks in several co-operating programs we need a be-directional communication from/to the process control program and all its helper programs. Here we could use pipes, sockets or queues, e.g. But here the programs tend to be tightly coupled as consumer and producer and one's failure may severely impede the other. For processes on the same machine shared memory is the easiest way to avoid such hard dependencies and, as well, allow flexible one-to-some relations.

A good approach for our (process control) use case is to organise all
●	process input,
●	process output,
●	command input and
●	status output
in one well-formed structure (of structures) clearly defined and centrally modifiable in one .h-file common to the application – i.e. all its processes.

This common structure is held and actualised in shared memory. Each co-operating process may work on the shared memory or on a local copy. The latter approach – doing the work on a local copy and copying the parts needed / modified from / to the shared memory – is better in most cases, as almost each access to shared memory will have to done under mutual exclusion by semaphore (not to be confused with above mutex) lock.

As the memory is shared between the cooperating processes they need a common handle or name, which in Raspian is an arbitrary int (32bit). The same is true for the semaphore set. This should be put in a .h-file (sweetHome.h e.g.) common to the co-operating processes:

#define ANZ_SEMAS 3 //!< Semaphore set of three (3..10)

#define SHARED_MEMORY_SIZE 512 //!< Shared memory size 512 bytes

#define SEMAPHORE_KEY 0xcaef1924 //!< Semaphore unique key "Käfig24"

#define SHARED_MEMORY_KEY 0xbaffe324 //!< Shared memory key "Buffer24"

// always include weShareMem.h after (!) having defined the four values above

#include "weShareMem.h"

Now a program can make or get the shared memory by:

int shMemId = -1; // shared memory ID (initialised none)

static valsSharMem_t * valsSharMem = (void *)-1; //!< pointer to shared memory

// ^ common data structure; ^ initialised as error/none

:::::

shMemId = shmget((key_t)SHARED_MEMORY_KEY, SHARED_MEMORY_SIZE,

0666 | IPC_CREAT);

if (shMemId == -1){ /* error handling */ }

shMemPt = shmat(shMemId, (void *)0, 0);

if (shMemPt == (void *)-1) { /* error handling */ }

This is best seen in more detail in the files weShareMem.h and weShareMem.c showing the detaching and destroying of shared memory also.

Using the shared memory attached in a program is best done in one place by e.g.:

memcpy(valsSharMem, &valFilVal,
sizeof(valFilVal_t)); // put process values for other programs

valsSharMem->copInCnt = copInCnt; // get commands from other program

Semaphore sets

Of course, this putting values to and getting values from shared memory has to be guarded by a semaphore of a common (shared) set. Additionally, having put values for other programs can be signalled to consuming programs by another semaphore of the set.

Mutexes are private to one program and act as binary locks between this program's threads. Between threads they guarantee exclusive access to variables and other resources. And, under such lock, signals between threads by (two) special functions to be used under lock only.

Contrary to mutexes semaphores
●	come in a named shareable set to
●	work between programs (processes) and
●	are not binary but have a value range of 0..max (max is probably 96766 with Raspbian).

A basic usage of a semaphore is a binary lock when
●	decrementing to 0 is the lock operation and
●	incrementing (under lock!) to 1 is the unlock operation.

This works because the decrement blocks as long as the value is 0. The blocking hence waits until another process unlocks. As this can be quite long or worst case for ever the lock operation can and should be guarded by a timeout by using semtimedop() instead of semop(). See the handling in weShareMem.h. and .weShareMem.c..

Such binary lock is used to guard the access to all co-operating programs shared memory using one (number 0) semaphore of the common set.

Our second usage is signalling from program to a limited maximum number (say 9) of other pro­grams by using other semaphores (1...) of the common set. The other programs wait for the signal just as above by lock best with a generous timeout.

The signalling program (here under semaphore number 0 lock) uses semctl() (not semop()!) to set the signal semaphore(s) to the maximum number (say 9) of other waiting programs. Then their lock will immediately succeed. After a time agreed upon (say Tsig = shortest signal period / 6) the signalling program uses semctl() to set the signal semaphores (1..) to 0. After a successful signal lock the other programs will wait > Tsig before waiting on the next signalling. (Again see the examples named above.)

Watchdog

A well written useful, cyclic etc. process control application should run 24/7 and normally will without trouble. But from time to time (about every three months in one of our former 2017 *) set-ups) it crashes. And with so few events the causes aren't easy to investigate. Probably it will be some semaphore / synchronizing “kuddelmuddel” *) when many HMI / CGI (see below) programs are active via shared memory and one of them (or its browser or its link) crash in an unfortunate instant. In very few cases we see unwanted (frozen) process outputs.

But always, good operation could be resumed by resetting the PI manually. And reset will bring well-designed process output to safe state.

As it is good practice in process control, this rescue reset should be done by an OS independent HW watchdog when the process control program's cyclic operation ceases. In our set-up we'd trigger such watchdog in every second run of the 1-s-cycle.

Luckily, the Pi's BCM processor has such watchdog, which is exposed under Linux as a device or pseudo file (just like the 1-wire approach). By dir /dev/watchdog we see:

crw------- 1 root root 10, 130 2018-08-16 17:17 /dev/watchdog

Writing any character except 'V' to /dev/watchdog will trigger it, causing a reset after 16s, when not re-triggering it within this interval. Hence, triggering every 2 s, as said above, will fit. Writing a 'V' will disarm the watchdog. The process control program would do this at normal shutdown and after (!) bringing all process output in idle (off) state.

As seen from the dir above, only root can write the watchdog device after every system start. As we won't sudo run the process control we'll have to

sudo chmod a+=rw /dev/watchdog

before starting process control. Hence, we sudo crontab -e the following:

@reboot sleep 5 && chmod a+=rw /dev/watchdog

to have after reboot

crw-rw-rw- 1 root root 10, 130 2018-08-16 17:17 /dev/watchdog

Finally place (distribute) the following lines of code at the appropriate places

static int watchdog;

watchdog = open("/dev/watchdog", O_RDWR); // in initProcessIO()

write(watchdog, "V", 1); // disarm wachtdog

close(watchdog); // and close at releaseProcessIO()

write(watchdog, "X", 1); // in 1s cycle (every second time)

Final note *):	After some system and library update/upgrade rounds the mystic freezing was gone by
summer 2018. Nevertheless, with cyclic / real time process control a watchdog is a must.

GCI programs

As we did provide the basic CGI configuration for Apache 2.4, already, we just test it by a GCI program written C (greet.c) to deliver a simple complete html page:

#include <stdio.h>

int main(int argc, char * argv[]){

printf("Content-type: text/html\n\n"); // Every CGI delivery starts with

printf("Hello, World."); // content-type followed by content

}

On the workstation cross-compile the program by

arm-linux-gnueabihf-gcc -o greet greet.c

and ftp the binary greet it to /var/www/myPi/cgi/ . Test is by browsing to http://myPi/cgi/greet.

sudo a2enmod cgi ## if not yet enabled

A more complete version of the greet.c program (listing 7) is to show the line of actions (and the ill URL coding) in more detail.

/**

* \file greet.c

$Revision: 76 $ ($Date: 2017-12-01 19:43:45 +0100 (Fr, 01. Dez 2017) $)

Copyright (c) 2017 Albrecht Weinert

weinert-automation.de a-weinert.de

cross-compile by:

arm-linux-gnueabihf-gcc -o greet greet.c

local compile by:

gcc -o greet greet.c

*/

#include <stdio.h> // printf()

#include <stdlib.h> // getenv()

#include <string.h> // strstr()

#include <ctype.h> // isdigit()

#include <stdint.h> // uint32_t

int urlDecode(char * str){

char *ptr = str;

char c = * str;

char c2 = *++str;

for (; c; c = c2, c2 = *++str) {

if (c != '%') { *ptr++ = c; continue; }

char c3 = *(str + 1);

if (!isxdigit(c2) || !isdigit(c3)) { *ptr++ = c; continue; }

uint8_t v1 = c2 <= '9' ? c2 - '0' :

(c2 <= 'F' ? c2 - 'A' + 10 : c2 - 'a' + 10);

uint8_t v2 = c3 <= '9' ? c3 - '0' :

(c3 <= 'F' ? c3 - 'A' + 10 : c3 - 'a' + 10);

*ptr++ = (v1 << 4) | v2;

str += 2;

c2 = *str;

}

*ptr = '\0';

return 0;

} // urlDecode(char *)

int main(int argc, char * argv[]){

printf("Content-type: text/html\n\n");

printf("Hello World! <br />\n");

char * browser = getenv("HTTP_USER_AGENT");

if(browser != NULL) {

if (strstr(browser, "Chrome") != NULL) {

printf("You're using Chrome like most of use.<br />\n");

} else {

printf("Your browser: %s <br />\n", browser);

}

} // got browser info

char * query = getenv("QUERY_STRING");

if(query != NULL) { // got query string

printf("Your query: %s <br />\n", query);

urlDecode(query);

printf("Decoded : %s <br />\n", query);

} // got query string

} // main(int, char * [])

Listing 7: Extended greet.c GCI example.

Apache's mechanics behind CGI are seen best by listing 7:
When a requested URL points into a directory configured with "ExecCGI" and the filename points to an executable, Apache will
●	provide request and browser information in an environment
●	run the executable with that environment
(Note the ill coding of the query string, when coming to blanks or
non-US-ASCII characters!)
and
●	deliver the redirected standard output to the requesting Web-client.

From this course of action it will be evident that the CGI program in question will be run once per request. Hence within itself it can't hold (session) state.

The little and the extended (listing 7) example delivers a (simple) complete web page. This allows to dynamically generate pages.

More interesting is to have a static html page's JavaScript send commands (in the query string) and get information in form agreed upon (plain text, XML, JSON or what ever) and use this data to dynamically modify the page displayed. This approach is called AJAX, and every modern browser on any platform can handle it. In our context its most important advantage is
●	to reduce the communication load and
●	put the load of filtering, rendering and displaying information away from
the little (Pi) machine to the client.

If this dynamically getting data happens often or regularly it's evident, too, compiled C as CGI executable being better (for poor little server) than interpreted languages or worse scripts.

A point against PHP, and indirectly for C, is PHP's handling of Linux shared memory and sema­phore sets being just buggy, while a C program wouldn't have the slightest problems in this respect. One may view a C CGI as just another one in a band of co-operation control processes co-ordinating themselves by semaphores and shared memory.

Data exchange with AJAX & JSON

The C CGI program forwards the commands received and get its data from the process control program as described above via shared memory with guards and signals by the common semaphore set. For the data forwarded to the requesting web page plain text and JSON has been used. See (svn checkout) the examples in question.

Getting Web data – cURL

The brethren of providing web content by C is getting it within a C program. One way is to install and use the cURL library on the Raspberry (Raspbian) and on the (Windows, Eclipse) development workstation. To install the library on the Raspberry do

sudo apt-get install libcurl4-openssl-dev

This brings the application curl, a wget equivalent, too. To locally compile and build a prepared application do:

g++ getLocalWeaterData.c -o getLocalWeaterData -lcurlbd

To cross-work on the (Windows, Eclipse) development workstation ftp libcurl.so.4.3.0 from /usr/lib/arm-linux-gnueabihf to C:\util\WinRaspi\arm-linux-gnueabihf\sysroot\usr\lib\. And in C:\util\WinRaspi\arm-linux-gnueabihf\sysroot\usr\lib\ copy libcurl.so.4.3.0 to libcurl.so.

Also ftp all the complete directory curl from /usr/include/ to
C:\util\WinRaspi\arm-linux-gnueabihf\sysroot\usr\include\. Now you can compile and build on the development workstation

arm-linux-gnueabihf-gcc getLocalWeaterData.c -o getLocalWeaterData -lcurl

The executable made can be transferred to and run on a Raspberry with the cURL library installed.

Asterisk

Asterisk is a very powerful telephone system software. Using VoIP it needs no extra connection hardware besides (W)LAN – and it can be installed and run on a Pi. It can also act as one or more (software implemented) IP phones in the site's or home's telephone system (by Fritzbox e.g.).

Such software phone may call one or more “real” phones on certain process events or alarms. A common use case is bringing door bells to the garden by portable phones. A software call is defined by a file, like e.g. /home/sweet/asteriskFiles/52call_tuer.txt:

Channel: SIP/629/**52

WaitTime: 19

Application: Playback

Data: /var/lib/asterisk/sounds/tuerklingel

This example calls number **52 for max. 19s and plays an audio “door bell” to the answering phone. On Asterisk that call is initialised by copying the respective file in the “outgoing” directory by

cp /home/sweet/asteriskFiles/625call_tuer.txt	/var/spool/asterisk/outgoing/

or in C by:

system("cp /home/sweet/asteriskFiles/52call_tuer.txt /var/spool/asterisk/outgoing/");

Note: Do not use home made copying loops to put a file to asterisk/outgoing/ .

Why not a “call VoIP” application

Just to call a phone once or twice a day, having the mighty Asterisk service running 24/7 on poor little Pi seem sheer overkill and aA small “just call VoIP once” command line application the better choice. But in the end it was hard find usable ones and get them running. And (if working at all) the were dead slow. Up to 8 s delay to call are not acceptable.

Although a sleeping Asterisk seems a much heavier burden than a sleeping Apache, repentently, we returned to Asterisk. On the other hand Asterisk never impeded the 1 ms process control cycle. Listing 8 shows an exemplary Asterisk configuration file for this minimal use case.

[general]

allowguest=no

port = 5060

bindaddr = 0.0.0.0

qualify = no

disable = all

allow = alaw

allow = ulaw

videosupport = no

dtmfmode = rfc2833

srvlookup = yes

localnet=192.168.178.0/255.255.255.0

directmedia = no

nat = no

register=klingByPi:secret#@192.168.178.1/629

;Die Caller-ID wird bei Anrufen auf kompatiblen Telefonen angezeigt.

callerid=klingelPi <629>

[629]

type = friend

insecure = invite,port

nat = no

canreinvite = no

; FritzOS / Fritzbox phone **629 user, name, password and the Fritzbox IP:

authuser = klingByPi

username = klingByPi

fromuser = klingByPi

fromdomain = fritz.box

secret = secretPW

host = 192.168.178.1

dtmfmode = rfc2833

context = incoming

disallow = all

callerid= Klingel

allow = ulaw

allow = alaw

transport = udp

Listing 8: Asterisk's sip.conf.

So the good side of Asterisk is: This works reliably.

The bad side is: This trifle took hours of trial and error and search in “documentation” and forums.

The result – and where we are

We can handle Raspberry's GPIO pins in C programs.

We can handle a bundle of IO libraries pigpio being the most promising one. And pigpio(d) is the only one we used for real process control applications in the end.

And we know how to cross-compile and cross-build from Windows (or Ubuntu) using make, Eclipse and SVN there. With the make tool and some include file wizardry it is possible to integrate the WinSCP.com file (Windows only) transfer to a target Raspberry in the automated make processing.
That means with one make call we can compile, build and document a program and transfer it to the target Pi.

C is still the lingua franca in embedded. On the other hand – under the same hard requirements and restrictions – alternative languages should be tried and considered. The first choice would be Java which has been experimentally used with some success; see [32].

Some actuators and sensors may directly connect to Raspberry's GPOI but often extra interfacing IO hardware will be needed. We used and tried piXtend which might be disappointing in the light of price and promises. Nevertheless it opens the world of Codesys and Web interfaces to slow process control applications.

Low price semi-professional relay and other modules enable the Raspberry to control actuators with some power, including 230/400V equipment.

Modbus (via TCP IP and RS485) and MQTT enable more remote sensing and actuating. As MQTT attached periphery we can use bought and/or self-programmed ESP 8266 (or ESP 32) devices.

For communication process IO and related data to/from small systems the quite old Modbus protocol is still in wide use. On base of the libmodbus library we made our Raspberries Modbus servers/slaves as well as clients/masters. With Modbus via RS485 we attach one to four smart meters to our Raspberry.

With shared memory and semaphore sets we can have multiple (C) programs share data and status with or send commands to a process control program running 24/7 as service.

Such co-operating (C) program can as well be a CCI program run on request by an Apache 2.4 web-server. By this we can put any graphical user interface imaginable by HTML (5) and CSS on a user's browser in the process controlling Raspberry's LAN. Considering graphics on a Raspberry detrimental to even modest real time requirements, process control HMI is to be done by a Web interface via AJAX / GCI – and can very well be done so with acceptable response times.
or on another machine.

We can use barcode and QR code scanners attached via USB

We can use an Asterisk telephone server on the same Pi to signal process events to arbitrary phones directly or indirectly (fritz!box) attached to the LAN.

And staying with C and the pigpio library we digged a little deeper on real-time, PLC like cyclic multi-threaded execution and put such extra features in utility libraries.

The approaches described here allow real time applications like this in one Raspberry Pi3 or 4:

A home automation application featuring
●	HMI via web interface (Apache 2.4),
●	2 smart three phase meters (RS485),
●	five remote measuring and power switching devices (MQTT),
●	three temperature sensors (1-wire) and
●	a lot of locally attached binary actuators and sensors, i.e.
relays, LEDs, buttons,
●	fine grained heater power control by phase packet switching
(using a solid state relay 100A/400V in the 20 ms cycle)
is running 24/7 for more than two years (after having an equivalent laboratory set-up for considerably longer).

A simple door bells and openers monitoring and logging application using
●	Asterisk on the same Pi to forward door bell rings to telephones and
●	an Apache 2.4 on the same Pi to show the log files.).
To sample and filter the four opto-coupler isolated AC signals we take 4 * 5 equidistant samples in the 1ms cycle.

As said the C library used offers 1ms, 10ms 20ms 100ms and 1s cycles in aPLC like manner. It is recommended not to enable and use more than three of them. Slower cycles can easily implemen­ted by sub-dividing a faster one.
And as also said: Do not use a non-lite Raspbiab or PiOS for real time process control in our sense. A graphical OS with graphic and pointing devices attached is the dead of all timing. And a graphical OS with graphic devices neither attached or used (why have a grahic OS then in the end?) at least kills the timing qualities of an 1 ms cycle.

A p p e n d i x

Miscellaneous commands

This is more or less an anthology of useful and proven tips.

List all visible WLANs

sudo iwlist wlan0 scan

sudo iwlist wlan0 scan | grep "Cell\|Frequency"

Cell 01 - Address: 2C:91:AB:40:EA:14

Frequency:5.26 GHz (Channel 52)

Cell 02 - Address: 60:B5:8D:47:7D:55

Frequency:5.18 GHz (Channel 36)

Cell 03 - Address: 2C:91:AB:40:EA:13

Frequency:2.437 GHz (Channel 6)

Cell 04 - Address: 60:B5:8D:47:7D:54

Frequency:2.462 GHz (Channel 11)

Make consistent and comparable directory listings

Make a good file listing command command by:

alias dir='ls -lAh --time-style=long-iso'

To make it permanent and have some comfort and the usability of sudo with it, best add the following in ~/.bash_aliases or in .bashrc (for one user):

alias dir='ls -lAh --time-style=long-iso'

alias diR='ls -lARh --time-style=long-iso'

alias sudo='sudo '

To have it for all users put it in a file /etc/profile.d/bash_aliases.sh instead; make it if not there.

Determine Pi’s hostname

hostname

cat /etc/hostname

pi4play

Change Pi’s hostname

sudo nano /etc/hostname

and reboot. At next login accept the new fingerprint. The change of the name per se is easy. Getting along with the consequences may get the hard part: DNS server / router, filezilla settings, scripted deployment (with make), … Any undetected use of the old name can cause future trouble.

Determine OS and version

lsb_release -a

cat /etc/os-release

PRETTY_NAME="Raspbian GNU/Linux 12 (bookworm)"

NAME="Raspbian GNU/Linux"

VERSION_ID="12"

VERSION="12 (bookworm)"

VERSION_CODENAME=bookworm

ID=raspbian

ID_LIKE=debian

HOME_URL="http://www.raspbian.org/"

SUPPORT_URL="http://www.raspbian.org/RaspbianForums"

BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"

Determine Pi OS full vs lite

cat /boot/issue.txt

Raspberry Pi reference 2024-10-22

Generated using pi-gen, https://github.com/RPi-Distro/pi-gen,
94f7acf599bd0fd119f523b5afefaccf7a7a1024, stage2

Here the answer is “stage2”. The meaning is: stage1 = very minimal system;
stage2 = our lite system; stage3 = base desktop system;
stage4 = standard system (would fit in 4G card); stage5: full desktop system with “all” programs.

Determine Pi RAM size

free -ght

total used free shared buff/cache available

Mem: 7.6Gi 189Mi 7.4Gi 3.5Mi 133Mi 7.4Gi

Swap: 199Mi 0B 199Mi

Total: 7.8Gi 189Mi 7.6Gi

Total/total 7.8GB in the example means a Pi wit 8GByte RAM.

Determine Pi type

cat /proc/device-tree/model

Raspberry Pi 4 Model B Rev 1.4

Get Pi’s hardware interfaces and pinout

pinout

Description : Raspberry Pi 4B rev 1.4

Revision : d03114

SoC : BCM2711

RAM : 8GB

Storage : MicroSD

USB ports : 4 (of which 2 USB3)

Ethernet ports : 1 (1000Mbps max. speed)

Wi-fi : True

Bluetooth : True

Camera ports (CSI) : 1

Display ports (DSI): 1

:::::

List installed libraries

apt list --installed | grep pig

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

libpigpio-dev/stable,now 1.79-1+rpt1 armhf [installed,automatic]

libpigpio1/stable,now 1.79-1+rpt1 armhf [installed,automatic]

libpigpiod-if-dev/stable,now 1.79-1+rpt1 armhf [installed,automatic]

libpigpiod-if1/stable,now 1.79-1+rpt1 armhf [installed,automatic]

libpigpiod-if2-1/stable,now 1.79-1+rpt1 armhf [installed,automatic]

pigpio-tools/stable,now 1.79-1+rpt1 armhf [installed,automatic]

pigpio/stable,now 1.79-1+rpt1 armhf [installed]

pigpiod/stable,now 1.79-1+rpt1 armhf [installed,automatic]

python3-pigpio/stable,stable,now 1.79-1+rpt1 all [installed]

See all groups a user (pius e.g.) is in

id pius

uid=1001(pius) gid=1000(sweet) groups=1000(sweet),4(adm),20(dialout),
24(cdrom),27(sudo),29(audio),44(video),46(plugdev),60(games),100(users),
102(input),105(render),106(netdev),995(spi),994(i2c)

Add user to group

sudo adduser user group

Remove user from gruop

sudo deluser user group

Act as Apaches user www-data

sudo su -l www-data -s /bin/bash

Show all threads of a program

ps aux | grep rdGnBlink

sweet 1386 0.0 0.0 3440 1280 ? Sl Nov24 0:15 rdGnBlink

and with the pid (1386 in example) do

ps -T -p 1386

PID SPID TTY TIME CMD

1386 1386 ? 00:00:15 rdGnBlink

1386 1389 ? 00:00:00 rdGnBlink

Show queues (none in example) semaphores and sheared memory

ipcs

------ Message Queues --------

key msqid owner perms used-bytes messages

------ Shared Memory Segments --------

key shmid owner perms bytes nattch status

0x25baffe6 0 www-data 666 1024 1

------ Semaphore Arrays --------

key semid owner perms nsems

0x25caef19 2 sweet 666 3

Stop / start / restart web server

sudo service apache2 stop

sudo service apache2 start

sudo service apache2 restart

Act as Apaches user www-data

sudo su -l www-data -s /bin/bash

Compare two files

On Windows:	fc file1 file2

On Linux:	diff file1 file2

On Linux: with visible report if equal:

diff -s rdGnBlink rdGnBlink01

Files rdGnBlink and rdGnBlink01 are identical

Determine recursively all files containing “text”

grep -r "modbus_connect" D:\eclipCe18-12WS\stephane\libmodbus\

Binary file D:\eclipCe18-12WS\stephane\libmodbus\/.git/index matches :::::

D:\eclipCe18-12WS\stephane\libmodbus\/docs/modbus_connect.md:int
modbus_connect(modbus_t *ctx);

::::

D:\eclipCe18-12WS\stephane\libmodbus\/src/modbus.c: modbus_connect(ctx);

::::

D:\eclipCe18-12WS\stephane\libmodbus\/tests/unit-test-server.c:
rc = modbus_connect(ctx);

Limit search to specific files

grep -r --include=*.h "modbus_connect" D:\eclipCe18-12WS\stephane\libmodbus\

D:\eclipCe18-12WS\stephane\libmodbus\/src/modbus.h:
MODBUS_API int modbus_connect(modbus_t *ctx);

grep -r --include=*.c "modbus_connect" D:\eclipCe18-12WS\stephane\libmodbus\

D:\eclipCe18-12WS\stephane\libmodbus\/src/modbus.c:
modbus_connect(ctx);

:::::: 11 others :::::::

D:\eclipCe18-12WS\stephane\libmodbus\/tests/unit-test-server.c:
rc = modbus_connect(ctx);

Add line numbers

grep -rn --include=*.c "modbus_connect" D:\eclipCe18-12WS\stephane\libmodbus\

D:\eclipCe18-12WS\stephane\libmodbus\/src/modbus.c:193:
modbus_connect(ctx);

:::::: 11 others :::::::

D:\eclipCe18-12WS\stephane\libmodbus\/tests/unit-test-server.c:132:
rc = modbus_connect(ctx);

grep on Windows

Above examples on listing files containing obviously came from a Windows machine. It has grep only because of ported Linux packages installed there.

where grep

C:\util\msys32\usr\bin\grep.exe

As grep on Windows is a ported Linux tool, all grep examples work on Linux, too.

Abbreviations

More abbreviations can be found in [29]'s Appendix.

24/7	24 hours on 7 days a week; uninterrupted service (by hardware or software)

A	(as unit) Ampere

ABI	Application binary interface; here the C-library interface to OS services

ACL	Access control list

AJAX	Asynchronous JavaScript and XML (or JSON en lieu de XML)

CGI	Common Gateway Interface

CLI	Command line interpreter/interface

CoDeSys	Controller development system no-free IEC 61131-3 IDE for Windows

CRC	Cyclic redundancy check, a polynomial division

CSS	Cascading Style Sheets

CSV	Coma separated value; a pseudo table format recognised by Excel, Calc and consorts

DHCP	Dynamic Host Configuration Protocol

DNS	Domain Name System; also used in the sense of domain name server.

FTP	File Transfer Protocol; RFC1579

GPIO	General purpose IO. The µP's IO pins that have no purpose for the Raspberry
nor for its OS, but kindly having been made available for other purposes on pin grids.

GUI	Graphical user interface / graphical HMI

HMI	Human Machine Interface (without political correctness formerly MMI)

HTML	Hypertext Markup Language

ID	Identity; in the sense of a domain's ID management

IDE	Integrated development environment (like Eclipse)

IO	Input and Output

IP	Internet protocol

JSON	JavaScript Object Notation

LAN	Local Area network; here in the sense of just Ethernet

M2M	Machine to machine

MMU	Memory management unit

MQTT	Message Queuing Telemetry Transport

MS	Microsoft

Nb.	Nota bene (Latin, Italian phrase): note well, important

NTFS	NT File System;	full featured file system with fine grained access rights, links and
all else used on all Windows NT inheritors

OASIS	Organization for the Advancement of Structured Information Standards

OS	Operating System; run time

P2P	point to point, exclusive direct link between two communication partners

piGpioD Pi GPIO daemon [61..63]

Pi OS	Raspberry Pi’s standard operating systems, formerly known as Raspian – Debian variant

PoE	Power over Ethernet

PWM	Pulse width modulation (of a rectangular signal of foxed frequency)

RAM	Random access memory, also implies writeable

RDP	Remote Desktop Protocol; from Microsoft

RFC	Request for comment; internet standard

ROM	Read only memory; storage for fixed values

RS485	or TIA-485, EIA-485	serial communication standard (RS stands for recommended std)

sic!	so, exactly so (even if unbelievable) or wrong in the (cited) source already

SD	Secure digital memory card; interfaces and protocols by SD Assoc.

SPI	Serial Peripheral Interface (shift register attachment protocol)

SSD	Solid state disc, a disc drive made of non-volatile RAM

SSH	Secure socket shell

SSHFS	(remote) File system over SSH

SSL	Secure Sockets Layer; former name of TSL

U[S]ART	Universal [serial] asynchronous receiver and transmitter

V	(as unit) Volt

VoIP	Voice over IP, protocol for telephones / telephone systen on (W)LAN

W	(as unit) Watt

WS	workstation, often in the sense of PCs and laptops of all sizes

W10	MS Windows 10

XML	Extensible Markup Language

xRDP	Linux' RDP (particulate) adaptation on the (X) server side

µP	Micro-processor

References

We use identical reference numbers in [29 .. 33], hence some gaps.

[29]	Albrecht Weinert, Ubuntu for remote services, Report, November 2016 – February 2019 ,
weinert-automation.de/pub/ubuntu4remoteServices.pdf

[31]	Albrecht Weinert, Raspberry for distributed control, Report, November 2024,
This paper (the last actual version): weinert-automation.de/pub/raspberry4distributedControl.pdf

[32]	Albrecht Weinert, Raspberry Pi GPIO mit Java — Java statt C,	Java Magazin 1|2020,
pp.78-84,	GIT	.pdf	jaxenter.de/java/java-programmiersprache-c-raspberry-pi-90176

[33]	Albrecht Weinert, Raspberry for remote services, Report, May 2017 .. Oct. 2024,
This paper [31]’s predecessor a-weinert.de/pub/raspberry4remoteServices.pdf

[51]	Raspberry Org, FTP, Tips on using SSHFS
https://www.raspberrypi.org/documentation/remote-access/ssh/sshfs.md

[52]	Raspberry Org, SFTP, Tips on using SFTP
https://www.raspberrypi.org/documentation/remote-access/ssh/sftp.md

[53]	Raspberry Org, SFTP, Tips on enabling WLAN by command line
https://www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md

[54]	Qube solutions, Linux Tools & Library for PiXtend – Manual for installation and use of the
pxdev-Package with Raspberry Pi and PiXtend	(says nothing on library use)
V1.05, April 2017	http://www.pixtend.de/files/manuals/AppNote_pxdev_DE.pdf

[55]	WinSCP, FTP [command line] client, documentation	https://winscp.net/eng/docs/start

[56]	Broadcom, BCM2835 ARM Peripherals, data sheet 2012
https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

[57]	Gert van Loo, QA7ARM Quad A7 core, Technical report on BCM2836, Rev3.4 2014
https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf

[58]	Broadcom, ARM® Cortex®-A53 MPCore Processor, Rev. r0p2Technical Reference Manual
DDI0500D_cortex_a53_r0p2_trm.pdf	(BCM2837 is Quad-core 64-bit ARM cortex A53 CPU)
http://infocenter.arm.com/help/topic/com.arm.doc.ddi0500d/DDI0500D_cortex_a53_r0p2_trm.pdf

[59]	Broadcom, ARM® Cortex®-A Series, Version 1.0, Programmer’s Guide for ARMv8-A
http://infocenter.arm.com/help/topic/com.arm.doc.den0024a/DEN0024A_v8_architecture_PG.pdf

[60]	Shore, Chris, ARM, Porting to 64-bit ARM, white paper July 2014,
https://community.arm.com/cfs-file/ ... /Porting-to-ARM-64_2D00_bit.pdf

[61]	N.N., Joan, pigpio library – Download & Install, http://abyz.me.uk/rpi/pigpio/download.html

[62]	N.N., Joan, pigpio library – pigpio C interface,	http://abyz.me.uk/rpi/pigpio/cif.html

[62]	N.N., Joan, pigpio library – pigpiod C interface,	http://abyz.me.uk/rpi/pigpio/pdif2.html

[63]	Hall, Brian “Beej Jorgensen”, Beej's Guide to Network Programming – Using Internet Sockets
Version 3.0.21, 2016	http://beej.us/guide/bgnet/output/print/bgnet_A4_2.pdf

[64]	Gordon Matzigkeit, Alexandre Oliva, Thomas Tanner, Gary V. Vaughan, GNU Libtool
Vers. 2.4.6, 2015	https://www.gnu.org/software/libtool/manual/libtool.pdf

[65]	Modicon, Modbus Protocol, Reference Guide
PI–MBUS–300 Rev. J 1996,	http://modbus.org/docs/PI_MBUS_300.pd

[66]	Modbus Org, MODBUS Application Protocol Specification V1.1b3, 2012
http://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf

[67]	OASIS, MQTT V3.1.1 Protocol Specification, 2014
http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.pdf

[68]	Rahmann, Leila F., Tutorial on Mosquitto and Paho, Eindhoven 2017,
http://www.win.tue.nl/~lrahman/iot_2016/tutorial/MQTT_2016.pdf

[69]	Mosquitto, The API documentation	https://mosquitto.org/api/files/mosquitto-h.html