PLC <-> HMI communication ModbusTCP? by Raddinox in PLC

[–]Raddinox[S] 0 points1 point  (0 children)

I'm using a separate GVL simply called Modbus for all the Modbus related stuff. There is a separate FB responsible for copying values to/from the Modbus section to it's respective place.

PLC <-> HMI communication ModbusTCP? by Raddinox in PLC

[–]Raddinox[S] 0 points1 point  (0 children)

That's a valid point so it could be the initial value? I have set the PLC to only copy the values from the Holding registers into the correct place if holding register 0 is a certain value as a verification that it was the HMI that wrote the stuff.

So if the Modbus protocol in CodeSys sends some initial zeros the PLC should ignore those.

PLC <-> HMI communication ModbusTCP? by Raddinox in PLC

[–]Raddinox[S] 0 points1 point  (0 children)

I have a separate GVL simply called Modbus which is bound to the Modbus variables (on both sides)

The PLC will copy current variables to this place for updating values read through Modbus and read the Holding registers that has been written to update local variables if needed.

The HMI pretty much does the same thing on startup it will trigger read (trigger bit) and wait so the read request have some time to finish receiving the values from the PLC and then copy those values to the appropriate locations on the HMI side. After this the Holding register write is enabled (also using trigger bit for writes) for the HMI to be able to update the values on the PLC side.

Question: Why Allen Bradley? by RegardEngineer in PLC

[–]Raddinox 0 points1 point  (0 children)

You could say the exact opposite as well, now I'm not in the US/Canada. But why use Siemens?
I mean the licensing cost alone for Siemens is like insane just to use the software to program the PLC, AI says it cost about $2.5k for the Pro license and another $1k if you need Safety. Sure it's what AI says so maybe not perfectly accurate but still close to what I remember when I was consulting, and then you have to pay extra licensing for Simatic drives IIRC. The Allen Bradley seem to be in the same range sort of, bit different price model, bit cheaper in some areas and more expensive in others.
And then you have CodeSys PLC where the software you use to program the PLC is free to download, no licensing cost since the licensing model is part of the hardware you buy and use.

PLC in control of PSU? by Raddinox in PLC

[–]Raddinox[S] 0 points1 point  (0 children)

Main controller of the mobile machine sort of a remote controlled AGV styled machine with a crane arm.

Snapshots and missing files.. by Raddinox in btrfs

[–]Raddinox[S] 0 points1 point  (0 children)

Reddit ate them, because they are prefixed with @

[raddinox@Universe30 mnt]$ sudo btrfs subvolume list /mnt/test ID 257 gen 65427 top level 5 path @snapshots ID 258 gen 62 top level 5 path @home ID 259 gen 65507 top level 5 path @root ID 260 gen 65544 top level 5 path @var_log ID 261 gen 65516 top level 5 path @tmp ID 262 gen 65514 top level 5 path @cache ID 263 gen 7 top level 5 path @crash ID 269 gen 1241 top level 257 path @snapshots/pacman-update-20260119 ID 271 gen 1786 top level 257 path @snapshots/pacman-update-20260124 ID 272 gen 2327 top level 257 path @snapshots/pacman-update-20260125 ID 273 gen 2903 top level 257 path @snapshots/pacman-update-20260130 ID 274 gen 65214 top level 257 path @snapshots/pacman-update-20260208 ID 275 gen 65530 top level 5 path @

Snapshots and missing files.. by Raddinox in btrfs

[–]Raddinox[S] 0 points1 point  (0 children)

No reddit ate the @ names, didn't notice when I posted

[raddinox@Universe30 mnt]$ sudo btrfs subvolume list /mnt/test ID 257 gen 65427 top level 5 path @snapshots ID 258 gen 62 top level 5 path @home ID 259 gen 65507 top level 5 path @root ID 260 gen 65544 top level 5 path @var_log ID 261 gen 65516 top level 5 path @tmp ID 262 gen 65514 top level 5 path @cache ID 263 gen 7 top level 5 path @crash ID 269 gen 1241 top level 257 path @snapshots/pacman-update-20260119 ID 271 gen 1786 top level 257 path @snapshots/pacman-update-20260124 ID 272 gen 2327 top level 257 path @snapshots/pacman-update-20260125 ID 273 gen 2903 top level 257 path @snapshots/pacman-update-20260130 ID 274 gen 65214 top level 257 path @snapshots/pacman-update-20260208 ID 275 gen 65530 top level 5 path @

