📜 ⬆️ ⬇️

Snake on PLC? Easy!

Good day, habrazhiteli!

It recently complained that the topic of "industrial programming" is not enough disclosed. I will try to fix it.
For clarity, we analyze how to write a classic snake for the controller of the Siemens s7-300 family.

image
')
If it became interesting - welcome under cat.
Attention - pictures and a lot of code in a language like assembly language!

The entire program is executed in the organization block OB1, consists of two functional blocks FB10 and FB11, with instance data blocks DB10 and 11.



The playing field of 10x10 cells is a two-dimensional byte array 10x10.



For our snake to work, we need to solve a small problem - we need an exact impulse arising in time. You can use the “Flashing bits”, this is a built-in feature of the controller, but we will create our own FB10 pulse generator with merkers and structures.

In the temporary variables of OB1, you can find a lot of interesting things, this time we need the time of the previous program cycle. During this time, the controller “digests” everything that is said and outputs values ​​to the outputs, then reads the inputs. It is measured with rather high accuracy, and we believe him.



As soon as the accumulated time becomes greater or equal, a pulse is generated (it will act only one cycle), we take 1000 from the accumulated value (all of a sudden we will get a little more than 1000, therefore it cannot be reset) and during one controller cycle we have a positive pulse.



Of these pulses, it is very easy to add large quantities, for example, 5 seconds.



You can also use minutes, hours, but that's another story.

Please note that you cannot use the same front variable twice, this will give a very difficult error in the logic of the program.

Now it's the turn of the function block of the snake itself.



Input variables are movement commands to the left, right, up, down and the command to start the game.
If you have a controller and a discrete input module at your fingertips, you can hang it on these variable inputs, to which non-latching buttons are attached. Get a full gaming machine. With a special desire, an array can be made from light bulbs, but I immediately get fired for that =)

The first example is the move left command.



If we filed it, do not move to the right, it happened only in this cycle, then we discard all previous commands and declare movement to the left.

