Warning
Wall of text, little bit of code. This question is about as much as introducing the language as it is about whether or not I still know how to write the language.
Introduction
Programmable Logic Controllers (PLCs) are industrial, heavy-duty computers used to control (semi-)automated processes in factories. They can be programmed in a large amount of languages and programmed using a multitude of languages. Up till '93, every brand had its own language. Thanks to the IEC 61131-3, languages are more (but not completely) standardized now.
One of the most used languages of the standard 5 described in the earlier mentioned IEC document is "Structured text" (ST), not to be confused with reStructuredText. ST looks a bit like Pascal and is, usually, quite easy to read.
In ST, there's a main program (traditionally called PLC_PRG) which can be accompanied by multiple Function Blocks (FBs). Every function gets its own FB. Variable declaration is separated from variable usage.
To program a PLC, you need a programmer. Those are usually packed within an IDE. An ever growing amount of PLCs can be programmed with the standard languages using CODESYS.
A typical set-up for a PLC is having a Human Machine Interface (HMI), a PLC, sensors and actuators. Most of those can be simulated and visualized so no actual hardware is required.
Problem and approach
How FizzBuzz works doesn't need much explanation. Except mine doesn't work quite like that. You see, I had a little trouble getting the text-box to work and by the time I figured it out it already looked just fine. So unlike an ordinary FizzBuzz, mine will always show the number. There will be extra output when the number is divisible by 3, 5 or both (15). All working as intended.
To keep it more interesting, I added a couple of extras. A switch to turn the program off, for example. After all, it's a PLC. It might be driving a chainsaw instead of a couple of indicators next. Everything you build in hardware needs a switch to turn it off.
The number indicator is limited to 90 or 255. Why? Because they're divisible by 15 and 100 isn't. It just looks better. There's also a reset button (self-explanatory) and an automated reset at 90. The automated reset can be toggled. If the reset isn't used, the count will go till 255 and overflow to 0. If the indicator is set to a scale end of 90, 91-255 will be indicated as 90.
The FizzBuzzer has its own FB. I've considered putting the IF
statements within it in their own FB as well, passing 3 and 5 as arguments from the FizzBuzzer, but that got surprisingly messy. Must've done something wrong. I'm also unable to make CONST
variables out of fizz_num
and buzz_num
, I kind-of forgot the proper syntax for that.
The simulation is controlled by mouse. Works like a train.
Code
PLC_PRG (PRG)
PROGRAM PLC_PRG
VAR
state : BOOL := FALSE;
reset : BOOL := FALSE;
toggle_reset: BOOL := FALSE;
i: USINT;
reset_above : USINT := 90;
fizzbuzz: FIZZBUZZ;
fizz: BOOL;
buzz: BOOL;
END_VAR
IF state THEN
i := i+1;
fizzbuzz(in:=i, fizz=>fizz, buzz=>buzz);
END_IF;
IF reset THEN
i := 0;
END_IF
IF toggle_reset AND i >= reset_above THEN
i := 0;
END_IF
FIZZBUZZ (FB)
FUNCTION_BLOCK FIZZBUZZ
VAR_INPUT
in : USINT;
END_VAR
VAR_OUTPUT
fizz : BOOL := FALSE;
buzz : BOOL := FALSE;
END_VAR
VAR
fizz_num : USINT := 3;
buzz_num : USINT := 5;
END_VAR
IF in MOD fizz_num = 0 THEN
fizz := TRUE;
ELSE
fizz := FALSE;
END_IF;
IF in MOD buzz_num = 0 THEN
buzz := TRUE;
ELSE
buzz := FALSE;
END_IF
Project overview
Result
To 90:
To 255:
From left to right, top to bottom, including the variable they're linked with:
- Index indicator (
PLC_PRG.i
) - Fizz indicator (
PLC_PRG.fizz
) - Buzz indicator (
PLC_PRG.buzz
) - Reset button (
PLC_PRG.reset
) - State switch (
PLC_PRG.state
) - Automatic reset switch (
PLC_PRG.toggle_reset
)
Whether or not that naming is logical depends on how you look at it and what you're used to, I guess. The State switch probably needs a better name.
Both animations use a MainTask interval of 250 ms and a VISU_TASK interval of 100 ms.
Footnote
This particular FizzBuzz is written in ST using CODESYS V3.5 SP12.