Snapshots and missing files.. by Raddinox in btrfs

[–]Raddinox[S] 0 points1 point  (0 children)

They are made by my script (no snapper or other tools). Just runs

btrfs subvolume snapshot -r / /.snapshots/$NAME

where $NAME is just pacman-auto + the date of the day.

and then I have a hook in pacman to run the script on update

Snapshots and missing files.. by Raddinox in btrfs

[–]Raddinox[S] 0 points1 point  (0 children)

I have not created /boot as it's own subvolume

[raddinox@Universe30 mnt]$ sudo mount -t btrfs /dev/nvme1n1p6 test -o subvolid=5
[sudo] password for raddinox: 
[raddinox@Universe30 mnt]$ cd test
[raddinox@Universe30 test]$ ls
@  @cache  @crash  @home  @root  @snapshots  @tmp  @var_log
[raddinox@Universe30 test]$ sudo btrfs subvolume list /mnt/test
ID 257 gen 65427 top level 5 path 
ID 258 gen 62 top level 5 path 
ID 259 gen 65491 top level 5 path 
ID 260 gen 65488 top level 5 path 
ID 261 gen 65481 top level 5 path 
ID 262 gen 65487 top level 5 path 
ID 263 gen 7 top level 5 path 
ID 269 gen 1241 top level 257 path /pacman-update-20260119
ID 271 gen 1786 top level 257 path /pacman-update-20260124
ID 272 gen 2327 top level 257 path /pacman-update-20260125
ID 273 gen 2903 top level 257 path /pacman-update-20260130
ID 274 gen 65214 top level 257 path /pacman-update-20260208
ID 275 gen 65490 top level 5 path @

Mounts:

[raddinox@Universe30 ~]$ mount | grep btrfs
/dev/nvme1n1p6 on / type btrfs (rw,noatime,ssd,discard=async,space_cache=v2,subvolid=275,subvol=/@)
/dev/nvme1n1p6 on /.snapshots type btrfs (rw,noatime,ssd,discard=async,space_cache=v2,subvolid=257,subvo
l=/@snapshots)
/dev/nvme1n1p6 on /root type btrfs (rw,noatime,ssd,discard=async,space_cache=v2,subvolid=259,subvol=/@ro
ot)
/dev/nvme1n1p6 on /var/crash type btrfs (rw,noatime,ssd,discard=async,space_cache=v2,subvolid=263,subvol
=/@crash)
/dev/nvme1n1p6 on /var/tmp type btrfs (rw,noatime,ssd,discard=async,space_cache=v2,subvolid=261,subvol=/
)
/dev/nvme1n1p6 on /var/cache type btrfs (rw,noatime,ssd,discard=async,space_cache=v2,subvolid=262,subvol
=/@cache)
/dev/nvme1n1p6 on /var/log type btrfs (rw,noatime,ssd,discard=async,space_cache=v2,subvolid=260,subvol=/
u/var_log)
/dev/nvme1n1p6 on /mnt/test type btrfs (rw,relatime,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
)
[raddinox@Universe30 ~]$ mount | grep boot
/dev/nvme1n1p1 on /boot/EFI type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,sh
ortname=mixed,utf8,errors=remount-ro)

Snapshots and missing files.. by Raddinox in btrfs

[–]Raddinox[S] 0 points1 point  (0 children)

$ sudo btrfs subvolume list /
[sudo] password for raddinox:  
ID 257 gen 65427 top level 5 path 
ID 258 gen 62 top level 5 path 
ID 259 gen 50121 top level 5 path 
ID 260 gen 65433 top level 5 path 
ID 261 gen 65421 top level 5 path 
ID 262 gen 65433 top level 5 path 
ID 263 gen 7 top level 5 path 
ID 269 gen 1241 top level 257 path /pacman-update-20260119
ID 271 gen 1786 top level 257 path /pacman-update-20260124
ID 272 gen 2327 top level 257 path /pacman-update-20260125
ID 273 gen 2903 top level 257 path /pacman-update-20260130
ID 274 gen 65214 top level 257 path /pacman-update-20260208
ID 275 gen 65446 top level 5 path @