Further, when starting the game, we release the array
A # snake.start
FP # frnts.pos5
JCN done
R # snake.gameover
L 0
T #looper
OPN "massive"
LAR1 P # 0.0
loop: L #looper
L 100
> = I
JC done
L 0
T DBB [AR1, P # 0.0]
+ AR1 P # 1.0
L 1
L #looper
+ I
T #looper
JU loop
done: NOP 0

It is freed by simply filling in zeros from 0 to 99th element. The fact is that in STL there is no work with two-dimensional arrays for indirect addressing, so we will represent this array as one-dimensional from 0 to 99th element.

At the start, we transfer the snake head in direct addressing to element 9.5, make its length 2, give the command to crawl up, reset the game over and give the command to eject food at a random point on the playing field.

game start
A # snake.start
FP # frnts.pos6
JCN strt
LP # 95.0
T #coordinate
L 5
T "massive" .x [9] .y [5]
L 2
T # tail_cut.lenght
S # move.up
R # snake.gameover
S # random.set_food
strt: nop 0

Next, we need to generate snake food. I repent, the generation algorithm itself was spied on one of Google’s first links, on the PLC for Good site.

It lies in the fact that the controller counts milliseconds of system time. If we take this number, add it to a random one, and then discard the extra - we get a pseudo-random number generator from 0 to the specified value.

Further, when we dropped randomly in X and Y, we take them and calculate the number of the array element. Each step in X means that you need to move to the next row of elements, that is, by 10, and each in Y means movement from the 0-element to 1.

As a result, the element of the array X [4] Y [7] turns into the 47th element of the one-dimensional array. We give him the status of 7 - food.

In case the element is busy, we start the generator again.

food generator
A # random.set_food
FP # frnts.pos7
JCN food
repl: CALL "TIME_TCK"
RET_VAL: = # random.tick
L # random.tick
AD DW # 16 # 1F
T # random.rot
L # random.tick
L # random.rot
Rld
L # random.tick
XOD
ABS
L 10
MOD
T # random.x
CALL "TIME_TCK"
RET_VAL: = # random.tick
L # random.tick
XOD DW # 16 # 1E12F
T # random.rot
L # random.tick
L # random.rot
Rld
L # random.tick
XOD
ABS
L 10
MOD
T # random.y
L 0
T #looper
LAR1 P # 0.0
posx: L #looper
L # random.x
> = I
JC next
LP # 10.0
+ AR1
L 1
L #looper
+ I
T #looper
JU posx
next: L 0
T #looper
posy: L #looper
L # random.y
> = I
JC poss
LP # 1.0
+ AR1
L 1
L #looper
+ I
T #looper
Ju posy
poss: OPN "massive"
L DBB [AR1, P # 0.0]
L 0
== I
JCN repl
L 7
T DBB [AR1, P # 0.0]
food: R # snake.omnomnom
R # random.set_food

After a successful ejection of food, the snake begins its movement, we consider an algorithm based on the movement to the left.

move left
A "db_pulsegen" .two_sec_pls
A # move.left
JCN ext1
OPN "massive"
LAR1 #coordinate
TAR1
LP # 10.0
MOD
LP # 0.0
== D
JCN ok_1
S # snake.gameover
JU gmov
ok_1: TAR1
LP # 1.0
-D
LAR1
OPN "massive"
L DBB [AR1, P # 0.0]
L 0
== I
JC nul1
L DBB [AR1, P # 0.0]
L 7
== I
JC eat1
SET
S # snake.gameover
JU gmov
eat1: SET
S # snake.omnomnom
L # tail_cut.lenght
L 1
+ I
T # tail_cut.lenght
nul1: l 3
T DBB [AR1, P # 0.0]
TAR1 #coordinate
ext1: NOP 0

In this algorithm, we immediately perform several checks. Divide by 10 - we get the rest - the number in the line of the current element. If we move to the left, being in the zero element - the end of the game.



The same thing happens if there is something other than food on the way. If you stumble upon a meal - we cock the bit that we have just eaten, it will start the generator, lengthen the tail by 1.

Next comes the last algorithm - “Tailbone”. It takes as a basis the last coordinate, reads the command on this coordinate and goes in the reverse order from the head of the snake to the tail. If the cell goes beyond the limits of length, we delete it by nulling the value

we cut tails
A # snake.start
AN # tail_cut.uncut
A "db_pulsegen" .two_sec_pls
JCN nop
L 0
T #looper
L #coordinate
T # tail_cut.tmp_coordinate
lpct: L #looper
L # tail_cut.lenght
> I
Jc cut
L # tail_cut.tmp_coordinate
LAR1
OPN "massive"
L DBB [AR1, P # 0.0]
L 3
== I
JC m_lf
L DBB [AR1, P # 0.0]
L 4
== I
JC m_rt
L DBB [AR1, P # 0.0]
L 5
== I
JC m_up
L DBB [AR1, P # 0.0]
L 6
== I
JC m_dn
Ju nop
m_lf: L # tail_cut.tmp_coordinate
LP # 1.0
+ D
T # tail_cut.tmp_coordinate
L #looper
L 1
+ I
T #looper
Ju lpct
m_rt: L # tail_cut.tmp_coordinate
LP # 1.0
-D
T # tail_cut.tmp_coordinate
L #looper
L 1
+ I
T #looper
Ju lpct
m_up: L # tail_cut.tmp_coordinate
LP # 10.0
+ D
T # tail_cut.tmp_coordinate
L #looper
L 1
+ I
T #looper
Ju lpct
m_dn: L # tail_cut.tmp_coordinate
LP # 10.0
-D
T # tail_cut.tmp_coordinate
L #looper
L 1
+ I
T #looper
Ju lpct
cut: L # tail_cut.tmp_coordinate
LAR1
OPN "massive"
L 0
T DBB [AR1, P # 0.0]
nop: nop 0

Thank you very much for the attention of everyone who reached the end of the article, I hope it was interesting.

Link to download the project

Source: https://habr.com/ru/post/203964/


All Articles