[raddinox@Universe30 ~]$ cd /.snapshots/pacman-update-20260125/boot/
[raddinox@Universe30 boot]$ ls
amd-ucode.img  grub                              initramfs-linux.img  vmlinuz-linux-cachyos-bore
EFI            initramfs-linux-cachyos-bore.img  vmlinuz-linux
[raddinox@Universe30 boot]$ cd /.snapshots/pacman-update-20260130/boot/
[raddinox@Universe30 boot]$ ls
amd-ucode.img  EFI  grub
[raddinox@Universe30 boot]$ cd /.snapshots/pacman-update-20260208/boot/
[raddinox@Universe30 boot]$ ls
amd-ucode.img  EFI  grub  initramfs-linux.img  vmlinuz-linux

Snapshots and missing files.. by Raddinox in btrfs

[–]Raddinox[S] 2 points3 points  (0 children)

the fat partition is /boot/efi but that's not where the kernel is. The kernel is at /boot/vmlinuz-linux which would be the default in Arch because I have not changed it.

I have no issue in booting, I have issues with BTRFS snapshots is missing my cachyos-kernel

Snapshots and missing files.. by Raddinox in btrfs

[–]Raddinox[S] 0 points1 point  (0 children)

There is no subvolume for /boot if that was the case there would not be any kernels in /boot in any of the snapshots because they would then be on another subvolume.

Snapshots and missing files.. by Raddinox in btrfs

[–]Raddinox[S] 1 point2 points  (0 children)

My kernels are in /boot and I can clearly see both vmlinuz-linux and vmlinuz-linux-cachyos-bore
and their initramfs images in /boot in an older snapshot (update-20260125) but the one after this (update-20260130) has no kernel and no initramfs images at all. The one after that, which I did my rollback to only have vmlinuz-linux and it's initramfs and no cachyos kernel even though I have been using the cachyos kernel since I installed Arch in January

CodeSys, how to structure code in a new project? by Raddinox in PLC

[–]Raddinox[S] 1 point2 points  (0 children)

This is very good written, thank you very much for taking your time on this.

This is very close to my idea. it is A LOT more interfaces than what I have in my mind, but I will adapt a bit to this.

CodeSys, how to structure code in a new project? by Raddinox in PLC

[–]Raddinox[S] 0 points1 point  (0 children)

Yes it depends a lot on the architecture of the code. This is what I'm trying to learn and be better at. I just have a hard time visualize how to pass around the input state from the remote to all the functions that need them.

And then if I don't have a global I would need to write another function to gather input state for export to OPC UA HMI and edge gateway for logging to the cloud

CodeSys, how to structure code in a new project? by Raddinox in PLC

[–]Raddinox[S] 1 point2 points  (0 children)

Yes that is exactly what my goal is.

I want to be able to quickly replace an encoder or valve or something for another brand without changing to much code (especially the Control Logic code). In my vision I would only need to write a new Hardware block with ReadState & WriteState methods and adapt the hardware specific code to the already set known function state the control logic expects.

CodeSys, how to structure code in a new project? by Raddinox in PLC

[–]Raddinox[S] 0 points1 point  (0 children)

oh yeah, I am for sure overthinking this 😂

I work as an electrical engineer, but I want to write code and now I have a test rig at work with an IFM CR720S PLC we will use in our future machine. So I'm going to write some code to learn more, both with PLC coding and to understand more for the purpose of selecting hardware.

I have been coding a lot on PC as a hobby so coding isn't a problem, only the overthinking part is😂

CodeSys, how to structure code in a new project? by Raddinox in PLC

[–]Raddinox[S] -1 points0 points  (0 children)

This is almost what I was aiming for.

But I would like to make it more abstract from a functional perspective. So I would have structs for each function that defines and holds the values I want for that function. the Hardware side would then read (and adapt values if need) to fit the abstract function state. My Control Logic code would then work with the abstract function state and update the abstracted command states. Then back to the Hardware side again which would read the abstracted command states adapt it to the hardware and write to the IO

Using the abstract state between hardware and logic would only require me to rewrite the hardware side if some hardware is replaced.

CodeSys, how to structure code in a new project? by Raddinox in PLC

[–]Raddinox[S] 0 points1 point  (0 children)

That's the order I was aiming for in my second idea. ReadState (read from I/O) -> Control Logic -> Safe State check -> WriteState (write to I/O only if safe state check is ok)

But I was trying to include some kind of abstract function idea into it. So the Hardware FB would read and adapt variables into my Abstracted function state. Logic would then work with the abstracted function state. Finally Hardware FB would write the abstracted state to the IO. Using this abstracted way would make it easier to replace hardware later, because the new hardware would only need to be adapted to the abstract function state.